@x-otto/tools 0.1.0-alpha.13 → 0.1.0-alpha.15
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.d.ts +3 -125
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -36
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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 ee,releaseFileLock as b,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,resolveTodoSyncTargets as ze}from"@x-otto/orchestration-contracts";import{fileURLToPath as Be,pathToFileURL as L}from"node:url";const Ve=e.object({label:e.string(),rationale:e.string().describe(`选这项的含义/利弊(plain language)`),recommended:e.boolean().optional(),preview:e.string().optional().describe(`两段具体产物并排对比`)}),He=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(Ve).optional(),allowFreeform:e.boolean().optional().default(!0),multiSelect:e.boolean().optional().default(!1),evidence:e.array(e.string()).optional().describe(`引用的本地证据/社区方案`)});function Ue(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:He,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),We=I(process.env.TOOLS_BINARY_DOWNLOAD_TIMEOUT_MS,12e5),Ge=I(process.env.TOOLS_MAX_WRITE_BYTES,10*1024*1024),V=I(process.env.TOOLS_MAX_FETCH_BYTES,5*1024*1024),Ke=I(process.env.TOOLS_BINARY_PROBE_TIMEOUT_MS,5e3);function H(e,t){return Object.assign(Error(e),{errorKind:t})}const qe=a(`@x-otto/tools:binary`),Je=256*1024;async function Ye(e,t=[`--version`]){try{return(await Xe(e,t,{timeout:Ke})).status===0}catch{return!1}}function Xe(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 Ze(e,t=e){let n=E.platform()===`win32`?`.exe`:``,r=d(t,e)+n;try{if(D(r))return r}catch{qe.debug(`Error checking existence of ${r}, will try PATH lookup: %s`,r)}return await Ye(e)?e:null}const Qe=`tool-download`;async function $e(e,t,n){if(!t){qe.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 et(e,t,n){let r=async(t,n)=>{try{let{status:e,stderr:r}=await Xe(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 tt(e,t,n){let i=Date.now()+We,a={prefix:Qe,timeoutMs:3e5};for(;;){if(await r(e,t,a))return!0;if(D(n)||Date.now()>i)return!1;await ke(200)}}async function nt(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 tt(o,n,l))try{if(D(l))return;let e=await fetch(r,{headers:{"User-Agent":je},signal:AbortSignal.timeout(We)});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 $e(d,i,r),O(c,{recursive:!0}),O(p,{recursive:!0}),await et(r,d,p);let n=rt(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 qe.warn(e),e instanceof Error?e:Error(String(e))}finally{he(d,{force:!0}),he(p,{recursive:!0,force:!0}),await b(o,n,{prefix:Qe})}}function rt(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 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 Ze(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 Ze(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 nt({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>=Je)return;let t=Je-u;e.byteLength>t?(c.push(e.subarray(0,t)),u=Je):(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 it(e,t,n){return`https://github.com/${e}/releases/download/v${t}/${n}`}const at={"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`},W=`10.4.2`,ot={"darwin/arm64":`fd-v${W}-aarch64-apple-darwin.tar.gz`,"darwin/x64":`fd-v${W}-x86_64-apple-darwin.tar.gz`,"linux/arm64":`fd-v${W}-aarch64-unknown-linux-gnu.tar.gz`,"linux/x64":`fd-v${W}-x86_64-unknown-linux-gnu.tar.gz`,"win32/arm64":`fd-v${W}-aarch64-pc-windows-msvc.zip`,"win32/x64":`fd-v${W}-x86_64-pc-windows-msvc.zip`};var st=class extends U{name=`fd`;version=W;repository=`sharkdp/fd`;constructor(e,t){super(e,t)}resolveAsset(){let e=`${T.platform()}/${T.arch()}`,t=ot[e];if(!t)throw Error(`Unsupported platform/architecture: ${e}`);return t}resolveUrl(){return it(this.repository,this.version,this.resolveAsset())}resolveChecksum(){return at[this.resolveAsset()]}};const ct=(e,t)=>new st(e,t);function lt(e,t,n){return`https://github.com/${e}/releases/download/${t}/${n}`}const ut={"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 dt=class extends U{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 lt(this.repository,this.version,this.resolveAsset())}resolveChecksum(){return ut[this.resolveAsset()]}};const ft=(e,t)=>new dt(e,t),pt=a(`@x-otto/tools:binary-executor-registry`);var mt=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)?pt.warn(`Tool ${e.name} is already registered, skipping`):this.binaries.set(e.name,e)}unregister(e){this.binaries.has(e)?this.binaries.delete(e):pt.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 ht=(e,t)=>{let n=new mt;return n.register(ct(e,t)),n.register(ft(e,t)),n};function gt(e){return typeof e==`function`?e():e}const _t=()=>({chunks:Buffer.alloc(0),stdout:Buffer.alloc(0),stderr:Buffer.alloc(0),exitCode:-1,timedOut:!1}),vt=(e,t)=>{try{process.kill(-e,t)}catch{try{process.kill(e,t)}catch{}}},yt=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=_t(),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}vt(e,`SIGTERM`),m=setTimeout(()=>vt(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 bt(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 xt(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(gt(n.backend)??yt).execute(e,t,{cwd:i,env:a,timeout:o,signal:s},c)}const St=new Set([2,126,127]),Ct={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 wt(e,t,n=`unknown`){if(e===159)return{denied:!0,reason:`exit code 159 (128+SIGSYS, seccomp violation)`,backend:n};if(e!==null&&e!==2&&St.has(e))return{denied:!1,backend:n};let r=Tt(t,Ct[n].benign),i=Et(r,Ct[n].fatal);if(i)return{denied:!0,reason:`${n} dialect: ${i}`,backend:n};let a=Et(r,Ct.unknown.fatal);return a?{denied:!0,reason:`generic signature: ${a}`,backend:`unknown`}:{denied:!1,backend:n}}function Tt(e,t){return t.length===0?e:e.split(`
|
|
2
2
|
`).filter(e=>!t.some(t=>t.test(e))).join(`
|
|
3
|
-
`)}function Et(e,t){for(let n of t){let t=n.exec(e);if(t)return t[0]}}const Dt=/\b(https?|wss?):\/\/([-\w.]+)(?::(\d{2,5}))?(\/[^\s)\],."']*)?/i,Ot=/\b(?:listening on\s+(?:port\s+)?|(?:running\s+)?at\s+:|on\s+:|port[:\s]+)(\d{2,5})\b/i;function kt(e){return/^wss?$/i.test(e)?`ws`:`http`}function At(e){let t=e.match(Dt);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:kt(n)}}let n=e.match(Ot);if(n){let e=Number(n[1]);if(e>0&&e<=65535)return{port:e,protocol:`tcp`}}return null}const jt=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.`)}),Mt=a(`@x-otto/tools:bash`);function Nt(e,t,n,r,i,a){return{name:`bash`,description:`Execute a shell command and return stdout/stderr. Default timeout is 30 seconds.`,parameters:jt,regenerableOutput:!0,async execute({params:o,signal:s,id:c,sessionId:l}){let u=P(e,o.targetDir??`.`),d=o.timeout??B,f=typeof n==`function`?n({command:o.command}):n,p=gt(i);return o.run_in_background?!r||!l?{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`}:It(o.command,u,e,f,r,l,c,a?.()):await Bt(o.command,u,e,d,t,s,f,p,a?.())}}}function Pt(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 Ft(e){let t=``;return n=>{t+=n.toString(`utf-8`);let r;for(;(r=t.indexOf(`
|
|
3
|
+
`)}function Et(e,t){for(let n of t){let t=n.exec(e);if(t)return t[0]}}const Dt=/\b(https?|wss?):\/\/([-\w.]+)(?::(\d{2,5}))?(\/[^\s)\],."']*)?/i,Ot=/\b(?:listening on\s+(?:port\s+)?|(?:running\s+)?at\s+:|on\s+:|port[:\s]+)(\d{2,5})\b/i;function kt(e){return/^wss?$/i.test(e)?`ws`:`http`}function At(e){let t=e.match(Dt);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:kt(n)}}let n=e.match(Ot);if(n){let e=Number(n[1]);if(e>0&&e<=65535)return{port:e,protocol:`tcp`}}return null}const jt=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.`)}),Mt=a(`@x-otto/tools:bash`);function Nt(e,t,n,r,i,a){return{name:`bash`,description:`Execute a shell command and return stdout/stderr. Default timeout is 30 seconds; set reasonable timeouts for long-running commands rather than waiting indefinitely. Avoid commands requiring interactive input. Prefer non-interactive flags (e.g. git --no-pager, --non-interactive).`,parameters:jt,regenerableOutput:!0,async execute({params:o,signal:s,id:c,sessionId:l}){let u=P(e,o.targetDir??`.`),d=o.timeout??B,f=typeof n==`function`?n({command:o.command}):n,p=gt(i);return o.run_in_background?!r||!l?{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`}:It(o.command,u,e,f,r,l,c,a?.()):await Bt(o.command,u,e,d,t,s,f,p,a?.())}}}function Pt(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 Ft(e){let t=``;return n=>{t+=n.toString(`utf-8`);let r;for(;(r=t.indexOf(`
|
|
4
4
|
`))!==-1;){let n=t.slice(0,r);t=t.slice(r+1);let i=At(n);i&&e(i)}}}function It(e,t,n,r,i,a,o,c){let l=`bg_${a}_${o}`,u=s();ne(Lt,Date.now());try{let o=Ft(e=>{i.updateDetectedService(l,e)}),{child:s,pid:d}=bt(`sh`,[`-c`,e],{cwd:t,projectRoot:n,sandbox:r,env:c,logPath:u,onOutputChunk:e=>o(e)}),f=Pt(e);try{i.register({id:l,sessionId:a,child:s,command:e,cwd:t,logPath:u,port:f})}catch(e){try{process.kill(-d,`SIGKILL`)}catch{try{s.kill(`SIGKILL`)}catch{}}throw e}return{content:[{type:`text`,text:`Started in background.\nid: ${l}\npid: ${d}\nlog: ${u}${f?`\nlikely port: ${f}`:``}\n\nRead output with bash_output(background_id="${l}"); stop it with kill_shell(background_id="${l}"). It is auto-killed when the session ends.`}],details:{backgroundProcessId:l,pid:d,logPath:u,port:f}}}catch(e){let t=e instanceof Error?e.message:String(e);return Mt.warn({err:t},`Failed to start background process`),{content:[{type:`text`,text:`Failed to start background process: ${t}`}],isError:!0,errorKind:`runtime`}}}const Lt=60*6e4;function Rt(e){return e?e.includes(`seatbelt`)?`seatbelt`:e.includes(`bubblewrap`)||e.includes(`bwrap`)?`bubblewrap`:`unknown`:`unknown`}function zt(e){let t=e.trim();return t?(t.length>2048?`${t.slice(0,2048)}…`:t).split(`
|
|
5
5
|
`).slice(0,8).join(`
|
|
6
6
|
`):``}async function Bt(e,t,n,r,i,a,c,u,d){let f=z*2,p=[],m=0,h=0,g=0,_=0,v,y,ee=()=>{if(!i)return;let e=w(Buffer.concat(p).toString(`utf-8`),{maxLines:R,maxBytes:z});i({content:[{type:`text`,text:e.content||``}],details:{truncation:x(e),outputPath:v}})},b=await xt(`sh`,[`-c`,e],{timeout:r,signal:a,cwd:t,sandbox:c,projectRoot:n,backend:u,env:d,onProgress:e=>{if(p.push(e),m+=e.byteLength,h+=e.byteLength,m>z&&!v){ne(Lt,Date.now()),te(6048e5,Date.now());let e=o();try{O(j(e),{recursive:!0})}catch{e=s()}v=e,y=ue(e),y.on(`error`,e=>{Mt.warn({err:e.message},`bash overflow log write failed; disabling overflow log`),y=void 0,v=void 0});for(let e of p)y.write(e)}else y&&y.write(e);for(;h>f&&p.length>1;){let e=p.shift();e&&(h-=e.byteLength)}g+=e.byteLength;let t=Date.now();g>=4096&&t-_>=100&&(g=0,_=t,ee())}});y?.end();let S=Buffer.concat(p).toString(`utf-8`),C=w(S,{maxLines:R,maxBytes:z}),T=C.content||`(no output)`,E={truncation:x(C),outputPath:v};if(C.truncated){let e=C.totalLines-C.outputLines+1,t=C.totalLines,n=v?` Full output: ${v}`:``;if(C.lastLinePartial){let e=l(Buffer.byteLength(S.split(`
|
|
@@ -84,54 +84,52 @@ import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_
|
|
|
84
84
|
|
|
85
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 or=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)}},sr=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 ir(await i.text(),t)}};function cr(e={},t=process.env){if(e.provider)return e.provider;let n=e.apiKey??t.BRAVE_SEARCH_API_KEY;return n?new or(n,e.fetchImpl):new sr(e.fetchImpl)}function lr(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:rr,readonly:!0,regenerableOutput:!0,async execute({params:e,signal:n}){let{query:r,limit:i=10}=e,a=cr(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:ar(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 ur(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
86
|
`)}const dr=e.string().optional().describe(`Optional child session ID; with sticky mode, later calls reuse the same child context.`),fr=e.enum([`ephemeral`,`sticky`]).optional().default(`ephemeral`).describe(`Child session lifecycle: ephemeral deletes after the task; sticky keeps for later reuse.`),pr=e.enum(Ie).optional().describe(`Model slot to use for this task`),mr=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:dr,sessionMode:fr,slot:pr,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 hr(e,t,n,r){if(!e)return;let i=ze(r,n);if(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 gr(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 _r(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 vr(e,t,n,r,i,a=1,o,s){let c=ur((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:mr,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 gr(r,e.prompt,s,c,a);if(e.mode===`team`)return _r(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 hr(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 yr=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 br(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:yr,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 xr=e.object({
|
|
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=wr(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 Er=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 Dr(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:Er,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(`
|
|
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 yr=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 br(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:yr,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 xr=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 Sr(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:xr,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(`
|
|
90
88
|
`)}],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(`
|
|
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
|
|
92
|
-
`)}`}]}}}}const
|
|
93
|
-
`)||`(empty todo list)`}],details:{todos:e}}}return{content:[{type:`text`,text:`Failed to update todos`}],isError:!0,errorKind:`runtime`}}}}const
|
|
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
|
|
95
|
-
`)}]}}const
|
|
89
|
+
`)}]}}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 Cr=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 wr(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:Cr,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 Tr=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 Er(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:Tr,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(`
|
|
90
|
+
`)}`}]}}}}const Dr=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`)}),Or=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(Dr).describe(`Todo items`)});function kr(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). 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.`,parameters:Or,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(`
|
|
91
|
+
`)||`(empty todo list)`}],details:{todos:e}}}return{content:[{type:`text`,text:`Failed to update todos`}],isError:!0,errorKind:`runtime`}}}}const Ar=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 jr(e){return{name:`capture_file_state`,description:`Capture a snapshot of the current workspace file state.`,parameters:Ar,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 Mr=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 Nr(e,t){return{name:`learn`,description:`Record an operational lesson learned during this session for future reference.`,parameters:Mr,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 Pr=e.object({query:e.string().describe(`Tool name or keyword to search for in the deferred tool pool`)});function Fr(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:Pr,readonly:!0,async execute({params:t}){let n=e(),r=t.query.toLowerCase(),i=n.find(e=>e.name.toLowerCase()===r);if(i)return Ir(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 Ir(a[0]);let o=a.map(e=>`- **${e.name}**: ${typeof e.description==`string`?e.description.slice(0,100):`(no description)`}`).join(`
|
|
92
|
+
`);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 Ir(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(`
|
|
93
|
+
`)}]}}const Lr={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`},Rr={1:`error`,2:`warning`,3:`information`,4:`hint`},zr={".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`},Br={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`},Vr={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 Be(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 Hr(e,t){try{let n=(await k(e,`utf-8`)).split(`
|
|
96
94
|
`),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(`
|
|
97
95
|
`))}}return await A(e,n.join(`
|
|
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
|
|
99
|
-
`+
|
|
100
|
-
`)}function
|
|
96
|
+
`),`utf-8`),{success:!0,editCount:t.length}}catch(e){return{success:!1,editCount:0,error:e instanceof Error?e.message:String(e)}}}async function Ur(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 Hr(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 Hr(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 Wr(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 Gr(e){return Lr[e]||`Unknown(${e})`}function Kr(e){return e?Rr[e]||`unknown(${e})`:`unknown`}function qr(e,t=0){let n=` `.repeat(t),r=Gr(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+=`
|
|
97
|
+
`+qr(n,t+1);return a}function Jr(e){let t=Gr(e.kind),n=Wr(e.location),r=e.containerName?` (in ${e.containerName})`:``;return`${e.name} (${t})${r} - ${n}`}function Yr(e){let t=Kr(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 Xr(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 Zr(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 Qr(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(`
|
|
98
|
+
`)}function $r(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 ei(){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 ti(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 ii(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 ii(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`),ni=32*1024*1024;function ri(e){let t=Buffer.from(e,`utf-8`);return Buffer.concat([Buffer.from(`Content-Length: ${t.byteLength}\r\n\r\n`,`ascii`),t])}var ii=class{process=null;nextId=1;pending=new Map;buffer=Buffer.alloc(0);contentLength=-1;processExited=!1;stderrBuffer=[];diagnosticsStore=new Map;diagnosticsWaiter=ei();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=ai(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 $r(r,2e3)}catch(e){let t=this.stderrBuffer.join(`
|
|
101
99
|
`);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>
|
|
100
|
+
`);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>ni&&this.contentLength===-1){X.warn(`LSP buffer exceeded ${ni} 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
|
|
103
101
|
\r
|
|
104
102
|
`);if(e===-1)break;let t=this.buffer.subarray(0,e).toString(`ascii`);for(let e of t.split(`\r
|
|
105
|
-
`))if(e.startsWith(`Content-Length: `)){let t=parseInt(e.slice(16),10);Number.isInteger(t)&&t>=0&&t<=
|
|
103
|
+
`))if(e.startsWith(`Content-Length: `)){let t=parseInt(e.slice(16),10);Number.isInteger(t)&&t>=0&&t<=ni&&(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,ti(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(`
|
|
106
104
|
`);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(`
|
|
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(
|
|
105
|
+
`);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(ri(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(ri(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(ri(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=zr[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 ai(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 oi(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 si(e){if(!D(e))return null;try{return y(fe(e,`utf-8`))}catch{return null}}function ci(e){let t=e+`.jsonc`;if(D(t))return t;let n=e+`.json`;return D(n)?n:t}function li(){let e=u();return{project:ci(M(Ae,`otto`)),user:ci(M(e,`otto`))}}let ui;function di(){if(ui)return ui;let e=li(),t=new Map,n=si(e.project);n&&t.set(`project`,n);let r=si(e.user);return r&&t.set(`user`,r),ui=t,t}function fi(){let e=di(),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(Vr))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 pi(e){let t=fi();for(let n of t)if(n.extensions.includes(e)&&oi(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=Br[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 mi=[`.git`,`package.json`,`pyproject.toml`,`Cargo.toml`,`go.mod`,`pom.xml`,`build.gradle`];function hi(e){let t=P(e);(!D(t)||!ge(t).isDirectory())&&(t=j(t));let n=``;for(;t!==n;){for(let e of mi)if(D(M(t,e)))return t;n=t,t=j(t)}return j(P(e))}function gi(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(`
|
|
108
106
|
`)}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(`
|
|
109
|
-
`)}async function Z(e,t){let n=P(e),r=
|
|
110
|
-
`)}]}}}}const
|
|
111
|
-
`)}]}}}}const
|
|
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=>
|
|
113
|
-
`)}]}}}}const
|
|
114
|
-
`)}]}}}}const
|
|
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
|
|
116
|
-
`)}]}}}}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.`),modelId:e.string().optional().describe(`Model to run this scheduled task with, e.g. "deepseek/deepseek-v4-flash". Defaults to the current session model. Invalid ids are rejected at creation time.`),thinkingLevel:e.enum([`low`,`medium`,`high`,`xhigh`,`max`]).optional().describe(`Thinking level for the model (defaults to the provider default).`),onEvent:e.enum([`job.applied`,`job.failed`,`job.canceled`]).optional().describe(`Fire when an in-process event happens instead of on the cron schedule. "job.applied" = when any background job's diff gets applied; "job.failed"/"job.canceled" similarly. Requires target:"session" (injects into THIS conversation) — event triggers cannot dispatch isolated jobs. Use for "wait until the current work lands, then continue" flows. When set, "cron" is ignored (pass any valid placeholder like "0 0 * * *").`)}),e.object({task_id:e.string().describe(`ID of the scheduled task to delete`)}),e.object({task_id:e.string().describe(`ID of the scheduled task to run immediately`)}),e.object({task_id:e.string().describe(`ID of the scheduled task whose currently running job should be canceled`)});const
|
|
107
|
+
`)}async function Z(e,t){let n=P(e),r=pi(we(n));if(r.status!==`found`)throw H(gi(r),`not_found`);let i=r.server,a=hi(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 _i=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 vi(e){return{name:`lsp_goto_definition`,description:`Jump to symbol definition. Find WHERE something is defined.`,parameters:_i,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(Wr).join(`
|
|
108
|
+
`)}]}}}}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`),includeDeclaration:e.boolean().optional().describe(`Include the declaration itself (default true)`)});function bi(e){return{name:`lsp_find_references`,description:`Find ALL usages/references of a symbol across the entire workspace.`,parameters:yi,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(Wr);return r&&i.unshift(`Found ${n} references (showing first 200):`),{content:[{type:`text`,text:i.join(`
|
|
109
|
+
`)}]}}}}const xi=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 Si(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:xi,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(Jr);return i&&a.unshift(`Found ${n} symbols (showing first ${r}):`),{content:[{type:`text`,text:a.join(`
|
|
110
|
+
`)}]}}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=>qr(e))):o.push(...a.map(Jr)),{content:[{type:`text`,text:o.join(`
|
|
111
|
+
`)}]}}}}const Ci=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 wi(e){return{name:`lsp_diagnostics`,description:`Get errors, warnings, hints from language server BEFORE running build.`,parameters:Ci,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=Xr(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(Yr);return i&&a.unshift(`Found ${r} diagnostics (showing first 200):`),{content:[{type:`text`,text:a.join(`
|
|
112
|
+
`)}]}}}}const Ti=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 Ei(e){return{name:`lsp_prepare_rename`,description:`Check if rename is valid. Use BEFORE lsp_rename.`,parameters:Ti,pathParams:[`filePath`],async execute({params:e}){return{content:[{type:`text`,text:Zr(await Z(e.filePath,async t=>await t.prepareRename(e.filePath,e.line,e.character)))}]}}}}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`),newName:e.string().describe(`New symbol name`)});function Oi(e){return{name:`lsp_rename`,description:`Rename symbol across entire workspace. APPLIES changes to all files.`,parameters:Di,pathParams:[`filePath`],async execute({params:t}){return{content:[{type:`text`,text:Qr(await Ur(await Z(t.filePath,async e=>await e.rename(t.filePath,t.line,t.character,t.newName)),e))}]}}}}const ki=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 Ai(e){let{sessionManager:t}=e;return{name:`session_manager`,description:`Manage conversation sessions: list, create, remove, compact.`,parameters:ki,async execute({params:e}){if(!t)throw Error(`SessionManager is not provided in options`);return await ji(e.action,t,e.sessionId,e.title)}}}async function ji(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(`
|
|
113
|
+
`)}]}}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 Mi=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 Ni(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:Mi,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?Pi(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?Pi(await n(e.name,a)):{content:[{type:`text`,text:`name or path is required for execute action`}],isError:!0,errorKind:`validation`}}}}}function Pi(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 Fi=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 Ii(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:Fi,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 Li=e.object({name:e.string().describe(`The memory title as shown in the memory index`)});function Ri(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:Li,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 zi=/^archive:(.+):(\d+)$/;function Bi(e){let t=zi.exec(e);return t?{sessionId:t[1],index:Number.parseInt(t[2],10)}:null}const Vi=e.object({archivePath:e.string().describe(`The archive id shown in a compaction summary, in the form "archive:<sessionId>:<n>".`)}),Hi=e.object({}),Ui=`Conversation archive recall is not available in this session (no session-entry backed storage).`;function Wi(e,t){let n=Bi(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 Gi(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:Vi,readonly:!0,async execute({params:t,sessionId:n}){if(!e)return{content:[{type:`text`,text:Ui}],isError:!0,errorKind:`runtime`};let r=Wi(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 Ki(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:Hi,readonly:!0,async execute({sessionId:t}){if(!e||!t)return{content:[{type:`text`,text:Ui}],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(`
|
|
114
|
+
`)}]}}}}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.`),modelId:e.string().optional().describe(`Model to run this scheduled task with, e.g. "deepseek/deepseek-v4-flash". Defaults to the current session model. Invalid ids are rejected at creation time.`),thinkingLevel:e.enum([`low`,`medium`,`high`,`xhigh`,`max`]).optional().describe(`Thinking level for the model (defaults to the provider default).`),onEvent:e.enum([`job.applied`,`job.failed`,`job.canceled`]).optional().describe(`Fire when an in-process event happens instead of on the cron schedule. "job.applied" = when any background job's diff gets applied; "job.failed"/"job.canceled" similarly. Requires target:"session" (injects into THIS conversation) — event triggers cannot dispatch isolated jobs. Use for "wait until the current work lands, then continue" flows. When set, "cron" is ignored (pass any valid placeholder like "0 0 * * *").`)}),e.object({task_id:e.string().describe(`ID of the scheduled task to delete`)}),e.object({task_id:e.string().describe(`ID of the scheduled task to run immediately`)}),e.object({task_id:e.string().describe(`ID of the scheduled task whose currently running job should be canceled`)});const qi=e.object({type:e.enum([`create`,`list`,`delete`,`run`,`cancel`]).describe(`Schedule action to perform: "create" (new task), "list" (view all), "delete" (remove task), "run" (trigger immediately), "cancel" (stop running job). See tool description for per-action parameters.`),cron:e.string().optional().describe(`5-field cron expression (minute hour dom month dow). Examples: "0 9 * * 1-5" = weekdays 9am, "*/30 * * * *" = every 30 min. Required for create.`),prompt:e.string().optional().describe(`Natural language instruction for the agent to execute at the scheduled time. Required for create.`),recurring:e.boolean().optional().describe(`true = run on schedule repeatedly, false = one-shot (delete after first fire). Required for create.`),name:e.string().optional().describe(`Short label for the task (shown in schedule list). Optional for create.`),maxDurationMs:e.number().int().positive().optional().describe(`Timeout in milliseconds for a single fire attempt (default 300000 = 5 minutes). Raise for long tasks. Optional for create.`),target:e.enum([`job`,`session`]).optional().describe(`Where the prompt fires. Default "job": isolated worktree, produces a diff for /job apply. "session": injects into THIS conversation. Optional for create.`),modelId:e.string().optional().describe(`Model to run this scheduled task with, e.g. "deepseek/deepseek-v4-flash". Defaults to current session model. Optional for create.`),thinkingLevel:e.enum([`low`,`medium`,`high`,`xhigh`,`max`]).optional().describe(`Thinking level for the model. Optional for create.`),onEvent:e.enum([`job.applied`,`job.failed`,`job.canceled`]).optional().describe(`Fire on an in-process event instead of cron (requires target:"session"). Optional for create.`),task_id:e.string().optional().describe(`ID of the scheduled task. Required for delete/run/cancel.`)});function Ji(e){return{name:`schedule`,description:`Manage scheduled tasks: create (new cron/event task), list (view all), delete (remove), run (trigger immediately), cancel (stop running job). Use the "type" field to select the action; per-action parameters are shown above.`,parameters:qi,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule scheduling is not available in this session.`}],isError:!0};let{type:n,...r}=t;switch(n){case`create`:{let{onEvent:t,...n}=r;if(!n.cron||!n.prompt||n.recurring===void 0)return{content:[{type:`text`,text:`schedule create requires cron, prompt, recurring`}],isError:!0};let i={...n,eventTrigger:t?{source:`job`,event:t.slice(4)}:void 0};try{let t=e.create(i),n=t.target===`session`?` (fires into this conversation)`:``;return{content:[{type:`text`,text:`Schedule created: ${t.taskId} — "${t.name}"${n}
|
|
117
115
|
Next fire: ${new Date(t.nextFireAt).toLocaleString()}
|
|
118
116
|
Use schedule (type:list) to view all schedules, schedule (type:delete) to remove.`}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}}case`list`:{let t=e.list();return t.length===0?{content:[{type:`text`,text:`No scheduled tasks.`}]}:{content:[{type:`text`,text:t.map(e=>`- ${e.id}: ${e.name} (${e.recurring?e.cronExpression:`one-shot`}) next: ${new Date(e.nextFireAt).toLocaleString()}`).join(`
|
|
119
|
-
`)}]}}case`delete`:return r.task_id?{content:[{type:`text`,text:e.delete(r.task_id)?`Schedule ${r.task_id} deleted.`:`Schedule ${r.task_id} not found.`}]}:{content:[{type:`text`,text:`schedule delete requires task_id`}],isError:!0};case`run`:if(!r.task_id)return{content:[{type:`text`,text:`schedule run requires task_id`}],isError:!0};try{return e.run(r.task_id),{content:[{type:`text`,text:`Triggering schedule ${r.task_id}…`}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}case`cancel`:{if(!r.task_id)return{content:[{type:`text`,text:`schedule cancel requires task_id`}],isError:!0};let t=e.cancel(r.task_id);return{content:[{type:`text`,text:t.ok?`Schedule ${r.task_id} canceled.`:`Could not cancel ${r.task_id}: ${t.reason??`unknown`}`}]}}default:return{content:[{type:`text`,text:`unknown schedule action "${n}"`}],isError:!0}}}}}const Q=(...e)=>e,
|
|
120
|
-
`),
|
|
121
|
-
`),
|
|
122
|
-
`),
|
|
123
|
-
`)
|
|
124
|
-
`),
|
|
125
|
-
`),
|
|
126
|
-
`),
|
|
127
|
-
`),
|
|
128
|
-
`),
|
|
129
|
-
`),
|
|
117
|
+
`)}]}}case`delete`:return r.task_id?{content:[{type:`text`,text:e.delete(r.task_id)?`Schedule ${r.task_id} deleted.`:`Schedule ${r.task_id} not found.`}]}:{content:[{type:`text`,text:`schedule delete requires task_id`}],isError:!0};case`run`:if(!r.task_id)return{content:[{type:`text`,text:`schedule run requires task_id`}],isError:!0};try{return e.run(r.task_id),{content:[{type:`text`,text:`Triggering schedule ${r.task_id}…`}]}}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}case`cancel`:{if(!r.task_id)return{content:[{type:`text`,text:`schedule cancel requires task_id`}],isError:!0};let t=e.cancel(r.task_id);return{content:[{type:`text`,text:t.ok?`Schedule ${r.task_id} canceled.`:`Could not cancel ${r.task_id}: ${t.reason??`unknown`}`}]}}default:return{content:[{type:`text`,text:`unknown schedule action "${n}"`}],isError:!0}}}}}const Q=(...e)=>e,Yi=[`Prefer edit over write for modifying existing files — edit replaces exact text and is safer.`,`For edit: provide enough surrounding context to avoid ambiguous matches.`].join(`
|
|
118
|
+
`),Xi=[`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.",`Prefer dedicated tools (read, write, edit, grep, find) over shell equivalents (cat, sed, grep) for file operations.`,`Always check the exit code; on failure, inspect stdout/stderr before deciding next steps.`].join(`
|
|
119
|
+
`),Zi=[`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
|
+
`),Qi=[`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
|
+
`),$i=[`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"). 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
|
+
`),ea=[`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
|
+
`),ta=[`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
|
+
`),na=[`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
|
+
`),ra=[`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
|
+
`),ia=[`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
|
+
`),aa=[{names:[`read`,`write`,`edit`],preset:`minimal`,category:`fs`,guidance:Yi,create:e=>Q(dn(e.projectRoot),_n(e.projectRoot),Fn(e.projectRoot))},{names:[`bash`],preset:`minimal`,category:`shell`,guidance:Xi,create:e=>Q(Nt(e.projectRoot,void 0,e.features.sandbox,e.features.processRegistry,void 0,e.features.shellEnv))},{names:[`bash_output`,`kill_shell`],preset:`standard`,category:`shell`,when:e=>!!e.features.processRegistry,create:e=>Q(Wt(e.features.processRegistry),Kt(e.features.processRegistry))},{names:[`find`,`grep`],preset:`standard`,category:`search`,guidance:Zi,create:e=>Q(Yt(e.projectRoot,{binaryExecutorRegistry:e.registry.getBinaryToolExecutors()}),nn(e.projectRoot,{binaryToolExecutorRegistry:e.registry.getBinaryToolExecutors(),processTracker:e.processTracker}))},{names:[`web_fetch`,`web_search`],preset:`standard`,category:`web`,guidance:Qi,create:e=>Q(tr(e.projectRoot),lr(e.projectRoot,e.features.webSearch))},{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(Fr(()=>e.registry.project(`full`).deferred))},{names:[`task_delegate`,`delegate_job`,`write_todos`],preset:`standard`,category:`orchestration`,guidance:$i,create:e=>Q(vr(e.projectRoot,e.orchestration.dispatchTask,e.orchestration.writeTodos,e.fork?.fork,e.team?.runTeam,void 0,e.agents.callAgent,e.agentProfiles??[]),br(e.orchestration.startJob),kr(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(vi(e.projectRoot),bi(e.projectRoot),Si(e.projectRoot),wi(e.projectRoot),Ei(e.projectRoot),Oi(e.projectRoot)))},{names:[`list_models`,`task_inspect`,`task_control`,`grill_me`,`capture_file_state`,`learn`],preset:`full`,category:`orchestration`,guidance:ea,create:e=>Q(Er(e.features.listModels,e.features.sessionId),Sr(e.projectRoot,{get:e.orchestration.getTask,list:e.orchestration.listTasks,output:e.orchestration.getBackgroundOutput}),wr(e.projectRoot,{update:e.orchestration.updateTask,cancelBackground:e.orchestration.cancelBackground}),Ue(e.projectRoot,e.features.grill),jr(e.features.captureFileState),Nr(e.features.sessionId??``,e.features.learn))},{names:[`session_manager`],preset:`full`,category:`session`,guidance:ta,when:e=>!!e.session,create:e=>{let t=e.session;return t?Q(Ai({sessionManager:t.manager})):Q()}},{names:[`skill`],preset:`full`,category:`skill`,guidance:na,create:e=>Q(Ni(e.projectRoot,e.loadSkill,e.executeSkill,e.features.sessionId))},{names:[`memory_record`,`memory_read`],preset:`full`,category:`memory`,guidance:ra,create:e=>Q(Ii(e.memory?.recordMemory),Ri(e.memory?.readMemory))},{names:[`memory_archive_read`,`memory_archive_list`],preset:`full`,category:`memory`,when:e=>!!e.memory?.archive,guidance:ia,create:e=>Q(Gi(e.memory?.archive?.read),Ki(e.memory?.archive?.list))},{names:[`schedule`],preset:`full`,category:`schedule`,guidance:`Schedule tool — manage timer tasks (create/list/delete/run/cancel) via the "type" action field. Semantics are in the tool description.`,when:e=>!!e.schedule,create:e=>Q(Ji(e.schedule))}],oa=`Skill provider not configured. Pass skillProvider to createApp().`;function sa(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 ca(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:oa})),executeSkill:n.skills?.executeSkill??(async()=>({success:!1,error:oa})),fork:n.fork,team:n.team,memory:n.memory,session:n.session,agentProfiles:n.agentProfiles,schedule:n.schedule,processTracker:n.processTracker};for(let t of aa){if(t.when&&!t.when(o))continue;let n=t.create(o);sa(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 la(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 ua=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(!la(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)}listRegenerableNames(){let e=[];for(let t of this.tools.values())t.regenerableOutput===!0&&e.push(t.name);return 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);if(o||(o={guideline:n,toolNames:[],entries:[]},i.set(a,o)),n){o.toolNames.push(e.name),t&&o.entries.push(`#### ${e.name}\nExample:\n\`\`\`\n${t}\n\`\`\``);continue}let s=[`#### ${e.name}`];t?s.push(`Example:\n\`\`\`\n${t}\n\`\`\``):s.push(e.description),o.entries.push(s.join(`
|
|
130
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(`
|
|
131
129
|
|
|
132
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(`
|
|
133
131
|
|
|
134
|
-
`)}`}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
|
|
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 da=(e,t)=>{let n=new ua,r=ht(e,t.processTracker);return n.setBinaryToolExecutors(r),ca(n,e,t),n},fa=new Set([`task_delegate`]),pa=new class{name=`noop`;wrap(e){return{command:e.command,args:e.args,options:e.options}}async dispose(){}};function ma(){return pa}var ha=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(`
|
|
135
133
|
`),o=this.options.network===`deny`?`(deny network*)
|
|
136
134
|
`:`(allow network*)
|
|
137
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)
|
|
@@ -154,5 +152,5 @@ ${s.size>0?`
|
|
|
154
152
|
; credential read isolation (opt-in) — last match wins, denies even within writable roots
|
|
155
153
|
`+[...s].map(e=>`(deny file-read* (subpath "${i(e)}"))`).join(`
|
|
156
154
|
`)+`
|
|
157
|
-
`:``}`}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
|
|
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 ga(e){return new ha(e)}var _a=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 va)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 va=[`/usr`,`/bin`,`/lib`,`/lib64`,`/etc`,`/dev`,`/sys`];function ya(e){return new _a(e)}function ba(){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 xa(e={}){switch(e.force??Sa()){case`seatbelt`:return ga({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});case`bubblewrap`:return ya({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});default:return ma()}}function Sa(){return process.platform===`darwin`?`seatbelt`:process.platform===`linux`?`bubblewrap`:`noop`}function Ca(){let e=Sa();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{mt as BinaryToolExecutorRegistry,ii as LSPClient,fa as ORCHESTRATION_TOOL_NAMES,aa as TOOL_NODES,ua as ToolRegistry,ur as buildDelegationAgentListDescription,ht as createBinaryToolExecutorRegistry,ya as createBubblewrapSandbox,jr as createCaptureFileState,Fn as createEdit,Ue as createGrillMe,Nr as createLearn,vi as createLspDefinition,wi as createLspDiagnostics,Ei as createLspPrepareRename,bi as createLspReferences,Oi as createLspRename,Si as createLspSymbols,ga as createMacOsSeatbeltSandbox,ma as createNoopSandbox,xa as createPlatformSandbox,dn as createRead,Ai as createSessionManagerTool,wr as createTaskControl,Sr as createTaskInspect,da as createToolRegistry,Fr as createToolSearch,tr as createWebFetch,lr as createWebSearch,_n as createWrite,kr as createWriteTodos,ba as credentialDenyReadPaths,hi as findWorkspaceRoot,Ca as isPlatformSandboxAvailable,Y as lspManager,ca as registerBuiltinTools,Z as withLspClient};
|
|
158
156
|
//# sourceMappingURL=index.js.map
|