aws-runtime-bridge 1.9.141 → 1.9.143

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.
@@ -1,16 +1,16 @@
1
- import{spawn as _}from"node:child_process";import{cpSync as F,existsSync as w,lstatSync as ie,mkdirSync as D,readFileSync as k,readdirSync as j,renameSync as H,rmSync as E,symlinkSync as se,writeFileSync as U}from"node:fs";import{dirname as J,join as m,resolve as Z}from"node:path";import{fileURLToPath as oe,pathToFileURL as ae}from"node:url";import{parseAcodeCommandMarkdown as le,splitQualifiedToolName as ue}from"@agentswork/acode";import{getBridgeInstancePluginsDir as ce,getBridgeTemplatePluginsDir as de,getBridgeVersion as fe}from"../config.js";import{sanitizeAgentId as N,withFileLock as W,downloadPluginZip as ge,sanitizeArchiveFileName as pe}from"../utils/file-utils.js";import{createLogger as me}from"../utils/logger.js";import{setDeployedDefaultsProvider as he,reconcileDefaultsFromDeployed as ye}from"./capability-config.js";import{isCapabilityEnabled as Se,getAgentPluginSources as we}from"./instance-features.js";import{PluginEventBuffer as ve}from"./plugin-event-buffer.js";import{extractPluginZip as x}from"./plugin-package.js";const f=me("plugin-manager"),$="plugin-manager",X="\u7CFB\u7EDF\u5185\u7F6E\uFF1A\u7BA1\u7406\u63D2\u4EF6\u7684\u751F\u547D\u5468\u671F\uFF08\u5B89\u88C5/\u5378\u8F7D/\u542F\u505C/\u91CD\u8F7D\uFF09\uFF1B\u5F00\u53D1\u63D2\u4EF6\u7528\u5185\u7F6E /create-plugin \u547D\u4EE4",Q=`\u300C\u63D2\u4EF6\u7BA1\u7406\u63D2\u4EF6\u300D\u7684\u751F\u547D\u5468\u671F\u5DE5\u5177\uFF1A\u7BA1\u7406\u63D2\u4EF6\uFF08\u5B89\u88C5/\u5378\u8F7D/\u91CD\u8F7D/\u542F\u505C/\u5217\u8868\uFF09\u3002\u4EC5\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u5B89\u88C5\u3001\u79FB\u9664\u6216\u4FEE\u590D\u63D2\u4EF6\u65F6\u4F7F\u7528\u3002
1
+ import{spawn as _}from"node:child_process";import{cpSync as F,existsSync as w,lstatSync as ie,mkdirSync as T,readFileSync as k,readdirSync as x,renameSync as j,rmSync as E,symlinkSync as se,writeFileSync as H}from"node:fs";import{dirname as U,join as m,resolve as Z}from"node:path";import{fileURLToPath as oe,pathToFileURL as ae}from"node:url";import{parseAcodeCommandMarkdown as le,splitQualifiedToolName as ue}from"@agentswork/acode";import{getBridgeInstancePluginsDir as ce,getBridgeTemplatePluginsDir as de,getBridgeVersion as fe}from"../config.js";import{sanitizeAgentId as N,withFileLock as W,downloadPluginZip as ge,sanitizeArchiveFileName as pe}from"../utils/file-utils.js";import{createLogger as he}from"../utils/logger.js";import{setDeployedDefaultsProvider as me,reconcileDefaultsFromDeployed as ye}from"./capability-config.js";import{isCapabilityEnabled as Se,getAgentPluginSources as we}from"./instance-features.js";import{PluginEventBuffer as ve}from"./plugin-event-buffer.js";import{extractPluginZip as q}from"./plugin-package.js";const f=he("plugin-manager"),$="plugin-manager",X="\u7CFB\u7EDF\u5185\u7F6E\uFF1A\u7BA1\u7406\u63D2\u4EF6\u7684\u751F\u547D\u5468\u671F\uFF08\u5B89\u88C5/\u5378\u8F7D/\u542F\u505C/\u91CD\u8F7D\uFF09\uFF1B\u5F00\u53D1\u63D2\u4EF6\u7528\u5185\u7F6E /create-plugin \u547D\u4EE4",Q=`\u300C\u63D2\u4EF6\u7BA1\u7406\u63D2\u4EF6\u300D\u7684\u751F\u547D\u5468\u671F\u5DE5\u5177\uFF1A\u7BA1\u7406\u63D2\u4EF6\uFF08\u5B89\u88C5/\u5378\u8F7D/\u91CD\u8F7D/\u542F\u505C/\u5217\u8868\uFF09\u3002\u4EC5\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u5B89\u88C5\u3001\u79FB\u9664\u6216\u4FEE\u590D\u63D2\u4EF6\u65F6\u4F7F\u7528\u3002
2
2
  - action=list\uFF1A\u5217\u51FA\u5DF2\u5B89\u88C5\u63D2\u4EF6\uFF08\u72B6\u6001\u3001\u76D1\u542C\u5668\u5065\u5EB7\u3001\u5DE5\u5177\u6570\uFF09\u3002
3
3
  - action=install\uFF1Asource \u4E3A\u63D2\u4EF6\u6765\u6E90\uFF0C\u5F53\u524D\u652F\u6301 "local:<\u4EFB\u610F\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>"\uFF08\u4E0D\u505A\u76EE\u5F55\u4F4D\u7F6E\u9650\u5236\uFF09\uFF1Bconfig \u4F20\u63D2\u4EF6 manifest \u91CC userConfig \u58F0\u660E\u7684\u914D\u7F6E\u503C\u3002\u5B89\u88C5\u65F6\u4F1A\u5148\u628A\u8BE5\u76EE\u5F55\u79FB\u52A8\u5230\u672C agent \u52A0\u8F7D\u63D2\u4EF6\u7684\u4F4D\u7F6E\uFF0C\u518D\u52A0\u8F7D\u79FB\u52A8\u540E\u7684\u526F\u672C\uFF08\u79FB\u52A8\u540E\u6E90\u76EE\u5F55\u4E0D\u518D\u4FDD\u7559\uFF09\uFF0C\u5B89\u88C5\u540E\u7ACB\u5373\u751F\u6548\uFF1A\u4E0B\u4E00\u6B21 poll_message \u7684 pluginCatalog \u5373\u53EF\u89C1\u3002
4
4
  - action=reload\uFF1A\u91CD\u8F7D\u6307\u5B9A\u63D2\u4EF6\uFF08\u6539\u8FC7\u63D2\u4EF6\u6587\u4EF6\u540E\u4F7F\u7528\uFF09\uFF0C\u539F\u5B50\u6362\u88C5\uFF0C\u76D1\u542C\u5668\u91CD\u542F\u3002
5
5
  - action=enable / disable\uFF1A\u505C\u7528/\u542F\u7528\uFF08disable \u4FDD\u7559\u5B89\u88C5\u4E0E\u914D\u7F6E\uFF0C\u4EC5\u505C\u6B62\u76D1\u542C\u4E0E\u5DE5\u5177\u8C03\u7528\uFF09\u3002
6
- - action=uninstall\uFF1A\u5378\u8F7D\u5E76\u5220\u9664\u63D2\u4EF6\u76EE\u5F55\uFF08\u4E0D\u53EF\u9006\uFF0C\u9700\u7528\u6237\u660E\u786E\u8981\u6C42\uFF09\u3002`,Pe={type:"object",properties:{action:{type:"string",enum:["install","uninstall","reload","enable","disable","list"],description:"\u751F\u547D\u5468\u671F\u52A8\u4F5C"},name:{type:"string",description:"\u63D2\u4EF6\u540D\uFF08uninstall/reload/enable/disable \u5FC5\u586B\uFF09"},source:{type:"string",description:'install \u6765\u6E90\uFF0C\u5F53\u524D\u652F\u6301 "local:<\u4EFB\u610F\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>"\uFF08\u5B89\u88C5\u65F6\u81EA\u52A8\u79FB\u52A8\u5230\u672C agent \u63D2\u4EF6\u4F4D\u7F6E\u540E\u52A0\u8F7D\uFF09'},config:{type:"object",description:"install \u65F6\u63D2\u4EF6 userConfig \u58F0\u660E\u7684\u914D\u7F6E\u503C\uFF08\u952E\u503C\u5BF9\uFF09",additionalProperties:{type:"string"}}},required:["action"],additionalProperties:!1},$e=new Set(["install","uninstall","reload","enable","disable","list"]);function ee(){const l=fe();return l&&l!=="unknown"?l:void 0}function Ee(){return oe(new URL("./builtin-commands/",import.meta.url))}const ke=200,R=256*1024,Ae=4*1024,te=6e4,q=6e4,be=/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/,z=/^[a-z0-9][a-z0-9-]*$/,De=["system.","aws-","acode-"],Le=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_REPL_HISTORY","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PYTHONHOME","PERL5LIB","PERL5OPT","RUBYOPT","RUBYLIB","BASH_ENV","ENV","SHELLOPTS","BASHOPTS","IFS","PS1","PS4","GIT_SSH","GIT_SSH_COMMAND","GIT_CONFIG","GIT_CONFIG_GLOBAL","PATH","HOME","USERPROFILE","SYSTEMROOT","COMSPEC","TEMP","TMP","LANG","TZ"]),Oe=/^[A-Za-z_][A-Za-z0-9_]*$/,Te=process.platform==="win32"?"junction":"dir",Ne=/[^\x20-\x7E]/,Ie=6e4,G=20,ne=80;function P(l){return typeof l=="object"&&l!==null&&!Array.isArray(l)}function I(l,e){let t=l;for(const n of e.split(".")){if(!P(t))return;t=t[n]}return t}function A(l,e){return l.length<=e?l:`${l.slice(0,e)}\u2026(\u622A\u65AD,\u5171 ${l.length} \u5B57\u7B26)`}async function C(l,e=8,t=200){for(let n=0;;n++)try{E(l,{recursive:!0,force:!0});return}catch(r){if(n>=e-1)throw r;await new Promise(i=>setTimeout(i,t))}}async function re(l,e){D(J(e),{recursive:!0});try{H(l,e);return}catch(t){const n=t?.code;if(!n||!["EXDEV","EPERM","EACCES","EBUSY","EINVAL"].includes(n))throw t}F(l,e,{recursive:!0,filter:t=>!/[\\/]\.git([\\/]|$)/.test(t)&&!/[\\/]node_modules([\\/]|$)/.test(t)}),await C(l)}function b(l,e){l.logs.push(`${new Date().toISOString()} ${e}`),l.logs.length>ke&&l.logs.shift()}function B(l,e,t="params"){if(!P(l))return[];const n=[],r=l.type;if(typeof r=="string"&&!Ce(r,e))return[`${t}: \u671F\u671B ${r}\uFF0C\u5B9E\u9645 ${Re(e)}`];if(Array.isArray(l.enum)&&l.enum.length>0&&(l.enum.some(s=>JSON.stringify(s)===JSON.stringify(e))||n.push(`${t}: \u503C ${JSON.stringify(e)} \u4E0D\u5728\u679A\u4E3E ${JSON.stringify(l.enum)} \u5185`)),r==="object"&&P(e)){const i=P(l.properties)?l.properties:{},s=Array.isArray(l.required)?l.required:[];for(const o of s)(!(o in e)||e[o]===void 0)&&n.push(`${t}.${o}: \u5FC5\u586B\u53C2\u6570\u7F3A\u5931`);for(const[o,a]of Object.entries(i))o in e&&e[o]!==void 0&&n.push(...B(a,e[o],`${t}.${o}`));if(l.additionalProperties===!1)for(const o of Object.keys(e))o in i||n.push(`${t}.${o}: \u4E0D\u5141\u8BB8\u7684\u672A\u77E5\u53C2\u6570\uFF08schema additionalProperties=false\uFF09`)}return r==="array"&&Array.isArray(e)&&P(l.items)&&e.forEach((i,s)=>{n.push(...B(l.items,i,`${t}[${s}]`))}),n}function Ce(l,e){switch(l){case"string":return typeof e=="string";case"number":return typeof e=="number"&&Number.isFinite(e);case"boolean":return typeof e=="boolean";case"object":return P(e);case"array":return Array.isArray(e);case"null":return e===null;default:return!0}}function Re(l){return l===null?"null":Array.isArray(l)?"array":typeof l}class Me{constructor(e={}){this.agents=new Map,this.moduleNonce=0,this.templatePullInFlight=new Set,this.backgroundTemplatePulls=new Set,this.rootDir=e.rootDir??de(),this.runtimeRootDir=e.runtimeRootDir}forAgent(e){return{listPlugins:()=>this.list(e),getCatalog:()=>this.getCatalog(e),getToolSchema:t=>this.getToolSchema(e,t),invokeTool:(t,n,r,i)=>this.invokeTool(e,t,n,r,i),manage:t=>this.manage(e,t),getCommandDirs:()=>this.getCommandDirs(e)}}setOnPluginsChanged(e){this.onPluginsChanged=e}notifyPluginsChanged(e){try{this.onPluginsChanged?.(e)}catch(t){f.warn("[agent:%s] onPluginsChanged \u56DE\u8C03\u5931\u8D25: %s",e,t instanceof Error?t.message:String(t))}}async getCommandDirs(e){return[...(await this.hydrate(e)).plugins.values()].filter(n=>n.enabled&&n.commandDir).map(n=>n.commandDir)}async getPluginCommands(e){const t=await this.hydrate(e),n=[];n.push(...this.parseCommandDir($,Ee()));for(const r of t.plugins.values())!r.enabled||!r.commandDir||n.push(...this.parsePluginCommandDir(r));return n}parsePluginCommandDir(e){return this.parseCommandDir(e.name,e.commandDir)}parseCommandDir(e,t){const n=[];if(!t)return n;try{for(const r of j(t)){if(!r.endsWith(".md"))continue;const i=m(t,r);try{const s=le(i,k(i,"utf8"));s.plugin=e,n.push(s)}catch(s){f.warn("[plugin:%s] \u547D\u4EE4\u89E3\u6790\u5931\u8D25 %s: %s",e,r,s instanceof Error?s.message:String(s))}}}catch{}return n}async list(e){const t=await this.hydrate(e),n=new Map;for(const r of t.plugins.values()){const i=this.toSummary(r);n.set(i.name,i)}n.has($)||n.set($,{name:$,version:ee(),description:X,status:"enabled",enabled:!0,source:"builtin",installedAt:new Date().toISOString(),toolCount:1,tools:[{name:"plugin-manager.manage",description:"\u63D2\u4EF6\u751F\u547D\u5468\u671F\u7BA1\u7406\uFF08install/uninstall/reload/disable/enable/list\uFF09"}],listeners:[],builtin:!0});for(const r of this.readSharedManifest()){const i=r.pluginName;if(!this.deployedPluginHealthy(i))continue;const o=n.get(i);if(o){String(o.source||"").startsWith("shared:")&&(o.enabled=this.effectiveSharedEnabled(e,r));continue}n.set(i,{name:i,version:this.deployedPluginVersion(i),status:"enabled",enabled:this.effectiveSharedEnabled(e,r),source:`shared:${r.fileName??`${i}.zip`}`,installedAt:new Date().toISOString(),toolCount:0,listeners:[]})}return[...n.values()].sort((r,i)=>r.name.localeCompare(i.name))}deployedPluginVersion(e){try{const t=JSON.parse(k(m(this.deployedPluginDir(e),"plugin.json"),"utf8"));return typeof t.version=="string"?t.version:void 0}catch{return}}async getCatalog(e){const t=await this.hydrate(e),n=[];for(const r of t.plugins.values()){if(!r.enabled||r.status==="error")continue;const i=r.tools.slice(0,G).map(s=>({name:s.name,description:A(s.title||s.description,ne),requiredParams:this.requiredParamsOf(s)}));r.tools.length>G&&i.push({name:`(\u53E6\u6709 ${r.tools.length-G} \u4E2A\u5DE5\u5177\u672A\u5217\u51FA)`,description:'\u7528 function_call {action:"catalog"} \u67E5\u770B\u5168\u91CF',requiredParams:void 0}),n.push({plugin:r.name,version:r.version,description:r.description,status:r.status,tools:i})}return n.push({plugin:$,version:ee(),description:X,status:"enabled",tools:[{name:"manage",description:A(Q,ne),requiredParams:["action"]}]}),{revision:t.catalogRevision,plugins:n}}async getToolSchema(e,t){const n=ue(t);if(!n)return null;if(n.plugin===$&&n.tool==="manage")return{plugin:$,name:"manage",description:Q,inputSchema:Pe};const i=(await this.hydrate(e)).plugins.get(n.plugin);if(!i||!i.enabled)return null;const s=i.tools.find(o=>o.name===n.tool);return s?{plugin:i.name,name:s.name,title:s.title,description:s.description,inputSchema:s.inputSchema??{type:"object",properties:{}}}:null}async invokeTool(e,t,n,r,i){if(t===$&&n==="manage"){const u=this.buildManageRequest(r);if(!u)return{ok:!1,error:"plugin-manager.manage \u7F3A\u5C11 action"};const c=await this.manage(e,u);if(!c.ok)return{ok:!1,error:c.error??"\u63D2\u4EF6\u7BA1\u7406\u64CD\u4F5C\u5931\u8D25"};const d={ok:!0};return c.plugins&&(d.plugins=c.plugins),c.summary&&(d.summary=c.summary),{ok:!0,result:d}}const s=await this.hydrate(e),o=s.plugins.get(t);if(!o)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\uFF08\u53EF\u7528\u63D2\u4EF6: ${[...s.plugins.keys()].join(", ")||"\u65E0"}\uFF09`};if(!o.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u5DF2\u505C\u7528\uFF0C\u53EF\u8BA9\u7528\u6237\u5728\u9762\u677F\u6216\u7528 plugin-manager.manage enable \u542F\u7528`};const a=o.tools.find(u=>u.name===n);if(!a)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5DE5\u5177 ${n}\uFF08\u53EF\u7528: ${o.tools.map(u=>u.name).join(", ")||"\u65E0"}\uFF09`};if(a.inputSchema){const u=B(a.inputSchema,r);if(u.length>0)return{ok:!1,error:`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${u.join("; ")}`,inputSchema:a.inputSchema}}return this.executeHandler(e,o,a,r,i)}buildManageRequest(e){const t=e.action;if(typeof t!="string"||!$e.has(t))return null;const n={action:t};if(typeof e.name=="string"&&e.name.trim()&&(n.name=e.name.trim()),typeof e.source=="string"&&e.source.trim()&&(n.source=e.source.trim()),e.config&&typeof e.config=="object"&&!Array.isArray(e.config)){const r={};for(const[i,s]of Object.entries(e.config))typeof s=="string"&&(r[i]=s);n.config=r}return n}async executeHandler(e,t,n,r,i){const s=Date.now(),o=n.handler??{type:"process"};try{return o.type==="mcp"?{ok:!1,error:"handler.type=mcp \u5C06\u5728\u540E\u7EED\u7248\u672C\u652F\u6301\uFF0C\u8BF7\u6539\u7528 process/module handler"}:o.type==="module"?{ok:!0,result:await this.invokeModuleHandler(t,o,r,e,i),durationMs:Date.now()-s}:!o.command||!o.command.trim()?{ok:!1,error:`\u5DE5\u5177 ${n.name} \u7684 process handler \u7F3A\u5C11 command`}:{ok:!0,result:await this.invokeProcessHandler(t,o,r,e,i),durationMs:Date.now()-s}}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a),durationMs:Date.now()-s}}}async invokeProcessHandler(e,t,n,r,i){const s=Y(t.command??"",{params:n});if(s.length===0)throw new Error("process handler command \u4E3A\u7A7A");const o=typeof t.timeoutMs=="number"&&t.timeoutMs>0?t.timeoutMs:te,a=new AbortController,u=()=>a.abort();i?.signal?.addEventListener("abort",u,{once:!0});const c=setTimeout(()=>a.abort(),o);try{return await new Promise((d,y)=>{f.info("[plugin:%s] spawn: %s",e.name,s.join(" "));const h=_(s[0],s.slice(1),{cwd:e.dir,env:this.buildPluginEnv(e,r),shell:t.shell===!0,windowsHide:!0,signal:a.signal});let g="",p="";h.stdin?.on("error",()=>{}),h.stdout?.on("data",v=>{g.length<R&&(g+=v.toString("utf8"))}),h.stderr?.on("data",v=>{p.length<R&&(p+=v.toString("utf8"))}),h.on("error",v=>{y(new Error(`\u63D2\u4EF6\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${v.message}`))}),h.on("close",v=>{const L=g.trim();if(v!==0){const O=A(p.trim()||L||"(\u65E0\u8F93\u51FA)",600);y(new Error(a.signal.aborted?`\u63D2\u4EF6\u8FDB\u7A0B\u8D85\u65F6(${o}ms)\u6216\u88AB\u4E2D\u6B62`:`\u63D2\u4EF6\u8FDB\u7A0B\u9000\u51FA\u7801 ${v}: ${O}`));return}if(!L){d({ok:!0});return}try{d(JSON.parse(L))}catch{d(L)}});try{t.stdin==="params"?h.stdin?.end(`${JSON.stringify(n)}
7
- `):h.stdin?.end()}catch{}})}finally{clearTimeout(c),i?.signal?.removeEventListener("abort",u)}}async invokeModuleHandler(e,t,n,r,i){if(!t.module)throw new Error("module handler \u7F3A\u5C11 module \u8DEF\u5F84");const s=m(e.dir,t.module);let o=e.moduleCache.get(s);o||(o=await import(`${ae(s).href}?acode_nonce=${e.importNonce}`),e.moduleCache.set(s,o));const a=t.export||"default",c=o[a];if(typeof c!="function")throw new Error(`\u6A21\u5757 ${t.module} \u672A\u5BFC\u51FA\u51FD\u6570 ${a}`);const d=typeof t.timeoutMs=="number"&&t.timeoutMs>0?t.timeoutMs:te;let y;const h=Promise.resolve().then(()=>c(n,{agentId:r,signal:i?.signal,pluginRoot:e.dir}));try{return await Promise.race([h,new Promise((g,p)=>{y=setTimeout(()=>p(new Error(`\u63D2\u4EF6\u6A21\u5757\u6267\u884C\u8D85\u65F6(${d}ms)`)),d)})])}finally{clearTimeout(y)}}buildPluginEnv(e,t){const n={};for(const i of["PATH","HOME","USERPROFILE","SystemRoot","COMSPEC","TEMP","TMP","LANG","TZ"]){const s=process.env[i];s!==void 0&&(n[i]=s)}n.ACODE_PLUGIN_ROOT=e.dir,n.ACODE_AGENT_ID=t,n.ACODE_PLUGIN_NAME=e.name;const r=new Set((e.userConfig??[]).map(i=>i.key));for(const[i,s]of Object.entries(e.configValues))r.has(i)&&(!Oe.test(i)||Le.has(i.toUpperCase())||(n[i]=s));return n}async manage(e,t){const n=this.agentState(e),r=n.queue.then(async()=>{const i=await this.hydrate(e);return this.doManage(i,t)});return n.queue=r.catch(()=>{}),r}async doManage(e,t){if(t.name===$&&(t.action==="uninstall"||t.action==="disable"||t.action==="enable"||t.action==="reload"))return{ok:!1,error:`\u5185\u7F6E\u7CFB\u7EDF\u63D2\u4EF6 ${$} \u4E0D\u53EF${t.action==="uninstall"?"\u5378\u8F7D":t.action==="reload"?"\u91CD\u8F7D":"\u505C\u7528/\u542F\u7528"}`};switch(t.action){case"list":return{ok:!0,plugins:await this.list(e.agentId)};case"install":return this.installPlugin(e,t);case"uninstall":return this.uninstallPlugin(e,t);case"reload":return this.reloadPlugin(e,t);case"enable":case"disable":return this.setEnabled(e,t,t.action==="enable");default:return{ok:!1,error:`\u672A\u77E5 action: ${String(t.action)}`}}}async installPlugin(e,t){const n=(t.source??"").trim();if(!n)return{ok:!1,error:"install \u9700\u8981 source\uFF08\u5F53\u524D\u652F\u6301 local:<\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>\uFF09"};const r=n.split(":",1)[0];if(r!=="local")return{ok:!1,error:`\u6682\u4E0D\u652F\u6301\u63D2\u4EF6\u6765\u6E90 "${r}:"\uFF08npm:/git: \u5728\u8DEF\u7EBF\u56FE\u4E2D\uFF1Bzip \u5305\u8BF7\u7528\u9762\u677F\u5BFC\u5165\u6216 POST /runtime/plugins/:agentId/upload\uFF09`};const i=n.slice(6).trim();return!i||!w(m(i,"plugin.json"))?{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11 plugin.json: ${i}`}:this.installFromDirectory(e,i,n,t.config)}async installFromZip(e,t,n,r){if(!t||t.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};const i=this.agentState(e),s=i.queue.then(async()=>{await this.hydrate(e);const o=m(this.rootDir,"tmp",`upload-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const a=await x(t,o);return w(m(a.pluginDir,"plugin.json"))?(f.info("[agent:%s] zip \u5BFC\u5165: %s\uFF08%d \u4E2A\u6587\u4EF6\uFF09",e,n,a.fileCount),await this.installFromDirectory(i,a.pluginDir,`upload:${n}`,r)):{ok:!1,error:"zip \u5185\u672A\u627E\u5230 plugin.json\uFF08\u8BF7\u6253\u5305\u63D2\u4EF6\u76EE\u5F55\uFF0C\u4E14 zip \u5185\u8DEF\u5F84\u4F7F\u7528\u82F1\u6587\uFF09"}}catch(a){const u=a instanceof Error?a.message:String(a);return f.warn("[agent:%s] zip \u5BFC\u5165\u5931\u8D25: %s",e,u),{ok:!1,error:`zip \u5BFC\u5165\u5931\u8D25: ${u}`}}finally{await C(o,3,100).catch(()=>{})}});return i.queue=s.catch(()=>{}),s}async installFromDirectory(e,t,n,r){const i=this.loadManifestOnly(t);if(!i.ok)return i;const s=i.manifest,o=s.name;if(o===$)return{ok:!1,error:`\u63D2\u4EF6\u540D ${o} \u4E3A\u7CFB\u7EDF\u4FDD\u7559\u540D\u79F0\uFF0C\u4E0D\u5141\u8BB8\u5B89\u88C5\u540C\u540D\u63D2\u4EF6`};const a=this.validateUserConfigValues(s,r??{});if(a)return{ok:!1,error:a};const u=this.loadPluginFromDir(e,t,n,new Date().toISOString(),r??{});if(!u.ok)return f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u6821\u9A8C\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,u.error),u;const c=m(this.rootDir,"cache",N(e.agentId),o),d=t===c,y=e.plugins.get(o);if(y&&this.stopAllListeners(y),!d)try{await C(c),await re(t,c)}catch(p){const v=p instanceof Error?p.message:String(p);return f.warn("[agent:%s] \u63D2\u4EF6\u79FB\u52A8\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,v),y&&this.startAllListeners(e,y),{ok:!1,error:`\u63D2\u4EF6\u79FB\u52A8\u5931\u8D25: ${v}`}}const h=this.loadPluginFromDir(e,c,n,new Date().toISOString(),r??{});if(!h.ok){if(f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,h.error),!d)try{await re(c,t)}catch(p){f.warn("[agent:%s] \u88C5\u8F7D\u5931\u8D25\u540E\u65E0\u6CD5\u79FB\u56DE\u6E90\u76EE\u5F55\uFF08%s\uFF09\uFF0C\u5DF2\u4FDD\u7559\u7F13\u5B58\u526F\u672C",e.agentId,p instanceof Error?p.message:String(p))}if(y)try{this.startAllListeners(e,y)}catch(p){f.warn("[agent:%s] \u6062\u590D\u65E7\u5B9E\u4F8B\u76D1\u542C\u5931\u8D25: %s",e.agentId,p instanceof Error?p.message:String(p))}return h}const g=h.plugin;return e.plugins.set(g.name,g),this.bumpCatalogRevision(e),this.persistInstalled(e),this.startAllListeners(e,g),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5B89\u88C5: %s@%s",e.agentId,g.name,g.version??"?"),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(g)}}async uninstallPlugin(e,t){const n=(t.name??"").trim(),r=e.plugins.get(n);return r?(this.stopAllListeners(r),e.plugins.delete(n),this.bumpCatalogRevision(e),this.readSharedManifest().some(s=>s.pluginName===n)&&this.removeRuntimeLink(e.agentId,n),this.persistInstalled(e),await C(r.dir),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5378\u8F7D: %s",e.agentId,n),this.notifyPluginsChanged(e.agentId),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`}}async reloadPlugin(e,t){const n=(t.name??"").trim(),r=e.plugins.get(n);if(!r)return{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`};const i=this.loadPluginFromDir(e,r.dir,r.source,r.installedAt,r.configValues);if(!i.ok)return{ok:!1,error:`\u91CD\u8F7D\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u7248\u672C\u8FD0\u884C\uFF09: ${i.error}`};const s=i.plugin;return this.stopAllListeners(r),e.plugins.set(n,s),this.bumpCatalogRevision(e),this.persistInstalled(e),this.startAllListeners(e,s),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u91CD\u8F7D: %s",e.agentId,n),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(s)}}async setEnabled(e,t,n){const r=(t.name??"").trim(),i=e.plugins.get(r);return i?(i.enabled=n,this.bumpCatalogRevision(e),n?this.startAllListeners(e,i):this.stopAllListeners(i),this.persistInstalled(e),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(i)}):{ok:!1,error:`\u63D2\u4EF6 ${r} \u672A\u5B89\u88C5`}}deployedDir(){return m(this.rootDir,"deployed")}deployedManifestFile(){return m(this.deployedDir(),"manifest.json")}deployedPluginDir(e){if(!V(e))throw new Error(`\u975E\u6CD5\u63D2\u4EF6\u540D\uFF08\u4EC5\u5141\u8BB8 ${z}\uFF09: ${JSON.stringify(e)}`);const t=m(this.deployedDir(),e),n=Z(this.deployedDir());if(Z(t)!==m(n,e))throw new Error(`\u63D2\u4EF6\u76EE\u5F55\u8D8A\u51FA\u90E8\u7F72\u533A: ${e}`);return t}legacySharedDir(){return m(this.rootDir,"shared")}readSharedManifest(){try{const e=JSON.parse(k(this.deployedManifestFile(),"utf8"));return Array.isArray(e.plugins)?e.plugins.filter(t=>t&&V(t.pluginName)):[]}catch{return[]}}writeSharedManifest(e){D(this.deployedDir(),{recursive:!0}),U(this.deployedManifestFile(),`${JSON.stringify({version:2,plugins:e},null,2)}
8
- `,"utf8")}async migrateLegacySharedIfNeeded(){try{if(w(this.deployedManifestFile()))return;const e=m(this.legacySharedDir(),"manifest.json");if(!w(e))return;const t=JSON.parse(k(e,"utf8")),n=Array.isArray(t.plugins)?t.plugins:[];if(n.length===0)return;const r=[];for(const i of n){const s=m(this.legacySharedDir(),i.zipFile);if(!(!i.pluginName||!w(s)))try{const o=await this.deployPluginZipToPersistentDir(k(s),i.pluginName);if(!o)continue;r.push(o),f.info("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u5230\u90E8\u7F72\u533A: %s",i.pluginName)}catch(o){f.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6 %s \u8FC1\u79FB\u5931\u8D25: %s",i.pluginName,o instanceof Error?o.message:String(o))}}r.length>0&&this.writeSharedManifest(r),E(this.legacySharedDir(),{recursive:!0,force:!0})}catch(e){f.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u672A\u6267\u884C\uFF08\u4E0B\u6B21\u91CD\u8BD5\uFF09: %s",e instanceof Error?e.message:String(e))}}deployedPluginHealthy(e){return w(m(this.deployedPluginDir(e),"plugin.json"))}runtimeDirFor(e){return this.runtimeRootDir?m(this.runtimeRootDir,N(e)):ce(e)}runtimeLinkPath(e,t){return m(this.runtimeDirFor(e),t)}ensureRuntimeLink(e,t){const n=this.runtimeLinkPath(e,t),r=this.deployedPluginDir(t);if(!w(m(r,"plugin.json")))return!1;D(this.runtimeDirFor(e),{recursive:!0});try{if(w(n)){let i=!1;try{i=ie(n).isSymbolicLink()}catch{i=!1}if(i)try{return j(n),!0}catch{}E(n,{recursive:!0,force:!0})}return se(r,n,Te),f.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u5EFA\u7ACB: %s \u2192 %s",e,n,r),!0}catch(i){return f.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5931\u8D25\uFF08%s \u2192 %s\uFF09: %s",e,t,r,i instanceof Error?i.message:String(i)),!1}}removeRuntimeLink(e,t){const n=this.runtimeLinkPath(e,t);try{w(n)&&(E(n,{recursive:!0,force:!0}),f.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u79FB\u9664: %s",e,t))}catch(r){f.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u79FB\u9664\u5931\u8D25\uFF08%s\uFF09: %s",e,t,r instanceof Error?r.message:String(r))}}async saveSharedPlugin(e,t,n=!0,r){if(!e||e.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};await this.migrateLegacySharedIfNeeded();const i=m(this.rootDir,"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const s=await x(e,i),o=this.loadManifestOnly(s.pluginDir);if(!o.ok)return o;const a=o.manifest.name,u=this.readSharedManifest().find(g=>g.pluginName===a);if(u&&u.sha256===r&&this.deployedPluginHealthy(a))return{ok:!0,pluginName:a};await this.suspendDeployedPluginAcrossAgents(a),D(this.deployedDir(),{recursive:!0});const c=this.deployedPluginDir(a),d=m(this.deployedDir(),`.old-${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);if(w(c))try{H(c,d)}catch{E(c,{recursive:!0,force:!0})}try{F(s.pluginDir,c,{recursive:!0,filter:g=>!/[\\/]\.git([\\/]|$)/.test(g)&&!/[\\/]node_modules([\\/]|$)/.test(g)})}catch(g){throw w(d)&&(E(c,{recursive:!0,force:!0}),H(d,c)),g}w(d)&&E(d,{recursive:!0,force:!0}),await W(this.deployedManifestFile(),async()=>{const g=this.readSharedManifest().filter(p=>p.pluginName!==a);g.push({pluginName:a,zipFile:u?.zipFile??`${a}.zip`,fileName:t,defaultEnabled:n,sha256:r}),this.writeSharedManifest(g)}),f.info("[shared] \u63D2\u4EF6\u5DF2\u90E8\u7F72\u5230 bridge: %s\uFF08\u6765\u6E90 %s%s\uFF09",a,t,r?`, sha256=${r.slice(0,12)}\u2026`:"");const h=(await this.applySharedToKnownAgents()).find(g=>g.installed.includes(a));return h&&f.info("[shared] \u5DF2\u540C\u6B65\u5230\u8FD0\u884C\u4E2D agent: %s",h.agentId),{ok:!0,pluginName:a}}catch(s){const o=s instanceof Error?s.message:String(s);return f.warn("[shared] \u63D2\u4EF6\u90E8\u7F72\u5931\u8D25\uFF08%s\uFF09: %s",t,o),{ok:!1,error:`\u63D2\u4EF6\u90E8\u7F72\u5931\u8D25: ${o}`}}finally{await C(i,3,100).catch(()=>{})}}async deployPluginZipToPersistentDir(e,t){if(!e||e.length===0)return null;const n=m(this.rootDir,"tmp",`migrate-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const r=await x(e,n),i=this.loadManifestOnly(r.pluginDir);if(!i.ok)return null;const s=i.manifest.name;if(t&&s!==t)return null;D(this.deployedDir(),{recursive:!0});const o=this.deployedPluginDir(s);return w(o)&&E(o,{recursive:!0,force:!0}),F(r.pluginDir,o,{recursive:!0,filter:a=>!/[\\/]\.git([\\/]|$)/.test(a)&&!/[\\/]node_modules([\\/]|$)/.test(a)}),{pluginName:s,zipFile:`${s}.zip`,fileName:`${s}.zip`,defaultEnabled:!0}}finally{E(n,{recursive:!0,force:!0})}}async suspendDeployedPluginAcrossAgents(e){for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const n=t.queue.then(async()=>{const r=t.plugins.get(e);r&&(this.stopAllListeners(r),t.plugins.delete(e),this.bumpCatalogRevision(t),f.info("[agent:%s] \u5DF2\u6458\u9664\u63D2\u4EF6\u7B49\u5F85\u90E8\u7F72\u533A\u66F4\u65B0: %s",t.agentId,e))});t.queue=n.catch(()=>{}),await n}}async applySharedToKnownAgents(){const e=[];for(const t of this.agents.values()){if(!t.hydrated)continue;const n=await this.syncSharedIntoStateQueued(t);n.length>0&&e.push({agentId:t.agentId,installed:n})}return e}async syncSharedIntoStateQueued(e){const t=e.queue.then(()=>this.syncSharedIntoState(e));return e.queue=t.catch(()=>[]),t}effectiveSharedEnabled(e,t){return Se(e,"plugins",t.pluginName)&&(t.defaultEnabled??!0)}async pullTemplatePluginsFromCapabilities(e){const t=we(e.agentId);if(!t)return;const n=this.readSharedManifest();for(const[r,i]of Object.entries(t)){if(!i.url)continue;const s=n.find(a=>a.pluginName===r);if(!s||!s.sha256||s.sha256===i.sha256&&this.deployedPluginHealthy(r)||this.templatePullInFlight.has(r))continue;this.templatePullInFlight.add(r);const o=m(this.rootDir,"tmp",`pull-${N(e.agentId)}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{D(o,{recursive:!0});let a=`${r}.zip`;try{const y=decodeURIComponent(i.url.split("/").pop()||"");y.toLowerCase().endsWith(".zip")&&(a=pe(y,`${r}.zip`))}catch{}const u=await ge({downloadUrl:i.url,fileName:a,sha256:i.sha256},o);if(!u)continue;const c=k(u.outputPath),d=await this.saveSharedPlugin(c,u.fileName,s.defaultEnabled??!0,i.sha256);d.ok?f.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u81EA\u52A8\u5BF9\u8D26\u66F4\u65B0\u6210\u529F: %s",e.agentId,d.pluginName??r):f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: %s: %s",e.agentId,r,d.error)}catch(a){f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6 %s \u66F4\u65B0\u5931\u8D25: %s",e.agentId,r,a instanceof Error?a.message:String(a))}finally{this.templatePullInFlight.delete(r),await C(o,3,100).catch(()=>{})}}}queueTemplatePull(e){this.backgroundTemplatePulls.has(e.agentId)||(this.backgroundTemplatePulls.add(e.agentId),this.pullTemplatePluginsFromCapabilities(e).catch(t=>{f.warn("[agent:%s] \u540E\u53F0\u63D2\u4EF6\u5BF9\u8D26\u66F4\u65B0\u5931\u8D25: %s",e.agentId,t instanceof Error?t.message:String(t))}).finally(()=>{this.backgroundTemplatePulls.delete(e.agentId)}))}async syncSharedIntoState(e){this.queueTemplatePull(e);const t=this.readSharedManifest(),n=new Set,r=[];let i=0;for(const s of t)if(n.add(s.pluginName),this.effectiveSharedEnabled(e.agentId,s)){const a=e.plugins.get(s.pluginName);if(!this.ensureRuntimeLink(e.agentId,s.pluginName))continue;const u=this.runtimeLinkPath(e.agentId,s.pluginName);if(!a){const c=this.loadPluginFromDir(e,u,`shared:${s.fileName}`,new Date().toISOString(),{});c.ok?(e.plugins.set(c.plugin.name,c.plugin),r.push(c.plugin.name),f.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u5DF2\u6309\u5F00\u542F\u6001\u52A0\u8F7D: %s",e.agentId,s.pluginName)):f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: %s: %s",e.agentId,s.pluginName,c.error)}}else{const a=e.plugins.get(s.pluginName);a&&(this.stopAllListeners(a),e.plugins.delete(s.pluginName),i++),this.removeRuntimeLink(e.agentId,s.pluginName)}return this.cleanupRuntimeDir(e,n),(r.length>0||i>0)&&this.bumpCatalogRevision(e),this.persistInstalled(e),r}cleanupRuntimeDir(e,t){const n=this.runtimeDirFor(e.agentId);try{if(!w(n))return;const r=new Set(t);for(const i of j(n))!r.has(i)&&!e.plugins.has(i)&&this.removeRuntimeLink(e.agentId,i)}catch{}}async applyCapabilities(e){const t=await this.hydrate(e);await this.pullTemplatePluginsFromCapabilities(t),await this.syncSharedIntoState(t)}async drainEvents(e,t={}){const n=await this.hydrate(e);this.ensureListenersRunning(n);const r=n.buffer.drain(t),i=await this.getCatalog(e);return{revision:r.revision,events:r.events,catalog:i,compactionSeq:n.compactionSeq}}markAgentCompacted(e){const t=this.agentState(e);t.compactionSeq++,f.debug("[agent:%s] \u4E0A\u4E0B\u6587\u538B\u7F29\u6807\u8BB0: compactionSeq=%d",e,t.compactionSeq)}async enqueueExternalEvent(e,t,n){const r=await this.hydrate(e),i=r.plugins.get(t);if(!i||!i.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\u6216\u5DF2\u505C\u7528`};const s=typeof n.event=="string"?n.event:"",o=i.listeners.find(a=>a.transport.type==="http-callback"&&(!s||a.event===s));return o?(this.enqueueEvent(r,i,o,n),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5339\u914D\u7684 http-callback \u76D1\u542C\u5668\uFF08event=${s||"(\u7A7A)"}\uFF09`}}bumpCatalogRevision(e){e.catalogRevision++}agentState(e){let t=this.agents.get(e);return t||(t={agentId:e,plugins:new Map,buffer:new ve(e),hydrated:!1,queue:Promise.resolve(),catalogRevision:0,eventSeq:0,compactionSeq:0},this.agents.set(e,t)),t}async hydrate(e){const t=this.agentState(e);return t.hydratePromise||(t.hydratePromise=this.doHydrate(t).catch(n=>{throw t.hydratePromise=void 0,n})),await t.hydratePromise,t}async doHydrate(e){const t=e.agentId;await this.migrateLegacySharedIfNeeded();const n=this.installedFile(t);if(!w(n)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const r=JSON.parse(k(n,"utf8")),i=this.readSecrets(t);for(const s of r.plugins??[]){if(String(s.source||"").startsWith("shared:"))continue;const a=m(this.rootDir,"cache",N(t),s.name);if(!w(m(a,"plugin.json"))){f.warn("[agent:%s] \u5DF2\u5B89\u88C5\u63D2\u4EF6\u76EE\u5F55\u7F3A\u5931\uFF0C\u8DF3\u8FC7: %s",t,s.name);continue}const u={...s.config,...i[s.name]??{}},c=this.loadPluginFromDir(e,a,s.source,s.installedAt,u);if(c.ok){const d=c.plugin;d.enabled=s.enabled,e.plugins.set(d.name,d)}else f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25: %s: %s",t,s.name,c.error)}}catch(r){const i=r instanceof Error?r.message:String(r);f.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,i)}await this.syncSharedIntoState(e).catch(r=>{f.warn("[agent:%s] \u5171\u4EAB\u63D2\u4EF6\u540C\u6B65\u5931\u8D25: %s",t,r instanceof Error?r.message:String(r))}),e.hydrated=!0}installedFile(e){return m(this.rootDir,"agents",N(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);D(J(t),{recursive:!0});const n=[...e.plugins.values()].map(r=>({name:r.name,source:r.source,installedAt:r.installedAt,enabled:r.enabled,config:this.stripSensitiveValues(r)}));U(t,`${JSON.stringify({version:1,plugins:n},null,2)}
9
- `,"utf8")}sensitiveKeysOf(e){return(e.userConfig??[]).filter(t=>t?.sensitive===!0&&typeof t.key=="string").map(t=>t.key)}stripSensitiveValues(e){const t=new Set(this.sensitiveKeysOf(e)),n={};for(const[r,i]of Object.entries(e.configValues))t.has(r)||(n[r]=i);return n}secretsFile(e){return m(this.rootDir,"agents",N(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(k(this.secretsFile(e),"utf8"));if(!P(t)||!P(t.secrets))return{};const n={};for(const[r,i]of Object.entries(t.secrets)){if(!P(i))continue;const s={};for(const[o,a]of Object.entries(i))typeof a=="string"&&(s[o]=a);n[r]=s}return n}catch{return{}}}persistSecrets(e){const t={};for(const r of e.plugins.values()){const i={};for(const s of this.sensitiveKeysOf(r)){const o=r.configValues[s];typeof o=="string"&&o.length>0&&(i[s]=o)}Object.keys(i).length>0&&(t[r.name]=i)}const n=this.secretsFile(e.agentId);if(Object.keys(t).length===0){try{E(n,{force:!0})}catch{}return}D(J(n),{recursive:!0}),U(n,`${JSON.stringify({version:1,secrets:t},null,2)}
10
- `,{encoding:"utf8",mode:384})}readJsonFile(e){if(!w(e))return{ok:!0,data:{}};try{const t=JSON.parse(k(e,"utf8"));return P(t)?{ok:!0,data:t}:{ok:!1,error:`${e}: \u6839\u8282\u70B9\u987B\u4E3A\u5BF9\u8C61`}}catch(t){const n=t instanceof Error?t.message:String(t);return{ok:!1,error:`${e}: JSON \u89E3\u6790\u5931\u8D25: ${n}`}}}loadManifestOnly(e){const t=this.readJsonFile(m(e,"plugin.json"));if(!t.ok)return t;const n=t.data,r=_e(n);return r?{ok:!1,error:r}:{ok:!0,manifest:n}}validateUserConfigValues(e,t){const n=Array.isArray(e.userConfig)?e.userConfig:[],r={...t};for(const i of n){if(!i||typeof i.key!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i.key))return"plugin.json: userConfig.key \u975E\u6CD5\uFF08\u987B\u4E3A\u73AF\u5883\u53D8\u91CF\u540D\uFF09";if(!(typeof r[i.key]=="string"&&r[i.key].length>0)&&i.required){const o=i.default!==void 0&&typeof i.default=="string"?i.default:"";if(!o)return`\u7F3A\u5C11\u5FC5\u9700\u914D\u7F6E ${i.key}${i.description?`\uFF08${i.description}\uFF09`:""}\uFF0C\u5B89\u88C5\u65F6\u8BF7\u5728 config \u4E2D\u63D0\u4F9B`;r[i.key]=o}}return null}loadPluginFromDir(e,t,n,r,i){const s=this.loadManifestOnly(t);if(!s.ok)return s;const o=s.manifest,a=m(t,o.tools??"tools/tools.json"),u=this.readJsonFile(a);if(!u.ok)return u;const c=[],d=Array.isArray(u.data.tools)?u.data.tools:[];for(const S of d){const T=Fe(S);if(T)return{ok:!1,error:`${o.tools??"tools/tools.json"}: ${T}`};c.push(S)}const y=m(t,o.listeners??"listeners/listeners.json"),h=this.readJsonFile(y);if(!h.ok)return h;const g=[],p=Array.isArray(h.data.listeners)?h.data.listeners:[];for(const S of p){const T=je(S);if(T)return{ok:!1,error:`${o.listeners??"listeners/listeners.json"}: ${T}`};g.push(S)}const v=Array.isArray(o.userConfig)?o.userConfig:[],L=this.validateUserConfigValues(o,i);if(L)return{ok:!1,error:L};const O={...i};for(const S of v){if(!S||typeof S.key!="string")continue;!(typeof O[S.key]=="string"&&O[S.key].length>0)&&S.required&&S.default!==void 0&&typeof S.default=="string"&&(O[S.key]=S.default)}const M=o.commands;let K;if(typeof M=="string"&&M.trim()){const S=m(t,M.trim());w(S)&&(K=S)}return{ok:!0,plugin:{name:o.name,version:typeof o.version=="string"?o.version:void 0,description:typeof o.description=="string"?o.description:void 0,source:n,installedAt:r,dir:t,enabled:!0,tools:c,listeners:g,commandDir:K,userConfig:v,configValues:O,status:"enabled",runtimes:g.map(S=>({def:S,stopping:!1,restarts:0,logs:[]})),moduleCache:new Map,importNonce:++this.moduleNonce}}}ensureListenersRunning(e){for(const t of e.plugins.values())if(t.enabled)for(const n of t.runtimes)!n.child&&!n.timer&&!n.backoffTimer&&!n.stopping&&this.startListener(e,t,n)}startAllListeners(e,t){for(const n of t.runtimes)this.stopListener(n),t.enabled&&this.startListener(e,t,n)}stopAllListeners(e){for(const t of e.runtimes)this.stopListener(t)}startListener(e,t,n){const r=n.def.transport??{type:"command-stream"};if(r.type==="http-callback"){n.stopping=!1;return}if(r.type==="poll-command"){const i=Math.max(5e3,r.intervalMs??3e4);n.stopping=!1,n.timer=setInterval(()=>{this.pollListenerOnce(e,t,n)},i),this.pollListenerOnce(e,t,n);return}if(!r.command){n.lastError="command-stream \u7F3A\u5C11 command",b(n,`ERROR ${n.lastError}`);return}n.stopping=!1,this.spawnListenerChild(e,t,n)}spawnListenerChild(e,t,n){const i=n.def.transport?.command??"",s=Y(i,{});try{b(n,`spawn: ${s.join(" ")}`);const o=_(s[0],s.slice(1),{cwd:t.dir,env:this.buildPluginEnv(t,e.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});n.child=o,n.lastError=void 0;let a="";o.stdout?.on("data",u=>{a+=u.toString("utf8");let c=a.indexOf(`
6
+ - action=uninstall\uFF1A\u5378\u8F7D\u5E76\u5220\u9664\u63D2\u4EF6\u76EE\u5F55\uFF08\u4E0D\u53EF\u9006\uFF0C\u9700\u7528\u6237\u660E\u786E\u8981\u6C42\uFF09\u3002`,Pe={type:"object",properties:{action:{type:"string",enum:["install","uninstall","reload","enable","disable","list"],description:"\u751F\u547D\u5468\u671F\u52A8\u4F5C"},name:{type:"string",description:"\u63D2\u4EF6\u540D\uFF08uninstall/reload/enable/disable \u5FC5\u586B\uFF09"},source:{type:"string",description:'install \u6765\u6E90\uFF0C\u5F53\u524D\u652F\u6301 "local:<\u4EFB\u610F\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>"\uFF08\u5B89\u88C5\u65F6\u81EA\u52A8\u79FB\u52A8\u5230\u672C agent \u63D2\u4EF6\u4F4D\u7F6E\u540E\u52A0\u8F7D\uFF09'},config:{type:"object",description:"install \u65F6\u63D2\u4EF6 userConfig \u58F0\u660E\u7684\u914D\u7F6E\u503C\uFF08\u952E\u503C\u5BF9\uFF09",additionalProperties:{type:"string"}}},required:["action"],additionalProperties:!1},$e=new Set(["install","uninstall","reload","enable","disable","list"]);function ee(){const l=fe();return l&&l!=="unknown"?l:void 0}function Ee(){return oe(new URL("./builtin-commands/",import.meta.url))}const ke=200,R=256*1024,Ae=4*1024,te=6e4,J=6e4,De=/^[a-zA-Z0-9_][a-zA-Z0-9_-]*$/,z=/^[a-z0-9][a-z0-9-]*$/,be=["system.","aws-","acode-"],Te=new Set(["NODE_OPTIONS","NODE_PATH","NODE_EXTRA_CA_CERTS","NODE_REPL_HISTORY","LD_PRELOAD","LD_LIBRARY_PATH","LD_AUDIT","DYLD_INSERT_LIBRARIES","DYLD_LIBRARY_PATH","DYLD_FRAMEWORK_PATH","PYTHONPATH","PYTHONSTARTUP","PYTHONHOME","PERL5LIB","PERL5OPT","RUBYOPT","RUBYLIB","BASH_ENV","ENV","SHELLOPTS","BASHOPTS","IFS","PS1","PS4","GIT_SSH","GIT_SSH_COMMAND","GIT_CONFIG","GIT_CONFIG_GLOBAL","PATH","HOME","USERPROFILE","SYSTEMROOT","COMSPEC","TEMP","TMP","LANG","TZ"]),Oe=/^[A-Za-z_][A-Za-z0-9_]*$/,Le=process.platform==="win32"?"junction":"dir",Ne=/[^\x20-\x7E]/,Ie=6e4,G=20,ne=80;function v(l){return typeof l=="object"&&l!==null&&!Array.isArray(l)}function I(l,e){let t=l;for(const n of e.split(".")){if(!v(t))return;t=t[n]}return t}function D(l,e){return l.length<=e?l:`${l.slice(0,e)}\u2026(\u622A\u65AD,\u5171 ${l.length} \u5B57\u7B26)`}async function C(l,e=8,t=200){for(let n=0;;n++)try{E(l,{recursive:!0,force:!0});return}catch(r){if(n>=e-1)throw r;await new Promise(i=>setTimeout(i,t))}}async function re(l,e){T(U(e),{recursive:!0});try{j(l,e);return}catch(t){const n=t?.code;if(!n||!["EXDEV","EPERM","EACCES","EBUSY","EINVAL"].includes(n))throw t}F(l,e,{recursive:!0,filter:t=>!/[\\/]\.git([\\/]|$)/.test(t)&&!/[\\/]node_modules([\\/]|$)/.test(t)}),await C(l)}function b(l,e){l.logs.push(`${new Date().toISOString()} ${e}`),l.logs.length>ke&&l.logs.shift()}function B(l,e,t="params"){if(!v(l))return[];const n=[],r=l.type;if(typeof r=="string"&&!Ce(r,e))return[`${t}: \u671F\u671B ${r}\uFF0C\u5B9E\u9645 ${Re(e)}`];if(Array.isArray(l.enum)&&l.enum.length>0&&(l.enum.some(s=>JSON.stringify(s)===JSON.stringify(e))||n.push(`${t}: \u503C ${JSON.stringify(e)} \u4E0D\u5728\u679A\u4E3E ${JSON.stringify(l.enum)} \u5185`)),r==="object"&&v(e)){const i=v(l.properties)?l.properties:{},s=Array.isArray(l.required)?l.required:[];for(const o of s)(!(o in e)||e[o]===void 0)&&n.push(`${t}.${o}: \u5FC5\u586B\u53C2\u6570\u7F3A\u5931`);for(const[o,a]of Object.entries(i))o in e&&e[o]!==void 0&&n.push(...B(a,e[o],`${t}.${o}`));if(l.additionalProperties===!1)for(const o of Object.keys(e))o in i||n.push(`${t}.${o}: \u4E0D\u5141\u8BB8\u7684\u672A\u77E5\u53C2\u6570\uFF08schema additionalProperties=false\uFF09`)}return r==="array"&&Array.isArray(e)&&v(l.items)&&e.forEach((i,s)=>{n.push(...B(l.items,i,`${t}[${s}]`))}),n}function Ce(l,e){switch(l){case"string":return typeof e=="string";case"number":return typeof e=="number"&&Number.isFinite(e);case"boolean":return typeof e=="boolean";case"object":return v(e);case"array":return Array.isArray(e);case"null":return e===null;default:return!0}}function Re(l){return l===null?"null":Array.isArray(l)?"array":typeof l}class Me{constructor(e={}){this.agents=new Map,this.moduleNonce=0,this.templatePullInFlight=new Set,this.backgroundTemplatePulls=new Set,this.rootDir=e.rootDir??de(),this.runtimeRootDir=e.runtimeRootDir}forAgent(e){return{listPlugins:()=>this.list(e),getCatalog:()=>this.getCatalog(e),getToolSchema:t=>this.getToolSchema(e,t),invokeTool:(t,n,r,i)=>this.invokeTool(e,t,n,r,i),manage:t=>this.manage(e,t),getCommandDirs:()=>this.getCommandDirs(e),exposePlugin:t=>this.exposePlugin(e,t)}}setOnPluginsChanged(e){this.onPluginsChanged=e}notifyPluginsChanged(e){try{this.onPluginsChanged?.(e)}catch(t){f.warn("[agent:%s] onPluginsChanged \u56DE\u8C03\u5931\u8D25: %s",e,t instanceof Error?t.message:String(t))}}async getCommandDirs(e){return[...(await this.hydrate(e)).plugins.values()].filter(n=>n.enabled&&n.commandDir).map(n=>n.commandDir)}async getPluginCommands(e){const t=await this.hydrate(e),n=[];n.push(...this.parseCommandDir($,Ee()));for(const r of t.plugins.values())!r.enabled||!r.commandDir||n.push(...this.parsePluginCommandDir(r));return n}parsePluginCommandDir(e){return this.parseCommandDir(e.name,e.commandDir)}parseCommandDir(e,t){const n=[];if(!t)return n;try{for(const r of x(t)){if(!r.endsWith(".md"))continue;const i=m(t,r);try{const s=le(i,k(i,"utf8"));s.plugin=e,n.push(s)}catch(s){f.warn("[plugin:%s] \u547D\u4EE4\u89E3\u6790\u5931\u8D25 %s: %s",e,r,s instanceof Error?s.message:String(s))}}}catch{}return n}async list(e){const t=await this.hydrate(e),n=new Map;for(const r of t.plugins.values()){const i=this.toSummary(r);n.set(i.name,i)}n.has($)||n.set($,{name:$,version:ee(),description:X,status:"enabled",enabled:!0,source:"builtin",installedAt:new Date().toISOString(),toolCount:1,tools:[{name:"plugin-manager.manage",description:"\u63D2\u4EF6\u751F\u547D\u5468\u671F\u7BA1\u7406\uFF08install/uninstall/reload/disable/enable/list\uFF09"}],listeners:[],builtin:!0});for(const r of this.readSharedManifest()){const i=r.pluginName;if(!this.deployedPluginHealthy(i))continue;const o=n.get(i);if(o){String(o.source||"").startsWith("shared:")&&(o.enabled=this.effectiveSharedEnabled(e,r));continue}n.set(i,{name:i,version:this.deployedPluginVersion(i),status:"enabled",enabled:this.effectiveSharedEnabled(e,r),source:`shared:${r.fileName??`${i}.zip`}`,installedAt:new Date().toISOString(),toolCount:0,listeners:[]})}return[...n.values()].sort((r,i)=>r.name.localeCompare(i.name))}deployedPluginVersion(e){try{const t=JSON.parse(k(m(this.deployedPluginDir(e),"plugin.json"),"utf8"));return typeof t.version=="string"?t.version:void 0}catch{return}}async getCatalog(e){const t=await this.hydrate(e),n=[];for(const r of t.plugins.values()){if(!r.enabled||r.status==="error")continue;const i=r.tools.slice(0,G).map(s=>({name:s.name,description:D(s.title||s.description,ne),requiredParams:this.requiredParamsOf(s)}));r.tools.length>G&&i.push({name:`(\u53E6\u6709 ${r.tools.length-G} \u4E2A\u5DE5\u5177\u672A\u5217\u51FA)`,description:'\u7528 function_call {action:"catalog"} \u67E5\u770B\u5168\u91CF',requiredParams:void 0}),n.push({plugin:r.name,version:r.version,description:r.description,status:r.status,tools:i})}return n.push({plugin:$,version:ee(),description:X,status:"enabled",tools:[{name:"manage",description:D(Q,ne),requiredParams:["action"]}]}),{revision:t.catalogRevision,plugins:n}}async getToolSchema(e,t){const n=ue(t);if(!n)return null;if(n.plugin===$&&n.tool==="manage")return{plugin:$,name:"manage",description:Q,inputSchema:Pe};const i=(await this.hydrate(e)).plugins.get(n.plugin);if(!i||!i.enabled)return null;const s=i.tools.find(o=>o.name===n.tool);return s?{plugin:i.name,name:s.name,title:s.title,description:s.description,inputSchema:s.inputSchema??{type:"object",properties:{}}}:null}async invokeTool(e,t,n,r,i){if(t===$&&n==="manage"){const u=this.buildManageRequest(r);if(!u)return{ok:!1,error:"plugin-manager.manage \u7F3A\u5C11 action"};const c=await this.manage(e,u);if(!c.ok)return{ok:!1,error:c.error??"\u63D2\u4EF6\u7BA1\u7406\u64CD\u4F5C\u5931\u8D25"};const d={ok:!0};return c.plugins&&(d.plugins=c.plugins),c.summary&&(d.summary=c.summary),{ok:!0,result:d}}const s=await this.hydrate(e),o=s.plugins.get(t);if(!o)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\uFF08\u53EF\u7528\u63D2\u4EF6: ${[...s.plugins.keys()].join(", ")||"\u65E0"}\uFF09`};if(!o.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u5DF2\u505C\u7528\uFF0C\u53EF\u8BA9\u7528\u6237\u5728\u9762\u677F\u6216\u7528 plugin-manager.manage enable \u542F\u7528`};const a=o.tools.find(u=>u.name===n);if(!a)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5DE5\u5177 ${n}\uFF08\u53EF\u7528: ${o.tools.map(u=>u.name).join(", ")||"\u65E0"}\uFF09`};if(a.inputSchema){const u=B(a.inputSchema,r);if(u.length>0)return{ok:!1,error:`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${u.join("; ")}`,inputSchema:a.inputSchema}}return this.executeHandler(e,o,a,r,i)}buildManageRequest(e){const t=e.action;if(typeof t!="string"||!$e.has(t))return null;const n={action:t};if(typeof e.name=="string"&&e.name.trim()&&(n.name=e.name.trim()),typeof e.source=="string"&&e.source.trim()&&(n.source=e.source.trim()),e.config&&typeof e.config=="object"&&!Array.isArray(e.config)){const r={};for(const[i,s]of Object.entries(e.config))typeof s=="string"&&(r[i]=s);n.config=r}return n}async executeHandler(e,t,n,r,i){const s=Date.now(),o=n.handler??{type:"process"};try{return o.type==="mcp"?{ok:!1,error:"handler.type=mcp \u5C06\u5728\u540E\u7EED\u7248\u672C\u652F\u6301\uFF0C\u8BF7\u6539\u7528 process/module handler"}:o.type==="module"?{ok:!0,result:await this.invokeModuleHandler(t,o,r,e,i),durationMs:Date.now()-s}:!o.command||!o.command.trim()?{ok:!1,error:`\u5DE5\u5177 ${n.name} \u7684 process handler \u7F3A\u5C11 command`}:{ok:!0,result:await this.invokeProcessHandler(t,o,r,e,i),durationMs:Date.now()-s}}catch(a){return{ok:!1,error:a instanceof Error?a.message:String(a),durationMs:Date.now()-s}}}async invokeProcessHandler(e,t,n,r,i){const s=Y(t.command??"",{params:n});if(s.length===0)throw new Error("process handler command \u4E3A\u7A7A");const o=typeof t.timeoutMs=="number"&&t.timeoutMs>0?t.timeoutMs:te,a=new AbortController,u=()=>a.abort();i?.signal?.addEventListener("abort",u,{once:!0});const c=setTimeout(()=>a.abort(),o);try{return await new Promise((d,y)=>{f.info("[plugin:%s] spawn: %s",e.name,s.join(" "));const p=_(s[0],s.slice(1),{cwd:e.dir,env:this.buildPluginEnv(e,r),shell:t.shell===!0,windowsHide:!0,signal:a.signal});let g="",h="";p.stdin?.on("error",()=>{}),p.stdout?.on("data",P=>{g.length<R&&(g+=P.toString("utf8"))}),p.stderr?.on("data",P=>{h.length<R&&(h+=P.toString("utf8"))}),p.on("error",P=>{y(new Error(`\u63D2\u4EF6\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${P.message}`))}),p.on("close",P=>{const A=g.trim();if(P!==0){const O=D(h.trim()||A||"(\u65E0\u8F93\u51FA)",600);y(new Error(a.signal.aborted?`\u63D2\u4EF6\u8FDB\u7A0B\u8D85\u65F6(${o}ms)\u6216\u88AB\u4E2D\u6B62`:`\u63D2\u4EF6\u8FDB\u7A0B\u9000\u51FA\u7801 ${P}: ${O}`));return}if(!A){d({ok:!0});return}try{d(JSON.parse(A))}catch{d(A)}});try{t.stdin==="params"?p.stdin?.end(`${JSON.stringify(n)}
7
+ `):p.stdin?.end()}catch{}})}finally{clearTimeout(c),i?.signal?.removeEventListener("abort",u)}}async invokeModuleHandler(e,t,n,r,i){if(!t.module)throw new Error("module handler \u7F3A\u5C11 module \u8DEF\u5F84");const s=m(e.dir,t.module);let o=e.moduleCache.get(s);o||(o=await import(`${ae(s).href}?acode_nonce=${e.importNonce}`),e.moduleCache.set(s,o));const a=t.export||"default",c=o[a];if(typeof c!="function")throw new Error(`\u6A21\u5757 ${t.module} \u672A\u5BFC\u51FA\u51FD\u6570 ${a}`);const d=typeof t.timeoutMs=="number"&&t.timeoutMs>0?t.timeoutMs:te;let y;const p=Promise.resolve().then(()=>c(n,{agentId:r,signal:i?.signal,pluginRoot:e.dir}));try{return await Promise.race([p,new Promise((g,h)=>{y=setTimeout(()=>h(new Error(`\u63D2\u4EF6\u6A21\u5757\u6267\u884C\u8D85\u65F6(${d}ms)`)),d)})])}finally{clearTimeout(y)}}buildPluginEnv(e,t){const n={};for(const i of["PATH","HOME","USERPROFILE","SystemRoot","COMSPEC","TEMP","TMP","LANG","TZ"]){const s=process.env[i];s!==void 0&&(n[i]=s)}n.ACODE_PLUGIN_ROOT=e.dir,n.ACODE_AGENT_ID=t,n.ACODE_PLUGIN_NAME=e.name;const r=new Set((e.userConfig??[]).map(i=>i.key));for(const[i,s]of Object.entries(e.configValues))r.has(i)&&(!Oe.test(i)||Te.has(i.toUpperCase())||(n[i]=s));return n}async manage(e,t){const n=this.agentState(e),r=n.queue.then(async()=>{const i=await this.hydrate(e);return this.doManage(i,t)});return n.queue=r.catch(()=>{}),r}async doManage(e,t){if(t.name===$&&(t.action==="uninstall"||t.action==="disable"||t.action==="enable"||t.action==="reload"))return{ok:!1,error:`\u5185\u7F6E\u7CFB\u7EDF\u63D2\u4EF6 ${$} \u4E0D\u53EF${t.action==="uninstall"?"\u5378\u8F7D":t.action==="reload"?"\u91CD\u8F7D":"\u505C\u7528/\u542F\u7528"}`};switch(t.action){case"list":return{ok:!0,plugins:await this.list(e.agentId)};case"install":return this.installPlugin(e,t);case"uninstall":return this.uninstallPlugin(e,t);case"reload":return this.reloadPlugin(e,t);case"enable":case"disable":return this.setEnabled(e,t,t.action==="enable");default:return{ok:!1,error:`\u672A\u77E5 action: ${String(t.action)}`}}}async installPlugin(e,t){const n=(t.source??"").trim();if(!n)return{ok:!1,error:"install \u9700\u8981 source\uFF08\u5F53\u524D\u652F\u6301 local:<\u63D2\u4EF6\u76EE\u5F55\u7EDD\u5BF9\u8DEF\u5F84>\uFF09"};const r=n.split(":",1)[0];if(r!=="local")return{ok:!1,error:`\u6682\u4E0D\u652F\u6301\u63D2\u4EF6\u6765\u6E90 "${r}:"\uFF08npm:/git: \u5728\u8DEF\u7EBF\u56FE\u4E2D\uFF1Bzip \u5305\u8BF7\u7528\u9762\u677F\u5BFC\u5165\u6216 POST /runtime/plugins/:agentId/upload\uFF09`};const i=n.slice(6).trim();return!i||!w(m(i,"plugin.json"))?{ok:!1,error:`\u63D2\u4EF6\u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u7F3A\u5C11 plugin.json: ${i}`}:this.installFromDirectory(e,i,n,t.config)}async installFromZip(e,t,n,r){if(!t||t.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};const i=this.agentState(e),s=i.queue.then(async()=>{await this.hydrate(e);const o=m(this.rootDir,"tmp",`upload-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const a=await q(t,o);return w(m(a.pluginDir,"plugin.json"))?(f.info("[agent:%s] zip \u5BFC\u5165: %s\uFF08%d \u4E2A\u6587\u4EF6\uFF09",e,n,a.fileCount),await this.installFromDirectory(i,a.pluginDir,`upload:${n}`,r)):{ok:!1,error:"zip \u5185\u672A\u627E\u5230 plugin.json\uFF08\u8BF7\u6253\u5305\u63D2\u4EF6\u76EE\u5F55\uFF0C\u4E14 zip \u5185\u8DEF\u5F84\u4F7F\u7528\u82F1\u6587\uFF09"}}catch(a){const u=a instanceof Error?a.message:String(a);return f.warn("[agent:%s] zip \u5BFC\u5165\u5931\u8D25: %s",e,u),{ok:!1,error:`zip \u5BFC\u5165\u5931\u8D25: ${u}`}}finally{await C(o,3,100).catch(()=>{})}});return i.queue=s.catch(()=>{}),s}async installFromDirectory(e,t,n,r){const i=this.loadManifestOnly(t);if(!i.ok)return i;const s=i.manifest,o=s.name;if(o===$)return{ok:!1,error:`\u63D2\u4EF6\u540D ${o} \u4E3A\u7CFB\u7EDF\u4FDD\u7559\u540D\u79F0\uFF0C\u4E0D\u5141\u8BB8\u5B89\u88C5\u540C\u540D\u63D2\u4EF6`};const a=this.validateUserConfigValues(s,r??{});if(a)return{ok:!1,error:a};const u=this.loadPluginFromDir(e,t,n,new Date().toISOString(),r??{});if(!u.ok)return f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u6821\u9A8C\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,u.error),u;const c=m(this.rootDir,"cache",N(e.agentId),o),d=t===c,y=e.plugins.get(o);if(y&&this.stopAllListeners(y),!d)try{await C(c),await re(t,c)}catch(h){const P=h instanceof Error?h.message:String(h);return f.warn("[agent:%s] \u63D2\u4EF6\u79FB\u52A8\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,P),y&&this.startAllListeners(e,y),{ok:!1,error:`\u63D2\u4EF6\u79FB\u52A8\u5931\u8D25: ${P}`}}const p=this.loadPluginFromDir(e,c,n,new Date().toISOString(),r??{});if(!p.ok){if(f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25\uFF08%s\uFF09: %s",e.agentId,n,p.error),!d)try{await re(c,t)}catch(h){f.warn("[agent:%s] \u88C5\u8F7D\u5931\u8D25\u540E\u65E0\u6CD5\u79FB\u56DE\u6E90\u76EE\u5F55\uFF08%s\uFF09\uFF0C\u5DF2\u4FDD\u7559\u7F13\u5B58\u526F\u672C",e.agentId,h instanceof Error?h.message:String(h))}if(y)try{this.startAllListeners(e,y)}catch(h){f.warn("[agent:%s] \u6062\u590D\u65E7\u5B9E\u4F8B\u76D1\u542C\u5931\u8D25: %s",e.agentId,h instanceof Error?h.message:String(h))}return p}const g=p.plugin;return e.plugins.set(g.name,g),this.bumpCatalogRevision(e),this.persistInstalled(e),this.startAllListeners(e,g),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5B89\u88C5: %s@%s",e.agentId,g.name,g.version??"?"),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(g)}}async uninstallPlugin(e,t){const n=(t.name??"").trim(),r=e.plugins.get(n);return r?(this.stopAllListeners(r),e.plugins.delete(n),e.pendingExposed.delete(n),e.exposedThisSession.has(n)&&this.pushDeprecatedNotice(e,n,"\u5DF2\u88AB\u5378\u8F7D"),this.bumpCatalogRevision(e),this.readSharedManifest().some(s=>s.pluginName===n)&&this.removeRuntimeLink(e.agentId,n),this.persistInstalled(e),await C(r.dir),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u5378\u8F7D: %s",e.agentId,n),this.notifyPluginsChanged(e.agentId),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`}}async reloadPlugin(e,t){const n=(t.name??"").trim(),r=e.plugins.get(n);if(!r)return{ok:!1,error:`\u63D2\u4EF6 ${n} \u672A\u5B89\u88C5`};const i=this.loadPluginFromDir(e,r.dir,r.source,r.installedAt,r.configValues);if(!i.ok)return{ok:!1,error:`\u91CD\u8F7D\u5931\u8D25\uFF08\u4FDD\u7559\u65E7\u7248\u672C\u8FD0\u884C\uFF09: ${i.error}`};const s=i.plugin;return this.stopAllListeners(r),e.plugins.set(n,s),this.bumpCatalogRevision(e),this.persistInstalled(e),this.startAllListeners(e,s),f.info("[agent:%s] \u63D2\u4EF6\u5DF2\u91CD\u8F7D: %s",e.agentId,n),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(s)}}async setEnabled(e,t,n){const r=(t.name??"").trim(),i=e.plugins.get(r);return i?(i.enabled=n,this.bumpCatalogRevision(e),n?this.startAllListeners(e,i):(e.pendingExposed.delete(r),e.exposedThisSession.has(r)&&this.pushDeprecatedNotice(e,r,"\u5DF2\u505C\u7528"),this.stopAllListeners(i)),this.persistInstalled(e),this.notifyPluginsChanged(e.agentId),{ok:!0,summary:this.toSummary(i)}):{ok:!1,error:`\u63D2\u4EF6 ${r} \u672A\u5B89\u88C5`}}deployedDir(){return m(this.rootDir,"deployed")}deployedManifestFile(){return m(this.deployedDir(),"manifest.json")}deployedPluginDir(e){if(!V(e))throw new Error(`\u975E\u6CD5\u63D2\u4EF6\u540D\uFF08\u4EC5\u5141\u8BB8 ${z}\uFF09: ${JSON.stringify(e)}`);const t=m(this.deployedDir(),e),n=Z(this.deployedDir());if(Z(t)!==m(n,e))throw new Error(`\u63D2\u4EF6\u76EE\u5F55\u8D8A\u51FA\u90E8\u7F72\u533A: ${e}`);return t}legacySharedDir(){return m(this.rootDir,"shared")}readSharedManifest(){try{const e=JSON.parse(k(this.deployedManifestFile(),"utf8"));return Array.isArray(e.plugins)?e.plugins.filter(t=>t&&V(t.pluginName)):[]}catch{return[]}}writeSharedManifest(e){T(this.deployedDir(),{recursive:!0}),H(this.deployedManifestFile(),`${JSON.stringify({version:2,plugins:e},null,2)}
8
+ `,"utf8")}async migrateLegacySharedIfNeeded(){try{if(w(this.deployedManifestFile()))return;const e=m(this.legacySharedDir(),"manifest.json");if(!w(e))return;const t=JSON.parse(k(e,"utf8")),n=Array.isArray(t.plugins)?t.plugins:[];if(n.length===0)return;const r=[];for(const i of n){const s=m(this.legacySharedDir(),i.zipFile);if(!(!i.pluginName||!w(s)))try{const o=await this.deployPluginZipToPersistentDir(k(s),i.pluginName);if(!o)continue;r.push(o),f.info("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u5230\u90E8\u7F72\u533A: %s",i.pluginName)}catch(o){f.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6 %s \u8FC1\u79FB\u5931\u8D25: %s",i.pluginName,o instanceof Error?o.message:String(o))}}r.length>0&&this.writeSharedManifest(r),E(this.legacySharedDir(),{recursive:!0,force:!0})}catch(e){f.warn("[shared] \u65E7\u5171\u4EAB\u63D2\u4EF6\u8FC1\u79FB\u672A\u6267\u884C\uFF08\u4E0B\u6B21\u91CD\u8BD5\uFF09: %s",e instanceof Error?e.message:String(e))}}deployedPluginHealthy(e){return w(m(this.deployedPluginDir(e),"plugin.json"))}runtimeDirFor(e){return this.runtimeRootDir?m(this.runtimeRootDir,N(e)):ce(e)}runtimeLinkPath(e,t){return m(this.runtimeDirFor(e),t)}ensureRuntimeLink(e,t){const n=this.runtimeLinkPath(e,t),r=this.deployedPluginDir(t);if(!w(m(r,"plugin.json")))return!1;T(this.runtimeDirFor(e),{recursive:!0});try{if(w(n)){let i=!1;try{i=ie(n).isSymbolicLink()}catch{i=!1}if(i)try{return x(n),!0}catch{}E(n,{recursive:!0,force:!0})}return se(r,n,Le),f.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u5EFA\u7ACB: %s \u2192 %s",e,n,r),!0}catch(i){return f.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5931\u8D25\uFF08%s \u2192 %s\uFF09: %s",e,t,r,i instanceof Error?i.message:String(i)),!1}}removeRuntimeLink(e,t){const n=this.runtimeLinkPath(e,t);try{w(n)&&(E(n,{recursive:!0,force:!0}),f.info("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u5DF2\u79FB\u9664: %s",e,t))}catch(r){f.warn("[agent:%s] \u63D2\u4EF6\u8FD0\u884C\u65F6\u94FE\u63A5\u79FB\u9664\u5931\u8D25\uFF08%s\uFF09: %s",e,t,r instanceof Error?r.message:String(r))}}async saveSharedPlugin(e,t,n=!0,r){if(!e||e.length===0)return{ok:!1,error:"zip \u5305\u5185\u5BB9\u4E3A\u7A7A"};await this.migrateLegacySharedIfNeeded();const i=m(this.rootDir,"tmp",`deploy-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const s=await q(e,i),o=this.loadManifestOnly(s.pluginDir);if(!o.ok)return o;const a=o.manifest.name,u=this.readSharedManifest().find(g=>g.pluginName===a);if(u&&u.sha256===r&&this.deployedPluginHealthy(a))return{ok:!0,pluginName:a};await this.suspendDeployedPluginAcrossAgents(a),T(this.deployedDir(),{recursive:!0});const c=this.deployedPluginDir(a),d=m(this.deployedDir(),`.old-${a}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);if(w(c))try{j(c,d)}catch{E(c,{recursive:!0,force:!0})}try{F(s.pluginDir,c,{recursive:!0,filter:g=>!/[\\/]\.git([\\/]|$)/.test(g)&&!/[\\/]node_modules([\\/]|$)/.test(g)})}catch(g){throw w(d)&&(E(c,{recursive:!0,force:!0}),j(d,c)),g}w(d)&&E(d,{recursive:!0,force:!0}),await W(this.deployedManifestFile(),async()=>{const g=this.readSharedManifest().filter(h=>h.pluginName!==a);g.push({pluginName:a,zipFile:u?.zipFile??`${a}.zip`,fileName:t,defaultEnabled:n,sha256:r}),this.writeSharedManifest(g)}),f.info("[shared] \u63D2\u4EF6\u5DF2\u90E8\u7F72\u5230 bridge: %s\uFF08\u6765\u6E90 %s%s\uFF09",a,t,r?`, sha256=${r.slice(0,12)}\u2026`:"");const p=(await this.applySharedToKnownAgents()).find(g=>g.installed.includes(a));return p&&f.info("[shared] \u5DF2\u540C\u6B65\u5230\u8FD0\u884C\u4E2D agent: %s",p.agentId),{ok:!0,pluginName:a}}catch(s){const o=s instanceof Error?s.message:String(s);return f.warn("[shared] \u63D2\u4EF6\u90E8\u7F72\u5931\u8D25\uFF08%s\uFF09: %s",t,o),{ok:!1,error:`\u63D2\u4EF6\u90E8\u7F72\u5931\u8D25: ${o}`}}finally{await C(i,3,100).catch(()=>{})}}async deployPluginZipToPersistentDir(e,t){if(!e||e.length===0)return null;const n=m(this.rootDir,"tmp",`migrate-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{const r=await q(e,n),i=this.loadManifestOnly(r.pluginDir);if(!i.ok)return null;const s=i.manifest.name;if(t&&s!==t)return null;T(this.deployedDir(),{recursive:!0});const o=this.deployedPluginDir(s);return w(o)&&E(o,{recursive:!0,force:!0}),F(r.pluginDir,o,{recursive:!0,filter:a=>!/[\\/]\.git([\\/]|$)/.test(a)&&!/[\\/]node_modules([\\/]|$)/.test(a)}),{pluginName:s,zipFile:`${s}.zip`,fileName:`${s}.zip`,defaultEnabled:!0}}finally{E(n,{recursive:!0,force:!0})}}async suspendDeployedPluginAcrossAgents(e){for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const n=t.queue.then(async()=>{const r=t.plugins.get(e);r&&(this.stopAllListeners(r),t.plugins.delete(e),this.bumpCatalogRevision(t),f.info("[agent:%s] \u5DF2\u6458\u9664\u63D2\u4EF6\u7B49\u5F85\u90E8\u7F72\u533A\u66F4\u65B0: %s",t.agentId,e))});t.queue=n.catch(()=>{}),await n}}async applySharedToKnownAgents(){const e=[];for(const t of this.agents.values()){if(!t.hydrated)continue;const n=await this.syncSharedIntoStateQueued(t);n.length>0&&e.push({agentId:t.agentId,installed:n})}return e}async syncSharedIntoStateQueued(e){const t=e.queue.then(()=>this.syncSharedIntoState(e));return e.queue=t.catch(()=>[]),t}effectiveSharedEnabled(e,t){return Se(e,"plugins",t.pluginName)&&(t.defaultEnabled??!0)}async pullTemplatePluginsFromCapabilities(e){const t=we(e.agentId);if(!t)return;const n=this.readSharedManifest();for(const[r,i]of Object.entries(t)){if(!i.url)continue;const s=n.find(a=>a.pluginName===r);if(!s||!s.sha256||s.sha256===i.sha256&&this.deployedPluginHealthy(r)||this.templatePullInFlight.has(r))continue;this.templatePullInFlight.add(r);const o=m(this.rootDir,"tmp",`pull-${N(e.agentId)}-${Date.now()}-${Math.random().toString(36).slice(2,8)}`);try{T(o,{recursive:!0});let a=`${r}.zip`;try{const y=decodeURIComponent(i.url.split("/").pop()||"");y.toLowerCase().endsWith(".zip")&&(a=pe(y,`${r}.zip`))}catch{}const u=await ge({downloadUrl:i.url,fileName:a,sha256:i.sha256},o);if(!u)continue;const c=k(u.outputPath),d=await this.saveSharedPlugin(c,u.fileName,s.defaultEnabled??!0,i.sha256);d.ok?f.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u81EA\u52A8\u5BF9\u8D26\u66F4\u65B0\u6210\u529F: %s",e.agentId,d.pluginName??r):f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u66F4\u65B0\u5931\u8D25: %s: %s",e.agentId,r,d.error)}catch(a){f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6 %s \u66F4\u65B0\u5931\u8D25: %s",e.agentId,r,a instanceof Error?a.message:String(a))}finally{this.templatePullInFlight.delete(r),await C(o,3,100).catch(()=>{})}}}queueTemplatePull(e){this.backgroundTemplatePulls.has(e.agentId)||(this.backgroundTemplatePulls.add(e.agentId),this.pullTemplatePluginsFromCapabilities(e).catch(t=>{f.warn("[agent:%s] \u540E\u53F0\u63D2\u4EF6\u5BF9\u8D26\u66F4\u65B0\u5931\u8D25: %s",e.agentId,t instanceof Error?t.message:String(t))}).finally(()=>{this.backgroundTemplatePulls.delete(e.agentId)}))}async syncSharedIntoState(e){this.queueTemplatePull(e);const t=this.readSharedManifest(),n=new Set,r=[];let i=0;for(const s of t)if(n.add(s.pluginName),this.effectiveSharedEnabled(e.agentId,s)){const a=e.plugins.get(s.pluginName);if(!this.ensureRuntimeLink(e.agentId,s.pluginName))continue;const u=this.runtimeLinkPath(e.agentId,s.pluginName);if(!a){const c=this.loadPluginFromDir(e,u,`shared:${s.fileName}`,new Date().toISOString(),{});c.ok?(e.plugins.set(c.plugin.name,c.plugin),r.push(c.plugin.name),f.info("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u5DF2\u6309\u5F00\u542F\u6001\u52A0\u8F7D: %s",e.agentId,s.pluginName)):f.warn("[agent:%s] \u5DF2\u90E8\u7F72\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: %s: %s",e.agentId,s.pluginName,c.error)}}else{const a=e.plugins.get(s.pluginName);a&&(this.stopAllListeners(a),e.plugins.delete(s.pluginName),i++),this.removeRuntimeLink(e.agentId,s.pluginName)}return this.cleanupRuntimeDir(e,n),(r.length>0||i>0)&&this.bumpCatalogRevision(e),this.persistInstalled(e),r}cleanupRuntimeDir(e,t){const n=this.runtimeDirFor(e.agentId);try{if(!w(n))return;const r=new Set(t);for(const i of x(n))!r.has(i)&&!e.plugins.has(i)&&this.removeRuntimeLink(e.agentId,i)}catch{}}async applyCapabilities(e){const t=await this.hydrate(e);await this.pullTemplatePluginsFromCapabilities(t),await this.syncSharedIntoState(t)}exposePlugin(e,t){if(!t)return;const n=this.agentState(e);n.pendingExposed.add(t),n.exposedThisSession.add(t),f.debug("[agent:%s] \u63D2\u4EF6\u5DF2\u89E6\u53D1\u5F85\u66B4\u9732: %s",e,t)}async drainEvents(e,t={}){const n=await this.hydrate(e);this.ensureListenersRunning(n);const r=n.buffer.drain(t),i=await this.consumeExposedCatalog(n);return{revision:r.revision,events:r.events,catalog:i,compactionSeq:n.compactionSeq}}async consumeExposedCatalog(e){if(e.pendingExposed.size===0)return null;const t=await this.getCatalog(e.agentId),n=[...e.pendingExposed];e.pendingExposed.clear();const r=t.plugins.filter(i=>n.includes(i.plugin));return r.length===0?null:{revision:t.revision,plugins:r}}pushDeprecatedNotice(e,t,n){e.buffer.push({plugin:t,event:"plugin-deprecated",priority:"urgent",receivedAt:Date.now(),message:{msgId:`plugin-deprecated-${t}-${Date.now()}`,channel:"plugin-event",senderId:`plugin:${t}`,senderName:`\u63D2\u4EF6[${t}]`,receiverId:e.agentId||void 0,content:`\u3010\u7CFB\u7EDF\u63D0\u793A\u3011\u63D2\u4EF6 ${t} ${n}\uFF0C\u5176\u63D0\u4F9B\u7684\u5DE5\u5177\u4E0D\u518D\u53EF\u8C03\u7528\u3002\u5982\u4ECD\u9700\u4F7F\u7528\u8BF7\u91CD\u65B0\u5B89\u88C5/\u542F\u7528\u8BE5\u63D2\u4EF6\u3002`,requireReply:!1,role:"system",timestamp:new Date().toISOString()}})}markAgentCompacted(e){const t=this.agentState(e);t.compactionSeq++,f.debug("[agent:%s] \u4E0A\u4E0B\u6587\u538B\u7F29\u6807\u8BB0: compactionSeq=%d",e,t.compactionSeq)}async enqueueExternalEvent(e,t,n){const r=await this.hydrate(e),i=r.plugins.get(t);if(!i||!i.enabled)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5\u6216\u5DF2\u505C\u7528`};const s=typeof n.event=="string"?n.event:"",o=i.listeners.find(a=>a.transport.type==="http-callback"&&(!s||a.event===s));return o?(this.enqueueEvent(r,i,o,n),{ok:!0}):{ok:!1,error:`\u63D2\u4EF6 ${t} \u65E0\u5339\u914D\u7684 http-callback \u76D1\u542C\u5668\uFF08event=${s||"(\u7A7A)"}\uFF09`}}bumpCatalogRevision(e){e.catalogRevision++}agentState(e){let t=this.agents.get(e);return t||(t={agentId:e,plugins:new Map,buffer:new ve(e),hydrated:!1,queue:Promise.resolve(),catalogRevision:0,eventSeq:0,compactionSeq:0,pendingExposed:new Set,exposedThisSession:new Set},this.agents.set(e,t)),t}async hydrate(e){const t=this.agentState(e);return t.hydratePromise||(t.hydratePromise=this.doHydrate(t).catch(n=>{throw t.hydratePromise=void 0,n})),await t.hydratePromise,t}async doHydrate(e){const t=e.agentId;await this.migrateLegacySharedIfNeeded();const n=this.installedFile(t);if(!w(n)){await this.syncSharedIntoState(e).catch(()=>{}),e.hydrated=!0;return}try{const r=JSON.parse(k(n,"utf8")),i=this.readSecrets(t);for(const s of r.plugins??[]){if(String(s.source||"").startsWith("shared:"))continue;const a=m(this.rootDir,"cache",N(t),s.name);if(!w(m(a,"plugin.json"))){f.warn("[agent:%s] \u5DF2\u5B89\u88C5\u63D2\u4EF6\u76EE\u5F55\u7F3A\u5931\uFF0C\u8DF3\u8FC7: %s",t,s.name);continue}const u={...s.config,...i[s.name]??{}},c=this.loadPluginFromDir(e,a,s.source,s.installedAt,u);if(c.ok){const d=c.plugin;d.enabled=s.enabled,e.plugins.set(d.name,d)}else f.warn("[agent:%s] \u63D2\u4EF6\u88C5\u8F7D\u5931\u8D25: %s: %s",t,s.name,c.error)}}catch(r){const i=r instanceof Error?r.message:String(r);f.error("[agent:%s] installed.json \u89E3\u6790\u5931\u8D25: %s",t,i)}await this.syncSharedIntoState(e).catch(r=>{f.warn("[agent:%s] \u5171\u4EAB\u63D2\u4EF6\u540C\u6B65\u5931\u8D25: %s",t,r instanceof Error?r.message:String(r))}),e.hydrated=!0}installedFile(e){return m(this.rootDir,"agents",N(e),"installed.json")}persistInstalled(e){this.persistSecrets(e);const t=this.installedFile(e.agentId);T(U(t),{recursive:!0});const n=[...e.plugins.values()].map(r=>({name:r.name,source:r.source,installedAt:r.installedAt,enabled:r.enabled,config:this.stripSensitiveValues(r)}));H(t,`${JSON.stringify({version:1,plugins:n},null,2)}
9
+ `,"utf8")}sensitiveKeysOf(e){return(e.userConfig??[]).filter(t=>t?.sensitive===!0&&typeof t.key=="string").map(t=>t.key)}stripSensitiveValues(e){const t=new Set(this.sensitiveKeysOf(e)),n={};for(const[r,i]of Object.entries(e.configValues))t.has(r)||(n[r]=i);return n}secretsFile(e){return m(this.rootDir,"agents",N(e),"secrets.json")}readSecrets(e){try{const t=JSON.parse(k(this.secretsFile(e),"utf8"));if(!v(t)||!v(t.secrets))return{};const n={};for(const[r,i]of Object.entries(t.secrets)){if(!v(i))continue;const s={};for(const[o,a]of Object.entries(i))typeof a=="string"&&(s[o]=a);n[r]=s}return n}catch{return{}}}persistSecrets(e){const t={};for(const r of e.plugins.values()){const i={};for(const s of this.sensitiveKeysOf(r)){const o=r.configValues[s];typeof o=="string"&&o.length>0&&(i[s]=o)}Object.keys(i).length>0&&(t[r.name]=i)}const n=this.secretsFile(e.agentId);if(Object.keys(t).length===0){try{E(n,{force:!0})}catch{}return}T(U(n),{recursive:!0}),H(n,`${JSON.stringify({version:1,secrets:t},null,2)}
10
+ `,{encoding:"utf8",mode:384})}readJsonFile(e){if(!w(e))return{ok:!0,data:{}};try{const t=JSON.parse(k(e,"utf8"));return v(t)?{ok:!0,data:t}:{ok:!1,error:`${e}: \u6839\u8282\u70B9\u987B\u4E3A\u5BF9\u8C61`}}catch(t){const n=t instanceof Error?t.message:String(t);return{ok:!1,error:`${e}: JSON \u89E3\u6790\u5931\u8D25: ${n}`}}}loadManifestOnly(e){const t=this.readJsonFile(m(e,"plugin.json"));if(!t.ok)return t;const n=t.data,r=_e(n);return r?{ok:!1,error:r}:{ok:!0,manifest:n}}validateUserConfigValues(e,t){const n=Array.isArray(e.userConfig)?e.userConfig:[],r={...t};for(const i of n){if(!i||typeof i.key!="string"||!/^[A-Za-z_][A-Za-z0-9_]*$/.test(i.key))return"plugin.json: userConfig.key \u975E\u6CD5\uFF08\u987B\u4E3A\u73AF\u5883\u53D8\u91CF\u540D\uFF09";if(!(typeof r[i.key]=="string"&&r[i.key].length>0)&&i.required){const o=i.default!==void 0&&typeof i.default=="string"?i.default:"";if(!o)return`\u7F3A\u5C11\u5FC5\u9700\u914D\u7F6E ${i.key}${i.description?`\uFF08${i.description}\uFF09`:""}\uFF0C\u5B89\u88C5\u65F6\u8BF7\u5728 config \u4E2D\u63D0\u4F9B`;r[i.key]=o}}return null}loadPluginFromDir(e,t,n,r,i){const s=this.loadManifestOnly(t);if(!s.ok)return s;const o=s.manifest,a=m(t,o.tools??"tools/tools.json"),u=this.readJsonFile(a);if(!u.ok)return u;const c=[],d=Array.isArray(u.data.tools)?u.data.tools:[];for(const S of d){const L=Fe(S);if(L)return{ok:!1,error:`${o.tools??"tools/tools.json"}: ${L}`};c.push(S)}const y=m(t,o.listeners??"listeners/listeners.json"),p=this.readJsonFile(y);if(!p.ok)return p;const g=[],h=Array.isArray(p.data.listeners)?p.data.listeners:[];for(const S of h){const L=xe(S);if(L)return{ok:!1,error:`${o.listeners??"listeners/listeners.json"}: ${L}`};g.push(S)}const P=Array.isArray(o.userConfig)?o.userConfig:[],A=this.validateUserConfigValues(o,i);if(A)return{ok:!1,error:A};const O={...i};for(const S of P){if(!S||typeof S.key!="string")continue;!(typeof O[S.key]=="string"&&O[S.key].length>0)&&S.required&&S.default!==void 0&&typeof S.default=="string"&&(O[S.key]=S.default)}const M=o.commands;let K;if(typeof M=="string"&&M.trim()){const S=m(t,M.trim());w(S)&&(K=S)}return{ok:!0,plugin:{name:o.name,version:typeof o.version=="string"?o.version:void 0,description:typeof o.description=="string"?o.description:void 0,source:n,installedAt:r,dir:t,enabled:!0,tools:c,listeners:g,commandDir:K,userConfig:P,configValues:O,status:"enabled",runtimes:g.map(S=>({def:S,stopping:!1,restarts:0,logs:[]})),moduleCache:new Map,importNonce:++this.moduleNonce}}}ensureListenersRunning(e){for(const t of e.plugins.values())if(t.enabled)for(const n of t.runtimes)!n.child&&!n.timer&&!n.backoffTimer&&!n.stopping&&this.startListener(e,t,n)}startAllListeners(e,t){for(const n of t.runtimes)this.stopListener(n),t.enabled&&this.startListener(e,t,n)}stopAllListeners(e){for(const t of e.runtimes)this.stopListener(t)}startListener(e,t,n){const r=n.def.transport??{type:"command-stream"};if(r.type==="http-callback"){n.stopping=!1;return}if(r.type==="poll-command"){const i=Math.max(5e3,r.intervalMs??3e4);n.stopping=!1,n.timer=setInterval(()=>{this.pollListenerOnce(e,t,n)},i),this.pollListenerOnce(e,t,n);return}if(!r.command){n.lastError="command-stream \u7F3A\u5C11 command",b(n,`ERROR ${n.lastError}`);return}n.stopping=!1,this.spawnListenerChild(e,t,n)}spawnListenerChild(e,t,n){const i=n.def.transport?.command??"",s=Y(i,{});try{b(n,`spawn: ${s.join(" ")}`);const o=_(s[0],s.slice(1),{cwd:t.dir,env:this.buildPluginEnv(t,e.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});n.child=o,n.lastError=void 0;let a="";o.stdout?.on("data",u=>{a+=u.toString("utf8");let c=a.indexOf(`
11
11
  `);for(;c>=0;){const d=a.slice(0,c).trim();a=a.slice(c+1),d&&this.handleListenerLine(e,t,n,d),c=a.indexOf(`
12
- `)}a.length>1024*1024&&(a=a.slice(-4096))}),o.stderr?.on("data",u=>{b(n,`stderr: ${A(u.toString("utf8").trimEnd(),400)}`)}),o.on("error",u=>{n.lastError=`\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${u.message}`,b(n,`ERROR ${n.lastError}`)}),o.on("close",u=>{n.child=void 0,!n.stopping&&(n.lastError=`\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\uFF08code=${u??"signal"}\uFF09`,b(n,`exit code=${u??"signal"}\uFF0C\u51C6\u5907\u91CD\u542F`),this.scheduleListenerRestart(e,t,n))})}catch(o){const a=o instanceof Error?o.message:String(o);n.lastError=a,b(n,`ERROR spawn: ${a}`),this.scheduleListenerRestart(e,t,n)}}scheduleListenerRestart(e,t,n){if(n.stopping)return;const r=Math.max(500,n.def.transport?.restartBackoffMs??2e3),i=Math.min(Ie,r*2**Math.min(n.restarts,6));n.restarts++,b(n,`restart #${n.restarts} in ${i}ms`),n.backoffTimer=setTimeout(()=>{n.backoffTimer=void 0,n.stopping||this.spawnListenerChild(e,t,n)},i)}async pollListenerOnce(e,t,n){if(n.polling)return;const r=n.def.transport?.command??"",i=Y(r,{});if(i.length===0)return;n.polling=!0;const s=new AbortController,o=setTimeout(()=>s.abort(),q);try{const a=await new Promise((u,c)=>{const d=_(i[0],i.slice(1),{cwd:t.dir,env:this.buildPluginEnv(t,e.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"],signal:s.signal});let y="",h="";d.stdout?.on("data",p=>{y.length<R&&(y+=p.toString("utf8"))}),d.stderr?.on("data",p=>{h.length<R&&(h+=p.toString("utf8"))});const g=()=>s.signal.aborted;d.on("error",p=>{c(g()?new Error(`poll \u8D85\u65F6(${q}ms)`):p)}),d.on("close",p=>{g()?c(new Error(`poll \u8D85\u65F6(${q}ms)`)):p===0?u(y):c(new Error(`exit ${p}: ${A(h||y,300)}`))})});n.lastError=void 0;for(const u of a.split(`
13
- `)){const c=u.trim();c&&this.handleListenerLine(e,t,n,c)}}catch(a){const u=a instanceof Error?a.message:String(a);n.lastError=u,b(n,`poll \u5931\u8D25: ${u}`)}finally{clearTimeout(o),n.polling=!1}}stopListener(e){if(e.stopping=!0,e.restarts=0,e.child){try{e.child.kill()}catch{}e.child=void 0}e.timer&&(clearInterval(e.timer),e.timer=void 0),e.backoffTimer&&(clearTimeout(e.backoffTimer),e.backoffTimer=void 0)}handleListenerLine(e,t,n,r){b(n,`event: ${A(r,300)}`);let i;try{i=JSON.parse(r)}catch{n.lastError="\u4E8B\u4EF6\u884C\u4E0D\u662F\u5408\u6CD5 JSON\uFF08\u8981\u6C42 JSONL\uFF09";return}if(!P(i))return;const s=typeof i.event=="string"?i.event:n.def.event,o=s===n.def.event?n.def:t.listeners.find(a=>a.event===s);o&&(o.delivery&&o.delivery!=="poll"&&f.debug("[plugin:%s] delivery=%s \u6682\u6309 poll \u4EA4\u4ED8",t.name,o.delivery),this.enqueueEvent(e,t,o,i))}enqueueEvent(e,t,n,r){for(const[y,h]of Object.entries(n.matcher??{})){const g=I(r,y);if(!(Array.isArray(h)?h:[h]).some(v=>JSON.stringify(v)===JSON.stringify(g)))return}let i;if(n.dedupKey){const y=I(r,n.dedupKey);y!=null&&(i=A(String(y),256))}e.eventSeq++;const s=A(Ue(r.data??r),Ae),o=n.promptTemplate?He(n.promptTemplate,r):`\u3010\u63D2\u4EF6\u4E8B\u4EF6\u3011${t.name} \xB7 ${n.event}${n.description?`
12
+ `)}a.length>1024*1024&&(a=a.slice(-4096))}),o.stderr?.on("data",u=>{b(n,`stderr: ${D(u.toString("utf8").trimEnd(),400)}`)}),o.on("error",u=>{n.lastError=`\u8FDB\u7A0B\u542F\u52A8\u5931\u8D25: ${u.message}`,b(n,`ERROR ${n.lastError}`)}),o.on("close",u=>{n.child=void 0,!n.stopping&&(n.lastError=`\u76D1\u542C\u8FDB\u7A0B\u9000\u51FA\uFF08code=${u??"signal"}\uFF09`,b(n,`exit code=${u??"signal"}\uFF0C\u51C6\u5907\u91CD\u542F`),this.scheduleListenerRestart(e,t,n))})}catch(o){const a=o instanceof Error?o.message:String(o);n.lastError=a,b(n,`ERROR spawn: ${a}`),this.scheduleListenerRestart(e,t,n)}}scheduleListenerRestart(e,t,n){if(n.stopping)return;const r=Math.max(500,n.def.transport?.restartBackoffMs??2e3),i=Math.min(Ie,r*2**Math.min(n.restarts,6));n.restarts++,b(n,`restart #${n.restarts} in ${i}ms`),n.backoffTimer=setTimeout(()=>{n.backoffTimer=void 0,n.stopping||this.spawnListenerChild(e,t,n)},i)}async pollListenerOnce(e,t,n){if(n.polling)return;const r=n.def.transport?.command??"",i=Y(r,{});if(i.length===0)return;n.polling=!0;const s=new AbortController,o=setTimeout(()=>s.abort(),J);try{const a=await new Promise((u,c)=>{const d=_(i[0],i.slice(1),{cwd:t.dir,env:this.buildPluginEnv(t,e.agentId),shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"],signal:s.signal});let y="",p="";d.stdout?.on("data",h=>{y.length<R&&(y+=h.toString("utf8"))}),d.stderr?.on("data",h=>{p.length<R&&(p+=h.toString("utf8"))});const g=()=>s.signal.aborted;d.on("error",h=>{c(g()?new Error(`poll \u8D85\u65F6(${J}ms)`):h)}),d.on("close",h=>{g()?c(new Error(`poll \u8D85\u65F6(${J}ms)`)):h===0?u(y):c(new Error(`exit ${h}: ${D(p||y,300)}`))})});n.lastError=void 0;for(const u of a.split(`
13
+ `)){const c=u.trim();c&&this.handleListenerLine(e,t,n,c)}}catch(a){const u=a instanceof Error?a.message:String(a);n.lastError=u,b(n,`poll \u5931\u8D25: ${u}`)}finally{clearTimeout(o),n.polling=!1}}stopListener(e){if(e.stopping=!0,e.restarts=0,e.child){try{e.child.kill()}catch{}e.child=void 0}e.timer&&(clearInterval(e.timer),e.timer=void 0),e.backoffTimer&&(clearTimeout(e.backoffTimer),e.backoffTimer=void 0)}handleListenerLine(e,t,n,r){b(n,`event: ${D(r,300)}`);let i;try{i=JSON.parse(r)}catch{n.lastError="\u4E8B\u4EF6\u884C\u4E0D\u662F\u5408\u6CD5 JSON\uFF08\u8981\u6C42 JSONL\uFF09";return}if(!v(i))return;const s=typeof i.event=="string"?i.event:n.def.event,o=s===n.def.event?n.def:t.listeners.find(a=>a.event===s);o&&(o.delivery&&o.delivery!=="poll"&&f.debug("[plugin:%s] delivery=%s \u6682\u6309 poll \u4EA4\u4ED8",t.name,o.delivery),this.enqueueEvent(e,t,o,i))}enqueueEvent(e,t,n,r){for(const[p,g]of Object.entries(n.matcher??{})){const h=I(r,p);if(!(Array.isArray(g)?g:[g]).some(A=>JSON.stringify(A)===JSON.stringify(h)))return}let i;if(n.dedupKey){const p=I(r,n.dedupKey);p!=null&&(i=D(String(p),256))}e.eventSeq++;const s=D(He(r.data??r),Ae),o=n.promptTemplate?je(n.promptTemplate,r):`\u3010\u63D2\u4EF6\u4E8B\u4EF6\u3011${t.name} \xB7 ${n.event}${n.description?`
14
14
  ${n.description}`:""}`,a=(n.toolHints??[]).join(","),u=`${o}
15
- [plugin-event] plugin=${t.name} event=${n.event}`+(a?` toolHints=${a}`:"")+` payload=${s}`,c={msgId:A(i?`plugin-event-${t.name}-${i}`:`plugin-event-${t.name}-${n.event}-${e.eventSeq}`,512),channel:"plugin-event",senderId:`plugin:${t.name}`,senderName:`\u63D2\u4EF6[${t.name}]`,receiverId:e.agentId||void 0,content:u,requireReply:!1,role:"system",timestamp:new Date().toISOString()},d=n.priority??"normal";e.buffer.push({plugin:t.name,event:n.event,priority:d,dedupKey:i,receivedAt:Date.now(),message:c})}requiredParamsOf(e){const t=e.inputSchema;if(!(!t||!Array.isArray(t.required)))return t.required.filter(n=>typeof n=="string")}toSummary(e){return{name:e.name,version:e.version,description:e.description,status:e.status,enabled:e.enabled,source:e.source,installedAt:e.installedAt,toolCount:e.tools.length,tools:e.tools.map(t=>({name:t.name,title:t.title,description:t.description})),listeners:e.runtimes.map(t=>({event:t.def.event,delivery:t.def.delivery??"poll",running:!!(t.child||t.timer)||t.def.transport?.type==="http-callback",error:t.lastError})),running:e.runtimes.some(t=>!!(t.child||t.timer)||t.def.transport?.type==="http-callback"),error:e.error}}async getLogs(e,t){const n=await this.hydrate(e);if(t===$)return{ok:!0,logs:[`\u2500\u2500 ${$} \u4E3A\u7CFB\u7EDF\u5185\u7F6E\u63D2\u4EF6\uFF08\u65E0\u9700\u5B89\u88C5\uFF09\uFF0C\u65E0\u72EC\u7ACB\u76D1\u542C\u5668\u8FD0\u884C\u65E5\u5FD7\u3002`,"\u5176\u751F\u547D\u5468\u671F\u64CD\u4F5C\uFF08install/reload/disable/enable/list\uFF09\u7531 bridge plugin-manager \u5904\u7406\uFF0C\u53EF\u901A\u8FC7\u300C\u91CD\u8F7D/\u542F\u7528/\u505C\u7528\u300D\u6309\u94AE\u89C2\u5BDF\u72B6\u6001\u53D8\u5316\u3002"]};const r=n.plugins.get(t);if(!r)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5`,logs:[]};const i=[];for(const s of r.runtimes)i.push(`\u2500\u2500 listener ${s.def.event} (${s.def.transport?.type}) ${s.lastError?`[error: ${s.lastError}]`:""}`),i.push(...s.logs);return{ok:!0,logs:i}}async stopAgent(e){const t=this.agents.get(e);if(t){for(const n of t.plugins.values())this.stopAllListeners(n);this.agents.delete(e)}}async stopAll(){for(const e of[...this.agents.keys()])await this.stopAgent(e)}async listDeployed(){await this.migrateLegacySharedIfNeeded();const e=this.readSharedManifest(),t=[];for(const n of e){const r=this.deployedPluginDir(n.pluginName),i=this.deployedPluginHealthy(n.pluginName);let s;if(i)try{const o=JSON.parse(k(m(r,"plugin.json"),"utf8"));typeof o.version=="string"&&(s=o.version)}catch{}t.push({pluginName:n.pluginName,version:s,fileName:n.fileName??`${n.pluginName}.zip`,defaultEnabled:n.defaultEnabled!==!1,sha256:n.sha256,deployed:i})}return t}deployedDefaultEnabled(){const e=this.readSharedManifest(),t={};for(const n of e)t[n.pluginName]=n.defaultEnabled!==!1;return t}async removeDeployedPlugin(e){if(!V(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};await this.migrateLegacySharedIfNeeded();for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const n=t.queue.then(async()=>{const r=t.plugins.get(e);r&&(this.stopAllListeners(r),t.plugins.delete(e)),this.removeRuntimeLink(t.agentId,e),this.persistInstalled(t)});t.queue=n.catch(()=>{}),await n}await W(this.deployedManifestFile(),async()=>{const t=this.readSharedManifest().filter(n=>n.pluginName!==e);this.writeSharedManifest(t)});try{E(this.deployedPluginDir(e),{recursive:!0,force:!0})}catch(t){return{ok:!1,error:`\u90E8\u7F72\u76EE\u5F55\u6E05\u7406\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`}}return f.info("[shared] \u63D2\u4EF6\u5DF2\u4ECE bridge \u79FB\u9664\u90E8\u7F72: %s",e),{ok:!0}}}function V(l){return typeof l=="string"&&l.length>0&&l.length<=64&&z.test(l)}function _e(l){const e=l.name;if(typeof e!="string"||!z.test(e))return`plugin.json: name \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF09: ${String(e)}`;if(e.length>64)return"plugin.json: name \u8FC7\u957F\uFF08\u226464\uFF09";for(const t of De)if(e===t.replace(/\.$/,"")||e.startsWith(t))return`plugin.json: name \u4F7F\u7528\u4E86\u4FDD\u7559\u524D\u7F00 ${t}`;return Ne.test(e)?"plugin.json: name \u542B\u975E ASCII \u5B57\u7B26\uFF08\u540C\u5F62\u5B57\u9632\u62A4\uFF09":l.version!==void 0&&typeof l.version!="string"?"plugin.json: version \u987B\u4E3A\u5B57\u7B26\u4E32":l.userConfig!==void 0&&!Array.isArray(l.userConfig)?"plugin.json: userConfig \u987B\u4E3A\u6570\u7EC4":null}function Fe(l){if(!P(l))return"\u5DE5\u5177\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";const e=l.name;if(typeof e!="string"||!be.test(e))return`\u5DE5\u5177 name \u975E\u6CD5: ${String(e)}`;if(typeof l.description!="string"||!l.description.trim())return`\u5DE5\u5177 ${e} \u7F3A\u5C11 description`;const t=l.handler;if(!P(t))return`\u5DE5\u5177 ${e} \u7F3A\u5C11 handler`;const n=t.type;return n!=="process"&&n!=="module"&&n!=="mcp"?`\u5DE5\u5177 ${e} handler.type \u975E\u6CD5: ${String(n)}`:n==="process"&&typeof t.command!="string"?`\u5DE5\u5177 ${e} \u7684 process handler \u7F3A\u5C11 command`:n==="module"&&typeof t.module!="string"?`\u5DE5\u5177 ${e} \u7684 module handler \u7F3A\u5C11 module`:l.inputSchema!==void 0&&!P(l.inputSchema)?`\u5DE5\u5177 ${e} inputSchema \u987B\u4E3A\u5BF9\u8C61`:null}function je(l){if(!P(l))return"\u76D1\u542C\u5668\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";if(typeof l.event!="string"||!l.event.trim())return"\u76D1\u542C\u5668\u7F3A\u5C11 event";const e=l.transport;if(!P(e))return`\u76D1\u542C\u5668 ${String(l.event)} \u7F3A\u5C11 transport`;const t=e.type;if(t!=="command-stream"&&t!=="poll-command"&&t!=="http-callback")return`\u76D1\u542C\u5668 ${String(l.event)} transport.type \u975E\u6CD5: ${String(t)}`;if((t==="command-stream"||t==="poll-command")&&typeof e.command!="string")return`\u76D1\u542C\u5668 ${String(l.event)} \u7684 ${t} transport \u7F3A\u5C11 command`;const n=l.delivery;return n!==void 0&&n!=="poll"&&n!=="force"&&n!=="wake"?`\u76D1\u542C\u5668 ${String(l.event)} delivery \u975E\u6CD5: ${String(n)}`:null}function Y(l,e){return l.trim().split(/\s+/).map(t=>{const n=/^\{\{(.+)\}\}$/.exec(t);if(n){const r=I(e,n[1].trim());return r==null?"":typeof r=="string"?r:JSON.stringify(r)}return t.replace(/\{\{(.+?)\}\}/g,(r,i)=>{const s=I(e,i.trim());return s==null?"":typeof s=="string"?s:JSON.stringify(s)})}).filter(t=>t!=="")}function He(l,e){return l.replace(/\{\{(.+?)\}\}/g,(t,n)=>{let r=I(e,n.trim());return r===void 0&&!n.trim().startsWith("params.")&&(r=I(e,`data.${n.trim()}`)),r==null?"":typeof r=="string"?r:JSON.stringify(r)})}function Ue(l){try{return JSON.stringify(l)??"null"}catch{return"(\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u4E8B\u4EF6\u8D1F\u8F7D)"}}const Je=new Me;he("plugins",()=>Je.deployedDefaultEnabled());const tt=()=>ye("plugins");export{X as BUILTIN_PLUGIN_DESCRIPTION,Q as BUILTIN_PLUGIN_MANAGE_DESC,$ as BUILTIN_PLUGIN_NAME,Me as PluginManager,Ee as getBuiltinCommandDir,V as isValidPluginName,Je as pluginManager,tt as reconcilePluginsDefaultsFromDeployed,Y as renderCommandArgv,B as validateAgainstSchema,_e as validateManifest};
15
+ [plugin-event] plugin=${t.name} event=${n.event}`+(a?` toolHints=${a}`:"")+` payload=${s}`,c={msgId:D(i?`plugin-event-${t.name}-${i}`:`plugin-event-${t.name}-${n.event}-${e.eventSeq}`,512),channel:"plugin-event",senderId:`plugin:${t.name}`,senderName:`\u63D2\u4EF6[${t.name}]`,receiverId:e.agentId||void 0,content:u,requireReply:!1,role:"system",timestamp:new Date().toISOString()},d=n.priority??"normal";e.buffer.push({plugin:t.name,event:n.event,priority:d,dedupKey:i,receivedAt:Date.now(),message:c})&&this.exposePlugin(e.agentId,t.name)}requiredParamsOf(e){const t=e.inputSchema;if(!(!t||!Array.isArray(t.required)))return t.required.filter(n=>typeof n=="string")}toSummary(e){return{name:e.name,version:e.version,description:e.description,status:e.status,enabled:e.enabled,source:e.source,installedAt:e.installedAt,toolCount:e.tools.length,tools:e.tools.map(t=>({name:t.name,title:t.title,description:t.description})),listeners:e.runtimes.map(t=>({event:t.def.event,delivery:t.def.delivery??"poll",running:!!(t.child||t.timer)||t.def.transport?.type==="http-callback",error:t.lastError})),running:e.runtimes.some(t=>!!(t.child||t.timer)||t.def.transport?.type==="http-callback"),error:e.error}}async getLogs(e,t){const n=await this.hydrate(e);if(t===$)return{ok:!0,logs:[`\u2500\u2500 ${$} \u4E3A\u7CFB\u7EDF\u5185\u7F6E\u63D2\u4EF6\uFF08\u65E0\u9700\u5B89\u88C5\uFF09\uFF0C\u65E0\u72EC\u7ACB\u76D1\u542C\u5668\u8FD0\u884C\u65E5\u5FD7\u3002`,"\u5176\u751F\u547D\u5468\u671F\u64CD\u4F5C\uFF08install/reload/disable/enable/list\uFF09\u7531 bridge plugin-manager \u5904\u7406\uFF0C\u53EF\u901A\u8FC7\u300C\u91CD\u8F7D/\u542F\u7528/\u505C\u7528\u300D\u6309\u94AE\u89C2\u5BDF\u72B6\u6001\u53D8\u5316\u3002"]};const r=n.plugins.get(t);if(!r)return{ok:!1,error:`\u63D2\u4EF6 ${t} \u672A\u5B89\u88C5`,logs:[]};const i=[];for(const s of r.runtimes)i.push(`\u2500\u2500 listener ${s.def.event} (${s.def.transport?.type}) ${s.lastError?`[error: ${s.lastError}]`:""}`),i.push(...s.logs);return{ok:!0,logs:i}}async stopAgent(e){const t=this.agents.get(e);if(t){for(const n of t.plugins.values())this.stopAllListeners(n);this.agents.delete(e)}}async stopAll(){for(const e of[...this.agents.keys()])await this.stopAgent(e)}async listDeployed(){await this.migrateLegacySharedIfNeeded();const e=this.readSharedManifest(),t=[];for(const n of e){const r=this.deployedPluginDir(n.pluginName),i=this.deployedPluginHealthy(n.pluginName);let s;if(i)try{const o=JSON.parse(k(m(r,"plugin.json"),"utf8"));typeof o.version=="string"&&(s=o.version)}catch{}t.push({pluginName:n.pluginName,version:s,fileName:n.fileName??`${n.pluginName}.zip`,defaultEnabled:n.defaultEnabled!==!1,sha256:n.sha256,deployed:i})}return t}deployedDefaultEnabled(){const e=this.readSharedManifest(),t={};for(const n of e)t[n.pluginName]=n.defaultEnabled!==!1;return t}async removeDeployedPlugin(e){if(!V(e))return{ok:!1,error:"pluginName \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF0C1~64 \u5B57\u7B26\uFF09"};await this.migrateLegacySharedIfNeeded();for(const t of[...this.agents.values()]){if(!t.hydrated)continue;const n=t.queue.then(async()=>{const r=t.plugins.get(e);r&&(this.stopAllListeners(r),t.plugins.delete(e)),this.removeRuntimeLink(t.agentId,e),this.persistInstalled(t)});t.queue=n.catch(()=>{}),await n}await W(this.deployedManifestFile(),async()=>{const t=this.readSharedManifest().filter(n=>n.pluginName!==e);this.writeSharedManifest(t)});try{E(this.deployedPluginDir(e),{recursive:!0,force:!0})}catch(t){return{ok:!1,error:`\u90E8\u7F72\u76EE\u5F55\u6E05\u7406\u5931\u8D25: ${t instanceof Error?t.message:String(t)}`}}return f.info("[shared] \u63D2\u4EF6\u5DF2\u4ECE bridge \u79FB\u9664\u90E8\u7F72: %s",e),{ok:!0}}}function V(l){return typeof l=="string"&&l.length>0&&l.length<=64&&z.test(l)}function _e(l){const e=l.name;if(typeof e!="string"||!z.test(e))return`plugin.json: name \u975E\u6CD5\uFF08\u987B\u4E3A kebab-case\uFF09: ${String(e)}`;if(e.length>64)return"plugin.json: name \u8FC7\u957F\uFF08\u226464\uFF09";for(const t of be)if(e===t.replace(/\.$/,"")||e.startsWith(t))return`plugin.json: name \u4F7F\u7528\u4E86\u4FDD\u7559\u524D\u7F00 ${t}`;return Ne.test(e)?"plugin.json: name \u542B\u975E ASCII \u5B57\u7B26\uFF08\u540C\u5F62\u5B57\u9632\u62A4\uFF09":l.version!==void 0&&typeof l.version!="string"?"plugin.json: version \u987B\u4E3A\u5B57\u7B26\u4E32":l.userConfig!==void 0&&!Array.isArray(l.userConfig)?"plugin.json: userConfig \u987B\u4E3A\u6570\u7EC4":null}function Fe(l){if(!v(l))return"\u5DE5\u5177\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";const e=l.name;if(typeof e!="string"||!De.test(e))return`\u5DE5\u5177 name \u975E\u6CD5: ${String(e)}`;if(typeof l.description!="string"||!l.description.trim())return`\u5DE5\u5177 ${e} \u7F3A\u5C11 description`;const t=l.handler;if(!v(t))return`\u5DE5\u5177 ${e} \u7F3A\u5C11 handler`;const n=t.type;return n!=="process"&&n!=="module"&&n!=="mcp"?`\u5DE5\u5177 ${e} handler.type \u975E\u6CD5: ${String(n)}`:n==="process"&&typeof t.command!="string"?`\u5DE5\u5177 ${e} \u7684 process handler \u7F3A\u5C11 command`:n==="module"&&typeof t.module!="string"?`\u5DE5\u5177 ${e} \u7684 module handler \u7F3A\u5C11 module`:l.inputSchema!==void 0&&!v(l.inputSchema)?`\u5DE5\u5177 ${e} inputSchema \u987B\u4E3A\u5BF9\u8C61`:null}function xe(l){if(!v(l))return"\u76D1\u542C\u5668\u6761\u76EE\u987B\u4E3A\u5BF9\u8C61";if(typeof l.event!="string"||!l.event.trim())return"\u76D1\u542C\u5668\u7F3A\u5C11 event";const e=l.transport;if(!v(e))return`\u76D1\u542C\u5668 ${String(l.event)} \u7F3A\u5C11 transport`;const t=e.type;if(t!=="command-stream"&&t!=="poll-command"&&t!=="http-callback")return`\u76D1\u542C\u5668 ${String(l.event)} transport.type \u975E\u6CD5: ${String(t)}`;if((t==="command-stream"||t==="poll-command")&&typeof e.command!="string")return`\u76D1\u542C\u5668 ${String(l.event)} \u7684 ${t} transport \u7F3A\u5C11 command`;const n=l.delivery;return n!==void 0&&n!=="poll"&&n!=="force"&&n!=="wake"?`\u76D1\u542C\u5668 ${String(l.event)} delivery \u975E\u6CD5: ${String(n)}`:null}function Y(l,e){return l.trim().split(/\s+/).map(t=>{const n=/^\{\{(.+)\}\}$/.exec(t);if(n){const r=I(e,n[1].trim());return r==null?"":typeof r=="string"?r:JSON.stringify(r)}return t.replace(/\{\{(.+?)\}\}/g,(r,i)=>{const s=I(e,i.trim());return s==null?"":typeof s=="string"?s:JSON.stringify(s)})}).filter(t=>t!=="")}function je(l,e){return l.replace(/\{\{(.+?)\}\}/g,(t,n)=>{let r=I(e,n.trim());return r===void 0&&!n.trim().startsWith("params.")&&(r=I(e,`data.${n.trim()}`)),r==null?"":typeof r=="string"?r:JSON.stringify(r)})}function He(l){try{return JSON.stringify(l)??"null"}catch{return"(\u4E0D\u53EF\u5E8F\u5217\u5316\u7684\u4E8B\u4EF6\u8D1F\u8F7D)"}}const Ue=new Me;me("plugins",()=>Ue.deployedDefaultEnabled());const tt=()=>ye("plugins");export{X as BUILTIN_PLUGIN_DESCRIPTION,Q as BUILTIN_PLUGIN_MANAGE_DESC,$ as BUILTIN_PLUGIN_NAME,Me as PluginManager,Ee as getBuiltinCommandDir,V as isValidPluginName,Ue as pluginManager,tt as reconcilePluginsDefaultsFromDeployed,Y as renderCommandArgv,B as validateAgainstSchema,_e as validateManifest};
16
16
 
@@ -1,4 +1,4 @@
1
- const c="ask_user_question",f=["\u5411\u7528\u6237\u63D0\u51FA\u7ED3\u6784\u5316\u95EE\u9898\u4EE5\u6536\u96C6\u504F\u597D\u3001\u6F84\u6E05\u9700\u6C42\u3001\u505A\u51B3\u7B56\u6216\u63D0\u4F9B\u65B9\u5411\u9009\u62E9\u3002","\u95EE\u9898\u6570\u91CF\u4E0D\u8BBE\u4E0A\u9650\uFF0C\u6BCF\u4E2A\u95EE\u9898 2-4 \u4E2A\u9009\u9879\uFF1BUI \u4F1A\u81EA\u52A8\u8FFD\u52A0\u201C\u5176\u5B83\u201D\u9009\u9879\u4F9B\u7528\u6237\u81EA\u5B9A\u4E49\u8F93\u5165\u3002","\u591A\u4E2A\u95EE\u9898\u65F6\uFF0C\u95EE\u9898\u4E4B\u95F4\u5FC5\u987B\u662F\u5E76\u5217\u5173\u7CFB\u3001\u76F8\u4E92\u72EC\u7ACB\uFF1A\u540E\u7EED\u95EE\u9898\u4E0D\u5F97\u4F9D\u8D56\u524D\u9762\u95EE\u9898\u7684\u7B54\u6848\uFF08\u4F8B\u5982\u4E0D\u80FD\u51FA\u73B0\u201C\u5982\u679C\u4E0A\u4E00\u4E2A\u9009 X \u90A3\u4E48\u2026\u201D\u7684\u6761\u4EF6\u5F0F\u63D0\u95EE\uFF09\uFF0C","\u4E5F\u4E0D\u8981\u5728\u9009\u9879\u6587\u6848\u4E2D\u5F15\u7528\u5176\u4ED6\u95EE\u9898\u7684\u56DE\u7B54\uFF1B\u82E5\u67D0\u95EE\u9898\u4F9D\u8D56\u53E6\u4E00\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u5E94\u62C6\u6210\u591A\u8F6E\u5206\u522B\u8C03\u7528\u672C\u5DE5\u5177\u3002","\u5DE5\u5177\u8C03\u7528\u4F1A\u963B\u585E\u5F53\u524D\u8F6E\u6B21\u76F4\u5230\u7528\u6237\u56DE\u7B54\uFF1B\u7528\u6237\u56DE\u7B54\u540E\u7B54\u6848\u4F1A\u4F5C\u4E3A tool result \u56DE\u4F20\u7ED9\u4F60\u3002","\u8BF7\u52FF\u7528\u6B64\u5DE5\u5177\u8BF7\u6C42\u8BA1\u5212\u5BA1\u6279\u2014\u2014\u90A3\u662F plan \u5DE5\u5177\u7684\u804C\u8D23\u3002","\u5982\u679C\u63A8\u8350\u67D0\u4E2A\u9009\u9879\uFF0C\u628A\u5B83\u653E\u5728\u7B2C\u4E00\u4F4D\u5E76\u5728 label \u672B\u5C3E\u52A0\u201C\uFF08\u63A8\u8350\uFF09\u201D\u3002"].join(`
1
+ const c="ask_user_question",f=["\u5411\u7528\u6237\u63D0\u51FA\u95EE\u9898\uFF0C\u6536\u96C6\u66F4\u591A\u4FE1\u606F\u4E3A\u505A\u51B3\u7B56\u63D0\u4F9B\u66F4\u591A\u7684\u65B9\u5411\u9009\u62E9\uFF0C\u6BCF\u4E2A\u95EE\u9898\u4F1A\u81EA\u52A8\u8FFD\u52A0\u201C\u5176\u5B83\u201D\u9009\u9879\u4F9B\u7528\u6237\u81EA\u5B9A\u4E49\u8F93\u5165\u3002","\u591A\u4E2A\u95EE\u9898\u65F6\u4E0D\u5F97\u4F9D\u8D56\u524D\u9762\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u82E5\u67D0\u95EE\u9898\u4F9D\u8D56\u53E6\u4E00\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u5E94\u62C6\u6210\u591A\u8F6E\u5206\u522B\u8C03\u7528\u672C\u5DE5\u5177\u3002","\u63D0\u4EA4\u540E\u8FDB\u5165\u7528\u6237\u5BA1\u6279\uFF0C\u5BA1\u6279\u901A\u8FC7\u7684\u9879\u4F1A\u76F4\u63A5\u521B\u5EFA\uFF08\u7ED3\u679C\u968F\u5DE5\u5177\u8FD4\u56DE\uFF09\u3002","\u5982\u679C\u63A8\u8350\u67D0\u4E2A\u9009\u9879\uFF0C\u628A\u5B83\u653E\u5728\u7B2C\u4E00\u4F4D\u5E76\u5728 label \u672B\u5C3E\u52A0\u201C\uFF08\u63A8\u8350\uFF09\u201D\u3002"].join(`
2
2
  `);function I(o,e){return{kind:"builtin",toolName:c,exposedName:c,description:f,inputSchema:m(),async execute(n,t){const r=t?.toolCallId;if(!r)return{ok:!1,tool:c,error:"missing toolCallId in execution context"};const s=y(n.questions);if(s.length===0)return{ok:!1,tool:c,error:"questions must be a non-empty array"};o.onQuestion(r,s);const u=e();let i;try{i=await o.waitForAnswer(r,u)}catch(p){const a=p instanceof Error?p.message:String(p);return{ok:!1,tool:c,error:a}}const l=b(s,i);return{ok:!0,tool:c,answers:i,summary:l}}}}function m(){return{type:"object",properties:{questions:{type:"array",description:"\u8981\u63D0\u95EE\u7684\u95EE\u9898\u5217\u8868\uFF08\u81F3\u5C11 1 \u4E2A\uFF0C\u6570\u91CF\u4E0D\u8BBE\u4E0A\u9650\uFF09\uFF1B\u591A\u4E2A\u95EE\u9898\u65F6\u5FC5\u987B\u76F8\u4E92\u72EC\u7ACB\u3001\u5E76\u5217\u63D0\u95EE\uFF0C\u540E\u7EED\u95EE\u9898\u4E0D\u5F97\u4F9D\u8D56\u524D\u9762\u95EE\u9898\u7684\u7B54\u6848\uFF0C\u6709\u4F9D\u8D56\u5173\u7CFB\u65F6\u5E94\u62C6\u6210\u591A\u6B21\u8C03\u7528",minItems:1,items:{type:"object",properties:{question:{type:"string",description:"\u5B8C\u6574\u95EE\u9898\u6587\u672C\uFF0C\u5EFA\u8BAE\u4EE5\u95EE\u53F7\u7ED3\u5C3E"},header:{type:"string",description:"\u226412 \u5B57\u7B26\u7684\u7B80\u77ED\u6807\u7B7E\uFF0C\u7528\u4E8E UI chip \u663E\u793A"},options:{type:"array",description:"2-4 \u4E2A\u4E92\u65A5\u9009\u9879\uFF08multiSelect=true \u65F6\u53EF\u4E0D\u4E92\u65A5\uFF09",minItems:2,maxItems:4,items:{type:"object",properties:{label:{type:"string",description:"1-5 \u8BCD\u7684\u7B80\u77ED\u6807\u7B7E"},description:{type:"string",description:"\u9009\u9879\u542B\u4E49\u6216\u6743\u8861\u8BF4\u660E"},requiresCustomInput:{type:"boolean",description:"\u9009\u4E2D\u6B64\u9879\u65F6 UI \u663E\u793A\u81EA\u5B9A\u4E49\u8F93\u5165\u6846"}},required:["label"]}},multiple:{type:"boolean",description:"\u662F\u5426\u5141\u8BB8\u591A\u9009\uFF1B\u9ED8\u8BA4 false"}},required:["question","options"]}},custom:{type:"boolean",description:"\u662F\u5426\u5141\u8BB8\u7528\u6237\u8865\u5145\u81EA\u5B9A\u4E49\u8F93\u5165\uFF1B\u9ED8\u8BA4 true"}},required:["questions"]}}function y(o){if(!Array.isArray(o)||o.length===0)return[];const e=[];for(const n of o){if(!n||typeof n!="object")continue;const t=n,r=typeof t.question=="string"?t.question.trim():"";if(!r)continue;const s=d(t.options);if(s.length<2)continue;const u=typeof t.header=="string"&&t.header.trim()?t.header.trim():void 0,i=t.multiple===!0||t.multiSelect===!0;e.push({question:r,header:u,options:s,multiple:i})}return e}function d(o){if(!Array.isArray(o))return[];const e=[];for(const n of o){if(typeof n=="string"){const i=n.trim();i&&e.push({label:i});continue}if(!n||typeof n!="object")continue;const t=n,r=typeof t.label=="string"?t.label.trim():typeof t.value=="string"?t.value.trim():typeof t.text=="string"?t.text.trim():"";if(!r)continue;const s=typeof t.description=="string"&&t.description.trim()?t.description.trim():void 0,u=t.requiresCustomInput===!0||t.requireCustomInput===!0||t.allowCustomInput===!0;if(e.push({label:r,...s?{description:s}:{},...u?{requiresCustomInput:u}:{}}),e.length>=4)break}return e}function b(o,e){const n=["\u7528\u6237\u5DF2\u56DE\u7B54\u4E86\u60A8\u7684\u95EE\u9898\uFF1A"];return o.forEach((t,r)=>{const s=String(r),u=e[s]||(t.header?e[t.header]:"")||e[t.question]||"\uFF08\u672A\u586B\u5199\uFF09",i=t.header||t.question;n.push(`\u2022 ${i}\uFF1A${u}`)}),n.join(`
3
3
  `)}function q(o){if(!o||typeof o!="object")return!1;const e=o;return e.ok===!0&&e.tool===c&&typeof e.answers=="object"&&e.answers!==null&&typeof e.summary=="string"}function j(o){const e={ok:!0,tool:c,answers:g(o.answers),summary:o.summary};return JSON.stringify(e)}function g(o){const e={};for(const[n,t]of Object.entries(o))e[n]=t;return e}export{c as ASK_USER_QUESTION_TOOL_NAME,I as createAskUserQuestionTool,q as isAskUserQuestionResult,j as serializeAskUserQuestionResult};
4
4
 
@@ -1,4 +1,4 @@
1
- const g=`Block and wait for one or more sub-agents AND/OR background commands to complete.
1
+ const _=6e5,g=`Block and wait for one or more sub-agents AND/OR background commands to complete.
2
2
 
3
3
  This is the unified replacement for the old await_sub_agents: it can wait for BOTH
4
4
  sub-agents (spawned via sub_agent) and background commands (started via Bash with
@@ -15,7 +15,7 @@ to fetch full output.
15
15
  Guidelines:
16
16
  - Pass sub-agent IDs in 'sub_agent_ids' and/or background command task_ids in 'bg_cmd_ids'
17
17
  - At least one of the two arrays must be non-empty
18
- - Use timeout_ms to avoid waiting forever (0 = no timeout, default 0)
19
- - After this returns, call poll_message to receive any pending messages`;function h(o){return{kind:"builtin",toolName:"await_complete",exposedName:"await_complete",description:g,inputSchema:{type:"object",properties:{sub_agent_ids:{type:"array",items:{type:"string"},description:"Array of sub-agent IDs to wait for (from sub_agent). Empty if none."},bg_cmd_ids:{type:"array",items:{type:"string"},description:"Array of background command task_ids to wait for (from Bash wait_seconds or list_background_commands). Empty if none."},timeout_ms:{type:"number",description:"Maximum time to wait in milliseconds, applied to the whole JOIN. 0 = no timeout (wait forever). Default: 0."}},additionalProperties:!1},async execute(i){const l=typeof i.timeout_ms=="number"?i.timeout_ms:0,m=i.sub_agent_ids,c=i.bg_cmd_ids,n=Array.isArray(m)?m.filter(t=>typeof t=="string"&&t.trim().length>0):[],u=Array.isArray(c)?c.filter(t=>typeof t=="string"&&t.trim().length>0):[];if(n.length===0&&u.length===0)return{title:"await_complete validation error",output:"Invalid input: provide at least one of 'sub_agent_ids' or 'bg_cmd_ids'.",metadata:{error:!0}};if(n.length>0&&!o.awaitSubAgents)return{title:"await_complete error",output:"sub_agent_ids provided but sub-agent manager is not enabled for this agent.",metadata:{error:!0}};try{let t=[];n.length>0&&o.awaitSubAgents&&(t=await o.awaitSubAgents(n,l)??[]);let a=[];u.length>0&&(a=await o.awaitBgCommands(u,l));const s=[],p=[],d=[];if(t.length>0){s.push(`=== ${t.length} sub-agent(s) ===`);for(const e of t)s.push(`[${e.status}] ${e.subId}`),e.error?(s.push("<task_error>"),s.push(e.error),s.push("</task_error>")):(s.push("<task_result>"),s.push(e.output||"(empty output)"),s.push("</task_result>")),s.push(""),p.push(e)}if(a.length>0){s.push(`=== ${a.length} background command(s) ===`);for(const e of a){if(!e){s.push("[not_found] (unknown task_id)"),s.push(""),d.push(null);continue}const r=[];e.timedOut&&r.push("timed_out"),e.aborted&&r.push("aborted"),s.push(`[${e.status}] ${e.taskId} exit=${e.exitCode??"?"}`+(r.length?` (${r.join(",")})`:"")+(e.outputFilePath?` output=${e.outputFilePath}`:"")),s.push(""),d.push(e)}}return{title:`awaited ${t.length+a.length} item(s)`,output:s.join(`
18
+ - Use timeout_ms to bound the wait (default 600000 = 10 minutes; 0 = wait forever)
19
+ - After this returns, call poll_message to receive any pending messages`;function h(o){return{kind:"builtin",toolName:"await_complete",exposedName:"await_complete",description:g,inputSchema:{type:"object",properties:{sub_agent_ids:{type:"array",items:{type:"string"},description:"Array of sub-agent IDs to wait for (from sub_agent). Empty if none."},bg_cmd_ids:{type:"array",items:{type:"string"},description:"Array of background command task_ids to wait for (from Bash wait_seconds or list_background_commands). Empty if none."},timeout_ms:{type:"number",description:"Maximum time to wait in milliseconds, applied to the whole JOIN. 0 = no timeout (wait forever). Default: 600000 (10 minutes)."}},additionalProperties:!1},async execute(i){const l=typeof i.timeout_ms=="number"?i.timeout_ms:6e5,m=i.sub_agent_ids,c=i.bg_cmd_ids,n=Array.isArray(m)?m.filter(t=>typeof t=="string"&&t.trim().length>0):[],u=Array.isArray(c)?c.filter(t=>typeof t=="string"&&t.trim().length>0):[];if(n.length===0&&u.length===0)return{title:"await_complete validation error",output:"Invalid input: provide at least one of 'sub_agent_ids' or 'bg_cmd_ids'.",metadata:{error:!0}};if(n.length>0&&!o.awaitSubAgents)return{title:"await_complete error",output:"sub_agent_ids provided but sub-agent manager is not enabled for this agent.",metadata:{error:!0}};try{let t=[];n.length>0&&o.awaitSubAgents&&(t=await o.awaitSubAgents(n,l)??[]);let a=[];u.length>0&&(a=await o.awaitBgCommands(u,l));const s=[],p=[],d=[];if(t.length>0){s.push(`=== ${t.length} sub-agent(s) ===`);for(const e of t)s.push(`[${e.status}] ${e.subId}`),e.error?(s.push("<task_error>"),s.push(e.error),s.push("</task_error>")):(s.push("<task_result>"),s.push(e.output||"(empty output)"),s.push("</task_result>")),s.push(""),p.push(e)}if(a.length>0){s.push(`=== ${a.length} background command(s) ===`);for(const e of a){if(!e){s.push("[not_found] (unknown task_id)"),s.push(""),d.push(null);continue}const r=[];e.timedOut&&r.push("timed_out"),e.aborted&&r.push("aborted"),s.push(`[${e.status}] ${e.taskId} exit=${e.exitCode??"?"}`+(r.length?` (${r.join(",")})`:"")+(e.outputFilePath?` output=${e.outputFilePath}`:"")),s.push(""),d.push(e)}}return{title:`awaited ${t.length+a.length} item(s)`,output:s.join(`
20
20
  `),metadata:{total:t.length+a.length,sub_agents:p,bg_cmds:d}}}catch(t){return{title:"await_complete error",output:`Failed to await: ${t instanceof Error?t.message:String(t)}`,metadata:{error:!0}}}}}}export{h as createAwaitCompleteTool};
21
21
 
@@ -115,24 +115,39 @@ export interface TaskOutputSnapshot {
115
115
  }
116
116
  export interface ReadOutputArgs {
117
117
  task_id: string;
118
- mode?: "head" | "tail" | "offset" | "grep";
119
- lines?: number;
120
- offset?: number;
118
+ mode?: "lines" | "grep" | "tail";
119
+
120
+ line_size?: number;
121
+
122
+ start_line?: number;
121
123
  pattern?: string;
124
+ context_before?: number;
125
+ context_after?: number;
122
126
  include_metadata?: boolean;
123
127
  }
128
+
129
+ export interface ReadOutputLine {
130
+ line: number;
131
+ text: string;
132
+ }
124
133
  export interface ReadOutputResult {
125
134
  taskId: string;
126
135
  status: BackgroundTaskStatus;
127
136
  mode: string;
128
- lines: string[];
137
+ lines: ReadOutputLine[];
129
138
  lineCount: number;
130
139
  metadata?: {
140
+
131
141
  totalLines: number;
132
142
  totalBytes: number;
133
143
  returnedBytes: number;
144
+
134
145
  truncated: boolean;
146
+
147
+ outputTruncated: boolean;
135
148
  hasMore: boolean;
149
+
150
+ nextStartLine: number | null;
136
151
  outputFile: string;
137
152
  };
138
153
  }
@@ -170,8 +185,11 @@ export declare class BackgroundCommandManager {
170
185
  getTaskOutputSnapshot(agentId: string, taskId: string, tailLines?: number): TaskOutputSnapshot | null;
171
186
 
172
187
  subscribeTaskOutput(agentId: string, taskId: string, subscriber: TaskOutputSubscriber): (() => void) | null;
188
+
173
189
  readOutput(agentId: string, args: ReadOutputArgs): Promise<ReadOutputResult>;
174
190
 
191
+ private _emptyResult;
192
+
175
193
  private readFromFile;
176
194
 
177
195
  awaitCompletion(agentId: string, taskIds: string[], timeoutMs?: number): Promise<Array<AwaitBgCommandResult | null>>;
@@ -1,7 +1,7 @@
1
- import{spawn as L}from"node:child_process";import{promises as T}from"node:fs";import{createInterface as _}from"node:readline";import{randomBytes as g}from"node:crypto";import{prepareShellCommand as E,shellExecutable as I}from"./builtins/shellCommandPreparation.js";import{createShellOutputDecoder as y,decodeShellOutputChunk as p}from"./builtins/shellOutputDecoder.js";import{BackgroundOutputRingBuffer as O}from"./background-output-ringbuffer.js";import{BackgroundCommandStore as x}from"./background-command-store.js";const R=1e3,A=10*60*1e3,w=50*1024*1024,B=16,C=256,D=256*1024,S=500,F=5e3,z=256,v=5*60*1e3,M=1024*1024;class G{constructor(){this.tasksByAgent=new Map,this.store=new x}globalCount(){let t=0;for(const i of this.tasksByAgent.values())t+=i.size;return t}startCommand(t){const i=t.agentId,e=this.getOrCreateBucket(i);if(e.size>=B)throw new b("too_many_tasks",`Agent ${i} has ${e.size} background tasks (limit ${B}). Kill or wait for completion first.`);if(this.globalCount()>=C)throw new b("global_limit_reached",`Global background task limit ${C} reached.`);const n=this.generateTaskId(e),s=Math.min(t.timeoutMs??A,A),o=typeof t.inactivityTimeoutMs=="number"&&Number.isFinite(t.inactivityTimeoutMs)&&t.inactivityTimeoutMs>0?t.inactivityTimeoutMs:0,r=this.store.outputPathFor(i,n),u=t.commandText.length>80?`${t.commandText.slice(0,77)}...`:t.commandText,a={taskId:n,agentId:i,command:t.commandText,commandPreview:u,cwd:t.cwd,status:"running",startedAt:Date.now(),finishedAt:null,exitCode:null,signal:null,killedReason:null,timeoutMs:s,inactivityTimeoutMs:o,child:null,ringBuffer:new O,outputPath:r,outputWriteChain:Promise.resolve(),outputTruncated:!1,outputLineCount:0,outputBytes:0,notified:!1,completionWaiters:[],onCompleted:t.onCompleted,onOutput:t.onOutput,outputSeq:0,outputSubscribers:new Set,timeoutTimer:null,killEscalationTimer:null,retentionTimer:null,inactivityTimer:null,inactivityTimedOut:!1,finalized:!1,stdoutDecoder:y(),stderrDecoder:y(),stdoutText:"",stderrText:"",commandLineCount:0},l=new Promise(c=>{a.completionResolve=c});return e.set(n,a),this.spawnAndAttach(a,t).catch(c=>{a.ringBuffer.push(`[ACode] spawn failed: ${c?.message??String(c)}
2
- `),this.finalizeTask(a,-1,null,"spawn_error")}),this.persistTask(a),{taskId:n,completion:l}}async spawnAndAttach(t,i){const e=I(),n=await E(i.commandText,i.workspaceRoot,e),s=process.platform==="win32",o=L(e.command,[...e.argsPrefix,n.commandText],{cwd:i.cwd,env:process.env,detached:!s,stdio:["ignore","pipe","pipe"],windowsVerbatimArguments:s,windowsHide:!0});t.child=o,t.commandFilePath=n.commandFilePath,t.commandLineCount=n.commandLineCount,t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),o.stdout?.on("data",r=>{const u=p(t.stdoutDecoder,r);this.onOutput(t,"stdout",u)}),o.stderr?.on("data",r=>{const u=p(t.stderrDecoder,r);this.onOutput(t,"stderr",u)}),o.on("error",r=>{this.onOutput(t,"stderr",`[ACode] child error: ${r.message}
3
- `),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),o.on("close",(r,u)=>{const a=p(t.stdoutDecoder,void 0,!0),l=p(t.stderrDecoder,void 0,!0);if(a&&this.onOutput(t,"stdout",a),l&&this.onOutput(t,"stderr",l),t.ringBuffer.flush(),!t.finalized){const c=t.killedReason??null;this.finalizeTask(t,r,u,c??void 0)}}),t.timeoutTimer=setTimeout(()=>{t.finalized||(t.killedReason="timeout",this.killProcessGroup(o))},t.timeoutMs),t.timeoutTimer.unref?.()}disableInactivityTimeout(t,i){const e=this.getTask(t,i);return e?(e.inactivityTimeoutMs=0,e.inactivityTimer&&(clearTimeout(e.inactivityTimer),e.inactivityTimer=null),!0):!1}onOutput(t,i,e){if(!e)return;t.outputSeq++;const n=t.outputSeq;if(t.outputSubscribers.size>0)for(const r of t.outputSubscribers)try{r.onLine({stream:i,text:e,seq:n})}catch{}const s=i==="stdout"?"stdoutText":"stderrText";if(Buffer.byteLength(t[s],"utf-8")<M){const r=M-Buffer.byteLength(t[s],"utf-8");t[s]+=r>0?e.slice(0,r):""}if(t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),t.outputBytes>=w){if(!t.outputTruncated){t.outputTruncated=!0;const r=`
1
+ import{spawn as E}from"node:child_process";import{promises as A}from"node:fs";import{createInterface as I}from"node:readline";import{randomBytes as S}from"node:crypto";import{prepareShellCommand as O,shellExecutable as R}from"./builtins/shellCommandPreparation.js";import{createShellOutputDecoder as M,decodeShellOutputChunk as w}from"./builtins/shellOutputDecoder.js";import{BackgroundOutputRingBuffer as D}from"./background-output-ringbuffer.js";import{BackgroundCommandStore as F}from"./background-command-store.js";const z=1e3,G=10*60*1e3,B=50*1024*1024,C=16,b=256,N=256*1024,W=100,X=5e3,$=256,v=20,_=5*60*1e3,L=1024*1024;class q{constructor(){this.tasksByAgent=new Map,this.store=new F}globalCount(){let t=0;for(const i of this.tasksByAgent.values())t+=i.size;return t}startCommand(t){const i=t.agentId,e=this.getOrCreateBucket(i);if(e.size>=C)throw new x("too_many_tasks",`Agent ${i} has ${e.size} background tasks (limit ${C}). Kill or wait for completion first.`);if(this.globalCount()>=b)throw new x("global_limit_reached",`Global background task limit ${b} reached.`);const n=this.generateTaskId(e),o=t.timeoutMs&&t.timeoutMs>0?Math.min(t.timeoutMs,G):0,s=typeof t.inactivityTimeoutMs=="number"&&Number.isFinite(t.inactivityTimeoutMs)&&t.inactivityTimeoutMs>0?t.inactivityTimeoutMs:0,r=this.store.outputPathFor(i,n),u=t.commandText.length>80?`${t.commandText.slice(0,77)}...`:t.commandText,a={taskId:n,agentId:i,command:t.commandText,commandPreview:u,cwd:t.cwd,status:"running",startedAt:Date.now(),finishedAt:null,exitCode:null,signal:null,killedReason:null,timeoutMs:o,inactivityTimeoutMs:s,child:null,ringBuffer:new D,outputPath:r,outputWriteChain:Promise.resolve(),outputTruncated:!1,outputLineCount:0,outputBytes:0,notified:!1,completionWaiters:[],onCompleted:t.onCompleted,onOutput:t.onOutput,outputSeq:0,outputSubscribers:new Set,timeoutTimer:null,killEscalationTimer:null,retentionTimer:null,inactivityTimer:null,inactivityTimedOut:!1,finalized:!1,stdoutDecoder:M(),stderrDecoder:M(),stdoutText:"",stderrText:"",commandLineCount:0},c=new Promise(d=>{a.completionResolve=d});return e.set(n,a),this.spawnAndAttach(a,t).catch(d=>{a.ringBuffer.push(`[ACode] spawn failed: ${d?.message??String(d)}
2
+ `),this.finalizeTask(a,-1,null,"spawn_error")}),this.persistTask(a),{taskId:n,completion:c}}async spawnAndAttach(t,i){const e=R(),n=await O(i.commandText,i.workspaceRoot,e),o=process.platform==="win32",s=E(e.command,[...e.argsPrefix,n.commandText],{cwd:i.cwd,env:process.env,detached:!o,stdio:["ignore","pipe","pipe"],windowsVerbatimArguments:o,windowsHide:!0});t.child=s,t.commandFilePath=n.commandFilePath,t.commandLineCount=n.commandLineCount,t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),s.stdout?.on("data",r=>{const u=w(t.stdoutDecoder,r);this.onOutput(t,"stdout",u)}),s.stderr?.on("data",r=>{const u=w(t.stderrDecoder,r);this.onOutput(t,"stderr",u)}),s.on("error",r=>{this.onOutput(t,"stderr",`[ACode] child error: ${r.message}
3
+ `),t.finalized||this.finalizeTask(t,-1,null,"child_error")}),s.on("close",(r,u)=>{const a=w(t.stdoutDecoder,void 0,!0),c=w(t.stderrDecoder,void 0,!0);if(a&&this.onOutput(t,"stdout",a),c&&this.onOutput(t,"stderr",c),t.ringBuffer.flush(),!t.finalized){const d=t.killedReason??null;this.finalizeTask(t,r,u,d??void 0)}}),t.timeoutMs>0&&(t.timeoutTimer=setTimeout(()=>{t.finalized||(t.killedReason="timeout",this.killProcessGroup(s))},t.timeoutMs),t.timeoutTimer.unref?.())}disableInactivityTimeout(t,i){const e=this.getTask(t,i);return e?(e.inactivityTimeoutMs=0,e.inactivityTimer&&(clearTimeout(e.inactivityTimer),e.inactivityTimer=null),!0):!1}onOutput(t,i,e){if(!e)return;t.outputSeq++;const n=t.outputSeq;if(t.outputSubscribers.size>0)for(const r of t.outputSubscribers)try{r.onLine({stream:i,text:e,seq:n})}catch{}const o=i==="stdout"?"stdoutText":"stderrText";if(Buffer.byteLength(t[o],"utf-8")<L){const r=L-Buffer.byteLength(t[o],"utf-8");t[o]+=r>0?e.slice(0,r):""}if(t.inactivityTimeoutMs>0&&this.scheduleInactivity(t),t.outputBytes>=B){if(!t.outputTruncated){t.outputTruncated=!0;const r=`
4
4
  [ACode] output truncated at 50MB
5
- `;t.ringBuffer.push(r),this.appendToFile(t,r)}t.ringBuffer.push(e);return}t.ringBuffer.push(e),t.onOutput?.({stream:i,text:e});const o=(e.match(/\n/g)??[]).length;t.outputLineCount+=o,t.outputBytes+=Buffer.byteLength(e,"utf-8"),this.appendToFile(t,e)}scheduleInactivity(t){t.finalized||t.inactivityTimeoutMs<=0||(t.inactivityTimer&&clearTimeout(t.inactivityTimer),t.inactivityTimer=setTimeout(()=>{t.finalized||!t.child||(t.inactivityTimedOut=!0,t.killedReason="inactivity",this.killProcessGroup(t.child))},t.inactivityTimeoutMs),t.inactivityTimer.unref?.())}appendToFile(t,i){return t.outputBytes>w?Promise.resolve():(t.outputWriteChain=t.outputWriteChain.then(()=>T.appendFile(t.outputPath,i,"utf-8")).catch(e=>{t.ringBuffer.push(`[ACode] output write failed: ${e?.message??e}
6
- `)}),t.outputWriteChain)}finalizeTask(t,i,e,n){if(t.finalized)return;t.finalized=!0,t.timeoutTimer&&(clearTimeout(t.timeoutTimer),t.timeoutTimer=null),t.killEscalationTimer&&(clearTimeout(t.killEscalationTimer),t.killEscalationTimer=null),t.inactivityTimer&&(clearTimeout(t.inactivityTimer),t.inactivityTimer=null),t.child=null,t.finishedAt=Date.now();let s;n?(s="killed",t.killedReason=n):i===0?s="completed":s="failed",t.status=s,t.exitCode=i,t.signal=e;const o=n==="inactivity"||t.inactivityTimedOut,r={taskId:t.taskId,exitCode:i,signal:e,timedOut:n==="timeout"||o,inactivityTimedOut:o,outputFilePath:t.outputPath,outputLineCount:t.outputLineCount,outputSpilled:t.outputTruncated,commandFilePath:t.commandFilePath,commandLineCount:t.commandLineCount,timeoutMs:t.timeoutMs,inactivityTimeoutMs:t.inactivityTimeoutMs,stdout:t.stdoutText,stderr:t.stderrText,durationMs:t.finishedAt-t.startedAt};t.completionResolve?.(r);const u={taskId:t.taskId,status:s,exitCode:i,signal:e,timedOut:n==="timeout"||o,aborted:!1,durationMs:t.finishedAt-t.startedAt},a=t.completionWaiters;t.completionWaiters=[];for(const l of a)l(u);if(this.persistTask(t),t.onCompleted?.({taskId:t.taskId,agentId:t.agentId,status:s,exitCode:i,signal:e,commandPreview:t.commandPreview,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,outputPath:t.outputPath,finishedAt:t.finishedAt,killedReason:t.killedReason}),t.outputSubscribers.size>0){const l=Array.from(t.outputSubscribers);t.outputSubscribers.clear();for(const c of l)try{c.onDone({status:s,exitCode:i,signal:e})}catch{}}t.retentionTimer=setTimeout(()=>{this.evictTask(t.agentId,t.taskId)},v),t.retentionTimer.unref?.()}evictTask(t,i){const e=this.tasksByAgent.get(t);if(!e)return;const n=e.get(i);n&&(T.unlink(n.outputPath).catch(()=>{}),e.delete(i),e.size===0&&this.tasksByAgent.delete(t))}listTasks(t,i){const e=this.tasksByAgent.get(t);if(!e)return[];const n=[];for(const s of e.values())i&&i!=="all"&&s.status!==i||n.push(this.toSummary(s));return n.sort((s,o)=>o.startedAt-s.startedAt)}toSummary(t){return{taskId:t.taskId,status:t.status,command:t.command,commandPreview:t.commandPreview,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}}getTaskOutputSnapshot(t,i,e=500){const n=this.getTask(t,i);if(!n)return null;const s=n.ringBuffer.tail(e);return{taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:s,nextSeq:n.outputSeq+1,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:n.outputTruncated}}subscribeTaskOutput(t,i,e){const n=this.getTask(t,i);if(!n)return null;n.outputSubscribers.add(e);let s=!0;return()=>{s&&(s=!1,n.outputSubscribers.delete(e))}}async readOutput(t,i){const e=String(i.task_id??""),n=this.getTask(t,e),s=i.mode??"tail",o=Math.min(Math.max(1,i.lines??50),S);if(!n)return{taskId:e,status:"killed",mode:s,lines:[],lineCount:0,metadata:{totalLines:0,totalBytes:0,returnedBytes:0,truncated:!1,hasMore:!1,outputFile:""}};let r=[],u=!1;if(s==="head")r=n.ringBuffer.head(o);else if(s==="tail")r=n.ringBuffer.tail(o);else if(s==="offset"){const d=Math.max(0,i.offset??0),f=n.ringBuffer.offset(d,o);if(f.length>0||n.ringBuffer.hasLine(d))r=f;else{const h=await this.readFromFile(n,"offset",d,o,void 0);r=h.lines,u=h.hasMore}}else if(s==="grep"){const d=String(i.pattern??"");if(d.length>z)return{taskId:e,status:n.status,mode:s,lines:[],lineCount:0,metadata:{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:0,truncated:!1,hasMore:!1,outputFile:n.outputPath}};let f;try{f=new RegExp(d,"i")}catch{return{taskId:e,status:n.status,mode:s,lines:[],lineCount:0,metadata:{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:0,truncated:!1,hasMore:!1,outputFile:n.outputPath}}}const h=await this.readFromFile(n,"grep",0,o,f);r=h.lines,u=h.hasMore}let a=0;const l=[];let c=!1;for(const d of r){if(l.length>=S){c=!0,u=!0;break}const f=Buffer.byteLength(d,"utf-8")+1;if(a+f>D){c=!0,u=!0;break}l.push(d),a+=f}const m=i.include_metadata!==!1;return{taskId:e,status:n.status,mode:s,lines:l,lineCount:l.length,metadata:m?{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:a,truncated:c,hasMore:u,outputFile:n.outputPath}:void 0}}async readFromFile(t,i,e,n,s){const o=[];let r=!1,u=0,a=0;try{const l=await T.open(t.outputPath,"r"),c=_({input:l.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const m of c){if(i==="grep"&&o.length>=F){r=!0;break}if(i==="offset"){if(u<e){u++;continue}if(a>=n){r=!0;break}o.push(m),a++,u++}else{if(s.test(m)){if(a>=n){r=!0;break}o.push(m),a++}u++}}c.close(),await l.close()}catch{return{lines:[],hasMore:!1}}return{lines:o,hasMore:r}}async awaitCompletion(t,i,e){return Promise.all(i.map(n=>this.awaitOne(t,n,e)))}async awaitOne(t,i,e){const n=this.getTask(t,i);if(n)return n.status!=="running"?this.taskToAwaitResult(n):new Promise(r=>{let u=null;const a=l=>{u&&clearTimeout(u),r(l)};n.completionWaiters.push(a),e&&e>0&&(u=setTimeout(()=>{n.completionWaiters=n.completionWaiters.filter(l=>l!==a),r({taskId:i,status:"running",exitCode:null,signal:null,timedOut:!0,aborted:!1,durationMs:Date.now()-n.startedAt})},e),u.unref?.())});const o=(await this.store.readSession(t))?.tasks.find(r=>r.taskId===i);return o?o.status!=="running"?{taskId:i,status:o.status,exitCode:o.exitCode,signal:o.signal,timedOut:o.killedReason==="timeout",aborted:!1,durationMs:(o.finishedAt??Date.now())-o.startedAt,...o.outputPath?{outputFilePath:o.outputPath}:{}}:{taskId:i,status:"killed",exitCode:null,signal:null,timedOut:!1,aborted:!1,durationMs:(o.finishedAt??Date.now())-o.startedAt}:null}taskToAwaitResult(t){return{taskId:t.taskId,status:t.status,exitCode:t.exitCode,signal:t.signal,timedOut:t.killedReason==="timeout",aborted:!1,durationMs:(t.finishedAt??Date.now())-t.startedAt,...t.status!=="running"&&t.outputPath?{outputFilePath:t.outputPath}:{}}}interruptAwaits(t,i){const e=this.tasksByAgent.get(t);if(e)for(const n of i){const s=e.get(n);if(!s)continue;const o=s.completionWaiters;if(o.length===0)continue;s.completionWaiters=[];const r={taskId:n,status:"running",exitCode:null,signal:null,timedOut:!1,aborted:!0,durationMs:Date.now()-s.startedAt};for(const u of o)u(r)}}async killTask(t,i){const e=this.getTask(t,i);return e?e.status!=="running"?{taskId:i,status:e.status,exitCode:e.exitCode??void 0}:e.child?(e.killedReason="user_kill",this.killProcessGroup(e.child)?(e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.killEscalationTimer=setTimeout(()=>{e.child&&!e.finalized&&this.killProcessGroup(e.child,!0)},R),e.killEscalationTimer.unref?.(),{taskId:i,status:"killed",signal:"SIGTERM"}):{taskId:i,status:e.status,error:"kill_failed",reason:"permission_denied"}):{taskId:i,status:e.status,error:"kill_failed",reason:"no_child"}:{taskId:i,status:"killed",error:"task_not_found"}}killProcessGroup(t,i=!1){const e=t.pid;if(!e)return!1;try{if(process.platform==="win32"){const n="/F",{spawnSync:s}=require("node:child_process");return s("taskkill",["/PID",String(e),"/T",n],{windowsHide:!0}),!0}try{process.kill(-e,i?"SIGKILL":"SIGTERM")}catch{process.kill(e,i?"SIGKILL":"SIGTERM")}return!0}catch{return!1}}async disposeAgent(t){const i=this.tasksByAgent.get(t);if(i){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="session_disposed",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer);this.tasksByAgent.delete(t)}await this.store.deleteAgent(t)}async disposeAll(){for(const[t,i]of this.tasksByAgent){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="process_exit",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer);this.tasksByAgent.delete(t)}}async reconcileOrphans(t){const i=await this.store.readSession(t);if(!i||i.tasks.length===0)return;let e=!1;for(const n of i.tasks)n.status==="running"&&(n.status="killed",n.killedReason="runtime_crashed",n.finishedAt=Date.now(),n.notified=!0,e=!0);e&&await this.store.persistSession(t,i.tasks)}async readSessionState(t){return this.store.readSession(t)}async markNotified(t,i){await this.store.markNotified(t,i)}getOrCreateBucket(t){let i=this.tasksByAgent.get(t);return i||(i=new Map,this.tasksByAgent.set(t,i)),i}getTask(t,i){const e=this.tasksByAgent.get(t);if(e&&/^[A-Za-z0-9]{8}$/.test(i))return e.get(i)}generateTaskId(t){for(let i=0;i<3;i++){const e=g(4).toString("hex").slice(0,8);if(!t.has(e))return e}return g(6).toString("hex").slice(0,8)}async persistTask(t){const i=await this.store.readSession(t.agentId)??{agentId:t.agentId,updatedAt:0,tasks:[]},e={taskId:t.taskId,agentId:t.agentId,command:t.command,commandPreview:t.commandPreview,cwd:t.cwd,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputPath:t.outputPath,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,notified:t.notified,killedReason:t.killedReason},n=i.tasks.findIndex(o=>o.taskId===t.taskId);n>=0?i.tasks[n]=e:i.tasks.push(e);const s=Date.now();i.tasks=i.tasks.filter(o=>o.status==="running"||!o.finishedAt||s-o.finishedAt<v),await this.store.persistSession(t.agentId,i.tasks)}}class b extends Error{constructor(t,i){super(i),this.name="BackgroundTaskError",this.code=t}}const U=new G;export{G as BackgroundCommandManager,b as BackgroundTaskError,U as backgroundCommandManager};
5
+ `;t.ringBuffer.push(r),this.appendToFile(t,r)}t.ringBuffer.push(e);return}t.ringBuffer.push(e),t.onOutput?.({stream:i,text:e});const s=(e.match(/\n/g)??[]).length;t.outputLineCount+=s,t.outputBytes+=Buffer.byteLength(e,"utf-8"),this.appendToFile(t,e)}scheduleInactivity(t){t.finalized||t.inactivityTimeoutMs<=0||(t.inactivityTimer&&clearTimeout(t.inactivityTimer),t.inactivityTimer=setTimeout(()=>{t.finalized||!t.child||(t.inactivityTimedOut=!0,t.killedReason="inactivity",this.killProcessGroup(t.child))},t.inactivityTimeoutMs),t.inactivityTimer.unref?.())}appendToFile(t,i){return t.outputBytes>B?Promise.resolve():(t.outputWriteChain=t.outputWriteChain.then(()=>A.appendFile(t.outputPath,i,"utf-8")).catch(e=>{t.ringBuffer.push(`[ACode] output write failed: ${e?.message??e}
6
+ `)}),t.outputWriteChain)}finalizeTask(t,i,e,n){if(t.finalized)return;t.finalized=!0,t.timeoutTimer&&(clearTimeout(t.timeoutTimer),t.timeoutTimer=null),t.killEscalationTimer&&(clearTimeout(t.killEscalationTimer),t.killEscalationTimer=null),t.inactivityTimer&&(clearTimeout(t.inactivityTimer),t.inactivityTimer=null),t.child=null,t.finishedAt=Date.now();let o;n?(o="killed",t.killedReason=n):i===0?o="completed":o="failed",t.status=o,t.exitCode=i,t.signal=e;const s=n==="inactivity"||t.inactivityTimedOut,r={taskId:t.taskId,exitCode:i,signal:e,timedOut:n==="timeout"||s,inactivityTimedOut:s,outputFilePath:t.outputPath,outputLineCount:t.outputLineCount,outputSpilled:t.outputTruncated,commandFilePath:t.commandFilePath,commandLineCount:t.commandLineCount,timeoutMs:t.timeoutMs,inactivityTimeoutMs:t.inactivityTimeoutMs,stdout:t.stdoutText,stderr:t.stderrText,durationMs:t.finishedAt-t.startedAt};t.completionResolve?.(r);const u={taskId:t.taskId,status:o,exitCode:i,signal:e,timedOut:n==="timeout"||s,aborted:!1,durationMs:t.finishedAt-t.startedAt},a=t.completionWaiters;t.completionWaiters=[];for(const c of a)c(u);if(this.persistTask(t),t.onCompleted?.({taskId:t.taskId,agentId:t.agentId,status:o,exitCode:i,signal:e,commandPreview:t.commandPreview,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,outputPath:t.outputPath,finishedAt:t.finishedAt,killedReason:t.killedReason}),t.outputSubscribers.size>0){const c=Array.from(t.outputSubscribers);t.outputSubscribers.clear();for(const d of c)try{d.onDone({status:o,exitCode:i,signal:e})}catch{}}t.retentionTimer=setTimeout(()=>{this.evictTask(t.agentId,t.taskId)},_),t.retentionTimer.unref?.()}evictTask(t,i){const e=this.tasksByAgent.get(t);if(!e)return;const n=e.get(i);n&&(A.unlink(n.outputPath).catch(()=>{}),e.delete(i),e.size===0&&this.tasksByAgent.delete(t))}listTasks(t,i){const e=this.tasksByAgent.get(t);if(!e)return[];const n=[];for(const o of e.values())i&&i!=="all"&&o.status!==i||n.push(this.toSummary(o));return n.sort((o,s)=>s.startedAt-o.startedAt)}toSummary(t){return{taskId:t.taskId,status:t.status,command:t.command,commandPreview:t.commandPreview,cwd:t.cwd,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes}}getTaskOutputSnapshot(t,i,e=500){const n=this.getTask(t,i);if(!n)return null;const o=n.ringBuffer.tail(e);return{taskId:n.taskId,status:n.status,exitCode:n.exitCode,signal:n.signal,startedAt:n.startedAt,finishedAt:n.finishedAt,command:n.command,lines:o,nextSeq:n.outputSeq+1,totalLines:n.outputLineCount,totalBytes:n.outputBytes,truncated:n.outputTruncated}}subscribeTaskOutput(t,i,e){const n=this.getTask(t,i);if(!n)return null;n.outputSubscribers.add(e);let o=!0;return()=>{o&&(o=!1,n.outputSubscribers.delete(e))}}async readOutput(t,i){const e=String(i.task_id??""),n=this.getTask(t,e),o=i.mode==="lines"||i.mode==="grep"?i.mode:"tail",s=Math.min(Math.max(1,i.line_size??100),W),r=Number.isFinite(i.start_line)&&(i.start_line??0)>0?Math.floor(i.start_line??0):0;if(!n)return{taskId:e,status:"killed",mode:o,lines:[],lineCount:0,metadata:{totalLines:0,totalBytes:0,returnedBytes:0,truncated:!1,outputTruncated:!1,hasMore:!1,nextStartLine:null,outputFile:""}};let u=[],a=!1,c=null;if(o==="tail"){const m=n.ringBuffer.tail(s),f=Math.max(0,n.outputLineCount-m.length);u=m.map((l,g)=>({line:f+g,text:l})),c=null}else if(o==="lines"){const m=n.ringBuffer.firstLineIndex(),f=m+n.ringBuffer.getLineCount();if(r>=m&&r<f)u=n.ringBuffer.offset(r,s).map((g,h)=>({line:r+h,text:g})),a=r+u.length<f,c=a?r+u.length:null;else if(r<f){const l=await this.readFromFile(n,"lines",r,s,0,0,void 0);u=l.rows,a=l.hasMore,c=l.nextStartLine}}else{const m=String(i.pattern??"");if(!m||m.length>$)return this._emptyResult(e,n,o);let f;try{f=new RegExp(m,"i")}catch{return this._emptyResult(e,n,o)}const l=Math.min(Math.max(0,i.context_before??0),v),g=Math.min(Math.max(0,i.context_after??0),v),h=await this.readFromFile(n,"grep",r,s,l,g,f);u=h.rows,a=h.hasMore,c=h.nextStartLine}const d=[];let p=0,T=!1;for(const m of u){const f=Buffer.byteLength(m.text,"utf-8")+1;if(d.length>=s||p+f>N){T=!0,a=!0;break}d.push(m),p+=f}const y=i.include_metadata!==!1;return{taskId:e,status:n.status,mode:o,lines:d,lineCount:d.length,metadata:y?{totalLines:n.outputLineCount,totalBytes:n.outputBytes,returnedBytes:p,truncated:T,outputTruncated:n.outputTruncated,hasMore:a,nextStartLine:a?c:null,outputFile:n.outputPath}:void 0}}_emptyResult(t,i,e){return{taskId:t,status:i.status,mode:e,lines:[],lineCount:0,metadata:{totalLines:i.outputLineCount,totalBytes:i.outputBytes,returnedBytes:0,truncated:!1,outputTruncated:i.outputTruncated,hasMore:!1,nextStartLine:null,outputFile:i.outputPath}}}async readFromFile(t,i,e,n,o,s,r){const u=[],a=new Set;let c=0,d=!1;const p=[];let T=-1/0;try{const y=await A.open(t.outputPath,"r"),m=I({input:y.createReadStream({encoding:"utf-8"}),crlfDelay:1/0});for await(const f of m){const l=c;if(c++,i==="grep"&&l-e>=X&&l>=e){d=!0;break}if(l>=e){if(i==="lines"){if(u.length>=n){d=!0;break}a.has(l)||(a.add(l),u.push({line:l,text:f}))}else if(r.test(f)){for(const h of p)h.line>=e&&!a.has(h.line)&&(a.add(h.line),u.push(h));if(a.has(l)||(a.add(l),u.push({line:l,text:f})),T=l,u.length>=n){d=!0;break}}else if(o>0&&(p.push({line:l,text:f}),p.length>o&&p.shift()),T>=e&&l-T<=s&&!a.has(l)&&(a.add(l),u.push({line:l,text:f}),u.length>=n)){d=!0;break}}}m.close(),await y.close()}catch{return{rows:[],hasMore:!1,nextStartLine:null}}return d?{rows:u,hasMore:d,nextStartLine:c}:{rows:u,hasMore:d,nextStartLine:null}}async awaitCompletion(t,i,e){return Promise.all(i.map(n=>this.awaitOne(t,n,e)))}async awaitOne(t,i,e){const n=this.getTask(t,i);if(n)return n.status!=="running"?this.taskToAwaitResult(n):new Promise(r=>{let u=null;const a=c=>{u&&clearTimeout(u),r(c)};n.completionWaiters.push(a),e&&e>0&&(u=setTimeout(()=>{n.completionWaiters=n.completionWaiters.filter(c=>c!==a),r({taskId:i,status:"running",exitCode:null,signal:null,timedOut:!0,aborted:!1,durationMs:Date.now()-n.startedAt})},e),u.unref?.())});const s=(await this.store.readSession(t))?.tasks.find(r=>r.taskId===i);return s?s.status!=="running"?{taskId:i,status:s.status,exitCode:s.exitCode,signal:s.signal,timedOut:s.killedReason==="timeout",aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt,...s.outputPath?{outputFilePath:s.outputPath}:{}}:{taskId:i,status:"killed",exitCode:null,signal:null,timedOut:!1,aborted:!1,durationMs:(s.finishedAt??Date.now())-s.startedAt}:null}taskToAwaitResult(t){return{taskId:t.taskId,status:t.status,exitCode:t.exitCode,signal:t.signal,timedOut:t.killedReason==="timeout",aborted:!1,durationMs:(t.finishedAt??Date.now())-t.startedAt,...t.status!=="running"&&t.outputPath?{outputFilePath:t.outputPath}:{}}}interruptAwaits(t,i){const e=this.tasksByAgent.get(t);if(e)for(const n of i){const o=e.get(n);if(!o)continue;const s=o.completionWaiters;if(s.length===0)continue;o.completionWaiters=[];const r={taskId:n,status:"running",exitCode:null,signal:null,timedOut:!1,aborted:!0,durationMs:Date.now()-o.startedAt};for(const u of s)u(r)}}async killTask(t,i){const e=this.getTask(t,i);return e?e.status!=="running"?{taskId:i,status:e.status,exitCode:e.exitCode??void 0}:e.child?(e.killedReason="user_kill",this.killProcessGroup(e.child)?(e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.killEscalationTimer=setTimeout(()=>{e.child&&!e.finalized&&this.killProcessGroup(e.child,!0)},z),e.killEscalationTimer.unref?.(),{taskId:i,status:"killed",signal:"SIGTERM"}):{taskId:i,status:e.status,error:"kill_failed",reason:"permission_denied"}):{taskId:i,status:e.status,error:"kill_failed",reason:"no_child"}:{taskId:i,status:"killed",error:"task_not_found"}}killProcessGroup(t,i=!1){const e=t.pid;if(!e)return!1;try{if(process.platform==="win32"){const n="/F",{spawnSync:o}=require("node:child_process");return o("taskkill",["/PID",String(e),"/T",n],{windowsHide:!0}),!0}try{process.kill(-e,i?"SIGKILL":"SIGTERM")}catch{process.kill(e,i?"SIGKILL":"SIGTERM")}return!0}catch{return!1}}async disposeAgent(t){const i=this.tasksByAgent.get(t);if(i){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="session_disposed",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer);this.tasksByAgent.delete(t)}await this.store.deleteAgent(t)}async disposeAll(){for(const[t,i]of this.tasksByAgent){for(const e of i.values())e.status==="running"&&e.child&&(e.killedReason="process_exit",this.killProcessGroup(e.child,!0)),e.timeoutTimer&&clearTimeout(e.timeoutTimer),e.killEscalationTimer&&clearTimeout(e.killEscalationTimer),e.retentionTimer&&clearTimeout(e.retentionTimer);this.tasksByAgent.delete(t)}}async reconcileOrphans(t){const i=await this.store.readSession(t);if(!i||i.tasks.length===0)return;let e=!1;for(const n of i.tasks)n.status==="running"&&(n.status="killed",n.killedReason="runtime_crashed",n.finishedAt=Date.now(),n.notified=!0,e=!0);e&&await this.store.persistSession(t,i.tasks)}async readSessionState(t){return this.store.readSession(t)}async markNotified(t,i){await this.store.markNotified(t,i)}getOrCreateBucket(t){let i=this.tasksByAgent.get(t);return i||(i=new Map,this.tasksByAgent.set(t,i)),i}getTask(t,i){const e=this.tasksByAgent.get(t);if(e&&/^[A-Za-z0-9]{8}$/.test(i))return e.get(i)}generateTaskId(t){for(let i=0;i<3;i++){const e=S(4).toString("hex").slice(0,8);if(!t.has(e))return e}return S(6).toString("hex").slice(0,8)}async persistTask(t){const i=await this.store.readSession(t.agentId)??{agentId:t.agentId,updatedAt:0,tasks:[]},e={taskId:t.taskId,agentId:t.agentId,command:t.command,commandPreview:t.commandPreview,cwd:t.cwd,status:t.status,startedAt:t.startedAt,finishedAt:t.finishedAt,exitCode:t.exitCode,signal:t.signal,outputPath:t.outputPath,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes,notified:t.notified,killedReason:t.killedReason},n=i.tasks.findIndex(s=>s.taskId===t.taskId);n>=0?i.tasks[n]=e:i.tasks.push(e);const o=Date.now();i.tasks=i.tasks.filter(s=>s.status==="running"||!s.finishedAt||o-s.finishedAt<_),await this.store.persistSession(t.agentId,i.tasks)}}class x extends Error{constructor(t,i){super(i),this.name="BackgroundTaskError",this.code=t}}const J=new q;export{q as BackgroundCommandManager,x as BackgroundTaskError,J as backgroundCommandManager};
7
7
 
@@ -1,18 +1,18 @@
1
- const s=`List all background commands for the current agent, including running and recently completed ones.
1
+ const r=`List all background commands for the current agent, including running and recently completed ones.
2
2
 
3
3
  Returns task_id, status, command (preview), startedAt, finishedAt, exitCode, outputLineCount, outputBytes.
4
- Does NOT include output content (use read_background_output to fetch output).`,d=`Terminate a running background command by task_id.
4
+ Does NOT include output content (use read_background_output to fetch output).`,s=`Terminate a running background command by task_id.
5
5
 
6
6
  Sends SIGTERM to the process group, then SIGKILL after 1s if still alive.
7
7
  No-op (idempotent) if the task already finished.
8
- Cross-agent task_id returns task_not_found (does not leak existence).`,r=`Read the output (stdout+stderr merged, interleaved in arrival order) of a background command.
8
+ Cross-agent task_id returns task_not_found (does not leak existence).`,d=`Read the output (stdout+stderr merged, interleaved in arrival order) of a background command.
9
9
 
10
- Query modes:
11
- - head: first N lines
12
- - tail (default): last N lines (fastest, in-memory)
13
- - offset: from line X (0-based) take N lines (reads output file, bypasses memory cap)
14
- - grep: filter lines by regex (JS syntax, case-insensitive)
10
+ Modes:
11
+ - lines: read a range by absolute line numbers (start_line + line_size). Use to fetch a window, or to walk forward by passing the returned nextStartLine back as start_line.
12
+ - grep: search matching lines with optional context_before/context_after. start_line is the scan start (resume paging by passing nextStartLine).
13
+ - tail (default): last N lines (live view; no paging anchor).
15
14
 
16
- Output capped at 256KB / 500 lines per call; truncated=true means more is available.`;function u(a,o){return{kind:"builtin",toolName:"list_background_commands",exposedName:"list_background_commands",description:s,inputSchema:{type:"object",properties:{status_filter:{type:"string",enum:["running","completed","failed","killed","all"],description:"Filter by status. Defaults to 'all'.",default:"all"}},additionalProperties:!1},async execute(t){const e=typeof t.status_filter=="string"?t.status_filter:"all",i=o.manager.listTasks(a,e);return{title:`${i.length} background commands`,output:JSON.stringify(i.map(n=>({taskId:n.taskId,status:n.status,command:n.commandPreview,cwd:n.cwd,startedAt:new Date(n.startedAt).toISOString(),finishedAt:n.finishedAt?new Date(n.finishedAt).toISOString():null,exitCode:n.exitCode,outputLineCount:n.outputLineCount,outputBytes:n.outputBytes})),null,2),metadata:{tasks:i,count:i.length}}}}}function l(a,o){return{kind:"builtin",toolName:"kill_background_command",exposedName:"kill_background_command",description:d,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id returned by Bash(wait_seconds=...) or list_background_commands."}},required:["task_id"],additionalProperties:!1},async execute(t){const e=typeof t.task_id=="string"?t.task_id:"",i=await o.manager.killTask(a,e);return{title:`kill ${e}: ${i.status}`,output:JSON.stringify(i,null,2),metadata:i}}}}function p(a,o){return{kind:"builtin",toolName:"read_background_output",exposedName:"read_background_output",description:r,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id to read output from."},mode:{type:"string",enum:["head","tail","offset","grep"],description:"head=first N, tail=last N (default), offset=from line X take N, grep=regex filter.",default:"tail"},lines:{type:"integer",description:"Number of lines (head/tail/offset). Default 50, max 500.",default:50,minimum:1,maximum:500},offset:{type:"integer",description:"Starting line number (0-based) for offset mode.",minimum:0},pattern:{type:"string",description:"Regex pattern for grep mode (JS syntax, case-insensitive)."},include_metadata:{type:"boolean",description:"Include totalLines/totalBytes/truncated/hasMore/outputFile in response.",default:!0}},required:["task_id"],additionalProperties:!1},async execute(t){const e=await o.manager.readOutput(a,{task_id:typeof t.task_id=="string"?t.task_id:"",mode:t.mode==="head"||t.mode==="tail"||t.mode==="offset"||t.mode==="grep"?t.mode:"tail",lines:typeof t.lines=="number"?t.lines:50,offset:typeof t.offset=="number"?t.offset:0,pattern:typeof t.pattern=="string"?t.pattern:void 0,include_metadata:t.include_metadata!==!1});return{title:`read ${e.taskId} (${e.mode}): ${e.lineCount} lines`,output:e.lines.join(`
17
- `),metadata:e}}}}export{l as createKillBackgroundCommandTool,u as createListBackgroundCommandsTool,p as createReadBackgroundOutputTool};
15
+ Every returned line is { line, text }: line is the 0-based absolute line number in the command output. Use the line number to locate the same content in the output / files, and lines(start_line=..., line_size=...) to expand around a match.
16
+ Output capped at 256KB / line_size lines per call; truncated=true means more is available (grep: continue from nextStartLine).`;function u(a,o){return{kind:"builtin",toolName:"list_background_commands",exposedName:"list_background_commands",description:r,inputSchema:{type:"object",properties:{status_filter:{type:"string",enum:["running","completed","failed","killed","all"],description:"Filter by status. Defaults to 'all'.",default:"all"}},additionalProperties:!1},async execute(e){const n=typeof e.status_filter=="string"?e.status_filter:"all",i=o.manager.listTasks(a,n);return{title:`${i.length} background commands`,output:JSON.stringify(i.map(t=>({taskId:t.taskId,status:t.status,command:t.commandPreview,cwd:t.cwd,startedAt:new Date(t.startedAt).toISOString(),finishedAt:t.finishedAt?new Date(t.finishedAt).toISOString():null,exitCode:t.exitCode,outputLineCount:t.outputLineCount,outputBytes:t.outputBytes})),null,2),metadata:{tasks:i,count:i.length}}}}}function l(a,o){return{kind:"builtin",toolName:"kill_background_command",exposedName:"kill_background_command",description:s,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id returned by Bash(wait_seconds=...) or list_background_commands."}},required:["task_id"],additionalProperties:!1},async execute(e){const n=typeof e.task_id=="string"?e.task_id:"",i=await o.manager.killTask(a,n);return{title:`kill ${n}: ${i.status}`,output:JSON.stringify(i,null,2),metadata:i}}}}function c(a,o){return{kind:"builtin",toolName:"read_background_output",exposedName:"read_background_output",description:d,inputSchema:{type:"object",properties:{task_id:{type:"string",description:"The task_id to read output from."},mode:{type:"string",enum:["lines","grep","tail"],description:"lines=read range by absolute line numbers, grep=search matching lines with context, tail=last N lines (default).",default:"tail"},line_size:{type:"integer",description:"lines/tail: number of lines to return; grep: max total lines returned (including context). Default 100, max 100.",default:100,minimum:1,maximum:100},start_line:{type:"integer",description:"lines: 0-based absolute line to start from; grep: scan start line (pass nextStartLine to page).",minimum:0},pattern:{type:"string",description:"grep: regex pattern (JS syntax, case-insensitive)."},context_before:{type:"integer",description:"grep: lines of context before each match (0-20, default 0).",minimum:0,maximum:20,default:0},context_after:{type:"integer",description:"grep: lines of context after each match (0-20, default 0).",minimum:0,maximum:20,default:0},include_metadata:{type:"boolean",description:"Include totalLines/totalBytes/truncated/outputTruncated/hasMore/nextStartLine/outputFile in response.",default:!0}},required:["task_id"],additionalProperties:!1},async execute(e){const n=await o.manager.readOutput(a,{task_id:typeof e.task_id=="string"?e.task_id:"",mode:e.mode==="lines"||e.mode==="grep"||e.mode==="tail"?e.mode:"tail",line_size:typeof e.line_size=="number"?e.line_size:void 0,start_line:typeof e.start_line=="number"?e.start_line:void 0,pattern:typeof e.pattern=="string"?e.pattern:void 0,context_before:typeof e.context_before=="number"?e.context_before:void 0,context_after:typeof e.context_after=="number"?e.context_after:void 0,include_metadata:e.include_metadata!==!1});return{title:`read ${n.taskId} (${n.mode}): ${n.lineCount} lines`,output:n.lines.map(({line:i,text:t})=>`${i}: ${t}`).join(`
17
+ `),metadata:n}}}}export{l as createKillBackgroundCommandTool,u as createListBackgroundCommandsTool,c as createReadBackgroundOutputTool};
18
18
 
@@ -4,6 +4,8 @@ export declare class BackgroundOutputRingBuffer {
4
4
  private bytes;
5
5
  private partialLine;
6
6
 
7
+ private droppedLines;
8
+
7
9
  push(text: string): void;
8
10
 
9
11
  flush(): void;
@@ -13,11 +15,11 @@ export declare class BackgroundOutputRingBuffer {
13
15
 
14
16
  tail(n: number): string[];
15
17
 
16
- offset(offset: number, limit: number): string[];
18
+ offset(globalStart: number, limit: number): string[];
19
+
20
+ firstLineIndex(): number;
17
21
 
18
22
  getLineCount(): number;
19
23
 
20
24
  getByteCount(): number;
21
-
22
- hasLine(lineIndex: number): boolean;
23
25
  }
@@ -1,3 +1,3 @@
1
- const n=1e3,h=262144;class r{constructor(){this.lines=[],this.bytes=0,this.partialLine=""}push(t){if(!t)return;this.partialLine+=t;const i=this.partialLine.split(`
2
- `);this.partialLine=i.pop()??"";for(const s of i)this.lines.push(s),this.bytes+=Buffer.byteLength(s,"utf-8");this.enforceLimits()}flush(){this.partialLine&&(this.lines.push(this.partialLine),this.bytes+=Buffer.byteLength(this.partialLine,"utf-8"),this.partialLine="",this.enforceLimits())}enforceLimits(){for(;this.lines.length>1e3;){const t=this.lines.shift();t!==void 0&&(this.bytes-=Buffer.byteLength(t,"utf-8"))}for(;this.bytes>262144&&this.lines.length>1;){const t=this.lines.shift();t!==void 0&&(this.bytes-=Buffer.byteLength(t,"utf-8"))}this.bytes<0&&(this.bytes=0)}head(t){return this.lines.slice(0,Math.max(0,t))}tail(t){const i=Math.max(0,this.lines.length-t);return this.lines.slice(i)}offset(t,i){const s=Math.max(0,t);return this.lines.slice(s,s+Math.max(0,i))}getLineCount(){return this.lines.length}getByteCount(){return this.bytes+Buffer.byteLength(this.partialLine,"utf-8")}hasLine(t){return t>=0&&t<this.lines.length}}export{r as BackgroundOutputRingBuffer};
1
+ const n=1e3,h=262144;class r{constructor(){this.lines=[],this.bytes=0,this.partialLine="",this.droppedLines=0}push(t){if(!t)return;this.partialLine+=t;const e=this.partialLine.split(`
2
+ `);this.partialLine=e.pop()??"";for(const i of e)this.lines.push(i),this.bytes+=Buffer.byteLength(i,"utf-8");this.enforceLimits()}flush(){this.partialLine&&(this.lines.push(this.partialLine),this.bytes+=Buffer.byteLength(this.partialLine,"utf-8"),this.partialLine="",this.enforceLimits())}enforceLimits(){for(;this.lines.length>1e3;){const t=this.lines.shift();t!==void 0&&(this.bytes-=Buffer.byteLength(t,"utf-8"),this.droppedLines++)}for(;this.bytes>262144&&this.lines.length>1;){const t=this.lines.shift();t!==void 0&&(this.bytes-=Buffer.byteLength(t,"utf-8"),this.droppedLines++)}this.bytes<0&&(this.bytes=0)}head(t){return this.lines.slice(0,Math.max(0,t))}tail(t){const e=Math.max(0,this.lines.length-t);return this.lines.slice(e)}offset(t,e){const i=t-this.droppedLines;return i<0||i>=this.lines.length?[]:this.lines.slice(i,i+Math.max(0,e))}firstLineIndex(){return this.droppedLines}getLineCount(){return this.lines.length}getByteCount(){return this.bytes+Buffer.byteLength(this.partialLine,"utf-8")}}export{r as BackgroundOutputRingBuffer};
3
3