grix-connector 3.26.0 → 3.26.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bridge/bridge.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import P from"node:path";import{realpath as Se,stat as F}from"node:fs/promises";import{tmpdir as be,homedir as N}from"node:os";import{createHash as we,randomUUID as W}from"node:crypto";import{ConnectionManager as Ce}from"../core/aibot/index.js";import{AUDIT_LOCAL_ACTION_TYPES as Ae}from"../core/aibot/types.js";import{ClaudeAdapter as M}from"../adapter/claude/index.js";import{CodexAdapter as te}from"../adapter/codex/index.js";import{PiAdapter as ke}from"../adapter/pi/index.js";import{AcpAdapter as E}from"../adapter/acp/index.js";import{OpenHumanAdapter as Re}from"../adapter/openhuman/index.js";import{CursorAdapter as B,CURSOR_MODE_OPTIONS as Ee,fetchCursorAccountUsage as ye}from"../adapter/cursor/index.js";import{CodeWhaleAdapter as ie}from"../adapter/codewhale/index.js";import{OpenCodeAdapter as K}from"../adapter/opencode/index.js";import{AgyAdapter as ne}from"../adapter/agy/index.js";import{getCachedAgyModels as $e}from"../adapter/agy/model-list.js";import{getCachedAgyQuotaInfo as Te,primeAgyQuotaInfo as Pe}from"../adapter/agy/quota.js";import{LOCAL_ACTION_ERROR_CODES as x,LOCAL_ACTION_TYPES as C,SESSION_CONTROL_ERROR_CODES as p,SESSION_CONTROL_VERBS as _,SESSION_MODE_IDS as R}from"../adapter/claude/protocol-contract.js";import{AuditLocalActionHandler as xe,isAuditLocalActionType as Le}from"../audit/query/audit-local-actions.js";import{AuditReplayQueryService as He}from"../audit/query/audit-replay-query-service.js";import{resolveDefaultAuditStorageRootDir as Me}from"../audit/runtime/default-audit-recorder.js";import{FileAuditReplayStore as Ie}from"../audit/storage/file-audit-replay-store.js";import{shouldEnableRawApiCapture as se}from"../audit/raw-capture/types.js";import{parseClaudeSessionUsage as De}from"../adapter/claude/usage-parser.js";import"../adapter/claude/session-history.js";import"../adapter/codex/session-history.js";import"../adapter/pi/session-history.js";import"../adapter/opencode/session-history.js";import"../adapter/codewhale/session-history.js";import{SESSION_HISTORY_ERROR_CODES as q,SessionHistoryError as Qe,getSessionHistoryReader as Be}from"../adapter/shared/session-history.js";import{resolveSkillScanMode as I}from"../adapter/claude/skill-scanner.js";import{buildReportedSkills as qe}from"../default-skills/index.js";import{buildWireSkills as oe}from"../core/skill-sync/sync-state.js";import{uploadSkill as Oe}from"../core/skill-sync/skill-upload.js";import{buildLibrarySkills as Ue}from"../core/skill-sync/library-skills.js";import{enableSkill as Fe,disableSkill as Ne,SkillEnableError as re}from"../core/skill-sync/skill-enable.js";import{fetchAvailableModels as G,getCachedModels as z,readSettingsEnv as We}from"../adapter/claude/model-list.js";import{parseAcpSessionUsage as Ke}from"../adapter/acp/usage-parser.js";import{applyProxyEnv as Ge,getProxyManager as ae,relayHostsForClientType as ze,buildGatewayRoute as je}from"../core/proxy/index.js";import{getKiroRelaySpawnEnv as Ve}from"../core/proxy/kiro-relay.js";import{supportsKiroCwRelay as de}from"../core/config/provider-env.js";import{RelayFetchError as Ye,deriveGatewayBaseUrls as ce,fetchRelayCredentialWithRetry as Je}from"../core/proxy/relay-credential-fetch.js";import{RelayStateSyncer as Xe}from"../core/proxy/relay-state-sync.js";import{isDirectRelayExplicitlyUnsupported as Ze}from"../core/proxy/relay-credential.js";import{buildProviderEnv as et}from"../core/config/provider-env.js";import{CLAUDE_EFFORT_LEVELS as D,isClaudeDirectEnv as tt,normalizeClaudeEffort as j,stripGrixRelayEnv as it}from"../core/config/claude-direct-env.js";import{sweepNativeProviderResidue as nt}from"./native-provider-sweep.js";import{parseCodexSessionUsage as st}from"../adapter/codex/usage-parser.js";import{readCodexProviderSettings as ot}from"../adapter/codex/codex-trust.js";import{resolveKimiProviderSettings as rt}from"../core/provider-quota/index.js";import{scanCodexSessions as at}from"../adapter/codex/session-scanner.js";import{scanClaudeSessions as dt}from"../adapter/claude/session-scanner.js";import{reasonixStampFromSessionId as ct,resolveAcpAgentTypes as lt,scanAcpSessions as ut,scanReasonixSessionTitles as ht}from"../adapter/acp/session-scanner.js";import{scanPiSessions as gt}from"../adapter/pi/session-scanner.js";import{scanCodeWhaleSessions as pt}from"../adapter/codewhale/session-scanner.js";import{scanCursorSessions as ft}from"../adapter/cursor/session-scanner.js";import{scanOpenCodeSessions as mt}from"../adapter/opencode/session-scanner.js";import{parsePiSessionUsage as _t}from"../adapter/pi/usage-parser.js";import{SessionScanCache as y,resolveCodexLeafDirs as vt,resolveClaudeLeafDirs as St,resolveAcpLeafDirs as bt,resolvePiLeafDirs as wt,resolveCodeWhaleLeafDirs as Ct,resolveCursorLeafDirs as At,resolveOpenCodeLeafDirs as kt,resolveReasonixLeafDirs as Rt}from"./session-scan-cache.js";import{log as h,ConversationLog as Et,AgentApiPacketLog as yt,BridgeEventLog as $t,GRIX_PATHS as T}from"../core/log/index.js";import{RevokeHandler as Tt}from"./revoke-handler.js";import{AdapterPool as Pt,PoolFullError as xt}from"./adapter-pool.js";import{parseSessionControlCommand as Lt,handleSessionControlCommand as le,handleSessionControlLocalAction as Ht}from"./session-controller.js";import{isOpenSessionDirectiveMessage as Mt,parseAgentQuestionReplyMessage as It}from"../core/protocol/interaction-parser.js";import{handleAcpSetModel as ue,handleAcpSetMode as he,resolveAcpInitialDefaults as Dt}from"./acp-toolbar-persist.js";import{SessionBindingStore as Qt}from"../core/persistence/session-binding-store.js";import{handleFileListAction as Bt,handleCreateFolderAction as qt,serveLocalFile as Ot,realHomeDir as ge}from"../core/files/index.js";import{getMachineName as Ut}from"../core/util/index.js";import{uploadReplyFileToAgentMedia as Ft}from"../core/protocol/agent-api-media.js";import{ActiveEventStore as Nt}from"../core/persistence/active-event-store.js";import{DEFAULT_CONNECTOR_RUNTIME_CONFIG as Wt,applyConnectorRuntimeConfigPatch as Kt,extractConnectorRuntimeConfigPatch as Gt}from"./runtime-config.js";import{DEFAULT_QUOTE_TRIGGER_BY_ADAPTER as zt,SendController as jt}from"./send-controller.js";import{sharedProviderQuotaService as V,detectProviderFromModel as Q,normalizeProviderId as Y,readPiProviderSettings as Vt,readOpenCodeProviderSettings as Yt,providerQuotaToRateLimits as pe,providerQuotaToCodexRateLimits as fe,isOpenaiCodexProviderName as Jt,queryOpenaiCodexQuota as me,openaiCodexQuotaCacheKey as Xt}from"../core/provider-quota/index.js";import{queryKiroQuota as J}from"../core/provider-quota/kiro.js";import{buildToolUseCard as L,buildToolResultCard as H,buildLocalGrixCardLink as _e}from"./tool-card-utils.js";import{planRawEventDelivery as Zt,buildAuxiliaryDetailText as ei}from"./raw-event-delivery.js";import{DeferredEventManager as ti}from"./deferred-events.js";import{buildAgentProbeResult as ii,PROBE_CACHE_TTL_STATIC_MS as ni,PROBE_CACHE_TTL_FULL_MS as si}from"./probe-helper.js";import{BridgeAuditController as oi}from"../audit/integration/bridge-audit-controller.js";import{isSettlableAuditProducer as ri,NoopAuditProducer as ai}from"../audit/producer/audit-producer.js";import{buildAuditStatePayload as X,AuditPreparationFailedError as di,AuditTurnScopeUnsupportedError as ci}from"../audit/core/audit-state.js";import{extractAuditOptions as li,stripAuditOptions as ui}from"../audit/core/audit-options.js";const hi=600*1e3,gi=60*1e3,ve=30*1e3,pi=10*1e3,fi=10*1e3;function mi(ee,e,n){const t=we("sha256").update(ee).update("\0").update(e).update("\0").update(n).digest("hex");return P.join(T.data,"audit-source","reasonix",`${t}.jsonl`)}const O=new Set(["claude","acp","agy","cursor","codex"]),_i=new Set(["claude","codex","cursor","codewhale","opencode","pi","openhuman","agy","acp"]),Z=3;class zn{config;name;aibotHandle;aibotConfig;pool;stopped=!1;revokeHandler=new Tt;sessionBindings=new Map;deferredMgr;sendCtrl=new jt(Wt);bindingStore;globalConfigStore;upgradeTrigger=null;agentDeletedHandler=null;shareSetHandler=null;skillSyncHandler=null;providerConfigHandler=null;relayStateApplyPorts=null;relayStateSyncer=null;agentProfile={agentName:"",introduction:""};activeEventStore;cachedRateLimits=null;cachedRateLimitsSampledAtMs=null;cachedCodexContextWindow=null;cachedCodexTokenUsage=null;cachedCodexUsageSampledAtMs=null;cachedAcpContextWindow=null;cachedAcpContextWindowSampledAtMs=null;cachedClaudeRateLimitState=null;cachedProviderQuota=null;cachedProviderQuotaSampledAtMs=null;cachedProviderQuotaKey=null;sessionProviderHints=new Map;sessionProviderQuotas=new Map;sessionProviderMeta=new Map;lastModelSwitchQuotaRefresh=null;claudeWorkerStatus=new Map;lastReportedSkillsHash="";conversationLog=null;packetLog=null;providerQuotaTimer=null;eventSessionIndex=new Map;inflightEvents=new Map;restartCount=new Map;selfDrivenSessions=new Set;selfDrivenLabels=new Map;probeCache=new Map;auditController;auditLocalActionHandler=null;sessionScanCache;reasonixTitleScan=null;isRateLimitsCacheFresh(e){if(!Number.isFinite(e))return!1;const n=Number(e);return n>0&&Date.now()-n<=gi}async resolveProviderQuotaSource(e){if(e?.disabled||e?.providerId==="openai_codex")return null;let n=this.config.providerBaseUrl??this.config.agent.provider?.baseUrl,t=this.config.providerApiKey??this.config.agent.provider?.apiKey;if((!n||!t)&&(this.config.adapterType??"acp")==="claude"){const i=We();n||(n=(i.ANTHROPIC_BASE_URL??"").trim()||void 0),t||(t=(i.ANTHROPIC_API_KEY??"").trim()||(i.ANTHROPIC_AUTH_TOKEN??"").trim()||void 0)}if((!n||!t)&&(this.config.adapterType??"acp")==="codex"){const i=ot();!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if((!n||!t)&&(this.config.adapterType??"acp")==="acp"&&this.config.aibot.clientType==="kimi"){const i=await rt();!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if((!n||!t)&&(this.config.adapterType??"acp")==="pi"){const i=Vt(e);!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if((!n||!t)&&(this.config.adapterType??"acp")==="opencode"){const i=Yt(e);!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if(!n||!t)return null;const o=Y(e?.providerId)??Q(e?.model)?.id??Y(this.config.agent.provider?.providerId)??Q(this.config.agent.provider?.model)?.id;return{baseUrl:n,...this.config.agent.provider?.quotaBaseUrl?{quotaBaseUrl:this.config.agent.provider.quotaBaseUrl}:{},apiKey:t,...o?{providerId:o}:{}}}async maybeQueryProviderQuota(e=!1,n){if(this.config.aibot.clientType==="kiro"){if(!e&&this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota)return this.cachedProviderQuota;try{const i=await J();return this.cachedProviderQuota=i,this.cachedProviderQuotaSampledAtMs=Date.now(),this.cachedProviderQuotaKey="kiro",h.info(this.name,`[provider-quota] kiro queried: success=${i.success}`+(i.balance?` balance=${i.balance.remaining} ${i.balance.unit}`:"")+(i.planName?` plan=${i.planName}`:"")+(i.error?` error=${i.error}`:"")),i}catch(i){return h.warn(this.name,`[provider-quota] kiro query failed: ${i instanceof Error?i.message:String(i)}`),null}}if(n?.providerId==="openai_codex")try{const i=await me();return i.quota.success?i.quota:null}catch(i){return h.warn(this.name,`[provider-quota] openai_codex query failed: ${i instanceof Error?i.message:String(i)}`),null}const t=await this.resolveProviderQuotaSource(n);if(!t)return null;const o=V.cacheKey(t);if(!e&&this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota&&this.cachedProviderQuotaKey===o)return this.cachedProviderQuota;try{const i=await V.query(t,{fresh:e}),s=i.quota;return this.cachedProviderQuota=s,this.cachedProviderQuotaSampledAtMs=i.sampledAt,this.cachedProviderQuotaKey=i.cacheKey,h.info(this.name,`[provider-quota] queried: provider=${s.provider} success=${s.success}`+(s.tiers.length>0?` tiers=${s.tiers.map(r=>`${r.name}=${r.usedPercent}%`).join(",")}`:"")+(s.balance?` balance=${s.balance.remaining} ${s.balance.unit}`:"")+(s.error?` error=${s.error}`:"")),s}catch(i){return h.warn(this.name,`[provider-quota] query failed: ${i instanceof Error?i.message:String(i)}`),null}}startProviderQuotaTimer(){this.providerQuotaTimer||(this.providerQuotaTimer=setInterval(()=>{if(this.stopped){this.stopProviderQuotaTimer();return}this.refreshAndPushProviderQuota().catch(()=>{})},ve),h.info(this.name,`[provider-quota-timer] started (interval=${ve}ms)`))}stopProviderQuotaTimer(){this.providerQuotaTimer&&(clearInterval(this.providerQuotaTimer),this.providerQuotaTimer=null,h.info(this.name,"[provider-quota-timer] stopped"))}async refreshAndPushProviderQuota(e=!1){try{let n;if(this.config.aibot.clientType==="kiro")n=await J(),this.cachedProviderQuota=n,this.cachedProviderQuotaSampledAtMs=Date.now(),this.cachedProviderQuotaKey="kiro";else{const t=[...this.sessionBindings.entries()].filter(([o,i])=>!!i&&this.sessionProviderHints.has(o));if(t.length>0&&await Promise.all(t.map(([o,i])=>this.refreshProviderQuotaForSession(o,i,e))),t.length===this.sessionBindings.size&&t.length>0)return;n=await this.maybeQueryProviderQuota(e)}n?.success&&this.pushProviderQuotaToBindings(n,this.sessionProviderHints.size>0)}catch{}}pushProviderQuotaToBindings(e,n=!1){const t=this.providerQuotaToRateLimits(e);for(const[o,i]of this.sessionBindings.entries()){if(!i||n&&this.sessionProviderHints.has(o))continue;const s={provider_quota:e};t&&(s.rate_limits=t);const r=this.cachedAcpContextWindow;r&&("usedPercentage"in r?s.context_window={usedPercentage:r.usedPercentage,remainingPercentage:100-r.usedPercentage}:s.context_window=r),this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:"ready",cwd:i,meta:s})}}enrichProviderQuotaMeta(e,n="standard",t=this.cachedProviderQuota,o=this.cachedProviderQuotaSampledAtMs){const i={...e??{}};if(!t?.success||(i.provider_quota||(i.provider_quota=t),i.rate_limits))return i;if(n==="codex"){const r=fe(t,o??Date.now());return r&&(i.rate_limits=r.rateLimits,i.rate_limit_primary_percent=r.primaryPercent,i.rate_limit_secondary_percent=r.secondaryPercent,i.rate_limit_primary_window_min=r.primaryWindowMin,i.rate_limit_secondary_window_min=r.secondaryWindowMin),i}const s=pe(t,o??Date.now());return s&&(i.rate_limits=s),i}providerQuotaMetaPayload(e,n,t){let o=this.cachedProviderQuota,i=this.cachedProviderQuotaSampledAtMs,s=!1;if(n){const a=typeof e?.model_provider=="string"?e.model_provider:void 0,d=typeof e?.model_id=="string"?e.model_id:void 0,c=Y(a)??Q(a)?.id??Q(d)?.id??(Jt(a)?"openai_codex":null);if(c||d){const u=a?.trim().toLowerCase()??"",g=u==="grix"||u.startsWith("grix-"),f={...c?{providerId:c}:{},...d?{model:d}:{},...!!a&&!g&&!c?{disabled:!0}:{}},S=this.sessionProviderHints.get(n),m=S?.providerId!==f.providerId||S?.model!==f.model||S?.disabled!==f.disabled;this.sessionProviderHints.set(n,f),this.sessionProviderMeta.set(n,{...e??{}}),m&&this.sessionProviderQuotas.delete(n),(m||!this.sessionProviderQuotas.has(n))&&t&&this.refreshProviderQuotaForSession(n,t,!1)}const l=this.sessionProviderQuotas.get(n);s=this.sessionProviderHints.has(n),s&&(o=null,i=null),l&&(o=l.quota,i=l.sampledAt)}const r=this.enrichProviderQuotaMeta(e,"standard",o,i);return s&&(r.provider_quota||(r.provider_quota=null),r.rate_limits||(r.rate_limits=null)),Object.keys(r).length>0?{meta:r}:{}}async refreshProviderQuotaForSession(e,n,t){const o=this.sessionProviderHints.get(e);if(!o)return;if(o.disabled){h.debug(this.name,`[provider-quota] session skip: session=${e} reason=hint_disabled providerId=${o.providerId??""} model=${o.model??""}`);return}const i=`${o.providerId??""}|${o.model??""}|false`;try{let s,r,a;if(o.providerId==="openai_codex"){const u=this.sessionProviderQuotas.get(e);if(!t&&u&&u.quota.success&&u.cacheKey.startsWith("openai_codex|")&&this.isRateLimitsCacheFresh(u.sampledAt))s=u.quota,r=u.sampledAt,a=u.cacheKey;else{const g=await me();s=g.quota,r=Date.now(),a=Xt(g.accountKey)}}else{const u=await this.resolveProviderQuotaSource(o);if(!u){h.debug(this.name,`[provider-quota] session skip: session=${e} reason=no_source providerId=${o.providerId??""} model=${o.model??""}`);return}const g=await V.query(u,{fresh:t});s=g.quota,r=g.sampledAt,a=g.cacheKey}const d=this.sessionProviderHints.get(e);if(`${d?.providerId??""}|${d?.model??""}|${d?.disabled===!0}`!==i)return;if(h.info(this.name,`[provider-quota] queried: provider=${s.provider} success=${s.success}`+(s.tiers.length>0?` tiers=${s.tiers.map(u=>`${u.name}=${u.usedPercent}%`).join(",")}`:"")+(s.balance?` balance=${s.balance.remaining} ${s.balance.unit}`:"")+(s.error?` error=${s.error}`:"")+` session=${e}`),!s.success){const u=this.sessionProviderQuotas.get(e);(!u?.quota.success||u.cacheKey!==a)&&this.sessionProviderQuotas.delete(e);return}this.sessionProviderQuotas.set(e,{quota:s,sampledAt:r,cacheKey:a});const l=this.enrichProviderQuotaMeta(this.sessionProviderMeta.get(e),"standard",s,r);this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:n,meta:l})}catch(s){h.warn(this.name,`[provider-quota] session query failed: session=${e} ${s instanceof Error?s.message:String(s)}`)}}refreshQuotaAfterModelSwitch(e,n,t){const o=n.trim();if(!e||!o)return;const i=t?.trim()||void 0,s=`${e}|${i??""}|${o}`,r=Date.now();this.lastModelSwitchQuotaRefresh&&this.lastModelSwitchQuotaRefresh.key===s&&r-this.lastModelSwitchQuotaRefresh.at<pi||(this.lastModelSwitchQuotaRefresh={key:s,at:r},this.doRefreshQuotaAfterModelSwitch(e,o,i).catch(a=>{h.warn(this.name,`[provider-quota] model-switch refresh failed: session=${e} model=${o} ${a instanceof Error?a.message:String(a)}`)}))}async doRefreshQuotaAfterModelSwitch(e,n,t){const o=this.sessionProviderHints.get(e);if(o||t){const s=t??Q(n)?.id??o?.providerId,r={...s?{providerId:s}:{},model:n,...o?.disabled?{disabled:!0}:{}},a=o?.providerId!==r.providerId||o?.model!==r.model||o?.disabled!==r.disabled;this.sessionProviderHints.set(e,r),a&&this.sessionProviderQuotas.delete(e);const d=this.sessionBindings.get(e)??this.bindingStore.get(e)?.cwd??"";(a||!this.sessionProviderQuotas.has(e))&&d&&await this.refreshProviderQuotaForSession(e,d,!0);return}const i=await this.maybeQueryProviderQuota(!0,{model:n});i?.success&&this.pushProviderQuotaToBindings(i,this.sessionProviderHints.size>0)}getFreshClaudeRateLimitState(){const e=this.cachedClaudeRateLimitState;return e&&this.isRateLimitsCacheFresh(e.sampledAt)?e:null}getFreshCodexGlobalRateLimitCache(){const e=this.cachedRateLimits,n=this.cachedCodexContextWindow,t=this.cachedCodexTokenUsage;return{sampledAt:Math.max(this.cachedRateLimitsSampledAtMs??0,this.cachedCodexUsageSampledAtMs??0)||null,rateLimits:e,contextWindow:n,tokenUsage:t,hasData:!!(e||n||t)}}getStatus(){const e=this.pool?.getStatus()??{total:0,ready:0,busy:0};return{name:this.name,agentId:this.config.aibot.agentId,alive:!this.stopped,busy:e.busy>0,exhausted:this.pool?[...this.pool.getAllSlots()].some(n=>n.respawn.exhausted):!1,adapterType:this.config.adapterType??"acp",clientType:this.config.aibot.clientType,pool:e}}hasPendingWork(){return this.pool?.hasPendingWork()??!1}hasPendingOrBackgroundWork(){return this.pool?.hasPendingOrBackgroundWork()??!1}relayEnvStale=!1;relayEnvSettledHandler;async recycleAdaptersForRelayChange(){if(!this.pool)return this.relayEnvStale=!1,!0;if(this.hasPendingWork())return this.relayEnvStale=!0,h.info(this.name,"relay changed but agent is busy; adapters keep the old env until the next message (marked stale)"),!1;const e=[...this.pool.getAllSlots()];let n=0,t=!1;for(const o of e){if(this.hasPendingWork())return this.relayEnvStale=!0,h.info(this.name,"new work arrived while recycling; remaining adapters stay stale until the next message"),!1;let i=!1;await this.pool.removeSlot(o.sessionId).catch(s=>{t=!0,i=!0,h.warn(this.name,`failed to recycle adapter slot ${o.sessionId} after relay change: ${s}`)}),i||(n+=1)}return n>0&&h.info(this.name,`recycled ${n} adapter slot(s) so the new Grix relay env takes effect`),t?(this.relayEnvStale=!0,!1):(this.relayEnvStale=!1,this.relayEnvSettledHandler?.(),!0)}setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}providerResolver;setProviderResolver(e){this.providerResolver=e}hasStaleRelayEnv(){return this.relayEnvStale}markRelayEnvStale(){this.relayEnvStale=!0}async probe(e={}){const n=e.conversation?"full":"static",t=e.conversation?si:ni;if(!e.fresh){const a=this.probeCache.get(n);if(a&&Date.now()-a.sampledAt<t)return{...a.result,cached:!0}}const o=this.config.adapterType??"acp",i=this.createAdapter(o,"__probe__"),s=e.conversation&&o==="acp"?()=>this.runAcpConversationProbe(i,e.timeoutMs??1e4):void 0;let r;try{r=await ii({adapter:i,agentName:this.name,clientType:this.config.aibot.clientType,adapterType:o,providerBaseUrl:this.config.providerBaseUrl??null,opts:e,launchConversationProbe:s})}finally{i.stop().catch(()=>{})}return this.probeCache.set(n,{result:r,sampledAt:r.probed_at}),r}async runAcpConversationProbe(e,n){const t=Date.now(),o="__probe__",i=`probe-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,s=this.config.agent.cwd||be(),r=()=>Date.now()-t;try{await e.start()}catch(u){return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_failed",message:`start failed: ${u instanceof Error?u.message:String(u)}`}}}if(!e.isAlive())return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"process_not_started",message:"agent process not alive"}};if(e instanceof E)try{await e.bindSession(o,s)}catch{}let a=null;const d=new Promise(u=>{a=g=>{g===i&&u()},e.on("eventDone",a)});e.deliverInboundEvent({event_id:i,session_id:o,content:"ping",msg_id:i}),e.deliverStopEvent(i,o);let c=null;const l=await Promise.race([d.then(()=>!1),new Promise(u=>{c=setTimeout(()=>u(!0),n)})]);return c&&clearTimeout(c),a&&e.removeListener("eventDone",a),l?{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_timeout",message:`no eventDone within ${n}ms`}}:{attempted:!0,ok:!0,latency_ms:r()}}constructor(e,n,t=new ai){this.config=e,this.auditController=new oi(t,{onAuditState:i=>{try{this.aibotHandle.sendAuditState(X(i,Date.now()))}catch{}}}),ri(t)&&t.addJobSettledHandler((i,s)=>{this.auditController.handleJobSettled(i,s)}),this.name=e.name;const o=e.adapterType??"acp";if(this.sendCtrl.setDefaultQuoteTrigger(zt[o]),this.aibotConfig={...e.aibot,...o==="claude"?{localActions:e.aibot.localActions??["session_control","claude_interaction_reply","get_session_usage","get_rate_limits","set_model","set_mode","set_reasoning_effort","thread_compact","get_agent_global_config",...Ae]}:{}},e.eventQueue&&(this.aibotConfig.concurrency={max_concurrent:e.eventQueue.maxConcurrent,max_queued:e.eventQueue.maxQueued,queue_timeout_ms:e.eventQueue.queueTimeoutMs,cancelable_queued:e.eventQueue.cancelableQueued,cancelable_running:e.eventQueue.cancelableRunning}),this.conversationLog=e.logDir?new Et(e.logDir):null,this.packetLog=e.logDir?new yt(e.logDir):null,this.bindingStore=new Qt(e.bindingsPath),this.bindingStore.load(),this.globalConfigStore=n??null,this.deferredMgr=new ti(this.name),this.activeEventStore=e.activeEventStorePath?new Nt(e.activeEventStorePath):null,o==="codex")this.sessionScanCache=new y(at,vt);else if(o==="claude")this.sessionScanCache=new y(dt,St);else if(o==="pi"){const i=e.agent.env?.PI_CODING_AGENT_DIR;this.sessionScanCache=new y(()=>gt(void 0,i),()=>wt(void 0,i))}else if(o==="codewhale")this.sessionScanCache=new y(pt,Ct);else if(o==="cursor")this.sessionScanCache=new y(ft,At);else if(o==="opencode")this.sessionScanCache=new y(mt,kt);else if(o==="agy")this.sessionScanCache=new y(()=>[],()=>[]);else{e.aibot.clientType?.trim().toLowerCase()==="reasonix"&&(this.reasonixTitleScan=new y(ht,Rt));const i=lt(e.aibot.clientType);this.sessionScanCache=i?new y(()=>ut(void 0,i),()=>bt(void 0,i)):new y(()=>[],()=>[])}}async start(){await nt({agentName:this.name,adapterType:this.config.adapterType??"acp",command:this.config.agent.command,piConfigDir:this.config.agent.env?.PI_CODING_AGENT_DIR,hasProvider:!!this.config.agent.provider,boundCwds:[...this.bindingStore.entries()].map(([,i])=>String(i.cwd??""))}).then(i=>{i.length>0&&h.info(this.name,`restored native provider config after relay disable: ${i.join(", ")}`)}).catch(i=>{h.warn(this.name,`native provider residue sweep failed: ${i instanceof Error?i.message:String(i)}`)}),(this.config.adapterType??"acp")==="claude"&&(await G().catch(()=>{}),this.maybeQueryProviderQuota().catch(()=>{}));const e=!!(this.config.providerBaseUrl&&this.config.providerApiKey||this.config.agent.provider?.baseUrl&&this.config.agent.provider?.apiKey),n=(this.config.adapterType??"acp")==="pi",t=(this.config.adapterType??"acp")==="opencode";this.config.aibot.clientType==="kiro"||this.config.aibot.clientType==="kimi"||e?(this.maybeQueryProviderQuota().catch(()=>{}),this.startProviderQuotaTimer()):(n||t)&&this.startProviderQuotaTimer(),(this.config.adapterType??"acp")==="codex"&&this.maybeQueryProviderQuota().catch(()=>{}),(this.config.adapterType??"acp")==="opencode"&&this.maybeQueryProviderQuota().catch(()=>{}),await this.connectAibot(),this.sendCtrl.bind(this.aibotHandle);const o=this.config.adapterType??"acp";this.pool=new Pt({maxPoolSize:this.config.poolMaxSize??20,idleTimeoutMs:this.config.poolIdleTimeoutMs??18e5,eventQueue:this.config.eventQueue},i=>{const s=this.createAdapter(o,i);return s instanceof E&&s.on("acpSessionReady",r=>{this.bindingStore.setAcpSessionId(i,r),this.sessionScanCache.invalidate()}),s},(i,s)=>{this.aibotHandle.sendEventAck({event_id:i,session_id:s,received_at:Date.now()})}),this.pool.setEventStateHandler((i,s,r,a)=>{if(h.info(this.name,`[queue-debug] send event_state session=${s} event=${i} state=${r} queue_pos=${a?.queue_position??""} queue_total=${a?.queue_total??""}`),this.aibotHandle.sendEventState({event_id:i,session_id:s,state:r,content_preview:a?.content_preview,content:a?.content,queue_position:a?.queue_position,queue_total:a?.queue_total,actions:a?.actions,reason:a?.reason,held:a?.held,held_reason:a?.held_reason,updated_at:Date.now()}),this.pushQueueSnapshotForSession(s),r==="canceled"||r==="failed"){const d=r==="canceled"?"canceled":"failed";this.aibotHandle.sendEventResult({event_id:i,status:d,msg:a?.reason,updated_at:Date.now()}),this.auditController.closeWithoutAdapter(i,d,a?.reason,{queueTerminalState:r}),this.discardEventTrackingState(i)}}),this.pool.setQueueComposingHandler((i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:i,kind:"composing",active:s,...s?{ttl_ms:3e4,ref_event_id:r}:{}})}),this.pool.setInternalErrorHandler(i=>{this.handleSessionInternalError(i).catch(s=>{h.error(this.name,`[recovery] handleSessionInternalError failed event=${i.eventId} session=${i.sessionId}: ${s instanceof Error?s.message:String(s)}`)})}),this.pool.setEventStartedHandler((i,s)=>{if(this.reportSessionSkills(s),this.config.adapterType!=="claude")return;this.claudeWorkerStatus.set(s,"busy");const r=this.bindingStore.get(s);r?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"busy",cwd:r.cwd,meta:this.buildClaudeToolbarMeta(s)})}),this.pool.setEventDoneHandler((i,s)=>{this.markAuditAdapterClosed(i,s);const r=this.config.adapterType??"acp";if(r==="claude"){this.claudeWorkerStatus.set(s,"ready");const a=this.bindingStore.get(s);a?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"ready",cwd:a.cwd,meta:this.buildClaudeToolbarMeta(s)})}else if(r==="agy"){const a=this.bindingStore.get(s);if(a?.cwd){const d=this.buildAgyToolbarMeta(s),c=d?.available_models??[];h.info(this.name,`[agy-toolbar-diag] eventDone push binding card session=${s} model_id=${String(d?.model_id??"")} available_models=${c.length} meta_keys=${d?Object.keys(d).join(","):"<none>"}`),this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"ready",cwd:a.cwd,meta:d})}else h.info(this.name,`[agy-toolbar-diag] eventDone skip binding card: no binding cwd session=${s}`)}}),this.pool.setSessionActivityHandler((i,s,r)=>{const a=this.selfDrivenSessions.has(i),d=this.selfDrivenLabels.get(i);s?(this.selfDrivenSessions.add(i),r&&this.selfDrivenLabels.set(i,r),this.aibotHandle.sendSessionActivitySet({session_id:i,kind:"composing",active:!0,ttl_ms:9e4})):(this.selfDrivenSessions.delete(i),this.selfDrivenLabels.delete(i),a&&this.aibotHandle.sendSessionActivitySet({session_id:i,kind:"composing",active:!1})),(a!==s||s&&r!==void 0&&r!==d)&&this.pushQueueSnapshotForSession(i)}),this.pool.startIdleSweep(),h.info(this.name,`Ready (adapter: ${o}, poolMax: ${this.config.poolMaxSize??20})`)}async stop(){this.stopped=!0,this.pool?.stopIdleSweep(),this.stopProviderQuotaTimer();const e=this.pool?.collectActiveEventIds()??[],n=new Map(e.map(i=>[i,this.eventSessionIndex.get(i)]));e.length>0&&this.activeEventStore&&await this.activeEventStore.save(e);for(const i of e)h.info(this.name,`Canceling active event on shutdown: ${i}`),this.sendEventResultWithCleanup(i,"canceled","process shutting down");e.length>0&&await new Promise(i=>setTimeout(i,100)),this.pool?.clearActiveEventsForShutdown();for(const i of e){const s=n.get(i),r=s?this.pool?.getSlot(s):void 0;this.auditController.markAdapterClosed(i,r?.adapter.takeAuditBoundary?.(i)??{processTerminated:!0})}const t=this.deferredMgr.getAllDeferredEvents();for(const i of t)h.info(this.name,`Failing deferred event on shutdown: ${i.event_id}`),this.sendEventResultWithCleanup(i.event_id,"failed","process shutting down"),this.auditController.markAdapterClosed(i.event_id,{adapterNotStarted:!0,processTerminated:!0});const o=this.pool?.drainAllQueuedEvents()??[];for(const i of o)h.info(this.name,`Failing queued event on shutdown: ${i.event_id}`),this.sendEventResultWithCleanup(i.event_id,"failed","process shutting down"),this.auditController.markAdapterClosed(i.event_id,{adapterNotStarted:!0,processTerminated:!0});this.deferredMgr.clearAll(),await this.pool?.stop(),this.aibotHandle?.disconnect(),e.length>0&&this.activeEventStore&&await this.activeEventStore.save([]),this.eventSessionIndex.clear(),this.inflightEvents.clear(),this.restartCount.clear(),this.auditController.dispose()}resolveSpawnEnv(){const e=this.providerResolver?this.providerResolver():this.config.agent.provider,n=et(this.config.agent.clientType,e),t=this.config.agent.env;let o=Object.keys(n).length>0?{...n,...t}:t;if(tt(n)){const i=ae()?.getRuntimeInfo(),s={...t??{},...n},r=it(s,{proxyUrl:i?.proxyUrl,caCertPath:i?.caCertPath,caBundlePath:i?.caBundlePath});return delete r.ANTHROPIC_API_KEY,delete r.CLAUDE_CODE_OAUTH_TOKEN,r}if(de(this.config.agent.clientType)){const i=Ve(this.name);i&&(o={...o??{},...i})}return Ge(o,this.name,this.config.agent.clientType)}createAdapter(e,n){switch(e){case"claude":return this.createClaudeAdapter(n);case"codex":return this.createCodexAdapter(n);case"pi":return this.createPiAdapter(n);case"openhuman":return this.createOpenHumanAdapter(n);case"codewhale":return this.createCodeWhaleAdapter(n);case"cursor":return this.createCursorAdapter(n);case"opencode":return this.createOpenCodeAdapter(n);case"agy":return this.createAgyAdapter(n);default:return this.createAcpAdapter(n)}}createCursorAdapter(e){const n={...this.config.adapterOptions??{}};n.bindingStore=this.bindingStore,n.aibotSessionId=e;const t=this.bindingStore.get(e),o=this.resolveSessionModelId(e);o&&(n.model=o);const i=this.bindingStore.getCursorModeId(e),s=i??"full_auto";i||this.bindingStore.setCursorModeId(e,s),n.mode=s,t?.cwd&&(n.workspace=t.cwd);const r={sendStreamChunk:(a,d,c,l,u,g,v)=>{this.sendStreamChunkByRuntimeConfig(a,d,c,l,u,g,v)},sendEventResult:(a,d,c)=>{this.sendEventResultWithCleanup(a,d,c)},sendEventAck:(a,d)=>{this.aibotHandle.sendEventAck({event_id:a,session_id:d,received_at:Date.now()})},sendRawEventEnvelope:(a,d,c)=>{this.deliverRawEventEnvelope(a,d,c,"cursor",this.buildCursorRawEventFallbackText(c))},sendAgentQuestionCard:(a,d,c)=>{const l=c.questions.map(g=>g.header).join(", "),u=_e(`[Agent Question] ${c.request_id}`,"agent_question",c);this.aibotHandle.sendText({event_id:a,session_id:d,content:u,msg_type:1,extra:{card_type:"agent_question",summary_text:l}})},agentInvoke:async(a,d,c)=>this.platformInvoke(a,d,c),sendLocalActionResult:(a,d,c,l,u)=>{this.aibotHandle.sendLocalActionResult({action_id:a,status:d,...c!==void 0?{result:c}:{},...l?{error_code:l}:{},...u?{error_msg:u}:{}})},sendUpdateBindingCard:(a,d,c,l)=>{this.aibotHandle.sendUpdateBindingCard({session_id:a,worker_status:d,cwd:c,...l?{meta:l}:{}})},getAgentProfile:()=>this.agentProfile};return new B({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:n},r)}createClaudeAdapter(e){const n={sendReply:(i,s,r,a,d)=>{this.sendReplyByRuntimeConfig(i,s,r,a,d)},sendStreamChunk:(i,s,r,a,d,c,l)=>{this.sendStreamChunkByRuntimeConfig(i,s,r,a,d,c,l)},sendMedia:(i,s,r,a,d,c,l)=>{this.aibotHandle.sendMedia({event_id:i,session_id:s,content:r,msg_type:2,quoted_message_id:d||void 0,client_msg_id:c||void 0,extra:l?{media_caption:a,...l}:{media_caption:a}})},sendEventResult:(i,s,r,a)=>{this.sendEventResultWithCleanup(i,s,r,a)},sendEventAck:(i,s)=>{this.aibotHandle.sendEventAck({event_id:i,session_id:s,received_at:Date.now()})},agentInvoke:async(i,s,r)=>this.platformInvoke(i,s,r),sendLocalActionResult:(i,s,r,a,d)=>{this.aibotHandle.sendLocalActionResult({action_id:i,status:s,...r!==void 0?{result:r}:{},...a?{error_code:a}:{},...d?{error_msg:d}:{}})},sendToolUse:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,L(r,a))},sendToolResult:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,H(r,a))},getWsUrl:()=>this.config.aibot.url,getAgentId:()=>this.config.aibot.agentId,getAgentProfile:()=>this.agentProfile,getApiKey:()=>this.config.aibot.apiKey,getActiveEventCount:()=>0,getPendingPermissionCount:()=>0,getPendingElicitationCount:()=>0,sendAgentQuestionCard:(i,s,r)=>{this.sendGrixAgentQuestionCard(i,s,r)},sendPermissionCard:i=>{this.sendGrixApprovalCard(i,"claude")},sendDirectMessage:i=>{this.aibotHandle.sendMsg({session_id:i.sessionId,msg_type:1,content:i.content,...i.clientMsgId?{client_msg_id:i.clientMsgId}:{},...i.quotedMessageId?{quoted_message_id:i.quotedMessageId}:{}})},onStatusLineUpdated:i=>{(i.rateLimits?.fiveHour||i.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=i);const s=this.bindingStore.get(e);s?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:this.claudeWorkerStatus.get(e)??"ready",cwd:s.cwd,meta:this.buildClaudeToolbarMeta(e)})},sendMcpFrame:i=>{this.aibotHandle.sendMcpFrame(e,i)}},t=this.config.adapterOptions??{},o={...t,sessionRuntimeResolver:()=>{const i=this.bindingStore.get(e),s=this.auditController.getSession(e);return{cwd:i?.cwd,modeId:i?.modeId??R.fullAuto,modelId:this.resolveSessionModelId(e),effort:this.resolveClaudeSessionEffort(e),pluginDir:t.pluginDir,claudeSessionId:i?.claudeSessionId,...s?{audit:{enabled:!0,profile:s.options.profile,capture:s.options.capture}}:{},onSessionIdAssigned:r=>{this.bindingStore.setClaudeSessionId(e,r),this.sessionScanCache.invalidate()}}}};return new M({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:o},n)}createCodexAdapter(e){let n=null;const t={sendEventResult:(r,a,d)=>{if(a!=="responded"){this.sendEventResultWithCleanup(r,a,d);return}this.sendCtrl.sendFinalStreamChunkReliable(r,e,`codex_terminal_fence_${r}`,5e3,!0).then(()=>this.sendEventResultWithCleanup(r,a,d),c=>{const l=`codex output acceptance fence failed: ${c instanceof Error?c.message:String(c)}`;this.sendEventResultWithCleanup(r,a==="responded"?"failed":a,a==="responded"?l:d)})},sendEventAck:(r,a)=>this.aibotHandle.sendEventAck({event_id:r,session_id:a,received_at:Date.now()}),sendCodexEvent:r=>{this.shouldDropCodexDisplayEvent(r.event_id,r.codex_method)||(this.captureCodexAuditOutput(r),this.aibotHandle.sendCodexEvent(this.stampCodexEventQuote(r)),this.logCodexEventToConversation(r))},sendRunError:(r,a,d)=>{this.sendStreamChunkByRuntimeConfig(r,a,`
|
|
1
|
+
import T from"node:path";import{realpath as Se,stat as F}from"node:fs/promises";import{tmpdir as be,homedir as N}from"node:os";import{createHash as we,randomUUID as W}from"node:crypto";import{ConnectionManager as Ce}from"../core/aibot/index.js";import{AUDIT_LOCAL_ACTION_TYPES as Ae}from"../core/aibot/types.js";import{ClaudeAdapter as M}from"../adapter/claude/index.js";import{CodexAdapter as te}from"../adapter/codex/index.js";import{PiAdapter as ke}from"../adapter/pi/index.js";import{AcpAdapter as E}from"../adapter/acp/index.js";import{OpenHumanAdapter as Re}from"../adapter/openhuman/index.js";import{CursorAdapter as B,CURSOR_MODE_OPTIONS as Ee,fetchCursorAccountUsage as ye}from"../adapter/cursor/index.js";import{CodeWhaleAdapter as ie}from"../adapter/codewhale/index.js";import{OpenCodeAdapter as K}from"../adapter/opencode/index.js";import{AgyAdapter as ne}from"../adapter/agy/index.js";import{getCachedAgyModels as $e}from"../adapter/agy/model-list.js";import{getCachedAgyQuotaInfo as Pe,primeAgyQuotaInfo as Te}from"../adapter/agy/quota.js";import{LOCAL_ACTION_ERROR_CODES as x,LOCAL_ACTION_TYPES as C,SESSION_CONTROL_ERROR_CODES as p,SESSION_CONTROL_VERBS as _,SESSION_MODE_IDS as R}from"../adapter/claude/protocol-contract.js";import{AuditLocalActionHandler as xe,isAuditLocalActionType as Le}from"../audit/query/audit-local-actions.js";import{AuditReplayQueryService as He}from"../audit/query/audit-replay-query-service.js";import{resolveDefaultAuditStorageRootDir as Me}from"../audit/runtime/default-audit-recorder.js";import{FileAuditReplayStore as Ie}from"../audit/storage/file-audit-replay-store.js";import{shouldEnableRawApiCapture as se}from"../audit/raw-capture/types.js";import{parseClaudeSessionUsage as De}from"../adapter/claude/usage-parser.js";import"../adapter/claude/session-history.js";import"../adapter/codex/session-history.js";import"../adapter/pi/session-history.js";import"../adapter/opencode/session-history.js";import"../adapter/codewhale/session-history.js";import{SESSION_HISTORY_ERROR_CODES as q,SessionHistoryError as Qe,getSessionHistoryReader as Be}from"../adapter/shared/session-history.js";import{resolveSkillScanMode as I}from"../adapter/claude/skill-scanner.js";import{buildReportedSkills as qe}from"../default-skills/index.js";import{buildWireSkills as oe}from"../core/skill-sync/sync-state.js";import{uploadSkill as Oe}from"../core/skill-sync/skill-upload.js";import{buildLibrarySkills as Ue}from"../core/skill-sync/library-skills.js";import{enableSkill as Fe,disableSkill as Ne,SkillEnableError as re}from"../core/skill-sync/skill-enable.js";import{fetchAvailableModels as G,getCachedModels as z,readSettingsEnv as We}from"../adapter/claude/model-list.js";import{parseAcpSessionUsage as Ke}from"../adapter/acp/usage-parser.js";import{applyProxyEnv as Ge,getProxyManager as ae,relayHostsForClientType as ze,buildGatewayRoute as je}from"../core/proxy/index.js";import{getKiroRelaySpawnEnv as Ve}from"../core/proxy/kiro-relay.js";import{supportsKiroCwRelay as de}from"../core/config/provider-env.js";import{RelayFetchError as Ye,deriveGatewayBaseUrls as ce,fetchRelayCredentialWithRetry as Je}from"../core/proxy/relay-credential-fetch.js";import{RelayStateSyncer as Xe}from"../core/proxy/relay-state-sync.js";import{isDirectRelayExplicitlyUnsupported as Ze}from"../core/proxy/relay-credential.js";import{buildProviderEnv as et}from"../core/config/provider-env.js";import{CLAUDE_EFFORT_LEVELS as D,isClaudeDirectEnv as tt,normalizeClaudeEffort as j,stripGrixRelayEnv as it}from"../core/config/claude-direct-env.js";import{sweepNativeProviderResidue as nt}from"./native-provider-sweep.js";import{parseCodexSessionUsage as st}from"../adapter/codex/usage-parser.js";import{readCodexProviderSettings as ot}from"../adapter/codex/codex-trust.js";import{resolveKimiProviderSettings as rt}from"../core/provider-quota/index.js";import{scanCodexSessions as at}from"../adapter/codex/session-scanner.js";import{scanClaudeSessions as dt}from"../adapter/claude/session-scanner.js";import{reasonixStampFromSessionId as ct,resolveAcpAgentTypes as lt,scanAcpSessions as ut,scanReasonixSessionTitles as ht}from"../adapter/acp/session-scanner.js";import{scanPiSessions as gt}from"../adapter/pi/session-scanner.js";import{scanCodeWhaleSessions as pt}from"../adapter/codewhale/session-scanner.js";import{scanCursorSessions as ft}from"../adapter/cursor/session-scanner.js";import{scanOpenCodeSessions as mt}from"../adapter/opencode/session-scanner.js";import{parsePiSessionUsage as _t}from"../adapter/pi/usage-parser.js";import{SessionScanCache as y,resolveCodexLeafDirs as vt,resolveClaudeLeafDirs as St,resolveAcpLeafDirs as bt,resolvePiLeafDirs as wt,resolveCodeWhaleLeafDirs as Ct,resolveCursorLeafDirs as At,resolveOpenCodeLeafDirs as kt,resolveReasonixLeafDirs as Rt}from"./session-scan-cache.js";import{log as h,ConversationLog as Et,AgentApiPacketLog as yt,BridgeEventLog as $t,GRIX_PATHS as P}from"../core/log/index.js";import{RevokeHandler as Pt}from"./revoke-handler.js";import{AdapterPool as Tt,PoolFullError as xt}from"./adapter-pool.js";import{parseSessionControlCommand as Lt,handleSessionControlCommand as le,handleSessionControlLocalAction as Ht}from"./session-controller.js";import{isOpenSessionDirectiveMessage as Mt,parseAgentQuestionReplyMessage as It}from"../core/protocol/interaction-parser.js";import{handleAcpSetModel as ue,handleAcpSetMode as he,resolveAcpInitialDefaults as Dt}from"./acp-toolbar-persist.js";import{SessionBindingStore as Qt}from"../core/persistence/session-binding-store.js";import{handleFileListAction as Bt,handleCreateFolderAction as qt,serveLocalFile as Ot,realHomeDir as ge}from"../core/files/index.js";import{getMachineName as Ut}from"../core/util/index.js";import{uploadReplyFileToAgentMedia as Ft}from"../core/protocol/agent-api-media.js";import{ActiveEventStore as Nt}from"../core/persistence/active-event-store.js";import{DEFAULT_CONNECTOR_RUNTIME_CONFIG as Wt,applyConnectorRuntimeConfigPatch as Kt,extractConnectorRuntimeConfigPatch as Gt}from"./runtime-config.js";import{DEFAULT_QUOTE_TRIGGER_BY_ADAPTER as zt,SendController as jt}from"./send-controller.js";import{sharedProviderQuotaService as V,detectProviderFromModel as Q,normalizeProviderId as Y,readPiProviderSettings as Vt,readOpenCodeProviderSettings as Yt,providerQuotaToRateLimits as pe,providerQuotaToCodexRateLimits as fe,isOpenaiCodexProviderName as Jt,queryOpenaiCodexQuota as me,openaiCodexQuotaCacheKey as Xt}from"../core/provider-quota/index.js";import{queryKiroQuota as J}from"../core/provider-quota/kiro.js";import{buildToolUseCard as L,buildToolResultCard as H,buildLocalGrixCardLink as _e}from"./tool-card-utils.js";import{planRawEventDelivery as Zt,buildAuxiliaryDetailText as ei}from"./raw-event-delivery.js";import{DeferredEventManager as ti}from"./deferred-events.js";import{buildAgentProbeResult as ii,PROBE_CACHE_TTL_STATIC_MS as ni,PROBE_CACHE_TTL_FULL_MS as si}from"./probe-helper.js";import{BridgeAuditController as oi}from"../audit/integration/bridge-audit-controller.js";import{isSettlableAuditProducer as ri,NoopAuditProducer as ai}from"../audit/producer/audit-producer.js";import{buildAuditStatePayload as X,AuditPreparationFailedError as di,AuditTurnScopeUnsupportedError as ci}from"../audit/core/audit-state.js";import{extractAuditOptions as li,stripAuditOptions as ui}from"../audit/core/audit-options.js";const hi=600*1e3,gi=60*1e3,ve=30*1e3,pi=10*1e3,fi=10*1e3;function mi(ee,e,n){const t=we("sha256").update(ee).update("\0").update(e).update("\0").update(n).digest("hex");return T.join(P.data,"audit-source","reasonix",`${t}.jsonl`)}const O=new Set(["claude","acp","agy","cursor","codex"]),_i=new Set(["claude","codex","cursor","codewhale","opencode","pi","openhuman","agy","acp"]),Z=3;class zn{config;name;aibotHandle;aibotConfig;pool;stopped=!1;revokeHandler=new Pt;sessionBindings=new Map;deferredMgr;sendCtrl=new jt(Wt);bindingStore;globalConfigStore;upgradeTrigger=null;agentDeletedHandler=null;shareSetHandler=null;skillSyncHandler=null;providerConfigHandler=null;relayStateApplyPorts=null;relayStateSyncer=null;agentProfile={agentName:"",introduction:""};activeEventStore;cachedRateLimits=null;cachedRateLimitsSampledAtMs=null;cachedCodexContextWindow=null;cachedCodexTokenUsage=null;cachedCodexUsageSampledAtMs=null;cachedAcpContextWindow=null;cachedAcpContextWindowSampledAtMs=null;cachedClaudeRateLimitState=null;cachedProviderQuota=null;cachedProviderQuotaSampledAtMs=null;cachedProviderQuotaKey=null;sessionProviderHints=new Map;sessionProviderQuotas=new Map;sessionProviderMeta=new Map;lastModelSwitchQuotaRefresh=null;claudeWorkerStatus=new Map;lastReportedSkillsHash="";conversationLog=null;packetLog=null;providerQuotaTimer=null;eventSessionIndex=new Map;inflightEvents=new Map;restartCount=new Map;selfDrivenSessions=new Set;selfDrivenLabels=new Map;probeCache=new Map;auditController;auditLocalActionHandler=null;sessionScanCache;reasonixTitleScan=null;isRateLimitsCacheFresh(e){if(!Number.isFinite(e))return!1;const n=Number(e);return n>0&&Date.now()-n<=gi}async resolveProviderQuotaSource(e){if(e?.disabled||e?.providerId==="openai_codex")return null;let n=this.config.providerBaseUrl??this.config.agent.provider?.baseUrl,t=this.config.providerApiKey??this.config.agent.provider?.apiKey;if((!n||!t)&&(this.config.adapterType??"acp")==="claude"){const i=We();n||(n=(i.ANTHROPIC_BASE_URL??"").trim()||void 0),t||(t=(i.ANTHROPIC_API_KEY??"").trim()||(i.ANTHROPIC_AUTH_TOKEN??"").trim()||void 0)}if((!n||!t)&&(this.config.adapterType??"acp")==="codex"){const i=ot();!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if((!n||!t)&&(this.config.adapterType??"acp")==="acp"&&this.config.aibot.clientType==="kimi"){const i=await rt();!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if((!n||!t)&&(this.config.adapterType??"acp")==="pi"){const i=Vt(e);!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if((!n||!t)&&(this.config.adapterType??"acp")==="opencode"){const i=Yt(e);!n&&i.baseUrl&&(n=i.baseUrl),!t&&i.apiKey&&(t=i.apiKey)}if(!n||!t)return null;const o=Y(e?.providerId)??Q(e?.model)?.id??Y(this.config.agent.provider?.providerId)??Q(this.config.agent.provider?.model)?.id;return{baseUrl:n,...this.config.agent.provider?.quotaBaseUrl?{quotaBaseUrl:this.config.agent.provider.quotaBaseUrl}:{},apiKey:t,...o?{providerId:o}:{}}}async maybeQueryProviderQuota(e=!1,n){if(this.config.aibot.clientType==="kiro"){if(!e&&this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota)return this.cachedProviderQuota;try{const i=await J();return this.cachedProviderQuota=i,this.cachedProviderQuotaSampledAtMs=Date.now(),this.cachedProviderQuotaKey="kiro",h.info(this.name,`[provider-quota] kiro queried: success=${i.success}`+(i.balance?` balance=${i.balance.remaining} ${i.balance.unit}`:"")+(i.planName?` plan=${i.planName}`:"")+(i.error?` error=${i.error}`:"")),i}catch(i){return h.warn(this.name,`[provider-quota] kiro query failed: ${i instanceof Error?i.message:String(i)}`),null}}if(n?.providerId==="openai_codex")try{const i=await me();return i.quota.success?i.quota:null}catch(i){return h.warn(this.name,`[provider-quota] openai_codex query failed: ${i instanceof Error?i.message:String(i)}`),null}const t=await this.resolveProviderQuotaSource(n);if(!t)return null;const o=V.cacheKey(t);if(!e&&this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)&&this.cachedProviderQuota&&this.cachedProviderQuotaKey===o)return this.cachedProviderQuota;try{const i=await V.query(t,{fresh:e}),s=i.quota;return this.cachedProviderQuota=s,this.cachedProviderQuotaSampledAtMs=i.sampledAt,this.cachedProviderQuotaKey=i.cacheKey,h.info(this.name,`[provider-quota] queried: provider=${s.provider} success=${s.success}`+(s.tiers.length>0?` tiers=${s.tiers.map(r=>`${r.name}=${r.usedPercent}%`).join(",")}`:"")+(s.balance?` balance=${s.balance.remaining} ${s.balance.unit}`:"")+(s.error?` error=${s.error}`:"")),s}catch(i){return h.warn(this.name,`[provider-quota] query failed: ${i instanceof Error?i.message:String(i)}`),null}}startProviderQuotaTimer(){this.providerQuotaTimer||(this.providerQuotaTimer=setInterval(()=>{if(this.stopped){this.stopProviderQuotaTimer();return}this.refreshAndPushProviderQuota().catch(()=>{})},ve),h.info(this.name,`[provider-quota-timer] started (interval=${ve}ms)`))}stopProviderQuotaTimer(){this.providerQuotaTimer&&(clearInterval(this.providerQuotaTimer),this.providerQuotaTimer=null,h.info(this.name,"[provider-quota-timer] stopped"))}async refreshAndPushProviderQuota(e=!1){try{let n;if(this.config.aibot.clientType==="kiro")n=await J(),this.cachedProviderQuota=n,this.cachedProviderQuotaSampledAtMs=Date.now(),this.cachedProviderQuotaKey="kiro";else{const t=[...this.sessionBindings.entries()].filter(([o,i])=>!!i&&this.sessionProviderHints.has(o));if(t.length>0&&await Promise.all(t.map(([o,i])=>this.refreshProviderQuotaForSession(o,i,e))),t.length===this.sessionBindings.size&&t.length>0)return;n=await this.maybeQueryProviderQuota(e)}n?.success&&this.pushProviderQuotaToBindings(n,this.sessionProviderHints.size>0)}catch{}}pushProviderQuotaToBindings(e,n=!1){const t=this.providerQuotaToRateLimits(e);for(const[o,i]of this.sessionBindings.entries()){if(!i||n&&this.sessionProviderHints.has(o))continue;const s={provider_quota:e};t&&(s.rate_limits=t);const r=this.cachedAcpContextWindow;r&&("usedPercentage"in r?s.context_window={usedPercentage:r.usedPercentage,remainingPercentage:100-r.usedPercentage}:s.context_window=r),this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:"ready",cwd:i,meta:s})}}enrichProviderQuotaMeta(e,n="standard",t=this.cachedProviderQuota,o=this.cachedProviderQuotaSampledAtMs){const i={...e??{}};if(!t?.success||(i.provider_quota||(i.provider_quota=t),i.rate_limits))return i;if(n==="codex"){const r=fe(t,o??Date.now());return r&&(i.rate_limits=r.rateLimits,i.rate_limit_primary_percent=r.primaryPercent,i.rate_limit_secondary_percent=r.secondaryPercent,i.rate_limit_primary_window_min=r.primaryWindowMin,i.rate_limit_secondary_window_min=r.secondaryWindowMin),i}const s=pe(t,o??Date.now());return s&&(i.rate_limits=s),i}providerQuotaMetaPayload(e,n,t){let o=this.cachedProviderQuota,i=this.cachedProviderQuotaSampledAtMs,s=!1;if(n){const a=typeof e?.model_provider=="string"?e.model_provider:void 0,d=typeof e?.model_id=="string"?e.model_id:void 0,c=Y(a)??Q(a)?.id??Q(d)?.id??(Jt(a)?"openai_codex":null);if(c||d){const u=a?.trim().toLowerCase()??"",g=u==="grix"||u.startsWith("grix-"),f={...c?{providerId:c}:{},...d?{model:d}:{},...!!a&&!g&&!c?{disabled:!0}:{}},S=this.sessionProviderHints.get(n),m=S?.providerId!==f.providerId||S?.model!==f.model||S?.disabled!==f.disabled;this.sessionProviderHints.set(n,f),this.sessionProviderMeta.set(n,{...e??{}}),m&&this.sessionProviderQuotas.delete(n),(m||!this.sessionProviderQuotas.has(n))&&t&&this.refreshProviderQuotaForSession(n,t,!1)}const l=this.sessionProviderQuotas.get(n);s=this.sessionProviderHints.has(n),s&&(o=null,i=null),l&&(o=l.quota,i=l.sampledAt)}const r=this.enrichProviderQuotaMeta(e,"standard",o,i);return s&&(r.provider_quota||(r.provider_quota=null),r.rate_limits||(r.rate_limits=null)),Object.keys(r).length>0?{meta:r}:{}}async refreshProviderQuotaForSession(e,n,t){const o=this.sessionProviderHints.get(e);if(!o)return;if(o.disabled){h.debug(this.name,`[provider-quota] session skip: session=${e} reason=hint_disabled providerId=${o.providerId??""} model=${o.model??""}`);return}const i=`${o.providerId??""}|${o.model??""}|false`;try{let s,r,a;if(o.providerId==="openai_codex"){const u=this.sessionProviderQuotas.get(e);if(!t&&u&&u.quota.success&&u.cacheKey.startsWith("openai_codex|")&&this.isRateLimitsCacheFresh(u.sampledAt))s=u.quota,r=u.sampledAt,a=u.cacheKey;else{const g=await me();s=g.quota,r=Date.now(),a=Xt(g.accountKey)}}else{const u=await this.resolveProviderQuotaSource(o);if(!u){h.debug(this.name,`[provider-quota] session skip: session=${e} reason=no_source providerId=${o.providerId??""} model=${o.model??""}`);return}const g=await V.query(u,{fresh:t});s=g.quota,r=g.sampledAt,a=g.cacheKey}const d=this.sessionProviderHints.get(e);if(`${d?.providerId??""}|${d?.model??""}|${d?.disabled===!0}`!==i)return;if(h.info(this.name,`[provider-quota] queried: provider=${s.provider} success=${s.success}`+(s.tiers.length>0?` tiers=${s.tiers.map(u=>`${u.name}=${u.usedPercent}%`).join(",")}`:"")+(s.balance?` balance=${s.balance.remaining} ${s.balance.unit}`:"")+(s.error?` error=${s.error}`:"")+` session=${e}`),!s.success){const u=this.sessionProviderQuotas.get(e);(!u?.quota.success||u.cacheKey!==a)&&this.sessionProviderQuotas.delete(e);return}this.sessionProviderQuotas.set(e,{quota:s,sampledAt:r,cacheKey:a});const l=this.enrichProviderQuotaMeta(this.sessionProviderMeta.get(e),"standard",s,r);this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:n,meta:l})}catch(s){h.warn(this.name,`[provider-quota] session query failed: session=${e} ${s instanceof Error?s.message:String(s)}`)}}refreshQuotaAfterModelSwitch(e,n,t){const o=n.trim();if(!e||!o)return;const i=t?.trim()||void 0,s=`${e}|${i??""}|${o}`,r=Date.now();this.lastModelSwitchQuotaRefresh&&this.lastModelSwitchQuotaRefresh.key===s&&r-this.lastModelSwitchQuotaRefresh.at<pi||(this.lastModelSwitchQuotaRefresh={key:s,at:r},this.doRefreshQuotaAfterModelSwitch(e,o,i).catch(a=>{h.warn(this.name,`[provider-quota] model-switch refresh failed: session=${e} model=${o} ${a instanceof Error?a.message:String(a)}`)}))}async doRefreshQuotaAfterModelSwitch(e,n,t){const o=this.sessionProviderHints.get(e);if(o||t){const s=t??Q(n)?.id??o?.providerId,r={...s?{providerId:s}:{},model:n,...o?.disabled?{disabled:!0}:{}},a=o?.providerId!==r.providerId||o?.model!==r.model||o?.disabled!==r.disabled;this.sessionProviderHints.set(e,r),a&&this.sessionProviderQuotas.delete(e);const d=this.sessionBindings.get(e)??this.bindingStore.get(e)?.cwd??"";(a||!this.sessionProviderQuotas.has(e))&&d&&await this.refreshProviderQuotaForSession(e,d,!0);return}const i=await this.maybeQueryProviderQuota(!0,{model:n});i?.success&&this.pushProviderQuotaToBindings(i,this.sessionProviderHints.size>0)}getFreshClaudeRateLimitState(){const e=this.cachedClaudeRateLimitState;return e&&this.isRateLimitsCacheFresh(e.sampledAt)?e:null}getFreshCodexGlobalRateLimitCache(){const e=this.cachedRateLimits,n=this.cachedCodexContextWindow,t=this.cachedCodexTokenUsage;return{sampledAt:Math.max(this.cachedRateLimitsSampledAtMs??0,this.cachedCodexUsageSampledAtMs??0)||null,rateLimits:e,contextWindow:n,tokenUsage:t,hasData:!!(e||n||t)}}getStatus(){const e=this.pool?.getStatus()??{total:0,ready:0,busy:0};return{name:this.name,agentId:this.config.aibot.agentId,alive:!this.stopped,busy:e.busy>0,exhausted:this.pool?[...this.pool.getAllSlots()].some(n=>n.respawn.exhausted):!1,adapterType:this.config.adapterType??"acp",clientType:this.config.aibot.clientType,pool:e}}hasPendingWork(){return this.pool?.hasPendingWork()??!1}hasPendingOrBackgroundWork(){return this.pool?.hasPendingOrBackgroundWork()??!1}relayEnvStale=!1;relayEnvSettledHandler;async recycleAdaptersForRelayChange(){if(!this.pool)return this.relayEnvStale=!1,!0;if(this.hasPendingWork())return this.relayEnvStale=!0,h.info(this.name,"relay changed but agent is busy; adapters keep the old env until the next message (marked stale)"),!1;const e=[...this.pool.getAllSlots()];let n=0,t=!1;for(const o of e){if(this.hasPendingWork())return this.relayEnvStale=!0,h.info(this.name,"new work arrived while recycling; remaining adapters stay stale until the next message"),!1;let i=!1;await this.pool.removeSlot(o.sessionId).catch(s=>{t=!0,i=!0,h.warn(this.name,`failed to recycle adapter slot ${o.sessionId} after relay change: ${s}`)}),i||(n+=1)}return n>0&&h.info(this.name,`recycled ${n} adapter slot(s) so the new Grix relay env takes effect`),t?(this.relayEnvStale=!0,!1):(this.relayEnvStale=!1,this.relayEnvSettledHandler?.(),!0)}setRelayEnvSettledHandler(e){this.relayEnvSettledHandler=e}providerResolver;setProviderResolver(e){this.providerResolver=e}hasStaleRelayEnv(){return this.relayEnvStale}markRelayEnvStale(){this.relayEnvStale=!0}async probe(e={}){const n=e.conversation?"full":"static",t=e.conversation?si:ni;if(!e.fresh){const a=this.probeCache.get(n);if(a&&Date.now()-a.sampledAt<t)return{...a.result,cached:!0}}const o=this.config.adapterType??"acp",i=this.createAdapter(o,"__probe__"),s=e.conversation&&o==="acp"?()=>this.runAcpConversationProbe(i,e.timeoutMs??1e4):void 0;let r;try{r=await ii({adapter:i,agentName:this.name,clientType:this.config.aibot.clientType,adapterType:o,providerBaseUrl:this.config.providerBaseUrl??null,opts:e,launchConversationProbe:s})}finally{i.stop().catch(()=>{})}return this.probeCache.set(n,{result:r,sampledAt:r.probed_at}),r}async runAcpConversationProbe(e,n){const t=Date.now(),o="__probe__",i=`probe-${Date.now()}-${Math.random().toString(36).slice(2,8)}`,s=this.config.agent.cwd||be(),r=()=>Date.now()-t;try{await e.start()}catch(u){return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_failed",message:`start failed: ${u instanceof Error?u.message:String(u)}`}}}if(!e.isAlive())return{attempted:!0,ok:!1,latency_ms:r(),error:{code:"process_not_started",message:"agent process not alive"}};if(e instanceof E)try{await e.bindSession(o,s)}catch{}let a=null;const d=new Promise(u=>{a=g=>{g===i&&u()},e.on("eventDone",a)});e.deliverInboundEvent({event_id:i,session_id:o,content:"ping",msg_id:i}),e.deliverStopEvent(i,o);let c=null;const l=await Promise.race([d.then(()=>!1),new Promise(u=>{c=setTimeout(()=>u(!0),n)})]);return c&&clearTimeout(c),a&&e.removeListener("eventDone",a),l?{attempted:!0,ok:!1,latency_ms:r(),error:{code:"conversation_timeout",message:`no eventDone within ${n}ms`}}:{attempted:!0,ok:!0,latency_ms:r()}}constructor(e,n,t=new ai){this.config=e,this.auditController=new oi(t,{onAuditState:i=>{try{this.aibotHandle.sendAuditState(X(i,Date.now()))}catch{}}}),ri(t)&&t.addJobSettledHandler((i,s)=>{this.auditController.handleJobSettled(i,s)}),this.name=e.name;const o=e.adapterType??"acp";if(this.sendCtrl.setDefaultQuoteTrigger(zt[o]),this.aibotConfig={...e.aibot,...o==="claude"?{localActions:e.aibot.localActions??["session_control","claude_interaction_reply","get_session_usage","get_rate_limits","set_model","set_mode","set_reasoning_effort","thread_compact","get_agent_global_config",...Ae]}:{}},e.eventQueue&&(this.aibotConfig.concurrency={max_concurrent:e.eventQueue.maxConcurrent,max_queued:e.eventQueue.maxQueued,queue_timeout_ms:e.eventQueue.queueTimeoutMs,cancelable_queued:e.eventQueue.cancelableQueued,cancelable_running:e.eventQueue.cancelableRunning}),this.conversationLog=e.logDir?new Et(e.logDir):null,this.packetLog=e.logDir?new yt(e.logDir):null,this.bindingStore=new Qt(e.bindingsPath,e.legacyBindingsPath),this.bindingStore.load(),this.globalConfigStore=n??null,this.deferredMgr=new ti(this.name),this.activeEventStore=e.activeEventStorePath?new Nt(e.activeEventStorePath):null,o==="codex")this.sessionScanCache=new y(at,vt);else if(o==="claude")this.sessionScanCache=new y(dt,St);else if(o==="pi"){const i=e.agent.env?.PI_CODING_AGENT_DIR;this.sessionScanCache=new y(()=>gt(void 0,i),()=>wt(void 0,i))}else if(o==="codewhale")this.sessionScanCache=new y(pt,Ct);else if(o==="cursor")this.sessionScanCache=new y(ft,At);else if(o==="opencode")this.sessionScanCache=new y(mt,kt);else if(o==="agy")this.sessionScanCache=new y(()=>[],()=>[]);else{e.aibot.clientType?.trim().toLowerCase()==="reasonix"&&(this.reasonixTitleScan=new y(ht,Rt));const i=lt(e.aibot.clientType);this.sessionScanCache=i?new y(()=>ut(void 0,i),()=>bt(void 0,i)):new y(()=>[],()=>[])}}async start(){await nt({agentName:this.name,adapterType:this.config.adapterType??"acp",command:this.config.agent.command,piConfigDir:this.config.agent.env?.PI_CODING_AGENT_DIR,hasProvider:!!this.config.agent.provider,boundCwds:[...this.bindingStore.entries()].map(([,i])=>String(i.cwd??""))}).then(i=>{i.length>0&&h.info(this.name,`restored native provider config after relay disable: ${i.join(", ")}`)}).catch(i=>{h.warn(this.name,`native provider residue sweep failed: ${i instanceof Error?i.message:String(i)}`)}),(this.config.adapterType??"acp")==="claude"&&(await G().catch(()=>{}),this.maybeQueryProviderQuota().catch(()=>{}));const e=!!(this.config.providerBaseUrl&&this.config.providerApiKey||this.config.agent.provider?.baseUrl&&this.config.agent.provider?.apiKey),n=(this.config.adapterType??"acp")==="pi",t=(this.config.adapterType??"acp")==="opencode";this.config.aibot.clientType==="kiro"||this.config.aibot.clientType==="kimi"||e?(this.maybeQueryProviderQuota().catch(()=>{}),this.startProviderQuotaTimer()):(n||t)&&this.startProviderQuotaTimer(),(this.config.adapterType??"acp")==="codex"&&this.maybeQueryProviderQuota().catch(()=>{}),(this.config.adapterType??"acp")==="opencode"&&this.maybeQueryProviderQuota().catch(()=>{}),await this.connectAibot(),this.sendCtrl.bind(this.aibotHandle);const o=this.config.adapterType??"acp";this.pool=new Tt({maxPoolSize:this.config.poolMaxSize??20,idleTimeoutMs:this.config.poolIdleTimeoutMs??18e5,eventQueue:this.config.eventQueue},i=>{const s=this.createAdapter(o,i);return s instanceof E&&s.on("acpSessionReady",r=>{this.bindingStore.setAcpSessionId(i,r),this.sessionScanCache.invalidate()}),s},(i,s)=>{this.aibotHandle.sendEventAck({event_id:i,session_id:s,received_at:Date.now()})}),this.pool.setEventStateHandler((i,s,r,a)=>{if(h.info(this.name,`[queue-debug] send event_state session=${s} event=${i} state=${r} queue_pos=${a?.queue_position??""} queue_total=${a?.queue_total??""}`),this.aibotHandle.sendEventState({event_id:i,session_id:s,state:r,content_preview:a?.content_preview,content:a?.content,queue_position:a?.queue_position,queue_total:a?.queue_total,actions:a?.actions,reason:a?.reason,held:a?.held,held_reason:a?.held_reason,updated_at:Date.now()}),this.pushQueueSnapshotForSession(s),r==="canceled"||r==="failed"){const d=r==="canceled"?"canceled":"failed";this.aibotHandle.sendEventResult({event_id:i,status:d,msg:a?.reason,updated_at:Date.now()}),this.auditController.closeWithoutAdapter(i,d,a?.reason,{queueTerminalState:r}),this.discardEventTrackingState(i)}}),this.pool.setQueueComposingHandler((i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:i,kind:"composing",active:s,...s?{ttl_ms:3e4,ref_event_id:r}:{}})}),this.pool.setInternalErrorHandler(i=>{this.handleSessionInternalError(i).catch(s=>{h.error(this.name,`[recovery] handleSessionInternalError failed event=${i.eventId} session=${i.sessionId}: ${s instanceof Error?s.message:String(s)}`)})}),this.pool.setEventStartedHandler((i,s)=>{if(this.reportSessionSkills(s),this.config.adapterType!=="claude")return;this.claudeWorkerStatus.set(s,"busy");const r=this.bindingStore.get(s);r?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"busy",cwd:r.cwd,meta:this.buildClaudeToolbarMeta(s)})}),this.pool.setEventDoneHandler((i,s)=>{this.markAuditAdapterClosed(i,s);const r=this.config.adapterType??"acp";if(r==="claude"){this.claudeWorkerStatus.set(s,"ready");const a=this.bindingStore.get(s);a?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"ready",cwd:a.cwd,meta:this.buildClaudeToolbarMeta(s)})}else if(r==="agy"){const a=this.bindingStore.get(s);if(a?.cwd){const d=this.buildAgyToolbarMeta(s),c=d?.available_models??[];h.info(this.name,`[agy-toolbar-diag] eventDone push binding card session=${s} model_id=${String(d?.model_id??"")} available_models=${c.length} meta_keys=${d?Object.keys(d).join(","):"<none>"}`),this.aibotHandle.sendUpdateBindingCard({session_id:s,worker_status:"ready",cwd:a.cwd,meta:d})}else h.info(this.name,`[agy-toolbar-diag] eventDone skip binding card: no binding cwd session=${s}`)}}),this.pool.setSessionActivityHandler((i,s,r)=>{const a=this.selfDrivenSessions.has(i),d=this.selfDrivenLabels.get(i);s?(this.selfDrivenSessions.add(i),r&&this.selfDrivenLabels.set(i,r),this.aibotHandle.sendSessionActivitySet({session_id:i,kind:"composing",active:!0,ttl_ms:9e4})):(this.selfDrivenSessions.delete(i),this.selfDrivenLabels.delete(i),a&&this.aibotHandle.sendSessionActivitySet({session_id:i,kind:"composing",active:!1})),(a!==s||s&&r!==void 0&&r!==d)&&this.pushQueueSnapshotForSession(i)}),this.pool.startIdleSweep(),h.info(this.name,`Ready (adapter: ${o}, poolMax: ${this.config.poolMaxSize??20})`)}async stop(){this.stopped=!0,this.pool?.stopIdleSweep(),this.stopProviderQuotaTimer();const e=this.pool?.collectActiveEventIds()??[],n=new Map(e.map(i=>[i,this.eventSessionIndex.get(i)]));e.length>0&&this.activeEventStore&&await this.activeEventStore.save(e);for(const i of e)h.info(this.name,`Canceling active event on shutdown: ${i}`),this.sendEventResultWithCleanup(i,"canceled","process shutting down");e.length>0&&await new Promise(i=>setTimeout(i,100)),this.pool?.clearActiveEventsForShutdown();for(const i of e){const s=n.get(i),r=s?this.pool?.getSlot(s):void 0;this.auditController.markAdapterClosed(i,r?.adapter.takeAuditBoundary?.(i)??{processTerminated:!0})}const t=this.deferredMgr.getAllDeferredEvents();for(const i of t)h.info(this.name,`Failing deferred event on shutdown: ${i.event_id}`),this.sendEventResultWithCleanup(i.event_id,"failed","process shutting down"),this.auditController.markAdapterClosed(i.event_id,{adapterNotStarted:!0,processTerminated:!0});const o=this.pool?.drainAllQueuedEvents()??[];for(const i of o)h.info(this.name,`Failing queued event on shutdown: ${i.event_id}`),this.sendEventResultWithCleanup(i.event_id,"failed","process shutting down"),this.auditController.markAdapterClosed(i.event_id,{adapterNotStarted:!0,processTerminated:!0});this.deferredMgr.clearAll(),await this.pool?.stop(),this.aibotHandle?.disconnect(),e.length>0&&this.activeEventStore&&await this.activeEventStore.save([]),this.eventSessionIndex.clear(),this.inflightEvents.clear(),this.restartCount.clear(),this.auditController.dispose()}resolveSpawnEnv(){const e=this.providerResolver?this.providerResolver():this.config.agent.provider,n=et(this.config.agent.clientType,e),t=this.config.agent.env;let o=Object.keys(n).length>0?{...n,...t}:t;if(tt(n)){const i=ae()?.getRuntimeInfo(),s={...t??{},...n},r=it(s,{proxyUrl:i?.proxyUrl,caCertPath:i?.caCertPath,caBundlePath:i?.caBundlePath});return delete r.ANTHROPIC_API_KEY,delete r.CLAUDE_CODE_OAUTH_TOKEN,r}if(de(this.config.agent.clientType)){const i=Ve(this.name);i&&(o={...o??{},...i})}return Ge(o,this.name,this.config.agent.clientType)}createAdapter(e,n){switch(e){case"claude":return this.createClaudeAdapter(n);case"codex":return this.createCodexAdapter(n);case"pi":return this.createPiAdapter(n);case"openhuman":return this.createOpenHumanAdapter(n);case"codewhale":return this.createCodeWhaleAdapter(n);case"cursor":return this.createCursorAdapter(n);case"opencode":return this.createOpenCodeAdapter(n);case"agy":return this.createAgyAdapter(n);default:return this.createAcpAdapter(n)}}createCursorAdapter(e){const n={...this.config.adapterOptions??{}};n.bindingStore=this.bindingStore,n.aibotSessionId=e;const t=this.bindingStore.get(e),o=this.resolveSessionModelId(e);o&&(n.model=o);const i=this.bindingStore.getCursorModeId(e),s=i??"full_auto";i||this.bindingStore.setCursorModeId(e,s),n.mode=s,t?.cwd&&(n.workspace=t.cwd);const r={sendStreamChunk:(a,d,c,l,u,g,v)=>{this.sendStreamChunkByRuntimeConfig(a,d,c,l,u,g,v)},sendEventResult:(a,d,c)=>{this.sendEventResultWithCleanup(a,d,c)},sendEventAck:(a,d)=>{this.aibotHandle.sendEventAck({event_id:a,session_id:d,received_at:Date.now()})},sendRawEventEnvelope:(a,d,c)=>{this.deliverRawEventEnvelope(a,d,c,"cursor",this.buildCursorRawEventFallbackText(c))},sendAgentQuestionCard:(a,d,c)=>{const l=c.questions.map(g=>g.header).join(", "),u=_e(`[Agent Question] ${c.request_id}`,"agent_question",c);this.aibotHandle.sendText({event_id:a,session_id:d,content:u,msg_type:1,extra:{card_type:"agent_question",summary_text:l}})},agentInvoke:async(a,d,c)=>this.platformInvoke(a,d,c),sendLocalActionResult:(a,d,c,l,u)=>{this.aibotHandle.sendLocalActionResult({action_id:a,status:d,...c!==void 0?{result:c}:{},...l?{error_code:l}:{},...u?{error_msg:u}:{}})},sendUpdateBindingCard:(a,d,c,l)=>{this.aibotHandle.sendUpdateBindingCard({session_id:a,worker_status:d,cwd:c,...l?{meta:l}:{}})},getAgentProfile:()=>this.agentProfile};return new B({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:n},r)}createClaudeAdapter(e){const n={sendReply:(i,s,r,a,d)=>{this.sendReplyByRuntimeConfig(i,s,r,a,d)},sendStreamChunk:(i,s,r,a,d,c,l)=>{this.sendStreamChunkByRuntimeConfig(i,s,r,a,d,c,l)},sendMedia:(i,s,r,a,d,c,l)=>{this.aibotHandle.sendMedia({event_id:i,session_id:s,content:r,msg_type:2,quoted_message_id:d||void 0,client_msg_id:c||void 0,extra:l?{media_caption:a,...l}:{media_caption:a}})},sendEventResult:(i,s,r,a)=>{this.sendEventResultWithCleanup(i,s,r,a)},sendEventAck:(i,s)=>{this.aibotHandle.sendEventAck({event_id:i,session_id:s,received_at:Date.now()})},agentInvoke:async(i,s,r)=>this.platformInvoke(i,s,r),sendLocalActionResult:(i,s,r,a,d)=>{this.aibotHandle.sendLocalActionResult({action_id:i,status:s,...r!==void 0?{result:r}:{},...a?{error_code:a}:{},...d?{error_msg:d}:{}})},sendToolUse:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,L(r,a))},sendToolResult:(i,s,r,a)=>{this.sendToolExecutionCard(i,s,H(r,a))},getWsUrl:()=>this.config.aibot.url,getAgentId:()=>this.config.aibot.agentId,getAgentProfile:()=>this.agentProfile,getApiKey:()=>this.config.aibot.apiKey,getActiveEventCount:()=>0,getPendingPermissionCount:()=>0,getPendingElicitationCount:()=>0,sendAgentQuestionCard:(i,s,r)=>{this.sendGrixAgentQuestionCard(i,s,r)},sendPermissionCard:i=>{this.sendGrixApprovalCard(i,"claude")},sendDirectMessage:i=>{this.aibotHandle.sendMsg({session_id:i.sessionId,msg_type:1,content:i.content,...i.clientMsgId?{client_msg_id:i.clientMsgId}:{},...i.quotedMessageId?{quoted_message_id:i.quotedMessageId}:{}})},onStatusLineUpdated:i=>{(i.rateLimits?.fiveHour||i.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=i);const s=this.bindingStore.get(e);s?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:this.claudeWorkerStatus.get(e)??"ready",cwd:s.cwd,meta:this.buildClaudeToolbarMeta(e)})},sendMcpFrame:i=>{this.aibotHandle.sendMcpFrame(e,i)}},t=this.config.adapterOptions??{},o={...t,sessionRuntimeResolver:()=>{const i=this.bindingStore.get(e),s=this.auditController.getSession(e);return{cwd:i?.cwd,modeId:i?.modeId??R.fullAuto,modelId:this.resolveSessionModelId(e),effort:this.resolveClaudeSessionEffort(e),pluginDir:t.pluginDir,claudeSessionId:i?.claudeSessionId,...s?{audit:{enabled:!0,profile:s.options.profile,capture:s.options.capture}}:{},onSessionIdAssigned:r=>{this.bindingStore.setClaudeSessionId(e,r),this.sessionScanCache.invalidate()}}}};return new M({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:o},n)}createCodexAdapter(e){let n=null;const t={sendEventResult:(r,a,d)=>{if(a!=="responded"){this.sendEventResultWithCleanup(r,a,d);return}this.sendCtrl.sendFinalStreamChunkReliable(r,e,`codex_terminal_fence_${r}`,5e3,!0).then(()=>this.sendEventResultWithCleanup(r,a,d),c=>{const l=`codex output acceptance fence failed: ${c instanceof Error?c.message:String(c)}`;this.sendEventResultWithCleanup(r,a==="responded"?"failed":a,a==="responded"?l:d)})},sendEventAck:(r,a)=>this.aibotHandle.sendEventAck({event_id:r,session_id:a,received_at:Date.now()}),sendCodexEvent:r=>{this.shouldDropCodexDisplayEvent(r.event_id,r.codex_method)||(this.captureCodexAuditOutput(r),this.aibotHandle.sendCodexEvent(this.stampCodexEventQuote(r)),this.logCodexEventToConversation(r))},sendRunError:(r,a,d)=>{this.sendStreamChunkByRuntimeConfig(r,a,`
|
|
2
2
|
|
|
3
|
-
Error: ${d}`,1,!1)},sendUpdateBindingCard:(r,a,d,c)=>{const l={...c??{}};if(!l.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const u=this.providerQuotaToCodexRateLimits(this.cachedProviderQuota);u&&(l.rate_limits=u.rateLimits,l.rate_limit_primary_percent=u.primaryPercent,l.rate_limit_secondary_percent=u.secondaryPercent,l.rate_limit_primary_window_min=u.primaryWindowMin,l.rate_limit_secondary_window_min=u.secondaryWindowMin)}!l.provider_quota&&this.cachedProviderQuota?.success&&(l.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:r,worker_status:a,cwd:d,...Object.keys(l).length>0?{meta:l}:{}})},agentInvoke:async(r,a,d)=>this.platformInvoke(r,a,d),sendLocalActionResult:(r,a,d,c,l)=>this.aibotHandle.sendLocalActionResult({action_id:r,status:a,...d!==void 0?{result:d}:{},...c?{error_code:c}:{},...l?{error_msg:l}:{}}),sendSessionActivitySet:(r,a,d,c)=>{this.aibotHandle.sendSessionActivitySet({session_id:r,kind:a,active:d,...c??{}})},getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile,onRateLimitsUpdated:r=>{this.cachedRateLimits=r,this.cachedRateLimitsSampledAtMs=Date.now(),this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const a=this.bindingStore.get(e);if(a?.cwd){const d=this.cachedRateLimitsSampledAtMs,c={rate_limits:{...r.primary.windowMinutes>0?{primary:r.primary}:{},...r.secondary.windowMinutes>0?{secondary:r.secondary}:{},sampledAt:d},rate_limit_primary_percent:r.primary.usedPercent,rate_limit_secondary_percent:r.secondary.usedPercent,rate_limit_primary_window_min:r.primary.windowMinutes,rate_limit_secondary_window_min:r.secondary.windowMinutes,credits:r.credits,...r.extras&&r.extras.length>0?{extra_limits:r.extras}:{},...n?.getEffortMeta()};this.cachedProviderQuota?.success&&(c.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:a.cwd,meta:c})}},onContextWindowUpdated:r=>{if(!r)return;this.cachedCodexContextWindow=r,this.cachedCodexUsageSampledAtMs=Date.now();const a=this.bindingStore.get(e);a?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:a.cwd,meta:{context_window:r,...n?.getEffortMeta()}})},onTokenUsageUpdated:r=>{r&&(this.cachedCodexTokenUsage=r,this.cachedCodexUsageSampledAtMs=Date.now())}},o=this.config.adapterOptions??{},i=this.globalConfigStore?.get(this.name),s=this.auditController.getAdapterPreparation(e);return n=new te({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...o,model:this.resolveCodexSessionModelId(e)??o.model,collaborationMode:this.bindingStore.getCodexModeId(e)??this.resolveCodexNewSessionGlobalDefault(e,i?.codexModeId,r=>this.bindingStore.setCodexModeId(e,r))??o.collaborationMode,reasoningEffort:i?.codexReasoningEffort??o.reasoningEffort,serviceTier:this.bindingStore.getCodexServiceTier(e)??this.resolveCodexNewSessionGlobalDefault(e,i?.codexServiceTier,r=>this.bindingStore.setCodexServiceTier(e,r))??o.serviceTier,sandboxMode:i?.codexSandboxMode??o.sandboxMode,aibotSessionId:e,bindingStore:this.bindingStore,...se(s?.options)?{rawApiCapture:{auditId:s.auditId,sessionId:e,spoolRootDir:
|
|
3
|
+
Error: ${d}`,1,!1)},sendUpdateBindingCard:(r,a,d,c)=>{const l={...c??{}};if(!l.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const u=this.providerQuotaToCodexRateLimits(this.cachedProviderQuota);u&&(l.rate_limits=u.rateLimits,l.rate_limit_primary_percent=u.primaryPercent,l.rate_limit_secondary_percent=u.secondaryPercent,l.rate_limit_primary_window_min=u.primaryWindowMin,l.rate_limit_secondary_window_min=u.secondaryWindowMin)}!l.provider_quota&&this.cachedProviderQuota?.success&&(l.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:r,worker_status:a,cwd:d,...Object.keys(l).length>0?{meta:l}:{}})},agentInvoke:async(r,a,d)=>this.platformInvoke(r,a,d),sendLocalActionResult:(r,a,d,c,l)=>this.aibotHandle.sendLocalActionResult({action_id:r,status:a,...d!==void 0?{result:d}:{},...c?{error_code:c}:{},...l?{error_msg:l}:{}}),sendSessionActivitySet:(r,a,d,c)=>{this.aibotHandle.sendSessionActivitySet({session_id:r,kind:a,active:d,...c??{}})},getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile,onRateLimitsUpdated:r=>{this.cachedRateLimits=r,this.cachedRateLimitsSampledAtMs=Date.now(),this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const a=this.bindingStore.get(e);if(a?.cwd){const d=this.cachedRateLimitsSampledAtMs,c={rate_limits:{...r.primary.windowMinutes>0?{primary:r.primary}:{},...r.secondary.windowMinutes>0?{secondary:r.secondary}:{},sampledAt:d},rate_limit_primary_percent:r.primary.usedPercent,rate_limit_secondary_percent:r.secondary.usedPercent,rate_limit_primary_window_min:r.primary.windowMinutes,rate_limit_secondary_window_min:r.secondary.windowMinutes,credits:r.credits,...r.extras&&r.extras.length>0?{extra_limits:r.extras}:{},...n?.getEffortMeta()};this.cachedProviderQuota?.success&&(c.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:a.cwd,meta:c})}},onContextWindowUpdated:r=>{if(!r)return;this.cachedCodexContextWindow=r,this.cachedCodexUsageSampledAtMs=Date.now();const a=this.bindingStore.get(e);a?.cwd&&this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:a.cwd,meta:{context_window:r,...n?.getEffortMeta()}})},onTokenUsageUpdated:r=>{r&&(this.cachedCodexTokenUsage=r,this.cachedCodexUsageSampledAtMs=Date.now())}},o=this.config.adapterOptions??{},i=this.globalConfigStore?.get(this.name),s=this.auditController.getAdapterPreparation(e);return n=new te({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...o,model:this.resolveCodexSessionModelId(e)??o.model,collaborationMode:this.bindingStore.getCodexModeId(e)??this.resolveCodexNewSessionGlobalDefault(e,i?.codexModeId,r=>this.bindingStore.setCodexModeId(e,r))??o.collaborationMode,reasoningEffort:i?.codexReasoningEffort??o.reasoningEffort,serviceTier:this.bindingStore.getCodexServiceTier(e)??this.resolveCodexNewSessionGlobalDefault(e,i?.codexServiceTier,r=>this.bindingStore.setCodexServiceTier(e,r))??o.serviceTier,sandboxMode:i?.codexSandboxMode??o.sandboxMode,aibotSessionId:e,bindingStore:this.bindingStore,...se(s?.options)?{rawApiCapture:{auditId:s.auditId,sessionId:e,spoolRootDir:T.join(P.data,"audit-replay","raw-spool")}}:{}}},t),n}createCodeWhaleAdapter(e){const n={sendEventResult:(o,i,s)=>{this.sendEventResultWithCleanup(o,i,s)},sendEventAck:(o,i)=>this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()}),sendStreamChunk:(o,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(o,i,s,r,a,d)},sendUpdateBindingCard:(o,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:s,...this.providerQuotaMetaPayload(r,o,s)}),sendLocalActionResult:(o,i,s,r,a)=>this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}}),sendSessionActivitySet:(o,i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:o,kind:i,active:s,...r??{}})},sendToolUse:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,L(s,r))},sendToolResult:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,H(s,r))},agentInvoke:async(o,i,s)=>this.platformInvoke(o,i,s),getConversationLog:()=>this.conversationLog,getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new ie({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...t,aibotSessionId:e,bindingStore:this.bindingStore,agentName:this.name,...this.config.agent.provider?{provider:this.config.agent.provider}:{}}},n)}createPiAdapter(e){const n={sendEventResult:(o,i,s)=>{this.sendEventResultWithCleanup(o,i,s),h.info("bridge",`[pi] sendEventResult event=${o} status=${i}`)},sendEventAck:(o,i)=>this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()}),sendUpdateBindingCard:(o,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:s,...this.providerQuotaMetaPayload(r,o,s)}),agentInvoke:async(o,i,s)=>this.platformInvoke(o,i,s),sendLocalActionResult:(o,i,s,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})},sendSessionActivitySet:(o,i,s,r)=>{this.aibotHandle.sendSessionActivitySet({session_id:o,kind:i,active:s,...r??{}})},sendToolUse:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,L(s,r))},sendToolResult:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,H(s,r))},sendStreamChunk:(o,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(o,i,s,r,a,d),a&&h.info("bridge",`[pi] sendFinalStreamChunk event=${o} seq=${r}`)},sendFinalStreamChunkReliable:this.reliableFinalWiring("pi"),sendThinking:(o,i,s)=>{this.sendThinkingByRuntimeConfig(o,i,s)},sendRunError:(o,i,s,r,a)=>{this.sendStreamChunkByRuntimeConfig(o,i,`
|
|
4
4
|
|
|
5
5
|
Error: ${s}`,r,!1,a)},getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new ke({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:{...t,aibotSessionId:e,bindingStore:this.bindingStore,agentName:this.name,...this.config.agent.provider?{provider:this.config.agent.provider}:{}}},n)}createOpenHumanAdapter(e){const n={sendStreamChunk:(o,i,s,r,a,d)=>{this.finalizeThinking(o,i),this.sendStreamChunkByRuntimeConfig(o,i,s,r,a,d)},sendFinalStreamChunkReliable:async(o,i,s,r)=>{try{await this.sendCtrl.sendFinalStreamChunkReliable(o,i,r)}catch(a){throw h.error("bridge",`[openhuman] sendFinalStreamChunkReliable ACK failed event=${o}: ${a}`),a}},sendEventResult:(o,i,s)=>{this.sendEventResultWithCleanup(o,i,s),h.info("bridge",`[openhuman] sendEventResult event=${o} status=${i}`)},sendEventAck:(o,i)=>this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()}),sendToolUse:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,L(s,r))},sendToolResult:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,H(s,r))},sendThinking:(o,i,s)=>{this.sendThinkingByRuntimeConfig(o,i,s)},sendRunError:(o,i,s)=>{this.sendStreamChunkByRuntimeConfig(o,i,`
|
|
6
6
|
|
|
7
7
|
Error: ${s}`,0,!1)},sendUpdateBindingCard:(o,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:s,...this.providerQuotaMetaPayload(r,o,s)}),agentInvoke:async(o,i,s)=>this.platformInvoke(o,i,s),sendLocalActionResult:(o,i,s,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})},getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new Re({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:t},n,{port:t.port,host:t.host,workspaceDir:t.workspace_dir,sessionToken:t.session_token,enableSessionBinding:!0,aibotSessionId:e})}createOpenCodeAdapter(e){const n={sendStreamChunk:(o,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(o,i,s,r,a,d)},sendFinalStreamChunkReliable:this.reliableFinalWiring("opencode"),sendEventResult:(o,i,s)=>{this.sendEventResultWithCleanup(o,i,s),h.info("bridge",`[opencode] sendEventResult event=${o} status=${i}`)},sendEventAck:(o,i)=>this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()}),sendToolUse:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,L(s,r))},sendToolResult:(o,i,s,r)=>{this.sendToolExecutionCard(o,i,H(s,r))},sendThinking:(o,i,s)=>{this.sendThinkingByRuntimeConfig(o,i,s)},sendRunError:(o,i,s)=>{this.sendRunErrorAsChunk(o,i,s)},sendUpdateBindingCard:(o,i,s,r)=>this.aibotHandle.sendUpdateBindingCard({session_id:o,worker_status:i,cwd:s,...this.providerQuotaMetaPayload(r,o,s)}),agentInvoke:async(o,i,s)=>this.platformInvoke(o,i,s),sendLocalActionResult:(o,i,s,r,a)=>{this.aibotHandle.sendLocalActionResult({action_id:o,status:i,...s!==void 0?{result:s}:{},...r?{error_code:r}:{},...a?{error_msg:a}:{}})},sendPermissionCard:o=>{this.sendGrixApprovalCard(o,"opencode")},sendAgentQuestionCard:(o,i,s)=>{this.sendGrixAgentQuestionCard(o,i,s)},getAgentProfile:()=>this.agentProfile},t=this.config.adapterOptions??{};return new K({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:t},n,{port:t.port,hostname:t.hostname,model:t.model??this.resolveSessionModelId(e),agent:t.agent,permissionPolicy:t.permission_policy,enableSessionBinding:!0,aibotSessionId:e,bindingStore:this.bindingStore,...this.config.agent.provider?{provider:this.config.agent.provider}:{}})}createAgyAdapter(e){const n={sendStreamChunk:(o,i,s,r,a,d)=>{this.sendStreamChunkByRuntimeConfig(o,i,s,r,a,d)},sendEventResult:(o,i,s)=>{this.sendEventResultWithCleanup(o,i,s)},sendEventAck:(o,i)=>{this.aibotHandle.sendEventAck({event_id:o,session_id:i,received_at:Date.now()})},agentInvoke:async(o,i,s)=>this.platformInvoke(o,i,s),forceCompleteInternalEvent:(o,i)=>{this.pool.eventComplete(o,i),this.pushQueueSnapshotForSession(i)},persistConversationId:(o,i)=>{this.bindingStore.setAgyConversationId(o,i)},getAgentProfile:()=>this.agentProfile,getAgentId:()=>this.config.aibot.agentId},t=o=>{const i=this.bindingStore.get(o);return{cwd:i?.cwd,modelId:this.resolveSessionModelId(o),conversationId:i?.agyConversationId}};return new ne({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv(),options:this.config.adapterOptions??{}},n,t)}createAcpAdapter(e){const n=this.isAcpRawTransportEnabled(),t={sendStreamChunk:(c,l,u,g,v,f,S)=>{this.finalizeThinking(c,l),this.sendStreamChunkByRuntimeConfig(c,l,u,g,v,f,S)},sendFinalStreamChunkReliable:this.reliableFinalWiring("acp",4e3),sendEventResult:(c,l,u)=>{this.sendEventResultWithCleanup(c,l,u)},sendEventAck:(c,l)=>{this.aibotHandle.sendEventAck({event_id:c,session_id:l,received_at:Date.now()})},agentInvoke:async(c,l,u)=>this.platformInvoke(c,l,u),sendLocalActionResult:(c,l,u,g,v)=>{this.aibotHandle.sendLocalActionResult({action_id:c,status:l,...u!==void 0?{result:u}:{},...g?{error_code:g}:{},...v?{error_msg:v}:{}})},sendRawEventEnvelope:(c,l,u)=>{this.sendAcpRawEventEnvelope(c,l,u)},sendToolUse:(c,l,u,g)=>{if(n){this.sendAcpRawEventEnvelope(c,l,{type:"tool_use",payload:{tool_name:u,tool_input:g??""}});return}this.sendToolExecutionCard(c,l,L(u,g))},sendToolResult:(c,l,u,g)=>{this.sendToolExecutionCard(c,l,H(u,g))},sendThinking:(c,l,u)=>{this.sendThinkingByRuntimeConfig(c,l,u)},sendRunError:(c,l,u)=>{this.sendStreamChunkByRuntimeConfig(c,l,`
|
|
8
8
|
|
|
9
|
-
Error: ${u}`,1,!1)},sendPermissionCard:c=>{h.info("bridge","sendPermissionCard callback entered",{rawTransport:n,eventId:c.eventId,sessionId:c.sessionId,toolCallId:c.toolCallId,toolName:c.toolName,toolTitle:c.toolTitle});const l=c.toolInput&&c.toolTitle&&c.toolInput!==c.toolTitle?`${c.toolTitle}: ${c.toolInput}`:c.toolTitle||c.toolInput||c.toolName;if(n){this.sendAcpRawEventEnvelope(c.eventId,c.sessionId,{type:"permission_request",payload:{tool_call_id:c.toolCallId,tool_name:c.toolName,tool_title:c.toolTitle,...c.toolInput?{tool_input:c.toolInput}:{},options:c.options}}),h.info("bridge","sendPermissionCard: sent via rawEventEnvelope",{eventId:c.eventId,toolCallId:c.toolCallId});return}const u=`perm_${W()}`,g={event_id:c.eventId,session_id:c.sessionId,client_msg_id:u,msg_type:1,content:c.toolTitle?`Permission required: ${c.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:c.toolCallId,approvalSlug:c.toolName},grix:{execApproval:{approval_command_id:c.toolCallId,command:l,host:"acp"}}},agent_api_origin:!0}};h.info("bridge","sendPermissionCard: about to invoke aibotHandle.sendMsg",{eventId:c.eventId,sessionId:c.sessionId,clientMsgId:u,toolCallId:c.toolCallId,contentLength:g.content.length});try{this.aibotHandle.sendMsg(g),h.info("bridge","sendPermissionCard: aibotHandle.sendMsg returned",{eventId:c.eventId,clientMsgId:u})}catch(v){h.error("bridge","sendPermissionCard: aibotHandle.sendMsg threw",{eventId:c.eventId,clientMsgId:u,error:v instanceof Error?v.message:String(v)})}},sendAuthNotification:(c,l)=>{c&&this.aibotHandle.sendMsg({session_id:c,msg_type:1,content:l,extra:{biz_card:{version:1,type:"agent_error",payload:{error:{name:"AuthRequired",message:l}}}}})},sendAgentMessage:(c,l)=>{c&&l&&this.aibotHandle.sendMsg({session_id:c,msg_type:1,content:l})},sendUpdateBindingCard:(c,l,u,g)=>{const v={...g??{}};if(!v.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const f=this.providerQuotaToRateLimits(this.cachedProviderQuota);f&&(v.rate_limits=f)}!v.provider_quota&&this.cachedProviderQuota?.success&&(v.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:c,worker_status:l,cwd:u,...Object.keys(v).length>0?{meta:v}:{}})},onSkillsUpdate:(c,l)=>{try{this.aibotHandle.sendSkillsUpdate({skills:oe(c,T.skills),library_skills:this.buildLibrarySkillsReport(l)})}catch(u){}},onContextWindowUpdated:c=>{this.cachedAcpContextWindow=c,this.cachedAcpContextWindowSampledAtMs=Date.now();const l="usedPercentage"in c?c.usedPercentage.toFixed(1):(c.used/c.size*100).toFixed(1);h.info(this.name,`[acp] context_window updated: ${l}%`);const u=this.bindingStore.get(e);if(u?.cwd){const g="usedPercentage"in c?c.usedPercentage:Math.min(100,c.used/c.size*100),v={context_window:{..."usedPercentage"in c?{}:c,usedPercentage:g,remainingPercentage:100-g}};if((this.config.aibot.clientType==="kiro"||this.config.aibot.clientType==="kimi")&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{}),v.provider_quota=this.cachedProviderQuota;const f=this.providerQuotaToRateLimits(this.cachedProviderQuota);f&&(v.rate_limits=f)}this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:u.cwd,meta:v})}},sendMcpFrame:c=>{this.aibotHandle.sendMcpFrame(e,c)},getAgentProfile:()=>this.agentProfile,getAgentId:()=>this.config.aibot.agentId},o=e?this.bindingStore.get(e):void 0,i=e?this.auditController.getSession(e):void 0,s=this.config.aibot.clientType==="reasonix"&&i?mi(this.name,e,i.auditId):void 0,r=this.globalConfigStore?.get(this.name),{initialModel:a,initialMode:d}=Dt({sessionBinding:o,globalDefaults:r,configInitialMode:this.config.acpInitialMode});return e&&a&&!o?.acpModelId&&this.bindingStore.setAcpModelId(e,a),(a||d)&&h.info(this.name,`[toolbar] hydrate from binding: session=${e} model=${a??"<none>"} mode=${d??"<none>"}`),new E({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv()},t,{acpAuthMethod:this.config.acpAuthMethod,acpInitialMode:d,acpInitialModel:a,acpMcpTools:this.config.acpMcpTools,rawTransport:n,eventResultsPath:this.config.eventResultsPath,approvalMode:this.config.approvalMode,bindingStore:this.config.enableSessionBinding?this.bindingStore:void 0,aibotSessionId:e,autoInjectArgs:this.config.autoInjectArgs,nativeProviderScope:this.name,bridgeLog:this.config.logDir?new $t(this.config.logDir,`${this.name}-${e}`):null,reasonixAuditTranscriptPath:s,agentType:this.config.aibot.clientType})}async connectAibot(){const e=new Ce;this.aibotHandle=await e.connect(this.aibotConfig,{aborted:()=>this.stopped,label:this.name,packetLog:this.packetLog,maxRetries:this.config.connectMaxRetries});const n=this.aibotHandle.authAck;if(this.applyAgentProfile(n?.agent_name,n?.introduction,{source:"auth_ack",respawnOnChange:!1}),this.aibotHandle.onEvent(t=>{this.handleAibotEvent(t).catch(o=>{h.error(this.name,`handleAibotEvent failed: ${o}`),this.aibotHandle.sendEventAck({event_id:t.event_id,session_id:t.session_id,received_at:Date.now()});const i=o instanceof Error?o.message:String(o);if(/CWD must be|Bound directory does not exist|Bound path is not a directory/i.test(i)&&t.session_id){this.bindingStore.delete(t.session_id),this.sessionBindings.delete(t.session_id),this.sessionProviderHints.delete(t.session_id),this.sessionProviderQuotas.delete(t.session_id),this.sessionProviderMeta.delete(t.session_id);const r=this.pool.getSlot(t.session_id);r?.adapter instanceof E&&r.adapter.getSessionBindings().delete(t.session_id);const a=this.config.adapterType??"acp",d=this.resolveBindingChannelKey(a);this.aibotHandle.sendMsg({event_id:t.event_id,session_id:t.session_id,msg_type:1,content:i,extra:{channel_data:{[d]:{sessionBinding:{status:"missing",reason:"binding_stale",error_code:p.invalidCwd}}}},quoted_message_id:t.msg_id});return}this.aibotHandle.sendEventResult({event_id:t.event_id,status:"failed",msg:i,updated_at:Date.now()})})}),this.aibotHandle.onStop(t=>{try{this.handleAibotStop(t)}catch(o){h.error(this.name,`handleAibotStop failed: ${o}`)}}),this.aibotHandle.onAgentDeleted(t=>{h.warn(this.name,`agent deleted on platform (source=${t?.source??"unknown"}${t?.reason?` reason=${t.reason}`:""}), notifying manager to clean up`);try{this.agentDeletedHandler?.()}catch(o){h.error(this.name,`agentDeletedHandler failed: ${o}`)}}),this.aibotHandle.onShareSet(t=>{try{this.shareSetHandler?.(Array.isArray(t?.shared_to)?t.shared_to:[])}catch(o){h.error(this.name,`onShareSet failed: ${o}`)}}),this.aibotHandle.onProfilePush(t=>{try{this.applyAgentProfile(t?.agent_name,t?.introduction,{source:"profile_push",respawnOnChange:!0})}catch(o){h.error(this.name,`onProfilePush failed: ${o}`)}}),this.aibotHandle.onSkillSync(t=>{try{h.info(this.name,`skill_sync received owner=${t?.owner_id??""} name=${t?.name??""}`),this.skillSyncHandler?.()}catch(o){h.error(this.name,`onSkillSync failed: ${o}`)}}),this.aibotHandle.onRevoke(t=>{try{this.handleAibotRevoke(t)}catch(o){h.error(this.name,`handleAibotRevoke failed: ${o}`)}}),this.aibotHandle.onLocalAction(t=>{this.handleAibotLocalAction(t).catch(o=>{h.error(this.name,`handleAibotLocalAction failed: ${o}`)})}),this.aibotHandle.onEventCancel(t=>{h.info(this.name,`recv event_cancel event_id=${t.event_id} session_id=${t.session_id}`),this.handleEventCancel(t).catch(o=>{h.error(this.name,`handleEventCancel failed: ${o}`)})}),this.aibotHandle.onMcpFrame((t,o)=>{const i=this.pool.getSlot(t)?.adapter;i?.deliverMcpFrameToAgent?i.deliverMcpFrameToAgent(o):h.warn(this.name,`mcp_frame: no adapter for session=${t}`)}),this.aibotHandle.onQueueClear(t=>{const o=this.pool.clearQueue(t.session_id);this.aibotHandle.sendQueueClearResult({session_id:t.session_id,canceled_event_ids:o}),this.pushQueueSnapshotForSession(t.session_id)}),typeof this.aibotHandle.onQueueReorder=="function"&&this.aibotHandle.onQueueReorder(t=>{const o=Array.isArray(t.ordered_event_ids)?t.ordered_event_ids.filter(s=>typeof s=="string"&&s.length>0):[],i=this.pool.reorderQueue(t.session_id,o);this.aibotHandle.sendQueueReorderResult({session_id:t.session_id,applied_event_ids:i}),this.pushQueueSnapshotForSession(t.session_id)}),typeof this.aibotHandle.onEventHold=="function"&&this.aibotHandle.onEventHold(t=>{const o=typeof t.session_id=="string"?t.session_id:"",i=typeof t.event_id=="string"?t.event_id:"",s=t.hold!==!1;if(h.info(this.name,`recv event_hold event_id=${i} session_id=${o} hold=${s} reason=${t.reason??""}`),!o||!i){this.aibotHandle.sendEventHoldResult({session_id:o,event_id:i,ok:!1,held:!1,error:"bad_request"});return}const r=typeof t.reason=="string"?t.reason:"",a=typeof t.ttl_ms=="number"?t.ttl_ms:void 0,d=this.pool.holdEvent(o,i,s,r,a),c=d==="ok";this.aibotHandle.sendEventHoldResult({session_id:o,event_id:i,ok:c,held:c?s:!1,...c?{}:{error:d}}),c&&this.pushQueueSnapshotForSession(o)}),typeof this.aibotHandle.onQueueEdit=="function"&&this.aibotHandle.onQueueEdit(t=>{const o=typeof t.session_id=="string"?t.session_id:"",i=typeof t.event_id=="string"?t.event_id:"";if(h.info(this.name,`recv queue_edit event_id=${i} session_id=${o}`),!o||!i){this.aibotHandle.sendQueueEditResult({session_id:o,event_id:i,ok:!1,error:"bad_request"});return}const s=typeof t.content=="string"?t.content:"",r=this.pool.editQueuedEvent(o,i,s),a=r==="ok";this.aibotHandle.sendQueueEditResult({session_id:o,event_id:i,ok:a,...a?{}:{error:r}}),a&&this.pushQueueSnapshotForSession(o)}),typeof this.aibotHandle.onQueueSnapshotQuery=="function"&&this.aibotHandle.onQueueSnapshotQuery(t=>{this.replyQueueSnapshotForSession(t.session_id)}),h.info(this.name,"Connected to aibot"),this.activeEventStore){const t=await this.activeEventStore.drain();if(t.length>0){h.warn(this.name,`Recovering ${t.length} stale event(s) from previous run`);for(const o of t)h.info(this.name,`Failing stale event on startup: ${o}`),this.aibotHandle.sendEventResult({event_id:o,status:"failed",msg:"process restarted, event lost",updated_at:Date.now()})}}this.pushQueueSnapshots(),this.relayStateSyncOnConnect(),this.aibotHandle.onReconnected(()=>{this.pushQueueSnapshots();const t=this.aibotHandle.authAck;this.applyAgentProfile(t?.agent_name,t?.introduction,{source:"reconnect",respawnOnChange:!0}),this.forceRefreshSkills(),this.relayStateSyncOnConnect()}),this.aibotHandle.onStreamRejected((t,o)=>{this.sendCtrl.markEventRejected(t)})}applyAgentProfile(e,n,t){const o=String(e??"").trim(),i=String(n??"").trim(),s=o!==this.agentProfile.agentName||i!==this.agentProfile.introduction;this.agentProfile={agentName:o,introduction:i},o||i?h.info(this.name,`agent profile (${t.source}): GOT name="${o}" intro_len=${i.length} changed=${s}`):h.warn(this.name,`agent profile (${t.source}): EMPTY \u2014 \u670D\u52A1\u7AEF\u672A\u4E0B\u53D1 agent_name/introduction\uFF08auth_ack \u5B57\u6BB5\u7F3A\u5931\u6216\u503C\u4E3A\u7A7A\uFF09`),s&&t.respawnOnChange&&this.applyProfileChangeToAdapters("agent_profile_changed")}applyProfileChangeToAdapters(e){if(!this.pool)return;const n=this.pool.getAllSlots();let t=0;for(const r of n)if(r.adapter.onAgentProfileChanged)try{r.adapter.onAgentProfileChanged(),t++}catch(a){h.warn(this.name,`onAgentProfileChanged failed for session=${r.sessionId}: ${a}`)}const o=n.filter(r=>r.adapter instanceof M),i=o.filter(r=>{if(r.state!=="ready")return!1;const a=r.adapter.getStatus();return!a.busy&&!a.backgroundBusy}),s=o.length-i.length;h.info(this.name,`${e}: notified ${t} adapter(s) via hook; respawning ${i.length} idle Claude slot(s), skipping ${s} busy`);for(const r of i)this.pool.removeSlot(r.sessionId).catch(a=>{h.warn(this.name,`removeSlot failed during ${e}: ${a}`)})}pushQueueSnapshots(){if(!(!this.config.eventQueue||!this.pool))for(const e of this.pool.getAllSlots())this.pushQueueSnapshotForSession(e.sessionId)}buildQueueSnapshotPayload(e){const n=this.pool?.getQueueSnapshot(e)??null,t=n?[...n.running]:[],o=n?n.running_items.map(s=>({event_id:s.event_id,...s.content_preview?{content_preview:s.content_preview}:{},...s.title?{title:s.title}:{},...s.summary?{summary:s.summary}:{},actions:[{type:"stop"}]})):[],i=n?n.queued.map(s=>({event_id:s.event_id,position:s.position,...s.content_preview?{content_preview:s.content_preview}:{},...typeof s.content=="string"?{content:s.content}:{},...s.title?{title:s.title}:{},...s.summary?{summary:s.summary}:{},held:s.held===!0,held_reason:s.held_reason??"",actions:[{type:"cancel"}]})):[];if(t.length===0&&this.selfDrivenSessions.has(e)&&this.pool?.getSlot(e)){const s=`selfdrive_${e}`,r=this.selfDrivenLabels.get(e)??"Background task in progress";t.push(s),o.push({event_id:s,content_preview:r,title:r,summary:r,actions:[]})}return{session_id:e,running:t,running_items:o,queued:i}}pushQueueSnapshotForSession(e){if(!this.config.eventQueue||!this.pool)return;const n=this.buildQueueSnapshotPayload(e);h.info(this.name,`[queue-debug] push snapshot session=${e} running=${n.running.length} queued=${n.queued.length} running_ids=[${n.running.join(",")}]`),this.aibotHandle.sendQueueSnapshot(n)}replyQueueSnapshotForSession(e){!this.config.eventQueue||!this.pool||this.aibotHandle.sendQueueSnapshot(this.buildQueueSnapshotPayload(e))}async platformInvoke(e,n,t){return e==="file_link"?Ot(n):e==="file_upload"?this.uploadFileAndSendMedia(n):this.aibotHandle.agentInvoke(e,n,t)}async uploadFileAndSendMedia(e){const n=String(e.file_path??"").trim(),t=String(e.session_id??"").trim(),o=String(e.caption??"").trim(),i=String(e.reply_to_message_id??"").trim();if(!n)throw new Error("file_path is required");if(!t)throw new Error("session_id is required");const s=await Ft({wsURL:this.config.aibot.url,apiKey:this.config.aibot.apiKey,sessionID:t,filePath:n}),r=await this.aibotHandle.sendMedia({session_id:t,msg_type:2,content:o||`[${s.attachment_type}]`,client_msg_id:`file_upload_${W()}`,...i?{quoted_message_id:i}:{},extra:s.extra});if(r.cmd!=="send_ack"){const d=r.payload??{},c=String(d.msg??r.cmd);throw new Error(`media message send failed: ${c}`)}const a=r.payload??{};return{ok:!0,file_name:s.file_name,attachment_type:s.attachment_type,access_url:s.access_url,message_id:a.msg_id!=null?String(a.msg_id):null}}sendReplyByRuntimeConfig(e,n,t,o,i){const s=this.indexEventSession(e,n)??n;this.auditController.captureReply(e,t),this.sendCtrl.sendReply(e,s,t,o,i),t&&this.conversationLog?.logOutbound?.(s,e,"reply",t)}stampCodexEventQuote(e){if(e.quoted_message_id!=null){if(e.quoted_message_id)return e;const{quoted_message_id:s,...r}=e;return r}const n=e;if(n.codex_method!=="item/agentMessage/delta")return e;const o=n.codex_payload?.params?.phase;if(typeof o=="string"&&o.trim()&&o.trim().toLowerCase()!=="final_answer")return e;const i=this.sendCtrl.getDefaultQuotedMessageId(e.event_id);return i?{...e,quoted_message_id:i}:e}captureCodexAuditOutput(e){if(e.codex_method!=="item/agentMessage/delta")return;const n=e.codex_payload?.params,t=typeof n?.phase=="string"?n.phase.trim().toLowerCase():"";if(t&&t!=="final_answer")return;const o=n?.delta;typeof o!="string"||!o||this.auditController.captureUnsequencedStreamChunk(e.event_id,o)}discardEventTrackingState(e){this.inflightEvents.delete(e),this.restartCount.delete(e),this.eventSessionIndex.delete(e),this.sendCtrl.discardEventState(e)}reliableFinalWiring(e,n){return async(t,o,i)=>{try{await this.sendCtrl.sendFinalStreamChunkReliable(t,o,i,n)}catch(s){throw h.error("bridge",`[${e}] sendFinalStreamChunkReliable ACK failed event=${t}: ${s}`),s}h.info("bridge",`[${e}] sendFinalStreamChunkReliable done event=${t}`)}}sendStreamChunkByRuntimeConfig(e,n,t,o,i,s,r){const a=this.indexEventSession(e,n)??n;this.auditController.captureStreamChunk(e,o,t),this.sendCtrl.sendStreamChunk(e,a,t,o,i,s,r),(t||i)&&this.conversationLog?.logOutbound?.(a,e,i?"stream_chunk_finish":"stream_chunk",t)}surfacedRunErrorEvents=new Set;sendRunErrorAsChunk(e,n,t){this.surfacedRunErrorEvents.has(e)||(this.sendStreamChunkByRuntimeConfig(e,n,`
|
|
9
|
+
Error: ${u}`,1,!1)},sendPermissionCard:c=>{h.info("bridge","sendPermissionCard callback entered",{rawTransport:n,eventId:c.eventId,sessionId:c.sessionId,toolCallId:c.toolCallId,toolName:c.toolName,toolTitle:c.toolTitle});const l=c.toolInput&&c.toolTitle&&c.toolInput!==c.toolTitle?`${c.toolTitle}: ${c.toolInput}`:c.toolTitle||c.toolInput||c.toolName;if(n){this.sendAcpRawEventEnvelope(c.eventId,c.sessionId,{type:"permission_request",payload:{tool_call_id:c.toolCallId,tool_name:c.toolName,tool_title:c.toolTitle,...c.toolInput?{tool_input:c.toolInput}:{},options:c.options}}),h.info("bridge","sendPermissionCard: sent via rawEventEnvelope",{eventId:c.eventId,toolCallId:c.toolCallId});return}const u=`perm_${W()}`,g={event_id:c.eventId,session_id:c.sessionId,client_msg_id:u,msg_type:1,content:c.toolTitle?`Permission required: ${c.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:c.toolCallId,approvalSlug:c.toolName},grix:{execApproval:{approval_command_id:c.toolCallId,command:l,host:"acp"}}},agent_api_origin:!0}};h.info("bridge","sendPermissionCard: about to invoke aibotHandle.sendMsg",{eventId:c.eventId,sessionId:c.sessionId,clientMsgId:u,toolCallId:c.toolCallId,contentLength:g.content.length});try{this.aibotHandle.sendMsg(g),h.info("bridge","sendPermissionCard: aibotHandle.sendMsg returned",{eventId:c.eventId,clientMsgId:u})}catch(v){h.error("bridge","sendPermissionCard: aibotHandle.sendMsg threw",{eventId:c.eventId,clientMsgId:u,error:v instanceof Error?v.message:String(v)})}},sendAuthNotification:(c,l)=>{c&&this.aibotHandle.sendMsg({session_id:c,msg_type:1,content:l,extra:{biz_card:{version:1,type:"agent_error",payload:{error:{name:"AuthRequired",message:l}}}}})},sendAgentMessage:(c,l)=>{c&&l&&this.aibotHandle.sendMsg({session_id:c,msg_type:1,content:l})},sendUpdateBindingCard:(c,l,u,g)=>{const v={...g??{}};if(!v.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const f=this.providerQuotaToRateLimits(this.cachedProviderQuota);f&&(v.rate_limits=f)}!v.provider_quota&&this.cachedProviderQuota?.success&&(v.provider_quota=this.cachedProviderQuota),this.aibotHandle.sendUpdateBindingCard({session_id:c,worker_status:l,cwd:u,...Object.keys(v).length>0?{meta:v}:{}})},onSkillsUpdate:(c,l)=>{try{this.aibotHandle.sendSkillsUpdate({skills:oe(c,P.skills),library_skills:this.buildLibrarySkillsReport(l)})}catch(u){}},onContextWindowUpdated:c=>{this.cachedAcpContextWindow=c,this.cachedAcpContextWindowSampledAtMs=Date.now();const l="usedPercentage"in c?c.usedPercentage.toFixed(1):(c.used/c.size*100).toFixed(1);h.info(this.name,`[acp] context_window updated: ${l}%`);const u=this.bindingStore.get(e);if(u?.cwd){const g="usedPercentage"in c?c.usedPercentage:Math.min(100,c.used/c.size*100),v={context_window:{..."usedPercentage"in c?{}:c,usedPercentage:g,remainingPercentage:100-g}};if((this.config.aibot.clientType==="kiro"||this.config.aibot.clientType==="kimi")&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{}),v.provider_quota=this.cachedProviderQuota;const f=this.providerQuotaToRateLimits(this.cachedProviderQuota);f&&(v.rate_limits=f)}this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:u.cwd,meta:v})}},sendMcpFrame:c=>{this.aibotHandle.sendMcpFrame(e,c)},getAgentProfile:()=>this.agentProfile,getAgentId:()=>this.config.aibot.agentId},o=e?this.bindingStore.get(e):void 0,i=e?this.auditController.getSession(e):void 0,s=this.config.aibot.clientType==="reasonix"&&i?mi(this.name,e,i.auditId):void 0,r=this.globalConfigStore?.get(this.name),{initialModel:a,initialMode:d}=Dt({sessionBinding:o,globalDefaults:r,configInitialMode:this.config.acpInitialMode});return e&&a&&!o?.acpModelId&&this.bindingStore.setAcpModelId(e,a),(a||d)&&h.info(this.name,`[toolbar] hydrate from binding: session=${e} model=${a??"<none>"} mode=${d??"<none>"}`),new E({command:this.config.agent.command,args:this.config.agent.args,env:this.resolveSpawnEnv()},t,{acpAuthMethod:this.config.acpAuthMethod,acpInitialMode:d,acpInitialModel:a,acpMcpTools:this.config.acpMcpTools,rawTransport:n,eventResultsPath:this.config.eventResultsPath,approvalMode:this.config.approvalMode,bindingStore:this.config.enableSessionBinding?this.bindingStore:void 0,aibotSessionId:e,autoInjectArgs:this.config.autoInjectArgs,nativeProviderScope:this.name,bridgeLog:this.config.logDir?new $t(this.config.logDir,`${this.name}-${e}`):null,reasonixAuditTranscriptPath:s,agentType:this.config.aibot.clientType})}async connectAibot(){const e=new Ce;this.aibotHandle=await e.connect(this.aibotConfig,{aborted:()=>this.stopped,label:this.name,packetLog:this.packetLog,maxRetries:this.config.connectMaxRetries});const n=this.aibotHandle.authAck;if(this.applyAgentProfile(n?.agent_name,n?.introduction,{source:"auth_ack",respawnOnChange:!1}),this.aibotHandle.onEvent(t=>{this.handleAibotEvent(t).catch(o=>{h.error(this.name,`handleAibotEvent failed: ${o}`),this.aibotHandle.sendEventAck({event_id:t.event_id,session_id:t.session_id,received_at:Date.now()});const i=o instanceof Error?o.message:String(o);if(/CWD must be|Bound directory does not exist|Bound path is not a directory/i.test(i)&&t.session_id){this.bindingStore.delete(t.session_id),this.sessionBindings.delete(t.session_id),this.sessionProviderHints.delete(t.session_id),this.sessionProviderQuotas.delete(t.session_id),this.sessionProviderMeta.delete(t.session_id);const r=this.pool.getSlot(t.session_id);r?.adapter instanceof E&&r.adapter.getSessionBindings().delete(t.session_id);const a=this.config.adapterType??"acp",d=this.resolveBindingChannelKey(a);this.aibotHandle.sendMsg({event_id:t.event_id,session_id:t.session_id,msg_type:1,content:i,extra:{channel_data:{[d]:{sessionBinding:{status:"missing",reason:"binding_stale",error_code:p.invalidCwd}}}},quoted_message_id:t.msg_id});return}this.aibotHandle.sendEventResult({event_id:t.event_id,status:"failed",msg:i,updated_at:Date.now()})})}),this.aibotHandle.onStop(t=>{try{this.handleAibotStop(t)}catch(o){h.error(this.name,`handleAibotStop failed: ${o}`)}}),this.aibotHandle.onAgentDeleted(t=>{h.warn(this.name,`agent deleted on platform (source=${t?.source??"unknown"}${t?.reason?` reason=${t.reason}`:""}), notifying manager to clean up`);try{this.agentDeletedHandler?.()}catch(o){h.error(this.name,`agentDeletedHandler failed: ${o}`)}}),this.aibotHandle.onShareSet(t=>{try{this.shareSetHandler?.(Array.isArray(t?.shared_to)?t.shared_to:[])}catch(o){h.error(this.name,`onShareSet failed: ${o}`)}}),this.aibotHandle.onProfilePush(t=>{try{this.applyAgentProfile(t?.agent_name,t?.introduction,{source:"profile_push",respawnOnChange:!0})}catch(o){h.error(this.name,`onProfilePush failed: ${o}`)}}),this.aibotHandle.onSkillSync(t=>{try{h.info(this.name,`skill_sync received owner=${t?.owner_id??""} name=${t?.name??""}`),this.skillSyncHandler?.()}catch(o){h.error(this.name,`onSkillSync failed: ${o}`)}}),this.aibotHandle.onRevoke(t=>{try{this.handleAibotRevoke(t)}catch(o){h.error(this.name,`handleAibotRevoke failed: ${o}`)}}),this.aibotHandle.onLocalAction(t=>{this.handleAibotLocalAction(t).catch(o=>{h.error(this.name,`handleAibotLocalAction failed: ${o}`)})}),this.aibotHandle.onEventCancel(t=>{h.info(this.name,`recv event_cancel event_id=${t.event_id} session_id=${t.session_id}`),this.handleEventCancel(t).catch(o=>{h.error(this.name,`handleEventCancel failed: ${o}`)})}),this.aibotHandle.onMcpFrame((t,o)=>{const i=this.pool.getSlot(t)?.adapter;i?.deliverMcpFrameToAgent?i.deliverMcpFrameToAgent(o):h.warn(this.name,`mcp_frame: no adapter for session=${t}`)}),this.aibotHandle.onQueueClear(t=>{const o=this.pool.clearQueue(t.session_id);this.aibotHandle.sendQueueClearResult({session_id:t.session_id,canceled_event_ids:o}),this.pushQueueSnapshotForSession(t.session_id)}),typeof this.aibotHandle.onQueueReorder=="function"&&this.aibotHandle.onQueueReorder(t=>{const o=Array.isArray(t.ordered_event_ids)?t.ordered_event_ids.filter(s=>typeof s=="string"&&s.length>0):[],i=this.pool.reorderQueue(t.session_id,o);this.aibotHandle.sendQueueReorderResult({session_id:t.session_id,applied_event_ids:i}),this.pushQueueSnapshotForSession(t.session_id)}),typeof this.aibotHandle.onEventHold=="function"&&this.aibotHandle.onEventHold(t=>{const o=typeof t.session_id=="string"?t.session_id:"",i=typeof t.event_id=="string"?t.event_id:"",s=t.hold!==!1;if(h.info(this.name,`recv event_hold event_id=${i} session_id=${o} hold=${s} reason=${t.reason??""}`),!o||!i){this.aibotHandle.sendEventHoldResult({session_id:o,event_id:i,ok:!1,held:!1,error:"bad_request"});return}const r=typeof t.reason=="string"?t.reason:"",a=typeof t.ttl_ms=="number"?t.ttl_ms:void 0,d=this.pool.holdEvent(o,i,s,r,a),c=d==="ok";this.aibotHandle.sendEventHoldResult({session_id:o,event_id:i,ok:c,held:c?s:!1,...c?{}:{error:d}}),c&&this.pushQueueSnapshotForSession(o)}),typeof this.aibotHandle.onQueueEdit=="function"&&this.aibotHandle.onQueueEdit(t=>{const o=typeof t.session_id=="string"?t.session_id:"",i=typeof t.event_id=="string"?t.event_id:"";if(h.info(this.name,`recv queue_edit event_id=${i} session_id=${o}`),!o||!i){this.aibotHandle.sendQueueEditResult({session_id:o,event_id:i,ok:!1,error:"bad_request"});return}const s=typeof t.content=="string"?t.content:"",r=this.pool.editQueuedEvent(o,i,s),a=r==="ok";this.aibotHandle.sendQueueEditResult({session_id:o,event_id:i,ok:a,...a?{}:{error:r}}),a&&this.pushQueueSnapshotForSession(o)}),typeof this.aibotHandle.onQueueSnapshotQuery=="function"&&this.aibotHandle.onQueueSnapshotQuery(t=>{this.replyQueueSnapshotForSession(t.session_id)}),h.info(this.name,"Connected to aibot"),this.activeEventStore){const t=await this.activeEventStore.drain();if(t.length>0){h.warn(this.name,`Recovering ${t.length} stale event(s) from previous run`);for(const o of t)h.info(this.name,`Failing stale event on startup: ${o}`),this.aibotHandle.sendEventResult({event_id:o,status:"failed",msg:"process restarted, event lost",updated_at:Date.now()})}}this.pushQueueSnapshots(),this.relayStateSyncOnConnect(),this.aibotHandle.onReconnected(()=>{this.pushQueueSnapshots();const t=this.aibotHandle.authAck;this.applyAgentProfile(t?.agent_name,t?.introduction,{source:"reconnect",respawnOnChange:!0}),this.forceRefreshSkills(),this.relayStateSyncOnConnect()}),this.aibotHandle.onStreamRejected((t,o)=>{this.sendCtrl.markEventRejected(t)})}applyAgentProfile(e,n,t){const o=String(e??"").trim(),i=String(n??"").trim(),s=o!==this.agentProfile.agentName||i!==this.agentProfile.introduction;this.agentProfile={agentName:o,introduction:i},o||i?h.info(this.name,`agent profile (${t.source}): GOT name="${o}" intro_len=${i.length} changed=${s}`):h.warn(this.name,`agent profile (${t.source}): EMPTY \u2014 \u670D\u52A1\u7AEF\u672A\u4E0B\u53D1 agent_name/introduction\uFF08auth_ack \u5B57\u6BB5\u7F3A\u5931\u6216\u503C\u4E3A\u7A7A\uFF09`),s&&t.respawnOnChange&&this.applyProfileChangeToAdapters("agent_profile_changed")}applyProfileChangeToAdapters(e){if(!this.pool)return;const n=this.pool.getAllSlots();let t=0;for(const r of n)if(r.adapter.onAgentProfileChanged)try{r.adapter.onAgentProfileChanged(),t++}catch(a){h.warn(this.name,`onAgentProfileChanged failed for session=${r.sessionId}: ${a}`)}const o=n.filter(r=>r.adapter instanceof M),i=o.filter(r=>{if(r.state!=="ready")return!1;const a=r.adapter.getStatus();return!a.busy&&!a.backgroundBusy}),s=o.length-i.length;h.info(this.name,`${e}: notified ${t} adapter(s) via hook; respawning ${i.length} idle Claude slot(s), skipping ${s} busy`);for(const r of i)this.pool.removeSlot(r.sessionId).catch(a=>{h.warn(this.name,`removeSlot failed during ${e}: ${a}`)})}pushQueueSnapshots(){if(!(!this.config.eventQueue||!this.pool))for(const e of this.pool.getAllSlots())this.pushQueueSnapshotForSession(e.sessionId)}buildQueueSnapshotPayload(e){const n=this.pool?.getQueueSnapshot(e)??null,t=n?[...n.running]:[],o=n?n.running_items.map(s=>({event_id:s.event_id,...s.content_preview?{content_preview:s.content_preview}:{},...s.title?{title:s.title}:{},...s.summary?{summary:s.summary}:{},actions:[{type:"stop"}]})):[],i=n?n.queued.map(s=>({event_id:s.event_id,position:s.position,...s.content_preview?{content_preview:s.content_preview}:{},...typeof s.content=="string"?{content:s.content}:{},...s.title?{title:s.title}:{},...s.summary?{summary:s.summary}:{},held:s.held===!0,held_reason:s.held_reason??"",actions:[{type:"cancel"}]})):[];if(t.length===0&&this.selfDrivenSessions.has(e)&&this.pool?.getSlot(e)){const s=`selfdrive_${e}`,r=this.selfDrivenLabels.get(e)??"Background task in progress";t.push(s),o.push({event_id:s,content_preview:r,title:r,summary:r,actions:[]})}return{session_id:e,running:t,running_items:o,queued:i}}pushQueueSnapshotForSession(e){if(!this.config.eventQueue||!this.pool)return;const n=this.buildQueueSnapshotPayload(e);h.info(this.name,`[queue-debug] push snapshot session=${e} running=${n.running.length} queued=${n.queued.length} running_ids=[${n.running.join(",")}]`),this.aibotHandle.sendQueueSnapshot(n)}replyQueueSnapshotForSession(e){!this.config.eventQueue||!this.pool||this.aibotHandle.sendQueueSnapshot(this.buildQueueSnapshotPayload(e))}async platformInvoke(e,n,t){return e==="file_link"?Ot(n):e==="file_upload"?this.uploadFileAndSendMedia(n):this.aibotHandle.agentInvoke(e,n,t)}async uploadFileAndSendMedia(e){const n=String(e.file_path??"").trim(),t=String(e.session_id??"").trim(),o=String(e.caption??"").trim(),i=String(e.reply_to_message_id??"").trim();if(!n)throw new Error("file_path is required");if(!t)throw new Error("session_id is required");const s=await Ft({wsURL:this.config.aibot.url,apiKey:this.config.aibot.apiKey,sessionID:t,filePath:n}),r=await this.aibotHandle.sendMedia({session_id:t,msg_type:2,content:o||`[${s.attachment_type}]`,client_msg_id:`file_upload_${W()}`,...i?{quoted_message_id:i}:{},extra:s.extra});if(r.cmd!=="send_ack"){const d=r.payload??{},c=String(d.msg??r.cmd);throw new Error(`media message send failed: ${c}`)}const a=r.payload??{};return{ok:!0,file_name:s.file_name,attachment_type:s.attachment_type,access_url:s.access_url,message_id:a.msg_id!=null?String(a.msg_id):null}}sendReplyByRuntimeConfig(e,n,t,o,i){const s=this.indexEventSession(e,n)??n;this.auditController.captureReply(e,t),this.sendCtrl.sendReply(e,s,t,o,i),t&&this.conversationLog?.logOutbound?.(s,e,"reply",t)}stampCodexEventQuote(e){if(e.quoted_message_id!=null){if(e.quoted_message_id)return e;const{quoted_message_id:s,...r}=e;return r}const n=e;if(n.codex_method!=="item/agentMessage/delta")return e;const o=n.codex_payload?.params?.phase;if(typeof o=="string"&&o.trim()&&o.trim().toLowerCase()!=="final_answer")return e;const i=this.sendCtrl.getDefaultQuotedMessageId(e.event_id);return i?{...e,quoted_message_id:i}:e}captureCodexAuditOutput(e){if(e.codex_method!=="item/agentMessage/delta")return;const n=e.codex_payload?.params,t=typeof n?.phase=="string"?n.phase.trim().toLowerCase():"";if(t&&t!=="final_answer")return;const o=n?.delta;typeof o!="string"||!o||this.auditController.captureUnsequencedStreamChunk(e.event_id,o)}discardEventTrackingState(e){this.inflightEvents.delete(e),this.restartCount.delete(e),this.eventSessionIndex.delete(e),this.sendCtrl.discardEventState(e)}reliableFinalWiring(e,n){return async(t,o,i)=>{try{await this.sendCtrl.sendFinalStreamChunkReliable(t,o,i,n)}catch(s){throw h.error("bridge",`[${e}] sendFinalStreamChunkReliable ACK failed event=${t}: ${s}`),s}h.info("bridge",`[${e}] sendFinalStreamChunkReliable done event=${t}`)}}sendStreamChunkByRuntimeConfig(e,n,t,o,i,s,r){const a=this.indexEventSession(e,n)??n;this.auditController.captureStreamChunk(e,o,t),this.sendCtrl.sendStreamChunk(e,a,t,o,i,s,r),(t||i)&&this.conversationLog?.logOutbound?.(a,e,i?"stream_chunk_finish":"stream_chunk",t)}surfacedRunErrorEvents=new Set;sendRunErrorAsChunk(e,n,t){this.surfacedRunErrorEvents.has(e)||(this.sendStreamChunkByRuntimeConfig(e,n,`
|
|
10
10
|
|
|
11
|
-
Error: ${t}`,1,!1),this.surfacedRunErrorEvents.add(e))}sendEventResultWithCleanup(e,n,t,o){const i=this.eventSessionIndex.get(e);n==="failed"&&i&&t?.trim()&&this.sendRunErrorAsChunk(e,i,t),this.auditController.markResponded(e,n,t),this.sendCtrl.sendEventResult(e,n,t,o),i&&(this.pool.eventComplete(e,i)===!1&&h.error(this.name,`Event terminal result could not release queue slot event=${e} session=${i} status=${n}`),this.pushQueueSnapshotForSession(i),this.conversationLog?.logResult?.(i,e,n,t),this.eventSessionIndex.delete(e)),this.inflightEvents.delete(e),this.restartCount.delete(e),this.surfacedRunErrorEvents.delete(e),n==="responded"&&(this.cachedProviderQuotaSampledAtMs=null,this.refreshAndPushProviderQuota(!0).catch(()=>{}))}async handleSessionInternalError(e){const{eventId:n,sessionId:t,errorMsg:o}=e;if(this.stopped)return;const i=this.inflightEvents.get(n);if(!i){h.warn(this.name,`[recovery] no inflight event for internalError event=${n} session=${t}; surface failure directly`),this.sendRunErrorAsChunk(n,t,o),this.sendEventResultWithCleanup(n,"failed",o,"agent_stop_failure");return}const s=(this.restartCount.get(n)??0)+1;this.restartCount.set(n,s);const r=this.config.adapterType??"acp";if(s>Z){h.error(this.name,`[recovery] adapter=${r} session=${t} event=${n} restart=${s}/${Z} outcome=give-up err=${o}`),this.sendRunErrorAsChunk(n,t,o),this.sendEventResultWithCleanup(n,"failed",o,"agent_stop_failure");return}h.info(this.name,`[recovery] adapter=${r} session=${t} event=${n} restart=${s}/${Z} outcome=restarting err=${o}`);const a=this.pool.drainQueuedForSession(t);a.length>0&&h.info(this.name,`[recovery] session=${t} preserved ${a.length} queued sibling event(s) across restart`);try{await this.pool.removeSlot(t)}catch(l){h.warn(this.name,`[recovery] removeSlot failed session=${t}: ${l instanceof Error?l.message:String(l)}`)}if(this.stopped)return;const d=e.replayOriginalContent?i.content:this.resolveRecoveryPrompt(r,i),c={...i,content:d};try{await this.pool.deliverInboundEvent(c)}catch(l){h.error(this.name,`[recovery] redeliver failed event=${n} session=${t}: ${l instanceof Error?l.message:String(l)}`),this.sendEventResultWithCleanup(n,"failed",l instanceof Error?l.message:String(l));return}for(const l of a){if(this.stopped)break;try{await this.pool.deliverInboundEvent(l)}catch(u){h.error(this.name,`[recovery] sibling redeliver failed event=${l.event_id} session=${t}: ${u instanceof Error?u.message:String(u)}`),this.sendEventResultWithCleanup(l.event_id,"failed",u instanceof Error?u.message:String(u))}}}resolveRecoveryPrompt(e,n){return e==="acp"?"continue":n.content}sendThinkingByRuntimeConfig(e,n,t){this.sendCtrl.sendThinking(e,n,t)}bufferStreamChunk(e,n,t,o,i){this.auditController.captureUnsequencedStreamChunk(e,t),this.sendCtrl.bufferOnly(e,n,t,o,i)}flushBufferedStreamText(e){}resolveEventRuntimeConfig(e){return this.sendCtrl.resolveEventRuntimeConfig(e)}captureEventRuntimeConfig(e){this.sendCtrl.captureEventRuntimeConfig(e),this.indexEventSession(e.event_id,e.session_id),this.auditController.markRecording(e.event_id),e.event_id&&!this.inflightEvents.has(e.event_id)&&this.inflightEvents.set(e.event_id,e)}indexEventSession(e,n){if(!e||!n)return;const t=this.eventSessionIndex.get(e);return t?(t!==n&&h.warn(this.name,`Ignoring event session mismatch event=${e} indexed=${t} supplied=${n}`),t):(this.eventSessionIndex.set(e,n),n)}shouldDropToolDisplayEvent(e){return this.sendCtrl.shouldDropToolDisplayEvent(e)}shouldDropThinkingDisplayEvent(e){return this.sendCtrl.shouldDropThinkingDisplayEvent(e)}shouldDropCodexDisplayEvent(e,n){return this.sendCtrl.shouldDropCodexDisplayEvent(e,n)}logCodexEventToConversation(e){if(!this.conversationLog||e.codex_method!=="item/agentMessage/delta")return;const t=e.codex_payload?.params?.delta;if(!t)return;const o=this.eventSessionIndex.get(e.event_id)??e.session_id;this.conversationLog.append(o,{ts:Date.now(),dir:"outbound",event_id:e.event_id,kind:"codex_delta",text_len:t.length,content:t})}isAcpRawTransportEnabled(){return(this.config.adapterOptions??{}).raw_transport===!0}shouldDropAcpRawDisplayEvent(e,n){return this.sendCtrl.shouldDropAcpRawDisplayEvent(e,n)}sendAcpRawEventEnvelope(e,n,t){this.shouldDropAcpRawDisplayEvent(e,t.type)||this.deliverRawEventEnvelope(e,n,t,"acp",this.buildAcpRawEventFallbackText(t))}buildAcpRawEventFallbackText(e){const n=String(e.type??"").trim();if(!n)return"[acp] event";switch(n){case"permission_request":return`Permission required: ${String(e.payload?.tool_title??e.payload?.tool_name??"permission request")}`;case"tool_use":return`[tool] ${String(e.payload?.tool_name??"tool")}`;case"tool_result":return"[tool result]";case"thinking":return"[thinking]";case"error":return`[error] ${String(e.payload?.message??"agent error")}`;case"result":return"[result]";default:return`[acp] ${n}`}}rawDetailSeq=0;deliverRawEventEnvelope(e,n,t,o,i){const s=Zt({envelope:t,fallbackText:i,channelKey:o,allocateRefId:()=>`${e}_rawd_${++this.rawDetailSeq}`}),r=()=>{this.aibotHandle.sendMsg({event_id:e,session_id:n,msg_type:1,content:s.fallbackText,extra:{channel_data:{[o]:{raw_event:s.envelope}},agent_api_origin:!0}})};if(!s.sharded){r();return}h.info("bridge",`${o} raw_event oversized, sharded delivery: event=${e} fields=${s.oversizedFields.map(a=>a.field).join(",")}`),(async()=>{for(const a of s.oversizedFields)await this.sendCtrl.deliverAuxiliaryLargeText(e,n,ei({envelopeType:t.type,field:a.field,fullText:a.fullText}),a.refClientMsgId);r()})().catch(a=>{h.warn("bridge",`${o} raw_event sharded delivery failed event=${e}: ${a}`)})}buildCursorRawEventFallbackText(e){const n=String(e?.type??"").trim(),t=e?.payload&&typeof e.payload=="object"?e.payload:{};switch(n){case"permission_request":return`Permission required: ${String(t.tool_title??t.tool_name??"permission request")}`;case"tool_use":case"tool_call":case"tool_execution_start":return`[tool] ${String(t.tool_name??t.toolName??"tool")}`;case"tool_result":case"tool_execution_end":case"tool_execution_update":return"[tool result]";case"error":return`[error] ${String(t.message??"agent error")}`;default:return n?`[cursor] ${n}`:"[cursor] event"}}sendToolExecutionCard(e,n,t,o){this.sendCtrl.sendToolExecutionCard(e,n,t,o)}sendGrixApprovalCard(e,n){this.aibotHandle.sendMsg({event_id:e.eventId,session_id:e.sessionId,client_msg_id:`perm_${W()}`,msg_type:1,content:e.toolTitle?`Permission required: ${e.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:e.approvalId,approvalSlug:e.toolName},grix:{execApproval:{approval_command_id:e.approvalId,command:e.toolTitle||e.toolName,host:n}}},agent_api_origin:!0}})}sendGrixAgentQuestionCard(e,n,t){const o=t.questions.map(s=>s.header).join(", "),i=_e(`[Agent Question] ${t.request_id}`,"agent_question",t);this.aibotHandle.sendText({event_id:e,session_id:n,content:i,msg_type:1,extra:{card_type:"agent_question",summary_text:o}})}handleOpenCodeQuestionReplyEvent(e){const n=It(String(e.content??""));if(!n)return!1;this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});const o=this.pool.getSlot(e.session_id)?.adapter,i=o instanceof K?o.handleQuestionReplyEvent(n):{delivered:!1,errorCode:"interaction_request_not_pending",errorMsg:"The OpenCode question is no longer pending; the reply was not delivered."};return i.delivered?(h.info(this.name,`[opencode] question reply delivered event=${e.event_id} session=${e.session_id} request=${n.request_id}`),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",updated_at:Date.now()})):(h.warn(this.name,`[opencode] question reply rejected event=${e.event_id} session=${e.session_id} request=${n.request_id} code=${i.errorCode??"interaction_reply_failed"}`),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:i.errorCode??"interaction_reply_failed",msg:i.errorMsg??"The question reply was not delivered.",updated_at:Date.now()})),!0}async handleAibotEvent(e){if(this.relayEnvStale&&!this.hasPendingWork()&&await this.recycleAdaptersForRelayChange(),this.stopped){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:"agent shutting down",updated_at:Date.now()});return}this.logInboundConversation(e);const n=this.config.adapterType??"acp";if(n==="opencode"&&this.handleOpenCodeQuestionReplyEvent(e))return;const t=Gt(e.extra);if(t.error){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"connector_config_invalid",msg:t.error,updated_at:Date.now()});return}const o=t.patch,i=li(e.extra);if((i.state!=="absent"||i.error)&&h.info(this.name,`[audit-marker] event=${e.event_id} session=${e.session_id} msg=${e.msg_id??""} state=${i.state??"error"} scope=${i.options?.enabled?i.options.scope:""} error=${i.error??""}`),i.error){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"audit_config_invalid",msg:i.error,updated_at:Date.now()});return}if(i.state==="enabled"&&n!=="claude"&&n!=="codex"&&n!=="cursor"&&n!=="opencode"&&n!=="pi"&&n!=="codewhale"&&n!=="acp"){if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"audit_provider_unsupported",msg:`Audit replay is not supported for adapter: ${n}`,updated_at:Date.now()}),i.options?.enabled&&i.options.scope==="turn")try{this.aibotHandle.sendAuditState(X({state:"failed",eventId:e.event_id,sessionId:e.session_id,...e.msg_id===void 0?{}:{msgId:e.msg_id},errorCode:"audit_provider_unsupported",errorMessage:`Audit replay is not supported for adapter: ${n}`},Date.now()))}catch{}return}const s=Lt(e);if(s){if(i.state!=="absent"||s.verb===_.open)try{this.auditController.resolveSession({sessionId:e.session_id,provider:this.resolveAuditProvider(),extra:e.extra})}catch(c){this.sendAuditConfigurationError(e,c);return}if(s.verb===_.exec){await this.handleSessionControlCommand(s,e);return}if(s.verb===_.listSessions){await this.handleListSessionsTextCommand(e);return}if(n==="claude"){await this.handleSessionControlCommand(s,e);return}if(n==="codex"&&s.verb===_.open){await this.handleCodexSessionControlOpen(s,e);return}if(n==="pi"&&s.verb===_.open){await this.handlePiSessionControlOpen(s,e);return}if(n==="pi"&&s.verb===_.restart){await this.handlePiSessionControlRestart(e);return}if((n==="openhuman"||n==="opencode")&&s.verb===_.open){await this.handleOpenHumanSessionControlOpen(s,e);return}if(n==="codewhale"&&s.verb===_.open){await this.handleCodeWhaleSessionControlOpen(s,e);return}if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),s.verb===_.open){const c=s.args.trim();if(!c){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:p.cwdRequired,msg:"cwd is required",updated_at:Date.now()});return}try{const l=P.resolve(c);if(!(await F(l)).isDirectory()){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:p.invalidCwd,msg:`Path is not a directory: ${l}`,updated_at:Date.now()});return}}catch(l){const u=String(l?.code??""),g=u==="ENOENT"?`Directory does not exist: ${P.resolve(c)}`:u==="EACCES"||u==="EPERM"?"Directory is not accessible":`Invalid path: ${c}`;this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:p.invalidCwd,msg:g,updated_at:Date.now()});return}}if(s.verb===_.open){const c=this.bindingStore.get(e.session_id);if(c?.cwd)try{await F(c.cwd)}catch{h.info("bridge",`Stale binding detected for session ${e.session_id}: ${c.cwd} no longer exists, clearing`),this.bindingStore.delete(e.session_id),this.sessionBindings.delete(e.session_id),this.sessionProviderHints.delete(e.session_id),this.sessionProviderQuotas.delete(e.session_id),this.sessionProviderMeta.delete(e.session_id);const l=this.pool.getSlot(e.session_id);l?.adapter instanceof E&&l.adapter.getSessionBindings().delete(e.session_id)}}if(await this.handleSessionControlForPool(s,e),n==="acp"&&s.verb===_.stop){const l=this.bindingStore.get(e.session_id)?.cwd??"";await this.pool.removeSlot(e.session_id).catch(()=>{}),this.sessionBindings.delete(e.session_id),this.sessionProviderHints.delete(e.session_id),this.sessionProviderQuotas.delete(e.session_id),this.sessionProviderMeta.delete(e.session_id),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:`Session worker stopped for ${l}`,updated_at:Date.now()});return}if(le(s,e,this.sessionControlCtx(e.session_id),{...this.sessionControlSenders(),sendEventAck:()=>{}}),s.verb===_.open&&(await this.deferredMgr.release(e.session_id,this.deferredCallbacks()),n==="agy")){const c=this.bindingStore.get(e.session_id)?.cwd??"";c&&this.aibotHandle.sendUpdateBindingCard({session_id:e.session_id,worker_status:"ready",cwd:c,meta:this.buildAgyToolbarMeta(e.session_id)})}return}if(e.mirror_mode==="record_only"){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",updated_at:Date.now()});return}if(this.isStaleEvent(e)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"event_stale",msg:"event is stale and will not be processed",updated_at:Date.now()});return}let r;try{r=this.auditController.resolveSession({sessionId:e.session_id,provider:this.resolveAuditProvider(),extra:e.extra,eventId:e.event_id}).session}catch(c){this.sendAuditConfigurationError(e,c);return}if(_i.has(n)&&!this.bindingStore.get(e.session_id)?.cwd){const l=n;h.info(this.name,`[${l}] binding missing session_id=${e.session_id} event_id=${e.event_id}`),this.deferredMgr.defer(l,String(e.session_id??"").trim(),this.prepareAuditedInboundEvent(e,o,r));const u=this.resolveBindingChannelKey(n);this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendMsg({event_id:e.event_id,session_id:e.session_id,msg_type:1,content:"Session binding missing.",extra:{channel_data:{[u]:{sessionBinding:{status:"missing",reason:"binding_missing",error_code:p.bindingMissing}}}},quoted_message_id:e.msg_id});return}if((this.config.adapterType??"acp")==="acp"){const l=String(e.content??"").trim().match(/^\/(\S+)\s*(.*)/);if(l){const[,u,g]=l,f=this.pool.getSlot(e.session_id)?.adapter;if(f?.execCommand&&(f.getSupportedCommands?.()??[]).some(m=>m.name===u||m.name===`/${u}`)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});try{const m=await f.execCommand(u,g.trim(),e.session_id);m.status==="options"&&m.data&&this.handleExecCommandOptions(e.session_id,u,m.data),this.aibotHandle.sendEventResult({event_id:e.event_id,status:m.status==="failed"?"failed":"responded",msg:m.message,updated_at:Date.now()})}catch(m){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:m instanceof Error?m.message:String(m),updated_at:Date.now()})}return}}}if(n==="codex"&&se(r?.options)){const c=this.pool.getSlot(e.session_id);if(c?.adapter instanceof te&&!c.adapter.hasRawApiCaptureRelay()){const l=c.eventQueue.snapshot(e.session_id);l.running.length===0&&l.queued.length===0?(h.info(this.name,`[audit-raw-capture] recreating idle Codex adapter before event=${e.event_id} session=${e.session_id}`),await this.pool.removeSlot(e.session_id)):h.warn(this.name,`[audit-raw-capture] Codex adapter lacks relay but is not idle event=${e.event_id} session=${e.session_id} running=${l.running.length} queued=${l.queued.length}`)}}const d=this.prepareAuditedInboundEvent(e,o,r);try{this.captureEventRuntimeConfig(d),await this.pool.deliverInboundEvent(d)}catch(c){if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.auditController.closeWithoutAdapter(e.event_id,"failed",c instanceof Error?c.message:String(c),{deliveryFailed:!0}),this.discardEventTrackingState(e.event_id),c instanceof xt)this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:c.message,updated_at:Date.now()});else throw c}}async handleCodexSessionControlOpen(e,n){const t=n.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()}),s=(r,a)=>this.aibotHandle.sendEventResult({event_id:n.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),s("failed",{msg:"Usage: /grix open <working-directory>",code:p.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{if(await this.resolveCwdForBinding(a.cwd)===r){i(),s("responded",{msg:`Session already bound to ${a.cwd}`});return}}catch{d=!0,h.info("bridge",`Stale codex binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){i(),s("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:p.rebindForbidden});return}}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),this.deferredMgr.sendCodexDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),s("responded",{msg:`Session bound to ${r}`})}catch(r){i(),s("failed",{code:p.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handleSessionControlForPool(e,n){e.verb===_.open&&await this.bindSessionForPool(n.session_id,e.args.trim())}async replayDeferredEventsForSession(e){await this.deferredMgr.release(e,this.deferredCallbacks())}async handleSessionControlLocalActionForPool(e){if(String(e.action_type??"")!==C.sessionControl)return;const n=e.params??{};if(String(n.verb??"").trim().toLowerCase()!==_.open)return;const o=String(n.session_id??"").trim(),i=String(n.cwd??"").trim();!o||!i||await this.bindSessionForPool(o,i)}async handleCodexSessionControlLocalActionOpen(e){const n=e.params??{},t=String(n.session_id??"").trim(),o=String(n.cwd??"").trim(),i=String(n.agent_session_id??"").trim(),s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){s("failed",void 0,p.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{const c=await this.resolveCwdForBinding(a.cwd);if(c===r){this.setResolvedAgentSessionId(t,i),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,c)});return}}catch{d=!0,h.info("bridge",`Stale codex binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){s("failed",void 0,p.rebindForbidden,`Session already bound to ${a.cwd}`);return}}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),this.deferredMgr.sendCodexDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){s("failed",void 0,r?.sessionControlErrorCode??p.invalidCwd,r instanceof Error?r.message:String(r))}}async handleCursorSessionControlLocalActionOpen(e){const n=e.params??{},t=String(n.session_id??"").trim(),o=String(n.cwd??"").trim(),i=String(n.agent_session_id??"").trim(),s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){s("failed",void 0,p.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{const c=await this.resolveCwdForBinding(a.cwd);if(c===r){this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,c),await this.bindSessionForPool(t,c),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:c,meta:this.buildCursorToolbarMeta(t)}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,c)});return}}catch{d=!0,h.info("bridge",`Stale cursor binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){s("failed",void 0,p.rebindForbidden,`Session already bound to ${a.cwd}`);return}}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),this.deferredMgr.sendCursorDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r,meta:this.buildCursorToolbarMeta(t)}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){s("failed",void 0,r?.sessionControlErrorCode??p.invalidCwd,r instanceof Error?r.message:String(r))}}async handlePiSessionControlOpen(e,n){const t=n.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()}),s=(r,a)=>this.aibotHandle.sendEventResult({event_id:n.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),s("failed",{msg:"Usage: /grix open <working-directory>",code:p.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{if(await this.resolveCwdForBinding(a.cwd)===r){i(),s("responded",{msg:`Session already bound to ${a.cwd}`}),this.aibotHandle.sendMsg({event_id:n.event_id,session_id:t,msg_type:1,content:`\u2705 Session already bound to \`${a.cwd}\``,quoted_message_id:n.msg_id});return}}catch{d=!0,h.info("bridge",`Stale pi binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){i(),s("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:p.rebindForbidden});return}}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),await this.ensureSlotStarted(t).catch(d=>{h.warn("bridge",`pi ensureSlotStarted on bind failed (non-fatal): ${d instanceof Error?d.message:String(d)}`)}),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),s("responded",{msg:`Session bound to ${r}`}),this.aibotHandle.sendMsg({event_id:n.event_id,session_id:t,msg_type:1,content:`\u2705 Working directory bound: \`${r}\``,quoted_message_id:n.msg_id})}catch(r){i(),s("failed",{code:p.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handlePiSessionControlRestart(e){const n=e.session_id,t=()=>this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:n,received_at:Date.now()}),o=(r,a)=>this.aibotHandle.sendEventResult({event_id:e.event_id,status:r,...a,updated_at:Date.now()}),s=this.bindingStore.get(n)?.cwd??"";if(!s){t(),o("failed",{msg:"session binding was not found",code:p.bindingMissing});return}t(),await this.pool.removeSlot(n).catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:"ready",cwd:s}),o("responded",{msg:`Session worker restarted for ${s}`})}async handlePiSessionControlRestartLocalAction(e){const n=e.params??{},t=String(n.session_id??"").trim(),i=(t?this.bindingStore.get(t):void 0)?.cwd??"",s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t){s("failed",void 0,"session_id_required","session_id is required for restart");return}if(!i){s("failed",void 0,p.bindingMissing,"Session binding missing. Open a workspace first.");return}await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i}),s("ok",{outcome:"restarted",binding:{aibotSessionId:t,cwd:i,workerStatus:"ready"}})}async syncOpenCodeBinding(e,n){if(!e||!n)return;try{this.resolveSessionModelId(e),this.resolveSessionModeId(e)}catch(o){h.warn(this.name,`[opencode] resolve global toolbar default failed: ${o instanceof Error?o.message:String(o)}`)}let t=this.pool.getSlot(e);!t&&(this.config.adapterType??"acp")==="opencode"&&(await this.ensureSlotStarted(e),t=this.pool.getSlot(e)),t?.startPromise&&await t.startPromise,t?.adapter instanceof K&&await t.adapter.bindSession(e,n)}async bindSessionForPool(e,n){const t=String(n??"").trim();if(!e||!t)return;const o=this.pool.getOrCreateSlot(e);if(!o||(o.startPromise&&await o.startPromise,!o.adapter))return;const i=o.adapter instanceof E?o.adapter:null;if(!i?.hasSessionBinding)return;const s=await this.resolveCwdForBinding(t);i.announceDeferredComposing(e),await i.bindSession(e,s),this.sessionBindings.set(e,s),this.sessionScanCache.invalidate(),i.replayDeferredEvents(e)}deferredCallbacks(){return{captureEventRuntimeConfig:e=>this.captureEventRuntimeConfig(e),deliverInboundEvent:e=>this.pool.deliverInboundEvent(e),sendEventResult:(e,n,t)=>this.sendEventResultWithCleanup(e,n,t),sendSessionComposing:(e,n,t)=>{const o={};n&&(o.ttl_ms=t?.ttlMs??3e4,t?.activity&&(o.activity=t.activity)),this.aibotHandle.sendSessionActivitySet({session_id:e,kind:"composing",active:n,...o})}}}async handleOpenHumanSessionControlOpen(e,n){const t=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:n.session_id,received_at:Date.now()});try{const o=await this.resolveCwdForBinding(e.args.trim());await this.handleSessionControlForPool(e,n),await this.syncOpenCodeBinding(n.session_id,o),le(e,n,this.sessionControlCtx(n.session_id),this.sessionControlSenders()),await this.deferredMgr.release(n.session_id,this.deferredCallbacks())}catch(o){t(),this.aibotHandle.sendEventResult({event_id:n.event_id,status:"failed",code:o?.cwdErrorCode,msg:o instanceof Error?o.message:String(o),updated_at:Date.now()})}}async handleCodeWhaleSessionControlOpen(e,n){const t=n.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()}),s=(r,a)=>this.aibotHandle.sendEventResult({event_id:n.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),s("failed",{msg:"Usage: /grix open <working-directory>",code:p.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){if(await this.resolveCwdForBinding(a.cwd)!==r){i(),s("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:p.rebindForbidden});return}i(),s("responded",{msg:`Session already bound to ${a.cwd}`});return}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),s("responded",{msg:`Session bound to ${r}`})}catch(r){i(),s("failed",{code:p.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handleCodeWhaleSessionControlLocalActionOpen(e){const n=e.params??{},t=String(n.session_id??"").trim(),o=String(n.cwd??"").trim(),i=String(n.agent_session_id??"").trim(),s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){s("failed",void 0,p.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){const d=await this.resolveCwdForBinding(a.cwd);if(d!==r){s("failed",void 0,p.rebindForbidden,`Session already bound to ${a.cwd}`);return}this.setResolvedAgentSessionId(t,i),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,d)});return}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){s("failed",void 0,r?.sessionControlErrorCode??p.invalidCwd,r instanceof Error?r.message:String(r))}}normalizeClaudeModeId(e){return String(e??"").trim().toLowerCase()===R.approval?R.approval:R.fullAuto}handleExecCommandOptions(e,n,t){const o=Array.isArray(t?.options)?t.options:[];if(o.length===0)return;const s=this.bindingStore.get(e)?.cwd??"",r={};if(n==="model"){r.available_models=o.map(d=>({id:d.id,displayName:d.label}));const a=o.find(d=>d.current);a&&(r.model_id=a.id)}else if(n==="mode"){r.available_modes=o.map(d=>({id:d.id,displayName:d.label}));const a=o.find(d=>d.current);a&&(r.mode_id=a.id)}else r[`${n}_options`]=o;this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:s,meta:r})}resolveSessionModelId(e){const n=this.bindingStore.getModelId(e);if(n)return n;const t=String(this.globalConfigStore?.get(this.name)?.modelId??"").trim();if(t)return this.bindingStore.setModelId(e,t),t}resolveSessionModeId(e){const n=this.bindingStore.getModeId(e);if(n)return n;const t=String(this.globalConfigStore?.get(this.name)?.modeId??"").trim();if(t)return this.bindingStore.setModeId(e,t),t}resolveClaudeSessionEffort(e){const n=this.bindingStore.getEffort(e);if(n)return n;if(this.bindingStore.get(e)?.claudeSessionId)return;const o=j(this.globalConfigStore?.get(this.name)?.effort);if(o)return this.bindingStore.setEffort(e,o),o}resolveCodexSessionModelId(e){const n=this.bindingStore.getCodexModelId(e);if(n)return n;if(this.bindingStore.getCodexThreadId(e))return;const t=String(this.globalConfigStore?.get(this.name)?.codexModelId??"").trim();if(t)return this.bindingStore.setCodexModelId(e,t),t}resolveCodexNewSessionGlobalDefault(e,n,t){const o=String(n??"").trim();if(o&&!this.bindingStore.getCodexThreadId(e))return t(o),o}buildCursorToolbarMeta(e){const n=this.pool.getSlot(e);if(n?.adapter instanceof B)return n.adapter.getToolbarMeta(e);const t=this.bindingStore.getModelId(e)??"auto",o=this.bindingStore.getCursorModeId(e)??"full_auto";return{model_id:t,mode_id:o,currentModelId:t,currentModeId:o,available_models:[],available_modes:Ee.map(i=>({...i}))}}buildAgyToolbarMeta(e){if(!this.bindingStore.get(e))return;const t=$e(this.config.agent.command),o=t.length>0?t[0].id:"";let i=this.resolveSessionModelId(e)||o;t.length>0&&!t.some(r=>r.id===i)&&(i=o);const s=Te();return{model_id:i,currentModelId:i,available_models:t.map(r=>({id:r.id,displayName:r.displayName})),...s.plan!==void 0&&{plan:s.plan},...s.quota_exhausted!==void 0&&{quota_exhausted:s.quota_exhausted},...s.quota_reset_at!==void 0&&{quota_reset_at:s.quota_reset_at},...s.available_credits!==void 0&&{available_credits:s.available_credits}}}async handleAgySetModel(e,n){const t=e.params??{},o=String(t.model_id??t.modelId??"").trim();if(!n){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for set_model"});return}if(!o){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"set_model_invalid",error_msg:"model_id is required"});return}const i=this.bindingStore.get(n);if(!i?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:p.bindingMissing,error_msg:"session binding was not found"});return}i.modelId!==o&&(this.bindingStore.setModelId(n,o),this.globalConfigStore?.set(this.name,{modelId:o})),await Pe().catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:"ready",cwd:i.cwd,meta:this.buildAgyToolbarMeta(n)}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"model_set",model_id:o,binding:{cwd:i.cwd,model_id:o}}})}buildClaudeToolbarMeta(e){const n=this.bindingStore.get(e);if(!n)return;const t=z(),o=t.length>0?t[0].id:"";let i=this.resolveSessionModelId(e)||o;t.length>0&&!t.some(g=>g.id===i)&&(i=o);const s=this.normalizeClaudeModeId(n.modeId);let r;try{const g=this.providerResolver?this.providerResolver():this.config.agent.provider;r=j(g?.effort)}catch{}const a=this.resolveClaudeSessionEffort(e)??r??"auto",d={model_id:i,mode_id:s,currentModelId:i,currentModeId:s,available_efforts:[...D],effort:a,reasoning_effort:a,available_models:t.map(g=>({id:g.id,displayName:g.displayName}))},c=this.pool.getSlot(e),l=c?.adapter instanceof M?c.adapter.getSessionState():null;(l?.rateLimits?.fiveHour||l?.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=l);const u=l??this.getFreshClaudeRateLimitState();if(u){const g=u.rateLimits;g&&(g.fiveHour||g.sevenDay)&&(d.rate_limits={...g.fiveHour?{fiveHour:g.fiveHour}:{},...g.sevenDay?{sevenDay:g.sevenDay}:{},sampledAt:u.sampledAt||Date.now()})}if(l){const g=l.contextWindow;g.usedPercentage!=null&&(d.context_window={usedPercentage:g.usedPercentage,remainingPercentage:g.remainingPercentage})}if(!d.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const g=this.providerQuotaToRateLimits(this.cachedProviderQuota);g&&(d.rate_limits=g),h.info(this.name,`[toolbar-meta] provider quota fallback: hasCached=${!!this.cachedProviderQuota} fromProvider=${JSON.stringify(g)}`)}return d.rate_limits&&h.info(this.name,`[toolbar-meta] rate_limits included: ${JSON.stringify(d.rate_limits)}`),d}providerQuotaToCodexRateLimits(e){return fe(e,this.cachedProviderQuotaSampledAtMs??Date.now())}providerQuotaToRateLimits(e){return pe(e,this.cachedProviderQuotaSampledAtMs??Date.now())}async resolveCwdForBinding(e){const n=String(e??"").trim();if(process.platform!=="win32"&&(/^[a-zA-Z]:[\\/]/.test(n)||/^\\\\/.test(n))){const i=new Error(`Specified path is not valid on this host: ${n}`);throw i.cwdErrorCode=p.invalidCwd,i}const t=P.resolve(n);let o;try{o=await F(t)}catch(i){const s=String(i?.code??"");if(s==="ENOENT"){const r=new Error(`Specified path does not exist: ${t}`);throw r.cwdErrorCode=p.invalidCwd,r}if(s==="EACCES"||s==="EPERM"){const r=new Error("Specified path is not accessible.");throw r.cwdErrorCode=p.invalidCwd,r}throw i}if(!o.isDirectory()){const i=new Error("Specified path is not a directory.");throw i.cwdErrorCode=p.invalidCwd,i}try{return await Se(t)}catch{return t}}getClaudeWorkerStatus(e){const n=this.pool.getSlot(e);return n?n.state==="starting"?"starting":n.state==="stopped"?"stopped":n.adapter.getStatus().busy?"busy":"ready":"stopped"}refreshClaudeWorkerStatusCard(e,n){const t=this.getClaudeWorkerStatus(e);return this.claudeWorkerStatus.set(e,t),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:t,cwd:n,meta:this.buildClaudeToolbarMeta(e)}),t}async handleSkillUploadLocalAction(e){const n=e.params??{},t=String(n.name??"").trim(),o=String(n.session_id??"").trim();if(!t){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"MISSING_SKILL_NAME",error_msg:"name is required"});return}const i=I(this.config.adapterType,this.config.aibot.clientType),s=this.bindingStore.get(o)?.cwd||this.sessionBindings.get(o)||void 0,r=i==="kiro"?s:s||process.cwd();try{await Oe(t,{mode:i,projectDir:r},{apiKey:this.config.aibot.apiKey,wsUrl:this.config.aibot.url}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{name:t}})}catch(a){h.warn(this.name,`skill_upload failed name=${t}: ${a instanceof Error?a.message:String(a)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"SKILL_UPLOAD_FAILED",error_msg:a instanceof Error?a.message:String(a)})}}async handleSkillEnableLocalAction(e){const n=e.params??{},t=String(n.name??"").trim(),o=String(n.session_id??"").trim(),i=String(n.scope??"").trim(),s=String(n.actor_id??"").trim(),r=n.force==="replace_link"||n.force==="replace_with_link"?n.force:void 0,a=I(this.config.adapterType,this.config.aibot.clientType),d=this.bindingStore.get(o)?.cwd||this.sessionBindings.get(o)||void 0;try{const c=await Fe({skillsDir:T.skills,mode:a,home:N(),cwd:d},{name:t,scope:i,actorId:s,force:r});h.info(this.name,`skill_enable name=${t} scope=${i} actor=${s||"-"} changed=${c.changed}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{name:c.name,scope:c.scope,path:c.path,changed:c.changed,enable_state:c.status,uninstallable:!0}})}catch(c){const l=c instanceof re?c.code:"SKILL_ENABLE_FAILED";h.warn(this.name,`skill_enable failed name=${t} scope=${i}: ${c instanceof Error?c.message:String(c)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:l,error_msg:c instanceof Error?c.message:String(c),result:{conflict_kind:l==="CONFLICT"||l==="NEEDS_FORCE"||l==="BLOCKED"?l.toLowerCase():void 0}})}this.forceRefreshSkills()}handleSkillRefreshLocalAction(e){const n=e.params??{},t=String(n.session_id??"").trim();if(this.forceRefreshSkills(t||void 0)){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{session_id:t||void 0}});return}h.warn(this.name,`skill_refresh produced no report session=${t||"-"}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"SKILL_REFRESH_FAILED",error_msg:"skill rescan produced no report (scan error or empty skill set)"})}async handleSkillDisableLocalAction(e){const n=e.params??{},t=String(n.name??"").trim(),o=String(n.session_id??"").trim(),i=String(n.scope??"").trim(),s=String(n.actor_id??"").trim(),r=I(this.config.adapterType,this.config.aibot.clientType),a=this.bindingStore.get(o)?.cwd||this.sessionBindings.get(o)||void 0;try{const d=await Ne({skillsDir:T.skills,mode:r,home:N(),cwd:a},{name:t,scope:i,actorId:s});h.info(this.name,`skill_disable name=${t} scope=${i} actor=${s||"-"} removed=${d.removed}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{name:d.name,scope:d.scope,path:d.path,removed:d.removed,enable_state:"none",uninstallable:!1}})}catch(d){const c=d instanceof re?d.code:"SKILL_DISABLE_FAILED";h.warn(this.name,`skill_disable failed name=${t} scope=${i}: ${d instanceof Error?d.message:String(d)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:c,error_msg:d instanceof Error?d.message:String(d),result:{conflict_kind:c==="CONFLICT"||c==="BLOCKED"?c.toLowerCase():void 0}})}this.forceRefreshSkills()}computeSkillReport(e){const n=I(this.config.adapterType,this.config.aibot.clientType),t=n==="kiro"?e:e||process.cwd();let o;try{o=qe({mode:n,projectDir:t})}catch{return null}if(o.length===0)return null;const i=JSON.stringify(o.map(s=>`${s.source}:${s.name}`));return{skills:oe(o,T.skills),hash:i}}reportSessionSkills(e){const n=this.bindingStore.get(e)?.cwd||this.sessionBindings.get(e)||void 0,t=this.computeSkillReport(n);if(t&&t.hash!==this.lastReportedSkillsHash){this.lastReportedSkillsHash=t.hash;try{this.aibotHandle.sendSkillsUpdate({skills:t.skills,library_skills:this.buildLibrarySkillsReport(n)})}catch{}}}forceRefreshSkills(e){const n=e?.trim(),t=n||this.bindingStore.getMostRecentlyUpdatedSessionId({requireCwd:!0}),o=n?this.bindingStore.get(n)?.cwd||this.sessionBindings.get(n)||void 0:t?this.bindingStore.get(t)?.cwd:void 0,i=this.computeSkillReport(o);if(!i)return!1;this.lastReportedSkillsHash=i.hash;try{return this.aibotHandle.sendSkillsUpdate({skills:i.skills,library_skills:this.buildLibrarySkillsReport(o)}),!0}catch{return!1}}buildLibrarySkillsReport(e){const n=I(this.config.adapterType,this.config.aibot.clientType);return Ue({skillsDir:T.skills,mode:n,home:N(),cwd:e})}async ensureSlotStarted(e,n=6e4){const t=this.pool.getOrCreateSlot(e);if(!t)throw new Error("Failed to allocate session slot");t.startPromise&&(h.info(this.name,`ensureSlotStarted: awaiting startPromise for session=${e}`),await Promise.race([t.startPromise,new Promise((o,i)=>setTimeout(()=>i(new Error(`ensureSlotStarted timeout (${n}ms) session=${e}`)),n))]),h.info(this.name,`ensureSlotStarted: startPromise resolved for session=${e}`))}resolveOrphanTitle(e){if(e){if(this.reasonixTitleScan){const n=ct(e);if(!n)return;const t=this.reasonixTitleScan.get().filter(o=>o.stamp===n);return t.length===1?t[0].title:void 0}if((this.config.adapterType??"acp")==="cursor")return this.sessionScanCache.get().find(t=>t.sessionId===e)?.title}}resolveAgentSessionId(e){switch(this.config.adapterType??"acp"){case"claude":return e.claudeSessionId;case"codex":return e.codexThreadId;case"pi":return e.piSessionPath;case"codewhale":return e.codewhaleThreadId;case"agy":return e.agyConversationId;default:return e.acpSessionId}}providerKeyForAdapter(){const e=this.config.adapterType??"acp";switch(e){case"claude":case"codex":case"pi":case"codewhale":return e;default:return"acp"}}setResolvedAgentSessionId(e,n){const t=String(e??"").trim(),o=String(n??"").trim();if(!t||!o)return;switch(this.config.adapterType??"acp"){case"claude":this.bindingStore.setClaudeSessionId(t,o);break;case"codex":this.bindingStore.setCodexThreadId(t,o);break;case"pi":this.bindingStore.setPiSessionPath(t,o);break;case"codewhale":this.bindingStore.setCodeWhaleThreadId(t,o);break;case"agy":this.bindingStore.setAgyConversationId(t,o);break;default:this.bindingStore.setAcpSessionId(t,o);break}this.sessionScanCache.invalidate()}normalizePathForCompare(e){const n=String(e??"").trim();if(!n)return"";const t=P.resolve(n);return process.platform==="win32"?t.toLowerCase():t}ensureImportedAgentSession(e,n){const t=String(e??"").trim();if(!t)return;const o=this.normalizePathForCompare(n);let i="";const s=this.config.adapterType??"acp";if(s==="codex"?i=this.sessionScanCache.get().find(d=>d.threadId===t)?.cwd??"":s==="claude"?i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??"":s==="cursor"?i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??"":s==="acp"&&(i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??""),!i){for(const[,a]of this.bindingStore.entries())if(this.resolveAgentSessionId(a)===t){i=a.cwd??"";break}}if(!i){const a=new Error(`agent session not found: ${t}`);throw a.sessionControlErrorCode=p.invalidAgentSession,a}const r=this.normalizePathForCompare(i);if(r&&o&&r!==o){const a=new Error(`agent session cwd mismatch: expected ${n}, got ${i}`);throw a.sessionControlErrorCode=p.invalidAgentSession,a}}buildOpenedBindingResult(e,n,t="ready"){const o=this.bindingStore.get(e),i=o?String(this.resolveAgentSessionId(o)??"").trim():"",s={aibotSessionId:e,providerKey:this.providerKeyForAdapter(),cwd:n,workerStatus:t};return i&&(s.bindingId=i,s.agentSessionId=i),s}hasDiskScanner(){const e=this.config.adapterType??"acp";return e==="codex"||e==="claude"||e==="acp"}async handleListSessionsTextCommand(e){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});const n=this.config.adapterType??"acp",t=new Map;for(const a of this.pool.getAllSlots())t.set(a.sessionId,a);const o=Array.from(this.bindingStore.entries()),i=new Map;for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c){const l=t.get(a),u=l?l.adapter.getStatus().busy?"busy":"ready":"inactive";i.set(c,{aibotSessionId:a,workerStatus:u})}}const s=[],r=new Set;if(n==="codex"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.threadId);const c=i.get(d.threadId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.threadId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??(d.archived?"archived":"inactive")}`),s.push(` Created: ${new Date(d.createdAt).toISOString()}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="claude"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="acp"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="pi"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionPath);const c=i.get(d.sessionPath);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionPath}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="codewhale"||n==="opencode"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c&&r.has(c))continue;const l=t.get(a),u=l?l.adapter.getStatus().busy?"busy":"ready":"closed",g=this.resolveOrphanTitle(c)??(c?c.slice(0,8)+"\u2026":a.slice(0,8)+"\u2026");s.push(` Title: ${g}`),s.push(` AIBot: ${a}`),c&&s.push(` Agent: ${c}`),s.push(` CWD: ${d.cwd??"-"}`),s.push(` State: ${u}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}if(s.length===0){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:"No sessions found.",updated_at:Date.now()});return}this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:`Sessions (${s.filter(a=>a==="---").length}):
|
|
11
|
+
Error: ${t}`,1,!1),this.surfacedRunErrorEvents.add(e))}sendEventResultWithCleanup(e,n,t,o){const i=this.eventSessionIndex.get(e);n==="failed"&&i&&t?.trim()&&this.sendRunErrorAsChunk(e,i,t),this.auditController.markResponded(e,n,t),this.sendCtrl.sendEventResult(e,n,t,o),i&&(this.pool.eventComplete(e,i)===!1&&h.error(this.name,`Event terminal result could not release queue slot event=${e} session=${i} status=${n}`),this.pushQueueSnapshotForSession(i),this.conversationLog?.logResult?.(i,e,n,t),this.eventSessionIndex.delete(e)),this.inflightEvents.delete(e),this.restartCount.delete(e),this.surfacedRunErrorEvents.delete(e),n==="responded"&&(this.cachedProviderQuotaSampledAtMs=null,this.refreshAndPushProviderQuota(!0).catch(()=>{}))}async handleSessionInternalError(e){const{eventId:n,sessionId:t,errorMsg:o}=e;if(this.stopped)return;const i=this.inflightEvents.get(n);if(!i){h.warn(this.name,`[recovery] no inflight event for internalError event=${n} session=${t}; surface failure directly`),this.sendRunErrorAsChunk(n,t,o),this.sendEventResultWithCleanup(n,"failed",o,"agent_stop_failure");return}const s=(this.restartCount.get(n)??0)+1;this.restartCount.set(n,s);const r=this.config.adapterType??"acp";if(s>Z){h.error(this.name,`[recovery] adapter=${r} session=${t} event=${n} restart=${s}/${Z} outcome=give-up err=${o}`),this.sendRunErrorAsChunk(n,t,o),this.sendEventResultWithCleanup(n,"failed",o,"agent_stop_failure");return}h.info(this.name,`[recovery] adapter=${r} session=${t} event=${n} restart=${s}/${Z} outcome=restarting err=${o}`);const a=this.pool.drainQueuedForSession(t);a.length>0&&h.info(this.name,`[recovery] session=${t} preserved ${a.length} queued sibling event(s) across restart`);try{await this.pool.removeSlot(t)}catch(l){h.warn(this.name,`[recovery] removeSlot failed session=${t}: ${l instanceof Error?l.message:String(l)}`)}if(this.stopped)return;const d=e.replayOriginalContent?i.content:this.resolveRecoveryPrompt(r,i),c={...i,content:d};try{await this.pool.deliverInboundEvent(c)}catch(l){h.error(this.name,`[recovery] redeliver failed event=${n} session=${t}: ${l instanceof Error?l.message:String(l)}`),this.sendEventResultWithCleanup(n,"failed",l instanceof Error?l.message:String(l));return}for(const l of a){if(this.stopped)break;try{await this.pool.deliverInboundEvent(l)}catch(u){h.error(this.name,`[recovery] sibling redeliver failed event=${l.event_id} session=${t}: ${u instanceof Error?u.message:String(u)}`),this.sendEventResultWithCleanup(l.event_id,"failed",u instanceof Error?u.message:String(u))}}}resolveRecoveryPrompt(e,n){return e==="acp"?"continue":n.content}sendThinkingByRuntimeConfig(e,n,t){this.sendCtrl.sendThinking(e,n,t)}bufferStreamChunk(e,n,t,o,i){this.auditController.captureUnsequencedStreamChunk(e,t),this.sendCtrl.bufferOnly(e,n,t,o,i)}flushBufferedStreamText(e){}resolveEventRuntimeConfig(e){return this.sendCtrl.resolveEventRuntimeConfig(e)}captureEventRuntimeConfig(e){this.sendCtrl.captureEventRuntimeConfig(e),this.indexEventSession(e.event_id,e.session_id),this.auditController.markRecording(e.event_id),e.event_id&&!this.inflightEvents.has(e.event_id)&&this.inflightEvents.set(e.event_id,e)}indexEventSession(e,n){if(!e||!n)return;const t=this.eventSessionIndex.get(e);return t?(t!==n&&h.warn(this.name,`Ignoring event session mismatch event=${e} indexed=${t} supplied=${n}`),t):(this.eventSessionIndex.set(e,n),n)}shouldDropToolDisplayEvent(e){return this.sendCtrl.shouldDropToolDisplayEvent(e)}shouldDropThinkingDisplayEvent(e){return this.sendCtrl.shouldDropThinkingDisplayEvent(e)}shouldDropCodexDisplayEvent(e,n){return this.sendCtrl.shouldDropCodexDisplayEvent(e,n)}logCodexEventToConversation(e){if(!this.conversationLog||e.codex_method!=="item/agentMessage/delta")return;const t=e.codex_payload?.params?.delta;if(!t)return;const o=this.eventSessionIndex.get(e.event_id)??e.session_id;this.conversationLog.append(o,{ts:Date.now(),dir:"outbound",event_id:e.event_id,kind:"codex_delta",text_len:t.length,content:t})}isAcpRawTransportEnabled(){return(this.config.adapterOptions??{}).raw_transport===!0}shouldDropAcpRawDisplayEvent(e,n){return this.sendCtrl.shouldDropAcpRawDisplayEvent(e,n)}sendAcpRawEventEnvelope(e,n,t){this.shouldDropAcpRawDisplayEvent(e,t.type)||this.deliverRawEventEnvelope(e,n,t,"acp",this.buildAcpRawEventFallbackText(t))}buildAcpRawEventFallbackText(e){const n=String(e.type??"").trim();if(!n)return"[acp] event";switch(n){case"permission_request":return`Permission required: ${String(e.payload?.tool_title??e.payload?.tool_name??"permission request")}`;case"tool_use":return`[tool] ${String(e.payload?.tool_name??"tool")}`;case"tool_result":return"[tool result]";case"thinking":return"[thinking]";case"error":return`[error] ${String(e.payload?.message??"agent error")}`;case"result":return"[result]";default:return`[acp] ${n}`}}rawDetailSeq=0;deliverRawEventEnvelope(e,n,t,o,i){const s=Zt({envelope:t,fallbackText:i,channelKey:o,allocateRefId:()=>`${e}_rawd_${++this.rawDetailSeq}`}),r=()=>{this.aibotHandle.sendMsg({event_id:e,session_id:n,msg_type:1,content:s.fallbackText,extra:{channel_data:{[o]:{raw_event:s.envelope}},agent_api_origin:!0}})};if(!s.sharded){r();return}h.info("bridge",`${o} raw_event oversized, sharded delivery: event=${e} fields=${s.oversizedFields.map(a=>a.field).join(",")}`),(async()=>{for(const a of s.oversizedFields)await this.sendCtrl.deliverAuxiliaryLargeText(e,n,ei({envelopeType:t.type,field:a.field,fullText:a.fullText}),a.refClientMsgId);r()})().catch(a=>{h.warn("bridge",`${o} raw_event sharded delivery failed event=${e}: ${a}`)})}buildCursorRawEventFallbackText(e){const n=String(e?.type??"").trim(),t=e?.payload&&typeof e.payload=="object"?e.payload:{};switch(n){case"permission_request":return`Permission required: ${String(t.tool_title??t.tool_name??"permission request")}`;case"tool_use":case"tool_call":case"tool_execution_start":return`[tool] ${String(t.tool_name??t.toolName??"tool")}`;case"tool_result":case"tool_execution_end":case"tool_execution_update":return"[tool result]";case"error":return`[error] ${String(t.message??"agent error")}`;default:return n?`[cursor] ${n}`:"[cursor] event"}}sendToolExecutionCard(e,n,t,o){this.sendCtrl.sendToolExecutionCard(e,n,t,o)}sendGrixApprovalCard(e,n){this.aibotHandle.sendMsg({event_id:e.eventId,session_id:e.sessionId,client_msg_id:`perm_${W()}`,msg_type:1,content:e.toolTitle?`Permission required: ${e.toolTitle}`:"Permission request",extra:{channel_data:{execApproval:{approvalId:e.approvalId,approvalSlug:e.toolName},grix:{execApproval:{approval_command_id:e.approvalId,command:e.toolTitle||e.toolName,host:n}}},agent_api_origin:!0}})}sendGrixAgentQuestionCard(e,n,t){const o=t.questions.map(s=>s.header).join(", "),i=_e(`[Agent Question] ${t.request_id}`,"agent_question",t);this.aibotHandle.sendText({event_id:e,session_id:n,content:i,msg_type:1,extra:{card_type:"agent_question",summary_text:o}})}handleOpenCodeQuestionReplyEvent(e){const n=It(String(e.content??""));if(!n)return!1;this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});const o=this.pool.getSlot(e.session_id)?.adapter,i=o instanceof K?o.handleQuestionReplyEvent(n):{delivered:!1,errorCode:"interaction_request_not_pending",errorMsg:"The OpenCode question is no longer pending; the reply was not delivered."};return i.delivered?(h.info(this.name,`[opencode] question reply delivered event=${e.event_id} session=${e.session_id} request=${n.request_id}`),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",updated_at:Date.now()})):(h.warn(this.name,`[opencode] question reply rejected event=${e.event_id} session=${e.session_id} request=${n.request_id} code=${i.errorCode??"interaction_reply_failed"}`),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:i.errorCode??"interaction_reply_failed",msg:i.errorMsg??"The question reply was not delivered.",updated_at:Date.now()})),!0}async handleAibotEvent(e){if(this.relayEnvStale&&!this.hasPendingWork()&&await this.recycleAdaptersForRelayChange(),this.stopped){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:"agent shutting down",updated_at:Date.now()});return}this.logInboundConversation(e);const n=this.config.adapterType??"acp";if(n==="opencode"&&this.handleOpenCodeQuestionReplyEvent(e))return;const t=Gt(e.extra);if(t.error){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"connector_config_invalid",msg:t.error,updated_at:Date.now()});return}const o=t.patch,i=li(e.extra);if((i.state!=="absent"||i.error)&&h.info(this.name,`[audit-marker] event=${e.event_id} session=${e.session_id} msg=${e.msg_id??""} state=${i.state??"error"} scope=${i.options?.enabled?i.options.scope:""} error=${i.error??""}`),i.error){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"audit_config_invalid",msg:i.error,updated_at:Date.now()});return}if(i.state==="enabled"&&n!=="claude"&&n!=="codex"&&n!=="cursor"&&n!=="opencode"&&n!=="pi"&&n!=="codewhale"&&n!=="acp"){if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"audit_provider_unsupported",msg:`Audit replay is not supported for adapter: ${n}`,updated_at:Date.now()}),i.options?.enabled&&i.options.scope==="turn")try{this.aibotHandle.sendAuditState(X({state:"failed",eventId:e.event_id,sessionId:e.session_id,...e.msg_id===void 0?{}:{msgId:e.msg_id},errorCode:"audit_provider_unsupported",errorMessage:`Audit replay is not supported for adapter: ${n}`},Date.now()))}catch{}return}const s=Lt(e);if(s){if(i.state!=="absent"||s.verb===_.open)try{this.auditController.resolveSession({sessionId:e.session_id,provider:this.resolveAuditProvider(),extra:e.extra})}catch(c){this.sendAuditConfigurationError(e,c);return}if(s.verb===_.exec){await this.handleSessionControlCommand(s,e);return}if(s.verb===_.listSessions){await this.handleListSessionsTextCommand(e);return}if(n==="claude"){await this.handleSessionControlCommand(s,e);return}if(n==="codex"&&s.verb===_.open){await this.handleCodexSessionControlOpen(s,e);return}if(n==="pi"&&s.verb===_.open){await this.handlePiSessionControlOpen(s,e);return}if(n==="pi"&&s.verb===_.restart){await this.handlePiSessionControlRestart(e);return}if((n==="openhuman"||n==="opencode")&&s.verb===_.open){await this.handleOpenHumanSessionControlOpen(s,e);return}if(n==="codewhale"&&s.verb===_.open){await this.handleCodeWhaleSessionControlOpen(s,e);return}if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),s.verb===_.open){const c=s.args.trim();if(!c){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:p.cwdRequired,msg:"cwd is required",updated_at:Date.now()});return}try{const l=T.resolve(c);if(!(await F(l)).isDirectory()){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:p.invalidCwd,msg:`Path is not a directory: ${l}`,updated_at:Date.now()});return}}catch(l){const u=String(l?.code??""),g=u==="ENOENT"?`Directory does not exist: ${T.resolve(c)}`:u==="EACCES"||u==="EPERM"?"Directory is not accessible":`Invalid path: ${c}`;this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:p.invalidCwd,msg:g,updated_at:Date.now()});return}}if(s.verb===_.open){const c=this.bindingStore.get(e.session_id);if(c?.cwd)try{await F(c.cwd)}catch{h.info("bridge",`Stale binding detected for session ${e.session_id}: ${c.cwd} no longer exists, clearing`),this.bindingStore.delete(e.session_id),this.sessionBindings.delete(e.session_id),this.sessionProviderHints.delete(e.session_id),this.sessionProviderQuotas.delete(e.session_id),this.sessionProviderMeta.delete(e.session_id);const l=this.pool.getSlot(e.session_id);l?.adapter instanceof E&&l.adapter.getSessionBindings().delete(e.session_id)}}if(await this.handleSessionControlForPool(s,e),n==="acp"&&s.verb===_.stop){const l=this.bindingStore.get(e.session_id)?.cwd??"";await this.pool.removeSlot(e.session_id).catch(()=>{}),this.sessionBindings.delete(e.session_id),this.sessionProviderHints.delete(e.session_id),this.sessionProviderQuotas.delete(e.session_id),this.sessionProviderMeta.delete(e.session_id),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:`Session worker stopped for ${l}`,updated_at:Date.now()});return}if(le(s,e,this.sessionControlCtx(e.session_id),{...this.sessionControlSenders(),sendEventAck:()=>{}}),s.verb===_.open&&(await this.deferredMgr.release(e.session_id,this.deferredCallbacks()),n==="agy")){const c=this.bindingStore.get(e.session_id)?.cwd??"";c&&this.aibotHandle.sendUpdateBindingCard({session_id:e.session_id,worker_status:"ready",cwd:c,meta:this.buildAgyToolbarMeta(e.session_id)})}return}if(e.mirror_mode==="record_only"){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",updated_at:Date.now()});return}if(this.isStaleEvent(e)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",code:"event_stale",msg:"event is stale and will not be processed",updated_at:Date.now()});return}let r;try{r=this.auditController.resolveSession({sessionId:e.session_id,provider:this.resolveAuditProvider(),extra:e.extra,eventId:e.event_id}).session}catch(c){this.sendAuditConfigurationError(e,c);return}if(_i.has(n)&&!this.bindingStore.get(e.session_id)?.cwd){const l=n;h.info(this.name,`[${l}] binding missing session_id=${e.session_id} event_id=${e.event_id}`),this.deferredMgr.defer(l,String(e.session_id??"").trim(),this.prepareAuditedInboundEvent(e,o,r));const u=this.resolveBindingChannelKey(n);this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.aibotHandle.sendMsg({event_id:e.event_id,session_id:e.session_id,msg_type:1,content:"Session binding missing.",extra:{channel_data:{[u]:{sessionBinding:{status:"missing",reason:"binding_missing",error_code:p.bindingMissing}}}},quoted_message_id:e.msg_id});return}if((this.config.adapterType??"acp")==="acp"){const l=String(e.content??"").trim().match(/^\/(\S+)\s*(.*)/);if(l){const[,u,g]=l,f=this.pool.getSlot(e.session_id)?.adapter;if(f?.execCommand&&(f.getSupportedCommands?.()??[]).some(m=>m.name===u||m.name===`/${u}`)){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});try{const m=await f.execCommand(u,g.trim(),e.session_id);m.status==="options"&&m.data&&this.handleExecCommandOptions(e.session_id,u,m.data),this.aibotHandle.sendEventResult({event_id:e.event_id,status:m.status==="failed"?"failed":"responded",msg:m.message,updated_at:Date.now()})}catch(m){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:m instanceof Error?m.message:String(m),updated_at:Date.now()})}return}}}if(n==="codex"&&se(r?.options)){const c=this.pool.getSlot(e.session_id);if(c?.adapter instanceof te&&!c.adapter.hasRawApiCaptureRelay()){const l=c.eventQueue.snapshot(e.session_id);l.running.length===0&&l.queued.length===0?(h.info(this.name,`[audit-raw-capture] recreating idle Codex adapter before event=${e.event_id} session=${e.session_id}`),await this.pool.removeSlot(e.session_id)):h.warn(this.name,`[audit-raw-capture] Codex adapter lacks relay but is not idle event=${e.event_id} session=${e.session_id} running=${l.running.length} queued=${l.queued.length}`)}}const d=this.prepareAuditedInboundEvent(e,o,r);try{this.captureEventRuntimeConfig(d),await this.pool.deliverInboundEvent(d)}catch(c){if(this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()}),this.auditController.closeWithoutAdapter(e.event_id,"failed",c instanceof Error?c.message:String(c),{deliveryFailed:!0}),this.discardEventTrackingState(e.event_id),c instanceof xt)this.aibotHandle.sendEventResult({event_id:e.event_id,status:"failed",msg:c.message,updated_at:Date.now()});else throw c}}async handleCodexSessionControlOpen(e,n){const t=n.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()}),s=(r,a)=>this.aibotHandle.sendEventResult({event_id:n.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),s("failed",{msg:"Usage: /grix open <working-directory>",code:p.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{if(await this.resolveCwdForBinding(a.cwd)===r){i(),s("responded",{msg:`Session already bound to ${a.cwd}`});return}}catch{d=!0,h.info("bridge",`Stale codex binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){i(),s("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:p.rebindForbidden});return}}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),this.deferredMgr.sendCodexDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),s("responded",{msg:`Session bound to ${r}`})}catch(r){i(),s("failed",{code:p.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handleSessionControlForPool(e,n){e.verb===_.open&&await this.bindSessionForPool(n.session_id,e.args.trim())}async replayDeferredEventsForSession(e){await this.deferredMgr.release(e,this.deferredCallbacks())}async handleSessionControlLocalActionForPool(e){if(String(e.action_type??"")!==C.sessionControl)return;const n=e.params??{};if(String(n.verb??"").trim().toLowerCase()!==_.open)return;const o=String(n.session_id??"").trim(),i=String(n.cwd??"").trim();!o||!i||await this.bindSessionForPool(o,i)}async handleCodexSessionControlLocalActionOpen(e){const n=e.params??{},t=String(n.session_id??"").trim(),o=String(n.cwd??"").trim(),i=String(n.agent_session_id??"").trim(),s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){s("failed",void 0,p.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{const c=await this.resolveCwdForBinding(a.cwd);if(c===r){this.setResolvedAgentSessionId(t,i),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,c)});return}}catch{d=!0,h.info("bridge",`Stale codex binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){s("failed",void 0,p.rebindForbidden,`Session already bound to ${a.cwd}`);return}}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),this.deferredMgr.sendCodexDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){s("failed",void 0,r?.sessionControlErrorCode??p.invalidCwd,r instanceof Error?r.message:String(r))}}async handleCursorSessionControlLocalActionOpen(e){const n=e.params??{},t=String(n.session_id??"").trim(),o=String(n.cwd??"").trim(),i=String(n.agent_session_id??"").trim(),s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){s("failed",void 0,p.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{const c=await this.resolveCwdForBinding(a.cwd);if(c===r){this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,c),await this.bindSessionForPool(t,c),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:c,meta:this.buildCursorToolbarMeta(t)}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,c)});return}}catch{d=!0,h.info("bridge",`Stale cursor binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){s("failed",void 0,p.rebindForbidden,`Session already bound to ${a.cwd}`);return}}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),this.deferredMgr.sendCursorDeferredReplayComposing(t,this.deferredCallbacks()),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r,meta:this.buildCursorToolbarMeta(t)}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){s("failed",void 0,r?.sessionControlErrorCode??p.invalidCwd,r instanceof Error?r.message:String(r))}}async handlePiSessionControlOpen(e,n){const t=n.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()}),s=(r,a)=>this.aibotHandle.sendEventResult({event_id:n.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),s("failed",{msg:"Usage: /grix open <working-directory>",code:p.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){let d=!1;try{if(await this.resolveCwdForBinding(a.cwd)===r){i(),s("responded",{msg:`Session already bound to ${a.cwd}`}),this.aibotHandle.sendMsg({event_id:n.event_id,session_id:t,msg_type:1,content:`\u2705 Session already bound to \`${a.cwd}\``,quoted_message_id:n.msg_id});return}}catch{d=!0,h.info("bridge",`Stale pi binding for session ${t}: ${a.cwd} no longer exists, allowing rebind`)}if(!d){i(),s("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:p.rebindForbidden});return}}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),await this.ensureSlotStarted(t).catch(d=>{h.warn("bridge",`pi ensureSlotStarted on bind failed (non-fatal): ${d instanceof Error?d.message:String(d)}`)}),await this.deferredMgr.release(t,this.deferredCallbacks()),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),s("responded",{msg:`Session bound to ${r}`}),this.aibotHandle.sendMsg({event_id:n.event_id,session_id:t,msg_type:1,content:`\u2705 Working directory bound: \`${r}\``,quoted_message_id:n.msg_id})}catch(r){i(),s("failed",{code:p.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handlePiSessionControlRestart(e){const n=e.session_id,t=()=>this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:n,received_at:Date.now()}),o=(r,a)=>this.aibotHandle.sendEventResult({event_id:e.event_id,status:r,...a,updated_at:Date.now()}),s=this.bindingStore.get(n)?.cwd??"";if(!s){t(),o("failed",{msg:"session binding was not found",code:p.bindingMissing});return}t(),await this.pool.removeSlot(n).catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:"ready",cwd:s}),o("responded",{msg:`Session worker restarted for ${s}`})}async handlePiSessionControlRestartLocalAction(e){const n=e.params??{},t=String(n.session_id??"").trim(),i=(t?this.bindingStore.get(t):void 0)?.cwd??"",s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t){s("failed",void 0,"session_id_required","session_id is required for restart");return}if(!i){s("failed",void 0,p.bindingMissing,"Session binding missing. Open a workspace first.");return}await this.pool.removeSlot(t).catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:i}),s("ok",{outcome:"restarted",binding:{aibotSessionId:t,cwd:i,workerStatus:"ready"}})}async syncOpenCodeBinding(e,n){if(!e||!n)return;try{this.resolveSessionModelId(e),this.resolveSessionModeId(e)}catch(o){h.warn(this.name,`[opencode] resolve global toolbar default failed: ${o instanceof Error?o.message:String(o)}`)}let t=this.pool.getSlot(e);!t&&(this.config.adapterType??"acp")==="opencode"&&(await this.ensureSlotStarted(e),t=this.pool.getSlot(e)),t?.startPromise&&await t.startPromise,t?.adapter instanceof K&&await t.adapter.bindSession(e,n)}async bindSessionForPool(e,n){const t=String(n??"").trim();if(!e||!t)return;const o=this.pool.getOrCreateSlot(e);if(!o||(o.startPromise&&await o.startPromise,!o.adapter))return;const i=o.adapter instanceof E?o.adapter:null;if(!i?.hasSessionBinding)return;const s=await this.resolveCwdForBinding(t);i.announceDeferredComposing(e),await i.bindSession(e,s),this.sessionBindings.set(e,s),this.sessionScanCache.invalidate(),i.replayDeferredEvents(e)}deferredCallbacks(){return{captureEventRuntimeConfig:e=>this.captureEventRuntimeConfig(e),deliverInboundEvent:e=>this.pool.deliverInboundEvent(e),sendEventResult:(e,n,t)=>this.sendEventResultWithCleanup(e,n,t),sendSessionComposing:(e,n,t)=>{const o={};n&&(o.ttl_ms=t?.ttlMs??3e4,t?.activity&&(o.activity=t.activity)),this.aibotHandle.sendSessionActivitySet({session_id:e,kind:"composing",active:n,...o})}}}async handleOpenHumanSessionControlOpen(e,n){const t=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:n.session_id,received_at:Date.now()});try{const o=await this.resolveCwdForBinding(e.args.trim());await this.handleSessionControlForPool(e,n),await this.syncOpenCodeBinding(n.session_id,o),le(e,n,this.sessionControlCtx(n.session_id),this.sessionControlSenders()),await this.deferredMgr.release(n.session_id,this.deferredCallbacks())}catch(o){t(),this.aibotHandle.sendEventResult({event_id:n.event_id,status:"failed",code:o?.cwdErrorCode,msg:o instanceof Error?o.message:String(o),updated_at:Date.now()})}}async handleCodeWhaleSessionControlOpen(e,n){const t=n.session_id,o=e.args.trim(),i=()=>this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()}),s=(r,a)=>this.aibotHandle.sendEventResult({event_id:n.event_id,status:r,...a,updated_at:Date.now()});if(!o){i(),s("failed",{msg:"Usage: /grix open <working-directory>",code:p.cwdRequired});return}try{const r=await this.resolveCwdForBinding(o),a=this.bindingStore.get(t);if(a?.cwd){if(await this.resolveCwdForBinding(a.cwd)!==r){i(),s("failed",{msg:`Session already bound to ${a.cwd}. Rebinding is not allowed.`,code:p.rebindForbidden});return}i(),s("responded",{msg:`Session already bound to ${a.cwd}`});return}this.bindingStore.set(t,r),this.sessionBindings.set(t,r),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),i(),s("responded",{msg:`Session bound to ${r}`})}catch(r){i(),s("failed",{code:p.invalidCwd,msg:r instanceof Error?r.message:String(r)})}}async handleCodeWhaleSessionControlLocalActionOpen(e){const n=e.params??{},t=String(n.session_id??"").trim(),o=String(n.cwd??"").trim(),i=String(n.agent_session_id??"").trim(),s=(r,a,d,c)=>{this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:r,...a!==void 0?{result:a}:{},...d?{error_code:d}:{},...c?{error_msg:c}:{}})};if(!t||!o){s("failed",void 0,p.cwdRequired,"session cwd is required");return}try{const r=await this.resolveCwdForBinding(o);this.ensureImportedAgentSession(i,r);const a=this.bindingStore.get(t);if(a?.cwd){const d=await this.resolveCwdForBinding(a.cwd);if(d!==r){s("failed",void 0,p.rebindForbidden,`Session already bound to ${a.cwd}`);return}this.setResolvedAgentSessionId(t,i),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,d)});return}this.bindingStore.set(t,r),this.setResolvedAgentSessionId(t,i),this.sessionBindings.set(t,r),await this.bindSessionForPool(t,r),await this.deferredMgr.release(t,this.deferredCallbacks(),{announceComposing:!1}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"ready",cwd:r}),s("ok",{outcome:"opened",binding:this.buildOpenedBindingResult(t,r)})}catch(r){s("failed",void 0,r?.sessionControlErrorCode??p.invalidCwd,r instanceof Error?r.message:String(r))}}normalizeClaudeModeId(e){return String(e??"").trim().toLowerCase()===R.approval?R.approval:R.fullAuto}handleExecCommandOptions(e,n,t){const o=Array.isArray(t?.options)?t.options:[];if(o.length===0)return;const s=this.bindingStore.get(e)?.cwd??"",r={};if(n==="model"){r.available_models=o.map(d=>({id:d.id,displayName:d.label}));const a=o.find(d=>d.current);a&&(r.model_id=a.id)}else if(n==="mode"){r.available_modes=o.map(d=>({id:d.id,displayName:d.label}));const a=o.find(d=>d.current);a&&(r.mode_id=a.id)}else r[`${n}_options`]=o;this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:"ready",cwd:s,meta:r})}resolveSessionModelId(e){const n=this.bindingStore.getModelId(e);if(n)return n;const t=String(this.globalConfigStore?.get(this.name)?.modelId??"").trim();if(t)return this.bindingStore.setModelId(e,t),t}resolveSessionModeId(e){const n=this.bindingStore.getModeId(e);if(n)return n;const t=String(this.globalConfigStore?.get(this.name)?.modeId??"").trim();if(t)return this.bindingStore.setModeId(e,t),t}resolveClaudeSessionEffort(e){const n=this.bindingStore.getEffort(e);if(n)return n;if(this.bindingStore.get(e)?.claudeSessionId)return;const o=j(this.globalConfigStore?.get(this.name)?.effort);if(o)return this.bindingStore.setEffort(e,o),o}resolveCodexSessionModelId(e){const n=this.bindingStore.getCodexModelId(e);if(n)return n;if(this.bindingStore.getCodexThreadId(e))return;const t=String(this.globalConfigStore?.get(this.name)?.codexModelId??"").trim();if(t)return this.bindingStore.setCodexModelId(e,t),t}resolveCodexNewSessionGlobalDefault(e,n,t){const o=String(n??"").trim();if(o&&!this.bindingStore.getCodexThreadId(e))return t(o),o}buildCursorToolbarMeta(e){const n=this.pool.getSlot(e);if(n?.adapter instanceof B)return n.adapter.getToolbarMeta(e);const t=this.bindingStore.getModelId(e)??"auto",o=this.bindingStore.getCursorModeId(e)??"full_auto";return{model_id:t,mode_id:o,currentModelId:t,currentModeId:o,available_models:[],available_modes:Ee.map(i=>({...i}))}}buildAgyToolbarMeta(e){if(!this.bindingStore.get(e))return;const t=$e(this.config.agent.command),o=t.length>0?t[0].id:"";let i=this.resolveSessionModelId(e)||o;t.length>0&&!t.some(r=>r.id===i)&&(i=o);const s=Pe();return{model_id:i,currentModelId:i,available_models:t.map(r=>({id:r.id,displayName:r.displayName})),...s.plan!==void 0&&{plan:s.plan},...s.quota_exhausted!==void 0&&{quota_exhausted:s.quota_exhausted},...s.quota_reset_at!==void 0&&{quota_reset_at:s.quota_reset_at},...s.available_credits!==void 0&&{available_credits:s.available_credits}}}async handleAgySetModel(e,n){const t=e.params??{},o=String(t.model_id??t.modelId??"").trim();if(!n){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"session_id_required",error_msg:"session_id is required for set_model"});return}if(!o){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"set_model_invalid",error_msg:"model_id is required"});return}const i=this.bindingStore.get(n);if(!i?.cwd){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:p.bindingMissing,error_msg:"session binding was not found"});return}i.modelId!==o&&(this.bindingStore.setModelId(n,o),this.globalConfigStore?.set(this.name,{modelId:o})),await Te().catch(()=>{}),this.aibotHandle.sendUpdateBindingCard({session_id:n,worker_status:"ready",cwd:i.cwd,meta:this.buildAgyToolbarMeta(n)}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"model_set",model_id:o,binding:{cwd:i.cwd,model_id:o}}})}buildClaudeToolbarMeta(e){const n=this.bindingStore.get(e);if(!n)return;const t=z(),o=t.length>0?t[0].id:"";let i=this.resolveSessionModelId(e)||o;t.length>0&&!t.some(g=>g.id===i)&&(i=o);const s=this.normalizeClaudeModeId(n.modeId);let r;try{const g=this.providerResolver?this.providerResolver():this.config.agent.provider;r=j(g?.effort)}catch{}const a=this.resolveClaudeSessionEffort(e)??r??"auto",d={model_id:i,mode_id:s,currentModelId:i,currentModeId:s,available_efforts:[...D],effort:a,reasoning_effort:a,available_models:t.map(g=>({id:g.id,displayName:g.displayName}))},c=this.pool.getSlot(e),l=c?.adapter instanceof M?c.adapter.getSessionState():null;(l?.rateLimits?.fiveHour||l?.rateLimits?.sevenDay)&&(this.cachedClaudeRateLimitState=l);const u=l??this.getFreshClaudeRateLimitState();if(u){const g=u.rateLimits;g&&(g.fiveHour||g.sevenDay)&&(d.rate_limits={...g.fiveHour?{fiveHour:g.fiveHour}:{},...g.sevenDay?{sevenDay:g.sevenDay}:{},sampledAt:u.sampledAt||Date.now()})}if(l){const g=l.contextWindow;g.usedPercentage!=null&&(d.context_window={usedPercentage:g.usedPercentage,remainingPercentage:g.remainingPercentage})}if(!d.rate_limits&&this.cachedProviderQuota?.success){this.isRateLimitsCacheFresh(this.cachedProviderQuotaSampledAtMs)||this.maybeQueryProviderQuota().catch(()=>{});const g=this.providerQuotaToRateLimits(this.cachedProviderQuota);g&&(d.rate_limits=g),h.info(this.name,`[toolbar-meta] provider quota fallback: hasCached=${!!this.cachedProviderQuota} fromProvider=${JSON.stringify(g)}`)}return d.rate_limits&&h.info(this.name,`[toolbar-meta] rate_limits included: ${JSON.stringify(d.rate_limits)}`),d}providerQuotaToCodexRateLimits(e){return fe(e,this.cachedProviderQuotaSampledAtMs??Date.now())}providerQuotaToRateLimits(e){return pe(e,this.cachedProviderQuotaSampledAtMs??Date.now())}async resolveCwdForBinding(e){const n=String(e??"").trim();if(process.platform!=="win32"&&(/^[a-zA-Z]:[\\/]/.test(n)||/^\\\\/.test(n))){const i=new Error(`Specified path is not valid on this host: ${n}`);throw i.cwdErrorCode=p.invalidCwd,i}const t=T.resolve(n);let o;try{o=await F(t)}catch(i){const s=String(i?.code??"");if(s==="ENOENT"){const r=new Error(`Specified path does not exist: ${t}`);throw r.cwdErrorCode=p.invalidCwd,r}if(s==="EACCES"||s==="EPERM"){const r=new Error("Specified path is not accessible.");throw r.cwdErrorCode=p.invalidCwd,r}throw i}if(!o.isDirectory()){const i=new Error("Specified path is not a directory.");throw i.cwdErrorCode=p.invalidCwd,i}try{return await Se(t)}catch{return t}}getClaudeWorkerStatus(e){const n=this.pool.getSlot(e);return n?n.state==="starting"?"starting":n.state==="stopped"?"stopped":n.adapter.getStatus().busy?"busy":"ready":"stopped"}refreshClaudeWorkerStatusCard(e,n){const t=this.getClaudeWorkerStatus(e);return this.claudeWorkerStatus.set(e,t),this.aibotHandle.sendUpdateBindingCard({session_id:e,worker_status:t,cwd:n,meta:this.buildClaudeToolbarMeta(e)}),t}async handleSkillUploadLocalAction(e){const n=e.params??{},t=String(n.name??"").trim(),o=String(n.session_id??"").trim();if(!t){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"MISSING_SKILL_NAME",error_msg:"name is required"});return}const i=I(this.config.adapterType,this.config.aibot.clientType),s=this.bindingStore.get(o)?.cwd||this.sessionBindings.get(o)||void 0,r=i==="kiro"?s:s||process.cwd();try{await Oe(t,{mode:i,projectDir:r},{apiKey:this.config.aibot.apiKey,wsUrl:this.config.aibot.url}),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{name:t}})}catch(a){h.warn(this.name,`skill_upload failed name=${t}: ${a instanceof Error?a.message:String(a)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"SKILL_UPLOAD_FAILED",error_msg:a instanceof Error?a.message:String(a)})}}async handleSkillEnableLocalAction(e){const n=e.params??{},t=String(n.name??"").trim(),o=String(n.session_id??"").trim(),i=String(n.scope??"").trim(),s=String(n.actor_id??"").trim(),r=n.force==="replace_link"||n.force==="replace_with_link"?n.force:void 0,a=I(this.config.adapterType,this.config.aibot.clientType),d=this.bindingStore.get(o)?.cwd||this.sessionBindings.get(o)||void 0;try{const c=await Fe({skillsDir:P.skills,mode:a,home:N(),cwd:d},{name:t,scope:i,actorId:s,force:r});h.info(this.name,`skill_enable name=${t} scope=${i} actor=${s||"-"} changed=${c.changed}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{name:c.name,scope:c.scope,path:c.path,changed:c.changed,enable_state:c.status,uninstallable:!0}})}catch(c){const l=c instanceof re?c.code:"SKILL_ENABLE_FAILED";h.warn(this.name,`skill_enable failed name=${t} scope=${i}: ${c instanceof Error?c.message:String(c)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:l,error_msg:c instanceof Error?c.message:String(c),result:{conflict_kind:l==="CONFLICT"||l==="NEEDS_FORCE"||l==="BLOCKED"?l.toLowerCase():void 0}})}this.forceRefreshSkills()}handleSkillRefreshLocalAction(e){const n=e.params??{},t=String(n.session_id??"").trim();if(this.forceRefreshSkills(t||void 0)){this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{session_id:t||void 0}});return}h.warn(this.name,`skill_refresh produced no report session=${t||"-"}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:"SKILL_REFRESH_FAILED",error_msg:"skill rescan produced no report (scan error or empty skill set)"})}async handleSkillDisableLocalAction(e){const n=e.params??{},t=String(n.name??"").trim(),o=String(n.session_id??"").trim(),i=String(n.scope??"").trim(),s=String(n.actor_id??"").trim(),r=I(this.config.adapterType,this.config.aibot.clientType),a=this.bindingStore.get(o)?.cwd||this.sessionBindings.get(o)||void 0;try{const d=await Ne({skillsDir:P.skills,mode:r,home:N(),cwd:a},{name:t,scope:i,actorId:s});h.info(this.name,`skill_disable name=${t} scope=${i} actor=${s||"-"} removed=${d.removed}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{name:d.name,scope:d.scope,path:d.path,removed:d.removed,enable_state:"none",uninstallable:!1}})}catch(d){const c=d instanceof re?d.code:"SKILL_DISABLE_FAILED";h.warn(this.name,`skill_disable failed name=${t} scope=${i}: ${d instanceof Error?d.message:String(d)}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:c,error_msg:d instanceof Error?d.message:String(d),result:{conflict_kind:c==="CONFLICT"||c==="BLOCKED"?c.toLowerCase():void 0}})}this.forceRefreshSkills()}computeSkillReport(e){const n=I(this.config.adapterType,this.config.aibot.clientType),t=n==="kiro"?e:e||process.cwd();let o;try{o=qe({mode:n,projectDir:t})}catch{return null}if(o.length===0)return null;const i=JSON.stringify(o.map(s=>`${s.source}:${s.name}`));return{skills:oe(o,P.skills),hash:i}}reportSessionSkills(e){const n=this.bindingStore.get(e)?.cwd||this.sessionBindings.get(e)||void 0,t=this.computeSkillReport(n);if(t&&t.hash!==this.lastReportedSkillsHash){this.lastReportedSkillsHash=t.hash;try{this.aibotHandle.sendSkillsUpdate({skills:t.skills,library_skills:this.buildLibrarySkillsReport(n)})}catch{}}}forceRefreshSkills(e){const n=e?.trim(),t=n||this.bindingStore.getMostRecentlyUpdatedSessionId({requireCwd:!0}),o=n?this.bindingStore.get(n)?.cwd||this.sessionBindings.get(n)||void 0:t?this.bindingStore.get(t)?.cwd:void 0,i=this.computeSkillReport(o);if(!i)return!1;this.lastReportedSkillsHash=i.hash;try{return this.aibotHandle.sendSkillsUpdate({skills:i.skills,library_skills:this.buildLibrarySkillsReport(o)}),!0}catch{return!1}}buildLibrarySkillsReport(e){const n=I(this.config.adapterType,this.config.aibot.clientType);return Ue({skillsDir:P.skills,mode:n,home:N(),cwd:e})}async ensureSlotStarted(e,n=6e4){const t=this.pool.getOrCreateSlot(e);if(!t)throw new Error("Failed to allocate session slot");t.startPromise&&(h.info(this.name,`ensureSlotStarted: awaiting startPromise for session=${e}`),await Promise.race([t.startPromise,new Promise((o,i)=>setTimeout(()=>i(new Error(`ensureSlotStarted timeout (${n}ms) session=${e}`)),n))]),h.info(this.name,`ensureSlotStarted: startPromise resolved for session=${e}`))}resolveOrphanTitle(e){if(e){if(this.reasonixTitleScan){const n=ct(e);if(!n)return;const t=this.reasonixTitleScan.get().filter(o=>o.stamp===n);return t.length===1?t[0].title:void 0}if((this.config.adapterType??"acp")==="cursor")return this.sessionScanCache.get().find(t=>t.sessionId===e)?.title}}resolveAgentSessionId(e){switch(this.config.adapterType??"acp"){case"claude":return e.claudeSessionId;case"codex":return e.codexThreadId;case"pi":return e.piSessionPath;case"codewhale":return e.codewhaleThreadId;case"agy":return e.agyConversationId;default:return e.acpSessionId}}providerKeyForAdapter(){const e=this.config.adapterType??"acp";switch(e){case"claude":case"codex":case"pi":case"codewhale":return e;default:return"acp"}}setResolvedAgentSessionId(e,n){const t=String(e??"").trim(),o=String(n??"").trim();if(!t||!o)return;switch(this.config.adapterType??"acp"){case"claude":this.bindingStore.setClaudeSessionId(t,o);break;case"codex":this.bindingStore.setCodexThreadId(t,o);break;case"pi":this.bindingStore.setPiSessionPath(t,o);break;case"codewhale":this.bindingStore.setCodeWhaleThreadId(t,o);break;case"agy":this.bindingStore.setAgyConversationId(t,o);break;default:this.bindingStore.setAcpSessionId(t,o);break}this.sessionScanCache.invalidate()}normalizePathForCompare(e){const n=String(e??"").trim();if(!n)return"";const t=T.resolve(n);return process.platform==="win32"?t.toLowerCase():t}ensureImportedAgentSession(e,n){const t=String(e??"").trim();if(!t)return;const o=this.normalizePathForCompare(n);let i="";const s=this.config.adapterType??"acp";if(s==="codex"?i=this.sessionScanCache.get().find(d=>d.threadId===t)?.cwd??"":s==="claude"?i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??"":s==="cursor"?i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??"":s==="acp"&&(i=this.sessionScanCache.get().find(d=>d.sessionId===t)?.cwd??""),!i){for(const[,a]of this.bindingStore.entries())if(this.resolveAgentSessionId(a)===t){i=a.cwd??"";break}}if(!i){const a=new Error(`agent session not found: ${t}`);throw a.sessionControlErrorCode=p.invalidAgentSession,a}const r=this.normalizePathForCompare(i);if(r&&o&&r!==o){const a=new Error(`agent session cwd mismatch: expected ${n}, got ${i}`);throw a.sessionControlErrorCode=p.invalidAgentSession,a}}buildOpenedBindingResult(e,n,t="ready"){const o=this.bindingStore.get(e),i=o?String(this.resolveAgentSessionId(o)??"").trim():"",s={aibotSessionId:e,providerKey:this.providerKeyForAdapter(),cwd:n,workerStatus:t};return i&&(s.bindingId=i,s.agentSessionId=i),s}hasDiskScanner(){const e=this.config.adapterType??"acp";return e==="codex"||e==="claude"||e==="acp"}async handleListSessionsTextCommand(e){this.aibotHandle.sendEventAck({event_id:e.event_id,session_id:e.session_id,received_at:Date.now()});const n=this.config.adapterType??"acp",t=new Map;for(const a of this.pool.getAllSlots())t.set(a.sessionId,a);const o=Array.from(this.bindingStore.entries()),i=new Map;for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c){const l=t.get(a),u=l?l.adapter.getStatus().busy?"busy":"ready":"inactive";i.set(c,{aibotSessionId:a,workerStatus:u})}}const s=[],r=new Set;if(n==="codex"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.threadId);const c=i.get(d.threadId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.threadId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??(d.archived?"archived":"inactive")}`),s.push(` Created: ${new Date(d.createdAt).toISOString()}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="claude"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="acp"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="pi"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionPath);const c=i.get(d.sessionPath);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionPath}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}else if(n==="codewhale"||n==="opencode"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId);d.title&&s.push(` Title: ${d.title}`),s.push(` Agent: ${d.sessionId}`),c&&s.push(` AIBot: ${c.aibotSessionId}`),s.push(` CWD: ${d.cwd||"-"}`),s.push(` State: ${c?.workerStatus??"inactive"}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}}for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c&&r.has(c))continue;const l=t.get(a),u=l?l.adapter.getStatus().busy?"busy":"ready":"closed",g=this.resolveOrphanTitle(c)??(c?c.slice(0,8)+"\u2026":a.slice(0,8)+"\u2026");s.push(` Title: ${g}`),s.push(` AIBot: ${a}`),c&&s.push(` Agent: ${c}`),s.push(` CWD: ${d.cwd??"-"}`),s.push(` State: ${u}`),s.push(` Updated: ${new Date(d.updatedAt).toISOString()}`),s.push("---")}if(s.length===0){this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:"No sessions found.",updated_at:Date.now()});return}this.aibotHandle.sendEventResult({event_id:e.event_id,status:"responded",msg:`Sessions (${s.filter(a=>a==="---").length}):
|
|
12
12
|
${s.join(`
|
|
13
13
|
`)}`,updated_at:Date.now()})}async handleListSessionsLocalAction(e){const n=this.config.adapterType??"acp",t=new Map;for(const a of this.pool.getAllSlots())t.set(a.sessionId,a);const o=Array.from(this.bindingStore.entries()),i=new Map;for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c){const l=t.get(a),u=l?l.adapter.getStatus().busy?"busy":"ready":"inactive";i.set(c,{aibotSessionId:a,workerStatus:u,bindingUpdatedAt:d.updatedAt??0})}}const s=[],r=new Set;if(n==="codex"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.threadId);const c=i.get(d.threadId),l={agentSessionId:d.threadId,cwd:d.cwd||null,workerStatus:c?.workerStatus??(d.archived?"archived":"inactive"),createdAt:d.createdAt,updatedAt:d.updatedAt,archived:d.archived};c&&(l.aibotSessionId=c.aibotSessionId),d.title&&(l.title=d.title),s.push(l)}}else if(n==="claude"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId),l={agentSessionId:d.sessionId,cwd:d.cwd||null,workerStatus:c?.workerStatus??"inactive",updatedAt:d.updatedAt};c&&(l.aibotSessionId=c.aibotSessionId),d.title&&(l.title=d.title),s.push(l)}}else if(n==="acp"){const a=this.sessionScanCache.get();for(const d of a){r.add(d.sessionId);const c=i.get(d.sessionId),l=c&&c.bindingUpdatedAt>0?c.bindingUpdatedAt:d.updatedAt,u={agentSessionId:d.sessionId,cwd:d.cwd||null,agentType:d.agentType,workerStatus:c?.workerStatus??"inactive",updatedAt:l};c&&(u.aibotSessionId=c.aibotSessionId),d.title&&(u.title=d.title),d.createdAt&&(u.createdAt=d.createdAt),s.push(u)}}else if(n==="pi"||n==="codewhale"||n==="opencode"){const a=this.sessionScanCache.get();for(const d of a){const c="sessionPath"in d?d.sessionPath:d.sessionId;r.add(c);const l=i.get(c),u={agentSessionId:c,cwd:d.cwd||null,workerStatus:l?.workerStatus??("archived"in d&&d.archived?"archived":"inactive"),updatedAt:l&&l.bindingUpdatedAt>0?l.bindingUpdatedAt:d.updatedAt};l&&(u.aibotSessionId=l.aibotSessionId),d.title&&(u.title=d.title),d.createdAt&&(u.createdAt=d.createdAt),"archived"in d&&(u.archived=d.archived),s.push(u)}}for(const[a,d]of o){const c=this.resolveAgentSessionId(d);if(c&&r.has(c))continue;const l=t.get(a),u=l?l.adapter.getStatus().busy?"busy":"ready":"closed",g={aibotSessionId:a,cwd:d.cwd??null,workerStatus:u,updatedAt:d.updatedAt,title:this.resolveOrphanTitle(c)??(c?`${c.slice(0,8)}\u2026`:`${a.slice(0,8)}\u2026`)};c&&(g.agentSessionId=c),s.push(g)}s.sort((a,d)=>d.updatedAt-a.updatedAt),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"sessions_listed",sessions:s,total:s.length}})}async handleSyncHistoryLocalAction(e){const n=e.params??{},t=String(n.provider_key??this.config.adapterType??"acp").trim().toLowerCase(),o=String(n.agent_session_id??"").trim(),i=String(n.cwd??"").trim(),s=typeof n.sync_run_id=="string"&&n.sync_run_id?n.sync_run_id:void 0,r=(d,c)=>{h.warn(this.name,`sync_history failed action_id=${e.action_id} provider=${t} code=${d} msg=${c}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"failed",error_code:d,error_msg:c})};if(!o){r(q.agentSessionRequired,"agent_session_id is required for sync_history");return}if(!i){r(q.cwdRequired,"cwd is required for sync_history");return}const a=Be(t);if(!a){r(q.providerUnsupported,`provider ${t} does not support sync_history`);return}try{const d=await a({session_id:String(n.session_id??""),provider_key:t,agent_session_id:o,cwd:i,cursor:typeof n.cursor=="string"&&n.cursor?n.cursor:void 0,limit:typeof n.limit=="number"?n.limit:void 0,sync_run_id:s});h.debug(this.name,`sync_history ok action_id=${e.action_id} provider=${t} agent_session_id=${o} messages=${d.messages.length} has_more=${d.has_more}`),this.aibotHandle.sendLocalActionResult({action_id:e.action_id,status:"ok",result:{outcome:"history_synced",messages:d.messages,has_more:d.has_more,next_cursor:d.next_cursor,...s?{sync_run_id:s}:{}}})}catch(d){d instanceof Qe?r(d.code,d.message):r(q.runtimeError,String(d))}}async handleSessionControlCommand(e,n){const t=n.session_id,o=(i,s,r)=>{this.aibotHandle.sendEventResult({event_id:n.event_id,status:i,...s?{msg:s}:{},...r?{code:r}:{},updated_at:Date.now()})};this.aibotHandle.sendEventAck({event_id:n.event_id,session_id:t,received_at:Date.now()});try{switch(e.verb){case _.open:{await G().catch(()=>{});const i=e.args.trim();if(!i){o("failed","Usage: /grix open <working-directory>",p.cwdRequired);return}let s="";try{s=await this.resolveCwdForBinding(i)}catch(a){o("failed",a instanceof Error?a.message:String(a),p.invalidCwd);return}const r=this.bindingStore.get(t);if(r?.cwd){const a=await this.resolveCwdForBinding(r.cwd);if(a!==s){o("failed","session binding cannot be changed to another working directory",p.rebindForbidden);return}this.bindingStore.ensureModeId(t,R.fullAuto),this.sessionBindings.set(t,a),await this.ensureSlotStarted(t),await this.deferredMgr.release(t,this.deferredCallbacks()),this.refreshClaudeWorkerStatusCard(t,a),o("responded",`Working directory already bound: ${a}`);return}this.bindingStore.set(t,s,{modeId:R.fullAuto}),this.sessionBindings.set(t,s),await this.ensureSlotStarted(t),await this.deferredMgr.release(t,this.deferredCallbacks()),this.refreshClaudeWorkerStatusCard(t,s),o("responded",`Session bound to ${s}`);return}case _.where:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",p.bindingMissing);return}o("responded",`Working directory: ${i.cwd}`);return}case _.status:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",p.bindingMissing);return}const s=this.normalizeClaudeModeId(i.modeId),r=this.getClaudeWorkerStatus(t);o("responded",`Status: worker=${r} mode=${s} cwd=${i.cwd}`);return}case _.stop:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",p.bindingMissing);return}await this.pool.removeSlot(t),this.claudeWorkerStatus.set(t,"stopped"),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"stopped",cwd:i.cwd}),this.aibotHandle.sendUpdateBindingCard({session_id:t,worker_status:"stopped",cwd:i.cwd,meta:this.buildClaudeToolbarMeta(t)}),o("responded",`Session worker stopped for ${i.cwd}`);return}case _.restart:{const i=this.bindingStore.get(t);if(!i?.cwd){o("failed","session binding was not found",p.bindingMissing);return}await this.pool.removeSlot(t),await this.ensureSlotStarted(t),this.refreshClaudeWorkerStatusCard(t,i.cwd),o("responded",`Session worker restarted for ${i.cwd}`);return}case _.setMode:{const i=e.args.trim();if(!i){o("failed","Usage: /grix set_mode <mode-id>",x.modeInvalid);return}const s=this.normalizeClaudeModeId(i);if(s!==i.toLowerCase()){o("failed","set mode_id is invalid",x.modeInvalid);return}const r=this.bindingStore.get(t);if(!r?.cwd){o("failed","session binding was not found",p.bindingMissing);return}if(this.normalizeClaudeModeId(r.modeId)===s){o("responded",`Mode unchanged: ${s}`);return}if(this.getClaudeWorkerStatus(t)==="busy"){o("failed","\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148 /grix stop \u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u5F0F",p.workerBusy);return}this.bindingStore.setModeId(t,s),await this.pool.removeSlot(t),await this.ensureSlotStarted(t),this.refreshClaudeWorkerStatusCard(t,r.cwd),o("responded",`Mode set to ${s}`);return}case _.setModel:{const i=e.args.trim();if(!i){o("failed","Usage: /grix set_model <model-id>");return}const s=this.bindingStore.get(t);if(!s?.cwd){o("failed","session binding was not found",p.bindingMissing);return}if((s.modelId??"")===i){o("responded",`Model unchanged: ${i}`);return}if(this.getClaudeWorkerStatus(t)==="busy"){o("failed","\u5F53\u524D\u6709\u6D88\u606F\u6B63\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148 /grix stop \u6216\u7B49\u5F85\u5176\u5B8C\u6210\u540E\u518D\u5207\u6362\u6A21\u578B",p.workerBusy);return}this.bindingStore.setModelId(t,i),this.globalConfigStore?.set(this.name,{modelId:i}),await this.pool.removeSlot(t),await this.ensureSlotStarted(t),this.refreshClaudeWorkerStatusCard(t,s.cwd),this.refreshQuotaAfterModelSwitch(t,i),o("responded",`Model set to ${i}`);return}case _.listOptions:{const i=z(),s=this.bindingStore.get(t),r=i.map(c=>c.id).join(", "),a=`${R.fullAuto}, ${R.approval}`,d=this.resolveClaudeSessionEffort(t)??"auto";o("responded",`Modes (current: ${this.normalizeClaudeModeId(s?.modeId)}): ${a}
|
|
14
14
|
Models (current: ${s?.modelId??"default"}): ${r}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{readJSONFile as
|
|
1
|
+
import{readJSONFile as l,writeJSONFileAtomic as u}from"../util/json-file.js";import{log as r}from"../log/index.js";import{SESSION_MODE_IDS as c}from"../../adapter/claude/protocol-contract.js";import{normalizeClaudeEffort as s}from"../config/claude-direct-env.js";import{chmodSync as I,copyFileSync as x,existsSync as a,renameSync as m,rmSync as p,writeFileSync as b}from"node:fs";import{randomUUID as y}from"node:crypto";const h=c.fullAuto;function o(g){const e=String(g??"").trim().toLowerCase();return e===c.approval||e===c.fullAuto?e:h}class T{bindings=new Map;filePath;legacyFilePath;writePromise=Promise.resolve();constructor(e,t){this.filePath=e??null,this.legacyFilePath=t&&t!==e?t:null}load(){if(!this.filePath)return;const e=a(this.filePath);let t=l(this.filePath),i=!1;if(e&&!Array.isArray(t)){const d=this.quarantineUnreadablePrimary();throw new Error(`Refusing to start with unreadable session bindings: primary=${this.filePath}`+(d?` quarantined=${d}`:"")+(this.legacyFilePath?` legacy=${this.legacyFilePath}`:"")+" (legacy fallback disabled to avoid clobbering newer bindings)")}if(!e&&this.legacyFilePath){i=a(this.legacyFilePath);const d=l(this.legacyFilePath);Array.isArray(d)&&(this.writeMigratedSnapshot(d),t=d,r.info("session-binding-store",`Migrated legacy bindings from ${this.legacyFilePath} to ${this.filePath}; legacy file retained`))}if(!Array.isArray(t)){if(e||i)throw new Error(`Refusing to start with unreadable session bindings: primary=${this.filePath}${this.legacyFilePath?` legacy=${this.legacyFilePath}`:""}`);return}this.bindings.clear();for(const d of t)d.aibotSessionId&&this.bindings.set(d.aibotSessionId,{...d,modeId:o(d.modeId),effort:s(d.effort)})}set(e,t,i){const d=this.bindings.get(e),n=o(i?.modeId??d?.modeId),f=t!==void 0?t:d?.cwd;this.bindings.set(e,{aibotSessionId:e,cwd:f,acpSessionId:d?.acpSessionId,claudeSessionId:d?.claudeSessionId,codexThreadId:d?.codexThreadId,codexModelId:d?.codexModelId,codexModeId:d?.codexModeId,codewhaleThreadId:d?.codewhaleThreadId,agyConversationId:d?.agyConversationId,codexReasoningEffort:d?.codexReasoningEffort,codexServiceTier:d?.codexServiceTier,codexSandboxMode:d?.codexSandboxMode,piSessionPath:d?.piSessionPath,modeId:n,modelId:d?.modelId,effort:d?.effort,cursorModeId:d?.cursorModeId,acpModelId:d?.acpModelId,acpModeId:d?.acpModeId,updatedAt:Date.now()}),this.scheduleWrite()}setAcpSessionId(e,t){this.updateBinding(e,{acpSessionId:t})}setClaudeSessionId(e,t){this.updateBinding(e,{claudeSessionId:t})}getClaudeSessionId(e){return this.bindings.get(e)?.claudeSessionId}setCodexThreadId(e,t){this.updateBinding(e,{codexThreadId:t})}getCodexThreadId(e){return this.bindings.get(e)?.codexThreadId}setAgyConversationId(e,t){this.updateBinding(e,{agyConversationId:t})}getAgyConversationId(e){return this.bindings.get(e)?.agyConversationId}setCodexContext(e,t){this.updateBinding(e,{codexModelId:t.modelId,codexModeId:t.modeId,codexReasoningEffort:t.reasoningEffort,codexServiceTier:t.serviceTier,codexSandboxMode:t.sandboxMode})}getCodexModelId(e){return this.bindings.get(e)?.codexModelId}setCodexModelId(e,t){const i=this.bindings.get(e);if(!i)return;const d=String(t??"").trim();!d||i.codexModelId===d||(this.bindings.set(e,{...i,codexModelId:d,updatedAt:Date.now()}),this.scheduleWrite())}setCodexModeId(e,t){const i=this.bindings.get(e);if(!i)return;const d=String(t??"").trim();!d||i.codexModeId===d||(this.bindings.set(e,{...i,codexModeId:d,updatedAt:Date.now()}),this.scheduleWrite())}setCodexServiceTier(e,t){const i=this.bindings.get(e);if(!i)return;const d=String(t??"").trim();!d||i.codexServiceTier===d||(this.bindings.set(e,{...i,codexServiceTier:d,updatedAt:Date.now()}),this.scheduleWrite())}getCodexModeId(e){return this.bindings.get(e)?.codexModeId}getCodexReasoningEffort(e){return this.bindings.get(e)?.codexReasoningEffort}getCodexServiceTier(e){return this.bindings.get(e)?.codexServiceTier}getCodexSandboxMode(e){return this.bindings.get(e)?.codexSandboxMode}setPiSessionPath(e,t){this.updateBinding(e,{piSessionPath:t})}getPiSessionPath(e){return this.bindings.get(e)?.piSessionPath}setCodeWhaleThreadId(e,t){this.updateBinding(e,{codewhaleThreadId:t})}getCodeWhaleThreadId(e){return this.bindings.get(e)?.codewhaleThreadId}setModeId(e,t){const i=this.bindings.get(e);if(!i)return;const d=o(t);i.modeId!==d&&(this.bindings.set(e,{...i,modeId:d,updatedAt:Date.now()}),this.scheduleWrite())}ensureModeId(e,t=h){const i=this.bindings.get(e);i&&(i.modeId||(this.bindings.set(e,{...i,modeId:o(t),updatedAt:Date.now()}),this.scheduleWrite()))}getAcpSessionId(e){return this.bindings.get(e)?.acpSessionId}getModeId(e){const t=this.bindings.get(e)?.modeId;return t?o(t):void 0}setModelId(e,t){const i=this.bindings.get(e);if(!i)return;const d=t.trim();i.modelId!==d&&(this.bindings.set(e,{...i,modelId:d,updatedAt:Date.now()}),this.scheduleWrite())}getModelId(e){return this.bindings.get(e)?.modelId}setEffort(e,t){const i=this.bindings.get(e);if(!i)return;const d=s(t);d&&i.effort!==d&&(this.bindings.set(e,{...i,effort:d,updatedAt:Date.now()}),this.scheduleWrite())}getEffort(e){return s(this.bindings.get(e)?.effort)}setCursorModeId(e,t){const i=this.bindings.get(e);if(!i)return;const d=String(t??"").trim().toLowerCase(),n=d===""?void 0:d;i.cursorModeId!==n&&(this.bindings.set(e,{...i,cursorModeId:n,updatedAt:Date.now()}),this.scheduleWrite())}getCursorModeId(e){return this.bindings.get(e)?.cursorModeId}setAcpModelId(e,t){const i=this.bindings.get(e);if(!i)return;const d=String(t??"").trim(),n=d===""?void 0:d;i.acpModelId!==n&&(this.bindings.set(e,{...i,acpModelId:n,updatedAt:Date.now()}),this.scheduleWrite())}getAcpModelId(e){return this.bindings.get(e)?.acpModelId}setAcpModeId(e,t){const i=this.bindings.get(e);if(!i)return;const d=String(t??"").trim(),n=d===""?void 0:d;i.acpModeId!==n&&(this.bindings.set(e,{...i,acpModeId:n,updatedAt:Date.now()}),this.scheduleWrite())}getAcpModeId(e){return this.bindings.get(e)?.acpModeId}get(e){return this.bindings.get(e)}getMostRecentlyUpdatedSessionId(e){const t=e?.requireCwd!==!1;let i,d=-1;for(const n of this.bindings.values())t&&!String(n.cwd??"").trim()||n.updatedAt>d&&(d=n.updatedAt,i=n.aibotSessionId);return i}delete(e){const t=this.bindings.get(e);t&&(r.info("session-binding-store",`delete binding session_id=${e} cwd=${t.cwd??""} codexThreadId=${t.codexThreadId??""}`),this.bindings.delete(e),this.scheduleWrite())}quarantineUnreadablePrimary(){if(!this.filePath||!a(this.filePath))return;const e=`${this.filePath}.corrupt.${Date.now()}`;try{return x(this.filePath,e),r.error("session-binding-store",`Copied unreadable primary bindings to ${e} (original retained to block legacy clobber)`),e}catch(t){r.error("session-binding-store",`Failed to copy unreadable primary ${this.filePath}: ${t instanceof Error?t.message:String(t)}`);return}}entries(){return this.bindings.entries()}async flush(){await this.writePromise}scheduleWrite(){this.filePath&&(this.writePromise=this.writePromise.then(()=>this.write()).catch(e=>{r.error("session-binding-store",`Persist failed: ${e instanceof Error?e.message:e}`)}))}async write(){if(!this.filePath)return;const e=Array.from(this.bindings.values());await u(this.filePath,e)}writeMigratedSnapshot(e){if(!this.filePath)return;const t=`${this.filePath}.${y()}.migration.tmp`;try{b(t,`${JSON.stringify(e,null,2)}
|
|
2
|
+
`,{encoding:"utf8",mode:384,flag:"wx"}),m(t,this.filePath);try{I(this.filePath,384)}catch{}}catch(i){if(a(t))try{p(t)}catch{}throw new Error(`Failed to migrate legacy session bindings from ${this.legacyFilePath} to ${this.filePath}: ${i instanceof Error?i.message:String(i)}`)}}updateBinding(e,t){const i=this.bindings.get(e),d={aibotSessionId:e,acpSessionId:Object.prototype.hasOwnProperty.call(t,"acpSessionId")?t.acpSessionId:i?.acpSessionId,claudeSessionId:Object.prototype.hasOwnProperty.call(t,"claudeSessionId")?t.claudeSessionId:i?.claudeSessionId,codexThreadId:Object.prototype.hasOwnProperty.call(t,"codexThreadId")?t.codexThreadId:i?.codexThreadId,codexModelId:Object.prototype.hasOwnProperty.call(t,"codexModelId")?t.codexModelId:i?.codexModelId,codexModeId:Object.prototype.hasOwnProperty.call(t,"codexModeId")?t.codexModeId:i?.codexModeId,codewhaleThreadId:Object.prototype.hasOwnProperty.call(t,"codewhaleThreadId")?t.codewhaleThreadId:i?.codewhaleThreadId,agyConversationId:Object.prototype.hasOwnProperty.call(t,"agyConversationId")?t.agyConversationId:i?.agyConversationId,codexReasoningEffort:Object.prototype.hasOwnProperty.call(t,"codexReasoningEffort")?t.codexReasoningEffort:i?.codexReasoningEffort,codexServiceTier:Object.prototype.hasOwnProperty.call(t,"codexServiceTier")?t.codexServiceTier:i?.codexServiceTier,codexSandboxMode:Object.prototype.hasOwnProperty.call(t,"codexSandboxMode")?t.codexSandboxMode:i?.codexSandboxMode,piSessionPath:Object.prototype.hasOwnProperty.call(t,"piSessionPath")?t.piSessionPath:i?.piSessionPath,cwd:i?.cwd,modeId:o(i?.modeId),modelId:i?.modelId,effort:Object.prototype.hasOwnProperty.call(t,"effort")?s(t.effort):s(i?.effort),cursorModeId:i?.cursorModeId,acpModelId:i?.acpModelId,acpModeId:i?.acpModeId,updatedAt:Date.now()};this.bindings.set(e,d),this.scheduleWrite()}}export{T as SessionBindingStore};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{readFileSync as f,writeFileSync as u,unlinkSync as d}from"node:fs";import{join as
|
|
1
|
+
import{readFileSync as f,writeFileSync as u,unlinkSync as d}from"node:fs";import{join as P}from"node:path";function l(e,t){return`${e}#shared:${t}`}function a(e,t){if(!e)return e;const i=e.lastIndexOf("."),n=Math.max(e.lastIndexOf("/"),e.lastIndexOf("\\"));return i>n?`${e.slice(0,i)}.shared.${t}${e.slice(i)}`:`${e}.shared.${t}`}function m(e,t){return{...e,name:l(e.name,t),aibot:{...e.aibot,sharedOwnerId:t,terminalOutboxPath:a(e.aibot.terminalOutboxPath,t),terminalCommitTokenStorePath:a(e.aibot.terminalCommitTokenStorePath,t),stopResultOutboxPath:a(e.aibot.stopResultOutboxPath,t)},eventResultsPath:a(e.eventResultsPath,t),bindingsPath:a(e.bindingsPath,t),legacyBindingsPath:a(e.legacyBindingsPath,t),activeEventStorePath:a(e.activeEventStorePath,t),logDir:e.logDir?`${e.logDir}/shared-${t}`:void 0,connectMaxRetries:e.connectMaxRetries??3}}function S(e,t,i){const n=new Set(i.map(r=>String(r).trim()).filter(r=>r.length>0)),o=[...t],s=`${e}#shared:`,h=[...n].filter(r=>!o.includes(l(e,r))),c=o.filter(r=>r.startsWith(s)).map(r=>({key:r,sharedOwnerId:r.slice(s.length)})).filter(r=>!n.has(r.sharedOwnerId));return{toAdd:h,toRemove:c}}class y{dataDir;constructor(t){this.dataDir=t}filePath(t){const i=t.replace(/[^a-zA-Z0-9_-]/g,"_");return P(this.dataDir,`shared-owners-${i}.json`)}load(t){try{const i=f(this.filePath(t),"utf-8"),n=JSON.parse(i);if(Array.isArray(n))return n.filter(o=>typeof o=="string"&&o.length>0)}catch{}return[]}save(t,i){const n=[...new Set(i.filter(o=>o.length>0))];if(n.length===0){this.delete(t);return}try{u(this.filePath(t),JSON.stringify(n),"utf-8")}catch{}}remove(t,i){const n=this.load(t).filter(o=>o!==i);n.length>0?this.save(t,n):this.delete(t)}delete(t){try{d(this.filePath(t))}catch{}}}export{y as SharedOwnersCache,m as buildSharedInstanceConfig,S as diffSharedOwners,l as sharedInstanceKey,a as suffixSharedPath};
|
package/dist/manager.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readFileSync as Z,readdirSync as ee,writeFileSync as te}from"node:fs";import{join as A}from"node:path";import{homedir as ne}from"node:os";import{AgentInstance as D}from"./bridge/bridge.js";import{buildSharedInstanceConfig as re,diffSharedOwners as ae,sharedInstanceKey as ie,SharedOwnersCache as se}from"./manager-share-config.js";import{isAgentDeletedError as O}from"./core/aibot/errors.js";import{GRIX_PATHS as y,log as p}from"./core/log/index.js";import{resolveClientVersion as oe}from"./core/util/client-version.js";import{UpgradeChecker as ce}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as le}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as de}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as pe}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as me}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as ue}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ge}from"./core/aibot/types.js";import{buildReportedSkills as fe,logDefaultSkillsCheck as he,cleanupProjectedSkills as N}from"./default-skills/index.js";import{supportsKiroCwRelay as q,supportsNonMitmRelayConfig as _e}from"./core/config/provider-env.js";import{ensureKiroRelay as H,stopKiroRelay as L}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as U}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as B}from"./core/runtime/cursor-resolve.js";import{getCliVersion as ye,resolveCliPath as we}from"./core/util/cli-probe.js";import{AgentInstaller as ve}from"./core/installer/installer.js";import{reportInstallFailure as Se}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Ae}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as ke}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as be}from"./adapter/codex/direct-home.js";const Ce=8e3;function xe(){const n=U();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:B()},{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"}]}async function $e(){return Promise.all(xe().map(async n=>{const e=await we(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await ye(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function Ie(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:B()};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=U();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function k(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 Te(n,e){return A(y.data,`session-bindings-${k(n,e)}.json`)}function Ee(n,e){return A(y.data,`active-events-${k(n,e)}.json`)}function Re(n,e){const t=k(n,"agent"),r=k(e,"unknown");return A(y.data,`terminal-outbox-${t}-${r}.json`)}function Pe(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const o=String(a??"").trim(),i=o.toLowerCase();!o||t.has(i)||(t.add(i),e.push(o))}return e.length>0?e:void 0}function je(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}},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 Me(n,e={}){const t=oe(),r=String(n.client_type??"").trim().toLowerCase(),a=Ie(r),o=String(n.ws_url??"").trim(),i="get_session_usage",m="get_rate_limits",c="get_agent_global_config",s=ge;if(!n.name?.trim())throw new Error("agent name is required");if(!o)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 l=a.adapterType,u=l==="acp",w=r==="qwen",d={...a.options??{}};if(n.codex_config_overrides!==void 0){if(l!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);ke(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),d.codexConfigOverrides=n.codex_config_overrides}if(l==="codex"&&n.provider?.wire_api!==void 0){const f=n.provider;if(f.wire_api!=="responses"||!f.base_url?.trim()||!f.api_key?.trim()||!f.model?.trim()||!f.catalog_version?.trim()||!f.catalog_sha256?.trim()||!f.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const M=be(y.data,n.name);d.codexHome=M,d.codexDirect={codexHome:M,config:{baseUrl:f.base_url.trim(),model:f.model.trim(),...f.reasoning_effort?.trim()?{reasoningEffort:f.reasoning_effort.trim()}:{},token:f.api_key.trim()},catalog:{version:f.catalog_version.trim(),sha256:f.catalog_sha256.trim(),json:f.catalog_json}}}const h=l==="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",i,m]}:null,_=l==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",i,m]}:null,g=w?{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",i],adapterHint:"qwen/base"}:null,S=l==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",i,m]}:null,v=l==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,C=l==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",m]}:null,T=l==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",i]}:null,x=l==="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,$=l==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=new Set(["hermes","reasonix","kimi","copilot"]),Q=u&&!E.has(r),K=u&&!w?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",...Q?[i]:[]]}:null,W=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",i,m]}:null,z=l==="codex"||l==="claude"||l==="opencode"||r==="gemini"?["session_control","set_model","set_mode",...l==="claude"?["set_reasoning_effort"]:[]]:void 0,G=l==="cursor"||l==="opencode"||l==="openhuman"||l==="agy"||u&&E.has(r)?[m]:[i,m];u&&d.raw_transport===void 0&&(d.raw_transport=r==="gemini");const V=`${r}/base`,I=pe(l,r),J=I==="kiro"?void 0:process.cwd(),R=fe({mode:I,projectDir:J}),P=R.filter(f=>f.source==="connector").map(f=>f.name);P.length>0&&!e.quiet&&p.info("manager",`[${n.name}] injecting connector skills: [${P.join(", ")}]`);const j=me(R,y.skills);let X=j.length>0?j:void 0;const Y=ue({skillsDir:y.skills,mode:I,home:ne()});return{name:n.name,adapterType:l,aibot:{url:o,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:a.adapterHint??g?.adapterHint??S?.adapterHint??v?.adapterHint??C?.adapterHint??x?.adapterHint??$?.adapterHint??V,capabilities:h?.capabilities??T?.capabilities??S?.capabilities??v?.capabilities??C?.capabilities??x?.capabilities??$?.capabilities??g?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:Pe(h?.localActions??T?.localActions??_?.localActions??S?.localActions??v?.localActions??C?.localActions??x?.localActions??$?.localActions??g?.localActions??W?.localActions??K?.localActions??["exec_approve","exec_reject"],z,G,["connector_rollback","connector_upgrade_push",c,"configure_gateway_provider","skill_upload","skill_enable","skill_disable","skill_refresh",...s]),skills:X,librarySkills:Y,terminalOutboxPath:Re(n.name,n.agent_id)},agent:{command:a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...n.provider?{provider:{...n.provider.provider_id?.trim()?{providerId:n.provider.provider_id.trim()}:{},...n.provider.base_url?.trim()?{baseUrl:n.provider.base_url.trim()}:{},...n.provider.quota_base_url?.trim()?{quotaBaseUrl:n.provider.quota_base_url.trim()}:{},...n.provider.api_key?.trim()?{apiKey:n.provider.api_key.trim()}:{},...n.provider.model?.trim()?{model:n.provider.model.trim()}:{},...n.provider.fast_model?.trim()?{fastModel:n.provider.fast_model.trim()}:{},...n.provider.effort?.trim()?{effort:n.provider.effort.trim()}:{}}}:{}},adapterOptions:d,acpAuthMethod:d.auth_method,acpInitialMode:d.initial_mode,acpMcpTools:d.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:Te(n.name,n.agent_id),activeEventStorePath:Ee(n.name,n.agent_id),...a.enableSessionBinding||u?{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,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:je(l,n.event_queue),logDir:y.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function De(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):Ce}function F(n){const e=[],t=[],r=[],a=ee(n).filter(o=>o.endsWith(".json")).sort();for(const o of a)try{const i=JSON.parse(Z(A(n,o),"utf-8"));if(i===null||typeof i!="object"||Array.isArray(i)||!("agents"in i)){r.push(o);continue}if(!Array.isArray(i.agents)){t.push({file:o,error:"agents is not an array"});continue}if(i.agents.length===0){t.push({file:o,error:"agents array is empty"});continue}for(const m of i.agents)e.push({entry:m,file:o})}catch(i){t.push({file:o,error:i instanceof Error?i.message:String(i)})}return{entries:e,fileErrors:t,skipped:r}}function b(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(b).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${b(e[r])}`).join(",")}}`}function Oe(n,e){return b(n)===b(e)}class pt{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=y.config;sharedOwnersCache=new se(y.data);installer=new ve;relaySecretResolver;auditProducer=Ae();async start(e){const t=e??y.config;this.configDir=t,p.info("manager",`Loading configs from ${t}`),he(),N(),this.globalConfigStore=new de(A(y.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:o}=F(t);for(const s of a)p.error("manager",`Failed to load ${s.file}: ${s.error}`);for(const s of o)p.info("manager",`Skipped ${s}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&o.length===0){p.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:l}of r)try{const u=this.toRuntimeInstanceConfig(s);i.push({config:u,entry:s,file:l}),p.info("manager",`Loaded ${u.name} (${u.adapterType??"acp"}) from ${l}`)}catch(u){const w=typeof s?.name=="string"?s.name:"<unknown>";p.error("manager",`Invalid agent config in ${l} (name=${w}): ${u}`),m++}let c=0;if(i.length>0){const s=De();p.info("manager",`Starting ${i.length} agent(s), startup wait=${s}ms`);const l=d=>{this.instances=this.instances.filter(h=>h!==d)},u=i.map(({config:d,entry:h,file:_})=>{const g=this.createConfiguredInstance(d);return this.instances.push(g),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:h,file:_}),{config:d,instance:g,startPromise:this.prepareProviderRuntime(d,h).then(()=>g.start())}}),w=await Promise.all(u.map(async d=>{const h=await new Promise(_=>{let g=!1;const S=setTimeout(()=>{g||(g=!0,_({kind:"timeout"}))},s);d.startPromise.then(()=>{g||(g=!0,clearTimeout(S),_({kind:"started"}))}).catch(v=>{g||(g=!0,clearTimeout(S),_({kind:"failed",error:v}))})});return{task:d,outcome:h}}));for(const{task:d,outcome:h}of w){if(h.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(h.kind==="failed"){if(O(h.error)){this.onAgentDeletedByPlatform(d.config.name);continue}l(d.instance),p.error("manager",`Failed to start ${d.config.name}: ${h.error}`);continue}c++,p.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{p.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config)}).catch(_=>{if(O(_)){this.onAgentDeletedByPlatform(d.config.name);return}l(d.instance),p.error("manager",`Delayed start failed: ${d.config.name}: ${_}`)})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-c);p.info("manager",`${d}/${i.length} agent(s) running now`)}c>0&&p.warn("manager",`${c} 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 ce(e,()=>this.instances.some(t=>t.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(t=>t.hasPendingOrBackgroundWork())),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new le(e,y.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshAllInstanceSkills()),await this.skillSyncer.start())}refreshAllInstanceSkills(){for(const e of this.instances)try{e.forceRefreshSkills()}catch(t){p.warn("manager",`forceRefreshSkills failed for ${e.name}: ${t}`)}for(const e of this.sharedInstances.values())try{e.forceRefreshSkills()}catch(t){p.warn("manager",`forceRefreshSkills failed for shared instance: ${t}`)}}async stop(){p.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=[],N(),p.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}=ae(e.name,this.sharedInstances.keys(),t);for(const o of r){if(this.stopping)break;const i=ie(e.name,o);try{const m=re(e,o),c=new D(m,this.globalConfigStore,this.auditProducer);this.sharedInstances.set(i,c),await c.start(),c.forceRefreshSkills(),p.info("manager",`shared instance started: ${i}`)}catch(m){this.sharedInstances.delete(i);const c=m instanceof Error?m.message:String(m);/Auth failed/i.test(c)?(this.sharedOwnersCache.remove(e.name,o),p.warn("manager",`shared instance auth rejected, removed from cache: ${i}`)):p.error("manager",`start shared instance failed ${i}: ${m}`)}}for(const{key:o}of a){const i=this.sharedInstances.get(o);i&&(this.sharedInstances.delete(o),i.stop().catch(m=>p.error("manager",`stop shared instance failed ${o}: ${m}`)),p.info("manager",`shared instance stopped: ${o}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(p.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(),p.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 D(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),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 o=this.relayStateApplier;return o?o.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(()=>this.getAgentProvider(e.name)),t}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);q(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await H(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);await a.start(),this.instances.push(a),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r}),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(),p.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(m=>m.name===e),a=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const o=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),o&&await o.catch(()=>{});const i=`${e}#shared:`;for(const[m,c]of[...this.sharedInstances.entries()])m.startsWith(i)&&(this.sharedInstances.delete(m),c.stop().catch(s=>p.error("manager",`stop shared instance failed ${m}: ${s}`)));t?.keepShareCache||this.sharedOwnersCache.delete(e),a&&await a.stop(),t?.keepShareCache||await L(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;p.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(),p.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){p.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 o=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,...o?{provider:o}:{}})}te(e,JSON.stringify({agents:t},null,4)+`
|
|
2
|
-
`,"utf-8")}catch(t){p.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
|
|
1
|
+
import{readFileSync as Z,readdirSync as ee,writeFileSync as te}from"node:fs";import{join as A}from"node:path";import{homedir as ne}from"node:os";import{AgentInstance as D}from"./bridge/bridge.js";import{buildSharedInstanceConfig as re,diffSharedOwners as ae,sharedInstanceKey as ie,SharedOwnersCache as se}from"./manager-share-config.js";import{isAgentDeletedError as O}from"./core/aibot/errors.js";import{GRIX_PATHS as _,log as p}from"./core/log/index.js";import{resolveClientVersion as oe}from"./core/util/client-version.js";import{UpgradeChecker as ce}from"./core/upgrade/upgrade-checker.js";import{SkillSyncer as le}from"./core/skill-sync/skill-syncer.js";import{AgentGlobalConfigStore as de}from"./core/persistence/agent-global-config-store.js";import{resolveSkillScanMode as pe}from"./adapter/claude/skill-scanner.js";import{buildWireSkills as me}from"./core/skill-sync/sync-state.js";import{buildLibrarySkills as ue}from"./core/skill-sync/library-skills.js";import{AUDIT_LOCAL_ACTION_TYPES as ge}from"./core/aibot/types.js";import{buildReportedSkills as fe,logDefaultSkillsCheck as he,cleanupProjectedSkills as N}from"./default-skills/index.js";import{supportsKiroCwRelay as q,supportsNonMitmRelayConfig as _e}from"./core/config/provider-env.js";import{ensureKiroRelay as H,stopKiroRelay as L}from"./core/proxy/kiro-relay.js";import{resolveCopilotCommand as U}from"./core/runtime/copilot-resolve.js";import{resolveCursorCommand as B}from"./core/runtime/cursor-resolve.js";import{getCliVersion as ye,resolveCliPath as we}from"./core/util/cli-probe.js";import{AgentInstaller as ve}from"./core/installer/installer.js";import{reportInstallFailure as Se}from"./core/observability/sentry.js";import{createDefaultAuditProducer as Ae}from"./audit/runtime/default-audit-recorder.js";import{normalizeCodexConfigOverrides as ke}from"./adapter/codex/config-overrides.js";import{resolveCodexDirectHome as be}from"./adapter/codex/direct-home.js";const Ce=8e3;function xe(){const n=U();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:B()},{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"}]}async function $e(){return Promise.all(xe().map(async n=>{const e=await we(n.command);if(!e)return{client_type:n.clientType,command:n.command,installed:!1,path:null,version:null};const t=await ye(e,n.versionArgs??["--version"]);return{client_type:n.clientType,command:n.command,installed:!0,path:e,version:t.version,error:t.error}}))}function Ie(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:B()};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=U();return{adapterType:"acp",command:e.command,args:[...e.prefixArgs,"--acp"],enableSessionBinding:!0}}case"agy":return{adapterType:"agy",command:"agy",enableSessionBinding:!0};case"hermes":return{adapterType:"acp",command:"hermes",args:["acp"],enableSessionBinding:!0};default:throw new Error(`Unsupported client_type: ${n}`)}}function k(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 Te(n){return A(_.data,`session-bindings-${k(n,"unknown")}.json`)}function Ee(n,e){return A(_.data,`session-bindings-${k(n,e)}.json`)}function Re(n,e){return A(_.data,`active-events-${k(n,e)}.json`)}function Pe(n,e){const t=k(n,"agent"),r=k(e,"unknown");return A(_.data,`terminal-outbox-${t}-${r}.json`)}function je(...n){const e=[],t=new Set;for(const r of n)for(const a of r??[]){const o=String(a??"").trim(),i=o.toLowerCase();!o||t.has(i)||(t.add(i),e.push(o))}return e.length>0?e:void 0}function Me(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}},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 De(n,e={}){const t=oe(),r=String(n.client_type??"").trim().toLowerCase(),a=Ie(r),o=String(n.ws_url??"").trim(),i="get_session_usage",m="get_rate_limits",c="get_agent_global_config",s=ge;if(!n.name?.trim())throw new Error("agent name is required");if(!o)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 l=a.adapterType,u=l==="acp",w=r==="qwen",d={...a.options??{}};if(n.codex_config_overrides!==void 0){if(l!=="codex")throw new Error(`agent ${n.name}: codex_config_overrides is only supported for client_type=codex`);ke(n.codex_config_overrides,`agent ${n.name}: codex_config_overrides`),d.codexConfigOverrides=n.codex_config_overrides}if(l==="codex"&&n.provider?.wire_api!==void 0){const f=n.provider;if(f.wire_api!=="responses"||!f.base_url?.trim()||!f.api_key?.trim()||!f.model?.trim()||!f.catalog_version?.trim()||!f.catalog_sha256?.trim()||!f.catalog_json?.trim())throw Object.assign(new Error(`agent ${n.name}: incomplete Codex direct relay config`),{code:"INVALID_CODEX_DIRECT_CONFIG"});const M=be(_.data,n.name);d.codexHome=M,d.codexDirect={codexHome:M,config:{baseUrl:f.base_url.trim(),model:f.model.trim(),...f.reasoning_effort?.trim()?{reasoningEffort:f.reasoning_effort.trim()}:{},token:f.api_key.trim()},catalog:{version:f.catalog_version.trim(),sha256:f.catalog_sha256.trim(),json:f.catalog_json}}}const h=l==="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",i,m]}:null,y=l==="claude"?{localActions:["session_control","set_mode","set_model","set_reasoning_effort","claude_interaction_reply","exec_approve","exec_reject","file_list","create_folder","thread_compact",i,m]}:null,g=w?{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",i],adapterHint:"qwen/base"}:null,S=l==="pi"?{adapterHint:"pi/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","get_context","file_list","create_folder",i,m]}:null,v=l==="openhuman"?{adapterHint:"openhuman/base",capabilities:["local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,C=l==="cursor"?{adapterHint:"cursor/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","set_mode","get_context","file_list","create_folder","thread_compact","claude_interaction_reply",m]}:null,T=l==="codewhale"?{capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder",i]}:null,x=l==="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,$=l==="agy"?{adapterHint:"agy/base",capabilities:["stream_chunk","local_action_v1"],localActions:["session_control","set_model","file_list","create_folder"]}:null,E=new Set(["hermes","reasonix","kimi","copilot"]),Q=u&&!E.has(r),K=u&&!w?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder",...Q?[i]:[]]}:null,W=r==="kiro"?{localActions:["exec_approve","exec_reject","permission_approve","permission_reject","session_control","set_model","set_mode","file_list","create_folder","thread_compact",i,m]}:null,z=l==="codex"||l==="claude"||l==="opencode"||r==="gemini"?["session_control","set_model","set_mode",...l==="claude"?["set_reasoning_effort"]:[]]:void 0,G=l==="cursor"||l==="opencode"||l==="openhuman"||l==="agy"||u&&E.has(r)?[m]:[i,m];u&&d.raw_transport===void 0&&(d.raw_transport=r==="gemini");const V=`${r}/base`,I=pe(l,r),J=I==="kiro"?void 0:process.cwd(),R=fe({mode:I,projectDir:J}),P=R.filter(f=>f.source==="connector").map(f=>f.name);P.length>0&&!e.quiet&&p.info("manager",`[${n.name}] injecting connector skills: [${P.join(", ")}]`);const j=me(R,_.skills);let X=j.length>0?j:void 0;const Y=ue({skillsDir:_.skills,mode:I,home:ne()});return{name:n.name,adapterType:l,aibot:{url:o,agentId:n.agent_id,apiKey:n.api_key,clientType:r,clientVersion:t,adapterHint:a.adapterHint??g?.adapterHint??S?.adapterHint??v?.adapterHint??C?.adapterHint??x?.adapterHint??$?.adapterHint??V,capabilities:h?.capabilities??T?.capabilities??S?.capabilities??v?.capabilities??C?.capabilities??x?.capabilities??$?.capabilities??g?.capabilities??["stream_chunk","local_action_v1","connector_upgrade"],localActions:je(h?.localActions??T?.localActions??y?.localActions??S?.localActions??v?.localActions??C?.localActions??x?.localActions??$?.localActions??g?.localActions??W?.localActions??K?.localActions??["exec_approve","exec_reject"],z,G,["connector_rollback","connector_upgrade_push",c,"configure_gateway_provider","skill_upload","skill_enable","skill_disable","skill_refresh",...s]),skills:X,librarySkills:Y,terminalOutboxPath:Pe(n.name,n.agent_id)},agent:{command:a.command,args:[...a.args??[],...n.args??[]],env:n.env,clientType:r,...n.provider?{provider:{...n.provider.provider_id?.trim()?{providerId:n.provider.provider_id.trim()}:{},...n.provider.base_url?.trim()?{baseUrl:n.provider.base_url.trim()}:{},...n.provider.quota_base_url?.trim()?{quotaBaseUrl:n.provider.quota_base_url.trim()}:{},...n.provider.api_key?.trim()?{apiKey:n.provider.api_key.trim()}:{},...n.provider.model?.trim()?{model:n.provider.model.trim()}:{},...n.provider.fast_model?.trim()?{fastModel:n.provider.fast_model.trim()}:{},...n.provider.effort?.trim()?{effort:n.provider.effort.trim()}:{}}}:{}},adapterOptions:d,acpAuthMethod:d.auth_method,acpInitialMode:d.initial_mode,acpMcpTools:d.acp_mcp_tools,promptTimeoutMs:n.prompt_timeout_ms,bindingsPath:Te(n.agent_id),legacyBindingsPath:Ee(n.name,n.agent_id),activeEventStorePath:Re(n.name,n.agent_id),...a.enableSessionBinding||u?{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,poolIdleTimeoutMs:n.pool?.idleTimeoutMs,eventQueue:Me(l,n.event_queue),logDir:_.log,providerBaseUrl:n.provider_base_url?.trim()||void 0,providerApiKey:n.provider_api_key?.trim()||void 0}}function Oe(){const n=process.env.GRIX_AGENT_STARTUP_WAIT_MS,e=Number(n);return Number.isFinite(e)&&e>=500?Math.floor(e):Ce}function F(n){const e=[],t=[],r=[],a=ee(n).filter(o=>o.endsWith(".json")).sort();for(const o of a)try{const i=JSON.parse(Z(A(n,o),"utf-8"));if(i===null||typeof i!="object"||Array.isArray(i)||!("agents"in i)){r.push(o);continue}if(!Array.isArray(i.agents)){t.push({file:o,error:"agents is not an array"});continue}if(i.agents.length===0){t.push({file:o,error:"agents array is empty"});continue}for(const m of i.agents)e.push({entry:m,file:o})}catch(i){t.push({file:o,error:i instanceof Error?i.message:String(i)})}return{entries:e,fileErrors:t,skipped:r}}function b(n){if(n===null||typeof n!="object")return JSON.stringify(n)??"null";if(Array.isArray(n))return`[${n.map(b).join(",")}]`;const e=n;return`{${Object.keys(e).sort().map(r=>`${JSON.stringify(r)}:${b(e[r])}`).join(",")}}`}function Ne(n,e){return b(n)===b(e)}class mt{instances=[];configMap=new Map;rawConfigMap=new Map;reloadChain=Promise.resolve();sharedInstances=new Map;shareSyncChains=new Map;stopping=!1;deletedAgentCleanups=new Set;upgradeChecker=null;skillSyncer=null;globalConfigStore;configDir=_.config;sharedOwnersCache=new se(_.data);installer=new ve;relaySecretResolver;auditProducer=Ae();async start(e){const t=e??_.config;this.configDir=t,p.info("manager",`Loading configs from ${t}`),he(),N(),this.globalConfigStore=new de(A(_.data,"agent-global-configs.json")),this.globalConfigStore.load();const{entries:r,fileErrors:a,skipped:o}=F(t);for(const s of a)p.error("manager",`Failed to load ${s.file}: ${s.error}`);for(const s of o)p.info("manager",`Skipped ${s}: not an agent config (no "agents" key)`);if(r.length===0&&a.length===0&&o.length===0){p.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:l}of r)try{const u=this.toRuntimeInstanceConfig(s);i.push({config:u,entry:s,file:l}),p.info("manager",`Loaded ${u.name} (${u.adapterType??"acp"}) from ${l}`)}catch(u){const w=typeof s?.name=="string"?s.name:"<unknown>";p.error("manager",`Invalid agent config in ${l} (name=${w}): ${u}`),m++}let c=0;if(i.length>0){const s=Oe();p.info("manager",`Starting ${i.length} agent(s), startup wait=${s}ms`);const l=d=>{this.instances=this.instances.filter(h=>h!==d)},u=i.map(({config:d,entry:h,file:y})=>{const g=this.createConfiguredInstance(d);return this.instances.push(g),this.configMap.set(d.name,d),this.rawConfigMap.set(d.name,{entry:h,file:y}),{config:d,instance:g,startPromise:this.prepareProviderRuntime(d,h).then(()=>g.start())}}),w=await Promise.all(u.map(async d=>{const h=await new Promise(y=>{let g=!1;const S=setTimeout(()=>{g||(g=!0,y({kind:"timeout"}))},s);d.startPromise.then(()=>{g||(g=!0,clearTimeout(S),y({kind:"started"}))}).catch(v=>{g||(g=!0,clearTimeout(S),y({kind:"failed",error:v}))})});return{task:d,outcome:h}}));for(const{task:d,outcome:h}of w){if(h.kind==="started"){this.restoreCachedSharedInstances(d.config);continue}if(h.kind==="failed"){if(O(h.error)){this.onAgentDeletedByPlatform(d.config.name);continue}l(d.instance),p.error("manager",`Failed to start ${d.config.name}: ${h.error}`);continue}c++,p.warn("manager",`Startup pending for ${d.config.name}, continue retrying in background`),d.startPromise.then(()=>{p.info("manager",`Delayed start succeeded: ${d.config.name}`),this.restoreCachedSharedInstances(d.config)}).catch(y=>{if(O(y)){this.onAgentDeletedByPlatform(d.config.name);return}l(d.instance),p.error("manager",`Delayed start failed: ${d.config.name}: ${y}`)})}if(this.instances.length>0){const d=Math.max(0,this.instances.length-c);p.info("manager",`${d}/${i.length} agent(s) running now`)}c>0&&p.warn("manager",`${c} 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 ce(e,()=>this.instances.some(t=>t.hasPendingOrBackgroundWork())||[...this.sharedInstances.values()].some(t=>t.hasPendingOrBackgroundWork())),await this.upgradeChecker.start()),this.skillSyncer||(this.skillSyncer=new le(e,_.skills),this.skillSyncer.setOnSyncSuccess(()=>this.refreshAllInstanceSkills()),await this.skillSyncer.start())}refreshAllInstanceSkills(){for(const e of this.instances)try{e.forceRefreshSkills()}catch(t){p.warn("manager",`forceRefreshSkills failed for ${e.name}: ${t}`)}for(const e of this.sharedInstances.values())try{e.forceRefreshSkills()}catch(t){p.warn("manager",`forceRefreshSkills failed for shared instance: ${t}`)}}async stop(){p.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=[],N(),p.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}=ae(e.name,this.sharedInstances.keys(),t);for(const o of r){if(this.stopping)break;const i=ie(e.name,o);try{const m=re(e,o),c=new D(m,this.globalConfigStore,this.auditProducer);this.sharedInstances.set(i,c),await c.start(),c.forceRefreshSkills(),p.info("manager",`shared instance started: ${i}`)}catch(m){this.sharedInstances.delete(i);const c=m instanceof Error?m.message:String(m);/Auth failed/i.test(c)?(this.sharedOwnersCache.remove(e.name,o),p.warn("manager",`shared instance auth rejected, removed from cache: ${i}`)):p.error("manager",`start shared instance failed ${i}: ${m}`)}}for(const{key:o}of a){const i=this.sharedInstances.get(o);i&&(this.sharedInstances.delete(o),i.stop().catch(m=>p.error("manager",`stop shared instance failed ${o}: ${m}`)),p.info("manager",`shared instance stopped: ${o}`))}}restoreCachedSharedInstances(e){const t=this.sharedOwnersCache.load(e.name);t.length!==0&&(p.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(),p.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 D(e,this.globalConfigStore,this.auditProducer);return t.setUpgradeTrigger(()=>this.upgradeChecker?.triggerCheck()),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 o=this.relayStateApplier;return o?o.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(()=>this.getAgentProvider(e.name)),t}async prepareProviderRuntime(e,t){const r=this.resolveAgentProvider(t.provider);q(t.client_type)&&r?.base_url?.trim()&&r?.api_key?.trim()&&r?.model?.trim()&&await H(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);await a.start(),this.instances.push(a),this.configMap.set(e.name,e),this.rawConfigMap.set(e.name,{entry:t,file:r}),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(),p.info("manager",`Removed agent: ${e}`),this.relayEnvSettledHandler?.()}async removeAgentInternal(e,t){const r=this.instances.findIndex(m=>m.name===e),a=r===-1?void 0:this.instances[r];r!==-1&&this.instances.splice(r,1),this.configMap.delete(e),this.rawConfigMap.delete(e);const o=this.shareSyncChains.get(e);this.shareSyncChains.delete(e),o&&await o.catch(()=>{});const i=`${e}#shared:`;for(const[m,c]of[...this.sharedInstances.entries()])m.startsWith(i)&&(this.sharedInstances.delete(m),c.stop().catch(s=>p.error("manager",`stop shared instance failed ${m}: ${s}`)));t?.keepShareCache||this.sharedOwnersCache.delete(e),a&&await a.stop(),t?.keepShareCache||await L(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;p.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(),p.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){p.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 o=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,...o?{provider:o}:{}})}te(e,JSON.stringify({agents:t},null,4)+`
|
|
2
|
+
`,"utf-8")}catch(t){p.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 De(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),p.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 o=String(a.entry.client_type??"").trim().toLowerCase();q(o)&&(t?.base_url?.trim()&&t?.api_key?.trim()&&t?.model?.trim()?await H(e,{baseUrl:t.base_url.trim(),apiKey:t.api_key.trim(),model:t.model.trim()}):await L(e));const i=t?.api_key_ref?.trim()?Object.fromEntries(Object.entries(t).filter(([c])=>c!=="api_key")):t,m={...a.entry,provider:i};if(r?.deferRestart){this.rawConfigMap.set(e,{...a,entry:m}),this.instances.find(c=>c.name===e)?.markRelayEnvStale(),this.persistAgentsConfig(),p.info("manager",`Updated provider config for agent (deferred restart): ${e}`);return}await this.replaceInstance(e,m,a.file),this.persistAgentsConfig(),p.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&&(_e(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){p.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}=F(this.configDir);for(const c of r)p.info("manager",`reload: skipped ${c}: not an agent config (no "agents" key)`);if(t.length>0){const c=t.map(s=>`${s.file}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${t.length} config file(s) failed to parse [${c}]`),{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:c}of e)try{this.toRuntimeInstanceConfig(c,{quiet:!0})}catch(s){const l=c?.name,u=typeof l=="string"&&l.trim()?l:"<unknown>";a.push({name:u,error:s instanceof Error?s.message:String(s)})}if(a.length>0){const c=a.map(s=>`${s.name}: ${s.error}`).join("; ");throw Object.assign(new Error(`reload aborted: ${a.length} agent config(s) invalid [${c}]`),{code:"RELOAD_UNSAFE"})}const o=new Map;for(const c of e){const s=String(c.entry?.name??"").trim();s&&(o.has(s)&&p.warn("manager",`reload: duplicate agent name "${s}", last one wins`),o.set(s,c))}const i={added:[],removed:[],restarted:[],unchanged:[],failed:[]},m=new Set(this.rawConfigMap.keys());for(const c of m)if(!o.has(c))try{await this.removeAgentInternal(c),i.removed.push(c)}catch(s){i.failed.push({name:c,error:s instanceof Error?s.message:String(s)})}for(const[c,s]of o){const l=this.rawConfigMap.get(c);if(l)if(Ne(l.entry,s.entry))this.rawConfigMap.set(c,s),i.unchanged.push(c);else try{await this.replaceInstance(c,s.entry,s.file),i.restarted.push(c)}catch(u){i.failed.push({name:c,error:u instanceof Error?u.message:String(u)})}else try{await this.addAgentInternal(s.entry,s.file),i.added.push(c)}catch(u){i.failed.push({name:c,error:u instanceof Error?u.message:String(u)})}}return p.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 qe(this.instances,e)}async probeOne(e,t={}){return He(this.instances,e,t)}listInstallable(){return this.installer.listInstallable()}async installAgent(e){const t=await this.installer.install(e);return Se(t),t}getInstallProgress(e){return this.installer.getProgress(e)??null}}async function qe(n,e){const t=e.concurrency??4,r=Date.now(),a=new Array(n.length);await new Promise(s=>{let l=0,u=0;const w=n.length;if(w===0){s();return}function d(g){const S=n[g];S.probe(e).then(v=>{a[g]=v,h()},v=>{a[g]={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:v?.message??String(v)}},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}},h()})}function h(){u++,l<w?d(l++):u===w&&s()}const y=Math.min(t,w);for(let g=0;g<y;g++)d(l++)});const o=a.filter(s=>s.status==="healthy").length,i=a.filter(s=>s.status==="degraded").length,m=a.filter(s=>s.status==="unavailable").length,c=await $e();return{ok:o===a.length&&a.length>0,total:a.length,healthy:o,degraded:i,unavailable:m,installed_clients:c,agents:a,probed_at:r,duration_ms:Date.now()-r}}async function He(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{mt as Manager,$e as probeInstalledClientCommands,qe as probeInstances,He as probeOneInstance};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
function a(
|
|
1
|
+
function a(t){const o=new Set([`http://127.0.0.1:${t.serverPort}`,`http://localhost:${t.serverPort}`,...t.allowedOrigins]),e=new Set([`127.0.0.1:${t.serverPort}`,`localhost:${t.serverPort}`,...t.allowedHosts]);return{validateRequest(s){const r=i(s,o);if(!r.ok)return r;const n=l(s,e);return n.ok?{ok:!0}:n}}}function i(t,o){const e=t.headers.origin;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Origin not allowed: ${e}`}:{ok:!0}}function l(t,o){const e=t.headers.host;return e?o.has(e)?{ok:!0}:{ok:!1,statusCode:403,message:`Host not allowed: ${e}`}:{ok:!1,statusCode:403,message:"Missing Host header"}}export{a as createSecurityPolicy};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grix-connector",
|
|
3
|
-
"version": "3.26.
|
|
3
|
+
"version": "3.26.2",
|
|
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",
|