@x-otto/coding 0.1.0-alpha.15 → 0.1.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +29 -29
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import{createRequire as e}from"node:module";import{Devtools as t}from"@x-otto/de
|
|
|
9
9
|
`)}function Ho(e){let t=Date.now(),n={model:e.model,systemPrompt:e.systemPrompt??``,messages:[{role:`user`,content:[{type:`text`,text:e.prompt}],timestamp:Date.now()}],tools:e.tools,thinkingLevel:e.thinkingLevel??`medium`,maxTokens:e.maxTokens},r,i=new At({stream:e.stream,logger:e.logger,agentName:e.runId,maxToolTurns:e.maxToolTurns,toolHookExecutor:$t({hookRegistry:e.hookRegistry,agentName:e.runId,sessionId:e.runId}),depth:e.depth??0,approval:(t,n,i)=>{let a=e.onAwaitingInput;return a?new Promise(n=>{un.setAwaitingApproval(e.runId,`${t}: ${i}`),r=t=>{un.clearAwaitingApproval(e.runId),n(t)};try{zo(e.outputPath,`⏳ 需要你确认「${t}」:${i}\n 进 Delegates 面板按 o,回复 yes 允许 / no 拒绝。`)}catch{}a(i)}):Promise.resolve(!1)}});i.on(`turn.end`,({message:t})=>{let n=Vo(t);if(n)try{zo(e.outputPath,n)}catch{}});let a=()=>{if(r){let e=r;r=void 0,e(!1)}i.abort()},o=Ht(i,{id:e.runId,agentName:e.runId,title:e.runId,prompt:e.prompt,model:e.model.id,origin:`user`,depth:e.depth??0,abort:a,messages:n.messages});return{promise:i.run(n).then(e=>({text:Vo(e),messages:n.messages,tokenUsage:{input:e.usage.inputTokens,output:e.usage.outputTokens,cacheRead:e.usage.cacheReadTokens},durationMs:Date.now()-t})).finally(()=>{o(),un.markEnded(e.runId)}),abort:a,steer:e=>{if(r){let t=r;r=void 0,t(/^\s*(y|yes|是|好|ok|approve|允许|同意|确认|可以|行)/i.test(e));return}i.steer({role:`user`,content:[{type:`text`,text:e}],timestamp:Date.now()})},outputPath:e.outputPath}}var Uo=class{ownedJobIds=new Set;handles=new Map;appliedDiffs=new Map;constructor(e){if(this.deps=e,e.appliedDiffStore)for(let{jobId:t,diff:n}of e.appliedDiffStore.loadAll())this.appliedDiffs.set(t,n)}start(e){let{repoRoot:t,registry:n,spawnRun:r,buildOutputPath:i,newJobId:a}=this.deps,o=a(),s=en(t,o),c=i(o),l;try{l=r({runId:o,prompt:e.prompt,outputPath:c,workspaceDir:s.path,onAwaitingInput:()=>this.deps.registry.update(o,{status:`needs_input`}),...e.model?{model:e.model}:{}})}catch(e){throw vn(s,this.deps.onWorktreeCleanupError),e}let u=n.register({id:o,sessionId:e.sessionId,title:e.title,origin:e.origin,worktree:s,outputPath:c,abort:l.abort});return this.ownedJobIds.add(o),this.handles.set(o,l),l.promise.then(t=>this.onRunComplete(o,e.title,e.sessionId,s,c,t)).catch(t=>this.onRunFailed(o,e.title,e.sessionId,t)).finally(()=>this.handles.delete(o)),u}supply(e,t){let n=this.handles.get(e);if(!n?.steer)return!1;let r=this.deps.registry.get(e);return!r||r.status!==`running`&&r.status!==`needs_input`?!1:(n.steer(t),r.status===`needs_input`&&this.deps.registry.update(e,{status:`running`}),!0)}dispose(){for(let e of this.ownedJobIds)this.deps.registry.cancel(e);this.ownedJobIds.clear(),this.handles.clear(),this.appliedDiffs.clear()}onRunComplete(e,t,n,r,i,a){let o=this.deps.registry.get(e);if(!o||o.status===`canceled`)return;let s=``;try{s=Tn(r)}catch(r){this.onRunFailed(e,t,n,r);return}if(this.deps.registry.update(e,{status:`ready`,diff:s}),s.trim()){this.deps.notify(n,{jobId:e,title:t,status:`ready`,summary:`produced a diff (${Ko(s)})`,outputPath:i,worktree:{path:r.path,branch:r.branch}});return}let c=Wo(a).trim(),l=c?`完成 — /jobs 按 o 看结果(约 ${c.length} 字)`:`完成(无文件改动、无文本输出)`;this.deps.notify(n,{jobId:e,title:t,status:`ready`,summary:l,outputPath:i})}onRunFailed(e,t,n,r){let i=this.deps.registry.get(e);if(!i||i.status===`canceled`)return;let a=r instanceof Error?r.message:String(r);this.deps.registry.update(e,{status:`failed`,error:a}),this.deps.notify(n,{jobId:e,title:t,status:`failed`,summary:a})}readDiffFromWorktree(e){try{return Tn(e.worktree)}catch{return``}}apply(e,t={}){let n=this.deps.registry.get(e);if(!n)return{applied:!1,status:`error`,message:`job ${e} not found`,conflictPaths:[]};if(n.status!==`ready`)return{applied:!1,status:`error`,message:`job ${e} is ${n.status}, not ready`,conflictPaths:[]};let r=n.diff??this.readDiffFromWorktree(n);if(r.trim()===``)return{applied:!1,status:`error`,message:`job ${e} has no diff (worktree missing or empty — rehydrated job may be stale)`,conflictPaths:[]};let i=wn(r),a=Cn(r),{added:o,removed:s}=Go(r),c=!Gt(this.deps.repoRoot,r),l=tn({forbiddenZone:i,capabilityTouched:a,diffStat:{added:o,removed:s},conflict:c,origin:t.origin??`user`},{level:0});if(l.action===`forbid`)return{applied:!1,status:`error`,message:`apply forbidden by governance policy: ${l.reasons.join(`; `)}`,conflictPaths:[],forbiddenZone:i,capabilityTouched:a.length>0};let u=Vt(this.deps.repoRoot,r);if(u.applied)return r.trim()&&(this.appliedDiffs.set(e,r),this.deps.appliedDiffStore?.save(e,r)),this.deps.registry.update(e,{status:`applied`}),{applied:!0,status:`success`,message:`applied cleanly`,conflictPaths:[],forbiddenZone:i,capabilityTouched:a.length>0};let d=pn(this.deps.repoRoot,n.worktree.base);return{applied:!1,status:u.conflictPaths.length?`partial`:`error`,message:u.conflictPaths.length?`conflicts in ${u.conflictPaths.length} file(s); resolve or cancel${d?` (main repo has moved since this job started)`:``}`:`apply failed`,conflictPaths:u.conflictPaths,repoDrifted:d}}revert(e){let t=this.appliedDiffs.get(e);if(!t)return{applied:!1,status:`error`,message:`job ${e} has no applied change to revert`,conflictPaths:[]};let n=Sn(this.deps.repoRoot,t);return n.applied?(this.appliedDiffs.delete(e),this.deps.appliedDiffStore?.delete(e),{applied:!0,status:`success`,message:`reverted cleanly`,conflictPaths:[]}):{applied:!1,status:n.conflictPaths.length?`partial`:`error`,message:n.conflictPaths.length?`cannot revert: ${n.conflictPaths.length} file(s) diverged since apply`:`revert failed`,conflictPaths:n.conflictPaths}}cancel(e){this.deps.registry.cancel(e)}};function Wo(e){let t=e?.text;return typeof t==`string`?t:``}function Go(e){let t=0,n=0,r=0,i=e.split(/^diff --git /m).filter(Boolean);for(let e of i){if(e.includes(`
|
|
10
10
|
GIT binary patch`)){r++;continue}for(let r of e.split(`
|
|
11
11
|
`))r.startsWith(`+`)&&!r.startsWith(`+++`)?t++:r.startsWith(`-`)&&!r.startsWith(`---`)&&n++}return{added:t,removed:n,binaryFiles:r}}function Ko(e){let{added:t,removed:n,binaryFiles:r}=Go(e);return`+${t}/-${n}${r>0?`, ${r} binary file${r===1?``:`s`}`:``}`}const qo=/^[a-zA-Z0-9_-]+$/;function Jo(e){return{save(t,n){if(!qo.test(t))throw Error(`Invalid jobId for applied diff store: ${t}`);S(e,{recursive:!0}),T(O(e,`${t}.patch`),n,`utf-8`)},load(t){let n=O(e,`${t}.patch`);if(x(n))return C(n,`utf-8`)},delete(t){let n=O(e,`${t}.patch`);x(n)&&Qe(n)},loadAll(){return x(e)?Ke(e).filter(e=>tt(e)===`.patch`).map(t=>({jobId:t.slice(0,-6),diff:C(O(e,t),`utf-8`)})):[]}}}const Yo=[`delegate_job`];function Xo(e){return e.filter(e=>!Yo.includes(e.name))}function Zo(){return`job_${ki().replace(/-/g,``).slice(0,8)}`}function Qo(e){let t=e.outputDir??O(et(),`otto-jobs`);return e.registry.setWorktreeCleanupErrorHandler((t,n,r)=>e.logger.warn({jobId:t,op:n,err:r instanceof Error?r.message:String(r)},`Failed to clean up agent job worktree`)),new Uo({repoRoot:e.repoRoot,registry:e.registry,notify:e.notify,onWorktreeCleanupError:(t,n)=>{e.logger.warn({op:t,err:n instanceof Error?n.message:String(n)},`Failed to clean up agent job worktree`)},appliedDiffStore:e.appliedDiffDir?Jo(e.appliedDiffDir):void 0,spawnRun:({runId:t,prompt:n,outputPath:r,workspaceDir:i,onAwaitingInput:a,model:o})=>{let s=o?ts(o,e):void 0;return Ho({runId:t,prompt:n,outputPath:r,model:s?.model??e.getModel(),thinkingLevel:s?.thinkingLevel,systemPrompt:e.systemPrompt,tools:[...Xo(e.buildTools(i)),...e.mcpTools?.()??[]],maxToolTurns:e.maxToolTurns,stream:e.stream,logger:e.logger,hookRegistry:e.hookRegistry,onAwaitingInput:a})},buildOutputPath:e=>(S(t,{recursive:!0}),O(t,`agent-job-${e}.log`)),newJobId:e.newJobId??Zo})}const $o=[`low`,`medium`,`high`,`xhigh`,`max`];function es(e){return $o.includes(e)}function ts(e,t){if(!t.findModel)throw Error(`per-job model is not wired (findModel not injected)`);let n=t.findModel(e.id);if(!n)throw Error(`Model "${e.id}" not found for job (check /model list)`);let r;if(e.thinkingLevel!==void 0){if(!es(e.thinkingLevel))throw Error(`Invalid thinking level "${e.thinkingLevel}" (expected low|medium|high|xhigh|max)`);r=e.thinkingLevel}return{model:n,thinkingLevel:r}}function ns(e){return Qo({repoRoot:e.workspaceDir,registry:cn,notify:(t,n)=>e.agentJobNotify?.(t,n),buildTools:t=>e.buildToolsForWorkspace(t),mcpTools:()=>e.connectorTools(),getModel:()=>{let t=e.settings.get(`model`),n=(t?e.modelRegistry.find(t):void 0)??e.modelRegistry.getAll().find(e=>e.id!==m);if(!n)throw Error(`No model available — configure a model or run /model first.`);return n},findModel:t=>e.modelRegistry.find(t),stream:e.stream,logger:e.logger,hookRegistry:e.hookRegistry,appliedDiffDir:c()?void 0:O(r,`jobs`,`applied`)})}function rs(e){for(let t=e.length-1;t>=0;t--){let n=e[t];if(n?.role!==`assistant`)continue;let r=(n.content??[]).filter(e=>e.type===`text`&&typeof e.text==`string`).map(e=>e.text).join(`
|
|
12
|
-
`).trim();if(r.length>0)return r}return``}function is(e){for(let t=e.length-1;t>=0;t--){let n=e[t];if(n?.role!==`assistant`)continue;let r=n.stopReason;return r===`refusal`||r===`max_tokens`?r:null}return null}function as(e){let t=Error(e);return t.name=`AbortError`,t}function os(e){let t=new Map;return{runSession:async n=>{let r=Date.now(),i=new AbortController;n.taskId&&t.set(n.taskId,i);let a=n.signal,o=()=>i.abort();a&&(a.aborted?i.abort():a.addEventListener(`abort`,o,{once:!0}));let s,c=n.sessionMode===`sticky`&&n.sessionId?e.getSession(n.sessionId):void 0;if(c)s=c;else{let t={agentName:n.agentName,slot:n.slot,modelId:n.model,category:n.category,delegated:!0,promptContext:n.promptContext??(n.agentName?{taskDescription:n.prompt}:void 0),depth:n.depth,parentTaskId:n.parentTaskId,readOnly:n.readOnly};s=await e.createSession(n.sessionMode===`sticky`&&n.sessionId?{...t,id:n.sessionId}:{...t,transient:!0})}let l=s.id,u=Ht(s.coreAgent,{id:l,agentName:n.agentName??`agent`,title:n.agentName??n.prompt.replace(/\s+/g,` `).slice(0,48),prompt:n.prompt,model:n.model??s.model.id,modelDecidedBy:s.modelResolution?.decidedBy,origin:`main`,depth:n.depth??0,abort:()=>s.abort(),messages:()=>s.session.messages()});try{if(i.signal.aborted)throw as(`Task cancelled before the session started`);let e=n.agentName?`<agent-guardrails>\n${Rn}\n</agent-guardrails>\n\n<task>\n${n.prompt}\n</task>`:n.prompt,t=s.prompt(e);t.catch(()=>{}),await Promise.race([t,new Promise((e,t)=>{i.signal.addEventListener(`abort`,()=>{s.abort(),t(as(`Task cancelled`))})})]);let a=s.session.messages(),o=rs(a),c=is(a);return c&&(o=`${o}\n\n[⚠ subagent did not complete cleanly (stop reason: ${c}) — the output above may be partial or unreliable; verify before relying on it]`),{text:o,sessionId:s.id,durationMs:Date.now()-r}}finally{un.setMessages(l,s.session.messages()),u(),un.markEnded(l),a?.removeEventListener(`abort`,o),n.taskId&&t.delete(n.taskId),n.sessionMode===`ephemeral`&&await e.removeSession(s.id).catch(t=>{e.logger.warn(`Failed to remove ephemeral session %s: %s`,s.id,t)})}},abortTask:e=>{t.get(e)?.abort()}}}function ss(e){let t=new Ni({focusTracking:process.env.OTTO_TUI_FOCUS===`1`}),n=new Mi({getConfig:e.getConfig,getCwd:e.getCwd,onInteraction:()=>t.recordInteraction(),isFollowUpPending:e.isFollowUpPending,gate:new Fi({getCondition:()=>e.getConfig()?.condition,getIdleThresholdMs:()=>e.getConfig()?.idle_threshold_ms,state:t})});return n.registerChannel(new Pi({getConfig:e.getConfig})),n.registerChannel(new Ai(`hook`,()=>!0,t=>e.emitHookNotification({sessionId:t.sessionId,message:t.message,title:t.title,notification_type:t.type}))),n.registerChannel(new ji({getConfig:e.getConfig,isTrusted:()=>Si(e.getCwd())})),{dispatcher:n,presenceTracker:t}}function cs(e,t){if(!t||t.length===0)return e;let n=t.map(e=>RegExp(`^`+e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`)+`$`,`i`));return e.filter(e=>!n.some(t=>t.test(e)))}var ls=class{catalog;derivedModelIds=new Map;constructor(e,t,n){this.modelRegistry=e,this.authStore=t,this.catalog=n??new f}async hydrate(){let e=new Set,t=await this.catalog.load();if(!t)return e;for(let n of Object.values(t.providers)){let t=ce(this.modelRegistry,n.mergeProvider,n.modelIds,{templateApi:n.modelApi});for(let n of t.availableIds)e.add(n);let r=this.derivedModelIds.get(n.mergeProvider)??new Set;for(let e of t.derivedIds)r.add(e);this.derivedModelIds.set(n.mergeProvider,r)}return e}refreshInFlight=null;async refresh(){return this.refreshInFlight||=this.doRefresh().finally(()=>{this.refreshInFlight=null}),this.refreshInFlight}async doRefresh(){let e=this.modelRegistry,t=this.authStore,n=t.listProviders(),r=await Promise.all(n.map(e=>t.getAuthSource(e).catch(()=>null))),i=[];for(let a=0;a<n.length;a++){let o=n[a];if(!r[a]){i.push({provider:o,status:`skipped`,reason:`not authenticated`});continue}let s=await t.resolveAuth(o).catch(()=>null);if(!s?.token){let e=r[a]===`oauth`&&!t.oauth.has(o)?`provider capability not granted — trust the plugin in /extension`:`credential expired or refresh failed — re-login`;i.push({provider:o,status:`skipped`,reason:e});continue}let c=fe(o,s.token,{mode:s.mode,beta:s.beta});if(!c){let t=e.getByProvider(o);if(t.length>0){let e=t.map(e=>e.id);await this.catalog.put(o,{fetchedAt:Date.now(),mergeProvider:o,modelIds:e}).catch(()=>{}),i.push({provider:o,status:`ok`,added:0,kept:e.length,total:e.length,availableIds:e})}else i.push({provider:o,status:`skipped`,reason:`no live models endpoint`});continue}try{let t=cs(await se(c),c.modelIdExclude),n=ce(e,c.mergeProvider,t,{templateApi:c.modelApi}),r=new Set(n.availableIds),a=this.derivedModelIds.get(c.mergeProvider)??new Set;for(let t of a)r.has(t)||e.unregister(t);this.derivedModelIds.set(c.mergeProvider,new Set([...[...a].filter(e=>r.has(e)),...n.derivedIds])),await this.catalog.put(o,{fetchedAt:Date.now(),mergeProvider:c.mergeProvider,modelApi:c.modelApi,modelIds:t}).catch(()=>{}),i.push({provider:o,status:`ok`,added:n.added,kept:n.kept,total:n.total,availableIds:n.availableIds})}catch(e){i.push({provider:o,status:`failed`,reason:e instanceof Error?e.message:String(e)})}}return i}};function us(e){return e.modelRegistry.getAll()}function ds(e,t){return e.modelRegistry.resolve(t)}function fs(e){return e.providerRegistry.list()}function ps(e,t){e.hookRegistry.register(t)}function ms(e,t){return e.hookRegistry.unregister(t)}async function hs(e){return e.modelCatalogManager.hydrate()}function gs(e,t){return e.codingSessionPool.getSessionAvailableModels(t)}function _s(e,t,n){e.codingSessionPool.setSessionAvailableModels(t,n)}async function vs(e){return e.modelCatalogManager.refresh()}async function ys(e,t){let n=new Set(t?e.codingSessionPool.getSessionAvailableModels(t):[]),r=ie(e.authStore,`delegation`);return Promise.all(e.modelRegistry.getAll().filter(e=>e.id!==m).map(async e=>{let t=await r(e);return{id:e.id,provider:e.provider,strengths:e.strengths??[],available:n.has(e.id),usable:t.usable,...t.reason?{unusableReason:t.reason}:{}}}))}var bs=class{constructor(e){this.host=e}get modelRegistry(){return this.host.modelRegistry}get authStore(){return this.host.authStore}getAllModels(){return us(this.host)}resolveModel(e){return ds(this.host,e)}listProviders(){return fs(this.host)}hydrateModelCache(){return hs(this.host)}getSessionAvailableModels(e){return gs(this.host,e)}setSessionAvailableModels(e,t){_s(this.host,e,t)}refreshModels(){return vs(this.host)}buildModelCatalog(e){return ys(this.host,e)}};function xs(e,t){e.notificationDispatcher.registerChannel(t)}function Ss(e,t){e.notificationDispatcher.registerChannel(new Ai(`inapp`,()=>!0,e=>t(e.title??`otto`,e.message,e.type)))}function Cs(e){e.presenceTracker.recordInteraction()}function ws(e,t){e.presenceTracker.setFocus(t)}var Ts=class{constructor(e){this.host=e}registerNotificationChannel(e){xs(this.host,e)}registerInAppNotificationChannel(e){Ss(this.host,e)}notifyUserInteraction(){Cs(this.host)}setTerminalFocus(e){ws(this.host,e)}setFollowUpPendingProbe(e){this.host.setFollowUpPendingProbe?.(e)}};async function Es(e,t,n){if(!t)return;if(n){e.permissionRegistry.addSessionAlwaysAllow(n,t),e.codingSessionPool.setSessionPermissionAllow(n,e.permissionRegistry.getSessionAlwaysAllow(n)),e.logger.info({tool:t,sessionId:n},`Tool added to session always-allow list`);return}let r=e.settings.get(`permission_always_allow`)??[];r.includes(t)||(e.permissionRegistry.addAlwaysAllow(t),await e.settings.persist({permission_always_allow:[...r,t]}),e.logger.info({tool:t},`Tool added to project always-allow list`))}function Ds(e){return e.settings.get(`permission_mode`)??`auto`}async function Os(e,t){let n=Ds(e);await e.settings.update({permission_mode:t}),e.settings.persist({permission_mode:t}).catch(n=>e.logger.warn({err:n instanceof Error?n.message:String(n),mode:t},`Failed to persist permission mode`)),e.logger.info({from:n,to:t},`Permission mode set`)}function ks(e,t){return e.permissionRegistry.getSessionAlwaysAllow(t)}async function As(e,t){if(!t)return!1;let n=e.settings.get(`permission_always_allow`)??[];if(!n.includes(t))return!1;let r=n.filter(e=>e!==t);return e.permissionRegistry.setAlwaysAllow(r),await e.settings.persist({permission_always_allow:r}),e.logger.info({tool:t},`Tool removed from project always-allow list`),!0}function js(e,t){t&&(e.permissionRegistry.clearSessionAlwaysAllow(t),e.codingSessionPool.setSessionPermissionAllow(t,[]),e.logger.info({sessionId:t},`Session always-allow list cleared`))}var Ms=class{constructor(e){this.host=e}allowToolAlways(e,t){return Es(this.host,e,t)}disallowToolAlways(e){return As(this.host,e)}setPermissionMode(e){return Os(this.host,e)}getPermissionMode(){return Ds(this.host)}getSessionAlwaysAllow(e){return ks(this.host,e)}clearSessionAlwaysAllow(e){js(this.host,e)}bashAlwaysAllowPrefix(e){return be(e)}isWellFormedAllowEntry(e){return Ne(e)}get activeSandbox(){return this.host.sandbox?.activeName??null}approveSandboxBypass(e){this.host.sandbox?.approveBypass(e)}getRecentPermissionDecisions(e,t=8){let n=this.host.auditLog;return n?n.getEntries({sessionId:e}).slice(-t).reverse().map(e=>({timestamp:e.timestamp,toolName:e.toolName,...e.filePath===void 0?{}:{filePath:e.filePath},effect:e.decision.effect,policyName:e.decision.policyName})):[]}recordAutoPermissionDecision(e){this.host.auditLog?.recordAutoDecision(e)}};async function Ns(e,t={}){e.ensureNotDisposed(),await e.ensureSettingsLoaded();let n=await e.sessionConfigResolver.resolve(t),r=await e.agentRuntime.createSession(n);return e.notificationDispatcher.attach(r),e.toolsSyncCoordinator?.record(r.id,{agentName:t.agentName,promptPreset:t.promptPreset??(t.agentName?`subagent`:`main`),readOnly:t.readOnly,hasExplicitTools:t.tools!==void 0}),e.logger.info({sessionId:r.id},`Session created`),r}function Ps(e,t){return e.codingSessionPool.getSession(t)}async function Fs(e,t,n){if(!e.storage.storageHost)throw Error(`shareSession requires a configured storage.host`);await e.storage.storageHost.host.updateMetadata(`sessions`,e.workspaceRef?.key,t,{visibility:n.visibility},void 0),e.logger.info({sessionId:t,visibility:n.visibility},`Session visibility changed`)}function Is(e){return e.codingSessionPool.active}function Ls(e,t){return e.codingSessionPool.listSessions(t)}async function Rs(e,t){let n=Ls(e,t).filter(t=>{let n=e.codingSessionPool.getSession(t.id);return n?!(n.transient===!0||n.depth>0||n.listable===!1):!0}),r;try{r=t?.workspaceId===void 0?e.workspaceRef?.key:zi(t.workspaceId)}catch{r=void 0}if(!r)return n;let i=await e.codingSessionPool.listAllPersistedSessions({workspaceKey:r}).catch(()=>[]),a=t?.workspaceId??e.workspaceRef?.id,o=new Set(n.map(e=>e.id)),s=i.filter(e=>!o.has(e.id)).map(e=>({...e,workspaceId:a})),c=[...n,...s];return c.sort((e,t)=>{let n=e.updatedAt?.getTime()??e.createdAt.getTime();return(t.updatedAt?.getTime()??t.createdAt.getTime())-n}),c}async function zs(e,t){e.notificationDispatcher.detach(t);let n=await e.codingSessionPool.removeSession(t);return n&&(e.toolsSyncCoordinator?.forget(t),e.logger.info({sessionId:t},`Session removed`)),n}async function Bs(e,t){let n=await e.codingSessionPool.archiveSession(t);return n&&e.logger.info({sessionId:t},`Session archived`),n}async function Vs(e,t,n){let r=await e.codingSessionPool.forkSession(t,n);return r&&e.notificationDispatcher.attach(r),r}async function Hs(e,t,n){let r=n.current;if(!r)throw Error(`导出归档仅支持关系化 SQLite 后端(sessions.db)。当前后端不支持。`);let i=await mi(e,r);if(!i)throw Error(`会话 ${e.slice(0,8)} 不存在或无法加载`);let a=JSON.stringify(i,null,2),o=Oi(`sha256`).update(a,`utf-8`).digest(`hex`);await Vi(D(t),{recursive:!0}),await Wi(t,a,`utf-8`);let s=await Ui(t,`utf-8`),c=Oi(`sha256`).update(s,`utf-8`).digest(`hex`);if(o!==c)throw Error(`Checksum 校验失败:写入前 sha256=${o.slice(0,16)},读回 sha256=${c.slice(0,16)}。导出文件 ${t} 可能已损坏,操作整体失败。`);return{entryCount:i.entries.length,checksum:o,path:t}}var Us=class{constructor(e){this.host=e}get sessionManager(){return this.host.codingSessionPool}createSession(e={}){return Ns(this.host,e)}getSession(e){return Ps(this.host,e)}getActiveSession(){return Is(this.host)}listSessions(e){return Ls(this.host,e)}listAllSessions(e){return Rs(this.host,e)}removeSession(e){return zs(this.host,e)}forkSession(e,t){return Vs(this.host,e,t)}shareSession(e,t){return Fs(this.host,e,t)}archiveSession(e){return Bs(this.host,e)}exportSessionArchive(e,t){return Hs(e,t,this.host.sessionRepoRef??{})}getResidencyConfig(){return this.host.getResidencyConfig?.()}sweepOrphanTraceFiles(){return this.host.sweepOrphanTraceFiles?.()??Promise.resolve()}},Ws=class{contexts=new Map;constructor(e){this.deps=e}record(e,t){t.hasExplicitTools||this.contexts.set(e,{agentName:t.agentName,promptPreset:t.promptPreset,readOnly:t.readOnly})}forget(e){this.contexts.delete(e)}resyncOne(e){let t=this.contexts.get(e);return t?this.resyncSession(e,t):!1}resync(){let e=new Set(this.deps.listSessionIds());for(let t of this.contexts.keys())e.has(t)||this.contexts.delete(t);let t=0;for(let[e,n]of this.contexts)this.resyncSession(e,n)&&t++;return t}resyncSession(e,t){let n=this.deps.getSession(e);if(!n)return!1;try{let e=this.deps.sessionConfigResolver.resolveToolsForResync(t.agentName,t.promptPreset,t.readOnly);return n.model.supportsTools===!1&&(e=[]),n.applyTools(e),!0}catch(t){return this.deps.logger.warn({sessionId:e,err:t instanceof Error?t.message:String(t)},`[session-tools-sync] resync failed for session, skipping (fail-soft)`),!1}}};const Gs=A(`@x-otto/coding:plugin-panel-manager`);function Ks(e){let t=e.plugins??ur({cwd:e.workspaceDir,homedir:e.homedir,disabled:e.disabledPlugins}),n=[];for(let e of t){let t=e.manifest.contributes?.panels;if(!t||t.length===0)continue;let r=N(e);if(!r.gated){if(!e.manifest.capabilities?.includes(`tui.renderer`)){Gs.warn({pluginId:e.id},`plugin declares contributes.panels but not capabilities:["tui.renderer"], skipped (fail-closed)`);continue}if(r.pendingCapabilities.includes(`tui.renderer`)){Gs.warn({pluginId:e.id},`plugin capability "tui.renderer" declared but not yet granted, skipping panels (fail-closed)`);continue}for(let r of t){let t=O(e.dir,r.entry);if(!x(t)){Gs.warn({pluginId:e.id,panelId:r.id,modulePath:t},`panel entry not found — postinstall build may not have run`);continue}n.push({id:`${e.id}:${r.id}`,pluginId:e.id,title:r.title,modulePath:t,exportName:r.export??`default`})}}}return n}function qs(e){let t=[];for(let n of e){if(N(n).gated)continue;let e=n.manifest.contributes?.statusItems;if(!e?.length)continue;let r=fr(n.manifest.contributes?.contextKeys);for(let i of e){if((i.point??tr.statuslineItem)!==tr.statuslineItem||!mr(i.when,r))continue;let e=r[i.dataSource.contextKey];e!=null&&t.push({item:{id:`${n.id}:${i.id}`,text:e},order:i.order??0})}}return t.sort((e,t)=>e.order-t.order).map(e=>e.item)}function Js(e){let t=[];for(let n of e){if(N(n).gated)continue;let e=n.manifest.contributes;if(!e?.menus?.length)continue;let r=fr(e.contextKeys);t.push(...jr(tr.menuItem,e.menus,e.actions,r))}return t}const Ys={capability:`tui.renderer`,axis:`renderers`,noun:`renderer`,pendingNote:`RFC-140 D3`,logger:A(`@x-otto/coding:plugin-renderer-loader`),entries:e=>e.manifest.contributes?.renderers,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({rendererId:e.id}),logTypeofOnNotFunction:!0,build:(e,t,n)=>({id:`${e.id}:${t.id}`,pluginId:e.id,matcher:t.matcher,render:n}),successLogFields:e=>({rendererId:e.id,matcher:e.matcher})};function Xs(e){return yo(e,Ys)}const Zs=A(`@x-otto/coding:plugin-a2ui-component-loader`),Qs=new Set([`Text`,`Card`,`List`,`Progress`,`Chip`,`Button`]),$s={capability:`a2ui.component`,axis:`a2uiComponents`,noun:`a2ui component`,pendingNote:`RFC-140 D3 pattern`,logger:Zs,entries:e=>e.manifest.contributes?.a2uiComponents,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({type:e.type}),logTypeofOnNotFunction:!1,preCheck:(e,t)=>Qs.has(t.type)?(Zs.warn({pluginId:e.id,type:t.type},`plugin a2ui component type collides with builtin reserved type, rejected (R4)`),!1):!0,build:(e,t,n)=>({type:t.type,pluginId:e.id,render:n})};function ec(e){return yo(e,$s,{key:e=>e.type,onConflict:(e,t)=>{Zs.warn({pluginId:e.pluginId,type:e.type,existingOwner:t},`a2ui component type already registered by another plugin, rejected (first-registered wins)`)}})}const tc={capability:`tui.renderer`,axis:`statusWidgets`,noun:`status widget`,pendingNote:`RFC-140 D3 pattern`,logger:A(`@x-otto/coding:plugin-status-widget-loader`),entries:e=>e.manifest.contributes?.statusWidgets,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({widgetId:e.id}),logTypeofOnNotFunction:!0,build:(e,t,n)=>({id:`${e.id}:${t.id}`,pluginId:e.id,render:n,tickMs:t.tickMs,order:t.order}),successLogFields:e=>({widgetId:e.id})};function nc(e){return yo(e,tc)}const rc={capability:`tui.renderer`,axis:`fileViewers`,noun:`file viewer`,pendingNote:`RFC-140 D3 pattern`,logger:A(`@x-otto/coding:plugin-file-viewer-loader`),entries:e=>e.manifest.contributes?.fileViewers,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({viewerId:e.id}),logTypeofOnNotFunction:!0,build:(e,t,n)=>({id:`${e.id}:${t.id}`,pluginId:e.id,label:t.label,matcher:t.matcher,render:n,order:t.order}),successLogFields:e=>({viewerId:e.id,matcher:e.matcher})};function ic(e){return yo(e,rc)}var ac=class{constructor(e){this.deps=e}listFileCommands(){return[...this.deps.compatProducts?.fileCommands??[],...this.deps.pluginModuleManager.commands]}computedPluginCommands(){return this.deps.pluginModuleManager.commands}pluginCliCommands(){return this.deps.pluginModuleManager.cliCommands}pluginSlashCommands(){return this.deps.pluginModuleManager.slashCommands}async callMcpTool(e,t,n){let r=this.deps.mcpController.getMcpClient(e);if(!r)throw Error(`MCP server "${e}" not connected or not found`);let i=await r.callTool({name:t,arguments:n});return{content:i.content,isError:i.isError}}getPluginPanelRegistrations(){return Ks({workspaceDir:this.deps.getWorkspaceDir(),homedir:E(),disabledPlugins:this.deps.settings.get(`disabled_plugins`),plugins:this.deps.getActivePlugins()})}getPluginStatusItems(){return qs(this.deps.getActivePlugins())}getGlobalMenuItems(){return Js(this.deps.getActivePlugins())}getPluginRenderers(){return Xs(this.deps.getActivePlugins())}getPluginA2uiComponents(){return ec(this.deps.getActivePlugins())}getPluginStatusWidgets(){return nc(this.deps.getActivePlugins())}getPluginFileViewers(){return ic(this.deps.getActivePlugins())}getPluginRegistrations(){let e=new Map,t=t=>{let n=e.get(t);return n||(n={pluginId:t},e.set(t,n)),n};for(let e of this.getPluginRenderers()){let n=t(e.pluginId);(n.renderers??=[]).push(e)}for(let e of this.getPluginStatusItems()){let n=t(e.id.split(`:`)[0]??e.id);(n.statusItems??=[]).push(e)}for(let e of this.getPluginPanelRegistrations()){let n=t(e.pluginId);(n.panels??=[]).push(e)}for(let e of this.getPluginA2uiComponents()){let n=t(e.pluginId);(n.a2uiComponents??=[]).push(e)}for(let e of this.getPluginStatusWidgets()){let n=t(e.pluginId);(n.statusWidgets??=[]).push(e)}for(let e of this.getPluginFileViewers()){let n=t(e.pluginId);(n.fileViewers??=[]).push(e)}return[...e.values()]}};const z=A(`@x-otto/coding:plugin-hooks`),oc=4096;function sc(e,t){let n=ct();return n.OTTO_PLUGIN_DIR=e.pluginDir,n.OTTO_PLUGIN_ID=e.pluginId,e.workspaceDir&&(n.OTTO_WORKSPACE_DIR=e.workspaceDir),t&&Object.assign(n,t),n}function cc(e){let t=lt(e);return t.safe?null:{success:!1,hook:`pre-check`,command:e,error:`Dangerous command blocked: ${t.reason}`}}function lc(e,t){let n=!1,r,i;if(e?.length){let t=typeof e==`string`?e:e.toString(`utf-8`);r=t.slice(0,oc),n=t.length>oc}if(t?.length){let e=typeof t==`string`?t:t.toString(`utf-8`);i=e.slice(0,oc),n||=e.length>oc}return{stdout:r,stderr:i,truncated:n}}async function uc(e,t){return cc(t)||(z.info({pluginId:e.pluginId,command:t},`Executing postinstall hook`),new Promise(n=>{let r=F(t,{cwd:e.pluginDir,env:sc(e),shell:!0,stdio:`pipe`}),i=[],a=[];r.stdout?.on(`data`,e=>{i.push(e)}),r.stderr?.on(`data`,e=>{a.push(e)});let o=!1,s=setTimeout(()=>{o=!0,r.kill()},3e4);r.on(`close`,(r,c)=>{clearTimeout(s);let{stdout:l,stderr:u,truncated:d}=lc(Buffer.concat(i),Buffer.concat(a)),f={success:!o&&r===0&&c===null,hook:`postinstall`,command:t,exitCode:r,signal:c??void 0,stdout:l,stderr:u,truncated:d||void 0};o&&(f.timeout=!0,f.error=`postinstall hook timed out (30s)`),f.success||z.warn({pluginId:e.pluginId,result:f},`Postinstall hook failed`),n(f)}),r.on(`error`,e=>{clearTimeout(s),n({success:!1,hook:`postinstall`,command:t,error:e.message})})}))}function dc(e,t,n,r,i){let a=cc(t);if(a){z.warn({pluginId:e.pluginId,command:t},`Setup hook blocked by safety check`),n?.(a);return}if(z.info({pluginId:e.pluginId,command:t},`Executing setup hook (async)`),r){let a=r.spawn({command:t,args:[],options:{shell:!0},owner:{type:`plugin-hook`,id:e.pluginId},category:`plugin`,label:`${e.pluginId}:setup`,lifecycle:`evictable`,killGraceMs:5e3,cwd:e.pluginDir,env:sc(e),signal:i}),o=setTimeout(()=>{z.warn({pluginId:e.pluginId},`Setup hook exceeded 60s — killing`),a.kill({reason:`setup timeout (60s)`})},6e4);o.unref?.();let s=``,c=``;a.onStdoutLine(e=>{s+=e+`
|
|
12
|
+
`).trim();if(r.length>0)return r}return``}function is(e){for(let t=e.length-1;t>=0;t--){let n=e[t];if(n?.role!==`assistant`)continue;let r=n.stopReason;return r===`refusal`||r===`max_tokens`||r===`incomplete`?r:null}return null}function as(e){let t=Error(e);return t.name=`AbortError`,t}function os(e){let t=new Map;return{runSession:async n=>{let r=Date.now(),i=new AbortController;n.taskId&&t.set(n.taskId,i);let a=n.signal,o=()=>i.abort();a&&(a.aborted?i.abort():a.addEventListener(`abort`,o,{once:!0}));let s,c=n.sessionMode===`sticky`&&n.sessionId?e.getSession(n.sessionId):void 0;if(c)s=c;else{let t={agentName:n.agentName,slot:n.slot,modelId:n.model,category:n.category,delegated:!0,promptContext:n.promptContext??(n.agentName?{taskDescription:n.prompt}:void 0),depth:n.depth,parentTaskId:n.parentTaskId,readOnly:n.readOnly};s=await e.createSession(n.sessionMode===`sticky`&&n.sessionId?{...t,id:n.sessionId}:{...t,transient:!0})}let l=s.id,u=Ht(s.coreAgent,{id:l,agentName:n.agentName??`agent`,title:n.agentName??n.prompt.replace(/\s+/g,` `).slice(0,48),prompt:n.prompt,model:n.model??s.model.id,modelDecidedBy:s.modelResolution?.decidedBy,origin:`main`,depth:n.depth??0,abort:()=>s.abort(),messages:()=>s.session.messages()});try{if(i.signal.aborted)throw as(`Task cancelled before the session started`);let e=n.agentName?`<agent-guardrails>\n${Rn}\n</agent-guardrails>\n\n<task>\n${n.prompt}\n</task>`:n.prompt,t=s.prompt(e);t.catch(()=>{}),await Promise.race([t,new Promise((e,t)=>{i.signal.addEventListener(`abort`,()=>{s.abort(),t(as(`Task cancelled`))})})]);let a=s.session.messages(),o=rs(a),c=is(a);return c&&(o=`${o}\n\n[⚠ subagent did not complete cleanly (stop reason: ${c}) — the output above may be partial or unreliable; verify before relying on it]`),{text:o,sessionId:s.id,durationMs:Date.now()-r}}finally{un.setMessages(l,s.session.messages()),u(),un.markEnded(l),a?.removeEventListener(`abort`,o),n.taskId&&t.delete(n.taskId),n.sessionMode===`ephemeral`&&await e.removeSession(s.id).catch(t=>{e.logger.warn(`Failed to remove ephemeral session %s: %s`,s.id,t)})}},abortTask:e=>{t.get(e)?.abort()}}}function ss(e){let t=new Ni({focusTracking:process.env.OTTO_TUI_FOCUS===`1`}),n=new Mi({getConfig:e.getConfig,getCwd:e.getCwd,onInteraction:()=>t.recordInteraction(),isFollowUpPending:e.isFollowUpPending,gate:new Fi({getCondition:()=>e.getConfig()?.condition,getIdleThresholdMs:()=>e.getConfig()?.idle_threshold_ms,state:t})});return n.registerChannel(new Pi({getConfig:e.getConfig})),n.registerChannel(new Ai(`hook`,()=>!0,t=>e.emitHookNotification({sessionId:t.sessionId,message:t.message,title:t.title,notification_type:t.type}))),n.registerChannel(new ji({getConfig:e.getConfig,isTrusted:()=>Si(e.getCwd())})),{dispatcher:n,presenceTracker:t}}function cs(e,t){if(!t||t.length===0)return e;let n=t.map(e=>RegExp(`^`+e.replace(/[.+^${}()|[\]\\]/g,`\\$&`).replace(/\*/g,`.*`)+`$`,`i`));return e.filter(e=>!n.some(t=>t.test(e)))}var ls=class{catalog;derivedModelIds=new Map;constructor(e,t,n){this.modelRegistry=e,this.authStore=t,this.catalog=n??new f}async hydrate(){let e=new Set,t=await this.catalog.load();if(!t)return e;for(let n of Object.values(t.providers)){let t=ce(this.modelRegistry,n.mergeProvider,n.modelIds,{templateApi:n.modelApi});for(let n of t.availableIds)e.add(n);let r=this.derivedModelIds.get(n.mergeProvider)??new Set;for(let e of t.derivedIds)r.add(e);this.derivedModelIds.set(n.mergeProvider,r)}return e}refreshInFlight=null;async refresh(){return this.refreshInFlight||=this.doRefresh().finally(()=>{this.refreshInFlight=null}),this.refreshInFlight}async doRefresh(){let e=this.modelRegistry,t=this.authStore,n=t.listProviders(),r=await Promise.all(n.map(e=>t.getAuthSource(e).catch(()=>null))),i=[];for(let a=0;a<n.length;a++){let o=n[a];if(!r[a]){i.push({provider:o,status:`skipped`,reason:`not authenticated`});continue}let s=await t.resolveAuth(o).catch(()=>null);if(!s?.token){let e=r[a]===`oauth`&&!t.oauth.has(o)?`provider capability not granted — trust the plugin in /extension`:`credential expired or refresh failed — re-login`;i.push({provider:o,status:`skipped`,reason:e});continue}let c=fe(o,s.token,{mode:s.mode,beta:s.beta});if(!c){let t=e.getByProvider(o);if(t.length>0){let e=t.map(e=>e.id);await this.catalog.put(o,{fetchedAt:Date.now(),mergeProvider:o,modelIds:e}).catch(()=>{}),i.push({provider:o,status:`ok`,added:0,kept:e.length,total:e.length,availableIds:e})}else i.push({provider:o,status:`skipped`,reason:`no live models endpoint`});continue}try{let t=cs(await se(c),c.modelIdExclude),n=ce(e,c.mergeProvider,t,{templateApi:c.modelApi}),r=new Set(n.availableIds),a=this.derivedModelIds.get(c.mergeProvider)??new Set;for(let t of a)r.has(t)||e.unregister(t);this.derivedModelIds.set(c.mergeProvider,new Set([...[...a].filter(e=>r.has(e)),...n.derivedIds])),await this.catalog.put(o,{fetchedAt:Date.now(),mergeProvider:c.mergeProvider,modelApi:c.modelApi,modelIds:t}).catch(()=>{}),i.push({provider:o,status:`ok`,added:n.added,kept:n.kept,total:n.total,availableIds:n.availableIds})}catch(e){i.push({provider:o,status:`failed`,reason:e instanceof Error?e.message:String(e)})}}return i}};function us(e){return e.modelRegistry.getAll()}function ds(e,t){return e.modelRegistry.resolve(t)}function fs(e){return e.providerRegistry.list()}function ps(e,t){e.hookRegistry.register(t)}function ms(e,t){return e.hookRegistry.unregister(t)}async function hs(e){return e.modelCatalogManager.hydrate()}function gs(e,t){return e.codingSessionPool.getSessionAvailableModels(t)}function _s(e,t,n){e.codingSessionPool.setSessionAvailableModels(t,n)}async function vs(e){return e.modelCatalogManager.refresh()}async function ys(e,t){let n=new Set(t?e.codingSessionPool.getSessionAvailableModels(t):[]),r=ie(e.authStore,`delegation`);return Promise.all(e.modelRegistry.getAll().filter(e=>e.id!==m).map(async e=>{let t=await r(e);return{id:e.id,provider:e.provider,strengths:e.strengths??[],available:n.has(e.id),usable:t.usable,...t.reason?{unusableReason:t.reason}:{}}}))}var bs=class{constructor(e){this.host=e}get modelRegistry(){return this.host.modelRegistry}get authStore(){return this.host.authStore}getAllModels(){return us(this.host)}resolveModel(e){return ds(this.host,e)}listProviders(){return fs(this.host)}hydrateModelCache(){return hs(this.host)}getSessionAvailableModels(e){return gs(this.host,e)}setSessionAvailableModels(e,t){_s(this.host,e,t)}refreshModels(){return vs(this.host)}buildModelCatalog(e){return ys(this.host,e)}};function xs(e,t){e.notificationDispatcher.registerChannel(t)}function Ss(e,t){e.notificationDispatcher.registerChannel(new Ai(`inapp`,()=>!0,e=>t(e.title??`otto`,e.message,e.type)))}function Cs(e){e.presenceTracker.recordInteraction()}function ws(e,t){e.presenceTracker.setFocus(t)}var Ts=class{constructor(e){this.host=e}registerNotificationChannel(e){xs(this.host,e)}registerInAppNotificationChannel(e){Ss(this.host,e)}notifyUserInteraction(){Cs(this.host)}setTerminalFocus(e){ws(this.host,e)}setFollowUpPendingProbe(e){this.host.setFollowUpPendingProbe?.(e)}};async function Es(e,t,n){if(!t)return;if(n){e.permissionRegistry.addSessionAlwaysAllow(n,t),e.codingSessionPool.setSessionPermissionAllow(n,e.permissionRegistry.getSessionAlwaysAllow(n)),e.logger.info({tool:t,sessionId:n},`Tool added to session always-allow list`);return}let r=e.settings.get(`permission_always_allow`)??[];r.includes(t)||(e.permissionRegistry.addAlwaysAllow(t),await e.settings.persist({permission_always_allow:[...r,t]}),e.logger.info({tool:t},`Tool added to project always-allow list`))}function Ds(e){return e.settings.get(`permission_mode`)??`auto`}async function Os(e,t){let n=Ds(e);await e.settings.update({permission_mode:t}),e.settings.persist({permission_mode:t}).catch(n=>e.logger.warn({err:n instanceof Error?n.message:String(n),mode:t},`Failed to persist permission mode`)),e.logger.info({from:n,to:t},`Permission mode set`)}function ks(e,t){return e.permissionRegistry.getSessionAlwaysAllow(t)}async function As(e,t){if(!t)return!1;let n=e.settings.get(`permission_always_allow`)??[];if(!n.includes(t))return!1;let r=n.filter(e=>e!==t);return e.permissionRegistry.setAlwaysAllow(r),await e.settings.persist({permission_always_allow:r}),e.logger.info({tool:t},`Tool removed from project always-allow list`),!0}function js(e,t){t&&(e.permissionRegistry.clearSessionAlwaysAllow(t),e.codingSessionPool.setSessionPermissionAllow(t,[]),e.logger.info({sessionId:t},`Session always-allow list cleared`))}var Ms=class{constructor(e){this.host=e}allowToolAlways(e,t){return Es(this.host,e,t)}disallowToolAlways(e){return As(this.host,e)}setPermissionMode(e){return Os(this.host,e)}getPermissionMode(){return Ds(this.host)}getSessionAlwaysAllow(e){return ks(this.host,e)}clearSessionAlwaysAllow(e){js(this.host,e)}bashAlwaysAllowPrefix(e){return be(e)}isWellFormedAllowEntry(e){return Ne(e)}get activeSandbox(){return this.host.sandbox?.activeName??null}approveSandboxBypass(e){this.host.sandbox?.approveBypass(e)}getRecentPermissionDecisions(e,t=8){let n=this.host.auditLog;return n?n.getEntries({sessionId:e}).slice(-t).reverse().map(e=>({timestamp:e.timestamp,toolName:e.toolName,...e.filePath===void 0?{}:{filePath:e.filePath},effect:e.decision.effect,policyName:e.decision.policyName})):[]}recordAutoPermissionDecision(e){this.host.auditLog?.recordAutoDecision(e)}};async function Ns(e,t={}){e.ensureNotDisposed(),await e.ensureSettingsLoaded();let n=await e.sessionConfigResolver.resolve(t),r=await e.agentRuntime.createSession(n);return e.notificationDispatcher.attach(r),e.toolsSyncCoordinator?.record(r.id,{agentName:t.agentName,promptPreset:t.promptPreset??(t.agentName?`subagent`:`main`),readOnly:t.readOnly,hasExplicitTools:t.tools!==void 0}),e.logger.info({sessionId:r.id},`Session created`),r}function Ps(e,t){return e.codingSessionPool.getSession(t)}async function Fs(e,t,n){if(!e.storage.storageHost)throw Error(`shareSession requires a configured storage.host`);await e.storage.storageHost.host.updateMetadata(`sessions`,e.workspaceRef?.key,t,{visibility:n.visibility},void 0),e.logger.info({sessionId:t,visibility:n.visibility},`Session visibility changed`)}function Is(e){return e.codingSessionPool.active}function Ls(e,t){return e.codingSessionPool.listSessions(t)}async function Rs(e,t){let n=Ls(e,t).filter(t=>{let n=e.codingSessionPool.getSession(t.id);return n?!(n.transient===!0||n.depth>0||n.listable===!1):!0}),r;try{r=t?.workspaceId===void 0?e.workspaceRef?.key:zi(t.workspaceId)}catch{r=void 0}if(!r)return n;let i=await e.codingSessionPool.listAllPersistedSessions({workspaceKey:r}).catch(()=>[]),a=t?.workspaceId??e.workspaceRef?.id,o=new Set(n.map(e=>e.id)),s=i.filter(e=>!o.has(e.id)).map(e=>({...e,workspaceId:a})),c=[...n,...s];return c.sort((e,t)=>{let n=e.updatedAt?.getTime()??e.createdAt.getTime();return(t.updatedAt?.getTime()??t.createdAt.getTime())-n}),c}async function zs(e,t){e.notificationDispatcher.detach(t);let n=await e.codingSessionPool.removeSession(t);return n&&(e.toolsSyncCoordinator?.forget(t),e.logger.info({sessionId:t},`Session removed`)),n}async function Bs(e,t){let n=await e.codingSessionPool.archiveSession(t);return n&&e.logger.info({sessionId:t},`Session archived`),n}async function Vs(e,t,n){let r=await e.codingSessionPool.forkSession(t,n);return r&&e.notificationDispatcher.attach(r),r}async function Hs(e,t,n){let r=n.current;if(!r)throw Error(`导出归档仅支持关系化 SQLite 后端(sessions.db)。当前后端不支持。`);let i=await mi(e,r);if(!i)throw Error(`会话 ${e.slice(0,8)} 不存在或无法加载`);let a=JSON.stringify(i,null,2),o=Oi(`sha256`).update(a,`utf-8`).digest(`hex`);await Vi(D(t),{recursive:!0}),await Wi(t,a,`utf-8`);let s=await Ui(t,`utf-8`),c=Oi(`sha256`).update(s,`utf-8`).digest(`hex`);if(o!==c)throw Error(`Checksum 校验失败:写入前 sha256=${o.slice(0,16)},读回 sha256=${c.slice(0,16)}。导出文件 ${t} 可能已损坏,操作整体失败。`);return{entryCount:i.entries.length,checksum:o,path:t}}var Us=class{constructor(e){this.host=e}get sessionManager(){return this.host.codingSessionPool}createSession(e={}){return Ns(this.host,e)}getSession(e){return Ps(this.host,e)}getActiveSession(){return Is(this.host)}listSessions(e){return Ls(this.host,e)}listAllSessions(e){return Rs(this.host,e)}removeSession(e){return zs(this.host,e)}forkSession(e,t){return Vs(this.host,e,t)}shareSession(e,t){return Fs(this.host,e,t)}archiveSession(e){return Bs(this.host,e)}exportSessionArchive(e,t){return Hs(e,t,this.host.sessionRepoRef??{})}getResidencyConfig(){return this.host.getResidencyConfig?.()}sweepOrphanTraceFiles(){return this.host.sweepOrphanTraceFiles?.()??Promise.resolve()}},Ws=class{contexts=new Map;constructor(e){this.deps=e}record(e,t){t.hasExplicitTools||this.contexts.set(e,{agentName:t.agentName,promptPreset:t.promptPreset,readOnly:t.readOnly})}forget(e){this.contexts.delete(e)}resyncOne(e){let t=this.contexts.get(e);return t?this.resyncSession(e,t):!1}resync(){let e=new Set(this.deps.listSessionIds());for(let t of this.contexts.keys())e.has(t)||this.contexts.delete(t);let t=0;for(let[e,n]of this.contexts)this.resyncSession(e,n)&&t++;return t}resyncSession(e,t){let n=this.deps.getSession(e);if(!n)return!1;try{let e=this.deps.sessionConfigResolver.resolveToolsForResync(t.agentName,t.promptPreset,t.readOnly);return n.model.supportsTools===!1&&(e=[]),n.applyTools(e),!0}catch(t){return this.deps.logger.warn({sessionId:e,err:t instanceof Error?t.message:String(t)},`[session-tools-sync] resync failed for session, skipping (fail-soft)`),!1}}};const Gs=A(`@x-otto/coding:plugin-panel-manager`);function Ks(e){let t=e.plugins??ur({cwd:e.workspaceDir,homedir:e.homedir,disabled:e.disabledPlugins}),n=[];for(let e of t){let t=e.manifest.contributes?.panels;if(!t||t.length===0)continue;let r=N(e);if(!r.gated){if(!e.manifest.capabilities?.includes(`tui.renderer`)){Gs.warn({pluginId:e.id},`plugin declares contributes.panels but not capabilities:["tui.renderer"], skipped (fail-closed)`);continue}if(r.pendingCapabilities.includes(`tui.renderer`)){Gs.warn({pluginId:e.id},`plugin capability "tui.renderer" declared but not yet granted, skipping panels (fail-closed)`);continue}for(let r of t){let t=O(e.dir,r.entry);if(!x(t)){Gs.warn({pluginId:e.id,panelId:r.id,modulePath:t},`panel entry not found — postinstall build may not have run`);continue}n.push({id:`${e.id}:${r.id}`,pluginId:e.id,title:r.title,modulePath:t,exportName:r.export??`default`})}}}return n}function qs(e){let t=[];for(let n of e){if(N(n).gated)continue;let e=n.manifest.contributes?.statusItems;if(!e?.length)continue;let r=fr(n.manifest.contributes?.contextKeys);for(let i of e){if((i.point??tr.statuslineItem)!==tr.statuslineItem||!mr(i.when,r))continue;let e=r[i.dataSource.contextKey];e!=null&&t.push({item:{id:`${n.id}:${i.id}`,text:e},order:i.order??0})}}return t.sort((e,t)=>e.order-t.order).map(e=>e.item)}function Js(e){let t=[];for(let n of e){if(N(n).gated)continue;let e=n.manifest.contributes;if(!e?.menus?.length)continue;let r=fr(e.contextKeys);t.push(...jr(tr.menuItem,e.menus,e.actions,r))}return t}const Ys={capability:`tui.renderer`,axis:`renderers`,noun:`renderer`,pendingNote:`RFC-140 D3`,logger:A(`@x-otto/coding:plugin-renderer-loader`),entries:e=>e.manifest.contributes?.renderers,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({rendererId:e.id}),logTypeofOnNotFunction:!0,build:(e,t,n)=>({id:`${e.id}:${t.id}`,pluginId:e.id,matcher:t.matcher,render:n}),successLogFields:e=>({rendererId:e.id,matcher:e.matcher})};function Xs(e){return yo(e,Ys)}const Zs=A(`@x-otto/coding:plugin-a2ui-component-loader`),Qs=new Set([`Text`,`Card`,`List`,`Progress`,`Chip`,`Button`]),$s={capability:`a2ui.component`,axis:`a2uiComponents`,noun:`a2ui component`,pendingNote:`RFC-140 D3 pattern`,logger:Zs,entries:e=>e.manifest.contributes?.a2uiComponents,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({type:e.type}),logTypeofOnNotFunction:!1,preCheck:(e,t)=>Qs.has(t.type)?(Zs.warn({pluginId:e.id,type:t.type},`plugin a2ui component type collides with builtin reserved type, rejected (R4)`),!1):!0,build:(e,t,n)=>({type:t.type,pluginId:e.id,render:n})};function ec(e){return yo(e,$s,{key:e=>e.type,onConflict:(e,t)=>{Zs.warn({pluginId:e.pluginId,type:e.type,existingOwner:t},`a2ui component type already registered by another plugin, rejected (first-registered wins)`)}})}const tc={capability:`tui.renderer`,axis:`statusWidgets`,noun:`status widget`,pendingNote:`RFC-140 D3 pattern`,logger:A(`@x-otto/coding:plugin-status-widget-loader`),entries:e=>e.manifest.contributes?.statusWidgets,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({widgetId:e.id}),logTypeofOnNotFunction:!0,build:(e,t,n)=>({id:`${e.id}:${t.id}`,pluginId:e.id,render:n,tickMs:t.tickMs,order:t.order}),successLogFields:e=>({widgetId:e.id})};function nc(e){return yo(e,tc)}const rc={capability:`tui.renderer`,axis:`fileViewers`,noun:`file viewer`,pendingNote:`RFC-140 D3 pattern`,logger:A(`@x-otto/coding:plugin-file-viewer-loader`),entries:e=>e.manifest.contributes?.fileViewers,entryPath:e=>e.entry,exportName:e=>e.export,idLogFields:e=>({viewerId:e.id}),logTypeofOnNotFunction:!0,build:(e,t,n)=>({id:`${e.id}:${t.id}`,pluginId:e.id,label:t.label,matcher:t.matcher,render:n,order:t.order}),successLogFields:e=>({viewerId:e.id,matcher:e.matcher})};function ic(e){return yo(e,rc)}var ac=class{constructor(e){this.deps=e}listFileCommands(){return[...this.deps.compatProducts?.fileCommands??[],...this.deps.pluginModuleManager.commands]}computedPluginCommands(){return this.deps.pluginModuleManager.commands}pluginCliCommands(){return this.deps.pluginModuleManager.cliCommands}pluginSlashCommands(){return this.deps.pluginModuleManager.slashCommands}async callMcpTool(e,t,n){let r=this.deps.mcpController.getMcpClient(e);if(!r)throw Error(`MCP server "${e}" not connected or not found`);let i=await r.callTool({name:t,arguments:n});return{content:i.content,isError:i.isError}}getPluginPanelRegistrations(){return Ks({workspaceDir:this.deps.getWorkspaceDir(),homedir:E(),disabledPlugins:this.deps.settings.get(`disabled_plugins`),plugins:this.deps.getActivePlugins()})}getPluginStatusItems(){return qs(this.deps.getActivePlugins())}getGlobalMenuItems(){return Js(this.deps.getActivePlugins())}getPluginRenderers(){return Xs(this.deps.getActivePlugins())}getPluginA2uiComponents(){return ec(this.deps.getActivePlugins())}getPluginStatusWidgets(){return nc(this.deps.getActivePlugins())}getPluginFileViewers(){return ic(this.deps.getActivePlugins())}getPluginRegistrations(){let e=new Map,t=t=>{let n=e.get(t);return n||(n={pluginId:t},e.set(t,n)),n};for(let e of this.getPluginRenderers()){let n=t(e.pluginId);(n.renderers??=[]).push(e)}for(let e of this.getPluginStatusItems()){let n=t(e.id.split(`:`)[0]??e.id);(n.statusItems??=[]).push(e)}for(let e of this.getPluginPanelRegistrations()){let n=t(e.pluginId);(n.panels??=[]).push(e)}for(let e of this.getPluginA2uiComponents()){let n=t(e.pluginId);(n.a2uiComponents??=[]).push(e)}for(let e of this.getPluginStatusWidgets()){let n=t(e.pluginId);(n.statusWidgets??=[]).push(e)}for(let e of this.getPluginFileViewers()){let n=t(e.pluginId);(n.fileViewers??=[]).push(e)}return[...e.values()]}};const z=A(`@x-otto/coding:plugin-hooks`),oc=4096;function sc(e,t){let n=ct();return n.OTTO_PLUGIN_DIR=e.pluginDir,n.OTTO_PLUGIN_ID=e.pluginId,e.workspaceDir&&(n.OTTO_WORKSPACE_DIR=e.workspaceDir),t&&Object.assign(n,t),n}function cc(e){let t=lt(e);return t.safe?null:{success:!1,hook:`pre-check`,command:e,error:`Dangerous command blocked: ${t.reason}`}}function lc(e,t){let n=!1,r,i;if(e?.length){let t=typeof e==`string`?e:e.toString(`utf-8`);r=t.slice(0,oc),n=t.length>oc}if(t?.length){let e=typeof t==`string`?t:t.toString(`utf-8`);i=e.slice(0,oc),n||=e.length>oc}return{stdout:r,stderr:i,truncated:n}}async function uc(e,t){return cc(t)||(z.info({pluginId:e.pluginId,command:t},`Executing postinstall hook`),new Promise(n=>{let r=F(t,{cwd:e.pluginDir,env:sc(e),shell:!0,stdio:`pipe`}),i=[],a=[];r.stdout?.on(`data`,e=>{i.push(e)}),r.stderr?.on(`data`,e=>{a.push(e)});let o=!1,s=setTimeout(()=>{o=!0,r.kill()},3e4);r.on(`close`,(r,c)=>{clearTimeout(s);let{stdout:l,stderr:u,truncated:d}=lc(Buffer.concat(i),Buffer.concat(a)),f={success:!o&&r===0&&c===null,hook:`postinstall`,command:t,exitCode:r,signal:c??void 0,stdout:l,stderr:u,truncated:d||void 0};o&&(f.timeout=!0,f.error=`postinstall hook timed out (30s)`),f.success||z.warn({pluginId:e.pluginId,result:f},`Postinstall hook failed`),n(f)}),r.on(`error`,e=>{clearTimeout(s),n({success:!1,hook:`postinstall`,command:t,error:e.message})})}))}function dc(e,t,n,r,i){let a=cc(t);if(a){z.warn({pluginId:e.pluginId,command:t},`Setup hook blocked by safety check`),n?.(a);return}if(z.info({pluginId:e.pluginId,command:t},`Executing setup hook (async)`),r){let a=r.spawn({command:t,args:[],options:{shell:!0},owner:{type:`plugin-hook`,id:e.pluginId},category:`plugin`,label:`${e.pluginId}:setup`,lifecycle:`evictable`,killGraceMs:5e3,cwd:e.pluginDir,env:sc(e),signal:i}),o=setTimeout(()=>{z.warn({pluginId:e.pluginId},`Setup hook exceeded 60s — killing`),a.kill({reason:`setup timeout (60s)`})},6e4);o.unref?.();let s=``,c=``;a.onStdoutLine(e=>{s+=e+`
|
|
13
13
|
`}),a.onStderrLine(e=>{c+=e+`
|
|
14
14
|
`}),a.onExit(r=>{clearTimeout(o);let{stdout:i,stderr:a,truncated:l}=lc(s,c),u={success:r.code===0&&r.signal===null,hook:`setup`,command:t,exitCode:r.code,signal:r.signal??void 0,stdout:i,stderr:a,truncated:l||void 0};u.success?z.info({pluginId:e.pluginId},`Setup hook succeeded`):z.error({pluginId:e.pluginId,exitCode:r.code,signal:r.signal,stderr:a?.slice(0,512)},`Setup hook failed`),n?.(u)}),a.onError(r=>{z.error({pluginId:e.pluginId,err:r},`Setup hook spawn error`),n?.({success:!1,hook:`setup`,command:t,error:r.message})});return}let o=F(t,{cwd:e.pluginDir,env:sc(e),shell:!0,timeout:6e4,stdio:`pipe`}),s=``,c=``;o.stdout?.on(`data`,e=>{s+=e.toString(`utf-8`)}),o.stderr?.on(`data`,e=>{c+=e.toString(`utf-8`)}),o.on(`close`,(r,i)=>{let{stdout:a,stderr:o,truncated:l}=lc(s,c),u={success:r===0&&i===null,hook:`setup`,command:t,exitCode:r,signal:i,stdout:a,stderr:o,truncated:l||void 0};u.success?z.info({pluginId:e.pluginId},`Setup hook succeeded`):z.error({pluginId:e.pluginId,exitCode:r,signal:i,stderr:o?.slice(0,512)},`Setup hook failed`),n?.(u)}),o.on(`error`,r=>{z.error({pluginId:e.pluginId,err:r},`Setup hook spawn error`),n?.({success:!1,hook:`setup`,command:t,error:r.message})})}const fc=A(`@x-otto/coding:plugin-lifecycle`),pc=`plugin_lifecycle`,mc=new Set;async function hc(e,t,n){let r=t.get(pc)?.setupDone??[],i=new Map(r.map(e=>[e.id,e.version])),a=new Set(e.map(e=>e.id));for(let e of i.keys())a.has(e)||i.delete(e);let o=!1;for(let r of e){let e=i.get(r.id),a=r.manifest.version,s=r.manifest.scripts?.setup;if(!s){(e===void 0||e!==a)&&(i.set(r.id,a),o=!0);continue}if(!(e!==void 0&&e===a)){if(N(r).gated){fc.info({pluginId:r.id},`Setup hook deferred: plugin not yet trusted`);continue}mc.has(r.id)||(mc.add(r.id),dc({pluginDir:r.dir,pluginId:r.id,workspaceDir:n},s,e=>{mc.delete(r.id),i.set(r.id,a),gc(t,i)},j))}}o&&await gc(t,i)}async function gc(e,t){let n=[...t].map(([e,t])=>({id:e,version:t}));await e.persist({[pc]:{setupDone:n}})}function _c(e,t){let n;try{n=w(t)}catch{return!1}let r;try{r=w(e)}catch{r=e}return r===n||r.startsWith(n+it)}const B=A(`@x-otto/coding:plugin-i18n-loader`),vc=/^([a-zA-Z0-9_-]+)\.json$/;function yc(e){return Buffer.byteLength(e,`utf8`)<=Yn?e:`${Buffer.from(e,`utf8`).subarray(0,Yn).toString(`utf8`)}…`}function bc(e,t,n){let r=new Map,i=k(t,n);if(!_c(i,t))return B.warn({pluginId:e,i18nDir:n},`i18nDir resolves outside plugin dir, skipped`),r;let a;try{a=Ke(i)}catch{return r}let o=a.filter(e=>vc.test(e)).sort();o.length>Jn&&(B.warn({pluginId:e,i18nDir:n,declared:o.length,limit:Jn},`plugin i18nDir declares more locale files than the per-plugin limit, truncated`),o=o.slice(0,Jn));for(let n of o){let a=vc.exec(n)?.[1];if(!a)continue;let o=k(i,n);if(!_c(o,t)){B.warn({pluginId:e,filename:n},`i18nDir locale file resolves outside plugin dir, skipped`);continue}let s;try{s=C(o,`utf8`)}catch(t){B.warn({pluginId:e,filename:n,err:String(t)},`i18nDir locale file read failed, skipped`);continue}if(Buffer.byteLength(s,`utf8`)>qn){B.warn({pluginId:e,filename:n,limit:qn},`i18nDir locale file exceeds byte limit, skipped`);continue}let c;try{c=JSON.parse(s)}catch(t){B.warn({pluginId:e,filename:n,err:String(t)},`i18nDir locale file is not valid JSON, skipped`);continue}if(typeof c!=`object`||!c||Array.isArray(c)){B.warn({pluginId:e,filename:n},`i18nDir locale file is not a flat object, skipped`);continue}for(let[e,t]of Object.entries(c)){if(typeof t!=`string`)continue;let n=r.get(e)??{};n[a]=t,r.set(e,n)}}return r}function xc(e){let t=[];for(let n of e){if(N(n).gated)continue;let e=n.manifest.contributes?.i18nDir,r=n.manifest.contributes?.i18n;if(!e&&!r?.length)continue;let i=e?bc(n.id,n.dir,e):new Map;if(r?.length)for(let e of r){let t=i.get(e.id)??{};for(let[r,i]of Object.entries(e.translations))r in t&&B.debug({pluginId:n.id,id:e.id,locale:r},`i18n inline entry overrides locale dir value`),t[r]=i;i.set(e.id,t)}let a=[...i.keys()];a.length>Kn&&(B.warn({pluginId:n.id,declared:a.length,limit:Kn},`plugin declares more i18n entries than the per-plugin limit, truncated`),a=a.slice(0,Kn));for(let e of a){let r=i.get(e);if(!r)continue;let a={};for(let[e,t]of Object.entries(r))a[e]=yc(t);t.push({pluginId:n.id,id:e,translations:a})}}return t}const Sc=A(`@x-otto/coding:declarative-agents`);function Cc(e){return{...e,defaultTools:e.defaultTools?.length?dt(e.defaultTools):e.defaultTools,disallowedTools:e.disallowedTools?.length?dt(e.disallowedTools):e.disallowedTools}}function wc(e,t){let n=ht(e);if(!n)return Sc.warn({sourcePath:t},`Agent definition missing frontmatter, skipped`),null;let{fields:r,body:i}=n,a=r.get(`name`),o=r.get(`description`);if(!a||!o||!i)return Sc.warn({sourcePath:t},`Agent definition requires name/description/body, skipped`),null;let s=pt(r.get(`tools`)),c=Number.parseInt(r.get(`maxtoolturns`)??``,10),l=Number.parseInt(r.get(`maxtoolturnextensions`)??``,10),u=r.get(`prompt-mode`),d=pt(r.get(`disallowed-tools`)),f=r.get(`allow-subagents`)===`true`?!0:r.get(`allow-subagents`)===`false`?!1:void 0;return{name:a,description:o,taskTypes:pt(r.get(`tasktypes`))??[o],capabilities:pt(r.get(`capabilities`))??[],systemPromptTemplate:i,promptMode:u===`append`||u===`replace`?u:void 0,defaultTools:s,disallowedTools:d?.length?d:void 0,preferredModelTier:r.get(`slot`)??r.get(`tier`),defaultMaxToolTurns:Number.isInteger(c)?c:void 0,defaultMaxToolTurnExtensions:Number.isInteger(l)?l:void 0,allowSubagents:f}}async function Tc(e,t){let{homedir:n}=await import(`node:os`),{join:r}=await import(`node:path`),i=t?.globalDir??r(n(),`.otto`,`agents`),a=r(e,`.otto`,`agents`),o=(t?.plugins??ur({cwd:e,homedir:n()})).filter(e=>e.manifest.contributes?.agents!==!1).filter(e=>!N(e).gated).map(e=>({dir:r(e.dir,`agents`),claude:!1})),c=s(),l=c&&(t?.claudeProjectAgentsAllowed??!1),u=[...o,...c?[{dir:r(n(),`.claude`,`agents`),claude:!0}]:[],{dir:i,claude:!1},...l?[{dir:r(e,`.claude`,`agents`),claude:!0}]:[],{dir:a,claude:!1}],d=new Map;for(let{dir:e,claude:t}of u)for(let n of await Ec(e))d.set(n.name,t?Cc(n):n);let f=[...d.values()];return f.length>0&&Sc.info(`Loaded %d declarative agent(s): %s`,f.length,f.map(e=>e.name).join(`, `)),f}async function Ec(e){let{readFile:t,readdir:n}=await import(`node:fs/promises`),{join:r}=await import(`node:path`),i;try{i=await n(e)}catch{return[]}let a=[];for(let n of i.filter(e=>e.endsWith(`.md`)).sort()){let i=r(e,n);try{let e=wc(await t(i,`utf-8`),i);e&&a.push(e)}catch(e){Sc.warn({sourcePath:i,err:e},`Failed to read agent definition, skipped`)}}return a}const Dc=A(`@x-otto/coding:declarative-teams`),Oc=new Set([`handoff`,`sequential`,`parallel`,`hierarchical`,`router`]);function kc(e){if(!e)return;let t=[];for(let n of e.split(`|`)){let[e,r]=n.split(/→|->/);if(e===void 0||r===void 0)continue;let i=e.split(`,`).map(e=>e.trim().toLowerCase()).filter(e=>e!==``),a=r.trim();i.length>0&&a!==``&&t.push({keywords:i,agentId:a})}return t.length>0?t:void 0}function Ac(e,t){let n=ht(e);if(!n)return Dc.warn({sourcePath:t},`Team definition missing frontmatter, skipped`),null;let{fields:r}=n,i=r.get(`name`),a=r.get(`pattern`),o=pt(r.get(`agents`));if(!i||!a||!Oc.has(a)||!o||o.length===0)return Dc.warn({sourcePath:t},`Team definition requires name, valid pattern and non-empty agents, skipped`),null;let s=Number.parseInt(r.get(`maxhandoffdepth`)??``,10);return{name:i,pattern:a,agentNames:o,pipeline:pt(r.get(`pipeline`)),supervisorId:r.get(`supervisor`),entryAgentId:r.get(`entry`),routerStrategy:r.get(`router`),rules:kc(r.get(`rules`)),maxHandoffDepth:Number.isInteger(s)?s:void 0}}async function jc(e,t){let{homedir:n}=await import(`node:os`),{join:r}=await import(`node:path`),i=t?.globalDir??r(n(),`.otto`,`teams`),a=r(e,`.otto`,`teams`),o=new Map;for(let e of[i,a])for(let t of await Mc(e))o.set(t.name,t);let s=[...o.values()];return s.length>0&&Dc.info(`Loaded %d declarative team(s): %s`,s.length,s.map(e=>e.name).join(`, `)),s}async function Mc(e){let{readFile:t,readdir:n}=await import(`node:fs/promises`),{join:r}=await import(`node:path`),i;try{i=await n(e)}catch{return[]}let a=[];for(let n of i.filter(e=>e.endsWith(`.md`)).sort()){let i=r(e,n);try{let e=Ac(await t(i,`utf-8`),i);e&&a.push(e)}catch(e){Dc.warn({sourcePath:i,err:e},`Failed to read team definition, skipped`)}}return a}var Nc=class{reloadQueue=Promise.resolve();constructor(e,t,n,r,i,a,o,s,c,l,u){this.settings=e,this.mcp=t,this.skills=n,this.pluginModules=r,this.pluginProviders=i,this.tools=a,this.modelCatalog=o,this.workspaceDir=s,this.activePlugins=c,this.i18n=l,this.getCompatTrustGated=u}async reload(){let e=this.reloadQueue.then(()=>this.reloadSerial());return this.reloadQueue=e.catch(()=>void 0),e}async reloadSerial(){await this.settings.load(),hc(this.activePlugins(),this.settings,this.workspaceDir),await this.mcp.reload(),this.skills&&(this.skills.updatePluginDirs(this.activePlugins().filter(e=>e.manifest.contributes?.skills!==!1).filter(e=>!N(e).gated).map(e=>({id:e.id,dir:O(e.dir,`skills`)}))),await this.skills.discover());let e=xc(this.activePlugins());this.i18n.register(e);let t=(await this.modelCatalog.refresh().catch(()=>[])).reduce((e,t)=>e+(t.added??0),0),n=await Tc(this.workspaceDir,{plugins:this.activePlugins(),claudeProjectAgentsAllowed:this.getCompatTrustGated?!this.getCompatTrustGated():void 0}),r=await jc(this.workspaceDir),i=[],a=[];try{a=await this.pluginModules.reload(new Set(n.map(e=>e.name)))}catch(e){i.push({path:`pluginModules`,error:e instanceof Error?e.message:String(e)})}n.push(...a);let o={pendingCapabilityPluginIds:[]};try{o=await this.pluginProviders.reload()}catch(e){i.push({path:`pluginProviders`,error:e instanceof Error?e.message:String(e)})}return{mcpTools:this.tools.getAvailable(`full`).filter(e=>e.name.startsWith(`mcp__`)).length,models:t,agents:n.length,teamCount:r.length,agentProfiles:n,teamDeclarations:r,pendingProviderCapabilities:o.pendingCapabilityPluginIds,i18nEntries:e.length,...i.length>0?{reloadFailures:i}:{}}}};const Pc=A(`@x-otto/coding:panel-backend-controller`);var Fc=class{stateListeners=new Map;statusListeners=new Map;inProcessHandlers=new Map;subscribedManager=null;constructor(e){this.deps=e}registerInProcessBackend(e,t){this.inProcessHandlers.set(e,t);let n=this.statusListeners.get(e);if(n)for(let e of n)e(`ready`)}unregisterInProcessBackend(e){this.inProcessHandlers.delete(e)}resolveServerForPlugin(e){if(this.inProcessHandlers.has(e))return{ok:!0};let t=this.deps.getActivePlugins().find(t=>t.id===e);if(!t)return{ok:!1,reason:`plugin "${e}" not found or not active`};if(!t.manifest.capabilities?.includes(`panel.backend`))return{ok:!1,reason:`plugin "${e}" does not declare capabilities:["panel.backend"]`};let n=N(t);if(n.pendingCapabilities.includes(`panel.backend`))return{ok:!1,reason:`plugin "${e}" capability "panel.backend" declared but not yet granted`};if(n.gated)return{ok:!1,reason:`plugin "${e}" is untrusted`};let r=this.deps.mcpController.listEffectiveServers(),i=Object.entries(r).filter(([,t])=>t.pluginId===e);return i.length===0?{ok:!1,reason:`plugin "${e}" has no associated MCP server`}:(i.length>1&&Pc.warn({pluginId:e,serverNames:i.map(([e])=>e)},`plugin contributes multiple MCP servers; panel backend uses the first match (multi-server routing not yet supported)`),{ok:!0,serverName:i[0][0]})}async call(e,t,n,r){let i=this.inProcessHandlers.get(e);if(i)return i(t,n);let a=this.resolveServerForPlugin(e);if(!a.ok||!a.serverName)throw Error(a.reason??`cannot resolve backend server for plugin "${e}"`);let o=this.deps.mcpController.getMcpClient(a.serverName);if(!o)throw Error(`MCP server "${a.serverName}" for plugin "${e}" is not connected`);let s=r?.operationId?{...n,__operationId:r.operationId}:n;return o.callRaw(t,s,r?.signal)}async cancel(e,t){if(this.inProcessHandlers.has(e))return;let n=this.resolveServerForPlugin(e);if(!n.ok||!n.serverName)return;let r=this.deps.mcpController.getMcpClient(n.serverName);r&&await r.callRaw(`backend/cancel`,{id:t}).catch(()=>void 0)}subscribeState(e,t){return this.ensureNotificationSubscription(),this.stateListeners.has(e)||this.stateListeners.set(e,new Set),this.stateListeners.get(e).add(t),()=>{this.stateListeners.get(e)?.delete(t)}}onStatusChange(e,t){return this.ensureNotificationSubscription(),this.statusListeners.has(e)||this.statusListeners.set(e,new Set),this.statusListeners.get(e).add(t),()=>{this.statusListeners.get(e)?.delete(t)}}getStatus(e){if(this.inProcessHandlers.has(e))return`ready`;let t=this.resolveServerForPlugin(e);if(!t.ok||!t.serverName||this.deps.mcpController.listEffectiveServers()[t.serverName]?.disabled)return`disabled`;let n=this.deps.mcpController.getMcpClient(t.serverName);if(!n)return`crashed`;let r=n.getStatus();return r===`ready`?`ready`:r===`connecting`||r===`reconnecting`?`connecting`:`crashed`}notifyReload(){this.ensureNotificationSubscription();for(let[e,t]of this.statusListeners){if(t.size===0)continue;let n=this.getStatus(e);for(let e of t)e(n)}}ensureNotificationSubscription(){let e=this.deps.mcpController.manager;if(this.subscribedManager===e||(this.subscribedManager=e,!e))return;e.on(`server.notification`,({name:e,method:t,params:n})=>{if(t!==`backend/state`)return;let r=this.pluginIdForServer(e);if(!r)return;let i=this.stateListeners.get(r);if(!(!i||i.size===0))for(let e of i)e(n??{})});let t=e=>{let t=this.pluginIdForServer(e);if(!t)return;let n=this.statusListeners.get(t);if(!n||n.size===0)return;let r=this.getStatus(t);for(let e of n)e(r)};e.on(`server.connected`,({name:e})=>t(e)),e.on(`server.disconnected`,({name:e})=>t(e))}pluginIdForServer(e){return this.deps.mcpController.listEffectiveServers()[e]?.pluginId}};const Ic=A(`@x-otto/coding:plugin-log-writer`);function Lc(){return k(r,`plugin-logs`)}function Rc(e){return Gn.test(e)?O(Lc(),`${e}.jsonl`):(Ic.warn({pluginId:e},`plugin id failed kebab-case validation, skipping log path resolution`),null)}function zc(e){let t;try{t=Xe(e).size}catch{return}if(!(t<=5242880))try{let t=C(e,`utf8`),n=Math.floor(t.length/2),r=t.indexOf(`
|
|
15
15
|
`,n);T(e,r>=0?t.slice(r+1):t.slice(n))}catch(t){Ic.warn({path:e,err:String(t)},`plugin log truncation failed (fail-soft)`)}}function Bc(e,t,n){if(!c())try{let r=Rc(e);if(!r)return;let i=Lc();x(i)||S(i,{recursive:!0}),zc(r),ze(r,`${JSON.stringify({ts:Date.now(),level:t,pluginId:e,msg:n})}\n`)}catch(t){Ic.warn({pluginId:e,err:String(t)},`plugin log append failed (fail-soft)`)}}const V=A(`@x-otto/coding:plugin-tool-wiring`);function H(e,t){return`plugin:${e}:${t}`}function Vc(e){let t=e.split(`:`);return t.length<3||t[0]!==`plugin`?null:t[1]||null}function Hc(e){let t=e?.content;return typeof t==`string`?t:Array.isArray(t)?t.map(e=>e&&typeof e==`object`&&`text`in e?String(e.text):``).join(``):``}const U={preToolUse:`tool.execute.before`,postToolUse:`tool.execute.after`,userPromptSubmit:`chat.message.before`,sessionStart:`session.created`,stop:`stream.end`,sessionRestored:`session.restored`,sessionDeleted:`session.deleted`,sessionIdle:`session.idle`,sessionError:`session.error`,streamStart:`stream.start`,compactionPending:`compaction.before`,compactionOccurred:`compaction.after`,taskCreated:`task.created`,taskStarted:`task.started`,taskCompleted:`task.completed`,taskFailed:`task.failed`,taskCancelled:`task.cancelled`,onNotification:`notification`};function Uc(e,t){let n=[];if(t.preToolUse){let r=t.preToolUse;n.push(I({name:H(e,`preToolUse`),timing:U.preToolUse,handle:async(t,n)=>{let i=await r({toolName:t.toolName,input:t.args,paths:t.paths,readonly:t.readonly,sessionId:t.sessionId});i&&(i.decision===`deny`?(n.decision=`deny`,n.cancelled=!0,n.cancelReason=i.reason??`denied by plugin ${e}`):i.decision===`ask`&&(n.decision=`ask`,n.decisionReason=i.reason??`requires approval (plugin ${e})`))}}))}if(t.postToolUse){let r=t.postToolUse;n.push(I({name:H(e,`postToolUse`),timing:U.postToolUse,handle:async e=>{await r({toolName:e.toolName,input:e.args,result:e.result,readonly:e.readonly,sessionId:e.sessionId})}}))}if(t.userPromptSubmit){let r=t.userPromptSubmit;n.push(I({name:H(e,`userPromptSubmit`),timing:U.userPromptSubmit,handle:async e=>{await r({prompt:Hc(e.message)})}}))}if(t.sessionStart){let r=t.sessionStart;n.push(I({name:H(e,`sessionStart`),timing:U.sessionStart,handle:async()=>{await r()}}))}if(t.stop){let r=t.stop;n.push(I({name:H(e,`stop`),timing:U.stop,handle:async()=>{await r()}}))}if(t.sessionRestored){let r=t.sessionRestored;n.push(I({name:H(e,`sessionRestored`),timing:U.sessionRestored,handle:async e=>{await r({sessionId:e.sessionId})}}))}if(t.sessionDeleted){let r=t.sessionDeleted;n.push(I({name:H(e,`sessionDeleted`),timing:U.sessionDeleted,handle:async e=>{await r({sessionId:e.sessionId})}}))}if(t.sessionIdle){let r=t.sessionIdle;n.push(I({name:H(e,`sessionIdle`),timing:U.sessionIdle,handle:async e=>{await r({sessionId:e.sessionId})}}))}if(t.sessionError){let r=t.sessionError;n.push(I({name:H(e,`sessionError`),timing:U.sessionError,handle:async e=>{await r({sessionId:e.sessionId,error:e.error})}}))}if(t.streamStart){let r=t.streamStart;n.push(I({name:H(e,`streamStart`),timing:U.streamStart,handle:async e=>{await r({sessionId:e.sessionId,model:e.model})}}))}if(t.compactionPending){let r=t.compactionPending;n.push(I({name:H(e,`compactionPending`),timing:U.compactionPending,handle:async e=>{await r({sessionId:e.sessionId,messageCount:e.messageCount})}}))}if(t.compactionOccurred){let r=t.compactionOccurred;n.push(I({name:H(e,`compactionOccurred`),timing:U.compactionOccurred,handle:async e=>{await r({sessionId:e.sessionId,retainedCount:e.retainedCount})}}))}if(t.taskCreated){let r=t.taskCreated;n.push(I({name:H(e,`taskCreated`),timing:U.taskCreated,handle:async e=>{await r({taskId:e.task.id})}}))}if(t.taskStarted){let r=t.taskStarted;n.push(I({name:H(e,`taskStarted`),timing:U.taskStarted,handle:async e=>{await r({taskId:e.task.id,agent:e.agent})}}))}if(t.onNotification){let r=t.onNotification;n.push(I({name:H(e,`onNotification`),timing:U.onNotification,handle:async e=>{await r({sessionId:e.sessionId,message:e.message,title:e.title,notification_type:e.notification_type})}}))}if(t.taskCompleted){let r=t.taskCompleted;n.push(I({name:H(e,`taskCompleted`),timing:U.taskCompleted,handle:async e=>{await r({taskId:e.task.id,status:e.task.status,durationMs:e.result.durationMs,tokenUsage:e.result.tokenUsage})}}))}if(t.taskFailed){let r=t.taskFailed;n.push(I({name:H(e,`taskFailed`),timing:U.taskFailed,handle:async e=>{await r({taskId:e.task.id,error:e.error.message})}}))}if(t.taskCancelled){let r=t.taskCancelled;n.push(I({name:H(e,`taskCancelled`),timing:U.taskCancelled,handle:async e=>{await r({taskId:e.taskId})}}))}if(t.waterfall)for(let r of Object.keys(t.waterfall)){if(!qi(r)){V.warn({pluginId:e,timing:r},`plugin waterfall timing not in whitelist, rejected (fail-closed)`),Bc(e,`warn`,`waterfall timing "${r}" not in whitelist, rejected (fail-closed)`);continue}let i=t.waterfall[r];i&&n.push(I({name:H(e,`waterfall:${r}`),timing:r,handle:async(t,n)=>r===`system.prompt.transform`?Wc(e,i,t,n):Gc(e,r,i,t,n)}))}return n}async function Wc(e,t,n,r){let i=r.systemTail?[...r.systemTail]:[],a=r.systemPrompt,o=await t(n,r),s=!1,c=!1,l=!1;r.systemPrompt!==a&&(s=!0,r.systemPrompt=a);let u=Array.isArray(r.systemTail)?r.systemTail:void 0;if(u===void 0)i.length>0&&(c=!0,r.systemTail=[...i]);else if(!i.every((e,t)=>u[t]===e))c=!0,r.systemTail=[...i];else{let e=u.slice(i.length),t=[...i],n=4096;for(let r of e){if(n<=0){l=!0;break}let e=qc(r,n);if(e.truncated){l=!0,t.push(`${e.content}${Jc}`);break}t.push(e.content),n-=Buffer.byteLength(e.content,`utf8`)}r.systemTail=t}if(s||c||l||Yc(i,r.systemTail)){let t=(r.systemTail?.length??0)-i.length,a=[s?`systemPrompt-write-rejected`:null,c?`systemTail-destructive-rejected`:null,t>0?`systemTail:+${t}el`:null,l?`systemTail-truncated`:null].filter(e=>e!==null).join(`, `);Kc(e,`system.prompt.transform`,n.sessionId,a)}return o}async function Gc(e,t,n,r,i){let a=Xc(i),o=await n(r,i),s=Zc(t,a,i);return s&&Kc(e,t,(typeof r==`object`&&r&&`sessionId`in r?String(r.sessionId??``):void 0)||void 0,s),o}function Kc(e,t,n,r){let i=r.length>400?`${r.slice(0,400)}…`:r;V.info({pluginId:e,timing:t,...n?{sessionId:n}:{}},`plugin waterfall rewrite: ${i}`)}function qc(e,t){let n=Buffer.from(e,`utf8`);if(n.length<=t)return{content:e,truncated:!1};let r=Math.max(0,t);for(;r>0&&r<n.length;){let e=n[r];if(e===void 0||(e&192)!=128)break;r--}return{content:n.subarray(0,r).toString(`utf8`).trimEnd(),truncated:!0}}const Jc=`
|