@x-otto/tools 0.0.1-alpha.5 → 0.1.0-alpha.11

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 CHANGED
@@ -910,8 +910,8 @@ declare function buildDelegationAgentListDescription(profiles: AgentSummary[]):
910
910
  declare const TaskInspectArgsSchema: z.ZodObject<{
911
911
  action: z.ZodEnum<{
912
912
  output: "output";
913
- get: "get";
914
913
  list: "list";
914
+ get: "get";
915
915
  }>;
916
916
  taskId: z.ZodOptional<z.ZodString>;
917
917
  statusFilter: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
package/dist/index.js CHANGED
@@ -97,14 +97,14 @@ import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_
97
97
  `))}}return await k(e,n.join(`
98
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 Yr(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=K(r);if(!q(e,t)){n.success=!1,n.errors.push(`${e}: refused (outside project root)`);continue}let a=await Jr(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=K(r.uri);if(!q(e,t)){n.success=!1,n.errors.push(`create ${e}: refused (outside project root)`);continue}(!E(e)||r.options?.overwrite)&&(await k(e,``,`utf-8`),n.filesModified.push(e))}else if(r.kind===`rename`){let e=K(r.oldUri),i=K(r.newUri);if(!q(e,t)||!q(i,t)){n.success=!1,n.errors.push(`rename ${e}→${i}: refused (outside project root)`);continue}await be(e,i),n.filesModified.push(i)}else if(r.kind===`delete`){let e=K(r.uri);if(!q(e,t)){n.success=!1,n.errors.push(`delete ${e}: refused (outside project root)`);continue}await Se(e),n.filesModified.push(e)}}catch(e){n.success=!1,n.errors.push(`${r.kind} ${r.uri??``}: ${e}`)}else{let e=K(r.textDocument.uri),t=await Jr(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 Xr(e){return`targetUri`in e?`${K(e.targetUri)}:${e.targetRange.start.line+1}:${e.targetRange.start.character}`:`${K(e.uri)}:${e.range.start.line+1}:${e.range.start.character}`}function Zr(e){return Ur[e]||`Unknown(${e})`}function Qr(e){return e?Wr[e]||`unknown(${e})`:`unknown`}function $r(e,t=0){let n=` `.repeat(t),r=Zr(e.kind),i=e.range.start.line+1,a=`${n}${e.name} (${r}) - line ${i}`;if(e.children&&e.children.length>0)for(let n of e.children)a+=`
99
99
  `+$r(n,t+1);return a}function ei(e){let t=Zr(e.kind),n=Xr(e.location),r=e.containerName?` (in ${e.containerName})`:``;return`${e.name} (${t})${r} - ${n}`}function ti(e){let t=Qr(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 ni(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 ri(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 ii(e){let t=[];if(e.success){t.push(`Applied ${e.totalEdits} edit(s) to ${e.filesModified.length} file(s):`);for(let n of e.filesModified)t.push(` - ${n}`)}else{t.push(`Failed to apply some changes:`);for(let n of e.errors)t.push(` Error: ${n}`);e.filesModified.length>0&&t.push(`Successfully modified: ${e.filesModified.join(`, `)}`)}return t.join(`
100
- `)}function ai(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 oi(){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 si(e,t){return e.map(e=>e.section&&t&&Object.prototype.hasOwnProperty.call(t,e.section)?t[e.section]:e.section===`json`?{validate:{enable:!0}}:{})}const J=class e{static instance;clients=new Map;cleanupInterval=null;IDLE_TIMEOUT=300*1e3;INIT_TIMEOUT=60*1e3;processTracker;constructor(){this.startCleanupTimer()}static getInstance(){return e.instance||=new e,e.instance}setProcessTracker(e){this.processTracker=e}getKey(e,t){return`${e}::${t}`}startCleanupTimer(){this.cleanupInterval||(this.cleanupInterval=setInterval(()=>this.cleanupIdleClients(),6e4),this.cleanupInterval.unref&&this.cleanupInterval.unref())}cleanupIdleClients(){let e=Date.now();for(let[t,n]of this.clients)n.refCount===0&&e-n.lastUsedAt>this.IDLE_TIMEOUT&&(n.client.stop(),this.clients.delete(t))}async getClient(e,t){let n=this.getKey(e,t.id),r=this.clients.get(n);if(r){let e=Date.now();if(r.isInitializing&&r.initializingSince!==void 0&&e-r.initializingSince>=this.INIT_TIMEOUT){try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}}if(r){if(r.initPromise)try{await r.initPromise}catch{try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}if(r){if(r.client.isAlive())return r.refCount++,r.lastUsedAt=Date.now(),r.client;try{await r.client.stop()}catch{}this.clients.delete(n)}}let i=new ui(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 ui(e,t,this.processTracker),i=Date.now(),a=(async()=>{await r.start(),await r.initialize()})();this.clients.set(n,{client:r,lastUsedAt:i,refCount:0,initPromise:a,isInitializing:!0,initializingSince:i}),a.then(()=>{let e=this.clients.get(n);e&&(e.initPromise=void 0,e.isInitializing=!1,e.initializingSince=void 0)}).catch(()=>{this.clients.delete(n),r.stop().catch(()=>{})})}releaseClient(e,t){let n=this.getKey(e,t),r=this.clients.get(n);r&&r.refCount>0&&(r.refCount--,r.lastUsedAt=Date.now())}isServerInitializing(e,t){let n=this.getKey(e,t);return this.clients.get(n)?.isInitializing??!1}async stopAll(){for(let[,e]of this.clients)await e.client.stop();this.clients.clear(),this.cleanupInterval&&=(clearInterval(this.cleanupInterval),null)}}.getInstance(),Y=a(`@x-otto/tools:lsp`),ci=32*1024*1024;function li(e){let t=Buffer.from(e,`utf-8`);return Buffer.concat([Buffer.from(`Content-Length: ${t.byteLength}\r\n\r\n`,`ascii`),t])}var ui=class{process=null;nextId=1;pending=new Map;buffer=Buffer.alloc(0);contentLength=-1;processExited=!1;stderrBuffer=[];diagnosticsStore=new Map;diagnosticsWaiter=oi();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=di(this.root);if(!e.valid)throw V(`[LSP] ${e.error}`,`validation`);if(this.server.command.length===0)throw V(`[LSP] Server command is empty`,`validation`);let[t,...n]=this.server.command;this.process=P(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,Y.debug(`LSP server exited with code ${e}`),this.rejectAllPending(Error(`LSP server exited with code ${e??`null`}`))}),r.on(`error`,e=>{this.processExited=!0,Y.error(`LSP spawn error: ${e.message}`),this.rejectAllPending(Error(`LSP spawn error: ${e.message}`))});try{await ai(r,2e3)}catch(e){let t=this.stderrBuffer.join(`
100
+ `)}function ai(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 oi(){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 si(e,t){return e.map(e=>e.section&&t&&Object.prototype.hasOwnProperty.call(t,e.section)?t[e.section]:e.section===`json`?{validate:{enable:!0}}:{})}const J=class e{static instance;clients=new Map;cleanupInterval=null;IDLE_TIMEOUT=300*1e3;INIT_TIMEOUT=60*1e3;processTracker;constructor(){this.startCleanupTimer()}static getInstance(){return e.instance||=new e,e.instance}setProcessTracker(e){this.processTracker=e}getKey(e,t){return`${e}::${t}`}startCleanupTimer(){this.cleanupInterval||(this.cleanupInterval=setInterval(()=>this.cleanupIdleClients(),6e4),this.cleanupInterval.unref&&this.cleanupInterval.unref())}cleanupIdleClients(){let e=Date.now();for(let[t,n]of this.clients)n.refCount===0&&e-n.lastUsedAt>this.IDLE_TIMEOUT&&(n.client.stop(),this.clients.delete(t))}async getClient(e,t){let n=this.getKey(e,t.id),r=this.clients.get(n);if(r){let e=Date.now();if(r.isInitializing&&r.initializingSince!==void 0&&e-r.initializingSince>=this.INIT_TIMEOUT){try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}}if(r){if(r.initPromise)try{await r.initPromise}catch{try{await r.client.stop()}catch{}this.clients.delete(n),r=void 0}if(r){if(r.client.isAlive())return r.refCount++,r.lastUsedAt=Date.now(),r.client;try{await r.client.stop()}catch{}this.clients.delete(n)}}let i=new li(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 li(e,t,this.processTracker),i=Date.now(),a=(async()=>{await r.start(),await r.initialize()})();this.clients.set(n,{client:r,lastUsedAt:i,refCount:0,initPromise:a,isInitializing:!0,initializingSince:i}),a.then(()=>{let e=this.clients.get(n);e&&(e.initPromise=void 0,e.isInitializing=!1,e.initializingSince=void 0)}).catch(()=>{this.clients.delete(n),r.stop().catch(()=>{})})}releaseClient(e,t){let n=this.getKey(e,t),r=this.clients.get(n);r&&r.refCount>0&&(r.refCount--,r.lastUsedAt=Date.now())}isServerInitializing(e,t){let n=this.getKey(e,t);return this.clients.get(n)?.isInitializing??!1}async stopAll(){for(let[,e]of this.clients)await e.client.stop();this.clients.clear(),this.cleanupInterval&&=(clearInterval(this.cleanupInterval),null)}}.getInstance(),Y=a(`@x-otto/tools:lsp`),ci=32*1024*1024;function X(e){let t=Buffer.from(e,`utf-8`);return Buffer.concat([Buffer.from(`Content-Length: ${t.byteLength}\r\n\r\n`,`ascii`),t])}var li=class{process=null;nextId=1;pending=new Map;buffer=Buffer.alloc(0);contentLength=-1;processExited=!1;stderrBuffer=[];diagnosticsStore=new Map;diagnosticsWaiter=oi();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=ui(this.root);if(!e.valid)throw V(`[LSP] ${e.error}`,`validation`);if(this.server.command.length===0)throw V(`[LSP] Server command is empty`,`validation`);let[t,...n]=this.server.command;this.process=P(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,Y.debug(`LSP server exited with code ${e}`),this.rejectAllPending(Error(`LSP server exited with code ${e??`null`}`))}),r.on(`error`,e=>{this.processExited=!0,Y.error(`LSP spawn error: ${e.message}`),this.rejectAllPending(Error(`LSP spawn error: ${e.message}`))});try{await ai(r,2e3)}catch(e){let t=this.stderrBuffer.join(`
101
101
  `);throw V(`LSP server failed to start: ${e.message}`+(t?`\nstderr: ${t}`:``),`runtime`)}if(r.exitCode!==null){let e=this.stderrBuffer.join(`
102
102
  `);throw V(`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=I(this.root).href;await this.sendRequest(`initialize`,{processId:process.pid,rootUri:e,rootPath:this.root,workspaceFolders:[{uri:e,name:`workspace`}],capabilities:{textDocument:{hover:{contentFormat:[`markdown`,`plaintext`]},definition:{linkSupport:!0},references:{},documentSymbol:{hierarchicalDocumentSymbolSupport:!0},publishDiagnostics:{},rename:{prepareSupport:!0,prepareSupportDefaultBehavior:1,honorsChangeAnnotations:!0},codeAction:{codeActionLiteralSupport:{codeActionKind:{valueSet:[`quickfix`,`refactor`,`refactor.extract`,`refactor.inline`,`refactor.rewrite`,`source`,`source.organizeImports`,`source.fixAll`]}},isPreferredSupport:!0,disabledSupport:!0,dataSupport:!0,resolveSupport:{properties:[`edit`,`command`]}}},workspace:{symbol:{},workspaceFolders:!0,configuration:!0,applyEdit:!0,workspaceEdit:{documentChanges:!0}}},...this.server.initialization}),this.sendNotification(`initialized`),this.sendNotification(`workspace/didChangeConfiguration`,{settings:this.server.settings??{json:{validate:{enable:!0}}}})}rejectAllPending(e){for(let[,t]of this.pending)clearTimeout(t.timer),t.reject(e);this.pending.clear()}async stop(){if(this.rejectAllPending(Error(`LSP client stopping`)),!this.processExited&&this.process){try{this.sendNotification(`shutdown`),this.sendNotification(`exit`)}catch{}let e=this.process;this.process=null;let t,n=await Promise.race([new Promise(t=>e.on(`exit`,()=>t(!0))),new Promise(e=>{t=setTimeout(()=>e(!1),5e3)})]);if(t&&clearTimeout(t),!n){Y.debug(`LSP process did not exit within timeout, sending SIGKILL`);try{e.kill(`SIGKILL`)}catch{}}}this.processExited=!0,this.process=null,this.diagnosticsStore.clear(),this.diagnosticsWaiter.clear(),this.openedFiles.clear(),this.documentVersions.clear(),this.lastSyncedText.clear()}isAlive(){return this.process!==null&&!this.processExited&&this.process.exitCode===null}processBuffer(e){if(this.buffer=this.buffer.length===0?e:Buffer.concat([this.buffer,e]),this.buffer.length>ci&&this.contentLength===-1){Y.warn(`LSP buffer exceeded ${ci} 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
103
  \r
104
104
  `);if(e===-1)break;let t=this.buffer.subarray(0,e).toString(`ascii`);for(let e of t.split(`\r
105
105
  `))if(e.startsWith(`Content-Length: `)){let t=parseInt(e.slice(16),10);Number.isInteger(t)&&t>=0&&t<=ci&&(this.contentLength=t)}if(this.contentLength===-1){this.buffer=this.buffer.subarray(e+4);continue}this.buffer=this.buffer.subarray(e+4)}if(this.buffer.length<this.contentLength)break;let e=this.buffer.subarray(0,this.contentLength).toString(`utf-8`);this.buffer=this.buffer.subarray(this.contentLength),this.contentLength=-1;try{this.onMessage(JSON.parse(e))}catch(e){Y.error(`Failed to parse LSP message: ${e}`)}}}onMessage(e){if(`id`in e&&(`result`in e||`error`in e)){let t=e.id,n=this.pending.get(t);if(n)if(this.pending.delete(t),clearTimeout(n.timer),`error`in e){let t=e.error;n.reject(Error(`LSP error ${t.code??``}: ${t.message}`))}else n.resolve(e.result);return}let t=e.method;if(!t)return;if(!(`id`in e)){if(t===`textDocument/publishDiagnostics`){let t=e.params;t.uri&&(this.diagnosticsStore.set(t.uri,t.diagnostics??[]),this.diagnosticsWaiter.notify(t.uri))}return}let n=e.id;if(t===`workspace/configuration`){let t=e.params;this.respond(n,si(t?.items??[],this.server.settings))}else this.respond(n,null)}sendRequest(e,t){if(!this.process?.stdin?.writable)throw V(`LSP client not started`,`runtime`);if(this.processExited||this.process&&this.process.exitCode!==null){let e=this.stderrBuffer.slice(-10).join(`
106
106
  `);throw V(`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(li(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(li(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(li(n))}async openFile(e){let t=N(e),n=I(t).href,r=await O(t,`utf-8`);if(!this.openedFiles.has(t)){let e=Gr[Te(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=N(e);return await this.openFile(r),this.sendRequest(`textDocument/definition`,{textDocument:{uri:I(r).href},position:{line:t-1,character:n}})}async references(e,t,n,r=!0){let i=N(e);return await this.openFile(i),this.sendRequest(`textDocument/references`,{textDocument:{uri:I(i).href},position:{line:t-1,character:n},context:{includeDeclaration:r}})}async documentSymbols(e){let t=N(e);return await this.openFile(t),this.sendRequest(`textDocument/documentSymbol`,{textDocument:{uri:I(t).href}})}async workspaceSymbols(e){return this.sendRequest(`workspace/symbol`,{query:e})}async diagnostics(e){let t=N(e),n=I(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=N(e);return await this.openFile(r),this.sendRequest(`textDocument/prepareRename`,{textDocument:{uri:I(r).href},position:{line:t-1,character:n}})}async rename(e,t,n,r){let i=N(e);return await this.openFile(i),this.sendRequest(`textDocument/rename`,{textDocument:{uri:I(i).href},position:{line:t-1,character:n},newName:r})}};function di(e){try{return E(e)?_e(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 fi(e){if(e.length===0)return!1;let t=e[0];if((t.includes(`/`)||t.includes(`\\`))&&E(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(E(j(e,t+n)))return!0;let s=u(),c=[j(je,`node_modules`,`.bin`),j(s,`bin`),j(s,`node_modules`,`.bin`)];for(let e of c)for(let n of r)if(E(j(e,t+n)))return!0;return t===`node`}function pi(e){if(!E(e))return null;try{return y(pe(e,`utf-8`))}catch{return null}}function mi(e){let t=e+`.jsonc`;if(E(t))return t;let n=e+`.json`;return E(n)?n:t}function hi(){let e=u();return{project:mi(j(je,`otto`)),user:mi(j(e,`otto`))}}let X;function gi(){if(X)return X;let e=hi(),t=new Map,n=pi(e.project);n&&t.set(`project`,n);let r=pi(e.user);return r&&t.set(`user`,r),X=t,t}function _i(){let e=gi(),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(qr))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 vi(e){let t=_i();for(let n of t)if(n.extensions.includes(e)&&fi(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=Kr[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 yi=[`.git`,`package.json`,`pyproject.toml`,`Cargo.toml`,`go.mod`,`pom.xml`,`build.gradle`];function bi(e){let t=N(e);(!E(t)||!_e(t).isDirectory())&&(t=A(t));let n=``;for(;t!==n;){for(let e of yi)if(E(j(t,e)))return t;n=t,t=A(t)}return A(N(e))}function xi(e){if(e.status===`not_installed`){let{server:t,installHint:n}=e;return[`LSP server '${t.id}' is configured but NOT INSTALLED.`,``,`Command not found: ${t.command[0]}`,``,`To install:`,` ${n}`,``,`Supported extensions: ${t.extensions.join(`, `)}`,``,`After installation, the server will be available automatically.`].join(`
107
+ `);i(Error(`LSP request timeout (method: ${e})`+(t?`\nrecent stderr: ${t}`:``)))},this.REQUEST_TIMEOUT);this.pending.set(n,{resolve:r,reject:i,timer:a});let o=JSON.stringify({jsonrpc:`2.0`,id:n,method:e,params:t});this.process.stdin.write(X(o))})}sendNotification(e,t){if(!this.process?.stdin?.writable||this.processExited||this.process&&this.process.exitCode!==null)return;let n=JSON.stringify({jsonrpc:`2.0`,method:e,params:t});this.process.stdin.write(X(n))}respond(e,t){if(!this.process?.stdin?.writable)return;let n=JSON.stringify({jsonrpc:`2.0`,id:e,result:t});this.process.stdin.write(X(n))}async openFile(e){let t=N(e),n=I(t).href,r=await O(t,`utf-8`);if(!this.openedFiles.has(t)){let e=Gr[Te(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=N(e);return await this.openFile(r),this.sendRequest(`textDocument/definition`,{textDocument:{uri:I(r).href},position:{line:t-1,character:n}})}async references(e,t,n,r=!0){let i=N(e);return await this.openFile(i),this.sendRequest(`textDocument/references`,{textDocument:{uri:I(i).href},position:{line:t-1,character:n},context:{includeDeclaration:r}})}async documentSymbols(e){let t=N(e);return await this.openFile(t),this.sendRequest(`textDocument/documentSymbol`,{textDocument:{uri:I(t).href}})}async workspaceSymbols(e){return this.sendRequest(`workspace/symbol`,{query:e})}async diagnostics(e){let t=N(e),n=I(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=N(e);return await this.openFile(r),this.sendRequest(`textDocument/prepareRename`,{textDocument:{uri:I(r).href},position:{line:t-1,character:n}})}async rename(e,t,n,r){let i=N(e);return await this.openFile(i),this.sendRequest(`textDocument/rename`,{textDocument:{uri:I(i).href},position:{line:t-1,character:n},newName:r})}};function ui(e){try{return E(e)?_e(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 di(e){if(e.length===0)return!1;let t=e[0];if((t.includes(`/`)||t.includes(`\\`))&&E(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(E(j(e,t+n)))return!0;let s=u(),c=[j(je,`node_modules`,`.bin`),j(s,`bin`),j(s,`node_modules`,`.bin`)];for(let e of c)for(let n of r)if(E(j(e,t+n)))return!0;return t===`node`}function fi(e){if(!E(e))return null;try{return y(pe(e,`utf-8`))}catch{return null}}function pi(e){let t=e+`.jsonc`;if(E(t))return t;let n=e+`.json`;return E(n)?n:t}function mi(){let e=u();return{project:pi(j(je,`otto`)),user:pi(j(e,`otto`))}}let hi;function gi(){if(hi)return hi;let e=mi(),t=new Map,n=fi(e.project);n&&t.set(`project`,n);let r=fi(e.user);return r&&t.set(`user`,r),hi=t,t}function _i(){let e=gi(),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(qr))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 vi(e){let t=_i();for(let n of t)if(n.extensions.includes(e)&&di(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=Kr[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 yi=[`.git`,`package.json`,`pyproject.toml`,`Cargo.toml`,`go.mod`,`pom.xml`,`build.gradle`];function bi(e){let t=N(e);(!E(t)||!_e(t).isDirectory())&&(t=A(t));let n=``;for(;t!==n;){for(let e of yi)if(E(j(t,e)))return t;n=t,t=A(t)}return A(N(e))}function xi(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
108
  `)}return[`No LSP server configured for extension: ${e.extension}`,``,`Available servers: ${e.availableServers.slice(0,10).join(`, `)}${e.availableServers.length>10?`...`:``}`,``,`To add a custom server, configure 'lsp' in otto.jsonc:`,` {`,` "lsp": {`,` "my-server": {`,` "command": ["my-lsp", "--stdio"],`,` "extensions": ["${e.extension}"]`,` }`,` }`,` }`].join(`
109
109
  `)}async function Z(e,t){let n=N(e),r=vi(Te(n));if(r.status!==`found`)throw V(xi(r),`not_found`);let i=r.server,a=bi(n),o=await J.getClient(a,i);try{return await t(o)}catch(e){throw e instanceof Error&&e.message.includes(`timeout`)&&J.isServerInitializing(a,i.id)?V(`LSP server is still initializing. Please retry in a few seconds. Original error: ${e.message}`,`timeout`):e}finally{J.releaseClient(a,i.id)}}const Si=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 Ci(e){return{name:`lsp_goto_definition`,description:`Jump to symbol definition. Find WHERE something is defined.`,parameters:Si,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(Xr).join(`
110
110
  `)}]}}}}const wi=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 Ti(e){return{name:`lsp_find_references`,description:`Find ALL usages/references of a symbol across the entire workspace.`,parameters:wi,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(Xr);return r&&i.unshift(`Found ${n} references (showing first 200):`),{content:[{type:`text`,text:i.join(`
@@ -113,23 +113,25 @@ import{z as e}from"zod";import{API_IMAGE_DIMENSION_LIMIT_PX as t,PROVIDER_IMAGE_
113
113
  `)}]}}}}const Oi=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 ki(e){return{name:`lsp_diagnostics`,description:`Get errors, warnings, hints from language server BEFORE running build.`,parameters:Oi,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=ni(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(ti);return i&&a.unshift(`Found ${r} diagnostics (showing first 200):`),{content:[{type:`text`,text:a.join(`
114
114
  `)}]}}}}const Ai=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 ji(e){return{name:`lsp_prepare_rename`,description:`Check if rename is valid. Use BEFORE lsp_rename.`,parameters:Ai,pathParams:[`filePath`],async execute({params:e}){return{content:[{type:`text`,text:ri(await Z(e.filePath,async t=>await t.prepareRename(e.filePath,e.line,e.character)))}]}}}}const Mi=e.object({filePath:e.string().describe(`Absolute path to the file`),line:e.number().int().min(1).describe(`1-based line number`),character:e.number().int().min(0).describe(`0-based character offset`),newName:e.string().describe(`New symbol name`)});function Ni(e){return{name:`lsp_rename`,description:`Rename symbol across entire workspace. APPLIES changes to all files.`,parameters:Mi,pathParams:[`filePath`],async execute({params:t}){return{content:[{type:`text`,text:ii(await Yr(await Z(t.filePath,async e=>await e.rename(t.filePath,t.line,t.character,t.newName)),e))}]}}}}const Pi=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 Fi(e){let{sessionManager:t}=e;return{name:`session_manager`,description:`Manage conversation sessions: list, create, remove, compact.`,parameters:Pi,async execute({params:e}){if(!t)throw Error(`SessionManager is not provided in options`);return await Ii(e.action,t,e.sessionId,e.title)}}}async function Ii(e,t,n,r){switch(e){case`list`:{let e=await t.list();return{content:[{type:`text`,text:e.length===0?`No sessions found.`:e.map(e=>`- ${e.id}: ${e.title} (${e.messageCount} messages)`).join(`
115
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 Li=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 Ri(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:Li,pathParams:[`path`],resolvePath:t=>N(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 G(`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?zi(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?zi(await n(e.name,a)):{content:[{type:`text`,text:`name or path is required for execute action`}],isError:!0,errorKind:`validation`}}}}}function zi(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 Bi=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 Vi(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:Bi,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 Hi=e.object({name:e.string().describe(`The memory title as shown in the memory index`)});function Ui(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:Hi,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 Wi=/^archive:(.+):(\d+)$/;function Gi(e){let t=Wi.exec(e);return t?{sessionId:t[1],index:Number.parseInt(t[2],10)}:null}const Ki=e.object({archivePath:e.string().describe(`The archive id shown in a compaction summary, in the form "archive:<sessionId>:<n>".`)}),qi=e.object({}),Ji=`Conversation archive recall is not available in this session (no session-entry backed storage).`;function Yi(e,t){let n=Gi(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 Xi(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:Ki,readonly:!0,async execute({params:t,sessionId:n}){if(!e)return{content:[{type:`text`,text:Ji}],isError:!0,errorKind:`runtime`};let r=Yi(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 Zi(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:qi,readonly:!0,async execute({sessionId:t}){if(!e||!t)return{content:[{type:`text`,text:Ji}],isError:!0,errorKind:`runtime`};let n=await e(t);return n.length===0?{content:[{type:`text`,text:`No compaction archives yet in this conversation.`}]}:{content:[{type:`text`,text:n.map(e=>`- ${e.path} — ${e.messageCount} messages, ${new Date(e.timestamp).toISOString()}\n ${e.summary}`).join(`
116
- `)}]}}}}const Qi=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 * * *").`)});function $i(e){return{name:`schedule_create`,description:`Schedule a recurring or one-shot scheduled task. The task fires automatically see the "target" parameter for whether it runs as an isolated background job or continues this conversation.`,parameters:Qi,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule scheduling is not available in this session.`}],isError:!0};let n;try{let{onEvent:r,...i}=t,a=r?{source:`job`,event:r.slice(4)}:void 0;n=e({...i,eventTrigger:a})}catch(e){return{content:[{type:`text`,text:e instanceof Error?e.message:String(e)}],isError:!0}}let r=n.target===`session`?` (fires into this conversation)`:``;return{content:[{type:`text`,text:`Schedule created: ${n.taskId} "${n.name}"${r}\nNext fire: ${new Date(n.nextFireAt).toLocaleString()}\nUse schedule_list to view all schedules, schedule_delete to remove.`}]}}}}function ea(t){return{name:`schedule_list`,description:`List all scheduled tasks with their IDs, cron schedules, and next fire times.`,parameters:e.object({}),execute:async()=>{if(!t)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};let e=t();return e.length===0?{content:[{type:`text`,text:`No scheduled tasks.`}]}:{content:[{type:`text`,text:e.map(e=>{let t=e.enabled?`◉`:`○`,n=e.recurring?e.cronExpression:`one-shot`,r=new Date(e.nextFireAt).toLocaleString(),i=e.lastFiredAt?` last: ${new Date(e.lastFiredAt).toLocaleString()}`:``;return`${t} ${e.id} ${e.name}\n ${n} next: ${r}${i}`}).join(`
117
- `)}]}}}}const ta=e.object({task_id:e.string().describe(`ID of the scheduled task to delete`)});function na(e){return{name:`schedule_delete`,description:`Delete a scheduled task by ID. Irreversible.`,parameters:ta,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};let n=e(t.task_id);return{content:[{type:`text`,text:n?`Schedule "${t.task_id}" deleted.`:`Schedule "${t.task_id}" not found.`}],isError:!n}}}}const ra=e.object({task_id:e.string().describe(`ID of the scheduled task to run immediately`)});function ia(e){return{name:`schedule_run`,description:`Run a scheduled task immediately (does not affect its regular schedule).`,parameters:ra,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Cron scheduling is not available.`}],isError:!0};try{return e(t.task_id),{content:[{type:`text`,text:`Schedule "${t.task_id}" triggered.`}]}}catch(e){return{content:[{type:`text`,text:`Failed to run schedule "${t.task_id}": ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}}const aa=e.object({task_id:e.string().describe(`ID of the scheduled task whose currently running job should be canceled`)});function oa(e){return{name:`schedule_cancel`,description:`Cancel the currently running job of a scheduled task. The schedule definition is not affected — the task will fire again at its next cron-triggered time. Only works on jobs that are still running or waiting for input; an already-completed or ready (diff-produced) job cannot be canceled.`,parameters:aa,execute:async({params:t})=>{if(!e)return{content:[{type:`text`,text:`Schedule cancel is not available in this session.`}],isError:!0};let n=e(t.task_id);return n.ok?{content:[{type:`text`,text:`Schedule task "${t.task_id}" canceled. The task will fire again at its next scheduled time.`}]}:{content:[{type:`text`,text:`Cannot cancel schedule "${t.task_id}": ${n.reason??`unknown reason`}.`}],isError:!0}}}}const Q=(...e)=>e,sa=[`Use read to understand existing code before making changes. Read targeted ranges instead of entire large files.`,`Prefer edit over write for modifying existing files — edit replaces exact text and is safer.`,`Use write only for new files or full content replacement.`,`Verify file existence with read before editing — never edit on stale assumptions.`,`For edit: provide enough surrounding context to avoid ambiguous matches.`].join(`
118
- `),ca=[`Use bash for running tests, installing dependencies, build commands, git operations, and system tasks that have no dedicated tool.`,"For long-lived processes (dev servers, watchers), pass run_in_background:true instead of blocking or trailing `&`; it returns immediately with a background id.","Read accumulated output with bash_output; stop with kill_shell. Background processes are auto-killed at session end — no cleanup of `&` jobs.",`Avoid programs requiring interactive input.`,`Prefer dedicated tools (read, write, edit, grep, find) over shell equivalents (cat, sed, grep) for file operations.`,`Avoid destructive commands (rm -rf, git reset --hard, DROP TABLE) without explicit user approval.`,`Set reasonable timeouts for long-running commands. Kill stale processes rather than waiting indefinitely.`,`Always check the exit code; on failure, inspect stdout/stderr before deciding next steps.`,`Prefer non-interactive flags, e.g. git --no-pager, --non-interactive.`].join(`
119
- `),la=[`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
- `),ua=[`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
- `),da=[`Use task_delegate to route tasks to more suitable sub-agents by category — useful for specialization or lifecycle management. Provide clear, complete context — sub-agents start with a blank slate.`,`Use write_todos for complex tasks to build and maintain a step list first, then work it to completion. Optional "title" labels the list (defaults to "Task").`,`Todo discipline: keep exactly ONE item in_progress; mark items done the moment you finish (no batching). Never end a turn with pending/in_progress items finish them or mark skipped/failed with a reason. Unfinished todos = reported incomplete.`,`Break complex tasks into small, independently verifiable subtasks (2-5 minutes each) before delegating.`,`Do not delegate tasks that require the current conversation context or interactive clarification.`].join(`
122
- `),fa=[`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
- `),pa=[`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
- `),ma=[`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
- `),ha=[`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
- `),ga=[`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
- `),_a=[{names:[`read`,`write`,`edit`],preset:`minimal`,category:`fs`,guidance:sa,create:e=>Q(fn(e.projectRoot),vn(e.projectRoot),In(e.projectRoot))},{names:[`bash`],preset:`minimal`,category:`shell`,guidance:ca,create:e=>Q(Pt(e.projectRoot,void 0,e.features.sandbox,e.features.processRegistry))},{names:[`bash_output`,`kill_shell`],preset:`standard`,category:`shell`,when:e=>!!e.features.processRegistry,create:e=>Q(Gt(e.features.processRegistry),qt(e.features.processRegistry))},{names:[`find`,`grep`],preset:`standard`,category:`search`,guidance:la,create:e=>Q(Xt(e.projectRoot,{binaryExecutorRegistry:e.registry.getBinaryToolExecutors()}),rn(e.projectRoot,{binaryToolExecutorRegistry:e.registry.getBinaryToolExecutors(),processTracker:e.processTracker}))},{names:[`web_fetch`,`web_search`],preset:`standard`,category:`web`,guidance:ua,create:e=>Q(nr(e.projectRoot),ur(e.projectRoot))},{names:[`tool_search`],preset:`standard`,category:`orchestration`,guidance:`Use tool_search to discover deferred tools by name or keyword and load their full parameter schema.`,create:e=>Q(Vr(()=>e.registry.project(`full`).deferred))},{names:[`task_delegate`,`delegate_job`,`write_todos`],preset:`standard`,category:`orchestration`,guidance:da,create:e=>Q(yr(e.projectRoot,e.orchestration.dispatchTask,e.orchestration.writeTodos,e.fork?.fork,e.team?.runTeam,void 0,e.agents.callAgent,e.agentProfiles??[]),xr(e.orchestration.startJob),Fr(e.orchestration.writeTodos))},{names:[`lsp_goto_definition`,`lsp_find_references`,`lsp_symbols`,`lsp_diagnostics`,`lsp_prepare_rename`,`lsp_rename`],preset:`standard`,category:`lsp`,when:e=>!!e.features.enableLsp,create:e=>(e.processTracker&&J.setProcessTracker(e.processTracker),Q(Ci(e.projectRoot),Ti(e.projectRoot),Di(e.projectRoot),ki(e.projectRoot),ji(e.projectRoot),Ni(e.projectRoot)))},{names:[`list_models`,`task_inspect`,`task_control`,`grill_me`,`capture_file_state`,`learn`],preset:`full`,category:`orchestration`,guidance:fa,create:e=>Q(Mr(e.features.listModels,e.features.sessionId),Or(e.projectRoot,{get:e.orchestration.getTask,list:e.orchestration.listTasks,output:e.orchestration.getBackgroundOutput}),Ar(e.projectRoot,{update:e.orchestration.updateTask,cancelBackground:e.orchestration.cancelBackground}),We(e.projectRoot,e.features.grill),Lr(e.features.captureFileState),zr(e.features.sessionId??``,e.features.learn))},{names:[`session_manager`],preset:`full`,category:`session`,guidance:pa,when:e=>!!e.session,create:e=>{let t=e.session;return t?Q(Fi({sessionManager:t.manager})):Q()}},{names:[`skill`],preset:`full`,category:`skill`,guidance:ma,create:e=>Q(Ri(e.projectRoot,e.loadSkill,e.executeSkill,e.features.sessionId))},{names:[`memory_record`,`memory_read`],preset:`full`,category:`memory`,guidance:ha,create:e=>Q(Vi(e.memory?.recordMemory),Ui(e.memory?.readMemory))},{names:[`memory_archive_read`,`memory_archive_list`],preset:`full`,category:`memory`,when:e=>!!e.memory?.archive,guidance:ga,create:e=>Q(Xi(e.memory?.archive?.read),Zi(e.memory?.archive?.list))},{names:[`schedule_create`,`schedule_list`,`schedule_delete`,`schedule_run`,`schedule_cancel`],preset:`full`,category:`schedule`,guidance:`Schedule tools — create/list/delete/run/cancel timer tasks that fire agent jobs on a schedule. Semantics are in each tool description.`,when:e=>!!e.schedule,create:e=>Q($i(e.schedule.create),ea(e.schedule.list),na(e.schedule.delete),ia(e.schedule.run),oa(e.schedule.cancel))},{names:[`capability_gap`],preset:`full`,category:`orchestration`,guidance:`Use capability_gap when you genuinely cannot do what the user asks with current tools: searches the plugin ecosystem for an existing capability, else returns a build blueprint (scaffold + delegate_job).`,when:e=>!!e.capabilityGap,create:e=>Q(Er(e.capabilityGap.search,{...e.capabilityGap.recorder?{recorder:e.capabilityGap.recorder}:{},...e.capabilityGap.now?{now:e.capabilityGap.now}:{}}))}],va=`Skill provider not configured. Pass skillProvider to createApp().`;function ya(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 ba(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:va})),executeSkill:n.skills?.executeSkill??(async()=>({success:!1,error:va})),fork:n.fork,team:n.team,memory:n.memory,session:n.session,agentProfiles:n.agentProfiles,schedule:n.schedule,capabilityGap:n.capabilityGap,processTracker:n.processTracker};for(let t of _a){if(t.when&&!t.when(o))continue;let n=t.create(o);ya(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 xa(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 Sa=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(!xa(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);o||(o={guideline:n,entries:[]},i.set(a,o));let s=[`#### ${e.name}`];!n&&!t&&s.push(e.description),t&&s.push(`Example:\n\`\`\`\n${t}\n\`\`\``),o.entries.push(s.join(`
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 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 $i(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
+ Next fire: ${new Date(t.nextFireAt).toLocaleString()}
118
+ 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,ea=[`Use read to understand existing code before making changes. Read targeted ranges instead of entire large files.`,`Prefer edit over write for modifying existing files edit replaces exact text and is safer.`,`Use write only for new files or full content replacement.`,`Verify file existence with read before editing never edit on stale assumptions.`,`For edit: provide enough surrounding context to avoid ambiguous matches.`].join(`
120
+ `),ta=[`Use bash for running tests, installing dependencies, build commands, git operations, and system tasks that have no dedicated tool.`,"For long-lived processes (dev servers, watchers), pass run_in_background:true instead of blocking or trailing `&`; it returns immediately with a background id.","Read accumulated output with bash_output; stop with kill_shell. Background processes are auto-killed at session end — no cleanup of `&` jobs.",`Avoid programs requiring interactive input.`,`Prefer dedicated tools (read, write, edit, grep, find) over shell equivalents (cat, sed, grep) for file operations.`,`Avoid destructive commands (rm -rf, git reset --hard, DROP TABLE) without explicit user approval.`,`Set reasonable timeouts for long-running commands. Kill stale processes rather than waiting indefinitely.`,`Always check the exit code; on failure, inspect stdout/stderr before deciding next steps.`,`Prefer non-interactive flags, e.g. git --no-pager, --non-interactive.`].join(`
121
+ `),na=[`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 toolfor 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(`
122
+ `),ra=[`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(`
123
+ `),ia=[`Use task_delegate to route tasks to more suitable sub-agents by category useful for specialization or lifecycle management. Provide clear, complete context — sub-agents start with a blank slate.`,`Use write_todos for complex tasks to build and maintain a step list first, then work it to completion. Optional "title" labels the list (defaults to "Task").`,`Todo discipline: keep exactly ONE item in_progress; mark items done the moment you finish (no batching). Never end a turn with pending/in_progress items — finish them or mark skipped/failed with a reason. Unfinished todos = reported incomplete.`,`Break complex tasks into small, independently verifiable subtasks (2-5 minutes each) before delegating.`,`Do not delegate tasks that require the current conversation context or interactive clarification.`].join(`
124
+ `),aa=[`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(`
125
+ `),oa=[`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(`
126
+ `),sa=[`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(`
127
+ `),ca=[`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(`
128
+ `),la=[`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(`
129
+ `),ua=[{names:[`read`,`write`,`edit`],preset:`minimal`,category:`fs`,guidance:ea,create:e=>Q(fn(e.projectRoot),vn(e.projectRoot),In(e.projectRoot))},{names:[`bash`],preset:`minimal`,category:`shell`,guidance:ta,create:e=>Q(Pt(e.projectRoot,void 0,e.features.sandbox,e.features.processRegistry))},{names:[`bash_output`,`kill_shell`],preset:`standard`,category:`shell`,when:e=>!!e.features.processRegistry,create:e=>Q(Gt(e.features.processRegistry),qt(e.features.processRegistry))},{names:[`find`,`grep`],preset:`standard`,category:`search`,guidance:na,create:e=>Q(Xt(e.projectRoot,{binaryExecutorRegistry:e.registry.getBinaryToolExecutors()}),rn(e.projectRoot,{binaryToolExecutorRegistry:e.registry.getBinaryToolExecutors(),processTracker:e.processTracker}))},{names:[`web_fetch`,`web_search`],preset:`standard`,category:`web`,guidance:ra,create:e=>Q(nr(e.projectRoot),ur(e.projectRoot))},{names:[`tool_search`],preset:`standard`,category:`orchestration`,guidance:`Use tool_search to discover deferred tools by name or keyword and load their full parameter schema.`,create:e=>Q(Vr(()=>e.registry.project(`full`).deferred))},{names:[`task_delegate`,`delegate_job`,`write_todos`],preset:`standard`,category:`orchestration`,guidance:ia,create:e=>Q(yr(e.projectRoot,e.orchestration.dispatchTask,e.orchestration.writeTodos,e.fork?.fork,e.team?.runTeam,void 0,e.agents.callAgent,e.agentProfiles??[]),xr(e.orchestration.startJob),Fr(e.orchestration.writeTodos))},{names:[`lsp_goto_definition`,`lsp_find_references`,`lsp_symbols`,`lsp_diagnostics`,`lsp_prepare_rename`,`lsp_rename`],preset:`standard`,category:`lsp`,when:e=>!!e.features.enableLsp,create:e=>(e.processTracker&&J.setProcessTracker(e.processTracker),Q(Ci(e.projectRoot),Ti(e.projectRoot),Di(e.projectRoot),ki(e.projectRoot),ji(e.projectRoot),Ni(e.projectRoot)))},{names:[`list_models`,`task_inspect`,`task_control`,`grill_me`,`capture_file_state`,`learn`],preset:`full`,category:`orchestration`,guidance:aa,create:e=>Q(Mr(e.features.listModels,e.features.sessionId),Or(e.projectRoot,{get:e.orchestration.getTask,list:e.orchestration.listTasks,output:e.orchestration.getBackgroundOutput}),Ar(e.projectRoot,{update:e.orchestration.updateTask,cancelBackground:e.orchestration.cancelBackground}),We(e.projectRoot,e.features.grill),Lr(e.features.captureFileState),zr(e.features.sessionId??``,e.features.learn))},{names:[`session_manager`],preset:`full`,category:`session`,guidance:oa,when:e=>!!e.session,create:e=>{let t=e.session;return t?Q(Fi({sessionManager:t.manager})):Q()}},{names:[`skill`],preset:`full`,category:`skill`,guidance:sa,create:e=>Q(Ri(e.projectRoot,e.loadSkill,e.executeSkill,e.features.sessionId))},{names:[`memory_record`,`memory_read`],preset:`full`,category:`memory`,guidance:ca,create:e=>Q(Vi(e.memory?.recordMemory),Ui(e.memory?.readMemory))},{names:[`memory_archive_read`,`memory_archive_list`],preset:`full`,category:`memory`,when:e=>!!e.memory?.archive,guidance:la,create:e=>Q(Xi(e.memory?.archive?.read),Zi(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($i(e.schedule))},{names:[`capability_gap`],preset:`full`,category:`orchestration`,guidance:`Use capability_gap when you genuinely cannot do what the user asks with current tools: searches the plugin ecosystem for an existing capability, else returns a build blueprint (scaffold + delegate_job).`,when:e=>!!e.capabilityGap,create:e=>Q(Er(e.capabilityGap.search,{...e.capabilityGap.recorder?{recorder:e.capabilityGap.recorder}:{},...e.capabilityGap.now?{now:e.capabilityGap.now}:{}}))}],da=`Skill provider not configured. Pass skillProvider to createApp().`;function fa(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 pa(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:da})),executeSkill:n.skills?.executeSkill??(async()=>({success:!1,error:da})),fork:n.fork,team:n.team,memory:n.memory,session:n.session,agentProfiles:n.agentProfiles,schedule:n.schedule,capabilityGap:n.capabilityGap,processTracker:n.processTracker};for(let t of ua){if(t.when&&!t.when(o))continue;let n=t.create(o);fa(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 ma(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 ha=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(!ma(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);o||(o={guideline:n,entries:[]},i.set(a,o));let s=[`#### ${e.name}`];!n&&!t&&s.push(e.description),t&&s.push(`Example:\n\`\`\`\n${t}\n\`\`\``),o.entries.push(s.join(`
128
130
  `))}let a=[];for(let e of i.values()){let t=[];e.guideline&&t.push(e.guideline),t.push(...e.entries),a.push(t.join(`
129
131
 
130
132
  `))}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(`
131
133
 
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 Ca=(e,t)=>{let n=new Sa,r=gt(e,t.processTracker);return n.setBinaryToolExecutors(r),ba(n,e,t),n},wa=new Set([`task_delegate`]),Ta=new class{name=`noop`;wrap(e){return{command:e.command,args:e.args,options:e.options}}async dispose(){}};function Ea(){return Ta}var Da=class{name=`macos-seatbelt`;options;profilePathByContent=new Map;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=this.buildProfile(e.projectRoot);return{command:`/usr/bin/sandbox-exec`,args:[`-f`,this.ensureProfileSync(t),e.command,...e.args],options:{...e.options}}}async dispose(){let e=[...this.profilePathByContent.values()];this.profilePathByContent.clear(),await Promise.allSettled(e.map(e=>Se(e).catch(()=>void 0)))}buildProfile(e){let t=N(e),n=ae(),r=new Set;for(let e of[t,n,...this.options.writablePaths.map(e=>N(e))]){r.add(e);try{r.add(he(e))}catch{}}let i=e=>e.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`),a=[...r].map(e=>`(subpath "${i(e)}")`).join(`
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 ga=(e,t)=>{let n=new ha,r=gt(e,t.processTracker);return n.setBinaryToolExecutors(r),pa(n,e,t),n},_a=new Set([`task_delegate`]),va=new class{name=`noop`;wrap(e){return{command:e.command,args:e.args,options:e.options}}async dispose(){}};function ya(){return va}var ba=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=>Se(e).catch(()=>void 0)))}buildProfile(e){let t=N(e),n=ae(),r=new Set;for(let e of[t,n,...this.options.writablePaths.map(e=>N(e))]){r.add(e);try{r.add(he(e))}catch{}}let i=e=>e.replace(/\\/g,`\\\\`).replace(/"/g,`\\"`),a=[...r].map(e=>`(subpath "${i(e)}")`).join(`
133
135
  `),o=this.options.network===`deny`?`(deny network*)
134
136
  `:`(allow network*)
135
137
  `,s=new Set;for(let e of this.options.denyReadPaths){let t=N(e);s.add(t);try{s.add(he(t))}catch{}}return`(version 1)
@@ -152,5 +154,5 @@ ${s.size>0?`
152
154
  ; credential read isolation (opt-in) — last match wins, denies even within writable roots
153
155
  `+[...s].map(e=>`(deny file-read* (subpath "${i(e)}"))`).join(`
154
156
  `)+`
155
- `:``}`}ensureProfileSync(e){let t=this.profilePathByContent.get(e);if(t&&E(t))return t;let n=`${ae()}/otto-sandbox-${we()}.sb`;return ve(n,e,`utf-8`),this.profilePathByContent.set(e,n),n}};function Oa(e){return new Da(e)}var ka=class{name=`bubblewrap`;options;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=N(e.projectRoot),n=N(e.options.cwd??t),r=[`--unshare-all`,`--cap-drop`,`ALL`,`--die-with-parent`];for(let e of Aa)E(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=N(e);i.push(t),r.push(`--bind`,t,t)}for(let e of this.options.denyReadPaths){let t=N(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 Aa=[`/usr`,`/bin`,`/lib`,`/lib64`,`/etc`,`/dev`,`/sys`];function ja(e){return new ka(e)}function Ma(){let e=ie(),t=process.env.OTTO_AUTH_PATH;return[j(e,`.ssh`),j(e,`.aws`),j(e,`.gnupg`),t?A(t):j(e,`.config`,`otto`)]}function Na(e={}){switch(e.force??Pa()){case`seatbelt`:return Oa({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});case`bubblewrap`:return ja({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});default:return Ea()}}function Pa(){return process.platform===`darwin`?`seatbelt`:process.platform===`linux`?`bubblewrap`:`noop`}function Fa(){let e=Pa();return e===`seatbelt`?E(`/usr/bin/sandbox-exec`):e===`bubblewrap`?[`/usr/bin/bwrap`,`/usr/local/bin/bwrap`,`/bin/bwrap`].some(e=>E(e))?!0:(process.env.PATH??``).split(`:`).filter(Boolean).some(e=>E(`${e}/bwrap`)):!1}export{ht as BinaryToolExecutorRegistry,Cr as CapabilityGapReportStore,ui as LSPClient,wa as ORCHESTRATION_TOOL_NAMES,_a as TOOL_NODES,Sa as ToolRegistry,dr as buildDelegationAgentListDescription,wr as capabilityGapDedupKey,gt as createBinaryToolExecutorRegistry,ja as createBubblewrapSandbox,Er as createCapabilityGap,Lr as createCaptureFileState,In as createEdit,We as createGrillMe,zr as createLearn,Ci as createLspDefinition,ki as createLspDiagnostics,ji as createLspPrepareRename,Ti as createLspReferences,Ni as createLspRename,Di as createLspSymbols,Oa as createMacOsSeatbeltSandbox,Ea as createNoopSandbox,Na as createPlatformSandbox,fn as createRead,Fi as createSessionManagerTool,Ar as createTaskControl,Or as createTaskInspect,Ca as createToolRegistry,Vr as createToolSearch,nr as createWebFetch,ur as createWebSearch,vn as createWrite,Fr as createWriteTodos,Ma as credentialDenyReadPaths,bi as findWorkspaceRoot,Fa as isPlatformSandboxAvailable,J as lspManager,ba as registerBuiltinTools,Z as withLspClient};
157
+ `:``}`}ensureProfileSync(e){let t=this.profilePathByContent.get(e);if(t&&E(t))return t;let n=`${ae()}/otto-sandbox-${we()}.sb`;return ve(n,e,`utf-8`),this.profilePathByContent.set(e,n),n}};function xa(e){return new ba(e)}var Sa=class{name=`bubblewrap`;options;constructor(e={}){this.options={network:e.network??`allow`,writablePaths:e.writablePaths??[],denyReadPaths:e.denyReadPaths??[]}}wrap(e){let t=N(e.projectRoot),n=N(e.options.cwd??t),r=[`--unshare-all`,`--cap-drop`,`ALL`,`--die-with-parent`];for(let e of Ca)E(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=N(e);i.push(t),r.push(`--bind`,t,t)}for(let e of this.options.denyReadPaths){let t=N(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 Ca=[`/usr`,`/bin`,`/lib`,`/lib64`,`/etc`,`/dev`,`/sys`];function wa(e){return new Sa(e)}function Ta(){let e=ie(),t=process.env.OTTO_AUTH_PATH;return[j(e,`.ssh`),j(e,`.aws`),j(e,`.gnupg`),t?A(t):j(e,`.config`,`otto`)]}function Ea(e={}){switch(e.force??Da()){case`seatbelt`:return xa({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});case`bubblewrap`:return wa({network:e.network,writablePaths:e.writablePaths,denyReadPaths:e.denyReadPaths});default:return ya()}}function Da(){return process.platform===`darwin`?`seatbelt`:process.platform===`linux`?`bubblewrap`:`noop`}function Oa(){let e=Da();return e===`seatbelt`?E(`/usr/bin/sandbox-exec`):e===`bubblewrap`?[`/usr/bin/bwrap`,`/usr/local/bin/bwrap`,`/bin/bwrap`].some(e=>E(e))?!0:(process.env.PATH??``).split(`:`).filter(Boolean).some(e=>E(`${e}/bwrap`)):!1}export{ht as BinaryToolExecutorRegistry,Cr as CapabilityGapReportStore,li as LSPClient,_a as ORCHESTRATION_TOOL_NAMES,ua as TOOL_NODES,ha as ToolRegistry,dr as buildDelegationAgentListDescription,wr as capabilityGapDedupKey,gt as createBinaryToolExecutorRegistry,wa as createBubblewrapSandbox,Er as createCapabilityGap,Lr as createCaptureFileState,In as createEdit,We as createGrillMe,zr as createLearn,Ci as createLspDefinition,ki as createLspDiagnostics,ji as createLspPrepareRename,Ti as createLspReferences,Ni as createLspRename,Di as createLspSymbols,xa as createMacOsSeatbeltSandbox,ya as createNoopSandbox,Ea as createPlatformSandbox,fn as createRead,Fi as createSessionManagerTool,Ar as createTaskControl,Or as createTaskInspect,ga as createToolRegistry,Vr as createToolSearch,nr as createWebFetch,ur as createWebSearch,vn as createWrite,Fr as createWriteTodos,Ta as credentialDenyReadPaths,bi as findWorkspaceRoot,Oa as isPlatformSandboxAvailable,J as lspManager,pa as registerBuiltinTools,Z as withLspClient};
156
158
  //# sourceMappingURL=index.js.map