grix-connector 3.34.0 → 4.0.0

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.
Files changed (46) hide show
  1. package/dist/adapter/acp/acp-adapter.js +13 -20
  2. package/dist/adapter/agy/agy-adapter.js +12 -7
  3. package/dist/adapter/claude/claude-adapter.js +1 -1
  4. package/dist/adapter/claude/claude-bridge-server.js +1 -1
  5. package/dist/adapter/claude/claude-tools.js +1 -1
  6. package/dist/adapter/claude/claude-worker-client.js +1 -1
  7. package/dist/adapter/claude/mcp-http-launcher.js +2 -2
  8. package/dist/adapter/claude/result-timeout.js +1 -1
  9. package/dist/adapter/codewhale/codewhale-adapter.js +3 -6
  10. package/dist/adapter/codex/codex-bridge.js +11 -9
  11. package/dist/adapter/cursor/cursor-adapter.js +10 -18
  12. package/dist/adapter/cursor/session-scanner.js +1 -1
  13. package/dist/adapter/deepseek-harness/profile-runtime.js +2 -2
  14. package/dist/adapter/opencode/opencode-adapter.js +7 -5
  15. package/dist/adapter/openhuman/openhuman-adapter.js +8 -6
  16. package/dist/adapter/pi/pi-adapter.js +8 -6
  17. package/dist/adapter/shared/identity-injector.js +2 -4
  18. package/dist/adapter/shared/inbound-message-id-injector.js +2 -2
  19. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.0.0.tgz +0 -0
  20. package/dist/assets/dsh-bridge/manifest.json +6 -6
  21. package/dist/bridge/adapter-pool.js +1 -1
  22. package/dist/core/access/allowlist-store.js +1 -1
  23. package/dist/core/file-ops/list-files.js +1 -1
  24. package/dist/core/mcp/internal-api-server.js +1 -1
  25. package/dist/core/observability/sentry-event-dedup.js +1 -0
  26. package/dist/core/observability/sentry.js +1 -1
  27. package/dist/core/protocol/message-metadata.js +1 -1
  28. package/dist/core/protocol/message-reference.js +7 -2
  29. package/dist/core/protocol/payload-parser.js +3 -4
  30. package/dist/core/upgrade/npm-upgrader.js +2 -2
  31. package/dist/core/upgrade/upgrade-checker.js +1 -1
  32. package/dist/core/util/session-title.js +1 -1
  33. package/dist/grix.js +1 -1
  34. package/dist/log.js +2 -2
  35. package/dist/manager.js +1 -1
  36. package/dist/mcp/stream-http/config.js +1 -1
  37. package/dist/mcp/stream-http/connection-binding.js +1 -1
  38. package/dist/mcp/stream-http/security.js +1 -1
  39. package/dist/mcp/stream-http/tool-executor.js +1 -1
  40. package/dist/mcp/stream-http/tool-registry.js +1 -1
  41. package/dist/mcp/stream-http/tool-schemas.js +1 -1
  42. package/dist/protocol/acp-client.js +1 -1
  43. package/openclaw-plugin/index.js +4 -4
  44. package/package.json +1 -1
  45. package/scripts/upgrade-guardian.sh +2 -4
  46. package/dist/assets/dsh-bridge/grix-dsh-bridge-3.34.0.tgz +0 -0
package/dist/grix.js CHANGED
@@ -38,5 +38,5 @@ Examples:
38
38
  using Startup folder auto-start instead. For full Task Scheduler integration,
39
39
  right-click the terminal and select "Run as administrator".`);const o=G(),d=S["config-dir"]??(S.profile?I.join(o.configDir,S.profile):void 0),h=I.resolve(process.argv[1]||`${o.rootDir}/dist/grix.js`),g=new ke({cliPath:h,nodePath:process.execPath});try{let u;switch(b){case"start":(await g.status({rootDir:o.rootDir})).installed?u=await g.start({rootDir:o.rootDir}):u=await g.install({rootDir:o.rootDir,configDir:d});break;case"stop":u=await g.stop({rootDir:o.rootDir});break;case"restart":(await g.status({rootDir:o.rootDir})).installed?u=await g.restart({rootDir:o.rootDir}):u=await g.install({rootDir:o.rootDir,configDir:d});break;case"status":u=await g.status({rootDir:o.rootDir});break}console.log(JSON.stringify(u,null,2)),process.exit(0)}catch(u){console.error(`${b} failed: ${u instanceof Error?u.message:u}`),process.exit(1)}}else b&&(console.error(`Unknown command: ${b}
40
40
  Valid commands: ${de.join(", ")}`),process.exit(1));const l=G(),ze=S["config-dir"]??(S.profile?`${l.configDir}/${S.profile}`:void 0),n=new Ae,B=new De,pe=Oe(),D=new Le(pe);let J=!1,C;async function ge(o){process.stderr.write(o.message+`
41
- `),s.error("main",o.message.replace(/\n/g," \u2014 "));const d=j(l.daemonLockFile);d?.pid===process.pid&&d.instance_id===C&&await H(l.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:C,updated_at:Date.now(),reason:`port_bind_${o.kind}:${o.label}:${o.port}`}).catch(()=>{}),await K(),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(1)}async function M(o){if(J)return;J=!0,s.info("main",`Received ${o}, shutting down...`),B.markShuttingDown();const d=setTimeout(()=>{s.error("main","Shutdown timed out, forcing exit"),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(2)},1e4);try{await n.stop(),await f()?.stop().catch(()=>{}),await D.stop(),await B.stop(),await K(),await L(l.daemonLockFile),await Fe(l.daemonStatusFile).catch(()=>{}),clearTimeout(d),F(),s.info("main","Shutdown complete"),process.exit(0)}catch(h){s.error("main",`Shutdown error: ${h}`),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(2)}}async function Qe(){if(xe(),Ee(),be(l.stdoutLogFile,l.stderrLogFile),te(!1),await Be(),process.platform==="win32"){const e=Ue(l.rootDir,"win32");await Ce(e,{platform:"win32"})}try{C=(await _e(l.daemonLockFile,l.rootDir)).instance_id}catch(e){console.error(e instanceof Error?e.message:e),process.exit(1)}ve(),s.info("main",`grix-connector starting (PID ${process.pid})`),await H(l.daemonStatusFile,{state:"starting",pid:process.pid,instance_id:C,updated_at:Date.now()});const o=parseInt(S["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await re({label:"health",port:o,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:a=>B.start(a)});e&&await ge(e)}const d=I.join(l.dataDir,"health-port");ee(d,String(o),"utf-8"),process.on("SIGINT",()=>M("SIGINT")),process.on("SIGTERM",()=>M("SIGTERM")),process.on("SIGHUP",()=>{J||(s.info("main","Received SIGHUP, reloading config..."),n.reload().then(e=>s.info("main",`reload done: ${JSON.stringify(e)}`)).catch(e=>s.error("main",`reload failed: ${e instanceof Error?e.message:e}`)))});let h="",g=0,u;process.on("uncaughtException",e=>{const a=e instanceof Error?e.stack??e.message:String(e);a===h?(g++,(g<=3||g%100===0)&&s.error("main",`Uncaught exception (x${g}): ${a}`)):(g>3&&s.error("main",`Previous exception repeated ${g} times total`),h=a,g=1,s.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),u||(u=setTimeout(()=>{g>3&&s.error("main",`Previous exception repeated ${g} times total`),h="",g=0,u=void 0},1e4).unref())),!ue(e)&&(V(e,"uncaughtException"),M("uncaughtException"))}),process.on("unhandledRejection",e=>{s.error("main",`Unhandled rejection: ${e}`),!ue(e)&&(V(e,"unhandledRejection"),M("unhandledRejection"))});const v=Me(l.dataDir);await v.clearRuntimeState();const P=new Ne(I.join(l.dataDir,"proxy","relay-state.json"),{warn:e=>s.warn("main",e)}),O=new Ge(I.join(l.dataDir,"secrets"));n.setRelaySecretResolver(e=>O.get(e));const T={hasDirectConfig:e=>n.hasDirectRelayConfig(e),clearDirectConfig:(e,a)=>n.setAgentProvider(e,void 0,{deferRestart:a?.deferRestart===!0})},N=()=>({pm:f(),agents:n,direct:T,store:P,secrets:O}),U=v.getRelayAgents();if(U.length>0)try{await v.startIfAnyRelayAgent(),s.info("main",`mitm proxy started for relay agents: ${U.join(", ")} (per-agent; others stay direct)`)}catch(e){s.error("main",`mitm proxy FAILED to start (relay agents: ${U.join(", ")}): ${e}. These agents will REFUSE to start (they must not silently fall back to your own account). Fix the proxy or turn relay off for them. Other agents are unaffected.`)}else s.info("main",'mitm proxy idle (no agent has Grix relay enabled; enable: PUT /api/proxy/agents/<name>/enabled {"enabled":true})');let _=null;if(f()){const e=async()=>{const t=f();t&&await oe(t,n)&&s.info("main","no agent uses Grix relay anymore; mitm proxy stopped")};n.setRelayEnvSettledHandler(()=>{const t=f();t&&ae(t,T,P),e().catch(r=>{s.warn("main",`failed to stop idle mitm proxy: ${r}`)})});const a=()=>{const t=f(),r=t.getRelayAgents(),i=t.getDegradedRelayAgents();return{enabled:r.length>0,relayAgents:r,staleRelayAgents:n.getAgentsWithStaleRelayEnv(),degradedRelayAgents:i,relayState:P.list(),runtime:t.getRuntimeInfo(),config:t.getConfigSnapshot()}},c=new je,A={status:()=>a(),setAgentRelay:async(t,r,i)=>{const p=f(),R={info:y=>s.info("main",y),warn:y=>s.warn("main",y)},w=y=>n.getAgentsStatus().find(x=>x.name===y),m=w(t);if(!r){c.cancel(t);const y=await c.runExclusive(t,async()=>{const x=await W(N(),R,{agentName:t,target:"off"});return{...a(),restarted:x.restarted,busy:x.busy,...x.pending?{pending:x.pending}:{}}});return n.reportRelayStateLocalChange(t),y}if(!m)throw Object.assign(new Error(`Agent "${t}" not found`),{code:"NOT_FOUND"});if(!Ie(m.clientType)&&ne(m.clientType).length===0)throw Object.assign(new Error(`client type "${m.clientType??""}" does not support Grix relay`),{code:"UNSUPPORTED_CLIENT_TYPE"});const $=await c.runExclusive(t,async()=>{const y=c.epoch(t),x=w(t);if(!x)throw Object.assign(new Error(`Agent "${t}" not found`),{code:"NOT_FOUND"});const z=String(x.agentId??"").trim();if(!z)throw new ie(`local agent "${t}" has no agent_id registered`,"MISSING_AGENT_ID");const Q=c.waitCancelled(t,y);let k;try{k=await Promise.race([n.fetchRelayCredential(t,{model:i}),Q.promise.then(()=>{throw new le("relay enable cancelled by a later disable","CANCELLED")})])}finally{Q.dispose()}c.assertCurrent(t,y);const{restarted:ye,busy:he,pending:Z}=await se(p,n,w,R,{localName:t,agentId:z,virtualKey:k.apiKey,anthropicBaseUrl:k.anthropicBaseUrl,openaiBaseUrl:k.openaiBaseUrl,model:i,...k.directRelay!==void 0?{directRelay:k.directRelay}:{}},(we,Re,Se)=>n.setAgentProvider(we,Re,{deferRestart:Se?.deferRestart===!0}),{store:P,secrets:O,direct:T});if(c.epoch(t)!==y)throw await W(N(),R,{agentName:t,target:"off"}),new le("relay enable cancelled by a later disable","CANCELLED");return{...a(),restarted:ye,busy:he,...Z?{pending:Z}:{}}});return n.reportRelayStateLocalChange(t),$},setAgentRelayCredential:async(t,r)=>{const i=f(),p=n.getAgentsStatus(),{restarted:R,busy:w,pending:m}=await se(i,n,E=>p.find($=>$.name===E),{info:E=>s.info("main",E),warn:E=>s.warn("main",E)},{localName:t,agentId:r.agentId,virtualKey:r.virtualKey,anthropicBaseUrl:r.anthropicBaseUrl,openaiBaseUrl:r.openaiBaseUrl,model:r.model,...r.directRelay!==void 0?{directRelay:r.directRelay}:{}},(E,$,y)=>n.setAgentProvider(E,$,{deferRestart:y?.deferRestart===!0}),{store:P,secrets:O,direct:T});return n.reportRelayStateLocalChange(t),{...a(),restarted:R,busy:w,...m?{pending:m}:{}}},disableAll:async()=>{const t=f(),r={info:p=>s.info("main",p),warn:p=>s.warn("main",p)},i=new Set([...t.getRelayAgents(),...n.getDirectRelayConfigAgentNames(),...Object.keys(P.list())]);for(const p of i)c.cancel(p);for(const p of i)await c.runExclusive(p,()=>W(N(),r,{agentName:p,target:"off"})),n.reportRelayStateLocalChange(p);return await oe(t,n),a()},setRoute:async(t,r)=>{const i=r,p=f();return p.setRoute({routeKey:t,targetBaseUrl:i.targetBaseUrl,...i.headers?{headers:i.headers}:{},...i.model?{model:i.model}:{},...i.modelMap?{modelMap:i.modelMap}:{},...i.passthrough?{passthrough:!0}:{},...i.relayOnly?{relayOnly:!0}:{},...i.codexResponsesToChat?{codexResponsesToChat:!0}:{}}),await p.persistConfig(),a()},deleteRoute:async t=>{const r=f();r.deleteRoute(t),await r.persistConfig()},setDefaultRoute:async t=>{const r=f();return r.setDefaultRouteKey(t),await r.persistConfig(),a()},setInterceptHosts:async t=>{const r=f();return r.setInterceptHosts(t),await r.persistConfig(),a()}};_=A,D.setProxyHandler(A),n.setRelayStateApplier({getLocalState:t=>{const r=f();if(r.getRelayAgents().includes(t)){const p=n.getAgentsStatus().find(m=>m.name===t),R=`grix-gateway-${String(p?.clientType??"").trim().toLowerCase()}`;return{enabled:!0,model:r.getConfigSnapshot().routes?.find(m=>m.routeKey===R)?.model?.trim()||void 0}}const i=n.getAgentProvider(t);return i?.api_key?.trim()?{enabled:!0,model:i.model?.trim()||void 0}:{enabled:!1}},applyEnable:async(t,r,i)=>{const p=n.getAgentsStatus().find(m=>m.name===t),R=String(p?.agentId??"").trim();if(!R)throw new ie(`local agent "${t}" has no agent_id registered`,"MISSING_AGENT_ID");const w=await A.setAgentRelayCredential(t,{agentId:R,virtualKey:r.apiKey,anthropicBaseUrl:r.anthropicBaseUrl,openaiBaseUrl:r.openaiBaseUrl,model:i??r.model,...r.directRelay!==void 0?{directRelay:r.directRelay}:{}});return{restarted:w.restarted===!0,busy:w.busy===!0,...w.pending?{pending:w.pending}:{}}},applyDisable:async t=>{const r=await A.setAgentRelay(t,!1);return{restarted:r.restarted===!0,busy:r.busy===!0,...r.pending?{pending:r.pending}:{}}}})}if(B.setStatusProvider(()=>n.getAgentsStatus()),await n.start(ze),U.length>0){const e=n.getAgentsStatus();for(const a of U){const c=e.find(t=>t.name===a);if(!c)continue;const A=ne(c.clientType);A.length!==0&&await v.setAgentRelayEnabled(a,!0,{relayHosts:A}).catch(t=>{s.warn("main",`failed to reconcile relay hosts for "${a}": ${t}`)})}}ae(v,T,P);const Y=parseInt(S["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);D.setAgentHandler({list:()=>n.getAgentsStatus(),add:e=>n.addAgent(e),remove:e=>n.removeAgent(e),restart:e=>n.restartAgent(e),reload:()=>n.reload()}),D.setUpgradeHandler({check:()=>n.checkUpgrade(),trigger:()=>n.triggerUpgrade()}),D.setProbeHandler({probeAll:e=>n.probeAll(e),probeOne:(e,a)=>n.probeOne(e,a)}),D.setInstallHandler({listInstallable:()=>n.listInstallable(),installAgent:e=>n.installAgent(e),getInstallProgress:e=>n.getInstallProgress(e)});const q={listAgents:()=>n.getAgentsStatus(),enableManaged:(e,a,c)=>{if(!_)throw new ce(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return _.setAgentRelayCredential(e,{agentId:a,virtualKey:c.virtualKey,anthropicBaseUrl:c.anthropicBaseUrl,openaiBaseUrl:c.openaiBaseUrl,model:c.model,...c.directRelay!==void 0?{directRelay:c.directRelay}:{}})},disableManaged:e=>{if(!_)throw new ce(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return _.setAgentRelay(e,!1)},enableHermesProfile:(e,a)=>Ke({agentId:e,baseUrl:a.openaiBaseUrl??"",virtualKey:a.virtualKey,model:a.model??""}),disableHermesProfile:e=>Ve(e)};D.setRelayCredentialHandler({listHermesProfiles:()=>We(),enable:async(e,a)=>{const{target:c,result:A}=await Je(q,e,a);return{target:c,...A}},disable:async e=>{const{target:a,result:c}=await Xe(q,e);return{target:a,...c}}});{const e=await re({label:"admin",port:Y,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:a=>D.start(a)});e&&await ge(e)}const fe=I.join(l.dataDir,"admin-token"),me=I.join(l.dataDir,"admin-port");He(fe,pe),ee(me,String(Y),"utf-8"),await H(l.daemonStatusFile,{state:"running",pid:process.pid,instance_id:C,updated_at:Date.now()}),process.send&&process.send("ready"),s.info("main","grix-connector ready")}Qe().catch(async o=>{const d=`Fatal: ${o instanceof Error?o.stack??o.message:String(o)}`;try{process.stderr.write(`${d}
41
+ `),s.error("main",o.message.replace(/\n/g," \u2014 "));const d=j(l.daemonLockFile);d?.pid===process.pid&&d.instance_id===C&&await H(l.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:C,updated_at:Date.now(),reason:`port_bind_${o.kind}:${o.label}:${o.port}`}).catch(()=>{}),await K(),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(1)}async function M(o){if(J)return;J=!0,s.info("main",`Received ${o}, shutting down...`),B.markShuttingDown();const d=setTimeout(()=>{s.error("main","Shutdown timed out, forcing exit"),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(2)},1e4);try{await n.stop(),await f()?.stop().catch(()=>{}),await D.stop(),await B.stop(),await K(),await L(l.daemonLockFile),await Fe(l.daemonStatusFile).catch(()=>{}),clearTimeout(d),F(),s.info("main","Shutdown complete"),process.exit(0)}catch(h){s.error("main",`Shutdown error: ${h}`),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(2)}}async function Qe(){if(xe(),Ee(),be(l.stdoutLogFile,l.stderrLogFile),te(!1),process.platform==="win32"){const e=Ue(l.rootDir,"win32");await Ce(e,{platform:"win32"})}try{C=(await _e(l.daemonLockFile,l.rootDir)).instance_id}catch(e){console.error(e instanceof Error?e.message:e),process.exit(1)}await Be(),ve(),s.info("main",`grix-connector starting (PID ${process.pid})`),await H(l.daemonStatusFile,{state:"starting",pid:process.pid,instance_id:C,updated_at:Date.now()});const o=parseInt(S["health-port"]??process.env.GRIX_HEALTH_PORT??"19579",10);{const e=await re({label:"health",port:o,envVar:"GRIX_HEALTH_PORT",cliFlag:"health-port",start:a=>B.start(a)});e&&await ge(e)}const d=I.join(l.dataDir,"health-port");ee(d,String(o),"utf-8"),process.on("SIGINT",()=>M("SIGINT")),process.on("SIGTERM",()=>M("SIGTERM")),process.on("SIGHUP",()=>{J||(s.info("main","Received SIGHUP, reloading config..."),n.reload().then(e=>s.info("main",`reload done: ${JSON.stringify(e)}`)).catch(e=>s.error("main",`reload failed: ${e instanceof Error?e.message:e}`)))});let h="",g=0,u;process.on("uncaughtException",e=>{const a=e instanceof Error?e.stack??e.message:String(e);a===h?(g++,(g<=3||g%100===0)&&s.error("main",`Uncaught exception (x${g}): ${a}`)):(g>3&&s.error("main",`Previous exception repeated ${g} times total`),h=a,g=1,s.error("main",`Uncaught exception: ${e instanceof Error?e.stack:e}`),u||(u=setTimeout(()=>{g>3&&s.error("main",`Previous exception repeated ${g} times total`),h="",g=0,u=void 0},1e4).unref())),!ue(e)&&(V(e,"uncaughtException"),M("uncaughtException"))}),process.on("unhandledRejection",e=>{s.error("main",`Unhandled rejection: ${e}`),!ue(e)&&(V(e,"unhandledRejection"),M("unhandledRejection"))});const v=Me(l.dataDir);await v.clearRuntimeState();const P=new Ne(I.join(l.dataDir,"proxy","relay-state.json"),{warn:e=>s.warn("main",e)}),O=new Ge(I.join(l.dataDir,"secrets"));n.setRelaySecretResolver(e=>O.get(e));const T={hasDirectConfig:e=>n.hasDirectRelayConfig(e),clearDirectConfig:(e,a)=>n.setAgentProvider(e,void 0,{deferRestart:a?.deferRestart===!0})},N=()=>({pm:f(),agents:n,direct:T,store:P,secrets:O}),U=v.getRelayAgents();if(U.length>0)try{await v.startIfAnyRelayAgent(),s.info("main",`mitm proxy started for relay agents: ${U.join(", ")} (per-agent; others stay direct)`)}catch(e){s.error("main",`mitm proxy FAILED to start (relay agents: ${U.join(", ")}): ${e}. These agents will REFUSE to start (they must not silently fall back to your own account). Fix the proxy or turn relay off for them. Other agents are unaffected.`)}else s.info("main",'mitm proxy idle (no agent has Grix relay enabled; enable: PUT /api/proxy/agents/<name>/enabled {"enabled":true})');let _=null;if(f()){const e=async()=>{const t=f();t&&await oe(t,n)&&s.info("main","no agent uses Grix relay anymore; mitm proxy stopped")};n.setRelayEnvSettledHandler(()=>{const t=f();t&&ae(t,T,P),e().catch(r=>{s.warn("main",`failed to stop idle mitm proxy: ${r}`)})});const a=()=>{const t=f(),r=t.getRelayAgents(),i=t.getDegradedRelayAgents();return{enabled:r.length>0,relayAgents:r,staleRelayAgents:n.getAgentsWithStaleRelayEnv(),degradedRelayAgents:i,relayState:P.list(),runtime:t.getRuntimeInfo(),config:t.getConfigSnapshot()}},c=new je,A={status:()=>a(),setAgentRelay:async(t,r,i)=>{const p=f(),R={info:y=>s.info("main",y),warn:y=>s.warn("main",y)},w=y=>n.getAgentsStatus().find(x=>x.name===y),m=w(t);if(!r){c.cancel(t);const y=await c.runExclusive(t,async()=>{const x=await W(N(),R,{agentName:t,target:"off"});return{...a(),restarted:x.restarted,busy:x.busy,...x.pending?{pending:x.pending}:{}}});return n.reportRelayStateLocalChange(t),y}if(!m)throw Object.assign(new Error(`Agent "${t}" not found`),{code:"NOT_FOUND"});if(!Ie(m.clientType)&&ne(m.clientType).length===0)throw Object.assign(new Error(`client type "${m.clientType??""}" does not support Grix relay`),{code:"UNSUPPORTED_CLIENT_TYPE"});const $=await c.runExclusive(t,async()=>{const y=c.epoch(t),x=w(t);if(!x)throw Object.assign(new Error(`Agent "${t}" not found`),{code:"NOT_FOUND"});const z=String(x.agentId??"").trim();if(!z)throw new ie(`local agent "${t}" has no agent_id registered`,"MISSING_AGENT_ID");const Q=c.waitCancelled(t,y);let k;try{k=await Promise.race([n.fetchRelayCredential(t,{model:i}),Q.promise.then(()=>{throw new le("relay enable cancelled by a later disable","CANCELLED")})])}finally{Q.dispose()}c.assertCurrent(t,y);const{restarted:ye,busy:he,pending:Z}=await se(p,n,w,R,{localName:t,agentId:z,virtualKey:k.apiKey,anthropicBaseUrl:k.anthropicBaseUrl,openaiBaseUrl:k.openaiBaseUrl,model:i,...k.directRelay!==void 0?{directRelay:k.directRelay}:{}},(we,Re,Se)=>n.setAgentProvider(we,Re,{deferRestart:Se?.deferRestart===!0}),{store:P,secrets:O,direct:T});if(c.epoch(t)!==y)throw await W(N(),R,{agentName:t,target:"off"}),new le("relay enable cancelled by a later disable","CANCELLED");return{...a(),restarted:ye,busy:he,...Z?{pending:Z}:{}}});return n.reportRelayStateLocalChange(t),$},setAgentRelayCredential:async(t,r)=>{const i=f(),p=n.getAgentsStatus(),{restarted:R,busy:w,pending:m}=await se(i,n,E=>p.find($=>$.name===E),{info:E=>s.info("main",E),warn:E=>s.warn("main",E)},{localName:t,agentId:r.agentId,virtualKey:r.virtualKey,anthropicBaseUrl:r.anthropicBaseUrl,openaiBaseUrl:r.openaiBaseUrl,model:r.model,...r.directRelay!==void 0?{directRelay:r.directRelay}:{}},(E,$,y)=>n.setAgentProvider(E,$,{deferRestart:y?.deferRestart===!0}),{store:P,secrets:O,direct:T});return n.reportRelayStateLocalChange(t),{...a(),restarted:R,busy:w,...m?{pending:m}:{}}},disableAll:async()=>{const t=f(),r={info:p=>s.info("main",p),warn:p=>s.warn("main",p)},i=new Set([...t.getRelayAgents(),...n.getDirectRelayConfigAgentNames(),...Object.keys(P.list())]);for(const p of i)c.cancel(p);for(const p of i)await c.runExclusive(p,()=>W(N(),r,{agentName:p,target:"off"})),n.reportRelayStateLocalChange(p);return await oe(t,n),a()},setRoute:async(t,r)=>{const i=r,p=f();return p.setRoute({routeKey:t,targetBaseUrl:i.targetBaseUrl,...i.headers?{headers:i.headers}:{},...i.model?{model:i.model}:{},...i.modelMap?{modelMap:i.modelMap}:{},...i.passthrough?{passthrough:!0}:{},...i.relayOnly?{relayOnly:!0}:{},...i.codexResponsesToChat?{codexResponsesToChat:!0}:{}}),await p.persistConfig(),a()},deleteRoute:async t=>{const r=f();r.deleteRoute(t),await r.persistConfig()},setDefaultRoute:async t=>{const r=f();return r.setDefaultRouteKey(t),await r.persistConfig(),a()},setInterceptHosts:async t=>{const r=f();return r.setInterceptHosts(t),await r.persistConfig(),a()}};_=A,D.setProxyHandler(A),n.setRelayStateApplier({getLocalState:t=>{const r=f();if(r.getRelayAgents().includes(t)){const p=n.getAgentsStatus().find(m=>m.name===t),R=`grix-gateway-${String(p?.clientType??"").trim().toLowerCase()}`;return{enabled:!0,model:r.getConfigSnapshot().routes?.find(m=>m.routeKey===R)?.model?.trim()||void 0}}const i=n.getAgentProvider(t);return i?.api_key?.trim()?{enabled:!0,model:i.model?.trim()||void 0}:{enabled:!1}},applyEnable:async(t,r,i)=>{const p=n.getAgentsStatus().find(m=>m.name===t),R=String(p?.agentId??"").trim();if(!R)throw new ie(`local agent "${t}" has no agent_id registered`,"MISSING_AGENT_ID");const w=await A.setAgentRelayCredential(t,{agentId:R,virtualKey:r.apiKey,anthropicBaseUrl:r.anthropicBaseUrl,openaiBaseUrl:r.openaiBaseUrl,model:i??r.model,...r.directRelay!==void 0?{directRelay:r.directRelay}:{}});return{restarted:w.restarted===!0,busy:w.busy===!0,...w.pending?{pending:w.pending}:{}}},applyDisable:async t=>{const r=await A.setAgentRelay(t,!1);return{restarted:r.restarted===!0,busy:r.busy===!0,...r.pending?{pending:r.pending}:{}}}})}if(B.setStatusProvider(()=>n.getAgentsStatus()),await n.start(ze),U.length>0){const e=n.getAgentsStatus();for(const a of U){const c=e.find(t=>t.name===a);if(!c)continue;const A=ne(c.clientType);A.length!==0&&await v.setAgentRelayEnabled(a,!0,{relayHosts:A}).catch(t=>{s.warn("main",`failed to reconcile relay hosts for "${a}": ${t}`)})}}ae(v,T,P);const Y=parseInt(S["admin-port"]??process.env.GRIX_ADMIN_PORT??"19580",10);D.setAgentHandler({list:()=>n.getAgentsStatus(),add:e=>n.addAgent(e),remove:e=>n.removeAgent(e),restart:e=>n.restartAgent(e),reload:()=>n.reload()}),D.setUpgradeHandler({check:()=>n.checkUpgrade(),trigger:()=>n.triggerUpgrade()}),D.setProbeHandler({probeAll:e=>n.probeAll(e),probeOne:(e,a)=>n.probeOne(e,a)}),D.setInstallHandler({listInstallable:()=>n.listInstallable(),installAgent:e=>n.installAgent(e),getInstallProgress:e=>n.getInstallProgress(e)});const q={listAgents:()=>n.getAgentsStatus(),enableManaged:(e,a,c)=>{if(!_)throw new ce(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return _.setAgentRelayCredential(e,{agentId:a,virtualKey:c.virtualKey,anthropicBaseUrl:c.anthropicBaseUrl,openaiBaseUrl:c.openaiBaseUrl,model:c.model,...c.directRelay!==void 0?{directRelay:c.directRelay}:{}})},disableManaged:e=>{if(!_)throw new ce(`agent "${e}" needs the MITM proxy for relay, but it is not running`,"PROXY_UNAVAILABLE");return _.setAgentRelay(e,!1)},enableHermesProfile:(e,a)=>Ke({agentId:e,baseUrl:a.openaiBaseUrl??"",virtualKey:a.virtualKey,model:a.model??""}),disableHermesProfile:e=>Ve(e)};D.setRelayCredentialHandler({listHermesProfiles:()=>We(),enable:async(e,a)=>{const{target:c,result:A}=await Je(q,e,a);return{target:c,...A}},disable:async e=>{const{target:a,result:c}=await Xe(q,e);return{target:a,...c}}});{const e=await re({label:"admin",port:Y,envVar:"GRIX_ADMIN_PORT",cliFlag:"admin-port",start:a=>D.start(a)});e&&await ge(e)}const fe=I.join(l.dataDir,"admin-token"),me=I.join(l.dataDir,"admin-port");He(fe,pe),ee(me,String(Y),"utf-8"),await H(l.daemonStatusFile,{state:"running",pid:process.pid,instance_id:C,updated_at:Date.now()}),process.send&&process.send("ready"),s.info("main","grix-connector ready")}Qe().catch(async o=>{const d=`Fatal: ${o instanceof Error?o.stack??o.message:String(o)}`;try{process.stderr.write(`${d}
42
42
  `)}catch{}te(!1),s.error("main",d),V(o,"startup");const h=j(l.daemonLockFile);h?.pid===process.pid&&h.instance_id===C&&await H(l.daemonStatusFile,{state:"failed",pid:process.pid,instance_id:h.instance_id,updated_at:Date.now(),reason:"startup_error"}).catch(()=>{}),await K(),L(l.daemonLockFile).catch(()=>{}),F(),process.exit(1)});const Ze=new Set(["ECONNRESET","ECONNREFUSED","ETIMEDOUT","EPIPE","EAI_AGAIN","ENOTFOUND","EHOSTUNREACH","ENETUNREACH","EIO"]);function ue(o){return o instanceof Error&&"code"in o?Ze.has(o.code):!1}
package/dist/log.js CHANGED
@@ -1,3 +1,3 @@
1
- import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as t}from"node:path";import{homedir as m}from"node:os";const i=t(m(),".grix"),s={base:i,config:t(i,"config"),log:t(i,"log"),data:t(i,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=t(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...n){const e=`${c()} [${o}] ${r}${n.length?" "+n.map(String).join(" "):""}`;console.log(e),a?.write(e+`
2
- `)},error(o,r,...n){const e=`${c()} [${o}] ERROR ${r}${n.length?" "+n.map(String).join(" "):""}`;console.error(e),a?.write(e+`
1
+ import{createWriteStream as g,mkdirSync as l,existsSync as f}from"node:fs";import{join as i}from"node:path";import{homedir as m}from"node:os";const n=i(m(),".grix"),s={base:n,config:i(n,"config"),log:i(n,"log"),data:i(n,"data")};function S(){for(const o of Object.values(s))f(o)||l(o,{recursive:!0})}let a=null;function $(){const o=new Date().toISOString().slice(0,10),r=i(s.log,`grix-acp-${o}.log`);a=g(r,{flags:"a"})}function c(){return new Date().toISOString().slice(11,19)}const u={info(o,r,...t){const e=`${c()} [${o}] ${r}${t.length?" "+t.map(String).join(" "):""}`;console.log(e),a?.write(e+`
2
+ `)},error(o,r,...t){const e=`${c()} [${o}] ERROR ${r}${t.length?" "+t.map(String).join(" "):""}`;console.error(e),a?.write(e+`
3
3
  `)}};export{s as GRIX_PATHS,S as ensureGrixDirs,$ as initLogger,u as log};
package/dist/manager.js CHANGED
@@ -1,2 +1,2 @@
1
- import{readFileSync as se,readdirSync as ie,writeFileSync as oe}from"node:fs";import{join as A}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as N}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,sharedInstanceKey as pe,SharedOwnersCache as me}from"./manager-share-config.js";import{isAgentDeletedError as q}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as ue}from"./core/util/client-version.js";import{UpgradeChecker as ge}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as he}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as _e}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as we}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as Ae,logDefaultSkillsCheck as Se,cleanupProjectedSkills as U}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as F,supportsNonMitmRelayConfig as ve}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as W,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Q}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as G}from"./core/runtime/cursor-resolve.js";import{getCliVersion as xe,resolveCliPath as $e}from"./core/util/cli-probe.js";import{AgentInstaller as Ie}from"./core/installer/installer.js";import{reportInstallFailure as Ee}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as Pe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Me}from"./adapter/codex/direct-home.js";function z(n){if(n)return{...n.provider_id?.trim()?{providerId:n.provider_id.trim()}:{},...n.base_url?.trim()?{baseUrl:n.base_url.trim()}:{},...n.quota_base_url?.trim()?{quotaBaseUrl:n.quota_base_url.trim()}:{},...n.api_key?.trim()?{apiKey:n.api_key.trim()}:{},...n.model?.trim()?{model:n.model.trim()}:{},...n.fast_model?.trim()?{fastModel:n.fast_model.trim()}:{},...n.effort?.trim()?{effort:n.effort.trim()}:{}}}const je=8e3;function Oe(){const n=Q();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:G()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function Re(){return Promise.all(Oe().map(async n=>{const e=await $e(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await xe(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function De(n){switch(n){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:G()};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=Q();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: ${n}`)}}function b(n,e){return String(n??"").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 Le(n){return A(w.data,`session-bindings-${b(n,"unknown")}.json`)}function He(n,e){return A(w.data,`session-bindings-${b(n,e)}.json`)}function Be(n,e){return A(w.data,`active-events-${b(n,e)}.json`)}function Ne(n){return A(w.data,`pending-events-${b(n,"unknown")}.json`)}function qe(n,e){const t=b(n,"agent"),r=b(e,"unknown");return A(w.data,`terminal-outbox-${t}-${r}.json`)}function Ue(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const c=String(a??"").trim(),i=c.toLowerCase();!c||t.has(i)||(t.add(i),e.push(c))}return e.length>0?e:void 0}function Fe(n,e){const t={claude:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codex:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},cursor:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},acp:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},pi:{maxConcurrent:1,maxQueued:5,queueTimeoutMs:0},codewhale:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},openhuman:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},opencode:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},agy:{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0},"deepseek-harness":{maxConcurrent:1,maxQueued:3,queueTimeoutMs:0}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??3,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(n,e={}){const t=ue(),r=String(n.client_type??"").trim().toLowerCase(),a=De(r),c=r==="deepseek"?n.dsh?.integration_mode??"profile_bridge":void 0,i=c==="embedded_jsonrpc",m=String(n.ws_url??"").trim(),o="get_session_usage",s="get_rate_limits",f="get_agent_global_config",g=ke;if(!n.name?.trim())throw new Error("agent name is required");if(!m)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const d=a.adapterType,p=d==="acp",y=r==="qwen",u={...a.options??{}};if(d==="deepseek-harness"&&(u.integrationMode=c,u.dshProfile=n.dsh?.profile?.trim()||"web",u.autoInstallBridge=n.dsh?.auto_install_bridge??!i,u.autoStartProfile=n.dsh?.auto_start_profile??!i),d==="deepseek-harness"&&n.provider?.model?.trim()&&(u.defaultModel=n.provider.model.trim()),n.codex_config_overrides!==void 0){if(d!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);Pe(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),u.codexConfigOverrides=n.codex_config_overrides}if(d==="codex"&&n.provider?.wire_api!==void 0){const h=n.provider;if(h.wire_api!=="responses"||!h.base_url?.trim()||!h.api_key?.trim()||!h.model?.trim()||!h.catalog_version?.trim()||!h.catalog_sha256?.trim()||!h.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const B=Me(w.data,n.name);u.codexHome=B,u.codexDirect={codexHome:B,config:{baseUrl:h.base_url.trim(),model:h.model.trim(),...h.reasoning_effort?.trim()?{reasoningEffort:h.reasoning_effort.trim()}:{},token:h.api_key.trim()},catalog:{version:h.catalog_version.trim(),sha256:h.catalog_sha256.trim(),json:h.catalog_json}}}const _=d==="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",o,s]}:null,S=d==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,s]}:null,k=y?{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",o],adapterHint:"qwen/base"}:null,$=d==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o,s]}:null,I=d==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=d==="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",s]}:null,O=d==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,T=d==="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,P=d==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",s]}:null,M=d==="deepseek-harness"?{adapterHint:i?"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",o,s,"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,R=new Set(["hermes","reasonix","kimi","copilot"]),J=p&&!R.has(r),X=p&&!y?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...J?[o]:[]]}:null,Y=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,s]}:null,Z=d==="codex"||d==="claude"||d==="opencode"||d==="deepseek-harness"||r==="gemini"?["session_control","set_model","set_mode",...d==="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"]:[],...d==="claude"?["set_reasoning_effort"]:[]]:void 0,ee=d==="cursor"||d==="opencode"||d==="openhuman"||d==="agy"||p&&R.has(r)?[s]:[o,s];p&&u.raw_transport===void 0&&(u.raw_transport=r==="gemini");const te=`${r}/base`,C=_e(d,r),ne=C==="kiro"?void 0:process.cwd(),j=z(n.provider),v={...process.env,...n.env};if(C==="hermes"&&!v.HERMES_HOME?.trim()){const h=be(r,j);v.HERMES_HOME=Ce({...h,...v},n.name)}const D=Ae({mode:C,projectDir:ne,env:v}),L=D.filter(h=>h.source==="connector").map(h=>h.name);L.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${L.join(", ")}]`);const H=ye(D,w.skills);let re=H.length>0?H:void 0;const ae=we({skillsDir:w.skills,mode:C,home:ce(),env:v});return{name:n.name,adapterType:d,aibot:{url:m,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:M?.adapterHint??a.adapterHint??k?.adapterHint??$?.adapterHint??I?.adapterHint??E?.adapterHint??T?.adapterHint??P?.adapterHint??te,capabilities:M?.capabilities??_?.capabilities??O?.capabilities??$?.capabilities??I?.capabilities??E?.capabilities??T?.capabilities??P?.capabilities??k?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Ue(M?.localActions??_?.localActions??O?.localActions??S?.localActions??$?.localActions??I?.localActions??E?.localActions??T?.localActions??P?.localActions??k?.localActions??Y?.localActions??X?.localActions??["exec_approve","exec_reject"],Z,ee,["connector_rollback","connector_upgrade_push",f,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...g]),skills:re,librarySkills:ae,terminalOutboxPath:qe(n.name,n.agent_id)},agent:{command:i?"dsh-jsonrpc-agent":a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...j?{provider:j}:{}},adapterOptions:u,acpAuthMethod:u.auth_method,acpInitialMode:u.initial_mode,acpMcpTools:u.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:Le(n.agent_id),legacyBindingsPath:He(n.name,n.agent_id),activeEventStorePath:Be(n.name,n.agent_id),pendingEventStorePath:Ne(n.agent_id),...a.enableSessionBinding||p?{enableSessionBinding:!0}:{},...a.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...a.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize??(d==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:Fe(d,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Ke(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function V(n){const e=[],t=[],r=[],a=ie(n).filter(c=>c.endsWith(".json")).sort();for(const c of a)try{const i=JSON.parse(se(A(n,c),"utf-8"));if(i===null||typeof i!="object"||Array.isArray(i)||!("agents"in i)){r.push(c);continue}if(!Array.isArray(i.agents)){t.push({file:c,error:"agents is not an array"});continue}if(i.agents.length===0){t.push({file:c,error:"agents array is empty"});continue}for(const m of i.agents)e.push({entry:m,file:c})}catch(i){t.push({file:c,error:i instanceof Error?i.message:String(i)})}return{entries:e,fileErrors:t,skipped:r}}function x(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(x).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${x(e[r])}`).join(",")}}`}function Qe(n,e){return x(n)===x(e)}class St{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new me(w.data);installer=new Ie;relaySecretResolver;auditProducer=Te();async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),Se(),U(),this.globalConfigStore=new he(A(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:c}=V(t);for(const s of a)l.error("manager",`Failed to load ${s.file}: ${s.error}`);for(const s of c)l.info("manager",`Skipped ${s}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&c.length===0){l.warn("manager",`No config files found in ${t}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const i=[];let m=a.length;for(const{entry:s,file:f}of r)try{const g=this.toRuntimeInstanceConfig(s);i.push({config:g,entry:s,file:f}),l.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${f}`)}catch(g){const d=typeof s?.name=="string"?s.name:"<unknown>";l.error("manager",`Invalid agent config in ${f} (name=${d}): ${g}`),m++}let o=0;if(i.length>0){const s=Ke();l.info("manager",`Starting ${i.length} agent(s), startup wait=${s}ms`);const f=p=>{this.instances=this.instances.filter(y=>y!==p)},g=i.map(({config:p,entry:y,file:u})=>{const _=this.createConfiguredInstance(p);return this.instances.push(_),this.configMap.set(p.name,p),this.rawConfigMap.set(p.name,{entry:y,file:u}),{config:p,instance:_,startPromise:this.prepareProviderRuntime(p,y).then(()=>_.start())}}),d=await Promise.all(g.map(async p=>{const y=await new Promise(u=>{let _=!1;const S=setTimeout(()=>{_||(_=!0,u({kind:"timeout"}))},s);p.startPromise.then(()=>{_||(_=!0,clearTimeout(S),u({kind:"started"}))}).catch(k=>{_||(_=!0,clearTimeout(S),u({kind:"failed",error:k}))})});return{task:p,outcome:y}}));for(const{task:p,outcome:y}of d){if(y.kind==="started"){this.restoreCachedSharedInstances(p.config);continue}if(y.kind==="failed"){if(q(y.error)){this.onAgentDeletedByPlatform(p.config.name);continue}f(p.instance),l.error("manager",`Failed to start ${p.config.name}: ${y.error}`);continue}o++,l.warn("manager",`Startup pending for ${p.config.name}, continue retrying in background`),p.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${p.config.name}`),this.restoreCachedSharedInstances(p.config)}).catch(u=>{if(q(u)){this.onAgentDeletedByPlatform(p.config.name);return}f(p.instance),l.error("manager",`Delayed start failed: ${p.config.name}: ${u}`)})}if(this.instances.length>0){const p=Math.max(0,this.instances.length-o);l.info("manager",`${p}/${i.length} agent(s) running now`)}o>0&&l.warn("manager",`${o} agent(s) still connecting in background`)}if(this.instances.length===0&&i.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.configMap.size===0)return;const e=[...this.configMap.values()].map(t=>({apiKey:t.aibot.apiKey,wsUrl:t.aibot.url}));this.upgradeChecker||(this.upgradeChecker=new ge(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission()),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new fe(e,w.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start())}refreshOwnerLibrarySkills(){const e=new Map,t=r=>{let a;try{a=r.skillsSyncGroupKey()}catch(i){l.warn("manager",`skillsSyncGroupKey failed for ${r.name}: ${i}`);return}const c=e.get(a)??[];c.push(r),e.set(a,c)};for(const r of this.instances)t(r);for(const r of this.sharedInstances.values())t(r);for(const[r,a]of e){let c=!1;const i=[];for(const m of a){if(c){i.push(m);continue}let o=!1;try{o=m.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(s){l.warn("manager",`owner library skills reporter threw group=${r} agent=${m.name}: ${s}`)}if(o){c=!0;continue}l.warn("manager",`owner library skills reporter failed group=${r} agent=${m.name}; trying next`)}if(!c){l.warn("manager",`owner library skills: no reporter succeeded group=${r}`);continue}for(const m of i)try{m.adoptSkillsWireHashFromDisk()}catch(o){l.warn("manager",`adoptSkillsWireHashFromDisk failed for ${m.name}: ${o}`)}}}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),await this.auditProducer.stop({timeoutMs:5e3}).catch(()=>!1),this.instances=[],U(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:a}=de(e.name,this.sharedInstances.keys(),t);for(const c of r){if(this.stopping)break;const i=pe(e.name,c);try{const m=le(e,c),o=new N(m,this.globalConfigStore,this.auditProducer);o.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),o.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&o.closeLifecycleAdmission(),this.sharedInstances.set(i,o),await o.start(),o.forceRefreshSkills(void 0,{force:!0}),l.info("manager",`shared instance started: ${i}`)}catch(m){this.sharedInstances.delete(i);const o=m instanceof Error?m.message:String(m);/Auth failed/i.test(o)?(this.sharedOwnersCache.remove(e.name,c),l.warn("manager",`shared instance auth rejected, removed from cache: ${i}`)):l.error("manager",`start shared instance failed ${i}: ${m}`)}}for(const{key:c}of a){const i=this.sharedInstances.get(c);i&&(await i.stop().catch(m=>l.error("manager",`stop shared instance failed ${c}: ${m}`)),this.sharedInstances.get(c)===i&&this.sharedInstances.delete(c),l.info("manager",`shared instance stopped: ${c}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),await this.ensureAuxServices(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=this.toRuntimeInstanceConfig(e);if(this.instances.some(a=>a.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}createConfiguredInstance(e){const t=new N(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),t.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(r=>this.installAgent(r)),t.setShareSetHandler(r=>this.onShareSet(e,r)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(r=>this.setAgentProvider(e.name,r)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(r,a)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,r,a):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const r=this.relayStateApplier;return r?r.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>z(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()}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);F(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await W(e.name,{baseUrl:r.base_url.trim(),apiKey:r.api_key.trim(),model:r.model.trim()})}async startInstanceFromConfig(e,t,r){await this.prepareProviderRuntime(e,t);const a=this.createConfiguredInstance(e);this.instances.push(a);try{await a.start(),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r})}catch(c){throw this.instances=this.instances.filter(i=>i!==a),c}this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(s=>s.name===e),a=r===-1?void 0:this.instances[r];this.configMap.delete(e),this.rawConfigMap.delete(e);const c=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),c&&await c.catch(()=>{});const i=t?.keepShareCache?void 0:{dropPendingEvents:!0},m=`${e}#shared:`,o=[];for(const[s,f]of[...this.sharedInstances.entries()])s.startsWith(m)&&o.push(f.stop(i).catch(g=>l.error("manager",`stop shared instance failed ${s}: ${g}`)).then(()=>{this.sharedInstances.get(s)===f&&this.sharedInstances.delete(s)}));if(t?.keepShareCache||this.sharedOwnersCache.delete(e),a)try{await a.stop(i)}finally{const s=this.instances.indexOf(a);s!==-1&&this.instances.splice(s,1)}await Promise.all(o),t?.keepShareCache||await K(e).catch(()=>{})}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap){const c=this.rawConfigMap.get(a.name)?.entry.provider;t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType,...c?{provider:c}:{}})}oe(e,JSON.stringify({agents:t},null,4)+`
1
+ import{readFileSync as se,readdirSync as ie,writeFileSync as oe}from"node:fs";import{join as A}from"node:path";import{homedir as ce}from"node:os";import{AgentInstance as N}from"./bridge/bridge.js";import{buildSharedInstanceConfig as le,diffSharedOwners as de,sharedInstanceKey as pe,SharedOwnersCache as me}from"./manager-share-config.js";import{isAgentDeletedError as q}from"./core/aibot/errors.js";import{GRIX_PATHS as w,log as l}from"./core/log/index.js";import{resolveClientVersion as ue}from"./core/util/client-version.js";import{UpgradeChecker as ge}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as fe}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as he}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as _e}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as ye}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as we}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ke}from"./core/aibot/types.js";import{buildReportedSkills as Ae,logDefaultSkillsCheck as Se,cleanupProjectedSkills as U}from"./default-skills/index.js";import{buildProviderEnv as be,supportsKiroCwRelay as F,supportsNonMitmRelayConfig as ve}from"./core/config/provider-env.js";import{resolveHermesHomeDir as Ce}from"./adapter/shared/native-provider-config.js";import{ensureKiroRelay as W,stopKiroRelay as K}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as Q}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as G}from"./core/runtime/cursor-resolve.js";import{getCliVersion as xe,resolveCliPath as $e}from"./core/util/cli-probe.js";import{AgentInstaller as Ie}from"./core/installer/installer.js";import{reportInstallFailure as Ee}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Te}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as Pe}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as Me}from"./adapter/codex/direct-home.js";function z(n){if(n)return{...n.provider_id?.trim()?{providerId:n.provider_id.trim()}:{},...n.base_url?.trim()?{baseUrl:n.base_url.trim()}:{},...n.quota_base_url?.trim()?{quotaBaseUrl:n.quota_base_url.trim()}:{},...n.api_key?.trim()?{apiKey:n.api_key.trim()}:{},...n.model?.trim()?{model:n.model.trim()}:{},...n.fast_model?.trim()?{fastModel:n.fast_model.trim()}:{},...n.effort?.trim()?{effort:n.effort.trim()}:{}}}const je=8e3;function Oe(){const n=Q();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:G()},{clientType:"pi",command:"pi"},{clientType:"openhuman",command:"openhuman-core"},{clientType:"kiro",command:"kiro-cli"},{clientType:"kimi",command:"kimi"},{clientType:"copilot",command:n.command},{clientType:"agy",command:"agy"},{clientType:"deepseek",command:"dsh"}]}async function Re(){return Promise.all(Oe().map(async n=>{const e=await $e(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await xe(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function De(n){switch(n){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:G()};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=Q();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: ${n}`)}}function b(n,e){return String(n??"").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 Le(n){return A(w.data,`session-bindings-${b(n,"unknown")}.json`)}function He(n,e){return A(w.data,`session-bindings-${b(n,e)}.json`)}function Be(n,e){return A(w.data,`active-events-${b(n,e)}.json`)}function Ne(n){return A(w.data,`pending-events-${b(n,"unknown")}.json`)}function qe(n,e){const t=b(n,"agent"),r=b(e,"unknown");return A(w.data,`terminal-outbox-${t}-${r}.json`)}function Ue(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const c=String(a??"").trim(),i=c.toLowerCase();!c||t.has(i)||(t.add(i),e.push(c))}return e.length>0?e:void 0}function Fe(n,e){const t={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}},r=t[n]??t.acp;return{maxConcurrent:e?.max_concurrent??r.maxConcurrent??1,maxQueued:e?.max_queued??r.maxQueued??99,queueTimeoutMs:e?.queue_timeout_ms??r.queueTimeoutMs??0,cancelableQueued:!0,cancelableRunning:!0}}function We(n,e={}){const t=ue(),r=String(n.client_type??"").trim().toLowerCase(),a=De(r),c=r==="deepseek"?n.dsh?.integration_mode??"profile_bridge":void 0,i=c==="embedded_jsonrpc",m=String(n.ws_url??"").trim(),o="get_session_usage",s="get_rate_limits",f="get_agent_global_config",g=ke;if(!n.name?.trim())throw new Error("agent name is required");if(!m)throw new Error(`agent ${n.name}: ws_url is required`);if(!n.agent_id?.trim())throw new Error(`agent ${n.name}: agent_id is required`);if(!n.api_key?.trim())throw new Error(`agent ${n.name}: api_key is required`);const d=a.adapterType,p=d==="acp",y=r==="qwen",u={...a.options??{}};if(d==="deepseek-harness"&&(u.integrationMode=c,u.dshProfile=n.dsh?.profile?.trim()||"web",u.autoInstallBridge=n.dsh?.auto_install_bridge??!i,u.autoStartProfile=n.dsh?.auto_start_profile??!i),d==="deepseek-harness"&&n.provider?.model?.trim()&&(u.defaultModel=n.provider.model.trim()),n.codex_config_overrides!==void 0){if(d!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);Pe(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),u.codexConfigOverrides=n.codex_config_overrides}if(d==="codex"&&n.provider?.wire_api!==void 0){const h=n.provider;if(h.wire_api!=="responses"||!h.base_url?.trim()||!h.api_key?.trim()||!h.model?.trim()||!h.catalog_version?.trim()||!h.catalog_sha256?.trim()||!h.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const B=Me(w.data,n.name);u.codexHome=B,u.codexDirect={codexHome:B,config:{baseUrl:h.base_url.trim(),model:h.model.trim(),...h.reasoning_effort?.trim()?{reasoningEffort:h.reasoning_effort.trim()}:{},token:h.api_key.trim()},catalog:{version:h.catalog_version.trim(),sha256:h.catalog_sha256.trim(),json:h.catalog_json}}}const _=d==="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",o,s]}:null,S=d==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",o,s]}:null,k=y?{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",o],adapterHint:"qwen/base"}:null,$=d==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",o,s]}:null,I=d==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=d==="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",s]}:null,O=d==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",o]}:null,T=d==="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,P=d==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",s]}:null,M=d==="deepseek-harness"?{adapterHint:i?"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",o,s,"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,R=new Set(["hermes","reasonix","kimi","copilot"]),J=p&&!R.has(r),X=p&&!y?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","claude_interaction_reply","file_list","create_folder",...J?[o]:[]]}:null,Y=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",o,s]}:null,Z=d==="codex"||d==="claude"||d==="opencode"||d==="deepseek-harness"||r==="gemini"?["session_control","set_model","set_mode",...d==="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"]:[],...d==="claude"?["set_reasoning_effort"]:[]]:void 0,ee=d==="cursor"||d==="opencode"||d==="openhuman"||d==="agy"||p&&R.has(r)?[s]:[o,s];p&&u.raw_transport===void 0&&(u.raw_transport=r==="gemini");const te=`${r}/base`,C=_e(d,r),ne=C==="kiro"?void 0:process.cwd(),j=z(n.provider),v={...process.env,...n.env};if(C==="hermes"&&!v.HERMES_HOME?.trim()){const h=be(r,j);v.HERMES_HOME=Ce({...h,...v},n.name)}const D=Ae({mode:C,projectDir:ne,env:v}),L=D.filter(h=>h.source==="connector").map(h=>h.name);L.length>0&&!e.quiet&&l.info("manager",`[${n.name}] injecting connector skills: [${L.join(", ")}]`);const H=ye(D,w.skills);let re=H.length>0?H:void 0;const ae=we({skillsDir:w.skills,mode:C,home:ce(),env:v});return{name:n.name,adapterType:d,aibot:{url:m,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:M?.adapterHint??a.adapterHint??k?.adapterHint??$?.adapterHint??I?.adapterHint??E?.adapterHint??T?.adapterHint??P?.adapterHint??te,capabilities:M?.capabilities??_?.capabilities??O?.capabilities??$?.capabilities??I?.capabilities??E?.capabilities??T?.capabilities??P?.capabilities??k?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Ue(M?.localActions??_?.localActions??O?.localActions??S?.localActions??$?.localActions??I?.localActions??E?.localActions??T?.localActions??P?.localActions??k?.localActions??Y?.localActions??X?.localActions??["exec_approve","exec_reject"],Z,ee,["connector_rollback","connector_upgrade_push",f,"configure_gateway_provider","skill_upload","skill_delete","skill_enable","skill_disable","skill_refresh",...g]),skills:re,librarySkills:ae,terminalOutboxPath:qe(n.name,n.agent_id)},agent:{command:i?"dsh-jsonrpc-agent":a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...j?{provider:j}:{}},adapterOptions:u,acpAuthMethod:u.auth_method,acpInitialMode:u.initial_mode,acpMcpTools:u.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:Le(n.agent_id),legacyBindingsPath:He(n.name,n.agent_id),activeEventStorePath:Be(n.name,n.agent_id),pendingEventStorePath:Ne(n.agent_id),...a.enableSessionBinding||p?{enableSessionBinding:!0}:{},...a.autoInjectArgs||r==="reasonix"&&n.provider?.model?.trim()?{autoInjectArgs:{...a.autoInjectArgs??{},...r==="reasonix"&&n.provider?.model?.trim()?{model:n.provider.model.trim()}:{}}}:{},poolMaxSize:n.pool?.maxSize??(d==="deepseek-harness"?4:void 0),poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:Fe(d,n.event_queue),logDir:w.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Ke(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):je}function V(n){const e=[],t=[],r=[],a=ie(n).filter(c=>c.endsWith(".json")).sort();for(const c of a)try{const i=JSON.parse(se(A(n,c),"utf-8"));if(i===null||typeof i!="object"||Array.isArray(i)||!("agents"in i)){r.push(c);continue}if(!Array.isArray(i.agents)){t.push({file:c,error:"agents is not an array"});continue}if(i.agents.length===0){t.push({file:c,error:"agents array is empty"});continue}for(const m of i.agents)e.push({entry:m,file:c})}catch(i){t.push({file:c,error:i instanceof Error?i.message:String(i)})}return{entries:e,fileErrors:t,skipped:r}}function x(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(x).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${x(e[r])}`).join(",")}}`}function Qe(n,e){return x(n)===x(e)}class St{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;lifecycleAdmissionClosed=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=w.config;sharedOwnersCache=new me(w.data);installer=new Ie;relaySecretResolver;auditProducer=Te();async start(e){const t=e??w.config;this.configDir=t,l.info("manager",`Loading configs from ${t}`),Se(),U(),this.globalConfigStore=new he(A(w.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:c}=V(t);for(const s of a)l.error("manager",`Failed to load ${s.file}: ${s.error}`);for(const s of c)l.info("manager",`Skipped ${s}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&c.length===0){l.warn("manager",`No config files found in ${t}; daemon idle (waiting for config). Add agents under this directory then run: grix-connector reload`);return}const i=[];let m=a.length;for(const{entry:s,file:f}of r)try{const g=this.toRuntimeInstanceConfig(s);i.push({config:g,entry:s,file:f}),l.info("manager",`Loaded ${g.name} (${g.adapterType??"acp"}) from ${f}`)}catch(g){const d=typeof s?.name=="string"?s.name:"<unknown>";l.error("manager",`Invalid agent config in ${f} (name=${d}): ${g}`),m++}let o=0;if(i.length>0){const s=Ke();l.info("manager",`Starting ${i.length} agent(s), startup wait=${s}ms`);const f=p=>{this.instances=this.instances.filter(y=>y!==p)},g=i.map(({config:p,entry:y,file:u})=>{const _=this.createConfiguredInstance(p);return this.instances.push(_),this.configMap.set(p.name,p),this.rawConfigMap.set(p.name,{entry:y,file:u}),{config:p,instance:_,startPromise:this.prepareProviderRuntime(p,y).then(()=>_.start())}}),d=await Promise.all(g.map(async p=>{const y=await new Promise(u=>{let _=!1;const S=setTimeout(()=>{_||(_=!0,u({kind:"timeout"}))},s);p.startPromise.then(()=>{_||(_=!0,clearTimeout(S),u({kind:"started"}))}).catch(k=>{_||(_=!0,clearTimeout(S),u({kind:"failed",error:k}))})});return{task:p,outcome:y}}));for(const{task:p,outcome:y}of d){if(y.kind==="started"){this.restoreCachedSharedInstances(p.config);continue}if(y.kind==="failed"){if(q(y.error)){this.onAgentDeletedByPlatform(p.config.name);continue}f(p.instance),l.error("manager",`Failed to start ${p.config.name}: ${y.error}`);continue}o++,l.warn("manager",`Startup pending for ${p.config.name}, continue retrying in background`),p.startPromise.then(()=>{l.info("manager",`Delayed start succeeded: ${p.config.name}`),this.restoreCachedSharedInstances(p.config)}).catch(u=>{if(q(u)){this.onAgentDeletedByPlatform(p.config.name);return}f(p.instance),l.error("manager",`Delayed start failed: ${p.config.name}: ${u}`)})}if(this.instances.length>0){const p=Math.max(0,this.instances.length-o);l.info("manager",`${p}/${i.length} agent(s) running now`)}o>0&&l.warn("manager",`${o} agent(s) still connecting in background`)}if(this.instances.length===0&&i.length>0)throw new Error("All agent configurations failed to start");await this.ensureAuxServices()}async ensureAuxServices(){if(this.configMap.size===0)return;const e=[...this.configMap.values()].map(t=>({apiKey:t.aibot.apiKey,wsUrl:t.aibot.url}));this.upgradeChecker||(this.upgradeChecker=new ge(e,()=>this.hasAnyPendingOrBackgroundWork(),()=>this.closeAllLifecycleAdmission()),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new fe(e,w.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshOwnerLibrarySkills()),await this.skillSyncer.start())}refreshOwnerLibrarySkills(){const e=new Map,t=r=>{let a;try{a=r.skillsSyncGroupKey()}catch(i){l.warn("manager",`skillsSyncGroupKey failed for ${r.name}: ${i}`);return}const c=e.get(a)??[];c.push(r),e.set(a,c)};for(const r of this.instances)t(r);for(const r of this.sharedInstances.values())t(r);for(const[r,a]of e){let c=!1;const i=[];for(const m of a){if(c){i.push(m);continue}let o=!1;try{o=m.forceRefreshSkills(void 0,{force:!0,ownerLibrarySync:!0})===!0}catch(s){l.warn("manager",`owner library skills reporter threw group=${r} agent=${m.name}: ${s}`)}if(o){c=!0;continue}l.warn("manager",`owner library skills reporter failed group=${r} agent=${m.name}; trying next`)}if(!c){l.warn("manager",`owner library skills: no reporter succeeded group=${r}`);continue}for(const m of i)try{m.adoptSkillsWireHashFromDisk()}catch(o){l.warn("manager",`adoptSkillsWireHashFromDisk failed for ${m.name}: ${o}`)}}}async stop(){l.info("manager","Stopping all agents..."),this.stopping=!0,this.upgradeChecker?.stop(),this.skillSyncer?.stop(),await Promise.allSettled([...this.shareSyncChains.values()]),this.shareSyncChains.clear();const e=[...this.sharedInstances.values()];this.sharedInstances.clear(),await Promise.allSettled([...this.instances.map(t=>t.stop()),...e.map(t=>t.stop())]),await this.globalConfigStore?.flush(),await this.auditProducer.stop({timeoutMs:5e3}).catch(()=>!1),this.instances=[],U(),l.info("manager","All stopped")}onShareSet(e,t){this.sharedOwnersCache.save(e.name,t);const a=(this.shareSyncChains.get(e.name)??Promise.resolve()).catch(()=>{}).then(()=>this.syncSharedInstances(e,t));this.shareSyncChains.set(e.name,a)}async syncSharedInstances(e,t){if(this.stopping)return;const{toAdd:r,toRemove:a}=de(e.name,this.sharedInstances.keys(),t);for(const c of r){if(this.stopping)break;const i=pe(e.name,c);try{const m=le(e,c),o=new N(m,this.globalConfigStore,this.auditProducer);o.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),o.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&o.closeLifecycleAdmission(),this.sharedInstances.set(i,o),await o.start(),o.forceRefreshSkills(void 0,{force:!0}),l.info("manager",`shared instance started: ${i}`)}catch(m){this.sharedInstances.delete(i);const o=m instanceof Error?m.message:String(m);/Auth failed/i.test(o)?(this.sharedOwnersCache.remove(e.name,c),l.warn("manager",`shared instance auth rejected, removed from cache: ${i}`)):l.error("manager",`start shared instance failed ${i}: ${m}`)}}for(const{key:c}of a){const i=this.sharedInstances.get(c);i&&(await i.stop().catch(m=>l.error("manager",`stop shared instance failed ${c}: ${m}`)),this.sharedInstances.get(c)===i&&this.sharedInstances.delete(c),l.info("manager",`shared instance stopped: ${c}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(l.info("manager",`restoring ${t.length} cached shared instance(s) for ${e.name}`),this.onShareSet(e,t))}getAgentsStatus(){return this.instances.map(e=>e.getStatus())}async addAgent(e){await this.addAgentInternal(e,"agents.json"),this.persistAgentsConfig(),await this.ensureAuxServices(),l.info("manager",`Added agent: ${e.name}`)}async addAgentInternal(e,t){const r=this.toRuntimeInstanceConfig(e);if(this.instances.some(a=>a.name===r.name))throw new Error(`Agent "${r.name}" already exists`);await this.startInstanceFromConfig(r,e,t)}createConfiguredInstance(e){const t=new N(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),t.setLifecycleBusyChecker(()=>this.hasAnyPendingOrBackgroundWork()),t.setLifecycleAdmissionCloser(()=>this.closeAllLifecycleAdmission()),this.lifecycleAdmissionClosed&&t.closeLifecycleAdmission(),t.setInstallAgentHandler(r=>this.installAgent(r)),t.setShareSetHandler(r=>this.onShareSet(e,r)),t.setAgentDeletedHandler(()=>this.onAgentDeletedByPlatform(e.name)),t.setSkillSyncHandler(()=>this.skillSyncer?.triggerSync()),t.setProviderConfigHandler(r=>this.setAgentProvider(e.name,r)),t.setRelayStateApplyPorts({getLocalState:()=>this.relayStateApplier?.getLocalState(e.name)??{enabled:!1},applyEnable:(r,a)=>{const c=this.relayStateApplier;return c?c.applyEnable(e.name,r,a):Promise.reject(new Error("relay state applier is not configured"))},applyDisable:()=>{const r=this.relayStateApplier;return r?r.applyDisable(e.name):Promise.reject(new Error("relay state applier is not configured"))}}),t.setRelayEnvSettledHandler(()=>this.relayEnvSettledHandler?.()),t.setProviderResolver(()=>z(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()}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);F(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await W(e.name,{baseUrl:r.base_url.trim(),apiKey:r.api_key.trim(),model:r.model.trim()})}async startInstanceFromConfig(e,t,r){await this.prepareProviderRuntime(e,t);const a=this.createConfiguredInstance(e);this.instances.push(a);try{await a.start(),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r})}catch(c){throw this.instances=this.instances.filter(i=>i!==a),c}this.relayEnvSettledHandler?.()}async removeAgent(e){if(!this.instances.some(t=>t.name===e))throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(s=>s.name===e),a=r===-1?void 0:this.instances[r];this.configMap.delete(e),this.rawConfigMap.delete(e);const c=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),c&&await c.catch(()=>{});const i=t?.keepShareCache?void 0:{dropPendingEvents:!0},m=`${e}#shared:`,o=[];for(const[s,f]of[...this.sharedInstances.entries()])s.startsWith(m)&&o.push(f.stop(i).catch(g=>l.error("manager",`stop shared instance failed ${s}: ${g}`)).then(()=>{this.sharedInstances.get(s)===f&&this.sharedInstances.delete(s)}));if(t?.keepShareCache||this.sharedOwnersCache.delete(e),a)try{await a.stop(i)}finally{const s=this.instances.indexOf(a);s!==-1&&this.instances.splice(s,1)}await Promise.all(o),t?.keepShareCache||await K(e).catch(()=>{})}onAgentDeletedByPlatform(e){this.stopping||this.deletedAgentCleanups.has(e)||(this.deletedAgentCleanups.add(e),(async()=>{try{if(!(this.rawConfigMap.has(e)||this.instances.some(r=>r.name===e)))return;l.warn("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u505C\u6B62\u5B9E\u4F8B\u5E76\u6E05\u7406\u672C\u5730\u914D\u7F6E`),await this.removeAgentInternal(e),this.persistAgentsConfig(),l.info("manager",`Agent "${e}" \u5DF2\u5728\u5E73\u53F0\u5220\u9664\uFF0C\u672C\u5730\u914D\u7F6E\u5DF2\u6E05\u7406\uFF08agents.json \u5DF2\u79FB\u9664\u8BE5\u6761\u76EE\uFF09`)}catch(t){l.error("manager",`cleanup for deleted agent "${e}" failed: ${t}`)}finally{this.deletedAgentCleanups.delete(e)}})())}persistAgentsConfig(){const e=A(this.configDir,"agents.json");try{const t=[];for(const[,a]of this.configMap){const c=this.rawConfigMap.get(a.name)?.entry.provider;t.push({name:a.name,ws_url:a.aibot.url,agent_id:a.aibot.agentId,api_key:a.aibot.apiKey,client_type:a.aibot.clientType,...c?{provider:c}:{}})}oe(e,JSON.stringify({agents:t},null,4)+`
2
2
  `,"utf-8")}catch(t){l.error("manager",`Failed to persist agents config: ${t}`)}}isAgentBusy(e){return this.instances.find(r=>r.name===e)?.hasPendingWork()??!1}getAgentsWithStaleRelayEnv(){return this.instances.filter(e=>e.hasStaleRelayEnv()).map(e=>e.name)}markAgentRelayEnvStale(e){const t=this.instances.find(r=>r.name===e);return t?(t.markRelayEnvStale(),!0):!1}relayEnvSettledHandler;setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}relayStateApplier;setRelayStateApplier(e){this.relayStateApplier=e}reportRelayStateLocalChange(e){this.instances.find(r=>r.name===e)?.reportRelayStateLocalChange()}setRelaySecretResolver(e){this.relaySecretResolver=e}resolveAgentProvider(e){if(!e?.api_key_ref?.trim())return e;const t=e.api_key_ref.trim(),r=this.relaySecretResolver?.(t);if(!r)throw Object.assign(new Error(`relay credential "${t}" is unavailable`),{code:"RELAY_SECRET_UNAVAILABLE"});return{...e,api_key:r}}toRuntimeInstanceConfig(e,t={}){const r=this.resolveAgentProvider(e.provider);return We(r===e.provider?e:{...e,provider:r},t)}getAgentProvider(e){return this.resolveAgentProvider(this.rawConfigMap.get(e)?.entry.provider)}async restartAgent(e){const t=this.rawConfigMap.get(e);if(!t)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});await this.replaceInstance(e,t.entry,t.file),l.info("manager",`Restarted agent: ${e}`)}async setAgentProvider(e,t,r){const a=this.rawConfigMap.get(e);if(!a)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});const c=String(a.entry.client_type??"").trim().toLowerCase();F(c)&&(t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()?await W(e,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()}):await K(e));const i=t?.api_key_ref?.trim()?Object.fromEntries(Object.entries(t).filter(([o])=>o!=="api_key")):t,m={...a.entry,provider:i};if(r?.deferRestart){this.rawConfigMap.set(e,{...a,entry:m}),this.instances.find(o=>o.name===e)?.markRelayEnvStale(),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}await this.replaceInstance(e,m,a.file),this.persistAgentsConfig(),l.info("manager",`Updated provider config for agent: ${e}`)}hasDirectRelayConfig(e){const t=this.rawConfigMap.get(e)?.entry.provider;return!!(t?.api_key_ref?.trim()||t?.api_key?.trim())}getDirectRelayConfigAgentNames(){return[...this.rawConfigMap.entries()].filter(([,e])=>!!e.entry.provider&&(ve(e.entry.client_type)||["claude","codex"].includes(String(e.entry.client_type??"").trim().toLowerCase()))).map(([e])=>e)}async fetchRelayCredential(e,t){const r=this.instances.find(a=>a.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.fetchRelayCredential(t)}async clearDirectProviderConfigs(){const e=this.getDirectRelayConfigAgentNames(),t=[];for(const r of e)try{await this.setAgentProvider(r,void 0,{deferRestart:this.isAgentBusy(r)}),t.push(r)}catch(a){l.warn("manager",`Failed to clear provider config for "${r}": ${a instanceof Error?a.message:String(a)}`)}return t}async replaceInstance(e,t,r){const a=this.toRuntimeInstanceConfig(t);await this.removeAgentInternal(e,{keepShareCache:!0}),await this.startInstanceFromConfig(a,t,r),this.restoreCachedSharedInstances(a)}async reload(){const e=this.reloadChain.catch(()=>{}).then(()=>this.doReload());return this.reloadChain=e.catch(()=>{}),e}async doReload(){if(this.stopping)throw Object.assign(new Error("manager is stopping"),{code:"RELOAD_UNSAFE"});const{entries:e,fileErrors:t,skipped:r}=V(this.configDir);for(const o of r)l.info("manager",`reload: skipped ${o}: not an agent config (no "agents" key)`);if(t.length>0){const o=t.map(s=>`${s.file}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${t.length} config file(s) failed to parse [${o}]`),{code:"RELOAD_UNSAFE"})}if(e.length===0)throw Object.assign(new Error("reload aborted: no valid agent config found"),{code:"RELOAD_UNSAFE"});const a=[];for(const{entry:o}of e)try{this.toRuntimeInstanceConfig(o,{quiet:!0})}catch(s){const f=o?.name,g=typeof f=="string"&&f.trim()?f:"<unknown>";a.push({name:g,error:s instanceof Error?s.message:String(s)})}if(a.length>0){const o=a.map(s=>`${s.name}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${a.length} agent config(s) invalid [${o}]`),{code:"RELOAD_UNSAFE"})}const c=new Map;for(const o of e){const s=String(o.entry?.name??"").trim();s&&(c.has(s)&&l.warn("manager",`reload: duplicate agent name "${s}", last one wins`),c.set(s,o))}const i={added:[],removed:[],restarted:[],unchanged:[],failed:[]},m=new Set(this.rawConfigMap.keys());for(const o of m)if(!c.has(o))try{await this.removeAgentInternal(o),i.removed.push(o)}catch(s){i.failed.push({name:o,error:s instanceof Error?s.message:String(s)})}for(const[o,s]of c){const f=this.rawConfigMap.get(o);if(f)if(Qe(f.entry,s.entry))this.rawConfigMap.set(o,s),i.unchanged.push(o);else try{await this.replaceInstance(o,s.entry,s.file),i.restarted.push(o)}catch(g){i.failed.push({name:o,error:g instanceof Error?g.message:String(g)})}else try{await this.addAgentInternal(s.entry,s.file),i.added.push(o)}catch(g){i.failed.push({name:o,error:g instanceof Error?g.message:String(g)})}}return l.info("manager",`reload done: +${i.added.length} -${i.removed.length} ~${i.restarted.length} =${i.unchanged.length} !${i.failed.length}`),await this.ensureAuxServices(),i}async checkUpgrade(){return this.upgradeChecker?this.upgradeChecker.checkForUpdate():{available:!1}}triggerUpgrade(){this.upgradeChecker?.triggerCheck()}async probeAll(e={}){return Ge(this.instances,e)}async probeOne(e,t={}){return ze(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return Ee(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function Ge(n,e){const t=e.concurrency??4,r=Date.now(),a=new Array(n.length);await new Promise(s=>{let f=0,g=0;const d=n.length;if(d===0){s();return}function p(_){const S=n[_];S.probe(e).then(k=>{a[_]=k,y()},k=>{a[_]={agent_name:S.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:k?.message??String(k)}},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}},y()})}function y(){g++,f<d?p(f++):g===d&&s()}const u=Math.min(t,d);for(let _=0;_<u;_++)p(f++)});const c=a.filter(s=>s.status==="healthy").length,i=a.filter(s=>s.status==="degraded").length,m=a.filter(s=>s.status==="unavailable").length,o=await Re();return{ok:c===a.length&&a.length>0,total:a.length,healthy:c,degraded:i,unavailable:m,installed_clients:o,agents:a,probed_at:r,duration_ms:Date.now()-r}}async function ze(n,e,t){const r=n.find(a=>a.name===e);if(!r)throw Object.assign(new Error(`Agent "${e}" not found`),{code:"NOT_FOUND"});return r.probe(t)}export{St as Manager,z as normalizeAgentProviderForRuntime,Re as probeInstalledClientCommands,Ge as probeInstances,ze as probeOneInstance};
@@ -1 +1 @@
1
- import*as i from"node:net";const e={bind:"127.0.0.1",port:0,endpoint:"/mcp",sessionTimeoutMs:18e5,invokeTimeoutMs:3e4};function n(o){const u={bind:o?.bind??e.bind,port:o?.port??e.port,endpoint:o?.endpoint??e.endpoint,sessionTimeoutMs:o?.sessionTimeoutMs??e.sessionTimeoutMs,invokeTimeoutMs:o?.invokeTimeoutMs??e.invokeTimeoutMs,allowedOrigins:o?.allowedOrigins,allowedHosts:o?.allowedHosts};return s(u.bind),u.port!==0&&t(u.port),r(u.sessionTimeoutMs),u}function s(o){if(!o||!i.isIPv4(o)&&!i.isIPv6(o))throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: bind \u5730\u5740 "${o}" \u4E0D\u662F\u5408\u6CD5\u7684 IPv4 \u6216 IPv6 \u5730\u5740`)}function t(o){if(!Number.isInteger(o)||o<1||o>65535)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: port \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1-65535 \u5185\u6216\u4E0D\u662F\u6574\u6570`)}function r(o){if(!Number.isInteger(o)||o<1e3||o>864e5)throw new Error(`\u914D\u7F6E\u6821\u9A8C\u5931\u8D25: session_timeout_ms \u503C ${o} \u4E0D\u5728\u5408\u6CD5\u8303\u56F4 1000-86400000 \u5185`)}export{n as createDefaultGatewayConfig};
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 r=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
+ 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
- function a(o){const e=new Set([`http://127.0.0.1:${o.serverPort}`,`http://localhost:${o.serverPort}`,...o.allowedOrigins]),t=new Set([`127.0.0.1:${o.serverPort}`,`localhost:${o.serverPort}`,...o.allowedHosts]);return{validateRequest(s){const r=i(s,e);if(!r.ok)return r;const n=l(s,t);return n.ok?{ok:!0}:n}}}function i(o,e){const t=o.headers.origin;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${t}`}:{ok:!0}}function l(o,e){const t=o.headers.host;return t?e.has(t)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${t}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
1
+ function a(e){const t=new Set([`http://127.0.0.1:${e.serverPort}`,`http://localhost:${e.serverPort}`,...e.allowedOrigins]),o=new Set([`127.0.0.1:${e.serverPort}`,`localhost:${e.serverPort}`,...e.allowedHosts]);return{validateRequest(s){const r=i(s,t);if(!r.ok)return r;const n=l(s,o);return n.ok?{ok:!0}:n}}}function i(e,t){const o=e.headers.origin;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${o}`}:{ok:!0}}function l(e,t){const o=e.headers.host;return o?t.has(o)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${o}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
@@ -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(t,e,r,n){if(!this.registry.hasTool(e))return this.errorResult(`\u672A\u77E5\u5DE5\u5177: ${e}`);const s=a(e,r);if(!s.valid)return this.errorResult(`\u53C2\u6570\u6821\u9A8C\u5931\u8D25: ${s.error}`);if(t.status!=="ready")return this.errorResult(`\u8FDE\u63A5\u4E0D\u53EF\u7528: \u5F53\u524D\u72B6\u6001\u4E3A ${t.status}`);if(p(e))return this.executeEventTool(t,e,r);const o=i(e,r);try{const u=await t.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(t){if(t==null||typeof t!="object")return this.successResult(t??null);const e=t,r=typeof e.code=="number"?e.code:0;if(r===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=${r}]: ${n}`)}successResult(t){return{content:[{type:"text",text:JSON.stringify(t)}],isError:!1}}errorResult(t){return{content:[{type:"text",text:t}],isError:!0}}async executeEventTool(t,e,r){return e==="grix_access_control"?this.executeAccessControl(t,r):d(t,e,r)}async executeAccessControl(t,e){const r=String(e.action??""),n={pair_approve:"pair_approve",pair_deny:"pair_deny",allow_sender:"sender_allow",remove_sender:"sender_remove",set_policy:"policy_set"}[r];if(!n)return this.errorResult(`\u672A\u77E5 access_control action: ${r}`);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 t.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
+ 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 s,EVENT_TOOLS as t}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=[...s.filter(o=>e.has(o.name)),...t.filter(o=>r.has(o.name))],this.toolMap=new Map(this.tools.map(o=>[o.name,o]))}getTools(){return this.tools}getTool(o){return this.toolMap.get(o)}hasTool(o){return this.toolMap.has(o)}}export{a as ToolRegistryImpl};
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(u,t){const e=C[u];if(!e)return{valid:!1,error:`\u672A\u77E5\u5DE5\u5177: ${u}`};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,r]of Object.entries(t)){if(r==null)continue;const n=e.properties[i];if(!n)continue;const s=$(i,r,n);if(s)return{valid:!1,error:s}}return{valid:!0}}function $(u,t,e){switch(e.type){case"string":if(typeof t!="string")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxLength!==void 0&&t.length>e.maxLength)return`\u53C2\u6570 ${u} \u8D85\u8FC7\u6700\u5927\u957F\u5EA6 ${e.maxLength}\uFF0C\u5B9E\u9645 ${t.length}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C "${t}" \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"integer":if(typeof t!="number"||!Number.isInteger(t))return`\u53C2\u6570 ${u} \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 ${u} \u503C ${t} \u5C0F\u4E8E\u6700\u5C0F\u503C ${e.minimum}`;if(e.maximum!==void 0&&t>e.maximum)return`\u53C2\u6570 ${u} \u503C ${t} \u5927\u4E8E\u6700\u5927\u503C ${e.maximum}`;if(e.enum&&!e.enum.includes(t))return`\u53C2\u6570 ${u} \u503C ${t} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.enum.join(", ")}]`;break;case"boolean":if(typeof t!="boolean")return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B boolean\uFF0C\u5B9E\u9645 ${typeof t}`;break;case"array":if(!Array.isArray(t))return`\u53C2\u6570 ${u} \u7C7B\u578B\u9519\u8BEF: \u671F\u671B array\uFF0C\u5B9E\u9645 ${typeof t}`;if(e.maxItems!==void 0&&t.length>e.maxItems)return`\u53C2\u6570 ${u} \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 r=t[i];if(e.items.type==="string"&&typeof r!="string")return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B string\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.type==="integer"){if(typeof r!="number"||!Number.isInteger(r))return`\u53C2\u6570 ${u}[${i}] \u7C7B\u578B\u9519\u8BEF: \u671F\u671B integer\uFF0C\u5B9E\u9645 ${typeof r}`;if(e.items.enum&&!e.items.enum.includes(r))return`\u53C2\u6570 ${u}[${i}] \u503C ${r} \u4E0D\u5728\u5141\u8BB8\u8303\u56F4 [${e.items.enum.join(", ")}]`}}break}}export{B as validateToolArgs};
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};
@@ -1 +1 @@
1
- import{EventEmitter as S}from"events";import{log as r}from"../core/log/index.js";import{AgentEventType as c}from"../types/events.js";import{mapSessionUpdate as M}from"./event-mapper.js";class p extends Error{authMethods;constructor(e){super("ACP authentication required"),this.name="AcpAuthRequiredError",this.authMethods=e}}function h(l){if(!l||typeof l!="object")return!1;const e=l;return e.code===-32e3?!0:e.code===-32603&&e.data?.details?/\b(401|token\s*expired|access\s*token)\b/i.test(e.data.details):!1}function m(l){return!l||typeof l!="object"?[{id:"oauth"}]:l.data?.authMethods??[{id:"oauth"}]}class u extends S{acpSessionId="";alive=!1;pendingPermissions=new Map;availableModes=[];availableModels=[];currentMode="";currentModel="";listSupported=!1;loadSupported=!1;_supportsCommandsExecute=!1;_availableCommands=[];transport;settleTimer=null;static SETTLE_MS=3e3;pendingToolCallIds=new Set;settleSuppressedSince=0;static SETTLE_SUPPRESS_MAX_MS=900*1e3;constructor(){super(),this.transport=null}get sessionId(){return this.acpSessionId}get isAlive(){return this.alive}get modes(){return[...this.availableModes]}get mode(){return this.currentMode}get models(){return[...this.availableModels]}get model(){return this.currentModel}get sessionOptions(){return{modes:[...this.availableModes],currentModeId:this.currentMode,models:[...this.availableModels],currentModelId:this.currentModel}}get supportsCommandsExecute(){return this._supportsCommandsExecute}get availableCommands(){return[...this._availableCommands]}async connect(e){this.transport=e.transport,this.transport.on("close",()=>{this.alive&&(this.alive=!1,this.emit("session-lost"))}),this.transport.setHandlers((n,a)=>{n==="session/update"?this.handleSessionUpdate(a):n==="_kiro.dev/metadata"?this.handleKiroMetadata(a):n==="_kiro.dev/commands/available"?this.handleCommandsAvailable(a):n==="_kiro.dev/compaction/status"?this.emit("compactionStatus",a):n==="_kiro.dev/clear/status"&&this.emit("clearStatus",a)},(n,a,d)=>{n==="session/request_permission"?this.handlePermissionRequest(a,d):n.startsWith("cursor/")?this.transport.respondSuccess(a,{}).catch(()=>{}):this.transport.respondError(a,-32601,"method not implemented").catch(()=>{})});const t=await this.initialize();this.listSupported=!!t.agentCapabilities?.sessionCapabilities?.list,this.loadSupported=!!t.agentCapabilities?.loadSession;const s=t.agentCapabilities??{};if(this._supportsCommandsExecute=!!(s.extensions?.["_kiro.dev/commands"]||s.commands),e.authMethod)try{await this.transport.call("authenticate",{methodId:e.authMethod})}catch(n){throw h(n)?new p(m(n)):n}const i=e.sessionId&&e.sessionId!=="__continue__";let o=!1;if(i){if(!this.loadSupported)throw new Error(`session/load not supported by agent, cannot resume session ${e.sessionId}`);try{await this.loadSession(e.sessionId,e)&&(o=!0)}catch(n){throw h(n)?new p(m(n)):n}if(!o)throw new Error(`session/load failed for session ${e.sessionId}`)}else try{await this.newSession(e)}catch(n){throw h(n)?new p(m(n)):n}e.initialMode&&await this.setLiveMode(e.initialMode).catch(()=>{}),e.initialModel&&await this.setModel(e.initialModel).catch(()=>{}),this.alive=!0}async initialize(){return await this.transport.call("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1},clientInfo:{name:"grix-connector-acp",version:"0.2.0"}})}buildMcpEntries(e){return(e??[]).map(t=>{const s={name:t.name,command:t.command};return t.args&&(s.args=t.args),t.env&&(s.env=Object.entries(t.env).map(([i,o])=>({name:i,value:o}))),s})}async newSession(e){const t=this.buildMcpEntries(e.mcpServers),s={cwd:e.cwd||process.cwd(),mcpServers:t};e.additionalDirectories&&e.additionalDirectories.length>0&&(s.additionalDirectories=e.additionalDirectories);const i=await this.transport.call("session/new",s);if(!i?.sessionId)throw new Error("session/new returned empty sessionId");this.acpSessionId=i.sessionId,this.absorbModes(i.modes),r.info("acp-client",`[handshake] session/new models block: ${JSON.stringify(i.models)}`),this.absorbModels(i.models),this.absorbConfigOptions(i.configOptions)}async loadSession(e,t){const s=this.buildMcpEntries(t.mcpServers),i={sessionId:e,cwd:t.cwd||process.cwd(),mcpServers:s};t.additionalDirectories&&t.additionalDirectories.length>0&&(i.additionalDirectories=t.additionalDirectories);const o=await this.transport.call("session/load",i);return o?.sessionId?(this.acpSessionId=o.sessionId,this.absorbModes(o.modes),r.info("acp-client",`[handshake] session/load models block: ${JSON.stringify(o.models)}`),this.absorbModels(o.models),this.absorbConfigOptions(o.configOptions),!0):o&&(o.modes||o.models||o.configOptions)?(this.acpSessionId=e,this.absorbModes(o.modes),r.info("acp-client",`[handshake] session/load (compat) models block: ${JSON.stringify(o.models)}`),this.absorbModels(o.models),this.absorbConfigOptions(o.configOptions),!0):(r.info("acp-client",`[handshake] session/load returned empty result for ${e}`),this.acpSessionId=e,!0)}absorbModes(e){e?.availableModes?.length&&(this.availableModes=[...e.availableModes],e.currentModeId&&(this.currentMode=e.currentModeId))}absorbConfigOptions(e){if(Array.isArray(e))for(const t of e){const s=t,i=Array.isArray(s?.options)?s.options.filter(n=>typeof n?.value=="string"&&n.value):[];if(i.length===0)continue;const o=s.category||s.id;o==="mode"&&this.availableModes.length===0?(this.availableModes=i.map(n=>({id:n.value,name:n.name||n.value,...n.description?{description:n.description}:{}})),typeof s.currentValue=="string"&&s.currentValue&&(this.currentMode=s.currentValue),r.info("acp-client",`[handshake] absorbConfigOptions modes: count=${this.availableModes.length} ids=${JSON.stringify(this.availableModes.map(n=>n.id))} current=${s.currentValue??"<none>"}`)):o==="model"&&this.availableModels.length===0&&(this.availableModels=i.map(n=>({modelId:n.value,name:n.name||n.value})),typeof s.currentValue=="string"&&s.currentValue&&(this.currentModel=s.currentValue),r.info("acp-client",`[handshake] absorbConfigOptions models: count=${this.availableModels.length} ids=${JSON.stringify(this.availableModels.map(n=>n.modelId))} current=${s.currentValue??"<none>"}`))}}absorbModels(e){if(!e?.availableModels?.length){r.info("acp-client","[handshake] absorbModels skipped: block empty");return}this.availableModels=[...e.availableModels],r.info("acp-client",`[handshake] absorbModels: count=${this.availableModels.length} ids=${JSON.stringify(this.availableModels.map(t=>t.modelId))} current=${e.currentModelId??"<none>"}`),e.currentModelId&&(this.currentModel=e.currentModelId)}async send(e,t,s){if(!this.alive)throw new Error("session not active");if(!this.acpSessionId)throw new Error("no agent session id");const i=[{type:"text",text:e}];if(t)for(const o of t)i.push({type:"image",data:o.data.toString("base64"),mimeType:o.mimeType});this.pendingToolCallIds.clear(),this.settleSuppressedSince=0,await this.transport.call("session/prompt",{sessionId:this.acpSessionId,prompt:i,...this.currentModel?{modelId:this.currentModel}:{}}),this.scheduleSettledResult()}scheduleSettledResult(){this.settleTimer&&clearTimeout(this.settleTimer),this.settleTimer=setTimeout(()=>{if(this.settleTimer=null,!!this.alive){if(this.pendingToolCallIds.size>0){if(this.settleSuppressedSince===0&&(this.settleSuppressedSince=Date.now()),Date.now()-this.settleSuppressedSince<u.SETTLE_SUPPRESS_MAX_MS){r.info("acp-client",`settle suppressed: ${this.pendingToolCallIds.size} pending tool call(s) for session ${this.acpSessionId}`),this.scheduleSettledResult();return}r.warn("acp-client",`settle suppression exceeded ${u.SETTLE_SUPPRESS_MAX_MS/6e4}min with ${this.pendingToolCallIds.size} pending tool call(s), emitting Result anyway`)}this.settleSuppressedSince=0,r.info("acp-client",`settle timer fired, emitting Result for session ${this.acpSessionId}`),this.emit("event",{type:c.Result,sessionId:this.acpSessionId,done:!0,synthetic:!0})}},u.SETTLE_MS),this.settleTimer.unref()}deferSettledResult(){this.scheduleSettledResult()}clearSettleTimer(){this.settleTimer&&(clearTimeout(this.settleTimer),this.settleTimer=null),this.settleSuppressedSince=0}async cancel(){if(this.acpSessionId){this.pendingToolCallIds.clear(),this.settleSuppressedSince=0;try{await this.transport.notify("session/cancel",{sessionId:this.acpSessionId})}catch{}}}async authenticate(e){await this.transport.call("authenticate",{methodId:e})}async respondPermission(e,t){const s=this.pendingPermissions.get(e);if(!s)throw new Error(`unknown permission request: ${e}`);this.pendingPermissions.delete(e);const i=this.pickPermissionOptionId(t.behavior,s.options),o=this.buildPermissionResult(t.behavior,i);await this.transport.respondSuccess(s.rpcId,o)}async respondPermissionOption(e,t){const s=this.pendingPermissions.get(e);if(!s)throw new Error(`unknown permission request: ${e}`);if(!s.options.some(i=>i.optionId===t))throw new Error(`unknown permission option: ${t}`);this.pendingPermissions.delete(e),await this.transport.respondSuccess(s.rpcId,{outcome:{outcome:"selected",optionId:t}})}async ping(e=1e4){if(!this.alive)return!1;try{const t=new AbortController,s=setTimeout(()=>t.abort(),e);return await this.transport.call("session/list",{},t.signal),clearTimeout(s),!0}catch(t){return t?.code===-32601}}async setLiveMode(e){if(!this.acpSessionId)return r.warn("acp-client",`setLiveMode("${e}") skipped: no active session`),!1;const t=this.matchAvailableMode(e);if(!t)return r.warn("acp-client",`setLiveMode("${e}") failed: mode not found in available [${this.availableModes.map(s=>s.id).join(",")}]`),!1;try{const s=new AbortController,i=setTimeout(()=>s.abort(),8e3);return await this.transport.call("session/set_mode",{sessionId:this.acpSessionId,modeId:t},s.signal),clearTimeout(i),this.currentMode=t,!0}catch(s){return r.warn("acp-client",`setLiveMode("${t}") RPC failed: ${s instanceof Error?s.message:s}`),!1}}async setModel(e){if(!this.acpSessionId)return r.warn("acp-client",`setModel("${e}") skipped: no active session`),!1;try{const t=new AbortController,s=setTimeout(()=>t.abort(),8e3);return await this.transport.call("session/set_model",{sessionId:this.acpSessionId,modelId:e},t.signal),clearTimeout(s),this.currentModel=e,!0}catch(t){return r.warn("acp-client",`setModel("${e}") RPC failed: ${t instanceof Error?t.message:t}`),!1}}handleSessionUpdate(e){this.emit("activity");const t=e?.update?.sessionUpdate,s=t==="usage_update";if(t==="tool_call"||t==="tool_call_update"){const o=e?.update??{},n=String(o.toolCallId??""),a=String(o.status??"").toLowerCase().trim();n&&(a==="completed"||a==="failed"?this.pendingToolCallIds.delete(n):t==="tool_call"&&this.pendingToolCallIds.add(n))}this.settleTimer&&!s&&this.scheduleSettledResult();const i=M(this.acpSessionId,e);for(const o of i)o.sessionId||(o.sessionId=this.acpSessionId),o.type===c.Result&&(this.pendingToolCallIds.clear(),this.settleTimer&&this.clearSettleTimer()),this.emit("event",o)}handleKiroMetadata(e){const s=e?.contextUsagePercentage;typeof s!="number"||!Number.isFinite(s)||this.emit("event",{type:c.ContextWindowUpdate,sessionId:this.acpSessionId,contextWindow:{usedPercentage:Math.min(100,Math.max(0,s))}})}handleCommandsAvailable(e){const t=e,s=Array.isArray(t?.commands)?t.commands:[];this._availableCommands=s.map(i=>({name:String(i.name??""),description:i.description?String(i.description):void 0,args:i.args?String(i.args):void 0})),this._supportsCommandsExecute=!0,this.emit("commandsAvailable",this._availableCommands)}async executeCommand(e,t){if(!this.alive)throw new Error("session not active");if(!this.acpSessionId)throw new Error("no agent session id");const s=new AbortController,i=setTimeout(()=>s.abort(),2e4);try{const o=await this.transport.call("_kiro.dev/commands/execute",{sessionId:this.acpSessionId,command:e,...t?{args:t}:{}},s.signal);return clearTimeout(i),{status:o?.status??"ok",message:o?.message,options:o?.options,data:o?.data}}catch(o){clearTimeout(i);const n=o instanceof Error?o.message:String(o);if(o?.code===-32601)throw this._supportsCommandsExecute=!1,o;return{status:"failed",message:n}}}handlePermissionRequest(e,t){const s=t,i=s?.toolCall??{},o=String(e),n=Array.isArray(s?.options)?s.options:[];this.pendingPermissions.set(o,{rpcId:e,options:n});const a=i.title||i.kind||"permission",d=i.toolCallId||o,f=i.title||"",w={type:c.PermissionRequest,requestId:o,toolName:a,toolInput:f||d,sessionId:this.acpSessionId,permissionRequest:{requestId:o,toolCallId:d,toolName:a,toolTitle:f,options:n,rawParams:t}};this.emit("event",w)}matchAvailableMode(e){const t=e.toLowerCase();for(const s of this.availableModes)if(s.id.toLowerCase()===t||s.name.toLowerCase()===t)return s.id;return""}pickPermissionOptionId(e,t){if(t.length===0)return"";if(e==="deny"){for(const i of t)if(i.kind==="reject_once"||i.kind==="reject_always")return i.optionId;for(const i of t)if(i.kind.toLowerCase().includes("reject")||i.kind.toLowerCase().includes("deny"))return i.optionId;for(const i of t)if(i.name.toLowerCase().includes("reject")||i.name.toLowerCase().includes("deny"))return i.optionId;return t[t.length-1].optionId}const s=e==="allow-always"?"allow_always":e==="allow-once"?"allow_once":null;if(s){for(const i of t)if(i.kind===s)return i.optionId}for(const i of t)if(i.kind.toLowerCase().includes("allow"))return i.optionId;for(const i of t)if(i.name.toLowerCase().includes("allow"))return i.optionId;return t[0].optionId}buildPermissionResult(e,t){return e==="deny"?t?{outcome:{outcome:"selected",optionId:t}}:{outcome:{outcome:"cancelled"}}:t?{outcome:{outcome:"selected",optionId:t}}:{outcome:{outcome:"cancelled"}}}}export{p as AcpAuthRequiredError,u as AcpClient,h as isAuthRequiredError};
1
+ import{EventEmitter as S}from"events";import{log as r}from"../core/log/index.js";import{AgentEventType as c}from"../types/events.js";import{mapSessionUpdate as b}from"./event-mapper.js";class p extends Error{authMethods;constructor(e){super("ACP authentication required"),this.name="AcpAuthRequiredError",this.authMethods=e}}function h(l){if(!l||typeof l!="object")return!1;const e=l;return e.code===-32e3?!0:e.code===-32603&&e.data?.details?/\b(401|token\s*expired|access\s*token)\b/i.test(e.data.details):!1}function m(l){return!l||typeof l!="object"?[{id:"oauth"}]:l.data?.authMethods??[{id:"oauth"}]}class u extends S{acpSessionId="";alive=!1;pendingPermissions=new Map;availableModes=[];availableModels=[];currentMode="";currentModel="";listSupported=!1;loadSupported=!1;_supportsCommandsExecute=!1;_availableCommands=[];transport;settleTimer=null;static SETTLE_MS=3e3;pendingToolCallIds=new Set;settleSuppressedSince=0;static SETTLE_SUPPRESS_MAX_MS=900*1e3;constructor(){super(),this.transport=null}get sessionId(){return this.acpSessionId}get isAlive(){return this.alive}get modes(){return[...this.availableModes]}get mode(){return this.currentMode}get models(){return[...this.availableModels]}get model(){return this.currentModel}get sessionOptions(){return{modes:[...this.availableModes],currentModeId:this.currentMode,models:[...this.availableModels],currentModelId:this.currentModel}}get supportsCommandsExecute(){return this._supportsCommandsExecute}get availableCommands(){return[...this._availableCommands]}async connect(e){this.transport=e.transport,this.transport.on("close",()=>{this.alive&&(this.alive=!1,this.emit("session-lost"))}),this.transport.setHandlers((n,a)=>{n==="session/update"?this.handleSessionUpdate(a):n==="_kiro.dev/metadata"?this.handleKiroMetadata(a):n==="_kiro.dev/commands/available"?this.handleCommandsAvailable(a):n==="_kiro.dev/compaction/status"?this.emit("compactionStatus",a):n==="_kiro.dev/clear/status"&&this.emit("clearStatus",a)},(n,a,d)=>{n==="session/request_permission"?this.handlePermissionRequest(a,d):n.startsWith("cursor/")?this.transport.respondSuccess(a,{}).catch(()=>{}):this.transport.respondError(a,-32601,"method not implemented").catch(()=>{})});const t=await this.initialize();this.listSupported=!!t.agentCapabilities?.sessionCapabilities?.list,this.loadSupported=!!t.agentCapabilities?.loadSession;const s=t.agentCapabilities??{};if(this._supportsCommandsExecute=!!(s.extensions?.["_kiro.dev/commands"]||s.commands),e.authMethod)try{await this.transport.call("authenticate",{methodId:e.authMethod})}catch(n){throw h(n)?new p(m(n)):n}const i=e.sessionId&&e.sessionId!=="__continue__";let o=!1;if(i){if(!this.loadSupported)throw new Error(`session/load not supported by agent, cannot resume session ${e.sessionId}`);try{await this.loadSession(e.sessionId,e)&&(o=!0)}catch(n){throw h(n)?new p(m(n)):n}if(!o)throw new Error(`session/load failed for session ${e.sessionId}`)}else try{await this.newSession(e)}catch(n){throw h(n)?new p(m(n)):n}e.initialMode&&await this.setLiveMode(e.initialMode).catch(()=>{}),e.initialModel&&await this.setModel(e.initialModel).catch(()=>{}),this.alive=!0}async initialize(){return await this.transport.call("initialize",{protocolVersion:1,clientCapabilities:{fs:{readTextFile:!1,writeTextFile:!1},terminal:!1},clientInfo:{name:"grix-connector-acp",version:"0.2.0"}})}buildMcpEntries(e){return(e??[]).map(t=>{if("type"in t){const i={type:t.type,name:t.name,url:t.url};return t.headers&&(i.headers=Object.entries(t.headers).map(([o,n])=>({name:o,value:n}))),i}const s={name:t.name,command:t.command};return t.args&&(s.args=t.args),t.env&&(s.env=Object.entries(t.env).map(([i,o])=>({name:i,value:o}))),s})}async newSession(e){const t=this.buildMcpEntries(e.mcpServers),s={cwd:e.cwd||process.cwd(),mcpServers:t};e.additionalDirectories&&e.additionalDirectories.length>0&&(s.additionalDirectories=e.additionalDirectories);const i=await this.transport.call("session/new",s);if(!i?.sessionId)throw new Error("session/new returned empty sessionId");this.acpSessionId=i.sessionId,this.absorbModes(i.modes),r.info("acp-client",`[handshake] session/new models block: ${JSON.stringify(i.models)}`),this.absorbModels(i.models),this.absorbConfigOptions(i.configOptions)}async loadSession(e,t){const s=this.buildMcpEntries(t.mcpServers),i={sessionId:e,cwd:t.cwd||process.cwd(),mcpServers:s};t.additionalDirectories&&t.additionalDirectories.length>0&&(i.additionalDirectories=t.additionalDirectories);const o=await this.transport.call("session/load",i);return o?.sessionId?(this.acpSessionId=o.sessionId,this.absorbModes(o.modes),r.info("acp-client",`[handshake] session/load models block: ${JSON.stringify(o.models)}`),this.absorbModels(o.models),this.absorbConfigOptions(o.configOptions),!0):o&&(o.modes||o.models||o.configOptions)?(this.acpSessionId=e,this.absorbModes(o.modes),r.info("acp-client",`[handshake] session/load (compat) models block: ${JSON.stringify(o.models)}`),this.absorbModels(o.models),this.absorbConfigOptions(o.configOptions),!0):(r.info("acp-client",`[handshake] session/load returned empty result for ${e}`),this.acpSessionId=e,!0)}absorbModes(e){e?.availableModes?.length&&(this.availableModes=[...e.availableModes],e.currentModeId&&(this.currentMode=e.currentModeId))}absorbConfigOptions(e){if(Array.isArray(e))for(const t of e){const s=t,i=Array.isArray(s?.options)?s.options.filter(n=>typeof n?.value=="string"&&n.value):[];if(i.length===0)continue;const o=s.category||s.id;o==="mode"&&this.availableModes.length===0?(this.availableModes=i.map(n=>({id:n.value,name:n.name||n.value,...n.description?{description:n.description}:{}})),typeof s.currentValue=="string"&&s.currentValue&&(this.currentMode=s.currentValue),r.info("acp-client",`[handshake] absorbConfigOptions modes: count=${this.availableModes.length} ids=${JSON.stringify(this.availableModes.map(n=>n.id))} current=${s.currentValue??"<none>"}`)):o==="model"&&this.availableModels.length===0&&(this.availableModels=i.map(n=>({modelId:n.value,name:n.name||n.value})),typeof s.currentValue=="string"&&s.currentValue&&(this.currentModel=s.currentValue),r.info("acp-client",`[handshake] absorbConfigOptions models: count=${this.availableModels.length} ids=${JSON.stringify(this.availableModels.map(n=>n.modelId))} current=${s.currentValue??"<none>"}`))}}absorbModels(e){if(!e?.availableModels?.length){r.info("acp-client","[handshake] absorbModels skipped: block empty");return}this.availableModels=[...e.availableModels],r.info("acp-client",`[handshake] absorbModels: count=${this.availableModels.length} ids=${JSON.stringify(this.availableModels.map(t=>t.modelId))} current=${e.currentModelId??"<none>"}`),e.currentModelId&&(this.currentModel=e.currentModelId)}async send(e,t,s){if(!this.alive)throw new Error("session not active");if(!this.acpSessionId)throw new Error("no agent session id");const i=[{type:"text",text:e}];if(t)for(const o of t)i.push({type:"image",data:o.data.toString("base64"),mimeType:o.mimeType});this.pendingToolCallIds.clear(),this.settleSuppressedSince=0,await this.transport.call("session/prompt",{sessionId:this.acpSessionId,prompt:i,...this.currentModel?{modelId:this.currentModel}:{}}),this.scheduleSettledResult()}scheduleSettledResult(){this.settleTimer&&clearTimeout(this.settleTimer),this.settleTimer=setTimeout(()=>{if(this.settleTimer=null,!!this.alive){if(this.pendingToolCallIds.size>0){if(this.settleSuppressedSince===0&&(this.settleSuppressedSince=Date.now()),Date.now()-this.settleSuppressedSince<u.SETTLE_SUPPRESS_MAX_MS){r.info("acp-client",`settle suppressed: ${this.pendingToolCallIds.size} pending tool call(s) for session ${this.acpSessionId}`),this.scheduleSettledResult();return}r.warn("acp-client",`settle suppression exceeded ${u.SETTLE_SUPPRESS_MAX_MS/6e4}min with ${this.pendingToolCallIds.size} pending tool call(s), emitting Result anyway`)}this.settleSuppressedSince=0,r.info("acp-client",`settle timer fired, emitting Result for session ${this.acpSessionId}`),this.emit("event",{type:c.Result,sessionId:this.acpSessionId,done:!0,synthetic:!0})}},u.SETTLE_MS),this.settleTimer.unref()}deferSettledResult(){this.scheduleSettledResult()}clearSettleTimer(){this.settleTimer&&(clearTimeout(this.settleTimer),this.settleTimer=null),this.settleSuppressedSince=0}async cancel(){if(this.acpSessionId){this.pendingToolCallIds.clear(),this.settleSuppressedSince=0;try{await this.transport.notify("session/cancel",{sessionId:this.acpSessionId})}catch{}}}async authenticate(e){await this.transport.call("authenticate",{methodId:e})}async respondPermission(e,t){const s=this.pendingPermissions.get(e);if(!s)throw new Error(`unknown permission request: ${e}`);this.pendingPermissions.delete(e);const i=this.pickPermissionOptionId(t.behavior,s.options),o=this.buildPermissionResult(t.behavior,i);await this.transport.respondSuccess(s.rpcId,o)}async respondPermissionOption(e,t){const s=this.pendingPermissions.get(e);if(!s)throw new Error(`unknown permission request: ${e}`);if(!s.options.some(i=>i.optionId===t))throw new Error(`unknown permission option: ${t}`);this.pendingPermissions.delete(e),await this.transport.respondSuccess(s.rpcId,{outcome:{outcome:"selected",optionId:t}})}async ping(e=1e4){if(!this.alive)return!1;try{const t=new AbortController,s=setTimeout(()=>t.abort(),e);return await this.transport.call("session/list",{},t.signal),clearTimeout(s),!0}catch(t){return t?.code===-32601}}async setLiveMode(e){if(!this.acpSessionId)return r.warn("acp-client",`setLiveMode("${e}") skipped: no active session`),!1;const t=this.matchAvailableMode(e);if(!t)return r.warn("acp-client",`setLiveMode("${e}") failed: mode not found in available [${this.availableModes.map(s=>s.id).join(",")}]`),!1;try{const s=new AbortController,i=setTimeout(()=>s.abort(),8e3);return await this.transport.call("session/set_mode",{sessionId:this.acpSessionId,modeId:t},s.signal),clearTimeout(i),this.currentMode=t,!0}catch(s){return r.warn("acp-client",`setLiveMode("${t}") RPC failed: ${s instanceof Error?s.message:s}`),!1}}async setModel(e){if(!this.acpSessionId)return r.warn("acp-client",`setModel("${e}") skipped: no active session`),!1;try{const t=new AbortController,s=setTimeout(()=>t.abort(),8e3);return await this.transport.call("session/set_model",{sessionId:this.acpSessionId,modelId:e},t.signal),clearTimeout(s),this.currentModel=e,!0}catch(t){return r.warn("acp-client",`setModel("${e}") RPC failed: ${t instanceof Error?t.message:t}`),!1}}handleSessionUpdate(e){this.emit("activity");const t=e?.update?.sessionUpdate,s=t==="usage_update";if(t==="tool_call"||t==="tool_call_update"){const o=e?.update??{},n=String(o.toolCallId??""),a=String(o.status??"").toLowerCase().trim();n&&(a==="completed"||a==="failed"?this.pendingToolCallIds.delete(n):t==="tool_call"&&this.pendingToolCallIds.add(n))}this.settleTimer&&!s&&this.scheduleSettledResult();const i=b(this.acpSessionId,e);for(const o of i)o.sessionId||(o.sessionId=this.acpSessionId),o.type===c.Result&&(this.pendingToolCallIds.clear(),this.settleTimer&&this.clearSettleTimer()),this.emit("event",o)}handleKiroMetadata(e){const s=e?.contextUsagePercentage;typeof s!="number"||!Number.isFinite(s)||this.emit("event",{type:c.ContextWindowUpdate,sessionId:this.acpSessionId,contextWindow:{usedPercentage:Math.min(100,Math.max(0,s))}})}handleCommandsAvailable(e){const t=e,s=Array.isArray(t?.commands)?t.commands:[];this._availableCommands=s.map(i=>({name:String(i.name??""),description:i.description?String(i.description):void 0,args:i.args?String(i.args):void 0})),this._supportsCommandsExecute=!0,this.emit("commandsAvailable",this._availableCommands)}async executeCommand(e,t){if(!this.alive)throw new Error("session not active");if(!this.acpSessionId)throw new Error("no agent session id");const s=new AbortController,i=setTimeout(()=>s.abort(),2e4);try{const o=await this.transport.call("_kiro.dev/commands/execute",{sessionId:this.acpSessionId,command:e,...t?{args:t}:{}},s.signal);return clearTimeout(i),{status:o?.status??"ok",message:o?.message,options:o?.options,data:o?.data}}catch(o){clearTimeout(i);const n=o instanceof Error?o.message:String(o);if(o?.code===-32601)throw this._supportsCommandsExecute=!1,o;return{status:"failed",message:n}}}handlePermissionRequest(e,t){const s=t,i=s?.toolCall??{},o=String(e),n=Array.isArray(s?.options)?s.options:[];this.pendingPermissions.set(o,{rpcId:e,options:n});const a=i.title||i.kind||"permission",d=i.toolCallId||o,f=i.title||"",w={type:c.PermissionRequest,requestId:o,toolName:a,toolInput:f||d,sessionId:this.acpSessionId,permissionRequest:{requestId:o,toolCallId:d,toolName:a,toolTitle:f,options:n,rawParams:t}};this.emit("event",w)}matchAvailableMode(e){const t=e.toLowerCase();for(const s of this.availableModes)if(s.id.toLowerCase()===t||s.name.toLowerCase()===t)return s.id;return""}pickPermissionOptionId(e,t){if(t.length===0)return"";if(e==="deny"){for(const i of t)if(i.kind==="reject_once"||i.kind==="reject_always")return i.optionId;for(const i of t)if(i.kind.toLowerCase().includes("reject")||i.kind.toLowerCase().includes("deny"))return i.optionId;for(const i of t)if(i.name.toLowerCase().includes("reject")||i.name.toLowerCase().includes("deny"))return i.optionId;return t[t.length-1].optionId}const s=e==="allow-always"?"allow_always":e==="allow-once"?"allow_once":null;if(s){for(const i of t)if(i.kind===s)return i.optionId}for(const i of t)if(i.kind.toLowerCase().includes("allow"))return i.optionId;for(const i of t)if(i.name.toLowerCase().includes("allow"))return i.optionId;return t[0].optionId}buildPermissionResult(e,t){return e==="deny"?t?{outcome:{outcome:"selected",optionId:t}}:{outcome:{outcome:"cancelled"}}:t?{outcome:{outcome:"selected",optionId:t}}:{outcome:{outcome:"cancelled"}}}}export{p as AcpAuthRequiredError,u as AcpClient,h as isAuthRequiredError};
@@ -6445,11 +6445,11 @@ function buildSessionContextBlock(sessionId, agentId) {
6445
6445
  }
6446
6446
 
6447
6447
  // src/openclaw/session-context-inject.ts
6448
- function maybePrependSessionContext(params) {
6448
+ function maybeAppendSessionContext(params) {
6449
6449
  if (params.previousTimestamp) return params.body;
6450
- return `${buildSessionContextBlock(params.sessionId, params.agentId)}
6450
+ return `${params.body}
6451
6451
 
6452
- ${params.body}`;
6452
+ ${buildSessionContextBlock(params.sessionId, params.agentId)}`;
6453
6453
  }
6454
6454
 
6455
6455
  // src/openclaw/outbound-text-delivery-plan.ts
@@ -8950,7 +8950,7 @@ async function processEvent(params) {
8950
8950
  storePath,
8951
8951
  sessionKey: route.sessionKey
8952
8952
  });
8953
- const bodyForAgentWithContext = maybePrependSessionContext({
8953
+ const bodyForAgentWithContext = maybeAppendSessionContext({
8954
8954
  previousTimestamp,
8955
8955
  sessionId,
8956
8956
  agentId: account.agentId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grix-connector",
3
- "version": "3.34.0",
3
+ "version": "4.0.0",
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",
@@ -121,8 +121,7 @@ if [ $CRASH_COUNT -ge $MAX_CRASHES ]; then
121
121
  ROLLBACK_CLI="$NPM_ROOT/$NPM_PACKAGE/dist/grix.js"
122
122
  if [ -n "$NPM_ROOT" ] && [ -f "$ROLLBACK_CLI" ] \
123
123
  && node "$ROLLBACK_CLI" start >> "$LOG_FILE" 2>&1; then
124
- rm -f "$PENDING_FILE"
125
- log "atomic rollback succeeded and managed runtime activated"
124
+ log "atomic rollback succeeded and managed runtime activated; leaving pending for rollback report"
126
125
  exit 0
127
126
  fi
128
127
  # 激活失败不退出:落到下面的 npm 回滚路径再试一次(npm install 旧版幂等)。
@@ -147,8 +146,7 @@ if [ $CRASH_COUNT -ge $MAX_CRASHES ]; then
147
146
  ROLLBACK_CLI="$NPM_ROOT/$NPM_PACKAGE/dist/grix.js"
148
147
  if [ -n "$NPM_ROOT" ] && [ -f "$ROLLBACK_CLI" ] \
149
148
  && node "$ROLLBACK_CLI" start >> "$LOG_FILE" 2>&1; then
150
- log "rollback succeeded and managed runtime activated"
151
- rm -f "$PENDING_FILE"
149
+ log "rollback succeeded and managed runtime activated; leaving pending for rollback report"
152
150
  else
153
151
  log "ROLLBACK ACTIVATION FAILED — package restored but managed runtime was not switched"
154
152
  fi