novacode 0.12.1 → 0.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/app-BShCIDQa.mjs +58 -0
- package/dist/app-BShCIDQa.mjs.map +1 -0
- package/dist/main.mjs +28 -29
- package/dist/main.mjs.map +1 -1
- package/dist/{reset-CWIz7872.mjs → reset-DiwM4lFK.mjs} +2 -2
- package/dist/{reset-CWIz7872.mjs.map → reset-DiwM4lFK.mjs.map} +1 -1
- package/package.json +1 -1
- package/dist/app-C-3NXlux.mjs +0 -58
- package/dist/app-C-3NXlux.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Open-source, multi-provider coding agent.
|
|
|
4
4
|
|
|
5
5
|
> **Currently in early development (v0.x). Breaking changes may occur.**
|
|
6
6
|
|
|
7
|
-
https://github.com/user-attachments/assets/
|
|
7
|
+
<img width="1155" height="851" alt="image" src="https://github.com/user-attachments/assets/3b761728-ebfc-4827-b95b-1ec6ccd76870" />
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import{t as e}from"./reset-DiwM4lFK.mjs";import{C as t,D as n,E as r,O as i,S as a,T as o,a as s,b as c,c as l,d as u,f as d,i as f,k as p,l as m,m as h,n as g,o as _,p as v,r as y,s as b,t as x,u as S,v as C,w,x as T,y as E}from"./main.mjs";import D from"chalk";import{generateText as O}from"ai";import{Box as k,Static as A,Text as j,render as ee,useApp as te,useInput as ne,useWindowSize as M}from"ink";import{memo as N,useCallback as P,useEffect as F,useMemo as I,useRef as L,useState as R}from"react";import{jsx as z,jsxs as B}from"react/jsx-runtime";import re from"ink-big-text";const ie=N(function({input:e,suggestions:t,selCmdIdx:n}){let r=m(),{rows:i}=M(),a=i||24,o=Math.max(3,Math.min(t.length,a-5));return B(k,{flexDirection:`column`,width:`100%`,flexShrink:0,backgroundColor:r.palette.bg,paddingX:1,paddingBottom:1,children:[t.length>0&&z(k,{paddingTop:1,children:z(s,{items:t,selectedIndex:n,visibleCount:o,keyExtractor:e=>e.name,renderItem:(e,t,n)=>B(k,{flexDirection:`row`,children:[B(j,{backgroundColor:n?r.palette.primary:void 0,color:n?r.palette.bg:r.palette.fg,wrap:`truncate-end`,children:[`/`,e.name.padEnd(12)]}),B(j,{color:r.palette.muted,children:[` `,e.desc]})]})})}),B(k,{flexDirection:`row`,paddingY:1,children:[z(k,{flexShrink:0,marginRight:1,children:z(j,{bold:!0,color:r.palette.muted,children:`❯`})}),z(k,{flexGrow:1,flexShrink:1,children:B(j,{color:r.palette.fg,wrap:`wrap`,children:[e,z(_,{})]})})]})]})});function ae(e){let t=e;return t=t.replace(/`([^`]+)`/g,(e,t)=>D.yellow(t)),t=t.replace(/\*\*([^*]+)\*\*/g,(e,t)=>D.bold(t)),t=t.replace(/__([^_]+)__/,(e,t)=>D.bold(t)),t=t.replace(/\*([^*]+)\*/g,(e,t)=>D.italic(t)),t=t.replace(/_([^_]+)_/g,(e,t)=>D.italic(t)),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,(e,t,n)=>`${D.blue(t)} ${D.dim(`(${n})`)}`),t}const V=e=>new Set(e.split(/\s+/).filter(Boolean)),oe=V(`
|
|
2
|
+
abstract as async await break case catch class const continue debugger default delete do else enum export extends
|
|
3
|
+
false finally for from function get if implements import in instanceof interface is let new null of package private
|
|
4
|
+
protected public readonly return set static super switch this throw true try type typeof undefined var void while
|
|
5
|
+
with yield
|
|
6
|
+
`),se=V(`
|
|
7
|
+
False None True and as assert async await break class continue def del elif else except finally for from global if
|
|
8
|
+
import in is lambda nonlocal not or pass raise return try while with yield
|
|
9
|
+
`),ce=V(`
|
|
10
|
+
if then else elif fi for in do done while until case esac function return break continue local export readonly
|
|
11
|
+
declare typeset
|
|
12
|
+
`),le=V(`
|
|
13
|
+
break case chan const continue default defer else fallthrough for func go goto if import interface map package range
|
|
14
|
+
return select struct switch type var nil true false
|
|
15
|
+
`),ue=V(`
|
|
16
|
+
as async await break const continue crate dyn else enum extern false fn for if impl in let loop match mod move mut
|
|
17
|
+
pub ref return self Self static struct super trait true type unsafe use where while yield
|
|
18
|
+
`),H=V(`
|
|
19
|
+
select from where and or not in is null as by group order limit offset insert into values update set delete create
|
|
20
|
+
table drop alter add column primary key foreign references join left right inner outer on
|
|
21
|
+
`),U={go:{comment:`//`,keywords:le},json:{comment:null,keywords:V(`true false null`)},py:{comment:`#`,keywords:se},rust:{comment:`//`,keywords:ue},sh:{comment:`#`,keywords:ce},sql:{comment:`--`,keywords:H},ts:{comment:`//`,keywords:oe},yaml:{comment:`#`,keywords:V(`true false null yes no on off`)}},de={bash:`sh`,javascript:`ts`,js:`ts`,jsx:`ts`,python:`py`,rs:`rust`,shell:`sh`,tsx:`ts`,typescript:`ts`,yml:`yaml`,zsh:`sh`},W=e=>U[de[e]??e]??null,fe=e=>W(e)!==null,pe=/'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`|\b\d+(?:\.\d+)?\b|[A-Za-z_$][\w$]*/g;function me(e,t){let n=W(t);if(!n)return e;if(n.comment&&e.trimStart().startsWith(n.comment))return D.gray(e);let r=``,i=0;for(let t of e.matchAll(pe)){let a=t[0];if(!a)continue;let o=t.index??0;o>i&&(r+=e.slice(i,o));let s=a[0];s===`"`||s===`'`||s==="`"?r+=D.green(a):s&&s>=`0`&&s<=`9`?r+=D.yellow(a):n.keywords.has(a)?r+=D.magenta(a):r+=a,i=o+a.length}return i<e.length&&(r+=e.slice(i)),r}var G=class{#e=!1;#t=``;constructor(e){e&&(this.#e=e.inCodeBlock,this.#t=e.codeBlockLang)}getState(){return{inCodeBlock:this.#e,codeBlockLang:this.#t}}renderChunk(e){return e.split(`
|
|
22
|
+
`).map(e=>this.renderLine(e)).join(`
|
|
23
|
+
`)}renderLine(e){let t=e.match(/^\s*(`{3,}|~{3,})(.*)$/);if(t)return this.#e?(this.#e=!1,``):(this.#e=!0,this.#t=(t[2]??``).trim(),this.#t?D.gray(`─ ${this.#t}`):``);if(this.#e){let t=this.#t;return` ${fe(t)?me(e,t):D.dim(e)}`}if(e.startsWith(`#`)){let t=e.match(/^(#{1,6})\s+(.*)$/);if(t?.[1]&&t[2]){let e=t[1].length,n=t[2];return e===1?D.bold.magenta.underline(n):e===2?D.bold.blue(n):D.bold.cyan(n)}}let n=e;return(n.startsWith(`- `)||n.startsWith(`* `))&&(n=` ${D.yellow(`•`)} ${n.slice(2)}`),ae(n)}};function he(e){return new G().renderChunk(e)}var ge=class{#e=``;#t=``;#n={inCodeBlock:!1,codeBlockLang:``};#r=``;#i=``;update(e){if(e===this.#r)return this.#i;this.#e&&!e.startsWith(this.#e)&&(this.#e=``,this.#t=``,this.#n={inCodeBlock:!1,codeBlockLang:``});let t=ve(e,this.#e.length,this.#n);if(t>this.#e.length){let n=e.slice(0,t),r=this.#e?n.slice(this.#e.length):n,i=new G(this.#n);this.#t+=i.renderChunk(r),this.#e=n,this.#n=i.getState()}let n=e.slice(this.#e.length),r=new G(this.#n),i=n?r.renderChunk(n):``;return this.#r=e,this.#i=this.#t+i,this.#i}reset(){this.#e=``,this.#t=``,this.#n={inCodeBlock:!1,codeBlockLang:``},this.#r=``,this.#i=``}};function _e(e,t){let n=e.inCodeBlock,r=e.codeBlockLang;for(let e of t.split(`
|
|
24
|
+
`)){let t=e.trim();/^(?:`{3,}|~{3,})/.test(t)&&(n?(n=!1,r=``):(n=!0,r=t.slice(3).trim()))}return{inCodeBlock:n,codeBlockLang:r}}function ve(e,t,n){let r=e.length;for(;r>t;){let i=e.lastIndexOf(`
|
|
25
|
+
|
|
26
|
+
`,r-1);if(i<t)return t;let a=i+2;if(!_e(n,e.slice(t,a)).inCodeBlock)return a;r=i}return t}const ye=N(function({content:e,update:t}){let n=m(),r=e.split(`
|
|
27
|
+
`);return B(k,{flexDirection:`column`,marginBottom:0,children:[z(re,{text:`novacode`,font:`block`,colors:[`cyan`,`blue`],space:!1,lineHeight:0}),r.map(e=>z(j,{color:n.palette.muted,children:e},e)),t&&B(k,{marginTop:1,children:[B(j,{color:n.palette.success,bold:!0,children:[`⬆ v`,t.current,` → v`,t.latest]}),z(j,{color:n.palette.muted,children:` Run /update to upgrade`})]})]})}),be=N(function({content:e}){let t=m();return z(k,{flexDirection:`column`,width:`100%`,marginBottom:1,children:z(k,{flexDirection:`column`,width:`100%`,paddingX:1,paddingY:1,backgroundColor:t.palette.bg,children:z(j,{bold:!0,color:t.palette.fg,wrap:`wrap`,children:e})})})}),xe=N(function({content:e,isStreaming:t=!1}){let n=m(),r=t?e:he(e);return z(k,{flexDirection:`column`,paddingX:1,paddingBottom:1,children:z(j,{color:n.palette.fg,wrap:`wrap`,children:r})})}),Se=N(function({event:e}){let t=m();if(e.type!==`ToolStarted`&&e.type!==`ToolCompleted`&&e.type!==`ToolFailed`)return null;let n=e.type===`ToolStarted`,r=e.type===`ToolFailed`;return B(k,{flexDirection:`column`,paddingX:1,paddingBottom:1,children:[B(k,{flexDirection:`row`,children:[B(j,{color:n?t.palette.warning:r?t.palette.error:t.palette.success,children:[n?`○`:`●`,` `]}),z(j,{bold:!0,color:t.palette.primary,children:e.toolName}),B(j,{color:t.palette.muted,children:[` `,e.args]}),e.type===`ToolCompleted`&&e.resultLineCount!==void 0&&B(j,{color:t.palette.muted,children:[` (`,e.resultLineCount,` lines)`]}),e.type===`ToolCompleted`&&e.resultMatchCount!==void 0&&B(j,{color:t.palette.muted,children:[` (`,e.resultMatchCount,` matches)`]})]}),r&&z(k,{marginLeft:2,children:z(j,{color:t.palette.error,children:e.error})})]})}),Ce=N(function({label:e}){let t=m();return B(k,{flexDirection:`row`,paddingX:1,paddingBottom:1,children:[z(k,{marginRight:1,children:z(b,{})}),z(j,{color:t.palette.warning,children:e})]})}),K=N(function({event:e}){let t=m();switch(e.type){case`Splash`:return z(ye,{content:e.content,update:e.update});case`UserMessage`:return z(be,{content:e.content});case`AssistantMessage`:return z(xe,{content:e.content,isStreaming:e.id===`active-text`});case`ToolStarted`:case`ToolCompleted`:case`ToolFailed`:return z(Se,{event:e});case`Thinking`:return z(Ce,{label:e.id===`active-working`?`Working…`:`Thinking…`});case`Warning`:return z(k,{flexDirection:`row`,marginBottom:0,children:B(j,{color:t.palette.warning,children:[`⚠ `,e.content]})});case`SystemMessage`:return z(k,{flexDirection:`row`,marginBottom:0,children:B(j,{color:t.palette.primary,children:[`ℹ `,e.content]})});case`Notice`:return z(k,{flexDirection:`column`,paddingX:1,paddingBottom:1,children:z(j,{wrap:`wrap`,children:e.content})});case`UpdateAvailable`:return B(k,{flexDirection:`column`,marginBottom:1,children:[z(k,{flexDirection:`row`,children:B(j,{color:t.palette.success,bold:!0,children:[`⬆ v`,e.current,` → v`,e.latest]})}),z(k,{marginLeft:2,children:z(j,{color:t.palette.muted,children:`Run /update to upgrade`})})]});default:return null}});function we({committedEvents:e,liveEvents:t}){let{columns:n}=M();return B(k,{flexDirection:`column`,flexGrow:1,children:[z(A,{items:e,style:{width:n??80},children:e=>z(K,{event:e},e.id)}),t.map(e=>z(K,{event:e},e.id))]})}function q(e){let t=e/1e3;return t>=100?`${Math.round(t)}K`:`${t.toFixed(1)}K`}function Te(e,t){if(e===0)return`0/${q(t)}`;let n=Math.round(e/t*100);return`${q(e)}/${q(t)} (${n}%)`}function Ee({activity:e,activityColor:t,model:n,contextTokens:r,tip:i}){let a=m();return B(k,{flexDirection:`row`,width:`100%`,flexShrink:0,paddingX:1,paddingBottom:1,backgroundColor:a.palette.bg,children:[z(j,{color:t,children:e}),z(j,{color:a.palette.muted,children:` • Tip: `}),z(j,{color:a.palette.primary,children:i}),z(k,{flexGrow:1}),z(j,{color:a.palette.muted,children:Te(r,n.contextWindow)}),z(j,{color:a.palette.muted,children:` • `}),z(j,{bold:!0,color:a.palette.fg,children:n.id})]})}function J(e){if(typeof e==`string`)return Math.ceil(e.length/4);let t=0;for(let n of e){let e=n.content;if(typeof e==`string`){t+=e.length;continue}for(let n of e)n.type===`text`?t+=n.text.length:n.type===`tool-call`?t+=JSON.stringify(n.input).length:n.type===`reasoning`&&(t+=n.text.length)}return Math.ceil(t/4)}function Y(e){return e.role===`tool`?e.content.map(e=>e.type===`tool-result`?r(e.output).text:``).join(`
|
|
28
|
+
`):typeof e.content==`string`?e.content:e.content.filter(e=>e.type===`text`).map(e=>e.type===`text`?e.text:``).join(``)}async function De(e,t,n,r,i,a){let o=await e.get(t),s=o&&o.contextTokens>0?o.contextTokens:J(n),c=Math.max(2e4,Math.round(r.contextWindow*.1)),l=0,u=n.length;for(let e=n.length-1;e>=0;e--){let t=J([n[e]]);if(l+t<=c)l+=t,u=e;else break}if(u<=0)return{compacted:!1,tokensBefore:s,tokensAfter:s};let d=n.slice(u),f=await Oe(n.slice(0,u).map(e=>e.role===`user`?`User: ${Y(e)}`:e.role===`assistant`?`Assistant: ${Y(e)}`:e.role===`tool`?`Tool: ${Y(e).slice(0,200)}`:``).join(`
|
|
29
|
+
|
|
30
|
+
`),r,i);if(!f)return{compacted:!1,tokensBefore:s,tokensAfter:s};let p={role:`user`,content:`[Prior context summary]\n${f}`};await e.endSession(t,`compacted`);let m=await e.createContinuation(t,a,r.id,r.provider),h=[p,...d];for(let t of h)await e.append(m.id,t);let g=J(h);return g>=s?{compacted:!1,tokensBefore:s,tokensAfter:s}:{compacted:!0,summary:f,tokensBefore:s,tokensAfter:g,newSessionId:m.id}}async function Oe(e,t,r){let a=h(t.provider);if(!a)return null;let{text:o}=await O({model:n(a.id,t.id,r),system:`Summarize this coding session concisely. Cover: what was asked, files touched, what was done, key decisions. Keep it under 300 words.`,prompt:e,providerOptions:t.reasoning?i(a.id):void 0});return o.trim()||null}async function ke(e,t,r){let a=h(t.provider);if(!a)return null;let o=e.slice(0,4).map(e=>e.role===`user`?`User: ${Y(e)}`:e.role===`assistant`?`Assistant: ${Y(e)}`:``).join(`
|
|
31
|
+
`),{text:s}=await O({model:n(a.id,t.id,r),system:`Generate a very short, descriptive, and concise title for this coding conversation. Do not use quotes or prefixes like 'Title:'. Max 6 words.`,prompt:o,providerOptions:t.reasoning?i(a.id):void 0});return s.replace(/\s+/g,` `).trim().replace(/^["']|["']$/g,``).slice(0,60).trim()||null}function X(e){if(e instanceof Error){let t=`lastError`in e?e.lastError:null;if(t instanceof Error)return X(t);let n=e.responseBody;if(n)try{let t=JSON.parse(n);return t.error?.message??t.message??e.message}catch{}return e.message}return String(e)}function Ae(e,n,i,a,o,s){let[c,l]=R(!1),[u,d]=R(!1),[f,p]=R([]),m=L(null),h=L(0),[g,_]=R(``),v=L(``),y=L(new ge),b=L(null),x=L(!1),S=P(()=>{if(b.current=null,!x.current)return;x.current=!1;let e=v.current;_(y.current.update(e))},[]),C=P(e=>{v.current+=e,x.current=!0,b.current||=setTimeout(S,16)},[S]),w=P(()=>{b.current&&=(clearTimeout(b.current),null),v.current=``,x.current=!1,y.current.reset(),_(``)},[]),T=P(()=>{m.current?.abort(),m.current=null},[]);return{busy:c,thinking:u,activeTools:f,bufferedStream:g,run:P(async c=>{let u=c.signal;m.current=c,l(!0),w(),d(!1),p([]),h.current=0;let f;try{let o=await e.prompt(u,async e=>{let t=e.usage;if(t&&t.inputTokens!=null&&(a(()=>t.inputTokens??0),await n.setContextTokens(i,t.inputTokens??0)),e.response?.messages?.length){let t=e.response.messages.slice(h.current);if(t.length===0)return;h.current=e.response.messages.length;let{committedToolCallIds:n,committedText:r}=await s(t);n.size>0&&p(e=>e.filter(e=>!n.has(e.id))),r&&w()}});for await(let e of o.fullStream)switch(e.type){case`text-delta`:e.text&&(d(!1),C(e.text));break;case`reasoning-delta`:d(!0);break;case`tool-call`:{d(!1);let n=t(e.input,!1);p(t=>t.some(t=>t.id===e.toolCallId)?t:[...t,{id:e.toolCallId,name:e.toolName,args:n,status:`running`}]);break}case`tool-result`:{let{text:t,isError:n}=r(e.output);p(r=>r.map(r=>{if(r.id!==e.toolCallId)return r;if(n)return{...r,status:`failure`,error:t};let i,a;return r.name===`read`?i=t.split(`
|
|
32
|
+
`).length:r.name===`grep`&&(a=t.split(`
|
|
33
|
+
`).filter(Boolean).length),{...r,status:`success`,lineCount:i,matchCount:a}}));break}case`error`:f=e.error;break}let c=(await o.response).messages.slice(h.current);c.length>0&&await s(c);try{let t=await n.get(i);if(t&&!t.title&&e.messages.length>=2){let t=await ke(e.messages,e.model,e.apiKey);t&&await n.setTitle(i,t)}}catch(e){console.error(`Failed to generate or save session title:`,e)}}catch(e){u.aborted?o({role:`assistant`,content:`(aborted)`}):o({role:`assistant`,content:`Error: ${X(f??e)}`})}finally{m.current=null,l(!1),w(),d(!1),p([])}},[e,n,i,a,o,s,C,w]),abort:T,setBusy:l}}async function je(e,t,n){let r=await De(t,n,e.messages,e.model,e.apiKey,process.cwd());if(r.compacted){let i=await t.messages(r.newSessionId??n);e.setMessages(i);let a=Math.round((r.tokensBefore-r.tokensAfter)/r.tokensBefore*100);return{result:D.green(`✓ Compacted (${a}% reduction)`),newSessionId:r.newSessionId}}return{result:D.yellow(`Context is small enough, no compaction needed.`)}}async function Me(e,t,n){let r=await E(),i=await C();if(e)return await Ne(e.trim(),t);if(!n)return D.red(`Prompts not available in this context`);let a=p.filter(e=>i.apiKeys[e.id]).flatMap(e=>v(e.id));if(!a.length)return D.yellow(`No models available. Use /providers to add a provider API key.`);let o=Math.max(...a.map(e=>e.id.length),20),s=[];for(let e of a){let t=e.id===r.model&&e.provider===r.provider,n=h(e.provider);s.push({value:`${e.provider}:${e.id}`,label:`${t?D.green(`●`):`○`} ${e.id.padEnd(o+2)} ${Pe(e.contextWindow).padEnd(8)}`,hint:n.name})}let c=await n.searchSelect({message:`Model (type to filter)`,options:s});if(!c)return``;let[l,d]=c.split(`:`),f=u(l,d),m=h(l);return!f||!m?D.red(`Error: Model or provider not found`):(r.provider=l,r.model=d,await T(r),t.updateConfig({provider:m.id,model:f,apiKey:i.apiKeys[l]??``}),D.green(`✓ Switched to ${d}`))}async function Ne(e,t){let n=await E(),r=await C(),i=d(e);if(!i)return D.yellow(`"${e}" not found. Use /models`);let a=i.provider;if(!r.apiKeys[a])return D.yellow(`No API key configured for ${a}. Use /providers`);let o=h(a);return o?(n.provider=a,n.model=e,await T(n),t.updateConfig({provider:o.id,model:i,apiKey:r.apiKeys[a]}),D.green(`✓ Switched to ${e}`)):D.red(`Error: Provider not found`)}const Pe=e=>e>=1e6?`${e/1e6}M`:`${e/1e3}K`;async function Fe(e,t){if(!t)return D.red(`Prompts not available in this context`);let n=await E(),r=await C(),i=p.filter(e=>!!r.apiKeys[e.id]).sort((e,t)=>e.name.localeCompare(t.name)),a=i.length===0?D.dim(`No providers configured. Use 'Add Provider' below.`):i.map(e=>{let t=e.id===n.provider,r=t?D.green(` ●`):``,i=t?n.model:S(e.id)?.id??``;return` ✅ ${e.name.padEnd(24)} ${i}${r}`}).join(`
|
|
34
|
+
`),o=await t.select({message:`Action`,header:a,options:[{value:`add`,label:`Add Provider`},{value:`update`,label:`Update API Key`},{value:`remove`,label:`Remove API Key`},{value:`default`,label:`Set Default Provider`},{value:`back`,label:`Back`}]});return!o||o===`back`?``:o===`add`?Ie(e,t):o===`update`?Le(e,t):o===`remove`?Re(e,t):o==="default"?ze(e,t):``}async function Ie(e,t){let n=await C(),r=await E(),i=p.filter(e=>!n.apiKeys[e.id]).sort((e,t)=>e.name.localeCompare(t.name));if(i.length===0)return D.yellow(`All providers already have API keys configured.`);let a=await t.select({message:`Add Provider`,options:i.map(e=>({value:e.id,label:e.name}))});if(!a)return``;let o=h(a);if(!o)return D.red(`Error: Provider not found`);let s=await t.password({message:`${o.name} API Key`,validate:e=>!e||e.length<8?`Enter a valid key`:void 0});if(!s)return``;n.apiKeys[o.id]=s,await c(n);let l=v(o.id),d=S(o.id),f=d?.id??``;if(d&&await t.select({message:`Model Selection`,options:[{value:`latest`,label:`Use default (${d.id})`},{value:`choose`,label:`Choose a model ID...`}]})===`choose`){let e=await t.select({message:`Select Model ID`,options:l.map(e=>({value:e.id,label:e.id}))});e&&(f=e)}let m=!r.provider;if(r.provider&&r.provider!==o.id&&await t.confirm({message:`Set ${o.name} as the default provider?`})&&(m=!0),m){r.provider=o.id,r.model=f,await T(r);let t=u(o.id,f);t&&e.updateConfig({provider:o.id,model:t,apiKey:s})}return D.green(`✓ ${o.name} configured`)}async function Le(e,t){let n=await C(),r=p.filter(e=>!!n.apiKeys[e.id]).sort((e,t)=>e.name.localeCompare(t.name));if(r.length===0)return D.yellow(`No providers configured. Use 'Add Provider' first.`);let i=await t.select({message:`Update API Key`,options:r.map(e=>({value:e.id,label:e.name}))});if(!i)return``;let a=h(i);if(!a)return D.red(`Error: Provider not found`);let o=await t.password({message:`New key for ${a.name}`});if(!o)return``;n.apiKeys[a.id]=o,await c(n);let s=await E();if(s.provider===a.id){let t=u(s.provider,s.model);t&&e.updateConfig({provider:a.id,model:t,apiKey:o})}return D.green(`✓ Key updated`)}async function Re(e,t){let n=await C(),r=await E(),i=p.filter(e=>!!n.apiKeys[e.id]).sort((e,t)=>e.name.localeCompare(t.name));if(i.length===0)return D.yellow(`No configured providers to remove.`);let a=await t.select({message:`Remove API Key`,options:i.map(e=>({value:e.id,label:e.name}))});if(!a||!await t.confirm({message:`Are you sure you want to remove the API key for ${a}?`}))return``;if(delete n.apiKeys[a],await c(n),r.provider===a){r.provider=``,r.model=``;let t=Object.keys(n.apiKeys)[0];if(t){let i=h(t),a=S(t);i&&a&&(r.provider=t,r.model=a.id,e.updateConfig({provider:i.id,model:a,apiKey:n.apiKeys[t]}))}await T(r)}return D.green(`✓ Removed API key for ${a}`)}async function ze(e,t){let n=await E(),r=await C(),i=[...p].sort((e,t)=>e.name.localeCompare(t.name)),a=await t.select({message:`Default Provider`,options:i.map(e=>({value:e.id,label:`${r.apiKeys[e.id]?`✅`:`❌`} ${e.name}`}))});if(!a)return``;if(!r.apiKeys[a])return D.yellow(`No API key for ${a}. Please set one first.`);let o=h(a),s=S(a);return!o||!s?D.red(`Error: Provider or model not found`):(n.provider=a,n.model=s.id,await T(n),e.updateConfig({provider:o.id,model:s,apiKey:r.apiKeys[a]}),D.green(`✓ Default set to ${o.name} (${s.id})`))}const Z=[{name:`models`,desc:`Switch model`,aliases:[`model`]},{name:`providers`,desc:`Manage providers`,aliases:[`prov`,`config`,`cfg`]},{name:`compact`,desc:`Compact context`},{name:`sessions`,desc:`List and switch sessions`},{name:`resume`,desc:`Resume previous session`},{name:`update`,desc:`Update novacode`},{name:`skills`,desc:`List available skills`},{name:`permission`,desc:`Switch permission mode (restricted/unrestricted)`,aliases:[`perm`,`mode`,`permiso`,`permesso`]},{name:`reset`,desc:`Reset all nova data`},{name:`help`,desc:`Show help`},{name:`clear`,desc:`Clear screen & start new session`,aliases:[`new`]},{name:`quit`,desc:`Exit (Ctrl+D)`,aliases:[`exit`]}],Be=`
|
|
35
|
+
${D.bold(`Commands:`)}
|
|
36
|
+
${Z.map(e=>` /${e.name.padEnd(12)} ${e.desc}`).join(`
|
|
37
|
+
`)}
|
|
38
|
+
|
|
39
|
+
${D.bold(`CLI:`)}
|
|
40
|
+
nova update Update to latest version
|
|
41
|
+
nova reset Delete all nova data and exit
|
|
42
|
+
nova -s ls List sessions
|
|
43
|
+
nova -s <id> / --sessions Resume sessions by ID
|
|
44
|
+
nova -r / --resume Resume last sessions
|
|
45
|
+
nova -s rm <id> Delete specific sessions
|
|
46
|
+
nova -s rm --all Delete all sessions
|
|
47
|
+
|
|
48
|
+
${D.dim(`Keys:`)}
|
|
49
|
+
Esc Abort
|
|
50
|
+
↑ / ↓ History
|
|
51
|
+
`;async function Ve(t,n,r,i,o,s,c,l,u=[]){let[d,...f]=t.slice(1).split(` `),p=f.join(` `);switch(d){case`models`:case`model`:return Me(p,n,o);case`providers`:case`prov`:case`config`:case`cfg`:return Fe(n,o);case`compact`:if(!r||!i)return D.red(`Session store not available`);{let{result:e,newSessionId:t}=await je(n,r,i);return t&&c&&await c(t),e}case`skills`:return Ue(u);case`sessions`:{if(!r||!o||!c)return D.red(`Session switching not available`);let e=await r.list(50);if(e.length===0)return D.yellow(`No sessions found.`);let t=e.map((e,t)=>{let n=a(e.updated),r=e.title?`"${e.title}"`:`Session: ${e.id}`;return e.id===i&&(r=e.title?`Current: "${e.title}"`:`Current Session`),{value:e.id,label:`${t+1}. ${r}`,hint:n}}),n=[D.bold(`
|
|
52
|
+
CLI Sessions Shortcuts:`),` ${D.cyan(`nova -r`)} / ${D.cyan(`--resume`)} Resume last sessions`,` ${D.cyan(`nova -s <id>`)} / ${D.cyan(`--sessions <id>`)} Resume specific sessions by ID`,` ${D.cyan(`nova -s ls [limit]`)} List last sessions (default: 10)`,` ${D.cyan(`nova -s rm <id>`)} Delete specific sessions`,` ${D.cyan(`nova -s rm --all`)} Delete all sessions`].join(`
|
|
53
|
+
`),s=await o.select({message:`Select a session to load:`,options:t,footer:n});if(s){await c(s);let t=e.find(e=>e.id===s),n=t?.title?`${t.title} (id: ${s})`:s;return D.green(`✓ Switched to session: ${n}`)}return D.yellow(`Session selection cancelled.`)}case`resume`:return"Use `nova --resume` from the CLI to resume your last session.";case`update`:return He();case`reset`:return e(o,s);case`help`:return Be;case`clear`:case`new`:return console.clear(),l&&await l(),``;case`quit`:return s?s():process.exit(0),null;case`exit`:return s?s():process.exit(0),null;default:return D.yellow(`Unknown: /${d}. Type /help`)}}async function He(){let e=await x();return e?e.hasUpdate?(console.log(D.yellow(`\n⚡ Updating novacode to v${e.latest}...`)),await y(!0)?D.green(`✓ Successfully updated to v${e.latest}! Please restart nova to apply changes.`):D.red(`✗ Update failed. Please try running 'nova update' manually in your terminal.`)):D.green(`✓ Already up to date (v${e.current})`):D.yellow(`Could not check for updates.`)}function Ue(e){if(e.length===0)return`${D.yellow(`No skills found.`)}\n\nSkill directories scanned (precedence order):\n .novacode/skills/\n .agents/skills/\n ~/.novacode/skills/\n ~/.agents/skills/`;let t=o(e),n=`${D.bold(`Available Skills:`)}\n`,r=1;for(let e of t){let t=e[0],i=e.length>1?`${D.yellow(`${t.name} (duplicate)`)}`:D.green(t.name);n+=`${r++}. ${i} — ${t.description}\n`;for(let t of e)n+=` ${D.dim(w(t.path))}\n`}return n+=D.dim(`
|
|
54
|
+
Skills are auto-loaded by the agent when relevant to your task.`),n}function We({agent:e,store:t,session:n,turn:r,prompts:i,mode:a,exit:o,handlePermissionSwitch:s,skills:c}){let[l,u]=R(``),[d,f]=R(0),[p,m]=R(null),h=L(null),g=L([]),_=L(-1);F(()=>{f(0)},[l]);let v=l.startsWith(`/`)&&!l.includes(` `),y=I(()=>{if(!v)return[];let e=l.slice(1).toLowerCase();return Z.filter(t=>t.name.startsWith(e)||t.aliases?.some(t=>t.startsWith(e)))},[l,v]);return ne((p,b)=>{if(b.ctrl&&(p===`c`||p===`d`)){if(r.busy){p===`c`&&r.abort();return}if(p===`d`){o();return}let e=Date.now();h.current&&h.current.key===`C`&&e-h.current.ts<2e3?o():(h.current={key:`C`,ts:e},m(`C`),setTimeout(()=>{h.current?.key===`C`&&Date.now()-h.current.ts>=2e3&&(h.current=null,m(null))},2e3));return}if(a.type!==`chat`)return;if(b.escape){r.busy?r.abort():l&&u(``);return}if(b.upArrow){if(v&&y.length>0){f(e=>e>0?e-1:y.length-1);return}g.current.length>0&&(_.current=Math.min(_.current+1,g.current.length-1),u(g.current[_.current]??``));return}if(b.downArrow){if(v&&y.length>0){f(e=>e<y.length-1?e+1:0);return}_.current=Math.max(_.current-1,-1),u(_.current>=0?g.current[_.current]??``:``);return}if(b.tab){if(v&&y.length>0){let e=y[d];e&&u(`/${e.name} `)}return}if(!b.return){u(e=>b.backspace||b.delete?e.slice(0,-1):e+(p||``));return}if(r.busy)return;let x=l.trim();if(!x)return;if(v&&y.length>0){let e=y[d];e&&(x=`/${e.name}`)}if(u(``),g.current.unshift(x),_.current=-1,x.startsWith(`/`)){let a=x.slice(1).split(` `)[0]?.toLowerCase();if(Z.find(e=>e.name===a||e.aliases?.includes(a??``))?.name===`permission`){s();return}(async()=>{x===`/compact`&&r.setBusy(!0);try{let r=await Ve(x,e,t,n.sessionId,i,o,n.switchSession,n.newSession,c);r&&n.addNotice(r)}catch(e){console.error(`Command dispatch error for "${x}":`,e)}finally{r.setBusy(!1)}})();return}let S={role:`user`,content:x};n.clearNotices(),n.commitMsg(S);let C=new AbortController;r.run(C)}),{input:l,setInput:u,suggestions:y,selCmdIdx:d,exitConfirmKey:p}}function Ge(e){let[t,n]=R({type:`chat`}),r=L(null),i=I(()=>({select:e=>new Promise(t=>{r.current=t,n({type:`select`,...e})}),searchSelect:e=>new Promise(t=>{r.current=t,n({type:`searchSelect`,...e})}),password:e=>new Promise(t=>{r.current=t,n({type:`password`,...e})}),confirm:e=>new Promise(t=>{r.current=t,n({type:`confirm`,...e})})}),[]),a=I(()=>({request:e=>new Promise(t=>{r.current=e=>t(e===!0),n({type:`approval`,req:e})})}),[]);return F(()=>(e.setApprover(a),()=>e.setApprover(null)),[e,a]),{mode:t,setMode:n,prompts:i,resolvePrompt:e=>{let t=r.current;r.current=null,n({type:`chat`}),t?.(e)}}}function Q(e,t,n,r){let[i,a]=R(n),[o,s]=R(r),[c,l]=R(0),[d,f]=R([]);F(()=>{async function e(){try{let e=await t.get(n);e&&l(e.contextTokens)}catch(e){console.error(`Failed to load initial session context size:`,e)}}e()},[t,n]);let p=P(n=>{s(e=>[...e,n]),e.appendMessages([n]),t.append(i,n).catch(e=>{console.error(`Error appending message to session store:`,e)})},[e,t,i]),m=P(e=>{f(t=>[...t,e])},[]),g=P(()=>f([]),[]);return{sessionId:i,messages:o,contextTokens:c,setContextTokens:l,commitMsg:p,commitDelta:P(async n=>{for(let e of n)await t.append(i,e);s(e=>[...e,...n]),e.appendMessages(n);let r=new Set;for(let e of n){if(e.role===`assistant`&&Array.isArray(e.content))for(let t of e.content)t.type===`tool-call`&&r.add(t.toolCallId);if(e.role===`tool`&&Array.isArray(e.content))for(let t of e.content)t.type===`tool-result`&&r.add(t.toolCallId)}return{committedToolCallIds:r,committedText:n.some(e=>e.role===`assistant`&&(typeof e.content==`string`?e.content.trim().length>0:e.content.some(e=>e.type===`text`&&e.text.trim().length>0)))}},[e,t,i]),switchSession:P(async n=>{let r=await t.get(n);if(!r)return;let i=h(r.provider),o=u(r.provider,r.model);if(i&&o){let t=(await C()).apiKeys[r.provider]||``;e.updateConfig({provider:i.id,model:o,apiKey:t})}let c=await t.messages(n),d=await t.history(n);process.stdout.isTTY&&process.stdout.write(`\x1B[2J\x1B[3J\x1B[H`),e.setMessages(c),s(d),a(n),f([]),o&&l(r.contextTokens)},[e,t]),newSession:P(async()=>{let n=e.model,r=await t.create(process.cwd(),n.id,n.provider);process.stdout.isTTY&&process.stdout.write(`\x1B[2J\x1B[3J\x1B[H`),e.setMessages([]),s([]),a(r.id),l(0),f([])},[e,t]),notices:d,addNotice:m,clearNotices:g}}function Ke(e,t,n,r){let i=[` v${e}`];if(n&&i.push(` AGENTS.md detected`),i.push(` permission: ${r}`),t.length>0){let e=o(t).map(e=>{let t=e[0].name;return e.length>1?`${t} (duplicate)`:t});i.push(` skills: ${e.join(`, `)}`)}return i.join(`
|
|
55
|
+
`)}function qe(e){let n=[];for(let i=0;i<e.length;i++){let a=e[i];if(a.role===`user`){let e=typeof a.content==`string`?a.content:a.content.map(e=>e.type===`text`?e.text:``).join(``);e.trim()&&n.push({id:`user-${i}`,type:`UserMessage`,content:e});continue}if(a.role===`assistant`){let o=typeof a.content==`string`?[{type:`text`,text:a.content}]:a.content;for(let a=0;a<o.length;a++){let s=o[a];if(s.type===`text`)s.text.trim()&&n.push({id:`assistant-${i}-${a}`,type:`AssistantMessage`,content:s.text});else if(s.type===`tool-call`){let a=null;for(let t=i+1;t<e.length;t++){let n=e[t];if(n.role===`tool`&&Array.isArray(n.content)){let e=n.content.find(e=>e.type===`tool-result`&&e.toolCallId===s.toolCallId);if(e){a=e;break}}}let o=t(s.input,!1);if(a){let{text:e,isError:t}=r(a.output);if(t)n.push({id:`tool-${s.toolCallId}`,type:`ToolFailed`,toolCallId:s.toolCallId,toolName:s.toolName,args:o,error:e});else{let t={id:`tool-${s.toolCallId}`,type:`ToolCompleted`,toolCallId:s.toolCallId,toolName:s.toolName,args:o};s.toolName===`read`?t.resultLineCount=e.split(`
|
|
56
|
+
`).length:s.toolName===`grep`&&(t.resultMatchCount=e.split(`
|
|
57
|
+
`).filter(Boolean).length),n.push(t)}}else n.push({id:`tool-${s.toolCallId}`,type:`ToolStarted`,toolCallId:s.toolCallId,toolName:s.toolName,args:o})}}}}return n}const $=[`Press / to open commands.`,`Use @ to reference files.`,`Press Esc to cancel input.`,`Use arrow keys to navigate history.`,`Use Tab for autocomplete.`,`Use Shift+Tab to move backwards.`,`Use Ctrl+C to stop execution.`,`Scroll terminal scrollback to review history.`,`Use /compact to shrink context when it gets long.`,`Use /models to switch providers and models.`,`Use /sessions to browse and resume past sessions.`,`Use /skills to list auto-loaded agent skills.`];function Je({messages:e,notices:t,contextTokens:n,version:r,skills:i,hasAgentsMd:a,permissionMode:o,turn:s}){let[c,l]=R(null),[u,d]=R(0);F(()=>{async function e(){try{let e=await x();e?.hasUpdate&&l({hasUpdate:!0,current:e.current,latest:e.latest})}catch(e){console.error(`Failed to check for updates:`,e)}}e()},[]),F(()=>{let e=setInterval(()=>{d(e=>(e+1)%$.length)},8e3);return()=>clearInterval(e)},[]);let f=$[u];return{committedEvents:I(()=>qe(e),[e]),liveEvents:I(()=>{let n=[];e.length===0?n.push({id:`splash`,type:`Splash`,content:Ke(r,i,a,o),update:c?.hasUpdate?{current:c.current,latest:c.latest}:void 0}):c?.hasUpdate&&n.push({id:`update-available`,type:`UpdateAvailable`,current:c.current,latest:c.latest});for(let e=0;e<t.length;e++)n.push({id:`notice-${e}`,type:`Notice`,content:t[e]});s.thinking&&n.push({id:`active-thinking`,type:`Thinking`}),s.bufferedStream&&n.push({id:`active-text`,type:`AssistantMessage`,content:s.bufferedStream});for(let e of s.activeTools)e.status===`running`?n.push({id:e.id,type:`ToolStarted`,toolCallId:e.id,toolName:e.name,args:e.args}):e.status===`success`?n.push({id:e.id,type:`ToolCompleted`,toolCallId:e.id,toolName:e.name,args:e.args,resultLineCount:e.lineCount,resultMatchCount:e.matchCount}):e.status===`failure`&&n.push({id:e.id,type:`ToolFailed`,toolCallId:e.id,toolName:e.name,args:e.args,error:e.error??`Unknown error`});return s.busy&&!s.thinking&&!s.bufferedStream&&n.push({id:`active-working`,type:`Thinking`}),n},[e.length,t,r,i,a,o,c,s.thinking,s.bufferedStream,s.activeTools,s.busy]),contextTokens:n,tip:f}}async function Ye(e,t,n,r=[],i=!1,a){process.stdout.write(`\x1B[?25l`);let o=await g(),s=await t.history(n);process.stdout.isTTY&&process.stdout.write(`\x1B[2J\x1B[3J\x1B[H`);try{let{waitUntilExit:c}=ee(z(l,{children:z(Xe,{agent:e,store:t,sessionId:n,skills:r,initialHistory:s,policy:a,version:o,hasAgentsMd:i})}),{exitOnCtrlC:!1});await c()}finally{process.stdout.write(`\x1B[?25h`),await t.prune()}}function Xe({agent:e,store:t,sessionId:n,skills:r,initialHistory:i,policy:a,version:o,hasAgentsMd:s}){let c=m(),l=Q(e,t,n,i),u=Ae(e,t,l.sessionId,l.setContextTokens,l.commitMsg,l.commitDelta),[d,p]=R(a.mode),{mode:h,prompts:g,resolvePrompt:_}=Ge(a),{committedEvents:v,liveEvents:y,contextTokens:b,tip:x}=Je({messages:l.messages,notices:l.notices,contextTokens:l.contextTokens,version:o,skills:r,hasAgentsMd:s,permissionMode:d,turn:u}),S=async()=>{let e=await g.select({message:`Permission mode`,options:[{value:`restricted`,label:`Restricted — ask permission before each action`,hint:d===`restricted`?`current`:void 0},{value:`unrestricted`,label:`Unrestricted — run without approval (may be dangerous)`,hint:d===`unrestricted`?`current`:void 0}]});e!==`restricted`&&e!==`unrestricted`||(a.setMode(e),p(e),l.addNotice(`✓ Permission mode set to ${e}.`))},{exit:C}=te(),{input:w,suggestions:T,selCmdIdx:E,exitConfirmKey:D}=We({agent:e,store:t,session:l,turn:u,prompts:g,mode:h,exit:C,handlePermissionSwitch:S,skills:r}),O=I(()=>D===`C`?{label:`Press Ctrl+C again to exit`,color:c.palette.warning}:h.type===`searchSelect`?{label:`Filtering...`,color:c.palette.primary}:h.type===`chat`?{label:`/help`,color:c.palette.muted}:{label:`Waiting for input`,color:c.palette.muted},[D,h.type,c]),A=h.type===`chat`?T:[];return B(k,{flexDirection:`column`,width:`100%`,children:[z(we,{committedEvents:v,liveEvents:y},l.sessionId),h.type!==`chat`&&z(f,{mode:h,onResolve:_}),z(ie,{input:w,suggestions:A,selCmdIdx:E}),z(Ee,{activity:O.label,activityColor:O.color,model:e.model,contextTokens:b,tip:x})]})}export{Ye as interactive};
|
|
58
|
+
//# sourceMappingURL=app-BShCIDQa.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-BShCIDQa.mjs","names":["#inCodeBlock","#codeBlockLang","#lastFullText","#lastFullOutput","#stableText","#stableOutput","#stableFenceState","runCompact"],"sources":["../src/tui/components/composer.tsx","../src/tui/markdown/richText.ts","../src/tui/markdown/syntax.ts","../src/tui/markdown/renderer.ts","../src/tui/markdown/stream.ts","../src/tui/components/message.tsx","../src/tui/components/conversation.tsx","../src/tui/components/statusBar.tsx","../src/tokens.ts","../src/compact.ts","../src/tui/hooks/useAgentTurn.ts","../src/commands/compact.ts","../src/commands/models.ts","../src/commands/providers.ts","../src/commands/index.ts","../src/tui/hooks/useInputHandler.ts","../src/tui/hooks/usePrompts.ts","../src/tui/hooks/useSession.ts","../src/tui/helpers.ts","../src/tui/hooks/useTuiTimeline.ts","../src/tui/app.tsx"],"sourcesContent":["import { Box, Text, useWindowSize } from \"ink\"\nimport { memo } from \"react\"\nimport type { Cmd } from \"../../types.ts\"\nimport { Cursor } from \"../core/liveArea.tsx\"\nimport { ScrollableList } from \"../core/scrollableList.tsx\"\nimport { useTheme } from \"../theme/index.tsx\"\n\nexport const Composer = memo(function Composer({\n\tinput,\n\tsuggestions,\n\tselCmdIdx,\n}: {\n\tinput: string\n\tsuggestions: Cmd[]\n\tselCmdIdx: number\n}) {\n\tconst theme = useTheme()\n\tconst { rows } = useWindowSize()\n\tconst terminalRows = rows || 24\n\tconst visibleCount = Math.max(3, Math.min(suggestions.length, terminalRows - 5))\n\n\treturn (\n\t\t<Box\n\t\t\tflexDirection=\"column\"\n\t\t\twidth=\"100%\"\n\t\t\tflexShrink={0}\n\t\t\tbackgroundColor={theme.palette.bg}\n\t\t\tpaddingX={1}\n\t\t\tpaddingBottom={1}\n\t\t>\n\t\t\t{suggestions.length > 0 && (\n\t\t\t\t<Box paddingTop={1}>\n\t\t\t\t\t<ScrollableList\n\t\t\t\t\t\titems={suggestions}\n\t\t\t\t\t\tselectedIndex={selCmdIdx}\n\t\t\t\t\t\tvisibleCount={visibleCount}\n\t\t\t\t\t\tkeyExtractor={(cmd) => cmd.name}\n\t\t\t\t\t\trenderItem={(cmd, _idx, isSelected) => (\n\t\t\t\t\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t\t\t\t\t<Text\n\t\t\t\t\t\t\t\t\tbackgroundColor={isSelected ? theme.palette.primary : undefined}\n\t\t\t\t\t\t\t\t\tcolor={isSelected ? theme.palette.bg : theme.palette.fg}\n\t\t\t\t\t\t\t\t\twrap=\"truncate-end\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t/{cmd.name.padEnd(12)}\n\t\t\t\t\t\t\t\t</Text>\n\t\t\t\t\t\t\t\t<Text color={theme.palette.muted}> {cmd.desc}</Text>\n\t\t\t\t\t\t\t</Box>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t\t<Box flexDirection=\"row\" paddingY={1}>\n\t\t\t\t<Box flexShrink={0} marginRight={1}>\n\t\t\t\t\t<Text bold color={theme.palette.muted}>\n\t\t\t\t\t\t{\"❯\"}\n\t\t\t\t\t</Text>\n\t\t\t\t</Box>\n\t\t\t\t<Box flexGrow={1} flexShrink={1}>\n\t\t\t\t\t<Text color={theme.palette.fg} wrap=\"wrap\">\n\t\t\t\t\t\t{input}\n\t\t\t\t\t\t<Cursor />\n\t\t\t\t\t</Text>\n\t\t\t\t</Box>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n})\n","import chalk from \"chalk\"\n\nexport function formatRichText(text: string): string {\n\tlet formatted = text\n\tformatted = formatted.replace(/`([^`]+)`/g, (_, code) => chalk.yellow(code))\n\tformatted = formatted.replace(/\\*\\*([^*]+)\\*\\*/g, (_, bold) => chalk.bold(bold))\n\tformatted = formatted.replace(/__([^_]+)__/, (_, bold) => chalk.bold(bold))\n\tformatted = formatted.replace(/\\*([^*]+)\\*/g, (_, italic) => chalk.italic(italic))\n\tformatted = formatted.replace(/_([^_]+)_/g, (_, italic) => chalk.italic(italic))\n\tformatted = formatted.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_, label, url) => {\n\t\treturn `${chalk.blue(label)} ${chalk.dim(`(${url})`)}`\n\t})\n\treturn formatted\n}\n","import chalk from \"chalk\"\n\ninterface LangSpec {\n\tcomment: null | string\n\tkeywords: Set<string>\n}\n\nconst kw = (s: string): Set<string> => new Set(s.split(/\\s+/).filter(Boolean))\n\nconst TS = kw(`\n\tabstract as async await break case catch class const continue debugger default delete do else enum export extends\n\tfalse finally for from function get if implements import in instanceof interface is let new null of package private\n\tprotected public readonly return set static super switch this throw true try type typeof undefined var void while\n\twith yield\n`)\n\nconst PY = kw(`\n\tFalse None True and as assert async await break class continue def del elif else except finally for from global if\n\timport in is lambda nonlocal not or pass raise return try while with yield\n`)\n\nconst SH = kw(`\n\tif then else elif fi for in do done while until case esac function return break continue local export readonly\n\tdeclare typeset\n`)\n\nconst GO = kw(`\n\tbreak case chan const continue default defer else fallthrough for func go goto if import interface map package range\n\treturn select struct switch type var nil true false\n`)\n\nconst RUST = kw(`\n\tas async await break const continue crate dyn else enum extern false fn for if impl in let loop match mod move mut\n\tpub ref return self Self static struct super trait true type unsafe use where while yield\n`)\n\nconst SQL = kw(`\n\tselect from where and or not in is null as by group order limit offset insert into values update set delete create\n\ttable drop alter add column primary key foreign references join left right inner outer on\n`)\n\nconst LANGS: Record<string, LangSpec> = {\n\tgo: { comment: \"//\", keywords: GO },\n\tjson: { comment: null, keywords: kw(\"true false null\") },\n\tpy: { comment: \"#\", keywords: PY },\n\trust: { comment: \"//\", keywords: RUST },\n\tsh: { comment: \"#\", keywords: SH },\n\tsql: { comment: \"--\", keywords: SQL },\n\tts: { comment: \"//\", keywords: TS },\n\tyaml: { comment: \"#\", keywords: kw(\"true false null yes no on off\") },\n}\n\nconst ALIAS: Record<string, string> = {\n\tbash: \"sh\",\n\tjavascript: \"ts\",\n\tjs: \"ts\",\n\tjsx: \"ts\",\n\tpython: \"py\",\n\trs: \"rust\",\n\tshell: \"sh\",\n\ttsx: \"ts\",\n\ttypescript: \"ts\",\n\tyml: \"yaml\",\n\tzsh: \"sh\",\n}\n\nconst resolve = (lang: string): LangSpec | null => LANGS[ALIAS[lang] ?? lang] ?? null\n\nexport const isHighlightable = (lang: string): boolean => resolve(lang) !== null\n\nconst TOKEN_RE =\n\t/'(?:[^'\\\\]|\\\\.)*'|\"(?:[^\"\\\\]|\\\\.)*\"|`(?:[^`\\\\]|\\\\.)*`|\\b\\d+(?:\\.\\d+)?\\b|[A-Za-z_$][\\w$]*/g\n\nexport function highlightCode(line: string, lang: string): string {\n\tconst spec = resolve(lang)\n\tif (!spec) return line\n\n\tif (spec.comment && line.trimStart().startsWith(spec.comment)) {\n\t\treturn chalk.gray(line)\n\t}\n\n\tlet result = \"\"\n\tlet last = 0\n\n\tfor (const m of line.matchAll(TOKEN_RE)) {\n\t\tconst tok = m[0]\n\t\tif (!tok) continue\n\t\tconst start = m.index ?? 0\n\t\tif (start > last) result += line.slice(last, start)\n\n\t\tconst ch = tok[0]\n\t\tif (ch === '\"' || ch === \"'\" || ch === \"`\") {\n\t\t\tresult += chalk.green(tok)\n\t\t} else if (ch && ch >= \"0\" && ch <= \"9\") {\n\t\t\tresult += chalk.yellow(tok)\n\t\t} else if (spec.keywords.has(tok)) {\n\t\t\tresult += chalk.magenta(tok)\n\t\t} else {\n\t\t\tresult += tok\n\t\t}\n\n\t\tlast = start + tok.length\n\t}\n\n\tif (last < line.length) result += line.slice(last)\n\treturn result\n}\n","import chalk from \"chalk\"\nimport { formatRichText } from \"./richText.ts\"\nimport { highlightCode, isHighlightable } from \"./syntax.ts\"\n\nexport type FenceState = { inCodeBlock: boolean; codeBlockLang: string }\n\nexport class MarkdownRenderer {\n\t#inCodeBlock = false\n\t#codeBlockLang = \"\"\n\n\tconstructor(seed?: FenceState) {\n\t\tif (seed) {\n\t\t\tthis.#inCodeBlock = seed.inCodeBlock\n\t\t\tthis.#codeBlockLang = seed.codeBlockLang\n\t\t}\n\t}\n\n\tgetState(): FenceState {\n\t\treturn {\n\t\t\tinCodeBlock: this.#inCodeBlock,\n\t\t\tcodeBlockLang: this.#codeBlockLang,\n\t\t}\n\t}\n\n\trenderChunk(text: string): string {\n\t\treturn text\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => this.renderLine(line))\n\t\t\t.join(\"\\n\")\n\t}\n\n\trenderLine(line: string): string {\n\t\tconst fence = line.match(/^\\s*(`{3,}|~{3,})(.*)$/)\n\t\tif (fence) {\n\t\t\tif (this.#inCodeBlock) {\n\t\t\t\tthis.#inCodeBlock = false\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t\tthis.#inCodeBlock = true\n\t\t\tthis.#codeBlockLang = (fence[2] ?? \"\").trim()\n\t\t\treturn this.#codeBlockLang ? chalk.gray(`─ ${this.#codeBlockLang}`) : \"\"\n\t\t}\n\n\t\tif (this.#inCodeBlock) {\n\t\t\tconst lang = this.#codeBlockLang\n\t\t\tconst code = isHighlightable(lang) ? highlightCode(line, lang) : chalk.dim(line)\n\t\t\treturn ` ${code}`\n\t\t}\n\n\t\tif (line.startsWith(\"#\")) {\n\t\t\tconst match = line.match(/^(#{1,6})\\s+(.*)$/)\n\t\t\tif (match?.[1] && match[2]) {\n\t\t\t\tconst level = match[1].length\n\t\t\t\tconst content = match[2]\n\t\t\t\tif (level === 1) return chalk.bold.magenta.underline(content)\n\t\t\t\tif (level === 2) return chalk.bold.blue(content)\n\t\t\t\treturn chalk.bold.cyan(content)\n\t\t\t}\n\t\t}\n\n\t\tlet formatted = line\n\t\tif (formatted.startsWith(\"- \") || formatted.startsWith(\"* \")) {\n\t\t\tformatted = ` ${chalk.yellow(\"•\")} ${formatted.slice(2)}`\n\t\t}\n\n\t\treturn formatRichText(formatted)\n\t}\n}\n\nexport function formatMarkdown(text: string): string {\n\treturn new MarkdownRenderer().renderChunk(text)\n}\n","import { type FenceState, MarkdownRenderer } from \"./renderer.ts\"\n\nexport class StreamingMarkdownRenderer {\n\t#stableText = \"\"\n\t#stableOutput = \"\"\n\t#stableFenceState: FenceState = { inCodeBlock: false, codeBlockLang: \"\" }\n\t#lastFullText = \"\"\n\t#lastFullOutput = \"\"\n\n\tupdate(fullText: string): string {\n\t\tif (fullText === this.#lastFullText) return this.#lastFullOutput\n\n\t\tif (this.#stableText && !fullText.startsWith(this.#stableText)) {\n\t\t\tthis.#stableText = \"\"\n\t\t\tthis.#stableOutput = \"\"\n\t\t\tthis.#stableFenceState = { inCodeBlock: false, codeBlockLang: \"\" }\n\t\t}\n\n\t\tconst boundary = findStableBoundary(fullText, this.#stableText.length, this.#stableFenceState)\n\n\t\tif (boundary > this.#stableText.length) {\n\t\t\tconst newStable = fullText.slice(0, boundary)\n\t\t\tconst chunk = this.#stableText ? newStable.slice(this.#stableText.length) : newStable\n\t\t\tconst renderer = new MarkdownRenderer(this.#stableFenceState)\n\t\t\tthis.#stableOutput += renderer.renderChunk(chunk)\n\t\t\tthis.#stableText = newStable\n\t\t\tthis.#stableFenceState = renderer.getState()\n\t\t}\n\n\t\tconst unstable = fullText.slice(this.#stableText.length)\n\t\tconst renderer = new MarkdownRenderer(this.#stableFenceState)\n\t\tconst unstableOutput = unstable ? renderer.renderChunk(unstable) : \"\"\n\n\t\tthis.#lastFullText = fullText\n\t\tthis.#lastFullOutput = this.#stableOutput + unstableOutput\n\t\treturn this.#lastFullOutput\n\t}\n\n\treset(): void {\n\t\tthis.#stableText = \"\"\n\t\tthis.#stableOutput = \"\"\n\t\tthis.#stableFenceState = { inCodeBlock: false, codeBlockLang: \"\" }\n\t\tthis.#lastFullText = \"\"\n\t\tthis.#lastFullOutput = \"\"\n\t}\n}\n\nfunction getFenceStateFromSeed(seed: FenceState, text: string): FenceState {\n\tlet inCodeBlock = seed.inCodeBlock\n\tlet codeBlockLang = seed.codeBlockLang\n\n\tfor (const line of text.split(\"\\n\")) {\n\t\tconst trimmed = line.trim()\n\t\tif (/^(?:`{3,}|~{3,})/.test(trimmed)) {\n\t\t\tif (inCodeBlock) {\n\t\t\t\tinCodeBlock = false\n\t\t\t\tcodeBlockLang = \"\"\n\t\t\t} else {\n\t\t\t\tinCodeBlock = true\n\t\t\t\tcodeBlockLang = trimmed.slice(3).trim()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { inCodeBlock, codeBlockLang }\n}\n\nfunction findStableBoundary(text: string, minIndex: number, stableFenceState: FenceState): number {\n\tlet idx = text.length\n\n\twhile (idx > minIndex) {\n\t\tconst boundary = text.lastIndexOf(\"\\n\\n\", idx - 1)\n\t\tif (boundary < minIndex) return minIndex\n\n\t\tconst splitAt = boundary + 2\n\t\tconst slice = text.slice(minIndex, splitAt)\n\t\tconst state = getFenceStateFromSeed(stableFenceState, slice)\n\t\tif (!state.inCodeBlock) {\n\t\t\treturn splitAt\n\t\t}\n\t\tidx = boundary\n\t}\n\n\treturn minIndex\n}\n","import { Box, Text } from \"ink\"\nimport BigText from \"ink-big-text\"\nimport { memo } from \"react\"\nimport { Spinner } from \"../core/liveArea.tsx\"\nimport { formatMarkdown } from \"../markdown/index.ts\"\nimport { useTheme } from \"../theme/index.tsx\"\nimport type { TimelineEvent } from \"../types.ts\"\n\nconst SplashView = memo(function SplashView({\n\tcontent,\n\tupdate,\n}: {\n\tcontent: string\n\tupdate?: { current: string; latest: string }\n}) {\n\tconst theme = useTheme()\n\tconst lines = content.split(\"\\n\")\n\treturn (\n\t\t<Box flexDirection=\"column\" marginBottom={0}>\n\t\t\t<BigText\n\t\t\t\ttext=\"novacode\"\n\t\t\t\tfont=\"block\"\n\t\t\t\tcolors={[\"cyan\", \"blue\"]}\n\t\t\t\tspace={false}\n\t\t\t\tlineHeight={0}\n\t\t\t/>\n\t\t\t{lines.map((line) => (\n\t\t\t\t<Text key={line} color={theme.palette.muted}>\n\t\t\t\t\t{line}\n\t\t\t\t</Text>\n\t\t\t))}\n\t\t\t{update && (\n\t\t\t\t<Box marginTop={1}>\n\t\t\t\t\t<Text color={theme.palette.success} bold>\n\t\t\t\t\t\t⬆ v{update.current} → v{update.latest}\n\t\t\t\t\t</Text>\n\t\t\t\t\t<Text color={theme.palette.muted}> Run /update to upgrade</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t)\n})\n\nconst UserMessageView = memo(function UserMessageView({ content }: { content: string }) {\n\tconst theme = useTheme()\n\treturn (\n\t\t<Box flexDirection=\"column\" width=\"100%\" marginBottom={1}>\n\t\t\t<Box\n\t\t\t\tflexDirection=\"column\"\n\t\t\t\twidth=\"100%\"\n\t\t\t\tpaddingX={1}\n\t\t\t\tpaddingY={1}\n\t\t\t\tbackgroundColor={theme.palette.bg}\n\t\t\t>\n\t\t\t\t<Text bold color={theme.palette.fg} wrap=\"wrap\">\n\t\t\t\t\t{content}\n\t\t\t\t</Text>\n\t\t\t</Box>\n\t\t</Box>\n\t)\n})\n\nconst AssistantMessageView = memo(function AssistantMessageView({\n\tcontent,\n\tisStreaming = false,\n}: {\n\tcontent: string\n\tisStreaming?: boolean\n}) {\n\tconst theme = useTheme()\n\tconst text = isStreaming ? content : formatMarkdown(content)\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1} paddingBottom={1}>\n\t\t\t<Text color={theme.palette.fg} wrap=\"wrap\">\n\t\t\t\t{text}\n\t\t\t</Text>\n\t\t</Box>\n\t)\n})\n\nconst ToolEventView = memo(function ToolEventView({ event }: { event: TimelineEvent }) {\n\tconst theme = useTheme()\n\tif (\n\t\tevent.type !== \"ToolStarted\" &&\n\t\tevent.type !== \"ToolCompleted\" &&\n\t\tevent.type !== \"ToolFailed\"\n\t) {\n\t\treturn null\n\t}\n\n\tconst isRunning = event.type === \"ToolStarted\"\n\tconst isFailure = event.type === \"ToolFailed\"\n\tconst bulletColor = isRunning\n\t\t? theme.palette.warning\n\t\t: isFailure\n\t\t\t? theme.palette.error\n\t\t\t: theme.palette.success\n\tconst bullet = isRunning ? \"○\" : \"●\"\n\n\treturn (\n\t\t<Box flexDirection=\"column\" paddingX={1} paddingBottom={1}>\n\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t<Text color={bulletColor}>{bullet} </Text>\n\t\t\t\t<Text bold color={theme.palette.primary}>\n\t\t\t\t\t{event.toolName}\n\t\t\t\t</Text>\n\t\t\t\t<Text color={theme.palette.muted}> {event.args}</Text>\n\t\t\t\t{event.type === \"ToolCompleted\" && event.resultLineCount !== undefined && (\n\t\t\t\t\t<Text color={theme.palette.muted}> ({event.resultLineCount} lines)</Text>\n\t\t\t\t)}\n\t\t\t\t{event.type === \"ToolCompleted\" && event.resultMatchCount !== undefined && (\n\t\t\t\t\t<Text color={theme.palette.muted}> ({event.resultMatchCount} matches)</Text>\n\t\t\t\t)}\n\t\t\t</Box>\n\t\t\t{isFailure && (\n\t\t\t\t<Box marginLeft={2}>\n\t\t\t\t\t<Text color={theme.palette.error}>{event.error}</Text>\n\t\t\t\t</Box>\n\t\t\t)}\n\t\t</Box>\n\t)\n})\n\nconst ThinkingView = memo(function ThinkingView({ label }: { label: string }) {\n\tconst theme = useTheme()\n\treturn (\n\t\t<Box flexDirection=\"row\" paddingX={1} paddingBottom={1}>\n\t\t\t<Box marginRight={1}>\n\t\t\t\t<Spinner />\n\t\t\t</Box>\n\t\t\t<Text color={theme.palette.warning}>{label}</Text>\n\t\t</Box>\n\t)\n})\n\nexport const EventRenderer = memo(function EventRenderer({ event }: { event: TimelineEvent }) {\n\tconst theme = useTheme()\n\tswitch (event.type) {\n\t\tcase \"Splash\":\n\t\t\treturn <SplashView content={event.content} update={event.update} />\n\n\t\tcase \"UserMessage\":\n\t\t\treturn <UserMessageView content={event.content} />\n\n\t\tcase \"AssistantMessage\":\n\t\t\treturn (\n\t\t\t\t<AssistantMessageView content={event.content} isStreaming={event.id === \"active-text\"} />\n\t\t\t)\n\n\t\tcase \"ToolStarted\":\n\t\tcase \"ToolCompleted\":\n\t\tcase \"ToolFailed\":\n\t\t\treturn <ToolEventView event={event} />\n\n\t\tcase \"Thinking\": {\n\t\t\tconst label = event.id === \"active-working\" ? \"Working…\" : \"Thinking…\"\n\t\t\treturn <ThinkingView label={label} />\n\t\t}\n\n\t\tcase \"Warning\":\n\t\t\treturn (\n\t\t\t\t<Box flexDirection=\"row\" marginBottom={0}>\n\t\t\t\t\t<Text color={theme.palette.warning}>⚠ {event.content}</Text>\n\t\t\t\t</Box>\n\t\t\t)\n\n\t\tcase \"SystemMessage\":\n\t\t\treturn (\n\t\t\t\t<Box flexDirection=\"row\" marginBottom={0}>\n\t\t\t\t\t<Text color={theme.palette.primary}>ℹ {event.content}</Text>\n\t\t\t\t</Box>\n\t\t\t)\n\n\t\tcase \"Notice\":\n\t\t\treturn (\n\t\t\t\t<Box flexDirection=\"column\" paddingX={1} paddingBottom={1}>\n\t\t\t\t\t<Text wrap=\"wrap\">{event.content}</Text>\n\t\t\t\t</Box>\n\t\t\t)\n\n\t\tcase \"UpdateAvailable\":\n\t\t\treturn (\n\t\t\t\t<Box flexDirection=\"column\" marginBottom={1}>\n\t\t\t\t\t<Box flexDirection=\"row\">\n\t\t\t\t\t\t<Text color={theme.palette.success} bold>\n\t\t\t\t\t\t\t⬆ v{event.current} → v{event.latest}\n\t\t\t\t\t\t</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t\t<Box marginLeft={2}>\n\t\t\t\t\t\t<Text color={theme.palette.muted}>Run /update to upgrade</Text>\n\t\t\t\t\t</Box>\n\t\t\t\t</Box>\n\t\t\t)\n\n\t\tdefault:\n\t\t\treturn null\n\t}\n})\n","import { Box, Static, useWindowSize } from \"ink\"\nimport type { TimelineEvent } from \"../types.ts\"\nimport { EventRenderer } from \"./message.tsx\"\n\nexport function Conversation({\n\tcommittedEvents,\n\tliveEvents,\n}: {\n\tcommittedEvents: TimelineEvent[]\n\tliveEvents: TimelineEvent[]\n}) {\n\tconst { columns } = useWindowSize()\n\tconst width = columns ?? 80\n\n\treturn (\n\t\t<Box flexDirection=\"column\" flexGrow={1}>\n\t\t\t<Static items={committedEvents} style={{ width }}>\n\t\t\t\t{(event) => <EventRenderer key={event.id} event={event} />}\n\t\t\t</Static>\n\t\t\t{liveEvents.map((event) => (\n\t\t\t\t<EventRenderer key={event.id} event={event} />\n\t\t\t))}\n\t\t</Box>\n\t)\n}\n","import { Box, Text } from \"ink\"\nimport type { Model } from \"../../types.ts\"\nimport { useTheme } from \"../theme/index.tsx\"\n\nfunction fmtK(n: number): string {\n\tconst k = n / 1000\n\treturn k >= 100 ? `${Math.round(k)}K` : `${k.toFixed(1)}K`\n}\n\nfunction formatTokenUsage(used: number, contextWindow: number): string {\n\tif (used === 0) return `0/${fmtK(contextWindow)}`\n\tconst pct = Math.round((used / contextWindow) * 100)\n\treturn `${fmtK(used)}/${fmtK(contextWindow)} (${pct}%)`\n}\n\nexport function StatusBar({\n\tactivity,\n\tactivityColor,\n\tmodel,\n\tcontextTokens,\n\ttip,\n}: {\n\tactivity: string\n\tactivityColor: string\n\tmodel: Model\n\tcontextTokens: number\n\ttip: string\n}) {\n\tconst theme = useTheme()\n\n\treturn (\n\t\t<Box\n\t\t\tflexDirection=\"row\"\n\t\t\twidth=\"100%\"\n\t\t\tflexShrink={0}\n\t\t\tpaddingX={1}\n\t\t\tpaddingBottom={1}\n\t\t\tbackgroundColor={theme.palette.bg}\n\t\t>\n\t\t\t<Text color={activityColor}>{activity}</Text>\n\t\t\t<Text color={theme.palette.muted}> • Tip: </Text>\n\t\t\t<Text color={theme.palette.primary}>{tip}</Text>\n\t\t\t<Box flexGrow={1} />\n\t\t\t<Text color={theme.palette.muted}>\n\t\t\t\t{formatTokenUsage(contextTokens, model.contextWindow)}\n\t\t\t</Text>\n\t\t\t<Text color={theme.palette.muted}> • </Text>\n\t\t\t<Text bold color={theme.palette.fg}>\n\t\t\t\t{model.id}\n\t\t\t</Text>\n\t\t</Box>\n\t)\n}\n","import type { ModelMessage } from \"ai\"\n\n// ~4 chars per token for English/code. Close enough for capacity warnings.\nexport function estimateTokens(input: ModelMessage[] | string): number {\n\tif (typeof input === \"string\") {\n\t\treturn Math.ceil(input.length / 4)\n\t}\n\n\tlet chars = 0\n\tfor (const msg of input) {\n\t\tconst content = msg.content\n\t\tif (typeof content === \"string\") {\n\t\t\tchars += content.length\n\t\t\tcontinue\n\t\t}\n\t\tfor (const part of content) {\n\t\t\tif (part.type === \"text\") chars += part.text.length\n\t\t\telse if (part.type === \"tool-call\") chars += JSON.stringify(part.input).length\n\t\t\telse if (part.type === \"reasoning\") chars += part.text.length\n\t\t}\n\t}\n\treturn Math.ceil(chars / 4)\n}\n","import { generateText, type ModelMessage } from \"ai\"\nimport { summarizeToolOutput } from \"./content.ts\"\nimport type { SessionStore } from \"./db/sessionStore.ts\"\nimport { getProvider } from \"./models/lookup.ts\"\nimport { createModel, reasoningOpts } from \"./providers.ts\"\nimport { estimateTokens } from \"./tokens.ts\"\nimport type { CompactResult, Model } from \"./types.ts\"\n\nfunction extractText(msg: ModelMessage): string {\n\tif (msg.role === \"tool\") {\n\t\treturn msg.content\n\t\t\t.map((p) => (p.type === \"tool-result\" ? summarizeToolOutput(p.output).text : \"\"))\n\t\t\t.join(\"\\n\")\n\t}\n\tif (typeof msg.content === \"string\") return msg.content\n\treturn msg.content\n\t\t.filter((p) => p.type === \"text\")\n\t\t.map((p) => (p.type === \"text\" ? p.text : \"\"))\n\t\t.join(\"\")\n}\n\nexport async function compact(\n\tstore: SessionStore,\n\tsessionId: string,\n\tmessages: ModelMessage[],\n\tmodel: Model,\n\tapiKey: string,\n\tcwd: string,\n): Promise<CompactResult> {\n\tconst stored = await store.get(sessionId)\n\t// Fall back to a char/4 estimate when no turn has run yet (fresh session).\n\tconst tokensBefore =\n\t\tstored && stored.contextTokens > 0 ? stored.contextTokens : estimateTokens(messages)\n\t// Tail protection token budget: 10% of total context window, minimum 20,000 tokens\n\tconst tailTokenBudget = Math.max(20000, Math.round(model.contextWindow * 0.1))\n\n\tlet accumulatedTokens = 0\n\tlet cutIndex = messages.length\n\n\t// Walk backward from the end to dynamically select the tail messages based purely on token budget\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst msgTokens = estimateTokens([messages[i]!])\n\n\t\tif (accumulatedTokens + msgTokens <= tailTokenBudget) {\n\t\t\taccumulatedTokens += msgTokens\n\t\t\tcutIndex = i\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif (cutIndex <= 0) {\n\t\treturn { compacted: false, tokensBefore, tokensAfter: tokensBefore }\n\t}\n\n\tconst tail = messages.slice(cutIndex)\n\tconst old = messages.slice(0, cutIndex)\n\n\tconst convo = old\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") return `User: ${extractText(m)}`\n\t\t\tif (m.role === \"assistant\") return `Assistant: ${extractText(m)}`\n\t\t\tif (m.role === \"tool\") return `Tool: ${extractText(m).slice(0, 200)}`\n\t\t\treturn \"\"\n\t\t})\n\t\t.join(\"\\n\\n\")\n\n\tconst summary = await generateSummary(convo, model, apiKey)\n\tif (!summary) {\n\t\treturn { compacted: false, tokensBefore, tokensAfter: tokensBefore }\n\t}\n\n\tconst summaryMsg: ModelMessage = {\n\t\trole: \"user\",\n\t\tcontent: `[Prior context summary]\\n${summary}`,\n\t}\n\n\tawait store.endSession(sessionId, \"compacted\")\n\tconst newSession = await store.createContinuation(sessionId, cwd, model.id, model.provider)\n\n\tconst newMsgs: ModelMessage[] = [summaryMsg, ...tail]\n\tfor (const msg of newMsgs) {\n\t\tawait store.append(newSession.id, msg)\n\t}\n\n\tconst tokensAfter = estimateTokens(newMsgs)\n\tif (tokensAfter >= tokensBefore) {\n\t\treturn { compacted: false, tokensBefore, tokensAfter: tokensBefore }\n\t}\n\n\treturn { compacted: true, summary, tokensBefore, tokensAfter, newSessionId: newSession.id }\n}\n\nasync function generateSummary(\n\tconvo: string,\n\tmodel: Model,\n\tapiKey: string,\n): Promise<string | null> {\n\tconst provider = getProvider(model.provider)\n\tif (!provider) return null\n\n\tconst { text } = await generateText({\n\t\tmodel: createModel(provider.id, model.id, apiKey),\n\t\tsystem:\n\t\t\t\"Summarize this coding session concisely. Cover: what was asked, files touched, what was done, key decisions. Keep it under 300 words.\",\n\t\tprompt: convo,\n\t\tproviderOptions: model.reasoning ? reasoningOpts(provider.id) : undefined,\n\t})\n\n\treturn text.trim() || null\n}\n\nexport async function generateSessionTitle(\n\tmessages: ModelMessage[],\n\tmodel: Model,\n\tapiKey: string,\n): Promise<string | null> {\n\tconst provider = getProvider(model.provider)\n\tif (!provider) return null\n\n\tconst convo = messages\n\t\t.slice(0, 4)\n\t\t.map((m) => {\n\t\t\tif (m.role === \"user\") return `User: ${extractText(m)}`\n\t\t\tif (m.role === \"assistant\") return `Assistant: ${extractText(m)}`\n\t\t\treturn \"\"\n\t\t})\n\t\t.join(\"\\n\")\n\n\tconst { text } = await generateText({\n\t\tmodel: createModel(provider.id, model.id, apiKey),\n\t\tsystem:\n\t\t\t\"Generate a very short, descriptive, and concise title for this coding conversation. Do not use quotes or prefixes like 'Title:'. Max 6 words.\",\n\t\tprompt: convo,\n\t\tproviderOptions: model.reasoning ? reasoningOpts(provider.id) : undefined,\n\t})\n\n\treturn (\n\t\ttext\n\t\t\t.replace(/\\s+/g, \" \")\n\t\t\t.trim()\n\t\t\t.replace(/^[\"']|[\"']$/g, \"\")\n\t\t\t.slice(0, 60)\n\t\t\t.trim() || null\n\t)\n}\n","import type { ToolResultOutput } from \"@ai-sdk/provider-utils\"\nimport type { ModelMessage } from \"ai\"\nimport { useCallback, useRef, useState } from \"react\"\nimport type { Agent } from \"../../agent/agent.ts\"\nimport { generateSessionTitle } from \"../../compact.ts\"\nimport { summarizeToolOutput } from \"../../content.ts\"\nimport type { SessionStore } from \"../../db/sessionStore.ts\"\nimport { formatToolArgs } from \"../../format.ts\"\nimport { StreamingMarkdownRenderer } from \"../markdown/index.ts\"\nimport type { ActiveTool } from \"../types.ts\"\n\nconst FLUSH_MS = 16\n\n/**\n * Extracts a human-readable error message from an API or execution error object.\n */\nfunction errorMessage(err: unknown): string {\n\tif (err instanceof Error) {\n\t\tconst last = \"lastError\" in err ? (err as { lastError: unknown }).lastError : null\n\t\tif (last instanceof Error) return errorMessage(last)\n\t\tconst body = (err as { responseBody?: string }).responseBody\n\t\tif (body) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(body) as { error?: { message?: string }; message?: string }\n\t\t\t\treturn parsed.error?.message ?? parsed.message ?? err.message\n\t\t\t} catch {}\n\t\t}\n\t\treturn err.message\n\t}\n\treturn String(err)\n}\n\n/**\n * Hook that manages the execution lifecycle of a single agent turn.\n *\n * It initiates the call to the agent provider, loops over the streaming response parts,\n * updates the list of active tools executing on the host machine, and records token usage\n * and generated messages in the database.\n *\n * It also encapsulates the frame-rate-limited stream buffering to smooth out rendering:\n * raw markdown updates are accumulated in a ref buffer and flushed to React state at 60fps\n * (every 16ms) to avoid choking the React layout engine during fast stream deliveries.\n */\nexport function useAgentTurn(\n\tagent: Agent,\n\tstore: SessionStore,\n\tsessionId: string,\n\tsetContextTokens: (updater: (prev: number) => number) => void,\n\tcommitMsg: (msg: ModelMessage) => void,\n\tcommitDelta: (\n\t\tdelta: ModelMessage[],\n\t) => Promise<{ committedToolCallIds: Set<string>; committedText: boolean }>,\n) {\n\tconst [busy, setBusy] = useState(false)\n\tconst [thinking, setThinking] = useState(false)\n\tconst [activeTools, setActiveTools] = useState<ActiveTool[]>([])\n\tconst abortRef = useRef<AbortController | null>(null)\n\tconst committedRef = useRef(0)\n\n\t// Stream buffering states & refs\n\tconst [bufferedStream, setBufferedStream] = useState(\"\")\n\tconst rawBuf = useRef(\"\")\n\tconst renderer = useRef(new StreamingMarkdownRenderer())\n\tconst flushTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n\tconst dirty = useRef(false)\n\n\t// Flushes the accumulated raw string delta through the markdown renderer to state.\n\tconst flushStream = useCallback(() => {\n\t\tflushTimer.current = null\n\t\tif (!dirty.current) return\n\t\tdirty.current = false\n\t\tconst raw = rawBuf.current\n\t\tconst rendered = renderer.current.update(raw)\n\t\tsetBufferedStream(rendered)\n\t}, [])\n\n\t// Appends streaming text to the raw buffer and schedules a throttle timer.\n\tconst appendStream = useCallback(\n\t\t(text: string) => {\n\t\t\trawBuf.current += text\n\t\t\tdirty.current = true\n\t\t\tif (!flushTimer.current) {\n\t\t\t\tflushTimer.current = setTimeout(flushStream, FLUSH_MS)\n\t\t\t}\n\t\t},\n\t\t[flushStream],\n\t)\n\n\t// Resets the streaming buffer state.\n\tconst resetStream = useCallback(() => {\n\t\tif (flushTimer.current) {\n\t\t\tclearTimeout(flushTimer.current)\n\t\t\tflushTimer.current = null\n\t\t}\n\t\trawBuf.current = \"\"\n\t\tdirty.current = false\n\t\trenderer.current.reset()\n\t\tsetBufferedStream(\"\")\n\t}, [])\n\n\tconst abort = useCallback(() => {\n\t\tabortRef.current?.abort()\n\t\tabortRef.current = null\n\t}, [])\n\n\tconst run = useCallback(\n\t\tasync (ctrl: AbortController) => {\n\t\t\tconst signal = ctrl.signal\n\t\t\tabortRef.current = ctrl\n\t\t\tsetBusy(true)\n\t\t\tresetStream()\n\t\t\tsetThinking(false)\n\t\t\tsetActiveTools([])\n\t\t\tcommittedRef.current = 0\n\t\t\tlet streamError: unknown\n\n\t\t\ttry {\n\t\t\t\t// The agent.prompt call initiates the AI SDK stream loop.\n\t\t\t\t// We supply an onStepFinish handler to checkpoint generated messages\n\t\t\t\t// and database records as soon as each turn step (e.g. tool execution) completes.\n\t\t\t\tconst result = await agent.prompt(signal, async (event) => {\n\t\t\t\t\tconst u = event.usage\n\t\t\t\t\tif (u && u.inputTokens != null) {\n\t\t\t\t\t\t// Overwrite, not accumulate: inputTokens is the full context size of this step.\n\t\t\t\t\t\tsetContextTokens(() => u.inputTokens ?? 0)\n\t\t\t\t\t\tawait store.setContextTokens(sessionId, u.inputTokens ?? 0)\n\t\t\t\t\t}\n\t\t\t\t\tif (event.response?.messages?.length) {\n\t\t\t\t\t\tconst delta = event.response.messages.slice(committedRef.current)\n\t\t\t\t\t\tif (delta.length === 0) return\n\t\t\t\t\t\tcommittedRef.current = event.response.messages.length\n\n\t\t\t\t\t\tconst { committedToolCallIds, committedText } = await commitDelta(delta)\n\n\t\t\t\t\t\tif (committedToolCallIds.size > 0) {\n\t\t\t\t\t\t\tsetActiveTools((prev) => prev.filter((t) => !committedToolCallIds.has(t.id)))\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Clear transient streaming text once it becomes a committed assistant message.\n\t\t\t\t\t\tif (committedText) resetStream()\n\t\t\t\t\t}\n\t\t\t\t})\n\n\t\t\t\t// Read text deltas, reasoning blocks, and tool events in real time.\n\t\t\t\tfor await (const part of result.fullStream) {\n\t\t\t\t\tswitch (part.type) {\n\t\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t\tif (part.text) {\n\t\t\t\t\t\t\t\tsetThinking(false)\n\t\t\t\t\t\t\t\tappendStream(part.text)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\t\t\tsetThinking(true)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\tcase \"tool-call\": {\n\t\t\t\t\t\t\tsetThinking(false)\n\t\t\t\t\t\t\tconst args = formatToolArgs(part.input as Record<string, unknown>, false)\n\t\t\t\t\t\t\tsetActiveTools((prev) => {\n\t\t\t\t\t\t\t\tif (prev.some((t) => t.id === part.toolCallId)) return prev\n\t\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t{ id: part.toolCallId, name: part.toolName, args, status: \"running\" as const },\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase \"tool-result\": {\n\t\t\t\t\t\t\tconst { text, isError } = summarizeToolOutput(part.output as ToolResultOutput)\n\t\t\t\t\t\t\tsetActiveTools((prev) =>\n\t\t\t\t\t\t\t\tprev.map((t) => {\n\t\t\t\t\t\t\t\t\tif (t.id !== part.toolCallId) return t\n\t\t\t\t\t\t\t\t\tif (isError) {\n\t\t\t\t\t\t\t\t\t\treturn { ...t, status: \"failure\" as const, error: text }\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tlet lineCount: number | undefined\n\t\t\t\t\t\t\t\t\tlet matchCount: number | undefined\n\t\t\t\t\t\t\t\t\tif (t.name === \"read\") lineCount = text.split(\"\\n\").length\n\t\t\t\t\t\t\t\t\telse if (t.name === \"grep\") matchCount = text.split(\"\\n\").filter(Boolean).length\n\t\t\t\t\t\t\t\t\treturn { ...t, status: \"success\" as const, lineCount, matchCount }\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcase \"error\":\n\t\t\t\t\t\t\tstreamError = part.error\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Complete any uncommitted messages remaining at the end of the stream.\n\t\t\t\tconst resp = await result.response\n\t\t\t\tconst finalDelta = resp.messages.slice(committedRef.current)\n\t\t\t\tif (finalDelta.length > 0) await commitDelta(finalDelta)\n\n\t\t\t\t// Automatically generate a session title if this is the first interaction turn.\n\t\t\t\ttry {\n\t\t\t\t\tconst s = await store.get(sessionId)\n\t\t\t\t\tif (s && !s.title && agent.messages.length >= 2) {\n\t\t\t\t\t\tconst title = await generateSessionTitle(agent.messages, agent.model, agent.apiKey)\n\t\t\t\t\t\tif (title) {\n\t\t\t\t\t\t\tawait store.setTitle(sessionId, title)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error(\"Failed to generate or save session title:\", err)\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tcommitMsg({ role: \"assistant\", content: \"(aborted)\" })\n\t\t\t\t} else {\n\t\t\t\t\tcommitMsg({\n\t\t\t\t\t\trole: \"assistant\",\n\t\t\t\t\t\tcontent: `Error: ${errorMessage(streamError ?? err)}`,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tabortRef.current = null\n\t\t\t\tsetBusy(false)\n\t\t\t\tresetStream()\n\t\t\t\tsetThinking(false)\n\t\t\t\tsetActiveTools([])\n\t\t\t}\n\t\t},\n\t\t[agent, store, sessionId, setContextTokens, commitMsg, commitDelta, appendStream, resetStream],\n\t)\n\n\treturn { busy, thinking, activeTools, bufferedStream, run, abort, setBusy }\n}\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { compact as runCompact } from \"../compact.ts\"\nimport type { SessionStore } from \"../db/sessionStore.ts\"\n\nexport async function handleCompact(\n\tagent: Agent,\n\tstore: SessionStore,\n\tsessionId: string,\n): Promise<{ result: string; newSessionId?: string }> {\n\tconst res = await runCompact(\n\t\tstore,\n\t\tsessionId,\n\t\tagent.messages,\n\t\tagent.model,\n\t\tagent.apiKey,\n\t\tprocess.cwd(),\n\t)\n\n\tif (res.compacted) {\n\t\tconst msgs = await store.messages(res.newSessionId ?? sessionId)\n\t\tagent.setMessages(msgs)\n\t\tconst pct = Math.round(((res.tokensBefore - res.tokensAfter) / res.tokensBefore) * 100)\n\t\treturn {\n\t\t\tresult: chalk.green(`✓ Compacted (${pct}% reduction)`),\n\t\t\tnewSessionId: res.newSessionId,\n\t\t}\n\t}\n\n\treturn { result: chalk.yellow(\"Context is small enough, no compaction needed.\") }\n}\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { loadAuth, loadConfig, saveConfig } from \"../config/store.ts\"\nimport { PROVIDERS } from \"../models/catalog.ts\"\nimport { getModel, getModelById, getModelsForProvider, getProvider } from \"../models/lookup.ts\"\nimport type { Prompts } from \"../types.ts\"\n\nexport async function handleModels(args: string, agent: Agent, prompts?: Prompts): Promise<string> {\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\n\tif (args) return await switchDirect(args.trim(), agent)\n\n\tif (!prompts) return chalk.red(\"Prompts not available in this context\")\n\n\tconst activeModels = PROVIDERS.filter((p) => auth.apiKeys[p.id]).flatMap((p) =>\n\t\tgetModelsForProvider(p.id),\n\t)\n\n\tif (!activeModels.length)\n\t\treturn chalk.yellow(\"No models available. Use /providers to add a provider API key.\")\n\n\tconst maxLen = Math.max(...activeModels.map((m) => m.id.length), 20)\n\n\tconst options: Array<{ value: string; label: string; hint?: string }> = []\n\tfor (const m of activeModels) {\n\t\tconst cur = m.id === config.model && m.provider === config.provider\n\t\tconst pDef = getProvider(m.provider)!\n\n\t\toptions.push({\n\t\t\tvalue: `${m.provider}:${m.id}`,\n\t\t\tlabel: `${cur ? chalk.green(\"●\") : \"○\"} ${m.id.padEnd(maxLen + 2)} ${fmt(m.contextWindow).padEnd(8)}`,\n\t\t\thint: pDef.name,\n\t\t})\n\t}\n\n\tconst pick = await prompts.searchSelect({ message: \"Model (type to filter)\", options })\n\tif (!pick) return \"\"\n\n\tconst [pk, mid] = pick.split(\":\")\n\tconst selectedModel = getModel(pk!, mid!)\n\tconst selectedProvider = getProvider(pk!)\n\n\tif (!selectedModel || !selectedProvider) return chalk.red(\"Error: Model or provider not found\")\n\n\tconfig.provider = pk!\n\tconfig.model = mid!\n\tawait saveConfig(config)\n\n\tagent.updateConfig({\n\t\tprovider: selectedProvider.id,\n\t\tmodel: selectedModel,\n\t\tapiKey: auth.apiKeys[pk!] ?? \"\",\n\t})\n\treturn chalk.green(`✓ Switched to ${mid}`)\n}\n\nasync function switchDirect(id: string, agent: Agent): Promise<string> {\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\n\tconst m = getModelById(id)\n\tif (!m) return chalk.yellow(`\"${id}\" not found. Use /models`)\n\n\tconst pk = m.provider\n\tif (!auth.apiKeys[pk]) {\n\t\treturn chalk.yellow(`No API key configured for ${pk}. Use /providers`)\n\t}\n\n\tconst selectedProvider = getProvider(pk)\n\tif (!selectedProvider) return chalk.red(\"Error: Provider not found\")\n\n\tconfig.provider = pk\n\tconfig.model = id\n\tawait saveConfig(config)\n\n\tagent.updateConfig({\n\t\tprovider: selectedProvider.id,\n\t\tmodel: m,\n\t\tapiKey: auth.apiKeys[pk],\n\t})\n\n\treturn chalk.green(`✓ Switched to ${id}`)\n}\n\nconst fmt = (n: number) => (n >= 1_000_000 ? `${n / 1_000_000}M` : `${n / 1000}K`)\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport { loadAuth, loadConfig, saveAuth, saveConfig } from \"../config/store.ts\"\nimport { PROVIDERS } from \"../models/catalog.ts\"\nimport { getDefaultModel, getModel, getModelsForProvider, getProvider } from \"../models/lookup.ts\"\nimport type { Prompts } from \"../types.ts\"\n\nexport async function handleProviders(agent: Agent, prompts?: Prompts): Promise<string> {\n\tif (!prompts) return chalk.red(\"Prompts not available in this context\")\n\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\tconst configured = PROVIDERS.filter((p) => !!auth.apiKeys[p.id]).sort((a, b) =>\n\t\ta.name.localeCompare(b.name),\n\t)\n\n\tconst headerLines =\n\t\tconfigured.length === 0\n\t\t\t? chalk.dim(\"No providers configured. Use 'Add Provider' below.\")\n\t\t\t: configured\n\t\t\t\t\t.map((p) => {\n\t\t\t\t\t\tconst isDefault = p.id === config.provider\n\t\t\t\t\t\tconst active = isDefault ? chalk.green(\" ●\") : \"\"\n\t\t\t\t\t\tconst currentModel = isDefault ? config.model : (getDefaultModel(p.id)?.id ?? \"\")\n\t\t\t\t\t\treturn ` ✅ ${p.name.padEnd(24)} ${currentModel}${active}`\n\t\t\t\t\t})\n\t\t\t\t\t.join(\"\\n\")\n\n\tconst act = await prompts.select({\n\t\tmessage: \"Action\",\n\t\theader: headerLines,\n\t\toptions: [\n\t\t\t{ value: \"add\", label: \"Add Provider\" },\n\t\t\t{ value: \"update\", label: \"Update API Key\" },\n\t\t\t{ value: \"remove\", label: \"Remove API Key\" },\n\t\t\t{ value: \"default\", label: \"Set Default Provider\" },\n\t\t\t{ value: \"back\", label: \"Back\" },\n\t\t],\n\t})\n\tif (!act || act === \"back\") return \"\"\n\n\tif (act === \"add\") return addProvider(agent, prompts)\n\tif (act === \"update\") return updateKey(agent, prompts)\n\tif (act === \"remove\") return removeKey(agent, prompts)\n\tif (act === \"default\") return setDefault(agent, prompts)\n\treturn \"\"\n}\n\nasync function addProvider(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst auth = await loadAuth()\n\tconst config = await loadConfig()\n\n\tconst available = PROVIDERS.filter((p) => !auth.apiKeys[p.id]).sort((a, b) =>\n\t\ta.name.localeCompare(b.name),\n\t)\n\tif (available.length === 0) {\n\t\treturn chalk.yellow(\"All providers already have API keys configured.\")\n\t}\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Add Provider\",\n\t\toptions: available.map((p) => ({ value: p.id, label: p.name })),\n\t})\n\tif (!pick) return \"\"\n\n\tconst pDef = getProvider(pick)\n\tif (!pDef) return chalk.red(\"Error: Provider not found\")\n\n\tconst key = await prompts.password({\n\t\tmessage: `${pDef.name} API Key`,\n\t\tvalidate: (v) => (!v || v.length < 8 ? \"Enter a valid key\" : undefined),\n\t})\n\tif (!key) return \"\"\n\n\tauth.apiKeys[pDef.id] = key\n\tawait saveAuth(auth)\n\n\tconst providerModels = getModelsForProvider(pDef.id)\n\tconst defaultModel = getDefaultModel(pDef.id)\n\tlet selectedModelId = defaultModel?.id ?? \"\"\n\n\tif (defaultModel) {\n\t\tconst choice = await prompts.select({\n\t\t\tmessage: \"Model Selection\",\n\t\t\toptions: [\n\t\t\t\t{ value: \"latest\", label: `Use default (${defaultModel.id})` },\n\t\t\t\t{ value: \"choose\", label: \"Choose a model ID...\" },\n\t\t\t],\n\t\t})\n\n\t\tif (choice === \"choose\") {\n\t\t\tconst pickedModel = await prompts.select({\n\t\t\t\tmessage: \"Select Model ID\",\n\t\t\t\toptions: providerModels.map((m) => ({ value: m.id, label: m.id })),\n\t\t\t})\n\t\t\tif (pickedModel) {\n\t\t\t\tselectedModelId = pickedModel\n\t\t\t}\n\t\t}\n\t}\n\n\tlet makeDefault = !config.provider\n\tif (config.provider && config.provider !== pDef.id) {\n\t\tconst confirm = await prompts.confirm({\n\t\t\tmessage: `Set ${pDef.name} as the default provider?`,\n\t\t})\n\t\tif (confirm) {\n\t\t\tmakeDefault = true\n\t\t}\n\t}\n\n\tif (makeDefault) {\n\t\tconfig.provider = pDef.id\n\t\tconfig.model = selectedModelId\n\t\tawait saveConfig(config)\n\t\tconst modelDef = getModel(pDef.id, selectedModelId)\n\t\tif (modelDef) {\n\t\t\tagent.updateConfig({\n\t\t\t\tprovider: pDef.id,\n\t\t\t\tmodel: modelDef,\n\t\t\t\tapiKey: key,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn chalk.green(`✓ ${pDef.name} configured`)\n}\n\nasync function updateKey(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst auth = await loadAuth()\n\n\tconst configured = PROVIDERS.filter((p) => !!auth.apiKeys[p.id]).sort((a, b) =>\n\t\ta.name.localeCompare(b.name),\n\t)\n\tif (configured.length === 0) {\n\t\treturn chalk.yellow(\"No providers configured. Use 'Add Provider' first.\")\n\t}\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Update API Key\",\n\t\toptions: configured.map((p) => ({ value: p.id, label: p.name })),\n\t})\n\tif (!pick) return \"\"\n\n\tconst pDef = getProvider(pick)\n\tif (!pDef) return chalk.red(\"Error: Provider not found\")\n\n\tconst key = await prompts.password({ message: `New key for ${pDef.name}` })\n\tif (!key) return \"\"\n\n\tauth.apiKeys[pDef.id] = key\n\tawait saveAuth(auth)\n\n\tconst config = await loadConfig()\n\tif (config.provider === pDef.id) {\n\t\tconst currentModel = getModel(config.provider, config.model)\n\t\tif (currentModel) {\n\t\t\tagent.updateConfig({\n\t\t\t\tprovider: pDef.id,\n\t\t\t\tmodel: currentModel,\n\t\t\t\tapiKey: key,\n\t\t\t})\n\t\t}\n\t}\n\n\treturn chalk.green(\"✓ Key updated\")\n}\n\nasync function removeKey(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst auth = await loadAuth()\n\tconst config = await loadConfig()\n\n\tconst configured = PROVIDERS.filter((p) => !!auth.apiKeys[p.id]).sort((a, b) =>\n\t\ta.name.localeCompare(b.name),\n\t)\n\tif (configured.length === 0) {\n\t\treturn chalk.yellow(\"No configured providers to remove.\")\n\t}\n\n\tconst pick = await prompts.select({\n\t\tmessage: \"Remove API Key\",\n\t\toptions: configured.map((p) => ({ value: p.id, label: p.name })),\n\t})\n\tif (!pick) return \"\"\n\n\tconst confirm = await prompts.confirm({\n\t\tmessage: `Are you sure you want to remove the API key for ${pick}?`,\n\t})\n\tif (!confirm) return \"\"\n\n\tdelete auth.apiKeys[pick]\n\tawait saveAuth(auth)\n\n\tif (config.provider === pick) {\n\t\tconfig.provider = \"\"\n\t\tconfig.model = \"\"\n\t\tconst next = Object.keys(auth.apiKeys)[0]\n\t\tif (next) {\n\t\t\tconst pDef = getProvider(next)\n\t\t\tconst mDef = getDefaultModel(next)\n\t\t\tif (pDef && mDef) {\n\t\t\t\tconfig.provider = next\n\t\t\t\tconfig.model = mDef.id\n\t\t\t\tagent.updateConfig({\n\t\t\t\t\tprovider: pDef.id,\n\t\t\t\t\tmodel: mDef,\n\t\t\t\t\tapiKey: auth.apiKeys[next]!,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tawait saveConfig(config)\n\t}\n\n\treturn chalk.green(`✓ Removed API key for ${pick}`)\n}\n\nasync function setDefault(agent: Agent, prompts: Prompts): Promise<string> {\n\tconst config = await loadConfig()\n\tconst auth = await loadAuth()\n\n\tconst sortedProviders = [...PROVIDERS].sort((a, b) => a.name.localeCompare(b.name))\n\tconst pick = await prompts.select({\n\t\tmessage: \"Default Provider\",\n\t\toptions: sortedProviders.map((p) => ({\n\t\t\tvalue: p.id,\n\t\t\tlabel: `${auth.apiKeys[p.id] ? \"✅\" : \"❌\"} ${p.name}`,\n\t\t})),\n\t})\n\tif (!pick) return \"\"\n\n\tif (!auth.apiKeys[pick]) {\n\t\treturn chalk.yellow(`No API key for ${pick}. Please set one first.`)\n\t}\n\n\tconst pDef = getProvider(pick)\n\tconst mDef = getDefaultModel(pick)\n\n\tif (!pDef || !mDef) return chalk.red(\"Error: Provider or model not found\")\n\n\tconfig.provider = pick\n\tconfig.model = mDef.id\n\tawait saveConfig(config)\n\n\tagent.updateConfig({\n\t\tprovider: pDef.id,\n\t\tmodel: mDef,\n\t\tapiKey: auth.apiKeys[pick],\n\t})\n\n\treturn chalk.green(`✓ Default set to ${pDef.name} (${mDef.id})`)\n}\n","import chalk from \"chalk\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport type { SessionStore } from \"../db/sessionStore.ts\"\nimport { formatRelativeTime } from \"../format.ts\"\nimport { shortenPath } from \"../paths.ts\"\nimport { groupSkills } from \"../skills/index.ts\"\nimport type { Cmd, Prompts, Skill } from \"../types.ts\"\nimport { checkForUpdate, runUpdate } from \"../update.ts\"\nimport { handleCompact } from \"./compact.ts\"\nimport { handleModels } from \"./models.ts\"\nimport { handleProviders } from \"./providers.ts\"\nimport { handleInteractiveReset } from \"./reset.ts\"\n\nexport const COMMANDS: Cmd[] = [\n\t{ name: \"models\", desc: \"Switch model\", aliases: [\"model\"] },\n\t{ name: \"providers\", desc: \"Manage providers\", aliases: [\"prov\", \"config\", \"cfg\"] },\n\t{ name: \"compact\", desc: \"Compact context\" },\n\t{ name: \"sessions\", desc: \"List and switch sessions\" },\n\t{ name: \"resume\", desc: \"Resume previous session\" },\n\t{ name: \"update\", desc: \"Update novacode\" },\n\t{ name: \"skills\", desc: \"List available skills\" },\n\t{\n\t\tname: \"permission\",\n\t\tdesc: \"Switch permission mode (restricted/unrestricted)\",\n\t\taliases: [\"perm\", \"mode\", \"permiso\", \"permesso\"],\n\t},\n\t{ name: \"reset\", desc: \"Reset all nova data\" },\n\t{ name: \"help\", desc: \"Show help\" },\n\t{ name: \"clear\", desc: \"Clear screen & start new session\", aliases: [\"new\"] },\n\t{ name: \"quit\", desc: \"Exit (Ctrl+D)\", aliases: [\"exit\"] },\n]\n\nconst HELP = `\n${chalk.bold(\"Commands:\")}\n${COMMANDS.map((c) => ` /${c.name.padEnd(12)} ${c.desc}`).join(\"\\n\")}\n\n${chalk.bold(\"CLI:\")}\n nova update Update to latest version\n nova reset Delete all nova data and exit\n nova -s ls List sessions\n nova -s <id> / --sessions Resume sessions by ID\n nova -r / --resume Resume last sessions\n nova -s rm <id> Delete specific sessions\n nova -s rm --all Delete all sessions\n\n${chalk.dim(\"Keys:\")}\n Esc Abort\n ↑ / ↓ History\n`\n\nexport async function dispatch(\n\tinput: string,\n\tagent: Agent,\n\tstore?: SessionStore,\n\tsessionId?: string,\n\tprompts?: Prompts,\n\tonExit?: () => void,\n\tonSwitchSession?: (sessionId: string) => Promise<void>,\n\tonNewSession?: () => Promise<void>,\n\tskills: Skill[] = [],\n): Promise<string | null> {\n\tconst [cmd, ...rest] = input.slice(1).split(\" \")\n\tconst args = rest.join(\" \")\n\n\tswitch (cmd) {\n\t\tcase \"models\":\n\t\tcase \"model\":\n\t\t\treturn handleModels(args, agent, prompts)\n\t\tcase \"providers\":\n\t\tcase \"prov\":\n\t\tcase \"config\":\n\t\tcase \"cfg\":\n\t\t\treturn handleProviders(agent, prompts)\n\t\tcase \"compact\":\n\t\t\tif (!store || !sessionId) return chalk.red(\"Session store not available\")\n\t\t\t{\n\t\t\t\tconst { result, newSessionId } = await handleCompact(agent, store, sessionId)\n\t\t\t\tif (newSessionId && onSwitchSession) await onSwitchSession(newSessionId)\n\t\t\t\treturn result\n\t\t\t}\n\t\tcase \"skills\":\n\t\t\treturn handleSkills(skills)\n\t\tcase \"sessions\": {\n\t\t\tif (!store || !prompts || !onSwitchSession)\n\t\t\t\treturn chalk.red(\"Session switching not available\")\n\t\t\tconst sessions = await store.list(50)\n\t\t\tif (sessions.length === 0) return chalk.yellow(\"No sessions found.\")\n\t\t\tconst options = sessions.map((s, idx) => {\n\t\t\t\tconst relTime = formatRelativeTime(s.updated)\n\t\t\t\tlet label = s.title ? `\"${s.title}\"` : `Session: ${s.id}`\n\t\t\t\tif (s.id === sessionId) {\n\t\t\t\t\tlabel = s.title ? `Current: \"${s.title}\"` : \"Current Session\"\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tvalue: s.id,\n\t\t\t\t\tlabel: `${idx + 1}. ${label}`,\n\t\t\t\t\thint: relTime,\n\t\t\t\t}\n\t\t\t})\n\t\t\tconst footer = [\n\t\t\t\tchalk.bold(\"\\nCLI Sessions Shortcuts:\"),\n\t\t\t\t` ${chalk.cyan(\"nova -r\")} / ${chalk.cyan(\"--resume\")} Resume last sessions`,\n\t\t\t\t` ${chalk.cyan(\"nova -s <id>\")} / ${chalk.cyan(\"--sessions <id>\")} Resume specific sessions by ID`,\n\t\t\t\t` ${chalk.cyan(\"nova -s ls [limit]\")} List last sessions (default: 10)`,\n\t\t\t\t` ${chalk.cyan(\"nova -s rm <id>\")} Delete specific sessions`,\n\t\t\t\t` ${chalk.cyan(\"nova -s rm --all\")} Delete all sessions`,\n\t\t\t].join(\"\\n\")\n\n\t\t\tconst selectedId = await prompts.select({\n\t\t\t\tmessage: \"Select a session to load:\",\n\t\t\t\toptions,\n\t\t\t\tfooter,\n\t\t\t})\n\t\t\tif (selectedId) {\n\t\t\t\tawait onSwitchSession(selectedId)\n\t\t\t\tconst selectedSession = sessions.find((s) => s.id === selectedId)\n\t\t\t\tconst displayName = selectedSession?.title\n\t\t\t\t\t? `${selectedSession.title} (id: ${selectedId})`\n\t\t\t\t\t: selectedId\n\t\t\t\treturn chalk.green(`✓ Switched to session: ${displayName}`)\n\t\t\t}\n\t\t\treturn chalk.yellow(\"Session selection cancelled.\")\n\t\t}\n\t\tcase \"resume\":\n\t\t\treturn \"Use `nova --resume` from the CLI to resume your last session.\"\n\t\tcase \"update\":\n\t\t\treturn handleUpdate()\n\t\tcase \"reset\":\n\t\t\treturn handleInteractiveReset(prompts, onExit)\n\t\tcase \"help\":\n\t\t\treturn HELP\n\t\tcase \"clear\":\n\t\tcase \"new\":\n\t\t\tconsole.clear()\n\t\t\tif (onNewSession) await onNewSession()\n\t\t\treturn \"\"\n\t\tcase \"quit\":\n\t\t\tif (onExit) {\n\t\t\t\tonExit()\n\t\t\t} else {\n\t\t\t\tprocess.exit(0)\n\t\t\t}\n\t\t\treturn null\n\t\tcase \"exit\":\n\t\t\tif (onExit) {\n\t\t\t\tonExit()\n\t\t\t} else {\n\t\t\t\tprocess.exit(0)\n\t\t\t}\n\t\t\treturn null\n\t\tdefault:\n\t\t\treturn chalk.yellow(`Unknown: /${cmd}. Type /help`)\n\t}\n}\n\nasync function handleUpdate(): Promise<string> {\n\tconst info = await checkForUpdate()\n\tif (!info) return chalk.yellow(\"Could not check for updates.\")\n\tif (!info.hasUpdate) return chalk.green(`✓ Already up to date (v${info.current})`)\n\n\tconsole.log(chalk.yellow(`\\n⚡ Updating novacode to v${info.latest}...`))\n\tconst success = await runUpdate(true)\n\tif (success) {\n\t\treturn chalk.green(\n\t\t\t`✓ Successfully updated to v${info.latest}! Please restart nova to apply changes.`,\n\t\t)\n\t}\n\treturn chalk.red(\"✗ Update failed. Please try running 'nova update' manually in your terminal.\")\n}\n\nfunction handleSkills(skills: Skill[]): string {\n\tif (skills.length === 0) {\n\t\treturn `${chalk.yellow(\"No skills found.\")}\\n\\nSkill directories scanned (precedence order):\\n .novacode/skills/\\n .agents/skills/\\n ~/.novacode/skills/\\n ~/.agents/skills/`\n\t}\n\n\tconst groups = groupSkills(skills)\n\tlet out = `${chalk.bold(\"Available Skills:\")}\\n`\n\tlet n = 1\n\tfor (const group of groups) {\n\t\tconst first = group[0]!\n\t\tconst name =\n\t\t\tgroup.length > 1 ? `${chalk.yellow(`${first.name} (duplicate)`)}` : chalk.green(first.name)\n\t\tout += `${n++}. ${name} — ${first.description}\\n`\n\t\tfor (const s of group) {\n\t\t\tout += ` ${chalk.dim(shortenPath(s.path))}\\n`\n\t\t}\n\t}\n\tout += chalk.dim(\"\\nSkills are auto-loaded by the agent when relevant to your task.\")\n\treturn out\n}\n","import type { ModelMessage } from \"ai\"\nimport { useInput } from \"ink\"\nimport { useEffect, useMemo, useRef, useState } from \"react\"\nimport type { Agent } from \"../../agent/agent.ts\"\nimport { COMMANDS, dispatch } from \"../../commands/index.ts\"\nimport type { SessionStore } from \"../../db/sessionStore.ts\"\nimport type { Prompts, Skill } from \"../../types.ts\"\nimport type { PromptMode } from \"../types.ts\"\n\n/**\n * Hook that registers the Ink console input listener and manages the input composer lifecycle.\n *\n * It acts as the keyboard router for the CLI:\n * - Detects control keys to abort executing tasks or exit the application.\n * - Routes navigation keys (arrows, page up/down, home/end) to scroll controls or history lists.\n * - Handles auto-completion triggers for slash commands.\n * - Accumulates characters in the input composer.\n * - Parses and dispatches slash commands or triggers a new agent execution turn.\n */\nexport function useInputHandler({\n\tagent,\n\tstore,\n\tsession,\n\tturn,\n\tprompts,\n\tmode,\n\texit,\n\thandlePermissionSwitch,\n\tskills,\n}: {\n\tagent: Agent\n\tstore: SessionStore\n\tsession: {\n\t\tsessionId: string\n\t\tcommitMsg: (msg: ModelMessage) => void\n\t\tswitchSession: (id: string) => Promise<void>\n\t\tnewSession: () => Promise<void>\n\t\taddNotice: (text: string) => void\n\t\tclearNotices: () => void\n\t}\n\tturn: {\n\t\tbusy: boolean\n\t\tsetBusy: (b: boolean) => void\n\t\trun: (ctrl: AbortController) => Promise<void>\n\t\tabort: () => void\n\t}\n\tprompts: Prompts\n\tmode: PromptMode\n\texit: () => void\n\thandlePermissionSwitch: () => Promise<void>\n\tskills: Skill[]\n}) {\n\tconst [input, setInput] = useState(\"\")\n\tconst [selCmdIdx, setSelCmdIdx] = useState(0)\n\tconst [exitConfirmKey, setExitConfirmKey] = useState<\"C\" | null>(null)\n\n\tconst lastExitPress = useRef<{ key: \"C\"; ts: number } | null>(null)\n\tconst history = useRef<string[]>([])\n\tconst hIdx = useRef(-1)\n\n\t// Reset command suggestion selection when input query changes.\n\t// biome-ignore lint/correctness/useExhaustiveDependencies: reset selection on input change\n\tuseEffect(() => {\n\t\tsetSelCmdIdx(0)\n\t}, [input])\n\n\tconst isTypingCmd = input.startsWith(\"/\") && !input.includes(\" \")\n\n\t// Filter suggestions based on typed command prefix.\n\tconst suggestions = useMemo(() => {\n\t\tif (!isTypingCmd) return []\n\t\tconst query = input.slice(1).toLowerCase()\n\t\treturn COMMANDS.filter(\n\t\t\t(c) => c.name.startsWith(query) || c.aliases?.some((a) => a.startsWith(query)),\n\t\t)\n\t}, [input, isTypingCmd])\n\n\tuseInput((ch, key) => {\n\t\t// --- 1. System Keys (Exit and Abort Control) ---\n\t\tif (key.ctrl && (ch === \"c\" || ch === \"d\")) {\n\t\t\tif (turn.busy) {\n\t\t\t\tif (ch === \"c\") turn.abort()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (ch === \"d\") {\n\t\t\t\texit()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// double-press Ctrl+C safety threshold\n\t\t\tconst now = Date.now()\n\t\t\tif (\n\t\t\t\tlastExitPress.current &&\n\t\t\t\tlastExitPress.current.key === \"C\" &&\n\t\t\t\tnow - lastExitPress.current.ts < 2000\n\t\t\t) {\n\t\t\t\texit()\n\t\t\t} else {\n\t\t\t\tlastExitPress.current = { key: \"C\", ts: now }\n\t\t\t\tsetExitConfirmKey(\"C\")\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (lastExitPress.current?.key === \"C\" && Date.now() - lastExitPress.current.ts >= 2000) {\n\t\t\t\t\t\tlastExitPress.current = null\n\t\t\t\t\t\tsetExitConfirmKey(null)\n\t\t\t\t\t}\n\t\t\t\t}, 2000)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// When a prompt modal is active (e.g. permission approval or option selection),\n\t\t// it captures inputs directly and ignores standard chat composer keys.\n\t\tif (mode.type !== \"chat\") return\n\n\t\tif (key.escape) {\n\t\t\tif (turn.busy) {\n\t\t\t\tturn.abort()\n\t\t\t} else if (input) {\n\t\t\t\tsetInput(\"\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// --- 2. Autocomplete / Suggestions / Command History recall ---\n\t\tif (key.upArrow) {\n\t\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\t\tsetSelCmdIdx((prev) => (prev > 0 ? prev - 1 : suggestions.length - 1))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (history.current.length > 0) {\n\t\t\t\thIdx.current = Math.min(hIdx.current + 1, history.current.length - 1)\n\t\t\t\tsetInput(history.current[hIdx.current] ?? \"\")\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (key.downArrow) {\n\t\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\t\tsetSelCmdIdx((prev) => (prev < suggestions.length - 1 ? prev + 1 : 0))\n\t\t\t\treturn\n\t\t\t}\n\t\t\thIdx.current = Math.max(hIdx.current - 1, -1)\n\t\t\tsetInput(hIdx.current >= 0 ? (history.current[hIdx.current] ?? \"\") : \"\")\n\t\t\treturn\n\t\t}\n\t\tif (key.tab) {\n\t\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\t\tconst match = suggestions[selCmdIdx]\n\t\t\t\tif (match) setInput(`/${match.name} `)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// --- 3. Text Modification & Accumulation ---\n\t\tif (!key.return) {\n\t\t\tsetInput((prev) => {\n\t\t\t\tif (key.backspace || key.delete) return prev.slice(0, -1)\n\t\t\t\treturn prev + (ch || \"\")\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Do not process text submissions if the agent turn loop is actively running.\n\t\tif (turn.busy) return\n\n\t\tlet line = input.trim()\n\t\tif (!line) return\n\n\t\t// Complete typed suggestions on enter.\n\t\tif (isTypingCmd && suggestions.length > 0) {\n\t\t\tconst match = suggestions[selCmdIdx]\n\t\t\tif (match) line = `/${match.name}`\n\t\t}\n\n\t\tsetInput(\"\")\n\t\thistory.current.unshift(line)\n\t\thIdx.current = -1\n\n\t\t// --- 4. Action Dispatcher (Slash Commands vs. Prompt Submission) ---\n\t\tif (line.startsWith(\"/\")) {\n\t\t\tconst cmdParts = line.slice(1).split(\" \")\n\t\t\tconst cmdName = cmdParts[0]?.toLowerCase()\n\t\t\tconst matchedCmd = COMMANDS.find(\n\t\t\t\t(c) => c.name === cmdName || c.aliases?.includes(cmdName ?? \"\"),\n\t\t\t)\n\n\t\t\tif (matchedCmd?.name === \"permission\") {\n\t\t\t\tvoid handlePermissionSwitch()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst runDispatch = async () => {\n\t\t\t\tif (line === \"/compact\") {\n\t\t\t\t\tturn.setBusy(true)\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst r = await dispatch(\n\t\t\t\t\t\tline,\n\t\t\t\t\t\tagent,\n\t\t\t\t\t\tstore,\n\t\t\t\t\t\tsession.sessionId,\n\t\t\t\t\t\tprompts,\n\t\t\t\t\t\texit,\n\t\t\t\t\t\tsession.switchSession,\n\t\t\t\t\t\tsession.newSession,\n\t\t\t\t\t\tskills,\n\t\t\t\t\t)\n\t\t\t\t\tif (r) {\n\t\t\t\t\t\tsession.addNotice(r)\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconsole.error(`Command dispatch error for \"${line}\":`, err)\n\t\t\t\t} finally {\n\t\t\t\t\tturn.setBusy(false)\n\t\t\t\t}\n\t\t\t}\n\t\t\tvoid runDispatch()\n\t\t\treturn\n\t\t}\n\n\t\t// Standard prompt query submission to LLM.\n\t\tconst userMsg: ModelMessage = { role: \"user\", content: line }\n\t\tsession.clearNotices()\n\t\tsession.commitMsg(userMsg)\n\n\t\tconst ctrl = new AbortController()\n\t\tvoid turn.run(ctrl)\n\t})\n\n\treturn {\n\t\tinput,\n\t\tsetInput,\n\t\tsuggestions,\n\t\tselCmdIdx,\n\t\texitConfirmKey,\n\t}\n}\n","import { useEffect, useMemo, useRef, useState } from \"react\"\nimport type { PolicyEngine } from \"../../policy/engine.ts\"\nimport type { ApprovalRequest, PolicyApprover, Prompts } from \"../../types.ts\"\nimport type { PromptMode } from \"../types.ts\"\n\n/**\n * Hook that manages interactive prompt screens and hooks them up to the agent policy engine.\n *\n * It acts as a bridge between the asynchronous command/policy validation loop (which expects\n * Promise-based inputs) and the React rendering tree. Calling any prompt method (like select or\n * confirm) returns a Promise, updates the React state to render the prompt overlay, and stores\n * the resolver callback in a ref to be resolved once user input is captured.\n */\nexport function usePrompts(policy: PolicyEngine) {\n\tconst [mode, setMode] = useState<PromptMode>({ type: \"chat\" })\n\t// Stores the active prompt's Promise resolver.\n\t// Since commands run sequentially, we only ever have a single active prompt at a time.\n\tconst resolveRef = useRef<((v: unknown) => void) | null>(null)\n\n\tconst prompts = useMemo<Prompts>(\n\t\t() => ({\n\t\t\tselect: (config) =>\n\t\t\t\tnew Promise<string | null>((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"select\", ...config })\n\t\t\t\t}),\n\t\t\tsearchSelect: (config) =>\n\t\t\t\tnew Promise<string | null>((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"searchSelect\", ...config })\n\t\t\t\t}),\n\t\t\tpassword: (config) =>\n\t\t\t\tnew Promise<string | null>((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"password\", ...config })\n\t\t\t\t}),\n\t\t\tconfirm: (config) =>\n\t\t\t\tnew Promise<boolean | null>((resolve) => {\n\t\t\t\t\tresolveRef.current = resolve as (v: unknown) => void\n\t\t\t\t\tsetMode({ type: \"confirm\", ...config })\n\t\t\t\t}),\n\t\t}),\n\t\t[],\n\t)\n\n\tconst approver = useMemo<PolicyApprover>(\n\t\t() => ({\n\t\t\trequest: (req: ApprovalRequest) =>\n\t\t\t\tnew Promise<boolean>((resolve) => {\n\t\t\t\t\tresolveRef.current = (v: unknown) => resolve(v === true)\n\t\t\t\t\tsetMode({ type: \"approval\", req })\n\t\t\t\t}),\n\t\t}),\n\t\t[],\n\t)\n\n\t// Automatically registers this TUI component's modal handler with the policy engine.\n\t// This intercepts unsafe tool calls and shows approval prompts inline within the TUI.\n\tuseEffect(() => {\n\t\tpolicy.setApprover(approver)\n\t\treturn () => policy.setApprover(null)\n\t}, [policy, approver])\n\n\tconst resolvePrompt = (value: unknown) => {\n\t\tconst fn = resolveRef.current\n\t\tresolveRef.current = null\n\t\tsetMode({ type: \"chat\" })\n\t\tfn?.(value)\n\t}\n\n\treturn {\n\t\tmode,\n\t\tsetMode,\n\t\tprompts,\n\t\tresolvePrompt,\n\t}\n}\n","import type { ModelMessage } from \"ai\"\nimport { useCallback, useEffect, useState } from \"react\"\nimport type { Agent } from \"../../agent/agent.ts\"\nimport { loadAuth } from \"../../config/store.ts\"\nimport type { SessionStore } from \"../../db/sessionStore.ts\"\nimport { getModel, getProvider } from \"../../models/lookup.ts\"\n\n/**\n * Hook that manages the state of the active workspace session and history.\n *\n * It bridges state between the local React tree (so updates render instantly), the stateful\n * Agent instance (which handles model interactions), and the persistent SQLite database store\n * (restoring history on resume and saving new message lines).\n */\nexport function useSession(\n\tagent: Agent,\n\tstore: SessionStore,\n\tinitialSessionId: string,\n\tinitialHistory: ModelMessage[],\n) {\n\tconst [sessionId, setSessionId] = useState(initialSessionId)\n\tconst [messages, setMessages] = useState<ModelMessage[]>(initialHistory)\n\tconst [contextTokens, setContextTokens] = useState(0)\n\t// Ephemeral UI-only affordances (slash-command output, settings confirmations).\n\t// Never persisted to the store nor fed to the model — not part of the session record.\n\tconst [notices, setNotices] = useState<string[]>([])\n\n\tuseEffect(() => {\n\t\tasync function fetchSession() {\n\t\t\ttry {\n\t\t\t\tconst s = await store.get(initialSessionId)\n\t\t\t\tif (s) setContextTokens(s.contextTokens)\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"Failed to load initial session context size:\", err)\n\t\t\t}\n\t\t}\n\t\tvoid fetchSession()\n\t}, [store, initialSessionId])\n\n\t// Commits a single message (e.g. user input query or assistant turn output).\n\tconst commitMsg = useCallback(\n\t\t(msg: ModelMessage) => {\n\t\t\tsetMessages((prev) => [...prev, msg])\n\t\t\tagent.appendMessages([msg])\n\t\t\tstore.append(sessionId, msg).catch((err) => {\n\t\t\t\tconsole.error(\"Error appending message to session store:\", err)\n\t\t\t})\n\t\t},\n\t\t[agent, store, sessionId],\n\t)\n\n\t// Shows a transient UI notice (e.g. slash-command output, settings confirmation).\n\t// Updates only React state for rendering — never reaches agent.messages or the store.\n\tconst addNotice = useCallback((text: string) => {\n\t\tsetNotices((prev) => [...prev, text])\n\t}, [])\n\n\tconst clearNotices = useCallback(() => setNotices([]), [])\n\n\t// Commits a block of delta messages (e.g. generated during assistant stream/thinking/tools).\n\tconst commitDelta = useCallback(\n\t\tasync (delta: ModelMessage[]) => {\n\t\t\tfor (const msg of delta) {\n\t\t\t\tawait store.append(sessionId, msg)\n\t\t\t}\n\n\t\t\tsetMessages((prev) => [...prev, ...delta])\n\t\t\tagent.appendMessages(delta)\n\n\t\t\t// Scan the committed parts to update active tools state (pruning completed tools).\n\t\t\tconst committedToolCallIds = new Set<string>()\n\t\t\tfor (const msg of delta) {\n\t\t\t\tif (msg.role === \"assistant\" && Array.isArray(msg.content)) {\n\t\t\t\t\tfor (const part of msg.content) {\n\t\t\t\t\t\tif (part.type === \"tool-call\") committedToolCallIds.add(part.toolCallId)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (msg.role === \"tool\" && Array.isArray(msg.content)) {\n\t\t\t\t\tfor (const part of msg.content) {\n\t\t\t\t\t\tif (part.type === \"tool-result\") committedToolCallIds.add(part.toolCallId)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst committedText = delta.some(\n\t\t\t\t(msg) =>\n\t\t\t\t\tmsg.role === \"assistant\" &&\n\t\t\t\t\t(typeof msg.content === \"string\"\n\t\t\t\t\t\t? msg.content.trim().length > 0\n\t\t\t\t\t\t: msg.content.some((part) => part.type === \"text\" && part.text.trim().length > 0)),\n\t\t\t)\n\n\t\t\treturn { committedToolCallIds, committedText }\n\t\t},\n\t\t[agent, store, sessionId],\n\t)\n\n\t// Switches the active workspace session and loads its full historical lineage.\n\tconst switchSession = useCallback(\n\t\tasync (newSessionId: string) => {\n\t\t\tconst s = await store.get(newSessionId)\n\t\t\tif (!s) return\n\n\t\t\t// Adjust model config to match the stored session settings.\n\t\t\tconst provider = getProvider(s.provider)\n\t\t\tconst model = getModel(s.provider, s.model)\n\t\t\tif (provider && model) {\n\t\t\t\tconst auth = await loadAuth()\n\t\t\t\tconst apiKey = auth.apiKeys[s.provider] || \"\"\n\t\t\t\tagent.updateConfig({ provider: provider.id, model, apiKey })\n\t\t\t}\n\n\t\t\tconst activeMsgs = await store.messages(newSessionId)\n\t\t\tconst fullHistory = await store.history(newSessionId)\n\n\t\t\t// Clear screen so <Static> remounts cleanly with the new session's history.\n\t\t\tif (process.stdout.isTTY) {\n\t\t\t\tprocess.stdout.write(\"\\x1b[2J\\x1b[3J\\x1b[H\")\n\t\t\t}\n\n\t\t\tagent.setMessages(activeMsgs)\n\t\t\tsetMessages(fullHistory)\n\t\t\tsetSessionId(newSessionId)\n\t\t\tsetNotices([])\n\n\t\t\tif (model) setContextTokens(s.contextTokens)\n\t\t},\n\t\t[agent, store],\n\t)\n\n\t// Initializes a clean workspace session.\n\tconst newSession = useCallback(async () => {\n\t\tconst m = agent.model\n\t\tconst session = await store.create(process.cwd(), m.id, m.provider)\n\n\t\t// Clear screen so <Static> remounts cleanly with no prior history.\n\t\tif (process.stdout.isTTY) {\n\t\t\tprocess.stdout.write(\"\\x1b[2J\\x1b[3J\\x1b[H\")\n\t\t}\n\n\t\tagent.setMessages([])\n\t\tsetMessages([])\n\t\tsetSessionId(session.id)\n\t\tsetContextTokens(0)\n\t\tsetNotices([])\n\t}, [agent, store])\n\n\treturn {\n\t\tsessionId,\n\t\tmessages,\n\t\tcontextTokens,\n\t\tsetContextTokens,\n\t\tcommitMsg,\n\t\tcommitDelta,\n\t\tswitchSession,\n\t\tnewSession,\n\t\tnotices,\n\t\taddNotice,\n\t\tclearNotices,\n\t}\n}\n","import type { ModelMessage, ToolResultPart } from \"ai\"\nimport { summarizeToolOutput } from \"../content.ts\"\nimport { formatToolArgs } from \"../format.ts\"\nimport { groupSkills } from \"../skills/index.ts\"\nimport type { PermissionMode, Skill } from \"../types.ts\"\nimport type { TimelineEvent } from \"./types.ts\"\n\nexport function buildSessionInfo(\n\tversion: string,\n\tskills: Skill[],\n\thasAgentsMd: boolean,\n\tpermissionMode: PermissionMode,\n): string {\n\tconst lines: string[] = [` v${version}`]\n\tif (hasAgentsMd) lines.push(\" AGENTS.md detected\")\n\tlines.push(` permission: ${permissionMode}`)\n\tif (skills.length > 0) {\n\t\tconst groups = groupSkills(skills)\n\t\tconst names = groups.map((g) => {\n\t\t\tconst name = g[0]!.name\n\t\t\treturn g.length > 1 ? `${name} (duplicate)` : name\n\t\t})\n\t\tlines.push(` skills: ${names.join(\", \")}`)\n\t}\n\treturn lines.join(\"\\n\")\n}\n\nexport function deriveEventsFromMessages(msgs: ModelMessage[]): TimelineEvent[] {\n\tconst events: TimelineEvent[] = []\n\n\tfor (let i = 0; i < msgs.length; i++) {\n\t\tconst msg = msgs[i]!\n\n\t\tif (msg.role === \"user\") {\n\t\t\tconst content =\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? msg.content\n\t\t\t\t\t: msg.content.map((c) => (c.type === \"text\" ? c.text : \"\")).join(\"\")\n\t\t\tif (content.trim()) {\n\t\t\t\tevents.push({ id: `user-${i}`, type: \"UserMessage\", content })\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\tif (msg.role === \"assistant\") {\n\t\t\tconst parts =\n\t\t\t\ttypeof msg.content === \"string\"\n\t\t\t\t\t? [{ type: \"text\" as const, text: msg.content }]\n\t\t\t\t\t: msg.content\n\n\t\t\tfor (let j = 0; j < parts.length; j++) {\n\t\t\t\tconst part = parts[j]!\n\t\t\t\tif (part.type === \"text\") {\n\t\t\t\t\tif (part.text.trim()) {\n\t\t\t\t\t\tevents.push({\n\t\t\t\t\t\t\tid: `assistant-${i}-${j}`,\n\t\t\t\t\t\t\ttype: \"AssistantMessage\",\n\t\t\t\t\t\t\tcontent: part.text,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else if (part.type === \"tool-call\") {\n\t\t\t\t\tlet foundResult: ToolResultPart | null = null\n\t\t\t\t\tfor (let k = i + 1; k < msgs.length; k++) {\n\t\t\t\t\t\tconst nextMsg = msgs[k]!\n\t\t\t\t\t\tif (nextMsg.role === \"tool\" && Array.isArray(nextMsg.content)) {\n\t\t\t\t\t\t\tconst resPart = nextMsg.content.find(\n\t\t\t\t\t\t\t\t(p): p is ToolResultPart =>\n\t\t\t\t\t\t\t\t\tp.type === \"tool-result\" && p.toolCallId === part.toolCallId,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\tif (resPart) {\n\t\t\t\t\t\t\t\tfoundResult = resPart\n\t\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst args = formatToolArgs(part.input as Record<string, unknown>, false)\n\n\t\t\t\t\tif (foundResult) {\n\t\t\t\t\t\tconst { text, isError } = summarizeToolOutput(foundResult.output)\n\t\t\t\t\t\tif (isError) {\n\t\t\t\t\t\t\tevents.push({\n\t\t\t\t\t\t\t\tid: `tool-${part.toolCallId}`,\n\t\t\t\t\t\t\t\ttype: \"ToolFailed\",\n\t\t\t\t\t\t\t\ttoolCallId: part.toolCallId,\n\t\t\t\t\t\t\t\ttoolName: part.toolName,\n\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t\terror: text,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst completedEvent: TimelineEvent = {\n\t\t\t\t\t\t\t\tid: `tool-${part.toolCallId}`,\n\t\t\t\t\t\t\t\ttype: \"ToolCompleted\",\n\t\t\t\t\t\t\t\ttoolCallId: part.toolCallId,\n\t\t\t\t\t\t\t\ttoolName: part.toolName,\n\t\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (part.toolName === \"read\") {\n\t\t\t\t\t\t\t\tcompletedEvent.resultLineCount = text.split(\"\\n\").length\n\t\t\t\t\t\t\t} else if (part.toolName === \"grep\") {\n\t\t\t\t\t\t\t\tcompletedEvent.resultMatchCount = text.split(\"\\n\").filter(Boolean).length\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tevents.push(completedEvent)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tevents.push({\n\t\t\t\t\t\t\tid: `tool-${part.toolCallId}`,\n\t\t\t\t\t\t\ttype: \"ToolStarted\",\n\t\t\t\t\t\t\ttoolCallId: part.toolCallId,\n\t\t\t\t\t\t\ttoolName: part.toolName,\n\t\t\t\t\t\t\targs,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn events\n}\n","import type { ModelMessage } from \"ai\"\nimport { useEffect, useMemo, useState } from \"react\"\nimport type { PermissionMode, Skill } from \"../../types.ts\"\nimport { checkForUpdate } from \"../../update.ts\"\nimport { buildSessionInfo, deriveEventsFromMessages } from \"../helpers.ts\"\nimport type { TimelineEvent } from \"../types.ts\"\n\nconst TIPS = [\n\t\"Press / to open commands.\",\n\t\"Use @ to reference files.\",\n\t\"Press Esc to cancel input.\",\n\t\"Use arrow keys to navigate history.\",\n\t\"Use Tab for autocomplete.\",\n\t\"Use Shift+Tab to move backwards.\",\n\t\"Use Ctrl+C to stop execution.\",\n\t\"Scroll terminal scrollback to review history.\",\n\t\"Use /compact to shrink context when it gets long.\",\n\t\"Use /models to switch providers and models.\",\n\t\"Use /sessions to browse and resume past sessions.\",\n\t\"Use /skills to list auto-loaded agent skills.\",\n]\n\nconst ROTATE_MS = 8000\n\n/**\n * Hook that constructs the unified TUI event timeline and tracks token usage.\n *\n * It aggregates completed historical events (derived from message history) and active\n * streaming/thinking/tool execution events (derived from the current agent execution turn).\n * It also manages background checks for CLI updates and cycles helpful tips.\n */\nexport function useTuiTimeline({\n\tmessages,\n\tnotices,\n\tcontextTokens,\n\tversion,\n\tskills,\n\thasAgentsMd,\n\tpermissionMode,\n\tturn,\n}: {\n\tmessages: ModelMessage[]\n\tnotices: string[]\n\tcontextTokens: number\n\tversion: string\n\tskills: Skill[]\n\thasAgentsMd: boolean\n\tpermissionMode: PermissionMode\n\tturn: {\n\t\tthinking: boolean\n\t\tbufferedStream: string\n\t\tactiveTools: Array<{\n\t\t\tid: string\n\t\t\tname: string\n\t\t\targs: string\n\t\t\tstatus: \"running\" | \"success\" | \"failure\"\n\t\t\terror?: string\n\t\t\tlineCount?: number\n\t\t\tmatchCount?: number\n\t\t}>\n\t\tbusy: boolean\n\t}\n}) {\n\tconst [updateInfo, setUpdateInfo] = useState<{\n\t\thasUpdate: boolean\n\t\tcurrent: string\n\t\tlatest: string\n\t} | null>(null)\n\tconst [tipIdx, setTipIdx] = useState(0)\n\n\t// Fetch update information from registry on mount.\n\tuseEffect(() => {\n\t\tasync function checkUpdate() {\n\t\t\ttry {\n\t\t\t\tconst info = await checkForUpdate()\n\t\t\t\tif (info?.hasUpdate) {\n\t\t\t\t\tsetUpdateInfo({ hasUpdate: true, current: info.current, latest: info.latest })\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"Failed to check for updates:\", err)\n\t\t\t}\n\t\t}\n\t\tvoid checkUpdate()\n\t}, [])\n\n\t// Rotate CLI tips every 8 seconds to improve discoverability of keyboard shortcuts.\n\tuseEffect(() => {\n\t\tconst id = setInterval(() => {\n\t\t\tsetTipIdx((i) => (i + 1) % TIPS.length)\n\t\t}, ROTATE_MS)\n\t\treturn () => clearInterval(id)\n\t}, [])\n\n\tconst tip = TIPS[tipIdx]!\n\n\t// Committed history: rendered once via <Static> into terminal scrollback.\n\tconst committedEvents = useMemo<TimelineEvent[]>(\n\t\t() => deriveEventsFromMessages(messages),\n\t\t[messages],\n\t)\n\n\t// Live (non-persistent) events: splash, update banner, streaming, thinking, active tools.\n\tconst liveEvents = useMemo<TimelineEvent[]>(() => {\n\t\tconst events: TimelineEvent[] = []\n\n\t\tif (messages.length === 0) {\n\t\t\tevents.push({\n\t\t\t\tid: \"splash\",\n\t\t\t\ttype: \"Splash\",\n\t\t\t\tcontent: buildSessionInfo(version, skills, hasAgentsMd, permissionMode),\n\t\t\t\tupdate: updateInfo?.hasUpdate\n\t\t\t\t\t? { current: updateInfo.current, latest: updateInfo.latest }\n\t\t\t\t\t: undefined,\n\t\t\t})\n\t\t} else if (updateInfo?.hasUpdate) {\n\t\t\tevents.push({\n\t\t\t\tid: \"update-available\",\n\t\t\t\ttype: \"UpdateAvailable\",\n\t\t\t\tcurrent: updateInfo.current,\n\t\t\t\tlatest: updateInfo.latest,\n\t\t\t})\n\t\t}\n\n\t\tfor (let i = 0; i < notices.length; i++) {\n\t\t\tevents.push({ id: `notice-${i}`, type: \"Notice\", content: notices[i]! })\n\t\t}\n\n\t\tif (turn.thinking) {\n\t\t\tevents.push({ id: \"active-thinking\", type: \"Thinking\" })\n\t\t}\n\n\t\tif (turn.bufferedStream) {\n\t\t\tevents.push({\n\t\t\t\tid: \"active-text\",\n\t\t\t\ttype: \"AssistantMessage\",\n\t\t\t\tcontent: turn.bufferedStream,\n\t\t\t})\n\t\t}\n\n\t\tfor (const t of turn.activeTools) {\n\t\t\tif (t.status === \"running\") {\n\t\t\t\tevents.push({\n\t\t\t\t\tid: t.id,\n\t\t\t\t\ttype: \"ToolStarted\",\n\t\t\t\t\ttoolCallId: t.id,\n\t\t\t\t\ttoolName: t.name,\n\t\t\t\t\targs: t.args,\n\t\t\t\t})\n\t\t\t} else if (t.status === \"success\") {\n\t\t\t\tevents.push({\n\t\t\t\t\tid: t.id,\n\t\t\t\t\ttype: \"ToolCompleted\",\n\t\t\t\t\ttoolCallId: t.id,\n\t\t\t\t\ttoolName: t.name,\n\t\t\t\t\targs: t.args,\n\t\t\t\t\tresultLineCount: t.lineCount,\n\t\t\t\t\tresultMatchCount: t.matchCount,\n\t\t\t\t})\n\t\t\t} else if (t.status === \"failure\") {\n\t\t\t\tevents.push({\n\t\t\t\t\tid: t.id,\n\t\t\t\t\ttype: \"ToolFailed\",\n\t\t\t\t\ttoolCallId: t.id,\n\t\t\t\t\ttoolName: t.name,\n\t\t\t\t\targs: t.args,\n\t\t\t\t\terror: t.error ?? \"Unknown error\",\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tif (turn.busy && !turn.thinking && !turn.bufferedStream) {\n\t\t\tevents.push({ id: \"active-working\", type: \"Thinking\" })\n\t\t}\n\n\t\treturn events\n\t}, [\n\t\tmessages.length,\n\t\tnotices,\n\t\tversion,\n\t\tskills,\n\t\thasAgentsMd,\n\t\tpermissionMode,\n\t\tupdateInfo,\n\t\tturn.thinking,\n\t\tturn.bufferedStream,\n\t\tturn.activeTools,\n\t\tturn.busy,\n\t])\n\n\treturn {\n\t\tcommittedEvents,\n\t\tliveEvents,\n\t\tcontextTokens,\n\t\ttip,\n\t}\n}\n","import type { ModelMessage } from \"ai\"\nimport { Box, render, useApp } from \"ink\"\nimport { useMemo, useState } from \"react\"\nimport type { Agent } from \"../agent/agent.ts\"\nimport type { SessionStore } from \"../db/sessionStore.ts\"\nimport type { PolicyEngine } from \"../policy/engine.ts\"\nimport type { PermissionMode, Skill } from \"../types.ts\"\nimport { getCurrentVersion } from \"../update.ts\"\nimport { Composer } from \"./components/composer.tsx\"\nimport { Conversation } from \"./components/conversation.tsx\"\nimport { StatusBar } from \"./components/statusBar.tsx\"\nimport { useAgentTurn } from \"./hooks/useAgentTurn.ts\"\nimport { useInputHandler } from \"./hooks/useInputHandler.ts\"\nimport { usePrompts } from \"./hooks/usePrompts.ts\"\nimport { useSession } from \"./hooks/useSession.ts\"\nimport { useTuiTimeline } from \"./hooks/useTuiTimeline.ts\"\nimport { PromptOverlay } from \"./prompts.tsx\"\nimport { ThemeProvider, useTheme } from \"./theme/index.tsx\"\n\nexport async function interactive(\n\tagent: Agent,\n\tstore: SessionStore,\n\tsessionId: string,\n\tskills: Skill[] = [],\n\thasAgentsMd = false,\n\tpolicy: PolicyEngine,\n): Promise<void> {\n\tprocess.stdout.write(\"\\x1B[?25l\")\n\tconst version = await getCurrentVersion()\n\tconst initialHistory: ModelMessage[] = await store.history(sessionId)\n\n\tif (process.stdout.isTTY) {\n\t\tprocess.stdout.write(\"\\x1b[2J\\x1b[3J\\x1b[H\")\n\t}\n\n\ttry {\n\t\tconst { waitUntilExit } = render(\n\t\t\t<ThemeProvider>\n\t\t\t\t<App\n\t\t\t\t\tagent={agent}\n\t\t\t\t\tstore={store}\n\t\t\t\t\tsessionId={sessionId}\n\t\t\t\t\tskills={skills}\n\t\t\t\t\tinitialHistory={initialHistory}\n\t\t\t\t\tpolicy={policy}\n\t\t\t\t\tversion={version}\n\t\t\t\t\thasAgentsMd={hasAgentsMd}\n\t\t\t\t/>\n\t\t\t</ThemeProvider>,\n\t\t\t{ exitOnCtrlC: false },\n\t\t)\n\t\tawait waitUntilExit()\n\t} finally {\n\t\tprocess.stdout.write(\"\\x1B[?25h\")\n\t\tawait store.prune()\n\t}\n}\n\nfunction App({\n\tagent,\n\tstore,\n\tsessionId: initialSessionId,\n\tskills,\n\tinitialHistory,\n\tpolicy,\n\tversion,\n\thasAgentsMd,\n}: {\n\tagent: Agent\n\tstore: SessionStore\n\tsessionId: string\n\tskills: Skill[]\n\tinitialHistory: ModelMessage[]\n\tpolicy: PolicyEngine\n\tversion: string\n\thasAgentsMd: boolean\n}) {\n\tconst theme = useTheme()\n\n\tconst session = useSession(agent, store, initialSessionId, initialHistory)\n\tconst turn = useAgentTurn(\n\t\tagent,\n\t\tstore,\n\t\tsession.sessionId,\n\t\tsession.setContextTokens,\n\t\tsession.commitMsg,\n\t\tsession.commitDelta,\n\t)\n\n\tconst [permissionMode, setPermissionMode] = useState<PermissionMode>(policy.mode)\n\n\t// Abstracted TUI business logic hooks\n\tconst { mode, prompts, resolvePrompt } = usePrompts(policy)\n\tconst { committedEvents, liveEvents, contextTokens, tip } = useTuiTimeline({\n\t\tmessages: session.messages,\n\t\tnotices: session.notices,\n\t\tcontextTokens: session.contextTokens,\n\t\tversion,\n\t\tskills,\n\t\thasAgentsMd,\n\t\tpermissionMode,\n\t\tturn,\n\t})\n\n\tconst handlePermissionSwitch = async () => {\n\t\tconst picked = await prompts.select({\n\t\t\tmessage: \"Permission mode\",\n\t\t\toptions: [\n\t\t\t\t{\n\t\t\t\t\tvalue: \"restricted\",\n\t\t\t\t\tlabel: \"Restricted — ask permission before each action\",\n\t\t\t\t\thint: permissionMode === \"restricted\" ? \"current\" : undefined,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tvalue: \"unrestricted\",\n\t\t\t\t\tlabel: \"Unrestricted — run without approval (may be dangerous)\",\n\t\t\t\t\thint: permissionMode === \"unrestricted\" ? \"current\" : undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t})\n\t\tif (picked !== \"restricted\" && picked !== \"unrestricted\") return\n\t\tpolicy.setMode(picked)\n\t\tsetPermissionMode(picked)\n\t\tsession.addNotice(`✓ Permission mode set to ${picked}.`)\n\t}\n\n\tconst { exit } = useApp()\n\n\tconst { input, suggestions, selCmdIdx, exitConfirmKey } = useInputHandler({\n\t\tagent,\n\t\tstore,\n\t\tsession,\n\t\tturn,\n\t\tprompts,\n\t\tmode,\n\t\texit,\n\t\thandlePermissionSwitch,\n\t\tskills,\n\t})\n\n\tconst activity = useMemo(() => {\n\t\tif (exitConfirmKey === \"C\")\n\t\t\treturn { label: \"Press Ctrl+C again to exit\", color: theme.palette.warning }\n\t\tif (mode.type === \"searchSelect\") return { label: \"Filtering...\", color: theme.palette.primary }\n\t\tif (mode.type !== \"chat\") return { label: \"Waiting for input\", color: theme.palette.muted }\n\t\treturn { label: \"/help\", color: theme.palette.muted }\n\t}, [exitConfirmKey, mode.type, theme])\n\n\tconst composerSuggestions = mode.type === \"chat\" ? suggestions : []\n\n\treturn (\n\t\t<Box flexDirection=\"column\" width=\"100%\">\n\t\t\t<Conversation\n\t\t\t\tkey={session.sessionId}\n\t\t\t\tcommittedEvents={committedEvents}\n\t\t\t\tliveEvents={liveEvents}\n\t\t\t/>\n\t\t\t{mode.type !== \"chat\" && <PromptOverlay mode={mode} onResolve={resolvePrompt} />}\n\t\t\t<Composer input={input} suggestions={composerSuggestions} selCmdIdx={selCmdIdx} />\n\t\t\t<StatusBar\n\t\t\t\tactivity={activity.label}\n\t\t\t\tactivityColor={activity.color}\n\t\t\t\tmodel={agent.model}\n\t\t\t\tcontextTokens={contextTokens}\n\t\t\t\ttip={tip}\n\t\t\t/>\n\t\t</Box>\n\t)\n}\n"],"mappings":"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"}
|