grix-connector 4.7.2 → 4.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -2
- package/dist/adapter/claude/claude-adapter.js +19 -19
- package/dist/adapter/claude/claude-bridge-server.js +1 -1
- package/dist/adapter/claude/claude-tools.js +1 -1
- package/dist/adapter/claude/claude-worker-client.js +1 -1
- package/dist/adapter/claude/mcp-http-launcher.js +2 -2
- package/dist/adapter/claude/result-timeout.js +1 -1
- package/dist/adapter/claude/usage-parser.js +7 -7
- package/dist/assets/dsh-bridge/grix-dsh-bridge-4.9.1.tgz +0 -0
- package/dist/assets/dsh-bridge/manifest.json +6 -6
- package/dist/bridge/bridge.js +5 -5
- package/dist/bridge/remote-admin.js +1 -0
- package/dist/bridge/send-controller.js +3 -3
- package/dist/bridge/session-usage.js +1 -1
- package/dist/core/access/allowlist-store.js +1 -1
- package/dist/core/admin/admin-server.js +1 -1
- package/dist/core/aibot/client.js +1 -1
- package/dist/core/config/remote-admin-settings.js +1 -0
- package/dist/core/file-ops/list-files.js +1 -1
- package/dist/core/installer/installer.js +10 -10
- package/dist/core/installer/progress-view.js +1 -0
- package/dist/core/mcp/internal-api-server.js +1 -1
- package/dist/core/mcp/tools.js +1 -1
- package/dist/core/probe/installed-clients.js +1 -0
- package/dist/core/protocol/inbound-attachments.js +1 -0
- package/dist/core/protocol/index.js +1 -1
- package/dist/default-skills/grix-agent-dispatch/SKILL.md +1 -1
- package/dist/grix.js +8 -8
- package/dist/log.js +2 -2
- package/dist/manager.js +1 -1
- package/dist/mcp/stdio/server.js +9 -8
- package/dist/mcp/stream-http/config.js +1 -1
- package/dist/mcp/stream-http/connection-binding.js +1 -1
- package/dist/mcp/stream-http/tool-executor.js +1 -1
- package/dist/mcp/stream-http/tool-registry.js +1 -1
- package/dist/mcp/stream-http/tool-schemas.js +1 -1
- package/openclaw-plugin/index.js +4 -2
- package/openclaw-plugin/skills/grix-agent-dispatch/SKILL.md +1 -1
- package/package.json +1 -1
- package/dist/assets/dsh-bridge/grix-dsh-bridge-4.7.2.tgz +0 -0
package/dist/manager.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{join as k}from"node:path";import{homedir as le}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{buildSharedInstanceConfig as de,diffSharedOwners as he,sharedInstanceKey as ue,SharedOwnersCache as pe}from"./manager-share-config.js";import{AgentDeletedError as me,isAgentDeletedError as I}from"./core/aibot/errors.js";import{GRIX_PATHS as A,log as m}from"./core/log/index.js";import{resolveClientVersion as ge}from"./core/util/client-version.js";import{UpgradeChecker as fe}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as _e}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as ye}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as we}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as Ae}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as Se}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as ve,logDefaultSkillsCheck as be,cleanupProjectedSkills as B}from"./default-skills/index.js";import{buildProviderEnv as Ce,supportsKiroCwRelay as W,supportsNonMitmRelayConfig as Ie}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ee}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as Q,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as z}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as V}from"./core/runtime/cursor-resolve.js";import{getCliVersion as $e,resolveCliPath as Te}from"./core/util/cli-probe.js";import{AgentInstaller as Pe}from"./core/installer/installer.js";import{reportInstallFailure as Me}from"./core/observability/sentry.js";import{createDefaultAuditProducer as xe}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as De}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Oe}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as X}from"./core/config/agent-config-repository.js";function Y(r){if(r)return{...r.provider_id?.trim()?{providerId:r.provider_id.trim()}:{},...r.base_url?.trim()?{baseUrl:r.base_url.trim()}:{},...r.quota_base_url?.trim()?{quotaBaseUrl:r.quota_base_url.trim()}:{},...r.api_key?.trim()?{apiKey:r.api_key.trim()}:{},...r.model?.trim()?{model:r.model.trim()}:{},...r.fast_model?.trim()?{fastModel:r.fast_model.trim()}:{},...r.effort?.trim()?{effort:r.effort.trim()}:{}}}const Re=8e3,je=1e4,Le=8e3;function Ne(){const r=z();return[{clientType:"openclaw",command:"openclaw"},{clientType:"claude",command:"claude"},{clientType:"codex",command:"codex"},{clientType:"qwen",command:"qwen"},{clientType:"hermes",command:"hermes"},{clientType:"reasonix",command:"reasonix"},{clientType:"codewhale",command:"codewhale"},{clientType:"opencode",command:"opencode"},{clientType:"cursor",command:V()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:r.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function Ue(){return Promise.all(Ne().map(async r=>{const e=await Te(r.command);if(!e)return{client_type:r.clientType,command:r.command,installed:!1,path:null,version:null};const n=await $e(e,r.versionArgs??["--version"]);return{client_type:r.clientType,command:r.command,installed:!0,path:e,version:n.version,error:n.error}}))}function Ge(r){switch(r){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:V()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const e=z();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"deepseek":return{adapterType:"deepseek-harness",command:"dsh",adapterHint:"deepseek/grix-bridge-v1",options:{integrationMode:"profile_bridge",dshProfile:"web",autoInstallBridge:!0,autoStartProfile:!0},enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${r}`)}}function v(r,e){return String(r??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function qe(r){return k(A.data,`session-bindings-${v(r,"unknown")}.json`)}function He(r,e){return k(A.data,`session-bindings-${v(r,e)}.json`)}function Fe(r,e){return k(A.data,`active-events-${v(r,e)}.json`)}function Be(r){return k(A.data,`pending-events-${v(r,"unknown")}.json`)}function We(r,e){const n=v(r,"agent"),t=v(e,"unknown");return k(A.data,`terminal-outbox-${n}-${t}.json`)}function Qe(...r){const e=[],n=new Set;for(const t of r)for(const s of t??[]){const i=String(s??"").trim(),c=i.toLowerCase();!i||n.has(c)||(n.add(c),e.push(i))}return e.length>0?e:void 0}function Ke(r,e){const n={claude:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0}},t=n[r]??n.acp;return{maxConcurrent:e?.max_concurrent??t.maxConcurrent??1,maxQueued:e?.max_queued??t.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??t.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function ze(r,e={}){const n=ge(),t=String(r.client_type??"").trim().toLowerCase(),s=Ge(t),i=t==="deepseek"?r.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(r.ws_url??"").trim(),p=typeof r.name=="string"?r.name:"",a=p.trim(),l="get_session_usage",g="get_rate_limits",h="get_agent_global_config",d=ke;if(!a)throw new Error("agent name is required");if(p!==a)throw new Error(`agent name must not contain leading or trailing whitespace: "${p}"`);if(!o)throw new Error(`agent ${r.name}: ws_url is required`);if(!r.agent_id?.trim())throw new Error(`agent ${r.name}: agent_id is required`);if(!r.api_key?.trim())throw new Error(`agent ${r.name}: api_key is required`);const u=s.adapterType,f=u==="acp",_=t==="qwen",w={...s.options??{}};if(u==="deepseek-harness"&&(w.integrationMode=i,w.dshProfile=r.dsh?.profile?.trim()||"web",w.autoInstallBridge=r.dsh?.auto_install_bridge??!c,w.autoStartProfile=r.dsh?.auto_start_profile??!c),u==="deepseek-harness"&&r.provider?.model?.trim()&&(w.defaultModel=r.provider.model.trim()),r.codex_config_overrides!==void 0){if(u!=="codex")throw new Error(`agent ${r.name}: codex_config_overrides is only supported for client_type=codex`);De(r.codex_config_overrides,`agent ${r.name}: codex_config_overrides`),w.codexConfigOverrides=r.codex_config_overrides}if(u==="codex"&&r.provider?.wire_api!==void 0){const y=r.provider;if(y.wire_api!=="responses"||!y.base_url?.trim()||!y.api_key?.trim()||!y.model?.trim()||!y.catalog_version?.trim()||!y.catalog_sha256?.trim()||!y.catalog_json?.trim())throw Object.assign(new Error(`agent ${r.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const H=Oe(A.data,r.name);w.codexHome=H,w.codexDirect={codexHome:H,config:{baseUrl:y.base_url.trim(),model:y.model.trim(),...y.reasoning_effort?.trim()?{reasoningEffort:y.reasoning_effort.trim()}:{},token:y.api_key.trim()},catalog:{version:y.catalog_version.trim(),sha256:y.catalog_sha256.trim(),json:y.catalog_json}}}const S=u==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",l,g]}:null,Z=u==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",l,g]}:null,T=_?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",l],adapterHint:"qwen/base"}:null,P=u==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","get_context","file_list","create_folder",l,g]}:null,M=u==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,x=u==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",g]}:null,L=u==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",l]}:null,D=u==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder"]}:null,O=u==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",g]}:null,R=u==="deepseek-harness"?{adapterHint:c?"deepseek/jsonrpc-v1":"deepseek/grix-bridge-v1",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","set_mode","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile",l,g,"exec_approve","exec_reject","permission_approve","permission_reject","file_list","create_folder","dsh_bridge_status","dsh_install_bridge","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill","dsh_list_mcp","dsh_list_tools","dsh_list_jobs","dsh_list_goals"]}:null,N=new Set(["hermes","reasonix","kimi","copilot"]),ee=f&&!N.has(t),te=f&&!_?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...ee?[l]:[]]}:null,ne=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",l,g]}:null,se=u==="codex"||u==="claude"||u==="opencode"||u==="deepseek-harness"||t==="gemini"?["session_control","set_model","set_mode",...u==="deepseek-harness"?["set_provider","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill"]:[],...u==="claude"?["set_reasoning_effort"]:[]]:void 0,re=u==="cursor"||u==="opencode"||u==="openhuman"||u==="agy"||f&&N.has(t)?[g]:[l,g];f&&w.raw_transport===void 0&&(w.raw_transport=t==="gemini");const ae=`${t}/base`,C=we(u,t),ie=C==="kiro"?void 0:process.cwd(),j=Y(r.provider),b={...process.env,...r.env};if(C==="hermes"&&!b.HERMES_HOME?.trim()){const y=Ce(t,j);b.HERMES_HOME=Ee({...y,...b},r.name)}const U=ve({mode:C,projectDir:ie,env:b}),G=U.filter(y=>y.source==="connector").map(y=>y.name);G.length>0&&!e.quiet&&m.info("manager",`[${r.name}] injecting connector skills: [${G.join(", ")}]`);const q=Ae(U,A.skills);let oe=q.length>0?q:void 0;const ce=Se({skillsDir:A.skills,mode:C,home:le(),env:b});return{name:a,adapterType:u,aibot:{url:o,agentId:r.agent_id,apiKey:r.api_key,clientType:t,clientVersion:n,adapterHint:R?.adapterHint??s.adapterHint??T?.adapterHint??P?.adapterHint??M?.adapterHint??x?.adapterHint??D?.adapterHint??O?.adapterHint??ae,capabilities:R?.capabilities??S?.capabilities??L?.capabilities??P?.capabilities??M?.capabilities??x?.capabilities??D?.capabilities??O?.capabilities??T?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Qe(R?.localActions??S?.localActions??L?.localActions??Z?.localActions??P?.localActions??M?.localActions??x?.localActions??D?.localActions??O?.localActions??T?.localActions??ne?.localActions??te?.localActions??["exec_approve","exec_reject"],se,re,["connector_rollback","connector_upgrade_push",h,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:oe,librarySkills:ce,terminalOutboxPath:We(r.name,r.agent_id)},agent:{command:c?"dsh-jsonrpc-agent":s.command,args:[...s.args??[],...r.args??[]],env:r.env,clientType:t,...j?{provider:j}:{}},adapterOptions:w,acpAuthMethod:w.auth_method,acpInitialMode:w.initial_mode,acpMcpTools:w.acp_mcp_tools,promptTimeoutMs:r.prompt_timeout_ms,bindingsPath:qe(r.agent_id),legacyBindingsPath:He(r.name,r.agent_id),activeEventStorePath:Fe(r.name,r.agent_id),pendingEventStorePath:Be(r.agent_id),...s.enableSessionBinding||f?{enableSessionBinding:!0}:{},...s.autoInjectArgs||t==="reasonix"&&r.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...t==="reasonix"&&r.provider?.model?.trim()?{model:r.provider.model.trim()}:{}}}:{},poolMaxSize:r.pool?.maxSize??(u==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:r.pool?.idleTimeoutMs,eventQueue:Ke(u,r.event_queue),logDir:A.log,providerBaseUrl:r.provider_base_url?.trim()||void 0,providerApiKey:r.provider_api_key?.trim()||void 0}}function J(){const r=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Re}function E(){const r=process.env.GRIX_AGENT_STOP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function Ve(){return Math.min(E(),Le)}function $(r){if(r===null||typeof r!="object")return JSON.stringify(r)??"null";if(Array.isArray(r))return`[${r.map($).join(",")}]`;const e=r;return`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${$(e[t])}`).join(",")}}`}function Xe(r,e){return $(r)===$(e)}class It{options;instances=[];configMap=new Map;rawConfigMap=new Map;mutationTail=Promise.resolve();startingInstances=new Set;failedStartAgents=new Set;removingAgents=new Set;instanceGenerations=new WeakMap;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;pendingPlatformDeletes=new Map;upgradeChecker=null;skillSyncer=null;globalConfigStore;configRepository=new X(A.config);sharedOwnersCache=new pe(A.data);installer=new Pe;relaySecretResolver;auditProducer=xe();constructor(e={}){this.options=e}runMutation(e,n=[]){const t=this.mutationTail.catch(()=>{}).then(async()=>{if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});await this.retryPendingPlatformDeletesUnlocked();const s=new Set([...this.pendingPlatformDeletes.values()].map(c=>c.name)),i=n.find(c=>s.has(c.trim()));if(i)throw Object.assign(new Error(`Agent "${i}" is pending permanent platform-deletion cleanup`),{code:"AGENT_DELETE_PENDING"});return e()});return this.mutationTail=t.then(()=>{},()=>{}),t}async startTrackedInstance(e){this.startingInstances.add(e);try{await e.start()}finally{this.startingInstances.delete(e)}}requestStartCancellation(e){const n=e.requestStop;typeof n=="function"&&n.call(e)}async stopInstance(e,n,t=E()){let s;try{await Promise.race([e.stop(n),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`Agent "${e.name}" stop timed out after ${t}ms`),{code:"AGENT_STOP_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForLifecycleDrain(e,n,t=E()){let s;try{await Promise.race([Promise.allSettled([...e]).then(()=>{}),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"LIFECYCLE_DRAIN_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForShutdownOperation(e,n,t){let s;try{return await Promise.race([e,new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"SHUTDOWN_OPERATION_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}isActiveGeneration(e){const n=this.instanceGenerations.get(e);return!!n&&!n.retired&&this.instances.find(t=>t.name===n.name)===e}retireInstance(e){const n=this.instanceGenerations.get(e);n&&(n.retired=!0)}async start(e){return this.runMutation(()=>this.startUnlocked(e))}async startUnlocked(e){const n=e??A.config;this.configRepository=new X(n),m.info("manager",`Loading configs from ${n}`),be(),B(),this.globalConfigStore=new ye(k(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:t,fileErrors:s,skipped:i}=this.configRepository.scan();for(const a of s)m.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)m.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(t.length===0&&s.length===0&&i.length===0){m.warn("manager",`No agent configurations found in ${n}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const c=[];let o=s.length;for(const{entry:a,file:l}of t)try{const g=this.toRuntimeInstanceConfig(a);c.push({config:g,entry:a,file:l}),m.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${l}`)}catch(g){const h=typeof a?.name=="string"?a.name:"<unknown>";m.error("manager",`Invalid agent config in ${l} (name=${h}): ${g}`),o++}let p=0;if(c.length>0){const a=J();m.info("manager",`Starting ${c.length} agent(s), startup wait=${a}ms`);const l=d=>{this.instances=this.instances.filter(u=>u!==d)},g=c.map(({config:d,entry:u,file:f})=>{const _=this.createConfiguredInstance(d);return this.instances.push(_),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:u,file:f}),{config:d,instance:_,startPromise:this.prepareProviderRuntime(d,u).then(()=>{if(this.stopping)throw this.requestStartCancellation(_),Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});return this.startTrackedInstance(_)})}}),h=await Promise.all(g.map(async d=>{const u=await new Promise(f=>{let _=!1;const w=setTimeout(()=>{_||(_=!0,f({kind:"timeout"}))},a);d.startPromise.then(()=>{_||(_=!0,clearTimeout(w),f({kind:"started"}))}).catch(S=>{_||(_=!0,clearTimeout(w),f({kind:"failed",error:S}))})});return{task:d,outcome:u}}));for(const{task:d,outcome:u}of h){if(u.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(u.kind==="failed"){if(I(u.error)){this.onAgentDeletedByPlatform(d.instance);continue}try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(f){this.failedStartAgents.add(d.config.name),m.error("manager",`Failed to clean up partially started ${d.config.name}: ${f}`)}m.error("manager",`Failed to start ${d.config.name}: ${u.error}`);continue}p++,m.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{this.stopping||this.removingAgents.has(d.config.name)||this.isActiveGeneration(d.instance)&&(m.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config))}).catch(async f=>{if(I(f)){this.onAgentDeletedByPlatform(d.instance);return}if(!(this.stopping||this.removingAgents.has(d.config.name))&&this.isActiveGeneration(d.instance)){try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(_){this.failedStartAgents.add(d.config.name),m.error("manager",`Failed to clean up delayed start ${d.config.name}: ${_}`)}m.error("manager",`Delayed start failed: ${d.config.name}: ${f}`)}})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-p);m.info("manager",`${d}/${c.length} agent(s) running now`)}p>0&&m.warn("manager",`${p} agent(s) still connecting in background`)}if(this.instances.length===0&&c.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.stopping||this.configMap.size===0)return;const e=[...this.configMap.values()].map(n=>({apiKey:n.aibot.apiKey,wsUrl:n.aibot.url}));if(!this.upgradeChecker&&(this.upgradeChecker=new fe(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission(),{requestGracefulShutdown:this.options.requestGracefulShutdown,reopenLifecycleAdmission:()=>this.reopenAllLifecycleAdmission(),isConnected:()=>this.instances.length===0||this.instances.some(n=>n.getStatus().wsConnected),onTransactionSettled:this.options.onTransactionSettled}),await this.upgradeChecker.start(),this.stopping)){this.upgradeChecker.stop();return}this.skillSyncer||(this.skillSyncer=new _e(e,A.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start(),this.stopping&&this.skillSyncer.stop())}refreshOwnerLibrarySkills(){const e=new Map,n=t=>{let s;try{s=t.skillsSyncGroupKey()}catch(c){m.warn("manager",`skillsSyncGroupKey failed for ${t.name}: ${c}`);return}const i=e.get(s)??[];i.push(t),e.set(s,i)};for(const t of this.instances)n(t);for(const t of this.sharedInstances.values())n(t);for(const[t,s]of e){let i=!1;const c=[];for(const o of s){if(i){c.push(o);continue}let p=!1;try{p=o.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(a){m.warn("manager",`owner library skills reporter threw group=${t} agent=${o.name}: ${a}`)}if(p){i=!0;continue}m.warn("manager",`owner library skills reporter failed group=${t} agent=${o.name}; trying next`)}if(!i){m.warn("manager",`owner library skills: no reporter succeeded group=${t}`);continue}for(const o of c)try{o.adoptSkillsWireHashFromDisk()}catch(p){m.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${p}`)}}}async stop(){m.info("manager","Stopping all agents..."),this.stopping=!0;const e=Ve(),n=Date.now()+e,t=()=>Math.max(1,n-Date.now()),s=new Set([...this.instances,...this.sharedInstances.values(),...this.startingInstances]);for(const o of s)this.requestStartCancellation(o);const i=new Map([...s].map(o=>[o,this.stopInstance(o,void 0,e)])),c=[];this.upgradeChecker?.stop(),this.skillSyncer?.stop();try{await this.waitForLifecycleDrain([this.mutationTail,...this.shareSyncChains.values(),...i.values()],"manager shutdown lifecycle drain",t())}catch(o){c.push(o)}this.upgradeChecker?.stop(),this.skillSyncer?.stop(),this.shareSyncChains.clear(),await Promise.all([...[...this.instances].map(async o=>{try{await(i.get(o)??this.stopInstance(o,void 0,t())),this.retireInstance(o),this.instances=this.instances.filter(p=>p!==o),this.startingInstances.delete(o)}catch(p){c.push(p)}}),...[...this.sharedInstances.entries()].map(async([o,p])=>{try{await(i.get(p)??this.stopInstance(p,void 0,t())),this.sharedInstances.get(o)===p&&this.sharedInstances.delete(o),this.startingInstances.delete(p)}catch(a){c.push(a)}})]);try{await this.waitForLifecycleDrain([this.retryPendingPlatformDeletesUnlocked(n)],"platform-delete shutdown drain",t())}catch(o){c.push(o)}try{await this.waitForShutdownOperation(Promise.resolve().then(()=>this.globalConfigStore?.flush()),"global config shutdown flush",t())}catch(o){c.push(o)}if(await this.auditProducer.stop({timeoutMs:Math.min(5e3,t())}).catch(()=>!1),B(),c.length>0)throw new AggregateError(c,"Failed to stop all agents completely");m.info("manager","All stopped")}onShareSet(e,n,t){if(this.stopping||this.removingAgents.has(n.name)||!this.isActiveGeneration(e))return;this.sharedOwnersCache.save(n.name,t);const i=(this.shareSyncChains.get(n.name)??Promise.resolve()).catch(()=>{}).then(()=>{if(!(this.stopping||this.removingAgents.has(n.name))&&this.isActiveGeneration(e))return this.syncSharedInstances(n,t,e)});this.shareSyncChains.set(n.name,i)}async syncSharedInstances(e,n,t){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))return;const{toAdd:s,toRemove:i}=he(e.name,this.sharedInstances.keys(),n);for(const c of s){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))break;const o=ue(e.name,c);let p;try{const a=de(e,c);p=new F(a,this.globalConfigStore,this.auditProducer),p.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),p.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),p.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&p.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&p.closeLifecycleAdmission(),this.sharedInstances.set(o,p),await this.startTrackedInstance(p),p.forceRefreshSkills(void 0,{force:!0}),m.info("manager",`shared instance started: ${o}`)}catch(a){if(p&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(p),this.sharedInstances.get(o)===p&&this.sharedInstances.delete(o)}catch(g){m.error("manager",`cleanup shared instance failed ${o}: ${g}`)}const l=a instanceof Error?a.message:String(a);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,c),m.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):m.error("manager",`start shared instance failed ${o}: ${a}`)}}for(const{key:c}of i){const o=this.sharedInstances.get(c);if(o)try{await this.stopInstance(o),this.sharedInstances.get(c)===o&&this.sharedInstances.delete(c),m.info("manager",`shared instance stopped: ${c}`)}catch(p){m.error("manager",`stop shared instance failed ${c}: ${p}`)}}}restoreCachedSharedInstances(e){const n=this.sharedOwnersCache.load(e.name);if(n.length===0)return;const t=this.instances.find(s=>s.name===e.name);!t||!this.isActiveGeneration(t)||(m.info("manager",`restoring ${n.length} cached shared instance(s) for ${e.name}`),this.onShareSet(t,e,n))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){return this.runMutation(()=>this.addAgentUnlocked(e),[String(e.name??"")])}async addAgentUnlocked(e){const n=this.toRuntimeInstanceConfig(e,{quiet:!0});if(this.instances.some(t=>t.name===n.name))throw new Error(`Agent "${n.name}" already exists`);this.configRepository.assertCanAdd(e),await this.addAgentInternal(e,"agents.json",{targetPersisted:!1});try{const t=this.configRepository.insert(e);this.rawConfigMap.set(e.name,t);const s=this.instances.find(c=>c.name===e.name),i=s?this.instanceGenerations.get(s):void 0;if(i){i.persisted=!0;const c=this.configMap.get(e.name);c&&this.restoreCachedSharedInstances(c)}}catch(t){try{await this.removeAgentInternal(e.name)}catch(s){throw new AggregateError([t,s],`Failed to persist and roll back agent "${e.name}"`)}throw t}await this.ensureAuxServices(),m.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,n,t){const s=this.toRuntimeInstanceConfig(e);if(this.instances.some(i=>i.name===s.name))throw new Error(`Agent "${s.name}" already exists`);await this.startInstanceFromConfig(s,e,n,t)}createConfiguredInstance(e,n=!0){const t=new F(e,this.globalConfigStore,this.auditProducer);return this.instanceGenerations.set(t,{name:e.name,agentId:e.aibot.agentId,persisted:n,retired:!1,platformDeletedObserved:!1}),t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),this.options.requestGracefulShutdown&&t.setDaemonShutdownRequester(this.options.requestGracefulShutdown),t.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),t.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(s=>this.installAgent(s)),t.setShareSetHandler(s=>this.onShareSet(t,e,s)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(t)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(s=>this.setAgentProvider(e.name,s)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(s,i)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,s,i):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const s=this.relayStateApplier;return s?s.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>Y(this.getAgentProvider(e.name))),t}hasAnyPendingOrBackgroundWork(){return this.instances.some(e=>e.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(e=>e.hasPendingOrBackgroundWork())}closeAllLifecycleAdmission(){this.lifecycleAdmissionClosed=!0;for(const e of this.instances)e.closeLifecycleAdmission();for(const e of this.sharedInstances.values())e.closeLifecycleAdmission()}reopenAllLifecycleAdmission(){if(!this.stopping){this.lifecycleAdmissionClosed=!1;for(const e of this.instances)e.openLifecycleAdmission();for(const e of this.sharedInstances.values())e.openLifecycleAdmission()}}async prepareProviderRuntime(e,n){const t=this.resolveAgentProvider(n.provider);W(n.client_type)&&t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()&&await Q(e.name,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()})}async startInstanceFromConfig(e,n,t,s){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});if(await this.prepareProviderRuntime(e,n),this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});const i=this.createConfiguredInstance(e,s.targetPersisted);this.instances.push(i);const c=this.startTrackedInstance(i),o=J(),p=await new Promise(h=>{let d=!1;const u=setTimeout(()=>{d||(d=!0,h({kind:"timeout"}))},o);c.then(()=>{d||(d=!0,clearTimeout(u),h({kind:"started"}))},f=>{d||(d=!0,clearTimeout(u),h({kind:"failed",error:f}))})}),a=this.instanceGenerations.get(i);if(a?.platformDeletedObserved&&!a.persisted){const h=new me(`Agent "${e.name}" was deleted before its local configuration commit`);try{await this.stopInstance(i),this.retireInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){throw this.failedStartAgents.add(e.name),new AggregateError([h,d],`Failed to clean up uncommitted deleted agent "${e.name}"`)}throw h}if(p.kind==="started"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),this.failedStartAgents.delete(e.name),s.targetPersisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.();return}if(p.kind==="timeout"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),m.warn("manager",`Start pending for ${e.name}, continue retrying in background`),c.then(async()=>{if(!this.removingAgents.has(e.name)&&this.isActiveGeneration(i)){if(this.stopping){try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i)}catch(h){this.failedStartAgents.add(e.name),m.error("manager",`Failed to stop late-started ${e.name}: ${h}`)}return}this.failedStartAgents.delete(e.name),this.instanceGenerations.get(i)?.persisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.(),m.info("manager",`Delayed start succeeded: ${e.name}`)}},async h=>{if(I(h)){this.onAgentDeletedByPlatform(i);return}if(!(this.stopping||this.removingAgents.has(e.name))&&this.isActiveGeneration(i)){try{await this.stopInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){this.failedStartAgents.add(e.name),m.error("manager",`Failed to clean up delayed start ${e.name}: ${d}`)}m.error("manager",`Delayed start failed ${e.name}: ${h}`)}}).catch(h=>{m.error("manager",`Delayed start finalizer failed ${e.name}: ${h}`)});return}const l=p.error;I(l)&&this.onAgentDeletedByPlatform(i);let g;try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i),this.failedStartAgents.delete(e.name)}catch(h){g=h,this.failedStartAgents.add(e.name)}throw s.targetPersisted&&(this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t})),g!==void 0?new AggregateError([l,g],`Failed to start and clean up agent "${e.name}"`):l}async removeAgent(e){return this.runMutation(()=>this.removeAgentUnlocked(e),[e])}async removeAgentUnlocked(e){if(!(this.instances.some(t=>t.name===e)||this.rawConfigMap.has(e)||[...this.sharedInstances.keys()].some(t=>t.startsWith(`${e}#shared:`))))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});this.configRepository.remove(e),await this.removeAgentInternal(e),m.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){this.removingAgents.add(e);try{const t=()=>n?.deadlineMs===void 0?E():Math.max(1,n.deadlineMs-Date.now()),s=this.instances.findIndex(h=>h.name===e),i=s===-1?void 0:this.instances[s],c=`${e}#shared:`,o=n?.keepShareCache?void 0:{dropPendingEvents:!0},p=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&p.set(i,this.stopInstance(i,o,t())));for(const[h,d]of this.sharedInstances)h.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&p.set(d,this.stopInstance(d,o,t())));const a=this.shareSyncChains.get(e);this.shareSyncChains.delete(e);const l=[];try{await this.waitForLifecycleDrain([...a?[a]:[],...p.values()],`agent "${e}" shared lifecycle drain`,t())}catch(h){l.push(h)}const g=[];for(const[h,d]of[...this.sharedInstances.entries()])h.startsWith(c)&&g.push((p.get(d)??this.stopInstance(d,o,t())).then(()=>{this.sharedInstances.get(h)===d&&this.sharedInstances.delete(h),this.startingInstances.delete(d)}).catch(u=>{l.push(u),m.error("manager",`stop shared instance failed ${h}: ${u}`)}));if(i)try{await(p.get(i)??this.stopInstance(i,o,t())),this.retireInstance(i);const h=this.instances.indexOf(i);h!==-1&&this.instances.splice(h,1),this.startingInstances.delete(i)}catch(h){l.push(h)}if(await Promise.all(g),!n?.keepShareCache){const h=K(e).catch(()=>{});if(n?.deadlineMs===void 0)await h;else try{await this.waitForLifecycleDrain([h],`agent "${e}" relay shutdown`,t())}catch(d){l.push(d)}}if(l.length>0)throw new AggregateError(l,`Failed to stop agent "${e}" completely`);this.configMap.delete(e),this.rawConfigMap.delete(e),this.failedStartAgents.delete(e),n?.keepShareCache||this.sharedOwnersCache.delete(e)}finally{this.removingAgents.delete(e)}}onAgentDeletedByPlatform(e){const n=this.instanceGenerations.get(e);if(!n||(n.platformDeletedObserved=!0,this.requestStartCancellation(e),!n.persisted))return;const t={source:e,name:n.name,agentId:n.agentId};this.pendingPlatformDeletes.set(e,t),!this.stopping&&(this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),this.runMutation(async()=>{}).catch(()=>{}).finally(()=>{this.deletedAgentCleanups.delete(e)})))}async retryPendingPlatformDeletesUnlocked(e){if(this.pendingPlatformDeletes.size!==0)for(const[n,t]of[...this.pendingPlatformDeletes])try{await this.cleanupPlatformDeletedUnlocked(t,e),this.pendingPlatformDeletes.delete(n)}catch(s){m.error("manager",`cleanup for deleted agent "${t.name}" failed: ${s}`)}}async cleanupPlatformDeletedUnlocked(e,n){const{source:t,name:s,agentId:i}=e,c=this.instances.find(g=>g.name===s),o=c?this.instanceGenerations.get(c)?.agentId??String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim():void 0;if(c&&c!==t&&o!==i)return;m.warn("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`);const p=this.rawConfigMap.get(s),a=this.configMap.get(s),l=[];if(c&&o===i)try{await this.removeAgentInternal(s,{deadlineMs:n})}catch(g){l.push(g)}try{const g=this.configRepository.scan();if(g.fileErrors.length>0){const u=g.fileErrors.map(f=>`${f.file}: ${f.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${u}]`)}const h=g.entries.filter(u=>String(u.entry?.name??"").trim()===s);if(!h.some(u=>String(u.entry?.agent_id??"").trim()!==i)&&h.length>0&&this.configRepository.remove(s),l.length===0){const u=String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim();(!u||u===i)&&(this.rawConfigMap.delete(s),this.configMap.delete(s),this.failedStartAgents.delete(s))}}catch(g){l.push(g),p&&this.rawConfigMap.set(s,p),a&&this.configMap.set(s,a)}if(l.length>0)throw new AggregateError(l,`Failed to fully clean up deleted agent "${s}"`);m.info("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406`)}isAgentBusy(e){return this.instances.find(t=>t.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const n=this.instances.find(t=>t.name===e);return n?(n.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(t=>t.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const n=e.api_key_ref.trim(),t=this.relaySecretResolver?.(n);if(!t)throw Object.assign(new Error(`relay credential "${n}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:t}}toRuntimeInstanceConfig(e,n={}){const t=this.resolveAgentProvider(e.provider);return ze(t===e.provider?e:{...e,provider:t},n)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){return this.runMutation(()=>this.restartAgentUnlocked(e),[e])}async restartAgentUnlocked(e){const n=this.rawConfigMap.get(e);if(!n)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,n.entry,n.file),m.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,n,t){return this.runMutation(()=>this.setAgentProviderUnlocked(e,n,t),[e])}async setAgentProviderUnlocked(e,n,t){if(!this.rawConfigMap.get(e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const i=n?.api_key_ref?.trim()?Object.fromEntries(Object.entries(n).filter(([a])=>a!=="api_key")):n,c=this.configRepository.updateExisting(e,a=>{const l={...a};return i===void 0?delete l.provider:l.provider=i,this.toRuntimeInstanceConfig(l,{quiet:!0}),l}),o=c.entry;this.rawConfigMap.set(e,c);const p=String(o.client_type??"").trim().toLowerCase();try{W(p)&&(n?.base_url?.trim()&&n?.api_key?.trim()&&n?.model?.trim()?await Q(e,{baseUrl:n.base_url.trim(),apiKey:n.api_key.trim(),model:n.model.trim()}):await K(e))}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}if(t?.deferRestart){this.instances.find(a=>a.name===e)?.markRelayEnvStale(),m.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}try{await this.replaceInstance(e,o,c.file)}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}m.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const n=this.rawConfigMap.get(e)?.entry.provider;return!!(n?.api_key_ref?.trim()||n?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&(Ie(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,n){const t=this.instances.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.fetchRelayCredential(n)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),n=[];for(const t of e)try{await this.setAgentProvider(t,void 0,{deferRestart:this.isAgentBusy(t)}),n.push(t)}catch(s){m.warn("manager",`Failed to clear provider config for "${t}": ${s instanceof Error?s.message:String(s)}`)}return n}async replaceInstance(e,n,t){const s=this.toRuntimeInstanceConfig(n);try{await this.removeAgentInternal(e,{keepShareCache:!0})}catch(o){throw this.failedStartAgents.add(e),o}const i=String(n.agent_id??"").trim();if([...this.pendingPlatformDeletes.values()].some(o=>o.name===e&&o.agentId===i))throw await this.retryPendingPlatformDeletesUnlocked(),Object.assign(new Error(`Agent "${e}" was permanently deleted while its instance was stopping`),{code:"AGENT_DELETE_PENDING"});try{await this.startInstanceFromConfig(s,n,t,{targetPersisted:!0})}catch(o){throw this.configMap.set(e,s),this.rawConfigMap.set(e,{entry:n,file:t}),o}}async reload(){return this.runMutation(()=>this.doReload())}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:n,skipped:t}=this.configRepository.scan();for(const a of t)m.info("manager",`reload: skipped ${a}: not an agent config (no "agents" key)`);if(n.length>0){const a=n.map(l=>`${l.file}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${a}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const s=[];for(const{entry:a}of e)try{this.toRuntimeInstanceConfig(a,{quiet:!0})}catch(l){const g=a?.name,h=typeof g=="string"&&g.trim()?g:"<unknown>";s.push({name:h,error:l instanceof Error?l.message:String(l)})}if(s.length>0){const a=s.map(l=>`${l.name}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${a}]`),{code:"RELOAD_UNSAFE"})}const i=new Map,c=new Set([...this.pendingPlatformDeletes.values()].map(a=>a.name));for(const a of e){const l=String(a.entry?.name??"").trim();l&&(c.has(l)||(i.has(l)&&m.warn("manager",`reload: duplicate agent name "${l}", last one wins`),i.set(l,a)))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},p=new Set([...this.rawConfigMap.keys()].filter(a=>!c.has(a)));for(const a of p)if(!i.has(a))try{await this.removeAgentInternal(a),o.removed.push(a)}catch(l){o.failed.push({name:a,error:l instanceof Error?l.message:String(l)})}for(const[a,l]of i){const g=this.rawConfigMap.get(a);if(g)if(!Xe(g.entry,l.entry)||!this.instances.some(h=>h.name===a)||this.failedStartAgents.has(a))try{await this.replaceInstance(a,l.entry,l.file),o.restarted.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}else this.rawConfigMap.set(a,l),o.unchanged.push(a);else try{await this.addAgentInternal(l.entry,l.file,{targetPersisted:!0}),o.added.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}}return m.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),await this.ensureAuxServices(),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return Ye(this.instances,e)}async probeOne(e,n={}){return Je(this.instances,e,n)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const n=await this.installer.install(e);return Me(n),n}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function Ye(r,e){const n=e.concurrency??4,t=Date.now(),s=new Array(r.length);await new Promise(a=>{let l=0,g=0;const h=r.length;if(h===0){a();return}function d(_){const w=r[_];w.probe(e).then(S=>{s[_]=S,u()},S=>{s[_]={agent_name:w.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:S?.message??String(S)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},u()})}function u(){g++,l<h?d(l++):g===h&&a()}const f=Math.min(n,h);for(let _=0;_<f;_++)d(l++)});const i=s.filter(a=>a.status==="healthy").length,c=s.filter(a=>a.status==="degraded").length,o=s.filter(a=>a.status==="unavailable").length,p=await Ue();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:p,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Je(r,e,n){const t=r.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.probe(n)}export{It as Manager,Y as normalizeAgentProviderForRuntime,Ue as probeInstalledClientCommands,Ye as probeInstances,Je as probeOneInstance};
|
|
1
|
+
import{join as k}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as F}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,sharedInstanceKey as he,SharedOwnersCache as ue}from"./manager-share-config.js";import{AgentDeletedError as ge,isAgentDeletedError as C}from"./core/aibot/errors.js";import{GRIX_PATHS as A,log as p}from"./core/log/index.js";import{resolveClientVersion as pe}from"./core/util/client-version.js";import{UpgradeChecker as me}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as _e}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as we}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as Ae}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as Se}from"./core/aibot/types.js";import{buildReportedSkills as ke,logDefaultSkillsCheck as ve,cleanupProjectedSkills as B}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as W,supportsNonMitmRelayConfig as Ie}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as Q,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Ee}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as $e}from"./core/runtime/cursor-resolve.js";import{probeInstalledClientCommands as z}from"./core/probe/installed-clients.js";import{AgentInstaller as Pe}from"./core/installer/installer.js";import{reportInstallFailure as Me}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as xe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as De}from"./adapter/codex/direct-home.js";import{AgentConfigRepository as V}from"./core/config/agent-config-repository.js";function X(r){if(r)return{...r.provider_id?.trim()?{providerId:r.provider_id.trim()}:{},...r.base_url?.trim()?{baseUrl:r.base_url.trim()}:{},...r.quota_base_url?.trim()?{quotaBaseUrl:r.quota_base_url.trim()}:{},...r.api_key?.trim()?{apiKey:r.api_key.trim()}:{},...r.model?.trim()?{model:r.model.trim()}:{},...r.fast_model?.trim()?{fastModel:r.fast_model.trim()}:{},...r.effort?.trim()?{effort:r.effort.trim()}:{}}}const Re=8e3,Oe=1e4,je=8e3;function Le(r,e){switch(r){case"claude":return{adapterType:"claude",command:"claude"};case"codex":return{adapterType:"codex",command:"codex",options:{sandboxMode:"danger-full-access"}};case"gemini":return{adapterType:"acp",command:"gemini",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"qwen":return{adapterType:"acp",command:"qwen",adapterHint:"qwen/base",autoInjectArgs:{acp:!0},enableSessionBinding:!0};case"pi":return{adapterType:"pi",command:"pi"};case"cursor":return{adapterType:"cursor",command:$e()};case"reasonix":return{adapterType:"acp",command:"reasonix",args:["acp"],enableSessionBinding:!0};case"codewhale":return{adapterType:"codewhale",command:"codewhale",enableSessionBinding:!0};case"openhuman":return{adapterType:"openhuman",command:"openhuman-core",enableSessionBinding:!0};case"kiro":return{adapterType:"acp",command:"kiro-cli",args:["acp"],enableSessionBinding:!0};case"kimi":return{adapterType:"acp",command:"kimi",args:["acp"],options:{initial_mode:"yolo"},enableSessionBinding:!0};case"opencode":return{adapterType:"opencode",command:"opencode",args:["serve"],enableSessionBinding:!0};case"copilot":{const n=Ee();return{adapterType:"acp",command:n.command,args:[...n.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"deepseek":return{adapterType:"deepseek-harness",command:"dsh",adapterHint:"deepseek/grix-bridge-v1",options:{integrationMode:"profile_bridge",dshProfile:"web",autoInstallBridge:!0,autoStartProfile:!0},enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};case"acp":{const n=typeof e?.command=="string"?e.command.trim():"";if(!n)throw new Error(`agent ${e?.name??"<unknown>"}: client_type=acp requires "command" (the ACP CLI executable) in the agent config; put its ACP subcommand or flag in "args"`);return{adapterType:"acp",command:n,adapterHint:"acp/base",enableSessionBinding:!0}}default:throw new Error(`Unsupported client_type: ${r}`)}}function v(r,e){return String(r??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||String(e??"").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"default"}function Ne(r){return k(A.data,`session-bindings-${v(r,"unknown")}.json`)}function Ge(r,e){return k(A.data,`session-bindings-${v(r,e)}.json`)}function Ue(r,e){return k(A.data,`active-events-${v(r,e)}.json`)}function qe(r){return k(A.data,`pending-events-${v(r,"unknown")}.json`)}function He(r,e){const n=v(r,"agent"),t=v(e,"unknown");return k(A.data,`terminal-outbox-${n}-${t}.json`)}function Fe(...r){const e=[],n=new Set;for(const t of r)for(const s of t??[]){const i=String(s??"").trim(),c=i.toLowerCase();!i||n.has(c)||(n.add(c),e.push(i))}return e.length>0?e:void 0}function Be(r,e){const n={claude:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:99,queueTimeoutMs:0}},t=n[r]??n.acp;return{maxConcurrent:e?.max_concurrent??t.maxConcurrent??1,maxQueued:e?.max_queued??t.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??t.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(r,e={}){const n=pe(),t=String(r.client_type??"").trim().toLowerCase(),s=Le(t,r),i=t==="deepseek"?r.dsh?.integration_mode??"profile_bridge":void 0,c=i==="embedded_jsonrpc",o=String(r.ws_url??"").trim(),g=typeof r.name=="string"?r.name:"",a=g.trim(),l="get_session_usage",m="get_rate_limits",h="get_agent_global_config",d=Se;if(!a)throw new Error("agent name is required");if(g!==a)throw new Error(`agent name must not contain leading or trailing whitespace: "${g}"`);if(!o)throw new Error(`agent ${r.name}: ws_url is required`);if(!r.agent_id?.trim())throw new Error(`agent ${r.name}: agent_id is required`);if(!r.api_key?.trim())throw new Error(`agent ${r.name}: api_key is required`);const u=s.adapterType,f=u==="acp",_=t==="qwen",y={...s.options??{}};if(u==="deepseek-harness"&&(y.integrationMode=i,y.dshProfile=r.dsh?.profile?.trim()||"web",y.autoInstallBridge=r.dsh?.auto_install_bridge??!c,y.autoStartProfile=r.dsh?.auto_start_profile??!c),u==="deepseek-harness"&&r.provider?.model?.trim()&&(y.defaultModel=r.provider.model.trim()),r.codex_config_overrides!==void 0){if(u!=="codex")throw new Error(`agent ${r.name}: codex_config_overrides is only supported for client_type=codex`);xe(r.codex_config_overrides,`agent ${r.name}: codex_config_overrides`),y.codexConfigOverrides=r.codex_config_overrides}if(u==="codex"&&r.provider?.wire_api!==void 0){const w=r.provider;if(w.wire_api!=="responses"||!w.base_url?.trim()||!w.api_key?.trim()||!w.model?.trim()||!w.catalog_version?.trim()||!w.catalog_sha256?.trim()||!w.catalog_json?.trim())throw Object.assign(new Error(`agent ${r.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const H=De(A.data,r.name);y.codexHome=H,y.codexDirect={codexHome:H,config:{baseUrl:w.base_url.trim(),model:w.model.trim(),...w.reasoning_effort?.trim()?{reasoningEffort:w.reasoning_effort.trim()}:{},token:w.api_key.trim()},catalog:{version:w.catalog_version.trim(),sha256:w.catalog_sha256.trim(),json:w.catalog_json}}}const S=u==="codex"?{capabilities:["local_action_v1","agent_invoke"],localActions:["session_control","get_context","set_model","set_mode","set_reasoning_effort","set_service_tier","set_sandbox_mode","exec_approve","exec_reject","file_list","create_folder","turn_interrupt","permission_approve","permission_reject","thread_compact",l,m]}:null,J=u==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",l,m]}:null,P=_?{capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",l],adapterHint:"qwen/base"}:null,M=u==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","get_context","file_list","create_folder",l,m]}:null,T=u==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,x=u==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",m]}:null,L=u==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",l]}:null,D=u==="opencode"?{adapterHint:"opencode/base",capabilities:["stream_chunk","local_action_v1"],localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder"]}:null,R=u==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",m]}:null,O=u==="deepseek-harness"?{adapterHint:c?"deepseek/jsonrpc-v1":"deepseek/grix-bridge-v1",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_provider","set_model","set_mode","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile",l,m,"exec_approve","exec_reject","permission_approve","permission_reject","file_list","create_folder","dsh_bridge_status","dsh_install_bridge","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill","dsh_list_mcp","dsh_list_tools","dsh_list_jobs","dsh_list_goals"]}:null,N=new Set(["hermes","reasonix","kimi","copilot","acp"]),Z=f&&!N.has(t),ee=f&&!_?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...Z?[l]:[]]}:null,te=t==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",l,m]}:null,ne=u==="codex"||u==="claude"||u==="opencode"||u==="deepseek-harness"||t==="gemini"?["session_control","set_model","set_mode",...u==="deepseek-harness"?["set_provider","set_thinking","set_reasoning_effort","set_preset","set_profile","create_profile","dsh_list_plugins","dsh_enable_plugin","dsh_disable_plugin","dsh_refresh_plugins","dsh_list_skills","dsh_enable_skill","dsh_disable_skill"]:[],...u==="claude"?["set_reasoning_effort"]:[]]:void 0,se=u==="cursor"||u==="opencode"||u==="openhuman"||u==="agy"||f&&N.has(t)?[m]:[l,m];f&&y.raw_transport===void 0&&(y.raw_transport=t==="gemini");const re=`${t}/base`,I=we(u,t),ae=I==="kiro"?void 0:process.cwd(),j=X(r.provider),b={...process.env,...r.env};if(I==="hermes"&&!b.HERMES_HOME?.trim()){const w=be(t,j);b.HERMES_HOME=Ce({...w,...b},r.name)}const G=ke({mode:I,projectDir:ae,env:b}),U=G.filter(w=>w.source==="connector").map(w=>w.name);U.length>0&&!e.quiet&&p.info("manager",`[${r.name}] injecting connector skills: [${U.join(", ")}]`);const q=ye(G,A.skills);let ie=q.length>0?q:void 0;const oe=Ae({skillsDir:A.skills,mode:I,home:ce(),env:b});return{name:a,adapterType:u,aibot:{url:o,agentId:r.agent_id,apiKey:r.api_key,clientType:t,clientVersion:n,adapterHint:O?.adapterHint??s.adapterHint??P?.adapterHint??M?.adapterHint??T?.adapterHint??x?.adapterHint??D?.adapterHint??R?.adapterHint??re,capabilities:O?.capabilities??S?.capabilities??L?.capabilities??M?.capabilities??T?.capabilities??x?.capabilities??D?.capabilities??R?.capabilities??P?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Fe(O?.localActions??S?.localActions??L?.localActions??J?.localActions??M?.localActions??T?.localActions??x?.localActions??D?.localActions??R?.localActions??P?.localActions??te?.localActions??ee?.localActions??["exec_approve","exec_reject"],ne,se,["connector_rollback","connector_upgrade_push",h,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...d]),skills:ie,librarySkills:oe,terminalOutboxPath:He(r.name,r.agent_id)},agent:{command:c?"dsh-jsonrpc-agent":s.command,args:[...s.args??[],...r.args??[]],env:r.env,clientType:t,...j?{provider:j}:{}},adapterOptions:y,acpAuthMethod:y.auth_method,acpInitialMode:y.initial_mode,acpMcpTools:y.acp_mcp_tools,promptTimeoutMs:r.prompt_timeout_ms,bindingsPath:Ne(r.agent_id),legacyBindingsPath:Ge(r.name,r.agent_id),activeEventStorePath:Ue(r.name,r.agent_id),pendingEventStorePath:qe(r.agent_id),...s.enableSessionBinding||f?{enableSessionBinding:!0}:{},...s.autoInjectArgs||t==="reasonix"&&r.provider?.model?.trim()?{autoInjectArgs:{...s.autoInjectArgs??{},...t==="reasonix"&&r.provider?.model?.trim()?{model:r.provider.model.trim()}:{}}}:{},poolMaxSize:r.pool?.maxSize??(u==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:r.pool?.idleTimeoutMs,eventQueue:Be(u,r.event_queue),logDir:A.log,providerBaseUrl:r.provider_base_url?.trim()||void 0,providerApiKey:r.provider_api_key?.trim()||void 0}}function Y(){const r=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Re}function E(){const r=process.env.GRIX_AGENT_STOP_WAIT_MS,e=Number(r);return Number.isFinite(e)&&e>=500?Math.floor(e):Oe}function Qe(){return Math.min(E(),je)}function $(r){if(r===null||typeof r!="object")return JSON.stringify(r)??"null";if(Array.isArray(r))return`[${r.map($).join(",")}]`;const e=r;return`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${$(e[t])}`).join(",")}}`}function Ke(r,e){return $(r)===$(e)}class vt{options;instances=[];configMap=new Map;rawConfigMap=new Map;mutationTail=Promise.resolve();startingInstances=new Set;failedStartAgents=new Set;removingAgents=new Set;instanceGenerations=new WeakMap;sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;pendingPlatformDeletes=new Map;upgradeChecker=null;skillSyncer=null;globalConfigStore;configRepository=new V(A.config);sharedOwnersCache=new ue(A.data);installer=new Pe;relaySecretResolver;auditProducer=Te();constructor(e={}){this.options=e}runMutation(e,n=[]){const t=this.mutationTail.catch(()=>{}).then(async()=>{if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});await this.retryPendingPlatformDeletesUnlocked();const s=new Set([...this.pendingPlatformDeletes.values()].map(c=>c.name)),i=n.find(c=>s.has(c.trim()));if(i)throw Object.assign(new Error(`Agent "${i}" is pending permanent platform-deletion cleanup`),{code:"AGENT_DELETE_PENDING"});return e()});return this.mutationTail=t.then(()=>{},()=>{}),t}async startTrackedInstance(e){this.startingInstances.add(e);try{await e.start()}finally{this.startingInstances.delete(e)}}requestStartCancellation(e){const n=e.requestStop;typeof n=="function"&&n.call(e)}async stopInstance(e,n,t=E()){let s;try{await Promise.race([e.stop(n),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`Agent "${e.name}" stop timed out after ${t}ms`),{code:"AGENT_STOP_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForLifecycleDrain(e,n,t=E()){let s;try{await Promise.race([Promise.allSettled([...e]).then(()=>{}),new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"LIFECYCLE_DRAIN_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}async waitForShutdownOperation(e,n,t){let s;try{return await Promise.race([e,new Promise((i,c)=>{s=setTimeout(()=>{c(Object.assign(new Error(`${n} timed out after ${t}ms`),{code:"SHUTDOWN_OPERATION_TIMEOUT"}))},t)})])}finally{s!==void 0&&clearTimeout(s)}}isActiveGeneration(e){const n=this.instanceGenerations.get(e);return!!n&&!n.retired&&this.instances.find(t=>t.name===n.name)===e}retireInstance(e){const n=this.instanceGenerations.get(e);n&&(n.retired=!0)}async start(e){return this.runMutation(()=>this.startUnlocked(e))}async startUnlocked(e){const n=e??A.config;this.configRepository=new V(n),p.info("manager",`Loading configs from ${n}`),ve(),B(),this.globalConfigStore=new _e(k(A.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:t,fileErrors:s,skipped:i}=this.configRepository.scan();for(const a of s)p.error("manager",`Failed to load ${a.file}: ${a.error}`);for(const a of i)p.info("manager",`Skipped ${a}: not an agent config (no "agents" key)`);if(t.length===0&&s.length===0&&i.length===0){p.warn("manager",`No agent configurations found in ${n}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const c=[];let o=s.length;for(const{entry:a,file:l}of t)try{const m=this.toRuntimeInstanceConfig(a);c.push({config:m,entry:a,file:l}),p.info("manager",`Loaded ${m.name} (${m.adapterType??"acp"}) from ${l}`)}catch(m){const h=typeof a?.name=="string"?a.name:"<unknown>";p.error("manager",`Invalid agent config in ${l} (name=${h}): ${m}`),o++}let g=0;if(c.length>0){const a=Y();p.info("manager",`Starting ${c.length} agent(s), startup wait=${a}ms`);const l=d=>{this.instances=this.instances.filter(u=>u!==d)},m=c.map(({config:d,entry:u,file:f})=>{const _=this.createConfiguredInstance(d);return this.instances.push(_),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:u,file:f}),{config:d,instance:_,startPromise:this.prepareProviderRuntime(d,u).then(()=>{if(this.stopping)throw this.requestStartCancellation(_),Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});return this.startTrackedInstance(_)})}}),h=await Promise.all(m.map(async d=>{const u=await new Promise(f=>{let _=!1;const y=setTimeout(()=>{_||(_=!0,f({kind:"timeout"}))},a);d.startPromise.then(()=>{_||(_=!0,clearTimeout(y),f({kind:"started"}))}).catch(S=>{_||(_=!0,clearTimeout(y),f({kind:"failed",error:S}))})});return{task:d,outcome:u}}));for(const{task:d,outcome:u}of h){if(u.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(u.kind==="failed"){if(C(u.error)){this.onAgentDeletedByPlatform(d.instance);continue}try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(f){this.failedStartAgents.add(d.config.name),p.error("manager",`Failed to clean up partially started ${d.config.name}: ${f}`)}p.error("manager",`Failed to start ${d.config.name}: ${u.error}`);continue}g++,p.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{this.stopping||this.removingAgents.has(d.config.name)||this.isActiveGeneration(d.instance)&&(p.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config))}).catch(async f=>{if(C(f)){this.onAgentDeletedByPlatform(d.instance);return}if(!(this.stopping||this.removingAgents.has(d.config.name))&&this.isActiveGeneration(d.instance)){try{await this.stopInstance(d.instance),l(d.instance),this.failedStartAgents.delete(d.config.name)}catch(_){this.failedStartAgents.add(d.config.name),p.error("manager",`Failed to clean up delayed start ${d.config.name}: ${_}`)}p.error("manager",`Delayed start failed: ${d.config.name}: ${f}`)}})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-g);p.info("manager",`${d}/${c.length} agent(s) running now`)}g>0&&p.warn("manager",`${g} agent(s) still connecting in background`)}if(this.instances.length===0&&c.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.stopping||this.configMap.size===0)return;const e=[...this.configMap.values()].map(n=>({apiKey:n.aibot.apiKey,wsUrl:n.aibot.url}));if(!this.upgradeChecker&&(this.upgradeChecker=new me(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission(),{requestGracefulShutdown:this.options.requestGracefulShutdown,reopenLifecycleAdmission:()=>this.reopenAllLifecycleAdmission(),isConnected:()=>this.instances.length===0||this.instances.some(n=>n.getStatus().wsConnected),onTransactionSettled:this.options.onTransactionSettled}),await this.upgradeChecker.start(),this.stopping)){this.upgradeChecker.stop();return}this.skillSyncer||(this.skillSyncer=new fe(e,A.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start(),this.stopping&&this.skillSyncer.stop())}refreshOwnerLibrarySkills(){const e=new Map,n=t=>{let s;try{s=t.skillsSyncGroupKey()}catch(c){p.warn("manager",`skillsSyncGroupKey failed for ${t.name}: ${c}`);return}const i=e.get(s)??[];i.push(t),e.set(s,i)};for(const t of this.instances)n(t);for(const t of this.sharedInstances.values())n(t);for(const[t,s]of e){let i=!1;const c=[];for(const o of s){if(i){c.push(o);continue}let g=!1;try{g=o.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(a){p.warn("manager",`owner library skills reporter threw group=${t} agent=${o.name}: ${a}`)}if(g){i=!0;continue}p.warn("manager",`owner library skills reporter failed group=${t} agent=${o.name}; trying next`)}if(!i){p.warn("manager",`owner library skills: no reporter succeeded group=${t}`);continue}for(const o of c)try{o.adoptSkillsWireHashFromDisk()}catch(g){p.warn("manager",`adoptSkillsWireHashFromDisk failed for ${o.name}: ${g}`)}}}async stop(){p.info("manager","Stopping all agents..."),this.stopping=!0;const e=Qe(),n=Date.now()+e,t=()=>Math.max(1,n-Date.now()),s=new Set([...this.instances,...this.sharedInstances.values(),...this.startingInstances]);for(const o of s)this.requestStartCancellation(o);const i=new Map([...s].map(o=>[o,this.stopInstance(o,void 0,e)])),c=[];this.upgradeChecker?.stop(),this.skillSyncer?.stop();try{await this.waitForLifecycleDrain([this.mutationTail,...this.shareSyncChains.values(),...i.values()],"manager shutdown lifecycle drain",t())}catch(o){c.push(o)}this.upgradeChecker?.stop(),this.skillSyncer?.stop(),this.shareSyncChains.clear(),await Promise.all([...[...this.instances].map(async o=>{try{await(i.get(o)??this.stopInstance(o,void 0,t())),this.retireInstance(o),this.instances=this.instances.filter(g=>g!==o),this.startingInstances.delete(o)}catch(g){c.push(g)}}),...[...this.sharedInstances.entries()].map(async([o,g])=>{try{await(i.get(g)??this.stopInstance(g,void 0,t())),this.sharedInstances.get(o)===g&&this.sharedInstances.delete(o),this.startingInstances.delete(g)}catch(a){c.push(a)}})]);try{await this.waitForLifecycleDrain([this.retryPendingPlatformDeletesUnlocked(n)],"platform-delete shutdown drain",t())}catch(o){c.push(o)}try{await this.waitForShutdownOperation(Promise.resolve().then(()=>this.globalConfigStore?.flush()),"global config shutdown flush",t())}catch(o){c.push(o)}if(await this.auditProducer.stop({timeoutMs:Math.min(5e3,t())}).catch(()=>!1),B(),c.length>0)throw new AggregateError(c,"Failed to stop all agents completely");p.info("manager","All stopped")}onShareSet(e,n,t){if(this.stopping||this.removingAgents.has(n.name)||!this.isActiveGeneration(e))return;this.sharedOwnersCache.save(n.name,t);const i=(this.shareSyncChains.get(n.name)??Promise.resolve()).catch(()=>{}).then(()=>{if(!(this.stopping||this.removingAgents.has(n.name))&&this.isActiveGeneration(e))return this.syncSharedInstances(n,t,e)});this.shareSyncChains.set(n.name,i)}async syncSharedInstances(e,n,t){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))return;const{toAdd:s,toRemove:i}=de(e.name,this.sharedInstances.keys(),n);for(const c of s){if(this.stopping||this.removingAgents.has(e.name)||t&&!this.isActiveGeneration(t))break;const o=he(e.name,c);let g;try{const a=le(e,c);g=new F(a,this.globalConfigStore,this.auditProducer),g.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),g.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),g.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.options.requestGracefulShutdown&&g.setDaemonShutdownRequester(this.options.requestGracefulShutdown),this.lifecycleAdmissionClosed&&g.closeLifecycleAdmission(),this.sharedInstances.set(o,g),await this.startTrackedInstance(g),g.forceRefreshSkills(void 0,{force:!0}),p.info("manager",`shared instance started: ${o}`)}catch(a){if(g&&!this.stopping&&!this.removingAgents.has(e.name))try{await this.stopInstance(g),this.sharedInstances.get(o)===g&&this.sharedInstances.delete(o)}catch(m){p.error("manager",`cleanup shared instance failed ${o}: ${m}`)}const l=a instanceof Error?a.message:String(a);/Auth failed/i.test(l)?(this.sharedOwnersCache.remove(e.name,c),p.warn("manager",`shared instance auth rejected, removed from cache: ${o}`)):p.error("manager",`start shared instance failed ${o}: ${a}`)}}for(const{key:c}of i){const o=this.sharedInstances.get(c);if(o)try{await this.stopInstance(o),this.sharedInstances.get(c)===o&&this.sharedInstances.delete(c),p.info("manager",`shared instance stopped: ${c}`)}catch(g){p.error("manager",`stop shared instance failed ${c}: ${g}`)}}}restoreCachedSharedInstances(e){const n=this.sharedOwnersCache.load(e.name);if(n.length===0)return;const t=this.instances.find(s=>s.name===e.name);!t||!this.isActiveGeneration(t)||(p.info("manager",`restoring ${n.length} cached shared instance(s) for ${e.name}`),this.onShareSet(t,e,n))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){return this.runMutation(()=>this.addAgentUnlocked(e),[String(e.name??"")])}async addAgentUnlocked(e){const n=this.toRuntimeInstanceConfig(e,{quiet:!0});if(this.instances.some(t=>t.name===n.name))throw new Error(`Agent "${n.name}" already exists`);this.configRepository.assertCanAdd(e),await this.addAgentInternal(e,"agents.json",{targetPersisted:!1});try{const t=this.configRepository.insert(e);this.rawConfigMap.set(e.name,t);const s=this.instances.find(c=>c.name===e.name),i=s?this.instanceGenerations.get(s):void 0;if(i){i.persisted=!0;const c=this.configMap.get(e.name);c&&this.restoreCachedSharedInstances(c)}}catch(t){try{await this.removeAgentInternal(e.name)}catch(s){throw new AggregateError([t,s],`Failed to persist and roll back agent "${e.name}"`)}throw t}await this.ensureAuxServices(),p.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,n,t){const s=this.toRuntimeInstanceConfig(e);if(this.instances.some(i=>i.name===s.name))throw new Error(`Agent "${s.name}" already exists`);await this.startInstanceFromConfig(s,e,n,t)}createConfiguredInstance(e,n=!0){const t=new F(e,this.globalConfigStore,this.auditProducer);return this.instanceGenerations.set(t,{name:e.name,agentId:e.aibot.agentId,persisted:n,retired:!1,platformDeletedObserved:!1}),t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),this.options.requestGracefulShutdown&&t.setDaemonShutdownRequester(this.options.requestGracefulShutdown),t.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),t.setLifecycleAdmissionRestorer(()=>this.reopenAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(s=>this.installAgent(s)),t.setRemoteAdminPorts({isEnabled:()=>this.remoteAdminGate?.()??!1,listInstallable:()=>this.listInstallable(),installAgent:s=>this.installAgent(s),getInstallProgress:s=>this.getInstallProgress(s),addAgent:s=>this.addAgent(s),probeAll:s=>this.probeAll(s)}),t.setShareSetHandler(s=>this.onShareSet(t,e,s)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(t)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(s=>this.setAgentProvider(e.name,s)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(s,i)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,s,i):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const s=this.relayStateApplier;return s?s.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>X(this.getAgentProvider(e.name))),t}hasAnyPendingOrBackgroundWork(){return this.instances.some(e=>e.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(e=>e.hasPendingOrBackgroundWork())}closeAllLifecycleAdmission(){this.lifecycleAdmissionClosed=!0;for(const e of this.instances)e.closeLifecycleAdmission();for(const e of this.sharedInstances.values())e.closeLifecycleAdmission()}reopenAllLifecycleAdmission(){if(!this.stopping){this.lifecycleAdmissionClosed=!1;for(const e of this.instances)e.openLifecycleAdmission();for(const e of this.sharedInstances.values())e.openLifecycleAdmission()}}async prepareProviderRuntime(e,n){const t=this.resolveAgentProvider(n.provider);W(n.client_type)&&t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()&&await Q(e.name,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()})}async startInstanceFromConfig(e,n,t,s){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});if(await this.prepareProviderRuntime(e,n),this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"MANAGER_STOPPING"});const i=this.createConfiguredInstance(e,s.targetPersisted);this.instances.push(i);const c=this.startTrackedInstance(i),o=Y(),g=await new Promise(h=>{let d=!1;const u=setTimeout(()=>{d||(d=!0,h({kind:"timeout"}))},o);c.then(()=>{d||(d=!0,clearTimeout(u),h({kind:"started"}))},f=>{d||(d=!0,clearTimeout(u),h({kind:"failed",error:f}))})}),a=this.instanceGenerations.get(i);if(a?.platformDeletedObserved&&!a.persisted){const h=new ge(`Agent "${e.name}" was deleted before its local configuration commit`);try{await this.stopInstance(i),this.retireInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){throw this.failedStartAgents.add(e.name),new AggregateError([h,d],`Failed to clean up uncommitted deleted agent "${e.name}"`)}throw h}if(g.kind==="started"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),this.failedStartAgents.delete(e.name),s.targetPersisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.();return}if(g.kind==="timeout"){this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t}),p.warn("manager",`Start pending for ${e.name}, continue retrying in background`),c.then(async()=>{if(!this.removingAgents.has(e.name)&&this.isActiveGeneration(i)){if(this.stopping){try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i)}catch(h){this.failedStartAgents.add(e.name),p.error("manager",`Failed to stop late-started ${e.name}: ${h}`)}return}this.failedStartAgents.delete(e.name),this.instanceGenerations.get(i)?.persisted&&this.restoreCachedSharedInstances(e),this.relayEnvSettledHandler?.(),p.info("manager",`Delayed start succeeded: ${e.name}`)}},async h=>{if(C(h)){this.onAgentDeletedByPlatform(i);return}if(!(this.stopping||this.removingAgents.has(e.name))&&this.isActiveGeneration(i)){try{await this.stopInstance(i),this.instances=this.instances.filter(d=>d!==i),this.failedStartAgents.delete(e.name)}catch(d){this.failedStartAgents.add(e.name),p.error("manager",`Failed to clean up delayed start ${e.name}: ${d}`)}p.error("manager",`Delayed start failed ${e.name}: ${h}`)}}).catch(h=>{p.error("manager",`Delayed start finalizer failed ${e.name}: ${h}`)});return}const l=g.error;C(l)&&this.onAgentDeletedByPlatform(i);let m;try{await this.stopInstance(i),this.instances=this.instances.filter(h=>h!==i),this.failedStartAgents.delete(e.name)}catch(h){m=h,this.failedStartAgents.add(e.name)}throw s.targetPersisted&&(this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:n,file:t})),m!==void 0?new AggregateError([l,m],`Failed to start and clean up agent "${e.name}"`):l}async removeAgent(e){return this.runMutation(()=>this.removeAgentUnlocked(e),[e])}async removeAgentUnlocked(e){if(!(this.instances.some(t=>t.name===e)||this.rawConfigMap.has(e)||[...this.sharedInstances.keys()].some(t=>t.startsWith(`${e}#shared:`))))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});this.configRepository.remove(e),await this.removeAgentInternal(e),p.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,n){this.removingAgents.add(e);try{const t=()=>n?.deadlineMs===void 0?E():Math.max(1,n.deadlineMs-Date.now()),s=this.instances.findIndex(h=>h.name===e),i=s===-1?void 0:this.instances[s],c=`${e}#shared:`,o=n?.keepShareCache?void 0:{dropPendingEvents:!0},g=new Map;i&&(this.requestStartCancellation(i),this.startingInstances.has(i)&&g.set(i,this.stopInstance(i,o,t())));for(const[h,d]of this.sharedInstances)h.startsWith(c)&&(this.requestStartCancellation(d),this.startingInstances.has(d)&&g.set(d,this.stopInstance(d,o,t())));const a=this.shareSyncChains.get(e);this.shareSyncChains.delete(e);const l=[];try{await this.waitForLifecycleDrain([...a?[a]:[],...g.values()],`agent "${e}" shared lifecycle drain`,t())}catch(h){l.push(h)}const m=[];for(const[h,d]of[...this.sharedInstances.entries()])h.startsWith(c)&&m.push((g.get(d)??this.stopInstance(d,o,t())).then(()=>{this.sharedInstances.get(h)===d&&this.sharedInstances.delete(h),this.startingInstances.delete(d)}).catch(u=>{l.push(u),p.error("manager",`stop shared instance failed ${h}: ${u}`)}));if(i)try{await(g.get(i)??this.stopInstance(i,o,t())),this.retireInstance(i);const h=this.instances.indexOf(i);h!==-1&&this.instances.splice(h,1),this.startingInstances.delete(i)}catch(h){l.push(h)}if(await Promise.all(m),!n?.keepShareCache){const h=K(e).catch(()=>{});if(n?.deadlineMs===void 0)await h;else try{await this.waitForLifecycleDrain([h],`agent "${e}" relay shutdown`,t())}catch(d){l.push(d)}}if(l.length>0)throw new AggregateError(l,`Failed to stop agent "${e}" completely`);this.configMap.delete(e),this.rawConfigMap.delete(e),this.failedStartAgents.delete(e),n?.keepShareCache||this.sharedOwnersCache.delete(e)}finally{this.removingAgents.delete(e)}}onAgentDeletedByPlatform(e){const n=this.instanceGenerations.get(e);if(!n||(n.platformDeletedObserved=!0,this.requestStartCancellation(e),!n.persisted))return;const t={source:e,name:n.name,agentId:n.agentId};this.pendingPlatformDeletes.set(e,t),!this.stopping&&(this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),this.runMutation(async()=>{}).catch(()=>{}).finally(()=>{this.deletedAgentCleanups.delete(e)})))}async retryPendingPlatformDeletesUnlocked(e){if(this.pendingPlatformDeletes.size!==0)for(const[n,t]of[...this.pendingPlatformDeletes])try{await this.cleanupPlatformDeletedUnlocked(t,e),this.pendingPlatformDeletes.delete(n)}catch(s){p.error("manager",`cleanup for deleted agent "${t.name}" failed: ${s}`)}}async cleanupPlatformDeletedUnlocked(e,n){const{source:t,name:s,agentId:i}=e,c=this.instances.find(m=>m.name===s),o=c?this.instanceGenerations.get(c)?.agentId??String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim():void 0;if(c&&c!==t&&o!==i)return;p.warn("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`);const g=this.rawConfigMap.get(s),a=this.configMap.get(s),l=[];if(c&&o===i)try{await this.removeAgentInternal(s,{deadlineMs:n})}catch(m){l.push(m)}try{const m=this.configRepository.scan();if(m.fileErrors.length>0){const u=m.fileErrors.map(f=>`${f.file}: ${f.error}`).join("; ");throw new Error(`cannot inspect deleted agent generation [${u}]`)}const h=m.entries.filter(u=>String(u.entry?.name??"").trim()===s);if(!h.some(u=>String(u.entry?.agent_id??"").trim()!==i)&&h.length>0&&this.configRepository.remove(s),l.length===0){const u=String(this.rawConfigMap.get(s)?.entry.agent_id??"").trim();(!u||u===i)&&(this.rawConfigMap.delete(s),this.configMap.delete(s),this.failedStartAgents.delete(s))}}catch(m){l.push(m),g&&this.rawConfigMap.set(s,g),a&&this.configMap.set(s,a)}if(l.length>0)throw new AggregateError(l,`Failed to fully clean up deleted agent "${s}"`);p.info("manager",`Agent "${s}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406`)}isAgentBusy(e){return this.instances.find(t=>t.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const n=this.instances.find(t=>t.name===e);return n?(n.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}remoteAdminGate;setRemoteAdminGate(e){this.remoteAdminGate=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(t=>t.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const n=e.api_key_ref.trim(),t=this.relaySecretResolver?.(n);if(!t)throw Object.assign(new Error(`relay credential "${n}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:t}}toRuntimeInstanceConfig(e,n={}){const t=this.resolveAgentProvider(e.provider);return We(t===e.provider?e:{...e,provider:t},n)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){return this.runMutation(()=>this.restartAgentUnlocked(e),[e])}async restartAgentUnlocked(e){const n=this.rawConfigMap.get(e);if(!n)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,n.entry,n.file),p.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,n,t){return this.runMutation(()=>this.setAgentProviderUnlocked(e,n,t),[e])}async setAgentProviderUnlocked(e,n,t){if(!this.rawConfigMap.get(e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const i=n?.api_key_ref?.trim()?Object.fromEntries(Object.entries(n).filter(([a])=>a!=="api_key")):n,c=this.configRepository.updateExisting(e,a=>{const l={...a};return i===void 0?delete l.provider:l.provider=i,this.toRuntimeInstanceConfig(l,{quiet:!0}),l}),o=c.entry;this.rawConfigMap.set(e,c);const g=String(o.client_type??"").trim().toLowerCase();try{W(g)&&(n?.base_url?.trim()&&n?.api_key?.trim()&&n?.model?.trim()?await Q(e,{baseUrl:n.base_url.trim(),apiKey:n.api_key.trim(),model:n.model.trim()}):await K(e))}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}if(t?.deferRestart){this.instances.find(a=>a.name===e)?.markRelayEnvStale(),p.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}try{await this.replaceInstance(e,o,c.file)}catch(a){throw this.failedStartAgents.add(e),this.instances.find(l=>l.name===e)?.markRelayEnvStale(),a}p.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const n=this.rawConfigMap.get(e)?.entry.provider;return!!(n?.api_key_ref?.trim()||n?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&(Ie(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,n){const t=this.instances.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.fetchRelayCredential(n)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),n=[];for(const t of e)try{await this.setAgentProvider(t,void 0,{deferRestart:this.isAgentBusy(t)}),n.push(t)}catch(s){p.warn("manager",`Failed to clear provider config for "${t}": ${s instanceof Error?s.message:String(s)}`)}return n}async replaceInstance(e,n,t){const s=this.toRuntimeInstanceConfig(n);try{await this.removeAgentInternal(e,{keepShareCache:!0})}catch(o){throw this.failedStartAgents.add(e),o}const i=String(n.agent_id??"").trim();if([...this.pendingPlatformDeletes.values()].some(o=>o.name===e&&o.agentId===i))throw await this.retryPendingPlatformDeletesUnlocked(),Object.assign(new Error(`Agent "${e}" was permanently deleted while its instance was stopping`),{code:"AGENT_DELETE_PENDING"});try{await this.startInstanceFromConfig(s,n,t,{targetPersisted:!0})}catch(o){throw this.configMap.set(e,s),this.rawConfigMap.set(e,{entry:n,file:t}),o}}async reload(){return this.runMutation(()=>this.doReload())}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:n,skipped:t}=this.configRepository.scan();for(const a of t)p.info("manager",`reload: skipped ${a}: not an agent config (no "agents" key)`);if(n.length>0){const a=n.map(l=>`${l.file}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${n.length} config file(s) failed to parse [${a}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const s=[];for(const{entry:a}of e)try{this.toRuntimeInstanceConfig(a,{quiet:!0})}catch(l){const m=a?.name,h=typeof m=="string"&&m.trim()?m:"<unknown>";s.push({name:h,error:l instanceof Error?l.message:String(l)})}if(s.length>0){const a=s.map(l=>`${l.name}: ${l.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${s.length} agent config(s) invalid [${a}]`),{code:"RELOAD_UNSAFE"})}const i=new Map,c=new Set([...this.pendingPlatformDeletes.values()].map(a=>a.name));for(const a of e){const l=String(a.entry?.name??"").trim();l&&(c.has(l)||(i.has(l)&&p.warn("manager",`reload: duplicate agent name "${l}", last one wins`),i.set(l,a)))}const o={added:[],removed:[],restarted:[],unchanged:[],failed:[]},g=new Set([...this.rawConfigMap.keys()].filter(a=>!c.has(a)));for(const a of g)if(!i.has(a))try{await this.removeAgentInternal(a),o.removed.push(a)}catch(l){o.failed.push({name:a,error:l instanceof Error?l.message:String(l)})}for(const[a,l]of i){const m=this.rawConfigMap.get(a);if(m)if(!Ke(m.entry,l.entry)||!this.instances.some(h=>h.name===a)||this.failedStartAgents.has(a))try{await this.replaceInstance(a,l.entry,l.file),o.restarted.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}else this.rawConfigMap.set(a,l),o.unchanged.push(a);else try{await this.addAgentInternal(l.entry,l.file,{targetPersisted:!0}),o.added.push(a)}catch(h){o.failed.push({name:a,error:h instanceof Error?h.message:String(h)})}}return p.info("manager",`reload done: +${o.added.length} -${o.removed.length} ~${o.restarted.length} =${o.unchanged.length} !${o.failed.length}`),await this.ensureAuxServices(),o}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return ze(this.instances,e)}async probeOne(e,n={}){return Ve(this.instances,e,n)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const n=await this.installer.install(e);return Me(n),n}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function ze(r,e){const n=e.concurrency??4,t=Date.now(),s=new Array(r.length);await new Promise(a=>{let l=0,m=0;const h=r.length;if(h===0){a();return}function d(_){const y=r[_];y.probe(e).then(S=>{s[_]=S,u()},S=>{s[_]={agent_name:y.name,client_type:"unknown",adapter_type:"acp",ok:!1,status:"error",probed_at:Date.now(),duration_ms:0,cached:!1,cli:{command:"",installed:!1,path:null,version:null,error:{code:"internal",message:S?.message??String(S)}},conversation:{attempted:!1,ok:!1,latency_ms:null},config:{model:null,base_url:null,source:{model:"unknown",base_url:"unknown"}},process:{started:!1,alive:!1,busy:!1}},u()})}function u(){m++,l<h?d(l++):m===h&&a()}const f=Math.min(n,h);for(let _=0;_<f;_++)d(l++)});const i=s.filter(a=>a.status==="healthy").length,c=s.filter(a=>a.status==="degraded").length,o=s.filter(a=>a.status==="unavailable").length,g=await z();return{ok:i===s.length&&s.length>0,total:s.length,healthy:i,degraded:c,unavailable:o,installed_clients:g,agents:s,probed_at:t,duration_ms:Date.now()-t}}async function Ve(r,e,n){const t=r.find(s=>s.name===e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return t.probe(n)}export{vt as Manager,X as normalizeAgentProviderForRuntime,z as probeInstalledClientCommands,ze as probeInstances,Ve as probeOneInstance};
|
package/dist/mcp/stdio/server.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
`),c.exit(1));const
|
|
4
|
-
`),await new Promise(i=>setTimeout(i,s)),
|
|
5
|
-
`)}}async function
|
|
6
|
-
`)
|
|
7
|
-
`)}),
|
|
8
|
-
`)})
|
|
9
|
-
`)
|
|
2
|
+
import R from"node:http";import I from"node:net";import c from"node:process";import{Server as A}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as x}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as $,ListToolsRequestSchema as C}from"@modelcontextprotocol/sdk/types.js";import{toolCallToInvoke as P,mapToolAlias as k,EXPOSED_TOOLS as L,TOOL_MAP as b,PHASE2_TOOL_NAMES as M}from"../../core/mcp/tools.js";function E(e,t){const o=c.argv.slice(2);for(let n=0;n<o.length;n++)if(o[n]===`--${e}`&&o[n+1])return o[n+1];return c.env[t]||void 0}const O=E("handle-url","GRIX_CONNECTOR_INTERNAL_API")??"";O||(c.stderr.write(`FATAL: --handle-url <url> required (or set GRIX_CONNECTOR_INTERNAL_API)
|
|
3
|
+
`),c.exit(1));const q=parseInt(E("notify-port","GRIX_MCP_NOTIFY_PORT")??"0",10),u=[1e3,2e3,4e3];function D(e){if(e instanceof Error){const t=e.message;if(t.includes("ECONNREFUSED")||t.includes("ECONNRESET")||t.includes("ETIMEDOUT")||t.includes("socket hang up"))return!0;if(t.includes("invoke timeout"))return!1}return!1}async function m(e,t,o=15e3,n=0){try{return await F(e,t,o)}catch(r){if(n>0&&D(r)){const s=u[u.length-n]??u[u.length-1];return c.stderr.write(`[grix-stdio] invokeApi retry (${u.length-n+1}/${u.length}) action=${e} after ${s}ms: ${r instanceof Error?r.message:r}
|
|
4
|
+
`),await new Promise(i=>setTimeout(i,s)),m(e,t,o,n-1)}throw r}}function F(e,t,o){const n=new URL(O),r=new URL("/api/invoke",n),s=n.searchParams.get("token")??"";s&&r.searchParams.set("token",s);const i=JSON.stringify({action:e,params:t,timeout_ms:o});return new Promise((l,a)=>{const N=setTimeout(()=>a(new Error("invoke timeout")),o+5e3),g=R.request(r,{method:"POST",headers:{"content-type":"application/json","content-length":Buffer.byteLength(i),...s?{authorization:`Bearer ${s}`}:{}}},h=>{const T=[];h.on("data",f=>T.push(f)),h.on("end",()=>{clearTimeout(N);const f=Buffer.concat(T).toString("utf8");try{const y=JSON.parse(f);y.ok?l(y.data??null):a(new Error(y.error??"invoke failed"))}catch{a(new Error(`invalid response: ${f.slice(0,200)}`))}})});g.on("error",h=>{clearTimeout(N),a(h)}),g.write(i),g.end()})}const p=new A({name:"grix",version:"1.0.0"},{capabilities:{experimental:{"claude/channel":{},"claude/channel/permission":{}},tools:{}},instructions:['Messages arrive as <channel source="grix-claude" chat_id="..." event_id="..." message_id="..." user_id="...">text</channel>.','Both your plain text and the reply tool are delivered to the user. You are talking directly to the user, so address them in the second person ("you"); do not narrate in the third person about them or tell them what you "already replied".',"Use plain text for short progress notes while you work; deliver your final conclusion through the reply tool \u2014 that is the message the user treats as your answer and the one the connector quotes. Your plain text reaches the user too \u2014 it is not private scratch space, so it is a real channel for progress and for anything the user needs mid-turn; you just need not restate the full conclusion as plain text once it is in the reply. The connector automatically quotes the message you are answering, so you normally do not need reply_to (set it only to quote a different earlier message).","Make the reply tool call the last thing you do in a turn and put your whole conclusion inside it: once you have called reply, any further text you write in the same turn is dropped, not delivered. Progress notes go before the reply; the reply ends the turn.",'Never explain the delivery mechanics to the user or tell them you "already sent it via the reply tool" \u2014 that is internal plumbing. If the user says they did not receive or cannot read something, do not insist you already sent it; assume the delivery or the format failed and send it again, switching format when that is the likely cause (e.g. render a diagram to an image, strip code fences, upload a file).',"If you intentionally do not want to send any visible message, call the complete tool with event_id and a final status."].join(" ")});p.setRequestHandler(C,async()=>({tools:L.map(e=>({name:e.name,description:e.description,inputSchema:e.inputSchema}))})),p.setRequestHandler($,async e=>{const t=String(e.params.name??""),o=e.params.arguments??{},n=k(t,o),r=n.name,s=n.args;if(!b.has(t))return{content:[{type:"text",text:`Unknown tool: ${t}`}],isError:!0};try{if(M.has(r)){const a=await m("event_tool_call",{tool_name:r,arguments:s},3e4,0);return{content:[{type:"text",text:typeof a=="string"?a:JSON.stringify(a,null,2)}]}}const i=P(r,s),l=await m(i.action,i.params,i.timeoutMs??15e3);return{content:[{type:"text",text:JSON.stringify(l,null,2)}]}}catch(i){const l=i instanceof Error?i.message:String(i);return{content:[{type:"text",text:JSON.stringify({error:l})}],isError:!0}}});let _=!1;const d=[],Y=100,H="notifications/claude/channel";function v(e){const t=e.meta;return(t&&typeof t.event_id=="string"?t.event_id:"")||"(none)"}function w(e){c.stderr.write(`[grix-stdio] ${e}
|
|
5
|
+
`),m("mcp_stdio_log",{message:e},5e3,0).catch(()=>{})}async function S(e,t){const o=e===H;if(!_){d.push({method:e,params:t}),d.length>Y&&d.shift(),o&&w(`channel notification queued before MCP connect: event=${v(t)} pending=${d.length}`);return}try{await p.notification({method:e,params:t}),o&&w(`channel notification sent to Claude: event=${v(t)}`)}catch(n){c.stderr.write(`DISPATCH_ERROR:${e}:${n}
|
|
6
|
+
`),o&&w(`channel notification failed: event=${v(t)} err=${n instanceof Error?n.message:String(n)}`)}}async function U(){for(;_&&d.length>0;){const e=d.shift();await S(e.method,e.params)}}function J(e){if(e<=0)return;const t=I.createServer(o=>{let n="";o.setEncoding("utf8"),o.on("data",r=>{n+=r;const s=n.split(`
|
|
7
|
+
`);n=s.pop()??"";for(const i of s){const l=i.trim();if(l)try{const a=JSON.parse(l);a.method&&S(a.method,a.params??{}).catch(()=>{})}catch{}}}),o.on("error",()=>{})});t.listen(e,"127.0.0.1",()=>{c.stderr.write(`NOTIFY_READY:${e}
|
|
8
|
+
`)}),t.on("error",o=>{c.stderr.write(`NOTIFY_ERROR: ${o.message}
|
|
9
|
+
`)})}async function X(){J(q);const e=new x;await p.connect(e),_=!0,c.stderr.write(`[MAIN] MCP connected, ready to dispatch notifications
|
|
10
|
+
`),await U()}X().catch(e=>{c.stderr.write(`FATAL: ${e}
|
|
10
11
|
`),c.exit(1)});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as n from"node:net";const i={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function s(u){const e={bind:u?.bind??i.bind,port:u?.port??i.port,endpoint:u?.endpoint??i.endpoint,sessionTimeoutMs:u?.sessionTimeoutMs??i.sessionTimeoutMs,invokeTimeoutMs:u?.invokeTimeoutMs??i.invokeTimeoutMs,allowedOrigins:u?.allowedOrigins,allowedHosts:u?.allowedHosts};return t(e.bind),e.port!==0&&o(e.port),r(e.sessionTimeoutMs),e}function t(u){if(!u||!n.isIPv4(u)&&!n.isIPv6(u))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${u}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function o(u){if(!Number.isInteger(u)||u<1||u>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(u){if(!Number.isInteger(u)||u<1e3||u>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${u} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{s as createDefaultGatewayConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const a=3e4;class c{connectionManager;onDisconnected;bindings=new Map;constructor(n,i){this.connectionManager=n,this.onDisconnected=i}async bind(n,i){if(this.bindings.has(n))throw new Error(`Session ${n} is already bound to a connection`);const e=await this.connectWithTimeout(i),t=[],s=e.onDisconnected(()=>{this.removeBinding(n),this.onDisconnected(n)});return t.push(s),this.bindings.set(n,{sessionId:n,handle:e,subscriptions:t}),e}getHandle(n){return this.bindings.get(n)?.handle}unbind(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e();i.handle.disconnect()}}unbindAll(){const n=[...this.bindings.keys()];for(const i of n)this.unbind(i)}connectWithTimeout(n){return new Promise((i,e)=>{let t=!1;const s=setTimeout(()=>{t||(t=!0,e(new Error("Connection bind timeout after 30000ms")))},3e4);this.connectionManager.connect({agentId:n.agentId,apiKey:n.apiKey,url:n.wsUrl,clientType:n.clientType,capabilities:["agent_invoke"],adapterHint:`${n.clientType}/base`},{maxRetries:0}).then(o=>{t?o.disconnect():(t=!0,clearTimeout(s),i(o))}).catch(o=>{t||(t=!0,clearTimeout(s),e(o))})})}removeBinding(n){const i=this.bindings.get(n);if(i){this.bindings.delete(n);for(const e of i.subscriptions)e()}}}export{c as ConnectionBindingImpl};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(
|
|
1
|
+
import{toolCallToInvoke as i}from"../../core/mcp/tools.js";import{ToolRegistryImpl as l}from"./tool-registry.js";import{validateToolArgs as a}from"./tool-schemas.js";import{isEventTool as p,executeEventTool as d}from"./event-tool-executor.js";class y{registry;constructor(){this.registry=new l}async execute(r,e,t,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,t);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(r.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${r.status}`);if(p(e))return this.executeEventTool(r,e,t);const o=i(e,t);try{const u=await r.agentInvoke(o.action,o.params,n);return this.normalizeResult(u)}catch(u){const c=u instanceof Error?u.message:String(u);return c.toLowerCase().includes("timeout")?this.errorResult(`\u8C03\u7528\u8D85\u65F6: ${c}`):this.errorResult(`\u8C03\u7528\u5931\u8D25: ${c}`)}}normalizeResult(r){if(r==null||typeof r!="object")return this.successResult(r??null);const e=r,t=typeof e.code=="number"?e.code:0;if(t===0){const s="data"in e?e.data:null;return this.successResult(s??null)}const n=typeof e.msg=="string"?e.msg:"\u672A\u77E5\u9519\u8BEF";return this.errorResult(`\u4E0A\u6E38\u9519\u8BEF [code=${t}]: ${n}`)}successResult(r){return{content:[{type:"text",text:JSON.stringify(r)}],isError:!1}}errorResult(r){return{content:[{type:"text",text:r}],isError:!0}}async executeEventTool(r,e,t){return e==="grix_access_control"?this.executeAccessControl(r,t):d(r,e,t)}async executeAccessControl(r,e){const t=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[t];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${t}`);const s={};e.code!=null&&(s.code=e.code),e.sender_id!=null&&(s.sender_id=e.sender_id),e.policy!=null&&(s.policy=e.policy);try{const o=await r.agentInvoke("claude_access_control",{verb:n,payload:s},3e4);return this.successResult(o)}catch(o){const u=o instanceof Error?o.message:String(o);return this.errorResult(`access_control \u8C03\u7528\u5931\u8D25: ${u}`)}}}export{y as ToolExecutorImpl};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{TOOLS as
|
|
1
|
+
import{TOOLS as o,EVENT_TOOLS as s}from"../../core/mcp/tools.js";const e=new Set(["grix_query","grix_group","grix_message_send","grix_message_unsend","grix_admin"]),r=new Set(["grix_reply","grix_complete","grix_event_ack","grix_composing","grix_access_control","grix_status"]);class a{tools;toolMap;constructor(){this.tools=[...o.filter(t=>e.has(t.name)),...s.filter(t=>r.has(t.name))],this.toolMap=new Map(this.tools.map(t=>[t.name,t]))}getTools(){return this.tools}getTool(t){return this.toolMap.get(t)}hasTool(t){return this.toolMap.has(t)}}export{a as ToolRegistryImpl};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(
|
|
1
|
+
const o={required:["action"],properties:{action:{type:"string",enum:["contact_search","session_search","message_history","message_search"]},id:{type:"string"},keyword:{type:"string",maxLength:200},limit:{type:"integer",minimum:1,maximum:100},offset:{type:"integer",minimum:0},sessionId:{type:"string"},beforeId:{type:"string"}}},a={required:["action"],properties:{action:{type:"string",enum:["create","detail","leave","add_members","remove_members","update_member_role","update_all_members_muted","update_member_speaking","dissolve"]},sessionId:{type:"string"},name:{type:"string",maxLength:128},memberIds:{type:"array",items:{type:"string"},maxItems:100},memberTypes:{type:"array",items:{type:"integer",enum:[1,2]}},memberId:{type:"string"},role:{type:"integer",enum:[1,2]},memberType:{type:"integer"},allMembersMuted:{type:"boolean"},isSpeakMuted:{type:"boolean"},canSpeakWhenAllMuted:{type:"boolean"}}},p={required:["sessionId","content"],properties:{sessionId:{type:"string"},content:{type:"string",maxLength:1e4},msgType:{type:"integer"},quotedMessageId:{type:"string"},threadId:{type:"string"}}},m={required:["sessionId","msgId"],properties:{sessionId:{type:"string"},msgId:{type:"string"}}},g={required:["action"],properties:{action:{type:"string",enum:["create_agent","list_categories","create_category","update_category","assign_category","rotate_api_key"]},agentName:{type:"string"},introduction:{type:"string"},isMain:{type:"boolean"},agentId:{type:"string"},categoryId:{type:"string"},name:{type:"string"},parentId:{type:"string"},sortOrder:{type:"integer"}}},y={required:["session_id","text"],properties:{event_id:{type:"string"},session_id:{type:"string"},text:{type:"string",maxLength:5e4},quoted_message_id:{type:"string"},is_final:{type:"boolean"}}},d={required:["event_id","status"],properties:{event_id:{type:"string"},status:{type:"string",enum:["responded","canceled","failed"]},msg:{type:"string",maxLength:500}}},c={required:["event_id"],properties:{event_id:{type:"string"},session_id:{type:"string"}}},l={required:["session_id","active"],properties:{session_id:{type:"string"},active:{type:"boolean"},event_id:{type:"string"}}},_={required:["action"],properties:{action:{type:"string",enum:["pair_approve","pair_deny","allow_sender","remove_sender","set_policy"]},code:{type:"string"},sender_id:{type:"string"},policy:{type:"string",enum:["allowlist","open","disabled"]}}},f={required:[],properties:{}},C={grix_query:o,grix_group:a,grix_message_send:p,grix_message_unsend:m,grix_admin:g,grix_reply:y,grix_complete:d,grix_event_ack:c,grix_composing:l,grix_access_control:_,grix_status:f};function B(r,t){const e=C[r];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${r}`};for(const i of e.required)if(t[i]===void 0||t[i]===null)return{valid:!1,error:`\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570: ${i}`};for(const[i,u]of Object.entries(t)){if(u==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,u,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(r,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof t=="number"?"\u6D6E\u70B9\u6570":typeof t}`;if(e.minimum!==void 0&&t<e.minimum)return`\u53C2\u6570 ${r} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${r} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${r} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${r} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${r} \u8D85\u8FC7\u6700\u5927\u5143\u7D20\u6570 ${e.maxItems}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.items)for(let i=0;i<t.length;i++){const u=t[i];if(e.items.type==="string"&&typeof u!="string")return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.type==="integer"){if(typeof u!="number"||!Number.isInteger(u))return`\u53C2\u6570 ${r}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof u}`;if(e.items.enum&&!e.items.enum.includes(u))return`\u53C2\u6570 ${r}[${i}] \u503C ${u} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
|
package/openclaw-plugin/index.js
CHANGED
|
@@ -10732,7 +10732,7 @@ var TOOLS = [
|
|
|
10732
10732
|
properties: {
|
|
10733
10733
|
agent_id: { type: "string", description: "Target agent's numeric ID, passed as a string." },
|
|
10734
10734
|
agent_name: { type: "string", description: "New display name (max 100 characters, must be unique among the owner's agents)." },
|
|
10735
|
-
introduction: { type: "string", description: "New text introduction (max
|
|
10735
|
+
introduction: { type: "string", description: "New text introduction (max 3072 characters)." }
|
|
10736
10736
|
},
|
|
10737
10737
|
required: ["agent_id"]
|
|
10738
10738
|
},
|
|
@@ -11455,7 +11455,9 @@ var PHASE1_INVOKE_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
11455
11455
|
"webhook_list",
|
|
11456
11456
|
"webhook_delete",
|
|
11457
11457
|
"skill_set",
|
|
11458
|
-
"skill_get"
|
|
11458
|
+
"skill_get",
|
|
11459
|
+
"egg_search",
|
|
11460
|
+
"egg_get"
|
|
11459
11461
|
]);
|
|
11460
11462
|
var ACCESS_CONTROL_ACTION_MAP = {
|
|
11461
11463
|
pair_approve: "pair_approve",
|
|
@@ -256,7 +256,7 @@ Provide at least one of `agent_name` / `introduction`.
|
|
|
256
256
|
- `agent_id` (required) — target agent's numeric ID, as a string.
|
|
257
257
|
- `agent_name` (optional) — new display name (max 100 chars); must be unique
|
|
258
258
|
among the owner's agents.
|
|
259
|
-
- `introduction` (optional) — new introduction text (max
|
|
259
|
+
- `introduction` (optional) — new introduction text (max 3072 chars).
|
|
260
260
|
|
|
261
261
|
Renaming only changes the platform-side display name; the local OpenClaw
|
|
262
262
|
channel/account configuration is untouched.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.1",
|
|
4
4
|
"description": "Connect local AI coding agents (Claude, Codex, Gemini, Qwen, DeepSeek, Cursor, OpenCode, Pi, OpenHuman, Reasonix) to the Grix scheduling platform. Also serves as an OpenClaw plugin for Grix channel transport.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
Binary file
|