@openadapter/koda 1.0.0-beta.17 → 1.0.0-beta.18
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.
|
@@ -79,7 +79,7 @@ export type AgentSessionEvent = Exclude<AgentEvent, {
|
|
|
79
79
|
model: string;
|
|
80
80
|
} | {
|
|
81
81
|
type: "overseer_end";
|
|
82
|
-
outcome: "complete" | "nudging" | "skipped";
|
|
82
|
+
outcome: "complete" | "nudging" | "skipped" | "gave_up";
|
|
83
83
|
detail?: string;
|
|
84
84
|
} | {
|
|
85
85
|
type: "model_switch_notice";
|
|
@@ -227,10 +227,10 @@ export declare class AgentSession {
|
|
|
227
227
|
private _zenitsuPreferred;
|
|
228
228
|
private _zenitsuCooldown;
|
|
229
229
|
private _zenitsuHealthCache;
|
|
230
|
-
private _overseerRounds;
|
|
231
230
|
private _currentGoal;
|
|
232
231
|
private _tierCache;
|
|
233
232
|
private _overseerLastError;
|
|
233
|
+
private _verifyLedger;
|
|
234
234
|
private _escalationsDone;
|
|
235
235
|
private _escalateTried;
|
|
236
236
|
private _sessionEscalations;
|
|
@@ -631,6 +631,11 @@ export declare class AgentSession {
|
|
|
631
631
|
private _detectVerify;
|
|
632
632
|
private _runVerification;
|
|
633
633
|
private _overseerCheck;
|
|
634
|
+
/** Build the evidence-bearing nudge the overseer sends back to the model. */
|
|
635
|
+
private _overseerNudge;
|
|
636
|
+
/** Stable signature of a failure so repeats are detectable across attempts (volatile
|
|
637
|
+
* bits — line/col numbers, hashes, counts — are normalised out). */
|
|
638
|
+
private _failureSignature;
|
|
634
639
|
private _taskEndReview;
|
|
635
640
|
private _reviewOnce;
|
|
636
641
|
/**
|
|
@@ -1,32 +1,36 @@
|
|
|
1
|
-
var Y=Object.defineProperty;var w=(P,e)=>Y(P,"name",{value:e,configurable:!0});import{existsSync as C,mkdirSync as K,readFileSync as E,unlinkSync as Z,writeFileSync as L}from"node:fs";import{homedir as X}from"node:os";import{basename as ee,dirname as B,join as S,relative as te,resolve as F}from"node:path";import{clampThinkingLevel as se,cleanupSessionResources as ne,getSupportedThinkingLevels as ie,isContextOverflow as $,modelsAreEqual as T,resetApiProviders as re,streamSimple as H}from"@openadapter/koda-ai";import{theme as oe}from"../modes/interactive/theme/theme.js";import{stripFrontmatter as z}from"../utils/frontmatter.js";import{sendKodaTelemetry as ae}from"../utils/koda-telemetry.js";import{resolvePath as le}from"../utils/paths.js";import{sleep as ce}from"../utils/sleep.js";import{formatNoApiKeyFoundMessage as O,formatNoModelSelectedMessage as j}from"./auth-guidance.js";import{executeBashWithOperations as he}from"./bash-executor.js";import{calculateContextTokens as G,collectEntriesForBranchSummary as de,compact as W,estimateContextTokens as q,generateBranchSummary as ue,prepareCompaction as V,shouldCompact as me}from"./compaction/index.js";import{DEFAULT_THINKING_LEVEL as ge}from"./defaults.js";import{execCommand as pe}from"./exec.js";import{exportSessionToHtml as fe}from"./export-html/index.js";import{createToolHtmlRenderer as _e}from"./export-html/tool-renderer.js";import{ExtensionRunner as ye,wrapRegisteredTools as J}from"./extensions/index.js";import{emitSessionShutdownEvent as we}from"./extensions/runner.js";import{expandPromptTemplate as I}from"./prompt-templates.js";import{CURRENT_SESSION_VERSION as be,getLatestCompactionEntry as Q}from"./session-manager.js";import{createSyntheticSourceInfo as N}from"./source-info.js";import{buildSystemPrompt as xe}from"./system-prompt.js";import{createLocalBashOperations as ve}from"./tools/bash.js";import{createAllToolDefinitions as Ae}from"./tools/index.js";import{createToolDefinitionFromAgentTool as Me}from"./tools/tool-definition-wrapper.js";function Ze(P){const e=P.match(/^<skill name="([^"]+)" location="([^"]+)">\n([\s\S]*?)\n<\/skill>(?:\n\n([\s\S]+))?$/);return e?{name:e[1],location:e[2],content:e[3],userMessage:e[4]?.trim()||void 0}:null}w(Ze,"parseSkillBlock");const ke=["off","minimal","low","medium","high"];class Xe{static{w(this,"AgentSession")}agent;sessionManager;settingsManager;_scopedModels;_unsubscribeAgent;_eventListeners=[];_steeringMessages=[];_followUpMessages=[];_pendingNextTurnMessages=[];_compactionAbortController=void 0;_autoCompactionAbortController=void 0;_overflowRecoveryAttempted=!1;_branchSummaryAbortController=void 0;_retryAbortController=void 0;_retryAttempt=0;_overseerAbortController=void 0;_recapAbortController=void 0;_turnActive=!1;_failoverTried=new Set;get _zenitsu(){return process.env.KODA_ZENITSU==="1"}_zenitsuPreferred;_zenitsuCooldown=new Map;_zenitsuHealthCache;_overseerRounds=0;_currentGoal;_tierCache;_overseerLastError="";_escalationsDone=0;_escalateTried=new Set;_sessionEscalations=0;_stickyModel;_homeModel;_sessionGoal;_pinnedSkills=new Map;_codeChangedThisTask=!1;_verifyCmd;_taskStartTs=0;_taskHadActivity=!1;_suggestShown=new Set;_suggestedThisTask=!1;_harnessTrace=[];_gatewayCircuit={failures:0,openUntil:0};_bashAbortController=void 0;_pendingBashMessages=[];_extensionRunner;_turnIndex=0;_resourceLoader;_customTools;_baseToolDefinitions=new Map;_cwd;_protectedSnap=new Map;_extensionRunnerRef;_initialActiveToolNames;_allowedToolNames;_excludedToolNames;_baseToolsOverride;_sessionStartEvent;_extensionUIContext;_extensionMode="print";_extensionCommandContextActions;_extensionAbortHandler;_extensionShutdownHandler;_extensionErrorListener;_extensionErrorUnsubscriber;_modelRegistry;_toolRegistry=new Map;_toolDefinitions=new Map;_toolPromptSnippets=new Map;_toolPromptGuidelines=new Map;_baseSystemPrompt="";_baseSystemPromptOptions;constructor(e){this.agent=e.agent,this.sessionManager=e.sessionManager,this.settingsManager=e.settingsManager,this._scopedModels=e.scopedModels??[],this._resourceLoader=e.resourceLoader,this._customTools=e.customTools??[],this._cwd=e.cwd,this._modelRegistry=e.modelRegistry,this._extensionRunnerRef=e.extensionRunnerRef,this._initialActiveToolNames=e.initialActiveToolNames,this._allowedToolNames=e.allowedToolNames?new Set(e.allowedToolNames):void 0,this._excludedToolNames=e.excludedToolNames?new Set(e.excludedToolNames):void 0,this._baseToolsOverride=e.baseToolsOverride,this._sessionStartEvent=e.sessionStartEvent??{type:"session_start",reason:"startup"},this._unsubscribeAgent=this.agent.subscribe(this._handleAgentEvent),this._installAgentToolHooks(),this._buildRuntime({activeToolNames:this._initialActiveToolNames,includeAllExtensionTools:!0})}get modelRegistry(){return this._modelRegistry}async _getRequiredRequestAuth(e){const t=await this._modelRegistry.getApiKeyAndHeaders(e);if(!t.ok)throw t.error.startsWith("No API key found")?new Error(O(e.provider)):new Error(t.error);if(t.apiKey)return{apiKey:t.apiKey,headers:t.headers};throw this._modelRegistry.isUsingOAuth(e)?new Error(`Authentication failed for "${e.provider}". Credentials may have expired or network is unavailable. Run '/login ${e.provider}' to re-authenticate.`):new Error(O(e.provider))}async _getCompactionRequestAuth(e){if(this.agent.streamFn===H)return this._getRequiredRequestAuth(e);const t=await this._modelRegistry.getApiKeyAndHeaders(e);return t.ok?{apiKey:t.apiKey,headers:t.headers}:{}}_installAgentToolHooks(){const e=process.env.KODA_UNSAFE==="1",t=process.cwd(),s=[/(^|\/)test_[^/]*\.[A-Za-z0-9]+$/,/(^|\/)[^/]*_test\.[A-Za-z0-9]+$/,/\.(test|spec)\.[A-Za-z0-9]+$/,/(^|\/)(tests?|__tests__|spec)\//,/(^|\/)\.git(\/|$)/,/(^|\/)\.env(\.[^/]*)?$/,/(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|poetry\.lock|Cargo\.lock)$/],n=(process.env.KODA_PROTECT||"").split(",").map(m=>m.trim()).filter(Boolean),i=w(m=>{const p=m.replace(/\\/g,"/");return s.some(y=>y.test(p))||n.some(y=>p.includes(y))},"isProtected"),o=w(m=>{for(const p of m.matchAll(/(?:>>?|\btee\s+(?:-a\s+)?)\s*([^\s;|&>]+)/g))if(i(p[1]))return`output redirect onto protected file '${p[1]}'`;return/\brm\s+(?:-\w+\s+)*[^\n;|&]*(?:test_|tests?\/|__tests__|\.git|\.env)/.test(m)?"rm targeting a protected path":/\bgit\s+(?:reset\s+--hard|clean\b|checkout\s+(?:--\s+)?\.)/.test(m)?"destructive git command":null},"dangerousBash"),r=S(process.env.KODA_CODING_AGENT_DIR||S(X(),".koda","agent"),"checkpoints",String(this.sessionId||"session")),l=new Set,a=w(m=>{if(!l.has(m)){l.add(m);try{K(r,{recursive:!0});const p=S(r,`${m.replace(/[^A-Za-z0-9]/g,"_")}.json`),y=C(m);L(p,JSON.stringify({path:m,existed:y,content:y?E(m,"utf-8"):null}))}catch{}}},"backup");this.agent.beforeToolCall=async({toolCall:m,args:p})=>{if((m.name==="write"||m.name==="edit")&&(this._codeChangedThisTask=!0),this._taskHadActivity=!0,m.name==="bash"){const v=String(p?.command??"");/\b(npm|pnpm|yarn|bun)\s+(run\s+)?(dev|start|serve|preview|watch)\b|\bvite\b|\bnext\s+(dev|start)\b|\bnuxt\s+dev\b|\bnodemon\b|http[-.]server\b|\bserve\b|flask\s+run|rails\s+s(erver)?\b|manage\.py\s+runserver|\b(uvicorn|gunicorn)\b/i.test(v)&&this._suggest("bg","That looks like a long-running server. Use run_background (or /bg) so it keeps running without blocking Koda.")}if(process.env.KODA_MODE==="plan"){const v=p,f=m.name;if(f==="write"||f==="edit")return{block:!0,reason:"Koda is in PLAN MODE \u2014 file changes are disabled. Investigate and present a step-by-step plan instead; the user will run /build to execute it."};if(f==="bash"&&this._isMutatingBash(String(v?.command??"")))return{block:!0,reason:"Koda is in PLAN MODE \u2014 commands that change state are disabled. Use read-only commands to investigate, then present a plan; the user will run /build to execute it."}}if(!e){const v=p,f=m.name;if(f==="write"||f==="edit"){const _=String(v?.path??v?.file_path??"");if(_){const A=F(t,_);if(te(t,A).startsWith(".."))return{block:!0,reason:`Refused: '${_}' is outside the workspace. Only modify files under the project directory.`};if(i(_))return{block:!0,reason:`Refused: '${_}' is a protected file (tests / .git / secrets / lockfiles). Do not modify it \u2014 change the source code instead.`};a(A)}}else if(f==="bash"){const _=o(String(v?.command??""));if(_)return{block:!0,reason:`Refused: ${_}. Use the edit/write tools on source files instead of shell redirects, and never touch tests/.git/secrets.`}}const M=[];if(f==="write"||f==="edit"){const _=String(v?.path??v?.file_path??"");_&&M.push(_)}else if(f==="bash")for(const _ of String(v?.command??"").matchAll(/[\w./-]*[\w]\.[A-Za-z0-9]+/g))M.push(_[0]);for(const _ of M){if(!i(_))continue;const A=F(t,_);if(!this._protectedSnap.has(A))try{this._protectedSnap.set(A,C(A)?E(A,"utf-8"):null)}catch{}}}const y=this._extensionRunner;if(y.hasHandlers("tool_call"))try{return await y.emitToolCall({type:"tool_call",toolName:m.name,toolCallId:m.id,input:p})}catch(v){throw v instanceof Error?v:new Error(`Extension failed, blocking execution: ${String(v)}`)}};const c=process.env.KODA_CASCADE_MODEL,d=Number(process.env.KODA_CASCADE_ERRORS)||3,u=Number(process.env.KODA_CASCADE_STEPS)||16;let g=0,x=0,h=!1,b;this.agent.afterToolCall=async({toolCall:m,args:p,result:y,isError:v})=>{for(const[_,A]of this._protectedSnap){let k=null;try{k=C(_)?E(_,"utf-8"):null}catch{continue}if(k!==A)try{A===null?C(_)&&Z(_):L(_,A,"utf-8"),console.error(`[koda safety] reverted unauthorized change to protected file ${_}`)}catch{}}if(c&&!h&&(x++,v?g++:g=0,g>=d||x>=u)){const _=this.agent.state.model,[A,k]=c.includes("/")?[c.slice(0,c.indexOf("/")),c.slice(c.indexOf("/")+1)]:[_.provider,c],R=this._modelRegistry.find(A,k);R&&!T(R,_)&&(b=R,this.agent.state.model=R,this.sessionManager.appendModelChange(R.provider,R.id),this._emitModelSelect(R,_,"cycle"),h=!0,console.error(`[koda cascade] escalated ${_.provider}/${_.id} \u2192 ${R.provider}/${R.id} (errors=${g}, toolCalls=${x})`))}const f=this._extensionRunner;if(!f.hasHandlers("tool_result"))return;const M=await f.emitToolResult({type:"tool_result",toolName:m.name,toolCallId:m.id,input:p,content:y.content,details:y.details,isError:v});if(M)return{content:M.content,details:M.details,isError:M.isError??v}},this.agent.prepareNextTurn=async()=>{if(this._zenitsu){const m=await this._zenitsuPickModel();if(m&&!T(m,this.agent.state.model))return{model:m}}return h&&b?{model:b}:void 0}}_emit(e){for(const t of this._eventListeners)t(e)}_emitQueueUpdate(){this._emit({type:"queue_update",steering:[...this._steeringMessages],followUp:[...this._followUpMessages]})}_lastAssistantMessage=void 0;_handleAgentEvent=w(async e=>{if(e.type==="message_start"&&e.message.role==="user"){this._overflowRecoveryAttempted=!1;const t=this._getUserMessageText(e.message);if(t){const s=this._steeringMessages.indexOf(t);if(s!==-1)this._steeringMessages.splice(s,1),this._emitQueueUpdate();else{const n=this._followUpMessages.indexOf(t);n!==-1&&(this._followUpMessages.splice(n,1),this._emitQueueUpdate())}}}if(await this._emitExtensionEvent(e),this._emit(e.type==="agent_end"?{...e,willRetry:this._willRetryAfterAgentEnd(e)}:e),e.type==="message_end"&&(e.message.role==="custom"?this.sessionManager.appendCustomMessageEntry(e.message.customType,e.message.content,e.message.display,e.message.details):(e.message.role==="user"||e.message.role==="assistant"||e.message.role==="toolResult")&&this.sessionManager.appendMessage(e.message),e.message.role==="assistant")){this._lastAssistantMessage=e.message;const t=e.message;t.stopReason!=="error"&&(this._overflowRecoveryAttempted=!1),t.stopReason!=="error"&&this._retryAttempt>0&&(this._emit({type:"auto_retry_end",success:!0,attempt:this._retryAttempt}),this._retryAttempt=0)}},"_handleAgentEvent");_willRetryAfterAgentEnd(e){const t=this.settingsManager.getRetrySettings();if(!t.enabled||this._retryAttempt>=t.maxRetries)return!1;for(let s=e.messages.length-1;s>=0;s--){const n=e.messages[s];if(n.role==="assistant")return this._isRetryableError(n)}return!1}_getUserMessageText(e){if(e.role!=="user")return"";const t=e.content;return typeof t=="string"?t:t.filter(n=>n.type==="text").map(n=>n.text).join("")}_findLastAssistantMessage(){const e=this.agent.state.messages;for(let t=e.length-1;t>=0;t--){const s=e[t];if(s.role==="assistant")return s}}_replaceMessageInPlace(e,t){if(e===t)return;const s=e;for(const n of Object.keys(s))delete s[n];Object.assign(s,t)}async _emitExtensionEvent(e){if(e.type==="agent_start")this._turnIndex=0,await this._extensionRunner.emit({type:"agent_start"});else if(e.type==="agent_end")await this._extensionRunner.emit({type:"agent_end",messages:e.messages});else if(e.type==="turn_start"){const t={type:"turn_start",turnIndex:this._turnIndex,timestamp:Date.now()};await this._extensionRunner.emit(t)}else if(e.type==="turn_end"){const t={type:"turn_end",turnIndex:this._turnIndex,message:e.message,toolResults:e.toolResults};await this._extensionRunner.emit(t),this._turnIndex++}else if(e.type==="message_start"){const t={type:"message_start",message:e.message};await this._extensionRunner.emit(t)}else if(e.type==="message_update"){const t={type:"message_update",message:e.message,assistantMessageEvent:e.assistantMessageEvent};await this._extensionRunner.emit(t)}else if(e.type==="message_end"){const t={type:"message_end",message:e.message},s=await this._extensionRunner.emitMessageEnd(t);s&&this._replaceMessageInPlace(e.message,s)}else if(e.type==="tool_execution_start"){const t={type:"tool_execution_start",toolCallId:e.toolCallId,toolName:e.toolName,args:e.args};await this._extensionRunner.emit(t)}else if(e.type==="tool_execution_update"){const t={type:"tool_execution_update",toolCallId:e.toolCallId,toolName:e.toolName,args:e.args,partialResult:e.partialResult};await this._extensionRunner.emit(t)}else if(e.type==="tool_execution_end"){const t={type:"tool_execution_end",toolCallId:e.toolCallId,toolName:e.toolName,result:e.result,isError:e.isError};await this._extensionRunner.emit(t)}}subscribe(e){return this._eventListeners.push(e),()=>{const t=this._eventListeners.indexOf(e);t!==-1&&this._eventListeners.splice(t,1)}}_disconnectFromAgent(){this._unsubscribeAgent&&(this._unsubscribeAgent(),this._unsubscribeAgent=void 0)}_reconnectToAgent(){this._unsubscribeAgent||(this._unsubscribeAgent=this.agent.subscribe(this._handleAgentEvent))}dispose(){try{this.abortRetry(),this.abortCompaction(),this.abortBranchSummary(),this.abortBash(),this.agent.abort()}catch{}this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload()."),this._disconnectFromAgent(),this._eventListeners=[],ne(this.sessionId)}get state(){return this.agent.state}get model(){return this.agent.state.model}get thinkingLevel(){return this.agent.state.thinkingLevel}get isStreaming(){return this.agent.state.isStreaming}get systemPrompt(){return this.agent.state.systemPrompt}get retryAttempt(){return this._retryAttempt}getActiveToolNames(){return this.agent.state.tools.map(e=>e.name)}getAllTools(){return Array.from(this._toolDefinitions.values()).map(({definition:e,sourceInfo:t})=>({name:e.name,description:e.description,parameters:e.parameters,promptGuidelines:e.promptGuidelines,sourceInfo:t}))}getToolDefinition(e){return this._toolDefinitions.get(e)?.definition}setActiveToolsByName(e){const t=[],s=[];for(const n of e){const i=this._toolRegistry.get(n);i&&(t.push(i),s.push(n))}this.agent.state.tools=t,this._baseSystemPrompt=this._rebuildSystemPrompt(s),this.agent.state.systemPrompt=this._baseSystemPrompt}get isCompacting(){return this._autoCompactionAbortController!==void 0||this._compactionAbortController!==void 0||this._branchSummaryAbortController!==void 0}get isWorking(){return this.isStreaming||this.isCompacting||this._turnActive}get messages(){return this.agent.state.messages}get steeringMode(){return this.agent.steeringMode}get followUpMode(){return this.agent.followUpMode}get sessionFile(){return this.sessionManager.getSessionFile()}get sessionId(){return this.sessionManager.getSessionId()}get sessionName(){return this.sessionManager.getSessionName()}get scopedModels(){return this._scopedModels}setScopedModels(e){this._scopedModels=e}get promptTemplates(){return this._resourceLoader.getPrompts().prompts}_normalizePromptSnippet(e){if(!e)return;const t=e.replace(/[\r\n]+/g," ").replace(/\s+/g," ").trim();return t.length>0?t:void 0}_normalizePromptGuidelines(e){if(!e||e.length===0)return[];const t=new Set;for(const s of e){const n=s.trim();n.length>0&&t.add(n)}return Array.from(t)}_rebuildSystemPrompt(e){const t=e.filter(d=>this._toolRegistry.has(d)),s={},n=[];for(const d of t){const u=this._toolPromptSnippets.get(d);u&&(s[d]=u);const g=this._toolPromptGuidelines.get(d);g&&n.push(...g)}const i=this._resourceLoader.getSystemPrompt(),r=[...this._resourceLoader.getAppendSystemPrompt(),...this._pinnedSkills.values()],l=r.length>0?r.join(`
|
|
1
|
+
var Y=Object.defineProperty;var w=(L,e)=>Y(L,"name",{value:e,configurable:!0});import{existsSync as R,mkdirSync as K,readFileSync as E,unlinkSync as Z,writeFileSync as P}from"node:fs";import{homedir as X}from"node:os";import{basename as ee,dirname as B,join as C,relative as te,resolve as F}from"node:path";import{clampThinkingLevel as se,cleanupSessionResources as ne,getSupportedThinkingLevels as ie,isContextOverflow as $,modelsAreEqual as T,resetApiProviders as re,streamSimple as H}from"@openadapter/koda-ai";import{theme as oe}from"../modes/interactive/theme/theme.js";import{stripFrontmatter as z}from"../utils/frontmatter.js";import{sendKodaTelemetry as ae}from"../utils/koda-telemetry.js";import{resolvePath as le}from"../utils/paths.js";import{sleep as ce}from"../utils/sleep.js";import{formatNoApiKeyFoundMessage as O,formatNoModelSelectedMessage as j}from"./auth-guidance.js";import{executeBashWithOperations as he}from"./bash-executor.js";import{calculateContextTokens as G,collectEntriesForBranchSummary as de,compact as q,estimateContextTokens as W,generateBranchSummary as ue,prepareCompaction as V,shouldCompact as me}from"./compaction/index.js";import{DEFAULT_THINKING_LEVEL as ge}from"./defaults.js";import{execCommand as pe}from"./exec.js";import{exportSessionToHtml as fe}from"./export-html/index.js";import{createToolHtmlRenderer as _e}from"./export-html/tool-renderer.js";import{ExtensionRunner as ye,wrapRegisteredTools as J}from"./extensions/index.js";import{emitSessionShutdownEvent as we}from"./extensions/runner.js";import{expandPromptTemplate as I}from"./prompt-templates.js";import{CURRENT_SESSION_VERSION as xe,getLatestCompactionEntry as Q}from"./session-manager.js";import{createSyntheticSourceInfo as N}from"./source-info.js";import{buildSystemPrompt as be}from"./system-prompt.js";import{createLocalBashOperations as ve}from"./tools/bash.js";import{createAllToolDefinitions as Ae}from"./tools/index.js";import{createToolDefinitionFromAgentTool as Me}from"./tools/tool-definition-wrapper.js";function Ze(L){const e=L.match(/^<skill name="([^"]+)" location="([^"]+)">\n([\s\S]*?)\n<\/skill>(?:\n\n([\s\S]+))?$/);return e?{name:e[1],location:e[2],content:e[3],userMessage:e[4]?.trim()||void 0}:null}w(Ze,"parseSkillBlock");const ke=["off","minimal","low","medium","high"];class Xe{static{w(this,"AgentSession")}agent;sessionManager;settingsManager;_scopedModels;_unsubscribeAgent;_eventListeners=[];_steeringMessages=[];_followUpMessages=[];_pendingNextTurnMessages=[];_compactionAbortController=void 0;_autoCompactionAbortController=void 0;_overflowRecoveryAttempted=!1;_branchSummaryAbortController=void 0;_retryAbortController=void 0;_retryAttempt=0;_overseerAbortController=void 0;_recapAbortController=void 0;_turnActive=!1;_failoverTried=new Set;get _zenitsu(){return process.env.KODA_ZENITSU==="1"}_zenitsuPreferred;_zenitsuCooldown=new Map;_zenitsuHealthCache;_currentGoal;_tierCache;_overseerLastError="";_verifyLedger={attempts:0,lastSig:"",escalated:!1,gaveUp:!1};_escalationsDone=0;_escalateTried=new Set;_sessionEscalations=0;_stickyModel;_homeModel;_sessionGoal;_pinnedSkills=new Map;_codeChangedThisTask=!1;_verifyCmd;_taskStartTs=0;_taskHadActivity=!1;_suggestShown=new Set;_suggestedThisTask=!1;_harnessTrace=[];_gatewayCircuit={failures:0,openUntil:0};_bashAbortController=void 0;_pendingBashMessages=[];_extensionRunner;_turnIndex=0;_resourceLoader;_customTools;_baseToolDefinitions=new Map;_cwd;_protectedSnap=new Map;_extensionRunnerRef;_initialActiveToolNames;_allowedToolNames;_excludedToolNames;_baseToolsOverride;_sessionStartEvent;_extensionUIContext;_extensionMode="print";_extensionCommandContextActions;_extensionAbortHandler;_extensionShutdownHandler;_extensionErrorListener;_extensionErrorUnsubscriber;_modelRegistry;_toolRegistry=new Map;_toolDefinitions=new Map;_toolPromptSnippets=new Map;_toolPromptGuidelines=new Map;_baseSystemPrompt="";_baseSystemPromptOptions;constructor(e){this.agent=e.agent,this.sessionManager=e.sessionManager,this.settingsManager=e.settingsManager,this._scopedModels=e.scopedModels??[],this._resourceLoader=e.resourceLoader,this._customTools=e.customTools??[],this._cwd=e.cwd,this._modelRegistry=e.modelRegistry,this._extensionRunnerRef=e.extensionRunnerRef,this._initialActiveToolNames=e.initialActiveToolNames,this._allowedToolNames=e.allowedToolNames?new Set(e.allowedToolNames):void 0,this._excludedToolNames=e.excludedToolNames?new Set(e.excludedToolNames):void 0,this._baseToolsOverride=e.baseToolsOverride,this._sessionStartEvent=e.sessionStartEvent??{type:"session_start",reason:"startup"},this._unsubscribeAgent=this.agent.subscribe(this._handleAgentEvent),this._installAgentToolHooks(),this._buildRuntime({activeToolNames:this._initialActiveToolNames,includeAllExtensionTools:!0})}get modelRegistry(){return this._modelRegistry}async _getRequiredRequestAuth(e){const t=await this._modelRegistry.getApiKeyAndHeaders(e);if(!t.ok)throw t.error.startsWith("No API key found")?new Error(O(e.provider)):new Error(t.error);if(t.apiKey)return{apiKey:t.apiKey,headers:t.headers};throw this._modelRegistry.isUsingOAuth(e)?new Error(`Authentication failed for "${e.provider}". Credentials may have expired or network is unavailable. Run '/login ${e.provider}' to re-authenticate.`):new Error(O(e.provider))}async _getCompactionRequestAuth(e){if(this.agent.streamFn===H)return this._getRequiredRequestAuth(e);const t=await this._modelRegistry.getApiKeyAndHeaders(e);return t.ok?{apiKey:t.apiKey,headers:t.headers}:{}}_installAgentToolHooks(){const e=process.env.KODA_UNSAFE==="1",t=process.cwd(),s=[/(^|\/)test_[^/]*\.[A-Za-z0-9]+$/,/(^|\/)[^/]*_test\.[A-Za-z0-9]+$/,/\.(test|spec)\.[A-Za-z0-9]+$/,/(^|\/)(tests?|__tests__|spec)\//,/(^|\/)\.git(\/|$)/,/(^|\/)\.env(\.[^/]*)?$/,/(package-lock\.json|yarn\.lock|pnpm-lock\.yaml|poetry\.lock|Cargo\.lock)$/],n=(process.env.KODA_PROTECT||"").split(",").map(g=>g.trim()).filter(Boolean),i=w(g=>{const p=g.replace(/\\/g,"/");return s.some(y=>y.test(p))||n.some(y=>p.includes(y))},"isProtected"),o=w(g=>{for(const p of g.matchAll(/(?:>>?|\btee\s+(?:-a\s+)?)\s*([^\s;|&>]+)/g))if(i(p[1]))return`output redirect onto protected file '${p[1]}'`;return/\brm\s+(?:-\w+\s+)*[^\n;|&]*(?:test_|tests?\/|__tests__|\.git|\.env)/.test(g)?"rm targeting a protected path":/\bgit\s+(?:reset\s+--hard|clean\b|checkout\s+(?:--\s+)?\.)/.test(g)?"destructive git command":null},"dangerousBash"),r=C(process.env.KODA_CODING_AGENT_DIR||C(X(),".koda","agent"),"checkpoints",String(this.sessionId||"session")),l=new Set,a=w(g=>{if(!l.has(g)){l.add(g);try{K(r,{recursive:!0});const p=C(r,`${g.replace(/[^A-Za-z0-9]/g,"_")}.json`),y=R(g);P(p,JSON.stringify({path:g,existed:y,content:y?E(g,"utf-8"):null}))}catch{}}},"backup");this.agent.beforeToolCall=async({toolCall:g,args:p})=>{if((g.name==="write"||g.name==="edit")&&(this._codeChangedThisTask=!0),this._taskHadActivity=!0,g.name==="bash"){const v=String(p?.command??"");/\b(npm|pnpm|yarn|bun)\s+(run\s+)?(dev|start|serve|preview|watch)\b|\bvite\b|\bnext\s+(dev|start)\b|\bnuxt\s+dev\b|\bnodemon\b|http[-.]server\b|\bserve\b|flask\s+run|rails\s+s(erver)?\b|manage\.py\s+runserver|\b(uvicorn|gunicorn)\b/i.test(v)&&this._suggest("bg","That looks like a long-running server. Use run_background (or /bg) so it keeps running without blocking Koda.")}if(process.env.KODA_MODE==="plan"){const v=p,f=g.name;if(f==="write"||f==="edit")return{block:!0,reason:"Koda is in PLAN MODE \u2014 file changes are disabled. Investigate and present a step-by-step plan instead; the user will run /build to execute it."};if(f==="bash"&&this._isMutatingBash(String(v?.command??"")))return{block:!0,reason:"Koda is in PLAN MODE \u2014 commands that change state are disabled. Use read-only commands to investigate, then present a plan; the user will run /build to execute it."}}if(!e){const v=p,f=g.name;if(f==="write"||f==="edit"){const _=String(v?.path??v?.file_path??"");if(_){const A=F(t,_);if(te(t,A).startsWith(".."))return{block:!0,reason:`Refused: '${_}' is outside the workspace. Only modify files under the project directory.`};if(i(_))return{block:!0,reason:`Refused: '${_}' is a protected file (tests / .git / secrets / lockfiles). Do not modify it \u2014 change the source code instead.`};a(A)}}else if(f==="bash"){const _=o(String(v?.command??""));if(_)return{block:!0,reason:`Refused: ${_}. Use the edit/write tools on source files instead of shell redirects, and never touch tests/.git/secrets.`}}const M=[];if(f==="write"||f==="edit"){const _=String(v?.path??v?.file_path??"");_&&M.push(_)}else if(f==="bash")for(const _ of String(v?.command??"").matchAll(/[\w./-]*[\w]\.[A-Za-z0-9]+/g))M.push(_[0]);for(const _ of M){if(!i(_))continue;const A=F(t,_);if(!this._protectedSnap.has(A))try{this._protectedSnap.set(A,R(A)?E(A,"utf-8"):null)}catch{}}}const y=this._extensionRunner;if(y.hasHandlers("tool_call"))try{return await y.emitToolCall({type:"tool_call",toolName:g.name,toolCallId:g.id,input:p})}catch(v){throw v instanceof Error?v:new Error(`Extension failed, blocking execution: ${String(v)}`)}};const d=process.env.KODA_CASCADE_MODEL,m=Number(process.env.KODA_CASCADE_ERRORS)||3,h=Number(process.env.KODA_CASCADE_STEPS)||16;let u=0,x=0,c=!1,b;this.agent.afterToolCall=async({toolCall:g,args:p,result:y,isError:v})=>{for(const[_,A]of this._protectedSnap){let k=null;try{k=R(_)?E(_,"utf-8"):null}catch{continue}if(k!==A)try{A===null?R(_)&&Z(_):P(_,A,"utf-8"),console.error(`[koda safety] reverted unauthorized change to protected file ${_}`)}catch{}}if(d&&!c&&(x++,v?u++:u=0,u>=m||x>=h)){const _=this.agent.state.model,[A,k]=d.includes("/")?[d.slice(0,d.indexOf("/")),d.slice(d.indexOf("/")+1)]:[_.provider,d],S=this._modelRegistry.find(A,k);S&&!T(S,_)&&(b=S,this.agent.state.model=S,this.sessionManager.appendModelChange(S.provider,S.id),this._emitModelSelect(S,_,"cycle"),c=!0,console.error(`[koda cascade] escalated ${_.provider}/${_.id} \u2192 ${S.provider}/${S.id} (errors=${u}, toolCalls=${x})`))}const f=this._extensionRunner;if(!f.hasHandlers("tool_result"))return;const M=await f.emitToolResult({type:"tool_result",toolName:g.name,toolCallId:g.id,input:p,content:y.content,details:y.details,isError:v});if(M)return{content:M.content,details:M.details,isError:M.isError??v}},this.agent.prepareNextTurn=async()=>{if(this._zenitsu){const g=await this._zenitsuPickModel();if(g&&!T(g,this.agent.state.model))return{model:g}}return c&&b?{model:b}:void 0}}_emit(e){for(const t of this._eventListeners)t(e)}_emitQueueUpdate(){this._emit({type:"queue_update",steering:[...this._steeringMessages],followUp:[...this._followUpMessages]})}_lastAssistantMessage=void 0;_handleAgentEvent=w(async e=>{if(e.type==="message_start"&&e.message.role==="user"){this._overflowRecoveryAttempted=!1;const t=this._getUserMessageText(e.message);if(t){const s=this._steeringMessages.indexOf(t);if(s!==-1)this._steeringMessages.splice(s,1),this._emitQueueUpdate();else{const n=this._followUpMessages.indexOf(t);n!==-1&&(this._followUpMessages.splice(n,1),this._emitQueueUpdate())}}}if(await this._emitExtensionEvent(e),this._emit(e.type==="agent_end"?{...e,willRetry:this._willRetryAfterAgentEnd(e)}:e),e.type==="message_end"&&(e.message.role==="custom"?this.sessionManager.appendCustomMessageEntry(e.message.customType,e.message.content,e.message.display,e.message.details):(e.message.role==="user"||e.message.role==="assistant"||e.message.role==="toolResult")&&this.sessionManager.appendMessage(e.message),e.message.role==="assistant")){this._lastAssistantMessage=e.message;const t=e.message;t.stopReason!=="error"&&(this._overflowRecoveryAttempted=!1),t.stopReason!=="error"&&this._retryAttempt>0&&(this._emit({type:"auto_retry_end",success:!0,attempt:this._retryAttempt}),this._retryAttempt=0)}},"_handleAgentEvent");_willRetryAfterAgentEnd(e){const t=this.settingsManager.getRetrySettings();if(!t.enabled||this._retryAttempt>=t.maxRetries)return!1;for(let s=e.messages.length-1;s>=0;s--){const n=e.messages[s];if(n.role==="assistant")return this._isRetryableError(n)}return!1}_getUserMessageText(e){if(e.role!=="user")return"";const t=e.content;return typeof t=="string"?t:t.filter(n=>n.type==="text").map(n=>n.text).join("")}_findLastAssistantMessage(){const e=this.agent.state.messages;for(let t=e.length-1;t>=0;t--){const s=e[t];if(s.role==="assistant")return s}}_replaceMessageInPlace(e,t){if(e===t)return;const s=e;for(const n of Object.keys(s))delete s[n];Object.assign(s,t)}async _emitExtensionEvent(e){if(e.type==="agent_start")this._turnIndex=0,await this._extensionRunner.emit({type:"agent_start"});else if(e.type==="agent_end")await this._extensionRunner.emit({type:"agent_end",messages:e.messages});else if(e.type==="turn_start"){const t={type:"turn_start",turnIndex:this._turnIndex,timestamp:Date.now()};await this._extensionRunner.emit(t)}else if(e.type==="turn_end"){const t={type:"turn_end",turnIndex:this._turnIndex,message:e.message,toolResults:e.toolResults};await this._extensionRunner.emit(t),this._turnIndex++}else if(e.type==="message_start"){const t={type:"message_start",message:e.message};await this._extensionRunner.emit(t)}else if(e.type==="message_update"){const t={type:"message_update",message:e.message,assistantMessageEvent:e.assistantMessageEvent};await this._extensionRunner.emit(t)}else if(e.type==="message_end"){const t={type:"message_end",message:e.message},s=await this._extensionRunner.emitMessageEnd(t);s&&this._replaceMessageInPlace(e.message,s)}else if(e.type==="tool_execution_start"){const t={type:"tool_execution_start",toolCallId:e.toolCallId,toolName:e.toolName,args:e.args};await this._extensionRunner.emit(t)}else if(e.type==="tool_execution_update"){const t={type:"tool_execution_update",toolCallId:e.toolCallId,toolName:e.toolName,args:e.args,partialResult:e.partialResult};await this._extensionRunner.emit(t)}else if(e.type==="tool_execution_end"){const t={type:"tool_execution_end",toolCallId:e.toolCallId,toolName:e.toolName,result:e.result,isError:e.isError};await this._extensionRunner.emit(t)}}subscribe(e){return this._eventListeners.push(e),()=>{const t=this._eventListeners.indexOf(e);t!==-1&&this._eventListeners.splice(t,1)}}_disconnectFromAgent(){this._unsubscribeAgent&&(this._unsubscribeAgent(),this._unsubscribeAgent=void 0)}_reconnectToAgent(){this._unsubscribeAgent||(this._unsubscribeAgent=this.agent.subscribe(this._handleAgentEvent))}dispose(){try{this.abortRetry(),this.abortCompaction(),this.abortBranchSummary(),this.abortBash(),this.agent.abort()}catch{}this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload()."),this._disconnectFromAgent(),this._eventListeners=[],ne(this.sessionId)}get state(){return this.agent.state}get model(){return this.agent.state.model}get thinkingLevel(){return this.agent.state.thinkingLevel}get isStreaming(){return this.agent.state.isStreaming}get systemPrompt(){return this.agent.state.systemPrompt}get retryAttempt(){return this._retryAttempt}getActiveToolNames(){return this.agent.state.tools.map(e=>e.name)}getAllTools(){return Array.from(this._toolDefinitions.values()).map(({definition:e,sourceInfo:t})=>({name:e.name,description:e.description,parameters:e.parameters,promptGuidelines:e.promptGuidelines,sourceInfo:t}))}getToolDefinition(e){return this._toolDefinitions.get(e)?.definition}setActiveToolsByName(e){const t=[],s=[];for(const n of e){const i=this._toolRegistry.get(n);i&&(t.push(i),s.push(n))}this.agent.state.tools=t,this._baseSystemPrompt=this._rebuildSystemPrompt(s),this.agent.state.systemPrompt=this._baseSystemPrompt}get isCompacting(){return this._autoCompactionAbortController!==void 0||this._compactionAbortController!==void 0||this._branchSummaryAbortController!==void 0}get isWorking(){return this.isStreaming||this.isCompacting||this._turnActive}get messages(){return this.agent.state.messages}get steeringMode(){return this.agent.steeringMode}get followUpMode(){return this.agent.followUpMode}get sessionFile(){return this.sessionManager.getSessionFile()}get sessionId(){return this.sessionManager.getSessionId()}get sessionName(){return this.sessionManager.getSessionName()}get scopedModels(){return this._scopedModels}setScopedModels(e){this._scopedModels=e}get promptTemplates(){return this._resourceLoader.getPrompts().prompts}_normalizePromptSnippet(e){if(!e)return;const t=e.replace(/[\r\n]+/g," ").replace(/\s+/g," ").trim();return t.length>0?t:void 0}_normalizePromptGuidelines(e){if(!e||e.length===0)return[];const t=new Set;for(const s of e){const n=s.trim();n.length>0&&t.add(n)}return Array.from(t)}_rebuildSystemPrompt(e){const t=e.filter(m=>this._toolRegistry.has(m)),s={},n=[];for(const m of t){const h=this._toolPromptSnippets.get(m);h&&(s[m]=h);const u=this._toolPromptGuidelines.get(m);u&&n.push(...u)}const i=this._resourceLoader.getSystemPrompt(),r=[...this._resourceLoader.getAppendSystemPrompt(),...this._pinnedSkills.values()],l=r.length>0?r.join(`
|
|
2
2
|
|
|
3
|
-
`):void 0,a=this._resourceLoader.getSkills().skills,
|
|
3
|
+
`):void 0,a=this._resourceLoader.getSkills().skills,d=this._resourceLoader.getAgentsFiles().agentsFiles;return this._baseSystemPromptOptions={cwd:this._cwd,skills:a,contextFiles:d,customPrompt:i,appendSystemPrompt:l,selectedTools:t,toolSnippets:s,promptGuidelines:n},be(this._baseSystemPromptOptions)}async _continueWithHeal(){try{return await this.agent.continue(),!0}catch(e){const t=e?.message||String(e);if(this._trace("recover",`continue failed: ${t.slice(0,90)}`),/cannot continue from message role/i.test(t)){this.agent.followUp({role:"user",content:[{type:"text",text:"Continue from where you left off and finish the task."}],timestamp:Date.now()});try{return await this.agent.continue(),!0}catch{}}return this._emit({type:"model_switch_notice",message:`Koda hit a snag and stopped cleanly (${t.slice(0,60)}). Your work is saved \u2014 send another message to continue.`}),ae({type:"error",error:t}),!1}}async _runAgentPrompt(e){this._turnActive=!0,this._failoverTried.clear(),this._verifyLedger={attempts:0,lastSig:"",escalated:!1,gaveUp:!1},this._escalationsDone=0,this._escalateTried.clear(),this._codeChangedThisTask=!1,this._taskStartTs=Date.now(),this._taskHadActivity=!1,this._suggestedThisTask=!1;const t=this._goalFromMessages(e);if(t&&(this._currentGoal=t),!this._homeModel)this._homeModel=this.agent.state.model;else{const s=this._stickyModel??this._homeModel;s.id!==this.agent.state.model.id&&this._modelRegistry.hasConfiguredAuth(s)&&(this.agent.state.model=s,this._zenitsuPreferred=s,this._stickyModel||(this._trace("revert",`\u2192 ${s.id} (new task)`),this._emit({type:"model_switch_notice",message:`\u2190 Back on your pick \u2014 ${s.id}`})))}try{await this.agent.prompt(e);let s=!0;for(;s&&await this._handlePostAgentRun();)s=await this._continueWithHeal();if(this._taskHadActivity&&process.env.KODA_RECAP!=="0"){this._emit({type:"recap_start"}),this._recapAbortController=new AbortController;const n=this._recapAbortController.signal;try{const i=await this._generateRecap(n);n.aborted||this._emit({type:"task_recap",elapsedMs:Date.now()-this._taskStartTs,recap:i||""})}finally{this._recapAbortController=void 0,this._emit({type:"recap_end"})}}for(this._taskHadActivity&&!this._sessionGoal&&Date.now()-this._taskStartTs>18e4&&this._suggest("goal","Working on something big? Set a goal with /goal \u2014 Koda tracks the milestones and won't stop until they're done.");s&&this.agent.hasQueuedMessages();)for(s=await this._continueWithHeal();s&&await this._handlePostAgentRun();)s=await this._continueWithHeal()}finally{this._turnActive=!1,this._flushPendingBashMessages()}}async _generateRecap(e){const t=this.agent.state.model,s=process.env.KODA_RECAP_MODEL?[process.env.KODA_RECAP_MODEL]:["free/qwen3-coder","free/qwen3-next-80b-a3b-instruct","GLM-4.7-Flash","GLM-4.7"];for(const n of s){if(e?.aborted)return null;const i=this._modelRegistry.find(t.provider,n);if(!i)continue;const o=await this._recapOnce(i,e);if(o)return o}return null}async _recapOnce(e,t){try{if(!e.baseUrl)return null;const s=await this._modelRegistry.getApiKeyAndHeaders(e);if(!s?.apiKey)return null;const n=new AbortController,i=setTimeout(()=>n.abort(),2e4),o=w(()=>n.abort(),"onExternalAbort");t&&(t.aborted?n.abort():t.addEventListener("abort",o));try{const r=await fetch(`${e.baseUrl.replace(/\/$/,"")}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${s.apiKey}`,"Content-Type":"application/json",...s.headers||{}},body:JSON.stringify({model:e.id,temperature:.2,max_tokens:150,messages:[{role:"system",content:'Output ONLY a 1-2 sentence, past-tense recap of what was done this turn (and the next step if any). No analysis, no numbered lists, no markdown, no preamble \u2014 just the recap. Example: "Added a /health endpoint to server.ts and confirmed the build passes. Next: add a test for it."'},{role:"user",content:`REQUEST:
|
|
4
4
|
${this._currentGoal||"(none)"}
|
|
5
5
|
|
|
6
6
|
WORK LOG:
|
|
7
|
-
${this._overseerContext()}`}]}),signal:n.signal});return r.ok&&((await r.json())?.choices?.[0]?.message?.content??"").replace(/<think>[\s\S]*?<\/think>/gi,"").trim().slice(0,400)||null}finally{clearTimeout(i),t&&t.removeEventListener("abort",o)}}catch{return null}}async _handlePostAgentRun(){const e=this._lastAssistantMessage;return this._lastAssistantMessage=void 0,e?(e.stopReason!=="error"&&this._maybeReturnHome(),this._isDegenerateOutput(e)&&await this._escalate("output broke down \u2014 bringing in a stronger model",{stripLast:!0})||this._zenitsu&&await this._zenitsuHandle429(e)||(!this._zenitsu&&this._isRateLimited(e)&&this._suggest("zenitsu","Getting rate limited \u2014 zenitsu mode reroutes to a faster model instantly instead of waiting. Turn it on with /zenitsu."),this._isRetryableError(e)&&await this._prepareRetry(e))||e.stopReason==="error"&&await this._failoverModel(e)||(e.stopReason==="error"&&this._retryAttempt>0&&(this._emit({type:"auto_retry_end",success:!1,attempt:this._retryAttempt,finalError:e.errorMessage}),this._retryAttempt=0),await this._checkCompaction(e))||await this._overseerCheck(e)?!0:this.agent.hasQueuedMessages()):!1}async prompt(e,t){const s=t?.expandPromptTemplates??!0,n=t?.preflightResult;let i;try{if(s&&e.startsWith("/")&&await this._tryExecuteExtensionCommand(e)){n?.(!0);return}let o=e,r=t?.images;if(this._extensionRunner.hasHandlers("input")){const
|
|
7
|
+
${this._overseerContext()}`}]}),signal:n.signal});return r.ok&&((await r.json())?.choices?.[0]?.message?.content??"").replace(/<think>[\s\S]*?<\/think>/gi,"").trim().slice(0,400)||null}finally{clearTimeout(i),t&&t.removeEventListener("abort",o)}}catch{return null}}async _handlePostAgentRun(){const e=this._lastAssistantMessage;return this._lastAssistantMessage=void 0,e?(e.stopReason!=="error"&&this._maybeReturnHome(),this._isDegenerateOutput(e)&&await this._escalate("output broke down \u2014 bringing in a stronger model",{stripLast:!0})||this._zenitsu&&await this._zenitsuHandle429(e)||(!this._zenitsu&&this._isRateLimited(e)&&this._suggest("zenitsu","Getting rate limited \u2014 zenitsu mode reroutes to a faster model instantly instead of waiting. Turn it on with /zenitsu."),this._isRetryableError(e)&&await this._prepareRetry(e))||e.stopReason==="error"&&await this._failoverModel(e)||(e.stopReason==="error"&&this._retryAttempt>0&&(this._emit({type:"auto_retry_end",success:!1,attempt:this._retryAttempt,finalError:e.errorMessage}),this._retryAttempt=0),await this._checkCompaction(e))||await this._overseerCheck(e)?!0:this.agent.hasQueuedMessages()):!1}async prompt(e,t){const s=t?.expandPromptTemplates??!0,n=t?.preflightResult;let i;try{if(s&&e.startsWith("/")&&await this._tryExecuteExtensionCommand(e)){n?.(!0);return}let o=e,r=t?.images;if(this._extensionRunner.hasHandlers("input")){const h=await this._extensionRunner.emitInput(o,r,t?.source??"interactive",this.isStreaming||this._turnActive?t?.streamingBehavior:void 0);if(h.action==="handled"){n?.(!0);return}h.action==="transform"&&(o=h.text,r=h.images??r)}let l=o;if(s&&(l=this._expandSkillCommand(l),l=I(l,[...this.promptTemplates])),this.isStreaming||this._turnActive){if(!t?.streamingBehavior)throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");t.streamingBehavior==="followUp"?await this._queueFollowUp(l,r):await this._queueSteer(l,r),n?.(!0);return}if(this._flushPendingBashMessages(),!this.model)throw new Error(j());if(!this._modelRegistry.hasConfiguredAuth(this.model))throw this._modelRegistry.isUsingOAuth(this.model)?new Error(`Authentication failed for "${this.model.provider}". Credentials may have expired or network is unavailable. Run '/login ${this.model.provider}' to re-authenticate.`):new Error(O(this.model.provider));const a=this._findLastAssistantMessage();if(a&&await this._checkCompaction(a,!1))try{for(await this.agent.continue();await this._handlePostAgentRun();)await this.agent.continue()}finally{this._flushPendingBashMessages()}i=[];const d=[{type:"text",text:l}];r&&d.push(...r),i.push({role:"user",content:d,timestamp:Date.now()});for(const h of this._pendingNextTurnMessages)i.push(h);this._pendingNextTurnMessages=[];const m=await this._extensionRunner.emitBeforeAgentStart(l,r,this._baseSystemPrompt,this._baseSystemPromptOptions);if(m?.messages)for(const h of m.messages)i.push({role:"custom",customType:h.customType,content:h.content,display:h.display,details:h.details,timestamp:Date.now()});m?.systemPrompt?this.agent.state.systemPrompt=m.systemPrompt:this.agent.state.systemPrompt=this._baseSystemPrompt}catch(o){throw n?.(!1),o}i&&(n?.(!0),await this._runAgentPrompt(i))}async _tryExecuteExtensionCommand(e){const t=e.indexOf(" "),s=t===-1?e.slice(1):e.slice(1,t),n=t===-1?"":e.slice(t+1),i=this._extensionRunner.getCommand(s);if(!i)return!1;const o=this._extensionRunner.createCommandContext();try{return await i.handler(n,o),!0}catch(r){return this._extensionRunner.emitError({extensionPath:`command:${s}`,event:"command",error:r instanceof Error?r.message:String(r)}),!0}}_expandSkillCommand(e){if(!e.startsWith("/skill:"))return e;const t=e.indexOf(" "),s=t===-1?e.slice(7):e.slice(7,t),n=t===-1?"":e.slice(t+1).trim(),i=this.resourceLoader.getSkills().skills.find(o=>o.name===s);if(!i)return e;try{const o=E(i.filePath,"utf-8"),r=z(o).trim(),l=`<skill name="${i.name}" location="${i.filePath}">
|
|
8
8
|
References are relative to ${i.baseDir}.
|
|
9
9
|
|
|
10
10
|
${r}
|
|
11
11
|
</skill>`;return n?`${l}
|
|
12
12
|
|
|
13
13
|
${n}`:l}catch(o){return this._extensionRunner.emitError({extensionPath:i.filePath,event:"skill_expansion",error:o instanceof Error?o.message:String(o)}),e}}async steer(e,t){e.startsWith("/")&&this._throwIfExtensionCommand(e);let s=this._expandSkillCommand(e);s=I(s,[...this.promptTemplates]),await this._queueSteer(s,t)}async followUp(e,t){e.startsWith("/")&&this._throwIfExtensionCommand(e);let s=this._expandSkillCommand(e);s=I(s,[...this.promptTemplates]),await this._queueFollowUp(s,t)}async _queueSteer(e,t){this._steeringMessages.push(e),this._emitQueueUpdate();const s=[{type:"text",text:e}];t&&s.push(...t),this.agent.steer({role:"user",content:s,timestamp:Date.now()})}async _queueFollowUp(e,t){this._followUpMessages.push(e),this._emitQueueUpdate();const s=[{type:"text",text:e}];t&&s.push(...t),this.agent.followUp({role:"user",content:s,timestamp:Date.now()})}_throwIfExtensionCommand(e){const t=e.indexOf(" "),s=t===-1?e.slice(1):e.slice(1,t);if(this._extensionRunner.getCommand(s))throw new Error(`Extension command "/${s}" cannot be queued. Use prompt() or execute the command when not streaming.`)}async sendCustomMessage(e,t){const s={role:"custom",customType:e.customType,content:e.content,display:e.display,details:e.details,timestamp:Date.now()};t?.deliverAs==="nextTurn"?this._pendingNextTurnMessages.push(s):this.isStreaming?t?.deliverAs==="followUp"?this.agent.followUp(s):this.agent.steer(s):t?.triggerTurn?await this._runAgentPrompt(s):(this.agent.state.messages.push(s),this.sessionManager.appendCustomMessageEntry(e.customType,e.content,e.display,e.details),this._emit({type:"message_start",message:s}),this._emit({type:"message_end",message:s}))}async sendUserMessage(e,t){let s,n;if(typeof e=="string")s=e;else{const i=[];n=[];for(const o of e)o.type==="text"?i.push(o.text):n.push(o);s=i.join(`
|
|
14
|
-
`),n.length===0&&(n=void 0)}await this.prompt(s,{expandPromptTemplates:!1,streamingBehavior:t?.deliverAs,images:n,source:"extension"})}clearQueue(){const e=[...this._steeringMessages],t=[...this._followUpMessages];return this._steeringMessages=[],this._followUpMessages=[],this.agent.clearAllQueues(),this._emitQueueUpdate(),{steering:e,followUp:t}}get pendingMessageCount(){return this._steeringMessages.length+this._followUpMessages.length}getSteeringMessages(){return this._steeringMessages}getFollowUpMessages(){return this._followUpMessages}get resourceLoader(){return this._resourceLoader}async abort(){this.abortRetry(),this.agent.abort(),await this.agent.waitForIdle()}async _emitModelSelect(e,t,s){T(t,e)||await this._extensionRunner.emit({type:"model_select",model:e,previousModel:t,source:s})}async setModel(e){if(!this._modelRegistry.hasConfiguredAuth(e))throw new Error(`No API key for ${e.provider}/${e.id}`);const t=this.model,s=this._getThinkingLevelForModelSwitch();this.agent.state.model=e,this._homeModel=e,this._stickyModel=void 0,this._sessionEscalations=0,this.sessionManager.appendModelChange(e.provider,e.id),this.settingsManager.setDefaultModelAndProvider(e.provider,e.id),this.setThinkingLevel(s),await this._emitModelSelect(e,t,"set")}async cycleModel(e="forward"){return this._scopedModels.length>0?this._cycleScopedModel(e):this._cycleAvailableModel(e)}async _cycleScopedModel(e){const t=this._scopedModels.filter(a=>this._modelRegistry.hasConfiguredAuth(a.model));if(t.length<=1)return;const s=this.model;let n=t.findIndex(a=>T(a.model,s));n===-1&&(n=0);const i=t.length,o=e==="forward"?(n+1)%i:(n-1+i)%i,r=t[o],l=this._getThinkingLevelForModelSwitch(r.thinkingLevel);return this.agent.state.model=r.model,this._homeModel=r.model,this._stickyModel=void 0,this._sessionEscalations=0,this.sessionManager.appendModelChange(r.model.provider,r.model.id),this.settingsManager.setDefaultModelAndProvider(r.model.provider,r.model.id),this.setThinkingLevel(l),await this._emitModelSelect(r.model,s,"cycle"),{model:r.model,thinkingLevel:this.thinkingLevel,isScoped:!0}}async _cycleAvailableModel(e){const t=await this._modelRegistry.getAvailable();if(t.length<=1)return;const s=this.model;let n=t.findIndex(a=>T(a,s));n===-1&&(n=0);const i=t.length,o=e==="forward"?(n+1)%i:(n-1+i)%i,r=t[o],l=this._getThinkingLevelForModelSwitch();return this.agent.state.model=r,this._homeModel=r,this._stickyModel=void 0,this._sessionEscalations=0,this.sessionManager.appendModelChange(r.provider,r.id),this.settingsManager.setDefaultModelAndProvider(r.provider,r.id),this.setThinkingLevel(l),await this._emitModelSelect(r,s,"cycle"),{model:r,thinkingLevel:this.thinkingLevel,isScoped:!1}}setThinkingLevel(e){const t=this.getAvailableThinkingLevels(),s=t.includes(e)?e:this._clampThinkingLevel(e,t),n=this.agent.state.thinkingLevel,i=s!==n;this.agent.state.thinkingLevel=s,i&&(this.sessionManager.appendThinkingLevelChange(s),(this.supportsThinking()||s!=="off")&&this.settingsManager.setDefaultThinkingLevel(s),this._emit({type:"thinking_level_changed",level:s}),this._extensionRunner.emit({type:"thinking_level_select",level:s,previousLevel:n}))}cycleThinkingLevel(){if(!this.supportsThinking())return;const e=this.getAvailableThinkingLevels(),s=(e.indexOf(this.thinkingLevel)+1)%e.length,n=e[s];return this.setThinkingLevel(n),n}getAvailableThinkingLevels(){return this.model?ie(this.model):ke}supportsThinking(){return!!this.model?.reasoning}_getThinkingLevelForModelSwitch(e){return e!==void 0?e:this.supportsThinking()?this.thinkingLevel:this.settingsManager.getDefaultThinkingLevel()??ge}_clampThinkingLevel(e,t){return this.model?se(this.model,e):"off"}setSteeringMode(e){this.agent.steeringMode=e,this.settingsManager.setSteeringMode(e)}setFollowUpMode(e){this.agent.followUpMode=e,this.settingsManager.setFollowUpMode(e)}async compact(e){this._disconnectFromAgent(),await this.abort(),this._compactionAbortController=new AbortController,this._emit({type:"compaction_start",reason:"manual"});try{if(!this.model)throw new Error(j());const{apiKey:t,headers:s}=await this._getCompactionRequestAuth(this.model),n=this.sessionManager.getBranch(),i=this.settingsManager.getCompactionSettings(),o=V(n,i);if(!o)throw n[n.length-1]?.type==="compaction"?new Error("Already compacted"):new Error("Nothing to compact (session too small)");let r,l=!1;if(this._extensionRunner.hasHandlers("session_before_compact")){const m=await this._extensionRunner.emit({type:"session_before_compact",preparation:o,branchEntries:n,customInstructions:e,signal:this._compactionAbortController.signal});if(m?.cancel)throw new Error("Compaction cancelled");m?.compaction&&(r=m.compaction,l=!0)}let a,c,d,u;if(r)a=r.summary,c=r.firstKeptEntryId,d=r.tokensBefore,u=r.details;else{const m=await W(o,this.model,t,s,e,this._compactionAbortController.signal,this.thinkingLevel,this.agent.streamFn);a=m.summary,c=m.firstKeptEntryId,d=m.tokensBefore,u=m.details}if(this._compactionAbortController.signal.aborted)throw new Error("Compaction cancelled");this.sessionManager.appendCompaction(a,c,d,u,l);const g=this.sessionManager.getEntries(),x=this.sessionManager.buildSessionContext();this.agent.state.messages=x.messages;const h=g.find(m=>m.type==="compaction"&&m.summary===a);this._extensionRunner&&h&&await this._extensionRunner.emit({type:"session_compact",compactionEntry:h,fromExtension:l});const b={summary:a,firstKeptEntryId:c,tokensBefore:d,details:u};return this._emit({type:"compaction_end",reason:"manual",result:b,aborted:!1,willRetry:!1}),b}catch(t){const s=t instanceof Error?t.message:String(t),n=s==="Compaction cancelled"||t instanceof Error&&t.name==="AbortError";throw this._emit({type:"compaction_end",reason:"manual",result:void 0,aborted:n,willRetry:!1,errorMessage:n?void 0:`Compaction failed: ${s}`}),t}finally{this._compactionAbortController=void 0,this._reconnectToAgent()}}abortCompaction(){this._compactionAbortController?.abort(),this._autoCompactionAbortController?.abort()}abortBranchSummary(){this._branchSummaryAbortController?.abort()}abortOverseer(){this._overseerAbortController?.abort()}abortRecap(){this._recapAbortController?.abort()}async _checkCompaction(e,t=!0){const s=this.settingsManager.getCompactionSettings();if(!s.enabled||t&&e.stopReason==="aborted")return!1;const n=this.model?.contextWindow??0,i=this.model&&e.provider===this.model.provider&&e.model===this.model.id,o=Q(this.sessionManager.getBranch());if(o!==null&&e.timestamp<=new Date(o.timestamp).getTime())return!1;if(i&&$(e,n)){if(this._overflowRecoveryAttempted)return this._emit({type:"compaction_end",reason:"overflow",result:void 0,aborted:!1,willRetry:!1,errorMessage:"Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."}),!1;this._overflowRecoveryAttempted=!0;const a=this.agent.state.messages;return a.length>0&&a[a.length-1].role==="assistant"&&(this.agent.state.messages=a.slice(0,-1)),await this._runAutoCompaction("overflow",!0)}let l;if(e.stopReason==="error"){const a=this.agent.state.messages,c=q(a);if(c.lastUsageIndex===null)return!1;const d=a[c.lastUsageIndex];if(o&&d.role==="assistant"&&d.timestamp<=new Date(o.timestamp).getTime())return!1;l=c.tokens}else l=G(e.usage);return me(l,n,s)?await this._runAutoCompaction("threshold",!1):!1}async _runAutoCompaction(e,t){const s=this.settingsManager.getCompactionSettings();this._emit({type:"compaction_start",reason:e}),this._autoCompactionAbortController=new AbortController;try{if(!this.model)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1}),!1;let n,i;if(this.agent.streamFn===H){const p=await this._modelRegistry.getApiKeyAndHeaders(this.model);if(!p.ok||!p.apiKey)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1}),!1;n=p.apiKey,i=p.headers}else({apiKey:n,headers:i}=await this._getCompactionRequestAuth(this.model));const o=this.sessionManager.getBranch(),r=V(o,s);if(!r)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1}),!1;let l,a=!1;if(this._extensionRunner.hasHandlers("session_before_compact")){const p=await this._extensionRunner.emit({type:"session_before_compact",preparation:r,branchEntries:o,customInstructions:void 0,signal:this._autoCompactionAbortController.signal});if(p?.cancel)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!0,willRetry:!1}),!1;p?.compaction&&(l=p.compaction,a=!0)}let c,d,u,g;if(l)c=l.summary,d=l.firstKeptEntryId,u=l.tokensBefore,g=l.details;else{const p=await W(r,this.model,n,i,void 0,this._autoCompactionAbortController.signal,this.thinkingLevel,this.agent.streamFn);c=p.summary,d=p.firstKeptEntryId,u=p.tokensBefore,g=p.details}if(this._autoCompactionAbortController.signal.aborted)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!0,willRetry:!1}),!1;this.sessionManager.appendCompaction(c,d,u,g,a);const x=this.sessionManager.getEntries(),h=this.sessionManager.buildSessionContext();this.agent.state.messages=h.messages;const b=x.find(p=>p.type==="compaction"&&p.summary===c);this._extensionRunner&&b&&await this._extensionRunner.emit({type:"session_compact",compactionEntry:b,fromExtension:a});const m={summary:c,firstKeptEntryId:d,tokensBefore:u,details:g};if(this._emit({type:"compaction_end",reason:e,result:m,aborted:!1,willRetry:t}),t){const p=this.agent.state.messages,y=p[p.length-1];return y?.role==="assistant"&&y.stopReason==="error"&&(this.agent.state.messages=p.slice(0,-1)),!0}return this.agent.hasQueuedMessages()}catch(n){const i=n instanceof Error?n.message:"compaction failed";return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1,errorMessage:e==="overflow"?`Context overflow recovery failed: ${i}`:`Auto-compaction failed: ${i}`}),!1}finally{this._autoCompactionAbortController=void 0}}setAutoCompactionEnabled(e){this.settingsManager.setCompactionEnabled(e)}get autoCompactionEnabled(){return this.settingsManager.getCompactionEnabled()}async bindExtensions(e){e.uiContext!==void 0&&(this._extensionUIContext=e.uiContext),e.mode!==void 0&&(this._extensionMode=e.mode),e.commandContextActions!==void 0&&(this._extensionCommandContextActions=e.commandContextActions),e.abortHandler!==void 0&&(this._extensionAbortHandler=e.abortHandler),e.shutdownHandler!==void 0&&(this._extensionShutdownHandler=e.shutdownHandler),e.onError!==void 0&&(this._extensionErrorListener=e.onError),this._applyExtensionBindings(this._extensionRunner),await this._extensionRunner.emit(this._sessionStartEvent),await this.extendResourcesFromExtensions(this._sessionStartEvent.reason==="reload"?"reload":"startup")}async extendResourcesFromExtensions(e){if(!this._extensionRunner.hasHandlers("resources_discover"))return;const{skillPaths:t,promptPaths:s,themePaths:n}=await this._extensionRunner.emitResourcesDiscover(this._cwd,e);if(t.length===0&&s.length===0&&n.length===0)return;const i={skillPaths:this.buildExtensionResourcePaths(t),promptPaths:this.buildExtensionResourcePaths(s),themePaths:this.buildExtensionResourcePaths(n)};this._resourceLoader.extendResources(i),this._baseSystemPrompt=this._rebuildSystemPrompt(this.getActiveToolNames()),this.agent.state.systemPrompt=this._baseSystemPrompt}buildExtensionResourcePaths(e){return e.map(t=>{const s=this.getExtensionSourceLabel(t.extensionPath),n=t.extensionPath.startsWith("<")?void 0:B(t.extensionPath);return{path:t.path,metadata:{source:s,scope:"temporary",origin:"top-level",baseDir:n}}})}getExtensionSourceLabel(e){return e.startsWith("<")?`extension:${e.replace(/[<>]/g,"")}`:`extension:${ee(e).replace(/\.(ts|js)$/,"")}`}_applyExtensionBindings(e){e.setUIContext(this._extensionUIContext,this._extensionMode),e.bindCommandContext(this._extensionCommandContextActions),this._extensionErrorUnsubscriber?.(),this._extensionErrorUnsubscriber=this._extensionErrorListener?e.onError(this._extensionErrorListener):void 0}_refreshCurrentModelFromRegistry(){const e=this.model;if(!e)return;const t=this._modelRegistry.find(e.provider,e.id);!t||t===e||(this.agent.state.model=t)}_bindExtensionCore(e){const t=w(()=>{const s=e.getRegisteredCommands().map(o=>({name:o.invocationName,description:o.description,source:"extension",sourceInfo:o.sourceInfo})),n=this.promptTemplates.map(o=>({name:o.name,description:o.description,source:"prompt",sourceInfo:o.sourceInfo})),i=this._resourceLoader.getSkills().skills.map(o=>({name:`skill:${o.name}`,description:o.description,source:"skill",sourceInfo:o.sourceInfo}));return[...s,...n,...i]},"getCommands");e.bindCore({sendMessage:w((s,n)=>{this.sendCustomMessage(s,n).catch(i=>{e.emitError({extensionPath:"<runtime>",event:"send_message",error:i instanceof Error?i.message:String(i)})})},"sendMessage"),sendUserMessage:w((s,n)=>{this.sendUserMessage(s,n).catch(i=>{e.emitError({extensionPath:"<runtime>",event:"send_user_message",error:i instanceof Error?i.message:String(i)})})},"sendUserMessage"),appendEntry:w((s,n)=>{this.sessionManager.appendCustomEntry(s,n)},"appendEntry"),setSessionName:w(s=>{this.setSessionName(s)},"setSessionName"),getSessionName:w(()=>this.sessionManager.getSessionName(),"getSessionName"),setLabel:w((s,n)=>{this.sessionManager.appendLabelChange(s,n)},"setLabel"),getActiveTools:w(()=>this.getActiveToolNames(),"getActiveTools"),getAllTools:w(()=>this.getAllTools(),"getAllTools"),setActiveTools:w(s=>this.setActiveToolsByName(s),"setActiveTools"),refreshTools:w(()=>this._refreshToolRegistry(),"refreshTools"),getCommands:t,setModel:w(async s=>this.modelRegistry.hasConfiguredAuth(s)?(await this.setModel(s),!0):!1,"setModel"),getThinkingLevel:w(()=>this.thinkingLevel,"getThinkingLevel"),setThinkingLevel:w(s=>this.setThinkingLevel(s),"setThinkingLevel")},{getModel:w(()=>this.model,"getModel"),isIdle:w(()=>!this.isStreaming,"isIdle"),getSignal:w(()=>this.agent.signal,"getSignal"),abort:w(()=>{if(this._extensionAbortHandler){this._extensionAbortHandler();return}this.abort()},"abort"),hasPendingMessages:w(()=>this.pendingMessageCount>0,"hasPendingMessages"),shutdown:w(()=>{this._extensionShutdownHandler?.()},"shutdown"),getContextUsage:w(()=>this.getContextUsage(),"getContextUsage"),compact:w(s=>{(async()=>{try{const n=await this.compact(s?.customInstructions);s?.onComplete?.(n)}catch(n){const i=n instanceof Error?n:new Error(String(n));s?.onError?.(i)}})()},"compact"),getSystemPrompt:w(()=>this.systemPrompt,"getSystemPrompt"),getSystemPromptOptions:w(()=>this._baseSystemPromptOptions,"getSystemPromptOptions")},{registerProvider:w((s,n)=>{this._modelRegistry.registerProvider(s,n),this._refreshCurrentModelFromRegistry()},"registerProvider"),unregisterProvider:w(s=>{this._modelRegistry.unregisterProvider(s),this._refreshCurrentModelFromRegistry()},"unregisterProvider")})}_refreshToolRegistry(e){const t=new Set(this._toolRegistry.keys()),s=this.getActiveToolNames(),n=this._allowedToolNames,i=this._excludedToolNames,o=w(h=>(!n||n.has(h))&&!i?.has(h),"isAllowedTool"),l=[...this._extensionRunner.getAllRegisteredTools(),...this._customTools.map(h=>({definition:h,sourceInfo:N(`<sdk:${h.name}>`,{source:"sdk"})}))].filter(h=>o(h.definition.name)),a=new Map(Array.from(this._baseToolDefinitions.entries()).filter(([h])=>o(h)).map(([h,b])=>[h,{definition:b,sourceInfo:N(`<builtin:${h}>`,{source:"builtin"})}]));for(const h of l)a.set(h.definition.name,{definition:h.definition,sourceInfo:h.sourceInfo});this._toolDefinitions=a,this._toolPromptSnippets=new Map(Array.from(a.values()).map(({definition:h})=>{const b=this._normalizePromptSnippet(h.promptSnippet);return b?[h.name,b]:void 0}).filter(h=>h!==void 0)),this._toolPromptGuidelines=new Map(Array.from(a.values()).map(({definition:h})=>{const b=this._normalizePromptGuidelines(h.promptGuidelines);return b.length>0?[h.name,b]:void 0}).filter(h=>h!==void 0));const c=this._extensionRunner,d=J(l,c),u=J(Array.from(this._baseToolDefinitions.values()).filter(h=>o(h.name)).map(h=>({definition:h,sourceInfo:N(`<builtin:${h.name}>`,{source:"builtin"})})),c),g=new Map(u.map(h=>[h.name,h]));for(const h of d)g.set(h.name,h);this._toolRegistry=g;const x=(e?.activeToolNames?[...e.activeToolNames]:[...s]).filter(h=>o(h));if(n)for(const h of this._toolRegistry.keys())n.has(h)&&x.push(h);else if(e?.includeAllExtensionTools)for(const h of d)x.push(h.name);else if(!e?.activeToolNames)for(const h of this._toolRegistry.keys())t.has(h)||x.push(h);this.setActiveToolsByName([...new Set(x)])}_buildRuntime(e){const t=this.settingsManager.getImageAutoResize(),s=this.settingsManager.getShellCommandPrefix(),n=this.settingsManager.getShellPath(),i=this._baseToolsOverride?Object.fromEntries(Object.entries(this._baseToolsOverride).map(([a,c])=>[a,Me(c)])):Ae(this._cwd,{read:{autoResizeImages:t},bash:{commandPrefix:s,shellPath:n}});this._baseToolDefinitions=new Map(Object.entries(i).map(([a,c])=>[a,c]));const o=this._resourceLoader.getExtensions();if(e.flagValues)for(const[a,c]of e.flagValues)o.runtime.flagValues.set(a,c);this._extensionRunner=new ye(o.extensions,o.runtime,this._cwd,this.sessionManager,this._modelRegistry),this._extensionRunnerRef&&(this._extensionRunnerRef.current=this._extensionRunner),this._bindExtensionCore(this._extensionRunner),this._applyExtensionBindings(this._extensionRunner);const r=this._baseToolsOverride?Object.keys(this._baseToolsOverride):["read","bash","edit","write"],l=e.activeToolNames??r;this._refreshToolRegistry({activeToolNames:l,includeAllExtensionTools:e.includeAllExtensionTools})}async reload(){const e=this._extensionRunner.getFlagValues();await we(this._extensionRunner,{type:"session_shutdown",reason:"reload"}),await this.settingsManager.reload(),re(),await this._resourceLoader.reload(),this._buildRuntime({activeToolNames:this.getActiveToolNames(),flagValues:e,includeAllExtensionTools:!0}),(this._extensionUIContext||this._extensionCommandContextActions||this._extensionShutdownHandler||this._extensionErrorListener)&&(await this._extensionRunner.emit({type:"session_start",reason:"reload"}),await this.extendResourcesFromExtensions("reload"))}_isNonRetryableProviderLimitError(e){return/GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(e)}_isRateLimited(e){return e.stopReason==="error"&&!!e.errorMessage&&/rate.?limit|429|too many requests|retry delay|quota/i.test(e.errorMessage)}_suggest(e,t){process.env.KODA_TIPS!=="0"&&(this._suggestedThisTask||this._suggestShown.has(e)||(this._suggestShown.add(e),this._suggestedThisTask=!0,this._emit({type:"suggest_feature",id:e,message:t})))}_isRetryableError(e){if(e.stopReason!=="error"||!e.errorMessage)return!1;const t=this.model?.contextWindow??0;if($(e,t))return!1;const s=e.errorMessage;return this._isNonRetryableProviderLimitError(s)?!1:/overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(s)}async _prepareRetry(e){const t=this.settingsManager.getRetrySettings();if(!t.enabled)return!1;if(this._retryAttempt++,this._retryAttempt>t.maxRetries)return this._retryAttempt--,!1;const s=t.baseDelayMs*2**(this._retryAttempt-1);this._emit({type:"auto_retry_start",attempt:this._retryAttempt,maxAttempts:t.maxRetries,delayMs:s,errorMessage:e.errorMessage||"Unknown error"});const n=this.agent.state.messages;n.length>0&&n[n.length-1].role==="assistant"&&(this.agent.state.messages=n.slice(0,-1)),this._retryAbortController=new AbortController;try{await ce(s,this._retryAbortController.signal)}catch{const i=this._retryAttempt;return this._retryAttempt=0,this._emit({type:"auto_retry_end",success:!1,attempt:i,finalError:"Retry cancelled"}),!1}finally{this._retryAbortController=void 0}return!0}async _failoverModel(e){if(e.stopReason!=="error"||$(e,this.model?.contextWindow??0))return!1;const t=this.agent.state.model;this._failoverTried.add(`${t.provider}/${t.id}`);const s=(process.env.KODA_FAILOVER||"openadapter/glm-5.1,openadapter/GLM-5,openadapter/Kimi-K2.6,openadapter/Kimi-K2.5,openadapter/MiniMax-M2.7,openadapter/DeepSeek-V4-Pro").split(",").map(f=>f.trim()).filter(Boolean),n=await this._fetchModelHealth(t),i=w(f=>({max:4,pro:3,lite:2,freemium:1})[(f||"").toLowerCase()]??0,"tierRank"),o=w(f=>f?.health!=null&&f.health<=3,"isHealthy"),r=w(f=>f?.latency??6,"latKey"),l=new Set,a=[],c=w((f,M,_)=>{const A=`${f}/${M}`;if(l.has(A)||this._failoverTried.has(A))return;const k=this._modelRegistry.find(f,M);!k||T(k,t)||(l.add(A),a.push({key:A,model:k,chainIdx:_}))},"consider");if(s.forEach((f,M)=>{const _=f.indexOf("/"),[A,k]=_>=0?[f.slice(0,_),f.slice(_+1)]:[t.provider,f];c(A,k,M)}),n)for(const f of Object.keys(n))f.includes("/")||f.includes("->")||c(t.provider,f,Number.POSITIVE_INFINITY);if(a.length===0)return!1;const d=w((f,M)=>{const _=n?.[f.model.id],A=n?.[M.model.id],k=o(_)?1:0,R=o(A)?1:0;if(k!==R)return R-k;const D=i(_?.tier),U=i(A?.tier);return D!==U?U-D:f.chainIdx!==M.chainIdx?f.chainIdx-M.chainIdx:r(_)-r(A)},"order"),u=i(n?.[t.id]?.tier),g=this._isRateLimited(e),x=a.filter(f=>i(n?.[f.model.id]?.tier)===u),h=a.filter(f=>i(n?.[f.model.id]?.tier)<u);let b;g&&u>0&&h.length?b=h:!g&&u>0&&x.length?b=x:b=a,b.sort(d);const m=b[0].model;this._failoverTried.add(b[0].key);const p=this.agent.state.messages;p.length>0&&p[p.length-1].role==="assistant"&&(this.agent.state.messages=p.slice(0,-1)),this.agent.state.model=m,this._retryAttempt=0,this.sessionManager.appendModelChange(m.provider,m.id),this._emitModelSelect(m,t,"cycle"),this._trace("failover",`${t.id} \u2192 ${m.id} (${g?"rate-limited":"provider error"})`);const y=i(n?.[m.id]?.tier);let v;return u&&y&&y<u?v=`\u2193 ${t.id} ${g?"rate-limited":"unavailable"} \u2014 Koda dropped to ${m.id} (lower tier) to keep going`:u&&y&&y>u?v=`\u2191 ${t.id} hit a wall \u2014 Koda beefed up to ${m.id}`:v=`\xBB ${t.id} ${g?"rate-limited":"hit a wall"} \u2014 Koda switched to ${m.id}`,this._emit({type:"model_switch_notice",message:v}),!0}async _fetchModelHealth(e){try{if(!e.baseUrl)return null;const t=await this._modelRegistry.getApiKeyAndHeaders(e);if(!t?.apiKey)return null;const s=new AbortController,n=setTimeout(()=>s.abort(),2500);try{const i=await fetch(`${e.baseUrl.replace(/\/$/,"")}/model-health`,{headers:{Authorization:`Bearer ${t.apiKey}`,...t.headers||{}},signal:s.signal});return i.ok?(await i.json())?.models??null:null}finally{clearTimeout(n)}}catch{return null}}async _zenitsuGetHealth(){const e=Date.now();if(this._zenitsuHealthCache&&e-this._zenitsuHealthCache.at<3e4)return this._zenitsuHealthCache.models;const t=this._zenitsuPreferred||this.agent.state.model,s=await this._fetchModelHealth(t);return s&&(this._zenitsuHealthCache={at:e,models:s}),this._zenitsuHealthCache?.models}async _zenitsuPickModel(){this._zenitsuPreferred||(this._zenitsuPreferred=this.agent.state.model);const e=this._zenitsuPreferred,t=e.provider,s=Date.now(),n=await this._zenitsuGetHealth()||{},i=w(g=>this._zenitsuCooldown.get(`${t}/${g}`)??0,"cooldownOf"),o=(process.env.KODA_ZENITSU_LADDER||"glm-5.1,Kimi-K2.6,MiniMax-M2.7,GLM-4.7,GLM-5,Kimi-K2.5,DeepSeek-V4-Pro").split(",").map(g=>g.trim()).filter(Boolean),r=[],l=new Set;for(const g of[e.id,...o]){if(l.has(g)||(l.add(g),i(g)>s))continue;const x=this._modelRegistry.find(t,g);if(!x)continue;const h=n[g]||{};r.push({id:g,model:x,healthy:h.health!=null&&h.health<=3,lat:h.latency??6})}if(r.length===0)return e;const a=i(e.id)<=s,c=r.find(g=>g.id===e.id),d=n[e.id]?.health!=null&&n[e.id].health>3;if(a&&c&&!d){const x=c.lat<=5?r.filter(h=>h.id!==e.id&&h.healthy&&h.lat<c.lat).sort((h,b)=>h.lat-b.lat)[0]:void 0;return x?x.model:e}const u=r.filter(g=>g.healthy).sort((g,x)=>g.lat-x.lat);return u.length?u[0].model:r.sort((g,x)=>g.lat-x.lat)[0].model}_messageText(e){const t=e?.content;return typeof t=="string"?t:Array.isArray(t)?t.filter(s=>s?.type==="text").map(s=>s.text).join(`
|
|
14
|
+
`),n.length===0&&(n=void 0)}await this.prompt(s,{expandPromptTemplates:!1,streamingBehavior:t?.deliverAs,images:n,source:"extension"})}clearQueue(){const e=[...this._steeringMessages],t=[...this._followUpMessages];return this._steeringMessages=[],this._followUpMessages=[],this.agent.clearAllQueues(),this._emitQueueUpdate(),{steering:e,followUp:t}}get pendingMessageCount(){return this._steeringMessages.length+this._followUpMessages.length}getSteeringMessages(){return this._steeringMessages}getFollowUpMessages(){return this._followUpMessages}get resourceLoader(){return this._resourceLoader}async abort(){this.abortRetry(),this.agent.abort(),await this.agent.waitForIdle()}async _emitModelSelect(e,t,s){T(t,e)||await this._extensionRunner.emit({type:"model_select",model:e,previousModel:t,source:s})}async setModel(e){if(!this._modelRegistry.hasConfiguredAuth(e))throw new Error(`No API key for ${e.provider}/${e.id}`);const t=this.model,s=this._getThinkingLevelForModelSwitch();this.agent.state.model=e,this._homeModel=e,this._stickyModel=void 0,this._sessionEscalations=0,this.sessionManager.appendModelChange(e.provider,e.id),this.settingsManager.setDefaultModelAndProvider(e.provider,e.id),this.setThinkingLevel(s),await this._emitModelSelect(e,t,"set")}async cycleModel(e="forward"){return this._scopedModels.length>0?this._cycleScopedModel(e):this._cycleAvailableModel(e)}async _cycleScopedModel(e){const t=this._scopedModels.filter(a=>this._modelRegistry.hasConfiguredAuth(a.model));if(t.length<=1)return;const s=this.model;let n=t.findIndex(a=>T(a.model,s));n===-1&&(n=0);const i=t.length,o=e==="forward"?(n+1)%i:(n-1+i)%i,r=t[o],l=this._getThinkingLevelForModelSwitch(r.thinkingLevel);return this.agent.state.model=r.model,this._homeModel=r.model,this._stickyModel=void 0,this._sessionEscalations=0,this.sessionManager.appendModelChange(r.model.provider,r.model.id),this.settingsManager.setDefaultModelAndProvider(r.model.provider,r.model.id),this.setThinkingLevel(l),await this._emitModelSelect(r.model,s,"cycle"),{model:r.model,thinkingLevel:this.thinkingLevel,isScoped:!0}}async _cycleAvailableModel(e){const t=await this._modelRegistry.getAvailable();if(t.length<=1)return;const s=this.model;let n=t.findIndex(a=>T(a,s));n===-1&&(n=0);const i=t.length,o=e==="forward"?(n+1)%i:(n-1+i)%i,r=t[o],l=this._getThinkingLevelForModelSwitch();return this.agent.state.model=r,this._homeModel=r,this._stickyModel=void 0,this._sessionEscalations=0,this.sessionManager.appendModelChange(r.provider,r.id),this.settingsManager.setDefaultModelAndProvider(r.provider,r.id),this.setThinkingLevel(l),await this._emitModelSelect(r,s,"cycle"),{model:r,thinkingLevel:this.thinkingLevel,isScoped:!1}}setThinkingLevel(e){const t=this.getAvailableThinkingLevels(),s=t.includes(e)?e:this._clampThinkingLevel(e,t),n=this.agent.state.thinkingLevel,i=s!==n;this.agent.state.thinkingLevel=s,i&&(this.sessionManager.appendThinkingLevelChange(s),(this.supportsThinking()||s!=="off")&&this.settingsManager.setDefaultThinkingLevel(s),this._emit({type:"thinking_level_changed",level:s}),this._extensionRunner.emit({type:"thinking_level_select",level:s,previousLevel:n}))}cycleThinkingLevel(){if(!this.supportsThinking())return;const e=this.getAvailableThinkingLevels(),s=(e.indexOf(this.thinkingLevel)+1)%e.length,n=e[s];return this.setThinkingLevel(n),n}getAvailableThinkingLevels(){return this.model?ie(this.model):ke}supportsThinking(){return!!this.model?.reasoning}_getThinkingLevelForModelSwitch(e){return e!==void 0?e:this.supportsThinking()?this.thinkingLevel:this.settingsManager.getDefaultThinkingLevel()??ge}_clampThinkingLevel(e,t){return this.model?se(this.model,e):"off"}setSteeringMode(e){this.agent.steeringMode=e,this.settingsManager.setSteeringMode(e)}setFollowUpMode(e){this.agent.followUpMode=e,this.settingsManager.setFollowUpMode(e)}async compact(e){this._disconnectFromAgent(),await this.abort(),this._compactionAbortController=new AbortController,this._emit({type:"compaction_start",reason:"manual"});try{if(!this.model)throw new Error(j());const{apiKey:t,headers:s}=await this._getCompactionRequestAuth(this.model),n=this.sessionManager.getBranch(),i=this.settingsManager.getCompactionSettings(),o=V(n,i);if(!o)throw n[n.length-1]?.type==="compaction"?new Error("Already compacted"):new Error("Nothing to compact (session too small)");let r,l=!1;if(this._extensionRunner.hasHandlers("session_before_compact")){const g=await this._extensionRunner.emit({type:"session_before_compact",preparation:o,branchEntries:n,customInstructions:e,signal:this._compactionAbortController.signal});if(g?.cancel)throw new Error("Compaction cancelled");g?.compaction&&(r=g.compaction,l=!0)}let a,d,m,h;if(r)a=r.summary,d=r.firstKeptEntryId,m=r.tokensBefore,h=r.details;else{const g=await q(o,this.model,t,s,e,this._compactionAbortController.signal,this.thinkingLevel,this.agent.streamFn);a=g.summary,d=g.firstKeptEntryId,m=g.tokensBefore,h=g.details}if(this._compactionAbortController.signal.aborted)throw new Error("Compaction cancelled");this.sessionManager.appendCompaction(a,d,m,h,l);const u=this.sessionManager.getEntries(),x=this.sessionManager.buildSessionContext();this.agent.state.messages=x.messages;const c=u.find(g=>g.type==="compaction"&&g.summary===a);this._extensionRunner&&c&&await this._extensionRunner.emit({type:"session_compact",compactionEntry:c,fromExtension:l});const b={summary:a,firstKeptEntryId:d,tokensBefore:m,details:h};return this._emit({type:"compaction_end",reason:"manual",result:b,aborted:!1,willRetry:!1}),b}catch(t){const s=t instanceof Error?t.message:String(t),n=s==="Compaction cancelled"||t instanceof Error&&t.name==="AbortError";throw this._emit({type:"compaction_end",reason:"manual",result:void 0,aborted:n,willRetry:!1,errorMessage:n?void 0:`Compaction failed: ${s}`}),t}finally{this._compactionAbortController=void 0,this._reconnectToAgent()}}abortCompaction(){this._compactionAbortController?.abort(),this._autoCompactionAbortController?.abort()}abortBranchSummary(){this._branchSummaryAbortController?.abort()}abortOverseer(){this._overseerAbortController?.abort()}abortRecap(){this._recapAbortController?.abort()}async _checkCompaction(e,t=!0){const s=this.settingsManager.getCompactionSettings();if(!s.enabled||t&&e.stopReason==="aborted")return!1;const n=this.model?.contextWindow??0,i=this.model&&e.provider===this.model.provider&&e.model===this.model.id,o=Q(this.sessionManager.getBranch());if(o!==null&&e.timestamp<=new Date(o.timestamp).getTime())return!1;if(i&&$(e,n)){if(this._overflowRecoveryAttempted)return this._emit({type:"compaction_end",reason:"overflow",result:void 0,aborted:!1,willRetry:!1,errorMessage:"Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."}),!1;this._overflowRecoveryAttempted=!0;const a=this.agent.state.messages;return a.length>0&&a[a.length-1].role==="assistant"&&(this.agent.state.messages=a.slice(0,-1)),await this._runAutoCompaction("overflow",!0)}let l;if(e.stopReason==="error"){const a=this.agent.state.messages,d=W(a);if(d.lastUsageIndex===null)return!1;const m=a[d.lastUsageIndex];if(o&&m.role==="assistant"&&m.timestamp<=new Date(o.timestamp).getTime())return!1;l=d.tokens}else l=G(e.usage);return me(l,n,s)?await this._runAutoCompaction("threshold",!1):!1}async _runAutoCompaction(e,t){const s=this.settingsManager.getCompactionSettings();this._emit({type:"compaction_start",reason:e}),this._autoCompactionAbortController=new AbortController;try{if(!this.model)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1}),!1;let n,i;if(this.agent.streamFn===H){const p=await this._modelRegistry.getApiKeyAndHeaders(this.model);if(!p.ok||!p.apiKey)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1}),!1;n=p.apiKey,i=p.headers}else({apiKey:n,headers:i}=await this._getCompactionRequestAuth(this.model));const o=this.sessionManager.getBranch(),r=V(o,s);if(!r)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1}),!1;let l,a=!1;if(this._extensionRunner.hasHandlers("session_before_compact")){const p=await this._extensionRunner.emit({type:"session_before_compact",preparation:r,branchEntries:o,customInstructions:void 0,signal:this._autoCompactionAbortController.signal});if(p?.cancel)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!0,willRetry:!1}),!1;p?.compaction&&(l=p.compaction,a=!0)}let d,m,h,u;if(l)d=l.summary,m=l.firstKeptEntryId,h=l.tokensBefore,u=l.details;else{const p=await q(r,this.model,n,i,void 0,this._autoCompactionAbortController.signal,this.thinkingLevel,this.agent.streamFn);d=p.summary,m=p.firstKeptEntryId,h=p.tokensBefore,u=p.details}if(this._autoCompactionAbortController.signal.aborted)return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!0,willRetry:!1}),!1;this.sessionManager.appendCompaction(d,m,h,u,a);const x=this.sessionManager.getEntries(),c=this.sessionManager.buildSessionContext();this.agent.state.messages=c.messages;const b=x.find(p=>p.type==="compaction"&&p.summary===d);this._extensionRunner&&b&&await this._extensionRunner.emit({type:"session_compact",compactionEntry:b,fromExtension:a});const g={summary:d,firstKeptEntryId:m,tokensBefore:h,details:u};if(this._emit({type:"compaction_end",reason:e,result:g,aborted:!1,willRetry:t}),t){const p=this.agent.state.messages,y=p[p.length-1];return y?.role==="assistant"&&y.stopReason==="error"&&(this.agent.state.messages=p.slice(0,-1)),!0}return this.agent.hasQueuedMessages()}catch(n){const i=n instanceof Error?n.message:"compaction failed";return this._emit({type:"compaction_end",reason:e,result:void 0,aborted:!1,willRetry:!1,errorMessage:e==="overflow"?`Context overflow recovery failed: ${i}`:`Auto-compaction failed: ${i}`}),!1}finally{this._autoCompactionAbortController=void 0}}setAutoCompactionEnabled(e){this.settingsManager.setCompactionEnabled(e)}get autoCompactionEnabled(){return this.settingsManager.getCompactionEnabled()}async bindExtensions(e){e.uiContext!==void 0&&(this._extensionUIContext=e.uiContext),e.mode!==void 0&&(this._extensionMode=e.mode),e.commandContextActions!==void 0&&(this._extensionCommandContextActions=e.commandContextActions),e.abortHandler!==void 0&&(this._extensionAbortHandler=e.abortHandler),e.shutdownHandler!==void 0&&(this._extensionShutdownHandler=e.shutdownHandler),e.onError!==void 0&&(this._extensionErrorListener=e.onError),this._applyExtensionBindings(this._extensionRunner),await this._extensionRunner.emit(this._sessionStartEvent),await this.extendResourcesFromExtensions(this._sessionStartEvent.reason==="reload"?"reload":"startup")}async extendResourcesFromExtensions(e){if(!this._extensionRunner.hasHandlers("resources_discover"))return;const{skillPaths:t,promptPaths:s,themePaths:n}=await this._extensionRunner.emitResourcesDiscover(this._cwd,e);if(t.length===0&&s.length===0&&n.length===0)return;const i={skillPaths:this.buildExtensionResourcePaths(t),promptPaths:this.buildExtensionResourcePaths(s),themePaths:this.buildExtensionResourcePaths(n)};this._resourceLoader.extendResources(i),this._baseSystemPrompt=this._rebuildSystemPrompt(this.getActiveToolNames()),this.agent.state.systemPrompt=this._baseSystemPrompt}buildExtensionResourcePaths(e){return e.map(t=>{const s=this.getExtensionSourceLabel(t.extensionPath),n=t.extensionPath.startsWith("<")?void 0:B(t.extensionPath);return{path:t.path,metadata:{source:s,scope:"temporary",origin:"top-level",baseDir:n}}})}getExtensionSourceLabel(e){return e.startsWith("<")?`extension:${e.replace(/[<>]/g,"")}`:`extension:${ee(e).replace(/\.(ts|js)$/,"")}`}_applyExtensionBindings(e){e.setUIContext(this._extensionUIContext,this._extensionMode),e.bindCommandContext(this._extensionCommandContextActions),this._extensionErrorUnsubscriber?.(),this._extensionErrorUnsubscriber=this._extensionErrorListener?e.onError(this._extensionErrorListener):void 0}_refreshCurrentModelFromRegistry(){const e=this.model;if(!e)return;const t=this._modelRegistry.find(e.provider,e.id);!t||t===e||(this.agent.state.model=t)}_bindExtensionCore(e){const t=w(()=>{const s=e.getRegisteredCommands().map(o=>({name:o.invocationName,description:o.description,source:"extension",sourceInfo:o.sourceInfo})),n=this.promptTemplates.map(o=>({name:o.name,description:o.description,source:"prompt",sourceInfo:o.sourceInfo})),i=this._resourceLoader.getSkills().skills.map(o=>({name:`skill:${o.name}`,description:o.description,source:"skill",sourceInfo:o.sourceInfo}));return[...s,...n,...i]},"getCommands");e.bindCore({sendMessage:w((s,n)=>{this.sendCustomMessage(s,n).catch(i=>{e.emitError({extensionPath:"<runtime>",event:"send_message",error:i instanceof Error?i.message:String(i)})})},"sendMessage"),sendUserMessage:w((s,n)=>{this.sendUserMessage(s,n).catch(i=>{e.emitError({extensionPath:"<runtime>",event:"send_user_message",error:i instanceof Error?i.message:String(i)})})},"sendUserMessage"),appendEntry:w((s,n)=>{this.sessionManager.appendCustomEntry(s,n)},"appendEntry"),setSessionName:w(s=>{this.setSessionName(s)},"setSessionName"),getSessionName:w(()=>this.sessionManager.getSessionName(),"getSessionName"),setLabel:w((s,n)=>{this.sessionManager.appendLabelChange(s,n)},"setLabel"),getActiveTools:w(()=>this.getActiveToolNames(),"getActiveTools"),getAllTools:w(()=>this.getAllTools(),"getAllTools"),setActiveTools:w(s=>this.setActiveToolsByName(s),"setActiveTools"),refreshTools:w(()=>this._refreshToolRegistry(),"refreshTools"),getCommands:t,setModel:w(async s=>this.modelRegistry.hasConfiguredAuth(s)?(await this.setModel(s),!0):!1,"setModel"),getThinkingLevel:w(()=>this.thinkingLevel,"getThinkingLevel"),setThinkingLevel:w(s=>this.setThinkingLevel(s),"setThinkingLevel")},{getModel:w(()=>this.model,"getModel"),isIdle:w(()=>!this.isStreaming,"isIdle"),getSignal:w(()=>this.agent.signal,"getSignal"),abort:w(()=>{if(this._extensionAbortHandler){this._extensionAbortHandler();return}this.abort()},"abort"),hasPendingMessages:w(()=>this.pendingMessageCount>0,"hasPendingMessages"),shutdown:w(()=>{this._extensionShutdownHandler?.()},"shutdown"),getContextUsage:w(()=>this.getContextUsage(),"getContextUsage"),compact:w(s=>{(async()=>{try{const n=await this.compact(s?.customInstructions);s?.onComplete?.(n)}catch(n){const i=n instanceof Error?n:new Error(String(n));s?.onError?.(i)}})()},"compact"),getSystemPrompt:w(()=>this.systemPrompt,"getSystemPrompt"),getSystemPromptOptions:w(()=>this._baseSystemPromptOptions,"getSystemPromptOptions")},{registerProvider:w((s,n)=>{this._modelRegistry.registerProvider(s,n),this._refreshCurrentModelFromRegistry()},"registerProvider"),unregisterProvider:w(s=>{this._modelRegistry.unregisterProvider(s),this._refreshCurrentModelFromRegistry()},"unregisterProvider")})}_refreshToolRegistry(e){const t=new Set(this._toolRegistry.keys()),s=this.getActiveToolNames(),n=this._allowedToolNames,i=this._excludedToolNames,o=w(c=>(!n||n.has(c))&&!i?.has(c),"isAllowedTool"),l=[...this._extensionRunner.getAllRegisteredTools(),...this._customTools.map(c=>({definition:c,sourceInfo:N(`<sdk:${c.name}>`,{source:"sdk"})}))].filter(c=>o(c.definition.name)),a=new Map(Array.from(this._baseToolDefinitions.entries()).filter(([c])=>o(c)).map(([c,b])=>[c,{definition:b,sourceInfo:N(`<builtin:${c}>`,{source:"builtin"})}]));for(const c of l)a.set(c.definition.name,{definition:c.definition,sourceInfo:c.sourceInfo});this._toolDefinitions=a,this._toolPromptSnippets=new Map(Array.from(a.values()).map(({definition:c})=>{const b=this._normalizePromptSnippet(c.promptSnippet);return b?[c.name,b]:void 0}).filter(c=>c!==void 0)),this._toolPromptGuidelines=new Map(Array.from(a.values()).map(({definition:c})=>{const b=this._normalizePromptGuidelines(c.promptGuidelines);return b.length>0?[c.name,b]:void 0}).filter(c=>c!==void 0));const d=this._extensionRunner,m=J(l,d),h=J(Array.from(this._baseToolDefinitions.values()).filter(c=>o(c.name)).map(c=>({definition:c,sourceInfo:N(`<builtin:${c.name}>`,{source:"builtin"})})),d),u=new Map(h.map(c=>[c.name,c]));for(const c of m)u.set(c.name,c);this._toolRegistry=u;const x=(e?.activeToolNames?[...e.activeToolNames]:[...s]).filter(c=>o(c));if(n)for(const c of this._toolRegistry.keys())n.has(c)&&x.push(c);else if(e?.includeAllExtensionTools)for(const c of m)x.push(c.name);else if(!e?.activeToolNames)for(const c of this._toolRegistry.keys())t.has(c)||x.push(c);this.setActiveToolsByName([...new Set(x)])}_buildRuntime(e){const t=this.settingsManager.getImageAutoResize(),s=this.settingsManager.getShellCommandPrefix(),n=this.settingsManager.getShellPath(),i=this._baseToolsOverride?Object.fromEntries(Object.entries(this._baseToolsOverride).map(([a,d])=>[a,Me(d)])):Ae(this._cwd,{read:{autoResizeImages:t},bash:{commandPrefix:s,shellPath:n}});this._baseToolDefinitions=new Map(Object.entries(i).map(([a,d])=>[a,d]));const o=this._resourceLoader.getExtensions();if(e.flagValues)for(const[a,d]of e.flagValues)o.runtime.flagValues.set(a,d);this._extensionRunner=new ye(o.extensions,o.runtime,this._cwd,this.sessionManager,this._modelRegistry),this._extensionRunnerRef&&(this._extensionRunnerRef.current=this._extensionRunner),this._bindExtensionCore(this._extensionRunner),this._applyExtensionBindings(this._extensionRunner);const r=this._baseToolsOverride?Object.keys(this._baseToolsOverride):["read","bash","edit","write"],l=e.activeToolNames??r;this._refreshToolRegistry({activeToolNames:l,includeAllExtensionTools:e.includeAllExtensionTools})}async reload(){const e=this._extensionRunner.getFlagValues();await we(this._extensionRunner,{type:"session_shutdown",reason:"reload"}),await this.settingsManager.reload(),re(),await this._resourceLoader.reload(),this._buildRuntime({activeToolNames:this.getActiveToolNames(),flagValues:e,includeAllExtensionTools:!0}),(this._extensionUIContext||this._extensionCommandContextActions||this._extensionShutdownHandler||this._extensionErrorListener)&&(await this._extensionRunner.emit({type:"session_start",reason:"reload"}),await this.extendResourcesFromExtensions("reload"))}_isNonRetryableProviderLimitError(e){return/GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(e)}_isRateLimited(e){return e.stopReason==="error"&&!!e.errorMessage&&/rate.?limit|429|too many requests|retry delay|quota/i.test(e.errorMessage)}_suggest(e,t){process.env.KODA_TIPS!=="0"&&(this._suggestedThisTask||this._suggestShown.has(e)||(this._suggestShown.add(e),this._suggestedThisTask=!0,this._emit({type:"suggest_feature",id:e,message:t})))}_isRetryableError(e){if(e.stopReason!=="error"||!e.errorMessage)return!1;const t=this.model?.contextWindow??0;if($(e,t))return!1;const s=e.errorMessage;return this._isNonRetryableProviderLimitError(s)?!1:/overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(s)}async _prepareRetry(e){const t=this.settingsManager.getRetrySettings();if(!t.enabled)return!1;if(this._retryAttempt++,this._retryAttempt>t.maxRetries)return this._retryAttempt--,!1;const s=t.baseDelayMs*2**(this._retryAttempt-1);this._emit({type:"auto_retry_start",attempt:this._retryAttempt,maxAttempts:t.maxRetries,delayMs:s,errorMessage:e.errorMessage||"Unknown error"});const n=this.agent.state.messages;n.length>0&&n[n.length-1].role==="assistant"&&(this.agent.state.messages=n.slice(0,-1)),this._retryAbortController=new AbortController;try{await ce(s,this._retryAbortController.signal)}catch{const i=this._retryAttempt;return this._retryAttempt=0,this._emit({type:"auto_retry_end",success:!1,attempt:i,finalError:"Retry cancelled"}),!1}finally{this._retryAbortController=void 0}return!0}async _failoverModel(e){if(e.stopReason!=="error"||$(e,this.model?.contextWindow??0))return!1;const t=this.agent.state.model;this._failoverTried.add(`${t.provider}/${t.id}`);const s=(process.env.KODA_FAILOVER||"openadapter/glm-5.1,openadapter/GLM-5,openadapter/Kimi-K2.6,openadapter/Kimi-K2.5,openadapter/MiniMax-M2.7,openadapter/DeepSeek-V4-Pro").split(",").map(f=>f.trim()).filter(Boolean),n=await this._fetchModelHealth(t),i=w(f=>({max:4,pro:3,lite:2,freemium:1})[(f||"").toLowerCase()]??0,"tierRank"),o=w(f=>f?.health!=null&&f.health<=3,"isHealthy"),r=w(f=>f?.latency??6,"latKey"),l=new Set,a=[],d=w((f,M,_)=>{const A=`${f}/${M}`;if(l.has(A)||this._failoverTried.has(A))return;const k=this._modelRegistry.find(f,M);!k||T(k,t)||(l.add(A),a.push({key:A,model:k,chainIdx:_}))},"consider");if(s.forEach((f,M)=>{const _=f.indexOf("/"),[A,k]=_>=0?[f.slice(0,_),f.slice(_+1)]:[t.provider,f];d(A,k,M)}),n)for(const f of Object.keys(n))f.includes("/")||f.includes("->")||d(t.provider,f,Number.POSITIVE_INFINITY);if(a.length===0)return!1;const m=w((f,M)=>{const _=n?.[f.model.id],A=n?.[M.model.id],k=o(_)?1:0,S=o(A)?1:0;if(k!==S)return S-k;const D=i(_?.tier),U=i(A?.tier);return D!==U?U-D:f.chainIdx!==M.chainIdx?f.chainIdx-M.chainIdx:r(_)-r(A)},"order"),h=i(n?.[t.id]?.tier),u=this._isRateLimited(e),x=a.filter(f=>i(n?.[f.model.id]?.tier)===h),c=a.filter(f=>i(n?.[f.model.id]?.tier)<h);let b;u&&h>0&&c.length?b=c:!u&&h>0&&x.length?b=x:b=a,b.sort(m);const g=b[0].model;this._failoverTried.add(b[0].key);const p=this.agent.state.messages;p.length>0&&p[p.length-1].role==="assistant"&&(this.agent.state.messages=p.slice(0,-1)),this.agent.state.model=g,this._retryAttempt=0,this.sessionManager.appendModelChange(g.provider,g.id),this._emitModelSelect(g,t,"cycle"),this._trace("failover",`${t.id} \u2192 ${g.id} (${u?"rate-limited":"provider error"})`);const y=i(n?.[g.id]?.tier);let v;return h&&y&&y<h?v=`\u2193 ${t.id} ${u?"rate-limited":"unavailable"} \u2014 Koda dropped to ${g.id} (lower tier) to keep going`:h&&y&&y>h?v=`\u2191 ${t.id} hit a wall \u2014 Koda beefed up to ${g.id}`:v=`\xBB ${t.id} ${u?"rate-limited":"hit a wall"} \u2014 Koda switched to ${g.id}`,this._emit({type:"model_switch_notice",message:v}),!0}async _fetchModelHealth(e){try{if(!e.baseUrl)return null;const t=await this._modelRegistry.getApiKeyAndHeaders(e);if(!t?.apiKey)return null;const s=new AbortController,n=setTimeout(()=>s.abort(),2500);try{const i=await fetch(`${e.baseUrl.replace(/\/$/,"")}/model-health`,{headers:{Authorization:`Bearer ${t.apiKey}`,...t.headers||{}},signal:s.signal});return i.ok?(await i.json())?.models??null:null}finally{clearTimeout(n)}}catch{return null}}async _zenitsuGetHealth(){const e=Date.now();if(this._zenitsuHealthCache&&e-this._zenitsuHealthCache.at<3e4)return this._zenitsuHealthCache.models;const t=this._zenitsuPreferred||this.agent.state.model,s=await this._fetchModelHealth(t);return s&&(this._zenitsuHealthCache={at:e,models:s}),this._zenitsuHealthCache?.models}async _zenitsuPickModel(){this._zenitsuPreferred||(this._zenitsuPreferred=this.agent.state.model);const e=this._zenitsuPreferred,t=e.provider,s=Date.now(),n=await this._zenitsuGetHealth()||{},i=w(u=>this._zenitsuCooldown.get(`${t}/${u}`)??0,"cooldownOf"),o=(process.env.KODA_ZENITSU_LADDER||"glm-5.1,Kimi-K2.6,MiniMax-M2.7,GLM-4.7,GLM-5,Kimi-K2.5,DeepSeek-V4-Pro").split(",").map(u=>u.trim()).filter(Boolean),r=[],l=new Set;for(const u of[e.id,...o]){if(l.has(u)||(l.add(u),i(u)>s))continue;const x=this._modelRegistry.find(t,u);if(!x)continue;const c=n[u]||{};r.push({id:u,model:x,healthy:c.health!=null&&c.health<=3,lat:c.latency??6})}if(r.length===0)return e;const a=i(e.id)<=s,d=r.find(u=>u.id===e.id),m=n[e.id]?.health!=null&&n[e.id].health>3;if(a&&d&&!m){const x=d.lat<=5?r.filter(c=>c.id!==e.id&&c.healthy&&c.lat<d.lat).sort((c,b)=>c.lat-b.lat)[0]:void 0;return x?x.model:e}const h=r.filter(u=>u.healthy).sort((u,x)=>u.lat-x.lat);return h.length?h[0].model:r.sort((u,x)=>u.lat-x.lat)[0].model}_messageText(e){const t=e?.content;return typeof t=="string"?t:Array.isArray(t)?t.filter(s=>s?.type==="text").map(s=>s.text).join(`
|
|
15
15
|
`):""}_goalFromMessages(e){const t=Array.isArray(e)?e:[e];for(let s=t.length-1;s>=0;s--)if(t[s]?.role==="user"){const n=this._messageText(t[s]).trim();if(n)return n}}_overseerContext(){const e=[];for(const t of this.agent.state.messages.slice(-12)){const s=t?.role;if(s==="assistant"){const n=this._messageText(t).trim();n&&e.push(`assistant: ${n.slice(0,600)}`);for(const i of Array.isArray(t.content)?t.content:[])(i?.type==="toolUse"||i?.type==="tool_use")&&e.push(` \u2192 tool ${i.name}(${JSON.stringify(i.input??i.args??{}).slice(0,160)})`)}else(s==="toolResult"||s==="tool")&&e.push(` result(${t.toolName??""}): ${this._messageText(t).slice(0,300)}`)}return e.join(`
|
|
16
|
-
`).slice(-4e3)}async _getModelTier(e){if(!this._tierCache){this._tierCache=new Map;try{if(e.baseUrl){const t=await this._modelRegistry.getApiKeyAndHeaders(e);if(t?.apiKey){const s=new AbortController,n=setTimeout(()=>s.abort(),3e3);try{const i=await fetch(`${e.baseUrl.replace(/\/$/,"")}/models`,{headers:{Authorization:`Bearer ${t.apiKey}`,...t.headers||{}},signal:s.signal});if(i.ok){const o=await i.json();for(const r of o.data??[])r.tier&&this._tierCache.set(r.id,r.tier)}}finally{clearTimeout(n)}}}}catch{}}return this._tierCache.get(e.id)}_isDegenerateOutput(e){const t=this._messageText(e);if(t.length<200)return!1;const s=t.match(/(.{3,60}?)\1{6,}/s);if(s&&s[0].length>150)return!0;const n=t.split(/\s+/).filter(Boolean);return n.length>60&&new Set(n).size/n.length<.15}async _escalate(e,t){if(this._escalationsDone>=2)return!1;const s=this.agent.state.model,n=(process.env.KODA_ESCALATE||"GLM-4.7,glm-5.1").split(",").map(r=>r.trim()).filter(Boolean);let i;for(const r of n){if(r===s.id||this._escalateTried.has(r))continue;const l=this._modelRegistry.find(s.provider,r);if(l){i=l;break}}if(!i)return!1;if(this._escalationsDone++,this._sessionEscalations++,this._escalateTried.add(i.id),this._sessionEscalations>=2&&(this._stickyModel=i),t?.stripLast){const r=this.agent.state.messages;r.length>0&&r[r.length-1].role==="assistant"&&(this.agent.state.messages=r.slice(0,-1))}this.agent.state.model=i,this._retryAttempt=0,this.
|
|
16
|
+
`).slice(-4e3)}async _getModelTier(e){if(!this._tierCache){this._tierCache=new Map;try{if(e.baseUrl){const t=await this._modelRegistry.getApiKeyAndHeaders(e);if(t?.apiKey){const s=new AbortController,n=setTimeout(()=>s.abort(),3e3);try{const i=await fetch(`${e.baseUrl.replace(/\/$/,"")}/models`,{headers:{Authorization:`Bearer ${t.apiKey}`,...t.headers||{}},signal:s.signal});if(i.ok){const o=await i.json();for(const r of o.data??[])r.tier&&this._tierCache.set(r.id,r.tier)}}finally{clearTimeout(n)}}}}catch{}}return this._tierCache.get(e.id)}_isDegenerateOutput(e){const t=this._messageText(e);if(t.length<200)return!1;const s=t.match(/(.{3,60}?)\1{6,}/s);if(s&&s[0].length>150)return!0;const n=t.split(/\s+/).filter(Boolean);return n.length>60&&new Set(n).size/n.length<.15}async _escalate(e,t){if(this._escalationsDone>=2)return!1;const s=this.agent.state.model,n=(process.env.KODA_ESCALATE||"GLM-4.7,glm-5.1").split(",").map(r=>r.trim()).filter(Boolean);let i;for(const r of n){if(r===s.id||this._escalateTried.has(r))continue;const l=this._modelRegistry.find(s.provider,r);if(l){i=l;break}}if(!i)return!1;if(this._escalationsDone++,this._sessionEscalations++,this._escalateTried.add(i.id),this._sessionEscalations>=2&&(this._stickyModel=i),t?.stripLast){const r=this.agent.state.messages;r.length>0&&r[r.length-1].role==="assistant"&&(this.agent.state.messages=r.slice(0,-1))}this.agent.state.model=i,this._retryAttempt=0,this._verifyLedger={attempts:0,lastSig:"",escalated:!1,gaveUp:!1},this.sessionManager.appendModelChange(i.provider,i.id),this._emitModelSelect(i,s,"cycle");const o=this._stickyModel?" (staying on it \u2014 this session needs the muscle)":"";return this._trace("escalate",`${s.id} \u2192 ${i.id}${this._stickyModel?" [sticky]":""} \u2014 ${e}`),this._emit({type:"model_switch_notice",message:`\u25B2 Koda escalated to ${i.id} \u2014 ${e}${o}`}),!0}listSkillNames(){return this._resourceLoader.getSkills().skills.map(e=>e.name)}getPinnedSkills(){return[...this._pinnedSkills.keys()]}pinSkill(e){const t=this._resourceLoader.getSkills().skills.find(s=>s.name===e);if(!t)return!1;try{const s=z(E(t.filePath,"utf-8")).trim();return this._pinnedSkills.set(e,`<active_skill name="${t.name}" location="${t.filePath}">
|
|
17
17
|
References are relative to ${t.baseDir}. Follow these instructions for this session:
|
|
18
18
|
|
|
19
19
|
${s}
|
|
20
|
-
</active_skill>`),this._refreshSystemPrompt(),!0}catch{return!1}}unpinSkill(e){const t=this._pinnedSkills.delete(e);return t&&this._refreshSystemPrompt(),t}_refreshSystemPrompt(){this._baseSystemPrompt=this._rebuildSystemPrompt(this.getActiveToolNames()),this.agent.state.systemPrompt=this._baseSystemPrompt}getSessionGoal(){return this._sessionGoal}clearSessionGoal(){this._sessionGoal=void 0,this._emit({type:"goal_cleared"})}async setSessionGoal(e){const t=e.trim();if(!t)return;const s=await this._generateMilestones(t);this._sessionGoal={text:t,milestones:s.map(n=>({text:n,done:!1})),achieved:!1},this._emit({type:"goal_set",text:t,milestones:this._sessionGoal.milestones})}async _generateMilestones(e){const t=[e];try{const s=this.agent.state.model,n=this._modelRegistry.find(s.provider,process.env.KODA_OVERSEER_MODEL||"GLM-4.7")||s;if(!n.baseUrl)return t;const i=await this._modelRegistry.getApiKeyAndHeaders(n);if(!i?.apiKey)return t;const o=await fetch(`${n.baseUrl.replace(/\/$/,"")}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${i.apiKey}`,"Content-Type":"application/json",...i.headers||{}},body:JSON.stringify({model:n.id,temperature:0,messages:[{role:"system",content:"Break the user's session goal into 3-6 concrete, verifiable milestones (short imperative phrases, in order). Reply with ONLY a JSON array of strings."},{role:"user",content:e}]})});if(!o.ok)return t;let l=((await o.json())?.choices?.[0]?.message?.content??"").trim();l=l.replace(/<think>[\s\S]*?<\/think>/gi,"");const a=l.match(/\[[\s\S]*\]/);a&&(l=a[0]);const
|
|
21
|
-
${s.stderr}`.trim();return{passed:s.code===0&&!s.killed,output:n.slice(-1500)}}catch(s){return{passed:!0,output:`(verify could not run: ${s.message})`}}}async _overseerCheck(e){if(e.stopReason==="error")return!1;const t=this.agent.state.model,s=this._sessionGoal,n=!!s&&!s.achieved,i=await this._getModelTier(t),o=process.env.KODA_OVERSEER!=="0"
|
|
20
|
+
</active_skill>`),this._refreshSystemPrompt(),!0}catch{return!1}}unpinSkill(e){const t=this._pinnedSkills.delete(e);return t&&this._refreshSystemPrompt(),t}_refreshSystemPrompt(){this._baseSystemPrompt=this._rebuildSystemPrompt(this.getActiveToolNames()),this.agent.state.systemPrompt=this._baseSystemPrompt}getSessionGoal(){return this._sessionGoal}clearSessionGoal(){this._sessionGoal=void 0,this._emit({type:"goal_cleared"})}async setSessionGoal(e){const t=e.trim();if(!t)return;const s=await this._generateMilestones(t);this._sessionGoal={text:t,milestones:s.map(n=>({text:n,done:!1})),achieved:!1},this._emit({type:"goal_set",text:t,milestones:this._sessionGoal.milestones})}async _generateMilestones(e){const t=[e];try{const s=this.agent.state.model,n=this._modelRegistry.find(s.provider,process.env.KODA_OVERSEER_MODEL||"GLM-4.7")||s;if(!n.baseUrl)return t;const i=await this._modelRegistry.getApiKeyAndHeaders(n);if(!i?.apiKey)return t;const o=await fetch(`${n.baseUrl.replace(/\/$/,"")}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${i.apiKey}`,"Content-Type":"application/json",...i.headers||{}},body:JSON.stringify({model:n.id,temperature:0,messages:[{role:"system",content:"Break the user's session goal into 3-6 concrete, verifiable milestones (short imperative phrases, in order). Reply with ONLY a JSON array of strings."},{role:"user",content:e}]})});if(!o.ok)return t;let l=((await o.json())?.choices?.[0]?.message?.content??"").trim();l=l.replace(/<think>[\s\S]*?<\/think>/gi,"");const a=l.match(/\[[\s\S]*\]/);a&&(l=a[0]);const d=JSON.parse(l),m=Array.isArray(d)?d.filter(h=>typeof h=="string"&&h.trim()).map(h=>h.trim()).slice(0,6):[];return m.length?m:t}catch{return t}}_trace(e,t){this._harnessTrace.push({ts:Date.now(),kind:e,detail:t}),this._harnessTrace.length>60&&this._harnessTrace.shift()}getHarnessTrace(){return[...this._harnessTrace]}_gatewayOpen(){return Date.now()<this._gatewayCircuit.openUntil}_gatewayResult(e){e?this._gatewayCircuit.failures=0:++this._gatewayCircuit.failures>=3&&(this._gatewayCircuit.openUntil=Date.now()+3e4,this._gatewayCircuit.failures=0,this._trace("circuit","gateway calls paused 30s after repeated failures"))}_detectVerify(){if(this._verifyCmd!==void 0)return this._verifyCmd;const e=w(s=>(this._verifyCmd=s,s),"set"),t=process.env.KODA_VERIFY;if(t==="0")return e(null);if(t)return e(t);try{const s=this._cwd,n=C(s,"package.json");if(R(n)){const i=JSON.parse(E(n,"utf-8")),o=i.scripts||{},r=Object.keys({...i.dependencies||{},...i.devDependencies||{}}).join(" ");if(/\b(react|vue|svelte|next|nuxt|vite|@angular\/core|solid-js|preact|astro)\b/i.test(r)){const a=[];if(o.typecheck?a.push("npm run typecheck"):o["type-check"]?a.push("npm run type-check"):R(C(s,"tsconfig.json"))&&a.push("npx tsc --noEmit"),o.lint&&a.push("npm run lint"),a.length)return e(a.join(" && "));if(o.build)return e("npm run build")}for(const a of["typecheck","type-check","build","lint"])if(o[a])return e(`npm run ${a}`)}if(R(C(s,"tsconfig.json")))return e("npx tsc --noEmit");if(R(C(s,"go.mod")))return e("go build ./...");if(R(C(s,"Cargo.toml")))return e("cargo check")}catch{}return e(null)}async _runVerification(e,t){try{const s=await pe("sh",["-c",e],this._cwd,{timeout:12e4,signal:t}),n=`${s.stdout}
|
|
21
|
+
${s.stderr}`.trim();return{passed:s.code===0&&!s.killed,output:n.slice(-1500)}}catch(s){return{passed:!0,output:`(verify could not run: ${s.message})`}}}async _overseerCheck(e){if(e.stopReason==="error"||this._verifyLedger.gaveUp)return!1;const t=this.agent.state.model,s=this._sessionGoal,n=!!s&&!s.achieved,i=await this._getModelTier(t),o=process.env.KODA_OVERSEER!=="0"&&!!this._currentGoal&&!!i&&i!=="max",r=this._codeChangedThisTask?this._detectVerify():null;if(!r&&!o&&!n)return!1;this._emit({type:"overseer_start",model:t.id}),this._overseerAbortController=new AbortController;const l=this._overseerAbortController.signal;try{let a;if(r){const u=await this._runVerification(r,l);if(l.aborted)return this._trace("overseer","cancelled during verify"),this._emit({type:"overseer_end",outcome:"skipped",detail:"cancelled"}),!1;a={cmd:r,passed:u.passed,output:u.output},this._trace("verify",`${r} \u2192 ${u.passed?"pass":"FAIL"}`)}const d=!!a&&!a.passed;if(!d&&!o&&!n)return this._emit({type:"overseer_end",outcome:"complete",detail:a?`${a.cmd} \u2713`:void 0}),!1;const m=o||d,h=await this._taskEndReview(m?this._currentGoal||"(complete the user's request)":void 0,this._overseerContext(),this.getActiveToolNames(),n?s:void 0,a,l);if(l.aborted)return this._trace("overseer","cancelled during review"),this._emit({type:"overseer_end",outcome:"skipped",detail:"cancelled"}),!1;if(!h)return this._emit({type:"overseer_end",outcome:"skipped",detail:this._overseerLastError||"unknown"}),!1;if(n&&s&&h.goal&&(h.goal.milestonesDone.forEach((u,x)=>{s.milestones[x]&&u&&(s.milestones[x].done=!0)}),s.achieved=h.goal.achieved||s.milestones.every(u=>u.done),this._emit({type:"goal_progress",text:s.text,milestones:s.milestones.map(u=>({...u})),achieved:s.achieved,drift:h.goal.drift||void 0})),m&&h.task&&!h.task.done){const u=this._failureSignature(d?a.output:h.task.missing),x=u!==""&&u===this._verifyLedger.lastSig;this._verifyLedger.attempts++,this._verifyLedger.lastSig=u;const b=x||this._verifyLedger.attempts>=3,g=n&&h.goal?.drift?` You've also drifted from the session goal: ${h.goal.drift}.`:"";if(b){if(!this._verifyLedger.escalated&&await this._escalate(d?"build still failing after fixes":`stuck \u2014 ${h.task.missing}`))return this._verifyLedger={attempts:0,lastSig:u,escalated:!0,gaveUp:!1},this.agent.followUp(this._overseerNudge(d,a,h.task.missing,h.task.evidence,g)),this._emit({type:"overseer_end",outcome:"nudging",detail:"escalating to a stronger model"}),!0;this._verifyLedger.gaveUp=!0;const p=d?`\`${a.cmd}\` still failing after ${this._verifyLedger.attempts} attempt(s) \u2014 stopped to avoid looping; likely pre-existing or needs you`:`couldn't confirm done after ${this._verifyLedger.attempts} attempt(s): ${h.task.missing}`;return this._emit({type:"overseer_end",outcome:"gave_up",detail:p}),!1}return this.agent.followUp(this._overseerNudge(d,a,h.task.missing,h.task.evidence,g)),this._emit({type:"overseer_end",outcome:"nudging",detail:d?`${a.cmd} failed`:h.task.missing}),!0}return this._emit({type:"overseer_end",outcome:"complete",detail:a?`${a.cmd} \u2713`:void 0}),!1}finally{this._overseerAbortController=void 0}}_overseerNudge(e,t,s,n,i){return{role:"user",content:[{type:"text",text:e&&t?`Not done \u2014 \`${t.cmd}\` failed. Find and fix the ROOT CAUSE, then re-run it. If a specific failure is clearly PRE-EXISTING and unrelated to this request, say so explicitly instead of guessing fixes for it.${i}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Actual output:
|
|
24
|
+
${t.output}`:`Not done yet \u2014 ${s}.${i} Do exactly that, then verify it works before you stop.`+(n?` (What's verified so far: ${n})`:"")+" If you're unsure how, call ask_for_help to get a step-by-step plan from a senior model and follow it."}],timestamp:Date.now()}}_failureSignature(e){return(e||"").toLowerCase().replace(/[0-9a-f]{7,}/g,"#").replace(/\d+/g,"#").replace(/\s+/g," ").trim().slice(0,800)}async _taskEndReview(e,t,s,n,i,o){try{if(this._gatewayOpen())return this._overseerLastError="gateway circuit open (recent failures)",null;const r=this.agent.state.model,l=[process.env.KODA_OVERSEER_MODEL||"GLM-4.7","GLM-5","glm-5.1"],a=l.map(d=>this._modelRegistry.find(r.provider,d)).filter(d=>!!d&&d.id!==r.id);if(a.length===0)return this._overseerLastError=`no judge model registered (tried ${l.join(", ")})`,null;for(const d of a){if(o?.aborted)return this._overseerLastError="cancelled",null;const m=await this._reviewOnce(d,e,t,s,n,i,o);if(this._gatewayResult(!m.error||m.error.startsWith("unparseable")),m.review)return m.review;this._overseerLastError=`${d.id}: ${m.error}`}return null}catch(r){return this._overseerLastError=r?.message?.slice(0,80)||"error",null}}async _reviewOnce(e,t,s,n,i,o,r){try{if(r?.aborted)return{error:"cancelled"};if(!e.baseUrl)return{error:"no baseUrl"};const l=await this._modelRegistry.getApiKeyAndHeaders(e);if(!l?.apiKey)return{error:"no api key"};const a=["You are a meticulous reviewer for a coding agent. Ground every judgement in the WORK LOG (a record of tool calls and their results); be skeptical of any claim with no matching evidence."],d=[];if(t){const x=o?.passed===!0?`NOTE: the harness just ran the project's check \`${o.cmd}\` and it PASSED, so the code compiles/works \u2014 judge only whether the request's scope is fully addressed. `:o&&!o.passed?`NOTE: the harness just ran \`${o.cmd}\` and it FAILED (see BUILD OUTPUT below). Decide from that output whether the failure is CAUSED BY or within the scope of this REQUEST. If the failing code is what this task was meant to add or change, it is NOT done \u2014 name the precise fix in "taskMissing". If the failure is clearly PRE-EXISTING or unrelated to the request, do NOT block on it: judge the request's own deliverables on their merits, and mention the pre-existing failure in "taskMissing" only if the request actually required a green build. `:"";a.push("TASK REVIEW \u2014 decide whether EVERY explicit deliverable of the REQUEST is present AND works. Judge only its explicit asks. If it says to run/test/verify, it is done only if the log shows that ran and succeeded. Completion requires EVIDENCE in the work log \u2014 files actually written, commands that actually ran and succeeded. The assistant's reasoning, plans, intentions, or confident claims are NOT evidence; if the log shows only thinking or failed/partial attempts with no successful results producing the deliverables, it is NOT done. "+x+`The assistant has these TOOLS: ${n.join(", ")||"(none)"}; if it guessed or gave up without using a tool that could have found the answer, it is NOT done \u2014 name the tool to call. "taskMissing" must name ONLY what is left (imperative; exact file/function/command/tool), never restate done work. "taskEvidence" must cite the concrete proof for your verdict (the file, command, or output line you relied on) \u2014 never a vague opinion.`),d.push('"taskDone": true|false, "taskMissing": "<only what is left; empty if done>", "taskEvidence": "<concrete proof for the verdict>"')}if(i){const x=i.milestones.map((c,b)=>`${b}: ${c.text}`).join("; ");a.push(`GOAL REVIEW \u2014 the session goal is: "${i.text}". Milestones by index: ${x}. From the WORK LOG, return "milestonesDone" as an array of booleans (one per index; true only if clearly accomplished in the log). Set "goalAchieved" true only if the whole goal is genuinely met. If recent work clearly wandered onto something unrelated to the goal, put a one-sentence note in "drift" (else "").`),d.push('"milestonesDone": [booleans], "goalAchieved": true|false, "drift": "<note or empty>"')}a.push(`Output ONLY a JSON object, no prose or fences: { ${d.join(", ")} }.`);const m=new AbortController,h=setTimeout(()=>m.abort(),45e3),u=w(()=>m.abort(),"onAbort");r?.addEventListener("abort",u,{once:!0});try{const x=await fetch(`${e.baseUrl.replace(/\/$/,"")}/chat/completions`,{method:"POST",headers:{Authorization:`Bearer ${l.apiKey}`,"Content-Type":"application/json",...l.headers||{}},body:JSON.stringify({model:e.id,temperature:0,messages:[{role:"system",content:a.join(`
|
|
24
25
|
|
|
25
26
|
`)},{role:"user",content:`${t?`REQUEST:
|
|
26
27
|
${t}
|
|
27
28
|
|
|
29
|
+
`:""}${o&&!o.passed?`BUILD OUTPUT (\`${o.cmd}\` FAILED):
|
|
30
|
+
${o.output}
|
|
31
|
+
|
|
28
32
|
`:""}WORK LOG:
|
|
29
|
-
${s}`}]}),signal:
|
|
30
|
-
${e}`:e;try{const r=await he(o,this.sessionManager.getCwd(),s?.operations??ve({shellPath:i}),{onChunk:t,signal:this._bashAbortController.signal});return this.recordBashResult(e,r,s),r}finally{this._bashAbortController=void 0}}recordBashResult(e,t,s){const n={role:"bashExecution",command:e,output:t.output,exitCode:t.exitCode,cancelled:t.cancelled,truncated:t.truncated,fullOutputPath:t.fullOutputPath,timestamp:Date.now(),excludeFromContext:s?.excludeFromContext};this.isStreaming?this._pendingBashMessages.push(n):(this.agent.state.messages.push(n),this.sessionManager.appendMessage(n))}abortBash(){this._bashAbortController?.abort()}get isBashRunning(){return this._bashAbortController!==void 0}get hasPendingBashMessages(){return this._pendingBashMessages.length>0}_flushPendingBashMessages(){if(this._pendingBashMessages.length!==0){for(const e of this._pendingBashMessages)this.agent.state.messages.push(e),this.sessionManager.appendMessage(e);this._pendingBashMessages=[]}}setSessionName(e){this.sessionManager.appendSessionInfo(e),this._emit({type:"session_info_changed",name:this.sessionManager.getSessionName()})}async navigateTree(e,t={}){const s=this.sessionManager.getLeafId();if(e===s)return{cancelled:!1};if(t.summarize&&!this.model)throw new Error("No model available for summarization");const n=this.sessionManager.getEntry(e);if(!n)throw new Error(`Entry ${e} not found`);const{entries:i,commonAncestorId:o}=de(this.sessionManager,s,e);let r=t.customInstructions,l=t.replaceInstructions,a=t.label;const
|
|
33
|
+
${s}`}]}),signal:m.signal});if(!x.ok)return{error:`HTTP ${x.status}`};const c=await x.json();let b=(c?.choices?.[0]?.message?.content??c?.choices?.[0]?.message?.reasoning_content??"").trim();b=b.replace(/<think>[\s\S]*?<\/think>/gi,"").replace(/^```(?:json)?/i,"").replace(/```\s*$/,"").trim();const g=b.match(/\{[\s\S]*\}/);if(g&&(b=g[0]),!b)return{error:"empty response"};let p;try{p=JSON.parse(b)}catch{return{error:"unparseable JSON"}}const y={};if(t){const v=p?.taskDone===!0||p?.taskDone==="true",f=String(Array.isArray(p?.taskMissing)?p.taskMissing.join("; "):p?.taskMissing??"").trim(),M=String(p?.taskEvidence??"").trim().slice(0,300);y.task=!v&&!f?{done:!0,missing:"",evidence:M}:{done:v,missing:f,evidence:M}}return i&&(y.goal={milestonesDone:Array.isArray(p?.milestonesDone)?p.milestonesDone.map(v=>v===!0||v==="true"):[],achieved:p?.goalAchieved===!0||p?.goalAchieved==="true",drift:String(p?.drift??"").trim()}),{review:y}}finally{clearTimeout(h),r?.removeEventListener("abort",u)}}catch(l){return{error:l?.name==="AbortError"?"timeout 45s":(l?.message||"error").slice(0,80)}}}_isMutatingBash(e){return/>>?\s*(?!\/dev\/null|&?\d)/.test(e)?!0:/\b(rm|rmdir|mv|cp|mkdir|touch|truncate|dd|chmod|chown|ln|tee|patch|sed\s+-i|install|npm\s+(i|install|add|remove|uninstall|ci)|pnpm\s+(install|add|remove)|yarn\s+(add|remove|install)|pip\d?\s+install|cargo\s+(add|install)|go\s+install|apt(-get)?\s+(install|remove)|brew\s+(install|uninstall)|git\s+(commit|add|push|checkout|reset|merge|rebase|clean|stash|apply|rm|mv))\b/.test(e)}_parseRetrySeconds(e){const t=/(?:retry|again)[^0-9]{0,16}?(\d+(?:\.\d+)?)\s*(ms|s\b|sec|second)/i.exec(e);if(t){const s=parseFloat(t[1]);return/ms/i.test(t[2])?Math.max(1,Math.ceil(s/1e3)):Math.ceil(s)}}async _zenitsuHandle429(e){if(e.stopReason!=="error"||!e.errorMessage||!/rate.?limit|429|too many requests|retry delay|quota/i.test(e.errorMessage))return!1;const t=this.agent.state.model,s=this._parseRetrySeconds(e.errorMessage)??30;this._zenitsuCooldown.set(`${t.provider}/${t.id}`,Date.now()+s*1e3);const n=await this._zenitsuPickModel();if(!n||T(n,t))return!1;const i=this.agent.state.messages;return i.length>0&&i[i.length-1].role==="assistant"&&(this.agent.state.messages=i.slice(0,-1)),this.agent.state.model=n,this._retryAttempt=0,this.sessionManager.appendModelChange(n.provider,n.id),this._emitModelSelect(n,t,"cycle"),this._trace("zenitsu",`${t.id} \u2192 ${n.id} (429)`),this._emit({type:"model_switch_notice",message:`\xBB ${t.id} rate-limited \u2014 Koda zipped over to ${n.id} (no waiting)`}),!0}_maybeReturnHome(){const e=this._stickyModel??this._homeModel;if(!e)return!1;const t=this.agent.state.model;return t.id===e.id&&t.provider===e.provider||!this._modelRegistry.hasConfiguredAuth(e)||(this._zenitsuCooldown.get(`${e.provider}/${e.id}`)??0)>Date.now()?!1:(this.agent.state.model=e,this._zenitsuPreferred=e,this._retryAttempt=0,this.sessionManager.appendModelChange(e.provider,e.id),this._emitModelSelect(e,t,"cycle"),this._trace("revert",`\u2192 ${e.id} (preferred available)`),this._emit({type:"model_switch_notice",message:`\u2190 Back on your pick \u2014 ${e.id}`}),!0)}abortRetry(){this._retryAbortController?.abort()}get isRetrying(){return this._retryAbortController!==void 0}get autoRetryEnabled(){return this.settingsManager.getRetryEnabled()}setAutoRetryEnabled(e){this.settingsManager.setRetryEnabled(e)}async executeBash(e,t,s){this._bashAbortController=new AbortController;const n=this.settingsManager.getShellCommandPrefix(),i=this.settingsManager.getShellPath(),o=n?`${n}
|
|
34
|
+
${e}`:e;try{const r=await he(o,this.sessionManager.getCwd(),s?.operations??ve({shellPath:i}),{onChunk:t,signal:this._bashAbortController.signal});return this.recordBashResult(e,r,s),r}finally{this._bashAbortController=void 0}}recordBashResult(e,t,s){const n={role:"bashExecution",command:e,output:t.output,exitCode:t.exitCode,cancelled:t.cancelled,truncated:t.truncated,fullOutputPath:t.fullOutputPath,timestamp:Date.now(),excludeFromContext:s?.excludeFromContext};this.isStreaming?this._pendingBashMessages.push(n):(this.agent.state.messages.push(n),this.sessionManager.appendMessage(n))}abortBash(){this._bashAbortController?.abort()}get isBashRunning(){return this._bashAbortController!==void 0}get hasPendingBashMessages(){return this._pendingBashMessages.length>0}_flushPendingBashMessages(){if(this._pendingBashMessages.length!==0){for(const e of this._pendingBashMessages)this.agent.state.messages.push(e),this.sessionManager.appendMessage(e);this._pendingBashMessages=[]}}setSessionName(e){this.sessionManager.appendSessionInfo(e),this._emit({type:"session_info_changed",name:this.sessionManager.getSessionName()})}async navigateTree(e,t={}){const s=this.sessionManager.getLeafId();if(e===s)return{cancelled:!1};if(t.summarize&&!this.model)throw new Error("No model available for summarization");const n=this.sessionManager.getEntry(e);if(!n)throw new Error(`Entry ${e} not found`);const{entries:i,commonAncestorId:o}=de(this.sessionManager,s,e);let r=t.customInstructions,l=t.replaceInstructions,a=t.label;const d={targetId:e,oldLeafId:s,commonAncestorId:o,entriesToSummarize:i,userWantsSummary:t.summarize??!1,customInstructions:r,replaceInstructions:l,label:a};this._branchSummaryAbortController=new AbortController;try{let m,h=!1;if(this._extensionRunner.hasHandlers("session_before_tree")){const y=await this._extensionRunner.emit({type:"session_before_tree",preparation:d,signal:this._branchSummaryAbortController.signal});if(y?.cancel)return{cancelled:!0};y?.summary&&t.summarize&&(m=y.summary,h=!0),y?.customInstructions!==void 0&&(r=y.customInstructions),y?.replaceInstructions!==void 0&&(l=y.replaceInstructions),y?.label!==void 0&&(a=y.label)}let u,x;if(t.summarize&&i.length>0&&!m){const y=this.model,{apiKey:v,headers:f}=await this._getRequiredRequestAuth(y),M=this.settingsManager.getBranchSummarySettings(),_=await ue(i,{model:y,apiKey:v,headers:f,signal:this._branchSummaryAbortController.signal,customInstructions:r,replaceInstructions:l,reserveTokens:M.reserveTokens,streamFn:this.agent.streamFn});if(_.aborted)return{cancelled:!0,aborted:!0};if(_.error)throw new Error(_.error);u=_.summary,x={readFiles:_.readFiles||[],modifiedFiles:_.modifiedFiles||[]}}else m&&(u=m.summary,x=m.details);let c,b;n.type==="message"&&n.message.role==="user"?(c=n.parentId,b=this._extractUserMessageText(n.message.content)):n.type==="custom_message"?(c=n.parentId,b=typeof n.content=="string"?n.content:n.content.filter(y=>y.type==="text").map(y=>y.text).join("")):c=e;let g;if(u){const y=this.sessionManager.branchWithSummary(c,u,x,h);g=this.sessionManager.getEntry(y),a&&this.sessionManager.appendLabelChange(y,a)}else c===null?this.sessionManager.resetLeaf():this.sessionManager.branch(c);a&&!u&&this.sessionManager.appendLabelChange(e,a);const p=this.sessionManager.buildSessionContext();return this.agent.state.messages=p.messages,await this._extensionRunner.emit({type:"session_tree",newLeafId:this.sessionManager.getLeafId(),oldLeafId:s,summaryEntry:g,fromExtension:u?h:void 0}),{editorText:b,cancelled:!1,summaryEntry:g}}finally{this._branchSummaryAbortController=void 0}}getUserMessagesForForking(){const e=this.sessionManager.getEntries(),t=[];for(const s of e){if(s.type!=="message"||s.message.role!=="user")continue;const n=this._extractUserMessageText(s.message.content);n&&t.push({entryId:s.id,text:n})}return t}_extractUserMessageText(e){return typeof e=="string"?e:Array.isArray(e)?e.filter(t=>t.type==="text").map(t=>t.text).join(""):""}getSessionStats(){const e=this.state,t=e.messages.filter(m=>m.role==="user").length,s=e.messages.filter(m=>m.role==="assistant").length,n=e.messages.filter(m=>m.role==="toolResult").length;let i=0,o=0,r=0,l=0,a=0,d=0;for(const m of e.messages)if(m.role==="assistant"){const h=m;i+=h.content.filter(u=>u.type==="toolCall").length,o+=h.usage.input,r+=h.usage.output,l+=h.usage.cacheRead,a+=h.usage.cacheWrite,d+=h.usage.cost.total}return{sessionFile:this.sessionFile,sessionId:this.sessionId,userMessages:t,assistantMessages:s,toolCalls:i,toolResults:n,totalMessages:e.messages.length,tokens:{input:o,output:r,cacheRead:l,cacheWrite:a,total:o+r+l+a},cost:d,contextUsage:this.getContextUsage()}}getContextUsage(){const e=this.model;if(!e)return;const t=e.contextWindow??0;if(t<=0)return;const s=this.sessionManager.getBranch(),n=Q(s);if(n){const r=s.lastIndexOf(n);let l=!1;for(let a=s.length-1;a>r;a--){const d=s[a];if(d.type==="message"&&d.message.role==="assistant"){const m=d.message;if(m.stopReason!=="aborted"&&m.stopReason!=="error"){G(m.usage)>0&&(l=!0);break}}}if(!l)return{tokens:null,contextWindow:t,percent:null}}const i=W(this.messages),o=i.tokens/t*100;return{tokens:i.tokens,contextWindow:t,percent:o}}async exportToHtml(e){const t=this.settingsManager.getTheme(),s=_e({getToolDefinition:w(n=>this.getToolDefinition(n),"getToolDefinition"),theme:oe,cwd:this.sessionManager.getCwd()});return await fe(this.sessionManager,this.state,{outputPath:e,themeName:t,toolRenderer:s})}exportToJsonl(e){const t=le(e??`session-${new Date().toISOString().replace(/[:.]/g,"-")}.jsonl`,process.cwd()),s=B(t);R(s)||K(s,{recursive:!0});const n={type:"session",version:xe,id:this.sessionManager.getSessionId(),timestamp:new Date().toISOString(),cwd:this.sessionManager.getCwd()},i=this.sessionManager.getBranch(),o=[JSON.stringify(n)];let r=null;for(const l of i){const a={...l,parentId:r};o.push(JSON.stringify(a)),r=l.id}return P(t,`${o.join(`
|
|
31
35
|
`)}
|
|
32
36
|
`),t}getLastAssistantText(){const e=this.messages.slice().reverse().find(s=>{if(s.role!=="assistant")return!1;const n=s;return!(n.stopReason==="aborted"&&n.content.length===0)});if(!e)return;let t="";for(const s of e.content)s.type==="text"&&(t+=s.text);return t.trim()||void 0}createReplacedSessionContext(){const e=Object.defineProperties({},Object.getOwnPropertyDescriptors(this._extensionRunner.createCommandContext()));return e.sendMessage=(t,s)=>this.sendCustomMessage(t,s),e.sendUserMessage=(t,s)=>this.sendUserMessage(t,s),e}hasExtensionHandlers(e){return this._extensionRunner.hasHandlers(e)}get extensionRunner(){return this._extensionRunner}}export{Xe as AgentSession,Ze as parseSkillBlock};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var Fe=Object.defineProperty;var h=(m,e)=>Fe(m,"name",{value:e,configurable:!0});import*as qe from"node:crypto";import*as v from"node:fs";import*as q from"node:os";import*as y from"node:path";import{getProviders as Be}from"@openadapter/koda-ai";import{CombinedAutocompleteProvider as Ue,Container as A,fuzzyFilter as Oe,getCapabilities as _e,hyperlink as Ne,Loader as L,Markdown as B,matchesKey as Ke,ProcessTerminal as je,Spacer as p,setKeybindings as ze,Text as f,TruncatedText as K,TUI as Ve,visibleWidth as Ge}from"@openadapter/koda-tui";import Qe from"chalk";import{spawn as j,spawnSync as Xe}from"child_process";import{detectImportable as Je,importExternalSessions as Ye}from"../../cli/import-sessions.js";import{maybeRefreshOpenAdapterModels as Ze}from"../../cli/openadapter-setup.js";import{APP_NAME as z,APP_TITLE as te,getAgentDir as se,getAuthPath as ie,getDebugLogPath as et,getDocsPath as tt,getShareViewerUrl as st,KODA_VERSION as it,VERSION as D}from"../../config.js";import{parseSkillBlock as nt}from"../../core/agent-session.js";import{SessionImportFileNotFoundError as ot}from"../../core/agent-session-runtime.js";import{FooterDataProvider as rt}from"../../core/footer-data-provider.js";import{configureHttpDispatcher as V,formatHttpIdleTimeoutMs as at}from"../../core/http-dispatcher.js";import{KeybindingsManager as ht}from"../../core/keybindings.js";import{createCompactionSummaryMessage as dt}from"../../core/messages.js";import{defaultModelPerProvider as ne,findExactModelReferenceMatch as lt,resolveModelScope as oe}from"../../core/model-resolver.js";import{DefaultPackageManager as ct}from"../../core/package-manager.js";import{BUILT_IN_PROVIDER_DISPLAY_NAMES as ut}from"../../core/provider-display-names.js";import{formatMissingSessionCwdPrompt as gt,MissingSessionCwdError as re}from"../../core/session-cwd.js";import{SessionManager as W}from"../../core/session-manager.js";import{BUILTIN_SLASH_COMMANDS as ae}from"../../core/slash-commands.js";import{isInstallTelemetryEnabled as pt}from"../../core/telemetry.js";import{runBackgroundSelfUpdate as mt}from"../../utils/auto-update.js";import{getChangelogPath as he,getNewEntries as ft,parseChangelog as de}from"../../utils/changelog.js";import{copyToClipboard as Ct}from"../../utils/clipboard.js";import{extensionForImageMimeType as wt,readClipboardImage as St}from"../../utils/clipboard-image.js";import{parseGitUrl as le}from"../../utils/git.js";import{getKodaUserAgent as Et}from"../../utils/koda-user-agent.js";import{getCwdRelativePath as yt}from"../../utils/paths.js";import{killTrackedDetachedChildren as G}from"../../utils/shell.js";import{ensureTool as ce}from"../../utils/tools-manager.js";import{checkForNewPiVersion as kt}from"../../utils/version-check.js";import{ArminComponent as bt}from"./components/armin.js";import{AssistantMessageComponent as U}from"./components/assistant-message.js";import{BashExecutionComponent as Q}from"./components/bash-execution.js";import{BorderedLoader as Mt}from"./components/bordered-loader.js";import{BranchSummaryMessageComponent as xt}from"./components/branch-summary-message.js";import{CompactionSummaryMessageComponent as Tt}from"./components/compaction-summary-message.js";import{CountdownTimer as $t}from"./components/countdown-timer.js";import{CustomEditor as Pt}from"./components/custom-editor.js";import{CustomMessageComponent as At}from"./components/custom-message.js";import{DaxnutsComponent as Rt}from"./components/daxnuts.js";import{DynamicBorder as k}from"./components/dynamic-border.js";import{EarendilAnnouncementComponent as vt}from"./components/earendil-announcement.js";import{ExtensionEditorComponent as It}from"./components/extension-editor.js";import{ExtensionInputComponent as Lt}from"./components/extension-input.js";import{ExtensionSelectorComponent as X}from"./components/extension-selector.js";import{FooterComponent as Dt}from"./components/footer.js";import{formatKeyText as ue,keyDisplayText as ge,keyHint as pe,keyText as $,rawKeyHint as R}from"./components/keybinding-hints.js";import{LoginDialogComponent as J}from"./components/login-dialog.js";import{ModelSelectorComponent as Wt}from"./components/model-selector.js";import{OAuthSelectorComponent as me}from"./components/oauth-selector.js";import{ScopedModelsSelectorComponent as Ht}from"./components/scoped-models-selector.js";import{SessionSelectorComponent as Ft}from"./components/session-selector.js";import{SettingsSelectorComponent as qt}from"./components/settings-selector.js";import{SkillInvocationMessageComponent as Bt}from"./components/skill-invocation-message.js";import{ToolExecutionComponent as H}from"./components/tool-execution.js";import{TreeSelectorComponent as Ut}from"./components/tree-selector.js";import{UserMessageComponent as fe}from"./components/user-message.js";import{UserMessageSelectorComponent as Ot}from"./components/user-message-selector.js";import{getAvailableThemes as _t,getAvailableThemesWithPaths as Nt,getEditorTheme as Ce,getMarkdownTheme as Kt,getThemeByName as jt,initTheme as zt,onThemeChange as Vt,setRegisteredThemes as Y,setTheme as O,setThemeInstance as Gt,stopThemeWatcher as Qt,Theme as Xt,theme as a}from"./theme/theme.js";function F(m){return typeof m=="object"&&m!==null&&"setExpanded"in m&&typeof m.setExpanded=="function"}h(F,"isExpandable");class we extends f{static{h(this,"ExpandableText")}getCollapsedText;getExpandedText;constructor(e,s,t=!1,i=0,n=0){super(t?s():e(),i,n),this.getCollapsedText=e,this.getExpandedText=s}setExpanded(e){this.setText(e?this.getExpandedText():this.getCollapsedText())}}const Jt=new Set(["EIO","EPIPE","ENOTCONN"]);function Yt(m){if(!m||typeof m!="object"||!("code"in m))return!1;const e=m.code;return e!==void 0&&Jt.has(e)}h(Yt,"isDeadTerminalError");const Se="Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";function Zt(m){return typeof m=="string"&&m.startsWith("sk-ant-oat")}h(Zt,"isAnthropicSubscriptionAuthKey");function es(m){return!!m&&m.provider==="unknown"&&m.id==="unknown"&&m.api==="unknown"}h(es,"isUnknownModel");function ts(m){return m.length>0&&!/[^a-zA-Z0-9_\-./~:@]/.test(m)?m:`'${m.replace(/'/g,"'\\''")}'`}h(ts,"quoteIfNeeded");function ss(m){const e=Math.floor((Date.now()-m.getTime())/6e4);if(e<1)return"just now";if(e<60)return`${e}m ago`;const s=Math.floor(e/60);return s<24?`${s}h ago`:`${Math.floor(s/24)}d ago`}h(ss,"formatRelativeAge");function is(m){if(!process.stdout.isTTY||!m.isPersisted())return;const e=m.getSessionFile();if(!e||!v.existsSync(e))return;const s=[z];return m.usesDefaultSessionDir()||s.push("--session-dir",ts(m.getSessionDir())),s.push("--session",m.getSessionId()),s.join(" ")}h(is,"formatResumeCommand");function ns(m){return m in ne}h(ns,"hasDefaultModelProvider");const os="amazon-bedrock",rs=new Set(Be());function as(m,e,s=rs){return ut[m]?!0:s.has(m)?!1:!e.has(m)}h(as,"isApiKeyLoginProvider");class _{static{h(this,"InteractiveMode")}runtimeHost;ui;chatContainer;pendingMessagesContainer;statusContainer;defaultEditor;editor;editorComponentFactory;autocompleteProvider;autocompleteProviderWrappers=[];fdPath;editorContainer;footer;footerDataProvider;keybindings;version;isInitialized=!1;onInputCallback;pendingUserInputs=[];loadingAnimation=void 0;workingMessage=void 0;workingVisible=!0;workingIndicatorOptions=void 0;defaultWorkingMessage="Koda is working\u2026";_workStartTs=0;_workWord="Working";_workTicker;static WORK_WORDS=["Flummoxing","Cogitating","Noodling","Percolating","Conjuring","Wrangling","Finagling","Cookifying","Flibbertigibbeting","Discombobulating","Hornswoggling","Bamboozling","Marinating","Concocting","Spelunking","Tinkering","Befuddling","Whirring","Schlepping","Razzledazzling","Confabulating","Snazzifying","Galumphing","Kerfuffling","Bedazzling","Vibing","Churning","Brewing"];defaultHiddenThinkingLabel="Thinking...";hiddenThinkingLabel=this.defaultHiddenThinkingLabel;lastSigintTime=0;lastEscapeTime=0;changelogMarkdown=void 0;startupNoticesShown=!1;anthropicSubscriptionWarningShown=!1;lastStatusSpacer=void 0;lastStatusText=void 0;streamingComponent=void 0;streamingMessage=void 0;pendingTools=new Map;toolOutputExpanded=!1;hideThinkingBlock=!1;skillCommands=new Map;unsubscribe;signalCleanupHandlers=[];isBashMode=!1;bashComponent=void 0;pendingBashComponents=[];autoCompactionLoader=void 0;autoCompactionEscapeHandler;overseerEscapeHandler;recapEscapeHandler;compactionStartMs=0;compactionReason=void 0;compactionCancelHint="";compactionTicker=void 0;_kodaTitleBase="idle";_kodaReviewing=!1;retryLoader=void 0;overseerLoader=void 0;recapLoader=void 0;updateCheckTimer=void 0;modelRefreshTimer=void 0;lastNotifiedUpdateVersion=void 0;retryCountdown=void 0;retryEscapeHandler;compactionQueuedMessages=[];shutdownRequested=!1;extensionSelector=void 0;extensionInput=void 0;extensionEditor=void 0;extensionTerminalInputUnsubscribers=new Set;extensionWidgetsAbove=new Map;extensionWidgetsBelow=new Map;widgetContainerAbove;widgetContainerBelow;customFooter=void 0;headerContainer;builtInHeader=void 0;customHeader=void 0;options;get session(){return this.runtimeHost.session}get agent(){return this.session.agent}get sessionManager(){return this.session.sessionManager}get settingsManager(){return this.session.settingsManager}constructor(e,s={}){this.runtimeHost=e,this.options=s,this.runtimeHost.setBeforeSessionInvalidate(()=>{this.resetExtensionUI()}),this.runtimeHost.setRebindSession(async()=>{await this.rebindCurrentSession()}),this.version=D,this.ui=new Ve(new je,this.settingsManager.getShowHardwareCursor()),this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink()),this.headerContainer=new A,this.chatContainer=new A,this.pendingMessagesContainer=new A,this.statusContainer=new A,this.widgetContainerAbove=new A,this.widgetContainerBelow=new A,this.keybindings=ht.create(),ze(this.keybindings);const t=this.settingsManager.getEditorPaddingX(),i=this.settingsManager.getAutocompleteMaxVisible();this.defaultEditor=new Pt(this.ui,Ce(),this.keybindings,{paddingX:t,autocompleteMaxVisible:i}),this.defaultEditor.title=this.kodaTitleText(),this.editor=this.defaultEditor,this.editorContainer=new A,this.editorContainer.addChild(this.editor),this.footerDataProvider=new rt(this.sessionManager.getCwd()),this.footer=new Dt(this.session,this.footerDataProvider),this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled),this.hideThinkingBlock=this.settingsManager.getHideThinkingBlock(),Y(this.session.resourceLoader.getThemes().themes),zt(this.settingsManager.getTheme(),!0)}getAutocompleteSourceTag(e){if(!e)return;const s=e.scope==="user"?"u":e.scope==="project"?"p":"t",t=e.source.trim();if(t==="auto"||t==="local"||t==="cli")return s;if(t.startsWith("npm:"))return`${s}:${t}`;const i=le(t);if(i){const n=i.ref?`@${i.ref}`:"";return`${s}:git:${i.host}/${i.path}${n}`}return s}prefixAutocompleteDescription(e,s){const t=this.getAutocompleteSourceTag(s);return t?e?`[${t}] ${e}`:`[${t}]`:e}getBuiltInCommandConflictDiagnostics(e){const s=new Set(ae.map(t=>t.name));return e.getRegisteredCommands().filter(t=>s.has(t.name)).map(t=>({type:"warning",message:t.invocationName===t.name?`Extension command '/${t.name}' conflicts with built-in interactive command. Skipping in autocomplete.`:`Extension command '/${t.name}' conflicts with built-in interactive command. Available as '/${t.invocationName}'.`,path:t.sourceInfo.path}))}createBaseAutocompleteProvider(){const e=ae.map(r=>({name:r.name,description:r.description})),s=e.find(r=>r.name==="model");s&&(s.getArgumentCompletions=r=>{const d=this.session.scopedModels.length>0?this.session.scopedModels.map(u=>u.model):this.session.modelRegistry.getAvailable();if(d.length===0)return null;const l=d.map(u=>({id:u.id,provider:u.provider,label:`${u.provider}/${u.id}`})),g=Oe(l,r,u=>`${u.id} ${u.provider}`);return g.length===0?null:g.map(u=>({value:u.label,label:u.id,description:u.provider}))});const t=this.session.promptTemplates.map(r=>({name:r.name,description:this.prefixAutocompleteDescription(r.description,r.sourceInfo),...r.argumentHint&&{argumentHint:r.argumentHint}})),i=new Set(e.map(r=>r.name)),n=this.session.extensionRunner.getRegisteredCommands().filter(r=>!i.has(r.name)).map(r=>({name:r.invocationName,description:this.prefixAutocompleteDescription(r.description,r.sourceInfo),getArgumentCompletions:r.getArgumentCompletions}));this.skillCommands.clear();const o=[];if(this.settingsManager.getEnableSkillCommands())for(const r of this.session.resourceLoader.getSkills().skills){const d=`skill:${r.name}`;this.skillCommands.set(d,r.filePath),o.push({name:d,description:this.prefixAutocompleteDescription(r.description,r.sourceInfo)})}return new Ue([...e,...t,...n,...o],this.sessionManager.getCwd(),this.fdPath)}setupAutocompleteProvider(){let e=this.createBaseAutocompleteProvider();for(const s of this.autocompleteProviderWrappers)e=s(e);this.autocompleteProvider=e,this.defaultEditor.setAutocompleteProvider(e),this.editor!==this.defaultEditor&&this.editor.setAutocompleteProvider?.(e)}showStartupNoticesIfNeeded(){if(!this.startupNoticesShown&&(this.startupNoticesShown=!0,!!this.changelogMarkdown)){if(this.chatContainer.children.length>0&&this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k),this.settingsManager.getCollapseChangelog()){const e=this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/),t=`Updated to v${e?e[1]:this.version}. Use ${a.bold("/changelog")} to view full changelog.`;this.chatContainer.addChild(new f(t,1,0))}else this.chatContainer.addChild(new f(a.bold(a.fg("accent","What's New")),1,0)),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new B(this.changelogMarkdown.trim(),1,0,this.getMarkdownThemeWithSettings())),this.chatContainer.addChild(new p(1));this.chatContainer.addChild(new k)}}async init(){if(this.isInitialized)return;this.registerSignalHandlers(),this.changelogMarkdown=this.getChangelogForDisplay();const[e]=await Promise.all([ce("fd"),ce("rg")]);if(this.fdPath=e,this.session.scopedModels.length>0&&(this.options.verbose||!this.settingsManager.getQuietStartup())){const s=this.session.scopedModels.map(n=>{const o=n.thinkingLevel?`:${n.thinkingLevel}`:"";return`${n.model.id}${o}`}).join(", "),t=this.keybindings.getKeys("app.model.cycleForward"),i=t.length>0?a.fg("muted",` (${ue(t.join("/"),{capitalize:!0})} to cycle)`):"";console.log(a.fg("dim",`Model scope: ${s}${i}`))}if(this.ui.addChild(this.headerContainer),this.options.verbose||!this.settingsManager.getQuietStartup()){const s=h(g=>a.bold(a.fg("accent",g)),"wm"),t=h(g=>a.fg("dim",g),"dm"),i=[t("\u250C\u2500 cli \u2500\u2500\u2510 ")+s("\u2588\u2584\u2580 \u2588\u2580\u2588 \u2588\u2580\u2584 \u2584\u2580\u2588"),t("\u2502 >_ \u2726 \u2502 ")+s("\u2588 \u2588 \u2588\u2584\u2588 \u2588\u2584\u2580 \u2588\u2580\u2588"),t("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 ")+t("by OpenAdapter"),t(`v${it}`)].join(`
|
|
1
|
+
var Fe=Object.defineProperty;var h=(m,e)=>Fe(m,"name",{value:e,configurable:!0});import*as qe from"node:crypto";import*as v from"node:fs";import*as q from"node:os";import*as E from"node:path";import{getProviders as Be}from"@openadapter/koda-ai";import{CombinedAutocompleteProvider as Ue,Container as A,fuzzyFilter as Oe,getCapabilities as _e,hyperlink as Ne,Loader as L,Markdown as B,matchesKey as Ke,ProcessTerminal as je,Spacer as p,setKeybindings as ze,Text as f,TruncatedText as K,TUI as Ve,visibleWidth as Ge}from"@openadapter/koda-tui";import Qe from"chalk";import{spawn as j,spawnSync as Xe}from"child_process";import{detectImportable as Je,importExternalSessions as Ye}from"../../cli/import-sessions.js";import{maybeRefreshOpenAdapterModels as Ze}from"../../cli/openadapter-setup.js";import{APP_NAME as z,APP_TITLE as te,getAgentDir as se,getAuthPath as ie,getDebugLogPath as et,getDocsPath as tt,getShareViewerUrl as st,KODA_VERSION as it,VERSION as D}from"../../config.js";import{parseSkillBlock as nt}from"../../core/agent-session.js";import{SessionImportFileNotFoundError as ot}from"../../core/agent-session-runtime.js";import{FooterDataProvider as rt}from"../../core/footer-data-provider.js";import{configureHttpDispatcher as V,formatHttpIdleTimeoutMs as at}from"../../core/http-dispatcher.js";import{KeybindingsManager as ht}from"../../core/keybindings.js";import{createCompactionSummaryMessage as dt}from"../../core/messages.js";import{defaultModelPerProvider as ne,findExactModelReferenceMatch as lt,resolveModelScope as oe}from"../../core/model-resolver.js";import{DefaultPackageManager as ct}from"../../core/package-manager.js";import{BUILT_IN_PROVIDER_DISPLAY_NAMES as ut}from"../../core/provider-display-names.js";import{formatMissingSessionCwdPrompt as gt,MissingSessionCwdError as re}from"../../core/session-cwd.js";import{SessionManager as W}from"../../core/session-manager.js";import{BUILTIN_SLASH_COMMANDS as ae}from"../../core/slash-commands.js";import{isInstallTelemetryEnabled as pt}from"../../core/telemetry.js";import{runBackgroundSelfUpdate as mt}from"../../utils/auto-update.js";import{getChangelogPath as he,getNewEntries as ft,parseChangelog as de}from"../../utils/changelog.js";import{copyToClipboard as Ct}from"../../utils/clipboard.js";import{extensionForImageMimeType as wt,readClipboardImage as St}from"../../utils/clipboard-image.js";import{parseGitUrl as le}from"../../utils/git.js";import{getKodaUserAgent as yt}from"../../utils/koda-user-agent.js";import{getCwdRelativePath as Et}from"../../utils/paths.js";import{killTrackedDetachedChildren as G}from"../../utils/shell.js";import{ensureTool as ce}from"../../utils/tools-manager.js";import{checkForNewPiVersion as kt}from"../../utils/version-check.js";import{ArminComponent as bt}from"./components/armin.js";import{AssistantMessageComponent as U}from"./components/assistant-message.js";import{BashExecutionComponent as Q}from"./components/bash-execution.js";import{BorderedLoader as Mt}from"./components/bordered-loader.js";import{BranchSummaryMessageComponent as xt}from"./components/branch-summary-message.js";import{CompactionSummaryMessageComponent as Tt}from"./components/compaction-summary-message.js";import{CountdownTimer as $t}from"./components/countdown-timer.js";import{CustomEditor as Pt}from"./components/custom-editor.js";import{CustomMessageComponent as At}from"./components/custom-message.js";import{DaxnutsComponent as Rt}from"./components/daxnuts.js";import{DynamicBorder as k}from"./components/dynamic-border.js";import{EarendilAnnouncementComponent as vt}from"./components/earendil-announcement.js";import{ExtensionEditorComponent as It}from"./components/extension-editor.js";import{ExtensionInputComponent as Lt}from"./components/extension-input.js";import{ExtensionSelectorComponent as X}from"./components/extension-selector.js";import{FooterComponent as Dt}from"./components/footer.js";import{formatKeyText as ue,keyDisplayText as ge,keyHint as pe,keyText as $,rawKeyHint as R}from"./components/keybinding-hints.js";import{LoginDialogComponent as J}from"./components/login-dialog.js";import{ModelSelectorComponent as Wt}from"./components/model-selector.js";import{OAuthSelectorComponent as me}from"./components/oauth-selector.js";import{ScopedModelsSelectorComponent as Ht}from"./components/scoped-models-selector.js";import{SessionSelectorComponent as Ft}from"./components/session-selector.js";import{SettingsSelectorComponent as qt}from"./components/settings-selector.js";import{SkillInvocationMessageComponent as Bt}from"./components/skill-invocation-message.js";import{ToolExecutionComponent as H}from"./components/tool-execution.js";import{TreeSelectorComponent as Ut}from"./components/tree-selector.js";import{UserMessageComponent as fe}from"./components/user-message.js";import{UserMessageSelectorComponent as Ot}from"./components/user-message-selector.js";import{getAvailableThemes as _t,getAvailableThemesWithPaths as Nt,getEditorTheme as Ce,getMarkdownTheme as Kt,getThemeByName as jt,initTheme as zt,onThemeChange as Vt,setRegisteredThemes as Y,setTheme as O,setThemeInstance as Gt,stopThemeWatcher as Qt,Theme as Xt,theme as a}from"./theme/theme.js";function F(m){return typeof m=="object"&&m!==null&&"setExpanded"in m&&typeof m.setExpanded=="function"}h(F,"isExpandable");class we extends f{static{h(this,"ExpandableText")}getCollapsedText;getExpandedText;constructor(e,s,t=!1,i=0,n=0){super(t?s():e(),i,n),this.getCollapsedText=e,this.getExpandedText=s}setExpanded(e){this.setText(e?this.getExpandedText():this.getCollapsedText())}}const Jt=new Set(["EIO","EPIPE","ENOTCONN"]);function Yt(m){if(!m||typeof m!="object"||!("code"in m))return!1;const e=m.code;return e!==void 0&&Jt.has(e)}h(Yt,"isDeadTerminalError");const Se="Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.";function Zt(m){return typeof m=="string"&&m.startsWith("sk-ant-oat")}h(Zt,"isAnthropicSubscriptionAuthKey");function es(m){return!!m&&m.provider==="unknown"&&m.id==="unknown"&&m.api==="unknown"}h(es,"isUnknownModel");function ts(m){return m.length>0&&!/[^a-zA-Z0-9_\-./~:@]/.test(m)?m:`'${m.replace(/'/g,"'\\''")}'`}h(ts,"quoteIfNeeded");function ss(m){const e=Math.floor((Date.now()-m.getTime())/6e4);if(e<1)return"just now";if(e<60)return`${e}m ago`;const s=Math.floor(e/60);return s<24?`${s}h ago`:`${Math.floor(s/24)}d ago`}h(ss,"formatRelativeAge");function is(m){if(!process.stdout.isTTY||!m.isPersisted())return;const e=m.getSessionFile();if(!e||!v.existsSync(e))return;const s=[z];return m.usesDefaultSessionDir()||s.push("--session-dir",ts(m.getSessionDir())),s.push("--session",m.getSessionId()),s.join(" ")}h(is,"formatResumeCommand");function ns(m){return m in ne}h(ns,"hasDefaultModelProvider");const os="amazon-bedrock",rs=new Set(Be());function as(m,e,s=rs){return ut[m]?!0:s.has(m)?!1:!e.has(m)}h(as,"isApiKeyLoginProvider");class _{static{h(this,"InteractiveMode")}runtimeHost;ui;chatContainer;pendingMessagesContainer;statusContainer;defaultEditor;editor;editorComponentFactory;autocompleteProvider;autocompleteProviderWrappers=[];fdPath;editorContainer;footer;footerDataProvider;keybindings;version;isInitialized=!1;onInputCallback;pendingUserInputs=[];loadingAnimation=void 0;workingMessage=void 0;workingVisible=!0;workingIndicatorOptions=void 0;defaultWorkingMessage="Koda is working\u2026";_workStartTs=0;_workWord="Working";_workTicker;static WORK_WORDS=["Flummoxing","Cogitating","Noodling","Percolating","Conjuring","Wrangling","Finagling","Cookifying","Flibbertigibbeting","Discombobulating","Hornswoggling","Bamboozling","Marinating","Concocting","Spelunking","Tinkering","Befuddling","Whirring","Schlepping","Razzledazzling","Confabulating","Snazzifying","Galumphing","Kerfuffling","Bedazzling","Vibing","Churning","Brewing"];defaultHiddenThinkingLabel="Thinking...";hiddenThinkingLabel=this.defaultHiddenThinkingLabel;lastSigintTime=0;lastEscapeTime=0;changelogMarkdown=void 0;startupNoticesShown=!1;anthropicSubscriptionWarningShown=!1;lastStatusSpacer=void 0;lastStatusText=void 0;streamingComponent=void 0;streamingMessage=void 0;pendingTools=new Map;toolOutputExpanded=!1;hideThinkingBlock=!1;skillCommands=new Map;unsubscribe;signalCleanupHandlers=[];isBashMode=!1;bashComponent=void 0;pendingBashComponents=[];autoCompactionLoader=void 0;autoCompactionEscapeHandler;overseerEscapeHandler;recapEscapeHandler;compactionStartMs=0;compactionReason=void 0;compactionCancelHint="";compactionTicker=void 0;_kodaTitleBase="idle";_kodaReviewing=!1;retryLoader=void 0;overseerLoader=void 0;recapLoader=void 0;updateCheckTimer=void 0;modelRefreshTimer=void 0;lastNotifiedUpdateVersion=void 0;retryCountdown=void 0;retryEscapeHandler;compactionQueuedMessages=[];shutdownRequested=!1;extensionSelector=void 0;extensionInput=void 0;extensionEditor=void 0;extensionTerminalInputUnsubscribers=new Set;extensionWidgetsAbove=new Map;extensionWidgetsBelow=new Map;widgetContainerAbove;widgetContainerBelow;customFooter=void 0;headerContainer;builtInHeader=void 0;customHeader=void 0;options;get session(){return this.runtimeHost.session}get agent(){return this.session.agent}get sessionManager(){return this.session.sessionManager}get settingsManager(){return this.session.settingsManager}constructor(e,s={}){this.runtimeHost=e,this.options=s,this.runtimeHost.setBeforeSessionInvalidate(()=>{this.resetExtensionUI()}),this.runtimeHost.setRebindSession(async()=>{await this.rebindCurrentSession()}),this.version=D,this.ui=new Ve(new je,this.settingsManager.getShowHardwareCursor()),this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink()),this.headerContainer=new A,this.chatContainer=new A,this.pendingMessagesContainer=new A,this.statusContainer=new A,this.widgetContainerAbove=new A,this.widgetContainerBelow=new A,this.keybindings=ht.create(),ze(this.keybindings);const t=this.settingsManager.getEditorPaddingX(),i=this.settingsManager.getAutocompleteMaxVisible();this.defaultEditor=new Pt(this.ui,Ce(),this.keybindings,{paddingX:t,autocompleteMaxVisible:i}),this.defaultEditor.title=this.kodaTitleText(),this.editor=this.defaultEditor,this.editorContainer=new A,this.editorContainer.addChild(this.editor),this.footerDataProvider=new rt(this.sessionManager.getCwd()),this.footer=new Dt(this.session,this.footerDataProvider),this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled),this.hideThinkingBlock=this.settingsManager.getHideThinkingBlock(),Y(this.session.resourceLoader.getThemes().themes),zt(this.settingsManager.getTheme(),!0)}getAutocompleteSourceTag(e){if(!e)return;const s=e.scope==="user"?"u":e.scope==="project"?"p":"t",t=e.source.trim();if(t==="auto"||t==="local"||t==="cli")return s;if(t.startsWith("npm:"))return`${s}:${t}`;const i=le(t);if(i){const n=i.ref?`@${i.ref}`:"";return`${s}:git:${i.host}/${i.path}${n}`}return s}prefixAutocompleteDescription(e,s){const t=this.getAutocompleteSourceTag(s);return t?e?`[${t}] ${e}`:`[${t}]`:e}getBuiltInCommandConflictDiagnostics(e){const s=new Set(ae.map(t=>t.name));return e.getRegisteredCommands().filter(t=>s.has(t.name)).map(t=>({type:"warning",message:t.invocationName===t.name?`Extension command '/${t.name}' conflicts with built-in interactive command. Skipping in autocomplete.`:`Extension command '/${t.name}' conflicts with built-in interactive command. Available as '/${t.invocationName}'.`,path:t.sourceInfo.path}))}createBaseAutocompleteProvider(){const e=ae.map(r=>({name:r.name,description:r.description})),s=e.find(r=>r.name==="model");s&&(s.getArgumentCompletions=r=>{const d=this.session.scopedModels.length>0?this.session.scopedModels.map(u=>u.model):this.session.modelRegistry.getAvailable();if(d.length===0)return null;const l=d.map(u=>({id:u.id,provider:u.provider,label:`${u.provider}/${u.id}`})),g=Oe(l,r,u=>`${u.id} ${u.provider}`);return g.length===0?null:g.map(u=>({value:u.label,label:u.id,description:u.provider}))});const t=this.session.promptTemplates.map(r=>({name:r.name,description:this.prefixAutocompleteDescription(r.description,r.sourceInfo),...r.argumentHint&&{argumentHint:r.argumentHint}})),i=new Set(e.map(r=>r.name)),n=this.session.extensionRunner.getRegisteredCommands().filter(r=>!i.has(r.name)).map(r=>({name:r.invocationName,description:this.prefixAutocompleteDescription(r.description,r.sourceInfo),getArgumentCompletions:r.getArgumentCompletions}));this.skillCommands.clear();const o=[];if(this.settingsManager.getEnableSkillCommands())for(const r of this.session.resourceLoader.getSkills().skills){const d=`skill:${r.name}`;this.skillCommands.set(d,r.filePath),o.push({name:d,description:this.prefixAutocompleteDescription(r.description,r.sourceInfo)})}return new Ue([...e,...t,...n,...o],this.sessionManager.getCwd(),this.fdPath)}setupAutocompleteProvider(){let e=this.createBaseAutocompleteProvider();for(const s of this.autocompleteProviderWrappers)e=s(e);this.autocompleteProvider=e,this.defaultEditor.setAutocompleteProvider(e),this.editor!==this.defaultEditor&&this.editor.setAutocompleteProvider?.(e)}showStartupNoticesIfNeeded(){if(!this.startupNoticesShown&&(this.startupNoticesShown=!0,!!this.changelogMarkdown)){if(this.chatContainer.children.length>0&&this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k),this.settingsManager.getCollapseChangelog()){const e=this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/),t=`Updated to v${e?e[1]:this.version}. Use ${a.bold("/changelog")} to view full changelog.`;this.chatContainer.addChild(new f(t,1,0))}else this.chatContainer.addChild(new f(a.bold(a.fg("accent","What's New")),1,0)),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new B(this.changelogMarkdown.trim(),1,0,this.getMarkdownThemeWithSettings())),this.chatContainer.addChild(new p(1));this.chatContainer.addChild(new k)}}async init(){if(this.isInitialized)return;this.registerSignalHandlers(),this.changelogMarkdown=this.getChangelogForDisplay();const[e]=await Promise.all([ce("fd"),ce("rg")]);if(this.fdPath=e,this.session.scopedModels.length>0&&(this.options.verbose||!this.settingsManager.getQuietStartup())){const s=this.session.scopedModels.map(n=>{const o=n.thinkingLevel?`:${n.thinkingLevel}`:"";return`${n.model.id}${o}`}).join(", "),t=this.keybindings.getKeys("app.model.cycleForward"),i=t.length>0?a.fg("muted",` (${ue(t.join("/"),{capitalize:!0})} to cycle)`):"";console.log(a.fg("dim",`Model scope: ${s}${i}`))}if(this.ui.addChild(this.headerContainer),this.options.verbose||!this.settingsManager.getQuietStartup()){const s=h(g=>a.bold(a.fg("accent",g)),"wm"),t=h(g=>a.fg("dim",g),"dm"),i=[t("\u250C\u2500 cli \u2500\u2500\u2510 ")+s("\u2588\u2584\u2580 \u2588\u2580\u2588 \u2588\u2580\u2584 \u2584\u2580\u2588"),t("\u2502 >_ \u2726 \u2502 ")+s("\u2588 \u2588 \u2588\u2584\u2588 \u2588\u2584\u2580 \u2588\u2580\u2588"),t("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 ")+t("by OpenAdapter"),t(`v${it}`)].join(`
|
|
2
2
|
`),n=h((g,u)=>pe(g,u),"hint"),o=[n("app.interrupt","to interrupt"),n("app.clear","to clear"),R(`${$("app.clear")} twice`,"to exit"),n("app.exit","to exit (empty)"),n("app.suspend","to suspend"),pe("tui.editor.deleteToLineEnd","to delete to end"),n("app.thinking.cycle","to cycle thinking level"),R(`${$("app.model.cycleForward")}/${$("app.model.cycleBackward")}`,"to cycle models"),n("app.model.select","to select model"),n("app.tools.expand","to expand tools"),n("app.thinking.toggle","to expand thinking"),n("app.editor.external","for external editor"),R("/","for commands"),R("!","to run bash"),R("!!","to run bash (no context)"),n("app.message.followUp","to queue follow-up"),n("app.message.dequeue","to edit all queued messages"),n("app.clipboard.pasteImage","to paste image"),R("drop files","to attach")].join(`
|
|
3
3
|
`),r=[n("app.interrupt","interrupt"),R(`${$("app.clear")}/${$("app.exit")}`,"clear/exit"),R("/","commands"),R("!","bash"),n("app.tools.expand","more")].join(a.fg("muted"," \xB7 ")),d=a.fg("dim",`Press ${$("app.tools.expand")} to show full startup help and loaded resources.`),l=a.fg("dim","Koda can explain its own features and look up its docs. Ask it how to use or extend Koda.");this.builtInHeader=new we(()=>`${i}
|
|
4
4
|
${r}
|
|
@@ -7,23 +7,23 @@ ${d}
|
|
|
7
7
|
${l}`,()=>`${i}
|
|
8
8
|
${o}
|
|
9
9
|
|
|
10
|
-
${l}`,this.getStartupExpansionState(),1,0),this.headerContainer.addChild(new p(1)),this.headerContainer.addChild(this.builtInHeader),this.headerContainer.addChild(new p(1))}else this.builtInHeader=new f("",0,0),this.headerContainer.addChild(this.builtInHeader);this.ui.addChild(this.chatContainer),this.ui.addChild(this.pendingMessagesContainer),this.ui.addChild(this.statusContainer),this.renderWidgets(),this.ui.addChild(this.widgetContainerAbove),this.ui.addChild(this.editorContainer),this.ui.addChild(this.widgetContainerBelow),this.ui.addChild(this.footer),this.ui.setFocus(this.editor),this.setupKeyHandlers(),this.setupEditorSubmitHandler(),this.ui.start(),this.isInitialized=!0,await this.rebindCurrentSession(),this.renderInitialMessages(),Vt(()=>{this.ui.invalidate(),this.updateEditorBorderColor(),this.ui.requestRender()}),this.footerDataProvider.onBranchChange(()=>{this.ui.requestRender()}),await this.updateAvailableProviderCount()}updateTerminalTitle(){const e=
|
|
10
|
+
${l}`,this.getStartupExpansionState(),1,0),this.headerContainer.addChild(new p(1)),this.headerContainer.addChild(this.builtInHeader),this.headerContainer.addChild(new p(1))}else this.builtInHeader=new f("",0,0),this.headerContainer.addChild(this.builtInHeader);this.ui.addChild(this.chatContainer),this.ui.addChild(this.pendingMessagesContainer),this.ui.addChild(this.statusContainer),this.renderWidgets(),this.ui.addChild(this.widgetContainerAbove),this.ui.addChild(this.editorContainer),this.ui.addChild(this.widgetContainerBelow),this.ui.addChild(this.footer),this.ui.setFocus(this.editor),this.setupKeyHandlers(),this.setupEditorSubmitHandler(),this.ui.start(),this.isInitialized=!0,await this.rebindCurrentSession(),this.renderInitialMessages(),Vt(()=>{this.ui.invalidate(),this.updateEditorBorderColor(),this.ui.requestRender()}),this.footerDataProvider.onBranchChange(()=>{this.ui.requestRender()}),await this.updateAvailableProviderCount()}updateTerminalTitle(){const e=E.basename(this.sessionManager.getCwd()),s=this.sessionManager.getSessionName();s?this.ui.terminal.setTitle(`${te} - ${s} - ${e}`):this.ui.terminal.setTitle(`${te} - ${e}`)}async run(){await this.init(),this._checkForUpdate(),this.updateCheckTimer=setInterval(()=>this._checkForUpdate(),360*60*1e3),this.updateCheckTimer.unref?.(),this.modelRefreshTimer=setInterval(()=>{this._refreshModelsWhenIdle()},1800*1e3),this.modelRefreshTimer.unref?.(),this.checkForPackageUpdates().then(r=>{r.length>0&&this.showPackageUpdateNotification(r)}),this.showResumeHintForCwd(),this.checkTmuxKeyboardSetup().then(r=>{r&&this.showWarning(r)});const{migratedProviders:e,modelFallbackMessage:s,initialMessage:t,initialImages:i,initialMessages:n}=this.options;e&&e.length>0&&this.showWarning(`Migrated credentials to auth.json: ${e.join(", ")}`);const o=this.session.modelRegistry.getError();if(o&&this.showError(`models.json error: ${o}`),s&&this.showWarning(s),this.maybeWarnAboutAnthropicSubscriptionAuth(),t)try{await this.session.prompt(t,{images:i})}catch(r){const d=r instanceof Error?r.message:"Unknown error occurred";this.showError(d)}if(n)for(const r of n)try{await this.session.prompt(r)}catch(d){const l=d instanceof Error?d.message:"Unknown error occurred";this.showError(l)}for(;;){const r=await this.getUserInput();try{await this.session.prompt(r)}catch(d){const l=d instanceof Error?d.message:"Unknown error occurred";this.showError(l)}finally{this.updateKodaTitle()}}}async checkForPackageUpdates(){if(process.env.KODA_OFFLINE)return[];try{return(await new ct({cwd:this.sessionManager.getCwd(),agentDir:se(),settingsManager:this.settingsManager}).checkForAvailableUpdates()).map(t=>t.displayName)}catch{return[]}}async checkTmuxKeyboardSetup(){if(!process.env.TMUX)return;const e=h(i=>new Promise(n=>{const o=j("tmux",["show","-gv",i],{stdio:["ignore","pipe","ignore"]});let r="";const d=setTimeout(()=>{o.kill(),n(void 0)},2e3);o.stdout?.on("data",l=>{r+=l.toString()}),o.on("error",()=>{clearTimeout(d),n(void 0)}),o.on("close",l=>{clearTimeout(d),n(l===0?r.trim():void 0)})}),"runTmuxShow"),[s,t]=await Promise.all([e("extended-keys"),e("extended-keys-format")]);if(s!==void 0){if(s!=="on"&&s!=="always")return"tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";if(t==="xterm")return"tmux extended-keys-format is xterm. Koda works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux."}}getChangelogForDisplay(){if(this.session.state.messages.length>0)return;const e=this.settingsManager.getLastChangelogVersion(),s=he(),t=de(s);if(!e){this.settingsManager.setLastChangelogVersion(D),this.reportInstallTelemetry(D);return}const i=ft(t,e);if(i.length>0)return this.settingsManager.setLastChangelogVersion(D),this.reportInstallTelemetry(D),i.map(n=>n.content).join(`
|
|
11
11
|
|
|
12
|
-
`)}reportInstallTelemetry(e){process.env.KODA_OFFLINE||pt(this.settingsManager)&&fetch(`https://openadapter.in/api/report-install?version=${encodeURIComponent(e)}`,{headers:{"User-Agent":
|
|
13
|
-
${r} ${a.fg("muted",`${i}%`)} ${a.fg("dim",this.compactionCancelHint)}`}kodaTitleText(){const e=a,s=process.env.KODA_YOLO==="1";return this._kodaReviewing?e.bold(e.fg("accent","koda"))+e.fg("muted"," \xB7 reviewing\u2026"):this._kodaTitleBase==="beefed"?e.bold(e.fg("error","KODA \u2191 BEEFED UP")):this._kodaTitleBase==="rerouted"?e.bold(e.fg("warning","koda \xBB REROUTED")):s?e.bold(e.fg("error","koda \xB7 AUTONOMOUS MODE")):e.bold(e.fg("accent","koda"))}updateKodaTitle(){this.defaultEditor.title=this.kodaTitleText(),this.ui.requestRender()}getCompactPathLabel(e,s){const t=this.getShortPath(e,s),n=t.replace(/\\/g,"/").split("/").filter(o=>o.length>0&&o!=="~");return n.length>0?n[n.length-1]:t}getCompactPackageSourceLabel(e){const s=e?.source??"";if(s.startsWith("npm:"))return s.slice(4)||s;const t=le(s);return t&&t.path||s}getCompactExtensionLabel(e,s){if(!this.isPackageSource(s))return this.getCompactPathLabel(e,s);const t=this.getCompactPackageSourceLabel(s);if(!t)return this.getCompactPathLabel(e,s);const i=this.getShortPath(e,s).replace(/\\/g,"/"),n=i.startsWith("extensions/")?i.slice(11):i,o=
|
|
12
|
+
`)}reportInstallTelemetry(e){process.env.KODA_OFFLINE||pt(this.settingsManager)&&fetch(`https://openadapter.in/api/report-install?version=${encodeURIComponent(e)}`,{headers:{"User-Agent":yt(e)},signal:AbortSignal.timeout(5e3)}).then(()=>{}).catch(()=>{})}getMarkdownThemeWithSettings(){return{...Kt(),codeBlockIndent:this.settingsManager.getCodeBlockIndent()}}formatDisplayPath(e){const s=q.homedir();let t=e;return t.startsWith(s)&&(t=`~${t.slice(s.length)}`),t}formatExtensionDisplayPath(e){let s=this.formatDisplayPath(e);return s=s.replace(/\/index\.ts$/,"").replace(/\/index\.js$/,""),s}formatContextPath(e){const s=E.resolve(this.sessionManager.getCwd()),t=E.isAbsolute(e)?E.resolve(e):E.resolve(s,e),i=Et(t,s);return i!==void 0?i:this.formatDisplayPath(t)}getStartupExpansionState(){return this.options.verbose||this.toolOutputExpanded}getShortPath(e,s){const t=s?.baseDir;if(t&&this.isPackageSource(s)){const r=E.relative(E.resolve(t),E.resolve(e));if(r&&r!=="."&&!r.startsWith("..")&&!r.startsWith(`..${E.sep}`)&&!E.isAbsolute(r))return r.replace(/\\/g,"/")}const i=s?.source??"",n=e.match(/node_modules\/(@?[^/]+(?:\/[^/]+)?)\/(.*)/);if(n&&i.startsWith("npm:"))return n[2];const o=e.match(/git\/[^/]+\/[^/]+\/(.*)/);return o&&i.startsWith("git:")?o[1]:this.formatDisplayPath(e)}formatCompactElapsed(e){const s=Math.max(0,Math.floor(e/1e3)),t=Math.floor(s/60);return t>0?`${t}m ${s%60}s`:`${s}s`}buildCompactionLabel(){const e=this.compactionStartMs?Date.now()-this.compactionStartMs:0,s=this.formatCompactElapsed(e),t=e/1e3,i=Math.min(95,Math.round(95*(1-Math.exp(-t/11)))),n=24,o=Math.max(0,Math.min(n,Math.round(i/100*n))),r=a.fg("accent","\u25B0".repeat(o))+a.fg("dim","\u25B1".repeat(n-o)),d=this.compactionReason==="overflow"?"Context overflow \u2014 compacting":this.compactionReason==="manual"?"Compacting context":"Auto-compacting context";return`${a.fg("muted",`${d} (${s})`)}
|
|
13
|
+
${r} ${a.fg("muted",`${i}%`)} ${a.fg("dim",this.compactionCancelHint)}`}kodaTitleText(){const e=a,s=process.env.KODA_YOLO==="1";return this._kodaReviewing?e.bold(e.fg("accent","koda"))+e.fg("muted"," \xB7 reviewing\u2026"):this._kodaTitleBase==="beefed"?e.bold(e.fg("error","KODA \u2191 BEEFED UP")):this._kodaTitleBase==="rerouted"?e.bold(e.fg("warning","koda \xBB REROUTED")):s?e.bold(e.fg("error","koda \xB7 AUTONOMOUS MODE")):e.bold(e.fg("accent","koda"))}updateKodaTitle(){this.defaultEditor.title=this.kodaTitleText(),this.ui.requestRender()}getCompactPathLabel(e,s){const t=this.getShortPath(e,s),n=t.replace(/\\/g,"/").split("/").filter(o=>o.length>0&&o!=="~");return n.length>0?n[n.length-1]:t}getCompactPackageSourceLabel(e){const s=e?.source??"";if(s.startsWith("npm:"))return s.slice(4)||s;const t=le(s);return t&&t.path||s}getCompactExtensionLabel(e,s){if(!this.isPackageSource(s))return this.getCompactPathLabel(e,s);const t=this.getCompactPackageSourceLabel(s);if(!t)return this.getCompactPathLabel(e,s);const i=this.getShortPath(e,s).replace(/\\/g,"/"),n=i.startsWith("extensions/")?i.slice(11):i,o=E.posix.parse(n);return o.name==="index"?!o.dir||o.dir==="."?t:`${t}:${o.dir}`:`${t}:${n}`}getCompactDisplayPathSegments(e){return this.formatDisplayPath(e).replace(/\\/g,"/").split("/").filter(s=>s.length>0&&s!=="~")}getCompactNonPackageExtensionLabel(e,s,t){const i=t[s]?.segments;if(!i||i.length===0)return this.getCompactPathLabel(e);for(let n=1;n<=i.length;n+=1){const o=i.slice(-n).join("/");if(t.every((d,l)=>l===s?!0:d.segments.slice(-n).join("/")!==o))return o}return i.join("/")}getCompactExtensionLabels(e){const s=e.map(t=>{const i=this.getCompactDisplayPathSegments(t.path),n=i[i.length-1];return i.length>1&&(n==="index.ts"||n==="index.js")&&i.pop(),{path:t.path,sourceInfo:t.sourceInfo,segments:i}}).filter(t=>!this.isPackageSource(t.sourceInfo));return e.map(t=>{if(this.isPackageSource(t.sourceInfo))return this.getCompactExtensionLabel(t.path,t.sourceInfo);const i=s.findIndex(n=>n.path===t.path);return i===-1?this.getCompactPathLabel(t.path,t.sourceInfo):this.getCompactNonPackageExtensionLabel(t.path,i,s)})}getDisplaySourceInfo(e){const s=e?.source??"local",t=e?.scope??"project";return s==="local"?t==="user"?{label:"user",color:"muted"}:t==="project"?{label:"project",color:"muted"}:t==="temporary"?{label:"path",scopeLabel:"temp",color:"muted"}:{label:"path",color:"muted"}:s==="cli"?{label:"path",scopeLabel:t==="temporary"?"temp":void 0,color:"muted"}:{label:s,scopeLabel:t==="user"?"user":t==="project"?"project":t==="temporary"?"temp":void 0,color:"accent"}}getScopeGroup(e){const s=e?.source??"local",t=e?.scope??"project";return s==="cli"||t==="temporary"?"path":t==="user"?"user":t==="project"?"project":"path"}isPackageSource(e){const s=e?.source??"";return s.startsWith("npm:")||s.startsWith("git:")}buildScopeGroups(e){const s={user:{scope:"user",paths:[],packages:new Map},project:{scope:"project",paths:[],packages:new Map},path:{scope:"path",paths:[],packages:new Map}};for(const t of e){const i=this.getScopeGroup(t.sourceInfo),n=s[i],o=t.sourceInfo?.source??"local";if(this.isPackageSource(t.sourceInfo)){const r=n.packages.get(o)??[];r.push(t),n.packages.set(o,r)}else n.paths.push(t)}return[s.project,s.user,s.path].filter(t=>t.paths.length>0||t.packages.size>0)}formatScopeGroups(e,s){const t=[];for(const i of e){t.push(` ${a.fg("accent",i.scope)}`);const n=[...i.paths].sort((r,d)=>r.path.localeCompare(d.path));for(const r of n)t.push(a.fg("dim",` ${s.formatPath(r)}`));const o=Array.from(i.packages.entries()).sort(([r],[d])=>r.localeCompare(d));for(const[r,d]of o){t.push(` ${a.fg("mdLink",r)}`);const l=[...d].sort((g,u)=>g.path.localeCompare(u.path));for(const g of l)t.push(a.fg("dim",` ${s.formatPackagePath(g,r)}`))}}return t.join(`
|
|
14
14
|
`)}findSourceInfoForPath(e,s){const t=s.get(e);if(t)return t;let i=e;for(;i.includes("/");){i=i.substring(0,i.lastIndexOf("/"));const n=s.get(i);if(n)return n}}formatPathWithSource(e,s){if(s){const t=this.getShortPath(e,s),{label:i,scopeLabel:n}=this.getDisplaySourceInfo(s);return`${n?`${i} (${n})`:i} ${t}`}return this.formatDisplayPath(e)}formatDiagnostics(e,s){const t=[],i=new Map,n=[];for(const o of e)if(o.type==="collision"&&o.collision){const r=i.get(o.collision.name)??[];r.push(o),i.set(o.collision.name,r)}else n.push(o);for(const[o,r]of i){const d=r[0]?.collision;if(d){t.push(a.fg("warning",` "${o}" collision:`)),t.push(a.fg("dim",` ${a.fg("success","\u2713")} ${this.formatPathWithSource(d.winnerPath,this.findSourceInfoForPath(d.winnerPath,s))}`));for(const l of r)l.collision&&t.push(a.fg("dim",` ${a.fg("warning","\u2717")} ${this.formatPathWithSource(l.collision.loserPath,this.findSourceInfoForPath(l.collision.loserPath,s))} (skipped)`))}}for(const o of n)if(o.path){const r=this.formatPathWithSource(o.path,this.findSourceInfoForPath(o.path,s));t.push(a.fg(o.type==="error"?"error":"warning",` ${r}`)),t.push(a.fg(o.type==="error"?"error":"warning",` ${o.message}`))}else t.push(a.fg(o.type==="error"?"error":"warning",` ${o.message}`));return t.join(`
|
|
15
|
-
`)}showLoadedResources(e){const s=e?.force||this.options.verbose||!this.settingsManager.getQuietStartup(),t=s||e?.showDiagnosticsWhenQuiet===!0;if(!s&&!t)return;const i=h((c,
|
|
16
|
-
${
|
|
15
|
+
`)}showLoadedResources(e){const s=e?.force||this.options.verbose||!this.settingsManager.getQuietStartup(),t=s||e?.showDiagnosticsWhenQuiet===!0;if(!s&&!t)return;const i=h((c,y="mdHeading")=>a.fg(y,`[${c}]`),"sectionHeader"),n=h((c,y)=>{const S=c.map(b=>b.trim()).filter(b=>b.length>0);return y?.sort!==!1&&S.sort((b,P)=>b.localeCompare(P)),a.fg("dim",` ${S.join(", ")}`)},"formatCompactList"),o=h((c,y,S=y,b="mdHeading")=>{const P=new we(()=>`${i(c,b)}
|
|
16
|
+
${y}`,()=>`${i(c,b)}
|
|
17
17
|
${S}`,this.getStartupExpansionState(),0,0);this.chatContainer.addChild(P),this.chatContainer.addChild(new p(1))},"addLoadedSection"),r=this.session.resourceLoader.getSkills(),d=this.session.resourceLoader.getPrompts(),l=this.session.resourceLoader.getThemes(),g=e?.extensions??this.session.resourceLoader.getExtensions().extensions.map(c=>({path:c.path,sourceInfo:c.sourceInfo})),u=new Map;for(const c of g)c.sourceInfo&&u.set(c.path,c.sourceInfo);for(const c of r.skills)c.sourceInfo&&u.set(c.filePath,c.sourceInfo);for(const c of d.prompts)c.sourceInfo&&u.set(c.filePath,c.sourceInfo);for(const c of l.themes)c.sourcePath&&c.sourceInfo&&u.set(c.sourcePath,c.sourceInfo);if(s){const c=this.session.resourceLoader.getAgentsFiles().agentsFiles;if(c.length>0){this.chatContainer.addChild(new p(1));const M=c.map(w=>a.fg("dim",` ${this.formatDisplayPath(w.path)}`)).join(`
|
|
18
|
-
`),x=n(c.map(w=>this.formatContextPath(w.path)),{sort:!1});o("Context",x,M)}const
|
|
19
|
-
${w}`,0,0)),this.chatContainer.addChild(new p(1))}const
|
|
18
|
+
`),x=n(c.map(w=>this.formatContextPath(w.path)),{sort:!1});o("Context",x,M)}const y=r.skills;if(y.length>0){const M=this.buildScopeGroups(y.map(C=>({path:C.filePath,sourceInfo:C.sourceInfo}))),x=this.formatScopeGroups(M,{formatPath:h(C=>this.formatDisplayPath(C.path),"formatPath"),formatPackagePath:h(C=>this.getShortPath(C.path,C.sourceInfo),"formatPackagePath")}),w=n(y.map(C=>C.name));o("Skills",w,x)}const S=this.session.promptTemplates;if(S.length>0){const M=this.buildScopeGroups(S.map(T=>({path:T.filePath,sourceInfo:T.sourceInfo}))),x=new Map(S.map(T=>[T.filePath,T])),w=this.formatScopeGroups(M,{formatPath:h(T=>{const I=x.get(T.path);return I?`/${I.name}`:this.formatDisplayPath(T.path)},"formatPath"),formatPackagePath:h(T=>{const I=x.get(T.path);return I?`/${I.name}`:this.formatDisplayPath(T.path)},"formatPackagePath")}),C=n(S.map(T=>`/${T.name}`));o("Prompts",C,w)}if(g.length>0){const M=this.buildScopeGroups(g),x=this.formatScopeGroups(M,{formatPath:h(C=>this.formatExtensionDisplayPath(C.path),"formatPath"),formatPackagePath:h(C=>this.formatExtensionDisplayPath(this.getShortPath(C.path,C.sourceInfo)),"formatPackagePath")}),w=n(this.getCompactExtensionLabels(g));o("Extensions",w,x,"mdHeading")}const P=l.themes.filter(M=>M.sourcePath);if(P.length>0){const M=this.buildScopeGroups(P.map(C=>({path:C.sourcePath,sourceInfo:C.sourceInfo}))),x=this.formatScopeGroups(M,{formatPath:h(C=>this.formatDisplayPath(C.path),"formatPath"),formatPackagePath:h(C=>this.getShortPath(C.path,C.sourceInfo),"formatPackagePath")}),w=n(P.map(C=>C.name??this.getCompactPathLabel(C.sourcePath,C.sourceInfo)));o("Themes",w,x)}}if(t){const c=r.diagnostics;if(c.length>0){const w=this.formatDiagnostics(c,u);this.chatContainer.addChild(new f(`${a.fg("warning","[Skill conflicts]")}
|
|
19
|
+
${w}`,0,0)),this.chatContainer.addChild(new p(1))}const y=d.diagnostics;if(y.length>0){const w=this.formatDiagnostics(y,u);this.chatContainer.addChild(new f(`${a.fg("warning","[Prompt conflicts]")}
|
|
20
20
|
${w}`,0,0)),this.chatContainer.addChild(new p(1))}const S=[],b=this.session.resourceLoader.getExtensions().errors;if(b.length>0)for(const w of b)S.push({type:"error",message:w.error,path:w.path});const P=this.session.extensionRunner.getCommandDiagnostics();S.push(...P),S.push(...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner));const M=this.session.extensionRunner.getShortcutDiagnostics();if(S.push(...M),S.length>0){const w=this.formatDiagnostics(S,u);this.chatContainer.addChild(new f(`${a.fg("warning","[Extension issues]")}
|
|
21
21
|
${w}`,0,0)),this.chatContainer.addChild(new p(1))}const x=l.diagnostics;if(x.length>0){const w=this.formatDiagnostics(x,u);this.chatContainer.addChild(new f(`${a.fg("warning","[Theme conflicts]")}
|
|
22
22
|
${w}`,0,0)),this.chatContainer.addChild(new p(1))}}}async bindCurrentSessionExtensions(){const e=this.createExtensionUIContext();await this.session.bindExtensions({uiContext:e,mode:"tui",abortHandler:h(()=>{this.restoreQueuedMessagesToEditor({abort:!0})},"abortHandler"),commandContextActions:{waitForIdle:h(()=>this.session.agent.waitForIdle(),"waitForIdle"),newSession:h(async t=>{this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear();try{const i=await this.runtimeHost.newSession(t);return i.cancelled||(this.renderCurrentSessionState(),this.ui.requestRender()),i}catch(i){return this.handleFatalRuntimeError("Failed to create session",i)}},"newSession"),fork:h(async(t,i)=>{try{const n=await this.runtimeHost.fork(t,i);return n.cancelled||(this.renderCurrentSessionState(),this.editor.setText(n.selectedText??""),this.showStatus("Forked to new session")),{cancelled:n.cancelled}}catch(n){return this.handleFatalRuntimeError("Failed to fork session",n)}},"fork"),navigateTree:h(async(t,i)=>{const n=await this.session.navigateTree(t,{summarize:i?.summarize,customInstructions:i?.customInstructions,replaceInstructions:i?.replaceInstructions,label:i?.label});return n.cancelled?{cancelled:!0}:(this.chatContainer.clear(),this.renderInitialMessages(),n.editorText&&!this.editor.getText().trim()&&this.editor.setText(n.editorText),this.showStatus("Navigated to selected point"),this.flushCompactionQueue({willRetry:!1}),{cancelled:!1})},"navigateTree"),switchSession:h(async(t,i)=>this.handleResumeSession(t,i),"switchSession"),reload:h(async()=>{await this.handleReloadCommand()},"reload")},shutdownHandler:h(()=>{this.shutdownRequested=!0,this.session.isStreaming||this.shutdown()},"shutdownHandler"),onError:h(t=>{this.showExtensionError(t.extensionPath,t.error,t.stack)},"onError")}),Y(this.session.resourceLoader.getThemes().themes),this.setupAutocompleteProvider();const s=this.session.extensionRunner;this.setupExtensionShortcuts(s),this.showLoadedResources({force:!1,showDiagnosticsWhenQuiet:!0}),this.showStartupNoticesIfNeeded()}applyRuntimeSettings(){V(this.settingsManager.getHttpIdleTimeoutMs()),this.footer.setSession(this.session),this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled),this.footerDataProvider.setCwd(this.sessionManager.getCwd()),this.hideThinkingBlock=this.settingsManager.getHideThinkingBlock(),this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor()),this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());const e=this.settingsManager.getEditorPaddingX(),s=this.settingsManager.getAutocompleteMaxVisible();this.defaultEditor.setPaddingX(e),this.defaultEditor.setAutocompleteMaxVisible(s),this.editor!==this.defaultEditor&&(this.editor.setPaddingX?.(e),this.editor.setAutocompleteMaxVisible?.(s))}async rebindCurrentSession(){this.unsubscribe?.(),this.unsubscribe=void 0,this.applyRuntimeSettings(),await this.bindCurrentSessionExtensions(),this.subscribeToAgent(),await this.updateAvailableProviderCount(),this.updateEditorBorderColor(),this.updateTerminalTitle()}async handleFatalRuntimeError(e,s){const t=s instanceof Error?s.message:String(s);this.showError(`${e}: ${t}`),Qt(),this.stop(),process.exit(1)}renderCurrentSessionState(){this.chatContainer.clear(),this.pendingMessagesContainer.clear(),this.compactionQueuedMessages=[],this.streamingComponent=void 0,this.streamingMessage=void 0,this.pendingTools.clear(),this.renderInitialMessages()}getRegisteredToolDefinition(e){return this.session.getToolDefinition(e)}setupExtensionShortcuts(e){const s=e.getShortcuts(this.keybindings.getEffectiveConfig());if(s.size===0)return;const t=h(()=>({ui:this.createExtensionUIContext(),mode:"tui",hasUI:!0,cwd:this.sessionManager.getCwd(),sessionManager:this.sessionManager,modelRegistry:this.session.modelRegistry,model:this.session.model,isIdle:h(()=>!this.session.isStreaming,"isIdle"),signal:this.session.agent.signal,abort:h(()=>{this.restoreQueuedMessagesToEditor({abort:!0})},"abort"),hasPendingMessages:h(()=>this.session.pendingMessageCount>0,"hasPendingMessages"),shutdown:h(()=>{this.shutdownRequested=!0},"shutdown"),getContextUsage:h(()=>this.session.getContextUsage(),"getContextUsage"),compact:h(i=>{(async()=>{try{const n=await this.session.compact(i?.customInstructions);i?.onComplete?.(n)}catch(n){const o=n instanceof Error?n:new Error(String(n));i?.onError?.(o)}})()},"compact"),getSystemPrompt:h(()=>this.session.systemPrompt,"getSystemPrompt")}),"createContext");this.defaultEditor.onExtensionShortcut=i=>{for(const[n,o]of s)if(Ke(i,n))return Promise.resolve(o.handler(t())).catch(r=>{this.showError(`Shortcut handler error: ${r instanceof Error?r.message:String(r)}`)}),!0;return!1}}setExtensionStatus(e,s){this.footerDataProvider.setExtensionStatus(e,s),this.ui.requestRender()}getWorkingLoaderMessage(){return this.workingMessage??this._workStatusText()}_pickWorkWord(){const e=_.WORK_WORDS;return e[Math.floor(Math.random()*e.length)]??"Working"}_workStatusText(){this._workStartTs||(this._workStartTs=Date.now());const e=Math.max(0,Math.floor((Date.now()-this._workStartTs)/1e3)),s=Math.floor(e/60),t=s>0?`${s}m ${e%60}s`:`${e}s`;let i="";try{const n=this.session.getContextUsage?.()?.tokens;n&&n>0&&(i=` \xB7 \u2191 ${n>=1e6?`${(n/1e6).toFixed(1)}M`:n>=1e3?`${(n/1e3).toFixed(1)}k`:String(n)} tokens`)}catch{}return`${this._workWord}\u2026 (${t}${i})`}_startWorkTicker(){this._clearWorkTicker(),!this.workingMessage&&(this._workTicker=setInterval(()=>{if(!this.loadingAnimation){this._clearWorkTicker();return}const e=Math.floor((Date.now()-this._workStartTs)/1e3);e>0&&e%10===0&&(this._workWord=this._pickWorkWord()),this.loadingAnimation.setMessage(this._workStatusText()),this.ui.requestRender()},1e3))}_clearWorkTicker(){this._workTicker&&(clearInterval(this._workTicker),this._workTicker=void 0)}createWorkingLoader(){this._workStartTs=Date.now(),this._workWord=this._pickWorkWord();const e=new L(this.ui,s=>a.fg("accent",s),s=>a.fg("muted",s),this.getWorkingLoaderMessage(),this.workingIndicatorOptions);return this._startWorkTicker(),e}stopWorkingLoader(){this._clearWorkTicker(),this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear()}setWorkingVisible(e){if(this.workingVisible=e,!e){this.stopWorkingLoader(),this.ui.requestRender();return}this.session.isStreaming&&!this.loadingAnimation&&(this.statusContainer.clear(),this.loadingAnimation=this.createWorkingLoader(),this.statusContainer.addChild(this.loadingAnimation)),this.ui.requestRender()}setWorkingIndicator(e){this.workingIndicatorOptions=e,this.loadingAnimation?.setIndicator(e),this.ui.requestRender()}setHiddenThinkingLabel(e){this.hiddenThinkingLabel=e??this.defaultHiddenThinkingLabel;for(const s of this.chatContainer.children)s instanceof U&&s.setHiddenThinkingLabel(this.hiddenThinkingLabel);this.streamingComponent&&this.streamingComponent.setHiddenThinkingLabel(this.hiddenThinkingLabel),this.ui.requestRender()}setExtensionWidget(e,s,t){const i=t?.placement??"aboveEditor",n=h(d=>{const l=d.get(e);l?.dispose&&l.dispose(),d.delete(e)},"removeExisting");if(n(this.extensionWidgetsAbove),n(this.extensionWidgetsBelow),s===void 0){this.renderWidgets();return}let o;if(Array.isArray(s)){const d=new A;for(const l of s.slice(0,_.MAX_WIDGET_LINES))d.addChild(new f(l,1,0));s.length>_.MAX_WIDGET_LINES&&d.addChild(new f(a.fg("muted","... (widget truncated)"),1,0)),o=d}else o=s(this.ui,a);(i==="belowEditor"?this.extensionWidgetsBelow:this.extensionWidgetsAbove).set(e,o),this.renderWidgets()}clearExtensionWidgets(){for(const e of this.extensionWidgetsAbove.values())e.dispose?.();for(const e of this.extensionWidgetsBelow.values())e.dispose?.();this.extensionWidgetsAbove.clear(),this.extensionWidgetsBelow.clear(),this.renderWidgets()}resetExtensionUI(){this.extensionSelector&&this.hideExtensionSelector(),this.extensionInput&&this.hideExtensionInput(),this.extensionEditor&&this.hideExtensionEditor(),this.ui.hideOverlay(),this.clearExtensionTerminalInputListeners(),this.setExtensionFooter(void 0),this.setExtensionHeader(void 0),this.clearExtensionWidgets(),this.footerDataProvider.clearExtensionStatuses(),this.footer.invalidate(),this.autocompleteProviderWrappers=[],this.setCustomEditorComponent(void 0),this.setupAutocompleteProvider(),this.defaultEditor.onExtensionShortcut=void 0,this.updateTerminalTitle(),this.workingMessage=void 0,this.workingVisible=!0,this.setWorkingIndicator(),this.loadingAnimation&&this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${$("app.interrupt")} to interrupt)`),this.setHiddenThinkingLabel()}static MAX_WIDGET_LINES=10;renderWidgets(){!this.widgetContainerAbove||!this.widgetContainerBelow||(this.renderWidgetContainer(this.widgetContainerAbove,this.extensionWidgetsAbove,!0,!0),this.renderWidgetContainer(this.widgetContainerBelow,this.extensionWidgetsBelow,!1,!1),this.ui.requestRender())}renderWidgetContainer(e,s,t,i){if(e.clear(),s.size===0){t&&e.addChild(new p(1));return}i&&e.addChild(new p(1));for(const n of s.values())e.addChild(n)}setExtensionFooter(e){this.customFooter?.dispose&&this.customFooter.dispose(),this.customFooter?this.ui.removeChild(this.customFooter):this.ui.removeChild(this.footer),e?(this.customFooter=e(this.ui,a,this.footerDataProvider),this.ui.addChild(this.customFooter)):(this.customFooter=void 0,this.ui.addChild(this.footer)),this.ui.requestRender()}setExtensionHeader(e){if(!this.builtInHeader)return;this.customHeader?.dispose&&this.customHeader.dispose();const s=this.customHeader||this.builtInHeader,t=this.headerContainer.children.indexOf(s);e?(this.customHeader=e(this.ui,a),F(this.customHeader)&&this.customHeader.setExpanded(this.toolOutputExpanded),t!==-1?this.headerContainer.children[t]=this.customHeader:this.headerContainer.children.unshift(this.customHeader)):(this.customHeader=void 0,F(this.builtInHeader)&&this.builtInHeader.setExpanded(this.toolOutputExpanded),t!==-1&&(this.headerContainer.children[t]=this.builtInHeader)),this.ui.requestRender()}addExtensionTerminalInputListener(e){const s=this.ui.addInputListener(e);return this.extensionTerminalInputUnsubscribers.add(s),()=>{s(),this.extensionTerminalInputUnsubscribers.delete(s)}}clearExtensionTerminalInputListeners(){for(const e of this.extensionTerminalInputUnsubscribers)e();this.extensionTerminalInputUnsubscribers.clear()}createExtensionUIContext(){return{select:h((e,s,t)=>this.showExtensionSelector(e,s,t),"select"),confirm:h((e,s,t)=>this.showExtensionConfirm(e,s,t),"confirm"),input:h((e,s,t)=>this.showExtensionInput(e,s,t),"input"),notify:h((e,s)=>this.showExtensionNotify(e,s),"notify"),onTerminalInput:h(e=>this.addExtensionTerminalInputListener(e),"onTerminalInput"),setStatus:h((e,s)=>this.setExtensionStatus(e,s),"setStatus"),setWorkingMessage:h(e=>{this.workingMessage=e,this.loadingAnimation&&this.loadingAnimation.setMessage(e??this.defaultWorkingMessage)},"setWorkingMessage"),setWorkingVisible:h(e=>this.setWorkingVisible(e),"setWorkingVisible"),setWorkingIndicator:h(e=>this.setWorkingIndicator(e),"setWorkingIndicator"),setHiddenThinkingLabel:h(e=>this.setHiddenThinkingLabel(e),"setHiddenThinkingLabel"),setWidget:h((e,s,t)=>this.setExtensionWidget(e,s,t),"setWidget"),setFooter:h(e=>this.setExtensionFooter(e),"setFooter"),setHeader:h(e=>this.setExtensionHeader(e),"setHeader"),setTitle:h(e=>this.ui.terminal.setTitle(e),"setTitle"),custom:h((e,s)=>this.showExtensionCustom(e,s),"custom"),pasteToEditor:h(e=>this.editor.handleInput(`\x1B[200~${e}\x1B[201~`),"pasteToEditor"),setEditorText:h(e=>this.editor.setText(e),"setEditorText"),getEditorText:h(()=>this.editor.getExpandedText?.()??this.editor.getText(),"getEditorText"),editor:h((e,s)=>this.showExtensionEditor(e,s),"editor"),addAutocompleteProvider:h(e=>{this.autocompleteProviderWrappers.push(e),this.setupAutocompleteProvider()},"addAutocompleteProvider"),setEditorComponent:h(e=>this.setCustomEditorComponent(e),"setEditorComponent"),getEditorComponent:h(()=>this.editorComponentFactory,"getEditorComponent"),get theme(){return a},getAllThemes:h(()=>Nt(),"getAllThemes"),getTheme:h(e=>jt(e),"getTheme"),setTheme:h(e=>{if(e instanceof Xt)return Gt(e),this.ui.requestRender(),{success:!0};const s=O(e,!0);return s.success&&(this.settingsManager.getTheme()!==e&&this.settingsManager.setTheme(e),this.ui.requestRender()),s},"setTheme"),getToolsExpanded:h(()=>this.toolOutputExpanded,"getToolsExpanded"),setToolsExpanded:h(e=>this.setToolsExpanded(e),"setToolsExpanded")}}showExtensionSelector(e,s,t){return new Promise(i=>{if(t?.signal?.aborted){i(void 0);return}const n=h(()=>{this.hideExtensionSelector(),i(void 0)},"onAbort");t?.signal?.addEventListener("abort",n,{once:!0}),this.extensionSelector=new X(e,s,o=>{t?.signal?.removeEventListener("abort",n),this.hideExtensionSelector(),i(o)},()=>{t?.signal?.removeEventListener("abort",n),this.hideExtensionSelector(),i(void 0)},{tui:this.ui,timeout:t?.timeout,onToggleToolsExpanded:h(()=>this.toggleToolOutputExpansion(),"onToggleToolsExpanded")}),this.editorContainer.clear(),this.editorContainer.addChild(this.extensionSelector),this.ui.setFocus(this.extensionSelector),this.ui.requestRender()})}hideExtensionSelector(){this.extensionSelector?.dispose(),this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.extensionSelector=void 0,this.ui.setFocus(this.editor),this.ui.requestRender()}async showExtensionConfirm(e,s,t){return await this.showExtensionSelector(`${e}
|
|
23
|
-
${s}`,["Yes","No"],t)==="Yes"}async promptForMissingSessionCwd(e){return await this.showExtensionConfirm("Session cwd not found",gt(e.issue))?e.issue.fallbackCwd:void 0}showExtensionInput(e,s,t){return new Promise(i=>{if(t?.signal?.aborted){i(void 0);return}const n=h(()=>{this.hideExtensionInput(),i(void 0)},"onAbort");t?.signal?.addEventListener("abort",n,{once:!0}),this.extensionInput=new Lt(e,s,o=>{t?.signal?.removeEventListener("abort",n),this.hideExtensionInput(),i(o)},()=>{t?.signal?.removeEventListener("abort",n),this.hideExtensionInput(),i(void 0)},{tui:this.ui,timeout:t?.timeout}),this.editorContainer.clear(),this.editorContainer.addChild(this.extensionInput),this.ui.setFocus(this.extensionInput),this.ui.requestRender()})}hideExtensionInput(){this.extensionInput?.dispose(),this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.extensionInput=void 0,this.ui.setFocus(this.editor),this.ui.requestRender()}showExtensionEditor(e,s){return new Promise(t=>{this.extensionEditor=new It(this.ui,this.keybindings,e,s,i=>{this.hideExtensionEditor(),t(i)},()=>{this.hideExtensionEditor(),t(void 0)}),this.editorContainer.clear(),this.editorContainer.addChild(this.extensionEditor),this.ui.setFocus(this.extensionEditor),this.ui.requestRender()})}hideExtensionEditor(){this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.extensionEditor=void 0,this.ui.setFocus(this.editor),this.ui.requestRender()}setCustomEditorComponent(e){this.editorComponentFactory=e;const s=this.editor.getText();if(this.editorContainer.clear(),e){const t=e(this.ui,Ce(),this.keybindings);t.onSubmit=this.defaultEditor.onSubmit,t.onChange=this.defaultEditor.onChange,t.setText(s),t.borderColor!==void 0&&(t.borderColor=this.defaultEditor.borderColor),t.setPaddingX!==void 0&&t.setPaddingX(this.defaultEditor.getPaddingX()),t.setAutocompleteProvider&&this.autocompleteProvider&&t.setAutocompleteProvider(this.autocompleteProvider);const i=t;if("actionHandlers"in i&&i.actionHandlers instanceof Map){i.onEscape||(i.onEscape=()=>this.defaultEditor.onEscape?.()),i.onCtrlD||(i.onCtrlD=()=>this.defaultEditor.onCtrlD?.()),i.onPasteImage||(i.onPasteImage=()=>this.defaultEditor.onPasteImage?.()),i.onExtensionShortcut||(i.onExtensionShortcut=n=>this.defaultEditor.onExtensionShortcut?.(n));for(const[n,o]of this.defaultEditor.actionHandlers)i.actionHandlers.set(n,o)}this.editor=t}else this.defaultEditor.setText(s),this.editor=this.defaultEditor;this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()}showExtensionNotify(e,s){s==="error"?this.showError(e):s==="warning"?this.showWarning(e):this.showStatus(e)}async showExtensionCustom(e,s){const t=this.editor.getText(),i=s?.overlay??!1,n=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.editor.setText(t),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor");return new Promise((o,r)=>{let d,l=!1;const g=h(u=>{if(!l){l=!0,i?this.ui.hideOverlay():n(),o(u);try{d?.dispose?.()}catch{}}},"close");Promise.resolve(e(this.ui,a,this.keybindings,g)).then(u=>{if(!l)if(d=u,i){const c=h(()=>{if(s?.overlayOptions)return typeof s.overlayOptions=="function"?s.overlayOptions():s.overlayOptions;const S=d.width;return S?{width:S}:void 0},"resolveOptions"),
|
|
23
|
+
${s}`,["Yes","No"],t)==="Yes"}async promptForMissingSessionCwd(e){return await this.showExtensionConfirm("Session cwd not found",gt(e.issue))?e.issue.fallbackCwd:void 0}showExtensionInput(e,s,t){return new Promise(i=>{if(t?.signal?.aborted){i(void 0);return}const n=h(()=>{this.hideExtensionInput(),i(void 0)},"onAbort");t?.signal?.addEventListener("abort",n,{once:!0}),this.extensionInput=new Lt(e,s,o=>{t?.signal?.removeEventListener("abort",n),this.hideExtensionInput(),i(o)},()=>{t?.signal?.removeEventListener("abort",n),this.hideExtensionInput(),i(void 0)},{tui:this.ui,timeout:t?.timeout}),this.editorContainer.clear(),this.editorContainer.addChild(this.extensionInput),this.ui.setFocus(this.extensionInput),this.ui.requestRender()})}hideExtensionInput(){this.extensionInput?.dispose(),this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.extensionInput=void 0,this.ui.setFocus(this.editor),this.ui.requestRender()}showExtensionEditor(e,s){return new Promise(t=>{this.extensionEditor=new It(this.ui,this.keybindings,e,s,i=>{this.hideExtensionEditor(),t(i)},()=>{this.hideExtensionEditor(),t(void 0)}),this.editorContainer.clear(),this.editorContainer.addChild(this.extensionEditor),this.ui.setFocus(this.extensionEditor),this.ui.requestRender()})}hideExtensionEditor(){this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.extensionEditor=void 0,this.ui.setFocus(this.editor),this.ui.requestRender()}setCustomEditorComponent(e){this.editorComponentFactory=e;const s=this.editor.getText();if(this.editorContainer.clear(),e){const t=e(this.ui,Ce(),this.keybindings);t.onSubmit=this.defaultEditor.onSubmit,t.onChange=this.defaultEditor.onChange,t.setText(s),t.borderColor!==void 0&&(t.borderColor=this.defaultEditor.borderColor),t.setPaddingX!==void 0&&t.setPaddingX(this.defaultEditor.getPaddingX()),t.setAutocompleteProvider&&this.autocompleteProvider&&t.setAutocompleteProvider(this.autocompleteProvider);const i=t;if("actionHandlers"in i&&i.actionHandlers instanceof Map){i.onEscape||(i.onEscape=()=>this.defaultEditor.onEscape?.()),i.onCtrlD||(i.onCtrlD=()=>this.defaultEditor.onCtrlD?.()),i.onPasteImage||(i.onPasteImage=()=>this.defaultEditor.onPasteImage?.()),i.onExtensionShortcut||(i.onExtensionShortcut=n=>this.defaultEditor.onExtensionShortcut?.(n));for(const[n,o]of this.defaultEditor.actionHandlers)i.actionHandlers.set(n,o)}this.editor=t}else this.defaultEditor.setText(s),this.editor=this.defaultEditor;this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()}showExtensionNotify(e,s){s==="error"?this.showError(e):s==="warning"?this.showWarning(e):this.showStatus(e)}async showExtensionCustom(e,s){const t=this.editor.getText(),i=s?.overlay??!1,n=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.editor.setText(t),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor");return new Promise((o,r)=>{let d,l=!1;const g=h(u=>{if(!l){l=!0,i?this.ui.hideOverlay():n(),o(u);try{d?.dispose?.()}catch{}}},"close");Promise.resolve(e(this.ui,a,this.keybindings,g)).then(u=>{if(!l)if(d=u,i){const c=h(()=>{if(s?.overlayOptions)return typeof s.overlayOptions=="function"?s.overlayOptions():s.overlayOptions;const S=d.width;return S?{width:S}:void 0},"resolveOptions"),y=this.ui.showOverlay(d,c());s?.onHandle?.(y)}else this.editorContainer.clear(),this.editorContainer.addChild(d),this.ui.setFocus(d),this.ui.requestRender()}).catch(u=>{l||(i||n(),r(u))})})}showExtensionError(e,s,t){const i=`Extension "${e}" error: ${s}`,n=new f(a.fg("error",i),1,0);if(this.chatContainer.addChild(n),t){const o=t.split(`
|
|
24
24
|
`).slice(1).map(r=>a.fg("dim",` ${r.trim()}`)).join(`
|
|
25
|
-
`);o&&this.chatContainer.addChild(new f(o,1,0))}this.ui.requestRender()}setupKeyHandlers(){this.defaultEditor.onEscape=()=>{if(this.session.isStreaming)this.restoreQueuedMessagesToEditor({abort:!0});else if(this.session.isBashRunning)this.session.abortBash();else if(this.isBashMode)this.editor.setText(""),this.isBashMode=!1,this.updateEditorBorderColor();else if(!this.editor.getText().trim()){const e=this.settingsManager.getDoubleEscapeAction();if(e!=="none"){const s=Date.now();s-this.lastEscapeTime<500?(e==="tree"?this.showTreeSelector():this.showUserMessageSelector(),this.lastEscapeTime=0):this.lastEscapeTime=s}}},this.defaultEditor.onAction("app.clear",()=>this.handleCtrlC()),this.defaultEditor.onCtrlD=()=>this.handleCtrlD(),this.defaultEditor.onAction("app.suspend",()=>this.handleCtrlZ()),this.defaultEditor.onAction("app.thinking.cycle",()=>this.cycleThinkingLevel()),this.defaultEditor.onAction("app.model.cycleForward",()=>this.cycleModel("forward")),this.defaultEditor.onAction("app.model.cycleBackward",()=>this.cycleModel("backward")),this.ui.onDebug=()=>this.handleDebugCommand(),this.defaultEditor.onAction("app.model.select",()=>this.showModelSelector()),this.defaultEditor.onAction("app.tools.expand",()=>this.toggleToolOutputExpansion()),this.defaultEditor.onAction("app.thinking.toggle",()=>this.toggleThinkingBlockVisibility()),this.defaultEditor.onAction("app.editor.external",()=>this.openExternalEditor()),this.defaultEditor.onAction("app.message.followUp",()=>this.handleFollowUp()),this.defaultEditor.onAction("app.message.dequeue",()=>this.handleDequeue()),this.defaultEditor.onAction("app.session.new",()=>this.handleClearCommand()),this.defaultEditor.onAction("app.session.tree",()=>this.showTreeSelector()),this.defaultEditor.onAction("app.session.fork",()=>this.showUserMessageSelector()),this.defaultEditor.onAction("app.session.resume",()=>this.showSessionSelector()),this.defaultEditor.onChange=e=>{const s=this.isBashMode;this.isBashMode=e.trimStart().startsWith("!"),s!==this.isBashMode&&this.updateEditorBorderColor()},this.defaultEditor.onPasteImage=()=>{this.handleClipboardImagePaste()}}async handleClipboardImagePaste(){try{const e=await St();if(!e)return;const s=q.tmpdir(),t=wt(e.mimeType)??"png",i=`pi-clipboard-${qe.randomUUID()}.${t}`,n=y.join(s,i);v.writeFileSync(n,Buffer.from(e.bytes)),this.editor.insertTextAtCursor?.(n),this.ui.requestRender()}catch{}}setupEditorSubmitHandler(){this.defaultEditor.onSubmit=async e=>{if(e=e.trim(),!!e){if(e==="/settings"){this.showSettingsSelector(),this.editor.setText("");return}if(e==="/scoped-models"){this.editor.setText(""),await this.showModelsSelector();return}if(e==="/model"||e.startsWith("/model ")){const s=e.startsWith("/model ")?e.slice(7).trim():void 0;this.editor.setText(""),await this.handleModelCommand(s);return}if(e==="/export"||e.startsWith("/export ")){await this.handleExportCommand(e),this.editor.setText("");return}if(e==="/import"||e.startsWith("/import ")){await this.handleImportCommand(e),this.editor.setText("");return}if(e==="/share"){await this.handleShareCommand(),this.editor.setText("");return}if(e==="/copy"){await this.handleCopyCommand(),this.editor.setText("");return}if(e==="/name"||e.startsWith("/name ")){this.handleNameCommand(e),this.editor.setText("");return}if(e==="/session"){this.handleSessionCommand(),this.editor.setText("");return}if(e==="/changelog"){this.handleChangelogCommand(),this.editor.setText("");return}if(e==="/hotkeys"){this.handleHotkeysCommand(),this.editor.setText("");return}if(e==="/fork"){this.showUserMessageSelector(),this.editor.setText("");return}if(e==="/clone"){this.editor.setText(""),await this.handleCloneCommand();return}if(e==="/tree"){this.showTreeSelector(),this.editor.setText("");return}if(e==="/login"){this.showOAuthSelector("login"),this.editor.setText("");return}if(e==="/logout"){this.showOAuthSelector("logout"),this.editor.setText("");return}if(e==="/new"||e==="/clear"){this.editor.setText(""),await this.handleClearCommand();return}if(e==="/goal"||e.startsWith("/goal ")){this.editor.setText(""),await this.handleGoalCommand(e.startsWith("/goal ")?e.slice(6).trim():"");return}if(e==="/use"||e.startsWith("/use ")){this.editor.setText(""),this.handleUseCommand(e.startsWith("/use ")?e.slice(5).trim():"");return}if(e==="/trace"){this.editor.setText(""),this.handleTraceCommand();return}if(e==="/compact"||e.startsWith("/compact ")){const s=e.startsWith("/compact ")?e.slice(9).trim():void 0;this.editor.setText(""),await this.handleCompactCommand(s);return}if(e==="/reload"){this.editor.setText(""),await this.handleReloadCommand();return}if(e==="/debug"){this.handleDebugCommand(),this.editor.setText("");return}if(e==="/arminsayshi"){this.handleArminSaysHi(),this.editor.setText("");return}if(e==="/dementedelves"){this.handleDementedDelves(),this.editor.setText("");return}if(e==="/resume"){this.showSessionSelector(),this.editor.setText("");return}if(e==="/quit"){this.editor.setText(""),await this.shutdown();return}if(e.startsWith("!")){const s=e.startsWith("!!"),t=s?e.slice(2).trim():e.slice(1).trim();if(t){if(this.session.isBashRunning){this.showWarning("A bash command is already running. Press Esc to cancel it first."),this.editor.setText(e);return}this.editor.addToHistory?.(e),await this.handleBashCommand(t,s),this.isBashMode=!1,this.updateEditorBorderColor();return}}if(this.session.isCompacting){this.isExtensionCommand(e)?(this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e)):this.queueCompactionMessage(e,"steer");return}if(this.session.isWorking){this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e,{streamingBehavior:"steer"}),this.updatePendingMessagesDisplay(),this.updateKodaTitle(),this.ui.requestRender();return}this.flushPendingBashComponents(),this.onInputCallback?this.onInputCallback(e):this.pendingUserInputs.push(e),this.editor.addToHistory?.(e)}}}subscribeToAgent(){this.unsubscribe=this.session.subscribe(async e=>{await this.handleEvent(e)})}async handleEvent(e){switch(this.isInitialized||await this.init(),this.footer.invalidate(),e.type){case"agent_start":this.pendingTools.clear(),this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!0),this.retryEscapeHandler&&(this.defaultEditor.onEscape=this.retryEscapeHandler,this.retryEscapeHandler=void 0),this.retryCountdown&&(this.retryCountdown.dispose(),this.retryCountdown=void 0),this.retryLoader&&(this.retryLoader.stop(),this.retryLoader=void 0),this.stopWorkingLoader(),this.workingVisible&&(this.loadingAnimation=this.createWorkingLoader(),this.statusContainer.addChild(this.loadingAnimation)),this.ui.requestRender();break;case"queue_update":this.updatePendingMessagesDisplay(),this.ui.requestRender();break;case"session_info_changed":this.updateTerminalTitle(),this.footer.invalidate(),this.ui.requestRender();break;case"thinking_level_changed":this.footer.invalidate(),this.updateEditorBorderColor();break;case"message_start":e.message.role==="custom"?(this.addMessageToChat(e.message),this.ui.requestRender()):e.message.role==="user"?(this.addMessageToChat(e.message),this.updatePendingMessagesDisplay(),this.ui.requestRender()):e.message.role==="assistant"&&(this.streamingComponent=new U(void 0,this.hideThinkingBlock,this.getMarkdownThemeWithSettings(),this.hiddenThinkingLabel),this.streamingMessage=e.message,this.chatContainer.addChild(this.streamingComponent),this.streamingComponent.updateContent(this.streamingMessage),this.ui.requestRender());break;case"message_update":if(this.streamingComponent&&e.message.role==="assistant"){this.streamingMessage=e.message,this.streamingComponent.updateContent(this.streamingMessage);for(const s of this.streamingMessage.content)if(s.type==="toolCall")if(this.pendingTools.has(s.id)){const t=this.pendingTools.get(s.id);t&&t.updateArgs(s.arguments)}else{const t=new H(s.name,s.id,s.arguments,{showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells()},this.getRegisteredToolDefinition(s.name),this.ui,this.sessionManager.getCwd());t.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(t),this.pendingTools.set(s.id,t)}this.ui.requestRender()}break;case"message_end":if(e.message.role==="user")break;if(this.streamingComponent&&e.message.role==="assistant"){this.streamingMessage=e.message;let s;if(this.streamingMessage.stopReason==="aborted"){const t=this.session.retryAttempt;s=t>0?`Aborted after ${t} retry attempt${t>1?"s":""}`:"Operation aborted",this.streamingMessage.errorMessage=s}if(this.streamingComponent.updateContent(this.streamingMessage),this.streamingMessage.stopReason==="aborted"||this.streamingMessage.stopReason==="error"){s||(s=this.streamingMessage.errorMessage||"Error");for(const[,t]of this.pendingTools.entries())t.updateResult({content:[{type:"text",text:s}],isError:!0});this.pendingTools.clear()}else for(const[,t]of this.pendingTools.entries())t.setArgsComplete();this.streamingComponent=void 0,this.streamingMessage=void 0,this.footer.invalidate()}this.ui.requestRender();break;case"tool_execution_start":{let s=this.pendingTools.get(e.toolCallId);s||(s=new H(e.toolName,e.toolCallId,e.args,{showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells()},this.getRegisteredToolDefinition(e.toolName),this.ui,this.sessionManager.getCwd()),s.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(s),this.pendingTools.set(e.toolCallId,s)),s.markExecutionStarted(),this.ui.requestRender();break}case"tool_execution_update":{const s=this.pendingTools.get(e.toolCallId);s&&(s.updateResult({...e.partialResult,isError:!1},!0),this.ui.requestRender());break}case"tool_execution_end":{const s=this.pendingTools.get(e.toolCallId);s&&(s.updateResult({...e.result,isError:e.isError}),this.pendingTools.delete(e.toolCallId),this.ui.requestRender());break}case"agent_end":this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!1),this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0,this.statusContainer.clear()),this.streamingComponent&&(this.chatContainer.removeChild(this.streamingComponent),this.streamingComponent=void 0,this.streamingMessage=void 0),this.pendingTools.clear(),await this.checkShutdownRequested(),this.ui.requestRender();break;case"compaction_start":{this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!0),this.autoCompactionEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortCompaction()},this.statusContainer.clear(),this.compactionStartMs=Date.now(),this.compactionReason=e.reason,this.compactionCancelHint=`(${$("app.interrupt")} to cancel)`,this.autoCompactionLoader=new L(this.ui,s=>a.fg("accent",s),s=>s,this.buildCompactionLabel()),this.statusContainer.addChild(this.autoCompactionLoader),this.compactionTicker&&clearInterval(this.compactionTicker),this.compactionTicker=setInterval(()=>{this.autoCompactionLoader?.setMessage(this.buildCompactionLabel())},250),this.ui.requestRender();break}case"compaction_end":{this.compactionTicker&&(clearInterval(this.compactionTicker),this.compactionTicker=void 0),this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!1),this.autoCompactionEscapeHandler&&(this.defaultEditor.onEscape=this.autoCompactionEscapeHandler,this.autoCompactionEscapeHandler=void 0),this.autoCompactionLoader&&(this.autoCompactionLoader.stop(),this.autoCompactionLoader=void 0,this.statusContainer.clear()),e.aborted?e.reason==="manual"?this.showError("Compaction cancelled"):this.showStatus("Auto-compaction cancelled"):e.result?(this.chatContainer.clear(),this.rebuildChatFromMessages(),this.addMessageToChat(dt(e.result.summary,e.result.tokensBefore,new Date().toISOString())),this.footer.invalidate()):e.errorMessage&&(e.reason==="manual"?this.showError(e.errorMessage):(this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("error",e.errorMessage),1,0)))),this.flushCompactionQueue({willRetry:e.willRetry}),this.ui.requestRender();break}case"auto_retry_start":{this.retryEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortRetry()},this.statusContainer.clear(),this.retryCountdown?.dispose();const s=h(t=>`Retrying (${e.attempt}/${e.maxAttempts}) in ${t}s... (${$("app.interrupt")} to cancel)`,"retryMessage");this.retryLoader=new L(this.ui,t=>a.fg("warning",t),t=>a.fg("muted",t),s(Math.ceil(e.delayMs/1e3))),this.retryCountdown=new $t(e.delayMs,this.ui,t=>{this.retryLoader?.setMessage(s(t))},()=>{this.retryCountdown=void 0}),this.statusContainer.addChild(this.retryLoader),this.ui.requestRender();break}case"auto_retry_end":{this.retryEscapeHandler&&(this.defaultEditor.onEscape=this.retryEscapeHandler,this.retryEscapeHandler=void 0),this.retryCountdown&&(this.retryCountdown.dispose(),this.retryCountdown=void 0),this.retryLoader&&(this.retryLoader.stop(),this.retryLoader=void 0,this.statusContainer.clear()),e.success||this.showError(`Retry failed after ${e.attempt} attempts: ${e.finalError||"Unknown error"}`),this.ui.requestRender();break}case"overseer_start":{this.overseerLoader?.stop(),this.statusContainer.clear();const s=`(${$("app.interrupt")} to skip)`;this.overseerLoader=new L(this.ui,t=>a.fg("accent",t),t=>a.fg("muted",t),`Koda is reviewing the result\u2026 ${s}`),this.statusContainer.addChild(this.overseerLoader),this.overseerEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortOverseer()},this._kodaReviewing=!0,this.updateKodaTitle(),this.ui.requestRender();break}case"overseer_end":{this.overseerLoader&&(this.overseerLoader.stop(),this.overseerLoader=void 0,this.statusContainer.clear()),this.overseerEscapeHandler!==void 0&&(this.defaultEditor.onEscape=this.overseerEscapeHandler,this.overseerEscapeHandler=void 0),this._kodaReviewing=!1,this.updateKodaTitle(),e.outcome==="nudging"?this.showStatus(`Overseer: not done yet \u2014 ${e.detail??"continuing"}`):e.outcome==="skipped"?this.showStatus(`Overseer: review skipped (${e.detail??"unavailable"})`):this.showStatus("Overseer: looks complete \u2713"),this.ui.requestRender();break}case"model_switch_notice":{const s=e.message;s.startsWith("\u2191")||s.startsWith("\u25B2")?this._kodaTitleBase="beefed":s.startsWith("\xBB")||s.startsWith("\u2193")?this._kodaTitleBase="rerouted":s.startsWith("\u2190")&&(this._kodaTitleBase="idle"),this.updateKodaTitle(),!s.startsWith("\xBB")&&!s.startsWith("\u2190")&&this.showStatus(e.message),this.ui.requestRender();break}case"goal_set":{this._goalAchievedShown=!1,this.renderGoalBlock({text:e.text,milestones:e.milestones});break}case"goal_progress":{const s=e.milestones.filter(i=>i.done).length,t=e.milestones.length;if(e.achieved&&!this._goalAchievedShown)this._goalAchievedShown=!0,this.renderGoalBlock({text:e.text,milestones:e.milestones,achieved:!0});else if(!e.achieved){const i=e.drift?` \xB7 drifting: ${e.drift}`:"";this.showStatus(`Goal: ${s}/${t} milestones${i}`)}break}case"goal_cleared":{this.showStatus("Goal cleared.");break}case"suggest_feature":{const s=e.message.split(/(\/[a-z_]+|run_background)/);let t=a.fg("warning",a.bold("\xBB tip "));s.forEach((i,n)=>{i&&(t+=n%2===1?a.fg("accent",a.bold(i)):a.fg("muted",i))}),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(t,1,0)),this.ui.requestRender();break}case"recap_start":{this.recapLoader?.stop(),this.statusContainer.clear();const s=`(${$("app.interrupt")} to skip)`;this.recapLoader=new L(this.ui,t=>a.fg("accent",t),t=>a.fg("muted",t),`Koda is wrapping up\u2026 ${s}`),this.statusContainer.addChild(this.recapLoader),this.recapEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortRecap()},this.ui.requestRender();break}case"recap_end":{this.recapLoader&&(this.recapLoader.stop(),this.recapLoader=void 0,this.statusContainer.clear()),this.recapEscapeHandler!==void 0&&(this.defaultEditor.onEscape=this.recapEscapeHandler,this.recapEscapeHandler=void 0),this.ui.requestRender();break}case"task_recap":{const s=e.elapsedMs,t=Math.max(0,Math.floor(s/1e3)),i=Math.floor(t/60),n=i>0?`${i}m ${t%60}s`:`${t}s`;this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("accent","\u273B ")+a.fg("dim",`Churned for ${n}`),1,0)),e.recap&&this.chatContainer.addChild(new f(a.fg("dim","\u203B recap: ")+a.fg("muted",e.recap),1,0)),this.ui.requestRender();break}}}getUserMessageText(e){return e.role!=="user"?"":(typeof e.content=="string"?[{type:"text",text:e.content}]:e.content.filter(t=>t.type==="text")).map(t=>t.text).join("")}showStatus(e){const s=this.chatContainer.children,t=s.length>0?s[s.length-1]:void 0,i=s.length>1?s[s.length-2]:void 0;if(t&&i&&t===this.lastStatusText&&i===this.lastStatusSpacer){this.lastStatusText.setText(a.fg("dim",e)),this.ui.requestRender();return}const n=new p(1),o=new f(a.fg("dim",e),1,0);this.chatContainer.addChild(n),this.chatContainer.addChild(o),this.lastStatusSpacer=n,this.lastStatusText=o,this.ui.requestRender()}addMessageToChat(e,s){switch(e.role){case"bashExecution":{const t=new Q(e.command,this.ui,e.excludeFromContext);e.output&&t.appendOutput(e.output),t.setComplete(e.exitCode,e.cancelled,e.truncated?{truncated:!0}:void 0,e.fullOutputPath),this.chatContainer.addChild(t);break}case"custom":{if(e.display){const t=this.session.extensionRunner.getMessageRenderer(e.customType),i=new At(e,t,this.getMarkdownThemeWithSettings());i.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(i)}break}case"compactionSummary":{this.chatContainer.addChild(new p(1));const t=new Tt(e,this.getMarkdownThemeWithSettings());t.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(t);break}case"branchSummary":{this.chatContainer.addChild(new p(1));const t=new xt(e,this.getMarkdownThemeWithSettings());t.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(t);break}case"user":{const t=this.getUserMessageText(e);if(t){this.chatContainer.children.length>0&&this.chatContainer.addChild(new p(1));const i=nt(t);if(i){const n=new Bt(i,this.getMarkdownThemeWithSettings());if(n.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(n),i.userMessage){const o=new fe(i.userMessage,this.getMarkdownThemeWithSettings());this.chatContainer.addChild(o)}}else{const n=new fe(t,this.getMarkdownThemeWithSettings());this.chatContainer.addChild(n)}s?.populateHistory&&this.editor.addToHistory?.(t)}break}case"assistant":{const t=new U(e,this.hideThinkingBlock,this.getMarkdownThemeWithSettings(),this.hiddenThinkingLabel);this.chatContainer.addChild(t);break}case"toolResult":break;default:{const t=e}}}renderSessionContext(e,s={}){this.pendingTools.clear();const t=new Map;s.updateFooter&&(this.footer.invalidate(),this.updateEditorBorderColor());for(const i of e.messages)if(i.role==="assistant"){this.addMessageToChat(i);for(const n of i.content)if(n.type==="toolCall"){const o=new H(n.name,n.id,n.arguments,{showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells()},this.getRegisteredToolDefinition(n.name),this.ui,this.sessionManager.getCwd());if(o.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(o),i.stopReason==="aborted"||i.stopReason==="error"){let r;if(i.stopReason==="aborted"){const d=this.session.retryAttempt;r=d>0?`Aborted after ${d} retry attempt${d>1?"s":""}`:"Operation aborted"}else r=i.errorMessage||"Error";o.updateResult({content:[{type:"text",text:r}],isError:!0})}else t.set(n.id,o)}}else if(i.role==="toolResult"){const n=t.get(i.toolCallId);n&&(n.updateResult(i),t.delete(i.toolCallId))}else this.addMessageToChat(i,s);for(const[i,n]of t)this.pendingTools.set(i,n);this.ui.requestRender()}renderInitialMessages(){const e=this.sessionManager.buildSessionContext();this.renderSessionContext(e,{updateFooter:!0,populateHistory:!0});const t=this.sessionManager.getEntries().filter(i=>i.type==="compaction").length;if(t>0){const i=t===1?"1 time":`${t} times`;this.showStatus(`Session compacted ${i}`)}}async getUserInput(){const e=this.pendingUserInputs.shift();return e!==void 0?e:new Promise(s=>{this.onInputCallback=t=>{this.onInputCallback=void 0,s(t)}})}rebuildChatFromMessages(){this.chatContainer.clear();const e=this.sessionManager.buildSessionContext();this.renderSessionContext(e)}handleCtrlC(){const e=Date.now();e-this.lastSigintTime<500?this.shutdown():(this.clearEditor(),this.lastSigintTime=e)}handleCtrlD(){this.shutdown()}isShuttingDown=!1;async shutdown(e){if(this.isShuttingDown)return;this.isShuttingDown=!0,this.unregisterSignalHandlers(),e?.fromSignal&&(await this.runtimeHost.dispose(),await this.ui.terminal.drainInput(1e3),this.stop(),process.exit(0)),await this.ui.terminal.drainInput(1e3),this.stop(),await this.runtimeHost.dispose();const s=is(this.sessionManager);s&&process.stdout.write(`${Qe.dim("To resume this session:")} ${s}
|
|
26
|
-
`),process.exit(0)}emergencyTerminalExit(){this.isShuttingDown=!0,this.unregisterSignalHandlers(),G(),process.exit(129)}uncaughtCrash(e){this.isShuttingDown&&process.exit(1),this.isShuttingDown=!0;try{this.unregisterSignalHandlers()}catch{}try{G()}catch{}try{this.ui.stop()}catch{}console.error("koda exiting due to uncaughtException:"),console.error(e),process.exit(1)}async checkShutdownRequested(){this.shutdownRequested&&await this.shutdown()}registerSignalHandlers(){this.unregisterSignalHandlers();const e=["SIGTERM"];process.platform!=="win32"&&e.push("SIGHUP");for(const i of e){const n=h(()=>{G(),this.shutdown({fromSignal:!0})},"handler");process.prependListener(i,n),this.signalCleanupHandlers.push(()=>process.off(i,n))}const s=h(i=>{throw Yt(i)&&this.emergencyTerminalExit(),i},"terminalErrorHandler");process.stdout.on("error",s),process.stderr.on("error",s),this.signalCleanupHandlers.push(()=>process.stdout.off("error",s)),this.signalCleanupHandlers.push(()=>process.stderr.off("error",s));const t=h(i=>this.uncaughtCrash(i),"uncaughtExceptionHandler");process.prependListener("uncaughtException",t),this.signalCleanupHandlers.push(()=>process.off("uncaughtException",t))}unregisterSignalHandlers(){for(const e of this.signalCleanupHandlers)e();this.signalCleanupHandlers=[]}handleCtrlZ(){if(process.platform==="win32"){this.showStatus("Suspend to background is not supported on Windows");return}const e=setInterval(()=>{},2**30),s=h(()=>{},"ignoreSigint");process.on("SIGINT",s),process.once("SIGCONT",()=>{clearInterval(e),process.removeListener("SIGINT",s),this.ui.start(),this.ui.requestRender(!0)});try{this.ui.stop(),process.kill(0,"SIGTSTP")}catch(t){throw clearInterval(e),process.removeListener("SIGINT",s),t}}async handleFollowUp(){const e=(this.editor.getExpandedText?.()??this.editor.getText()).trim();if(e){if(this.session.isCompacting){this.isExtensionCommand(e)?(this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e)):this.queueCompactionMessage(e,"followUp");return}this.session.isStreaming?(this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e,{streamingBehavior:"followUp"}),this.updatePendingMessagesDisplay(),this.ui.requestRender()):this.editor.onSubmit&&(this.editor.setText(""),this.editor.onSubmit(e))}}handleDequeue(){const e=this.restoreQueuedMessagesToEditor();e===0?this.showStatus("No queued messages to restore"):this.showStatus(`Restored ${e} queued message${e>1?"s":""} to editor`)}updateEditorBorderColor(){if(this.isBashMode)this.editor.borderColor=a.getBashModeBorderColor();else{const e=this.session.thinkingLevel||"off";this.editor.borderColor=a.getThinkingBorderColor(e)}this.ui.requestRender()}cycleThinkingLevel(){const e=this.session.cycleThinkingLevel();e===void 0?this.showStatus("Current model does not support thinking"):(this.footer.invalidate(),this.updateEditorBorderColor(),this.showStatus(`Thinking level: ${e}`))}async cycleModel(e){try{const s=await this.session.cycleModel(e);if(s===void 0){const t=this.session.scopedModels.length>0?"Only one model in scope":"Only one model available";this.showStatus(t)}else{this.footer.invalidate(),this.updateEditorBorderColor();const t=s.model.reasoning&&s.thinkingLevel!=="off"?` (thinking: ${s.thinkingLevel})`:"";this.showStatus(`Switched to ${s.model.name||s.model.id}${t}`),this.maybeWarnAboutAnthropicSubscriptionAuth(s.model)}}catch(s){this.showError(s instanceof Error?s.message:String(s))}}toggleToolOutputExpansion(){this.setToolsExpanded(!this.toolOutputExpanded)}setToolsExpanded(e){this.toolOutputExpanded=e;const s=this.customHeader??this.builtInHeader;F(s)&&s.setExpanded(e);for(const t of this.chatContainer.children)F(t)&&t.setExpanded(e);this.ui.requestRender()}toggleThinkingBlockVisibility(){this.hideThinkingBlock=!this.hideThinkingBlock,this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock),this.chatContainer.clear(),this.rebuildChatFromMessages(),this.streamingComponent&&this.streamingMessage&&(this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock),this.streamingComponent.updateContent(this.streamingMessage),this.chatContainer.addChild(this.streamingComponent)),this.showStatus(`Thinking blocks: ${this.hideThinkingBlock?"hidden":"visible"}`)}async openExternalEditor(){const e=process.env.VISUAL||process.env.EDITOR;if(!e){this.showWarning("No editor configured. Set $VISUAL or $EDITOR environment variable.");return}const s=this.editor.getExpandedText?.()??this.editor.getText(),t=
|
|
25
|
+
`);o&&this.chatContainer.addChild(new f(o,1,0))}this.ui.requestRender()}setupKeyHandlers(){this.defaultEditor.onEscape=()=>{if(this.session.isStreaming)this.restoreQueuedMessagesToEditor({abort:!0});else if(this.session.isBashRunning)this.session.abortBash();else if(this.isBashMode)this.editor.setText(""),this.isBashMode=!1,this.updateEditorBorderColor();else if(!this.editor.getText().trim()){const e=this.settingsManager.getDoubleEscapeAction();if(e!=="none"){const s=Date.now();s-this.lastEscapeTime<500?(e==="tree"?this.showTreeSelector():this.showUserMessageSelector(),this.lastEscapeTime=0):this.lastEscapeTime=s}}},this.defaultEditor.onAction("app.clear",()=>this.handleCtrlC()),this.defaultEditor.onCtrlD=()=>this.handleCtrlD(),this.defaultEditor.onAction("app.suspend",()=>this.handleCtrlZ()),this.defaultEditor.onAction("app.thinking.cycle",()=>this.cycleThinkingLevel()),this.defaultEditor.onAction("app.model.cycleForward",()=>this.cycleModel("forward")),this.defaultEditor.onAction("app.model.cycleBackward",()=>this.cycleModel("backward")),this.ui.onDebug=()=>this.handleDebugCommand(),this.defaultEditor.onAction("app.model.select",()=>this.showModelSelector()),this.defaultEditor.onAction("app.tools.expand",()=>this.toggleToolOutputExpansion()),this.defaultEditor.onAction("app.thinking.toggle",()=>this.toggleThinkingBlockVisibility()),this.defaultEditor.onAction("app.editor.external",()=>this.openExternalEditor()),this.defaultEditor.onAction("app.message.followUp",()=>this.handleFollowUp()),this.defaultEditor.onAction("app.message.dequeue",()=>this.handleDequeue()),this.defaultEditor.onAction("app.session.new",()=>this.handleClearCommand()),this.defaultEditor.onAction("app.session.tree",()=>this.showTreeSelector()),this.defaultEditor.onAction("app.session.fork",()=>this.showUserMessageSelector()),this.defaultEditor.onAction("app.session.resume",()=>this.showSessionSelector()),this.defaultEditor.onChange=e=>{const s=this.isBashMode;this.isBashMode=e.trimStart().startsWith("!"),s!==this.isBashMode&&this.updateEditorBorderColor()},this.defaultEditor.onPasteImage=()=>{this.handleClipboardImagePaste()}}async handleClipboardImagePaste(){try{const e=await St();if(!e)return;const s=q.tmpdir(),t=wt(e.mimeType)??"png",i=`pi-clipboard-${qe.randomUUID()}.${t}`,n=E.join(s,i);v.writeFileSync(n,Buffer.from(e.bytes)),this.editor.insertTextAtCursor?.(n),this.ui.requestRender()}catch{}}setupEditorSubmitHandler(){this.defaultEditor.onSubmit=async e=>{if(e=e.trim(),!!e){if(e==="/settings"){this.showSettingsSelector(),this.editor.setText("");return}if(e==="/scoped-models"){this.editor.setText(""),await this.showModelsSelector();return}if(e==="/model"||e.startsWith("/model ")){const s=e.startsWith("/model ")?e.slice(7).trim():void 0;this.editor.setText(""),await this.handleModelCommand(s);return}if(e==="/export"||e.startsWith("/export ")){await this.handleExportCommand(e),this.editor.setText("");return}if(e==="/import"||e.startsWith("/import ")){await this.handleImportCommand(e),this.editor.setText("");return}if(e==="/share"){await this.handleShareCommand(),this.editor.setText("");return}if(e==="/copy"){await this.handleCopyCommand(),this.editor.setText("");return}if(e==="/name"||e.startsWith("/name ")){this.handleNameCommand(e),this.editor.setText("");return}if(e==="/session"){this.handleSessionCommand(),this.editor.setText("");return}if(e==="/changelog"){this.handleChangelogCommand(),this.editor.setText("");return}if(e==="/hotkeys"){this.handleHotkeysCommand(),this.editor.setText("");return}if(e==="/fork"){this.showUserMessageSelector(),this.editor.setText("");return}if(e==="/clone"){this.editor.setText(""),await this.handleCloneCommand();return}if(e==="/tree"){this.showTreeSelector(),this.editor.setText("");return}if(e==="/login"){this.showOAuthSelector("login"),this.editor.setText("");return}if(e==="/logout"){this.showOAuthSelector("logout"),this.editor.setText("");return}if(e==="/new"||e==="/clear"){this.editor.setText(""),await this.handleClearCommand();return}if(e==="/goal"||e.startsWith("/goal ")){this.editor.setText(""),await this.handleGoalCommand(e.startsWith("/goal ")?e.slice(6).trim():"");return}if(e==="/use"||e.startsWith("/use ")){this.editor.setText(""),this.handleUseCommand(e.startsWith("/use ")?e.slice(5).trim():"");return}if(e==="/trace"){this.editor.setText(""),this.handleTraceCommand();return}if(e==="/compact"||e.startsWith("/compact ")){const s=e.startsWith("/compact ")?e.slice(9).trim():void 0;this.editor.setText(""),await this.handleCompactCommand(s);return}if(e==="/reload"){this.editor.setText(""),await this.handleReloadCommand();return}if(e==="/debug"){this.handleDebugCommand(),this.editor.setText("");return}if(e==="/arminsayshi"){this.handleArminSaysHi(),this.editor.setText("");return}if(e==="/dementedelves"){this.handleDementedDelves(),this.editor.setText("");return}if(e==="/resume"){this.showSessionSelector(),this.editor.setText("");return}if(e==="/quit"){this.editor.setText(""),await this.shutdown();return}if(e.startsWith("!")){const s=e.startsWith("!!"),t=s?e.slice(2).trim():e.slice(1).trim();if(t){if(this.session.isBashRunning){this.showWarning("A bash command is already running. Press Esc to cancel it first."),this.editor.setText(e);return}this.editor.addToHistory?.(e),await this.handleBashCommand(t,s),this.isBashMode=!1,this.updateEditorBorderColor();return}}if(this.session.isCompacting){this.isExtensionCommand(e)?(this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e)):this.queueCompactionMessage(e,"steer");return}if(this.session.isWorking){this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e,{streamingBehavior:"steer"}),this.updatePendingMessagesDisplay(),this.updateKodaTitle(),this.ui.requestRender();return}this.flushPendingBashComponents(),this.onInputCallback?this.onInputCallback(e):this.pendingUserInputs.push(e),this.editor.addToHistory?.(e)}}}subscribeToAgent(){this.unsubscribe=this.session.subscribe(async e=>{await this.handleEvent(e)})}async handleEvent(e){switch(this.isInitialized||await this.init(),this.footer.invalidate(),e.type){case"agent_start":this.pendingTools.clear(),this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!0),this.retryEscapeHandler&&(this.defaultEditor.onEscape=this.retryEscapeHandler,this.retryEscapeHandler=void 0),this.retryCountdown&&(this.retryCountdown.dispose(),this.retryCountdown=void 0),this.retryLoader&&(this.retryLoader.stop(),this.retryLoader=void 0),this.stopWorkingLoader(),this.workingVisible&&(this.loadingAnimation=this.createWorkingLoader(),this.statusContainer.addChild(this.loadingAnimation)),this.ui.requestRender();break;case"queue_update":this.updatePendingMessagesDisplay(),this.ui.requestRender();break;case"session_info_changed":this.updateTerminalTitle(),this.footer.invalidate(),this.ui.requestRender();break;case"thinking_level_changed":this.footer.invalidate(),this.updateEditorBorderColor();break;case"message_start":e.message.role==="custom"?(this.addMessageToChat(e.message),this.ui.requestRender()):e.message.role==="user"?(this.addMessageToChat(e.message),this.updatePendingMessagesDisplay(),this.ui.requestRender()):e.message.role==="assistant"&&(this.streamingComponent=new U(void 0,this.hideThinkingBlock,this.getMarkdownThemeWithSettings(),this.hiddenThinkingLabel),this.streamingMessage=e.message,this.chatContainer.addChild(this.streamingComponent),this.streamingComponent.updateContent(this.streamingMessage),this.ui.requestRender());break;case"message_update":if(this.streamingComponent&&e.message.role==="assistant"){this.streamingMessage=e.message,this.streamingComponent.updateContent(this.streamingMessage);for(const s of this.streamingMessage.content)if(s.type==="toolCall")if(this.pendingTools.has(s.id)){const t=this.pendingTools.get(s.id);t&&t.updateArgs(s.arguments)}else{const t=new H(s.name,s.id,s.arguments,{showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells()},this.getRegisteredToolDefinition(s.name),this.ui,this.sessionManager.getCwd());t.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(t),this.pendingTools.set(s.id,t)}this.ui.requestRender()}break;case"message_end":if(e.message.role==="user")break;if(this.streamingComponent&&e.message.role==="assistant"){this.streamingMessage=e.message;let s;if(this.streamingMessage.stopReason==="aborted"){const t=this.session.retryAttempt;s=t>0?`Aborted after ${t} retry attempt${t>1?"s":""}`:"Operation aborted",this.streamingMessage.errorMessage=s}if(this.streamingComponent.updateContent(this.streamingMessage),this.streamingMessage.stopReason==="aborted"||this.streamingMessage.stopReason==="error"){s||(s=this.streamingMessage.errorMessage||"Error");for(const[,t]of this.pendingTools.entries())t.updateResult({content:[{type:"text",text:s}],isError:!0});this.pendingTools.clear()}else for(const[,t]of this.pendingTools.entries())t.setArgsComplete();this.streamingComponent=void 0,this.streamingMessage=void 0,this.footer.invalidate()}this.ui.requestRender();break;case"tool_execution_start":{let s=this.pendingTools.get(e.toolCallId);s||(s=new H(e.toolName,e.toolCallId,e.args,{showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells()},this.getRegisteredToolDefinition(e.toolName),this.ui,this.sessionManager.getCwd()),s.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(s),this.pendingTools.set(e.toolCallId,s)),s.markExecutionStarted(),this.ui.requestRender();break}case"tool_execution_update":{const s=this.pendingTools.get(e.toolCallId);s&&(s.updateResult({...e.partialResult,isError:!1},!0),this.ui.requestRender());break}case"tool_execution_end":{const s=this.pendingTools.get(e.toolCallId);s&&(s.updateResult({...e.result,isError:e.isError}),this.pendingTools.delete(e.toolCallId),this.ui.requestRender());break}case"agent_end":this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!1),this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0,this.statusContainer.clear()),this.streamingComponent&&(this.chatContainer.removeChild(this.streamingComponent),this.streamingComponent=void 0,this.streamingMessage=void 0),this.pendingTools.clear(),await this.checkShutdownRequested(),this.ui.requestRender();break;case"compaction_start":{this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!0),this.autoCompactionEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortCompaction()},this.statusContainer.clear(),this.compactionStartMs=Date.now(),this.compactionReason=e.reason,this.compactionCancelHint=`(${$("app.interrupt")} to cancel)`,this.autoCompactionLoader=new L(this.ui,s=>a.fg("accent",s),s=>s,this.buildCompactionLabel()),this.statusContainer.addChild(this.autoCompactionLoader),this.compactionTicker&&clearInterval(this.compactionTicker),this.compactionTicker=setInterval(()=>{this.autoCompactionLoader?.setMessage(this.buildCompactionLabel())},250),this.ui.requestRender();break}case"compaction_end":{this.compactionTicker&&(clearInterval(this.compactionTicker),this.compactionTicker=void 0),this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!1),this.autoCompactionEscapeHandler&&(this.defaultEditor.onEscape=this.autoCompactionEscapeHandler,this.autoCompactionEscapeHandler=void 0),this.autoCompactionLoader&&(this.autoCompactionLoader.stop(),this.autoCompactionLoader=void 0,this.statusContainer.clear()),e.aborted?e.reason==="manual"?this.showError("Compaction cancelled"):this.showStatus("Auto-compaction cancelled"):e.result?(this.chatContainer.clear(),this.rebuildChatFromMessages(),this.addMessageToChat(dt(e.result.summary,e.result.tokensBefore,new Date().toISOString())),this.footer.invalidate()):e.errorMessage&&(e.reason==="manual"?this.showError(e.errorMessage):(this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("error",e.errorMessage),1,0)))),this.flushCompactionQueue({willRetry:e.willRetry}),this.ui.requestRender();break}case"auto_retry_start":{this.retryEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortRetry()},this.statusContainer.clear(),this.retryCountdown?.dispose();const s=h(t=>`Retrying (${e.attempt}/${e.maxAttempts}) in ${t}s... (${$("app.interrupt")} to cancel)`,"retryMessage");this.retryLoader=new L(this.ui,t=>a.fg("warning",t),t=>a.fg("muted",t),s(Math.ceil(e.delayMs/1e3))),this.retryCountdown=new $t(e.delayMs,this.ui,t=>{this.retryLoader?.setMessage(s(t))},()=>{this.retryCountdown=void 0}),this.statusContainer.addChild(this.retryLoader),this.ui.requestRender();break}case"auto_retry_end":{this.retryEscapeHandler&&(this.defaultEditor.onEscape=this.retryEscapeHandler,this.retryEscapeHandler=void 0),this.retryCountdown&&(this.retryCountdown.dispose(),this.retryCountdown=void 0),this.retryLoader&&(this.retryLoader.stop(),this.retryLoader=void 0,this.statusContainer.clear()),e.success||this.showError(`Retry failed after ${e.attempt} attempts: ${e.finalError||"Unknown error"}`),this.ui.requestRender();break}case"overseer_start":{this.overseerLoader?.stop(),this.statusContainer.clear();const s=`(${$("app.interrupt")} to skip)`;this.overseerLoader=new L(this.ui,t=>a.fg("accent",t),t=>a.fg("muted",t),`Koda is reviewing the result\u2026 ${s}`),this.statusContainer.addChild(this.overseerLoader),this.overseerEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortOverseer()},this._kodaReviewing=!0,this.updateKodaTitle(),this.ui.requestRender();break}case"overseer_end":{this.overseerLoader&&(this.overseerLoader.stop(),this.overseerLoader=void 0,this.statusContainer.clear()),this.overseerEscapeHandler!==void 0&&(this.defaultEditor.onEscape=this.overseerEscapeHandler,this.overseerEscapeHandler=void 0),this._kodaReviewing=!1,this.updateKodaTitle(),e.outcome==="nudging"?this.showStatus(`Overseer: not done yet \u2014 ${e.detail??"continuing"}`):e.outcome==="gave_up"?this.showStatus(`Overseer: stopped \u2014 ${e.detail??"couldn't verify completion"}. Left for you to review.`):e.outcome==="skipped"?this.showStatus(`Overseer: review skipped (${e.detail??"unavailable"})`):this.showStatus("Overseer: looks complete \u2713"),this.ui.requestRender();break}case"model_switch_notice":{const s=e.message;s.startsWith("\u2191")||s.startsWith("\u25B2")?this._kodaTitleBase="beefed":s.startsWith("\xBB")||s.startsWith("\u2193")?this._kodaTitleBase="rerouted":s.startsWith("\u2190")&&(this._kodaTitleBase="idle"),this.updateKodaTitle(),!s.startsWith("\xBB")&&!s.startsWith("\u2190")&&this.showStatus(e.message),this.ui.requestRender();break}case"goal_set":{this._goalAchievedShown=!1,this.renderGoalBlock({text:e.text,milestones:e.milestones});break}case"goal_progress":{const s=e.milestones.filter(i=>i.done).length,t=e.milestones.length;if(e.achieved&&!this._goalAchievedShown)this._goalAchievedShown=!0,this.renderGoalBlock({text:e.text,milestones:e.milestones,achieved:!0});else if(!e.achieved){const i=e.drift?` \xB7 drifting: ${e.drift}`:"";this.showStatus(`Goal: ${s}/${t} milestones${i}`)}break}case"goal_cleared":{this.showStatus("Goal cleared.");break}case"suggest_feature":{const s=e.message.split(/(\/[a-z_]+|run_background)/);let t=a.fg("warning",a.bold("\xBB tip "));s.forEach((i,n)=>{i&&(t+=n%2===1?a.fg("accent",a.bold(i)):a.fg("muted",i))}),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(t,1,0)),this.ui.requestRender();break}case"recap_start":{this.recapLoader?.stop(),this.statusContainer.clear();const s=`(${$("app.interrupt")} to skip)`;this.recapLoader=new L(this.ui,t=>a.fg("accent",t),t=>a.fg("muted",t),`Koda is wrapping up\u2026 ${s}`),this.statusContainer.addChild(this.recapLoader),this.recapEscapeHandler=this.defaultEditor.onEscape,this.defaultEditor.onEscape=()=>{this.session.abortRecap()},this.ui.requestRender();break}case"recap_end":{this.recapLoader&&(this.recapLoader.stop(),this.recapLoader=void 0,this.statusContainer.clear()),this.recapEscapeHandler!==void 0&&(this.defaultEditor.onEscape=this.recapEscapeHandler,this.recapEscapeHandler=void 0),this.ui.requestRender();break}case"task_recap":{const s=e.elapsedMs,t=Math.max(0,Math.floor(s/1e3)),i=Math.floor(t/60),n=i>0?`${i}m ${t%60}s`:`${t}s`;this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("accent","\u273B ")+a.fg("dim",`Churned for ${n}`),1,0)),e.recap&&this.chatContainer.addChild(new f(a.fg("dim","\u203B recap: ")+a.fg("muted",e.recap),1,0)),this.ui.requestRender();break}}}getUserMessageText(e){return e.role!=="user"?"":(typeof e.content=="string"?[{type:"text",text:e.content}]:e.content.filter(t=>t.type==="text")).map(t=>t.text).join("")}showStatus(e){const s=this.chatContainer.children,t=s.length>0?s[s.length-1]:void 0,i=s.length>1?s[s.length-2]:void 0;if(t&&i&&t===this.lastStatusText&&i===this.lastStatusSpacer){this.lastStatusText.setText(a.fg("dim",e)),this.ui.requestRender();return}const n=new p(1),o=new f(a.fg("dim",e),1,0);this.chatContainer.addChild(n),this.chatContainer.addChild(o),this.lastStatusSpacer=n,this.lastStatusText=o,this.ui.requestRender()}addMessageToChat(e,s){switch(e.role){case"bashExecution":{const t=new Q(e.command,this.ui,e.excludeFromContext);e.output&&t.appendOutput(e.output),t.setComplete(e.exitCode,e.cancelled,e.truncated?{truncated:!0}:void 0,e.fullOutputPath),this.chatContainer.addChild(t);break}case"custom":{if(e.display){const t=this.session.extensionRunner.getMessageRenderer(e.customType),i=new At(e,t,this.getMarkdownThemeWithSettings());i.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(i)}break}case"compactionSummary":{this.chatContainer.addChild(new p(1));const t=new Tt(e,this.getMarkdownThemeWithSettings());t.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(t);break}case"branchSummary":{this.chatContainer.addChild(new p(1));const t=new xt(e,this.getMarkdownThemeWithSettings());t.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(t);break}case"user":{const t=this.getUserMessageText(e);if(t){this.chatContainer.children.length>0&&this.chatContainer.addChild(new p(1));const i=nt(t);if(i){const n=new Bt(i,this.getMarkdownThemeWithSettings());if(n.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(n),i.userMessage){const o=new fe(i.userMessage,this.getMarkdownThemeWithSettings());this.chatContainer.addChild(o)}}else{const n=new fe(t,this.getMarkdownThemeWithSettings());this.chatContainer.addChild(n)}s?.populateHistory&&this.editor.addToHistory?.(t)}break}case"assistant":{const t=new U(e,this.hideThinkingBlock,this.getMarkdownThemeWithSettings(),this.hiddenThinkingLabel);this.chatContainer.addChild(t);break}case"toolResult":break;default:{const t=e}}}renderSessionContext(e,s={}){this.pendingTools.clear();const t=new Map;s.updateFooter&&(this.footer.invalidate(),this.updateEditorBorderColor());for(const i of e.messages)if(i.role==="assistant"){this.addMessageToChat(i);for(const n of i.content)if(n.type==="toolCall"){const o=new H(n.name,n.id,n.arguments,{showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells()},this.getRegisteredToolDefinition(n.name),this.ui,this.sessionManager.getCwd());if(o.setExpanded(this.toolOutputExpanded),this.chatContainer.addChild(o),i.stopReason==="aborted"||i.stopReason==="error"){let r;if(i.stopReason==="aborted"){const d=this.session.retryAttempt;r=d>0?`Aborted after ${d} retry attempt${d>1?"s":""}`:"Operation aborted"}else r=i.errorMessage||"Error";o.updateResult({content:[{type:"text",text:r}],isError:!0})}else t.set(n.id,o)}}else if(i.role==="toolResult"){const n=t.get(i.toolCallId);n&&(n.updateResult(i),t.delete(i.toolCallId))}else this.addMessageToChat(i,s);for(const[i,n]of t)this.pendingTools.set(i,n);this.ui.requestRender()}renderInitialMessages(){const e=this.sessionManager.buildSessionContext();this.renderSessionContext(e,{updateFooter:!0,populateHistory:!0});const t=this.sessionManager.getEntries().filter(i=>i.type==="compaction").length;if(t>0){const i=t===1?"1 time":`${t} times`;this.showStatus(`Session compacted ${i}`)}}async getUserInput(){const e=this.pendingUserInputs.shift();return e!==void 0?e:new Promise(s=>{this.onInputCallback=t=>{this.onInputCallback=void 0,s(t)}})}rebuildChatFromMessages(){this.chatContainer.clear();const e=this.sessionManager.buildSessionContext();this.renderSessionContext(e)}handleCtrlC(){const e=Date.now();e-this.lastSigintTime<500?this.shutdown():(this.clearEditor(),this.lastSigintTime=e)}handleCtrlD(){this.shutdown()}isShuttingDown=!1;async shutdown(e){if(this.isShuttingDown)return;this.isShuttingDown=!0,this.unregisterSignalHandlers(),e?.fromSignal&&(await this.runtimeHost.dispose(),await this.ui.terminal.drainInput(1e3),this.stop(),process.exit(0)),await this.ui.terminal.drainInput(1e3),this.stop(),await this.runtimeHost.dispose();const s=is(this.sessionManager);s&&process.stdout.write(`${Qe.dim("To resume this session:")} ${s}
|
|
26
|
+
`),process.exit(0)}emergencyTerminalExit(){this.isShuttingDown=!0,this.unregisterSignalHandlers(),G(),process.exit(129)}uncaughtCrash(e){this.isShuttingDown&&process.exit(1),this.isShuttingDown=!0;try{this.unregisterSignalHandlers()}catch{}try{G()}catch{}try{this.ui.stop()}catch{}console.error("koda exiting due to uncaughtException:"),console.error(e),process.exit(1)}async checkShutdownRequested(){this.shutdownRequested&&await this.shutdown()}registerSignalHandlers(){this.unregisterSignalHandlers();const e=["SIGTERM"];process.platform!=="win32"&&e.push("SIGHUP");for(const i of e){const n=h(()=>{G(),this.shutdown({fromSignal:!0})},"handler");process.prependListener(i,n),this.signalCleanupHandlers.push(()=>process.off(i,n))}const s=h(i=>{throw Yt(i)&&this.emergencyTerminalExit(),i},"terminalErrorHandler");process.stdout.on("error",s),process.stderr.on("error",s),this.signalCleanupHandlers.push(()=>process.stdout.off("error",s)),this.signalCleanupHandlers.push(()=>process.stderr.off("error",s));const t=h(i=>this.uncaughtCrash(i),"uncaughtExceptionHandler");process.prependListener("uncaughtException",t),this.signalCleanupHandlers.push(()=>process.off("uncaughtException",t))}unregisterSignalHandlers(){for(const e of this.signalCleanupHandlers)e();this.signalCleanupHandlers=[]}handleCtrlZ(){if(process.platform==="win32"){this.showStatus("Suspend to background is not supported on Windows");return}const e=setInterval(()=>{},2**30),s=h(()=>{},"ignoreSigint");process.on("SIGINT",s),process.once("SIGCONT",()=>{clearInterval(e),process.removeListener("SIGINT",s),this.ui.start(),this.ui.requestRender(!0)});try{this.ui.stop(),process.kill(0,"SIGTSTP")}catch(t){throw clearInterval(e),process.removeListener("SIGINT",s),t}}async handleFollowUp(){const e=(this.editor.getExpandedText?.()??this.editor.getText()).trim();if(e){if(this.session.isCompacting){this.isExtensionCommand(e)?(this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e)):this.queueCompactionMessage(e,"followUp");return}this.session.isStreaming?(this.editor.addToHistory?.(e),this.editor.setText(""),await this.session.prompt(e,{streamingBehavior:"followUp"}),this.updatePendingMessagesDisplay(),this.ui.requestRender()):this.editor.onSubmit&&(this.editor.setText(""),this.editor.onSubmit(e))}}handleDequeue(){const e=this.restoreQueuedMessagesToEditor();e===0?this.showStatus("No queued messages to restore"):this.showStatus(`Restored ${e} queued message${e>1?"s":""} to editor`)}updateEditorBorderColor(){if(this.isBashMode)this.editor.borderColor=a.getBashModeBorderColor();else{const e=this.session.thinkingLevel||"off";this.editor.borderColor=a.getThinkingBorderColor(e)}this.ui.requestRender()}cycleThinkingLevel(){const e=this.session.cycleThinkingLevel();e===void 0?this.showStatus("Current model does not support thinking"):(this.footer.invalidate(),this.updateEditorBorderColor(),this.showStatus(`Thinking level: ${e}`))}async cycleModel(e){try{const s=await this.session.cycleModel(e);if(s===void 0){const t=this.session.scopedModels.length>0?"Only one model in scope":"Only one model available";this.showStatus(t)}else{this.footer.invalidate(),this.updateEditorBorderColor();const t=s.model.reasoning&&s.thinkingLevel!=="off"?` (thinking: ${s.thinkingLevel})`:"";this.showStatus(`Switched to ${s.model.name||s.model.id}${t}`),this.maybeWarnAboutAnthropicSubscriptionAuth(s.model)}}catch(s){this.showError(s instanceof Error?s.message:String(s))}}toggleToolOutputExpansion(){this.setToolsExpanded(!this.toolOutputExpanded)}setToolsExpanded(e){this.toolOutputExpanded=e;const s=this.customHeader??this.builtInHeader;F(s)&&s.setExpanded(e);for(const t of this.chatContainer.children)F(t)&&t.setExpanded(e);this.ui.requestRender()}toggleThinkingBlockVisibility(){this.hideThinkingBlock=!this.hideThinkingBlock,this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock),this.chatContainer.clear(),this.rebuildChatFromMessages(),this.streamingComponent&&this.streamingMessage&&(this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock),this.streamingComponent.updateContent(this.streamingMessage),this.chatContainer.addChild(this.streamingComponent)),this.showStatus(`Thinking blocks: ${this.hideThinkingBlock?"hidden":"visible"}`)}async openExternalEditor(){const e=process.env.VISUAL||process.env.EDITOR;if(!e){this.showWarning("No editor configured. Set $VISUAL or $EDITOR environment variable.");return}const s=this.editor.getExpandedText?.()??this.editor.getText(),t=E.join(q.tmpdir(),`pi-editor-${Date.now()}.pi.md`);try{v.writeFileSync(t,s,"utf-8"),this.ui.stop();const[i,...n]=e.split(" ");if(process.stdout.write(`Launching external editor: ${e}
|
|
27
27
|
Pi will resume when the editor exits.
|
|
28
28
|
`),await new Promise(r=>{const d=j(i,[...n,t],{stdio:"inherit",shell:process.platform==="win32"});d.on("error",()=>r(null)),d.on("close",l=>r(l))})===0){const r=v.readFileSync(t,"utf-8").replace(/\n$/,"");this.editor.setText(r)}}finally{try{v.unlinkSync(t)}catch{}this.ui.start(),this.ui.requestRender(!0)}}clearEditor(){this.editor.setText(""),this.ui.requestRender()}showError(e){this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("error",`Error: ${e}`),1,0)),this.chatContainer.addChild(new p(1)),this.ui.requestRender()}showWarning(e){this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("warning",`Warning: ${e}`),1,0)),this.ui.requestRender()}async showResumeHintForCwd(){if(process.env.KODA_NO_RESUME_HINT!=="1")try{const e=this.sessionManager.getCwd(),s=this.sessionManager.getSessionFile(),t=(await W.list(e)).filter(g=>g.path!==s&&g.messageCount>0);if(t.length===0)return;const i=t[0],n=(i.name||i.firstMessage||"").replace(/^\[imported from \w+\]\s*/i,"").replace(/\s+/g," ").trim().slice(0,56),o=ss(i.modified),r=a,d=t.length===1?"1 past session":`${t.length} past sessions`;let l=`${r.fg("accent","\u21A9 ")}${r.fg("muted",`${d} in this folder`)}`;n&&(l+=r.fg("muted",` \xB7 latest: "${n}"`)),o&&(l+=r.fg("dim",` (${o})`)),l+=r.fg("muted"," \u2014 ")+r.fg("accent","/resume")+r.fg("muted"," to pick one, or ")+r.fg("accent","koda --continue")+r.fg("muted"," for the latest."),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(l,1,0)),this.ui.requestRender()}catch{}}async runImportFromSettings(){try{const e=await Je(),s=e.claude+e.opencode;if(s===0){this.showStatus("No new sessions to import from Claude Code or OpenCode.");return}this.showStatus(`Importing ${s} session(s) from Claude Code + OpenCode\u2026`);const t=await Ye({onProgress:h((i,n)=>this.showStatus(`Importing\u2026 ${i}/${n}`),"onProgress")});this.showStatus(`Imported ${t.imported} session(s) (${t.perSource.claude} Claude Code, ${t.perSource.opencode} OpenCode). Use /resume to open them.`)}catch(e){this.showError(`Import failed: ${e.message}`)}}async _refreshModelsWhenIdle(){if(!this.session.isStreaming)try{await Ze(se(),108e5)&&!this.session.isStreaming&&(this.session.modelRegistry.refresh(),this.showStatus("Model list refreshed from OpenAdapter."),this.ui.requestRender())}catch{}}_checkForUpdate(){kt(this.version).then(e=>{if(!e||this.lastNotifiedUpdateVersion===e.version)return;this.lastNotifiedUpdateVersion=e.version;const s=process.env.KODA_NO_AUTO_UPDATE!=="1";this.showNewVersionNotification(e,s),s&&mt().then(t=>{t?this.showStatus(`Updated to ${e.version} \u2014 restart koda to apply.`):this.showStatus(`Auto-update didn't finish \u2014 run \`koda update\` to get ${e.version}.`)})})}showNewVersionNotification(e,s=!1){const t=a.fg("accent",`${z} update`),i=s?"Updating Koda":"Update Available",n=s?a.fg("muted",`Updating to ${e.version} in the background \u2014 you'll get a restart prompt when it's ready.`):a.fg("muted",`New version ${e.version} is available. Run `)+t,o="https://openadapter.in/changelog",r=_e().hyperlinks?Ne(a.fg("accent","open changelog"),o):a.fg("accent",o),d=a.fg("muted","Changelog: ")+r,l=e.note?.trim();this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k(g=>a.fg("warning",g))),this.chatContainer.addChild(new f(`${a.bold(a.fg("warning",i))}
|
|
29
29
|
${n}`,1,0)),l&&(this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new B(l,1,0,this.getMarkdownThemeWithSettings(),{color:h(g=>a.fg("muted",g),"color")})),this.chatContainer.addChild(new p(1))),this.chatContainer.addChild(new f(d,1,0)),this.chatContainer.addChild(new k(g=>a.fg("warning",g))),this.ui.requestRender()}showPackageUpdateNotification(e){const s=a.fg("accent",`${z} update`),t=a.fg("muted","Package updates are available. Run ")+s,i=e.map(n=>`- ${n}`).join(`
|
|
@@ -35,8 +35,8 @@ ${i}`,1,0)),this.chatContainer.addChild(new k(n=>a.fg("warning",n))),this.ui.req
|
|
|
35
35
|
`),o=e?.currentText??this.editor.getText(),r=[n,o].filter(d=>d.trim()).join(`
|
|
36
36
|
|
|
37
37
|
`);return this.editor.setText(r),this.updatePendingMessagesDisplay(),e?.abort&&this.agent.abort(),i.length}queueCompactionMessage(e,s){this.compactionQueuedMessages.push({text:e,mode:s}),this.editor.addToHistory?.(e),this.editor.setText(""),this.updatePendingMessagesDisplay(),this.showStatus("Queued message for after compaction")}isExtensionCommand(e){if(!e.startsWith("/"))return!1;const s=this.session.extensionRunner,t=e.indexOf(" "),i=t===-1?e.slice(1):e.slice(1,t);return!!s.getCommand(i)}async flushCompactionQueue(e){if(this.compactionQueuedMessages.length===0)return;const s=[...this.compactionQueuedMessages];this.compactionQueuedMessages=[],this.updatePendingMessagesDisplay();const t=h(i=>{this.session.clearQueue(),this.compactionQueuedMessages=s,this.updatePendingMessagesDisplay(),this.showError(`Failed to send queued message${s.length>1?"s":""}: ${i instanceof Error?i.message:String(i)}`)},"restoreQueue");try{if(e?.willRetry){for(const l of s)this.isExtensionCommand(l.text)?await this.session.prompt(l.text):l.mode==="followUp"?await this.session.followUp(l.text):await this.session.steer(l.text);this.updatePendingMessagesDisplay();return}const i=s.findIndex(l=>!this.isExtensionCommand(l.text));if(i===-1){for(const l of s)await this.session.prompt(l.text);return}const n=s.slice(0,i),o=s[i],r=s.slice(i+1);for(const l of n)await this.session.prompt(l.text);const d=this.session.prompt(o.text).catch(l=>{t(l)});for(const l of r)this.isExtensionCommand(l.text)?await this.session.prompt(l.text):l.mode==="followUp"?await this.session.followUp(l.text):await this.session.steer(l.text);this.updatePendingMessagesDisplay()}catch(i){t(i)}}flushPendingBashComponents(){for(const e of this.pendingBashComponents)this.pendingMessagesContainer.removeChild(e),this.chatContainer.addChild(e);this.pendingBashComponents=[]}showSelector(e){const s=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor)},"done"),{component:t,focus:i}=e(s);this.editorContainer.clear(),this.editorContainer.addChild(t),this.ui.setFocus(i),this.ui.requestRender()}showSettingsSelector(){this.showSelector(e=>{const s=new qt({autoCompact:this.session.autoCompactionEnabled,showImages:this.settingsManager.getShowImages(),imageWidthCells:this.settingsManager.getImageWidthCells(),autoResizeImages:this.settingsManager.getImageAutoResize(),blockImages:this.settingsManager.getBlockImages(),enableSkillCommands:this.settingsManager.getEnableSkillCommands(),steeringMode:this.session.steeringMode,followUpMode:this.session.followUpMode,transport:this.settingsManager.getTransport(),httpIdleTimeoutMs:this.settingsManager.getHttpIdleTimeoutMs(),thinkingLevel:this.session.thinkingLevel,availableThinkingLevels:this.session.getAvailableThinkingLevels(),currentTheme:this.settingsManager.getTheme()||"dark",availableThemes:_t(),hideThinkingBlock:this.hideThinkingBlock,collapseChangelog:this.settingsManager.getCollapseChangelog(),enableInstallTelemetry:this.settingsManager.getEnableInstallTelemetry(),doubleEscapeAction:this.settingsManager.getDoubleEscapeAction(),treeFilterMode:this.settingsManager.getTreeFilterMode(),showHardwareCursor:this.settingsManager.getShowHardwareCursor(),editorPaddingX:this.settingsManager.getEditorPaddingX(),autocompleteMaxVisible:this.settingsManager.getAutocompleteMaxVisible(),quietStartup:this.settingsManager.getQuietStartup(),clearOnShrink:this.settingsManager.getClearOnShrink(),showTerminalProgress:this.settingsManager.getShowTerminalProgress(),warnings:this.settingsManager.getWarnings()},{onAutoCompactChange:h(t=>{this.session.setAutoCompactionEnabled(t),this.footer.setAutoCompactEnabled(t)},"onAutoCompactChange"),onShowImagesChange:h(t=>{this.settingsManager.setShowImages(t);for(const i of this.chatContainer.children)i instanceof H&&i.setShowImages(t)},"onShowImagesChange"),onImageWidthCellsChange:h(t=>{this.settingsManager.setImageWidthCells(t);for(const i of this.chatContainer.children)i instanceof H&&i.setImageWidthCells(t)},"onImageWidthCellsChange"),onAutoResizeImagesChange:h(t=>{this.settingsManager.setImageAutoResize(t)},"onAutoResizeImagesChange"),onBlockImagesChange:h(t=>{this.settingsManager.setBlockImages(t)},"onBlockImagesChange"),onEnableSkillCommandsChange:h(t=>{this.settingsManager.setEnableSkillCommands(t),this.setupAutocompleteProvider()},"onEnableSkillCommandsChange"),onSteeringModeChange:h(t=>{this.session.setSteeringMode(t)},"onSteeringModeChange"),onFollowUpModeChange:h(t=>{this.session.setFollowUpMode(t)},"onFollowUpModeChange"),onTransportChange:h(t=>{this.settingsManager.setTransport(t),this.session.agent.transport=t},"onTransportChange"),onHttpIdleTimeoutMsChange:h(t=>{this.settingsManager.setHttpIdleTimeoutMs(t),V(t),this.showStatus(`HTTP idle timeout: ${at(t)}`)},"onHttpIdleTimeoutMsChange"),onThinkingLevelChange:h(t=>{this.session.setThinkingLevel(t),this.footer.invalidate(),this.updateEditorBorderColor()},"onThinkingLevelChange"),onThemeChange:h(t=>{const i=O(t,!0);this.settingsManager.setTheme(t),this.ui.invalidate(),i.success||this.showError(`Failed to load theme "${t}": ${i.error}
|
|
38
|
-
Fell back to dark theme.`)},"onThemeChange"),onThemePreview:h(t=>{O(t,!0).success&&(this.ui.invalidate(),this.ui.requestRender())},"onThemePreview"),onHideThinkingBlockChange:h(t=>{this.hideThinkingBlock=t,this.settingsManager.setHideThinkingBlock(t);for(const i of this.chatContainer.children)i instanceof U&&i.setHideThinkingBlock(t);this.chatContainer.clear(),this.rebuildChatFromMessages()},"onHideThinkingBlockChange"),onCollapseChangelogChange:h(t=>{this.settingsManager.setCollapseChangelog(t)},"onCollapseChangelogChange"),onEnableInstallTelemetryChange:h(t=>{this.settingsManager.setEnableInstallTelemetry(t)},"onEnableInstallTelemetryChange"),onQuietStartupChange:h(t=>{this.settingsManager.setQuietStartup(t)},"onQuietStartupChange"),onDoubleEscapeActionChange:h(t=>{this.settingsManager.setDoubleEscapeAction(t)},"onDoubleEscapeActionChange"),onTreeFilterModeChange:h(t=>{this.settingsManager.setTreeFilterMode(t)},"onTreeFilterModeChange"),onShowHardwareCursorChange:h(t=>{this.settingsManager.setShowHardwareCursor(t),this.ui.setShowHardwareCursor(t)},"onShowHardwareCursorChange"),onEditorPaddingXChange:h(t=>{this.settingsManager.setEditorPaddingX(t),this.defaultEditor.setPaddingX(t),this.editor!==this.defaultEditor&&this.editor.setPaddingX!==void 0&&this.editor.setPaddingX(t)},"onEditorPaddingXChange"),onAutocompleteMaxVisibleChange:h(t=>{this.settingsManager.setAutocompleteMaxVisible(t),this.defaultEditor.setAutocompleteMaxVisible(t),this.editor!==this.defaultEditor&&this.editor.setAutocompleteMaxVisible!==void 0&&this.editor.setAutocompleteMaxVisible(t)},"onAutocompleteMaxVisibleChange"),onClearOnShrinkChange:h(t=>{this.settingsManager.setClearOnShrink(t),this.ui.setClearOnShrink(t)},"onClearOnShrinkChange"),onShowTerminalProgressChange:h(t=>{this.settingsManager.setShowTerminalProgress(t)},"onShowTerminalProgressChange"),onWarningsChange:h(t=>{this.settingsManager.setWarnings(t)},"onWarningsChange"),onImportSessions:h(()=>{e(),this.ui.requestRender(),this.runImportFromSettings()},"onImportSessions"),onCancel:h(()=>{e(),this.ui.requestRender()},"onCancel")});return{component:s,focus:s.getSettingsList()}})}async handleModelCommand(e){if(!e){this.showModelSelector();return}const s=await this.findExactModelMatch(e);if(s){try{await this.session.setModel(s),this.footer.invalidate(),this.updateEditorBorderColor(),this.showStatus(`Model: ${s.id}`),this.maybeWarnAboutAnthropicSubscriptionAuth(s),this.checkDaxnutsEasterEgg(s)}catch(t){this.showError(t instanceof Error?t.message:String(t))}return}this.showModelSelector(e)}async findExactModelMatch(e){const s=await this.getModelCandidates();return lt(e,s)}async getModelCandidates(){if(this.session.scopedModels.length>0)return this.session.scopedModels.map(e=>e.model);this.session.modelRegistry.refresh();try{return await this.session.modelRegistry.getAvailable()}catch{return[]}}async updateAvailableProviderCount(){const e=await this.getModelCandidates(),s=new Set(e.map(t=>t.provider));this.footerDataProvider.setAvailableProviderCount(s.size)}async maybeWarnAboutAnthropicSubscriptionAuth(e=this.session.model){if(this.settingsManager.getWarnings().anthropicExtraUsage===!1||this.anthropicSubscriptionWarningShown||!e||e.provider!=="anthropic")return;if(this.session.modelRegistry.authStorage.get("anthropic")?.type==="oauth"){this.anthropicSubscriptionWarningShown=!0,this.showWarning(Se);return}try{const t=await this.session.modelRegistry.getApiKeyForProvider(e.provider);if(!Zt(t))return;this.anthropicSubscriptionWarningShown=!0,this.showWarning(Se)}catch{}}showModelSelector(e){this.showSelector(s=>{const t=new Wt(this.ui,this.session.model,this.settingsManager,this.session.modelRegistry,this.session.scopedModels,async i=>{try{await this.session.setModel(i),this.footer.invalidate(),this.updateEditorBorderColor(),s(),this.showStatus(`Model: ${i.id}`),this.maybeWarnAboutAnthropicSubscriptionAuth(i),this.checkDaxnutsEasterEgg(i)}catch(n){s(),this.showError(n instanceof Error?n.message:String(n))}},()=>{s(),this.ui.requestRender()},e);return{component:t,focus:t}})}async showModelsSelector(){this.session.modelRegistry.refresh();const e=this.session.modelRegistry.getAvailable();if(e.length===0){this.showStatus("No models available");return}const s=this.session.scopedModels,t=s.length>0;let i=null;if(t)i=s.map(o=>`${o.model.provider}/${o.model.id}`);else{const o=this.settingsManager.getEnabledModels();o!==void 0&&o.length>0&&(i=(await oe(o,this.session.modelRegistry)).map(d=>`${d.model.provider}/${d.model.id}`))}const n=h(async o=>{if(i=o===null?null:[...o],o&&o.length>0&&o.length<e.length){const r=await oe(o,this.session.modelRegistry);this.session.setScopedModels(r.map(d=>({model:d.model,thinkingLevel:d.thinkingLevel})))}else this.session.setScopedModels([]);await this.updateAvailableProviderCount(),this.ui.requestRender()},"updateSessionModels");this.showSelector(o=>{const r=new Ht({allModels:e,enabledModelIds:i},{onChange:h(async d=>{await n(d)},"onChange"),onPersist:h(d=>{const l=d===null||d.length===e.length?void 0:d;this.settingsManager.setEnabledModels(l?[...l]:void 0),this.showStatus("Model selection saved to settings")},"onPersist"),onCancel:h(()=>{o(),this.ui.requestRender()},"onCancel")});return{component:r,focus:r}})}showUserMessageSelector(){const e=this.session.getUserMessagesForForking();if(e.length===0){this.showStatus("No messages to fork from");return}const s=e[e.length-1]?.entryId;this.showSelector(t=>{const i=new Ot(e.map(n=>({id:n.entryId,text:n.text})),async n=>{try{const o=await this.runtimeHost.fork(n);if(o.cancelled){t(),this.ui.requestRender();return}this.renderCurrentSessionState(),this.editor.setText(o.selectedText??""),t(),this.showStatus("Forked to new session")}catch(o){t(),this.showError(o instanceof Error?o.message:String(o))}},()=>{t(),this.ui.requestRender()},s);return{component:i,focus:i.getMessageList()}})}async handleCloneCommand(){const e=this.sessionManager.getLeafId();if(!e){this.showStatus("Nothing to clone yet");return}try{if((await this.runtimeHost.fork(e,{position:"at"})).cancelled){this.ui.requestRender();return}this.renderCurrentSessionState(),this.editor.setText(""),this.showStatus("Cloned to new session")}catch(s){this.showError(s instanceof Error?s.message:String(s))}}showTreeSelector(e){const s=this.sessionManager.getTree(),t=this.sessionManager.getLeafId(),i=this.settingsManager.getTreeFilterMode();if(s.length===0){this.showStatus("No entries in session");return}this.showSelector(n=>{const o=new Ut(s,t,this.ui.terminal.rows,async r=>{if(r===t){n(),this.showStatus("Already at this point");return}n();let d=!1,l;if(!this.settingsManager.getBranchSummarySkipPrompt())for(;;){const c=await this.showExtensionSelector("Summarize branch?",["No summary","Summarize","Summarize with custom prompt"]);if(c===void 0){this.showTreeSelector(r);return}if(d=c!=="No summary",!(c==="Summarize with custom prompt"&&(l=await this.showExtensionEditor("Custom summarization instructions"),l===void 0)))break}let g;const u=this.defaultEditor.onEscape;d&&(this.defaultEditor.onEscape=()=>{this.session.abortBranchSummary()},this.chatContainer.addChild(new p(1)),g=new L(this.ui,c=>a.fg("accent",c),c=>a.fg("muted",c),`Summarizing branch... (${$("app.interrupt")} to cancel)`),this.statusContainer.addChild(g),this.ui.requestRender());try{const c=await this.session.navigateTree(r,{summarize:d,customInstructions:l});if(c.aborted){this.showStatus("Branch summarization cancelled"),this.showTreeSelector(r);return}if(c.cancelled){this.showStatus("Navigation cancelled");return}this.chatContainer.clear(),this.renderInitialMessages(),c.editorText&&!this.editor.getText().trim()&&this.editor.setText(c.editorText),this.showStatus("Navigated to selected point"),this.flushCompactionQueue({willRetry:!1})}catch(c){this.showError(c instanceof Error?c.message:String(c))}finally{g&&(g.stop(),this.statusContainer.clear()),this.defaultEditor.onEscape=u}},()=>{n(),this.ui.requestRender()},(r,d)=>{this.sessionManager.appendLabelChange(r,d),this.ui.requestRender()},e,i);return{component:o,focus:o}})}showSessionSelector(){this.showSelector(e=>{const s=new Ft(t=>W.list(this.sessionManager.getCwd(),this.sessionManager.getSessionDir(),t),t=>this.sessionManager.usesDefaultSessionDir()?W.listAll(t):W.listAll(this.sessionManager.getSessionDir(),t),async t=>{e(),await this.handleResumeSession(t)},()=>{e(),this.ui.requestRender()},()=>{this.shutdown()},()=>this.ui.requestRender(),{renameSession:h(async(t,i)=>{const n=(i??"").trim();if(!n)return;W.open(t).appendSessionInfo(n)},"renameSession"),showRenameHint:!0,keybindings:this.keybindings},this.sessionManager.getSessionFile());return{component:s,focus:s}})}async handleResumeSession(e,s){this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear();try{const t=await this.runtimeHost.switchSession(e,{withSession:s?.withSession});return t.cancelled||(this.renderCurrentSessionState(),this.showStatus("Resumed session")),t}catch(t){if(t instanceof re){const i=await this.promptForMissingSessionCwd(t);if(!i)return this.showStatus("Resume cancelled"),{cancelled:!0};const n=await this.runtimeHost.switchSession(e,{cwdOverride:i,withSession:s?.withSession});return n.cancelled||(this.renderCurrentSessionState(),this.showStatus("Resumed session in current cwd")),n}return this.handleFatalRuntimeError("Failed to resume session",t)}}getLoginProviderOptions(e){const t=this.session.modelRegistry.authStorage.getOAuthProviders(),i=new Set(t.map(d=>d.id)),n=t.map(d=>({id:d.id,name:d.name,authType:"oauth"})),o=new Set(this.session.modelRegistry.getAll().map(d=>d.provider));for(const d of o)as(d,i)&&n.push({id:d,name:this.session.modelRegistry.getProviderDisplayName(d),authType:"api_key"});return(e?n.filter(d=>d.authType===e):n).sort((d,l)=>d.name.localeCompare(l.name))}getLogoutProviderOptions(){const e=this.session.modelRegistry.authStorage,s=[];for(const t of e.list()){const i=e.get(t);i&&s.push({id:t,name:this.session.modelRegistry.getProviderDisplayName(t),authType:i.type})}return s.sort((t,i)=>t.name.localeCompare(i.name))}showLoginAuthTypeSelector(){const e="Use a subscription",s="Use an API key";this.showSelector(t=>{const i=new X("Select authentication method:",[e,s],n=>{t();const o=n===e?"oauth":"api_key";this.showLoginProviderSelector(o)},()=>{t(),this.ui.requestRender()});return{component:i,focus:i}})}showLoginProviderSelector(e){const s=this.getLoginProviderOptions(e);if(s.length===0){this.showStatus(e==="oauth"?"No subscription providers available.":"No API key providers available.");return}this.showSelector(t=>{const i=new me("login",this.session.modelRegistry.authStorage,s,async n=>{t();const o=s.find(r=>r.id===n);o&&(o.authType==="oauth"?await this.showLoginDialog(o.id,o.name):o.id===os?this.showBedrockSetupDialog(o.id,o.name):await this.showApiKeyLoginDialog(o.id,o.name))},()=>{t(),this.showLoginAuthTypeSelector()},n=>this.session.modelRegistry.getProviderAuthStatus(n));return{component:i,focus:i}})}async showOAuthSelector(e){if(e==="login"){this.showLoginAuthTypeSelector();return}const s=this.getLogoutProviderOptions();if(s.length===0){this.showStatus("No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.");return}this.showSelector(t=>{const i=new me(e,this.session.modelRegistry.authStorage,s,async n=>{t();const o=s.find(r=>r.id===n);if(o)try{this.session.modelRegistry.authStorage.logout(o.id),this.session.modelRegistry.refresh(),await this.updateAvailableProviderCount();const r=o.authType==="oauth"?`Logged out of ${o.name}`:`Removed stored API key for ${o.name}. Environment variables and models.json config are unchanged.`;this.showStatus(r)}catch(r){this.showError(`Logout failed: ${r instanceof Error?r.message:String(r)}`)}},()=>{t(),this.ui.requestRender()});return{component:i,focus:i}})}async completeProviderAuthentication(e,s,t,i){this.session.modelRegistry.refresh();const n=t==="oauth"?`Logged in to ${s}`:`Saved API key for ${s}`;let o,r;if(es(i)){const l=this.session.modelRegistry.getAvailable().filter(g=>g.provider===e);if(!ns(e))r=`${n}, but no default model is configured for provider "${e}". Use /model to select a model.`;else if(l.length===0)r=`${n}, but no models are available for that provider. Use /model to select a model.`;else{const g=ne[e];if(o=l.find(u=>u.id===g),!o)r=`${n}, but its default model "${g}" is not available. Use /model to select a model.`;else try{await this.session.setModel(o)}catch(u){o=void 0;const c=u instanceof Error?u.message:String(u);r=`${n}, but selecting its default model failed: ${c}. Use /model to select a model.`}}}await this.updateAvailableProviderCount(),this.footer.invalidate(),this.updateEditorBorderColor(),o?(this.showStatus(`${n}. Selected ${o.id}. Credentials saved to ${ie()}`),this.maybeWarnAboutAnthropicSubscriptionAuth(o),this.checkDaxnutsEasterEgg(o)):(this.showStatus(`${n}. Credentials saved to ${ie()}`),r?this.showError(r):this.maybeWarnAboutAnthropicSubscriptionAuth())}showBedrockSetupDialog(e,s){const t=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor"),i=new J(this.ui,e,()=>t(),s,"Amazon Bedrock setup");i.showInfo([a.fg("text","Amazon Bedrock uses AWS credentials instead of a single API key."),a.fg("text","Configure an AWS profile, IAM keys, bearer token, or role-based credentials."),a.fg("muted","See:"),a.fg("accent",` ${y.join(tt(),"providers.md")}`)]),this.editorContainer.clear(),this.editorContainer.addChild(i),this.ui.setFocus(i),this.ui.requestRender()}async showApiKeyLoginDialog(e,s){const t=this.session.model,i=new J(this.ui,e,(o,r)=>{},s);this.editorContainer.clear(),this.editorContainer.addChild(i),this.ui.setFocus(i),this.ui.requestRender();const n=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor");try{const o=(await i.showPrompt("Enter API key:")).trim();if(!o)throw new Error("API key cannot be empty.");this.session.modelRegistry.authStorage.set(e,{type:"api_key",key:o}),n(),await this.completeProviderAuthentication(e,s,"api_key",t)}catch(o){n();const r=o instanceof Error?o.message:String(o);r!=="Login cancelled"&&this.showError(`Failed to save API key for ${s}: ${r}`)}}showOAuthLoginSelect(e,s){return new Promise(t=>{const i=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(e),this.ui.setFocus(e),this.ui.requestRender()},"restoreDialog"),n=s.options.map(r=>r.label),o=new X(s.message,n,r=>{i(),t(s.options.find(d=>d.label===r)?.id)},()=>{i(),t(void 0)});this.editorContainer.clear(),this.editorContainer.addChild(o),this.ui.setFocus(o),this.ui.requestRender()})}async showLoginDialog(e,s){const t=this.session.modelRegistry.authStorage.getOAuthProviders().find(u=>u.id===e),i=this.session.model,n=t?.usesCallbackServer??!1,o=new J(this.ui,e,(u,c)=>{},s);this.editorContainer.clear(),this.editorContainer.addChild(o),this.ui.setFocus(o),this.ui.requestRender();let r,d;const l=new Promise((u,c)=>{r=u,d=c}),g=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor");try{await this.session.modelRegistry.authStorage.login(e,{onAuth:h(u=>{o.showAuth(u.url,u.instructions),n&&o.showManualInput("Paste redirect URL below, or complete login in browser:").then(c=>{c&&r&&(r(c),r=void 0)}).catch(()=>{d&&(d(new Error("Login cancelled")),d=void 0)})},"onAuth"),onDeviceCode:h(u=>{o.showDeviceCode(u),o.showWaiting("Waiting for authentication...")},"onDeviceCode"),onPrompt:h(async u=>o.showPrompt(u.message,u.placeholder),"onPrompt"),onProgress:h(u=>{o.showProgress(u)},"onProgress"),onSelect:h(u=>this.showOAuthLoginSelect(o,u),"onSelect"),onManualCodeInput:h(()=>l,"onManualCodeInput"),signal:o.signal}),g(),await this.completeProviderAuthentication(e,s,"oauth",i)}catch(u){g();const c=u instanceof Error?u.message:String(u);c!=="Login cancelled"&&this.showError(`Failed to login to ${s}: ${c}`)}}async handleReloadCommand(){if(this.session.isStreaming){this.showWarning("Wait for the current response to finish before reloading.");return}if(this.session.isCompacting){this.showWarning("Wait for compaction to finish before reloading.");return}this.resetExtensionUI();const e=new A,s=h(n=>a.fg("border",n),"borderColor");e.addChild(new k(s)),e.addChild(new p(1)),e.addChild(new f(a.fg("muted","Reloading keybindings, extensions, skills, prompts, themes..."),1,0)),e.addChild(new p(1)),e.addChild(new k(s));const t=this.editor;this.editorContainer.clear(),this.editorContainer.addChild(e),this.ui.setFocus(e),this.ui.requestRender(!0),await new Promise(n=>process.nextTick(n));const i=h(n=>{this.editorContainer.clear(),this.editorContainer.addChild(n),this.ui.setFocus(n),this.ui.requestRender()},"dismissReloadBox");try{await this.session.reload(),V(this.settingsManager.getHttpIdleTimeoutMs()),this.keybindings.reload();const n=this.customHeader??this.builtInHeader;F(n)&&n.setExpanded(this.toolOutputExpanded),Y(this.session.resourceLoader.getThemes().themes),this.hideThinkingBlock=this.settingsManager.getHideThinkingBlock();const o=this.settingsManager.getTheme(),r=o?O(o,!0):{success:!0};r.success||this.showError(`Failed to load theme "${o}": ${r.error}
|
|
39
|
-
Fell back to dark theme.`);const d=this.settingsManager.getEditorPaddingX(),l=this.settingsManager.getAutocompleteMaxVisible();this.defaultEditor.setPaddingX(d),this.defaultEditor.setAutocompleteMaxVisible(l),this.editor!==this.defaultEditor&&(this.editor.setPaddingX?.(d),this.editor.setAutocompleteMaxVisible?.(l)),this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor()),this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink()),this.setupAutocompleteProvider();const g=this.session.extensionRunner;this.setupExtensionShortcuts(g),this.rebuildChatFromMessages(),i(this.editor),this.showLoadedResources({force:!1,showDiagnosticsWhenQuiet:!0});const u=this.session.modelRegistry.getError();u&&this.showError(`models.json error: ${u}`),this.showStatus("Reloaded keybindings, extensions, skills, prompts, themes")}catch(n){i(t),this.showError(`Reload failed: ${n instanceof Error?n.message:String(n)}`)}}async handleExportCommand(e){const s=this.getPathCommandArgument(e,"/export");try{if(s?.endsWith(".jsonl")){const t=this.session.exportToJsonl(s);this.showStatus(`Session exported to: ${t}`)}else{const t=await this.session.exportToHtml(s);this.showStatus(`Session exported to: ${t}`)}}catch(t){this.showError(`Failed to export session: ${t instanceof Error?t.message:"Unknown error"}`)}}getPathCommandArgument(e,s){if(e===s||!e.startsWith(`${s} `))return;const t=e.slice(s.length+1).trimStart();if(!t)return;const i=t[0];if(i==='"'||i==="'"){const o=t.indexOf(i,1);return o<0?void 0:t.slice(1,o)}const n=t.search(/\s/);return n<0?t:t.slice(0,n)}async handleImportCommand(e){const s=this.getPathCommandArgument(e,"/import");if(!s){this.showError("Usage: /import <path.jsonl>");return}if(!await this.showExtensionConfirm("Import session",`Replace current session with ${s}?`)){this.showStatus("Import cancelled");return}try{if(this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear(),(await this.runtimeHost.importFromJsonl(s)).cancelled){this.showStatus("Import cancelled");return}this.renderCurrentSessionState(),this.showStatus(`Session imported from: ${s}`)}catch(i){if(i instanceof re){const n=await this.promptForMissingSessionCwd(i);if(!n){this.showStatus("Import cancelled");return}if((await this.runtimeHost.importFromJsonl(s,n)).cancelled){this.showStatus("Import cancelled");return}this.renderCurrentSessionState(),this.showStatus(`Session imported from: ${s}`);return}if(i instanceof ot){this.showError(`Failed to import session: ${i.message}`);return}await this.handleFatalRuntimeError("Failed to import session",i)}}async handleShareCommand(){try{if(Xe("gh",["auth","status"],{encoding:"utf-8"}).status!==0){this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");return}}catch{this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");return}const e=
|
|
38
|
+
Fell back to dark theme.`)},"onThemeChange"),onThemePreview:h(t=>{O(t,!0).success&&(this.ui.invalidate(),this.ui.requestRender())},"onThemePreview"),onHideThinkingBlockChange:h(t=>{this.hideThinkingBlock=t,this.settingsManager.setHideThinkingBlock(t);for(const i of this.chatContainer.children)i instanceof U&&i.setHideThinkingBlock(t);this.chatContainer.clear(),this.rebuildChatFromMessages()},"onHideThinkingBlockChange"),onCollapseChangelogChange:h(t=>{this.settingsManager.setCollapseChangelog(t)},"onCollapseChangelogChange"),onEnableInstallTelemetryChange:h(t=>{this.settingsManager.setEnableInstallTelemetry(t)},"onEnableInstallTelemetryChange"),onQuietStartupChange:h(t=>{this.settingsManager.setQuietStartup(t)},"onQuietStartupChange"),onDoubleEscapeActionChange:h(t=>{this.settingsManager.setDoubleEscapeAction(t)},"onDoubleEscapeActionChange"),onTreeFilterModeChange:h(t=>{this.settingsManager.setTreeFilterMode(t)},"onTreeFilterModeChange"),onShowHardwareCursorChange:h(t=>{this.settingsManager.setShowHardwareCursor(t),this.ui.setShowHardwareCursor(t)},"onShowHardwareCursorChange"),onEditorPaddingXChange:h(t=>{this.settingsManager.setEditorPaddingX(t),this.defaultEditor.setPaddingX(t),this.editor!==this.defaultEditor&&this.editor.setPaddingX!==void 0&&this.editor.setPaddingX(t)},"onEditorPaddingXChange"),onAutocompleteMaxVisibleChange:h(t=>{this.settingsManager.setAutocompleteMaxVisible(t),this.defaultEditor.setAutocompleteMaxVisible(t),this.editor!==this.defaultEditor&&this.editor.setAutocompleteMaxVisible!==void 0&&this.editor.setAutocompleteMaxVisible(t)},"onAutocompleteMaxVisibleChange"),onClearOnShrinkChange:h(t=>{this.settingsManager.setClearOnShrink(t),this.ui.setClearOnShrink(t)},"onClearOnShrinkChange"),onShowTerminalProgressChange:h(t=>{this.settingsManager.setShowTerminalProgress(t)},"onShowTerminalProgressChange"),onWarningsChange:h(t=>{this.settingsManager.setWarnings(t)},"onWarningsChange"),onImportSessions:h(()=>{e(),this.ui.requestRender(),this.runImportFromSettings()},"onImportSessions"),onCancel:h(()=>{e(),this.ui.requestRender()},"onCancel")});return{component:s,focus:s.getSettingsList()}})}async handleModelCommand(e){if(!e){this.showModelSelector();return}const s=await this.findExactModelMatch(e);if(s){try{await this.session.setModel(s),this.footer.invalidate(),this.updateEditorBorderColor(),this.showStatus(`Model: ${s.id}`),this.maybeWarnAboutAnthropicSubscriptionAuth(s),this.checkDaxnutsEasterEgg(s)}catch(t){this.showError(t instanceof Error?t.message:String(t))}return}this.showModelSelector(e)}async findExactModelMatch(e){const s=await this.getModelCandidates();return lt(e,s)}async getModelCandidates(){if(this.session.scopedModels.length>0)return this.session.scopedModels.map(e=>e.model);this.session.modelRegistry.refresh();try{return await this.session.modelRegistry.getAvailable()}catch{return[]}}async updateAvailableProviderCount(){const e=await this.getModelCandidates(),s=new Set(e.map(t=>t.provider));this.footerDataProvider.setAvailableProviderCount(s.size)}async maybeWarnAboutAnthropicSubscriptionAuth(e=this.session.model){if(this.settingsManager.getWarnings().anthropicExtraUsage===!1||this.anthropicSubscriptionWarningShown||!e||e.provider!=="anthropic")return;if(this.session.modelRegistry.authStorage.get("anthropic")?.type==="oauth"){this.anthropicSubscriptionWarningShown=!0,this.showWarning(Se);return}try{const t=await this.session.modelRegistry.getApiKeyForProvider(e.provider);if(!Zt(t))return;this.anthropicSubscriptionWarningShown=!0,this.showWarning(Se)}catch{}}showModelSelector(e){this.showSelector(s=>{const t=new Wt(this.ui,this.session.model,this.settingsManager,this.session.modelRegistry,this.session.scopedModels,async i=>{try{await this.session.setModel(i),this.footer.invalidate(),this.updateEditorBorderColor(),s(),this.showStatus(`Model: ${i.id}`),this.maybeWarnAboutAnthropicSubscriptionAuth(i),this.checkDaxnutsEasterEgg(i)}catch(n){s(),this.showError(n instanceof Error?n.message:String(n))}},()=>{s(),this.ui.requestRender()},e);return{component:t,focus:t}})}async showModelsSelector(){this.session.modelRegistry.refresh();const e=this.session.modelRegistry.getAvailable();if(e.length===0){this.showStatus("No models available");return}const s=this.session.scopedModels,t=s.length>0;let i=null;if(t)i=s.map(o=>`${o.model.provider}/${o.model.id}`);else{const o=this.settingsManager.getEnabledModels();o!==void 0&&o.length>0&&(i=(await oe(o,this.session.modelRegistry)).map(d=>`${d.model.provider}/${d.model.id}`))}const n=h(async o=>{if(i=o===null?null:[...o],o&&o.length>0&&o.length<e.length){const r=await oe(o,this.session.modelRegistry);this.session.setScopedModels(r.map(d=>({model:d.model,thinkingLevel:d.thinkingLevel})))}else this.session.setScopedModels([]);await this.updateAvailableProviderCount(),this.ui.requestRender()},"updateSessionModels");this.showSelector(o=>{const r=new Ht({allModels:e,enabledModelIds:i},{onChange:h(async d=>{await n(d)},"onChange"),onPersist:h(d=>{const l=d===null||d.length===e.length?void 0:d;this.settingsManager.setEnabledModels(l?[...l]:void 0),this.showStatus("Model selection saved to settings")},"onPersist"),onCancel:h(()=>{o(),this.ui.requestRender()},"onCancel")});return{component:r,focus:r}})}showUserMessageSelector(){const e=this.session.getUserMessagesForForking();if(e.length===0){this.showStatus("No messages to fork from");return}const s=e[e.length-1]?.entryId;this.showSelector(t=>{const i=new Ot(e.map(n=>({id:n.entryId,text:n.text})),async n=>{try{const o=await this.runtimeHost.fork(n);if(o.cancelled){t(),this.ui.requestRender();return}this.renderCurrentSessionState(),this.editor.setText(o.selectedText??""),t(),this.showStatus("Forked to new session")}catch(o){t(),this.showError(o instanceof Error?o.message:String(o))}},()=>{t(),this.ui.requestRender()},s);return{component:i,focus:i.getMessageList()}})}async handleCloneCommand(){const e=this.sessionManager.getLeafId();if(!e){this.showStatus("Nothing to clone yet");return}try{if((await this.runtimeHost.fork(e,{position:"at"})).cancelled){this.ui.requestRender();return}this.renderCurrentSessionState(),this.editor.setText(""),this.showStatus("Cloned to new session")}catch(s){this.showError(s instanceof Error?s.message:String(s))}}showTreeSelector(e){const s=this.sessionManager.getTree(),t=this.sessionManager.getLeafId(),i=this.settingsManager.getTreeFilterMode();if(s.length===0){this.showStatus("No entries in session");return}this.showSelector(n=>{const o=new Ut(s,t,this.ui.terminal.rows,async r=>{if(r===t){n(),this.showStatus("Already at this point");return}n();let d=!1,l;if(!this.settingsManager.getBranchSummarySkipPrompt())for(;;){const c=await this.showExtensionSelector("Summarize branch?",["No summary","Summarize","Summarize with custom prompt"]);if(c===void 0){this.showTreeSelector(r);return}if(d=c!=="No summary",!(c==="Summarize with custom prompt"&&(l=await this.showExtensionEditor("Custom summarization instructions"),l===void 0)))break}let g;const u=this.defaultEditor.onEscape;d&&(this.defaultEditor.onEscape=()=>{this.session.abortBranchSummary()},this.chatContainer.addChild(new p(1)),g=new L(this.ui,c=>a.fg("accent",c),c=>a.fg("muted",c),`Summarizing branch... (${$("app.interrupt")} to cancel)`),this.statusContainer.addChild(g),this.ui.requestRender());try{const c=await this.session.navigateTree(r,{summarize:d,customInstructions:l});if(c.aborted){this.showStatus("Branch summarization cancelled"),this.showTreeSelector(r);return}if(c.cancelled){this.showStatus("Navigation cancelled");return}this.chatContainer.clear(),this.renderInitialMessages(),c.editorText&&!this.editor.getText().trim()&&this.editor.setText(c.editorText),this.showStatus("Navigated to selected point"),this.flushCompactionQueue({willRetry:!1})}catch(c){this.showError(c instanceof Error?c.message:String(c))}finally{g&&(g.stop(),this.statusContainer.clear()),this.defaultEditor.onEscape=u}},()=>{n(),this.ui.requestRender()},(r,d)=>{this.sessionManager.appendLabelChange(r,d),this.ui.requestRender()},e,i);return{component:o,focus:o}})}showSessionSelector(){this.showSelector(e=>{const s=new Ft(t=>W.list(this.sessionManager.getCwd(),this.sessionManager.getSessionDir(),t),t=>this.sessionManager.usesDefaultSessionDir()?W.listAll(t):W.listAll(this.sessionManager.getSessionDir(),t),async t=>{e(),await this.handleResumeSession(t)},()=>{e(),this.ui.requestRender()},()=>{this.shutdown()},()=>this.ui.requestRender(),{renameSession:h(async(t,i)=>{const n=(i??"").trim();if(!n)return;W.open(t).appendSessionInfo(n)},"renameSession"),showRenameHint:!0,keybindings:this.keybindings},this.sessionManager.getSessionFile());return{component:s,focus:s}})}async handleResumeSession(e,s){this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear();try{const t=await this.runtimeHost.switchSession(e,{withSession:s?.withSession});return t.cancelled||(this.renderCurrentSessionState(),this.showStatus("Resumed session")),t}catch(t){if(t instanceof re){const i=await this.promptForMissingSessionCwd(t);if(!i)return this.showStatus("Resume cancelled"),{cancelled:!0};const n=await this.runtimeHost.switchSession(e,{cwdOverride:i,withSession:s?.withSession});return n.cancelled||(this.renderCurrentSessionState(),this.showStatus("Resumed session in current cwd")),n}return this.handleFatalRuntimeError("Failed to resume session",t)}}getLoginProviderOptions(e){const t=this.session.modelRegistry.authStorage.getOAuthProviders(),i=new Set(t.map(d=>d.id)),n=t.map(d=>({id:d.id,name:d.name,authType:"oauth"})),o=new Set(this.session.modelRegistry.getAll().map(d=>d.provider));for(const d of o)as(d,i)&&n.push({id:d,name:this.session.modelRegistry.getProviderDisplayName(d),authType:"api_key"});return(e?n.filter(d=>d.authType===e):n).sort((d,l)=>d.name.localeCompare(l.name))}getLogoutProviderOptions(){const e=this.session.modelRegistry.authStorage,s=[];for(const t of e.list()){const i=e.get(t);i&&s.push({id:t,name:this.session.modelRegistry.getProviderDisplayName(t),authType:i.type})}return s.sort((t,i)=>t.name.localeCompare(i.name))}showLoginAuthTypeSelector(){const e="Use a subscription",s="Use an API key";this.showSelector(t=>{const i=new X("Select authentication method:",[e,s],n=>{t();const o=n===e?"oauth":"api_key";this.showLoginProviderSelector(o)},()=>{t(),this.ui.requestRender()});return{component:i,focus:i}})}showLoginProviderSelector(e){const s=this.getLoginProviderOptions(e);if(s.length===0){this.showStatus(e==="oauth"?"No subscription providers available.":"No API key providers available.");return}this.showSelector(t=>{const i=new me("login",this.session.modelRegistry.authStorage,s,async n=>{t();const o=s.find(r=>r.id===n);o&&(o.authType==="oauth"?await this.showLoginDialog(o.id,o.name):o.id===os?this.showBedrockSetupDialog(o.id,o.name):await this.showApiKeyLoginDialog(o.id,o.name))},()=>{t(),this.showLoginAuthTypeSelector()},n=>this.session.modelRegistry.getProviderAuthStatus(n));return{component:i,focus:i}})}async showOAuthSelector(e){if(e==="login"){this.showLoginAuthTypeSelector();return}const s=this.getLogoutProviderOptions();if(s.length===0){this.showStatus("No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.");return}this.showSelector(t=>{const i=new me(e,this.session.modelRegistry.authStorage,s,async n=>{t();const o=s.find(r=>r.id===n);if(o)try{this.session.modelRegistry.authStorage.logout(o.id),this.session.modelRegistry.refresh(),await this.updateAvailableProviderCount();const r=o.authType==="oauth"?`Logged out of ${o.name}`:`Removed stored API key for ${o.name}. Environment variables and models.json config are unchanged.`;this.showStatus(r)}catch(r){this.showError(`Logout failed: ${r instanceof Error?r.message:String(r)}`)}},()=>{t(),this.ui.requestRender()});return{component:i,focus:i}})}async completeProviderAuthentication(e,s,t,i){this.session.modelRegistry.refresh();const n=t==="oauth"?`Logged in to ${s}`:`Saved API key for ${s}`;let o,r;if(es(i)){const l=this.session.modelRegistry.getAvailable().filter(g=>g.provider===e);if(!ns(e))r=`${n}, but no default model is configured for provider "${e}". Use /model to select a model.`;else if(l.length===0)r=`${n}, but no models are available for that provider. Use /model to select a model.`;else{const g=ne[e];if(o=l.find(u=>u.id===g),!o)r=`${n}, but its default model "${g}" is not available. Use /model to select a model.`;else try{await this.session.setModel(o)}catch(u){o=void 0;const c=u instanceof Error?u.message:String(u);r=`${n}, but selecting its default model failed: ${c}. Use /model to select a model.`}}}await this.updateAvailableProviderCount(),this.footer.invalidate(),this.updateEditorBorderColor(),o?(this.showStatus(`${n}. Selected ${o.id}. Credentials saved to ${ie()}`),this.maybeWarnAboutAnthropicSubscriptionAuth(o),this.checkDaxnutsEasterEgg(o)):(this.showStatus(`${n}. Credentials saved to ${ie()}`),r?this.showError(r):this.maybeWarnAboutAnthropicSubscriptionAuth())}showBedrockSetupDialog(e,s){const t=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor"),i=new J(this.ui,e,()=>t(),s,"Amazon Bedrock setup");i.showInfo([a.fg("text","Amazon Bedrock uses AWS credentials instead of a single API key."),a.fg("text","Configure an AWS profile, IAM keys, bearer token, or role-based credentials."),a.fg("muted","See:"),a.fg("accent",` ${E.join(tt(),"providers.md")}`)]),this.editorContainer.clear(),this.editorContainer.addChild(i),this.ui.setFocus(i),this.ui.requestRender()}async showApiKeyLoginDialog(e,s){const t=this.session.model,i=new J(this.ui,e,(o,r)=>{},s);this.editorContainer.clear(),this.editorContainer.addChild(i),this.ui.setFocus(i),this.ui.requestRender();const n=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor");try{const o=(await i.showPrompt("Enter API key:")).trim();if(!o)throw new Error("API key cannot be empty.");this.session.modelRegistry.authStorage.set(e,{type:"api_key",key:o}),n(),await this.completeProviderAuthentication(e,s,"api_key",t)}catch(o){n();const r=o instanceof Error?o.message:String(o);r!=="Login cancelled"&&this.showError(`Failed to save API key for ${s}: ${r}`)}}showOAuthLoginSelect(e,s){return new Promise(t=>{const i=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(e),this.ui.setFocus(e),this.ui.requestRender()},"restoreDialog"),n=s.options.map(r=>r.label),o=new X(s.message,n,r=>{i(),t(s.options.find(d=>d.label===r)?.id)},()=>{i(),t(void 0)});this.editorContainer.clear(),this.editorContainer.addChild(o),this.ui.setFocus(o),this.ui.requestRender()})}async showLoginDialog(e,s){const t=this.session.modelRegistry.authStorage.getOAuthProviders().find(u=>u.id===e),i=this.session.model,n=t?.usesCallbackServer??!1,o=new J(this.ui,e,(u,c)=>{},s);this.editorContainer.clear(),this.editorContainer.addChild(o),this.ui.setFocus(o),this.ui.requestRender();let r,d;const l=new Promise((u,c)=>{r=u,d=c}),g=h(()=>{this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor),this.ui.requestRender()},"restoreEditor");try{await this.session.modelRegistry.authStorage.login(e,{onAuth:h(u=>{o.showAuth(u.url,u.instructions),n&&o.showManualInput("Paste redirect URL below, or complete login in browser:").then(c=>{c&&r&&(r(c),r=void 0)}).catch(()=>{d&&(d(new Error("Login cancelled")),d=void 0)})},"onAuth"),onDeviceCode:h(u=>{o.showDeviceCode(u),o.showWaiting("Waiting for authentication...")},"onDeviceCode"),onPrompt:h(async u=>o.showPrompt(u.message,u.placeholder),"onPrompt"),onProgress:h(u=>{o.showProgress(u)},"onProgress"),onSelect:h(u=>this.showOAuthLoginSelect(o,u),"onSelect"),onManualCodeInput:h(()=>l,"onManualCodeInput"),signal:o.signal}),g(),await this.completeProviderAuthentication(e,s,"oauth",i)}catch(u){g();const c=u instanceof Error?u.message:String(u);c!=="Login cancelled"&&this.showError(`Failed to login to ${s}: ${c}`)}}async handleReloadCommand(){if(this.session.isStreaming){this.showWarning("Wait for the current response to finish before reloading.");return}if(this.session.isCompacting){this.showWarning("Wait for compaction to finish before reloading.");return}this.resetExtensionUI();const e=new A,s=h(n=>a.fg("border",n),"borderColor");e.addChild(new k(s)),e.addChild(new p(1)),e.addChild(new f(a.fg("muted","Reloading keybindings, extensions, skills, prompts, themes..."),1,0)),e.addChild(new p(1)),e.addChild(new k(s));const t=this.editor;this.editorContainer.clear(),this.editorContainer.addChild(e),this.ui.setFocus(e),this.ui.requestRender(!0),await new Promise(n=>process.nextTick(n));const i=h(n=>{this.editorContainer.clear(),this.editorContainer.addChild(n),this.ui.setFocus(n),this.ui.requestRender()},"dismissReloadBox");try{await this.session.reload(),V(this.settingsManager.getHttpIdleTimeoutMs()),this.keybindings.reload();const n=this.customHeader??this.builtInHeader;F(n)&&n.setExpanded(this.toolOutputExpanded),Y(this.session.resourceLoader.getThemes().themes),this.hideThinkingBlock=this.settingsManager.getHideThinkingBlock();const o=this.settingsManager.getTheme(),r=o?O(o,!0):{success:!0};r.success||this.showError(`Failed to load theme "${o}": ${r.error}
|
|
39
|
+
Fell back to dark theme.`);const d=this.settingsManager.getEditorPaddingX(),l=this.settingsManager.getAutocompleteMaxVisible();this.defaultEditor.setPaddingX(d),this.defaultEditor.setAutocompleteMaxVisible(l),this.editor!==this.defaultEditor&&(this.editor.setPaddingX?.(d),this.editor.setAutocompleteMaxVisible?.(l)),this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor()),this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink()),this.setupAutocompleteProvider();const g=this.session.extensionRunner;this.setupExtensionShortcuts(g),this.rebuildChatFromMessages(),i(this.editor),this.showLoadedResources({force:!1,showDiagnosticsWhenQuiet:!0});const u=this.session.modelRegistry.getError();u&&this.showError(`models.json error: ${u}`),this.showStatus("Reloaded keybindings, extensions, skills, prompts, themes")}catch(n){i(t),this.showError(`Reload failed: ${n instanceof Error?n.message:String(n)}`)}}async handleExportCommand(e){const s=this.getPathCommandArgument(e,"/export");try{if(s?.endsWith(".jsonl")){const t=this.session.exportToJsonl(s);this.showStatus(`Session exported to: ${t}`)}else{const t=await this.session.exportToHtml(s);this.showStatus(`Session exported to: ${t}`)}}catch(t){this.showError(`Failed to export session: ${t instanceof Error?t.message:"Unknown error"}`)}}getPathCommandArgument(e,s){if(e===s||!e.startsWith(`${s} `))return;const t=e.slice(s.length+1).trimStart();if(!t)return;const i=t[0];if(i==='"'||i==="'"){const o=t.indexOf(i,1);return o<0?void 0:t.slice(1,o)}const n=t.search(/\s/);return n<0?t:t.slice(0,n)}async handleImportCommand(e){const s=this.getPathCommandArgument(e,"/import");if(!s){this.showError("Usage: /import <path.jsonl>");return}if(!await this.showExtensionConfirm("Import session",`Replace current session with ${s}?`)){this.showStatus("Import cancelled");return}try{if(this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear(),(await this.runtimeHost.importFromJsonl(s)).cancelled){this.showStatus("Import cancelled");return}this.renderCurrentSessionState(),this.showStatus(`Session imported from: ${s}`)}catch(i){if(i instanceof re){const n=await this.promptForMissingSessionCwd(i);if(!n){this.showStatus("Import cancelled");return}if((await this.runtimeHost.importFromJsonl(s,n)).cancelled){this.showStatus("Import cancelled");return}this.renderCurrentSessionState(),this.showStatus(`Session imported from: ${s}`);return}if(i instanceof ot){this.showError(`Failed to import session: ${i.message}`);return}await this.handleFatalRuntimeError("Failed to import session",i)}}async handleShareCommand(){try{if(Xe("gh",["auth","status"],{encoding:"utf-8"}).status!==0){this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");return}}catch{this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");return}const e=E.join(q.tmpdir(),"session.html");try{await this.session.exportToHtml(e)}catch(n){this.showError(`Failed to export session: ${n instanceof Error?n.message:"Unknown error"}`);return}const s=new Mt(this.ui,a,"Creating gist...");this.editorContainer.clear(),this.editorContainer.addChild(s),this.ui.setFocus(s),this.ui.requestRender();const t=h(()=>{s.dispose(),this.editorContainer.clear(),this.editorContainer.addChild(this.editor),this.ui.setFocus(this.editor);try{v.unlinkSync(e)}catch{}},"restoreEditor");let i=null;s.onAbort=()=>{i?.kill(),t(),this.showStatus("Share cancelled")};try{const n=await new Promise(l=>{i=j("gh",["gist","create","--public=false",e]);let g="",u="";i.stdout?.on("data",c=>{g+=c.toString()}),i.stderr?.on("data",c=>{u+=c.toString()}),i.on("close",c=>l({stdout:g,stderr:u,code:c}))});if(s.signal.aborted)return;if(t(),n.code!==0){const l=n.stderr?.trim()||"Unknown error";this.showError(`Failed to create gist: ${l}`);return}const o=n.stdout?.trim(),r=o?.split("/").pop();if(!r){this.showError("Failed to parse gist ID from gh output");return}const d=st(r);this.showStatus(`Share URL: ${d}
|
|
40
40
|
Gist: ${o}`)}catch(n){s.signal.aborted||(t(),this.showError(`Failed to create gist: ${n instanceof Error?n.message:"Unknown error"}`))}}async handleCopyCommand(){const e=this.session.getLastAssistantText();if(!e){this.showError("No agent messages to copy yet.");return}try{await Ct(e),this.showStatus("Copied last agent message to clipboard")}catch(s){this.showError(s instanceof Error?s.message:String(s))}}handleNameCommand(e){const s=e.replace(/^\/name\s*/,"").trim();if(!s){const t=this.sessionManager.getSessionName();t?(this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("dim",`Session name: ${t}`),1,0))):this.showWarning("Usage: /name <name>"),this.ui.requestRender();return}this.session.setSessionName(s),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(a.fg("dim",`Session name set: ${s}`),1,0)),this.ui.requestRender()}handleSessionCommand(){const e=this.session.getSessionStats(),s=this.sessionManager.getSessionName();let t=`${a.bold("Session Info")}
|
|
41
41
|
|
|
42
42
|
`;s&&(t+=`${a.fg("dim","Name:")} ${s}
|
|
@@ -60,7 +60,7 @@ Gist: ${o}`)}catch(n){s.signal.aborted||(t(),this.showError(`Failed to create gi
|
|
|
60
60
|
${a.bold("Cost")}
|
|
61
61
|
`,t+=`${a.fg("dim","Total:")} ${e.cost.toFixed(4)}`),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(t,1,0)),this.ui.requestRender()}handleChangelogCommand(){const e=he(),s=de(e),t=s.length>0?s.reverse().map(i=>i.content).join(`
|
|
62
62
|
|
|
63
|
-
`):"No changelog entries found.";this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k),this.chatContainer.addChild(new f(a.bold(a.fg("accent","What's New")),1,0)),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new B(t,1,1,this.getMarkdownThemeWithSettings())),this.chatContainer.addChild(new k),this.ui.requestRender()}getAppKeyDisplay(e){return ge(e)}getEditorKeyDisplay(e){return ge(e)}handleHotkeysCommand(){const e=this.getEditorKeyDisplay("tui.editor.cursorUp"),s=this.getEditorKeyDisplay("tui.editor.cursorDown"),t=this.getEditorKeyDisplay("tui.editor.cursorLeft"),i=this.getEditorKeyDisplay("tui.editor.cursorRight"),n=this.getEditorKeyDisplay("tui.editor.cursorWordLeft"),o=this.getEditorKeyDisplay("tui.editor.cursorWordRight"),r=this.getEditorKeyDisplay("tui.editor.cursorLineStart"),d=this.getEditorKeyDisplay("tui.editor.cursorLineEnd"),l=this.getEditorKeyDisplay("tui.editor.jumpForward"),g=this.getEditorKeyDisplay("tui.editor.jumpBackward"),u=this.getEditorKeyDisplay("tui.editor.pageUp"),c=this.getEditorKeyDisplay("tui.editor.pageDown"),
|
|
63
|
+
`):"No changelog entries found.";this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k),this.chatContainer.addChild(new f(a.bold(a.fg("accent","What's New")),1,0)),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new B(t,1,1,this.getMarkdownThemeWithSettings())),this.chatContainer.addChild(new k),this.ui.requestRender()}getAppKeyDisplay(e){return ge(e)}getEditorKeyDisplay(e){return ge(e)}handleHotkeysCommand(){const e=this.getEditorKeyDisplay("tui.editor.cursorUp"),s=this.getEditorKeyDisplay("tui.editor.cursorDown"),t=this.getEditorKeyDisplay("tui.editor.cursorLeft"),i=this.getEditorKeyDisplay("tui.editor.cursorRight"),n=this.getEditorKeyDisplay("tui.editor.cursorWordLeft"),o=this.getEditorKeyDisplay("tui.editor.cursorWordRight"),r=this.getEditorKeyDisplay("tui.editor.cursorLineStart"),d=this.getEditorKeyDisplay("tui.editor.cursorLineEnd"),l=this.getEditorKeyDisplay("tui.editor.jumpForward"),g=this.getEditorKeyDisplay("tui.editor.jumpBackward"),u=this.getEditorKeyDisplay("tui.editor.pageUp"),c=this.getEditorKeyDisplay("tui.editor.pageDown"),y=this.getEditorKeyDisplay("tui.input.submit"),S=this.getEditorKeyDisplay("tui.input.newLine"),b=this.getEditorKeyDisplay("tui.editor.deleteWordBackward"),P=this.getEditorKeyDisplay("tui.editor.deleteWordForward"),M=this.getEditorKeyDisplay("tui.editor.deleteToLineStart"),x=this.getEditorKeyDisplay("tui.editor.deleteToLineEnd"),w=this.getEditorKeyDisplay("tui.editor.yank"),C=this.getEditorKeyDisplay("tui.editor.yankPop"),T=this.getEditorKeyDisplay("tui.editor.undo"),I=this.getEditorKeyDisplay("tui.input.tab"),ye=this.getAppKeyDisplay("app.interrupt"),Ee=this.getAppKeyDisplay("app.clear"),ke=this.getAppKeyDisplay("app.exit"),be=this.getAppKeyDisplay("app.suspend"),Me=this.getAppKeyDisplay("app.thinking.cycle"),xe=this.getAppKeyDisplay("app.model.cycleForward"),Te=this.getAppKeyDisplay("app.model.select"),$e=this.getAppKeyDisplay("app.tools.expand"),Pe=this.getAppKeyDisplay("app.thinking.toggle"),Ae=this.getAppKeyDisplay("app.editor.external"),Re=this.getAppKeyDisplay("app.model.cycleBackward"),ve=this.getAppKeyDisplay("app.message.followUp"),Ie=this.getAppKeyDisplay("app.message.dequeue"),Le=this.getAppKeyDisplay("app.clipboard.pasteImage");let N=`
|
|
64
64
|
**Navigation**
|
|
65
65
|
| Key | Action |
|
|
66
66
|
|-----|--------|
|
|
@@ -75,7 +75,7 @@ ${a.bold("Cost")}
|
|
|
75
75
|
**Editing**
|
|
76
76
|
| Key | Action |
|
|
77
77
|
|-----|--------|
|
|
78
|
-
| \`${
|
|
78
|
+
| \`${y}\` | Send message |
|
|
79
79
|
| \`${S}\` | New line${process.platform==="win32"?" (Ctrl+Enter on Windows Terminal)":""} |
|
|
80
80
|
| \`${b}\` | Delete word backwards |
|
|
81
81
|
| \`${P}\` | Delete word forwards |
|
|
@@ -89,8 +89,8 @@ ${a.bold("Cost")}
|
|
|
89
89
|
| Key | Action |
|
|
90
90
|
|-----|--------|
|
|
91
91
|
| \`${I}\` | Path completion / accept autocomplete |
|
|
92
|
-
| \`${
|
|
93
|
-
| \`${
|
|
92
|
+
| \`${ye}\` | Cancel autocomplete / abort streaming |
|
|
93
|
+
| \`${Ee}\` | Clear editor (first) / exit (second) |
|
|
94
94
|
| \`${ke}\` | Exit (when editor is empty) |
|
|
95
95
|
| \`${be}\` | Suspend to background |
|
|
96
96
|
| \`${Me}\` | Cycle thinking level |
|
|
@@ -111,5 +111,5 @@ ${a.bold("Cost")}
|
|
|
111
111
|
|-----|--------|
|
|
112
112
|
`;for(const[De,ee]of Z){const We=ee.description??ee.extensionPath,He=ue(De,{capitalize:!0});N+=`| \`${He}\` | ${We} |
|
|
113
113
|
`}}this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k),this.chatContainer.addChild(new f(a.bold(a.fg("accent","Keyboard Shortcuts")),1,0)),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new B(N.trim(),1,1,this.getMarkdownThemeWithSettings())),this.chatContainer.addChild(new k),this.ui.requestRender()}_goalAchievedShown=!1;renderGoalBlock(e){const s=e.milestones.filter(n=>n.done).length,t=e.milestones.length;this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k);const i=e.achieved?a.bold(a.fg("success",`\u2713 Goal achieved \u2014 ${e.text}`)):a.bold(a.fg("accent",`Goal \u2014 ${e.text}`))+a.fg("dim",` ${s}/${t}`);this.chatContainer.addChild(new f(i,1,0));for(const n of e.milestones){const o=n.done?a.fg("success","\u2713"):a.fg("dim","\u25CB"),r=n.done?a.fg("success",n.text):a.fg("text",n.text);this.chatContainer.addChild(new f(` ${o} ${r}`,1,0))}this.chatContainer.addChild(new k),this.ui.requestRender()}async handleGoalCommand(e){if(e==="clear"){this.session.clearSessionGoal();return}if(e){this._goalAchievedShown=!1,this.statusContainer.clear();const t=new L(this.ui,i=>a.fg("accent",i),i=>a.fg("muted",i),"Setting goal \u2014 breaking it into milestones\u2026");this.statusContainer.addChild(t),this.ui.requestRender();try{await this.session.setSessionGoal(e)}finally{t.stop(),this.statusContainer.clear(),this.ui.requestRender()}return}const s=this.session.getSessionGoal();if(!s){this.showStatus("No session goal set. Use /goal <what you want to accomplish this session>.");return}this.renderGoalBlock(s)}handleTraceCommand(){const e=this.session.getHarnessTrace();if(e.length===0){this.showStatus("No harness activity yet (no model switches, verification, or escalation this session).");return}this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new k),this.chatContainer.addChild(new f(a.bold(a.fg("accent","Harness trace")),1,0));for(const s of e.slice(-25)){const t=new Date(s.ts).toLocaleTimeString();this.chatContainer.addChild(new f(` ${a.fg("dim",t)} ${a.fg("accent",s.kind.padEnd(9))} ${a.fg("text",s.detail)}`,1,0))}this.chatContainer.addChild(new k),this.ui.requestRender()}handleUseCommand(e){const s=this.session.getPinnedSkills(),t=this.session.listSkillNames();if(!e){if(t.length===0){this.showStatus("No skills installed. Drop SKILL.md folders in ~/.koda/agent/skills.");return}const i=t.map(n=>s.includes(n)?`${n} (active)`:n).join(", ");this.showStatus(`Skills: ${i}. /use <name> to toggle on/off (no chat).`);return}if(e==="clear"||e==="none"){for(const i of s)this.session.unpinSkill(i);this.showStatus(s.length?`Skills off: ${s.join(", ")}.`:"No skills were active.");return}s.includes(e)?(this.session.unpinSkill(e),this.showStatus(`Skill "${e}" off.`)):this.session.pinSkill(e)?this.showStatus(`\u25B8 Skill "${e}" active for this session \u2014 it'll guide every task until /use ${e} again.`):this.showStatus(`No skill named "${e}". Available: ${t.join(", ")||"(none)"}.`)}async handleClearCommand(){this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear();try{if((await this.runtimeHost.newSession()).cancelled)return;this.renderCurrentSessionState(),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(`${a.fg("accent","\u2713 New session started")}`,1,1)),this.ui.requestRender()}catch(e){await this.handleFatalRuntimeError("Failed to create session",e)}}handleDebugCommand(){const e=this.ui.terminal.columns,s=this.ui.terminal.rows,t=this.ui.render(e),i=et(),n=[`Debug output at ${new Date().toISOString()}`,`Terminal: ${e}x${s}`,`Total lines: ${t.length}`,"","=== All rendered lines with visible widths ===",...t.map((o,r)=>{const d=Ge(o),l=JSON.stringify(o);return`[${r}] (w=${d}) ${l}`}),"","=== Agent messages (JSONL) ===",...this.session.messages.map(o=>JSON.stringify(o)),""].join(`
|
|
114
|
-
`);v.mkdirSync(
|
|
114
|
+
`);v.mkdirSync(E.dirname(i),{recursive:!0}),v.writeFileSync(i,n),this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new f(`${a.fg("accent","\u2713 Debug log written")}
|
|
115
115
|
${a.fg("muted",i)}`,1,1)),this.ui.requestRender()}handleArminSaysHi(){this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new bt(this.ui)),this.ui.requestRender()}handleDementedDelves(){this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new vt),this.ui.requestRender()}handleDaxnuts(){this.chatContainer.addChild(new p(1)),this.chatContainer.addChild(new Rt(this.ui)),this.ui.requestRender()}checkDaxnutsEasterEgg(e){e.provider==="opencode"&&e.id.toLowerCase().includes("kimi-k2.5")&&this.handleDaxnuts()}async handleBashCommand(e,s=!1){const i=await this.session.extensionRunner.emitUserBash({type:"user_bash",command:e,excludeFromContext:s,cwd:this.sessionManager.getCwd()});if(i?.result){const o=i.result;this.bashComponent=new Q(e,this.ui,s),this.session.isStreaming?(this.pendingMessagesContainer.addChild(this.bashComponent),this.pendingBashComponents.push(this.bashComponent)):this.chatContainer.addChild(this.bashComponent),o.output&&this.bashComponent.appendOutput(o.output),this.bashComponent.setComplete(o.exitCode,o.cancelled,o.truncated?{truncated:!0,content:o.output}:void 0,o.fullOutputPath),this.session.recordBashResult(e,o,{excludeFromContext:s}),this.bashComponent=void 0,this.ui.requestRender();return}const n=this.session.isStreaming;this.bashComponent=new Q(e,this.ui,s),n?(this.pendingMessagesContainer.addChild(this.bashComponent),this.pendingBashComponents.push(this.bashComponent)):this.chatContainer.addChild(this.bashComponent),this.ui.requestRender();try{const o=await this.session.executeBash(e,r=>{this.bashComponent&&(this.bashComponent.appendOutput(r),this.ui.requestRender())},{excludeFromContext:s,operations:i?.operations});this.bashComponent&&this.bashComponent.setComplete(o.exitCode,o.cancelled,o.truncated?{truncated:!0,content:o.output}:void 0,o.fullOutputPath)}catch(o){this.bashComponent&&this.bashComponent.setComplete(void 0,!1),this.showError(`Bash command failed: ${o instanceof Error?o.message:"Unknown error"}`)}this.bashComponent=void 0,this.ui.requestRender()}async handleCompactCommand(e){if(this.sessionManager.getEntries().filter(i=>i.type==="message").length<2){this.showWarning("Nothing to compact (no messages yet)");return}this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.statusContainer.clear();try{await this.session.compact(e)}catch{}}stop(){this.unregisterSignalHandlers(),this.settingsManager.getShowTerminalProgress()&&this.ui.terminal.setProgress(!1),this.loadingAnimation&&(this.loadingAnimation.stop(),this.loadingAnimation=void 0),this.clearExtensionTerminalInputListeners(),this.footer.dispose(),this.footerDataProvider.dispose(),this.unsubscribe&&this.unsubscribe(),this.isInitialized&&(this.ui.stop(),this.isInitialized=!1)}}export{_ as InteractiveMode,is as formatResumeCommand,as as isApiKeyLoginProvider};
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openadapter/koda",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.18",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openadapter/koda",
|
|
9
|
-
"version": "1.0.0-beta.
|
|
9
|
+
"version": "1.0.0-beta.18",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
13
|
-
"@openadapter/koda-agent-core": "^1.0.0-beta.
|
|
14
|
-
"@openadapter/koda-ai": "^1.0.0-beta.
|
|
15
|
-
"@openadapter/koda-tui": "^1.0.0-beta.
|
|
13
|
+
"@openadapter/koda-agent-core": "^1.0.0-beta.18",
|
|
14
|
+
"@openadapter/koda-ai": "^1.0.0-beta.18",
|
|
15
|
+
"@openadapter/koda-tui": "^1.0.0-beta.18",
|
|
16
16
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
17
17
|
"chalk": "5.6.2",
|
|
18
18
|
"cross-spawn": "7.0.6",
|
|
@@ -753,11 +753,11 @@
|
|
|
753
753
|
]
|
|
754
754
|
},
|
|
755
755
|
"node_modules/@openadapter/koda-agent-core": {
|
|
756
|
-
"version": "1.0.0-beta.
|
|
757
|
-
"resolved": "https://registry.npmjs.org/@openadapter/koda-agent-core/-/koda-agent-core-1.0.0-beta.
|
|
756
|
+
"version": "1.0.0-beta.18",
|
|
757
|
+
"resolved": "https://registry.npmjs.org/@openadapter/koda-agent-core/-/koda-agent-core-1.0.0-beta.18.tgz",
|
|
758
758
|
"license": "MIT",
|
|
759
759
|
"dependencies": {
|
|
760
|
-
"@openadapter/koda-ai": "^1.0.0-beta.
|
|
760
|
+
"@openadapter/koda-ai": "^1.0.0-beta.18",
|
|
761
761
|
"ignore": "7.0.5",
|
|
762
762
|
"typebox": "1.1.38",
|
|
763
763
|
"yaml": "2.9.0"
|
|
@@ -767,8 +767,8 @@
|
|
|
767
767
|
}
|
|
768
768
|
},
|
|
769
769
|
"node_modules/@openadapter/koda-ai": {
|
|
770
|
-
"version": "1.0.0-beta.
|
|
771
|
-
"resolved": "https://registry.npmjs.org/@openadapter/koda-ai/-/koda-ai-1.0.0-beta.
|
|
770
|
+
"version": "1.0.0-beta.18",
|
|
771
|
+
"resolved": "https://registry.npmjs.org/@openadapter/koda-ai/-/koda-ai-1.0.0-beta.18.tgz",
|
|
772
772
|
"license": "MIT",
|
|
773
773
|
"dependencies": {
|
|
774
774
|
"@anthropic-ai/sdk": "0.91.1",
|
|
@@ -790,8 +790,8 @@
|
|
|
790
790
|
}
|
|
791
791
|
},
|
|
792
792
|
"node_modules/@openadapter/koda-tui": {
|
|
793
|
-
"version": "1.0.0-beta.
|
|
794
|
-
"resolved": "https://registry.npmjs.org/@openadapter/koda-tui/-/koda-tui-1.0.0-beta.
|
|
793
|
+
"version": "1.0.0-beta.18",
|
|
794
|
+
"resolved": "https://registry.npmjs.org/@openadapter/koda-tui/-/koda-tui-1.0.0-beta.18.tgz",
|
|
795
795
|
"license": "MIT",
|
|
796
796
|
"dependencies": {
|
|
797
797
|
"get-east-asian-width": "1.6.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openadapter/koda",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.18",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
46
|
-
"@openadapter/koda-agent-core": "^1.0.0-beta.
|
|
47
|
-
"@openadapter/koda-ai": "^1.0.0-beta.
|
|
48
|
-
"@openadapter/koda-tui": "^1.0.0-beta.
|
|
46
|
+
"@openadapter/koda-agent-core": "^1.0.0-beta.18",
|
|
47
|
+
"@openadapter/koda-ai": "^1.0.0-beta.18",
|
|
48
|
+
"@openadapter/koda-tui": "^1.0.0-beta.18",
|
|
49
49
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
50
50
|
"chalk": "5.6.2",
|
|
51
51
|
"cross-spawn": "7.0.6",
|