koda-tui 2.3.8 → 2.3.10
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/index.js +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -536,17 +536,17 @@ ${c}`:"");n(JSON.stringify({exit_code:d,stdout:o||"",stderr:c||"",output:_.slice
|
|
|
536
536
|
`);if(v!==n&&o.split(v).length===2)return Mt.writeFileSync(t,o.replace(v,a),"utf-8"),{success:!0,content:`Edit applied successfully.
|
|
537
537
|
[Fast Apply: fuzzy matched region]`}}}}catch{}return{success:!1,content:""}}_preReadFilesForSubAgent(t,n){let a={},d=0,_=[];if(n.path&&_.push(n.path),n.file_path&&_.push(n.file_path),n.paths&&_.push(...n.paths),n.query&&t==="scatter_search")try{let g=Ra.execSync(`grep -rl "${n.query.replace(/"/g,'\\"')}" "${this.cwd}" --include="*.py" --include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" 2>/dev/null | head -20`,{encoding:"utf-8",timeout:1e4}).trim().split(`
|
|
538
538
|
`).filter(Boolean);_.push(...g.map(m=>Bn.relative(this.cwd,m)))}catch{}if(n.error_message||n.error){let g=n.error_message||n.error||"",m=/(?:File "([^"]+)"|at\s+(?:\S+\s+\()?([^:)\s]+\.\w{1,5})):?(\d+)?/g,h;for(;(h=m.exec(g))!==null;){let T=h[1]||h[2];T&&!_.includes(T)&&_.push(T)}}for(let g of _){if(d>=20)break;try{let m=Bn.isAbsolute(g)?g:Bn.resolve(this.cwd,g);if(Mt.statSync(m).size>5e4){let T=Mt.openSync(m,"r"),v=Buffer.alloc(5e4),N=Mt.readSync(T,v,0,5e4,0);Mt.closeSync(T),a[g]=v.toString("utf-8",0,N)+`
|
|
539
|
-
... [truncated]`}else a[g]=Mt.readFileSync(m,"utf-8");d++}catch{}}return a}_pendingPlanReview=null;_planReviewTimeout=null;_requestPlanReview(t){return new Promise(n=>{this._pendingPlanReview=n,this.emit("event","plan_review",{plan:t,actions:["accept","reject","chat"]}),this._planReviewTimeout=setTimeout(()=>{this._pendingPlanReview&&(this._pendingPlanReview=null,n("accept"))},6e5)})}handlePlanReviewDecision(t){if(this._planReviewTimeout&&(clearTimeout(this._planReviewTimeout),this._planReviewTimeout=null),this._pendingPlanReview){let n=this._pendingPlanReview;this._pendingPlanReview=null,n(t)}}_sanitizeMessages(t){let n=new Set;for(let o of t)if(o.role==="assistant"&&o.tool_calls)for(let c of o.tool_calls)c.id&&n.add(c.id);let a=t.filter(o=>o.role==="tool"&&o.tool_call_id?n.has(o.tool_call_id):!0);for(let o of a)(o.content===""||o.content===null)&&(o.role==="tool"?o.content="(no output)":o.role==="assistant"&&!o.tool_calls&&(o.content="(completed)"));return a}_persistChat(t,n){try{let a=this._chatPersistentId||`chat-${Date.now()}`,o=Bn.join(K0.homedir(),".koda","chats");Mt.existsSync(o)||Mt.mkdirSync(o,{recursive:!0});let c=t.map(d=>d.role==="user"&&Array.isArray(d.content)?{...d,content:d.content.map(_=>_.type==="image_url"?{type:"text",text:"[image was provided]"}:_)}:d);Mt.writeFileSync(Bn.join(o,`${a}.json`),JSON.stringify({messages:c,turnState:n,timestamp:Date.now()}),"utf-8")}catch{}}_applyValidationActions(t,n){for(let a of t)if(a.action==="rollback"&&a.path&&a.content)try{Mt.writeFileSync(Bn.isAbsolute(a.path)?a.path:Bn.resolve(this.cwd,a.path),a.content,"utf-8")}catch{}else if(a.action==="rewrite"&&a.path&&a.content)try{Mt.writeFileSync(Bn.isAbsolute(a.path)?a.path:Bn.resolve(this.cwd,a.path),a.content,"utf-8")}catch{}else if(a.action==="append_to_tool_result"&&a.tool_call_id){for(let o of n)if(o.role==="tool"&&o.tool_call_id===a.tool_call_id){o.content=(o.content||"")+a.text;break}}}async chatV2(t){this._chatCancelled=!1,this._chatComplete=!1,this._lastModel=t.model||"gpt-5.2",this._lastMode=t.mode||"code",this._chatPersistentId||(this._chatPersistentId=`chat-${Date.now()}-${Math.random().toString(36).slice(2,10)}`);let n=this._chatTurnState||{},a=[...this.chatMessages],o=()=>{this.chatMessages=a,this._chatTurnState=n};if(t.text||t.inline_images?.length||t.images?.length){let h=t.text||"";if(!t.inline_images?.length&&!t.images?.length)a.push({role:"user",content:h});else{let T=[];h&&T.push({type:"text",text:h});for(let v of t.inline_images||[]){let N=v.data||v.base64||"";N&&T.push({type:"image_url",image_url:{url:`data:${v.mimeType||"image/png"};base64,${N}`}})}for(let v of t.images||[])try{let N=Bn.isAbsolute(v)?v:Bn.resolve(this.cwd,v),y=Mt.readFileSync(N).toString("base64"),x=Bn.extname(v).toLowerCase(),L={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp"};T.push({type:"image_url",image_url:{url:`data:${L[x]||"image/png"};base64,${y}`}})}catch{}a.push({role:"user",content:T.length>0?T:h})}}let c=[];if(t.context_files?.length)for(let h of t.context_files)try{let T=Bn.isAbsolute(h)?h:Bn.resolve(this.cwd,h),N=Mt.readFileSync(T,"utf-8").split(`
|
|
539
|
+
... [truncated]`}else a[g]=Mt.readFileSync(m,"utf-8");d++}catch{}}return a}_pendingPlanReview=null;_planReviewTimeout=null;_requestPlanReview(t){return new Promise(n=>{this._pendingPlanReview=n,this.emit("event","plan_review",{plan:t,actions:["accept","reject","chat"]}),this._planReviewTimeout=setTimeout(()=>{this._pendingPlanReview&&(this._pendingPlanReview=null,n("accept"))},6e5)})}handlePlanReviewDecision(t){if(this._planReviewTimeout&&(clearTimeout(this._planReviewTimeout),this._planReviewTimeout=null),this._pendingPlanReview){let n=this._pendingPlanReview;this._pendingPlanReview=null,n(t)}}_stripEphemeralMessages(t){let n=["[SYSTEM]","[SYSTEM NOTICE]","[SYSTEM WARNING]","[CUA SYSTEM]","<system-reminder>"];return t.filter(a=>{if(a.role!=="user"||typeof a.content!="string")return!0;let o=a.content.trimStart();return!n.some(c=>o.startsWith(c))})}_sanitizeMessages(t){let n=new Set;for(let o of t)if(o.role==="assistant"&&o.tool_calls)for(let c of o.tool_calls)c.id&&n.add(c.id);let a=t.filter(o=>o.role==="tool"&&o.tool_call_id?n.has(o.tool_call_id):!0);for(let o of a)(o.content===""||o.content===null)&&(o.role==="tool"?o.content="(no output)":o.role==="assistant"&&!o.tool_calls&&(o.content="(completed)"));return a}_persistChat(t,n){try{let a=this._chatPersistentId||`chat-${Date.now()}`,o=Bn.join(K0.homedir(),".koda","chats");Mt.existsSync(o)||Mt.mkdirSync(o,{recursive:!0});let c=t.map(d=>d.role==="user"&&Array.isArray(d.content)?{...d,content:d.content.map(_=>_.type==="image_url"?{type:"text",text:"[image was provided]"}:_)}:d);Mt.writeFileSync(Bn.join(o,`${a}.json`),JSON.stringify({messages:c,turnState:n,timestamp:Date.now()}),"utf-8")}catch{}}_applyValidationActions(t,n){for(let a of t)if(a.action==="rollback"&&a.path&&a.content)try{Mt.writeFileSync(Bn.isAbsolute(a.path)?a.path:Bn.resolve(this.cwd,a.path),a.content,"utf-8")}catch{}else if(a.action==="rewrite"&&a.path&&a.content)try{Mt.writeFileSync(Bn.isAbsolute(a.path)?a.path:Bn.resolve(this.cwd,a.path),a.content,"utf-8")}catch{}else if(a.action==="append_to_tool_result"&&a.tool_call_id){for(let o of n)if(o.role==="tool"&&o.tool_call_id===a.tool_call_id){o.content=(o.content||"")+a.text;break}}}async chatV2(t){this._chatCancelled=!1,this._chatComplete=!1,this._lastModel=t.model||"gpt-5.2",this._lastMode=t.mode||"code",this._chatPersistentId||(this._chatPersistentId=`chat-${Date.now()}-${Math.random().toString(36).slice(2,10)}`);let n=this._chatTurnState||{},a=[...this.chatMessages],o=()=>{this.chatMessages=a,this._chatTurnState=n};if(t.text||t.inline_images?.length||t.images?.length){let h=t.text||"";if(!t.inline_images?.length&&!t.images?.length)a.push({role:"user",content:h});else{let T=[];h&&T.push({type:"text",text:h});for(let v of t.inline_images||[]){let N=v.data||v.base64||"";N&&T.push({type:"image_url",image_url:{url:`data:${v.mimeType||"image/png"};base64,${N}`}})}for(let v of t.images||[])try{let N=Bn.isAbsolute(v)?v:Bn.resolve(this.cwd,v),y=Mt.readFileSync(N).toString("base64"),x=Bn.extname(v).toLowerCase(),L={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp"};T.push({type:"image_url",image_url:{url:`data:${L[x]||"image/png"};base64,${y}`}})}catch{}a.push({role:"user",content:T.length>0?T:h})}}let c=[];if(t.context_files?.length)for(let h of t.context_files)try{let T=Bn.isAbsolute(h)?h:Bn.resolve(this.cwd,h),N=Mt.readFileSync(T,"utf-8").split(`
|
|
540
540
|
`).slice(0,100).join(`
|
|
541
541
|
`);c.push({role:"user",content:`<system-reminder>
|
|
542
542
|
CONTEXT FILE: ${h}
|
|
543
543
|
${N}
|
|
544
|
-
</system-reminder>`})}catch{}t.prefetched_context?.length&&c.push(...t.prefetched_context),this.emit("event","agent_started",{});let d=null,_=0,g=0;a=this._sanitizeMessages(a),o();let m=200;try{for(let h=0;h<m&&!(this._chatCancelled||this._chatComplete);h++){let T;try{let x={messages:a,model:t.model||"gpt-5.2",mode:t.mode||"code",working_directory:t.working_directory||this.cwd,turn_state:n,auto_mode:t.auto_mode!==void 0?t.auto_mode:!0,prefetched_context:c,active_file:t.active_file,context_mode:t.context_mode||"standard"};if(d&&(x.tool_results=d,d=null),T=await this._httpPost("/api/turn",x,{timeout:3e5,trackAs:"_activeV2Request"}),this._activeV2Request=null,this._chatCancelled)break}catch(x){this.emit("event","error",{content:`Server error: ${x.message}`});break}if(T.error){if(T.error==="context_window_exceeded"&&T.compact_needed)try{a=(await this._httpPost("/api/compact",{messages:a,working_directory:this.cwd},{timeout:12e4})).messages||a,n=T.turn_state||n;continue}catch(x){this.emit("event","error",{content:`Compaction failed: ${x.message}`});break}this.emit("event","error",{content:T.error});break}n=T.turn_state||n;for(let x of T.thinking_blocks||[])this.emit("event","reasoning",{content:x,is_thinking:!0});T.context_usage&&this.emit("event","context_usage",T.context_usage);for(let x of T.system_injections||[])a.push(x);if(T.compact_needed&&!T.error)try{a=(await this._httpPost("/api/compact",{messages:a,working_directory:this.cwd},{timeout:12e4})).messages||a,n.compact_cooldown!==void 0&&(n.compact_cooldown=3)}catch{}if(T.validation_actions?.length>0&&this._applyValidationActions(T.validation_actions,a),!T.assistant_message)break
|
|
544
|
+
</system-reminder>`})}catch{}t.prefetched_context?.length&&c.push(...t.prefetched_context),this.emit("event","agent_started",{});let d=null,_=0,g=0;a=this._sanitizeMessages(a),o();let m=200;try{for(let h=0;h<m&&!(this._chatCancelled||this._chatComplete);h++){a=this._stripEphemeralMessages(a);let T;try{let x={messages:a,model:t.model||"gpt-5.2",mode:t.mode||"code",working_directory:t.working_directory||this.cwd,turn_state:n,auto_mode:t.auto_mode!==void 0?t.auto_mode:!0,prefetched_context:c,active_file:t.active_file,context_mode:t.context_mode||"standard"};if(d&&(x.tool_results=d,d=null),T=await this._httpPost("/api/turn",x,{timeout:3e5,trackAs:"_activeV2Request"}),this._activeV2Request=null,this._chatCancelled)break}catch(x){this.emit("event","error",{content:`Server error: ${x.message}`}),this._chatComplete=!0;break}if(T.error){if(T.error==="context_window_exceeded"&&T.compact_needed)try{a=(await this._httpPost("/api/compact",{messages:a,working_directory:this.cwd},{timeout:12e4})).messages||a,n=T.turn_state||n;continue}catch(x){this.emit("event","error",{content:`Compaction failed: ${x.message}`}),this._chatComplete=!0;break}this.emit("event","error",{content:T.error}),this._chatComplete=!0;break}n=T.turn_state||n;for(let x of T.thinking_blocks||[])this.emit("event","reasoning",{content:x,is_thinking:!0});T.context_usage&&this.emit("event","context_usage",T.context_usage);for(let x of T.system_injections||[])a.push(x);if(T.compact_needed&&!T.error)try{a=(await this._httpPost("/api/compact",{messages:a,working_directory:this.cwd},{timeout:12e4})).messages||a,n.compact_cooldown!==void 0&&(n.compact_cooldown=3)}catch{}if(T.validation_actions?.length>0&&this._applyValidationActions(T.validation_actions,a),!T.assistant_message){this._chatComplete=!0;break}if(a.push(T.assistant_message),!T.tool_calls||T.tool_calls.length===0){let x=T.assistant_message.content||"";if(!x.trim()&&g<3){g++,a.length>0&&a[a.length-1]===T.assistant_message&&a.pop(),a.push({role:"user",content:"[SYSTEM] You returned an empty response with no tool calls. The user is still waiting. If your previous tool results were empty or unhelpful, try a different approach \u2014 broaden your search, try different keywords, check different directories, or use alternative tools. Do NOT stop silently. You MUST either continue working or respond to the user explaining what you found and what you'll try next."});continue}this.emit("event","final_response",{content:x||"(The agent completed without generating a response.)"}),this._chatComplete=!0;break}if(T.assistant_message.content&&this.emit("event","reasoning",{content:T.assistant_message.content}),!(t.auto_mode!==void 0?t.auto_mode:!0)&&T.tool_calls?.length>0){let x=T.tool_calls.filter(L=>["run_terminal_command","write_file","edit_file","multi_edit_file","regex_replace"].includes(L.function.name));if(x.length>0){let L=x.map(k=>{let Q;try{Q=JSON.parse(k.function.arguments)}catch{Q={}}return{tool:k.function.name,args:Q,id:k.id}});this.emit("event","tool_approval_request",{tools:L})}}g=0;let N=[];for(let x of T.tool_calls){if(this._chatCancelled)break;let L;try{L=JSON.parse(x.function.arguments)}catch{L={}}_++,this.emit("event","tool_start",{tool:x.function.name,args:L,tool_call_id:x.id,step_id:_});let k=await this._executeToolLocally(x);a.push({role:"tool",tool_call_id:x.id,name:x.function.name,content:k.content||"(no output)"}),N.push({tool_call_id:x.id,name:x.function.name,content:k.content||"(no output)",args:L,...k.file_modified?{file_snapshot:this._capSnapshot(k.file_modified)}:{},...k.multi_file_snapshots?{multi_file_snapshots:k.multi_file_snapshots.map(Q=>this._capSnapshot(Q))}:{}}),this.emit("event","tool_end",{tool:x.function.name,args:L,tool_call_id:x.id,result:(k.content||"").slice(0,200),step_id:_}),k.file_modified&&this.emit("event","file_modified",{...k.file_modified,step_id:_}),k.command_output&&this.emit("event","command_output",k.command_output),x.function.name==="generate_image"&&k.content&&!String(k.content).startsWith("Error")&&this.emit("event","image_generated",{content:k.content,step_id:_})}if(this._chatCancelled&&T.tool_calls){let x=new Set(N.map(L=>L.tool_call_id));for(let L of T.tool_calls)x.has(L.id)||a.push({role:"tool",tool_call_id:L.id,name:L.function.name,content:"Tool execution cancelled by user."})}if(N.some(x=>["write_file","edit_file","multi_edit_file"].includes(x.name)&&x.file_snapshot)&&!this._chatCancelled)try{let{validateToolResults:x}=(zP(),P8(VP)),L=await x(N,this._validationConfig,{httpPost:this._httpPost.bind(this),cwd:this.cwd,model:this._lastModel,isCancelled:()=>this._chatCancelled});L.length>0&&this._applyValidationActions(L,a);for(let k of N)k.file_snapshot&&["write_file","edit_file","multi_edit_file"].includes(k.name)&&(k._client_validated=!0)}catch(x){console.warn(`[V2 ClientValidation] ${x.message}`)}d=N,o(),this.emit("event","history_update",{messages:a,message_count:a.length,token_estimate:$P(a),token_limit:VA(this._lastModel)}),n.context_dirty&&(n.context_dirty=!1),a=this._sanitizeMessages(a)}if(!this._chatCancelled&&!this._chatComplete)try{a.push({role:"user",content:"[SYSTEM] You have reached the maximum turn limit ("+m+` turns). You MUST stop all tool use and respond to the user NOW.
|
|
545
545
|
Provide a clear summary:
|
|
546
546
|
1. Every file you created or modified (with what changed)
|
|
547
547
|
2. Every action you took
|
|
548
548
|
3. What is left to do, if anything
|
|
549
|
-
Do NOT call any tools. Just respond with text.`});let h=await this._httpPost("/api/turn",{messages:a,model:t.model||"gpt-5.2",mode:t.mode||"code",working_directory:t.working_directory||this.cwd,turn_state:n,auto_mode:!1,active_file:t.active_file,context_mode:t.context_mode||"standard"},{timeout:12e4}),T=h?.assistant_message?.content||"";T.trim()?(a.push(h.assistant_message),this.emit("event","final_response",{content:T})):this.emit("event","final_response",{content:"I've reached the turn limit ("+m+" turns). Please review the changes above and send a follow-up message if you'd like me to continue."})}catch(h){console.warn(`[HttpBridge] Turn-limit wrap-up LLM call failed: ${h.message}`),this.emit("event","final_response",{content:"I've reached the turn limit ("+m+" turns). Please review the changes above and send a follow-up message if you'd like me to continue."})}}finally{o(),this.emit("event","history_update",{messages:a,message_count:a.length,token_estimate:$P(a),token_limit:VA(this._lastModel)}),this.
|
|
549
|
+
Do NOT call any tools. Just respond with text.`});let h=await this._httpPost("/api/turn",{messages:a,model:t.model||"gpt-5.2",mode:t.mode||"code",working_directory:t.working_directory||this.cwd,turn_state:n,auto_mode:!1,active_file:t.active_file,context_mode:t.context_mode||"standard"},{timeout:12e4}),T=h?.assistant_message?.content||"";T.trim()?(a.push(h.assistant_message),this.emit("event","final_response",{content:T})):this.emit("event","final_response",{content:"I've reached the turn limit ("+m+" turns). Please review the changes above and send a follow-up message if you'd like me to continue."})}catch(h){console.warn(`[HttpBridge] Turn-limit wrap-up LLM call failed: ${h.message}`),this.emit("event","final_response",{content:"I've reached the turn limit ("+m+" turns). Please review the changes above and send a follow-up message if you'd like me to continue."})}}finally{a=this._stripEphemeralMessages(a),o(),this.emit("event","history_update",{messages:a,message_count:a.length,token_estimate:$P(a),token_limit:VA(this._lastModel)}),this.emit("event","agent_complete",{}),this._chatComplete=!0,this._persistChat(a,n)}return{messages:a}}stopChatV2(){if(this._chatCancelled=!0,this._chatComplete=!0,this._activeV2Request){try{this._activeV2Request.destroy()}catch{}this._activeV2Request=null}}};var QZ=xZ(import.meta.url),PZ=OZ(QZ);function ZP(e,t=8e3){return new Promise((n,a)=>{let c=(e.startsWith("https")?LZ:FZ).get(e,{timeout:t},d=>{if(d.statusCode===301||d.statusCode===302){d.resume(),ZP(d.headers.location,t).then(n).catch(a);return}let _="";d.on("data",g=>_+=g),d.on("end",()=>{try{n(JSON.parse(_))}catch(g){a(g)}})});c.on("error",a),c.on("timeout",()=>{c.destroy(),a(new Error("timeout"))})})}function kZ(e,t){if(!e||!t)return!1;let n=e.split(".").map(Number),a=t.split(".").map(Number);for(let o=0;o<3;o++){if((n[o]||0)>(a[o]||0))return!0;if((n[o]||0)<(a[o]||0))return!1}return!1}async function UZ(){let e="";try{e=wZ(import.meta.url)(BZ(PZ,"..","package.json")).version||""}catch{return}let t;try{t=await ZP("https://aifc.dev.att.com/downloads/version.json")}catch{return}let n=t?.tui_version||"",a=t?.tui_tarball||"";if(!n||!a||!kZ(n,e))return;process.stdout.write(`
|
|
550
550
|
${P.BOLD}${P.CYAN}Koda CLI update available${P.RESET} ${P.DIM}v${e} \u2192 v${n}${P.RESET}
|
|
551
551
|
${P.DIM}Updating...${P.RESET}
|
|
552
552
|
`);try{JP(`npm install -g "${a}"`,{stdio:"inherit"})}catch{let d=`https://registry.npmjs.org/koda-tui/-/koda-tui-${n}.tgz`;process.stdout.write(` ${P.DIM}Retrying from npm registry...${P.RESET}
|