grix-connector 3.34.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/adapter/acp/acp-adapter.js +13 -20
  2. package/dist/adapter/agy/agy-adapter.js +12 -7
  3. package/dist/adapter/claude/claude-adapter.js +1 -1
  4. package/dist/adapter/claude/claude-bridge-server.js +1 -1
  5. package/dist/adapter/claude/claude-tools.js +1 -1
  6. package/dist/adapter/claude/claude-worker-client.js +1 -1
  7. package/dist/adapter/claude/mcp-http-launcher.js +2 -2
  8. package/dist/adapter/claude/result-timeout.js +1 -1
  9. package/dist/adapter/codewhale/codewhale-adapter.js +3 -6
  10. package/dist/adapter/codex/codex-bridge.js +11 -9
  11. package/dist/adapter/cursor/cursor-adapter.js +10 -18
  12. package/dist/adapter/cursor/session-scanner.js +1 -1
  13. package/dist/adapter/deepseek-harness/profile-runtime.js +2 -2
  14. package/dist/adapter/opencode/opencode-adapter.js +7 -5
  15. package/dist/adapter/openhuman/openhuman-adapter.js +8 -6
  16. package/dist/adapter/pi/pi-adapter.js +8 -6
  17. package/dist/adapter/shared/identity-injector.js +2 -4
  18. package/dist/adapter/shared/inbound-message-id-injector.js +2 -2
  19. package/dist/assets/dsh-bridge/grix-dsh-bridge-4.0.0.tgz +0 -0
  20. package/dist/assets/dsh-bridge/manifest.json +6 -6
  21. package/dist/bridge/adapter-pool.js +1 -1
  22. package/dist/core/access/allowlist-store.js +1 -1
  23. package/dist/core/file-ops/list-files.js +1 -1
  24. package/dist/core/mcp/internal-api-server.js +1 -1
  25. package/dist/core/observability/sentry-event-dedup.js +1 -0
  26. package/dist/core/observability/sentry.js +1 -1
  27. package/dist/core/protocol/message-metadata.js +1 -1
  28. package/dist/core/protocol/message-reference.js +7 -2
  29. package/dist/core/protocol/payload-parser.js +3 -4
  30. package/dist/core/upgrade/npm-upgrader.js +2 -2
  31. package/dist/core/upgrade/upgrade-checker.js +1 -1
  32. package/dist/core/util/session-title.js +1 -1
  33. package/dist/grix.js +1 -1
  34. package/dist/log.js +2 -2
  35. package/dist/manager.js +1 -1
  36. package/dist/mcp/stream-http/config.js +1 -1
  37. package/dist/mcp/stream-http/connection-binding.js +1 -1
  38. package/dist/mcp/stream-http/security.js +1 -1
  39. package/dist/mcp/stream-http/tool-executor.js +1 -1
  40. package/dist/mcp/stream-http/tool-registry.js +1 -1
  41. package/dist/mcp/stream-http/tool-schemas.js +1 -1
  42. package/dist/protocol/acp-client.js +1 -1
  43. package/openclaw-plugin/index.js +4 -4
  44. package/package.json +1 -1
  45. package/scripts/upgrade-guardian.sh +2 -4
  46. package/dist/assets/dsh-bridge/grix-dsh-bridge-3.34.0.tgz +0 -0
@@ -1,10 +1,12 @@
1
- import{execFile as b}from"node:child_process";import{promisify as w}from"node:util";import{EventEmitter as I}from"node:events";import{stat as T}from"node:fs/promises";import{mkdirSync as $,writeFileSync as A,unlinkSync as k,statSync as f,readFileSync as R,existsSync as C}from"node:fs";import{join as m,resolve as M}from"node:path";import{fileURLToPath as F}from"node:url";import{tmpdir as B,homedir as g}from"node:os";import{resolveCommandPath as D,spawnCommand as N,killProcessGroup as E,hasChildProcesses as G}from"../../core/runtime/spawn.js";import{InternalApiServer as L}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as O}from"../shared/identity-injector.js";import{compactToolInputForWire as j}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as q}from"../../default-skills/index.js";import{PiTransport as U}from"./pi-transport.js";import{log as a}from"../../core/log/index.js";import{scanSkills as z}from"../claude/skill-scanner.js";import{SessionBindingStore as H}from"../../core/persistence/session-binding-store.js";import{splitTextForAibotProtocol as W}from"../../core/protocol/index.js";import{InboundMessageIdInjector as Y}from"../shared/inbound-message-id-injector.js";import{buildSimpleProbeReport as K}from"../shared/probe-util.js";import{piGrixProviderId as V,removePiGrixProvider as X,writePiGrixProvider as J}from"./pi-provider-config.js";const x=new Map;function Q(){const d=m(g(),".pi","agent","mcp.json");try{if(!C(d))return{};const e=JSON.parse(R(d,"utf8"))?.mcpServers;return!e||typeof e!="object"||Array.isArray(e)?{}:e}catch(t){return a.warn("pi-adapter",`Failed to read Pi user MCP config ${d}: ${t instanceof Error?t.message:String(t)}`),{}}}async function Z(d){const t=x.get(d);if(t!==void 0)return t;let e=!1;try{const s=w(b),i=process.platform==="win32",n=i&&/\s/.test(d)&&!d.startsWith('"')?`"${d}"`:d,{stdout:r,stderr:c}=await s(n,["--help"],{timeout:5e3,encoding:"utf-8",...i?{shell:!0}:{}});e=/--mcp-config\b/.test(`${r??""}${c??""}`)}catch{e=!1}return x.set(d,e),a.info("pi-adapter",`Pi --mcp-config \u652F\u6301\u63A2\u6D4B: ${e?"\u652F\u6301":"\u4E0D\u652F\u6301"} (${d})`),e}class tt extends I{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitDone(t){this.emit("done",t)}emitError(t){if(this.listenerCount("error")===0){a.warn("pi-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const et=12e4,st=90*1e3,y=1800*1e3;function _(d){try{return process.kill(d,0),!0}catch(t){return t.code==="EPERM"}}const it=500,nt=2e3,v=3,rt=500;function at(d){return new Promise(t=>setTimeout(t,d))}function u(d){if(d===void 0)return"undefined";if(d===null)return"null";if(typeof d=="string")return d.length>500?`${d.slice(0,500)}...`:d;try{const t=JSON.stringify(d);return t.length>500?`${t.slice(0,500)}...`:t}catch{return String(d)}}class P extends I{type="pi";config;callbacks;process=null;transport=new U;alive=!1;stopped=!1;internalApi=null;mcpConfigPath=null;identity;inboundMessageIds=new Y;piSessionPath=null;activeEventId=null;activeSessionId=null;activeAuditCapture=null;completedAuditBoundaries=new Map;isStreaming=!1;streamSeq=0;clientMsgSeq=0;activeClientMsgId=null;thinkingSeq=0;textBuffer="";emittedTextByIndex=new Map;textFlushTimer=null;activeRunError=null;runErrorEmitted=!1;idleTimer=null;lastRpcAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;composingSessionId=null;composingInterval=null;doneGuardTimer=null;bindingStore=null;aibotSessionId="";sessionReadyPromise=null;provider;agentName="";constructor(t,e){super(),this.config=t,this.callbacks=e,this.identity=new O("pi-adapter",e.getAgentProfile);const s=t.options??{};this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.agentName=String(s.agentName??"").trim(),this.provider=s.provider,this.bindingStore=s.bindingStore instanceof H?s.bindingStore:null,this.bindingStore&&this.aibotSessionId&&(this.piSessionPath=this.bindingStore.getPiSessionPath(this.aibotSessionId)??null)}async start(){await this.startInternalApi(),await this.spawnPi(),await this.ensureSessionReady(),a.info("pi-adapter",`Ready (pid=${this.process?.pid})`),await this.syncModelBinding()}async syncModelBinding(){if(this.aibotSessionId)for(let t=1;t<=v;t++)try{const[e,s]=await Promise.all([this.transport.send("get_available_models"),this.transport.send("get_state")]),i=e.data?.models;if(!i||i.length===0){if(t<v){await at(rt);continue}a.warn("pi-adapter",`syncModelBinding: empty model list after ${v} attempts, giving up`);return}const n=s.data?.model,r={available_models:i.map(c=>({id:c.id,display_name:c.name??c.id,...c.provider?{provider:c.provider}:{}}))};n?.id&&(r.model_id=n.id),n?.provider&&(r.model_provider=n.provider),this.callbacks.sendUpdateBindingCard(this.aibotSessionId,this.isStreaming?"composing":"ready",this.resolveCwd(),r),a.info("pi-adapter",`synced model binding: ${i.length} models, current=${n?.id??"unknown"} (attempt ${t})`);return}catch(e){a.warn("pi-adapter",`syncModelBinding failed (non-fatal): ${e instanceof Error?e.message:String(e)}`);return}}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.cancelDoneGuard(),this.transport.close(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.mcpConfigPath){try{k(this.mcpConfigPath)}catch{}this.mcpConfigPath=null}if(this.process){const t=this.process;try{E(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{E(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){this.sessionReadyPromise=null,await this.createNewSession();const e=this.piSessionPath||`pi-${Date.now()}`;return a.info("pi-adapter",`Session created: ${e} (path=${this.piSessionPath})`),e}async resumeSession(t,e){await this.switchSession()}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.inboundMessageIds.forgetSession(t),this.piSessionPath=null,this.sessionReadyPromise=null,this.persistPiSessionPath(void 0)}sendPrompt(t){const e=new tt(t.adapterSessionId),s=this.buildPromptTextFromRequest(t);return this.ensureSessionReady().then(()=>this.transport.send("prompt",{message:s})).then(i=>{i.success||e.emitDone({status:"failed",error:i.error})}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){try{await this.transport.send("abort")}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.buildPromptText(t);if(this.isStreaming){if(this.activeEventId&&this.activeEventId!==e){const r=this.activeEventId;a.info("pi-adapter",`steer: cancel ${r} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(r,"canceled","steered to new event"),this.activeAuditCapture?.eventId===r&&(this.snapshotAuditBoundary(r),this.emit("eventDone",r))}this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n,streamingBehavior:"steer"}).catch(r=>{a.error("pi-adapter",`steer failed: ${r}`),this.callbacks.sendEventResult(e,"failed",String(r))})}else a.info("pi-adapter",`prompt: event=${e} session=${s}`),this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.isStreaming=!0,this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n}).then(r=>{r.success||(a.error("pi-adapter",`prompt rejected: ${r.error}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",r.error),this.clearActive())}).catch(r=>{a.error("pi-adapter",`prompt error: ${r}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",String(r)),this.clearActive()})}deliverStopEvent(t,e){if(this.activeEventId===t){a.info("pi-adapter",`stop: event=${t}, releasing busy immediately`),this.transport.send("abort").catch(()=>{}),this.flushTextBuffer();const s=this.nextStreamSeq(),i=this.activeClientMsgId??void 0;this.callbacks.sendStreamChunk(t,this.activeSessionId??"","",s,!0,i),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActive()}}async handleLocalAction(t){const e=t.action_id;switch(t.action_type){case"set_model":{const s=t.params??{},i=(s.modelId??s.model_id??"").trim();if(!i)return this.callbacks.sendLocalActionResult(e,"failed",void 0,"invalid_model","model_id is required"),{handled:!0,kind:"set_model_error"};try{let n=s.provider?.trim();return n||(n=(await this.transport.send("get_available_models")).data?.models?.find(l=>l.id===i)?.provider?.trim()),n?(await this.transport.send("set_model",{provider:n,modelId:i}),this.callbacks.sendLocalActionResult(e,"ok",{outcome:"model_set",provider:n,modelId:i}),this.syncModelBinding(),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e,"failed",void 0,"model_not_found",`No provider for model: ${i}`),{handled:!0,kind:"set_model_error"})}catch(n){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"set_model_error",n instanceof Error?n.message:String(n)),{handled:!0,kind:"set_model_error"}}}case"get_context":try{const s=await this.transport.send("get_state");return this.callbacks.sendLocalActionResult(e,"ok",{state:s}),{handled:!0,kind:"get_context"}}catch(s){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"get_context_error",s instanceof Error?s.message:String(s)),{handled:!1,kind:""}}case"pi_extension_ui_response":return this.transport.sendNoWait({type:"extension_ui_response",...t.params??{}}),this.callbacks.sendLocalActionResult(e,"ok"),{handled:!0,kind:"extension_ui_response"};default:return{handled:!1,kind:""}}}setPermissionHandler(t){}async ping(t){try{return await this.transport.send("get_state"),!0}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.isStreaming,sessions:this.piSessionPath?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}clearActiveEventForShutdown(){this.stopIdleTimer(),this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const t=M(F(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?G(t,[t]):!1}async probe(t){const e=this.getStatus();return{...await K(this.config.command||"pi",{alive:e.alive,busy:e.busy,started:!!this.process},t),session:this.probeSessionRecord()}}probeSessionRecord(){const t=this.piSessionPath;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const e=f(t);return{recordPath:t,lastActivityMs:e.mtimeMs,freshMs:Date.now()-e.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"model",description:"List or set model",args:"[provider:model_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(t,e,s){try{if(!this.alive)return{status:"failed",message:"Pi process not running"};switch(t){case"model":{const i=e.trim();if(i){const c=i.indexOf(":");if(c<1)return{status:"failed",message:"Format: provider:model_id"};const l=i.slice(0,c),h=i.slice(c+1);if(!h)return{status:"failed",message:"Format: provider:model_id"};const p=await this.transport.send("set_model",{provider:l,modelId:h});return p?.success?{status:"ok",message:`Model set to ${l}:${h}`}:{status:"failed",message:`Failed to set model: ${p?.error??"unknown error"}`}}const r=(await this.transport.send("get_available_models"))?.data?.models;return r&&r.length>0?{status:"ok",message:`Available models:
1
+ import{execFile as b}from"node:child_process";import{promisify as w}from"node:util";import{EventEmitter as I}from"node:events";import{stat as $}from"node:fs/promises";import{mkdirSync as T,writeFileSync as A,unlinkSync as k,statSync as f,readFileSync as R,existsSync as C}from"node:fs";import{join as m,resolve as M}from"node:path";import{fileURLToPath as F}from"node:url";import{tmpdir as B,homedir as g}from"node:os";import{resolveCommandPath as D,spawnCommand as N,killProcessGroup as E,hasChildProcesses as G}from"../../core/runtime/spawn.js";import{InternalApiServer as L}from"../../core/mcp/internal-api-server.js";import{IdentityInjector as O}from"../shared/identity-injector.js";import{compactToolInputForWire as j}from"../shared/tool-wire-payload.js";import{syncDefaultSkillsToDir as q}from"../../default-skills/index.js";import{PiTransport as U}from"./pi-transport.js";import{log as a}from"../../core/log/index.js";import{scanSkills as z}from"../claude/skill-scanner.js";import{SessionBindingStore as H}from"../../core/persistence/session-binding-store.js";import{splitTextForAibotProtocol as W}from"../../core/protocol/index.js";import{InboundMessageIdInjector as Y}from"../shared/inbound-message-id-injector.js";import{buildSimpleProbeReport as K}from"../shared/probe-util.js";import{piGrixProviderId as V,removePiGrixProvider as X,writePiGrixProvider as J}from"./pi-provider-config.js";const y=new Map;function Q(){const d=m(g(),".pi","agent","mcp.json");try{if(!C(d))return{};const e=JSON.parse(R(d,"utf8"))?.mcpServers;return!e||typeof e!="object"||Array.isArray(e)?{}:e}catch(t){return a.warn("pi-adapter",`Failed to read Pi user MCP config ${d}: ${t instanceof Error?t.message:String(t)}`),{}}}async function Z(d){const t=y.get(d);if(t!==void 0)return t;let e=!1;try{const s=w(b),i=process.platform==="win32",n=i&&/\s/.test(d)&&!d.startsWith('"')?`"${d}"`:d,{stdout:r,stderr:c}=await s(n,["--help"],{timeout:5e3,encoding:"utf-8",...i?{shell:!0}:{}});e=/--mcp-config\b/.test(`${r??""}${c??""}`)}catch{e=!1}return y.set(d,e),a.info("pi-adapter",`Pi --mcp-config \u652F\u6301\u63A2\u6D4B: ${e?"\u652F\u6301":"\u4E0D\u652F\u6301"} (${d})`),e}class tt extends I{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitDone(t){this.emit("done",t)}emitError(t){if(this.listenerCount("error")===0){a.warn("pi-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const et=12e4,st=90*1e3,x=1800*1e3;function _(d){try{return process.kill(d,0),!0}catch(t){return t.code==="EPERM"}}const it=500,nt=2e3,v=3,rt=500;function at(d){return new Promise(t=>setTimeout(t,d))}function u(d){if(d===void 0)return"undefined";if(d===null)return"null";if(typeof d=="string")return d.length>500?`${d.slice(0,500)}...`:d;try{const t=JSON.stringify(d);return t.length>500?`${t.slice(0,500)}...`:t}catch{return String(d)}}class P extends I{type="pi";config;callbacks;process=null;transport=new U;alive=!1;stopped=!1;internalApi=null;mcpConfigPath=null;identity;inboundMessageIds=new Y;piSessionPath=null;activeEventId=null;activeSessionId=null;activeAuditCapture=null;completedAuditBoundaries=new Map;isStreaming=!1;streamSeq=0;clientMsgSeq=0;activeClientMsgId=null;thinkingSeq=0;textBuffer="";emittedTextByIndex=new Map;textFlushTimer=null;activeRunError=null;runErrorEmitted=!1;idleTimer=null;lastRpcAt=0;livenessExtendStartAt;toolCallsInFlight=new Set;composingSessionId=null;composingInterval=null;doneGuardTimer=null;bindingStore=null;aibotSessionId="";sessionReadyPromise=null;provider;agentName="";constructor(t,e){super(),this.config=t,this.callbacks=e,this.identity=new O("pi-adapter",e.getAgentProfile);const s=t.options??{};this.aibotSessionId=String(s.aibotSessionId??"").trim(),this.agentName=String(s.agentName??"").trim(),this.provider=s.provider,this.bindingStore=s.bindingStore instanceof H?s.bindingStore:null,this.bindingStore&&this.aibotSessionId&&(this.piSessionPath=this.bindingStore.getPiSessionPath(this.aibotSessionId)??null)}async start(){await this.startInternalApi(),await this.spawnPi(),await this.ensureSessionReady(),a.info("pi-adapter",`Ready (pid=${this.process?.pid})`),await this.syncModelBinding()}async syncModelBinding(){if(this.aibotSessionId)for(let t=1;t<=v;t++)try{const[e,s]=await Promise.all([this.transport.send("get_available_models"),this.transport.send("get_state")]),i=e.data?.models;if(!i||i.length===0){if(t<v){await at(rt);continue}a.warn("pi-adapter",`syncModelBinding: empty model list after ${v} attempts, giving up`);return}const n=s.data?.model,r={available_models:i.map(c=>({id:c.id,display_name:c.name??c.id,...c.provider?{provider:c.provider}:{}}))};n?.id&&(r.model_id=n.id),n?.provider&&(r.model_provider=n.provider),this.callbacks.sendUpdateBindingCard(this.aibotSessionId,this.isStreaming?"composing":"ready",this.resolveCwd(),r),a.info("pi-adapter",`synced model binding: ${i.length} models, current=${n?.id??"unknown"} (attempt ${t})`);return}catch(e){a.warn("pi-adapter",`syncModelBinding failed (non-fatal): ${e instanceof Error?e.message:String(e)}`);return}}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.cancelDoneGuard(),this.transport.close(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.mcpConfigPath){try{k(this.mcpConfigPath)}catch{}this.mcpConfigPath=null}if(this.process){const t=this.process;try{E(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{E(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){this.sessionReadyPromise=null,await this.createNewSession();const e=this.piSessionPath||`pi-${Date.now()}`;return a.info("pi-adapter",`Session created: ${e} (path=${this.piSessionPath})`),e}async resumeSession(t,e){await this.switchSession()}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.inboundMessageIds.forgetSession(t),this.piSessionPath=null,this.sessionReadyPromise=null,this.persistPiSessionPath(void 0)}sendPrompt(t){const e=new tt(t.adapterSessionId),s=this.buildPromptTextFromRequest(t);return this.ensureSessionReady().then(()=>this.transport.send("prompt",{message:s})).then(i=>{i.success||e.emitDone({status:"failed",error:i.error})}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){try{await this.transport.send("abort")}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t,n=this.buildPromptText(t);if(this.isStreaming){if(this.activeEventId&&this.activeEventId!==e){const r=this.activeEventId;a.info("pi-adapter",`steer: cancel ${r} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(r,"canceled","steered to new event"),this.activeAuditCapture?.eventId===r&&(this.snapshotAuditBoundary(r),this.emit("eventDone",r))}this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n,streamingBehavior:"steer"}).catch(r=>{a.error("pi-adapter",`steer failed: ${r}`),this.callbacks.sendEventResult(e,"failed",String(r))})}else a.info("pi-adapter",`prompt: event=${e} session=${s}`),this.activeEventId=e,this.activeSessionId=s,this.beginAuditCapture(t),this.isStreaming=!0,this.resetRunStreamState(),this.startComposing(s,e),this.lastRpcAt=Date.now(),this.livenessExtendStartAt=void 0,this.resetIdleTimer(),this.transport.send("prompt",{message:n}).then(r=>{r.success||(a.error("pi-adapter",`prompt rejected: ${r.error}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",r.error),this.clearActive())}).catch(r=>{a.error("pi-adapter",`prompt error: ${r}`),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(e,"failed",String(r)),this.clearActive()})}deliverStopEvent(t,e){if(this.activeEventId===t){a.info("pi-adapter",`stop: event=${t}, releasing busy immediately`),this.transport.send("abort").catch(()=>{}),this.flushTextBuffer();const s=this.nextStreamSeq(),i=this.activeClientMsgId??void 0;this.callbacks.sendStreamChunk(t,this.activeSessionId??"","",s,!0,i),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.callbacks.sendEventResult(t,"canceled","stopped by user"),this.clearActive()}}async handleLocalAction(t){const e=t.action_id;switch(t.action_type){case"set_model":{const s=t.params??{},i=(s.modelId??s.model_id??"").trim();if(!i)return this.callbacks.sendLocalActionResult(e,"failed",void 0,"invalid_model","model_id is required"),{handled:!0,kind:"set_model_error"};try{let n=s.provider?.trim();return n||(n=(await this.transport.send("get_available_models")).data?.models?.find(l=>l.id===i)?.provider?.trim()),n?(await this.transport.send("set_model",{provider:n,modelId:i}),this.callbacks.sendLocalActionResult(e,"ok",{outcome:"model_set",provider:n,modelId:i}),this.syncModelBinding(),{handled:!0,kind:"set_model"}):(this.callbacks.sendLocalActionResult(e,"failed",void 0,"model_not_found",`No provider for model: ${i}`),{handled:!0,kind:"set_model_error"})}catch(n){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"set_model_error",n instanceof Error?n.message:String(n)),{handled:!0,kind:"set_model_error"}}}case"get_context":try{const s=await this.transport.send("get_state");return this.callbacks.sendLocalActionResult(e,"ok",{state:s}),{handled:!0,kind:"get_context"}}catch(s){return this.callbacks.sendLocalActionResult(e,"failed",void 0,"get_context_error",s instanceof Error?s.message:String(s)),{handled:!1,kind:""}}case"pi_extension_ui_response":return this.transport.sendNoWait({type:"extension_ui_response",...t.params??{}}),this.callbacks.sendLocalActionResult(e,"ok"),{handled:!0,kind:"extension_ui_response"};default:return{handled:!1,kind:""}}}setPermissionHandler(t){}async ping(t){try{return await this.transport.send("get_state"),!0}catch{return!1}}getStatus(){return{alive:this.alive,busy:this.isStreaming,sessions:this.piSessionPath?1:0}}getActiveEventIds(){return this.activeEventId?[this.activeEventId]:[]}takeAuditBoundary(t){const e=this.completedAuditBoundaries.get(t);return e&&this.completedAuditBoundaries.delete(t),e}clearActiveEventForShutdown(){this.stopIdleTimer(),this.activeEventId&&this.snapshotAuditBoundary(this.activeEventId),this.activeEventId=null}getMcpConfig(){if(!this.internalApi)return null;const t=M(F(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:"grix-connector-tools",command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?G(t,[t]):!1}async probe(t){const e=this.getStatus();return{...await K(this.config.command||"pi",{alive:e.alive,busy:e.busy,started:!!this.process},t),session:this.probeSessionRecord()}}probeSessionRecord(){const t=this.piSessionPath;if(!t)return{recordPath:null,lastActivityMs:null,freshMs:null};try{const e=f(t);return{recordPath:t,lastActivityMs:e.mtimeMs,freshMs:Date.now()-e.mtimeMs}}catch{return{recordPath:t,lastActivityMs:null,freshMs:null}}}getSupportedCommands(){return[{name:"model",description:"List or set model",args:"[provider:model_id]"},{name:"interrupt",description:"Interrupt current run"},{name:"status",description:"Show session status"},{name:"skills",description:"List available skills"}]}async execCommand(t,e,s){try{if(!this.alive)return{status:"failed",message:"Pi process not running"};switch(t){case"model":{const i=e.trim();if(i){const c=i.indexOf(":");if(c<1)return{status:"failed",message:"Format: provider:model_id"};const l=i.slice(0,c),h=i.slice(c+1);if(!h)return{status:"failed",message:"Format: provider:model_id"};const p=await this.transport.send("set_model",{provider:l,modelId:h});return p?.success?{status:"ok",message:`Model set to ${l}:${h}`}:{status:"failed",message:`Failed to set model: ${p?.error??"unknown error"}`}}const r=(await this.transport.send("get_available_models"))?.data?.models;return r&&r.length>0?{status:"ok",message:`Available models:
2
2
  ${r.map(l=>` ${l.provider??"unknown"}:${l.id} (${l.name??l.id})`).join(`
3
3
  `)}`,data:r}:{status:"ok",message:"No models available",data:[]}}case"interrupt":return this.isStreaming?(await this.transport.send("abort"),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"canceled","interrupted"),this.clearActive(),{status:"ok",message:"Run interrupted"}):{status:"failed",message:"No active run to interrupt"};case"status":{const i=this.getStatus();return{status:"ok",message:`Alive: ${i.alive}, Busy: ${i.busy}, Session: ${this.piSessionPath??"none"}`,data:{alive:i.alive,busy:i.busy,sessions:i.sessions,sessionPath:this.piSessionPath}}}case"skills":{const i=z({mode:"pi"}),n=i.map(r=>`- ${r.name}${r.trigger?` (${r.trigger})`:""} [${r.source}]: ${r.description}`);return{status:"ok",message:n.length>0?n.join(`
4
- `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${t}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}async startInternalApi(){try{this.internalApi=new L,this.internalApi.setInvokeHandler(async(r,c,l,h)=>this.callbacks.agentInvoke(r,c,h)),await this.internalApi.start(0),a.info("pi-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=Q(),s=m(B(),"grix-pi-mcp");$(s,{recursive:!0}),this.mcpConfigPath=m(s,`mcp-${process.pid}-${Date.now()}.json`),A(this.mcpConfigPath,JSON.stringify({mcpServers:{...e,[t.name]:{command:t.command,args:t.args,directTools:!0,lifecycle:"eager"}}}),"utf8"),a.info("pi-adapter",`MCP config written to ${this.mcpConfigPath}`);const i=m(g(),".pi","agent","skills"),n=q(i);n.length>0&&a.info("pi-adapter",`Synced connector skills to ${i}: [${n.join(", ")}]`)}catch(t){a.warn("pi-adapter",`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnPi(){const t=this.config.command||"pi",e=D(t,typeof process.env.PATH=="string"?process.env.PATH:void 0),s=this.config.args??[],n=s.some(o=>o.startsWith("--mode"))?[...s]:["--mode","rpc",...s],r=String(this.config.env?.PI_CODING_AGENT_DIR??"").trim()||m(g(),".pi","agent"),c=V(this.agentName);this.provider?await J(r,c,this.provider)?(n.includes("--provider")||n.push("--provider",c),!n.includes("--model")&&this.provider.model?.trim()&&n.push("--model",this.provider.model.trim())):a.warn("pi-adapter","provider config incomplete (baseUrl/model required); Grix provider not applied"):await X(r,c).then(o=>{o&&a.info("pi-adapter",`Removed stale Grix provider "${c}" from models.json`)}).catch(o=>{a.warn("pi-adapter",`Failed to clean up Grix provider config: ${o instanceof Error?o.message:String(o)}`)}),this.mcpConfigPath&&!n.some(o=>o==="--mcp-config")&&(await Z(e)?n.push("--mcp-config",this.mcpConfigPath):a.warn("pi-adapter","\u5F53\u524D Pi \u4E0D\u652F\u6301 --mcp-config\uFF08\u672A\u68C0\u6D4B\u5230 pi-mcp-adapter \u6269\u5C55\uFF09\uFF0C\u8DF3\u8FC7 MCP \u6CE8\u5165\u4EE5\u907F\u514D\u542F\u52A8\u5931\u8D25"));const l={...process.env,...this.config.env},h=this.provider?.apiKey?.trim();h&&!l.GRIX_PROVIDER_API_KEY?.trim()&&(l.GRIX_PROVIDER_API_KEY=h);const p=this.resolveCwd();a.info("pi-adapter",`Spawning: ${e} ${n.join(" ")}`);try{if(!(await T(p)).isDirectory())throw new Error(`Bound path is not a directory: ${p}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${p}. Please rebind with /grix open <valid-directory>.`):o}try{this.process=N(e,n,{env:l,cwd:p}).process}catch(o){throw a.error("pi-adapter",`PI spawn threw: ${o}`),this.alive=!1,o}this.process.on("error",o=>{a.error("pi-adapter",`Spawn error: ${o.message}`),this.alive=!1,this.transport.close(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed",`Spawn error: ${o.message}`),this.clearActive()),this.stopped||this.emit("exit",1)}),this.process.on("exit",o=>{a.info("pi-adapter",`PI process exited (code=${o})`),this.alive=!1,this.transport.close(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`PI process exited (code=${o})`),this.isStreaming=!1,this.clearActive(),this.stopped||this.emit("exit",o??1)}),this.process.stderr?.on("data",o=>{const S=o.toString().trim();S&&a.info("pi-adapter",`[pi stderr] ${S}`)}),this.transport.on("event",o=>this.handlePiEvent(o)),this.transport.bind(this.process.stdin,this.process.stdout),this.alive=!0}handlePiEvent(t){if(this.stopped)return;if(this.lastRpcAt=Date.now(),t.type==="tool_execution_start"){const s=t;s.toolCallId&&this.toolCallsInFlight.add(s.toolCallId)}else if(t.type==="tool_execution_end"){const s=t;s.toolCallId&&this.toolCallsInFlight.delete(s.toolCallId)}switch(this.resetIdleTimer(),t.type){case"message_update":{const s=t.assistantMessageEvent;if(!s)break;const i=s.type;if(i==="text_delta"){const n=s.delta;if(n){const r=typeof s.contentIndex=="number"?s.contentIndex:0;this.rememberEmittedText(r,n),this.appendText(n)}}else if(i==="text_end"){const n=s.content,r=typeof s.contentIndex=="number"?s.contentIndex:0;n&&this.appendMissingText(r,n)}else if(i==="thinking_delta"){const n=s.delta;n&&this.activeEventId&&this.activeSessionId&&(this.thinkingSeq++,this.callbacks.sendThinking(this.activeEventId,this.activeSessionId,n))}else if(i==="done"||i==="error"){if(this.flushTextBuffer(),i==="error"){const n=this.piMessageError(s.error,s.reason);a.warn("pi-adapter",`terminal message_update error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${u(s.error)} reason=${u(s.reason)}`),this.recordRunError(n)}this.scheduleDoneGuard()}break}case"message_end":{const s=t.message;s?.role==="assistant"&&s.stopReason==="error"&&(a.warn("pi-adapter",`terminal message_end error event=${this.activeEventId} sessionId=${this.activeSessionId} errorMessage=${u(s.errorMessage)} message=${u(s)}`),this.recordRunError(this.piMessageError(s,s.errorMessage)),this.scheduleDoneGuard());break}case"tool_execution_start":{if(this.cancelDoneGuard(),this.flushTextBuffer(),!this.activeEventId||!this.activeSessionId)break;const s=t;if(s.toolName){const i=j(s.args);this.callbacks.sendToolUse(this.activeEventId,this.activeSessionId,s.toolName,i)}break}case"tool_execution_end":{if(!this.activeEventId||!this.activeSessionId)break;const s=t;s.isError&&this.callbacks.sendToolResult(this.activeEventId,this.activeSessionId,s.toolName,"(failed)");break}case"agent_end":{if(a.info("pi-adapter",`agent_end event=${this.activeEventId} sessionId=${this.activeSessionId}`),this.cancelDoneGuard(),t.willRetry===!0&&this.activeEventId){a.info("pi-adapter",`agent_end willRetry=true \u2014 holding event=${this.activeEventId} for auto-retry`),this.activeRunError=null,this.emittedTextByIndex.clear(),this.resetIdleTimer();break}this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent();break}case"agent_start":{this.cancelDoneGuard(),this.activeEventId&&(a.info("pi-adapter",`agent_start event=${this.activeEventId}`),this.isStreaming=!0);break}default:break}}appendText(t){if(this.textBuffer+=t,this.textBuffer.length>=nt){this.flushTextBuffer();return}this.scheduleTextFlush()}scheduleTextFlush(){this.textFlushTimer||(this.textFlushTimer=setTimeout(()=>{this.textFlushTimer=null,this.flushTextBuffer()},it))}flushTextBuffer(){this.stopTextFlush(),!(!this.textBuffer||!this.activeEventId||!this.activeSessionId)&&(this.sendTextChunks(this.textBuffer),this.textBuffer="")}sendTextChunks(t){if(!(!this.activeEventId||!this.activeSessionId))for(const e of W(t))this.callbacks.sendStreamChunk(this.activeEventId,this.activeSessionId,e,this.nextStreamSeq(),!1,this.activeClientMsgId??void 0)}stopTextFlush(){this.textFlushTimer&&(clearTimeout(this.textFlushTimer),this.textFlushTimer=null)}buildPromptText(t){let e=t.content||"";if(t.context_messages_json)try{const i=JSON.parse(t.context_messages_json);Array.isArray(i)&&i.length>0&&(e=i.map(r=>`[context] ${r.sender_id??"unknown"}: ${r.content}`).join(`
5
- `)+`
4
+ `):"No skills found",data:i}}default:return{status:"unsupported",message:`Unknown command: ${t}`}}}catch(i){return{status:"failed",message:i instanceof Error?i.message:String(i)}}}resolveCwd(){if(this.bindingStore&&this.aibotSessionId){const e=this.bindingStore.get(this.aibotSessionId);if(e?.cwd)return e.cwd}const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}async startInternalApi(){try{this.internalApi=new L,this.internalApi.setInvokeHandler(async(r,c,l,h)=>this.callbacks.agentInvoke(r,c,h)),await this.internalApi.start(0),a.info("pi-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=Q(),s=m(B(),"grix-pi-mcp");T(s,{recursive:!0}),this.mcpConfigPath=m(s,`mcp-${process.pid}-${Date.now()}.json`),A(this.mcpConfigPath,JSON.stringify({mcpServers:{...e,[t.name]:{command:t.command,args:t.args,directTools:!0,lifecycle:"eager"}}}),"utf8"),a.info("pi-adapter",`MCP config written to ${this.mcpConfigPath}`);const i=m(g(),".pi","agent","skills"),n=q(i);n.length>0&&a.info("pi-adapter",`Synced connector skills to ${i}: [${n.join(", ")}]`)}catch(t){a.warn("pi-adapter",`Failed to start MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnPi(){const t=this.config.command||"pi",e=D(t,typeof process.env.PATH=="string"?process.env.PATH:void 0),s=this.config.args??[],n=s.some(o=>o.startsWith("--mode"))?[...s]:["--mode","rpc",...s],r=String(this.config.env?.PI_CODING_AGENT_DIR??"").trim()||m(g(),".pi","agent"),c=V(this.agentName);this.provider?await J(r,c,this.provider)?(n.includes("--provider")||n.push("--provider",c),!n.includes("--model")&&this.provider.model?.trim()&&n.push("--model",this.provider.model.trim())):a.warn("pi-adapter","provider config incomplete (baseUrl/model required); Grix provider not applied"):await X(r,c).then(o=>{o&&a.info("pi-adapter",`Removed stale Grix provider "${c}" from models.json`)}).catch(o=>{a.warn("pi-adapter",`Failed to clean up Grix provider config: ${o instanceof Error?o.message:String(o)}`)}),this.mcpConfigPath&&!n.some(o=>o==="--mcp-config")&&(await Z(e)?n.push("--mcp-config",this.mcpConfigPath):a.warn("pi-adapter","\u5F53\u524D Pi \u4E0D\u652F\u6301 --mcp-config\uFF08\u672A\u68C0\u6D4B\u5230 pi-mcp-adapter \u6269\u5C55\uFF09\uFF0C\u8DF3\u8FC7 MCP \u6CE8\u5165\u4EE5\u907F\u514D\u542F\u52A8\u5931\u8D25"));const l={...process.env,...this.config.env},h=this.provider?.apiKey?.trim();h&&!l.GRIX_PROVIDER_API_KEY?.trim()&&(l.GRIX_PROVIDER_API_KEY=h);const p=this.resolveCwd();a.info("pi-adapter",`Spawning: ${e} ${n.join(" ")}`);try{if(!(await $(p)).isDirectory())throw new Error(`Bound path is not a directory: ${p}`)}catch(o){throw String(o?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${p}. Please rebind with /grix open <valid-directory>.`):o}try{this.process=N(e,n,{env:l,cwd:p}).process}catch(o){throw a.error("pi-adapter",`PI spawn threw: ${o}`),this.alive=!1,o}this.process.on("error",o=>{a.error("pi-adapter",`Spawn error: ${o.message}`),this.alive=!1,this.transport.close(),this.activeEventId&&(this.callbacks.sendEventResult(this.activeEventId,"failed",`Spawn error: ${o.message}`),this.clearActive()),this.stopped||this.emit("exit",1)}),this.process.on("exit",o=>{a.info("pi-adapter",`PI process exited (code=${o})`),this.alive=!1,this.transport.close(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeEventId&&this.callbacks.sendEventResult(this.activeEventId,"failed",`PI process exited (code=${o})`),this.isStreaming=!1,this.clearActive(),this.stopped||this.emit("exit",o??1)}),this.process.stderr?.on("data",o=>{const S=o.toString().trim();S&&a.info("pi-adapter",`[pi stderr] ${S}`)}),this.transport.on("event",o=>this.handlePiEvent(o)),this.transport.bind(this.process.stdin,this.process.stdout),this.alive=!0}handlePiEvent(t){if(this.stopped)return;if(this.lastRpcAt=Date.now(),t.type==="tool_execution_start"){const s=t;s.toolCallId&&this.toolCallsInFlight.add(s.toolCallId)}else if(t.type==="tool_execution_end"){const s=t;s.toolCallId&&this.toolCallsInFlight.delete(s.toolCallId)}switch(this.resetIdleTimer(),t.type){case"message_update":{const s=t.assistantMessageEvent;if(!s)break;const i=s.type;if(i==="text_delta"){const n=s.delta;if(n){const r=typeof s.contentIndex=="number"?s.contentIndex:0;this.rememberEmittedText(r,n),this.appendText(n)}}else if(i==="text_end"){const n=s.content,r=typeof s.contentIndex=="number"?s.contentIndex:0;n&&this.appendMissingText(r,n)}else if(i==="thinking_delta"){const n=s.delta;n&&this.activeEventId&&this.activeSessionId&&(this.thinkingSeq++,this.callbacks.sendThinking(this.activeEventId,this.activeSessionId,n))}else if(i==="done"||i==="error"){if(this.flushTextBuffer(),i==="error"){const n=this.piMessageError(s.error,s.reason);a.warn("pi-adapter",`terminal message_update error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${u(s.error)} reason=${u(s.reason)}`),this.recordRunError(n)}this.scheduleDoneGuard()}break}case"message_end":{const s=t.message;s?.role==="assistant"&&s.stopReason==="error"&&(a.warn("pi-adapter",`terminal message_end error event=${this.activeEventId} sessionId=${this.activeSessionId} errorMessage=${u(s.errorMessage)} message=${u(s)}`),this.recordRunError(this.piMessageError(s,s.errorMessage)),this.scheduleDoneGuard());break}case"tool_execution_start":{if(this.cancelDoneGuard(),this.flushTextBuffer(),!this.activeEventId||!this.activeSessionId)break;const s=t;if(s.toolName){const i=j(s.args);this.callbacks.sendToolUse(this.activeEventId,this.activeSessionId,s.toolName,i)}break}case"tool_execution_end":{if(!this.activeEventId||!this.activeSessionId)break;const s=t;s.isError&&this.callbacks.sendToolResult(this.activeEventId,this.activeSessionId,s.toolName,"(failed)");break}case"agent_end":{if(a.info("pi-adapter",`agent_end event=${this.activeEventId} sessionId=${this.activeSessionId}`),this.cancelDoneGuard(),t.willRetry===!0&&this.activeEventId){a.info("pi-adapter",`agent_end willRetry=true \u2014 holding event=${this.activeEventId} for auto-retry`),this.activeRunError=null,this.emittedTextByIndex.clear(),this.resetIdleTimer();break}this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent();break}case"agent_start":{this.cancelDoneGuard(),this.activeEventId&&(a.info("pi-adapter",`agent_start event=${this.activeEventId}`),this.isStreaming=!0);break}default:break}}appendText(t){if(this.textBuffer+=t,this.textBuffer.length>=nt){this.flushTextBuffer();return}this.scheduleTextFlush()}scheduleTextFlush(){this.textFlushTimer||(this.textFlushTimer=setTimeout(()=>{this.textFlushTimer=null,this.flushTextBuffer()},it))}flushTextBuffer(){this.stopTextFlush(),!(!this.textBuffer||!this.activeEventId||!this.activeSessionId)&&(this.sendTextChunks(this.textBuffer),this.textBuffer="")}sendTextChunks(t){if(!(!this.activeEventId||!this.activeSessionId))for(const e of W(t))this.callbacks.sendStreamChunk(this.activeEventId,this.activeSessionId,e,this.nextStreamSeq(),!1,this.activeClientMsgId??void 0)}stopTextFlush(){this.textFlushTimer&&(clearTimeout(this.textFlushTimer),this.textFlushTimer=null)}buildPromptText(t){let e=t.content||"";if(t.context_messages_json)try{const i=JSON.parse(t.context_messages_json);if(Array.isArray(i)&&i.length>0){const n=i.map(r=>`[context] ${r.sender_id??"unknown"}: ${r.content}`).join(`
5
+ `);e=`${e}
6
6
 
7
- `+e)}catch{}const s=this.identity.injectOnce(t.session_id,e);return this.inboundMessageIds.inject(t.session_id,s,t.msg_id)}buildPromptTextFromRequest(t){let e=t.text;return t.contextMessages&&t.contextMessages.length>0&&(e=t.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
8
- `)+`
7
+ [Conversation context]
8
+ ${n}`}}catch{}const s=this.identity.injectOnce(t.session_id,e);return this.inboundMessageIds.inject(t.session_id,s,t.msg_id)}buildPromptTextFromRequest(t){let e=t.text;if(t.contextMessages&&t.contextMessages.length>0){const s=t.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
9
+ `);e=`${e}
9
10
 
10
- `+e),this.identity.injectOnce(t.adapterSessionId,e)}clearActive(){const t=this.activeEventId;t&&this.snapshotAuditBoundary(t),this.activeEventId=null,this.activeSessionId=null,this.activeClientMsgId=null,this.activeRunError=null,this.textBuffer="",this.emittedTextByIndex.clear(),this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}beginAuditCapture(t){if(this.activeAuditCapture?.eventId===t.event_id||(this.activeAuditCapture=null,t.audit?.enabled!==!0))return;const e=this.piSessionPath??void 0;let s;if(e)try{s=f(e).size}catch{s=0}this.activeAuditCapture={eventId:t.event_id,...e?{transcriptPath:e}:{},...s===void 0?{}:{transcriptStartOffset:s}}}snapshotAuditBoundary(t){const e=this.activeAuditCapture;if(!e||e.eventId!==t)return;this.activeAuditCapture=null;const s={adapterType:"pi"};if(e.transcriptPath){let i=e.transcriptStartOffset??0;try{i=f(e.transcriptPath).size}catch{}s.transcriptPath=e.transcriptPath,s.transcriptStartOffset=e.transcriptStartOffset??0,s.transcriptEndOffset=i,s.providerSessionId=this.piSessionPath??e.transcriptPath}this.completedAuditBoundaries.set(t,s)}resetRunStreamState(){this.streamSeq=0,this.thinkingSeq=0,this.textBuffer="",this.activeRunError=null,this.runErrorEmitted=!1,this.emittedTextByIndex.clear(),this.activeClientMsgId=`pi_${++this.clientMsgSeq}_${Date.now()}`}nextStreamSeq(){return this.streamSeq++,this.streamSeq}completeActiveEvent(){if(!this.activeEventId){this.clearActive();return}const t=this.activeEventId,e=this.activeSessionId??"",s=this.activeClientMsgId??void 0,i=this.activeRunError??void 0;i&&this.emitPendingRunError(),this.clearActive(),this.finalizeEvent(t,e,s,i)}finalizeEvent(t,e,s,i){const n=this.nextStreamSeq(),r=i?"failed":"responded";a.info("pi-adapter",`finalize event=${t} sessionId=${e} status=${r} clientMsgId=${s??""} terminalError=${i?u(i):""}`),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(t,e,s).then(()=>{this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} (reliable) event=${t}`)}).catch(c=>{a.error("pi-adapter",`finalStreamChunk ACK failed event=${t}: ${c}`),this.callbacks.sendEventResult(t,"failed",`final output delivery failed: ${c instanceof Error?c.message:String(c)}`),a.info("pi-adapter",`event failed (final delivery) event=${t}`)}):(this.callbacks.sendStreamChunk(t,e,"",n,!0,s),this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} event=${t}`))}recordRunError(t){if(this.activeRunError||!this.activeEventId||!this.activeSessionId)return;const e=t.trim()||"Pi request failed";this.activeRunError=e,a.warn("pi-adapter",`recorded terminal error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${u(e)}`),this.flushTextBuffer()}emitPendingRunError(){this.runErrorEmitted||!this.activeRunError||!this.activeEventId||!this.activeSessionId||(this.runErrorEmitted=!0,this.callbacks.sendRunError(this.activeEventId,this.activeSessionId,this.activeRunError,this.nextStreamSeq(),this.activeClientMsgId??void 0))}piMessageError(t,e){if(t&&typeof t=="object"){const s=t.errorMessage;if(typeof s=="string"&&s.trim())return s.trim()}return typeof t=="string"&&t.trim()?t.trim():typeof e=="string"&&e.trim()&&e.trim()!=="error"?e.trim():"Pi request failed"}rememberEmittedText(t,e){this.emittedTextByIndex.set(t,(this.emittedTextByIndex.get(t)??"")+e)}appendMissingText(t,e){const s=this.emittedTextByIndex.get(t)??"";if(e!==s){if(e.startsWith(s)){const i=e.slice(s.length);i&&(this.rememberEmittedText(t,i),this.appendText(i));return}if(!s){this.rememberEmittedText(t,e),this.appendText(e);return}a.info("pi-adapter",`text_end content mismatch at index=${t}, keeping streamed deltas`)}}startComposing(t,e){this.stopComposing(),this.composingSessionId=t,this.callbacks.sendSessionActivitySet(t,"composing",!0,{ref_event_id:e,ttl_ms:12e4}),this.composingInterval=setInterval(()=>{this.composingSessionId&&this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!0,{ttl_ms:12e4})},3e4)}stopComposing(){this.composingInterval&&(clearInterval(this.composingInterval),this.composingInterval=null),this.composingSessionId&&(this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!1),this.composingSessionId=null)}static DONE_GUARD_MS=5e3;scheduleDoneGuard(){this.cancelDoneGuard(),!(!this.activeEventId||!this.activeSessionId)&&(this.doneGuardTimer=setTimeout(()=>{this.doneGuardTimer=null,!(!this.activeEventId||!this.isStreaming)&&(a.info("pi-adapter",`done guard triggered \u2014 no agent_end received, ending event=${this.activeEventId}`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent())},P.DONE_GUARD_MS))}cancelDoneGuard(){this.doneGuardTimer&&(clearTimeout(this.doneGuardTimer),this.doneGuardTimer=null)}shouldExtendByLiveness(){if(!this.isStreaming||!this.activeEventId)return!1;const t=this.process?.pid;if(!this.alive||!t||!_(t))return!1;const e=Date.now(),s=this.lastRpcAt>0?e-this.lastRpcAt:Number.POSITIVE_INFINITY;if(s<st)return this.livenessExtendStartAt=e,a.info("pi-adapter",`Liveness check: fresh RPC for ${this.activeEventId} (rpcAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const n=this.livenessExtendStartAt??e;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=n),e-n>y?(a.warn("pi-adapter",`Liveness extension budget exhausted for ${this.activeEventId} (no progress for ${Math.round((e-n)/6e4)}min), allowing idle fail`),!1):(a.info("pi-adapter",`Liveness check: turn in progress for ${this.activeEventId} (toolsInFlight=${this.toolCallsInFlight.size}, rpcAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.isStreaming)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.isStreaming)return;if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const t=this.process?.pid,s=!!(this.alive&&t&&_(t))?`liveness budget exhausted after ${y/6e4}min`:"process not alive";a.error("pi-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.activeEventId&&(this.emitPendingRunError(),this.callbacks.sendEventResult(this.activeEventId,"failed",`idle timeout: ${s}`)),this.clearActive(),this.emit("exit",-1)},et))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}async ensureSessionReady(){return this.sessionReadyPromise?this.sessionReadyPromise:(this.sessionReadyPromise=this.restoreOrCreateSession().finally(()=>{this.sessionReadyPromise=null}),this.sessionReadyPromise)}async restoreOrCreateSession(){try{await this.switchSession();return}catch(t){this.piSessionPath&&a.error("pi-adapter",`switch_session failed, creating new session: ${t}`)}await this.createNewSession()}async switchSession(){if(!this.piSessionPath)throw new Error("no PI session path");const t=await this.transport.send("switch_session",{sessionPath:this.piSessionPath});if(!t.success)throw this.piSessionPath=null,this.persistPiSessionPath(void 0),new Error(`switch_session failed: ${t.error}`)}async createNewSession(){const t=await this.transport.send("new_session");if(!t.success)throw new Error(`new_session failed: ${t.error}`);let e=null;if(e=t.data?.sessionFile??null,!e)try{const i=await this.transport.send("get_state");i.success&&(e=i.data?.sessionFile??null)}catch{}this.piSessionPath=e,this.persistPiSessionPath(this.piSessionPath??void 0)}persistPiSessionPath(t){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setPiSessionPath(this.aibotSessionId,t)}}export{P as PiAdapter};
11
+ [Conversation context]
12
+ ${s}`}return this.identity.injectOnce(t.adapterSessionId,e)}clearActive(){const t=this.activeEventId;t&&this.snapshotAuditBoundary(t),this.activeEventId=null,this.activeSessionId=null,this.activeClientMsgId=null,this.activeRunError=null,this.textBuffer="",this.emittedTextByIndex.clear(),this.toolCallsInFlight.clear(),this.livenessExtendStartAt=void 0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}beginAuditCapture(t){if(this.activeAuditCapture?.eventId===t.event_id||(this.activeAuditCapture=null,t.audit?.enabled!==!0))return;const e=this.piSessionPath??void 0;let s;if(e)try{s=f(e).size}catch{s=0}this.activeAuditCapture={eventId:t.event_id,...e?{transcriptPath:e}:{},...s===void 0?{}:{transcriptStartOffset:s}}}snapshotAuditBoundary(t){const e=this.activeAuditCapture;if(!e||e.eventId!==t)return;this.activeAuditCapture=null;const s={adapterType:"pi"};if(e.transcriptPath){let i=e.transcriptStartOffset??0;try{i=f(e.transcriptPath).size}catch{}s.transcriptPath=e.transcriptPath,s.transcriptStartOffset=e.transcriptStartOffset??0,s.transcriptEndOffset=i,s.providerSessionId=this.piSessionPath??e.transcriptPath}this.completedAuditBoundaries.set(t,s)}resetRunStreamState(){this.streamSeq=0,this.thinkingSeq=0,this.textBuffer="",this.activeRunError=null,this.runErrorEmitted=!1,this.emittedTextByIndex.clear(),this.activeClientMsgId=`pi_${++this.clientMsgSeq}_${Date.now()}`}nextStreamSeq(){return this.streamSeq++,this.streamSeq}completeActiveEvent(){if(!this.activeEventId){this.clearActive();return}const t=this.activeEventId,e=this.activeSessionId??"",s=this.activeClientMsgId??void 0,i=this.activeRunError??void 0;i&&this.emitPendingRunError(),this.clearActive(),this.finalizeEvent(t,e,s,i)}finalizeEvent(t,e,s,i){const n=this.nextStreamSeq(),r=i?"failed":"responded";a.info("pi-adapter",`finalize event=${t} sessionId=${e} status=${r} clientMsgId=${s??""} terminalError=${i?u(i):""}`),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(t,e,s).then(()=>{this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} (reliable) event=${t}`)}).catch(c=>{a.error("pi-adapter",`finalStreamChunk ACK failed event=${t}: ${c}`),this.callbacks.sendEventResult(t,"failed",`final output delivery failed: ${c instanceof Error?c.message:String(c)}`),a.info("pi-adapter",`event failed (final delivery) event=${t}`)}):(this.callbacks.sendStreamChunk(t,e,"",n,!0,s),this.callbacks.sendEventResult(t,r,i),a.info("pi-adapter",`event ${r} event=${t}`))}recordRunError(t){if(this.activeRunError||!this.activeEventId||!this.activeSessionId)return;const e=t.trim()||"Pi request failed";this.activeRunError=e,a.warn("pi-adapter",`recorded terminal error event=${this.activeEventId} sessionId=${this.activeSessionId} error=${u(e)}`),this.flushTextBuffer()}emitPendingRunError(){this.runErrorEmitted||!this.activeRunError||!this.activeEventId||!this.activeSessionId||(this.runErrorEmitted=!0,this.callbacks.sendRunError(this.activeEventId,this.activeSessionId,this.activeRunError,this.nextStreamSeq(),this.activeClientMsgId??void 0))}piMessageError(t,e){if(t&&typeof t=="object"){const s=t.errorMessage;if(typeof s=="string"&&s.trim())return s.trim()}return typeof t=="string"&&t.trim()?t.trim():typeof e=="string"&&e.trim()&&e.trim()!=="error"?e.trim():"Pi request failed"}rememberEmittedText(t,e){this.emittedTextByIndex.set(t,(this.emittedTextByIndex.get(t)??"")+e)}appendMissingText(t,e){const s=this.emittedTextByIndex.get(t)??"";if(e!==s){if(e.startsWith(s)){const i=e.slice(s.length);i&&(this.rememberEmittedText(t,i),this.appendText(i));return}if(!s){this.rememberEmittedText(t,e),this.appendText(e);return}a.info("pi-adapter",`text_end content mismatch at index=${t}, keeping streamed deltas`)}}startComposing(t,e){this.stopComposing(),this.composingSessionId=t,this.callbacks.sendSessionActivitySet(t,"composing",!0,{ref_event_id:e,ttl_ms:12e4}),this.composingInterval=setInterval(()=>{this.composingSessionId&&this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!0,{ttl_ms:12e4})},3e4)}stopComposing(){this.composingInterval&&(clearInterval(this.composingInterval),this.composingInterval=null),this.composingSessionId&&(this.callbacks.sendSessionActivitySet(this.composingSessionId,"composing",!1),this.composingSessionId=null)}static DONE_GUARD_MS=5e3;scheduleDoneGuard(){this.cancelDoneGuard(),!(!this.activeEventId||!this.activeSessionId)&&(this.doneGuardTimer=setTimeout(()=>{this.doneGuardTimer=null,!(!this.activeEventId||!this.isStreaming)&&(a.info("pi-adapter",`done guard triggered \u2014 no agent_end received, ending event=${this.activeEventId}`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.stopIdleTimer(),this.completeActiveEvent())},P.DONE_GUARD_MS))}cancelDoneGuard(){this.doneGuardTimer&&(clearTimeout(this.doneGuardTimer),this.doneGuardTimer=null)}shouldExtendByLiveness(){if(!this.isStreaming||!this.activeEventId)return!1;const t=this.process?.pid;if(!this.alive||!t||!_(t))return!1;const e=Date.now(),s=this.lastRpcAt>0?e-this.lastRpcAt:Number.POSITIVE_INFINITY;if(s<st)return this.livenessExtendStartAt=e,a.info("pi-adapter",`Liveness check: fresh RPC for ${this.activeEventId} (rpcAge=${s}ms, toolsInFlight=${this.toolCallsInFlight.size}), extending`),!0;const n=this.livenessExtendStartAt??e;return this.livenessExtendStartAt===void 0&&(this.livenessExtendStartAt=n),e-n>x?(a.warn("pi-adapter",`Liveness extension budget exhausted for ${this.activeEventId} (no progress for ${Math.round((e-n)/6e4)}min), allowing idle fail`),!1):(a.info("pi-adapter",`Liveness check: turn in progress for ${this.activeEventId} (toolsInFlight=${this.toolCallsInFlight.size}, rpcAge=${Number.isFinite(s)?`${s}ms`:"n/a"}), extending`),!0)}resetIdleTimer(){this.stopIdleTimer(),!(this.stopped||!this.isStreaming)&&(this.idleTimer=setTimeout(()=>{if(this.stopped||!this.isStreaming)return;if(this.shouldExtendByLiveness()){this.resetIdleTimer();return}const t=this.process?.pid,s=!!(this.alive&&t&&_(t))?`liveness budget exhausted after ${x/6e4}min`:"process not alive";a.error("pi-adapter",`Idle timeout (${s}, toolsInFlight=${this.toolCallsInFlight.size}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.isStreaming=!1,this.stopComposing(),this.activeEventId&&(this.emitPendingRunError(),this.callbacks.sendEventResult(this.activeEventId,"failed",`idle timeout: ${s}`)),this.clearActive(),this.emit("exit",-1)},et))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}async ensureSessionReady(){return this.sessionReadyPromise?this.sessionReadyPromise:(this.sessionReadyPromise=this.restoreOrCreateSession().finally(()=>{this.sessionReadyPromise=null}),this.sessionReadyPromise)}async restoreOrCreateSession(){try{await this.switchSession();return}catch(t){this.piSessionPath&&a.error("pi-adapter",`switch_session failed, creating new session: ${t}`)}await this.createNewSession()}async switchSession(){if(!this.piSessionPath)throw new Error("no PI session path");const t=await this.transport.send("switch_session",{sessionPath:this.piSessionPath});if(!t.success)throw this.piSessionPath=null,this.persistPiSessionPath(void 0),new Error(`switch_session failed: ${t.error}`)}async createNewSession(){const t=await this.transport.send("new_session");if(!t.success)throw new Error(`new_session failed: ${t.error}`);let e=null;if(e=t.data?.sessionFile??null,!e)try{const i=await this.transport.send("get_state");i.success&&(e=i.data?.sessionFile??null)}catch{}this.piSessionPath=e,this.persistPiSessionPath(this.piSessionPath??void 0)}persistPiSessionPath(t){!this.bindingStore||!this.aibotSessionId||this.bindingStore.setPiSessionPath(this.aibotSessionId,t)}}export{P as PiAdapter};
@@ -1,4 +1,2 @@
1
- import{log as r}from"../../core/log/index.js";class d{logTag;getProfile;injectedSessions=new Set;constructor(e,i){this.logTag=e,this.getProfile=i}injectOnce(e,i){if(!e||this.injectedSessions.has(e))return i;const o=this.getProfile?.(),n=o?.agentName?.trim()??"",s=o?.introduction?.trim()??"";if(!n&&!s)return i;this.injectedSessions.add(e);const t=["[system-identity]"];return n&&t.push(`Your name is "${n}".`),s&&t.push(`Your self-introduction: ${s}`),t.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),t.push("[/system-identity]"),r.info(this.logTag,`Injected agent identity for session=${e} name="${n}"`),`${t.join(`
2
- `)}
3
-
4
- ${i}`}onProfileChanged(){this.injectedSessions.size>0&&(r.info(this.logTag,`agent profile changed; clearing ${this.injectedSessions.size} identity-injected session(s)`),this.injectedSessions.clear())}forgetSession(e){this.injectedSessions.delete(e)}}export{d as IdentityInjector};
1
+ import{log as r}from"../../core/log/index.js";import{insertBeforeTrailingMessageReferences as c}from"../../core/protocol/message-reference.js";class d{logTag;getProfile;injectedSessions=new Set;constructor(e,i){this.logTag=e,this.getProfile=i}injectOnce(e,i){if(!e||this.injectedSessions.has(e))return i;const o=this.getProfile?.(),n=o?.agentName?.trim()??"",s=o?.introduction?.trim()??"";if(!n&&!s)return i;this.injectedSessions.add(e);const t=["[system-identity]"];return n&&t.push(`Your name is "${n}".`),s&&t.push(`Your self-introduction: ${s}`),t.push("Treat this as an out-of-band instruction; do not echo or repeat it in your replies."),t.push("[/system-identity]"),r.info(this.logTag,`Injected agent identity for session=${e} name="${n}"`),c(i,t.join(`
2
+ `))}onProfileChanged(){this.injectedSessions.size>0&&(r.info(this.logTag,`agent profile changed; clearing ${this.injectedSessions.size} identity-injected session(s)`),this.injectedSessions.clear())}forgetSession(e){this.injectedSessions.delete(e)}}export{d as IdentityInjector};
@@ -1,3 +1,3 @@
1
- import{formatInboundMessageReferenceText as a}from"../../core/protocol/message-reference.js";const d=["[grix protocol] An incoming message may end with a [[message_id:<id>]] marker.","The id is the platform message id of the message you are currently handling.","The marker is metadata, not user prose. Do not echo or repeat it in replies; use the id only when a tool requires a message id."].join(" ");class g{instructedSessions=new Set;prepare(e,t,n){const s=String(n??"").trim();if(!s)return{text:t,instructionPending:!1};const r=String(e??"").trim();let i=t;const o=!r||!this.instructedSessions.has(r);return o&&(i=`${d}
1
+ import{formatInboundMessageReferenceText as a}from"../../core/protocol/message-reference.js";const d=["[grix protocol] An incoming message may end with a [[message_id:<id>]] marker.","The id is the platform message id of the message you are currently handling.","The marker is metadata, not user prose. Do not echo or repeat it in replies; use the id only when a tool requires a message id."].join(" ");class g{instructedSessions=new Set;prepare(e,t,n){const s=String(n??"").trim();if(!s)return{text:t,instructionPending:!1};const r=String(e??"").trim();let i=t;const o=!r||!this.instructedSessions.has(r);return o&&(i=`${i}
2
2
 
3
- ${i}`),{text:a(i,{messageId:s}),instructionPending:o}}commitInstruction(e){const t=String(e??"").trim();t&&this.instructedSessions.add(t)}inject(e,t,n){const s=this.prepare(e,t,n);return s.instructionPending&&this.commitInstruction(e),s.text}forgetSession(e){this.instructedSessions.delete(String(e??"").trim())}reset(){this.instructedSessions.clear()}}export{d as INBOUND_MESSAGE_ID_PROTOCOL_INSTRUCTION,g as InboundMessageIdInjector};
3
+ ${d}`),{text:a(i,{messageId:s}),instructionPending:o}}commitInstruction(e){const t=String(e??"").trim();t&&this.instructedSessions.add(t)}inject(e,t,n){const s=this.prepare(e,t,n);return s.instructionPending&&this.commitInstruction(e),s.text}forgetSession(e){this.instructedSessions.delete(String(e??"").trim())}reset(){this.instructedSessions.clear()}}export{d as INBOUND_MESSAGE_ID_PROTOCOL_INSTRUCTION,g as InboundMessageIdInjector};
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "bridgeVersion": "3.34.0",
4
- "tarball": "grix-dsh-bridge-3.34.0.tgz",
5
- "size": 22012,
6
- "unpackedSize": 87635,
7
- "shasum": "0f39593cb6ec0efd7a821d38ba0884e3198e5394",
8
- "integrity": "sha512-mc33+bePy9LPdBqXaGqUcqhGILY5GLTpax2EmkMzYQAcCRmGv4c+GV1vQWZzY5RpJrv8qktn3ysKu2+lz2MtTg=="
3
+ "bridgeVersion": "4.0.0",
4
+ "tarball": "grix-dsh-bridge-4.0.0.tgz",
5
+ "size": 22006,
6
+ "unpackedSize": 87631,
7
+ "shasum": "0339a6cd18a101c3c0ee152d72f0f6d08d2f392a",
8
+ "integrity": "sha512-06N1ubbuEOTP0PdxsAENmu0IRVP3YBoCDJUaSNHMOi1FEgvRrF3Q/Kq8j/WH/gJTPfEvQMuDiSNZNxIV/lbzeg=="
9
9
  }
@@ -1 +1 @@
1
- import{log as i}from"../core/log/index.js";import{RespawnManager as c}from"./respawn-manager.js";import{EventQueue as v}from"./event-queue.js";const l=6e4;async function d(u){if(!u.startPromise)return;let e;try{await Promise.race([u.startPromise,new Promise((t,s)=>{e=setTimeout(()=>s(new Error(`adapter start timeout (${l}ms) session=${u.sessionId}`)),l)})])}finally{e&&clearTimeout(e)}}const h=12e4,f=5;class g extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const m=6e4;class w{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;startupEvents=new Map;drainedStartupEventIds=new Set;sweepTimer=null;pendingRemovals=new Map;constructor(e,t,s){this.config=e,this.factory=t,this.sendAck=s}setEventStateHandler(e){this.onEventState=e}setQueueComposingHandler(e){this.onQueueComposing=e}setInternalErrorHandler(e){this.onInternalError=e}setEventStartedHandler(e){this.onEventStarted=e}setEventDoneHandler(e){this.onEventDone=e}setSessionActivityHandler(e){this.onSessionActivity=e}async deliverInboundEvent(e){if(this.stopped)throw new Error("adapter pool stopped");const t=e.session_id,s=this.slots.get(t);if(s){if(s.stopping)return await this.pendingRemovals.get(t)?.catch(()=>{}),this.deliverInboundEvent(e);if(s.startPromise){await this.submitAfterStartup(s,e);return}s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&i.info("adapter-pool",`Holding event ${e.event_id} in paused queue for restarting slot ${t}`),this.submitToSlotQueue(s,e);return}if(this.slots.size>=this.config.maxPoolSize&&!this.evictDeadSlot()&&!this.evictIdleSlot())throw new g(this.config.maxPoolSize);const n=this.createSlot(t);await this.submitAfterStartup(n,e)}async submitAfterStartup(e,t){this.sendAck(t.event_id,t.session_id),this.startupEvents.set(t.event_id,t);try{await e.startPromise}catch(s){throw this.startupEvents.delete(t.event_id),s}this.drainedStartupEventIds.delete(t.event_id)||this.stopped||(this.startupEvents.delete(t.event_id),e.lastActivityAt=Date.now(),this.submitToSlotQueue(e,t))}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&i.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);if(!s)return i.error("adapter-pool",`Cannot complete event ${e}: session slot not found (${t})`),!1;const n=s.eventQueue.complete(e);return n||i.error("adapter-pool",`Cannot complete event ${e}: not running in session ${t}`),n}cancelEvent(e,t){if(this.cancelStartupEvent(e,t))return!0;const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}cancelStartupEvent(e,t){const s=this.startupEvents.get(e);if(s&&!(t&&s.session_id!==t))return this.startupEvents.delete(e),this.drainedStartupEventIds.add(e),s}removeQueuedEvent(e,t){const s=this.slots.get(e);return s?s.eventQueue.removeQueued(t):!1}clearQueue(e){const t=this.slots.get(e);return t?t.eventQueue.clear(e):[]}reorderQueue(e,t){const s=this.slots.get(e);return s?s.eventQueue.reorder(e,t):[]}holdEvent(e,t,s,n,r){const a=this.slots.get(e);return a?s?a.eventQueue.hold(t,n,r):a.eventQueue.release(t):"not_found"}editQueuedEvent(e,t,s){const n=this.slots.get(e);return n?n.eventQueue.editQueued(t,s):"not_found"}pauseAllQueues(e){for(const t of this.slots.values())t.eventQueue.pause(e)}drainAllQueuedEvents(){const e=[...this.startupEvents.values()];for(const t of e)this.drainedStartupEventIds.add(t.event_id);this.startupEvents.clear();for(const t of this.slots.values())e.push(...t.eventQueue.drainQueuedForSession(t.sessionId));return e}drainQueuedForSession(e){if(!this.config.eventQueue)return[];const t=this.slots.get(e);return t?t.eventQueue.drainQueuedForSession(e):[]}getQueueSnapshot(e){if(!this.config.eventQueue)return null;const t=this.slots.get(e);return t?t.eventQueue.snapshot(e):null}deliverStopEvent(e,t){if(t){const s=this.slots.get(t);i.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}i.info("adapter-pool",`[stop-trace] pool.deliverStopEvent (broadcast, no session) event=${e} slots=${this.slots.size}`);for(const s of this.slots.values())s.adapter.deliverStopEvent(e)}collectActiveEventIds(){const e=[];for(const t of this.slots.values()){const s=t.adapter.getActiveEventIds;typeof s=="function"&&e.push(...s.call(t.adapter))}return e}clearActiveEventsForShutdown(){for(const e of this.slots.values()){const t=e.adapter.clearActiveEventForShutdown;typeof t=="function"&&t.call(e.adapter)}}async deliverLocalAction(e,t){const s=String((e.params??{}).session_id??"");let n=s?this.slots.get(s):void 0;if(n?.stopping&&(await this.pendingRemovals.get(s)?.catch(()=>{}),n=this.slots.get(s)),!n&&s&&t?.autoCreateSlot&&!this.stopped&&(this.slots.size>=this.config.maxPoolSize&&(this.evictDeadSlot()||this.evictIdleSlotSync()),this.slots.size<this.config.maxPoolSize)){i.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const o=this.createSlot(s);try{await o.startPromise,n=o}catch(p){i.error("adapter-pool",`Failed to auto-create slot for ${s}: ${p}`)}}return n?.adapter?.handleLocalAction?(n.lastActivityAt=Date.now(),n.adapter.handleLocalAction(e)):s?{handled:!1,kind:""}:(await Promise.allSettled([...this.slots.values()].filter(o=>!o.stopping).map(o=>o.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(o=>o.status==="fulfilled"&&o.value.handled)?.value??{handled:!1,kind:""}}async waitForSlotRemoval(e){await this.pendingRemovals.get(e)?.catch(()=>{})}async getOrCreateSlot(e){let t=this.slots.get(e);if(t?.stopping&&(await this.pendingRemovals.get(e)?.catch(()=>{}),t=this.slots.get(e)),t)return await d(t),t;if(this.stopped)return;const s=this.createSlot(e);return await d(s),s}getSlot(e){return this.slots.get(e)}getAllSlots(){return[...this.slots.values()]}async removeSlot(e){const t=this.slots.get(e);if(!t){await this.pendingRemovals.get(e)?.catch(()=>{});return}if(t.stopping){await this.pendingRemovals.get(e)?.catch(()=>{});return}t.stopping=!0;const s=this.doRemoveSlot(t);this.pendingRemovals.set(e,s);try{await s}finally{this.pendingRemovals.get(e)===s&&this.pendingRemovals.delete(e)}}async doRemoveSlot(e){const t=e.sessionId;this.onSessionActivity?.(t,!1),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachSlotEventListenersExceptDone(e),await e.adapter.stop().catch(()=>{}),e.adapter.removeAllListeners("eventDone"),this.slots.delete(t)}async stop(){this.stopped=!0,this.drainedStartupEventIds.clear(),this.stopIdleSweep(),await Promise.allSettled([...this.pendingRemovals.values()]);const e=[...this.slots.values()];this.slots.clear(),await Promise.allSettled(e.map(async t=>{t.eventQueue.destroy(),t.respawn.stopAll(),this.detachAllSlotEventListeners(t),await t.adapter.stop().catch(()=>{})}))}startIdleSweep(){this.stopIdleSweep(),this.sweepTimer=setInterval(()=>this.sweepIdle(),m)}stopIdleSweep(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null)}sweepIdle(){const e=Date.now();for(const[t,s]of this.slots){if(s.stopping)continue;if(s.state==="stopped"&&s.respawn.exhausted){i.info("adapter-pool",`Removing dead slot for session ${t} (sweep)`),this.removeSlot(t).catch(()=>{});continue}if(s.state!=="ready")continue;if(s.eventQueue.runningCount>0||s.eventQueue.queuedCount>0){s.lastActivityAt=e;continue}const n=s.adapter.getStatus();if(n.busy||n.backgroundBusy){s.lastActivityAt=e;continue}e-s.lastActivityAt<=this.config.idleTimeoutMs||(s.adapter.hasBackgroundWork?s.adapter.hasBackgroundWork().then(r=>{r?(i.info("adapter-pool",`Deferred eviction for session ${t}: background grandchild processes detected`),s.lastActivityAt=Date.now()):this.evictAfterProbe(t,s,"probe idle")}).catch(()=>{this.evictAfterProbe(t,s,"probe failed")}):(i.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{i.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})))}}evictAfterProbe(e,t,s){if(this.slots.get(e)!==t||t.stopping)return;if(t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0){t.lastActivityAt=Date.now();return}const n=t.adapter.getStatus();if(n.busy||n.backgroundBusy){t.lastActivityAt=Date.now();return}Date.now()-t.lastActivityAt<=this.config.idleTimeoutMs||(i.info("adapter-pool",`Evicting idle slot for session ${e} (${s})`),this.removeSlot(e).catch(r=>{i.error("adapter-pool",`Failed to evict slot ${e}: ${r}`)}))}evictDeadSlot(){for(const[e,t]of this.slots)if(t.state==="stopped"&&t.respawn.exhausted)return i.info("adapter-pool",`Removing dead slot for session ${e} (exhausted respawn)`),this.removeSlot(e).catch(()=>{}),!0;return!1}evictIdleSlot(){let e=null;for(const t of this.slots.values()){if(t.stopping||t.state!=="ready"||t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0)continue;const s=t.adapter.getStatus();s.busy||s.backgroundBusy||(!e||t.lastActivityAt<e.lastActivityAt)&&(e=t)}return e?(i.info("adapter-pool",`Evicting idle slot for session ${e.sessionId} (pool full)`),this.removeSlot(e.sessionId).catch(()=>{}),!0):!1}evictIdleSlotSync(){let e=null,t="";for(const[s,n]of this.slots){if(n.stopping||n.state!=="ready"||n.eventQueue.runningCount>0||n.eventQueue.queuedCount>0)continue;const r=n.adapter.getStatus();r.busy||r.backgroundBusy||(!e||n.lastActivityAt<e.lastActivityAt)&&(e=n,t=s)}return!e||!t?!1:(i.info("adapter-pool",`Evicting idle slot for session ${t} (pool full, sync)`),this.slots.delete(t),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachAllSlotEventListeners(e),e.adapter.stop().catch(()=>{}),!0)}getStatus(){let e=0,t=0;for(const s of this.slots.values())s.state==="ready"&&(e++,s.adapter.getStatus().busy&&t++);return{total:this.slots.size,ready:e,busy:t}}hasPendingWork(){if(this.startupEvents.size>0)return!0;for(const e of this.slots.values())if(e.startPromise||e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}hasPendingOrBackgroundWork(){if(this.startupEvents.size>0)return!0;for(const e of this.slots.values()){if(e.startPromise||e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0)return!0;if(e.state==="ready"){const t=e.adapter.getStatus();if(t.busy||t.backgroundBusy)return!0}}return!1}createSlot(e){const t=this.factory(e),s=new c,n=this.createEventQueue(e),r={sessionId:e,adapter:t,respawn:s,state:"starting",lastActivityAt:Date.now(),startPromise:null,eventQueue:n,stopping:!1};this.slots.set(e,r);const a=(async()=>{try{this.wireSlotEvents(r),await t.start(),r.state="ready"}catch(o){throw this.detachAllSlotEventListeners(r),this.slots.delete(e),o}finally{r.startPromise=null}})();return r.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(r)),r}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(i.error("adapter-pool",`Slot ${e.sessionId} adapter exited (code=${t})`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on("stuck",()=>{this.stopped||this.slots.get(e.sessionId)===e&&(i.error("adapter-pool",`Slot ${e.sessionId} adapter stuck, triggering respawn`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on?.("error",t=>{if(this.stopped)return;const s=t instanceof Error?t.message:String(t);i.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){i.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){i.warn("adapter-pool",`Slot ${e.sessionId} emitted internalError but no handler registered: event=${t.eventId} msg=${t.errorMsg}`);return}this.onInternalError(t)}}),e.adapter.on("eventStarted",(t,s)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventStarted?.(t,s)}),e.adapter.on("eventDone",t=>{this.stopped||this.slots.get(e.sessionId)===e&&(this.onEventDone?.(t,e.sessionId),e.eventQueue.markAdapterDone(t))}),e.adapter.on("sessionActivity",(t,s,n)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onSessionActivity?.(t||e.sessionId,s,n)}),e.adapter.on("pauseIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.pause(t)}),e.adapter.on("resumeIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.resume(t)})}detachAllSlotEventListeners(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("eventDone"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}detachSlotEventListenersExceptDone(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}slotRespawnCtx(e){const t=this,s=e.sessionId,n=e.adapter.type==="agy";return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:r=>e.adapter.ping(r),...n?{healthCheckIntervalMs:h,healthCheckMaxFailures:f}:{},onRespawnNeeded:async()=>{e.eventQueue.pause("restart"),this.detachAllSlotEventListeners(e),await e.adapter.stop().catch(()=>{}),e.adapter=t.factory(s),e.state="starting",t.wireSlotEvents(e),await e.adapter.start(),e.state="ready",e.respawn.resetAttempts(),e.respawn.resetHealthFailures(),e.respawn.stopSlowRetry(),e.respawn.startHealthCheck(t.slotRespawnCtx(e)),e.eventQueue.resume("compaction"),e.eventQueue.resume("barrier"),e.eventQueue.resume("restart")},onCleanupAgent:()=>{"cleanup"in e.adapter&&e.adapter.cleanup()},onAbortActiveRun:r=>{}}}createEventQueue(e){const t=this.config.eventQueue??{maxConcurrent:1,maxQueued:5,queueTimeoutMs:3e5,cancelableQueued:!0,cancelableRunning:!0},s={onDeliver:n=>{const r=this.slots.get(e);r&&r.adapter.deliverInboundEvent(n)},onStateChange:(n,r,a,o)=>{this.onEventState?.(n,r,a,o)},onCancelRunning:n=>{this.deliverStopEvent(n,e)},onRejected:(n,r)=>{this.onEventState?.(n.event_id,n.session_id,"failed",{reason:r})},onComposing:(n,r,a)=>{this.onQueueComposing?.(n,r,a)}};return new v(t,s)}}export{w as AdapterPool,g as PoolFullError};
1
+ import{log as i}from"../core/log/index.js";import{RespawnManager as c}from"./respawn-manager.js";import{EventQueue as v}from"./event-queue.js";const l=6e4;async function d(u){if(!u.startPromise)return;let e;try{await Promise.race([u.startPromise,new Promise((t,s)=>{e=setTimeout(()=>s(new Error(`adapter start timeout (${l}ms) session=${u.sessionId}`)),l)})])}finally{e&&clearTimeout(e)}}const h=12e4,f=5;class g extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const m=6e4;class w{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;startupEvents=new Map;drainedStartupEventIds=new Set;sweepTimer=null;pendingRemovals=new Map;constructor(e,t,s){this.config=e,this.factory=t,this.sendAck=s}setEventStateHandler(e){this.onEventState=e}setQueueComposingHandler(e){this.onQueueComposing=e}setInternalErrorHandler(e){this.onInternalError=e}setEventStartedHandler(e){this.onEventStarted=e}setEventDoneHandler(e){this.onEventDone=e}setSessionActivityHandler(e){this.onSessionActivity=e}async deliverInboundEvent(e){if(this.stopped)throw new Error("adapter pool stopped");const t=e.session_id,s=this.slots.get(t);if(s){if(s.stopping)return await this.pendingRemovals.get(t)?.catch(()=>{}),this.deliverInboundEvent(e);if(s.startPromise){await this.submitAfterStartup(s,e);return}s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&i.info("adapter-pool",`Holding event ${e.event_id} in paused queue for restarting slot ${t}`),this.submitToSlotQueue(s,e);return}if(this.slots.size>=this.config.maxPoolSize&&!this.evictDeadSlot()&&!this.evictIdleSlot())throw new g(this.config.maxPoolSize);const n=this.createSlot(t);await this.submitAfterStartup(n,e)}async submitAfterStartup(e,t){this.sendAck(t.event_id,t.session_id),this.startupEvents.set(t.event_id,t);try{await e.startPromise}catch(s){throw this.startupEvents.delete(t.event_id),s}this.drainedStartupEventIds.delete(t.event_id)||this.stopped||(this.startupEvents.delete(t.event_id),e.lastActivityAt=Date.now(),this.submitToSlotQueue(e,t))}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&i.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);if(!s)return i.error("adapter-pool",`Cannot complete event ${e}: session slot not found (${t})`),!1;const n=s.eventQueue.complete(e);return n||i.error("adapter-pool",`Cannot complete event ${e}: not running in session ${t}`),n}cancelEvent(e,t){if(this.cancelStartupEvent(e,t))return!0;const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}cancelStartupEvent(e,t){const s=this.startupEvents.get(e);if(s&&!(t&&s.session_id!==t))return this.startupEvents.delete(e),this.drainedStartupEventIds.add(e),s}removeQueuedEvent(e,t){const s=this.slots.get(e);return s?s.eventQueue.removeQueued(t):!1}clearQueue(e){const t=this.slots.get(e);return t?t.eventQueue.clear(e):[]}reorderQueue(e,t){const s=this.slots.get(e);return s?s.eventQueue.reorder(e,t):[]}holdEvent(e,t,s,n,r){const a=this.slots.get(e);return a?s?a.eventQueue.hold(t,n,r):a.eventQueue.release(t):"not_found"}editQueuedEvent(e,t,s){const n=this.slots.get(e);return n?n.eventQueue.editQueued(t,s):"not_found"}pauseAllQueues(e){for(const t of this.slots.values())t.eventQueue.pause(e)}drainAllQueuedEvents(){const e=[...this.startupEvents.values()];for(const t of e)this.drainedStartupEventIds.add(t.event_id);this.startupEvents.clear();for(const t of this.slots.values())e.push(...t.eventQueue.drainQueuedForSession(t.sessionId));return e}drainQueuedForSession(e){if(!this.config.eventQueue)return[];const t=this.slots.get(e);return t?t.eventQueue.drainQueuedForSession(e):[]}getQueueSnapshot(e){if(!this.config.eventQueue)return null;const t=this.slots.get(e);return t?t.eventQueue.snapshot(e):null}deliverStopEvent(e,t){if(t){const s=this.slots.get(t);i.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}i.info("adapter-pool",`[stop-trace] pool.deliverStopEvent (broadcast, no session) event=${e} slots=${this.slots.size}`);for(const s of this.slots.values())s.adapter.deliverStopEvent(e)}collectActiveEventIds(){const e=[];for(const t of this.slots.values()){const s=t.adapter.getActiveEventIds;typeof s=="function"&&e.push(...s.call(t.adapter))}return e}clearActiveEventsForShutdown(){for(const e of this.slots.values()){const t=e.adapter.clearActiveEventForShutdown;typeof t=="function"&&t.call(e.adapter)}}async deliverLocalAction(e,t){const s=String((e.params??{}).session_id??"");let n=s?this.slots.get(s):void 0;if(n?.stopping&&(await this.pendingRemovals.get(s)?.catch(()=>{}),n=this.slots.get(s)),!n&&s&&t?.autoCreateSlot&&!this.stopped&&(this.slots.size>=this.config.maxPoolSize&&(this.evictDeadSlot()||this.evictIdleSlotSync()),this.slots.size<this.config.maxPoolSize)){i.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const o=this.createSlot(s);try{await o.startPromise,n=o}catch(p){i.error("adapter-pool",`Failed to auto-create slot for ${s}: ${p}`)}}return n?.adapter?.handleLocalAction?(n.lastActivityAt=Date.now(),n.adapter.handleLocalAction(e)):s?{handled:!1,kind:""}:(await Promise.allSettled([...this.slots.values()].filter(o=>!o.stopping).map(o=>o.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(o=>o.status==="fulfilled"&&o.value.handled)?.value??{handled:!1,kind:""}}async waitForSlotRemoval(e){await this.pendingRemovals.get(e)?.catch(()=>{})}async getOrCreateSlot(e){let t=this.slots.get(e);if(t?.stopping&&(await this.pendingRemovals.get(e)?.catch(()=>{}),t=this.slots.get(e)),t)return await d(t),t;if(this.stopped)return;const s=this.createSlot(e);return await d(s),s}getSlot(e){return this.slots.get(e)}getAllSlots(){return[...this.slots.values()]}async removeSlot(e){const t=this.slots.get(e);if(!t){await this.pendingRemovals.get(e)?.catch(()=>{});return}if(t.stopping){await this.pendingRemovals.get(e)?.catch(()=>{});return}t.stopping=!0;const s=this.doRemoveSlot(t);this.pendingRemovals.set(e,s);try{await s}finally{this.pendingRemovals.get(e)===s&&this.pendingRemovals.delete(e)}}async doRemoveSlot(e){const t=e.sessionId;this.onSessionActivity?.(t,!1),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachSlotEventListenersExceptDone(e),await e.adapter.stop().catch(()=>{}),e.adapter.removeAllListeners("eventDone"),this.slots.delete(t)}async stop(){this.stopped=!0,this.drainedStartupEventIds.clear(),this.stopIdleSweep(),await Promise.allSettled([...this.pendingRemovals.values()]);const e=[...this.slots.values()];this.slots.clear(),await Promise.allSettled(e.map(async t=>{t.eventQueue.destroy(),t.respawn.stopAll(),this.detachAllSlotEventListeners(t),await t.adapter.stop().catch(()=>{})}))}startIdleSweep(){this.stopIdleSweep(),this.sweepTimer=setInterval(()=>this.sweepIdle(),m)}stopIdleSweep(){this.sweepTimer&&(clearInterval(this.sweepTimer),this.sweepTimer=null)}sweepIdle(){const e=Date.now();for(const[t,s]of this.slots){if(s.stopping)continue;if(s.state==="stopped"&&s.respawn.exhausted){i.info("adapter-pool",`Removing dead slot for session ${t} (sweep)`),this.removeSlot(t).catch(()=>{});continue}if(s.state!=="ready")continue;if(s.eventQueue.runningCount>0||s.eventQueue.queuedCount>0){s.lastActivityAt=e;continue}const n=s.adapter.getStatus();if(n.busy||n.backgroundBusy){s.lastActivityAt=e;continue}e-s.lastActivityAt<=this.config.idleTimeoutMs||(s.adapter.hasBackgroundWork?s.adapter.hasBackgroundWork().then(r=>{r?(i.info("adapter-pool",`Deferred eviction for session ${t}: background grandchild processes detected`),s.lastActivityAt=Date.now()):this.evictAfterProbe(t,s,"probe idle")}).catch(()=>{this.evictAfterProbe(t,s,"probe failed")}):(i.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{i.error("adapter-pool",`Failed to evict slot ${t}: ${r}`)})))}}evictAfterProbe(e,t,s){if(this.slots.get(e)!==t||t.stopping)return;if(t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0){t.lastActivityAt=Date.now();return}const n=t.adapter.getStatus();if(n.busy||n.backgroundBusy){t.lastActivityAt=Date.now();return}Date.now()-t.lastActivityAt<=this.config.idleTimeoutMs||(i.info("adapter-pool",`Evicting idle slot for session ${e} (${s})`),this.removeSlot(e).catch(r=>{i.error("adapter-pool",`Failed to evict slot ${e}: ${r}`)}))}evictDeadSlot(){for(const[e,t]of this.slots)if(t.state==="stopped"&&t.respawn.exhausted)return i.info("adapter-pool",`Removing dead slot for session ${e} (exhausted respawn)`),this.removeSlot(e).catch(()=>{}),!0;return!1}evictIdleSlot(){let e=null;for(const t of this.slots.values()){if(t.stopping||t.state!=="ready"||t.eventQueue.runningCount>0||t.eventQueue.queuedCount>0)continue;const s=t.adapter.getStatus();s.busy||s.backgroundBusy||(!e||t.lastActivityAt<e.lastActivityAt)&&(e=t)}return e?(i.info("adapter-pool",`Evicting idle slot for session ${e.sessionId} (pool full)`),this.removeSlot(e.sessionId).catch(()=>{}),!0):!1}evictIdleSlotSync(){let e=null,t="";for(const[s,n]of this.slots){if(n.stopping||n.state!=="ready"||n.eventQueue.runningCount>0||n.eventQueue.queuedCount>0)continue;const r=n.adapter.getStatus();r.busy||r.backgroundBusy||(!e||n.lastActivityAt<e.lastActivityAt)&&(e=n,t=s)}return!e||!t?!1:(i.info("adapter-pool",`Evicting idle slot for session ${t} (pool full, sync)`),this.slots.delete(t),e.eventQueue.clear(t,"session slot removed"),e.eventQueue.destroy(),e.respawn.stopAll(),this.detachAllSlotEventListeners(e),e.adapter.stop().catch(()=>{}),!0)}getStatus(){let e=0,t=0;for(const s of this.slots.values())s.state==="ready"&&(e++,s.adapter.getStatus().busy&&t++);return{total:this.slots.size,ready:e,busy:t}}hasPendingWork(){if(this.startupEvents.size>0)return!0;for(const e of this.slots.values())if(e.startPromise||e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}hasPendingOrBackgroundWork(){if(this.startupEvents.size>0)return!0;for(const e of this.slots.values()){if(e.startPromise||e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0)return!0;if(e.state==="ready"){const t=e.adapter.getStatus();if(t.busy||t.backgroundBusy)return!0}}return!1}createSlot(e){const t=this.factory(e),s=new c,n=this.createEventQueue(e),r={sessionId:e,adapter:t,respawn:s,state:"starting",lastActivityAt:Date.now(),startPromise:null,eventQueue:n,stopping:!1};this.slots.set(e,r);const a=(async()=>{try{this.wireSlotEvents(r),await t.start(),r.state="ready"}catch(o){throw this.detachAllSlotEventListeners(r),this.slots.delete(e),o}finally{r.startPromise=null}})();return r.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(r)),r}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(i.error("adapter-pool",`Slot ${e.sessionId} adapter exited (code=${t})`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on("stuck",()=>{this.stopped||this.slots.get(e.sessionId)===e&&(i.error("adapter-pool",`Slot ${e.sessionId} adapter stuck, triggering respawn`),e.state="stopped",e.eventQueue.pause("restart"),e.respawn.scheduleRespawn(this.slotRespawnCtx(e)))}),e.adapter.on?.("error",t=>{if(this.stopped)return;const s=t instanceof Error?t.message:String(t);i.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){i.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){i.warn("adapter-pool",`Slot ${e.sessionId} emitted internalError but no handler registered: event=${t.eventId} msg=${t.errorMsg}`);return}this.onInternalError(t)}}),e.adapter.on("eventStarted",(t,s)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onEventStarted?.(t,s)}),e.adapter.on("eventDone",t=>{this.stopped||this.slots.get(e.sessionId)===e&&(this.onEventDone?.(t,e.sessionId),e.eventQueue.markAdapterDone(t))}),e.adapter.on("sessionActivity",(t,s,n)=>{this.stopped||this.slots.get(e.sessionId)===e&&this.onSessionActivity?.(t||e.sessionId,s,n)}),e.adapter.on("pauseIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.pause(t)}),e.adapter.on("resumeIntake",t=>{this.stopped||this.slots.get(e.sessionId)===e&&e.eventQueue.resume(t)})}detachAllSlotEventListeners(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("eventDone"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}detachSlotEventListenersExceptDone(e){e.adapter.removeAllListeners("exit"),e.adapter.removeAllListeners("error"),e.adapter.removeAllListeners("stuck"),e.adapter.removeAllListeners("internalError"),e.adapter.removeAllListeners("eventStarted"),e.adapter.removeAllListeners("sessionActivity"),e.adapter.removeAllListeners("pauseIntake"),e.adapter.removeAllListeners("resumeIntake")}slotRespawnCtx(e){const t=this,s=e.sessionId,n=e.adapter.type==="agy";return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:r=>e.adapter.ping(r),...n?{healthCheckIntervalMs:h,healthCheckMaxFailures:f}:{},onRespawnNeeded:async()=>{e.eventQueue.pause("restart"),this.detachAllSlotEventListeners(e),await e.adapter.stop().catch(()=>{}),e.adapter=t.factory(s),e.state="starting",t.wireSlotEvents(e),await e.adapter.start(),e.state="ready",e.respawn.resetAttempts(),e.respawn.resetHealthFailures(),e.respawn.stopSlowRetry(),e.respawn.startHealthCheck(t.slotRespawnCtx(e)),e.eventQueue.resume("compaction"),e.eventQueue.resume("barrier"),e.eventQueue.resume("restart")},onCleanupAgent:()=>{"cleanup"in e.adapter&&e.adapter.cleanup()},onAbortActiveRun:r=>{}}}createEventQueue(e){const t=this.config.eventQueue??{maxConcurrent:1,maxQueued:99,queueTimeoutMs:3e5,cancelableQueued:!0,cancelableRunning:!0},s={onDeliver:n=>{const r=this.slots.get(e);r&&r.adapter.deliverInboundEvent(n)},onStateChange:(n,r,a,o)=>{this.onEventState?.(n,r,a,o)},onCancelRunning:n=>{this.deliverStopEvent(n,e)},onRejected:(n,r)=>{this.onEventState?.(n.event_id,n.session_id,"failed",{reason:r})},onComposing:(n,r,a)=>{this.onQueueComposing?.(n,r,a)}};return new v(t,s)}}export{w as AdapterPool,g as PoolFullError};
@@ -1 +1 @@
1
- import{readJSONFile as o,writeJSONFileAtomic as e}from"../util/json-file.js";function n(t){const r=o(t);return r&&typeof r=="object"&&"owners"in r&&Array.isArray(r.owners)?r.owners.filter(i=>typeof i=="string"&&i.trim().length>0):[]}async function s(t,r){await e(t,{owners:r})}export{n as readAllowlist,s as writeAllowlist};
1
+ import{readJSONFile as o,writeJSONFileAtomic as i}from"../util/json-file.js";function n(t){const r=o(t);return r&&typeof r=="object"&&"owners"in r&&Array.isArray(r.owners)?r.owners.filter(e=>typeof e=="string"&&e.trim().length>0):[]}async function s(t,r){await i(t,{owners:r})}export{n as readAllowlist,s as writeAllowlist};
@@ -1 +1 @@
1
- import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const t of c){if(!p&&t.name.startsWith("."))continue;const i=l(a,t.name),e={id:i,name:t.name,is_directory:t.isDirectory()};try{if(t.isDirectory()){const o=await m(i);e.modified_at=o.mtime.toISOString()}else{const o=await m(i);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(t.name)}}catch{}s.push(e)}return s.sort((t,i)=>t.is_directory!==i.is_directory?t.is_directory?-1:1:t.name.localeCompare(i.name)),s}export{f as listFiles,n as resolveMimeType};
1
+ import{readdir as r,stat as m}from"node:fs/promises";import{join as l,extname as d}from"node:path";const x={pdf:"application/pdf",doc:"application/msword",docx:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",xls:"application/vnd.ms-excel",xlsx:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",ppt:"application/vnd.ms-powerpoint",pptx:"application/vnd.openxmlformats-officedocument.presentationml.presentation",txt:"text/plain",md:"text/markdown",csv:"text/csv",json:"application/json",xml:"application/xml",yaml:"text/yaml",yml:"text/yaml",html:"text/html",css:"text/css",js:"text/javascript",ts:"text/typescript",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",gz:"application/gzip",jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",mp4:"video/mp4",mov:"video/quicktime",avi:"video/x-msvideo",mkv:"video/x-matroska",webm:"video/webm",mp3:"audio/mpeg",wav:"audio/wav",flac:"audio/flac",aac:"audio/aac"};function n(a){const p=d(a).slice(1).toLowerCase();return x[p]}async function f(a,p=!1){const c=await r(a,{withFileTypes:!0}),s=[];for(const i of c){if(!p&&i.name.startsWith("."))continue;const t=l(a,i.name),e={id:t,name:i.name,is_directory:i.isDirectory()};try{if(i.isDirectory()){const o=await m(t);e.modified_at=o.mtime.toISOString()}else{const o=await m(t);e.size=o.size,e.modified_at=o.mtime.toISOString(),e.mime_type=n(i.name)}}catch{}s.push(e)}return s.sort((i,t)=>i.is_directory!==t.is_directory?i.is_directory?-1:1:i.name.localeCompare(t.name)),s}export{f as listFiles,n as resolveMimeType};
@@ -1 +1 @@
1
- import y from"node:http";import{randomBytes as O}from"node:crypto";import{log as c}from"../log/index.js";import{PHASE1_INVOKE_ACTIONS as S}from"./tools.js";import{WebSocketServer as w,WebSocket as k}from"ws";const f=1024*1024,v=32*1024*1024,H=50,b=1e3,g=75e3,N=256,J=new Set([...S,"event_tool_call"]);class E{server=null;port=0;authToken;invokeHandler=null;statusLineHandler=null;otelHandler=null;activeInvokes=0;wss=null;bridgeWs=null;mcpBridgeUpHandler=null;downstreamOutbox=[];constructor(){this.authToken=O(32).toString("hex")}setInvokeHandler(e){this.invokeHandler=e}setStatusLineHandler(e){this.statusLineHandler=e}setOtelHandler(e){this.otelHandler=e}async start(e=0){return new Promise((t,r)=>{this.server=y.createServer((o,i)=>this.handleRequest(o,i)),this.wss=new w({noServer:!0}),this.server.on("upgrade",(o,i,n)=>{const s=new URL(o.url??"/",`http://127.0.0.1:${this.port}`);if(s.pathname!=="/mcp-bridge"||s.searchParams.get("token")!==this.authToken){i.destroy();return}this.wss.handleUpgrade(o,i,n,d=>{if(this.bridgeWs=d,c.info("internal-api","mcp bridge connected"),this.downstreamOutbox.length>0){const a=this.downstreamOutbox;this.downstreamOutbox=[];for(const l of a)d.send(l);c.info("internal-api",`mcp bridge flushed ${a.length} buffered downstream frame(s)`)}d.on("message",a=>{let l;try{l=JSON.parse(a.toString("utf8"))}catch{return}this.mcpBridgeUpHandler?.(l)}),d.on("close",()=>{this.bridgeWs===d&&(this.bridgeWs=null),c.info("internal-api","mcp bridge disconnected")}),d.on("error",()=>{})})}),this.server.on("error",r),this.server.listen(e,"127.0.0.1",()=>{const o=this.server.address();typeof o=="object"&&o&&(this.port=o.port),this.server.removeListener("error",r),t(this.port)})})}get url(){return`http://127.0.0.1:${this.port}?token=${this.authToken}`}get baseUrl(){return`http://127.0.0.1:${this.port}`}get mcpBridgeWsUrl(){return`ws://127.0.0.1:${this.port}/mcp-bridge?token=${this.authToken}`}setMcpBridgeUpHandler(e){this.mcpBridgeUpHandler=e}sendMcpFrameToBridge(e){const t=JSON.stringify(e);if(this.bridgeWs&&this.bridgeWs.readyState===k.OPEN){this.bridgeWs.send(t);return}this.downstreamOutbox.length>=N&&(this.downstreamOutbox.shift(),c.warn("internal-api","mcp downstream outbox full, dropping oldest frame")),this.downstreamOutbox.push(t)}get token(){return this.authToken}async stop(){if(this.server)return new Promise(e=>{this.server.close(()=>{this.server=null,e()})})}checkAuth(e){return e.headers.authorization===`Bearer ${this.authToken}`||new URL(e.url??"/",`http://127.0.0.1:${this.port}`).searchParams.get("token")===this.authToken}handleRequest(e,t){if(e.method!=="POST"){t.writeHead(405),t.end(JSON.stringify({error:"method not allowed"}));return}if(!this.checkAuth(e)){t.writeHead(401),t.end(JSON.stringify({ok:!1,error:"unauthorized"}));return}const r=new URL(e.url??"/",`http://127.0.0.1:${this.port}`);if(r.pathname==="/api/status-line"){this.handleStatusLine(e,t);return}if(r.pathname==="/v1/logs"||r.pathname==="/v1/traces"){this.handleOtel(r.pathname==="/v1/logs"?"logs":"traces",e,t);return}if(!r.pathname.startsWith("/api/invoke")){t.writeHead(404),t.end(JSON.stringify({error:"not found"}));return}const o=[];let i=0;e.on("data",n=>{if(i+=n.length,i>f){t.writeHead(413),t.end(JSON.stringify({ok:!1,error:"request body too large"})),e.destroy();return}o.push(n)}),e.on("end",async()=>{let n;try{n=JSON.parse(Buffer.concat(o).toString("utf8"))}catch{t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"invalid JSON"}));return}const s=n.action,d=n.params??{};if(!s||!this.invokeHandler){t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"missing action or no handler"}));return}if(!J.has(s)){t.writeHead(400),t.end(JSON.stringify({ok:!1,error:`action '${s}' is not allowed`}));return}if(this.activeInvokes>=H){t.writeHead(429),t.end(JSON.stringify({ok:!1,error:"too many concurrent invokes"}));return}const a=n.timeout_ms!=null?Math.max(b,Math.min(n.timeout_ms,g)):g,l=Date.now();this.activeInvokes++;const u=new AbortController;try{const h=await Promise.race([this.invokeHandler(s,d,u.signal,a),new Promise((p,m)=>setTimeout(()=>{u.abort(),m(new Error("invoke timeout"))},a))]);t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,data:h})),c.info("internal-api",`invoke ok action=${s} tool=${d.tool_name??"-"} ms=${Date.now()-l}`)}catch(h){u.abort();const p=h instanceof Error?h.message:String(h);t.writeHead(500),t.end(JSON.stringify({ok:!1,error:p})),c.error("internal-api",`invoke error action=${s} tool=${d.tool_name??"-"} ms=${Date.now()-l} err=${p}`)}finally{this.activeInvokes--}})}handleStatusLine(e,t){const r=[];let o=0;e.on("data",i=>{if(o+=i.length,o>f){t.writeHead(413),t.end(JSON.stringify({ok:!1,error:"request body too large"})),e.destroy();return}r.push(i)}),e.on("end",async()=>{let i;try{i=JSON.parse(Buffer.concat(r).toString("utf8"))}catch{t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"invalid JSON"}));return}if(!this.statusLineHandler){t.writeHead(200),t.end(JSON.stringify({ok:!0}));return}try{await this.statusLineHandler(i),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0}))}catch(n){console.error("[internal-api] statusLine handler error:",n instanceof Error?n.message:n),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0}))}})}handleOtel(e,t,r){const o=[];let i=0,n=!1;t.on("data",s=>{if(!n){if(i+=s.length,i>v){n=!0,r.writeHead(413,{"content-type":"application/json"}),r.end(JSON.stringify({error:"OTLP request body too large"}));return}o.push(s)}}),t.on("end",async()=>{if(n)return;const s=Buffer.concat(o).toString("utf8");let d;try{const a=JSON.parse(s);if(!a||typeof a!="object"||Array.isArray(a))throw new Error("OTLP body is not an object");d=a}catch{r.writeHead(400,{"content-type":"application/json"}),r.end(JSON.stringify({error:"invalid OTLP JSON"}));return}try{await this.otelHandler?.(e,d,s)}catch(a){c.warn("internal-api",`OTLP ${e} evidence persistence failed: ${a instanceof Error?a.message:String(a)}`)}r.writeHead(200,{"content-type":"application/json"}),r.end("{}")})}}export{E as InternalApiServer};
1
+ import J from"node:http";import{randomBytes as E}from"node:crypto";import{log as c}from"../log/index.js";import{PHASE1_INVOKE_ACTIONS as M,TOOLS as S,toolCallToInvoke as P}from"./tools.js";import{WebSocketServer as R,WebSocket as _}from"ws";const w=1024*1024,$=32*1024*1024,v=50,k=1e3,f=75e3,I="2024-11-05",A=256,b=new Set([...M,"event_tool_call"]);class D{server=null;port=0;authToken;invokeHandler=null;statusLineHandler=null;otelHandler=null;activeInvokes=0;wss=null;bridgeWs=null;mcpBridgeUpHandler=null;downstreamOutbox=[];mcpBridgeHttpActive=!1;pendingBridgeResponses=new Map;constructor(){this.authToken=E(32).toString("hex")}setInvokeHandler(e){this.invokeHandler=e}setStatusLineHandler(e){this.statusLineHandler=e}setOtelHandler(e){this.otelHandler=e}async start(e=0){return new Promise((t,r)=>{this.server=J.createServer((i,n)=>this.handleRequest(i,n)),this.wss=new R({noServer:!0}),this.server.on("upgrade",(i,n,s)=>{const o=new URL(i.url??"/",`http://127.0.0.1:${this.port}`);if(o.pathname!=="/mcp-bridge"||o.searchParams.get("token")!==this.authToken){n.destroy();return}this.wss.handleUpgrade(i,n,s,d=>{if(this.bridgeWs=d,c.info("internal-api","mcp bridge connected"),this.downstreamOutbox.length>0){const l=this.downstreamOutbox;this.downstreamOutbox=[];for(const u of l)d.send(u);c.info("internal-api",`mcp bridge flushed ${l.length} buffered downstream frame(s)`)}d.on("message",l=>{let u;try{u=JSON.parse(l.toString("utf8"))}catch{return}this.mcpBridgeUpHandler?.(u)}),d.on("close",()=>{this.bridgeWs===d&&(this.bridgeWs=null),c.info("internal-api","mcp bridge disconnected")}),d.on("error",()=>{})})}),this.server.on("error",r),this.server.listen(e,"127.0.0.1",()=>{const i=this.server.address();typeof i=="object"&&i&&(this.port=i.port),this.server.removeListener("error",r),t(this.port)})})}get url(){return`http://127.0.0.1:${this.port}?token=${this.authToken}`}get baseUrl(){return`http://127.0.0.1:${this.port}`}get mcpBridgeWsUrl(){return`ws://127.0.0.1:${this.port}/mcp-bridge?token=${this.authToken}`}get mcpBridgeHttpUrl(){return this.mcpBridgeHttpActive=!0,`${this.baseUrl}/mcp/app-bridge`}get mcpToolsHttpUrl(){return`${this.baseUrl}/mcp/tools`}setMcpBridgeUpHandler(e){this.mcpBridgeUpHandler=e}sendMcpFrameToBridge(e){const t=U(e);if(t!==void 0){const i=T(t),n=this.pendingBridgeResponses.get(i);if(n){this.pendingBridgeResponses.delete(i),clearTimeout(n.timer),n.resolve(e);return}}const r=JSON.stringify(e);if(this.bridgeWs&&this.bridgeWs.readyState===_.OPEN){this.bridgeWs.send(r);return}if(this.mcpBridgeHttpActive){c.debug("internal-api","dropping unsolicited MCP bridge frame without an HTTP stream");return}this.downstreamOutbox.length>=A&&(this.downstreamOutbox.shift(),c.warn("internal-api","mcp downstream outbox full, dropping oldest frame")),this.downstreamOutbox.push(r)}get token(){return this.authToken}async stop(){if(this.server){for(const[e,t]of this.pendingBridgeResponses)clearTimeout(t.timer),t.reject(new Error("internal API stopped")),this.pendingBridgeResponses.delete(e);return new Promise(e=>{this.server.close(()=>{this.server=null,e()})})}}checkAuth(e){return e.headers.authorization===`Bearer ${this.authToken}`||new URL(e.url??"/",`http://127.0.0.1:${this.port}`).searchParams.get("token")===this.authToken}handleRequest(e,t){if(e.method!=="POST"){t.writeHead(405),t.end(JSON.stringify({error:"method not allowed"}));return}if(!this.checkAuth(e)){t.writeHead(401),t.end(JSON.stringify({ok:!1,error:"unauthorized"}));return}const r=new URL(e.url??"/",`http://127.0.0.1:${this.port}`);if(r.pathname==="/mcp/tools"){this.handleMcpTools(e,t);return}if(r.pathname==="/mcp/app-bridge"){this.handleMcpBridgeHttp(e,t);return}if(r.pathname==="/api/status-line"){this.handleStatusLine(e,t);return}if(r.pathname==="/v1/logs"||r.pathname==="/v1/traces"){this.handleOtel(r.pathname==="/v1/logs"?"logs":"traces",e,t);return}if(!r.pathname.startsWith("/api/invoke")){t.writeHead(404),t.end(JSON.stringify({error:"not found"}));return}const i=[];let n=0;e.on("data",s=>{if(n+=s.length,n>w){t.writeHead(413),t.end(JSON.stringify({ok:!1,error:"request body too large"})),e.destroy();return}i.push(s)}),e.on("end",async()=>{let s;try{s=JSON.parse(Buffer.concat(i).toString("utf8"))}catch{t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"invalid JSON"}));return}const o=s.action,d=s.params??{};if(!o||!this.invokeHandler){t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"missing action or no handler"}));return}if(!b.has(o)){t.writeHead(400),t.end(JSON.stringify({ok:!1,error:`action '${o}' is not allowed`}));return}if(this.activeInvokes>=v){t.writeHead(429),t.end(JSON.stringify({ok:!1,error:"too many concurrent invokes"}));return}const l=s.timeout_ms!=null?Math.max(k,Math.min(s.timeout_ms,f)):f,u=Date.now();this.activeInvokes++;const m=new AbortController;try{const h=await Promise.race([this.invokeHandler(o,d,m.signal,l),new Promise((y,B)=>setTimeout(()=>{m.abort(),B(new Error("invoke timeout"))},l))]);t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,data:h})),c.info("internal-api",`invoke ok action=${o} tool=${d.tool_name??"-"} ms=${Date.now()-u}`)}catch(h){m.abort();const y=h instanceof Error?h.message:String(h);t.writeHead(500),t.end(JSON.stringify({ok:!1,error:y})),c.error("internal-api",`invoke error action=${o} tool=${d.tool_name??"-"} ms=${Date.now()-u} err=${y}`)}finally{this.activeInvokes--}})}async handleMcpTools(e,t){let r;try{r=N(await H(e))}catch(o){p(t,null,-32700,o instanceof Error?o.message:String(o),400);return}if(r.method==="notifications/initialized"){t.writeHead(202),t.end();return}const i=r.id??null;if(r.method==="initialize"){g(t,i,{protocolVersion:I,capabilities:{tools:{}},serverInfo:{name:"grix-connector-tools",version:"1.0.0"}});return}if(r.method==="ping"){g(t,i,{});return}if(r.method==="tools/list"){g(t,i,{tools:S.map(({name:o,description:d,inputSchema:l})=>({name:o,description:d,inputSchema:l}))});return}if(r.method!=="tools/call"){p(t,i,-32601,`Method not found: ${r.method}`);return}const n=String(r.params?.name??""),s=r.params?.arguments??{};if(!S.some(o=>o.name===n)){p(t,i,-32602,`Unknown tool: ${n}`);return}try{const o=P(n,s),d=await this.invokeMcpTool(o.action,o.params,o.timeoutMs);g(t,i,{content:[{type:"text",text:JSON.stringify(d,null,2)??"null"}]})}catch(o){const d=o instanceof Error?o.message:String(o);g(t,i,{content:[{type:"text",text:JSON.stringify({error:d})}],isError:!0})}}async handleMcpBridgeHttp(e,t){let r;try{r=N(await H(e))}catch(n){p(t,null,-32700,n instanceof Error?n.message:String(n),400);return}if(!this.mcpBridgeUpHandler){p(t,r.id??null,-32603,"MCP bridge is unavailable",503);return}if(r.id===void 0){this.mcpBridgeUpHandler(r),t.writeHead(202),t.end();return}const i=T(r.id);if(this.pendingBridgeResponses.has(i)){p(t,r.id,-32600,"Duplicate in-flight MCP request id",409);return}try{const n=await new Promise((s,o)=>{const d=setTimeout(()=>{this.pendingBridgeResponses.delete(i),o(new Error("MCP bridge response timeout"))},f);this.pendingBridgeResponses.set(i,{resolve:s,reject:o,timer:d});try{this.mcpBridgeUpHandler(r)}catch(l){clearTimeout(d),this.pendingBridgeResponses.delete(i),o(l instanceof Error?l:new Error(String(l)))}});O(t,200,n)}catch(n){p(t,r.id,-32603,n instanceof Error?n.message:String(n),504)}}async invokeMcpTool(e,t,r){if(!this.invokeHandler)throw new Error("invoke handler is unavailable");if(!b.has(e))throw new Error(`action '${e}' is not allowed`);if(this.activeInvokes>=v)throw new Error("too many concurrent invokes");const i=r!=null?Math.max(k,Math.min(r,f)):f,n=new AbortController;let s;this.activeInvokes++;try{return await Promise.race([this.invokeHandler(e,t,n.signal,i),new Promise((o,d)=>{s=setTimeout(()=>{n.abort(),d(new Error("invoke timeout"))},i)})])}finally{s&&clearTimeout(s),this.activeInvokes--}}handleStatusLine(e,t){const r=[];let i=0;e.on("data",n=>{if(i+=n.length,i>w){t.writeHead(413),t.end(JSON.stringify({ok:!1,error:"request body too large"})),e.destroy();return}r.push(n)}),e.on("end",async()=>{let n;try{n=JSON.parse(Buffer.concat(r).toString("utf8"))}catch{t.writeHead(400),t.end(JSON.stringify({ok:!1,error:"invalid JSON"}));return}if(!this.statusLineHandler){t.writeHead(200),t.end(JSON.stringify({ok:!0}));return}try{await this.statusLineHandler(n),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0}))}catch(s){console.error("[internal-api] statusLine handler error:",s instanceof Error?s.message:s),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0}))}})}handleOtel(e,t,r){const i=[];let n=0,s=!1;t.on("data",o=>{if(!s){if(n+=o.length,n>$){s=!0,r.writeHead(413,{"content-type":"application/json"}),r.end(JSON.stringify({error:"OTLP request body too large"}));return}i.push(o)}}),t.on("end",async()=>{if(s)return;const o=Buffer.concat(i).toString("utf8");let d;try{const l=JSON.parse(o);if(!l||typeof l!="object"||Array.isArray(l))throw new Error("OTLP body is not an object");d=l}catch{r.writeHead(400,{"content-type":"application/json"}),r.end(JSON.stringify({error:"invalid OTLP JSON"}));return}try{await this.otelHandler?.(e,d,o)}catch(l){c.warn("internal-api",`OTLP ${e} evidence persistence failed: ${l instanceof Error?l.message:String(l)}`)}r.writeHead(200,{"content-type":"application/json"}),r.end("{}")})}}function H(a){return new Promise((e,t)=>{const r=[];let i=0,n=!1;a.on("data",s=>{if(!n){if(i+=s.length,i>w){n=!0,t(new Error("request body too large")),a.resume();return}r.push(s)}}),a.on("end",()=>{if(!n){n=!0;try{e(JSON.parse(Buffer.concat(r).toString("utf8")))}catch{t(new Error("invalid JSON"))}}}),a.on("error",s=>{n||(n=!0,t(s))})})}function N(a){if(!a||typeof a!="object"||Array.isArray(a))throw new Error("invalid JSON-RPC request");const e=a;if(e.jsonrpc!=="2.0"||typeof e.method!="string")throw new Error("invalid JSON-RPC request");const t=e.id;if(t!=null&&typeof t!="string"&&typeof t!="number")throw new Error("invalid JSON-RPC id");return a}function U(a){if(!a||typeof a!="object"||Array.isArray(a)||!Object.prototype.hasOwnProperty.call(a,"id"))return;const e=a.id;if(e===null||typeof e=="string"||typeof e=="number")return e}function T(a){return`${typeof a}:${String(a)}`}function O(a,e,t){a.writeHead(e,{"content-type":"application/json"}),a.end(JSON.stringify(t))}function g(a,e,t){O(a,200,{jsonrpc:"2.0",id:e,result:t})}function p(a,e,t,r,i=200){O(a,i,{jsonrpc:"2.0",id:e,error:{code:t,message:r}})}export{D as InternalApiServer};
@@ -0,0 +1 @@
1
+ import{createHash as p}from"node:crypto";import{closeSync as d,existsSync as m,fsyncSync as f,mkdirSync as u,openSync as l,readFileSync as g,renameSync as y,statSync as S,unlinkSync as E,writeFileSync as w}from"node:fs";import{dirname as x,join as b}from"node:path";import{GRIX_PATHS as P}from"../log/logger.js";const _=1440*60*1e3,R=512,N=256*1024,c=300*1e3;function a(n){return n.replace(/\b(session(?:_id)?|sid|message_id|mid|trace(?:_id)?|request(?:_id)?|rid|event(?:_id)?)\s*[=:]\s*[^\s,;]+/gi,"$1=<id>").replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi,"<uuid>").replace(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})\b/gi,"<timestamp>").replace(/\b0x[0-9a-f]{12,}\b/gi,"<address>").replace(/\b[0-9a-f]{16,}\b/gi,"<id>").replace(/\b\d{13}\b/g,"<timestamp>")}function h(n){let e;if(n.exception?.values?.length)e={exceptions:n.exception.values.map(t=>({type:t.type??"",value:a(t.value??""),frames:(t.stacktrace?.frames??[]).slice(-8).map(s=>({file:s.filename??"",function:s.function??"",line:s.lineno??0,column:s.colno??0}))}))};else if(n.message)e={message:a(n.message)};else if(!n.fingerprint?.length)return;const i=JSON.stringify({release:n.release??"",platform:n.platform??"",logger:n.logger??"",level:n.level??"",fingerprint:n.fingerprint?.map(a)??[],identity:e});return p("sha256").update(i).digest("hex")}class D{filePath;windowMs;maxEntries;maxFileBytes;now;entries=new Map;pending=new Map;loaded=!1;needsRepair=!1;constructor(e={}){this.filePath=e.filePath??b(P.data,"sentry-event-dedup.json"),this.windowMs=e.windowMs??_,this.maxEntries=e.maxEntries??R,this.maxFileBytes=e.maxFileBytes??N,this.now=e.now??Date.now}shouldSend(e){const i=h(e);if(!i)return!0;this.loadOnce();const t=this.now(),s=this.entries.size;this.prune(t),this.entries.size!==s&&(this.needsRepair=!0),this.needsRepair&&(this.trimToLimit(),this.persistBestEffort(),this.needsRepair=!1);const r=this.entries.get(i);if(r!==void 0&&t-r<this.windowMs)return!1;const o=this.pending.get(i);return o!==void 0&&t-o<c?!1:(this.pending.set(i,t),this.trimPendingToLimit(),!0)}recordSendResult(e,i){const t=h(e);if(!t||(this.pending.delete(t),!i))return;const s=this.now();this.prune(s),this.entries.set(t,s),this.trimToLimit(),this.persistBestEffort()}loadOnce(){if(!this.loaded&&(this.loaded=!0,!!m(this.filePath)))try{if(S(this.filePath).size>this.maxFileBytes){this.needsRepair=!0;return}const e=JSON.parse(g(this.filePath,"utf8"));if(e.version!==1||!e.entries||typeof e.entries!="object"){this.needsRepair=!0;return}const i=Object.entries(e.entries),t=i.filter(([s,r])=>/^[0-9a-f]{64}$/.test(s)&&Number.isFinite(r)).sort((s,r)=>Number(r[1])-Number(s[1])).slice(0,this.maxEntries);this.needsRepair=t.length!==i.length;for(const[s,r]of t)this.entries.set(s,Number(r))}catch{this.needsRepair=!0}}prune(e){for(const[i,t]of this.entries)(e-t>=this.windowMs||t>e)&&this.entries.delete(i);for(const[i,t]of this.pending)(e-t>=c||t>e)&&this.pending.delete(i)}trimToLimit(){if(this.entries.size<=this.maxEntries)return;const e=[...this.entries.entries()].sort((i,t)=>i[1]-t[1]);for(const[i]of e.slice(0,e.length-this.maxEntries))this.entries.delete(i)}trimPendingToLimit(){if(this.pending.size<=this.maxEntries)return;const e=[...this.pending.entries()].sort((i,t)=>i[1]-t[1]);for(const[i]of e.slice(0,e.length-this.maxEntries))this.pending.delete(i)}persistBestEffort(){let e;try{const i=x(this.filePath);u(i,{recursive:!0,mode:448}),e=`${this.filePath}.${process.pid}.${Date.now()}.tmp`;const t={version:1,entries:Object.fromEntries(this.entries)},s=l(e,"wx",384);try{w(s,JSON.stringify(t),{encoding:"utf8"}),f(s)}finally{d(s)}y(e,this.filePath),e=void 0;try{const r=l(i,"r");try{f(r)}finally{d(r)}}catch{}}catch{}finally{if(e)try{E(e)}catch{}}}}export{D as PersistentSentryDeduplicator,_ as SENTRY_DEDUP_WINDOW_MS,h as buildSentryEventFingerprint};
@@ -1 +1 @@
1
- import{log as i}from"../log/logger.js";import{resolveClientVersion as g}from"../util/client-version.js";let s=null,a=!1;const E="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function l(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||E}async function I(){const e=l();if(e){try{s=await import("@sentry/node")}catch(n){i.warn("sentry",`Sentry \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\uFF08\u5E73\u53F0\u4E0D\u517C\u5BB9\uFF09\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${n instanceof Error?n.message:String(n)}`);return}try{s.init({dsn:e,release:`grix-connector@${g()}`,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production",sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0,beforeSend(n){const r=u(n);return r&&c(r)?null:n}}),a=!0}catch(n){i.error("sentry",`Sentry \u521D\u59CB\u5316\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}}function u(e){if(typeof e.message=="string"&&e.message)return e.message;const n=e.exception?.values;if(!n?.length)return"";const r=n[0],o=typeof r?.value=="string"?r.value:"",t=typeof r?.type=="string"?r.type:"";return o||t}function d(){return a}const S=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function y(e){if(e.ok)return!1;const n=e.error?.code;return!!n&&S.has(n)}function R(e){if(!a||!s||!y(e))return;const n=e.error?.code??"INTERNAL",r=e.error?.message??"unknown install failure",o=e.environment;try{s.withScope(t=>{t.setLevel("error"),t.setTags({agent_type:e.agentType,error_code:n,phase:e.phase}),t.setContext("install",{agentType:e.agentType,code:n,phase:e.phase,durationMs:e.durationMs,os:o?.platform,osVersion:o?.osVersion,arch:o?.arch,nodeVersion:o?.nodeVersion,npmVersion:o?.npmVersion,isDocker:o?.isDocker,isCI:o?.isCI,outputTail:(e.output??"").slice(-2e3)}),t.setFingerprint(["agent-install-failure",e.agentType,n]),s.captureException(new Error(`Agent install failed [${e.agentType}/${n}]: ${r}`))}),i.info("sentry",`\u5DF2\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25: ${e.agentType}/${n}`)}catch(t){i.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}const N=[/No config files found/,/spawn wmic ENOENT/i];function c(e){const n=e instanceof Error?e.message:String(e);return N.some(r=>r.test(n))}const p=new Set;function _(e,n){if(!a||!s||c(e))return;const r=e instanceof Error?e.message:String(e),o=`${n}:${r}`;if(!p.has(o)){p.add(o);try{s.withScope(t=>{t.setLevel("fatal"),t.setTag("crash_type",n);const f=e instanceof Error?e:new Error(String(e));s.captureException(f)}),i.info("sentry",`\u5DF2\u4E0A\u62A5\u5D29\u6E83: ${n}`)}catch(t){i.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}}async function h(e=2e3){if(!(!a||!s))try{await s.close(e)}catch{}}export{h as closeSentry,u as extractEventMessage,I as initSentry,c as isNonReportable,d as isSentryEnabled,_ as reportFatal,R as reportInstallFailure,y as shouldReportInstallFailure};
1
+ import{log as i}from"../log/logger.js";import{resolveClientVersion as f}from"../util/client-version.js";import{PersistentSentryDeduplicator as l}from"./sentry-event-dedup.js";let o=null,a=!1;const c=new l,u="https://e8e202d7625372b1314b3ff4e85a7ff9@o119262.ingest.us.sentry.io/4511410543329280";function E(){if(!process.env.GRIX_SENTRY_DISABLE)return process.env.SENTRY_DSN||process.env.GRIX_SENTRY_DSN||u}async function R(){const e=E();if(e){try{o=await import("@sentry/node")}catch(n){i.warn("sentry",`Sentry \u6A21\u5757\u52A0\u8F7D\u5931\u8D25\uFF08\u5E73\u53F0\u4E0D\u517C\u5BB9\uFF09\uFF0C\u9519\u8BEF\u4E0A\u62A5\u5DF2\u7981\u7528: ${n instanceof Error?n.message:String(n)}`);return}try{o.init({dsn:e,release:`grix-connector@${f()}`,environment:process.env.SENTRY_ENVIRONMENT||process.env.NODE_ENV||"production",sendDefaultPii:!1,initialScope:{tags:{component:"grix-connector"}},defaultIntegrations:!1,tracesSampleRate:0,beforeSend:S}),o.getClient()?.on("afterSendEvent",(n,t)=>{const r=t.statusCode;c.recordSendResult(n,r!==void 0&&r>=200&&r<300)}),a=!0}catch(n){i.error("sentry",`Sentry \u521D\u59CB\u5316\u5931\u8D25: ${n instanceof Error?n.message:String(n)}`)}}}function g(e){if(typeof e.message=="string"&&e.message)return e.message;const n=e.exception?.values;if(!n?.length)return"";const t=n[0],r=typeof t?.value=="string"?t.value:"",s=typeof t?.type=="string"?t.type:"";return r||s}function _(){return a}function S(e,n,t=c){const r=g(e);return r&&p(r)?null:t.shouldSend(e)?e:null}const y=new Set(["INSTALL_FAILED","INSTALL_TIMEOUT","FALLBACK_EXHAUSTED","PREREQ_INSTALL_FAILED","VERIFICATION_FAILED","ENVIRONMENT_UNSUPPORTED","INTERNAL"]);function d(e){if(e.ok)return!1;const n=e.error?.code;return!!n&&y.has(n)}function h(e){if(!a||!o||!d(e))return;const n=e.error?.code??"INTERNAL",t=e.error?.message??"unknown install failure",r=e.environment;try{o.withScope(s=>{s.setLevel("error"),s.setTags({agent_type:e.agentType,error_code:n,phase:e.phase}),s.setContext("install",{agentType:e.agentType,code:n,phase:e.phase,durationMs:e.durationMs,os:r?.platform,osVersion:r?.osVersion,arch:r?.arch,nodeVersion:r?.nodeVersion,npmVersion:r?.npmVersion,isDocker:r?.isDocker,isCI:r?.isCI,outputTail:(e.output??"").slice(-2e3)}),s.setFingerprint(["agent-install-failure",e.agentType,n]),o.captureException(new Error(`Agent install failed [${e.agentType}/${n}]: ${t}`))}),i.info("sentry",`\u5DF2\u63D0\u4EA4\u5B89\u88C5\u5931\u8D25\u7ED9 Sentry SDK: ${e.agentType}/${n}`)}catch(s){i.error("sentry",`\u4E0A\u62A5\u5B89\u88C5\u5931\u8D25\u65F6\u51FA\u9519: ${s instanceof Error?s.message:String(s)}`)}}const N=[/No config files found/,/spawn wmic ENOENT/i];function p(e){const n=e instanceof Error?e.message:String(e);return N.some(t=>t.test(n))}function D(e,n){if(!(!a||!o)&&!p(e))try{o.withScope(t=>{t.setLevel("fatal"),t.setTag("crash_type",n);const r=e instanceof Error?e:new Error(String(e));o.captureException(r)}),i.info("sentry",`\u5DF2\u63D0\u4EA4\u5D29\u6E83\u7ED9 Sentry SDK: ${n}`)}catch(t){i.error("sentry",`\u4E0A\u62A5\u5D29\u6E83\u65F6\u51FA\u9519: ${t instanceof Error?t.message:String(t)}`)}}async function L(e=2e3){if(!(!a||!o))try{await o.close(e)}catch{}}export{L as closeSentry,g as extractEventMessage,S as filterSentryEvent,R as initSentry,p as isNonReportable,_ as isSentryEnabled,D as reportFatal,h as reportInstallFailure,d as shouldReportInstallFailure};
@@ -1,2 +1,2 @@
1
- function i(t,s){const e=[];return s.quotedMessageId?.trim()&&e.push(`[[quoted_message_id:${s.quotedMessageId.trim()}]]`),s.messageId?.trim()&&e.push(`[[message_id:${s.messageId.trim()}]]`),e.push(t),e.join(`
1
+ function i(t,e){const s=[t];return e.messageId?.trim()&&s.push(`[[message_id:${e.messageId.trim()}]]`),e.quotedMessageId?.trim()&&s.push(`[[quoted_message_id:${e.quotedMessageId.trim()}]]`),s.join(`
2
2
  `)}export{i as injectMessageMetadata};
@@ -1,2 +1,7 @@
1
- import{normalizeString as o}from"../util/normalize-string.js";const a=/^\[\[quoted_message_id:([^\]\r\n]+)\]\](?:\r?\n)?/u;function g(r,{messageId:e="",quotedMessageId:t=""}={}){const u=String(r??""),d=o(e),n=o(t),s=[];return u&&s.push(u),d&&s.push(`[[message_id:${d}]]`),n&&s.push(`[[quoted_message_id:${n}]]`),s.join(`
2
- `)}function i(r){const e=String(r??""),t=a.exec(e);return t?{text:e.slice(t[0].length),quotedMessageId:o(t[1])}:{text:e,quotedMessageId:""}}function f({replyTo:r="",text:e="",inboundQuotedMessageId:t="",inboundMessageId:u=""}={}){const d=o(r);if(d)return{text:String(e??""),quotedMessageId:d};const n=i(e);if(n.quotedMessageId)return n;const s=o(t)||o(u);return{text:n.text,quotedMessageId:s}}export{i as extractOutboundQuotedMessageReference,g as formatInboundMessageReferenceText,f as resolveOutboundQuotedMessageId};
1
+ import{normalizeString as u}from"../util/normalize-string.js";const c=/^\[\[quoted_message_id:([^\]\r\n]+)\]\](?:\r?\n)?/u,d=/(?:\r?\n)?((?:\[\[(?:quoted_)?message_id:[^\]\r\n]+\]\](?:\r?\n|$))+)[ \t]*$/u;function m(i,{messageId:t="",quotedMessageId:e=""}={}){const n=String(i??""),r=u(t),s=u(e),o=[];return n&&o.push(n),r&&o.push(`[[message_id:${r}]]`),s&&o.push(`[[quoted_message_id:${s}]]`),o.join(`
2
+ `)}function I(i,t){const e=String(i??""),n=String(t??"").trim();if(!n)return e;const r=d.exec(e);if(!r)return e?`${e}
3
+
4
+ ${n}`:n;const s=e.slice(0,r.index).trimEnd(),o=r[1].trim();return`${s?`${s}
5
+
6
+ ${n}`:n}
7
+ ${o}`}function a(i){const t=String(i??""),e=c.exec(t);return e?{text:t.slice(e[0].length),quotedMessageId:u(e[1])}:{text:t,quotedMessageId:""}}function l({replyTo:i="",text:t="",inboundQuotedMessageId:e="",inboundMessageId:n=""}={}){const r=u(i);if(r)return{text:String(t??""),quotedMessageId:r};const s=a(t);if(s.quotedMessageId)return s;const o=u(e)||u(n);return{text:s.text,quotedMessageId:o}}export{a as extractOutboundQuotedMessageReference,m as formatInboundMessageReferenceText,I as insertBeforeTrailingMessageReferences,l as resolveOutboundQuotedMessageId};
@@ -1,9 +1,8 @@
1
1
  import{isAbsolute as y}from"node:path";const p="[\u5F15\u7528\u6D88\u606F]",g=/^(?:(?:[ \t]*>[^\r\n]*(?:\r?\n|$))+)(?:[ \t]*\r?\n)*[ \t]*-{3,}[ \t]*(?:\r?\n|$)(?:[ \t]*\r?\n)*/;function M(t,n){const e=_(t.extra_json),o=h(t.context_messages_json),r=A(t,e),i=b(r,n),s=x(t,r,o),u=S(r),c=E(r,"additional_directories","additionalDirectories"),f=l(r,"mode_id","modeId"),a=l(r,"model_id","modelId"),d=$(r,"timeout_ms","timeoutMs");return{cwd:i,prompt:s,mcpServers:u,additionalDirectories:c??[],modeId:f,modelId:a,timeoutMs:d}}function A(t,n){const e=[t,m(t.acp),m(t.gemini_acp)];return n&&(e.push(n),e.push(m(n.acp)),e.push(m(n.gemini_acp))),e.filter(o=>o!==null)}function _(t){if(t==null)return null;if(typeof t=="object"&&!Array.isArray(t))return t;if(typeof t!="string")return null;const n=t.trim();if(!n)return null;try{const e=JSON.parse(n);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return null}function h(t){if(t==null)return[];if(Array.isArray(t))return t;if(typeof t!="string")return[];const n=t.trim();if(!n)return[];try{const e=JSON.parse(n);if(Array.isArray(e))return e}catch{}return[]}function b(t,n){const o=l(t,"cwd","workdir","working_directory")?.trim()||n?.trim()||"";if(!o)return process.cwd();if(!y(o))throw new Error(`CWD must be an absolute path: ${o}`);return o}function x(t,n,e){const o=D(n,"prompt");if(o&&o.length>0){const f=o.map(a=>a?.type==="text"?a.text:"").filter(Boolean);if(f.length>0)return f.join(`
2
- `)}const r=t.quoted_message_id,i=t.msg_id;let s=l(n,"text")??t.content?.trim()??"";s=I(s,e,r);const u=Number(t.session_type)===2,c=j(e,i,u);return!c&&!s?"":c?s?`Conversation context:
3
- `+c+`
2
+ `)}const r=t.quoted_message_id,i=t.msg_id;let s=l(n,"text")??t.content?.trim()??"";s=I(s,e,r);const u=Number(t.session_type)===2,c=j(e,i,u);return!c&&!s?"":c?s?s+`
4
3
 
5
- Latest user message:
6
- `+s:`Conversation context:
4
+ [Conversation context]
5
+ `+c:`Conversation context:
7
6
  `+c:s}function j(t,n,e){if(!t||t.length===0)return"";const o=[];for(const r of t){if(n&&r.msg_id===n)continue;const i=r.content?.trim();if(!i)continue;const s=e&&r.sender_id?` (from ${r.sender_id})`:"",u=C(i);if(u)o.push(`Quoted message${s}:
8
7
  `+u);else{const c=r.sender_type===2?"Assistant message":"User message";o.push(`${c}${s}:
9
8
  ${i}`)}}return o.join(`
@@ -1,2 +1,2 @@
1
- import{execFile as C,execFileSync as h}from"node:child_process";import{promisify as H}from"node:util";import{existsSync as c,mkdirSync as P,mkdtempSync as j,readFileSync as y,renameSync as f,rmSync as d,statfsSync as G,symlinkSync as Y,unlinkSync as $,writeFileSync as J}from"node:fs";import{basename as x,join as u,relative as K,resolve as g,sep as D}from"node:path";import{GRIX_PATHS as F}from"../log/index.js";import{appendRotatingFileSync as W}from"../log/rotation.js";import{getMachineName as X}from"../util/host-info.js";import{resolveInstallId as z}from"../util/install-id.js";import{npmInstallWithMirror as N,probeRegistries as q}from"../installer/npm-registry.js";const Q=H(C);class a extends Error{code;constructor(t,r){super(r),this.name="UpgradeError",this.code=t}}const M=".grix-upgrade-backup",Z=".grix-upgrade-failed";function A(){return u(F.log,"upgrade.log")}function w(){return u(F.data,"upgrade-pending.json")}function ge(){return c(w())}function v(){const e=w();if(!c(e))return null;try{return JSON.parse(y(e,"utf-8"))}catch{return null}}function me(e,t){const r={from_version:e,target_version:t,upgraded_at:new Date().toISOString(),crash_count:0},i=w();L(i,r)}function L(e,t){const r=e+".tmp";J(r,JSON.stringify(t),"utf-8"),f(r,e)}function S(e,t){const r=g(e);return g(t)===`${r}${M}`&&r.includes(`${D}node_modules${D}`)&&x(r).length>0}function ee(e,t){const r=v();!r||!S(e,t)||(r.package_dir=g(e),r.backup_dir=g(t),L(w(),r))}function he(e=v()){const t=e?.package_dir,r=e?.backup_dir;if(!(!t||!r||!S(t,r)))try{d(r,{recursive:!0,force:!0})}catch{}}function we(e=v()){const t=e?.package_dir,r=e?.backup_dir;if(!t||!r||!S(t,r)||!c(r))return!1;const i=`${t}${Z}`;try{return d(i,{recursive:!0,force:!0}),c(t)&&f(t,i),f(r,t),d(i,{recursive:!0,force:!0}),p(`atomic rollback restored ${t}`),!0}catch(n){try{!c(t)&&c(i)&&f(i,t)}catch{}return p(`atomic rollback failed: ${n instanceof Error?n.message:String(n)}`),!1}}function _e(){const e=w();if(c(e))try{$(e)}catch{}}function p(e){const t=`[${new Date().toISOString()}] ${e}
2
- `;try{W(A(),t)}catch{}}function ye(e=4096){const t=A();if(!c(t))return"";try{const r=y(t,"utf-8");return r.length<=e?r:r.slice(-e)}catch{return""}}function O(){try{const e=process.platform==="win32";return h(e?"cmd.exe":"npm",e?["/c","npm","--version"]:["--version"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("NPM_NOT_FOUND","npm is not available or timed out")}}const m=1e6;function R(){if(process.platform==="win32")return m;let e;try{e=h("npm",["prefix","-g"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{return m}try{const t=G(e),r=Math.floor(Number(t.bavail)*Number(t.bsize)/(1024*1024));return!Number.isFinite(r)||r<0?m:Math.min(r,m)}catch{return m}}function ke(){let e;try{e=O()}catch(r){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:r instanceof Error?r.message:"npm not available"}}const t=R();return t<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${t}MB free disk space (need >= 100MB)`,npmVersion:e,diskFreeMb:t}:{ok:!0,npmVersion:e,diskFreeMb:t}}function ve(){let e="";try{e=O()}catch{}let t=m;try{t=R()}catch{}return{npm_version:e,node_version:process.version,disk_free_mb:t,platform:process.platform,arch:process.arch,host_name:X(),install_id:z()}}function b(e,t,r){const i=u(e,"package.json");let n;try{n=JSON.parse(y(i,"utf-8"))}catch{throw new a("VERSION_VERIFY_FAILED",`Unable to read staged package metadata: ${i}`)}if(n.name!==t||n.version?.replace(/^v(?=\d)/,"")!==r)throw new a("VERSION_MISMATCH",`Staged package ${String(n.name)}@${String(n.version)} does not match ${t}@${r}`);return n}function te(e){return typeof e.bin=="string"?[[x(e.name),e.bin]]:!e.bin||typeof e.bin!="object"?[]:Object.entries(e.bin).filter(t=>typeof t[1]=="string")}function E(e,t){const r=te(t);if(r.length===0)throw new a("VERSION_VERIFY_FAILED",`Staged package ${t.name} has no executable bin`);for(const[,i]of r){const n=u(e,i);if(!c(n))throw new a("VERSION_VERIFY_FAILED",`Staged package entrypoint is missing: ${n}`)}return r}function re(e,t,r){if(process.platform==="win32")return;const i=u(e,"bin");P(i,{recursive:!0});for(const[n,o]of r){const s=u(i,n);if(c(s))continue;const l=`${s}.grix-upgrade-${process.pid}`;try{$(l)}catch{}Y(K(i,u(t,o)),l),f(l,s)}}function ne(e){const t=g(e.activePackageDir),r=g(e.stagedPackageDir),i=`${t}${M}`,n=b(r,e.packageName,e.version),o=E(r,n);c(i)&&(c(t)?d(i,{recursive:!0,force:!0}):f(i,t)),ee(t,i);let s=!1;try{c(t)&&(f(t,i),s=!0),f(r,t);const l=b(t,e.packageName,e.version),k=E(t,l);return re(e.npmPrefix,t,k),{packageDir:t,backupDir:i}}catch(l){try{c(t)&&d(t,{recursive:!0,force:!0}),s&&c(i)&&f(i,t)}catch{}throw l}}function I(e){try{return h("npm",e,{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("NPM_NOT_FOUND",`Unable to resolve npm ${e.join(" ")}`)}}async function ie(e,t,r){const i=`${e}@${t}`,n=I(["prefix","-g"]),o=I(["root","-g"]);P(n,{recursive:!0});const s=j(u(n,".grix-upgrade-stage-"));try{p(`staging npm install ${i} under ${s}`);const{registry:l}=await N(i,r,10*1024*1024,{prefix:s}),k=I(["root","-g","--prefix",s]),_=u(k,e),U=b(_,e,t),T=E(_,U),V=u(_,T[0][1]);h(process.execPath,[V,"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0});const B=ne({packageName:e,version:t,npmPrefix:n,activePackageDir:u(o,e),stagedPackageDir:_});p(`atomic package switch succeeded via ${l}; backup=${B.backupDir}`)}finally{try{d(s,{recursive:!0,force:!0})}catch{}}}async function Se(e,t,r=12e4){const i=`${e}@${t}`;p(`npm install -g ${i} starting (atomic staging with mirror fallback)`);try{if(process.platform==="win32"){const{registry:n}=await N(i,r,10485760);p(`npm install succeeded via ${n}`)}else await ie(e,t,r)}catch(n){if(n instanceof a)throw n;const o=n instanceof Error?n.message:String(n);throw p(`npm install failed: ${o}`),/timed out|ETIMEDOUT/i.test(o)?new a("NPM_TIMEOUT",`npm install timed out after ${r/1e3}s (tried all mirrors): ${o}`):o.includes("EACCES")||o.includes("permission denied")?new a("NPM_INSTALL_FAILED",`Permission denied: ${o}`):o.includes("ENOSPC")||o.includes("no space left")?new a("DISK_FULL",`Disk full: ${o}`):o.includes("404")||o.includes("not found")?new a("NPM_INSTALL_FAILED",`Package not found: ${o}`):new a("NPM_INSTALL_FAILED",o)}}function be(e){let t;try{const i=process.platform==="win32";t=h(i?"cmd.exe":"npm",i?["/c","npm","root","-g"]:["root","-g"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("VERSION_VERIFY_FAILED","Unable to resolve the global npm package directory")}let r;try{const i=u(t,"grix-connector","package.json"),n=JSON.parse(y(i,"utf-8"));if(n.name!=="grix-connector"||typeof n.version!="string"||!n.version.trim())throw new Error("invalid package metadata");r=n.version.trim().replace(/^v(?=\d)/,"")}catch{throw new a("VERSION_VERIFY_FAILED","Unable to read the installed grix-connector version")}if(r!==e)throw new a("VERSION_MISMATCH",`Installed version ${r} does not match expected ${e}`);return r}async function Ee(e,t){let r=!1;for(const i of await q())try{const{stdout:n}=await Q("npm",["view",`${e}@${t}`,"version","--registry",i.url,"--no-audit","--no-fund"],{timeout:8e3});if(n.trim())return!0}catch(n){const o=n,s=o.stderr||o.message||String(n);/ETARGET|No matching version|notarget|E404/i.test(s)&&(r=!0)}return!r}export{a as UpgradeError,R as checkDiskSpace,O as checkNpmAvailable,he as cleanupPendingBackup,ve as collectEnvInfo,ne as commitStagedGlobalPackage,ye as getUpgradeLogTail,Ee as isVersionAvailable,Se as npmInstall,ge as pendingExists,ke as preflightCheck,v as readPending,_e as removePending,we as rollbackPendingInstall,p as upgradeLog,be as verifyInstalledVersion,me as writePending};
1
+ import{execFile as C,execFileSync as h}from"node:child_process";import{promisify as j}from"node:util";import{existsSync as c,mkdirSync as $,mkdtempSync as H,readFileSync as _,renameSync as f,rmSync as g,statfsSync as G,symlinkSync as J,unlinkSync as x,writeFileSync as Y}from"node:fs";import{basename as D,join as u,relative as K,resolve as p,sep as k}from"node:path";import{GRIX_PATHS as F}from"../log/index.js";import{appendRotatingFileSync as W}from"../log/rotation.js";import{getMachineName as X}from"../util/host-info.js";import{resolveInstallId as z}from"../util/install-id.js";import{npmInstallWithMirror as N,probeRegistries as q}from"../installer/npm-registry.js";const Q=j(C);class a extends Error{code;constructor(t,r){super(r),this.name="UpgradeError",this.code=t}}const M=".grix-upgrade-backup",Z=".grix-upgrade-failed";function O(){return u(F.log,"upgrade.log")}function w(){return u(F.data,"upgrade-pending.json")}function ge(){return c(w())}function S(){const e=w();if(!c(e))return null;try{return JSON.parse(_(e,"utf-8"))}catch{return null}}function me(e,t){const r={from_version:e,target_version:t,upgraded_at:new Date().toISOString(),crash_count:0},n=w();A(n,r)}function he(e){const t=e.package_dir;if(!t)return null;const r=p(t);if(!r.includes(`${k}node_modules${k}`))return null;try{const n=JSON.parse(_(u(r,"package.json"),"utf-8"));return n.name!=="grix-connector"||typeof n.version!="string"?null:n.version.trim().replace(/^v/,"")||null}catch{return null}}function A(e,t){const r=e+".tmp";Y(r,JSON.stringify(t),"utf-8"),f(r,e)}function b(e,t){const r=p(e);return p(t)===`${r}${M}`&&r.includes(`${k}node_modules${k}`)&&D(r).length>0}function ee(e,t){const r=S();!r||!b(e,t)||(r.package_dir=p(e),r.backup_dir=p(t),A(w(),r))}function _e(e=S()){const t=e?.package_dir,r=e?.backup_dir;if(!(!t||!r||!b(t,r)))try{g(r,{recursive:!0,force:!0})}catch{}}function we(e=S()){const t=e?.package_dir,r=e?.backup_dir;if(!t||!r||!b(t,r)||!c(r))return!1;const n=`${t}${Z}`;try{return g(n,{recursive:!0,force:!0}),c(t)&&f(t,n),f(r,t),g(n,{recursive:!0,force:!0}),d(`atomic rollback restored ${t}`),!0}catch(i){try{!c(t)&&c(n)&&f(n,t)}catch{}return d(`atomic rollback failed: ${i instanceof Error?i.message:String(i)}`),!1}}function ye(){const e=w();if(c(e))try{x(e)}catch{}}function d(e){const t=`[${new Date().toISOString()}] ${e}
2
+ `;try{W(O(),t)}catch{}}function ke(e=4096){const t=O();if(!c(t))return"";try{const r=_(t,"utf-8");return r.length<=e?r:r.slice(-e)}catch{return""}}function L(){try{const e=process.platform==="win32";return h(e?"cmd.exe":"npm",e?["/c","npm","--version"]:["--version"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("NPM_NOT_FOUND","npm is not available or timed out")}}const m=1e6;function R(){if(process.platform==="win32")return m;let e;try{e=h("npm",["prefix","-g"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{return m}try{const t=G(e),r=Math.floor(Number(t.bavail)*Number(t.bsize)/(1024*1024));return!Number.isFinite(r)||r<0?m:Math.min(r,m)}catch{return m}}function ve(){let e;try{e=L()}catch(r){return{ok:!1,errorCode:"NPM_NOT_FOUND",errorMsg:r instanceof Error?r.message:"npm not available"}}const t=R();return t<100?{ok:!1,errorCode:"DISK_FULL",errorMsg:`Only ${t}MB free disk space (need >= 100MB)`,npmVersion:e,diskFreeMb:t}:{ok:!0,npmVersion:e,diskFreeMb:t}}function Se(){let e="";try{e=L()}catch{}let t=m;try{t=R()}catch{}return{npm_version:e,node_version:process.version,disk_free_mb:t,platform:process.platform,arch:process.arch,host_name:X(),install_id:z()}}function P(e,t,r){const n=u(e,"package.json");let i;try{i=JSON.parse(_(n,"utf-8"))}catch{throw new a("VERSION_VERIFY_FAILED",`Unable to read staged package metadata: ${n}`)}if(i.name!==t||i.version?.replace(/^v(?=\d)/,"")!==r)throw new a("VERSION_MISMATCH",`Staged package ${String(i.name)}@${String(i.version)} does not match ${t}@${r}`);return i}function te(e){return typeof e.bin=="string"?[[D(e.name),e.bin]]:!e.bin||typeof e.bin!="object"?[]:Object.entries(e.bin).filter(t=>typeof t[1]=="string")}function E(e,t){const r=te(t);if(r.length===0)throw new a("VERSION_VERIFY_FAILED",`Staged package ${t.name} has no executable bin`);for(const[,n]of r){const i=u(e,n);if(!c(i))throw new a("VERSION_VERIFY_FAILED",`Staged package entrypoint is missing: ${i}`)}return r}function re(e,t,r){if(process.platform==="win32")return;const n=u(e,"bin");$(n,{recursive:!0});for(const[i,o]of r){const s=u(n,i);if(c(s))continue;const l=`${s}.grix-upgrade-${process.pid}`;try{x(l)}catch{}J(K(n,u(t,o)),l),f(l,s)}}function ne(e){const t=p(e.activePackageDir),r=p(e.stagedPackageDir),n=`${t}${M}`,i=P(r,e.packageName,e.version),o=E(r,i);c(n)&&(c(t)?g(n,{recursive:!0,force:!0}):f(n,t)),ee(t,n);let s=!1;try{c(t)&&(f(t,n),s=!0),f(r,t);const l=P(t,e.packageName,e.version),v=E(t,l);return re(e.npmPrefix,t,v),{packageDir:t,backupDir:n}}catch(l){try{c(t)&&g(t,{recursive:!0,force:!0}),s&&c(n)&&f(n,t)}catch{}throw l}}function I(e){try{return h("npm",e,{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("NPM_NOT_FOUND",`Unable to resolve npm ${e.join(" ")}`)}}async function ie(e,t,r){const n=`${e}@${t}`,i=I(["prefix","-g"]),o=I(["root","-g"]);$(i,{recursive:!0});const s=H(u(i,".grix-upgrade-stage-"));try{d(`staging npm install ${n} under ${s}`);const{registry:l}=await N(n,r,10*1024*1024,{prefix:s}),v=I(["root","-g","--prefix",s]),y=u(v,e),U=P(y,e,t),T=E(y,U),V=u(y,T[0][1]);h(process.execPath,[V,"--version"],{encoding:"utf-8",timeout:15e3,maxBuffer:2*1024*1024,windowsHide:!0});const B=ne({packageName:e,version:t,npmPrefix:i,activePackageDir:u(o,e),stagedPackageDir:y});d(`atomic package switch succeeded via ${l}; backup=${B.backupDir}`)}finally{try{g(s,{recursive:!0,force:!0})}catch{}}}async function be(e,t,r=12e4){const n=`${e}@${t}`;d(`npm install -g ${n} starting (atomic staging with mirror fallback)`);try{if(process.platform==="win32"){const{registry:i}=await N(n,r,10485760);d(`npm install succeeded via ${i}`)}else await ie(e,t,r)}catch(i){if(i instanceof a)throw i;const o=i instanceof Error?i.message:String(i);throw d(`npm install failed: ${o}`),/timed out|ETIMEDOUT/i.test(o)?new a("NPM_TIMEOUT",`npm install timed out after ${r/1e3}s (tried all mirrors): ${o}`):o.includes("EACCES")||o.includes("permission denied")?new a("NPM_INSTALL_FAILED",`Permission denied: ${o}`):o.includes("ENOSPC")||o.includes("no space left")?new a("DISK_FULL",`Disk full: ${o}`):o.includes("404")||o.includes("not found")?new a("NPM_INSTALL_FAILED",`Package not found: ${o}`):new a("NPM_INSTALL_FAILED",o)}}function Pe(e){let t;try{const n=process.platform==="win32";t=h(n?"cmd.exe":"npm",n?["/c","npm","root","-g"]:["root","-g"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim()}catch{throw new a("VERSION_VERIFY_FAILED","Unable to resolve the global npm package directory")}let r;try{const n=u(t,"grix-connector","package.json"),i=JSON.parse(_(n,"utf-8"));if(i.name!=="grix-connector"||typeof i.version!="string"||!i.version.trim())throw new Error("invalid package metadata");r=i.version.trim().replace(/^v(?=\d)/,"")}catch{throw new a("VERSION_VERIFY_FAILED","Unable to read the installed grix-connector version")}if(r!==e)throw new a("VERSION_MISMATCH",`Installed version ${r} does not match expected ${e}`);return r}async function Ee(e,t){let r=!1;for(const n of await q())try{const{stdout:i}=await Q("npm",["view",`${e}@${t}`,"version","--registry",n.url,"--no-audit","--no-fund"],{timeout:8e3});if(i.trim())return!0}catch(i){const o=i,s=o.stderr||o.message||String(i);/ETARGET|No matching version|notarget|E404/i.test(s)&&(r=!0)}return!r}export{a as UpgradeError,R as checkDiskSpace,L as checkNpmAvailable,_e as cleanupPendingBackup,Se as collectEnvInfo,ne as commitStagedGlobalPackage,ke as getUpgradeLogTail,Ee as isVersionAvailable,be as npmInstall,ge as pendingExists,ve as preflightCheck,S as readPending,he as readPendingPackageVersion,ye as removePending,we as rollbackPendingInstall,d as upgradeLog,Pe as verifyInstalledVersion,me as writePending};
@@ -1 +1 @@
1
- import{existsSync as h,mkdirSync as C,readFileSync as v,renameSync as M,writeFileSync as $}from"node:fs";import{join as g,dirname as R}from"node:path";import{spawn as L}from"node:child_process";import{log as o}from"../log/index.js";import{GRIX_PATHS as m}from"../log/index.js";import{resolveClientVersion as _}from"../util/client-version.js";import{UpgradeError as O,cleanupPendingBackup as y,collectEnvInfo as D,getUpgradeLogTail as N,isVersionAvailable as F,npmInstall as H,pendingExists as S,preflightCheck as x,readPending as B,removePending as u,rollbackPendingInstall as z,upgradeLog as a,verifyInstalledVersion as G,writePending as V}from"./npm-upgrader.js";import{isRetriableRegistryError as j}from"../installer/npm-registry.js";const K=360*60*1e3,X=300*1e3,T=1800*1e3,q=2,J=3,k="grix-connector",A=1e4,b=15,Y=2e3,Z=8e3,W=1e3;function P(l){return new URL(l.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function E(){return g(m.data,"upgrade-state.json")}function I(){const l=E();if(!h(l))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(v(l,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function Q(l){const t=E();C(R(t),{recursive:!0});const e=t+".tmp";$(e,JSON.stringify(l),"utf-8"),M(e,t)}function U(){return new Date().toISOString().slice(0,10)}class ct{agentConfigs;isBusy;closeAdmission;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e,i){this.agentConfigs=t,this.isBusy=e,this.closeAdmission=i}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),K)))},X)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){return this.agentConfigs.length===0?{available:!1}:(await Promise.all(this.agentConfigs.map(i=>this.queryUpgrade(i)))).find(i=>i.available&&i.release)??{available:!1}}async handlePendingOnStartup(){if(!S())return;const t=B();if(!t){u();return}const e=_(),i=e===t.target_version,r=i?{from_version:t.from_version,to_version:t.target_version,status:"success"}:{from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count};if(a(i?`daemon startup: version matches target ${t.target_version}, upgrade succeeded`:`daemon startup: version ${e} != target ${t.target_version}, rolled back`),i){const s=await this.confirmHealthz(Z);a(s?"healthz confirmed healthy, reporting success":"healthz not confirmed within window; reporting success on version-match (guardian still guards crashes)")}if(await this.reportUpgrade(r)){i&&y(t),u();return}this.retryStartupReport(r,i?t:null)}async retryStartupReport(t,e){for(let i=2;i<=b;i++){if(this.stopped)return;const r=Y*(i-1)+Math.floor(Math.random()*1e3);if(await new Promise(s=>setTimeout(s,r)),this.stopped)return;if(await this.reportUpgrade(t)){y(e),u();return}}a(`startup report (${t.status}) not delivered after ${b} attempts; clearing pending to unblock future checks`),y(e),u()}async confirmHealthz(t){let e="19579";try{const s=g(m.data,"health-port");h(s)&&(e=v(s,"utf-8").trim()||e)}catch{}const i=`http://127.0.0.1:${e}/healthz`,r=Date.now()+t;for(;Date.now()<r&&!this.stopped;){try{if((await fetch(i,{signal:AbortSignal.timeout(2e3)})).ok)return!0}catch{}await new Promise(s=>setTimeout(s,W))}return!1}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){o.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(S()||this.agentConfigs.length===0)return;const i=(await Promise.all(this.agentConfigs.map(n=>this.queryUpgrade(n)))).find(n=>n.available&&n.release)?.release;if(!i)return;const r=i.version,s=_();if(!i.force&&!this.checkRateLimit(r))return;const p=x();if(!p.ok){await this.reportUpgrade({from_version:s,to_version:r,status:"failed",error_code:p.errorCode,error_msg:p.errorMsg});return}if(!await F(k,r)){a(`target ${r} not resolvable on any registry yet; skipping this cycle (no failure recorded)`);return}a(`upgrade start: ${s} -> ${r}`);const w=Date.now();try{if(V(s,r),await H(k,r),G(r),a("npm install verified, reporting installed"),await this.reportUpgrade({from_version:s,to_version:r,status:"installed",duration_ms:Date.now()-w}),a("shutting down for restart"),!await this.waitUntilIdle()){a("upgrade aborted: checker stopped during wait");return}this.startGuardian(),process.kill(process.pid,"SIGTERM")}catch(n){const d=n instanceof O?n.code:"NPM_INSTALL_FAILED",f=n instanceof Error?n.message:String(n);a(`upgrade failed: ${d} ${f}`),z()&&a("upgrade failed before restart; previous package restored atomically"),u(),j(f)||d==="NPM_TIMEOUT"?a(`failure is retriable (${d}); skipping cooldown, will retry next cycle`):this.recordFailure(r),await this.reportUpgrade({from_version:s,to_version:r,status:"failed",error_code:d,error_msg:f,duration_ms:Date.now()-w,upgrade_log:N()})}}async waitUntilIdle(){if(this.closeAdmission?.(),!this.isBusy?.())return!this.stopped;a("active tasks detected, waiting until idle before restart (no max wait)");const t=5e3,e=600*1e3;let i=Date.now();for(;this.isBusy()&&!this.stopped;)await new Promise(r=>setTimeout(r,t)),Date.now()-i>=e&&(i=Date.now(),a("still waiting for active tasks to finish before restart"));return this.stopped?!1:(a("all tasks completed, proceeding with restart"),!0)}checkRateLimit(t){const e=I();if(e.last_failure_at){const c=Date.now()-new Date(e.last_failure_at).getTime();if(c<T)return o.info("upgrade",`In cooldown, ${(T-c)/6e4}m remaining`),!1}const i=e.version_attempts[t]??0;if(i>=J)return o.info("upgrade",`Version ${t} already tried ${i} times, skipping`),!1;const r=U(),s=e.daily_attempts[r]??0;return s>=q?(o.info("upgrade",`Already ${s} attempts today, skipping`),!1):!0}recordFailure(t){const e=I(),i=U();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[i]=(e.daily_attempts[i]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,Q(e)}async queryUpgrade(t){const i=`${P(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:_(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(A)});if(!r.ok)return o.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const s=await r.json();return s.code!==0?{available:!1}:s.data}catch(r){return o.warn("upgrade",`Check API error: ${r instanceof Error?r.message:r}`),{available:!1}}}async reportUpgrade(t){if(this.agentConfigs.length===0)return!1;const e={client_type:"grix-connector",...t.npm_version?t:{...t,...D()}};return(await Promise.all(this.agentConfigs.map(async r=>{const c=`${P(r.wsUrl)}/v1/agent-api/upgrade/report`;try{return(await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(A)})).ok}catch{return!1}}))).some(r=>r)}startGuardian(){const t=g(m.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!h(t)){o.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=L(t,[],{detached:!0,stdio:"ignore"});e.unref(),a(`guardian started (pid ${e.pid})`)}catch(e){o.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{ct as UpgradeChecker};
1
+ import{existsSync as g,mkdirSync as M,readFileSync as v,renameSync as C,writeFileSync as R}from"node:fs";import{join as h,dirname as O}from"node:path";import{spawn as L}from"node:child_process";import{log as l}from"../log/index.js";import{GRIX_PATHS as m}from"../log/index.js";import{resolveClientVersion as _}from"../util/client-version.js";import{UpgradeError as D,cleanupPendingBackup as w,collectEnvInfo as N,getUpgradeLogTail as x,isVersionAvailable as F,npmInstall as H,pendingExists as T,preflightCheck as V,readPending as B,readPendingPackageVersion as z,removePending as p,rollbackPendingInstall as G,upgradeLog as s,verifyInstalledVersion as j,writePending as K}from"./npm-upgrader.js";import{isRetriableRegistryError as X}from"../installer/npm-registry.js";const q=360*60*1e3,J=300*1e3,k=1800*1e3,W=2,Y=3,S="grix-connector",A=1e4,b=15,Z=2e3,Q=8e3,tt=1e3,P=600*1e3;function et(o){const t=Date.parse(o.upgraded_at??"");return Number.isNaN(t)?!0:Date.now()-t>P}function I(o){return new URL(o.replace(/^wss:/,"https:").replace(/^ws:/,"http:")).origin}function E(){return h(m.data,"upgrade-state.json")}function $(){const o=E();if(!g(o))return{daily_attempts:{},version_attempts:{}};try{return JSON.parse(v(o,"utf-8"))}catch{return{daily_attempts:{},version_attempts:{}}}}function rt(o){const t=E();M(O(t),{recursive:!0});const e=t+".tmp";R(e,JSON.stringify(o),"utf-8"),C(e,t)}function U(){return new Date().toISOString().slice(0,10)}class dt{agentConfigs;isBusy;closeAdmission;timer=null;initialTimer=null;running=!1;stopped=!1;constructor(t,e,i){this.agentConfigs=t,this.isBusy=e,this.closeAdmission=i}async start(){await this.handlePendingOnStartup(),this.initialTimer=setTimeout(()=>{this.stopped||(this.runCheck(),!this.stopped&&(this.timer=setInterval(()=>this.runCheck(),q)))},J)}stop(){this.stopped=!0,this.initialTimer&&(clearTimeout(this.initialTimer),this.initialTimer=null),this.timer&&(clearInterval(this.timer),this.timer=null)}triggerCheck(){this.stopped||this.runCheck()}async checkForUpdate(){return this.agentConfigs.length===0?{available:!1}:(await Promise.all(this.agentConfigs.map(i=>this.queryUpgrade(i)))).find(i=>i.available&&i.release)??{available:!1}}async handlePendingOnStartup(){if(!T())return;const t=B();if(!t){p();return}const e=z(t),i=_(),r=!e||e===t.target_version,a=i===t.target_version&&r;if(!a&&r){if(!et(t)){s(`daemon startup: runtime ${i} is waiting for guardian activation of ${t.target_version} (active package=${e??"unknown"})`);return}s(`daemon startup: activation wait for ${t.target_version} expired (> ${P/6e4}min); no guardian will finish the switch, reporting by version verdict`)}const c=a?{from_version:t.from_version,to_version:t.target_version,status:"success"}:{from_version:t.from_version,to_version:t.target_version,status:"rolled_back",error_code:"STARTUP_CRASH",crash_count:t.crash_count};if(s(a?`daemon startup: version matches target ${t.target_version}, upgrade succeeded`:`daemon startup: runtime=${i}, active package=${e??"unknown"}, target=${t.target_version}; rolled back`),a){const u=await this.confirmHealthz(Q);s(u?"healthz confirmed healthy, reporting success":"healthz not confirmed within window; reporting success on version-match (guardian still guards crashes)")}if(await this.reportUpgrade(c)){a&&w(t),p();return}this.retryStartupReport(c,a?t:null)}async retryStartupReport(t,e){for(let i=2;i<=b;i++){if(this.stopped)return;const r=Z*(i-1)+Math.floor(Math.random()*1e3);if(await new Promise(a=>setTimeout(a,r)),this.stopped)return;if(await this.reportUpgrade(t)){w(e),p();return}}s(`startup report (${t.status}) not delivered after ${b} attempts; clearing pending to unblock future checks`),w(e),p()}async confirmHealthz(t){let e="19579";try{const a=h(m.data,"health-port");g(a)&&(e=v(a,"utf-8").trim()||e)}catch{}const i=`http://127.0.0.1:${e}/healthz`,r=Date.now()+t;for(;Date.now()<r&&!this.stopped;){try{if((await fetch(i,{signal:AbortSignal.timeout(2e3)})).ok)return!0}catch{}await new Promise(a=>setTimeout(a,tt))}return!1}async runCheck(){if(!this.running){this.running=!0;try{await this.check()}catch(t){l.error("upgrade",`Check failed: ${t instanceof Error?t.message:t}`)}finally{this.running=!1}}}async check(){if(T()||this.agentConfigs.length===0)return;const i=(await Promise.all(this.agentConfigs.map(n=>this.queryUpgrade(n)))).find(n=>n.available&&n.release)?.release;if(!i)return;const r=i.version,a=_();if(!i.force&&!this.checkRateLimit(r))return;const u=V();if(!u.ok){await this.reportUpgrade({from_version:a,to_version:r,status:"failed",error_code:u.errorCode,error_msg:u.errorMsg});return}if(!await F(S,r)){s(`target ${r} not resolvable on any registry yet; skipping this cycle (no failure recorded)`);return}s(`upgrade start: ${a} -> ${r}`);const y=Date.now();try{if(K(a,r),await H(S,r),j(r),s("npm install verified, reporting installed"),await this.reportUpgrade({from_version:a,to_version:r,status:"installed",duration_ms:Date.now()-y}),s("shutting down for restart"),!await this.waitUntilIdle()){s("upgrade aborted: checker stopped during wait");return}this.startGuardian(),process.kill(process.pid,"SIGTERM")}catch(n){const d=n instanceof D?n.code:"NPM_INSTALL_FAILED",f=n instanceof Error?n.message:String(n);s(`upgrade failed: ${d} ${f}`),G()&&s("upgrade failed before restart; previous package restored atomically"),p(),X(f)||d==="NPM_TIMEOUT"?s(`failure is retriable (${d}); skipping cooldown, will retry next cycle`):this.recordFailure(r),await this.reportUpgrade({from_version:a,to_version:r,status:"failed",error_code:d,error_msg:f,duration_ms:Date.now()-y,upgrade_log:x()})}}async waitUntilIdle(){if(this.closeAdmission?.(),!this.isBusy?.())return!this.stopped;s("active tasks detected, waiting until idle before restart (no max wait)");const t=5e3,e=600*1e3;let i=Date.now();for(;this.isBusy()&&!this.stopped;)await new Promise(r=>setTimeout(r,t)),Date.now()-i>=e&&(i=Date.now(),s("still waiting for active tasks to finish before restart"));return this.stopped?!1:(s("all tasks completed, proceeding with restart"),!0)}checkRateLimit(t){const e=$();if(e.last_failure_at){const c=Date.now()-new Date(e.last_failure_at).getTime();if(c<k)return l.info("upgrade",`In cooldown, ${(k-c)/6e4}m remaining`),!1}const i=e.version_attempts[t]??0;if(i>=Y)return l.info("upgrade",`Version ${t} already tried ${i} times, skipping`),!1;const r=U(),a=e.daily_attempts[r]??0;return a>=W?(l.info("upgrade",`Already ${a} attempts today, skipping`),!1):!0}recordFailure(t){const e=$(),i=U();e.last_failure_at=new Date().toISOString(),e.last_failure_version=t,e.daily_attempts[i]=(e.daily_attempts[i]??0)+1,e.version_attempts[t]=(e.version_attempts[t]??0)+1,rt(e)}async queryUpgrade(t){const i=`${I(t.wsUrl)}/v1/agent-api/upgrade/check?`+new URLSearchParams({client_type:"grix-connector",client_version:_(),channel:t.channel??"stable",platform:process.platform,arch:process.arch}).toString();try{const r=await fetch(i,{headers:{Authorization:`Bearer ${t.apiKey}`},signal:AbortSignal.timeout(A)});if(!r.ok)return l.warn("upgrade",`Check API returned ${r.status}`),{available:!1};const a=await r.json();return a.code!==0?{available:!1}:a.data}catch(r){return l.warn("upgrade",`Check API error: ${r instanceof Error?r.message:r}`),{available:!1}}}async reportUpgrade(t){if(this.agentConfigs.length===0)return!1;const e={client_type:"grix-connector",...t.npm_version?t:{...t,...N()}};return(await Promise.all(this.agentConfigs.map(async r=>{const c=`${I(r.wsUrl)}/v1/agent-api/upgrade/report`;try{return(await fetch(c,{method:"POST",headers:{Authorization:`Bearer ${r.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e),signal:AbortSignal.timeout(A)})).ok}catch{return!1}}))).some(r=>r)}startGuardian(){const t=h(m.base,"bin","upgrade-guardian.sh");if(process.platform==="win32"||!g(t)){l.info("upgrade","Guardian not available on this platform, skipping");return}try{const e=L(t,[],{detached:!0,stdio:"ignore"});e.unref(),s(`guardian started (pid ${e.pid})`)}catch(e){l.warn("upgrade",`Failed to start guardian: ${e instanceof Error?e.message:e}`)}}}export{dt as UpgradeChecker};
@@ -1 +1 @@
1
- const r=100;function c(t){const e=t.replace(/\[\[message_id:[^\]]+\]\]/g,"").replace(/\s+/g," ").trim();if(e)return e.length>100?e.slice(0,100)+"\u2026":e}function l(t){const e=t.replace(/\[system-identity\][\s\S]*?\[\/system-identity\]/g,""),n=[...e.matchAll(/\[\[message_id:[^\]]+\]\]/g)];if(n.length===0)return e;const s=n[n.length-1],i=e.slice((s.index??0)+s[0].length);return i.trim()?i:e}export{c as makeTitle,l as stripInjectedPreamble};
1
+ const s=100;function i(t){const e=n(t).replace(/\s+/g," ").trim();if(e)return e.length>100?e.slice(0,100)+"\u2026":e}function n(t){let e=String(t??"").replace(/\[(system-identity|system-context|grix-message-protocol|grix-system-instructions|recovery-context)\][\s\S]*?\[\/\1\]/gi,"").replace(/^[ \t]*\[grix protocol\][^\r\n]*(?:\r?\n)?/gim,"").replace(/\[\[(?:quoted_)?message_id:[^\]\r\n]+\]\]/g,"").replace(/\n+(?:\[Conversation context\]|Conversation context \(JSON\):|\[User answered a previous agent question\]|\[Previous conversation summary[^\]]*\])[\s\S]*$/i,"");const r=/(?:^|\n)(?:Latest user message:|\[Current user message\]|\[当前用户消息\])\s*\n([\s\S]*)$/i.exec(e);return r&&(e=r[1]),e.trim()}export{i as makeTitle,n as stripInjectedPreamble};