@x-otto/tools 0.0.1-alpha.3 → 0.0.1-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,29 +1,33 @@
1
- import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_INLINE_MAX_BYTES as n,buildAllowedEnv as r,createLogger as i,createProjectOverflowLogPath as a,createTempLoggerPath as o,exists as s,formatBytes as c,getHomeDir as l,getThirdPartyToolBinaryDir as u,getThirdPartyToolDir as d,invariant as f,isDirectory as p,isFile as m,mime as h,mkdirp as g,normalizePath as _,parseJsonc as v,probeImageDimensions as y,sweepStaleProjectOverflowLogs as ee,sweepStaleTempLogs as te,toTruncationMeta as b,truncateHead as x,truncateLine as ne,truncateTail as S}from"@x-otto/shared";import C from"os";import w,{homedir as re,tmpdir as ie}from"node:os";import{appendFileSync as ae,chmodSync as oe,closeSync as se,copyFileSync as ce,createReadStream as le,createWriteStream as ue,existsSync as T,mkdirSync as E,openSync as de,readFileSync as fe,readdirSync as pe,realpathSync as me,renameSync as he,rmSync as D,statSync as O,writeFileSync as ge}from"node:fs";import{glob as _e,readFile as k,rename as ve,stat as ye,unlink as be,writeFile as A}from"node:fs/promises";import{createHash as xe,randomUUID as Se}from"node:crypto";import{dirname as j,extname as Ce,join as M,relative as N,resolve as P}from"node:path";import{Readable as we}from"node:stream";import{pipeline as Te}from"node:stream/promises";import{setTimeout as Ee}from"node:timers/promises";import{spawn as F}from"node:child_process";import{OTTO_PROJECT_DIR as De,OTTO_USER_AGENT as Oe,SETTING_OFFLINE_MODE_ENABLED as ke,normalizeEnv as I}from"@x-otto/env";import{createInterface as Ae}from"node:readline";import{Worker as je}from"node:worker_threads";import{diffLines as Me}from"diff";import{WORKFLOW_SLOTS as Ne}from"@x-otto/ai";import{checkDelegationDepth as Pe,createTodoStore as Fe}from"@x-otto/orchestration-contracts";import{fileURLToPath as Ie,pathToFileURL as L}from"node:url";const Le=e.object({label:e.string(),rationale:e.string().describe(`选这项的含义/利弊(plain language)`),recommended:e.boolean().optional(),preview:e.string().optional().describe(`两段具体产物并排对比`)}),Re=e.object({header:e.string().max(24).describe(`短主题标签(≤24 字),便于 UI/日志`),question:e.string().describe(`一个清晰具体的关键问题`),background:e.string().describe(`详细背景与前因后果(grill 纪律:必填)`),recommendation:e.string().describe(`推荐答案 + 理由(必填);无真人应答时被自动采纳`),options:e.array(Le).optional(),allowFreeform:e.boolean().optional().default(!0),multiSelect:e.boolean().optional().default(!1),evidence:e.array(e.string()).optional().describe(`引用的本地证据/社区方案`)});function ze(e,t){return{name:`grill_me`,description:`Background and recommendation are required fields; advisory only — side effects still pass permission/approval. Otherwise decide yourself.`,parameters:Re,readonly:!1,async execute({params:e,agentName:n,sessionId:r}){if(!t||!r)return{content:[{type:`text`,text:`grill_me is not available in this context. If you need clarification, return your open question in your result so the lead/main agent can ask the human.`}],isError:!0,errorKind:`runtime`};let i=await t(r,{header:e.header,question:e.question,background:e.background,recommendation:e.recommendation,options:e.options,allowFreeform:e.allowFreeform,multiSelect:e.multiSelect,evidence:e.evidence,askedBy:n??`agent`});return{content:[{type:`text`,text:`${i.resolvedBy===`auto_recommendation`?`(无真人应答,已采纳推荐)`:``}${i.answer}`}],details:{resolvedBy:i.resolvedBy,acceptedRecommendation:i.acceptedRecommendation,selectedOptionLabels:i.selectedOptionLabels,freeformText:i.freeformText}}}}}const R=I(process.env.TOOLS_MAX_OUTPUT_LINES,2e3),z=I(process.env.TOOLS_MAX_OUTPUT_BYTES,60*1024),B=I(process.env.TOOLS_EXECUTE_TIMEOUT_MS,3e4),Be=I(process.env.TOOLS_BINARY_DOWNLOAD_TIMEOUT_MS,12e5),Ve=I(process.env.TOOLS_MAX_WRITE_BYTES,10*1024*1024),V=I(process.env.TOOLS_MAX_FETCH_BYTES,5*1024*1024),He=I(process.env.TOOLS_BINARY_PROBE_TIMEOUT_MS,5e3);function H(e,t){return Object.assign(Error(e),{errorKind:t})}const Ue=i(`@x-otto/tools:binary`),We=256*1024;async function Ge(e,t=[`--version`]){try{return(await Ke(e,t,{timeout:He})).status===0}catch{return!1}}function Ke(e,t,n){return new Promise((r,i)=>{let a=F(e,t,{stdio:[`ignore`,`ignore`,`pipe`]}),o=``;a.stderr?.on(`data`,e=>{o+=e.toString()}),n?.timeout&&setTimeout(()=>{a.kill(),i(Error(`timeout`))},n.timeout),a.on(`close`,e=>r({status:e,stderr:o})),a.on(`error`,i)})}async function qe(e,t=e){let n=w.platform()===`win32`?`.exe`:``,r=u(t,e)+n;try{if(T(r))return r}catch{Ue.debug(`Error checking existence of ${r}, will try PATH lookup: %s`,r)}return await Ge(e)?e:null}async function Je(e,t,n){if(!t){Ue.warn(`No checksum configured for %s — integrity not verified`,n);return}let r=xe(`sha256`);r.update(await k(e));let i=r.digest(`hex`);if(i.toLowerCase()!==t.toLowerCase())throw H(`Checksum mismatch for ${n}: expected ${t}, got ${i}`,`validation`)}async function Ye(e,t,n){let r=async(t,n)=>{try{let{status:e,stderr:r}=await Ke(t,n);if(e!==0)throw Error(r?.trim()||`exit code ${e}`)}catch(t){throw H(`Failed to extract ${e}: ${t instanceof Error?t.message:String(t)}`,`io`)}};if(e.endsWith(`.tar.gz`)||e.endsWith(`.tgz`)){await r(`tar`,[`xzf`,t,`-C`,n]);return}if(e.endsWith(`.zip`)){w.platform()===`win32`?await r(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -LiteralPath '${t}' -DestinationPath '${n}' -Force`]):await r(`unzip`,[`-o`,`-q`,t,`-d`,n]);return}throw H(`Unsupported archive format: ${e}`,`validation`)}async function Xe(e,t){let n=Date.now()+Be;for(;;){try{return se(de(e,`wx`)),!0}catch(e){if(e.code!==`EEXIST`)throw e}try{if(Date.now()-O(e).mtimeMs>3e5){let t=`${e}.stale-${Se()}`;try{he(e,t),D(t,{force:!0})}catch{}continue}}catch{continue}if(T(t)||Date.now()>n)return!1;await Ee(200)}}async function Ze(e){let{name:t,cacheDir:n,url:r,checksum:i}=e,a=w.platform(),o=d(),s=t+(a===`win32`?`.exe`:``),c=P(o,n),l=P(c,s),u=P(o,`${n}.lock`),f=Se(),p=P(o,`${n}-${f}.download`),m=P(o,`${n}-extract-${f}`);if(E(o,{recursive:!0}),await Xe(u,l))try{if(T(l))return;let e=await fetch(r,{headers:{"User-Agent":Oe},signal:AbortSignal.timeout(Be)});if(!e.ok||!e.body)throw H(`Failed to download: ${e.status}`,`network`);let t=ue(p);await Te(we.fromWeb(e.body),t),await Je(p,i,r),E(c,{recursive:!0}),E(m,{recursive:!0}),await Ye(r,p,m);let n=Qe(m,s);if(!n)throw H(`Binary ${s} not found in archive`,`not_found`);ce(n,l),a!==`win32`&&oe(l,493)}catch(e){return Ue.warn(e),e instanceof Error?e:Error(String(e))}finally{D(p,{force:!0}),D(m,{recursive:!0,force:!0}),D(u,{force:!0})}}function Qe(e,t){let n=[e];for(;n.length>0;){let e=n.pop(),r;try{r=pe(e,{encoding:`utf-8`})}catch{continue}for(let i of r){let r=M(e,i);try{let e=O(r);if(e.isDirectory())n.push(r);else if(e.isFile()&&i===t)return r}catch{continue}}}}var U=class{get cacheDir(){return`${this.name}-${this.version}`}projectRoot;downloadTask=null;lastDownloadError;processTracker;constructor(e,t){this.projectRoot=e,this.processTracker=t}resolveChecksum(){}async ensure(){let e=await qe(this.name,this.cacheDir);if(e)return e;if(ke)throw H(`${this.name} not found and offline mode is enabled`,`not_found`);this.downloadTask||=this.download().finally(()=>{this.downloadTask=null}),await this.downloadTask;let t=await qe(this.name,this.cacheDir);if(!t){let e=this.lastDownloadError,t=H(`${this.name}: download failed${e?` — ${e.message}`:` (binary not found)`}`,`not_found`);throw e&&(t.cause=e),t}return t}async download(){let e=this.resolveUrl();f(e,`No download URL for ${this.name}`),this.lastDownloadError=await Ze({name:this.name,cacheDir:this.cacheDir,url:e,checksum:this.resolveChecksum()})}async execute(e,t){let n=await this.ensure(),r=t?.maxBuffer;return new Promise((i,a)=>{let o=F(n,e,{cwd:t?.cwd,env:t?.env,timeout:t?.timeout,signal:t?.signal});this.processTracker?.registerChild({command:n,args:e,owner:{type:`agent-tool`,id:`binary`},category:`tool`,lifecycle:`evictable`,cwd:typeof t?.cwd==`string`?t.cwd:void 0},o);let s=[],c=[],l=0,u=0,d=!1,f,p=()=>{try{o.kill(`SIGTERM`)}catch{}f=setTimeout(()=>{try{o.kill(`SIGKILL`)}catch{}},2e3),f.unref?.()};o.stdout.on(`data`,e=>{if(!d){if(r!==void 0&&l+e.byteLength>r){let t=r-l;t>0&&(s.push(e.subarray(0,t)),l=r),d=!0,p();return}s.push(e),l+=e.byteLength}}),o.stderr.on(`data`,e=>{if(u>=We)return;let t=We-u;e.byteLength>t?(c.push(e.subarray(0,t)),u=We):(c.push(e),u+=e.byteLength)}),o.on(`error`,e=>{f&&clearTimeout(f),a(e)}),o.on(`close`,e=>{if(f&&clearTimeout(f),!d&&e!==0)return a(Error(`Process exited with code ${e}: ${Buffer.concat(c).toString()}`));i({stdout:Buffer.concat(s).toString(),stderr:Buffer.concat(c).toString(),exitCode:d?0:e,truncated:d})})})}};function $e(e,t,n){return`https://github.com/${e}/releases/download/v${t}/${n}`}const et={"fd-v10.4.2-aarch64-apple-darwin.tar.gz":`623dc0afc81b92e4d4606b380d7bc91916ba7b97814263e554d50923a39e480a`,"fd-v10.4.2-aarch64-unknown-linux-gnu.tar.gz":`6c51f7c5446b3338b1e401ff15dc194c590bb2fa64fd43ff3278300f073adec5`,"fd-v10.4.2-x86_64-unknown-linux-gnu.tar.gz":`def59805cd14b5651b68990855f426ad087f3b96881296d963910431ba3143c8`,"fd-v10.4.2-aarch64-pc-windows-msvc.zip":`4f9110c2d5b33a7f760bfa5510f4c113d828109f7277d421b1053a9943c0fc92`,"fd-v10.4.2-x86_64-pc-windows-msvc.zip":`b2816e506390a89941c63c9187d58a3cc10e9a55f2ef0685f9ea0eccaf7c98c8`};var tt=class extends U{name=`fd`;version=`10.4.2`;repository=`sharkdp/fd`;constructor(e,t){super(e,t)}resolveAsset(){let e=C.platform(),t=C.arch();switch(e){case`darwin`:return t===`arm64`?`fd-v${this.version}-aarch64-apple-darwin.tar.gz`:`fd-v${this.version}-x86_64-apple-darwin.tar.gz`;case`linux`:return t===`arm64`?`fd-v${this.version}-aarch64-unknown-linux-gnu.tar.gz`:`fd-v${this.version}-x86_64-unknown-linux-gnu.tar.gz`;case`win32`:return t===`arm64`?`fd-v${this.version}-aarch64-pc-windows-msvc.zip`:`fd-v${this.version}-x86_64-pc-windows-msvc.zip`}throw Error(`Unsupported platform/architecture: ${e}/${t}`)}resolveUrl(){return $e(this.repository,this.version,this.resolveAsset())}resolveChecksum(){return et[this.resolveAsset()]}};const nt=(e,t)=>new tt(e,t);function rt(e,t,n){return`https://github.com/${e}/releases/download/${t}/${n}`}const it={"ripgrep-15.1.0-aarch64-apple-darwin.tar.gz":`378e973289176ca0c6054054ee7f631a065874a352bf43f0fa60ef079b6ba715`,"ripgrep-15.1.0-x86_64-apple-darwin.tar.gz":`64811cb24e77cac3057d6c40b63ac9becf9082eedd54ca411b475b755d334882`,"ripgrep-15.1.0-aarch64-unknown-linux-gnu.tar.gz":`2b661c6ef508e902f388e9098d9c4c5aca72c87b55922d94abdba830b4dc885e`,"ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz":`1c9297be4a084eea7ecaedf93eb03d058d6faae29bbc57ecdaf5063921491599`,"ripgrep-15.1.0-aarch64-pc-windows-msvc.zip":`00d931fb5237c9696ca49308818edb76d8eb6fc132761cb2a1bd616b2df02f8e`,"ripgrep-15.1.0-x86_64-pc-windows-msvc.zip":`124510b94b6baa3380d051fdf4650eaa80a302c876d611e9dba0b2e18d87493a`};var at=class extends U{name=`rg`;repository=`BurntSushi/ripgrep`;version=`15.1.0`;constructor(e,t){super(e,t)}resolveAsset(){let e=C.platform(),t=C.arch();switch(e){case`darwin`:return t===`arm64`?`ripgrep-${this.version}-aarch64-apple-darwin.tar.gz`:`ripgrep-${this.version}-x86_64-apple-darwin.tar.gz`;case`linux`:return t===`arm64`?`ripgrep-${this.version}-aarch64-unknown-linux-gnu.tar.gz`:`ripgrep-${this.version}-x86_64-unknown-linux-musl.tar.gz`;case`win32`:return t===`arm64`?`ripgrep-${this.version}-aarch64-pc-windows-msvc.zip`:`ripgrep-${this.version}-x86_64-pc-windows-msvc.zip`}throw Error(`Unsupported platform/architecture: ${e}/${t}`)}resolveUrl(){return rt(this.repository,this.version,this.resolveAsset())}resolveChecksum(){return it[this.resolveAsset()]}};const ot=(e,t)=>new at(e,t),st=i(`@x-otto/tools:binary-executor-registry`);var ct=class{binaries=new Map;has(e){return this.binaries.has(e)}get(e){return this.binaries.get(e)}register(e){this.binaries.has(e.name)?st.warn(`Tool ${e.name} is already registered, skipping`):this.binaries.set(e.name,e)}unregister(e){this.binaries.has(e)?this.binaries.delete(e):st.warn(`Tool ${e} is not registered, skipping`)}async ensureAll(){for(let e of this.binaries.values())e instanceof U&&await e.ensure()}async ensure(e){let t=this.binaries.get(e);if(!t)throw Error(`Tool ${e} not found in registry`);return t instanceof U&&await t.ensure(),t}dispose(){this.binaries.clear()}};const lt=(e,t)=>{let n=new ct;return n.register(nt(e,t)),n.register(ot(e,t)),n};function ut(e){return typeof e==`function`?e():e}const dt=()=>({chunks:Buffer.alloc(0),stdout:Buffer.alloc(0),stderr:Buffer.alloc(0),exitCode:-1,timedOut:!1}),ft=(e,t)=>{try{process.kill(-e,t)}catch{try{process.kill(e,t)}catch{}}},pt=new class{name=`local`;execute(e,t,n,i){let{cwd:a,env:o,timeout:s,signal:c}=n;return new Promise((n,l)=>{if(c?.aborted){l(Error(`Process execution aborted before start`));return}let u=dt(),d=[],f=[],p=[],m,h=()=>{let e=y.pid;if(e!==void 0){if(process.platform===`win32`){F(`taskkill`,[`/F`,`/T`,`/PID`,`${e}`],{stdio:`ignore`,detached:!0});return}ft(e,`SIGTERM`),m=setTimeout(()=>ft(e,`SIGKILL`),2e3)}},g=setTimeout(()=>{u.timedOut=!0,h()},s),_=()=>h();c?.addEventListener(`abort`,_,{once:!0});let v=()=>{clearTimeout(g),m&&clearTimeout(m),c?.removeEventListener(`abort`,_)},y=F(e,t,{cwd:a,env:r(o),stdio:[`ignore`,`pipe`,`pipe`],detached:process.platform!==`win32`});y.stdout?.on(`data`,e=>{d.push(e),p.push(e),i?.(e,`stdout`)}),y.stderr?.on(`data`,e=>{f.push(e),p.push(e),i?.(e,`stderr`)}),y.on(`error`,e=>{v(),l(e)}),y.on(`close`,e=>{u.exitCode=e??-1,u.stdout=Buffer.concat(d),u.stderr=Buffer.concat(f),u.chunks=Buffer.concat(p),v(),u.timedOut?l(Error(`Process timed out after ${s}ms`)):n(u)})})}};function mt(e,t,n){let i=e,a=t,o=n.cwd,s=n.env;if(n.sandbox){let e=n.projectRoot??o,t=n.sandbox.wrap({command:i,args:a,options:{timeout:0,cwd:o,env:s,projectRoot:e},projectRoot:e});i=t.command,a=t.args,o=t.options.cwd??o,s=t.options.env}let c=n.onOutputChunk!==void 0,l,u;if(c){u=de(n.logPath,`a`),l=F(i,a,{cwd:o,env:r(s),stdio:[`ignore`,`pipe`,`pipe`],detached:process.platform!==`win32`});let e=(e,t)=>{if(u!==void 0)try{ae(u,e)}catch{}n.onOutputChunk?.(e,t)};l.stdout?.on(`data`,t=>e(t,`stdout`)),l.stderr?.on(`data`,t=>e(t,`stderr`)),l.once(`exit`,()=>{if(u!==void 0){try{se(u)}catch{}u=void 0}}),l.stdout?.unref?.(),l.stderr?.unref?.()}else{let e=de(n.logPath,`a`);try{l=F(i,a,{cwd:o,env:r(s),stdio:[`ignore`,e,e],detached:process.platform!==`win32`})}finally{se(e)}}l.once(`error`,e=>{try{ae(n.logPath,`\n[otto] background spawn error: ${e.message}\n`)}catch{}}),l.unref();let d=l.pid;if(d===void 0)throw Error(`Failed to spawn background process (no pid)`);return{child:l,pid:d}}function ht(e,t,n){let r=n.sandbox;if(r){let i=n.projectRoot??n.cwd??process.cwd(),a=r.wrap({command:e,args:t,options:n,projectRoot:i});e=a.command,t=a.args,n=a.options}let{cwd:i,env:a,timeout:o,signal:s,onProgress:c}=n;return(ut(n.backend)??pt).execute(e,t,{cwd:i,env:a,timeout:o,signal:s},c)}const gt=/\b(https?|wss?):\/\/([-\w.]+)(?::(\d{2,5}))?(\/[^\s)\],."']*)?/i,_t=/\b(?:listening on\s+(?:port\s+)?|(?:running\s+)?at\s+:|on\s+:|port[:\s]+)(\d{2,5})\b/i;function vt(e){return/^wss?$/i.test(e)?`ws`:`http`}function yt(e){let t=e.match(gt);if(t){let[e,n,,r]=t,i=r?Number(r):void 0;return{url:e,port:i!==void 0&&i>0&&i<=65535?i:void 0,protocol:vt(n)}}let n=e.match(_t);if(n){let e=Number(n[1]);if(e>0&&e<=65535)return{port:e,protocol:`tcp`}}return null}const bt=e.object({command:e.string().describe(`Shell command to execute`),targetDir:e.string().optional().describe(`Working directory (relative to project root)`),timeout:e.number().int().min(1e3).optional().describe(`Timeout (ms), default 30000`),run_in_background:e.boolean().optional().describe(`Run as a long-lived background process (dev servers, watchers). Returns immediately with a background id; read output with bash_output and stop with kill_shell.`)}),xt=i(`@x-otto/tools:bash`);function St(e,t,n,r,i){return{name:`bash`,description:`Execute a shell command and return stdout/stderr. Default timeout is 30 seconds.`,parameters:bt,async execute({params:a,signal:o,id:s,sessionId:c}){let l=P(e,a.targetDir??`.`),u=a.timeout??B,d=typeof n==`function`?n({command:a.command}):n,f=ut(i);return a.run_in_background?!r||!c?{content:[{type:`text`,text:`Background execution is unavailable in this context (no process registry / session). Re-run without run_in_background, or run a short-lived foreground command.`}],isError:!0,errorKind:`runtime`}:Tt(a.command,l,e,d,r,c,s):await Dt(a.command,l,e,u,t,o,d,f)}}}function Ct(e){let t=e.match(/(?:--port[ =]|-p\s+|localhost:|0\.0\.0\.0:|127\.0\.0\.1:|http\.server\s+)(\d{2,5})\b/i);if(t){let e=Number(t[1]);if(e>0&&e<=65535)return e}}function wt(e){let t=``;return n=>{t+=n.toString(`utf-8`);let r;for(;(r=t.indexOf(`
2
- `))!==-1;){let n=t.slice(0,r);t=t.slice(r+1);let i=yt(n);i&&e(i)}}}function Tt(e,t,n,r,i,a,s){let c=`bg_${a}_${s}`,l=o();te(Et,Date.now());try{let o=wt(e=>{i.updateDetectedService(c,e)}),{child:s,pid:u}=mt(`sh`,[`-c`,e],{cwd:t,projectRoot:n,sandbox:r,logPath:l,onOutputChunk:e=>o(e)}),d=Ct(e);try{i.register({id:c,sessionId:a,child:s,command:e,cwd:t,logPath:l,port:d})}catch(e){try{process.kill(-u,`SIGKILL`)}catch{try{s.kill(`SIGKILL`)}catch{}}throw e}return{content:[{type:`text`,text:`Started in background.\nid: ${c}\npid: ${u}\nlog: ${l}${d?`\nlikely port: ${d}`:``}\n\nRead output with bash_output(background_id="${c}"); stop it with kill_shell(background_id="${c}"). It is auto-killed when the session ends.`}],details:{backgroundProcessId:c,pid:u,logPath:l,port:d}}}catch(e){let t=e instanceof Error?e.message:String(e);return xt.warn({err:t},`Failed to start background process`),{content:[{type:`text`,text:`Failed to start background process: ${t}`}],isError:!0,errorKind:`runtime`}}}const Et=60*6e4;async function Dt(e,t,n,r,i,s,l,u){let d=z*2,f=[],p=0,m=0,h=0,g=0,_,v,y=()=>{if(!i)return;let e=S(Buffer.concat(f).toString(`utf-8`),{maxLines:R,maxBytes:z});i({content:[{type:`text`,text:e.content||``}],details:{truncation:b(e),outputPath:_}})},x=await ht(`sh`,[`-c`,e],{timeout:r,signal:s,cwd:t,sandbox:l,projectRoot:n,backend:u,onProgress:e=>{if(f.push(e),p+=e.byteLength,m+=e.byteLength,p>z&&!_){te(Et,Date.now()),ee(6048e5,Date.now());let e=a();try{E(j(e),{recursive:!0})}catch{e=o()}_=e,v=ue(e),v.on(`error`,e=>{xt.warn({err:e.message},`bash overflow log write failed; disabling overflow log`),v=void 0,_=void 0});for(let e of f)v.write(e)}else v&&v.write(e);for(;m>d&&f.length>1;){let e=f.shift();e&&(m-=e.byteLength)}h+=e.byteLength;let t=Date.now();h>=4096&&t-g>=100&&(h=0,g=t,y())}});v?.end();let ne=Buffer.concat(f).toString(`utf-8`),C=S(ne,{maxLines:R,maxBytes:z}),w=C.content||`(no output)`,re={truncation:b(C),outputPath:_};if(C.truncated){let e=C.totalLines-C.outputLines+1,t=C.totalLines,n=_?` Full output: ${_}`:``;if(C.lastLinePartial){let e=c(Buffer.byteLength(ne.split(`
3
- `).pop()||``,`utf-8`));w+=`\n\n(Showing last ${c(C.outputBytes)} of line ${t} (line is ${e}).${n})`}else C.truncatedBy===`lines`?w+=`\n\n(Showing lines ${e}-${t} of ${C.totalLines}.${n})`:w+=`\n\n(Showing lines ${e}-${t} of ${C.totalLines} (${c(z)} limit).${n})`}return x.exitCode===0?{content:[{type:`text`,text:w}],details:re}:(w+=`\n\nCommand exited with code ${x.exitCode}`,{content:[{type:`text`,text:w}],isError:!0,errorKind:`runtime`,details:re})}const Ot=e.object({background_id:e.string().describe(`Background process id returned by bash with run_in_background:true`),block:e.boolean().optional().describe(`Wait until the process exits before returning (default false)`),timeout_ms:e.number().int().min(0).max(6e5).optional().describe(`Max wait when block=true (ms), default from TOOLS_EXECUTE_TIMEOUT_MS (30000)`)}),kt=e=>new Promise(t=>setTimeout(t,e));async function At(e){if(!e.logPath)return`(no output captured)`;let t;try{t=await k(e.logPath,`utf-8`)}catch{return`(output log unavailable)`}return S(t,{maxLines:R,maxBytes:z}).content||`(no output yet)`}function jt(e){return{name:`bash_output`,description:`Read accumulated output and status of a background process.`,parameters:Ot,readonly:!0,async execute({params:t,sessionId:n}){let r=t.background_id,i=n?e.getForSession(n,r):e.get(r);if(!i)return{content:[{type:`text`,text:`No background process with id "${r}".`}],isError:!0,errorKind:`not_found`};let a=Date.now()+(t.timeout_ms??B);for(;t.block&&i.status===`running`&&Date.now()<a;){await kt(200);let t=e.get(r);if(!t)return{content:[{type:`text`,text:`[status: untracked]\nBackground process "${r}" is no longer tracked (likely evicted at the process limit).`}],details:{status:`untracked`,logPath:i.logPath}};i=t}let o=await At(i);return{content:[{type:`text`,text:`${i.status===`running`?`[status: running]`:`[status: ${i.status}${i.exitCode===void 0?``:`, exit ${i.exitCode}`}]`}\n${o}`}],details:{status:i.status,exitCode:i.exitCode,logPath:i.logPath}}}}}const Mt=e.object({background_id:e.string().describe(`Background process id (returned by bash run_in_background) to stop`)});function Nt(e){return{name:`kill_shell`,description:`Stop a background process.`,parameters:Mt,async execute({params:t,sessionId:n}){let r=t.background_id,i=n?e.getForSession(n,r):e.get(r);return i?i.status!==`running`&&i.status!==`killing`?{content:[{type:`text`,text:`Background process "${r}" already ${i.status}.`}]}:(await e.kill(r),{content:[{type:`text`,text:`Stopped background process "${r}".`}]}):{content:[{type:`text`,text:`No background process with id "${r}".`}],isError:!0,errorKind:`not_found`}}}}const Pt=i(`@x-otto/tools:find`),Ft=e.object({pattern:e.string().describe(`File name pattern with optional wildcards (*, ?), e.g. "*.ts", "data-??.json"`),path:e.string().optional().describe(`Search starting path (default is project root)`),limit:e.number().int().min(1).max(1e3).optional().default(1e3).describe(`Maximum number of results to return`)});function It(e,t={}){return{name:`find`,description:`Search for files by glob pattern. Returns matching file paths relative to the search directory. Respects .gitignore. Output is truncated to ${R} results or ${z/1024}KB (whichever is hit first).`,parameters:Ft,pathParams:[`path`],readonly:!0,async execute({params:n}){let r=_(P(e,n.path??`.`)),i=n.limit??R;if(!await s(r))throw H(`Search path does not exist: ${n.path}`,`not_found`);if(!await p(r))throw H(`Search path is not a directory: ${n.path}`,`validation`);return await Bt(r,n.pattern,i,t.glob,t.binaryExecutorRegistry)}}}const Lt=new Set([`node_modules`,`.git`,`dist`,`build`,`.next`,`target`,`vendor`]);async function Rt(e,t,n){let r=[`--glob`,`--color=never`,`--hidden`,`--max-results`,String(n)],i=new Set,a=M(t,`.gitignore`);await s(a)&&i.add(a);try{let e=Date.now(),n=_e(`**/`,{cwd:t,exclude:e=>Lt.has(e)});for await(let r of n){let n=M(t,r,`.gitignore`);if(await s(n)&&i.add(n),i.size>=200||Date.now()-e>1500){Pt.warn({normalizedSearchDir:t,found:i.size,elapsedMs:Date.now()-e},`find: .gitignore discovery hit time/count budget, stopping early (best-effort)`);break}}}catch{}for(let e of i)r.push(`--ignore-file`,e);return r.push(`--`,e,t),r}function zt(e,t,n,r=[]){let i=e.length>=t,a=x(e.join(`
4
- `),{maxLines:R,maxBytes:z}),o=a.content,s={},l=[];i&&(l.push(n),s.resultLimitReached=t),a.truncated&&(l.push(`${c(z)} limit reached`),s.truncation=b(a));for(let e of r)l.push(e.text),s[e.detailKey]=e.detailValue;return l.length>0&&(o+=`\n\n(${l.join(`. `)})`),{content:[{type:`text`,text:o}],details:s}}async function Bt(e,t,n,r,i){if(typeof r==`function`){Pt.debug(`Using custom glob implementation for find tool`);let i=await r(t,e,{ignore:[`**/node_modules/**`,`**/.git/**`],limit:n});return i.length===0?{content:[{type:`text`,text:`No files found matching pattern`}]}:zt(i.map(t=>t.startsWith(e)?t.slice(e.length+1):N(e,t)),n,`${n} results limit reached`)}Pt.debug(`No custom glob provided, using fd binary for find tool`);let a=await i?.ensure(`fd`);if(!a)throw H(`Find tool requires a glob implementation or fd binary available`,`runtime`);let o=await Rt(t,e,n),s=await a.execute(o,{maxBuffer:z*4});if(s.exitCode!==0)return{content:[{type:`text`,text:s.stderr?.trim()||`fd exited with code ${s.exitCode}`}]};let c=s.stdout?.trim()||``;if(!c)return{content:[{type:`text`,text:`No files found matching pattern`}]};let l=c.split(`
5
- `),u=[];for(let t of l){let n=t.replace(/\r$/,``).trim();if(!n)continue;let r=n.endsWith(`/`)||n.endsWith(`\\`),i=n;i=n.startsWith(e)?n.slice(e.length+1):N(e,n),r&&!i.endsWith(`/`)&&(i+=`/`),u.push(i)}let d=s.truncated?[{text:`output exceeded buffer limit; results are incomplete (refine the pattern)`,detailKey:`bufferTruncated`,detailValue:!0}]:[];return zt(u,n,`${n} results limit reached. Use limit=${n*2} for more, or refine pattern`,d)}const Vt=z*8,Ht=e.object({pattern:e.string().describe(`Search pattern (regex or literal string)`),path:e.string().optional().describe(`Directory or file to search (default: current directory)`),glob:e.string().optional().describe(`Filter files by glob pattern, e.g. "*.ts" or "**/*.spec.ts"`),ignoreCase:e.boolean().optional().describe(`Case-insensitive search (default: false)`),literal:e.boolean().optional().describe(`Treat pattern as literal string instead of regex (default: false)`),context:e.number().int().min(0).optional().describe(`Number of lines to show before and after each match (default: 0)`),limit:e.number().int().min(1).optional().describe(`Maximum number of matches to return (default: 100)`)});function Ut(e,t){return{name:`grep`,description:`Search file contents for pattern matches. Returns matching lines with file paths and line numbers. Respects .gitignore. Truncated to 100 matches or ${z/1024}KB. Long lines truncated to 500 chars.`,parameters:Ht,pathParams:[`path`],readonly:!0,async execute({params:n}){let r=_(P(e,n.path??`.`));if(!await s(r))throw H(`Search path does not exist: ${n.path??`.`}`,`not_found`);return await Yt(n.pattern,r,e,n.glob,n.literal??!1,n.ignoreCase??!1,n.context??0,n.limit??100,t.binaryToolExecutorRegistry,t.processTracker)}}}function Wt(e,t,n,r,i,a,o){let s=[`--json`,`--line-number`,`--color=never`,`--hidden`];return n&&s.push(`--ignore-case`),r&&s.push(`--fixed-strings`),i&&s.push(`--glob`,i),a!=null&&s.push(`--max-count`,String(a)),o!=null&&o>0&&s.push(`--context`,String(o)),s.push(`--`,e,t),s}function Gt(e){let t=[];for(let n of e.split(`
6
- `))if(n.trim())try{let e=JSON.parse(n);if(e.type===`match`||e.type===`context`){let n=e.data;t.push({kind:e.type,path:n.path?.text??``,lineNumber:n.line_number??0,text:n.lines?.text?.replace(/\n$/,``)??``})}}catch{}return t}function Kt(e,t,n){return e.startsWith(t)?N(t,e):N(n,e)}function qt(e,t,n,r){let i=e.reduce((e,t)=>e+(t.kind===`match`?1:0),0),a=[],o=0,s=0,c=null,l=0,u=!1;for(let i of e){if(i.kind===`match`){if(o>=r)break;o++,o>=r&&(u=!0)}else if(u&&(i.path!==c||i.lineNumber!==l+1))break;c!==null&&(i.path!==c||i.lineNumber>l+1)&&a.push(`--`);let{text:e}=ne(i.text,500);i.text.length>500&&s++;let d=i.kind===`match`?`:`:`-`,f=Kt(i.path,t,n);a.push(`${f}${d}${i.lineNumber}${d} ${e}`),c=i.path,l=i.lineNumber}return{text:a.join(`
7
- `),totalMatches:i,matchLimitReached:i>r,linesTruncated:s}}async function Jt(e,t,n,r){return new Promise(i=>{let a=F(e,t,{stdio:`pipe`});r?.registerChild({command:e,args:t,owner:{type:`agent-tool`,id:`ripgrep`},category:`tool`,lifecycle:`evictable`},a);let o=[],s=0,c=!1;a.stdout.on(`data`,e=>{if(!c){if(s+e.byteLength>n){let t=n-s;t>0&&(o.push(e.subarray(0,t)),s=n),c=!0,a.kill();return}o.push(e),s+=e.byteLength}}),a.stderr.on(`data`,()=>{}),a.on(`error`,e=>{i({stdout:``,exitCode:-999,error:e.message,streamTruncated:!1})}),a.on(`close`,e=>{i({stdout:Buffer.concat(o).toString(`utf-8`),exitCode:c?0:e,streamTruncated:c})})})}async function Yt(e,t,n,r,i,a,o,s,l,u){if(!l)throw H(`Binary tool executor registry is not available`,`runtime`);let d=l.get(`rg`);if(!d)throw H(`ripgrep (rg) executor is not available`,`runtime`);let f;f=d instanceof U?await d.ensure():`rg`;let p=Wt(e,t,a,i,r,s,o),m=await Jt(f,p,Vt,u);if(m.exitCode===-999)throw H(`Failed to execute ripgrep: ${m.error??`unknown error`}`,`io`);if(m.exitCode===2&&!m.streamTruncated)throw H(`ripgrep error while searching for "${e}"`,`io`);let h=qt(Gt(m.stdout),n,t,s);if(h.totalMatches===0)return{content:[{type:`text`,text:`No matches found.`}]};let g=x(h.text,{maxLines:R,maxBytes:z}),_=g.content,v={truncation:b(g)},y=[];return h.matchLimitReached&&(y.push(`${s} match limit reached use a more specific pattern or increase limit`),v.matchLimitReached=s),g.truncated&&y.push(`Output truncated (${c(z)} limit)`),m.streamTruncated&&(y.push(`search output exceeded buffer limit; results are incomplete (refine the pattern)`),v.streamTruncated=!0),h.linesTruncated>0&&(y.push(`${h.linesTruncated} long lines truncated to 500 chars`),v.linesTruncated=h.linesTruncated),y.length>0&&(_+=`
8
-
9
- (`+y.join(`. `)+`)`),{content:[{type:`text`,text:_}],details:v}}const Xt=e.object({path:e.string().describe(`Path to the file to read (relative or absolute)`),limit:e.number().int().min(1).optional().describe(`Maximum number of lines to read (text files only)`),offset:e.number().int().min(1).optional().describe(`Starting line number (1-based, text files only)`)});function Zt(e){return{name:`read`,description:`Read file content. For text files, can specify line range with limit and offset.`,parameters:Xt,pathParams:[`path`],resolvePath:t=>P(e,t),readonly:!0,async execute({params:t}){let n=_(P(e,t.path));if(!await s(n))throw H(`File not found: ${t.path}`,`not_found`);if(!await m(n))throw H(`Not a file: ${t.path}`,`validation`);let r=await h(n);return r?.startsWith(`image/`)&&r!==`image/svg+xml`?$t(n,t.path,r):tn(n,t.path,t.offset,t.limit)}}}const Qt=n;async function $t(e,n,r){let i=await k(e);if(i.length>Qt)return{content:[{type:`text`,text:`[image omitted: ${n} is ${i.length} bytes, exceeds 5MB limit for inline image context — view a downscaled copy or crop the region you need]`}],details:{path:e,type:`image`,size:i.length,mime:r||`application/octet-stream`}};if(i.length>0){let a=y(i.toString(`base64`),r||``);if(a&&(a.width>t||a.height>t))return{content:[{type:`text`,text:`[image omitted: ${n} is ${a.width}x${a.height}px, exceeds Anthropic per-image dimension hard limit of ${t}px — view a downscaled copy or crop the region you need]`}],details:{path:e,type:`image`,size:i.length,mime:r||`application/octet-stream`,dimensions:a}}}let a=i.toString(`base64`);return{content:[{type:`text`,text:`Read image file ${n} (${i.length} bytes)`},{type:`image`,mime:r||`application/octet-stream`,source:`data:${r||`application/octet-stream`};base64,${a}`}],details:{path:e,type:`image`,size:i.length,mime:r||`application/octet-stream`}}}async function en(e,t,n,r){let i=le(e,`utf-8`),a=Ae({input:i,crlfDelay:1/0}),o=[],s=0,c=0,l=!1;try{for await(let e of a){if(s>=t){if(o.length>=n||c>=r){l=!0;break}o.push(e),c+=Buffer.byteLength(e,`utf-8`)+1}s++}}finally{a.close(),i.destroy()}return o.length===0&&t>=s?{lines:o,hasMore:!1,totalLinesIfBeyond:s}:{lines:o,hasMore:l}}async function tn(e,t,n=1,r=R,i=z){let a=n?Math.max(0,n-1):0,o=a+1,s=await en(e,a,r,i);if(s.totalLinesIfBeyond!==void 0)return{content:[{type:`text`,text:`Offset ${n} is beyond end of file (${s.totalLinesIfBeyond} lines total)`}]};let l=x(s.lines.join(`
10
- `),{maxBytes:z,maxLines:R}),u;if(l.firstLineExceedsLimit)u=`(Line ${o} is ${c(Buffer.byteLength(s.lines[0]??``,`utf-8`))}, exceeds ${c(i)} limit. Use bash: sed -n '${o}p' ${e} | head -c ${i})`;else if(l.truncated){let e=o+l.outputLines-1,t=e+1;u=l.content,l.truncatedBy===`lines`?u+=`\n\n(Showing lines ${o}-${e}. Use offset=${t} to continue)`:u+=`\n\n(Showing lines ${o}-${e} (${c(i)} limit). Use offset=${t} to continue)`}else if(s.hasMore){let e=o+s.lines.length-1+1;u=l.content,u+=`\n\n(More lines follow. Use offset=${e} to continue)`}else u=l.content;return{content:[{type:`text`,text:u}],details:{truncation:b(l)}}}const nn=e.object({path:e.string().describe(`Path to the file to write (relative or absolute)`),content:e.string().describe(`Content to write to the file`),createDirectories:e.boolean().optional().default(!0).describe(`Whether to create parent directories if they do not exist`)});function rn(e){return{name:`write`,description:`Create or overwrite a file, automatically creating parent directories if they do not exist`,parameters:nn,pathParams:[`path`],resolvePath:t=>P(e,t),async execute({params:t,signal:n}){let r=_(P(e,t.path)),i=Buffer.byteLength(t.content,`utf-8`);if(i>Ve)throw H(`Content too large: ${i} bytes exceeds the write limit of ${Ve} bytes`,`validation`);if(t.createDirectories!==!1&&await g(j(r)),n.aborted)throw H(`Write operation was aborted`,`aborted`);return await an(r,t.content,n)}}}async function an(e,t,n){if(n.aborted)throw H(`Write operation was aborted`,`aborted`);let r=await on(e);return await A(e,t,`utf-8`),{content:[{type:`text`,text:`Successfully ${r?`overwrote`:`created`} ${e}`}],details:{overwritten:r}}}async function on(e){try{return await ye(e),!0}catch{return!1}}const sn=`\x1B[0m`;function cn(e,t,n=4){let r=Me(e,t),i=[],a=e.split(`
1
+ import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_INLINE_MAX_BYTES as n,acquireFileLock as r,buildAllowedEnv as i,createLogger as a,createProjectOverflowLogPath as o,createTempLoggerPath as s,exists as c,formatBytes as l,getHomeDir as u,getThirdPartyToolBinaryDir as d,getThirdPartyToolDir as f,invariant as p,isDirectory as m,isFile as h,mime as g,mkdirp as _,normalizePath as v,parseJsonc as y,probeImageDimensions as b,releaseFileLock as ee,sweepStaleProjectOverflowLogs as te,sweepStaleTempLogs as ne,toTruncationMeta as x,truncateHead as S,truncateLine as C,truncateTail as w}from"@x-otto/shared";import T from"os";import E,{homedir as re,tmpdir as ie}from"node:os";import{appendFileSync as ae,chmodSync as oe,closeSync as se,copyFileSync as ce,createReadStream as le,createWriteStream as ue,existsSync as D,mkdirSync as O,openSync as de,readFileSync as fe,readdirSync as pe,realpathSync as me,rmSync as he,statSync as ge,writeFileSync as _e}from"node:fs";import{glob as ve,readFile as k,rename as ye,stat as be,unlink as xe,writeFile as A}from"node:fs/promises";import{createHash as Se,randomUUID as Ce}from"node:crypto";import{dirname as j,extname as we,isAbsolute as Te,join as M,relative as N,resolve as P,sep as Ee}from"node:path";import{Readable as De}from"node:stream";import{pipeline as Oe}from"node:stream/promises";import{setTimeout as ke}from"node:timers/promises";import{spawn as F}from"node:child_process";import{OTTO_PROJECT_DIR as Ae,OTTO_USER_AGENT as je,SETTING_OFFLINE_MODE_ENABLED as Me,normalizeEnv as I}from"@x-otto/env";import{createInterface as Ne}from"node:readline";import{Worker as Pe}from"node:worker_threads";import{diffLines as Fe}from"diff";import{WORKFLOW_SLOTS as Ie}from"@x-otto/ai";import{checkDelegationDepth as Le,createTodoStore as Re}from"@x-otto/orchestration-contracts";import{fileURLToPath as ze,pathToFileURL as L}from"node:url";const Be=e.object({label:e.string(),rationale:e.string().describe(`选这项的含义/利弊(plain language)`),recommended:e.boolean().optional(),preview:e.string().optional().describe(`两段具体产物并排对比`)}),Ve=e.object({header:e.string().max(24).describe(`短主题标签(≤24 字),便于 UI/日志`),question:e.string().describe(`一个清晰具体的关键问题`),background:e.string().describe(`详细背景与前因后果(grill 纪律:必填)`),recommendation:e.string().describe(`推荐答案 + 理由(必填);无真人应答时被自动采纳`),options:e.array(Be).optional(),allowFreeform:e.boolean().optional().default(!0),multiSelect:e.boolean().optional().default(!1),evidence:e.array(e.string()).optional().describe(`引用的本地证据/社区方案`)});function He(e,t){return{name:`grill_me`,description:`Background and recommendation are required fields; advisory only — side effects still pass permission/approval. Otherwise decide yourself.`,parameters:Ve,readonly:!1,async execute({params:e,agentName:n,sessionId:r}){if(!t||!r)return{content:[{type:`text`,text:`grill_me is not available in this context. If you need clarification, return your open question in your result so the lead/main agent can ask the human.`}],isError:!0,errorKind:`runtime`};let i=await t(r,{header:e.header,question:e.question,background:e.background,recommendation:e.recommendation,options:e.options,allowFreeform:e.allowFreeform,multiSelect:e.multiSelect,evidence:e.evidence,askedBy:n??`agent`});return{content:[{type:`text`,text:`${i.resolvedBy===`auto_recommendation`?`(无真人应答,已采纳推荐)`:``}${i.answer}`}],details:{resolvedBy:i.resolvedBy,acceptedRecommendation:i.acceptedRecommendation,selectedOptionLabels:i.selectedOptionLabels,freeformText:i.freeformText}}}}}const R=I(process.env.TOOLS_MAX_OUTPUT_LINES,2e3),z=I(process.env.TOOLS_MAX_OUTPUT_BYTES,60*1024),B=I(process.env.TOOLS_EXECUTE_TIMEOUT_MS,3e4),Ue=I(process.env.TOOLS_BINARY_DOWNLOAD_TIMEOUT_MS,12e5),We=I(process.env.TOOLS_MAX_WRITE_BYTES,10*1024*1024),V=I(process.env.TOOLS_MAX_FETCH_BYTES,5*1024*1024),Ge=I(process.env.TOOLS_BINARY_PROBE_TIMEOUT_MS,5e3);function H(e,t){return Object.assign(Error(e),{errorKind:t})}const U=a(`@x-otto/tools:binary`),Ke=256*1024;async function qe(e,t=[`--version`]){try{return(await Je(e,t,{timeout:Ge})).status===0}catch{return!1}}function Je(e,t,n){return new Promise((r,i)=>{let a=F(e,t,{stdio:[`ignore`,`ignore`,`pipe`]}),o=``;a.stderr?.on(`data`,e=>{o+=e.toString()}),n?.timeout&&setTimeout(()=>{a.kill(),i(Error(`timeout`))},n.timeout),a.on(`close`,e=>r({status:e,stderr:o})),a.on(`error`,i)})}async function Ye(e,t=e){let n=E.platform()===`win32`?`.exe`:``,r=d(t,e)+n;try{if(D(r))return r}catch{U.debug(`Error checking existence of ${r}, will try PATH lookup: %s`,r)}return await qe(e)?e:null}const Xe=`tool-download`;async function Ze(e,t,n){if(!t){U.warn(`No checksum configured for %s — integrity not verified`,n);return}let r=Se(`sha256`);r.update(await k(e));let i=r.digest(`hex`);if(i.toLowerCase()!==t.toLowerCase())throw H(`Checksum mismatch for ${n}: expected ${t}, got ${i}`,`validation`)}async function Qe(e,t,n){let r=async(t,n)=>{try{let{status:e,stderr:r}=await Je(t,n);if(e!==0)throw Error(r?.trim()||`exit code ${e}`)}catch(t){throw H(`Failed to extract ${e}: ${t instanceof Error?t.message:String(t)}`,`io`)}};if(e.endsWith(`.tar.gz`)||e.endsWith(`.tgz`)){await r(`tar`,[`xzf`,t,`-C`,n]);return}if(e.endsWith(`.zip`)){E.platform()===`win32`?await r(`powershell`,[`-NoProfile`,`-Command`,`Expand-Archive -LiteralPath '${t}' -DestinationPath '${n}' -Force`]):await r(`unzip`,[`-o`,`-q`,t,`-d`,n]);return}throw H(`Unsupported archive format: ${e}`,`validation`)}async function $e(e,t,n){let i=Date.now()+Ue,a={prefix:Xe,timeoutMs:3e5};for(;;){if(await r(e,t,a))return!0;if(D(n)||Date.now()>i)return!1;await ke(200)}}async function et(e){let{name:t,cacheDir:n,url:r,checksum:i}=e,a=E.platform(),o=f(),s=t+(a===`win32`?`.exe`:``),c=P(o,n),l=P(c,s),u=Ce(),d=P(o,`${n}-${u}.download`),p=P(o,`${n}-extract-${u}`);if(O(o,{recursive:!0}),await $e(o,n,l))try{if(D(l))return;let e=await fetch(r,{headers:{"User-Agent":je},signal:AbortSignal.timeout(Ue)});if(!e.ok||!e.body)throw H(`Failed to download: ${e.status}`,`network`);let t=ue(d);await Oe(De.fromWeb(e.body),t),await Ze(d,i,r),O(c,{recursive:!0}),O(p,{recursive:!0}),await Qe(r,d,p);let n=tt(p,s);if(!n)throw H(`Binary ${s} not found in archive`,`not_found`);ce(n,l),a!==`win32`&&oe(l,493)}catch(e){return U.warn(e),e instanceof Error?e:Error(String(e))}finally{he(d,{force:!0}),he(p,{recursive:!0,force:!0}),await ee(o,n,{prefix:Xe})}}function tt(e,t){let n=[e];for(;n.length>0;){let e=n.pop(),r;try{r=pe(e,{encoding:`utf-8`})}catch{continue}for(let i of r){let r=M(e,i);try{let e=ge(r);if(e.isDirectory())n.push(r);else if(e.isFile()&&i===t)return r}catch{continue}}}}var W=class{get cacheDir(){return`${this.name}-${this.version}`}projectRoot;downloadTask=null;lastDownloadError;processTracker;constructor(e,t){this.projectRoot=e,this.processTracker=t}resolveChecksum(){}async ensure(){let e=await Ye(this.name,this.cacheDir);if(e)return e;if(Me)throw H(`${this.name} not found and offline mode is enabled`,`not_found`);this.downloadTask||=this.download().finally(()=>{this.downloadTask=null}),await this.downloadTask;let t=await Ye(this.name,this.cacheDir);if(!t){let e=this.lastDownloadError,t=H(`${this.name}: download failed${e?` — ${e.message}`:` (binary not found)`}`,`not_found`);throw e&&(t.cause=e),t}return t}async download(){let e=this.resolveUrl();p(e,`No download URL for ${this.name}`),this.lastDownloadError=await et({name:this.name,cacheDir:this.cacheDir,url:e,checksum:this.resolveChecksum()})}async execute(e,t){let n=await this.ensure(),r=t?.maxBuffer;return new Promise((i,a)=>{let o=F(n,e,{cwd:t?.cwd,env:t?.env,timeout:t?.timeout,signal:t?.signal});this.processTracker?.registerChild({command:n,args:e,owner:{type:`agent-tool`,id:`binary`},category:`tool`,lifecycle:`evictable`,cwd:typeof t?.cwd==`string`?t.cwd:void 0},o);let s=[],c=[],l=0,u=0,d=!1,f,p=()=>{try{o.kill(`SIGTERM`)}catch{}f=setTimeout(()=>{try{o.kill(`SIGKILL`)}catch{}},2e3),f.unref?.()};o.stdout.on(`data`,e=>{if(!d){if(r!==void 0&&l+e.byteLength>r){let t=r-l;t>0&&(s.push(e.subarray(0,t)),l=r),d=!0,p();return}s.push(e),l+=e.byteLength}}),o.stderr.on(`data`,e=>{if(u>=Ke)return;let t=Ke-u;e.byteLength>t?(c.push(e.subarray(0,t)),u=Ke):(c.push(e),u+=e.byteLength)}),o.on(`error`,e=>{f&&clearTimeout(f),a(e)}),o.on(`close`,e=>{if(f&&clearTimeout(f),!d&&e!==0)return a(Error(`Process exited with code ${e}: ${Buffer.concat(c).toString()}`));i({stdout:Buffer.concat(s).toString(),stderr:Buffer.concat(c).toString(),exitCode:d?0:e,truncated:d})})})}};function nt(e,t,n){return`https://github.com/${e}/releases/download/v${t}/${n}`}const rt={"fd-v10.4.2-aarch64-apple-darwin.tar.gz":`623dc0afc81b92e4d4606b380d7bc91916ba7b97814263e554d50923a39e480a`,"fd-v10.4.2-aarch64-unknown-linux-gnu.tar.gz":`6c51f7c5446b3338b1e401ff15dc194c590bb2fa64fd43ff3278300f073adec5`,"fd-v10.4.2-x86_64-unknown-linux-gnu.tar.gz":`def59805cd14b5651b68990855f426ad087f3b96881296d963910431ba3143c8`,"fd-v10.4.2-aarch64-pc-windows-msvc.zip":`4f9110c2d5b33a7f760bfa5510f4c113d828109f7277d421b1053a9943c0fc92`,"fd-v10.4.2-x86_64-pc-windows-msvc.zip":`b2816e506390a89941c63c9187d58a3cc10e9a55f2ef0685f9ea0eccaf7c98c8`};var it=class extends W{name=`fd`;version=`10.4.2`;repository=`sharkdp/fd`;constructor(e,t){super(e,t)}resolveAsset(){let e=T.platform(),t=T.arch();switch(e){case`darwin`:return t===`arm64`?`fd-v${this.version}-aarch64-apple-darwin.tar.gz`:`fd-v${this.version}-x86_64-apple-darwin.tar.gz`;case`linux`:return t===`arm64`?`fd-v${this.version}-aarch64-unknown-linux-gnu.tar.gz`:`fd-v${this.version}-x86_64-unknown-linux-gnu.tar.gz`;case`win32`:return t===`arm64`?`fd-v${this.version}-aarch64-pc-windows-msvc.zip`:`fd-v${this.version}-x86_64-pc-windows-msvc.zip`}throw Error(`Unsupported platform/architecture: ${e}/${t}`)}resolveUrl(){return nt(this.repository,this.version,this.resolveAsset())}resolveChecksum(){return rt[this.resolveAsset()]}};const at=(e,t)=>new it(e,t);function ot(e,t,n){return`https://github.com/${e}/releases/download/${t}/${n}`}const st={"ripgrep-15.1.0-aarch64-apple-darwin.tar.gz":`378e973289176ca0c6054054ee7f631a065874a352bf43f0fa60ef079b6ba715`,"ripgrep-15.1.0-x86_64-apple-darwin.tar.gz":`64811cb24e77cac3057d6c40b63ac9becf9082eedd54ca411b475b755d334882`,"ripgrep-15.1.0-aarch64-unknown-linux-gnu.tar.gz":`2b661c6ef508e902f388e9098d9c4c5aca72c87b55922d94abdba830b4dc885e`,"ripgrep-15.1.0-x86_64-unknown-linux-musl.tar.gz":`1c9297be4a084eea7ecaedf93eb03d058d6faae29bbc57ecdaf5063921491599`,"ripgrep-15.1.0-aarch64-pc-windows-msvc.zip":`00d931fb5237c9696ca49308818edb76d8eb6fc132761cb2a1bd616b2df02f8e`,"ripgrep-15.1.0-x86_64-pc-windows-msvc.zip":`124510b94b6baa3380d051fdf4650eaa80a302c876d611e9dba0b2e18d87493a`};var ct=class extends W{name=`rg`;repository=`BurntSushi/ripgrep`;version=`15.1.0`;constructor(e,t){super(e,t)}resolveAsset(){let e=T.platform(),t=T.arch();switch(e){case`darwin`:return t===`arm64`?`ripgrep-${this.version}-aarch64-apple-darwin.tar.gz`:`ripgrep-${this.version}-x86_64-apple-darwin.tar.gz`;case`linux`:return t===`arm64`?`ripgrep-${this.version}-aarch64-unknown-linux-gnu.tar.gz`:`ripgrep-${this.version}-x86_64-unknown-linux-musl.tar.gz`;case`win32`:return t===`arm64`?`ripgrep-${this.version}-aarch64-pc-windows-msvc.zip`:`ripgrep-${this.version}-x86_64-pc-windows-msvc.zip`}throw Error(`Unsupported platform/architecture: ${e}/${t}`)}resolveUrl(){return ot(this.repository,this.version,this.resolveAsset())}resolveChecksum(){return st[this.resolveAsset()]}};const lt=(e,t)=>new ct(e,t),ut=a(`@x-otto/tools:binary-executor-registry`);var dt=class{binaries=new Map;has(e){return this.binaries.has(e)}get(e){return this.binaries.get(e)}register(e){this.binaries.has(e.name)?ut.warn(`Tool ${e.name} is already registered, skipping`):this.binaries.set(e.name,e)}unregister(e){this.binaries.has(e)?this.binaries.delete(e):ut.warn(`Tool ${e} is not registered, skipping`)}async ensureAll(){for(let e of this.binaries.values())e instanceof W&&await e.ensure()}async ensure(e){let t=this.binaries.get(e);if(!t)throw Error(`Tool ${e} not found in registry`);return t instanceof W&&await t.ensure(),t}dispose(){this.binaries.clear()}};const ft=(e,t)=>{let n=new dt;return n.register(at(e,t)),n.register(lt(e,t)),n};function pt(e){return typeof e==`function`?e():e}const mt=()=>({chunks:Buffer.alloc(0),stdout:Buffer.alloc(0),stderr:Buffer.alloc(0),exitCode:-1,timedOut:!1}),ht=(e,t)=>{try{process.kill(-e,t)}catch{try{process.kill(e,t)}catch{}}},gt=new class{name=`local`;execute(e,t,n,r){let{cwd:a,env:o,timeout:s,signal:c}=n;return new Promise((n,l)=>{if(c?.aborted){l(Error(`Process execution aborted before start`));return}let u=mt(),d=[],f=[],p=[],m,h=()=>{let e=y.pid;if(e!==void 0){if(process.platform===`win32`){F(`taskkill`,[`/F`,`/T`,`/PID`,`${e}`],{stdio:`ignore`,detached:!0});return}ht(e,`SIGTERM`),m=setTimeout(()=>ht(e,`SIGKILL`),2e3)}},g=setTimeout(()=>{u.timedOut=!0,h()},s),_=()=>h();c?.addEventListener(`abort`,_,{once:!0});let v=()=>{clearTimeout(g),m&&clearTimeout(m),c?.removeEventListener(`abort`,_)},y=F(e,t,{cwd:a,env:i(o),stdio:[`ignore`,`pipe`,`pipe`],detached:process.platform!==`win32`});y.stdout?.on(`data`,e=>{d.push(e),p.push(e),r?.(e,`stdout`)}),y.stderr?.on(`data`,e=>{f.push(e),p.push(e),r?.(e,`stderr`)}),y.on(`error`,e=>{v(),l(e)}),y.on(`close`,e=>{u.exitCode=e??-1,u.stdout=Buffer.concat(d),u.stderr=Buffer.concat(f),u.chunks=Buffer.concat(p),v(),u.timedOut?l(Error(`Process timed out after ${s}ms`)):n(u)})})}};function _t(e,t,n){let r=e,a=t,o=n.cwd,s=n.env;if(n.sandbox){let e=n.projectRoot??o,t=n.sandbox.wrap({command:r,args:a,options:{timeout:0,cwd:o,env:s,projectRoot:e},projectRoot:e});r=t.command,a=t.args,o=t.options.cwd??o,s=t.options.env}let c=n.onOutputChunk!==void 0,l,u;if(c){u=de(n.logPath,`a`),l=F(r,a,{cwd:o,env:i(s),stdio:[`ignore`,`pipe`,`pipe`],detached:process.platform!==`win32`});let e=(e,t)=>{if(u!==void 0)try{ae(u,e)}catch{}n.onOutputChunk?.(e,t)};l.stdout?.on(`data`,t=>e(t,`stdout`)),l.stderr?.on(`data`,t=>e(t,`stderr`)),l.once(`exit`,()=>{if(u!==void 0){try{se(u)}catch{}u=void 0}}),l.stdout?.unref?.(),l.stderr?.unref?.()}else{let e=de(n.logPath,`a`);try{l=F(r,a,{cwd:o,env:i(s),stdio:[`ignore`,e,e],detached:process.platform!==`win32`})}finally{se(e)}}l.once(`error`,e=>{try{ae(n.logPath,`\n[otto] background spawn error: ${e.message}\n`)}catch{}}),l.unref();let d=l.pid;if(d===void 0)throw Error(`Failed to spawn background process (no pid)`);return{child:l,pid:d}}function vt(e,t,n){let r=n.sandbox;if(r){let i=n.projectRoot??n.cwd??process.cwd(),a=r.wrap({command:e,args:t,options:n,projectRoot:i});e=a.command,t=a.args,n=a.options}let{cwd:i,env:a,timeout:o,signal:s,onProgress:c}=n;return(pt(n.backend)??gt).execute(e,t,{cwd:i,env:a,timeout:o,signal:s},c)}const yt=new Set([2,126,127]),bt={seatbelt:{fatal:[/operation not permitted/i,/permission denied/i,/deny file-write/i,/sandbox.*denied/i,/not allowed by sandbox/i],benign:[]},bubblewrap:{fatal:[/bwrap.*(?:denied|refused|not permitted)/i,/operation not permitted/i,/read-only file system/i,/no such file or directory: \/proc/i],benign:[/^bwrap: warning/i,/^bwrap: creating new namespace/i]},"windows-acl":{fatal:[/access is denied/i,/eacces/i,/erofs/i,/file.*denied/i],benign:[]},unknown:{fatal:[/permission denied/i,/eacces/i,/eperm/i,/erofs/i,/read-only file system/i,/seccomp/i,/landlock/i,/operation not permitted/i],benign:[]}};function xt(e,t,n=`unknown`){if(e!==null&&yt.has(e))return{denied:!1,backend:n};if(e===159)return{denied:!0,reason:`exit code 159 (128+SIGSYS, seccomp violation)`,backend:n};let r=St(t,bt[n].benign),i=Ct(r,bt[n].fatal);if(i)return{denied:!0,reason:`${n} dialect: ${i}`,backend:n};let a=Ct(r,bt.unknown.fatal);return a?{denied:!0,reason:`generic signature: ${a}`,backend:`unknown`}:{denied:!1,backend:n}}function St(e,t){return t.length===0?e:e.split(`
2
+ `).filter(e=>!t.some(t=>t.test(e))).join(`
3
+ `)}function Ct(e,t){for(let n of t){let t=n.exec(e);if(t)return t[0]}}const wt=/\b(https?|wss?):\/\/([-\w.]+)(?::(\d{2,5}))?(\/[^\s)\],."']*)?/i,Tt=/\b(?:listening on\s+(?:port\s+)?|(?:running\s+)?at\s+:|on\s+:|port[:\s]+)(\d{2,5})\b/i;function Et(e){return/^wss?$/i.test(e)?`ws`:`http`}function Dt(e){let t=e.match(wt);if(t){let[e,n,,r]=t,i=r?Number(r):void 0;return{url:e,port:i!==void 0&&i>0&&i<=65535?i:void 0,protocol:Et(n)}}let n=e.match(Tt);if(n){let e=Number(n[1]);if(e>0&&e<=65535)return{port:e,protocol:`tcp`}}return null}const Ot=e.object({command:e.string().describe(`Shell command to execute`),targetDir:e.string().optional().describe(`Working directory (relative to project root)`),timeout:e.number().int().min(1e3).optional().describe(`Timeout (ms), default 30000`),run_in_background:e.boolean().optional().describe(`Run as a long-lived background process (dev servers, watchers). Returns immediately with a background id.`)}),kt=a(`@x-otto/tools:bash`);function At(e,t,n,r,i){return{name:`bash`,description:`Execute a shell command and return stdout/stderr. Default timeout is 30 seconds.`,parameters:Ot,async execute({params:a,signal:o,id:s,sessionId:c}){let l=P(e,a.targetDir??`.`),u=a.timeout??B,d=typeof n==`function`?n({command:a.command}):n,f=pt(i);return a.run_in_background?!r||!c?{content:[{type:`text`,text:`Background execution is unavailable in this context (no process registry / session). Re-run without run_in_background, or run a short-lived foreground command.`}],isError:!0,errorKind:`runtime`}:Nt(a.command,l,e,d,r,c,s):await Lt(a.command,l,e,u,t,o,d,f)}}}function jt(e){let t=e.match(/(?:--port[ =]|-p\s+|localhost:|0\.0\.0\.0:|127\.0\.0\.1:|http\.server\s+)(\d{2,5})\b/i);if(t){let e=Number(t[1]);if(e>0&&e<=65535)return e}}function Mt(e){let t=``;return n=>{t+=n.toString(`utf-8`);let r;for(;(r=t.indexOf(`
4
+ `))!==-1;){let n=t.slice(0,r);t=t.slice(r+1);let i=Dt(n);i&&e(i)}}}function Nt(e,t,n,r,i,a,o){let c=`bg_${a}_${o}`,l=s();ne(Pt,Date.now());try{let o=Mt(e=>{i.updateDetectedService(c,e)}),{child:s,pid:u}=_t(`sh`,[`-c`,e],{cwd:t,projectRoot:n,sandbox:r,logPath:l,onOutputChunk:e=>o(e)}),d=jt(e);try{i.register({id:c,sessionId:a,child:s,command:e,cwd:t,logPath:l,port:d})}catch(e){try{process.kill(-u,`SIGKILL`)}catch{try{s.kill(`SIGKILL`)}catch{}}throw e}return{content:[{type:`text`,text:`Started in background.\nid: ${c}\npid: ${u}\nlog: ${l}${d?`\nlikely port: ${d}`:``}\n\nRead output with bash_output(background_id="${c}"); stop it with kill_shell(background_id="${c}"). It is auto-killed when the session ends.`}],details:{backgroundProcessId:c,pid:u,logPath:l,port:d}}}catch(e){let t=e instanceof Error?e.message:String(e);return kt.warn({err:t},`Failed to start background process`),{content:[{type:`text`,text:`Failed to start background process: ${t}`}],isError:!0,errorKind:`runtime`}}}const Pt=60*6e4;function Ft(e){return e?e.includes(`seatbelt`)?`seatbelt`:e.includes(`bubblewrap`)||e.includes(`bwrap`)?`bubblewrap`:`unknown`:`unknown`}function It(e){let t=e.trim();return t?(t.length>2048?`${t.slice(0,2048)}…`:t).split(`
5
+ `).slice(0,8).join(`
6
+ `):``}async function Lt(e,t,n,r,i,a,c,u){let d=z*2,f=[],p=0,m=0,h=0,g=0,_,v,y=()=>{if(!i)return;let e=w(Buffer.concat(f).toString(`utf-8`),{maxLines:R,maxBytes:z});i({content:[{type:`text`,text:e.content||``}],details:{truncation:x(e),outputPath:_}})},b=await vt(`sh`,[`-c`,e],{timeout:r,signal:a,cwd:t,sandbox:c,projectRoot:n,backend:u,onProgress:e=>{if(f.push(e),p+=e.byteLength,m+=e.byteLength,p>z&&!_){ne(Pt,Date.now()),te(6048e5,Date.now());let e=o();try{O(j(e),{recursive:!0})}catch{e=s()}_=e,v=ue(e),v.on(`error`,e=>{kt.warn({err:e.message},`bash overflow log write failed; disabling overflow log`),v=void 0,_=void 0});for(let e of f)v.write(e)}else v&&v.write(e);for(;m>d&&f.length>1;){let e=f.shift();e&&(m-=e.byteLength)}h+=e.byteLength;let t=Date.now();h>=4096&&t-g>=100&&(h=0,g=t,y())}});v?.end();let ee=Buffer.concat(f).toString(`utf-8`),S=w(ee,{maxLines:R,maxBytes:z}),C=S.content||`(no output)`,T={truncation:x(S),outputPath:_};if(S.truncated){let e=S.totalLines-S.outputLines+1,t=S.totalLines,n=_?` Full output: ${_}`:``;if(S.lastLinePartial){let e=l(Buffer.byteLength(ee.split(`
7
+ `).pop()||``,`utf-8`));C+=`\n\n(Showing last ${l(S.outputBytes)} of line ${t} (line is ${e}).${n})`}else S.truncatedBy===`lines`?C+=`\n\n(Showing lines ${e}-${t} of ${S.totalLines}.${n})`:C+=`\n\n(Showing lines ${e}-${t} of ${S.totalLines} (${l(z)} limit).${n})`}if(b.exitCode!==0){if(C+=`\n\nCommand exited with code ${b.exitCode}`,c){let e=Ft(c.name),t=xt(b.exitCode,b.stderr.toString(`utf-8`),e);if(t.denied){let e=It(b.stderr.toString(`utf-8`));return C+=`\n\n[sandbox denied] ${t.reason??`sandbox policy`}\n${e}`,{content:[{type:`text`,text:C}],isError:!0,errorKind:`sandbox-denied`,details:{...T,sandboxBackend:t.backend,denialReason:t.reason}}}}return{content:[{type:`text`,text:C}],isError:!0,errorKind:`runtime`,details:T}}else return{content:[{type:`text`,text:C}],details:T}}const Rt=e.object({background_id:e.string().describe(`Background process id returned by bash with run_in_background:true`),block:e.boolean().optional().describe(`Wait until the process exits before returning (default false)`),timeout_ms:e.number().int().min(0).max(6e5).optional().describe(`Max wait when block=true (ms), default from TOOLS_EXECUTE_TIMEOUT_MS (30000)`)}),zt=e=>new Promise(t=>setTimeout(t,e));async function Bt(e){if(!e.logPath)return`(no output captured)`;let t;try{t=await k(e.logPath,`utf-8`)}catch{return`(output log unavailable)`}return w(t,{maxLines:R,maxBytes:z}).content||`(no output yet)`}function Vt(e){return{name:`bash_output`,description:`Read accumulated output and status of a background process.`,parameters:Rt,readonly:!0,async execute({params:t,sessionId:n}){let r=t.background_id,i=n?e.getForSession(n,r):e.get(r);if(!i)return{content:[{type:`text`,text:`No background process with id "${r}".`}],isError:!0,errorKind:`not_found`};let a=Date.now()+(t.timeout_ms??B);for(;t.block&&i.status===`running`&&Date.now()<a;){await zt(200);let t=e.get(r);if(!t)return{content:[{type:`text`,text:`[status: untracked]\nBackground process "${r}" is no longer tracked (likely evicted at the process limit).`}],details:{status:`untracked`,logPath:i.logPath}};i=t}let o=await Bt(i);return{content:[{type:`text`,text:`${i.status===`running`?`[status: running]`:`[status: ${i.status}${i.exitCode===void 0?``:`, exit ${i.exitCode}`}]`}\n${o}`}],details:{status:i.status,exitCode:i.exitCode,logPath:i.logPath}}}}}const Ht=e.object({background_id:e.string().describe(`Background process id (returned by bash run_in_background) to stop`)});function Ut(e){return{name:`kill_shell`,description:`Stop a background process.`,parameters:Ht,async execute({params:t,sessionId:n}){let r=t.background_id,i=n?e.getForSession(n,r):e.get(r);return i?i.status!==`running`&&i.status!==`killing`?{content:[{type:`text`,text:`Background process "${r}" already ${i.status}.`}]}:(await e.kill(r),{content:[{type:`text`,text:`Stopped background process "${r}".`}]}):{content:[{type:`text`,text:`No background process with id "${r}".`}],isError:!0,errorKind:`not_found`}}}}const Wt=a(`@x-otto/tools:find`),Gt=e.object({pattern:e.string().describe(`File name pattern with optional wildcards (*, ?), e.g. "*.ts", "data-??.json"`),path:e.string().optional().describe(`Search starting path (default is project root)`),limit:e.number().int().min(1).max(1e3).optional().default(1e3).describe(`Maximum number of results to return`)});function Kt(e,t={}){return{name:`find`,description:`Search for files by glob pattern. Returns matching file paths relative to the search directory. Respects .gitignore. Output is truncated to ${R} results or ${z/1024}KB (whichever is hit first).`,parameters:Gt,pathParams:[`path`],readonly:!0,async execute({params:n}){let r=v(P(e,n.path??`.`)),i=n.limit??R;if(!await c(r))throw H(`Search path does not exist: ${n.path}`,`not_found`);if(!await m(r))throw H(`Search path is not a directory: ${n.path}`,`validation`);return await Xt(r,n.pattern,i,t.glob,t.binaryExecutorRegistry)}}}const qt=new Set([`node_modules`,`.git`,`dist`,`build`,`.next`,`target`,`vendor`]);async function Jt(e,t,n){let r=[`--glob`,`--color=never`,`--hidden`,`--max-results`,String(n)],i=new Set,a=M(t,`.gitignore`);await c(a)&&i.add(a);try{let e=Date.now(),n=ve(`**/`,{cwd:t,exclude:e=>qt.has(e)});for await(let r of n){let n=M(t,r,`.gitignore`);if(await c(n)&&i.add(n),i.size>=200||Date.now()-e>1500){Wt.warn({normalizedSearchDir:t,found:i.size,elapsedMs:Date.now()-e},`find: .gitignore discovery hit time/count budget, stopping early (best-effort)`);break}}}catch{}for(let e of i)r.push(`--ignore-file`,e);return r.push(`--`,e,t),r}function Yt(e,t,n,r=[]){let i=e.length>=t,a=S(e.join(`
8
+ `),{maxLines:R,maxBytes:z}),o=a.content,s={},c=[];i&&(c.push(n),s.resultLimitReached=t),a.truncated&&(c.push(`${l(z)} limit reached`),s.truncation=x(a));for(let e of r)c.push(e.text),s[e.detailKey]=e.detailValue;return c.length>0&&(o+=`\n\n(${c.join(`. `)})`),{content:[{type:`text`,text:o}],details:s}}async function Xt(e,t,n,r,i){if(typeof r==`function`){Wt.debug(`Using custom glob implementation for find tool`);let i=await r(t,e,{ignore:[`**/node_modules/**`,`**/.git/**`],limit:n});return i.length===0?{content:[{type:`text`,text:`No files found matching pattern`}]}:Yt(i.map(t=>t.startsWith(e)?t.slice(e.length+1):N(e,t)),n,`${n} results limit reached`)}Wt.debug(`No custom glob provided, using fd binary for find tool`);let a=await i?.ensure(`fd`);if(!a)throw H(`Find tool requires a glob implementation or fd binary available`,`runtime`);let o=await Jt(t,e,n),s=await a.execute(o,{maxBuffer:z*4});if(s.exitCode!==0)return{content:[{type:`text`,text:s.stderr?.trim()||`fd exited with code ${s.exitCode}`}]};let c=s.stdout?.trim()||``;if(!c)return{content:[{type:`text`,text:`No files found matching pattern`}]};let l=c.split(`
9
+ `),u=[];for(let t of l){let n=t.replace(/\r$/,``).trim();if(!n)continue;let r=n.endsWith(`/`)||n.endsWith(`\\`),i=n;i=n.startsWith(e)?n.slice(e.length+1):N(e,n),r&&!i.endsWith(`/`)&&(i+=`/`),u.push(i)}let d=s.truncated?[{text:`output exceeded buffer limit; results are incomplete (refine the pattern)`,detailKey:`bufferTruncated`,detailValue:!0}]:[];return Yt(u,n,`${n} results limit reached. Use limit=${n*2} for more, or refine pattern`,d)}const Zt=z*8,Qt=e.object({pattern:e.string().describe(`Search pattern (regex or literal string)`),path:e.string().optional().describe(`Directory or file to search (default: current directory)`),glob:e.string().optional().describe(`Filter files by glob pattern, e.g. "*.ts" or "**/*.spec.ts"`),ignoreCase:e.boolean().optional().describe(`Case-insensitive search (default: false)`),literal:e.boolean().optional().describe(`Treat pattern as literal string instead of regex (default: false)`),context:e.number().int().min(0).optional().describe(`Number of lines to show before and after each match (default: 0)`),limit:e.number().int().min(1).optional().describe(`Maximum number of matches to return (default: 100)`)});function $t(e,t){return{name:`grep`,description:`Search file contents for pattern matches. Returns matching lines with file paths and line numbers. Respects .gitignore. Truncated to 100 matches or ${z/1024}KB. Long lines truncated to 500 chars.`,parameters:Qt,pathParams:[`path`],readonly:!0,async execute({params:n}){let r=v(P(e,n.path??`.`));if(!await c(r))throw H(`Search path does not exist: ${n.path??`.`}`,`not_found`);return await on(n.pattern,r,e,n.glob,n.literal??!1,n.ignoreCase??!1,n.context??0,n.limit??100,t.binaryToolExecutorRegistry,t.processTracker)}}}function en(e,t,n,r,i,a,o){let s=[`--json`,`--line-number`,`--color=never`,`--hidden`];return n&&s.push(`--ignore-case`),r&&s.push(`--fixed-strings`),i&&s.push(`--glob`,i),a!=null&&s.push(`--max-count`,String(a)),o!=null&&o>0&&s.push(`--context`,String(o)),s.push(`--`,e,t),s}function tn(e){let t=[];for(let n of e.split(`
10
+ `))if(n.trim())try{let e=JSON.parse(n);if(e.type===`match`||e.type===`context`){let n=e.data;t.push({kind:e.type,path:n.path?.text??``,lineNumber:n.line_number??0,text:n.lines?.text?.replace(/\n$/,``)??``})}}catch{}return t}function nn(e,t,n){return e.startsWith(t)?N(t,e):N(n,e)}function rn(e,t,n,r){let i=e.reduce((e,t)=>e+(t.kind===`match`?1:0),0),a=[],o=0,s=0,c=null,l=0,u=!1;for(let i of e){if(i.kind===`match`){if(o>=r)break;o++,o>=r&&(u=!0)}else if(u&&(i.path!==c||i.lineNumber!==l+1))break;c!==null&&(i.path!==c||i.lineNumber>l+1)&&a.push(`--`);let{text:e}=C(i.text,500);i.text.length>500&&s++;let d=i.kind===`match`?`:`:`-`,f=nn(i.path,t,n);a.push(`${f}${d}${i.lineNumber}${d} ${e}`),c=i.path,l=i.lineNumber}return{text:a.join(`
11
+ `),totalMatches:i,matchLimitReached:i>r,linesTruncated:s}}async function an(e,t,n,r){return new Promise(i=>{let a=F(e,t,{stdio:`pipe`});r?.registerChild({command:e,args:t,owner:{type:`agent-tool`,id:`ripgrep`},category:`tool`,lifecycle:`evictable`},a);let o=[],s=0,c=!1;a.stdout.on(`data`,e=>{if(!c){if(s+e.byteLength>n){let t=n-s;t>0&&(o.push(e.subarray(0,t)),s=n),c=!0,a.kill();return}o.push(e),s+=e.byteLength}}),a.stderr.on(`data`,()=>{}),a.on(`error`,e=>{i({stdout:``,exitCode:-999,error:e.message,streamTruncated:!1})}),a.on(`close`,e=>{i({stdout:Buffer.concat(o).toString(`utf-8`),exitCode:c?0:e,streamTruncated:c})})})}async function on(e,t,n,r,i,a,o,s,c,u){if(!c)throw H(`Binary tool executor registry is not available`,`runtime`);let d=c.get(`rg`);if(!d)throw H(`ripgrep (rg) executor is not available`,`runtime`);let f;f=d instanceof W?await d.ensure():`rg`;let p=en(e,t,a,i,r,s,o),m=await an(f,p,Zt,u);if(m.exitCode===-999)throw H(`Failed to execute ripgrep: ${m.error??`unknown error`}`,`io`);if(m.exitCode===2&&!m.streamTruncated)throw H(`ripgrep error while searching for "${e}"`,`io`);let h=rn(tn(m.stdout),n,t,s);if(h.totalMatches===0)return{content:[{type:`text`,text:`No matches found.`}]};let g=S(h.text,{maxLines:R,maxBytes:z}),_=g.content,v={truncation:x(g)},y=[];return h.matchLimitReached&&(y.push(`${s} match limit reached — use a more specific pattern or increase limit`),v.matchLimitReached=s),g.truncated&&y.push(`Output truncated (${l(z)} limit)`),m.streamTruncated&&(y.push(`search output exceeded buffer limit; results are incomplete (refine the pattern)`),v.streamTruncated=!0),h.linesTruncated>0&&(y.push(`${h.linesTruncated} long lines truncated to 500 chars`),v.linesTruncated=h.linesTruncated),y.length>0&&(_+=`
12
+
13
+ (`+y.join(`. `)+`)`),{content:[{type:`text`,text:_}],details:v}}const sn=e.object({path:e.string().describe(`Path to the file to read (relative or absolute)`),limit:e.number().int().min(1).optional().describe(`Maximum number of lines to read (text files only)`),offset:e.number().int().min(1).optional().describe(`Starting line number (1-based, text files only)`)});function cn(e){return{name:`read`,description:`Read file content. For text files, can specify line range with limit and offset.`,parameters:sn,pathParams:[`path`],resolvePath:t=>P(e,t),readonly:!0,async execute({params:t}){let n=v(P(e,t.path));if(!await c(n))throw H(`File not found: ${t.path}`,`not_found`);if(!await h(n))throw H(`Not a file: ${t.path}`,`validation`);let r=await g(n);return r?.startsWith(`image/`)&&r!==`image/svg+xml`?un(n,t.path,r):fn(n,t.path,t.offset,t.limit)}}}const ln=n;async function un(e,n,r){let i=await k(e);if(i.length>ln)return{content:[{type:`text`,text:`[image omitted: ${n} is ${i.length} bytes, exceeds 5MB limit for inline image context — view a downscaled copy or crop the region you need]`}],details:{path:e,type:`image`,size:i.length,mime:r||`application/octet-stream`}};if(i.length>0){let a=b(i.toString(`base64`),r||``);if(a&&(a.width>t||a.height>t))return{content:[{type:`text`,text:`[image omitted: ${n} is ${a.width}x${a.height}px, exceeds Anthropic per-image dimension hard limit of ${t}px — view a downscaled copy or crop the region you need]`}],details:{path:e,type:`image`,size:i.length,mime:r||`application/octet-stream`,dimensions:a}}}let a=i.toString(`base64`);return{content:[{type:`text`,text:`Read image file ${n} (${i.length} bytes)`},{type:`image`,mime:r||`application/octet-stream`,source:`data:${r||`application/octet-stream`};base64,${a}`}],details:{path:e,type:`image`,size:i.length,mime:r||`application/octet-stream`}}}async function dn(e,t,n,r){let i=le(e,`utf-8`),a=Ne({input:i,crlfDelay:1/0}),o=[],s=0,c=0,l=!1;try{for await(let e of a){if(s>=t){if(o.length>=n||c>=r){l=!0;break}o.push(e),c+=Buffer.byteLength(e,`utf-8`)+1}s++}}finally{a.close(),i.destroy()}return o.length===0&&t>=s?{lines:o,hasMore:!1,totalLinesIfBeyond:s}:{lines:o,hasMore:l}}async function fn(e,t,n=1,r=R,i=z){let a=n?Math.max(0,n-1):0,o=a+1,s=await dn(e,a,r,i);if(s.totalLinesIfBeyond!==void 0)return{content:[{type:`text`,text:`Offset ${n} is beyond end of file (${s.totalLinesIfBeyond} lines total)`}]};let c=S(s.lines.join(`
14
+ `),{maxBytes:z,maxLines:R}),u;if(c.firstLineExceedsLimit)u=`(Line ${o} is ${l(Buffer.byteLength(s.lines[0]??``,`utf-8`))}, exceeds ${l(i)} limit. Use bash: sed -n '${o}p' ${e} | head -c ${i})`;else if(c.truncated){let e=o+c.outputLines-1,t=e+1;u=c.content,c.truncatedBy===`lines`?u+=`\n\n(Showing lines ${o}-${e}. Use offset=${t} to continue)`:u+=`\n\n(Showing lines ${o}-${e} (${l(i)} limit). Use offset=${t} to continue)`}else if(s.hasMore){let e=o+s.lines.length-1+1;u=c.content,u+=`\n\n(More lines follow. Use offset=${e} to continue)`}else u=c.content;return{content:[{type:`text`,text:u}],details:{truncation:x(c)}}}const pn=e.object({path:e.string().describe(`Path to the file to write (relative or absolute)`),content:e.string().describe(`Content to write to the file`),createDirectories:e.boolean().optional().default(!0).describe(`Whether to create parent directories if they do not exist`)});function mn(e){return{name:`write`,description:`Create or overwrite a file, automatically creating parent directories if they do not exist`,parameters:pn,pathParams:[`path`],resolvePath:t=>P(e,t),async execute({params:t,signal:n}){let r=v(P(e,t.path)),i=Buffer.byteLength(t.content,`utf-8`);if(i>We)throw H(`Content too large: ${i} bytes exceeds the write limit of ${We} bytes`,`validation`);if(t.createDirectories!==!1&&await _(j(r)),n.aborted)throw H(`Write operation was aborted`,`aborted`);return await hn(r,t.content,n)}}}async function hn(e,t,n){if(n.aborted)throw H(`Write operation was aborted`,`aborted`);let r=await gn(e);return await A(e,t,`utf-8`),{content:[{type:`text`,text:`Successfully ${r?`overwrote`:`created`} ${e}`}],details:{overwritten:r}}}async function gn(e){try{return await be(e),!0}catch{return!1}}const _n=`\x1B[0m`;function vn(e,t,n=4){let r=Fe(e,t),i=[],a=e.split(`
11
15
  `),o=t.split(`
12
16
  `),s=Math.max(a.length,o.length),c=String(s).length,l=1,u=1,d=!1,f;for(let e=0;e<r.length;e++){let t=r[e],a=t.value.split(`
13
- `);if(a[a.length-1]===``&&a.pop(),t.added||t.removed){f===void 0&&(f=u);for(let e of a)if(t.added){let t=String(u).padStart(c,` `);i.push(`+${t} ${e}${sn}`),u++}else{let t=String(l).padStart(c,` `);i.push(`-${t} ${e}${sn}`),l++}d=!0}else{let t=r[e+1],o=e<r.length-1&&(t.added||t.removed);if(d||o){let e=a,t=0,r=0;d||(t=Math.max(0,a.length-n),e=a.slice(t)),!o&&e.length>n&&(r=e.length-n,e=e.slice(0,n)),t>0&&(i.push(` ${``.padStart(c,` `)} ...`),l+=t,u+=t);for(let t of e){let e=String(l).padStart(c,` `);i.push(` ${e} ${t}${sn}`),l++,u++}r>0&&(i.push(` ${``.padStart(c,` `)} ...`),l+=r,u+=r)}else l+=a.length,u+=a.length;d=!1}}return{content:i.join(`
14
- `),firstChangedLine:f}}const ln=/[\u2018\u2019\u201A\u201B]/,un=/[\u201C\u201D\u201E\u201F]/,dn=/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/,fn=/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/;function pn(e){return ln.test(e)?`'`:un.test(e)?`"`:dn.test(e)?`-`:fn.test(e)?` `:e}function mn(e){let t=[],n=[],r=e.length,i=0;for(let a=0;a<=r;a++){if(a<r&&e[a]!==`
15
- `)continue;let o=e.slice(i,a),s=o.trimEnd().length;for(let e=0;e<s;e++)t.push(pn(o.charAt(e))),n.push(i+e);a<r&&(t.push(`
16
- `),n.push(a)),i=a+1}return{text:t.join(``),map:n}}function W(e,t){return t.length===0?0:e.split(t).length-1}const hn=Object.freeze({found:!1,index:-1,matchLength:0,occurrences:0,usedFuzzyMatch:!1});function gn(e,t){let n=e.indexOf(t);if(n!==-1&&t.length>0)return{found:!0,index:n,matchLength:t.length,occurrences:W(e,t),usedFuzzyMatch:!1};let{text:r,map:i}=mn(e),a=mn(t).text;if(a.length===0)return hn;let o=r.indexOf(a);if(o===-1)return hn;let s=i[o]??0;return{found:!0,index:s,matchLength:(i[o+a.length-1]??s)+1-s,occurrences:W(r,a),usedFuzzyMatch:!0}}const _n=e.object({path:e.string().describe(`Path to the file to edit (relative or absolute)`),oldContent:e.string().describe(`Exact existing text to locate (must be unique in the file). Provide it verbatim — no diff "-" prefixes.`),newContent:e.string().describe(`The full replacement text for oldContent. Do NOT include diff "+"/"-" prefixes. Example: to rename a-b → a_b, oldContent="a-b", newContent="a_b" (NOT "a-b,a_b").`)}).superRefine((t,n)=>{let r=t.oldContent,i=t.newContent;r===void 0&&n.addIssue({code:e.ZodIssueCode.custom,message:`Missing old content text.`,path:[`oldContent`]}),i===void 0&&n.addIssue({code:e.ZodIssueCode.custom,message:`Missing new content text.`,path:[`newContent`]})}),vn=e=>e.replace(/\r\n/g,`
17
+ `);if(a[a.length-1]===``&&a.pop(),t.added||t.removed){f===void 0&&(f=u);for(let e of a)if(t.added){let t=String(u).padStart(c,` `);i.push(`+${t} ${e}${_n}`),u++}else{let t=String(l).padStart(c,` `);i.push(`-${t} ${e}${_n}`),l++}d=!0}else{let t=r[e+1],o=e<r.length-1&&(t.added||t.removed);if(d||o){let e=a,t=0,r=0;d||(t=Math.max(0,a.length-n),e=a.slice(t)),!o&&e.length>n&&(r=e.length-n,e=e.slice(0,n)),t>0&&(i.push(` ${``.padStart(c,` `)} ...`),l+=t,u+=t);for(let t of e){let e=String(l).padStart(c,` `);i.push(` ${e} ${t}${_n}`),l++,u++}r>0&&(i.push(` ${``.padStart(c,` `)} ...`),l+=r,u+=r)}else l+=a.length,u+=a.length;d=!1}}return{content:i.join(`
18
+ `),firstChangedLine:f}}const yn=/[\u2018\u2019\u201A\u201B]/,bn=/[\u201C\u201D\u201E\u201F]/,xn=/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/,Sn=/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/;function Cn(e){return yn.test(e)?`'`:bn.test(e)?`"`:xn.test(e)?`-`:Sn.test(e)?` `:e}function wn(e){let t=[],n=[],r=e.length,i=0;for(let a=0;a<=r;a++){if(a<r&&e[a]!==`
19
+ `)continue;let o=e.slice(i,a),s=o.trimEnd().length;for(let e=0;e<s;e++)t.push(Cn(o.charAt(e))),n.push(i+e);a<r&&(t.push(`
20
+ `),n.push(a)),i=a+1}return{text:t.join(``),map:n}}function Tn(e,t){return t.length===0?0:e.split(t).length-1}const En=Object.freeze({found:!1,index:-1,matchLength:0,occurrences:0,usedFuzzyMatch:!1});function Dn(e,t){let n=e.indexOf(t);if(n!==-1&&t.length>0)return{found:!0,index:n,matchLength:t.length,occurrences:Tn(e,t),usedFuzzyMatch:!1};let{text:r,map:i}=wn(e),a=wn(t).text;if(a.length===0)return En;let o=r.indexOf(a);if(o===-1)return En;let s=i[o]??0;return{found:!0,index:s,matchLength:(i[o+a.length-1]??s)+1-s,occurrences:Tn(r,a),usedFuzzyMatch:!0}}const On=e.object({path:e.string().describe(`Path to the file to edit (relative or absolute)`),oldContent:e.string().describe(`Exact existing text to locate (must be unique in the file). Provide it verbatim — no diff "-" prefixes.`),newContent:e.string().describe(`The full replacement text for oldContent. Do NOT include diff "+"/"-" prefixes. Example: to rename a-b → a_b, oldContent="a-b", newContent="a_b" (NOT "a-b,a_b").`)}).superRefine((t,n)=>{let r=t.oldContent,i=t.newContent;r===void 0&&n.addIssue({code:e.ZodIssueCode.custom,message:`Missing old content text.`,path:[`oldContent`]}),i===void 0&&n.addIssue({code:e.ZodIssueCode.custom,message:`Missing new content text.`,path:[`newContent`]})}),kn=e=>e.replace(/\r\n/g,`
17
21
  `).replace(/\r/g,`
18
- `);async function yn(e,t,n){let r=e.indexOf(t);return r!==-1&&t.length>0?{found:!0,index:r,matchLength:t.length,occurrences:W(e,t),usedFuzzyMatch:!1}:e.length<=51200?gn(e,t):bn(e,t,n)}function bn(e,t,n){return new Promise((r,i)=>{let a=new je(new URL(`./fuzzy-match-worker.ts`,import.meta.url),{workerData:{content:e,oldContent:t}}),o=()=>{a.terminate(),i(new DOMException(`Aborted`,`AbortError`))};if(n.aborted){o();return}n.addEventListener(`abort`,o,{once:!0}),a.on(`message`,e=>{n.removeEventListener(`abort`,o),a.terminate(),r(e)}),a.on(`error`,i=>{n.removeEventListener(`abort`,o),a.terminate(),r(gn(e,t))})})}function xn(e){return{name:`edit`,description:`Edit a file by replacing exact text. newContent fully replaces oldContent — never echo oldContent back inside newContent (that duplicates it), and never include diff +/- line prefixes.`,parameters:_n,pathParams:[`path`],resolvePath:t=>P(e,t),execute:async({params:t,signal:n})=>{let r=t.oldContent,i=t.newContent;if(r===void 0)throw H(`Missing oldContent`,`validation`);if(i===void 0)throw H(`Missing newContent`,`validation`);let a=_(P(e,t.path));if(!await s(a))throw H(`File not found: ${t.path}`,`not_found`);if(!await m(a))throw H(`Not a file: ${t.path}`,`validation`);return await Sn(a,r,i,n)}}}async function Sn(e,t,n,r){let i=await k(e,`utf-8`);if(r.aborted)throw H(`Operation aborted`,`aborted`);let{bom:a,content:o}=i.startsWith(``)?{bom:``,content:i.slice(1)}:{bom:``,content:i},s=o.indexOf(`\r
22
+ `);async function An(e,t,n){let r=e.indexOf(t);return r!==-1&&t.length>0?{found:!0,index:r,matchLength:t.length,occurrences:Tn(e,t),usedFuzzyMatch:!1}:e.length<=51200?Dn(e,t):jn(e,t,n)}function jn(e,t,n){return new Promise((r,i)=>{let a=new Pe(new URL(`./fuzzy-match-worker.ts`,import.meta.url),{workerData:{content:e,oldContent:t}}),o=()=>{a.terminate(),i(new DOMException(`Aborted`,`AbortError`))};if(n.aborted){o();return}n.addEventListener(`abort`,o,{once:!0}),a.on(`message`,e=>{n.removeEventListener(`abort`,o),a.terminate(),r(e)}),a.on(`error`,i=>{n.removeEventListener(`abort`,o),a.terminate(),r(Dn(e,t))})})}function Mn(e){return{name:`edit`,description:`Edit a file by replacing exact text. newContent fully replaces oldContent — never echo oldContent back inside newContent (that duplicates it), and never include diff +/- line prefixes.`,parameters:On,pathParams:[`path`],resolvePath:t=>P(e,t),execute:async({params:t,signal:n})=>{let r=t.oldContent,i=t.newContent;if(r===void 0)throw H(`Missing oldContent`,`validation`);if(i===void 0)throw H(`Missing newContent`,`validation`);let a=v(P(e,t.path));if(!await c(a))throw H(`File not found: ${t.path}`,`not_found`);if(!await h(a))throw H(`Not a file: ${t.path}`,`validation`);return await Nn(a,r,i,n)}}}async function Nn(e,t,n,r){let i=await k(e,`utf-8`);if(r.aborted)throw H(`Operation aborted`,`aborted`);let{bom:a,content:o}=i.startsWith(``)?{bom:``,content:i.slice(1)}:{bom:``,content:i},s=o.indexOf(`\r
19
23
  `),c=o.indexOf(`
20
24
  `),l=`
21
25
  `;l=c===-1||s===-1?`
22
26
  `:s<c?`\r
23
27
  `:`
24
- `;let u=vn(o),d=vn(t),f=vn(n),p=await yn(u,d,r);if(!p.found)return{content:[{type:`text`,text:`Could not find the specified text in ${e}. No changes were made.`}]};if(p.occurrences>1)return{content:[{type:`text`,text:`Found ${p.occurrences} occurrences of the text in ${e}. The text must be unique. Please provide more context to make it unique.`}]};let m=u.slice(0,p.index)+f+u.slice(p.index+p.matchLength);return m===u?{content:[{type:`text`,text:`No changes made to ${e}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`}]}:(await A(e,a+(l===`\r
28
+ `;let u=kn(o),d=kn(t),f=kn(n),p=await An(u,d,r);if(!p.found)return{content:[{type:`text`,text:`Could not find the specified text in ${e}. No changes were made.`}]};if(p.occurrences>1)return{content:[{type:`text`,text:`Found ${p.occurrences} occurrences of the text in ${e}. The text must be unique. Please provide more context to make it unique.`}]};let m=u.slice(0,p.index)+f+u.slice(p.index+p.matchLength);return m===u?{content:[{type:`text`,text:`No changes made to ${e}. The replacement produced identical content. This might indicate an issue with special characters or the text not existing as expected.`}]}:(await A(e,a+(l===`\r
25
29
  `?m.replace(/\n/g,`\r
26
- `):m)),{content:[{type:`text`,text:`Successfully replaced text in ${e}.`}],details:{diff:cn(u,m)}})}const Cn=new Set([`file:`,`ftp:`,`data:`,`javascript:`]),wn=new Set([`localhost`,`127.0.0.1`,`0.0.0.0`,`[::1]`,`::1`,`::`,`metadata.google.internal`,`169.254.169.254`]);function Tn(e){let t=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(e);if(!t)return null;let n=t.slice(1,5).map(Number);return n.every(e=>e>=0&&e<=255)?n:null}function G(e){let[t=0,n=0]=e;return t===0||t===10||t===127||t===169&&n===254||t===172&&n>=16&&n<=31||t===192&&n===168||t===100&&n>=64&&n<=127}function En(e){let t=e.split(`%`)[0].toLowerCase();if(!t.includes(`:`))return null;let n=/^(.*:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(t);if(n){let e=Tn(n[2]);if(!e)return null;let r=(e[0]<<8|e[1]).toString(16),i=(e[2]<<8|e[3]).toString(16);t=`${n[1]}${r}:${i}`}let r=t.split(`::`);if(r.length>2)return null;let i=e=>e===``?[]:e.split(`:`).map(e=>/^[0-9a-f]{1,4}$/.test(e)?parseInt(e,16):NaN),a=i(r[0]),o=r.length===2?i(r[1]):[],s;if(r.length===2){let e=8-a.length-o.length;if(e<0)return null;s=[...a,...Array.from({length:e},()=>0),...o]}else s=a;return s.length!==8||s.some(e=>!Number.isInteger(e)||e<0||e>65535)?null:s}function Dn(e){if(e.every(e=>e===0)||e.slice(0,7).every(e=>e===0)&&e[7]===1||(e[0]&65472)==65152||(e[0]&65024)==64512)return!0;let t=e.slice(0,5).every(e=>e===0)&&e[5]===65535,n=e.slice(0,6).every(e=>e===0)&&(e[6]!==0||e[7]!==0);return t||n?G([e[6]>>8,e[6]&255,e[7]>>8,e[7]&255]):e[0]===8194?G([e[1]>>8,e[1]&255,e[2]>>8,e[2]&255]):e[0]===100&&e[1]===65435&&e.slice(2,6).every(e=>e===0)?G([e[6]>>8,e[6]&255,e[7]>>8,e[7]&255]):(e[0]&65280)==65280||(e[0]&65472)==65216}function On(e){if(!/^\d+$/.test(e))return null;let t=Number(e);return!Number.isInteger(t)||t<0||t>4294967295?null:[t>>>24&255,t>>>16&255,t>>>8&255,t&255]}function kn(e){let t=e.replace(/^\[|\]$/g,``).toLowerCase(),n=Tn(t);if(n)return G(n);let r=On(t);if(r)return G(r);let i=En(t);return i?Dn(i):!1}function An(e){return Tn(e)!==null||On(e)!==null||e.includes(`:`)}function jn(e){let t;try{t=new URL(e)}catch{throw Error(`Invalid URL: ${e}`)}if(Cn.has(t.protocol))throw Error(`Blocked protocol: ${t.protocol}`);if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`Unsupported protocol: ${t.protocol}`);let n=t.hostname.replace(/^\[|\]$/g,``);if(wn.has(n))throw Error(`Blocked host: ${n}`);if(kn(n))throw Error(`Blocked private IP: ${n}`);return t}const Mn=async e=>{let{lookup:t}=await import(`node:dns/promises`);return(await t(e,{all:!0})).map(e=>e.address)};async function Nn(e,t={}){let n=jn(e),r=n.hostname.replace(/^\[|\]$/g,``);if(An(r))return{url:n};let i=t.resolve??Mn,a;try{a=await i(r)}catch{throw Error(`DNS resolution failed for ${r}`)}if(a.length===0)throw Error(`DNS resolution returned no addresses for ${r}`);for(let e of a)if(kn(e))throw Error(`Blocked resolved IP: ${e} (for ${r})`);let o=a[0];return{url:n,pin:{address:o,family:o.includes(`:`)?6:4}}}async function Pn(e,t,n){let r=new URL(e).protocol===`https:`,[i,{Readable:a}]=await Promise.all([r?import(`node:https`):import(`node:http`),import(`node:stream`)]);return new Promise((r,o)=>{let s=i.request(e,{method:`GET`,headers:t.headers,signal:t.signal,...n?{lookup:(e,t,r)=>{t?.all?r(null,[{address:n.address,family:n.family}]):r(null,n.address,n.family)}}:{}},e=>{let t=new Headers;for(let[n,r]of Object.entries(e.headers))if(Array.isArray(r))for(let e of r)t.append(n,e);else r!=null&&t.set(n,r);let n=e.statusCode??502,i=n===204||n===304?null:a.toWeb(e);r(new Response(i,{status:n,statusText:e.statusMessage??``,headers:t}))});s.on(`error`,o),s.end()})}async function Fn(e,t){let n=t.maxRedirects??5,r=await Nn(e,t),i=0;for(;;){let a=await(t.fetchImpl??((e,t)=>Pn(e,t,r.pin)))(r.url.href,{headers:t.headers,signal:t.signal,redirect:`manual`});if(a.status>=300&&a.status<400){let o=a.headers.get(`location`);if(!o)return a;if(++i>n)throw Error(`Too many redirects (> ${n}) for ${e}`);r=await Nn(new URL(o,r.url).href,t);continue}return a}}const In={"&amp;":`&`,"&lt;":`<`,"&gt;":`>`,"&quot;":`"`,"&#39;":`'`,"&apos;":`'`,"&nbsp;":` `,"&ndash;":`–`,"&mdash;":`—`,"&laquo;":`«`,"&raquo;":`»`,"&copy;":`©`,"&reg;":`®`,"&trade;":`™`,"&hellip;":`…`};function Ln(e){let t=e.replace(/&[a-zA-Z]+;/g,e=>In[e]??e);return t=t.replace(/&#(\d+);/g,(e,t)=>{let n=parseInt(t,10);return n>0&&n<1114111?String.fromCodePoint(n):``}),t=t.replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{let n=parseInt(t,16);return n>0&&n<1114111?String.fromCodePoint(n):``}),t}function Rn(e){let t=e;return t=t.replace(/<script[\s\S]*?<\/script>/gi,``),t=t.replace(/<style[\s\S]*?<\/style>/gi,``),t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,``),t=t.replace(/<svg[\s\S]*?<\/svg>/gi,``),t=t.replace(/<!--[\s\S]*?-->/g,``),t=t.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi,`
30
+ `):m)),{content:[{type:`text`,text:`Successfully replaced text in ${e}.`}],details:{diff:vn(u,m)}})}const Pn=new Set([`file:`,`ftp:`,`data:`,`javascript:`]),Fn=new Set([`localhost`,`127.0.0.1`,`0.0.0.0`,`[::1]`,`::1`,`::`,`metadata.google.internal`,`169.254.169.254`]);function In(e){let t=/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(e);if(!t)return null;let n=t.slice(1,5).map(Number);return n.every(e=>e>=0&&e<=255)?n:null}function G(e){let[t=0,n=0]=e;return t===0||t===10||t===127||t===169&&n===254||t===172&&n>=16&&n<=31||t===192&&n===168||t===100&&n>=64&&n<=127}function Ln(e){let t=e.split(`%`)[0].toLowerCase();if(!t.includes(`:`))return null;let n=/^(.*:)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(t);if(n){let e=In(n[2]);if(!e)return null;let r=(e[0]<<8|e[1]).toString(16),i=(e[2]<<8|e[3]).toString(16);t=`${n[1]}${r}:${i}`}let r=t.split(`::`);if(r.length>2)return null;let i=e=>e===``?[]:e.split(`:`).map(e=>/^[0-9a-f]{1,4}$/.test(e)?parseInt(e,16):NaN),a=i(r[0]),o=r.length===2?i(r[1]):[],s;if(r.length===2){let e=8-a.length-o.length;if(e<0)return null;s=[...a,...Array.from({length:e},()=>0),...o]}else s=a;return s.length!==8||s.some(e=>!Number.isInteger(e)||e<0||e>65535)?null:s}function Rn(e){if(e.every(e=>e===0)||e.slice(0,7).every(e=>e===0)&&e[7]===1||(e[0]&65472)==65152||(e[0]&65024)==64512)return!0;let t=e.slice(0,5).every(e=>e===0)&&e[5]===65535,n=e.slice(0,6).every(e=>e===0)&&(e[6]!==0||e[7]!==0);return t||n?G([e[6]>>8,e[6]&255,e[7]>>8,e[7]&255]):e[0]===8194?G([e[1]>>8,e[1]&255,e[2]>>8,e[2]&255]):e[0]===100&&e[1]===65435&&e.slice(2,6).every(e=>e===0)?G([e[6]>>8,e[6]&255,e[7]>>8,e[7]&255]):(e[0]&65280)==65280||(e[0]&65472)==65216}function zn(e){if(!/^\d+$/.test(e))return null;let t=Number(e);return!Number.isInteger(t)||t<0||t>4294967295?null:[t>>>24&255,t>>>16&255,t>>>8&255,t&255]}function Bn(e){let t=e.replace(/^\[|\]$/g,``).toLowerCase(),n=In(t);if(n)return G(n);let r=zn(t);if(r)return G(r);let i=Ln(t);return i?Rn(i):!1}function Vn(e){return In(e)!==null||zn(e)!==null||e.includes(`:`)}function Hn(e){let t;try{t=new URL(e)}catch{throw Error(`Invalid URL: ${e}`)}if(Pn.has(t.protocol))throw Error(`Blocked protocol: ${t.protocol}`);if(t.protocol!==`http:`&&t.protocol!==`https:`)throw Error(`Unsupported protocol: ${t.protocol}`);let n=t.hostname.replace(/^\[|\]$/g,``);if(Fn.has(n))throw Error(`Blocked host: ${n}`);if(Bn(n))throw Error(`Blocked private IP: ${n}`);return t}const Un=async e=>{let{lookup:t}=await import(`node:dns/promises`);return(await t(e,{all:!0})).map(e=>e.address)};async function Wn(e,t={}){let n=Hn(e),r=n.hostname.replace(/^\[|\]$/g,``);if(Vn(r))return{url:n};let i=t.resolve??Un,a;try{a=await i(r)}catch{throw Error(`DNS resolution failed for ${r}`)}if(a.length===0)throw Error(`DNS resolution returned no addresses for ${r}`);for(let e of a)if(Bn(e))throw Error(`Blocked resolved IP: ${e} (for ${r})`);let o=a[0];return{url:n,pin:{address:o,family:o.includes(`:`)?6:4}}}async function Gn(e,t,n){let r=new URL(e).protocol===`https:`,[i,{Readable:a}]=await Promise.all([r?import(`node:https`):import(`node:http`),import(`node:stream`)]);return new Promise((r,o)=>{let s=i.request(e,{method:`GET`,headers:t.headers,signal:t.signal,...n?{lookup:(e,t,r)=>{t?.all?r(null,[{address:n.address,family:n.family}]):r(null,n.address,n.family)}}:{}},e=>{let t=new Headers;for(let[n,r]of Object.entries(e.headers))if(Array.isArray(r))for(let e of r)t.append(n,e);else r!=null&&t.set(n,r);let n=e.statusCode??502,i=n===204||n===304?null:a.toWeb(e);r(new Response(i,{status:n,statusText:e.statusMessage??``,headers:t}))});s.on(`error`,o),s.end()})}async function Kn(e,t){let n=t.maxRedirects??5,r=await Wn(e,t),i=0;for(;;){let a=await(t.fetchImpl??((e,t)=>Gn(e,t,r.pin)))(r.url.href,{headers:t.headers,signal:t.signal,redirect:`manual`});if(a.status>=300&&a.status<400){let o=a.headers.get(`location`);if(!o)return a;if(++i>n)throw Error(`Too many redirects (> ${n}) for ${e}`);r=await Wn(new URL(o,r.url).href,t);continue}return a}}const qn={"&amp;":`&`,"&lt;":`<`,"&gt;":`>`,"&quot;":`"`,"&#39;":`'`,"&apos;":`'`,"&nbsp;":` `,"&ndash;":`–`,"&mdash;":`—`,"&laquo;":`«`,"&raquo;":`»`,"&copy;":`©`,"&reg;":`®`,"&trade;":`™`,"&hellip;":`…`};function Jn(e){let t=e.replace(/&[a-zA-Z]+;/g,e=>qn[e]??e);return t=t.replace(/&#(\d+);/g,(e,t)=>{let n=parseInt(t,10);return n>0&&n<1114111?String.fromCodePoint(n):``}),t=t.replace(/&#x([0-9a-fA-F]+);/g,(e,t)=>{let n=parseInt(t,16);return n>0&&n<1114111?String.fromCodePoint(n):``}),t}function Yn(e){let t=e;return t=t.replace(/<script[\s\S]*?<\/script>/gi,``),t=t.replace(/<style[\s\S]*?<\/style>/gi,``),t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,``),t=t.replace(/<svg[\s\S]*?<\/svg>/gi,``),t=t.replace(/<!--[\s\S]*?-->/g,``),t=t.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi,`
27
31
 
28
32
  ## $1
29
33
 
@@ -37,9 +41,9 @@ import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_
37
41
  ---
38
42
  `),t=t.replace(/<\/blockquote>/gi,`
39
43
  `),t=t.replace(/<blockquote[^>]*>/gi,`
40
- > `),t=t.replace(/<a[^>]+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi,`$2 ($1)`),t=t.replace(/<img[^>]+alt="([^"]*)"[^>]*\/?>/gi,`[$1]`),t=t.replace(/<[^>]+>/g,``),t=Ln(t),t=t.replace(/\n{3,}/g,`
44
+ > `),t=t.replace(/<a[^>]+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi,`$2 ($1)`),t=t.replace(/<img[^>]+alt="([^"]*)"[^>]*\/?>/gi,`[$1]`),t=t.replace(/<[^>]+>/g,``),t=Jn(t),t=t.replace(/\n{3,}/g,`
41
45
 
42
- `),t=t.replace(/[ \t]+/g,` `),t=t.replace(/^ +/gm,``),t=t.trim(),t}function zn(e){let t=e;return t=t.replace(/<script[\s\S]*?<\/script>/gi,``),t=t.replace(/<style[\s\S]*?<\/style>/gi,``),t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,``),t=t.replace(/<svg[\s\S]*?<\/svg>/gi,``),t=t.replace(/<!--[\s\S]*?-->/g,``),t=t.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi,`
46
+ `),t=t.replace(/[ \t]+/g,` `),t=t.replace(/^ +/gm,``),t=t.trim(),t}function Xn(e){let t=e;return t=t.replace(/<script[\s\S]*?<\/script>/gi,``),t=t.replace(/<style[\s\S]*?<\/style>/gi,``),t=t.replace(/<noscript[\s\S]*?<\/noscript>/gi,``),t=t.replace(/<svg[\s\S]*?<\/svg>/gi,``),t=t.replace(/<!--[\s\S]*?-->/g,``),t=t.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi,`
43
47
 
44
48
  # $1
45
49
 
@@ -73,59 +77,59 @@ import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_
73
77
  `),t=t.replace(/<\/tr>/gi,`
74
78
  `),t=t.replace(/<td[^>]*>/gi,` | `),t=t.replace(/<th[^>]*>/gi,` | `),t=t.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi,(e,t)=>t.split(`
75
79
  `).map(e=>`> ${e}`).join(`
76
- `)),t=t.replace(/<[^>]+>/g,``),t=Ln(t),t=t.replace(/\n{3,}/g,`
80
+ `)),t=t.replace(/<[^>]+>/g,``),t=Jn(t),t=t.replace(/\n{3,}/g,`
77
81
 
78
- `),t=t.replace(/[ \t]+$/gm,``),t=t.trim(),t}const Bn=e.object({url:e.string().describe(`URL to fetch content from`),format:e.enum([`text`,`markdown`,`raw`]).optional().default(`text`).describe(`Output format: text (cleaned, default), markdown, or raw HTML`),headers:e.record(e.string(),e.string()).optional().describe(`Additional HTTP headers`),maxLength:e.number().int().min(1e3).max(5e5).optional().describe(`Maximum output length in bytes (capped at the global output limit)`)});function Vn(e){return{name:`web_fetch`,description:`Fetch a web page and return its content. Supports text (default), markdown, and raw HTML formats. Cannot render JavaScript-heavy pages (SPAs). Best for documentation, articles, and static pages. Output truncated to ${c(z)}.`,parameters:Bn,readonly:!0,async execute({params:e,signal:t}){let n=await Fn(e.url,{headers:{"User-Agent":`OttoBot/1.0`,Accept:`text/html, application/json, text/plain, */*`,...e.headers},signal:t??AbortSignal.timeout(B)});if(!n.ok)return{content:[{type:`text`,text:`HTTP ${n.status} ${n.statusText} for ${n.url||e.url}`}],isError:!0,errorKind:`network`};let r=n.headers.get(`content-type`)??``,i=parseInt(n.headers.get(`content-length`)??`0`,10),a=e=>({content:[{type:`text`,text:`Response too large: ${c(e)} (limit: ${c(V)})`}],isError:!0,errorKind:`runtime`});if(i>V)return a(i);let o=await Hn(n,V);if(o.overflow)return a(V);let s=o.data,l=new TextDecoder().decode(s),u;if(r.includes(`application/json`))try{u=JSON.stringify(JSON.parse(l),null,2)}catch{u=l}else if(r.includes(`text/html`)){let t=e.format??`text`;u=t===`raw`?l:t===`markdown`?zn(l):Rn(l)}else u=l;let d=e.maxLength??z,f=S(u,{maxBytes:Math.min(d,z),maxLines:1/0}),p=f.content;return f.truncated&&(p+=`\n\n(Content truncated. Showing ${c(f.outputBytes)} of ${c(f.totalBytes)})`),{content:[{type:`text`,text:p}],details:{url:n.url||e.url,contentType:r,size:s.byteLength,truncation:b(f)}}}}}async function Hn(e,t){let n=e.body?.getReader();if(!n){let n=new Uint8Array(await e.arrayBuffer());return{data:n,overflow:n.byteLength>t}}let r=[],i=0;for(;;){let{done:e,value:a}=await n.read();if(e)break;if(a){if(i+=a.byteLength,i>t)return await n.cancel().catch(()=>void 0),{data:new Uint8Array,overflow:!0};r.push(a)}}let a=new Uint8Array(i),o=0;for(let e of r)a.set(e,o),o+=e.byteLength;return{data:a,overflow:!1}}const Un=e.object({query:e.string().min(1).max(500).describe(`Search query`),limit:e.number().int().min(1).max(20).optional().default(10).describe(`Maximum number of results to return (default: 10)`)});function Wn(e,t){let n=[],r=e.split(/data-type="web"/);for(let e=1;e<r.length&&n.length<t;e++){let t=r[e],i=t.match(/<a\s+href="(https?:\/\/[^"]+)"[^>]*class="[^"]*svelte-/);if(!i)continue;let a=i[1],o=t.match(/class="title\s+search-snippet-title[^"]*"[^>]*title="([^"]*)"/),s=``;if(o)s=o[1];else{let e=t.match(/class="title\s+search-snippet-title[^"]*"[^>]*>([\s\S]*?)<\/div>/);e&&(s=Rn(e[1]).trim())}let c=t.match(/class="generic-snippet[^"]*"[\s\S]*?class="content[^"]*"[^>]*>([\s\S]*?)<\/div>/),l=c?Rn(c[1]).trim():``;a&&s&&n.push({title:s,url:a,snippet:l})}return n}function Gn(e,t,n){if(t.length===0)return`No results found for: "${e}"`;let r=[`Search results for: "${e}"\n`];for(let e=0;e<t.length;e++){let n=t[e];r.push(`${e+1}. ${n.title}`),r.push(` ${n.url}`),n.snippet&&r.push(` ${n.snippet}`),r.push(``)}let i=r.join(`
82
+ `),t=t.replace(/[ \t]+$/gm,``),t=t.trim(),t}const Zn=e.object({url:e.string().describe(`URL to fetch content from`),format:e.enum([`text`,`markdown`,`raw`]).optional().default(`text`).describe(`Output format: text (cleaned, default), markdown, or raw HTML`),headers:e.record(e.string(),e.string()).optional().describe(`Additional HTTP headers`),maxLength:e.number().int().min(1e3).max(5e5).optional().describe(`Maximum output length in bytes (capped at the global output limit)`)});function Qn(e){return{name:`web_fetch`,description:`Fetch a web page and return its content. Supports text (default), markdown, and raw HTML formats. Cannot render JavaScript-heavy pages (SPAs). Best for documentation, articles, and static pages. Output truncated to ${l(z)}.`,parameters:Zn,readonly:!0,async execute({params:e,signal:t}){let n=await Kn(e.url,{headers:{"User-Agent":`OttoBot/1.0`,Accept:`text/html, application/json, text/plain, */*`,...e.headers},signal:t??AbortSignal.timeout(B)});if(!n.ok)return{content:[{type:`text`,text:`HTTP ${n.status} ${n.statusText} for ${n.url||e.url}`}],isError:!0,errorKind:`network`};let r=n.headers.get(`content-type`)??``,i=parseInt(n.headers.get(`content-length`)??`0`,10),a=e=>({content:[{type:`text`,text:`Response too large: ${l(e)} (limit: ${l(V)})`}],isError:!0,errorKind:`runtime`});if(i>V)return a(i);let o=await $n(n,V);if(o.overflow)return a(V);let s=o.data,c=new TextDecoder().decode(s),u;if(r.includes(`application/json`))try{u=JSON.stringify(JSON.parse(c),null,2)}catch{u=c}else if(r.includes(`text/html`)){let t=e.format??`text`;u=t===`raw`?c:t===`markdown`?Xn(c):Yn(c)}else u=c;let d=e.maxLength??z,f=w(u,{maxBytes:Math.min(d,z),maxLines:1/0}),p=f.content;return f.truncated&&(p+=`\n\n(Content truncated. Showing ${l(f.outputBytes)} of ${l(f.totalBytes)})`),{content:[{type:`text`,text:p}],details:{url:n.url||e.url,contentType:r,size:s.byteLength,truncation:x(f)}}}}}async function $n(e,t){let n=e.body?.getReader();if(!n){let n=new Uint8Array(await e.arrayBuffer());return{data:n,overflow:n.byteLength>t}}let r=[],i=0;for(;;){let{done:e,value:a}=await n.read();if(e)break;if(a){if(i+=a.byteLength,i>t)return await n.cancel().catch(()=>void 0),{data:new Uint8Array,overflow:!0};r.push(a)}}let a=new Uint8Array(i),o=0;for(let e of r)a.set(e,o),o+=e.byteLength;return{data:a,overflow:!1}}const er=e.object({query:e.string().min(1).max(500).describe(`Search query`),limit:e.number().int().min(1).max(20).optional().default(10).describe(`Maximum number of results to return (default: 10)`)});function tr(e,t){let n=[],r=e.split(/data-type="web"/);for(let e=1;e<r.length&&n.length<t;e++){let t=r[e],i=t.match(/<a\s+href="(https?:\/\/[^"]+)"[^>]*class="[^"]*svelte-/);if(!i)continue;let a=i[1],o=t.match(/class="title\s+search-snippet-title[^"]*"[^>]*title="([^"]*)"/),s=``;if(o)s=o[1];else{let e=t.match(/class="title\s+search-snippet-title[^"]*"[^>]*>([\s\S]*?)<\/div>/);e&&(s=Yn(e[1]).trim())}let c=t.match(/class="generic-snippet[^"]*"[\s\S]*?class="content[^"]*"[^>]*>([\s\S]*?)<\/div>/),l=c?Yn(c[1]).trim():``;a&&s&&n.push({title:s,url:a,snippet:l})}return n}function nr(e,t,n){if(t.length===0)return`No results found for: "${e}"`;let r=[`Search results for: "${e}"\n`];for(let e=0;e<t.length;e++){let n=t[e];r.push(`${e+1}. ${n.title}`),r.push(` ${n.url}`),n.snippet&&r.push(` ${n.snippet}`),r.push(``)}let i=r.join(`
79
83
  `).trim();return n||(i+=`
80
84
 
81
- (Note: results were scraped from search HTML and may be incomplete or out of date. Set BRAVE_SEARCH_API_KEY to use the official Brave Search API for reliable results.)`),i}var Kn=class{id=`brave-api`;reliable=!0;constructor(e,t=fetch){this.apiKey=e,this.fetchImpl=t}async search(e,t,n){let r=new URLSearchParams({q:e,count:String(t)}),i=await this.fetchImpl(`https://api.search.brave.com/res/v1/web/search?${r}`,{headers:{"X-Subscription-Token":this.apiKey,Accept:`application/json`},signal:n??AbortSignal.timeout(B)});if(!i.ok)throw Error(`Brave Search API error: HTTP ${i.status}`);return((await i.json()).web?.results??[]).slice(0,t).map(e=>({title:e.title??``,url:e.url??``,snippet:e.description??``})).filter(e=>e.url)}},qn=class{id=`brave-html`;reliable=!1;constructor(e=fetch){this.fetchImpl=e}async search(e,t,n){let r=new URLSearchParams({q:e,source:`web`}),i=await this.fetchImpl(`https://search.brave.com/search?${r}`,{headers:{"User-Agent":`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36`,Accept:`text/html,application/xhtml+xml`,"Accept-Language":`en-US,en;q=0.9`},redirect:`follow`,signal:n??AbortSignal.timeout(B)});if(!i.ok)throw Error(`Search request failed: HTTP ${i.status}`);return Wn(await i.text(),t)}};function Jn(e={},t=process.env){if(e.provider)return e.provider;let n=e.apiKey??t.BRAVE_SEARCH_API_KEY;return n?new Kn(n,e.fetchImpl):new qn(e.fetchImpl)}function Yn(e,t={}){return{name:`web_search`,description:`Search the web and return results with titles, URLs, and snippets. Use this to discover relevant pages before fetching them with web_fetch.`,parameters:Un,readonly:!0,async execute({params:e,signal:n}){let{query:r,limit:i=10}=e,a=Jn(t),o;try{o=await a.search(r,i,n)}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:`Search request failed`}],isError:!0,errorKind:`network`}}return{content:[{type:`text`,text:Gn(r,o,a.reliable)}],details:{query:r,provider:a.id,reliable:a.reliable,resultCount:o.length,results:o}}}}}function K(e,t){return{content:[{type:`text`,text:`${e} not available: this capability is not configured in the host.${t?` ${t}`:``}`}],isError:!0,errorKind:`runtime`}}function Xn(e){if(e.length===0)return``;let t=e.slice(0,8),n=e.length>8?`… and ${e.length-8} more — pass any name, fuzzy match applies.`:``;return[`Available agents:`,...t.map(e=>{let t=e.description.length>80?e.description.slice(0,77)+`…`:e.description,n=[];e.slot&&n.push(`slot=${e.slot}`),e.readOnly&&n.push(`read-only`);let r=n.length>0?` (${n.join(`, `)})`:``;return`- **${e.name}**: ${t}${r}`}),n,`Pass the agent name — fuzzy match applies for unknown names.`].filter(Boolean).join(`
82
- `)}const Zn=e.string().optional().describe(`Optional child session ID. When used with sticky mode, later calls can reuse the same child context.`),Qn=e.enum([`ephemeral`,`sticky`]).optional().default(`ephemeral`).describe(`Child session lifecycle. ephemeral deletes the child session after the task; sticky keeps it for later reuse.`),$n=e.enum(Ne).optional().describe(`Model slot to use for this task`),er=e.object({prompt:e.string().describe(`Task description to delegate (for mode=team, the input handed to the team)`),subagent:e.string().optional().describe(`Agent name to delegate the task to (e.g. "explore"). Used by mode=sync/background.`),category:e.string().optional().describe(`Task category — drives automatic model selection: "search"/"quick" → cheaper/faster model, "deep"/"critique"/"review" → powerful model. Set on mode=sync/background whenever the task fits a category, even without a named subagent.`),team:e.string().optional().describe(`Declared team name (see .otto/teams/*.md). Required for mode=team.`),mode:e.enum([`sync`,`background`,`fork`,`team`]).optional().default(`sync`).describe(`Execution mode. 'sync' (default): sub-agent (subagent/category) in the shared workspace, returns inline — use for tracked subtasks that edit files or whose output you need now. 'background': same but detached and READ-ONLY (no edit/write/bash) — analysis/research only. 'fork': fresh ISOLATED session that does NOT inherit your history/system-prompt/tools (put all needed context in the prompt) — best for summarization, batch checks, in-context Q&A. 'team': declared multi-agent team (sequential/parallel/hierarchical/handoff/router) from .otto/teams/*.md.`),sessionId:Zn,sessionMode:Qn,slot:$n,model:e.string().optional().describe("Explicit model id chosen by capability strength (overrides slot/category). Pick only models marked usable by list_models; unauthenticated or rate-limit-risky models are rejected and fall back. Prefer leaving unset and using `category` so the host picks a usable model automatically."),taskIds:e.array(e.string()).optional().describe(`Task IDs from the parent write_todos list that this sub-agent is responsible for completing.`),tracked:e.boolean().optional().default(!0).describe(`Host governance: true (default) = full task tracking (recorded, retryable, todo-sync). false = lightweight probe (no record/retry, ephemeral, synchronous) — use for quick agent opinions/reviews; requires explicit subagent name.`)}).refine(e=>{let t=e.mode??`sync`;return e.tracked===!1?t===`sync`&&e.subagent!==void 0&&e.category===void 0&&e.team===void 0&&e.sessionId===void 0&&e.sessionMode===void 0&&e.taskIds===void 0:t===`team`?e.team!==void 0:t===`fork`?!0:e.subagent!==void 0||e.category!==void 0},{message:`mode=sync/background requires subagent or category; mode=team requires team; mode=fork needs only prompt; tracked:false requires explicit subagent with mode=sync (no category/team/sessionId/sessionMode/taskIds)`});async function tr(e,t,n,r){if(!e)return;let i;if(n&&n.length>0)i=n.filter(e=>r.some(t=>t.id===e&&t.status!==`done`));else{let e=r.filter(e=>e.status===`in_progress`);e.length===1&&(i=[e[0].id])}if(!(!i||i.length===0)&&(await e({action:`update`,todos:r.map(e=>i.includes(e.id)?{...e,status:`done`}:e),sessionId:t})).ok)return`✓ auto-synced parent task list: ${i.join(`, `)} → done`}async function nr(e,t,n,r,i){if(!e)return K(`task_delegate (mode=fork)`);let{childDepth:a,exceeded:o}=Pe(n,i);if(o)return{isError:!0,errorKind:`validation`,content:[{type:`text`,text:`fork rejected: delegation depth ${a} exceeds max ${i}`}]};let s=await e(t,a,r);return s.ok?{content:[{type:`text`,text:s.value||`(no output)`}]}:{content:[{type:`text`,text:`Fork failed: ${s.error}`}],isError:!0,errorKind:`runtime`}}async function rr(e,t,n,r,i,a){if(!e)return K(`task_delegate (mode=team)`,`Declare a team in .otto/teams/*.md.`);if(!t)return{content:[{type:`text`,text:`mode=team requires a team name`}],isError:!0,errorKind:`validation`};let o=await e(t,n,r,i,a);return o.success?{content:[{type:`text`,text:o.output??``}],details:{team:t,pattern:o.pattern}}:{content:[{type:`text`,text:`Team "${t}" failed: ${o.error}`}],isError:!0,errorKind:`runtime`}}function ir(e,t,n,r,i,a=1,o,s){let c=Xn((s??[]).map(e=>({name:e.name,description:e.description,slot:e.preferredModelTier})));return{name:`task_delegate`,description:`Delegate work to a sub-agent, an isolated fork, or a declared team. Modes: sync/background/fork/team — semantics in the mode parameter. tracked:false (sync only, explicit subagent) → lightweight probe: no record, no retry, returns inline — use for quick agent opinions/reviews.`+(c?`\n${c}`:``),parameters:er,isConcurrencySafe:e=>e?.mode===`fork`||e?.mode===`team`||e?.sessionMode!==`sticky`,lifecycleAsync:e=>(e?.mode===`sync`||e?.mode===void 0)&&e?.sessionMode!==`sticky`&&e?.tracked!==!1,lifecycleAsyncNote:`Note: the delegated sub-agent may edit files in the shared workspace while it runs. Until you receive its completion notice, do not read or write paths its task may touch.`,async execute({params:e,depth:s,signal:c,sessionId:l,parentTaskId:u}){if(e.tracked===!1){if((e.mode??`sync`)!==`sync`||e.category!==void 0||e.team!==void 0||e.sessionId!==void 0||e.sessionMode!==void 0||e.taskIds!==void 0||e.subagent===void 0)return{isError:!0,errorKind:`validation`,content:[{type:`text`,text:`tracked:false requires explicit subagent with mode=sync (no category/team/sessionId/sessionMode/taskIds)`}]};if(!o)return K(`task_delegate (tracked:false)`);let t=await o(e.subagent,e.prompt,{slot:e.slot,model:e.model,depth:s,signal:c,parentTaskId:u});return t.ok?{content:[{type:`text`,text:t.value||`(no output)`}]}:{content:[{type:`text`,text:`Agent call failed: ${t.error}`}],isError:!0,errorKind:`runtime`}}if(e.mode===`fork`)return nr(r,e.prompt,s,c,a);if(e.mode===`team`)return rr(i,e.team,e.prompt,c,s,u);if(!t)return K(`task_delegate`);let d=[];if(e.mode!==`background`&&n){let e=await n({action:`update`,todos:[],sessionId:l});e.ok&&(d=e.value)}let f=e.prompt;e.taskIds&&e.taskIds.length>0&&(f=`You are responsible for completing parent task item(s): ${e.taskIds.map(e=>`"${e}"`).join(`, `)}. When done, your host will auto-sync these to "done". No need to call write_todos for parent items.\n\n${e.prompt}`);let p=await t({prompt:f,subagent:e.subagent,category:e.category,mode:e.mode,sessionId:e.sessionId,sessionMode:e.sessionMode,slot:e.slot,model:e.model,depth:s,signal:c,parentTaskId:u});if(p.ok){let{id:t,status:r,output:i}=p.value,a=e.mode===`background`,o=a?void 0:await tr(n,l,e.taskIds,d),s=[];return a?s.push(`Task delegated in background${t?`: ${t}`:``}`):s.push(`Task delegated successfully`),i&&s.push(i),o&&s.push(o),{content:[{type:`text`,text:s.join(`
83
- `)}],details:{mode:e.mode,status:r,sessionId:e.sessionId,sessionMode:e.sessionMode,...o?{todoSync:o}:{}}}}return{content:[{type:`text`,text:`Task delegation failed: ${p.error}`}],isError:!0,errorKind:`runtime`}}}}const ar=e.object({prompt:e.string().describe(`Self-contained instruction for the background job. It runs in an isolated git worktree with full tools (incl. file edits + MCP), so include all needed context.`),title:e.string().optional().describe(`Short label for the job (shown in the /jobs panel). Defaults to a slice of the prompt.`)});function or(e){return{name:`delegate_job`,description:`Delegate a file-changing task to an isolated background job. Runs in a separate git worktree with full tools (edits + MCP), then produces a REVIEWABLE DIFF — it does NOT touch your working tree directly. Use for independent changes you want isolated and audited (e.g. a self-contained refactor) while you keep working. The diff is applied by the user via /job apply (autonomous apply is off by default). For read-only background analysis use task_delegate(background); for in-conversation edits use task_delegate(sync).`,parameters:ar,async execute({params:t,sessionId:n}){if(!e)return K(`delegate_job`);if(!n)return{isError:!0,errorKind:`validation`,content:[{type:`text`,text:`delegate_job requires a session context (no sessionId available).`}]};let r=t.title?.trim()||t.prompt.replace(/\s+/g,` `).slice(0,48),{jobId:i}=e({prompt:t.prompt,title:r,sessionId:n});return{content:[{type:`text`,text:`Delegated background job ${i} (isolated worktree). It will produce a reviewable diff; you'll get a notification when it's ready. The user applies it with /job apply ${i} (or reviews it in the /jobs panel). It does not modify the current working tree.`}],details:{jobId:i,origin:`main`}}}}}const sr=e.object({need:e.string().min(1).describe(`What capability is missing, in plain language (e.g. "query Jira issue status"). Describe the user-facing need, not an implementation.`),searchTerms:e.array(e.string().min(1)).min(1).describe(`Keywords to search the plugin ecosystem with (e.g. ["jira", "issue tracker"]). Use 2-4 distinct terms; a single narrow term often misses existing plugins.`)});function cr(e){return[...new Set(e.map(e=>e.trim().toLowerCase()).filter(Boolean))].sort().join(`|`)}function lr(e){return`plugin-${e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).split(`-`).filter(Boolean).slice(0,3).join(`-`)||`custom`}`}function ur(e){let t=new Set;return{name:`capability_gap`,description:`Report a capability you lack and discover whether the plugin ecosystem already provides it. Use this when the user asks for something you genuinely cannot do with your current tools — instead of only saying "I can't", check whether a plugin exists, and if not, get a blueprint for building one. This tool only returns guidance: it never installs anything (installation always requires explicit user approval).`,parameters:sr,async execute({params:n}){if(!e)return K(`capability_gap`);let r=cr(n.searchTerms),i=t.has(r);if(t.add(r),i)return{content:[{type:`text`,text:`Already reported this capability gap earlier in the session (search terms: ${n.searchTerms.join(`, `)}). Don't re-report it — either act on the earlier guidance, or tell the user plainly that this capability is unavailable.`}],details:{deduped:!0}};let a=await e(n.searchTerms);if(!a){let e=lr(n.need);return{content:[{type:`text`,text:`Could not reach the plugin registry (network or configuration issue), so it is unknown whether an existing plugin provides "${n.need}".\n\nYou can still offer to build it: ask the user whether they want a plugin scaffolded, then\n otto extension create ${e} --with-tool\nand use delegate_job to implement it in an isolated worktree (produces a reviewable diff).`}],details:{suggestion:`generate`,registryReachable:!1}}}let{hits:o,warnings:s}=a,c=s.length>0?`\n\nRegistry warnings:\n${s.map(e=>`- ${e}`).join(`
84
- `)}`:``;if(o.length>0){let e=o.map(e=>{let t=e.capabilities?.length?` [declares: ${e.capabilities.join(`, `)}]`:``,n=e.stale?` (cached index may be out of date)`:``;return`- ${e.id} — ${e.description}${t} (from ${e.sourceName})${n}\n install: otto extension install ${e.id}`});return{content:[{type:`text`,text:`Found ${o.length} existing plugin(s) that may provide "${n.need}":\n\n${e.join(`
85
- `)}\n\nTell the user what you found and let THEM run the install command — you cannot install it yourself, and installing high-risk capabilities requires their explicit approval.`+c}],details:{suggestion:`install`,hits:o.length}}}let l=lr(n.need);return{content:[{type:`text`,text:`No existing plugin in the ecosystem provides "${n.need}".\n\nYou can offer to build one. Blueprint:\n 1. Ask the user whether they want this capability added as a plugin.\n 2. Scaffold it: otto extension create ${l} --with-tool\n 3. Implement it with delegate_job (isolated worktree, produces a reviewable diff) — describe the tool's name, parameters, and behaviour in the job prompt.\n 4. The user reviews the diff (/job apply), then installs it — installation and any high-risk capability grants always go through their explicit approval.`+c}],details:{suggestion:`generate`,hits:0}}}}}const dr=e.object({action:e.enum([`get`,`list`,`output`]).describe(`get task by ID, list all tasks, or get background task output`),taskId:e.string().optional().describe(`Task ID (required for get/output actions)`),statusFilter:e.enum([`all`,`pending`,`running`,`completed`,`failed`,`cancelled`]).optional().default(`all`).describe(`Filter by status (list action only)`)});function fr(e,t){let{get:n,list:r,output:i}=t;return{name:`task_inspect`,description:`Inspect tasks: get details by ID, list with optional status filter, or get background task output.`,parameters:dr,readonly:!0,async execute({params:e}){switch(e.action){case`get`:{if(!n)return K(`task_inspect(get)`);if(!e.taskId)return{content:[{type:`text`,text:`taskId is required for get action`}],isError:!0,errorKind:`validation`};let t=await n(e.taskId);if(!t.ok)return{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`};let r=t.value,i=r.status===`failed`||!!r.error;return{content:[{type:`text`,text:[`Task: ${r.id}`,`Status: ${r.status}`,r.prompt?`Prompt: ${r.prompt}`:void 0,`Output: ${r.output??`N/A`}`,r.error?`Error: ${r.error}`:void 0].filter(e=>!!e).join(`
85
+ (Note: results were scraped from search HTML and may be incomplete or out of date. Set BRAVE_SEARCH_API_KEY to use the official Brave Search API for reliable results.)`),i}var rr=class{id=`brave-api`;reliable=!0;constructor(e,t=fetch){this.apiKey=e,this.fetchImpl=t}async search(e,t,n){let r=new URLSearchParams({q:e,count:String(t)}),i=await this.fetchImpl(`https://api.search.brave.com/res/v1/web/search?${r}`,{headers:{"X-Subscription-Token":this.apiKey,Accept:`application/json`},signal:n??AbortSignal.timeout(B)});if(!i.ok)throw Error(`Brave Search API error: HTTP ${i.status}`);return((await i.json()).web?.results??[]).slice(0,t).map(e=>({title:e.title??``,url:e.url??``,snippet:e.description??``})).filter(e=>e.url)}},ir=class{id=`brave-html`;reliable=!1;constructor(e=fetch){this.fetchImpl=e}async search(e,t,n){let r=new URLSearchParams({q:e,source:`web`}),i=await this.fetchImpl(`https://search.brave.com/search?${r}`,{headers:{"User-Agent":`Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36`,Accept:`text/html,application/xhtml+xml`,"Accept-Language":`en-US,en;q=0.9`},redirect:`follow`,signal:n??AbortSignal.timeout(B)});if(!i.ok)throw Error(`Search request failed: HTTP ${i.status}`);return tr(await i.text(),t)}};function ar(e={},t=process.env){if(e.provider)return e.provider;let n=e.apiKey??t.BRAVE_SEARCH_API_KEY;return n?new rr(n,e.fetchImpl):new ir(e.fetchImpl)}function or(e,t={}){return{name:`web_search`,description:`Search the web and return results with titles, URLs, and snippets. Use this to discover relevant pages before fetching them with web_fetch.`,parameters:er,readonly:!0,async execute({params:e,signal:n}){let{query:r,limit:i=10}=e,a=ar(t),o;try{o=await a.search(r,i,n)}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:`Search request failed`}],isError:!0,errorKind:`network`}}return{content:[{type:`text`,text:nr(r,o,a.reliable)}],details:{query:r,provider:a.id,reliable:a.reliable,resultCount:o.length,results:o}}}}}function K(e,t){return{content:[{type:`text`,text:`${e} not available: this capability is not configured in the host.${t?` ${t}`:``}`}],isError:!0,errorKind:`runtime`}}function sr(e){if(e.length===0)return``;let t=e.slice(0,8),n=e.length>8?`… and ${e.length-8} more — pass any name, fuzzy match applies.`:``;return[`Available agents:`,...t.map(e=>{let t=e.description.length>80?e.description.slice(0,77)+`…`:e.description,n=[];e.slot&&n.push(`slot=${e.slot}`),e.readOnly&&n.push(`read-only`);let r=n.length>0?` (${n.join(`, `)})`:``;return`- **${e.name}**: ${t}${r}`}),n,`Pass the agent name — fuzzy match applies for unknown names.`].filter(Boolean).join(`
86
+ `)}const cr=e.string().optional().describe(`Optional child session ID; with sticky mode, later calls reuse the same child context.`),lr=e.enum([`ephemeral`,`sticky`]).optional().default(`ephemeral`).describe(`Child session lifecycle: ephemeral deletes after the task; sticky keeps for later reuse.`),ur=e.enum(Ie).optional().describe(`Model slot to use for this task`),dr=e.object({prompt:e.string().describe(`Task description to delegate (for mode=team, the input handed to the team)`),subagent:e.string().optional().describe(`Agent name to delegate to (e.g. "explore"). Used by sync/background.`),category:e.string().optional().describe(`Task category — drives auto model selection: "search"/"quick" → cheap/fast, "deep"/"critique"/"review" → powerful. Set on sync/background whenever fitting, even without a named subagent.`),team:e.string().optional().describe(`Declared team name (see .otto/teams/*.md). Required for mode=team.`),mode:e.enum([`sync`,`background`,`fork`,`team`]).optional().default(`sync`).describe(`Execution mode. 'sync' (default): sub-agent in shared workspace, returns inline — for tracked subtasks that edit files or whose output you need now. 'background': same but detached and READ-ONLY — analysis/research only. 'fork': fresh ISOLATED session that does NOT inherit your history/system-prompt/tools (put all needed context in the prompt) — best for summarization, batch checks, in-context Q&A. 'team': declared multi-agent team (sequential/parallel/hierarchical/handoff/router) from .otto/teams/*.md.`),sessionId:cr,sessionMode:lr,slot:ur,model:e.string().optional().describe("Explicit model id by capability strength (overrides slot/category). Pick only models marked usable by list_models; others are rejected and fall back. Prefer unset + `category` so the host picks a usable model automatically."),taskIds:e.array(e.string()).optional().describe(`Parent write_todos task IDs this sub-agent is responsible for completing.`),tracked:e.boolean().optional().default(!0).describe(`Host governance: true (default) = full tracking (recorded, retryable, todo-sync). false = lightweight probe (no record/retry, ephemeral, synchronous) — for quick agent opinions/reviews; requires explicit subagent.`)}).refine(e=>{let t=e.mode??`sync`;return e.tracked===!1?t===`sync`&&e.subagent!==void 0&&e.category===void 0&&e.team===void 0&&e.sessionId===void 0&&e.sessionMode===void 0&&e.taskIds===void 0:t===`team`?e.team!==void 0:t===`fork`?!0:e.subagent!==void 0||e.category!==void 0},{message:`mode=sync/background requires subagent or category; mode=team requires team; mode=fork needs only prompt; tracked:false requires explicit subagent with mode=sync (no category/team/sessionId/sessionMode/taskIds)`});async function fr(e,t,n,r){if(!e)return;let i;if(n&&n.length>0)i=n.filter(e=>r.some(t=>t.id===e&&t.status!==`done`));else{let e=r.filter(e=>e.status===`in_progress`);e.length===1&&(i=[e[0].id])}if(!(!i||i.length===0)&&(await e({action:`update`,todos:r.map(e=>i.includes(e.id)?{...e,status:`done`}:e),sessionId:t})).ok)return`✓ auto-synced parent task list: ${i.join(`, `)} → done`}async function pr(e,t,n,r,i){if(!e)return K(`task_delegate (mode=fork)`);let{childDepth:a,exceeded:o}=Le(n,i);if(o)return{isError:!0,errorKind:`validation`,content:[{type:`text`,text:`fork rejected: delegation depth ${a} exceeds max ${i}`}]};let s=await e(t,a,r);return s.ok?{content:[{type:`text`,text:s.value||`(no output)`}]}:{content:[{type:`text`,text:`Fork failed: ${s.error}`}],isError:!0,errorKind:`runtime`}}async function mr(e,t,n,r,i,a){if(!e)return K(`task_delegate (mode=team)`,`Declare a team in .otto/teams/*.md.`);if(!t)return{content:[{type:`text`,text:`mode=team requires a team name`}],isError:!0,errorKind:`validation`};let o=await e(t,n,r,i,a);return o.success?{content:[{type:`text`,text:o.output??``}],details:{team:t,pattern:o.pattern}}:{content:[{type:`text`,text:`Team "${t}" failed: ${o.error}`}],isError:!0,errorKind:`runtime`}}function hr(e,t,n,r,i,a=1,o,s){let c=sr((s??[]).map(e=>({name:e.name,description:e.description,slot:e.preferredModelTier})));return{name:`task_delegate`,description:`Delegate work to a sub-agent, an isolated fork, or a declared team. Modes: sync/background/fork/team — semantics in the mode parameter. tracked:false (sync only, explicit subagent) → lightweight probe: no record, no retry, returns inline — use for quick agent opinions/reviews.`+(c?`\n${c}`:``),parameters:dr,isConcurrencySafe:e=>e?.mode===`fork`||e?.mode===`team`||e?.sessionMode!==`sticky`,lifecycleAsync:e=>(e?.mode===`sync`||e?.mode===void 0)&&e?.sessionMode!==`sticky`&&e?.tracked!==!1,lifecycleAsyncNote:`Note: the delegated sub-agent may edit files in the shared workspace while it runs. Until you receive its completion notice, do not read or write paths its task may touch.`,async execute({params:e,depth:s,signal:c,sessionId:l,parentTaskId:u}){if(e.tracked===!1){if((e.mode??`sync`)!==`sync`||e.category!==void 0||e.team!==void 0||e.sessionId!==void 0||e.sessionMode!==void 0||e.taskIds!==void 0||e.subagent===void 0)return{isError:!0,errorKind:`validation`,content:[{type:`text`,text:`tracked:false requires explicit subagent with mode=sync (no category/team/sessionId/sessionMode/taskIds)`}]};if(!o)return K(`task_delegate (tracked:false)`);let t=await o(e.subagent,e.prompt,{slot:e.slot,model:e.model,depth:s,signal:c,parentTaskId:u});return t.ok?{content:[{type:`text`,text:t.value||`(no output)`}]}:{content:[{type:`text`,text:`Agent call failed: ${t.error}`}],isError:!0,errorKind:`runtime`}}if(e.mode===`fork`)return pr(r,e.prompt,s,c,a);if(e.mode===`team`)return mr(i,e.team,e.prompt,c,s,u);if(!t)return K(`task_delegate`);let d=[];if(e.mode!==`background`&&n){let e=await n({action:`update`,todos:[],sessionId:l});e.ok&&(d=e.value)}let f=e.prompt;e.taskIds&&e.taskIds.length>0&&(f=`You are responsible for completing parent task item(s): ${e.taskIds.map(e=>`"${e}"`).join(`, `)}. When done, your host will auto-sync these to "done". No need to call write_todos for parent items.\n\n${e.prompt}`);let p=await t({prompt:f,subagent:e.subagent,category:e.category,mode:e.mode,sessionId:e.sessionId,sessionMode:e.sessionMode,slot:e.slot,model:e.model,depth:s,signal:c,parentTaskId:u});if(p.ok){let{id:t,status:r,output:i}=p.value,a=e.mode===`background`,o=a?void 0:await fr(n,l,e.taskIds,d),s=[];return a?s.push(`Task delegated in background${t?`: ${t}`:``}`):s.push(`Task delegated successfully`),i&&s.push(i),o&&s.push(o),{content:[{type:`text`,text:s.join(`
87
+ `)}],details:{mode:e.mode,status:r,sessionId:e.sessionId,sessionMode:e.sessionMode,...o?{todoSync:o}:{}}}}return{content:[{type:`text`,text:`Task delegation failed: ${p.error}`}],isError:!0,errorKind:`runtime`}}}}const gr=e.object({prompt:e.string().describe(`Self-contained instruction for the background job. It runs in an isolated git worktree with full tools (incl. file edits + MCP), so include all needed context.`),title:e.string().optional().describe(`Short label for the job (shown in the /jobs panel). Defaults to a slice of the prompt.`)});function _r(e){return{name:`delegate_job`,description:`Delegate a file-changing task to an isolated background job. Runs in a separate git worktree with full tools (edits + MCP), then produces a REVIEWABLE DIFF — it does NOT touch your working tree directly. Use for independent changes you want isolated and audited (e.g. a self-contained refactor) while you keep working. The diff is applied by the user via /job apply (autonomous apply is off by default). For read-only background analysis use task_delegate(background); for in-conversation edits use task_delegate(sync).`,parameters:gr,async execute({params:t,sessionId:n}){if(!e)return K(`delegate_job`);if(!n)return{isError:!0,errorKind:`validation`,content:[{type:`text`,text:`delegate_job requires a session context (no sessionId available).`}]};let r=t.title?.trim()||t.prompt.replace(/\s+/g,` `).slice(0,48),{jobId:i}=e({prompt:t.prompt,title:r,sessionId:n});return{content:[{type:`text`,text:`Delegated background job ${i} (isolated worktree). It will produce a reviewable diff; you'll get a notification when it's ready. The user applies it with /job apply ${i} (or reviews it in the /jobs panel). It does not modify the current working tree.`}],details:{jobId:i,origin:`main`}}}}}const vr=e.object({need:e.string().min(1).describe(`What capability is missing, in plain language (e.g. "query Jira issue status"). Describe the user-facing need, not an implementation.`),searchTerms:e.array(e.string().min(1)).min(1).describe(`Keywords to search the plugin ecosystem with (e.g. ["jira", "issue tracker"]). Use 2-4 distinct terms; a single narrow term often misses existing plugins.`)});var yr=class{gaps=new Map;record(e){let t=this.gaps.get(e.dedupKey)??{dedupKey:e.dedupKey,reportCount:0,dedupedCount:0,firstReportedAt:e.at,lastReportedAt:e.at,needs:[],searchTerms:e.searchTerms};t.reportCount+=1,e.deduped&&(t.dedupedCount+=1),t.needs.includes(e.need)||t.needs.push(e.need),t.searchTerms=e.searchTerms,t.lastReportedAt=e.at,this.gaps.set(e.dedupKey,t)}get(e){return this.gaps.get(e)}all(){return[...this.gaps.values()]}get size(){return this.gaps.size}};function br(e){return[...new Set(e.map(e=>e.trim().toLowerCase()).filter(Boolean))].sort().join(`|`)}function xr(e){return`plugin-${e.toLowerCase().replace(/[^a-z0-9]+/g,`-`).replace(/^-+|-+$/g,``).split(`-`).filter(Boolean).slice(0,3).join(`-`)||`custom`}`}function Sr(e,t={}){let n=new Set,r=t.now??(()=>Date.now()),i=t.recorder;return{name:`capability_gap`,description:`Report a capability you lack and discover whether the plugin ecosystem already provides it. Use this when the user asks for something you genuinely cannot do with your current tools — instead of only saying "I can't", check whether a plugin exists, and if not, get a blueprint for building one. This tool only returns guidance: it never installs anything (installation always requires explicit user approval).`,parameters:vr,async execute({params:t}){if(!e)return K(`capability_gap`);let a=br(t.searchTerms),o=n.has(a);if(n.add(a),i?.record({dedupKey:a,need:t.need,searchTerms:t.searchTerms,deduped:o,at:r()}),o)return{content:[{type:`text`,text:`Already reported this capability gap earlier in the session (search terms: ${t.searchTerms.join(`, `)}). Don't re-report it — either act on the earlier guidance, or tell the user plainly that this capability is unavailable.`}],details:{deduped:!0}};let s=await e(t.searchTerms);if(!s){let e=xr(t.need);return{content:[{type:`text`,text:`Could not reach the plugin registry (network or configuration issue), so it is unknown whether an existing plugin provides "${t.need}".\n\nYou can still offer to build it: ask the user whether they want a plugin scaffolded, then\n otto extension create ${e} --with-tool\nand use delegate_job to implement it in an isolated worktree (produces a reviewable diff).`}],details:{suggestion:`generate`,registryReachable:!1}}}let{hits:c,warnings:l}=s,u=l.length>0?`\n\nRegistry warnings:\n${l.map(e=>`- ${e}`).join(`
88
+ `)}`:``;if(c.length>0){let e=c.map(e=>{let t=e.capabilities?.length?` [declares: ${e.capabilities.join(`, `)}]`:``,n=e.stale?` (cached index may be out of date)`:``;return`- ${e.id} — ${e.description}${t} (from ${e.sourceName})${n}\n install: otto extension install ${e.id}`});return{content:[{type:`text`,text:`Found ${c.length} existing plugin(s) that may provide "${t.need}":\n\n${e.join(`
89
+ `)}\n\nTell the user what you found and let THEM run the install command — you cannot install it yourself, and installing high-risk capabilities requires their explicit approval.`+u}],details:{suggestion:`install`,hits:c.length}}}let d=xr(t.need);return{content:[{type:`text`,text:`No existing plugin in the ecosystem provides "${t.need}".\n\nYou can offer to build one. Blueprint:\n 1. Ask the user whether they want this capability added as a plugin.\n 2. Scaffold it: otto extension create ${d} --with-tool\n 3. Implement it with delegate_job (isolated worktree, produces a reviewable diff) — describe the tool's name, parameters, and behaviour in the job prompt.\n 4. The user reviews the diff (/job apply), then installs it — installation and any high-risk capability grants always go through their explicit approval.`+u}],details:{suggestion:`generate`,hits:0}}}}}const Cr=e.object({action:e.enum([`get`,`list`,`output`]).describe(`get task by ID, list all tasks, or get background task output`),taskId:e.string().optional().describe(`Task ID (required for get/output actions)`),statusFilter:e.enum([`all`,`pending`,`running`,`completed`,`failed`,`cancelled`]).optional().default(`all`).describe(`Filter by status (list action only)`)});function wr(e,t){let{get:n,list:r,output:i}=t;return{name:`task_inspect`,description:`Inspect tasks: get details by ID, list with optional status filter, or get background task output.`,parameters:Cr,readonly:!0,async execute({params:e}){switch(e.action){case`get`:{if(!n)return K(`task_inspect(get)`);if(!e.taskId)return{content:[{type:`text`,text:`taskId is required for get action`}],isError:!0,errorKind:`validation`};let t=await n(e.taskId);if(!t.ok)return{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`};let r=t.value,i=r.status===`failed`||!!r.error;return{content:[{type:`text`,text:[`Task: ${r.id}`,`Status: ${r.status}`,r.prompt?`Prompt: ${r.prompt}`:void 0,`Output: ${r.output??`N/A`}`,r.error?`Error: ${r.error}`:void 0].filter(e=>!!e).join(`
86
90
  `)}],isError:i,details:{task:r}}}case`list`:{if(!r)return K(`task_inspect(list)`);let t=await r(e.statusFilter===`all`?void 0:e.statusFilter);if(t.ok){let e=t.value;return e.length===0?{content:[{type:`text`,text:`No tasks found.`}]}:{content:[{type:`text`,text:e.map(e=>`- [${e.status}] ${e.id}: ${e.prompt.slice(0,80)}`).join(`
87
- `)}]}}throw Error(t.error)}case`output`:{if(!i)return K(`task_inspect(output)`);if(!e.taskId)return{content:[{type:`text`,text:`taskId is required for output action`}],isError:!0,errorKind:`validation`};let t=await i(e.taskId);if(t.ok){let{status:e,output:n}=t.value;return{content:[{type:`text`,text:`Status: ${e}\nOutput:\n${n??`(no output)`}`}],details:{status:e}}}return{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}}}}}const pr=e.object({action:e.enum([`cancel`,`retry`]).describe(`cancel a running task, or retry a failed task`),taskId:e.string().describe(`Task ID to act on`)});function mr(e,t){let{update:n,cancelBackground:r}=t;return{name:`task_control`,description:`Control tasks: cancel a running task or retry a failed one.`,parameters:pr,async execute({params:e}){switch(e.action){case`cancel`:if(n){let t=await n(e.taskId,`cancel`);return t.ok?{content:[{type:`text`,text:t.value}]}:{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}if(r){let t=await r(e.taskId);return t.ok?{content:[{type:`text`,text:`Task ${e.taskId} cancelled successfully`}]}:{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}return K(`task_control(cancel)`);case`retry`:{if(!n)return K(`task_control(retry)`);let t=await n(e.taskId,`retry`);return t.ok?{content:[{type:`text`,text:t.value}]}:{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}}}}}const hr=e.object({availableOnly:e.boolean().optional().describe(`Only list models usable by the current account (authenticated, not rate-limit-risky) — recommended before choosing a subagent model`)});function gr(e,t){return{name:`list_models`,description:"List models with their capability strengths (planning/knowledge/coding/reasoning/vision/speed/long-context) and usability. Use this to choose the best model for a sub-agent via the `model` param of task_delegate. Only pick models marked ✓ (usable); ✗ models are unauthenticated or rate-limit-risky and will be rejected.",parameters:hr,readonly:!0,isConcurrencySafe:()=>!0,async execute({params:n}){if(!e)return K(`list_models`);let r=await e(t);return n.availableOnly&&(r=r.filter(e=>e.usable!==!1)),r.length===0?{content:[{type:`text`,text:`No usable models. Run /login or /model refresh first.`}]}:{content:[{type:`text`,text:`Models (✓ = usable, ✗ = unauthenticated/rate-limit-risky):\n${r.map(e=>{let t=e.usable===!1?`✗`:`✓`,n=e.usable===!1&&e.unusableReason?` (${e.unusableReason})`:``;return`${t} ${e.id} [${e.strengths.join(`/`)||`general`}]${n}`}).join(`
88
- `)}`}]}}}}const _r=e.object({id:e.string().describe(`Unique todo identifier`),title:e.string().describe(`Short description of the task`),status:e.enum([`pending`,`in_progress`,`done`,`skipped`,`failed`]).describe(`Current status`)}),vr=e.object({title:e.string().optional().describe(`Optional title for the todo list (e.g. "Bug fixes", "Feature implementation"). Defaults to "Task" if not provided.`),action:e.enum([`set`,`update`]).describe(`set: replace all todos; update: merge by id`),todos:e.array(_r).describe(`Todo items`)});function yr(e){let t=e??Fe();return{name:`write_todos`,description:`Track progress with a lightweight todo list (shown pinned above the input). Use "set" to replace all todos, "update" to merge changes by id. Set a meaningful "title" to label the task list (e.g. "Bug fixes", "Feature implementation"). Defaults to "Task" if omitted. Use for multi-step tasks (3+ steps).`,parameters:vr,async execute({params:e,sessionId:n}){let r=await t({title:e.title,action:e.action,todos:e.todos,sessionId:n});if(r.ok){let e=r.value;return{content:[{type:`text`,text:e.map(e=>`[${e.status}] ${e.id}: ${e.title}`).join(`
89
- `)||`(empty todo list)`}],details:{todos:e}}}return{content:[{type:`text`,text:`Failed to update todos`}],isError:!0,errorKind:`runtime`}}}}const br=e.object({plan_status:e.string().optional().describe(`Current plan execution status summary`),recent_files:e.array(e.string()).optional().describe(`Recently modified file paths to focus on`),findings:e.array(e.string()).optional().describe(`Key findings or observations to record`)});function xr(e){return{name:`capture_file_state`,description:`Capture a snapshot of the current workspace file state.`,parameters:br,readonly:!0,async execute({params:t}){if(!e)return K(`capture_file_state`);let n=await e({planStatus:t.plan_status,recentFiles:t.recent_files,findings:t.findings});return n.success?{content:[{type:`text`,text:n.summary}],details:{timestamp:n.timestamp}}:{content:[{type:`text`,text:`Failed to capture file state`}],isError:!0,errorKind:`io`}}}}const Sr=e.object({tags:e.array(e.string()).describe(`Free-form tags categorizing this lesson`),trigger:e.string().describe(`What situation or pattern triggered this insight`),insight:e.string().describe(`The lesson learned — what to do or avoid`),scope:e.enum([`workspace`,`local`]).optional().default(`workspace`).describe(`Lesson scope (default 'workspace'). Use 'workspace' for durable, project-applicable lessons that hold on any machine. Use 'local' for machine-specific lessons (tied to this machine's local paths, installed tools/plugins, or environment quirks) that should never sync to other devices.`)});function Cr(e,t){return{name:`learn`,description:`Record an operational lesson learned during this session for future reference.`,parameters:Sr,async execute({params:n}){if(!t)return K(`learn`);let r=await t({tags:n.tags,trigger:n.trigger,insight:n.insight,sessionId:e,scope:n.scope});return r.success?{content:[{type:`text`,text:`${r.deduped?`Similar lesson already recorded, reusing`:`Lesson recorded`} (${r.lessonId}): [${n.tags.join(`, `)}] ${n.trigger} → ${n.insight}`}],details:{lessonId:r.lessonId,deduped:r.deduped??!1}}:{content:[{type:`text`,text:`Failed to record lesson: ${r.error??`Unknown error`}`}],isError:!0,errorKind:`runtime`}}}}const wr=e.object({query:e.string().describe(`Tool name or keyword to search for in the deferred tool pool`)});function Tr(e){return{name:`tool_search`,description:`Search for available tools not shown in the default tool list (e.g. MCP server tools). Use this to discover and load tool schemas on demand.`,parameters:wr,readonly:!0,async execute({params:t}){let n=e(),r=t.query.toLowerCase(),i=n.find(e=>e.name.toLowerCase()===r);if(i)return Er(i);let a=n.filter(e=>e.name.toLowerCase().includes(r)||typeof e.description==`string`&&e.description.toLowerCase().includes(r));if(a.length===0)return{content:[{type:`text`,text:`No deferred tools found matching "${t.query}". Available deferred tools: ${n.map(e=>e.name).join(`, `)||`(none)`}`}]};if(a.length===1)return Er(a[0]);let o=a.map(e=>`- **${e.name}**: ${typeof e.description==`string`?e.description.slice(0,100):`(no description)`}`).join(`
90
- `);return{content:[{type:`text`,text:`Multiple deferred tools match "${t.query}":\n\n${o}\n\nUse tool_search with the exact tool name to load its full schema.`}]}}}}function Er(e){let t=`(no parameters)`;if(e.parameters)try{let n=e.parameters;t=typeof n.toJSONSchema==`function`?JSON.stringify(n.toJSONSchema(),null,2):`(binary schema — parameters not introspectable)`}catch{t=`(schema serialization failed)`}let n=typeof e.description==`string`?e.description:``;return{content:[{type:`text`,text:[`## ${e.name}`,``,n?`${n}`:``,``,`**Parameters:**`,"```json",t,"```"].filter(Boolean).join(`
91
- `)}]}}const Dr={1:`File`,2:`Module`,3:`Namespace`,4:`Package`,5:`Class`,6:`Method`,7:`Property`,8:`Field`,9:`Constructor`,10:`Enum`,11:`Interface`,12:`Function`,13:`Variable`,14:`Constant`,15:`String`,16:`Number`,17:`Boolean`,18:`Array`,19:`Object`,20:`Key`,21:`Null`,22:`EnumMember`,23:`Struct`,24:`Event`,25:`Operator`,26:`TypeParameter`},Or={1:`error`,2:`warning`,3:`information`,4:`hint`},kr={".abap":`abap`,".bat":`bat`,".bib":`bibtex`,".bibtex":`bibtex`,".clj":`clojure`,".cljs":`clojure`,".cljc":`clojure`,".edn":`clojure`,".coffee":`coffeescript`,".c":`c`,".cpp":`cpp`,".cxx":`cpp`,".cc":`cpp`,".c++":`cpp`,".cs":`csharp`,".css":`css`,".d":`d`,".pas":`pascal`,".pascal":`pascal`,".diff":`diff`,".patch":`diff`,".dart":`dart`,".dockerfile":`dockerfile`,".ex":`elixir`,".exs":`elixir`,".erl":`erlang`,".hrl":`erlang`,".fs":`fsharp`,".fsi":`fsharp`,".fsx":`fsharp`,".fsscript":`fsharp`,".gitcommit":`git-commit`,".gitrebase":`git-rebase`,".go":`go`,".groovy":`groovy`,".gleam":`gleam`,".hbs":`handlebars`,".handlebars":`handlebars`,".hs":`haskell`,".html":`html`,".htm":`html`,".ini":`ini`,".java":`java`,".js":`javascript`,".jsx":`javascriptreact`,".json":`json`,".jsonc":`jsonc`,".tex":`latex`,".latex":`latex`,".less":`less`,".lua":`lua`,".makefile":`makefile`,makefile:`makefile`,".md":`markdown`,".markdown":`markdown`,".m":`objective-c`,".mm":`objective-cpp`,".pl":`perl`,".pm":`perl`,".pm6":`perl6`,".php":`php`,".ps1":`powershell`,".psm1":`powershell`,".pug":`jade`,".jade":`jade`,".py":`python`,".pyi":`python`,".r":`r`,".cshtml":`razor`,".razor":`razor`,".rb":`ruby`,".rake":`ruby`,".gemspec":`ruby`,".ru":`ruby`,".erb":`erb`,".html.erb":`erb`,".js.erb":`erb`,".css.erb":`erb`,".json.erb":`erb`,".rs":`rust`,".scss":`scss`,".sass":`sass`,".scala":`scala`,".shader":`shaderlab`,".sh":`shellscript`,".bash":`shellscript`,".zsh":`shellscript`,".ksh":`shellscript`,".sql":`sql`,".svelte":`svelte`,".swift":`swift`,".ts":`typescript`,".tsx":`typescriptreact`,".mts":`typescript`,".cts":`typescript`,".mtsx":`typescriptreact`,".ctsx":`typescriptreact`,".xml":`xml`,".xsl":`xsl`,".yaml":`yaml`,".yml":`yaml`,".mjs":`javascript`,".cjs":`javascript`,".vue":`vue`,".zig":`zig`,".zon":`zig`,".astro":`astro`,".ml":`ocaml`,".mli":`ocaml`,".tf":`terraform`,".tfvars":`terraform-vars`,".hcl":`hcl`,".nix":`nix`,".typ":`typst`,".typc":`typst`,".ets":`typescript`,".lhs":`haskell`,".kt":`kotlin`,".kts":`kotlin`,".prisma":`prisma`,".h":`c`,".hpp":`cpp`,".hh":`cpp`,".hxx":`cpp`,".h++":`cpp`,".objc":`objective-c`,".objcpp":`objective-cpp`,".fish":`fish`,".graphql":`graphql`,".gql":`graphql`},Ar={typescript:`npm install -g typescript-language-server typescript`,deno:`Install Deno from https://deno.land`,vue:`npm install -g @vue/language-server`,eslint:`npm install -g vscode-langservers-extracted`,oxlint:`npm install -g oxlint`,biome:`npm install -g @biomejs/biome`,gopls:`go install golang.org/x/tools/gopls@latest`,"ruby-lsp":`gem install ruby-lsp`,basedpyright:`pip install basedpyright`,pyright:`pip install pyright`,ty:`pip install ty`,ruff:`pip install ruff`,"elixir-ls":`See https://github.com/elixir-lsp/elixir-ls`,zls:`See https://github.com/zigtools/zls`,csharp:`dotnet tool install -g csharp-ls`,fsharp:`dotnet tool install -g fsautocomplete`,"sourcekit-lsp":`Included with Xcode or Swift toolchain`,rust:`rustup component add rust-analyzer`,clangd:`See https://clangd.llvm.org/installation`,svelte:`npm install -g svelte-language-server`,astro:`npm install -g @astrojs/language-server`,"bash-ls":`npm install -g bash-language-server`,jdtls:`See https://github.com/eclipse-jdtls/eclipse.jdt.ls`,"yaml-ls":`npm install -g yaml-language-server`,"lua-ls":`See https://github.com/LuaLS/lua-language-server`,php:`npm install -g intelephense`,dart:`Included with Dart SDK`,"terraform-ls":`See https://github.com/hashicorp/terraform-ls`,terraform:`See https://github.com/hashicorp/terraform-ls`,prisma:`npm install -g prisma`,"ocaml-lsp":`opam install ocaml-lsp-server`,texlab:`See https://github.com/latex-lsp/texlab`,dockerfile:`npm install -g dockerfile-language-server-nodejs`,gleam:`See https://gleam.run/getting-started/installing/`,"clojure-lsp":`See https://clojure-lsp.io/installation/`,nixd:`nix profile install nixpkgs#nixd`,tinymist:`See https://github.com/Myriad-Dreamin/tinymist`,"haskell-language-server":`ghcup install hls`,bash:`npm install -g bash-language-server`,"kotlin-ls":`See https://github.com/Kotlin/kotlin-lsp`},jr={typescript:{command:[`typescript-language-server`,`--stdio`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`]},deno:{command:[`deno`,`lsp`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`]},vue:{command:[`vue-language-server`,`--stdio`],extensions:[`.vue`]},eslint:{command:[`vscode-eslint-language-server`,`--stdio`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`,`.vue`]},oxlint:{command:[`oxlint`,`--lsp`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`,`.vue`,`.astro`,`.svelte`]},biome:{command:[`biome`,`lsp-proxy`,`--stdio`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`,`.json`,`.jsonc`,`.vue`,`.astro`,`.svelte`,`.css`,`.graphql`,`.gql`,`.html`]},gopls:{command:[`gopls`],extensions:[`.go`]},"ruby-lsp":{command:[`rubocop`,`--lsp`],extensions:[`.rb`,`.rake`,`.gemspec`,`.ru`]},basedpyright:{command:[`basedpyright-langserver`,`--stdio`],extensions:[`.py`,`.pyi`]},pyright:{command:[`pyright-langserver`,`--stdio`],extensions:[`.py`,`.pyi`]},ty:{command:[`ty`,`server`],extensions:[`.py`,`.pyi`]},ruff:{command:[`ruff`,`server`],extensions:[`.py`,`.pyi`]},"elixir-ls":{command:[`elixir-ls`],extensions:[`.ex`,`.exs`]},zls:{command:[`zls`],extensions:[`.zig`,`.zon`]},csharp:{command:[`csharp-ls`],extensions:[`.cs`]},fsharp:{command:[`fsautocomplete`],extensions:[`.fs`,`.fsi`,`.fsx`,`.fsscript`]},"sourcekit-lsp":{command:[`sourcekit-lsp`],extensions:[`.swift`,`.objc`,`.objcpp`]},rust:{command:[`rust-analyzer`],extensions:[`.rs`]},clangd:{command:[`clangd`,`--background-index`,`--clang-tidy`],extensions:[`.c`,`.cpp`,`.cc`,`.cxx`,`.c++`,`.h`,`.hpp`,`.hh`,`.hxx`,`.h++`]},svelte:{command:[`svelteserver`,`--stdio`],extensions:[`.svelte`]},astro:{command:[`astro-ls`,`--stdio`],extensions:[`.astro`]},bash:{command:[`bash-language-server`,`start`],extensions:[`.sh`,`.bash`,`.zsh`,`.ksh`]},"bash-ls":{command:[`bash-language-server`,`start`],extensions:[`.sh`,`.bash`,`.zsh`,`.ksh`]},jdtls:{command:[`jdtls`],extensions:[`.java`]},"yaml-ls":{command:[`yaml-language-server`,`--stdio`],extensions:[`.yaml`,`.yml`]},"lua-ls":{command:[`lua-language-server`],extensions:[`.lua`]},php:{command:[`intelephense`,`--stdio`],extensions:[`.php`]},dart:{command:[`dart`,`language-server`,`--lsp`],extensions:[`.dart`]},terraform:{command:[`terraform-ls`,`serve`],extensions:[`.tf`,`.tfvars`]},"terraform-ls":{command:[`terraform-ls`,`serve`],extensions:[`.tf`,`.tfvars`]},prisma:{command:[`prisma`,`language-server`],extensions:[`.prisma`]},"ocaml-lsp":{command:[`ocamllsp`],extensions:[`.ml`,`.mli`]},texlab:{command:[`texlab`],extensions:[`.tex`,`.bib`]},dockerfile:{command:[`docker-langserver`,`--stdio`],extensions:[`.dockerfile`]},gleam:{command:[`gleam`,`lsp`],extensions:[`.gleam`]},"clojure-lsp":{command:[`clojure-lsp`,`listen`],extensions:[`.clj`,`.cljs`,`.cljc`,`.edn`]},nixd:{command:[`nixd`],extensions:[`.nix`]},tinymist:{command:[`tinymist`],extensions:[`.typ`,`.typc`]},"haskell-language-server":{command:[`haskell-language-server-wrapper`,`--lsp`],extensions:[`.hs`,`.lhs`]},"kotlin-ls":{command:[`kotlin-lsp`],extensions:[`.kt`,`.kts`]}};function q(e){return Ie(e)}async function Mr(e,t){try{let n=(await k(e,`utf-8`)).split(`
91
+ `)}]}}throw Error(t.error)}case`output`:{if(!i)return K(`task_inspect(output)`);if(!e.taskId)return{content:[{type:`text`,text:`taskId is required for output action`}],isError:!0,errorKind:`validation`};let t=await i(e.taskId);if(t.ok){let{status:e,output:n}=t.value;return{content:[{type:`text`,text:`Status: ${e}\nOutput:\n${n??`(no output)`}`}],details:{status:e}}}return{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}}}}}const Tr=e.object({action:e.enum([`cancel`,`retry`]).describe(`cancel a running task, or retry a failed task`),taskId:e.string().describe(`Task ID to act on`)});function Er(e,t){let{update:n,cancelBackground:r}=t;return{name:`task_control`,description:`Control tasks: cancel a running task or retry a failed one.`,parameters:Tr,async execute({params:e}){switch(e.action){case`cancel`:if(n){let t=await n(e.taskId,`cancel`);return t.ok?{content:[{type:`text`,text:t.value}]}:{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}if(r){let t=await r(e.taskId);return t.ok?{content:[{type:`text`,text:`Task ${e.taskId} cancelled successfully`}]}:{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}return K(`task_control(cancel)`);case`retry`:{if(!n)return K(`task_control(retry)`);let t=await n(e.taskId,`retry`);return t.ok?{content:[{type:`text`,text:t.value}]}:{content:[{type:`text`,text:t.error}],isError:!0,errorKind:`runtime`}}}}}}const Dr=e.object({availableOnly:e.boolean().optional().describe(`Only list models usable by the current account (authenticated, not rate-limit-risky) — recommended before picking a subagent model`)});function Or(e,t){return{name:`list_models`,description:"List models with their capability strengths (planning/knowledge/coding/reasoning/vision/speed/long-context) and usability. Use this to choose the best model for a sub-agent via the `model` param of task_delegate. Only pick models marked ✓ (usable); ✗ models are unauthenticated or rate-limit-risky and will be rejected.",parameters:Dr,readonly:!0,isConcurrencySafe:()=>!0,async execute({params:n}){if(!e)return K(`list_models`);let r=await e(t);return n.availableOnly&&(r=r.filter(e=>e.usable!==!1)),r.length===0?{content:[{type:`text`,text:`No usable models. Run /login or /model refresh first.`}]}:{content:[{type:`text`,text:`Models (✓ = usable, ✗ = unauthenticated/rate-limit-risky):\n${r.map(e=>{let t=e.usable===!1?`✗`:`✓`,n=e.usable===!1&&e.unusableReason?` (${e.unusableReason})`:``;return`${t} ${e.id} [${e.strengths.join(`/`)||`general`}]${n}`}).join(`
92
+ `)}`}]}}}}const kr=e.object({id:e.string().describe(`Unique identifier`),title:e.string().describe(`Short description of the task`),status:e.enum([`pending`,`in_progress`,`done`,`skipped`,`failed`]).describe(`Current status`)}),Ar=e.object({title:e.string().optional().describe(`Optional list title (e.g. "Bug fixes"); defaults to "Task".`),action:e.enum([`set`,`update`]).describe(`set: replace all todos; update: merge by id`),todos:e.array(kr).describe(`Todo items`)});function jr(e){let t=e??Re();return{name:`write_todos`,description:`Track progress with a lightweight todo list (shown pinned above the input). "set" replaces all todos; "update" merges by id. Optional "title" labels the list (defaults to "Task"). Use for multi-step tasks (3+ steps).`,parameters:Ar,async execute({params:e,sessionId:n}){let r=await t({title:e.title,action:e.action,todos:e.todos,sessionId:n});if(r.ok){let e=r.value;return{content:[{type:`text`,text:e.map(e=>`[${e.status}] ${e.id}: ${e.title}`).join(`
93
+ `)||`(empty todo list)`}],details:{todos:e}}}return{content:[{type:`text`,text:`Failed to update todos`}],isError:!0,errorKind:`runtime`}}}}const Mr=e.object({plan_status:e.string().optional().describe(`Current plan execution status summary`),recent_files:e.array(e.string()).optional().describe(`Recently modified file paths to focus on`),findings:e.array(e.string()).optional().describe(`Key findings or observations to record`)});function Nr(e){return{name:`capture_file_state`,description:`Capture a snapshot of the current workspace file state.`,parameters:Mr,readonly:!0,async execute({params:t}){if(!e)return K(`capture_file_state`);let n=await e({planStatus:t.plan_status,recentFiles:t.recent_files,findings:t.findings});return n.success?{content:[{type:`text`,text:n.summary}],details:{timestamp:n.timestamp}}:{content:[{type:`text`,text:`Failed to capture file state`}],isError:!0,errorKind:`io`}}}}const Pr=e.object({tags:e.array(e.string()).describe(`Free-form tags categorizing this lesson`),trigger:e.string().describe(`What situation or pattern triggered this insight`),insight:e.string().describe(`The lesson learned — what to do or avoid`),scope:e.enum([`workspace`,`local`]).optional().default(`workspace`).describe(`Lesson scope (default 'workspace'). Use 'workspace' for durable, project-applicable lessons that hold on any machine. Use 'local' for machine-specific lessons (tied to this machine's local paths, installed tools/plugins, or environment quirks) that should never sync to other devices.`)});function Fr(e,t){return{name:`learn`,description:`Record an operational lesson learned during this session for future reference.`,parameters:Pr,async execute({params:n}){if(!t)return K(`learn`);let r=await t({tags:n.tags,trigger:n.trigger,insight:n.insight,sessionId:e,scope:n.scope});return r.success?{content:[{type:`text`,text:`${r.deduped?`Similar lesson already recorded, reusing`:`Lesson recorded`} (${r.lessonId}): [${n.tags.join(`, `)}] ${n.trigger} → ${n.insight}`}],details:{lessonId:r.lessonId,deduped:r.deduped??!1}}:{content:[{type:`text`,text:`Failed to record lesson: ${r.error??`Unknown error`}`}],isError:!0,errorKind:`runtime`}}}}const Ir=e.object({query:e.string().describe(`Tool name or keyword to search for in the deferred tool pool`)});function Lr(e){return{name:`tool_search`,description:`Search for available tools not shown in the default tool list (e.g. MCP server tools). Use this to discover and load tool schemas on demand.`,parameters:Ir,readonly:!0,async execute({params:t}){let n=e(),r=t.query.toLowerCase(),i=n.find(e=>e.name.toLowerCase()===r);if(i)return Rr(i);let a=n.filter(e=>e.name.toLowerCase().includes(r)||typeof e.description==`string`&&e.description.toLowerCase().includes(r));if(a.length===0)return{content:[{type:`text`,text:`No deferred tools found matching "${t.query}". Available deferred tools: ${n.map(e=>e.name).join(`, `)||`(none)`}`}]};if(a.length===1)return Rr(a[0]);let o=a.map(e=>`- **${e.name}**: ${typeof e.description==`string`?e.description.slice(0,100):`(no description)`}`).join(`
94
+ `);return{content:[{type:`text`,text:`Multiple deferred tools match "${t.query}":\n\n${o}\n\nUse tool_search with the exact tool name to load its full schema.`}]}}}}function Rr(e){let t=`(no parameters)`;if(e.parameters)try{let n=e.parameters;t=typeof n.toJSONSchema==`function`?JSON.stringify(n.toJSONSchema(),null,2):`(binary schema — parameters not introspectable)`}catch{t=`(schema serialization failed)`}let n=typeof e.description==`string`?e.description:``;return{content:[{type:`text`,text:[`## ${e.name}`,``,n?`${n}`:``,``,`**Parameters:**`,"```json",t,"```"].filter(Boolean).join(`
95
+ `)}]}}const zr={1:`File`,2:`Module`,3:`Namespace`,4:`Package`,5:`Class`,6:`Method`,7:`Property`,8:`Field`,9:`Constructor`,10:`Enum`,11:`Interface`,12:`Function`,13:`Variable`,14:`Constant`,15:`String`,16:`Number`,17:`Boolean`,18:`Array`,19:`Object`,20:`Key`,21:`Null`,22:`EnumMember`,23:`Struct`,24:`Event`,25:`Operator`,26:`TypeParameter`},Br={1:`error`,2:`warning`,3:`information`,4:`hint`},Vr={".abap":`abap`,".bat":`bat`,".bib":`bibtex`,".bibtex":`bibtex`,".clj":`clojure`,".cljs":`clojure`,".cljc":`clojure`,".edn":`clojure`,".coffee":`coffeescript`,".c":`c`,".cpp":`cpp`,".cxx":`cpp`,".cc":`cpp`,".c++":`cpp`,".cs":`csharp`,".css":`css`,".d":`d`,".pas":`pascal`,".pascal":`pascal`,".diff":`diff`,".patch":`diff`,".dart":`dart`,".dockerfile":`dockerfile`,".ex":`elixir`,".exs":`elixir`,".erl":`erlang`,".hrl":`erlang`,".fs":`fsharp`,".fsi":`fsharp`,".fsx":`fsharp`,".fsscript":`fsharp`,".gitcommit":`git-commit`,".gitrebase":`git-rebase`,".go":`go`,".groovy":`groovy`,".gleam":`gleam`,".hbs":`handlebars`,".handlebars":`handlebars`,".hs":`haskell`,".html":`html`,".htm":`html`,".ini":`ini`,".java":`java`,".js":`javascript`,".jsx":`javascriptreact`,".json":`json`,".jsonc":`jsonc`,".tex":`latex`,".latex":`latex`,".less":`less`,".lua":`lua`,".makefile":`makefile`,makefile:`makefile`,".md":`markdown`,".markdown":`markdown`,".m":`objective-c`,".mm":`objective-cpp`,".pl":`perl`,".pm":`perl`,".pm6":`perl6`,".php":`php`,".ps1":`powershell`,".psm1":`powershell`,".pug":`jade`,".jade":`jade`,".py":`python`,".pyi":`python`,".r":`r`,".cshtml":`razor`,".razor":`razor`,".rb":`ruby`,".rake":`ruby`,".gemspec":`ruby`,".ru":`ruby`,".erb":`erb`,".html.erb":`erb`,".js.erb":`erb`,".css.erb":`erb`,".json.erb":`erb`,".rs":`rust`,".scss":`scss`,".sass":`sass`,".scala":`scala`,".shader":`shaderlab`,".sh":`shellscript`,".bash":`shellscript`,".zsh":`shellscript`,".ksh":`shellscript`,".sql":`sql`,".svelte":`svelte`,".swift":`swift`,".ts":`typescript`,".tsx":`typescriptreact`,".mts":`typescript`,".cts":`typescript`,".mtsx":`typescriptreact`,".ctsx":`typescriptreact`,".xml":`xml`,".xsl":`xsl`,".yaml":`yaml`,".yml":`yaml`,".mjs":`javascript`,".cjs":`javascript`,".vue":`vue`,".zig":`zig`,".zon":`zig`,".astro":`astro`,".ml":`ocaml`,".mli":`ocaml`,".tf":`terraform`,".tfvars":`terraform-vars`,".hcl":`hcl`,".nix":`nix`,".typ":`typst`,".typc":`typst`,".ets":`typescript`,".lhs":`haskell`,".kt":`kotlin`,".kts":`kotlin`,".prisma":`prisma`,".h":`c`,".hpp":`cpp`,".hh":`cpp`,".hxx":`cpp`,".h++":`cpp`,".objc":`objective-c`,".objcpp":`objective-cpp`,".fish":`fish`,".graphql":`graphql`,".gql":`graphql`},Hr={typescript:`npm install -g typescript-language-server typescript`,deno:`Install Deno from https://deno.land`,vue:`npm install -g @vue/language-server`,eslint:`npm install -g vscode-langservers-extracted`,oxlint:`npm install -g oxlint`,biome:`npm install -g @biomejs/biome`,gopls:`go install golang.org/x/tools/gopls@latest`,"ruby-lsp":`gem install ruby-lsp`,basedpyright:`pip install basedpyright`,pyright:`pip install pyright`,ty:`pip install ty`,ruff:`pip install ruff`,"elixir-ls":`See https://github.com/elixir-lsp/elixir-ls`,zls:`See https://github.com/zigtools/zls`,csharp:`dotnet tool install -g csharp-ls`,fsharp:`dotnet tool install -g fsautocomplete`,"sourcekit-lsp":`Included with Xcode or Swift toolchain`,rust:`rustup component add rust-analyzer`,clangd:`See https://clangd.llvm.org/installation`,svelte:`npm install -g svelte-language-server`,astro:`npm install -g @astrojs/language-server`,"bash-ls":`npm install -g bash-language-server`,jdtls:`See https://github.com/eclipse-jdtls/eclipse.jdt.ls`,"yaml-ls":`npm install -g yaml-language-server`,"lua-ls":`See https://github.com/LuaLS/lua-language-server`,php:`npm install -g intelephense`,dart:`Included with Dart SDK`,"terraform-ls":`See https://github.com/hashicorp/terraform-ls`,terraform:`See https://github.com/hashicorp/terraform-ls`,prisma:`npm install -g prisma`,"ocaml-lsp":`opam install ocaml-lsp-server`,texlab:`See https://github.com/latex-lsp/texlab`,dockerfile:`npm install -g dockerfile-language-server-nodejs`,gleam:`See https://gleam.run/getting-started/installing/`,"clojure-lsp":`See https://clojure-lsp.io/installation/`,nixd:`nix profile install nixpkgs#nixd`,tinymist:`See https://github.com/Myriad-Dreamin/tinymist`,"haskell-language-server":`ghcup install hls`,bash:`npm install -g bash-language-server`,"kotlin-ls":`See https://github.com/Kotlin/kotlin-lsp`},Ur={typescript:{command:[`typescript-language-server`,`--stdio`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`]},deno:{command:[`deno`,`lsp`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`]},vue:{command:[`vue-language-server`,`--stdio`],extensions:[`.vue`]},eslint:{command:[`vscode-eslint-language-server`,`--stdio`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`,`.vue`]},oxlint:{command:[`oxlint`,`--lsp`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`,`.vue`,`.astro`,`.svelte`]},biome:{command:[`biome`,`lsp-proxy`,`--stdio`],extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mjs`,`.cjs`,`.mts`,`.cts`,`.json`,`.jsonc`,`.vue`,`.astro`,`.svelte`,`.css`,`.graphql`,`.gql`,`.html`]},gopls:{command:[`gopls`],extensions:[`.go`]},"ruby-lsp":{command:[`rubocop`,`--lsp`],extensions:[`.rb`,`.rake`,`.gemspec`,`.ru`]},basedpyright:{command:[`basedpyright-langserver`,`--stdio`],extensions:[`.py`,`.pyi`]},pyright:{command:[`pyright-langserver`,`--stdio`],extensions:[`.py`,`.pyi`]},ty:{command:[`ty`,`server`],extensions:[`.py`,`.pyi`]},ruff:{command:[`ruff`,`server`],extensions:[`.py`,`.pyi`]},"elixir-ls":{command:[`elixir-ls`],extensions:[`.ex`,`.exs`]},zls:{command:[`zls`],extensions:[`.zig`,`.zon`]},csharp:{command:[`csharp-ls`],extensions:[`.cs`]},fsharp:{command:[`fsautocomplete`],extensions:[`.fs`,`.fsi`,`.fsx`,`.fsscript`]},"sourcekit-lsp":{command:[`sourcekit-lsp`],extensions:[`.swift`,`.objc`,`.objcpp`]},rust:{command:[`rust-analyzer`],extensions:[`.rs`]},clangd:{command:[`clangd`,`--background-index`,`--clang-tidy`],extensions:[`.c`,`.cpp`,`.cc`,`.cxx`,`.c++`,`.h`,`.hpp`,`.hh`,`.hxx`,`.h++`]},svelte:{command:[`svelteserver`,`--stdio`],extensions:[`.svelte`]},astro:{command:[`astro-ls`,`--stdio`],extensions:[`.astro`]},bash:{command:[`bash-language-server`,`start`],extensions:[`.sh`,`.bash`,`.zsh`,`.ksh`]},"bash-ls":{command:[`bash-language-server`,`start`],extensions:[`.sh`,`.bash`,`.zsh`,`.ksh`]},jdtls:{command:[`jdtls`],extensions:[`.java`]},"yaml-ls":{command:[`yaml-language-server`,`--stdio`],extensions:[`.yaml`,`.yml`]},"lua-ls":{command:[`lua-language-server`],extensions:[`.lua`]},php:{command:[`intelephense`,`--stdio`],extensions:[`.php`]},dart:{command:[`dart`,`language-server`,`--lsp`],extensions:[`.dart`]},terraform:{command:[`terraform-ls`,`serve`],extensions:[`.tf`,`.tfvars`]},"terraform-ls":{command:[`terraform-ls`,`serve`],extensions:[`.tf`,`.tfvars`]},prisma:{command:[`prisma`,`language-server`],extensions:[`.prisma`]},"ocaml-lsp":{command:[`ocamllsp`],extensions:[`.ml`,`.mli`]},texlab:{command:[`texlab`],extensions:[`.tex`,`.bib`]},dockerfile:{command:[`docker-langserver`,`--stdio`],extensions:[`.dockerfile`]},gleam:{command:[`gleam`,`lsp`],extensions:[`.gleam`]},"clojure-lsp":{command:[`clojure-lsp`,`listen`],extensions:[`.clj`,`.cljs`,`.cljc`,`.edn`]},nixd:{command:[`nixd`],extensions:[`.nix`]},tinymist:{command:[`tinymist`],extensions:[`.typ`,`.typc`]},"haskell-language-server":{command:[`haskell-language-server-wrapper`,`--lsp`],extensions:[`.hs`,`.lhs`]},"kotlin-ls":{command:[`kotlin-lsp`],extensions:[`.kt`,`.kts`]}};function q(e){return ze(e)}function J(e,t){if(t===void 0)return!0;let n=N(P(t),P(e));return n===``||!n.startsWith(`..`+Ee)&&n!==`..`&&!Te(n)}async function Wr(e,t){try{let n=(await k(e,`utf-8`)).split(`
92
96
  `),r=[...t].sort((e,t)=>t.range.start.line===e.range.start.line?t.range.start.character-e.range.start.character:t.range.start.line-e.range.start.line);for(let e of r){let{start:t,end:r}=e.range;if(t.line===r.line){let i=n[t.line]||``;n[t.line]=i.substring(0,t.character)+e.newText+i.substring(r.character)}else{let i=n[t.line]||``,a=n[r.line]||``,o=i.substring(0,t.character)+e.newText+a.substring(r.character);n.splice(t.line,r.line-t.line+1,...o.split(`
93
97
  `))}}return await A(e,n.join(`
94
- `),`utf-8`),{success:!0,editCount:t.length}}catch(e){return{success:!1,editCount:0,error:e instanceof Error?e.message:String(e)}}}async function Nr(e){if(!e)return{success:!1,filesModified:[],totalEdits:0,errors:[`No edit provided`]};let t={success:!0,filesModified:[],totalEdits:0,errors:[]};if(e.changes)for(let[n,r]of Object.entries(e.changes)){let e=q(n),i=await Mr(e,r);i.success?(t.filesModified.push(e),t.totalEdits+=i.editCount):(t.success=!1,t.errors.push(`${e}: ${i.error}`))}if(e.documentChanges)for(let n of e.documentChanges)if(`kind`in n)try{if(n.kind===`create`){let e=q(n.uri);(!T(e)||n.options?.overwrite)&&(await A(e,``,`utf-8`),t.filesModified.push(e))}else if(n.kind===`rename`){let e=q(n.oldUri),r=q(n.newUri);await ve(e,r),t.filesModified.push(r)}else if(n.kind===`delete`){let e=q(n.uri);await be(e),t.filesModified.push(e)}}catch(e){t.success=!1,t.errors.push(`${n.kind} ${n.uri??``}: ${e}`)}else{let e=q(n.textDocument.uri),r=await Mr(e,n.edits);r.success?(t.filesModified.push(e),t.totalEdits+=r.editCount):(t.success=!1,t.errors.push(`${e}: ${r.error}`))}return t}function Pr(e){return`targetUri`in e?`${q(e.targetUri)}:${e.targetRange.start.line+1}:${e.targetRange.start.character}`:`${q(e.uri)}:${e.range.start.line+1}:${e.range.start.character}`}function Fr(e){return Dr[e]||`Unknown(${e})`}function Ir(e){return e?Or[e]||`unknown(${e})`:`unknown`}function Lr(e,t=0){let n=` `.repeat(t),r=Fr(e.kind),i=e.range.start.line+1,a=`${n}${e.name} (${r}) - line ${i}`;if(e.children&&e.children.length>0)for(let n of e.children)a+=`
95
- `+Lr(n,t+1);return a}function Rr(e){let t=Fr(e.kind),n=Pr(e.location),r=e.containerName?` (in ${e.containerName})`:``;return`${e.name} (${t})${r} - ${n}`}function zr(e){let t=Ir(e.severity),n=e.range.start.line+1,r=e.range.start.character;return`${t}${e.source?`[${e.source}]`:``}${e.code?` (${e.code})`:``} at ${n}:${r}: ${e.message}`}function Br(e,t){if(!t||t===`all`)return e;let n={error:1,warning:2,information:3,hint:4}[t];return e.filter(e=>e.severity===n)}function Vr(e){return e?`defaultBehavior`in e?e.defaultBehavior?`Rename supported (using default behavior)`:`Cannot rename at this position`:`range`in e&&e.range?`Rename available at ${e.range.start.line+1}:${e.range.start.character}-${e.range.end.line+1}:${e.range.end.character}${e.placeholder?` (current: "${e.placeholder}")`:``}`:`start`in e&&`end`in e?`Rename available at ${e.start.line+1}:${e.start.character}-${e.end.line+1}:${e.end.character}`:`Cannot rename at this position`:`Cannot rename at this position`}function Hr(e){let t=[];if(e.success){t.push(`Applied ${e.totalEdits} edit(s) to ${e.filesModified.length} file(s):`);for(let n of e.filesModified)t.push(` - ${n}`)}else{t.push(`Failed to apply some changes:`);for(let n of e.errors)t.push(` Error: ${n}`);e.filesModified.length>0&&t.push(`Successfully modified: ${e.filesModified.join(`, `)}`)}return t.join(`
96
- `)}function Ur(e,t){return new Promise((n,r)=>{let i=!1,a=e=>{i||(i=!0,clearTimeout(o),e())},o=setTimeout(()=>a(n),t);e.once(`spawn`,()=>a(n)),e.once(`error`,e=>a(()=>r(e instanceof Error?e:Error(String(e))))),e.once(`exit`,e=>a(()=>r(Error(`process exited early with code ${e}`))))})}function Wr(){let e=new Map;return{wait(t,n){return new Promise(r=>{let i=!1,a=()=>{i||(i=!0,clearTimeout(o),e.get(t)?.delete(a),r())},o=setTimeout(a,n),s=e.get(t)??new Set;s.add(a),e.set(t,s)})},notify(t){let n=e.get(t);if(n){e.delete(t);for(let e of n)e()}},clear(){e.clear()}}}function Gr(e,t){return e.map(e=>e.section&&t&&Object.prototype.hasOwnProperty.call(t,e.section)?t[e.section]:e.section===`json`?{validate:{enable:!0}}:{})}const J=class e{static instance;clients=new Map;cleanupInterval=null;IDLE_TIMEOUT=300*1e3;INIT_TIMEOUT=60*1e3;processTracker;constructor(){this.startCleanupTimer()}static getInstance(){return e.instance||=new e,e.instance}setProcessTracker(e){this.processTracker=e}getKey(e,t){return`${e}::${t}`}startCleanupTimer(){this.cleanupInterval||(this.cleanupInterval=setInterval(()=>this.cleanupIdleClients(),6e4),this.cleanupInterval.unref&&this.cleanupInterval.unref())}cleanupIdleClients(){let e=Date.now();for(let[t,n]of this.clients)n.refCount===0&&e-n.lastUsedAt>this.IDLE_TIMEOUT&&(n.client.stop(),this.clients.delete(t))}async getClient(e,t){let n=this.getKey(e,t.id),r=this.clients.get(n);if(r){let e=Date.now();if(r.isInitializing&&r.initializingSince!==void 0&&e-r.initializingSince>=this.INIT_TIMEOUT){try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}}if(r){if(r.initPromise)try{await r.initPromise}catch{try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}if(r){if(r.client.isAlive())return r.refCount++,r.lastUsedAt=Date.now(),r.client;try{await r.client.stop()}catch{}this.clients.delete(n)}}let i=new qr(e,t,this.processTracker),a=Date.now(),o=(async()=>{await i.start(),await i.initialize()})();this.clients.set(n,{client:i,lastUsedAt:a,refCount:1,initPromise:o,isInitializing:!0,initializingSince:a});try{await o}catch(e){this.clients.delete(n);try{await i.stop()}catch{}throw e}let s=this.clients.get(n);return s&&(s.initPromise=void 0,s.isInitializing=!1,s.initializingSince=void 0),i}warmupClient(e,t){let n=this.getKey(e,t.id);if(this.clients.has(n))return;let r=new qr(e,t,this.processTracker),i=Date.now(),a=(async()=>{await r.start(),await r.initialize()})();this.clients.set(n,{client:r,lastUsedAt:i,refCount:0,initPromise:a,isInitializing:!0,initializingSince:i}),a.then(()=>{let e=this.clients.get(n);e&&(e.initPromise=void 0,e.isInitializing=!1,e.initializingSince=void 0)}).catch(()=>{this.clients.delete(n),r.stop().catch(()=>{})})}releaseClient(e,t){let n=this.getKey(e,t),r=this.clients.get(n);r&&r.refCount>0&&(r.refCount--,r.lastUsedAt=Date.now())}isServerInitializing(e,t){let n=this.getKey(e,t);return this.clients.get(n)?.isInitializing??!1}async stopAll(){for(let[,e]of this.clients)await e.client.stop();this.clients.clear(),this.cleanupInterval&&=(clearInterval(this.cleanupInterval),null)}}.getInstance(),Y=i(`@x-otto/tools:lsp`),Kr=32*1024*1024;function X(e){let t=Buffer.from(e,`utf-8`);return Buffer.concat([Buffer.from(`Content-Length: ${t.byteLength}\r\n\r\n`,`ascii`),t])}var qr=class{process=null;nextId=1;pending=new Map;buffer=Buffer.alloc(0);contentLength=-1;processExited=!1;stderrBuffer=[];diagnosticsStore=new Map;diagnosticsWaiter=Wr();openedFiles=new Set;documentVersions=new Map;lastSyncedText=new Map;REQUEST_TIMEOUT=15e3;constructor(e,t,n){this.root=e,this.server=t,this.processTracker=n}async start(){let e=Jr(this.root);if(!e.valid)throw H(`[LSP] ${e.error}`,`validation`);if(this.server.command.length===0)throw H(`[LSP] Server command is empty`,`validation`);let[t,...n]=this.server.command;this.process=F(t,n,{cwd:this.root,env:r(this.server.env),stdio:[`pipe`,`pipe`,`pipe`],detached:!0}),this.processTracker?.registerChild({command:t,args:n,owner:{type:`lsp-server`,id:this.root},category:`lsp`,lifecycle:`pinned`,cwd:this.root},this.process);let i=this.process;i.on(`exit`,e=>{this.processExited=!0,Y.debug(`LSP server exited with code ${e}`),this.rejectAllPending(Error(`LSP server exited with code ${e??`null`}`))}),i.on(`error`,e=>{this.processExited=!0,Y.error(`LSP spawn error: ${e.message}`),this.rejectAllPending(Error(`LSP spawn error: ${e.message}`))});try{await Ur(i,2e3)}catch(e){let t=this.stderrBuffer.join(`
97
- `);throw H(`LSP server failed to start: ${e.message}`+(t?`\nstderr: ${t}`:``),`runtime`)}if(i.exitCode!==null){let e=this.stderrBuffer.join(`
98
- `);throw H(`LSP server exited immediately with code ${i.exitCode}`+(e?`\nstderr: ${e}`:``),`runtime`)}i.stdout?.on(`data`,e=>{this.processBuffer(e)}),i.stderr?.on(`data`,e=>{let t=e.toString(`utf-8`);this.stderrBuffer.push(t),this.stderrBuffer.length>100&&this.stderrBuffer.shift()})}async initialize(){let e=L(this.root).href;await this.sendRequest(`initialize`,{processId:process.pid,rootUri:e,rootPath:this.root,workspaceFolders:[{uri:e,name:`workspace`}],capabilities:{textDocument:{hover:{contentFormat:[`markdown`,`plaintext`]},definition:{linkSupport:!0},references:{},documentSymbol:{hierarchicalDocumentSymbolSupport:!0},publishDiagnostics:{},rename:{prepareSupport:!0,prepareSupportDefaultBehavior:1,honorsChangeAnnotations:!0},codeAction:{codeActionLiteralSupport:{codeActionKind:{valueSet:[`quickfix`,`refactor`,`refactor.extract`,`refactor.inline`,`refactor.rewrite`,`source`,`source.organizeImports`,`source.fixAll`]}},isPreferredSupport:!0,disabledSupport:!0,dataSupport:!0,resolveSupport:{properties:[`edit`,`command`]}}},workspace:{symbol:{},workspaceFolders:!0,configuration:!0,applyEdit:!0,workspaceEdit:{documentChanges:!0}}},...this.server.initialization}),this.sendNotification(`initialized`),this.sendNotification(`workspace/didChangeConfiguration`,{settings:this.server.settings??{json:{validate:{enable:!0}}}})}rejectAllPending(e){for(let[,t]of this.pending)clearTimeout(t.timer),t.reject(e);this.pending.clear()}async stop(){if(this.rejectAllPending(Error(`LSP client stopping`)),!this.processExited&&this.process){try{this.sendNotification(`shutdown`),this.sendNotification(`exit`)}catch{}let e=this.process;this.process=null;let t,n=await Promise.race([new Promise(t=>e.on(`exit`,()=>t(!0))),new Promise(e=>{t=setTimeout(()=>e(!1),5e3)})]);if(t&&clearTimeout(t),!n){Y.debug(`LSP process did not exit within timeout, sending SIGKILL`);try{e.kill(`SIGKILL`)}catch{}}}this.processExited=!0,this.process=null,this.diagnosticsStore.clear(),this.diagnosticsWaiter.clear(),this.openedFiles.clear(),this.documentVersions.clear(),this.lastSyncedText.clear()}isAlive(){return this.process!==null&&!this.processExited&&this.process.exitCode===null}processBuffer(e){if(this.buffer=this.buffer.length===0?e:Buffer.concat([this.buffer,e]),this.buffer.length>Kr&&this.contentLength===-1){Y.warn(`LSP buffer exceeded ${Kr} bytes without finding header terminator; resetting`),this.buffer=Buffer.alloc(0),this.contentLength=-1;return}for(;;){if(this.contentLength===-1){let e=this.buffer.indexOf(`\r
98
+ `),`utf-8`),{success:!0,editCount:t.length}}catch(e){return{success:!1,editCount:0,error:e instanceof Error?e.message:String(e)}}}async function Gr(e,t){if(!e)return{success:!1,filesModified:[],totalEdits:0,errors:[`No edit provided`]};let n={success:!0,filesModified:[],totalEdits:0,errors:[]};if(e.changes)for(let[r,i]of Object.entries(e.changes)){let e=q(r);if(!J(e,t)){n.success=!1,n.errors.push(`${e}: refused (outside project root)`);continue}let a=await Wr(e,i);a.success?(n.filesModified.push(e),n.totalEdits+=a.editCount):(n.success=!1,n.errors.push(`${e}: ${a.error}`))}if(e.documentChanges)for(let r of e.documentChanges)if(`kind`in r)try{if(r.kind===`create`){let e=q(r.uri);if(!J(e,t)){n.success=!1,n.errors.push(`create ${e}: refused (outside project root)`);continue}(!D(e)||r.options?.overwrite)&&(await A(e,``,`utf-8`),n.filesModified.push(e))}else if(r.kind===`rename`){let e=q(r.oldUri),i=q(r.newUri);if(!J(e,t)||!J(i,t)){n.success=!1,n.errors.push(`rename ${e}→${i}: refused (outside project root)`);continue}await ye(e,i),n.filesModified.push(i)}else if(r.kind===`delete`){let e=q(r.uri);if(!J(e,t)){n.success=!1,n.errors.push(`delete ${e}: refused (outside project root)`);continue}await xe(e),n.filesModified.push(e)}}catch(e){n.success=!1,n.errors.push(`${r.kind} ${r.uri??``}: ${e}`)}else{let e=q(r.textDocument.uri),t=await Wr(e,r.edits);t.success?(n.filesModified.push(e),n.totalEdits+=t.editCount):(n.success=!1,n.errors.push(`${e}: ${t.error}`))}return n}function Kr(e){return`targetUri`in e?`${q(e.targetUri)}:${e.targetRange.start.line+1}:${e.targetRange.start.character}`:`${q(e.uri)}:${e.range.start.line+1}:${e.range.start.character}`}function qr(e){return zr[e]||`Unknown(${e})`}function Jr(e){return e?Br[e]||`unknown(${e})`:`unknown`}function Yr(e,t=0){let n=` `.repeat(t),r=qr(e.kind),i=e.range.start.line+1,a=`${n}${e.name} (${r}) - line ${i}`;if(e.children&&e.children.length>0)for(let n of e.children)a+=`
99
+ `+Yr(n,t+1);return a}function Xr(e){let t=qr(e.kind),n=Kr(e.location),r=e.containerName?` (in ${e.containerName})`:``;return`${e.name} (${t})${r} - ${n}`}function Zr(e){let t=Jr(e.severity),n=e.range.start.line+1,r=e.range.start.character;return`${t}${e.source?`[${e.source}]`:``}${e.code?` (${e.code})`:``} at ${n}:${r}: ${e.message}`}function Qr(e,t){if(!t||t===`all`)return e;let n={error:1,warning:2,information:3,hint:4}[t];return e.filter(e=>e.severity===n)}function $r(e){return e?`defaultBehavior`in e?e.defaultBehavior?`Rename supported (using default behavior)`:`Cannot rename at this position`:`range`in e&&e.range?`Rename available at ${e.range.start.line+1}:${e.range.start.character}-${e.range.end.line+1}:${e.range.end.character}${e.placeholder?` (current: "${e.placeholder}")`:``}`:`start`in e&&`end`in e?`Rename available at ${e.start.line+1}:${e.start.character}-${e.end.line+1}:${e.end.character}`:`Cannot rename at this position`:`Cannot rename at this position`}function ei(e){let t=[];if(e.success){t.push(`Applied ${e.totalEdits} edit(s) to ${e.filesModified.length} file(s):`);for(let n of e.filesModified)t.push(` - ${n}`)}else{t.push(`Failed to apply some changes:`);for(let n of e.errors)t.push(` Error: ${n}`);e.filesModified.length>0&&t.push(`Successfully modified: ${e.filesModified.join(`, `)}`)}return t.join(`
100
+ `)}function ti(e,t){return new Promise((n,r)=>{let i=!1,a=e=>{i||(i=!0,clearTimeout(o),e())},o=setTimeout(()=>a(n),t);e.once(`spawn`,()=>a(n)),e.once(`error`,e=>a(()=>r(e instanceof Error?e:Error(String(e))))),e.once(`exit`,e=>a(()=>r(Error(`process exited early with code ${e}`))))})}function ni(){let e=new Map;return{wait(t,n){return new Promise(r=>{let i=!1,a=()=>{i||(i=!0,clearTimeout(o),e.get(t)?.delete(a),r())},o=setTimeout(a,n),s=e.get(t)??new Set;s.add(a),e.set(t,s)})},notify(t){let n=e.get(t);if(n){e.delete(t);for(let e of n)e()}},clear(){e.clear()}}}function ri(e,t){return e.map(e=>e.section&&t&&Object.prototype.hasOwnProperty.call(t,e.section)?t[e.section]:e.section===`json`?{validate:{enable:!0}}:{})}const Y=class e{static instance;clients=new Map;cleanupInterval=null;IDLE_TIMEOUT=300*1e3;INIT_TIMEOUT=60*1e3;processTracker;constructor(){this.startCleanupTimer()}static getInstance(){return e.instance||=new e,e.instance}setProcessTracker(e){this.processTracker=e}getKey(e,t){return`${e}::${t}`}startCleanupTimer(){this.cleanupInterval||(this.cleanupInterval=setInterval(()=>this.cleanupIdleClients(),6e4),this.cleanupInterval.unref&&this.cleanupInterval.unref())}cleanupIdleClients(){let e=Date.now();for(let[t,n]of this.clients)n.refCount===0&&e-n.lastUsedAt>this.IDLE_TIMEOUT&&(n.client.stop(),this.clients.delete(t))}async getClient(e,t){let n=this.getKey(e,t.id),r=this.clients.get(n);if(r){let e=Date.now();if(r.isInitializing&&r.initializingSince!==void 0&&e-r.initializingSince>=this.INIT_TIMEOUT){try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}}if(r){if(r.initPromise)try{await r.initPromise}catch{try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}if(r){if(r.client.isAlive())return r.refCount++,r.lastUsedAt=Date.now(),r.client;try{await r.client.stop()}catch{}this.clients.delete(n)}}let i=new oi(e,t,this.processTracker),a=Date.now(),o=(async()=>{await i.start(),await i.initialize()})();this.clients.set(n,{client:i,lastUsedAt:a,refCount:1,initPromise:o,isInitializing:!0,initializingSince:a});try{await o}catch(e){this.clients.delete(n);try{await i.stop()}catch{}throw e}let s=this.clients.get(n);return s&&(s.initPromise=void 0,s.isInitializing=!1,s.initializingSince=void 0),i}warmupClient(e,t){let n=this.getKey(e,t.id);if(this.clients.has(n))return;let r=new oi(e,t,this.processTracker),i=Date.now(),a=(async()=>{await r.start(),await r.initialize()})();this.clients.set(n,{client:r,lastUsedAt:i,refCount:0,initPromise:a,isInitializing:!0,initializingSince:i}),a.then(()=>{let e=this.clients.get(n);e&&(e.initPromise=void 0,e.isInitializing=!1,e.initializingSince=void 0)}).catch(()=>{this.clients.delete(n),r.stop().catch(()=>{})})}releaseClient(e,t){let n=this.getKey(e,t),r=this.clients.get(n);r&&r.refCount>0&&(r.refCount--,r.lastUsedAt=Date.now())}isServerInitializing(e,t){let n=this.getKey(e,t);return this.clients.get(n)?.isInitializing??!1}async stopAll(){for(let[,e]of this.clients)await e.client.stop();this.clients.clear(),this.cleanupInterval&&=(clearInterval(this.cleanupInterval),null)}}.getInstance(),X=a(`@x-otto/tools:lsp`),ii=32*1024*1024;function ai(e){let t=Buffer.from(e,`utf-8`);return Buffer.concat([Buffer.from(`Content-Length: ${t.byteLength}\r\n\r\n`,`ascii`),t])}var oi=class{process=null;nextId=1;pending=new Map;buffer=Buffer.alloc(0);contentLength=-1;processExited=!1;stderrBuffer=[];diagnosticsStore=new Map;diagnosticsWaiter=ni();openedFiles=new Set;documentVersions=new Map;lastSyncedText=new Map;REQUEST_TIMEOUT=15e3;constructor(e,t,n){this.root=e,this.server=t,this.processTracker=n}async start(){let e=si(this.root);if(!e.valid)throw H(`[LSP] ${e.error}`,`validation`);if(this.server.command.length===0)throw H(`[LSP] Server command is empty`,`validation`);let[t,...n]=this.server.command;this.process=F(t,n,{cwd:this.root,env:i(this.server.env),stdio:[`pipe`,`pipe`,`pipe`],detached:!0}),this.processTracker?.registerChild({command:t,args:n,owner:{type:`lsp-server`,id:this.root},category:`lsp`,lifecycle:`pinned`,cwd:this.root},this.process);let r=this.process;r.on(`exit`,e=>{this.processExited=!0,X.debug(`LSP server exited with code ${e}`),this.rejectAllPending(Error(`LSP server exited with code ${e??`null`}`))}),r.on(`error`,e=>{this.processExited=!0,X.error(`LSP spawn error: ${e.message}`),this.rejectAllPending(Error(`LSP spawn error: ${e.message}`))});try{await ti(r,2e3)}catch(e){let t=this.stderrBuffer.join(`
101
+ `);throw H(`LSP server failed to start: ${e.message}`+(t?`\nstderr: ${t}`:``),`runtime`)}if(r.exitCode!==null){let e=this.stderrBuffer.join(`
102
+ `);throw H(`LSP server exited immediately with code ${r.exitCode}`+(e?`\nstderr: ${e}`:``),`runtime`)}r.stdout?.on(`data`,e=>{this.processBuffer(e)}),r.stderr?.on(`data`,e=>{let t=e.toString(`utf-8`);this.stderrBuffer.push(t),this.stderrBuffer.length>100&&this.stderrBuffer.shift()})}async initialize(){let e=L(this.root).href;await this.sendRequest(`initialize`,{processId:process.pid,rootUri:e,rootPath:this.root,workspaceFolders:[{uri:e,name:`workspace`}],capabilities:{textDocument:{hover:{contentFormat:[`markdown`,`plaintext`]},definition:{linkSupport:!0},references:{},documentSymbol:{hierarchicalDocumentSymbolSupport:!0},publishDiagnostics:{},rename:{prepareSupport:!0,prepareSupportDefaultBehavior:1,honorsChangeAnnotations:!0},codeAction:{codeActionLiteralSupport:{codeActionKind:{valueSet:[`quickfix`,`refactor`,`refactor.extract`,`refactor.inline`,`refactor.rewrite`,`source`,`source.organizeImports`,`source.fixAll`]}},isPreferredSupport:!0,disabledSupport:!0,dataSupport:!0,resolveSupport:{properties:[`edit`,`command`]}}},workspace:{symbol:{},workspaceFolders:!0,configuration:!0,applyEdit:!0,workspaceEdit:{documentChanges:!0}}},...this.server.initialization}),this.sendNotification(`initialized`),this.sendNotification(`workspace/didChangeConfiguration`,{settings:this.server.settings??{json:{validate:{enable:!0}}}})}rejectAllPending(e){for(let[,t]of this.pending)clearTimeout(t.timer),t.reject(e);this.pending.clear()}async stop(){if(this.rejectAllPending(Error(`LSP client stopping`)),!this.processExited&&this.process){try{this.sendNotification(`shutdown`),this.sendNotification(`exit`)}catch{}let e=this.process;this.process=null;let t,n=await Promise.race([new Promise(t=>e.on(`exit`,()=>t(!0))),new Promise(e=>{t=setTimeout(()=>e(!1),5e3)})]);if(t&&clearTimeout(t),!n){X.debug(`LSP process did not exit within timeout, sending SIGKILL`);try{e.kill(`SIGKILL`)}catch{}}}this.processExited=!0,this.process=null,this.diagnosticsStore.clear(),this.diagnosticsWaiter.clear(),this.openedFiles.clear(),this.documentVersions.clear(),this.lastSyncedText.clear()}isAlive(){return this.process!==null&&!this.processExited&&this.process.exitCode===null}processBuffer(e){if(this.buffer=this.buffer.length===0?e:Buffer.concat([this.buffer,e]),this.buffer.length>ii&&this.contentLength===-1){X.warn(`LSP buffer exceeded ${ii} bytes without finding header terminator; resetting`),this.buffer=Buffer.alloc(0),this.contentLength=-1;return}for(;;){if(this.contentLength===-1){let e=this.buffer.indexOf(`\r
99
103
  \r
100
104
  `);if(e===-1)break;let t=this.buffer.subarray(0,e).toString(`ascii`);for(let e of t.split(`\r
101
- `))if(e.startsWith(`Content-Length: `)){let t=parseInt(e.slice(16),10);Number.isInteger(t)&&t>=0&&t<=Kr&&(this.contentLength=t)}if(this.contentLength===-1){this.buffer=this.buffer.subarray(e+4);continue}this.buffer=this.buffer.subarray(e+4)}if(this.buffer.length<this.contentLength)break;let e=this.buffer.subarray(0,this.contentLength).toString(`utf-8`);this.buffer=this.buffer.subarray(this.contentLength),this.contentLength=-1;try{this.onMessage(JSON.parse(e))}catch(e){Y.error(`Failed to parse LSP message: ${e}`)}}}onMessage(e){if(`id`in e&&(`result`in e||`error`in e)){let t=e.id,n=this.pending.get(t);if(n)if(this.pending.delete(t),clearTimeout(n.timer),`error`in e){let t=e.error;n.reject(Error(`LSP error ${t.code??``}: ${t.message}`))}else n.resolve(e.result);return}let t=e.method;if(!t)return;if(!(`id`in e)){if(t===`textDocument/publishDiagnostics`){let t=e.params;t.uri&&(this.diagnosticsStore.set(t.uri,t.diagnostics??[]),this.diagnosticsWaiter.notify(t.uri))}return}let n=e.id;if(t===`workspace/configuration`){let t=e.params;this.respond(n,Gr(t?.items??[],this.server.settings))}else this.respond(n,null)}sendRequest(e,t){if(!this.process?.stdin?.writable)throw H(`LSP client not started`,`runtime`);if(this.processExited||this.process&&this.process.exitCode!==null){let e=this.stderrBuffer.slice(-10).join(`
105
+ `))if(e.startsWith(`Content-Length: `)){let t=parseInt(e.slice(16),10);Number.isInteger(t)&&t>=0&&t<=ii&&(this.contentLength=t)}if(this.contentLength===-1){this.buffer=this.buffer.subarray(e+4);continue}this.buffer=this.buffer.subarray(e+4)}if(this.buffer.length<this.contentLength)break;let e=this.buffer.subarray(0,this.contentLength).toString(`utf-8`);this.buffer=this.buffer.subarray(this.contentLength),this.contentLength=-1;try{this.onMessage(JSON.parse(e))}catch(e){X.error(`Failed to parse LSP message: ${e}`)}}}onMessage(e){if(`id`in e&&(`result`in e||`error`in e)){let t=e.id,n=this.pending.get(t);if(n)if(this.pending.delete(t),clearTimeout(n.timer),`error`in e){let t=e.error;n.reject(Error(`LSP error ${t.code??``}: ${t.message}`))}else n.resolve(e.result);return}let t=e.method;if(!t)return;if(!(`id`in e)){if(t===`textDocument/publishDiagnostics`){let t=e.params;t.uri&&(this.diagnosticsStore.set(t.uri,t.diagnostics??[]),this.diagnosticsWaiter.notify(t.uri))}return}let n=e.id;if(t===`workspace/configuration`){let t=e.params;this.respond(n,ri(t?.items??[],this.server.settings))}else this.respond(n,null)}sendRequest(e,t){if(!this.process?.stdin?.writable)throw H(`LSP client not started`,`runtime`);if(this.processExited||this.process&&this.process.exitCode!==null){let e=this.stderrBuffer.slice(-10).join(`
102
106
  `);throw H(`LSP server already exited (code: ${this.process?.exitCode})`+(e?`\nstderr: ${e}`:``),`runtime`)}let n=this.nextId++;return new Promise((r,i)=>{let a=setTimeout(()=>{this.pending.delete(n);let t=this.stderrBuffer.slice(-5).join(`
103
- `);i(Error(`LSP request timeout (method: ${e})`+(t?`\nrecent stderr: ${t}`:``)))},this.REQUEST_TIMEOUT);this.pending.set(n,{resolve:r,reject:i,timer:a});let o=JSON.stringify({jsonrpc:`2.0`,id:n,method:e,params:t});this.process.stdin.write(X(o))})}sendNotification(e,t){if(!this.process?.stdin?.writable||this.processExited||this.process&&this.process.exitCode!==null)return;let n=JSON.stringify({jsonrpc:`2.0`,method:e,params:t});this.process.stdin.write(X(n))}respond(e,t){if(!this.process?.stdin?.writable)return;let n=JSON.stringify({jsonrpc:`2.0`,id:e,result:t});this.process.stdin.write(X(n))}async openFile(e){let t=P(e),n=L(t).href,r=await k(t,`utf-8`);if(!this.openedFiles.has(t)){let e=kr[Ce(t)]||`plaintext`;this.sendNotification(`textDocument/didOpen`,{textDocument:{uri:n,languageId:e,version:1,text:r}}),this.openedFiles.add(t),this.documentVersions.set(n,1),this.lastSyncedText.set(n,r),await this.diagnosticsWaiter.wait(n,2e3);return}if(this.lastSyncedText.get(n)===r)return;let i=(this.documentVersions.get(n)??1)+1;this.documentVersions.set(n,i),this.lastSyncedText.set(n,r),this.sendNotification(`textDocument/didChange`,{textDocument:{uri:n,version:i},contentChanges:[{text:r}]}),this.sendNotification(`textDocument/didSave`,{textDocument:{uri:n},text:r})}async definition(e,t,n){let r=P(e);return await this.openFile(r),this.sendRequest(`textDocument/definition`,{textDocument:{uri:L(r).href},position:{line:t-1,character:n}})}async references(e,t,n,r=!0){let i=P(e);return await this.openFile(i),this.sendRequest(`textDocument/references`,{textDocument:{uri:L(i).href},position:{line:t-1,character:n},context:{includeDeclaration:r}})}async documentSymbols(e){let t=P(e);return await this.openFile(t),this.sendRequest(`textDocument/documentSymbol`,{textDocument:{uri:L(t).href}})}async workspaceSymbols(e){return this.sendRequest(`workspace/symbol`,{query:e})}async diagnostics(e){let t=P(e),n=L(t).href;await this.openFile(t);try{let e=await this.sendRequest(`textDocument/diagnostic`,{textDocument:{uri:n}});if(e&&typeof e==`object`&&`items`in e)return e}catch{}return{items:this.diagnosticsStore.get(n)??[]}}async prepareRename(e,t,n){let r=P(e);return await this.openFile(r),this.sendRequest(`textDocument/prepareRename`,{textDocument:{uri:L(r).href},position:{line:t-1,character:n}})}async rename(e,t,n,r){let i=P(e);return await this.openFile(i),this.sendRequest(`textDocument/rename`,{textDocument:{uri:L(i).href},position:{line:t-1,character:n},newName:r})}};function Jr(e){try{return T(e)?O(e).isDirectory()?{valid:!0}:{valid:!1,error:`Path is not a directory: ${e}`}:{valid:!1,error:`Working directory does not exist: ${e}`}}catch(t){return{valid:!1,error:`Cannot access working directory: ${e} (${t instanceof Error?t.message:String(t)})`}}}function Yr(e){if(e.length===0)return!1;let t=e[0];if((t.includes(`/`)||t.includes(`\\`))&&T(t))return!0;let n=process.platform===`win32`,r=[``];if(n){let e=process.env.PATHEXT||``;if(e){let t=e.split(`;`).filter(Boolean);r=[...new Set([...r,...t,`.exe`,`.cmd`,`.bat`,`.ps1`])]}else r=[``,`.exe`,`.cmd`,`.bat`,`.ps1`]}let i=process.env.PATH||``;n&&!i&&(i=process.env.Path||``);let a=n?`;`:`:`,o=i.split(a);for(let e of o)for(let n of r)if(T(M(e,t+n)))return!0;let s=l(),c=[M(De,`node_modules`,`.bin`),M(s,`bin`),M(s,`node_modules`,`.bin`)];for(let e of c)for(let n of r)if(T(M(e,t+n)))return!0;return t===`node`}function Xr(e){if(!T(e))return null;try{return v(fe(e,`utf-8`))}catch{return null}}function Zr(e){let t=e+`.jsonc`;if(T(t))return t;let n=e+`.json`;return T(n)?n:t}function Qr(){let e=l();return{project:Zr(M(De,`otto`)),user:Zr(M(e,`otto`))}}let $r;function ei(){if($r)return $r;let e=Qr(),t=new Map,n=Xr(e.project);n&&t.set(`project`,n);let r=Xr(e.user);return r&&t.set(`user`,r),$r=t,t}function ti(){let e=ei(),t=[],n=new Set,r=new Set;for(let i of[`project`,`user`]){let a=e.get(i);if(a?.lsp)for(let[e,o]of Object.entries(a.lsp)){if(o.disabled){n.add(e);continue}r.has(e)||!o.command||!o.extensions||(t.push({id:e,command:o.command,extensions:o.extensions,priority:o.priority??0,env:o.env,initialization:o.initialization,source:i}),r.add(e))}}for(let[e,i]of Object.entries(jr))n.has(e)||r.has(e)||t.push({id:e,command:i.command,extensions:i.extensions,priority:-100,source:`builtin`});return t.sort((e,t)=>{if(e.source!==t.source){let n={project:0,user:1,builtin:2};return(n[e.source]??2)-(n[t.source]??2)}return t.priority-e.priority})}function ni(e){let t=ti();for(let n of t)if(n.extensions.includes(e)&&Yr(n.command))return{status:`found`,server:{id:n.id,command:n.command,extensions:n.extensions,priority:n.priority,env:n.env,initialization:n.initialization}};for(let n of t)if(n.extensions.includes(e)){let e=Ar[n.id]||`Install '${n.command[0]}' and ensure it's in your PATH`;return{status:`not_installed`,server:{id:n.id,command:n.command,extensions:n.extensions},installHint:e}}return{status:`not_configured`,extension:e,availableServers:[...new Set(t.map(e=>e.id))]}}const ri=[`.git`,`package.json`,`pyproject.toml`,`Cargo.toml`,`go.mod`,`pom.xml`,`build.gradle`];function ii(e){let t=P(e);(!T(t)||!O(t).isDirectory())&&(t=j(t));let n=``;for(;t!==n;){for(let e of ri)if(T(M(t,e)))return t;n=t,t=j(t)}return j(P(e))}function ai(e){if(e.status===`not_installed`){let{server:t,installHint:n}=e;return[`LSP server '${t.id}' is configured but NOT INSTALLED.`,``,`Command not found: ${t.command[0]}`,``,`To install:`,` ${n}`,``,`Supported extensions: ${t.extensions.join(`, `)}`,``,`After installation, the server will be available automatically.`].join(`
107
+ `);i(Error(`LSP request timeout (method: ${e})`+(t?`\nrecent stderr: ${t}`:``)))},this.REQUEST_TIMEOUT);this.pending.set(n,{resolve:r,reject:i,timer:a});let o=JSON.stringify({jsonrpc:`2.0`,id:n,method:e,params:t});this.process.stdin.write(ai(o))})}sendNotification(e,t){if(!this.process?.stdin?.writable||this.processExited||this.process&&this.process.exitCode!==null)return;let n=JSON.stringify({jsonrpc:`2.0`,method:e,params:t});this.process.stdin.write(ai(n))}respond(e,t){if(!this.process?.stdin?.writable)return;let n=JSON.stringify({jsonrpc:`2.0`,id:e,result:t});this.process.stdin.write(ai(n))}async openFile(e){let t=P(e),n=L(t).href,r=await k(t,`utf-8`);if(!this.openedFiles.has(t)){let e=Vr[we(t)]||`plaintext`;this.sendNotification(`textDocument/didOpen`,{textDocument:{uri:n,languageId:e,version:1,text:r}}),this.openedFiles.add(t),this.documentVersions.set(n,1),this.lastSyncedText.set(n,r),await this.diagnosticsWaiter.wait(n,2e3);return}if(this.lastSyncedText.get(n)===r)return;let i=(this.documentVersions.get(n)??1)+1;this.documentVersions.set(n,i),this.lastSyncedText.set(n,r),this.sendNotification(`textDocument/didChange`,{textDocument:{uri:n,version:i},contentChanges:[{text:r}]}),this.sendNotification(`textDocument/didSave`,{textDocument:{uri:n},text:r})}async definition(e,t,n){let r=P(e);return await this.openFile(r),this.sendRequest(`textDocument/definition`,{textDocument:{uri:L(r).href},position:{line:t-1,character:n}})}async references(e,t,n,r=!0){let i=P(e);return await this.openFile(i),this.sendRequest(`textDocument/references`,{textDocument:{uri:L(i).href},position:{line:t-1,character:n},context:{includeDeclaration:r}})}async documentSymbols(e){let t=P(e);return await this.openFile(t),this.sendRequest(`textDocument/documentSymbol`,{textDocument:{uri:L(t).href}})}async workspaceSymbols(e){return this.sendRequest(`workspace/symbol`,{query:e})}async diagnostics(e){let t=P(e),n=L(t).href;await this.openFile(t);try{let e=await this.sendRequest(`textDocument/diagnostic`,{textDocument:{uri:n}});if(e&&typeof e==`object`&&`items`in e)return e}catch{}return{items:this.diagnosticsStore.get(n)??[]}}async prepareRename(e,t,n){let r=P(e);return await this.openFile(r),this.sendRequest(`textDocument/prepareRename`,{textDocument:{uri:L(r).href},position:{line:t-1,character:n}})}async rename(e,t,n,r){let i=P(e);return await this.openFile(i),this.sendRequest(`textDocument/rename`,{textDocument:{uri:L(i).href},position:{line:t-1,character:n},newName:r})}};function si(e){try{return D(e)?ge(e).isDirectory()?{valid:!0}:{valid:!1,error:`Path is not a directory: ${e}`}:{valid:!1,error:`Working directory does not exist: ${e}`}}catch(t){return{valid:!1,error:`Cannot access working directory: ${e} (${t instanceof Error?t.message:String(t)})`}}}function ci(e){if(e.length===0)return!1;let t=e[0];if((t.includes(`/`)||t.includes(`\\`))&&D(t))return!0;let n=process.platform===`win32`,r=[``];if(n){let e=process.env.PATHEXT||``;if(e){let t=e.split(`;`).filter(Boolean);r=[...new Set([...r,...t,`.exe`,`.cmd`,`.bat`,`.ps1`])]}else r=[``,`.exe`,`.cmd`,`.bat`,`.ps1`]}let i=process.env.PATH||``;n&&!i&&(i=process.env.Path||``);let a=n?`;`:`:`,o=i.split(a);for(let e of o)for(let n of r)if(D(M(e,t+n)))return!0;let s=u(),c=[M(Ae,`node_modules`,`.bin`),M(s,`bin`),M(s,`node_modules`,`.bin`)];for(let e of c)for(let n of r)if(D(M(e,t+n)))return!0;return t===`node`}function li(e){if(!D(e))return null;try{return y(fe(e,`utf-8`))}catch{return null}}function ui(e){let t=e+`.jsonc`;if(D(t))return t;let n=e+`.json`;return D(n)?n:t}function di(){let e=u();return{project:ui(M(Ae,`otto`)),user:ui(M(e,`otto`))}}let fi;function pi(){if(fi)return fi;let e=di(),t=new Map,n=li(e.project);n&&t.set(`project`,n);let r=li(e.user);return r&&t.set(`user`,r),fi=t,t}function mi(){let e=pi(),t=[],n=new Set,r=new Set;for(let i of[`project`,`user`]){let a=e.get(i);if(a?.lsp)for(let[e,o]of Object.entries(a.lsp)){if(o.disabled){n.add(e);continue}r.has(e)||!o.command||!o.extensions||(t.push({id:e,command:o.command,extensions:o.extensions,priority:o.priority??0,env:o.env,initialization:o.initialization,source:i}),r.add(e))}}for(let[e,i]of Object.entries(Ur))n.has(e)||r.has(e)||t.push({id:e,command:i.command,extensions:i.extensions,priority:-100,source:`builtin`});return t.sort((e,t)=>{if(e.source!==t.source){let n={project:0,user:1,builtin:2};return(n[e.source]??2)-(n[t.source]??2)}return t.priority-e.priority})}function hi(e){let t=mi();for(let n of t)if(n.extensions.includes(e)&&ci(n.command))return{status:`found`,server:{id:n.id,command:n.command,extensions:n.extensions,priority:n.priority,env:n.env,initialization:n.initialization}};for(let n of t)if(n.extensions.includes(e)){let e=Hr[n.id]||`Install '${n.command[0]}' and ensure it's in your PATH`;return{status:`not_installed`,server:{id:n.id,command:n.command,extensions:n.extensions},installHint:e}}return{status:`not_configured`,extension:e,availableServers:[...new Set(t.map(e=>e.id))]}}const gi=[`.git`,`package.json`,`pyproject.toml`,`Cargo.toml`,`go.mod`,`pom.xml`,`build.gradle`];function _i(e){let t=P(e);(!D(t)||!ge(t).isDirectory())&&(t=j(t));let n=``;for(;t!==n;){for(let e of gi)if(D(M(t,e)))return t;n=t,t=j(t)}return j(P(e))}function vi(e){if(e.status===`not_installed`){let{server:t,installHint:n}=e;return[`LSP server '${t.id}' is configured but NOT INSTALLED.`,``,`Command not found: ${t.command[0]}`,``,`To install:`,` ${n}`,``,`Supported extensions: ${t.extensions.join(`, `)}`,``,`After installation, the server will be available automatically.`].join(`
104
108
  `)}return[`No LSP server configured for extension: ${e.extension}`,``,`Available servers: ${e.availableServers.slice(0,10).join(`, `)}${e.availableServers.length>10?`...`:``}`,``,`To add a custom server, configure 'lsp' in otto.jsonc:`,` {`,` "lsp": {`,` "my-server": {`,` "command": ["my-lsp", "--stdio"],`,` "extensions": ["${e.extension}"]`,` }`,` }`,` }`].join(`
105
- `)}async function Z(e,t){let n=P(e),r=ni(Ce(n));if(r.status!==`found`)throw H(ai(r),`not_found`);let i=r.server,a=ii(n),o=await J.getClient(a,i);try{return await t(o)}catch(e){throw e instanceof Error&&e.message.includes(`timeout`)&&J.isServerInitializing(a,i.id)?H(`LSP server is still initializing. Please retry in a few seconds. Original error: ${e.message}`,`timeout`):e}finally{J.releaseClient(a,i.id)}}const oi=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`)});function si(e){return{name:`lsp_goto_definition`,description:`Jump to symbol definition. Find WHERE something is defined.`,parameters:oi,pathParams:[`filePath`],readonly:!0,async execute({params:e}){let t=await Z(e.filePath,async t=>await t.definition(e.filePath,e.line,e.character));if(!t)return{content:[{type:`text`,text:`No definition found`}]};let n=Array.isArray(t)?t:[t];return n.length===0?{content:[{type:`text`,text:`No definition found`}]}:{content:[{type:`text`,text:n.map(Pr).join(`
106
- `)}]}}}}const ci=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`),includeDeclaration:e.boolean().optional().describe(`Include the declaration itself (default true)`)});function li(e){return{name:`lsp_find_references`,description:`Find ALL usages/references of a symbol across the entire workspace.`,parameters:ci,pathParams:[`filePath`],readonly:!0,async execute({params:e}){let t=await Z(e.filePath,async t=>await t.references(e.filePath,e.line,e.character,e.includeDeclaration??!0));if(!t||t.length===0)return{content:[{type:`text`,text:`No references found`}]};let n=t.length,r=n>200,i=(r?t.slice(0,200):t).map(Pr);return r&&i.unshift(`Found ${n} references (showing first 200):`),{content:[{type:`text`,text:i.join(`
107
- `)}]}}}}const ui=e.object({filePath:e.string().describe(`File path for LSP context`),scope:e.enum([`document`,`workspace`]).default(`document`).describe(`'document' for file symbols, 'workspace' for project-wide search`),query:e.string().optional().describe(`Symbol name to search (required for workspace scope)`),limit:e.number().int().min(1).max(500).optional().describe(`Max results (default/max 200)`)});function di(e){return{name:`lsp_symbols`,description:`Get symbols from file (document) or search across workspace. Use scope='document' for file outline, scope='workspace' for project-wide symbol search.`,parameters:ui,pathParams:[`filePath`],readonly:!0,async execute({params:e}){if((e.scope??`document`)===`workspace`){if(!e.query)return{content:[{type:`text`,text:`Error: 'query' is required for workspace scope`}],isError:!0,errorKind:`validation`};let t=await Z(e.filePath,async t=>await t.workspaceSymbols(e.query));if(!t||t.length===0)return{content:[{type:`text`,text:`No symbols found`}]};let n=t.length,r=Math.min(e.limit??200,200),i=n>r,a=t.slice(0,r).map(Rr);return i&&a.unshift(`Found ${n} symbols (showing first ${r}):`),{content:[{type:`text`,text:a.join(`
108
- `)}]}}let t=await Z(e.filePath,async t=>await t.documentSymbols(e.filePath));if(!t||t.length===0)return{content:[{type:`text`,text:`No symbols found`}]};let n=t.length,r=Math.min(e.limit??200,200),i=n>r,a=i?t.slice(0,r):t,o=[];return i&&o.push(`Found ${n} symbols (showing first ${r}):`),a[0]&&`range`in a[0]?o.push(...a.map(e=>Lr(e))):o.push(...a.map(Rr)),{content:[{type:`text`,text:o.join(`
109
- `)}]}}}}const fi=e.object({filePath:e.string().describe(`Absolute path to the file`),severity:e.enum([`error`,`warning`,`information`,`hint`,`all`]).optional().describe(`Filter by severity level`)});function pi(e){return{name:`lsp_diagnostics`,description:`Get errors, warnings, hints from language server BEFORE running build.`,parameters:fi,pathParams:[`filePath`],readonly:!0,async execute({params:e}){let t=await Z(e.filePath,async t=>await t.diagnostics(e.filePath)),n=[];if(t&&(Array.isArray(t)?n=t:t.items&&(n=t.items)),n=Br(n,e.severity),n.length===0)return{content:[{type:`text`,text:`No diagnostics found`}]};let r=n.length,i=r>200,a=(i?n.slice(0,200):n).map(zr);return i&&a.unshift(`Found ${r} diagnostics (showing first 200):`),{content:[{type:`text`,text:a.join(`
110
- `)}]}}}}const mi=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`)});function hi(e){return{name:`lsp_prepare_rename`,description:`Check if rename is valid. Use BEFORE lsp_rename.`,parameters:mi,pathParams:[`filePath`],async execute({params:e}){return{content:[{type:`text`,text:Vr(await Z(e.filePath,async t=>await t.prepareRename(e.filePath,e.line,e.character)))}]}}}}const gi=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`),newName:e.string().describe(`New symbol name`)});function _i(e){return{name:`lsp_rename`,description:`Rename symbol across entire workspace. APPLIES changes to all files.`,parameters:gi,pathParams:[`filePath`],async execute({params:e}){return{content:[{type:`text`,text:Hr(await Nr(await Z(e.filePath,async t=>await t.rename(e.filePath,e.line,e.character,e.newName))))}]}}}}const vi=e.object({action:e.enum([`list`,`create`,`remove`,`compact`]).describe(`Session management action to perform`),sessionId:e.string().optional().describe(`Session ID (required for remove/compact)`),title:e.string().optional().describe(`New session title (optional when creating)`)});function yi(e){let{sessionManager:t}=e;return{name:`session_manager`,description:`Manage conversation sessions: list, create, remove, compact.`,parameters:vi,async execute({params:e}){if(!t)throw Error(`SessionManager is not provided in options`);return await bi(e.action,t,e.sessionId,e.title)}}}async function bi(e,t,n,r){switch(e){case`list`:{let e=await t.list();return{content:[{type:`text`,text:e.length===0?`No sessions found.`:e.map(e=>`- ${e.id}: ${e.title} (${e.messageCount} messages)`).join(`
111
- `)}]}}case`create`:return{content:[{type:`text`,text:`Session created: ${(await t.create(r)).id}`}]};case`remove`:{if(!n)throw Error(`sessionId required for remove`);let e=await t.remove(n);return{content:[{type:`text`,text:e?`Session ${n} removed`:`Session not found`}],isError:!e}}case`compact`:{if(!n)throw Error(`sessionId required for compact`);let e=await t.compact(n);return{content:[{type:`text`,text:e?`Session ${n} compacted`:`Compact failed`}],isError:!e}}}}const xi=e.object({action:e.enum([`load`,`execute`]).describe(`'load' to register a skill; 'execute' to run it (auto-loads if needed)`),path:e.string().optional().describe(`SKILL.md file path, relative to project root (load action)`),name:e.string().optional().describe(`Skill name (execute action; if path is also given, loads first)`)});function Si(e,t,n){return{name:`skill`,description:`Load or execute a reusable skill (workflow template). Use action=load to register a SKILL.md; use action=execute to run a loaded skill (auto-loads via path if not yet registered).`,parameters:xi,pathParams:[`path`],resolvePath:t=>P(e,t),async execute({params:e}){switch(e.action){case`load`:{if(!e.path)return{content:[{type:`text`,text:`path is required for load action`}],isError:!0,errorKind:`validation`};let n=await t(e.path);return n.success?{content:[{type:`text`,text:`Skill "${n.name}" loaded from ${e.path}`}]}:{content:[{type:`text`,text:`Failed to load skill: ${n.error}`}],isError:!0,errorKind:`runtime`}}case`execute`:if(!n)return K(`skill(execute)`);if(e.path){let r=await t(e.path);if(!r.success)return{content:[{type:`text`,text:`Failed to load skill: ${r.error}`}],isError:!0,errorKind:`runtime`};let i=r.name??e.name;return i?Ci(await n(i)):{content:[{type:`text`,text:`Cannot execute: no skill name resolved from path or argument`}],isError:!0,errorKind:`validation`}}return e.name?Ci(await n(e.name)):{content:[{type:`text`,text:`name or path is required for execute action`}],isError:!0,errorKind:`validation`}}}}}function Ci(e){return e.success?{content:[{type:`text`,text:e.content??`Skill executed successfully`}]}:{content:[{type:`text`,text:`Skill failed: ${e.error}`}],isError:!0,errorKind:`runtime`}}const wi=e.object({name:e.string().describe(`Short, stable memory title (used as the lookup key)`),description:e.string().describe(`One-line hook shown in the always-visible memory index`),content:e.string().describe(`The full fact to remember across sessions`),scope:e.enum([`workspace`,`local`,`project-shared`]).optional().default(`workspace`).describe(`Memory scope (default 'workspace'): 'workspace' = durable facts portable across machines, 'local' = machine-specific (never synced), 'project-shared' = team-visible AGENTS.md conventions.`)});function Ti(e){return{name:`memory_record`,description:`Persist a fact across sessions (auto-memory). The memory index is injected into future sessions; use memory_read to retrieve full entries.`,parameters:wi,async execute({params:t}){return e?(await e(t),{content:[{type:`text`,text:`Memory "${t.name}" recorded.`}]}):{content:[{type:`text`,text:`Auto-memory is not configured. Set storage.memory in createApp() options.`}],isError:!0,errorKind:`runtime`}}}}const Ei=e.object({name:e.string().describe(`The memory title as shown in the memory index`)});function Di(e){return{name:`memory_read`,description:`Read the full content of a persisted memory entry by its name (see the memory index in the system prompt).`,parameters:Ei,readonly:!0,async execute({params:t}){if(!e)return{content:[{type:`text`,text:`Auto-memory is not configured. Set storage.memory in createApp() options.`}],isError:!0,errorKind:`runtime`};let n=await e(t.name);return n===null?{content:[{type:`text`,text:`Memory "${t.name}" not found.`}],isError:!0,errorKind:`not_found`}:{content:[{type:`text`,text:n}]}}}}const Oi=/^archive:(.+):(\d+)$/;function ki(e){let t=Oi.exec(e);return t?{sessionId:t[1],index:Number.parseInt(t[2],10)}:null}const Ai=e.object({archivePath:e.string().describe(`The archive id shown in a compaction summary, in the form "archive:<sessionId>:<n>".`)}),ji=e.object({}),Mi=`Conversation archive recall is not available in this session (no session-entry backed storage).`;function Ni(e,t){let n=ki(e);return n?!t||n.sessionId!==t?{content:[{type:`text`,text:`This archive belongs to a different conversation and cannot be read from here.`}],isError:!0,errorKind:`permission`}:null:{content:[{type:`text`,text:`Invalid archive id "${e}". Expected the form "archive:<sessionId>:<n>" exactly as shown in the compaction summary.`}],isError:!0,errorKind:`validation`}}function Pi(e){return{name:`memory_archive_read`,description:`Read the full original messages behind a compaction summary. Use this when a summary is marked low-fidelity, or when you need details the summary does not cover. Pass the archive id shown in the summary.`,parameters:Ai,readonly:!0,async execute({params:t,sessionId:n}){if(!e)return{content:[{type:`text`,text:Mi}],isError:!0,errorKind:`runtime`};let r=Ni(t.archivePath,n);if(r)return r;let i=await e(t.archivePath);return i?{content:[{type:`text`,text:i}]}:{content:[{type:`text`,text:`Archive "${t.archivePath}" not found in this conversation.`}],isError:!0,errorKind:`not_found`}}}}function Fi(e){return{name:`memory_archive_list`,description:`List the compaction archives of the current conversation (each entry = one folded chunk of earlier history, with its archive id and a summary preview).`,parameters:ji,readonly:!0,async execute({sessionId:t}){if(!e||!t)return{content:[{type:`text`,text:Mi}],isError:!0,errorKind:`runtime`};let n=await e(t);return n.length===0?{content:[{type:`text`,text:`No compaction archives yet in this conversation.`}]}:{content:[{type:`text`,text:n.map(e=>`- ${e.path} — ${e.messageCount} messages, ${new Date(e.timestamp).toISOString()}\n ${e.summary}`).join(`
112
- `)}]}}}}const Ii=e.object({cron:e.string().describe(`5-field cron expression (minute hour dom month dow). Examples: "0 9 * * 1-5" = weekdays 9am, "*/30 * * * *" = every 30 min`),prompt:e.string().describe(`Natural language instruction for the agent to execute at the scheduled time`),recurring:e.boolean().describe(`true = run on schedule repeatedly, false = one-shot (delete after first fire)`),name:e.string().optional().describe(`Short label for the task (shown in schedule list)`),maxDurationMs:e.number().int().positive().optional().describe(`Timeout in milliseconds for a single fire attempt (default 300000 = 5 minutes). Raise this for tasks expected to take longer.`),target:e.enum([`job`,`session`]).optional().describe(`Where the prompt fires. Default "job": isolated git worktree, produces a diff for /job apply — use for tasks expected to change files, needing independent review, or that should stay decoupled from this conversation (e.g. "scan dependencies for updates every morning"). "session": injects the prompt into THIS conversation when it fires (no worktree, no diff) — use for polling/check-in/report-back tasks whose result should continue this conversation and that typically do not change files (e.g. "check on the long-running eval every 10 minutes and only speak up when there is real progress, it finishes, or it crashes"). A visible notice is shown when a model-created session task is first created and each time it fires.`)});function Li(e){return{name:`schedule_create`,description:`Schedule a recurring or one-shot scheduled task. The task fires automatically — see the "target" parameter for whether it runs as an isolated background job or continues this conversation.`,parameters:Ii,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule scheduling is not available in this session.`}],isError:!0};let n;try{n=e(t)}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}let r=n.target===`session`?` (fires into this conversation)`:``;return{content:[{type:`text`,text:`Schedule created: ${n.taskId} — "${n.name}"${r}\nNext fire: ${new Date(n.nextFireAt).toLocaleString()}\nUse schedule_list to view all schedules, schedule_delete to remove.`}]}}}}function Ri(t){return{name:`schedule_list`,description:`List all scheduled tasks with their IDs, cron schedules, and next fire times.`,parameters:e.object({}),execute:async()=>{if(!t)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};let e=t();return e.length===0?{content:[{type:`text`,text:`No scheduled tasks.`}]}:{content:[{type:`text`,text:e.map(e=>{let t=e.enabled?`◉`:`○`,n=e.recurring?e.cronExpression:`one-shot`,r=new Date(e.nextFireAt).toLocaleString(),i=e.lastFiredAt?` last: ${new Date(e.lastFiredAt).toLocaleString()}`:``;return`${t} ${e.id} ${e.name}\n ${n} next: ${r}${i}`}).join(`
113
- `)}]}}}}const zi=e.object({task_id:e.string().describe(`ID of the scheduled task to delete`)});function Bi(e){return{name:`schedule_delete`,description:`Delete a scheduled task by ID. Irreversible.`,parameters:zi,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};let n=e(t.task_id);return{content:[{type:`text`,text:n?`Schedule "${t.task_id}" deleted.`:`Schedule "${t.task_id}" not found.`}],isError:!n}}}}const Vi=e.object({task_id:e.string().describe(`ID of the scheduled task to run immediately`)});function Hi(e){return{name:`schedule_run`,description:`Run a scheduled task immediately (does not affect its regular schedule).`,parameters:Vi,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};try{return e(t.task_id),{content:[{type:`text`,text:`Schedule "${t.task_id}" triggered.`}]}}catch(e){return{content:[{type:`text`,text:`Failed to run schedule "${t.task_id}": ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}}const Ui=e.object({task_id:e.string().describe(`ID of the scheduled task whose currently running job should be canceled`)});function Wi(e){return{name:`schedule_cancel`,description:`Cancel the currently running job of a scheduled task. The schedule definition is not affected — the task will fire again at its next cron-triggered time. Only works on jobs that are still running or waiting for input; an already-completed or ready (diff-produced) job cannot be canceled.`,parameters:Ui,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule cancel is not available in this session.`}],isError:!0};let n=e(t.task_id);return n.ok?{content:[{type:`text`,text:`Schedule task "${t.task_id}" canceled. The task will fire again at its next scheduled time.`}]}:{content:[{type:`text`,text:`Cannot cancel schedule "${t.task_id}": ${n.reason??`unknown reason`}.`}],isError:!0}}}}const Q=(...e)=>e,Gi=[`Use read to understand existing code before making changes. Read targeted ranges instead of entire large files.`,`Prefer edit over write for modifying existing files — edit replaces exact text and is safer against accidental overwrites.`,`Use write only for creating new files or when the entire file content needs replacement. Prefer editing an existing file over creating a new one.`,`Always verify file existence with read before editing to avoid operating on stale assumptions.`,`For edit: provide enough surrounding context to avoid ambiguous matches.`].join(`
114
- `),Ki=[`Use bash for running tests, installing dependencies, build commands, git operations, and system tasks that have no dedicated tool.`,"For long-lived service processes (dev servers, watchers, `http.server`), pass run_in_background:true instead of a blocking call or a trailing `&`. It returns immediately with a background id.","Then use bash_output to read its accumulated output and kill_shell to stop it. Background processes are auto-killed when the session ends — no manual cleanup of `&` jobs.",`Avoid programs requiring interactive input.`,`Prefer dedicated tools (read, write, edit, grep, find) over shell equivalents (cat, sed, grep) for file operations.`,`Avoid destructive commands (rm -rf, git reset --hard, DROP TABLE) without explicit user approval.`,`Set reasonable timeouts for long-running commands. Kill stale processes rather than waiting indefinitely.`,`Always check the exit code; on failure, inspect stdout/stderr before deciding next steps.`,`Prefer non-interactive flags, e.g. git --no-pager, --non-interactive.`].join(`
115
- `),qi=[`Use find to locate files by name or glob pattern; use grep to search file contents by text or regex.`,"Start broad (find) then narrow (grep → read) to efficiently navigate unfamiliar codebases. There is no dedicated listing tool — for ad-hoc directory listing run `ls` via bash.",`Combine grep results with read to understand full context around matches.`,`grep should use precise patterns; prefer regex alternation for multiple candidates rather than many separate searches.`].join(`
116
- `),Ji=[`Use web_fetch to read specific URLs when you know the page address.`,`Prefer web_search → web_fetch workflow: search first, then fetch specific results. (See the Factual Verification rule in the base instructions for when verification is required.)`,`web_fetch cannot render JavaScript-heavy pages (SPAs). Use for documentation, articles, APIs.`].join(`
117
- `),Yi=[`Use task_delegate to route tasks to more suitable sub-agents by category — useful for tasks requiring specialization or lifecycle management. Provide clear, complete context — sub-agents start with a blank slate.`,`Use write_todos for complex tasks to build and maintain a step list first, then work it to completion. Set a meaningful "title" (e.g. "Bug fixes", "Feature implementation"); defaults to "Task" if omitted.`,`Todo discipline: keep exactly ONE item in_progress at a time; mark an item done the moment you finish it (do not batch). Do NOT end your turn while any item is still pending or in_progress either finish the remaining work, or explicitly mark items skipped/failed with a reason. A turn that stops with unfinished todos is reported to the user as incomplete, not done.`,`Break complex tasks into small, independently verifiable subtasks (2-5 minutes each) before delegating.`,`Do not delegate tasks that require the current conversation context or interactive clarification.`].join(`
118
- `),Xi=[`Use task_delegate for delegation — tracked:false for lightweight synchronous agent opinions, default (tracked) for background or stateful execution with retries.`,`Use task_inspect to check task status and output before acting; use task_control to cancel or retry tasks.`,`Use grill_me to ask the human ONE key decision (with detailed background + a required recommendation) when a single fork genuinely needs a human steer. Advisory only — it never authorizes side effects.`,`Use capture_file_state when conversation is long and you need to refresh understanding of workspace changes.`,`Use learn to record reusable insights (patterns, mistakes, strategies) — not routine progress notes.`].join(`
119
- `),Zi=[`Use session management to organize separate conversation threads per task or topic.`,`Compact sessions proactively when conversation history grows long to avoid context window exhaustion.`,`Do not create excessive sessions — reuse existing ones when the topic is the same.`].join(`
120
- `),Qi=[`Use skill to load or execute reusable workflow templates. Skills are auto-loaded on first execute if a path is given.`,`Prefer invoking a matching skill over ad-hoc multi-step workflows when one exists.`,`Skills encapsulate best practices — follow their structure rather than shortcutting steps.`].join(`
121
- `),$i=[`Use memory_record to persist durable facts (user preferences, environment quirks, project constraints) across sessions.`,`The memory index is always visible in the system prompt; fetch full entries on demand with memory_read.`,`Choose scope: 'workspace' (default, portable structured facts), 'local' (machine-specific, never synced), or 'project-shared' (team-visible conventions written to AGENTS.md — never edit/write it directly, .otto state is protected).`].join(`
122
- `),ea=[`When a conversation summary is marked low-fidelity (only part of the history made it into the summary), use memory_archive_read to pull back the original messages instead of guessing or asking the user to repeat themselves.`,`Use memory_archive_list to see which chunks of earlier history are available and pick the right archive id.`,`Archives are scoped to the current conversation — you cannot read another session's history.`].join(`
123
- `),ta=[{names:[`read`,`write`,`edit`],preset:`minimal`,category:`fs`,guidance:Gi,create:e=>Q(Zt(e.projectRoot),rn(e.projectRoot),xn(e.projectRoot))},{names:[`bash`],preset:`minimal`,category:`shell`,guidance:Ki,create:e=>Q(St(e.projectRoot,void 0,e.features.sandbox,e.features.processRegistry))},{names:[`bash_output`,`kill_shell`],preset:`standard`,category:`shell`,when:e=>!!e.features.processRegistry,create:e=>Q(jt(e.features.processRegistry),Nt(e.features.processRegistry))},{names:[`find`,`grep`],preset:`standard`,category:`search`,guidance:qi,create:e=>Q(It(e.projectRoot,{binaryExecutorRegistry:e.registry.getBinaryToolExecutors()}),Ut(e.projectRoot,{binaryToolExecutorRegistry:e.registry.getBinaryToolExecutors(),processTracker:e.processTracker}))},{names:[`web_fetch`,`web_search`],preset:`standard`,category:`web`,guidance:Ji,create:e=>Q(Vn(e.projectRoot),Yn(e.projectRoot))},{names:[`tool_search`],preset:`standard`,category:`orchestration`,guidance:`Use tool_search to discover deferred tools by name or keyword and load their full parameter schema.`,create:e=>Q(Tr(()=>e.registry.project(`full`).deferred))},{names:[`task_delegate`,`delegate_job`,`write_todos`],preset:`standard`,category:`orchestration`,guidance:Yi,create:e=>Q(ir(e.projectRoot,e.orchestration.dispatchTask,e.orchestration.writeTodos,e.fork?.fork,e.team?.runTeam,void 0,e.agents.callAgent,e.agentProfiles??[]),or(e.orchestration.startJob),yr(e.orchestration.writeTodos))},{names:[`lsp_goto_definition`,`lsp_find_references`,`lsp_symbols`,`lsp_diagnostics`,`lsp_prepare_rename`,`lsp_rename`],preset:`standard`,category:`lsp`,when:e=>!!e.features.enableLsp,create:e=>(e.processTracker&&J.setProcessTracker(e.processTracker),Q(si(e.projectRoot),li(e.projectRoot),di(e.projectRoot),pi(e.projectRoot),hi(e.projectRoot),_i(e.projectRoot)))},{names:[`list_models`,`task_inspect`,`task_control`,`grill_me`,`capture_file_state`,`learn`],preset:`full`,category:`orchestration`,guidance:Xi,create:e=>Q(gr(e.features.listModels,e.features.sessionId),fr(e.projectRoot,{get:e.orchestration.getTask,list:e.orchestration.listTasks,output:e.orchestration.getBackgroundOutput}),mr(e.projectRoot,{update:e.orchestration.updateTask,cancelBackground:e.orchestration.cancelBackground}),ze(e.projectRoot,e.features.grill),xr(e.features.captureFileState),Cr(e.features.sessionId??``,e.features.learn))},{names:[`session_manager`],preset:`full`,category:`session`,guidance:Zi,when:e=>!!e.session,create:e=>{let t=e.session;return t?Q(yi({sessionManager:t.manager})):Q()}},{names:[`skill`],preset:`full`,category:`skill`,guidance:Qi,create:e=>Q(Si(e.projectRoot,e.loadSkill,e.executeSkill))},{names:[`memory_record`,`memory_read`],preset:`full`,category:`memory`,guidance:$i,create:e=>Q(Ti(e.memory?.recordMemory),Di(e.memory?.readMemory))},{names:[`memory_archive_read`,`memory_archive_list`],preset:`full`,category:`memory`,when:e=>!!e.memory?.archive,guidance:ea,create:e=>Q(Pi(e.memory?.archive?.read),Fi(e.memory?.archive?.list))},{names:[`schedule_create`,`schedule_list`,`schedule_delete`,`schedule_run`,`schedule_cancel`],preset:`full`,category:`schedule`,guidance:`Schedule tools — create/list/delete/run/cancel timer tasks that fire agent jobs on a schedule. Per-tool semantics are in each tool's description.`,when:e=>!!e.schedule,create:e=>Q(Li(e.schedule.create),Ri(e.schedule.list),Bi(e.schedule.delete),Hi(e.schedule.run),Wi(e.schedule.cancel))},{names:[`capability_gap`],preset:`full`,category:`orchestration`,guidance:`Use capability_gap when the user asks for something you genuinely cannot do with your current tools. It searches the plugin ecosystem for an existing capability and, if none exists, returns a blueprint for building one (scaffold + delegate_job).`,when:e=>!!e.capabilityGap,create:e=>Q(ur(e.capabilityGap.search))}],na=`Skill provider not configured. Pass skillProvider to createApp().`;function ra(e,t){let n=t.map(e=>e.name).sort(),r=[...e.names].sort();if(n.length!==r.length||n.some((e,t)=>e!==r[t]))throw Error(`TOOL_NODES drift in category "${e.category}": declared [${r.join(`, `)}] but factory produced [${n.join(`, `)}]`)}function ia(e,t,n){let{orchestration:r,agents:i,features:a={}}=n,o={projectRoot:t,registry:e,orchestration:r,agents:i,features:a,loadSkill:n.skills?.loadSkill??(async()=>({success:!1,error:na})),executeSkill:n.skills?.executeSkill??(async()=>({success:!1,error:na})),fork:n.fork,team:n.team,memory:n.memory,session:n.session,agentProfiles:n.agentProfiles,schedule:n.schedule,capabilityGap:n.capabilityGap,processTracker:n.processTracker};for(let t of ta){if(t.when&&!t.when(o))continue;let n=t.create(o);ra(t,n),e.register(n,{preset:t.preset,category:t.category,builtin:!0,guideline:t.guidance})}}const $={minimal:new Set([`minimal`]),standard:new Set([`minimal`,`standard`]),full:new Set([`minimal`,`standard`,`full`])};function aa(e){if(!e||typeof e!=`object`)return!1;let t=e,n=t.description;return typeof t.name==`string`&&(typeof n==`string`||typeof n==`function`)&&typeof t.execute==`function`&&t.parameters!==void 0}var oa=class e{tools=new Map;binaryToolExecutors=null;disposers=[];onMutation;get size(){return this.tools.size}setBinaryToolExecutors(e){this.binaryToolExecutors=e}getBinaryToolExecutors(){if(!this.binaryToolExecutors)throw Error(`BinaryToolExecutorRegistry is not set in ToolRegistry`);return this.binaryToolExecutors}registerDisposer(e){this.disposers.push(e)}register(e,t={}){let n={preset:t.preset??`full`,category:t.category,builtin:t.builtin??!1,snippet:t.snippet,guideline:t.guideline,source:t.source},r=Array.isArray(e)?e:[e];for(let e of r){if(!aa(e))throw Error(`Invalid tool registration input`);let r=e,i=typeof r.description==`function`?r.description({projectRoot:t.projectRoot??``}):r.description,a={...r,description:i,metadata:n};this.tools.set(r.name,a)}this.onMutation?.({added:r.map(e=>e.name),source:t.source})}unregister(e){let t=[];if(Array.isArray(e)){let n=!0;for(let r of e)this.tools.delete(r)?t.push(r):n=!1;return t.length>0&&this.onMutation?.({removed:t}),n}let n=this.tools.delete(e);return n&&this.onMutation?.({removed:[e]}),n}unregisterBySource(e){let t=[...this.tools.values()].filter(t=>t.metadata.source===e).map(e=>e.name);for(let e of t)this.tools.delete(e);return t.length>0&&this.onMutation?.({removed:t,source:e}),t.length}get(e){return this.tools.get(e)}has(e){return this.tools.has(e)}getAll(){return[...this.tools.values()].sort((e,t)=>e.name.localeCompare(t.name))}getAvailable(e=`standard`){let t=$[e]??$.standard;return this.getAll().filter(e=>t.has(e.metadata.preset))}project(e=`standard`){let t=$[e]??$.standard,n=[],r=[];for(let e of this.getAll())t.has(e.metadata.preset)&&(e.exposure===`deferred`?r.push(e):e.exposure!==`hidden`&&n.push(e));return{direct:n,deferred:r}}static TOOL_GUIDANCE_BUDGET_TOKENS=5e3;static estimatedTokens(e){let t=0;for(let n of e)/[\u3000-\u9fff\uff00-\uffef]/.test(n)&&t++;return Math.ceil(t+(e.length-t)/4)}buildToolGuidance(t=`standard`){let{direct:n,deferred:r}=this.project(t);if(n.length===0&&r.length===0)return``;let i=new Map;for(let e of n){let{snippet:t,guideline:n,category:r}=e.metadata,a=n?`${r??``}\u0000${n}`:`\u0000tool:${e.name}`,o=i.get(a);o||(o={guideline:n,entries:[]},i.set(a,o));let s=[`#### ${e.name}`];!n&&!t&&s.push(e.description),t&&s.push(`Example:\n\`\`\`\n${t}\n\`\`\``),o.entries.push(s.join(`
109
+ `)}async function Z(e,t){let n=P(e),r=hi(we(n));if(r.status!==`found`)throw H(vi(r),`not_found`);let i=r.server,a=_i(n),o=await Y.getClient(a,i);try{return await t(o)}catch(e){throw e instanceof Error&&e.message.includes(`timeout`)&&Y.isServerInitializing(a,i.id)?H(`LSP server is still initializing. Please retry in a few seconds. Original error: ${e.message}`,`timeout`):e}finally{Y.releaseClient(a,i.id)}}const yi=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`)});function bi(e){return{name:`lsp_goto_definition`,description:`Jump to symbol definition. Find WHERE something is defined.`,parameters:yi,pathParams:[`filePath`],readonly:!0,async execute({params:e}){let t=await Z(e.filePath,async t=>await t.definition(e.filePath,e.line,e.character));if(!t)return{content:[{type:`text`,text:`No definition found`}]};let n=Array.isArray(t)?t:[t];return n.length===0?{content:[{type:`text`,text:`No definition found`}]}:{content:[{type:`text`,text:n.map(Kr).join(`
110
+ `)}]}}}}const xi=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`),includeDeclaration:e.boolean().optional().describe(`Include the declaration itself (default true)`)});function Si(e){return{name:`lsp_find_references`,description:`Find ALL usages/references of a symbol across the entire workspace.`,parameters:xi,pathParams:[`filePath`],readonly:!0,async execute({params:e}){let t=await Z(e.filePath,async t=>await t.references(e.filePath,e.line,e.character,e.includeDeclaration??!0));if(!t||t.length===0)return{content:[{type:`text`,text:`No references found`}]};let n=t.length,r=n>200,i=(r?t.slice(0,200):t).map(Kr);return r&&i.unshift(`Found ${n} references (showing first 200):`),{content:[{type:`text`,text:i.join(`
111
+ `)}]}}}}const Ci=e.object({filePath:e.string().describe(`File path for LSP context`),scope:e.enum([`document`,`workspace`]).default(`document`).describe(`'document' for file symbols, 'workspace' for project-wide search`),query:e.string().optional().describe(`Symbol name to search (required for workspace scope)`),limit:e.number().int().min(1).max(500).optional().describe(`Max results (default/max 200)`)});function wi(e){return{name:`lsp_symbols`,description:`Get symbols from file (document) or search across workspace. Use scope='document' for file outline, scope='workspace' for project-wide symbol search.`,parameters:Ci,pathParams:[`filePath`],readonly:!0,async execute({params:e}){if((e.scope??`document`)===`workspace`){if(!e.query)return{content:[{type:`text`,text:`Error: 'query' is required for workspace scope`}],isError:!0,errorKind:`validation`};let t=await Z(e.filePath,async t=>await t.workspaceSymbols(e.query));if(!t||t.length===0)return{content:[{type:`text`,text:`No symbols found`}]};let n=t.length,r=Math.min(e.limit??200,200),i=n>r,a=t.slice(0,r).map(Xr);return i&&a.unshift(`Found ${n} symbols (showing first ${r}):`),{content:[{type:`text`,text:a.join(`
112
+ `)}]}}let t=await Z(e.filePath,async t=>await t.documentSymbols(e.filePath));if(!t||t.length===0)return{content:[{type:`text`,text:`No symbols found`}]};let n=t.length,r=Math.min(e.limit??200,200),i=n>r,a=i?t.slice(0,r):t,o=[];return i&&o.push(`Found ${n} symbols (showing first ${r}):`),a[0]&&`range`in a[0]?o.push(...a.map(e=>Yr(e))):o.push(...a.map(Xr)),{content:[{type:`text`,text:o.join(`
113
+ `)}]}}}}const Ti=e.object({filePath:e.string().describe(`Absolute path to the file`),severity:e.enum([`error`,`warning`,`information`,`hint`,`all`]).optional().describe(`Filter by severity level`)});function Ei(e){return{name:`lsp_diagnostics`,description:`Get errors, warnings, hints from language server BEFORE running build.`,parameters:Ti,pathParams:[`filePath`],readonly:!0,async execute({params:e}){let t=await Z(e.filePath,async t=>await t.diagnostics(e.filePath)),n=[];if(t&&(Array.isArray(t)?n=t:t.items&&(n=t.items)),n=Qr(n,e.severity),n.length===0)return{content:[{type:`text`,text:`No diagnostics found`}]};let r=n.length,i=r>200,a=(i?n.slice(0,200):n).map(Zr);return i&&a.unshift(`Found ${r} diagnostics (showing first 200):`),{content:[{type:`text`,text:a.join(`
114
+ `)}]}}}}const Di=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`)});function Oi(e){return{name:`lsp_prepare_rename`,description:`Check if rename is valid. Use BEFORE lsp_rename.`,parameters:Di,pathParams:[`filePath`],async execute({params:e}){return{content:[{type:`text`,text:$r(await Z(e.filePath,async t=>await t.prepareRename(e.filePath,e.line,e.character)))}]}}}}const ki=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`),newName:e.string().describe(`New symbol name`)});function Ai(e){return{name:`lsp_rename`,description:`Rename symbol across entire workspace. APPLIES changes to all files.`,parameters:ki,pathParams:[`filePath`],async execute({params:t}){return{content:[{type:`text`,text:ei(await Gr(await Z(t.filePath,async e=>await e.rename(t.filePath,t.line,t.character,t.newName)),e))}]}}}}const ji=e.object({action:e.enum([`list`,`create`,`remove`,`compact`]).describe(`Session management action to perform`),sessionId:e.string().optional().describe(`Session ID (required for remove/compact)`),title:e.string().optional().describe(`New session title (optional when creating)`)});function Mi(e){let{sessionManager:t}=e;return{name:`session_manager`,description:`Manage conversation sessions: list, create, remove, compact.`,parameters:ji,async execute({params:e}){if(!t)throw Error(`SessionManager is not provided in options`);return await Ni(e.action,t,e.sessionId,e.title)}}}async function Ni(e,t,n,r){switch(e){case`list`:{let e=await t.list();return{content:[{type:`text`,text:e.length===0?`No sessions found.`:e.map(e=>`- ${e.id}: ${e.title} (${e.messageCount} messages)`).join(`
115
+ `)}]}}case`create`:return{content:[{type:`text`,text:`Session created: ${(await t.create(r)).id}`}]};case`remove`:{if(!n)throw Error(`sessionId required for remove`);let e=await t.remove(n);return{content:[{type:`text`,text:e?`Session ${n} removed`:`Session not found`}],isError:!e}}case`compact`:{if(!n)throw Error(`sessionId required for compact`);let e=await t.compact(n);return{content:[{type:`text`,text:e?`Session ${n} compacted`:`Compact failed`}],isError:!e}}}}const Pi=e.object({action:e.enum([`load`,`execute`]).describe(`'load' to register a skill; 'execute' to run it (auto-loads if needed)`),path:e.string().optional().describe(`SKILL.md file path, relative to project root (load action)`),name:e.string().optional().describe(`Skill name (execute action; if path is also given, loads first)`)});function Fi(e,t,n,r){return{name:`skill`,description:`Load or execute a reusable skill (workflow template). Use action=load to register a SKILL.md; use action=execute to run a loaded skill (auto-loads via path if not yet registered).`,parameters:Pi,pathParams:[`path`],resolvePath:t=>P(e,t),async execute({params:e,sessionId:i}){let a=i??r;switch(e.action){case`load`:{if(!e.path)return{content:[{type:`text`,text:`path is required for load action`}],isError:!0,errorKind:`validation`};let n=await t(e.path);return n.success?{content:[{type:`text`,text:`Skill "${n.name}" loaded from ${e.path}`}]}:{content:[{type:`text`,text:`Failed to load skill: ${n.error}`}],isError:!0,errorKind:`runtime`}}case`execute`:if(!n)return K(`skill(execute)`);if(e.path){let r=await t(e.path);if(!r.success)return{content:[{type:`text`,text:`Failed to load skill: ${r.error}`}],isError:!0,errorKind:`runtime`};let i=r.name??e.name;return i?Ii(await n(i,a)):{content:[{type:`text`,text:`Cannot execute: no skill name resolved from path or argument`}],isError:!0,errorKind:`validation`}}return e.name?Ii(await n(e.name,a)):{content:[{type:`text`,text:`name or path is required for execute action`}],isError:!0,errorKind:`validation`}}}}}function Ii(e){return e.success?{content:[{type:`text`,text:e.content??`Skill executed successfully`}]}:{content:[{type:`text`,text:`Skill failed: ${e.error}`}],isError:!0,errorKind:`runtime`}}const Li=e.object({name:e.string().describe(`Short, stable memory title (used as the lookup key)`),description:e.string().describe(`One-line hook shown in the always-visible memory index`),content:e.string().describe(`The full fact to remember across sessions`),scope:e.enum([`workspace`,`local`,`project-shared`]).optional().default(`workspace`).describe(`Memory scope (default 'workspace'): 'workspace' = durable facts portable across machines, 'local' = machine-specific (never synced), 'project-shared' = team-visible AGENTS.md conventions.`)});function Ri(e){return{name:`memory_record`,description:`Persist a fact across sessions (auto-memory). The memory index is injected into future sessions; use memory_read to retrieve full entries.`,parameters:Li,async execute({params:t}){return e?(await e(t),{content:[{type:`text`,text:`Memory "${t.name}" recorded.`}]}):{content:[{type:`text`,text:`Auto-memory is not configured. Set storage.memory in createApp() options.`}],isError:!0,errorKind:`runtime`}}}}const zi=e.object({name:e.string().describe(`The memory title as shown in the memory index`)});function Bi(e){return{name:`memory_read`,description:`Read the full content of a persisted memory entry by its name (see the memory index in the system prompt).`,parameters:zi,readonly:!0,async execute({params:t}){if(!e)return{content:[{type:`text`,text:`Auto-memory is not configured. Set storage.memory in createApp() options.`}],isError:!0,errorKind:`runtime`};let n=await e(t.name);return n===null?{content:[{type:`text`,text:`Memory "${t.name}" not found.`}],isError:!0,errorKind:`not_found`}:{content:[{type:`text`,text:n}]}}}}const Vi=/^archive:(.+):(\d+)$/;function Hi(e){let t=Vi.exec(e);return t?{sessionId:t[1],index:Number.parseInt(t[2],10)}:null}const Ui=e.object({archivePath:e.string().describe(`The archive id shown in a compaction summary, in the form "archive:<sessionId>:<n>".`)}),Wi=e.object({}),Gi=`Conversation archive recall is not available in this session (no session-entry backed storage).`;function Ki(e,t){let n=Hi(e);return n?!t||n.sessionId!==t?{content:[{type:`text`,text:`This archive belongs to a different conversation and cannot be read from here.`}],isError:!0,errorKind:`permission`}:null:{content:[{type:`text`,text:`Invalid archive id "${e}". Expected the form "archive:<sessionId>:<n>" exactly as shown in the compaction summary.`}],isError:!0,errorKind:`validation`}}function qi(e){return{name:`memory_archive_read`,description:`Read the full original messages behind a compaction summary. Use this when a summary is marked low-fidelity, or when you need details the summary does not cover. Pass the archive id shown in the summary.`,parameters:Ui,readonly:!0,async execute({params:t,sessionId:n}){if(!e)return{content:[{type:`text`,text:Gi}],isError:!0,errorKind:`runtime`};let r=Ki(t.archivePath,n);if(r)return r;let i=await e(t.archivePath);return i?{content:[{type:`text`,text:i}]}:{content:[{type:`text`,text:`Archive "${t.archivePath}" not found in this conversation.`}],isError:!0,errorKind:`not_found`}}}}function Ji(e){return{name:`memory_archive_list`,description:`List the compaction archives of the current conversation (each entry = one folded chunk of earlier history, with its archive id and a summary preview).`,parameters:Wi,readonly:!0,async execute({sessionId:t}){if(!e||!t)return{content:[{type:`text`,text:Gi}],isError:!0,errorKind:`runtime`};let n=await e(t);return n.length===0?{content:[{type:`text`,text:`No compaction archives yet in this conversation.`}]}:{content:[{type:`text`,text:n.map(e=>`- ${e.path} — ${e.messageCount} messages, ${new Date(e.timestamp).toISOString()}\n ${e.summary}`).join(`
116
+ `)}]}}}}const Yi=e.object({cron:e.string().describe(`5-field cron expression (minute hour dom month dow). Examples: "0 9 * * 1-5" = weekdays 9am, "*/30 * * * *" = every 30 min`),prompt:e.string().describe(`Natural language instruction for the agent to execute at the scheduled time`),recurring:e.boolean().describe(`true = run on schedule repeatedly, false = one-shot (delete after first fire)`),name:e.string().optional().describe(`Short label for the task (shown in schedule list)`),maxDurationMs:e.number().int().positive().optional().describe(`Timeout in milliseconds for a single fire attempt (default 300000 = 5 minutes). Raise this for tasks expected to take longer.`),target:e.enum([`job`,`session`]).optional().describe(`Where the prompt fires. Default "job": isolated git worktree, produces a diff for /job apply — for tasks that change files, need independent review, or should stay decoupled from this conversation (e.g. "scan dependencies for updates every morning"). "session": injects into THIS conversation when it fires (no worktree, no diff) — for polling/check-in/report-back tasks that continue this conversation and typically do not change files. A visible notice is shown on first creation and each fire.`)});function Xi(e){return{name:`schedule_create`,description:`Schedule a recurring or one-shot scheduled task. The task fires automatically — see the "target" parameter for whether it runs as an isolated background job or continues this conversation.`,parameters:Yi,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule scheduling is not available in this session.`}],isError:!0};let n;try{n=e(t)}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}let r=n.target===`session`?` (fires into this conversation)`:``;return{content:[{type:`text`,text:`Schedule created: ${n.taskId} — "${n.name}"${r}\nNext fire: ${new Date(n.nextFireAt).toLocaleString()}\nUse schedule_list to view all schedules, schedule_delete to remove.`}]}}}}function Zi(t){return{name:`schedule_list`,description:`List all scheduled tasks with their IDs, cron schedules, and next fire times.`,parameters:e.object({}),execute:async()=>{if(!t)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};let e=t();return e.length===0?{content:[{type:`text`,text:`No scheduled tasks.`}]}:{content:[{type:`text`,text:e.map(e=>{let t=e.enabled?`◉`:`○`,n=e.recurring?e.cronExpression:`one-shot`,r=new Date(e.nextFireAt).toLocaleString(),i=e.lastFiredAt?` last: ${new Date(e.lastFiredAt).toLocaleString()}`:``;return`${t} ${e.id} ${e.name}\n ${n} next: ${r}${i}`}).join(`
117
+ `)}]}}}}const Qi=e.object({task_id:e.string().describe(`ID of the scheduled task to delete`)});function $i(e){return{name:`schedule_delete`,description:`Delete a scheduled task by ID. Irreversible.`,parameters:Qi,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};let n=e(t.task_id);return{content:[{type:`text`,text:n?`Schedule "${t.task_id}" deleted.`:`Schedule "${t.task_id}" not found.`}],isError:!n}}}}const ea=e.object({task_id:e.string().describe(`ID of the scheduled task to run immediately`)});function ta(e){return{name:`schedule_run`,description:`Run a scheduled task immediately (does not affect its regular schedule).`,parameters:ea,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};try{return e(t.task_id),{content:[{type:`text`,text:`Schedule "${t.task_id}" triggered.`}]}}catch(e){return{content:[{type:`text`,text:`Failed to run schedule "${t.task_id}": ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}}const na=e.object({task_id:e.string().describe(`ID of the scheduled task whose currently running job should be canceled`)});function ra(e){return{name:`schedule_cancel`,description:`Cancel the currently running job of a scheduled task. The schedule definition is not affected — the task will fire again at its next cron-triggered time. Only works on jobs that are still running or waiting for input; an already-completed or ready (diff-produced) job cannot be canceled.`,parameters:na,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule cancel is not available in this session.`}],isError:!0};let n=e(t.task_id);return n.ok?{content:[{type:`text`,text:`Schedule task "${t.task_id}" canceled. The task will fire again at its next scheduled time.`}]}:{content:[{type:`text`,text:`Cannot cancel schedule "${t.task_id}": ${n.reason??`unknown reason`}.`}],isError:!0}}}}const Q=(...e)=>e,ia=[`Use read to understand existing code before making changes. Read targeted ranges instead of entire large files.`,`Prefer edit over write for modifying existing files — edit replaces exact text and is safer.`,`Use write only for new files or full content replacement.`,`Verify file existence with read before editing never edit on stale assumptions.`,`For edit: provide enough surrounding context to avoid ambiguous matches.`].join(`
118
+ `),aa=[`Use bash for running tests, installing dependencies, build commands, git operations, and system tasks that have no dedicated tool.`,"For long-lived processes (dev servers, watchers), pass run_in_background:true instead of blocking or trailing `&`; it returns immediately with a background id.","Read accumulated output with bash_output; stop with kill_shell. Background processes are auto-killed at session end — no cleanup of `&` jobs.",`Avoid programs requiring interactive input.`,`Prefer dedicated tools (read, write, edit, grep, find) over shell equivalents (cat, sed, grep) for file operations.`,`Avoid destructive commands (rm -rf, git reset --hard, DROP TABLE) without explicit user approval.`,`Set reasonable timeouts for long-running commands. Kill stale processes rather than waiting indefinitely.`,`Always check the exit code; on failure, inspect stdout/stderr before deciding next steps.`,`Prefer non-interactive flags, e.g. git --no-pager, --non-interactive.`].join(`
119
+ `),oa=[`Use find to locate files by name or glob pattern; use grep to search file contents by text or regex.`,"Start broad (find) then narrow (grep → read) to efficiently navigate unfamiliar codebases. There is no dedicated listing tool — for ad-hoc directory listing run `ls` via bash.",`Combine grep results with read to understand full context around matches.`,`Use precise grep patterns; prefer regex alternation over many separate searches.`].join(`
120
+ `),sa=[`Use web_fetch to read specific URLs when you know the page address.`,`Prefer web_search → web_fetch workflow: search first, then fetch specific results. (See the Factual Verification rule in the base instructions for when verification is required.)`,`web_fetch cannot render JavaScript-heavy pages (SPAs). Use for documentation, articles, APIs.`].join(`
121
+ `),ca=[`Use task_delegate to route tasks to more suitable sub-agents by category — useful for specialization or lifecycle management. Provide clear, complete context — sub-agents start with a blank slate.`,`Use write_todos for complex tasks to build and maintain a step list first, then work it to completion. Optional "title" labels the list (defaults to "Task").`,`Todo discipline: keep exactly ONE item in_progress; mark items done the moment you finish (no batching). Never end a turn with pending/in_progress items — finish them or mark skipped/failed with a reason. Unfinished todos = reported incomplete.`,`Break complex tasks into small, independently verifiable subtasks (2-5 minutes each) before delegating.`,`Do not delegate tasks that require the current conversation context or interactive clarification.`].join(`
122
+ `),la=[`Use task_delegate for delegation — tracked:false for lightweight synchronous agent opinions, default (tracked) for background or stateful execution with retries.`,`Use task_inspect to check task status and output before acting; use task_control to cancel or retry tasks.`,`Use grill_me to ask the human ONE key decision (detailed background + required recommendation) when a fork genuinely needs a human steer. Advisory only — never authorizes side effects.`,`Use capture_file_state when conversation is long and you need to refresh understanding of workspace changes.`,`Use learn to record reusable insights (patterns, mistakes, strategies) — not routine progress notes.`].join(`
123
+ `),ua=[`Use session management to organize separate conversation threads per task or topic.`,`Compact sessions proactively when conversation history grows long to avoid context window exhaustion.`,`Do not create excessive sessions — reuse existing ones when the topic is the same.`].join(`
124
+ `),da=[`Use skill to load or execute reusable workflow templates. Skills are auto-loaded on first execute if a path is given.`,`Prefer invoking a matching skill over ad-hoc multi-step workflows when one exists.`,`Skills encapsulate best practices — follow their structure rather than shortcutting steps.`].join(`
125
+ `),fa=[`Use memory_record to persist durable facts (user preferences, environment quirks, project constraints) across sessions.`,`The memory index is always visible in the system prompt; fetch full entries on demand with memory_read.`,`Scope choice and what to remember: see <memory_guidelines> (injected with the memory index).`].join(`
126
+ `),pa=[`When a conversation summary is marked low-fidelity (only part of the history made it into the summary), use memory_archive_read to pull back the original messages instead of guessing or asking the user to repeat themselves.`,`Use memory_archive_list to see which chunks of earlier history are available and pick the right archive id.`,`Archives are scoped to the current conversation — you cannot read another session's history.`].join(`
127
+ `),ma=[{names:[`read`,`write`,`edit`],preset:`minimal`,category:`fs`,guidance:ia,create:e=>Q(cn(e.projectRoot),mn(e.projectRoot),Mn(e.projectRoot))},{names:[`bash`],preset:`minimal`,category:`shell`,guidance:aa,create:e=>Q(At(e.projectRoot,void 0,e.features.sandbox,e.features.processRegistry))},{names:[`bash_output`,`kill_shell`],preset:`standard`,category:`shell`,when:e=>!!e.features.processRegistry,create:e=>Q(Vt(e.features.processRegistry),Ut(e.features.processRegistry))},{names:[`find`,`grep`],preset:`standard`,category:`search`,guidance:oa,create:e=>Q(Kt(e.projectRoot,{binaryExecutorRegistry:e.registry.getBinaryToolExecutors()}),$t(e.projectRoot,{binaryToolExecutorRegistry:e.registry.getBinaryToolExecutors(),processTracker:e.processTracker}))},{names:[`web_fetch`,`web_search`],preset:`standard`,category:`web`,guidance:sa,create:e=>Q(Qn(e.projectRoot),or(e.projectRoot))},{names:[`tool_search`],preset:`standard`,category:`orchestration`,guidance:`Use tool_search to discover deferred tools by name or keyword and load their full parameter schema.`,create:e=>Q(Lr(()=>e.registry.project(`full`).deferred))},{names:[`task_delegate`,`delegate_job`,`write_todos`],preset:`standard`,category:`orchestration`,guidance:ca,create:e=>Q(hr(e.projectRoot,e.orchestration.dispatchTask,e.orchestration.writeTodos,e.fork?.fork,e.team?.runTeam,void 0,e.agents.callAgent,e.agentProfiles??[]),_r(e.orchestration.startJob),jr(e.orchestration.writeTodos))},{names:[`lsp_goto_definition`,`lsp_find_references`,`lsp_symbols`,`lsp_diagnostics`,`lsp_prepare_rename`,`lsp_rename`],preset:`standard`,category:`lsp`,when:e=>!!e.features.enableLsp,create:e=>(e.processTracker&&Y.setProcessTracker(e.processTracker),Q(bi(e.projectRoot),Si(e.projectRoot),wi(e.projectRoot),Ei(e.projectRoot),Oi(e.projectRoot),Ai(e.projectRoot)))},{names:[`list_models`,`task_inspect`,`task_control`,`grill_me`,`capture_file_state`,`learn`],preset:`full`,category:`orchestration`,guidance:la,create:e=>Q(Or(e.features.listModels,e.features.sessionId),wr(e.projectRoot,{get:e.orchestration.getTask,list:e.orchestration.listTasks,output:e.orchestration.getBackgroundOutput}),Er(e.projectRoot,{update:e.orchestration.updateTask,cancelBackground:e.orchestration.cancelBackground}),He(e.projectRoot,e.features.grill),Nr(e.features.captureFileState),Fr(e.features.sessionId??``,e.features.learn))},{names:[`session_manager`],preset:`full`,category:`session`,guidance:ua,when:e=>!!e.session,create:e=>{let t=e.session;return t?Q(Mi({sessionManager:t.manager})):Q()}},{names:[`skill`],preset:`full`,category:`skill`,guidance:da,create:e=>Q(Fi(e.projectRoot,e.loadSkill,e.executeSkill,e.features.sessionId))},{names:[`memory_record`,`memory_read`],preset:`full`,category:`memory`,guidance:fa,create:e=>Q(Ri(e.memory?.recordMemory),Bi(e.memory?.readMemory))},{names:[`memory_archive_read`,`memory_archive_list`],preset:`full`,category:`memory`,when:e=>!!e.memory?.archive,guidance:pa,create:e=>Q(qi(e.memory?.archive?.read),Ji(e.memory?.archive?.list))},{names:[`schedule_create`,`schedule_list`,`schedule_delete`,`schedule_run`,`schedule_cancel`],preset:`full`,category:`schedule`,guidance:`Schedule tools — create/list/delete/run/cancel timer tasks that fire agent jobs on a schedule. Semantics are in each tool description.`,when:e=>!!e.schedule,create:e=>Q(Xi(e.schedule.create),Zi(e.schedule.list),$i(e.schedule.delete),ta(e.schedule.run),ra(e.schedule.cancel))},{names:[`capability_gap`],preset:`full`,category:`orchestration`,guidance:`Use capability_gap when you genuinely cannot do what the user asks with current tools: searches the plugin ecosystem for an existing capability, else returns a build blueprint (scaffold + delegate_job).`,when:e=>!!e.capabilityGap,create:e=>Q(Sr(e.capabilityGap.search,{...e.capabilityGap.recorder?{recorder:e.capabilityGap.recorder}:{},...e.capabilityGap.now?{now:e.capabilityGap.now}:{}}))}],ha=`Skill provider not configured. Pass skillProvider to createApp().`;function ga(e,t){let n=t.map(e=>e.name).sort(),r=[...e.names].sort();if(n.length!==r.length||n.some((e,t)=>e!==r[t]))throw Error(`TOOL_NODES drift in category "${e.category}": declared [${r.join(`, `)}] but factory produced [${n.join(`, `)}]`)}function _a(e,t,n){let{orchestration:r,agents:i,features:a={}}=n,o={projectRoot:t,registry:e,orchestration:r,agents:i,features:a,loadSkill:n.skills?.loadSkill??(async()=>({success:!1,error:ha})),executeSkill:n.skills?.executeSkill??(async()=>({success:!1,error:ha})),fork:n.fork,team:n.team,memory:n.memory,session:n.session,agentProfiles:n.agentProfiles,schedule:n.schedule,capabilityGap:n.capabilityGap,processTracker:n.processTracker};for(let t of ma){if(t.when&&!t.when(o))continue;let n=t.create(o);ga(t,n),e.register(n,{preset:t.preset,category:t.category,builtin:!0,guideline:t.guidance})}}const $={minimal:new Set([`minimal`]),standard:new Set([`minimal`,`standard`]),full:new Set([`minimal`,`standard`,`full`])};function va(e){if(!e||typeof e!=`object`)return!1;let t=e,n=t.description;return typeof t.name==`string`&&(typeof n==`string`||typeof n==`function`)&&typeof t.execute==`function`&&t.parameters!==void 0}var ya=class e{tools=new Map;binaryToolExecutors=null;disposers=[];onMutation;get size(){return this.tools.size}setBinaryToolExecutors(e){this.binaryToolExecutors=e}getBinaryToolExecutors(){if(!this.binaryToolExecutors)throw Error(`BinaryToolExecutorRegistry is not set in ToolRegistry`);return this.binaryToolExecutors}registerDisposer(e){this.disposers.push(e)}register(e,t={}){let n={preset:t.preset??`full`,category:t.category,builtin:t.builtin??!1,snippet:t.snippet,guideline:t.guideline,source:t.source},r=Array.isArray(e)?e:[e];for(let e of r){if(!va(e))throw Error(`Invalid tool registration input`);let r=e,i=typeof r.description==`function`?r.description({projectRoot:t.projectRoot??``}):r.description,a={...r,description:i,metadata:n};this.tools.set(r.name,a)}this.onMutation?.({added:r.map(e=>e.name),source:t.source})}unregister(e){let t=[];if(Array.isArray(e)){let n=!0;for(let r of e)this.tools.delete(r)?t.push(r):n=!1;return t.length>0&&this.onMutation?.({removed:t}),n}let n=this.tools.delete(e);return n&&this.onMutation?.({removed:[e]}),n}unregisterBySource(e){let t=[...this.tools.values()].filter(t=>t.metadata.source===e).map(e=>e.name);for(let e of t)this.tools.delete(e);return t.length>0&&this.onMutation?.({removed:t,source:e}),t.length}get(e){return this.tools.get(e)}has(e){return this.tools.has(e)}getAll(){return[...this.tools.values()].sort((e,t)=>e.name.localeCompare(t.name))}getAvailable(e=`standard`){let t=$[e]??$.standard;return this.getAll().filter(e=>t.has(e.metadata.preset))}project(e=`standard`){let t=$[e]??$.standard,n=[],r=[];for(let e of this.getAll())t.has(e.metadata.preset)&&(e.exposure===`deferred`?r.push(e):e.exposure!==`hidden`&&n.push(e));return{direct:n,deferred:r}}static TOOL_GUIDANCE_BUDGET_TOKENS=5e3;static estimatedTokens(e){let t=0;for(let n of e)/[\u3000-\u9fff\uff00-\uffef]/.test(n)&&t++;return Math.ceil(t+(e.length-t)/4)}buildToolGuidance(t=`standard`){let{direct:n,deferred:r}=this.project(t);if(n.length===0&&r.length===0)return``;let i=new Map;for(let e of n){let{snippet:t,guideline:n,category:r}=e.metadata,a=n?`${r??``}\u0000${n}`:`\u0000tool:${e.name}`,o=i.get(a);o||(o={guideline:n,entries:[]},i.set(a,o));let s=[`#### ${e.name}`];!n&&!t&&s.push(e.description),t&&s.push(`Example:\n\`\`\`\n${t}\n\`\`\``),o.entries.push(s.join(`
124
128
  `))}let a=[];for(let e of i.values()){let t=[];e.guideline&&t.push(e.guideline),t.push(...e.entries),a.push(t.join(`
125
129
 
126
130
  `))}let o=e.TOOL_GUIDANCE_BUDGET_TOKENS,s=0,c=[],l=0;for(let t of a){let n=e.estimatedTokens(t);s+n<=o?(c.push(t),s+=n):l++}return l>0&&c.push(`> ⚠ [${l} additional tool guidance section(s) omitted — guidance budget (${o} tokens) reached; use tool_search or the tool's own description]`),r.length>0&&c.push(`> ${r.length} additional tool(s) discoverable via tool_search (not shown here to keep this section focused; use tool_search to look them up by name or capability).`),c.length===0?``:`### Tool Reference (per category)\n\n${c.join(`
127
131
 
128
- `)}`}clear(){this.tools.size>0&&this.tools.clear()}dispose(){this.clear(),this.binaryToolExecutors&&=(this.binaryToolExecutors.dispose(),null);for(let e of this.disposers.splice(0))Promise.resolve().then(e).catch(()=>void 0)}};const sa=(e,t)=>{let n=new oa,r=lt(e,t.processTracker);return n.setBinaryToolExecutors(r),ia(n,e,t),n},ca=new Set([`task_delegate`]),la=new class{name=`noop`;wrap(e){return{command:e.command,args:e.args,options:e.options}}async dispose(){}};function ua(){return la}var da=class{name=`macos-seatbelt`;options;profilePathByContent=new Map;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=this.buildProfile(e.projectRoot);return{command:`/usr/bin/sandbox-exec`,args:[`-f`,this.ensureProfileSync(t),e.command,...e.args],options:{...e.options}}}async dispose(){let e=[...this.profilePathByContent.values()];this.profilePathByContent.clear(),await Promise.allSettled(e.map(e=>be(e).catch(()=>void 0)))}buildProfile(e){let t=P(e),n=ie(),r=new Set;for(let e of[t,n,...this.options.writablePaths.map(e=>P(e))]){r.add(e);try{r.add(me(e))}catch{}}let i=e=>e.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`),a=[...r].map(e=>`(subpath "${i(e)}")`).join(`
132
+ `)}`}clear(){this.tools.size>0&&this.tools.clear()}dispose(){this.clear(),this.binaryToolExecutors&&=(this.binaryToolExecutors.dispose(),null);for(let e of this.disposers.splice(0))Promise.resolve().then(e).catch(()=>void 0)}};const ba=(e,t)=>{let n=new ya,r=ft(e,t.processTracker);return n.setBinaryToolExecutors(r),_a(n,e,t),n},xa=new Set([`task_delegate`]),Sa=new class{name=`noop`;wrap(e){return{command:e.command,args:e.args,options:e.options}}async dispose(){}};function Ca(){return Sa}var wa=class{name=`macos-seatbelt`;options;profilePathByContent=new Map;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=this.buildProfile(e.projectRoot);return{command:`/usr/bin/sandbox-exec`,args:[`-f`,this.ensureProfileSync(t),e.command,...e.args],options:{...e.options}}}async dispose(){let e=[...this.profilePathByContent.values()];this.profilePathByContent.clear(),await Promise.allSettled(e.map(e=>xe(e).catch(()=>void 0)))}buildProfile(e){let t=P(e),n=ie(),r=new Set;for(let e of[t,n,...this.options.writablePaths.map(e=>P(e))]){r.add(e);try{r.add(me(e))}catch{}}let i=e=>e.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`),a=[...r].map(e=>`(subpath "${i(e)}")`).join(`
129
133
  `),o=this.options.network===`deny`?`(deny network*)
130
134
  `:`(allow network*)
131
135
  `,s=new Set;for(let e of this.options.denyReadPaths){let t=P(e);s.add(t);try{s.add(me(t))}catch{}}return`(version 1)
@@ -148,5 +152,5 @@ ${s.size>0?`
148
152
  ; credential read isolation (opt-in) — last match wins, denies even within writable roots
149
153
  `+[...s].map(e=>`(deny file-read* (subpath "${i(e)}"))`).join(`
150
154
  `)+`
151
- `:``}`}ensureProfileSync(e){let t=this.profilePathByContent.get(e);if(t&&T(t))return t;let n=`${ie()}/otto-sandbox-${Se()}.sb`;return ge(n,e,`utf-8`),this.profilePathByContent.set(e,n),n}};function fa(e){return new da(e)}var pa=class{name=`bubblewrap`;options;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=P(e.projectRoot),n=P(e.options.cwd??t),r=[`--unshare-all`,`--cap-drop`,`ALL`,`--die-with-parent`];for(let e of ma)T(e)&&r.push(`--ro-bind`,e,e);r.push(`--tmpfs`,`/tmp`),r.push(`--bind`,t,t);let i=[t];for(let e of this.options.writablePaths){let t=P(e);i.push(t),r.push(`--bind`,t,t)}for(let e of this.options.denyReadPaths){let t=P(e);i.some(e=>t===e||t.startsWith(`${e}/`))&&r.push(`--tmpfs`,t)}return r.push(`--proc`,`/proc`),this.options.network===`deny`?r.push(`--unshare-net`):r.push(`--share-net`),r.push(`--chdir`,n),r.push(e.command,...e.args),{command:`bwrap`,args:r,options:{...e.options}}}async dispose(){}};const ma=[`/usr`,`/bin`,`/lib`,`/lib64`,`/etc`,`/dev`,`/sys`];function ha(e){return new pa(e)}function ga(){let e=re(),t=process.env.OTTO_AUTH_PATH;return[M(e,`.ssh`),M(e,`.aws`),M(e,`.gnupg`),t?j(t):M(e,`.config`,`otto`)]}function _a(e={}){switch(e.force??va()){case`seatbelt`:return fa({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});case`bubblewrap`:return ha({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});default:return ua()}}function va(){return process.platform===`darwin`?`seatbelt`:process.platform===`linux`?`bubblewrap`:`noop`}function ya(){let e=va();return e===`seatbelt`?T(`/usr/bin/sandbox-exec`):e===`bubblewrap`?[`/usr/bin/bwrap`,`/usr/local/bin/bwrap`,`/bin/bwrap`].some(e=>T(e))?!0:(process.env.PATH??``).split(`:`).filter(Boolean).some(e=>T(`${e}/bwrap`)):!1}export{ct as BinaryToolExecutorRegistry,qr as LSPClient,ca as ORCHESTRATION_TOOL_NAMES,ta as TOOL_NODES,oa as ToolRegistry,Xn as buildDelegationAgentListDescription,cr as capabilityGapDedupKey,lt as createBinaryToolExecutorRegistry,ha as createBubblewrapSandbox,ur as createCapabilityGap,xr as createCaptureFileState,xn as createEdit,ze as createGrillMe,Cr as createLearn,si as createLspDefinition,pi as createLspDiagnostics,hi as createLspPrepareRename,li as createLspReferences,_i as createLspRename,di as createLspSymbols,fa as createMacOsSeatbeltSandbox,ua as createNoopSandbox,_a as createPlatformSandbox,Zt as createRead,yi as createSessionManagerTool,mr as createTaskControl,fr as createTaskInspect,sa as createToolRegistry,Tr as createToolSearch,Vn as createWebFetch,Yn as createWebSearch,rn as createWrite,yr as createWriteTodos,ga as credentialDenyReadPaths,ii as findWorkspaceRoot,ya as isPlatformSandboxAvailable,J as lspManager,ia as registerBuiltinTools,Z as withLspClient};
155
+ `:``}`}ensureProfileSync(e){let t=this.profilePathByContent.get(e);if(t&&D(t))return t;let n=`${ie()}/otto-sandbox-${Ce()}.sb`;return _e(n,e,`utf-8`),this.profilePathByContent.set(e,n),n}};function Ta(e){return new wa(e)}var Ea=class{name=`bubblewrap`;options;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=P(e.projectRoot),n=P(e.options.cwd??t),r=[`--unshare-all`,`--cap-drop`,`ALL`,`--die-with-parent`];for(let e of Da)D(e)&&r.push(`--ro-bind`,e,e);r.push(`--tmpfs`,`/tmp`),r.push(`--bind`,t,t);let i=[t];for(let e of this.options.writablePaths){let t=P(e);i.push(t),r.push(`--bind`,t,t)}for(let e of this.options.denyReadPaths){let t=P(e);i.some(e=>t===e||t.startsWith(`${e}/`))&&r.push(`--tmpfs`,t)}return r.push(`--proc`,`/proc`),this.options.network===`deny`?r.push(`--unshare-net`):r.push(`--share-net`),r.push(`--chdir`,n),r.push(e.command,...e.args),{command:`bwrap`,args:r,options:{...e.options}}}async dispose(){}};const Da=[`/usr`,`/bin`,`/lib`,`/lib64`,`/etc`,`/dev`,`/sys`];function Oa(e){return new Ea(e)}function ka(){let e=re(),t=process.env.OTTO_AUTH_PATH;return[M(e,`.ssh`),M(e,`.aws`),M(e,`.gnupg`),t?j(t):M(e,`.config`,`otto`)]}function Aa(e={}){switch(e.force??ja()){case`seatbelt`:return Ta({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});case`bubblewrap`:return Oa({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});default:return Ca()}}function ja(){return process.platform===`darwin`?`seatbelt`:process.platform===`linux`?`bubblewrap`:`noop`}function Ma(){let e=ja();return e===`seatbelt`?D(`/usr/bin/sandbox-exec`):e===`bubblewrap`?[`/usr/bin/bwrap`,`/usr/local/bin/bwrap`,`/bin/bwrap`].some(e=>D(e))?!0:(process.env.PATH??``).split(`:`).filter(Boolean).some(e=>D(`${e}/bwrap`)):!1}export{dt as BinaryToolExecutorRegistry,yr as CapabilityGapReportStore,oi as LSPClient,xa as ORCHESTRATION_TOOL_NAMES,ma as TOOL_NODES,ya as ToolRegistry,sr as buildDelegationAgentListDescription,br as capabilityGapDedupKey,ft as createBinaryToolExecutorRegistry,Oa as createBubblewrapSandbox,Sr as createCapabilityGap,Nr as createCaptureFileState,Mn as createEdit,He as createGrillMe,Fr as createLearn,bi as createLspDefinition,Ei as createLspDiagnostics,Oi as createLspPrepareRename,Si as createLspReferences,Ai as createLspRename,wi as createLspSymbols,Ta as createMacOsSeatbeltSandbox,Ca as createNoopSandbox,Aa as createPlatformSandbox,cn as createRead,Mi as createSessionManagerTool,Er as createTaskControl,wr as createTaskInspect,ba as createToolRegistry,Lr as createToolSearch,Qn as createWebFetch,or as createWebSearch,mn as createWrite,jr as createWriteTodos,ka as credentialDenyReadPaths,_i as findWorkspaceRoot,Ma as isPlatformSandboxAvailable,Y as lspManager,_a as registerBuiltinTools,Z as withLspClient};
152
156
  //# sourceMappingURL=index.js.map