grix-connector 3.33.2 → 3.33.3

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 (39) hide show
  1. package/dist/adapter/acp/acp-adapter.js +2 -2
  2. package/dist/adapter/agy/agy-adapter.js +5 -5
  3. package/dist/adapter/agy/model-list.js +2 -2
  4. package/dist/adapter/claude/claude-adapter.js +5 -5
  5. package/dist/adapter/claude/model-list.js +1 -1
  6. package/dist/adapter/codewhale/codewhale-adapter.js +2 -2
  7. package/dist/adapter/codewhale/model-list.js +2 -2
  8. package/dist/adapter/codex/codex-bridge.js +1 -1
  9. package/dist/adapter/cursor/cursor-adapter.js +7 -7
  10. package/dist/adapter/cursor/fetch-models.js +2 -2
  11. package/dist/adapter/cursor/resolve-command.js +1 -1
  12. package/dist/adapter/openhuman/openhuman-adapter.js +3 -3
  13. package/dist/assets/dsh-bridge/{grix-dsh-bridge-3.33.2.tgz → grix-dsh-bridge-3.33.3.tgz} +0 -0
  14. package/dist/assets/dsh-bridge/manifest.json +5 -5
  15. package/dist/audit/adapters/openclaw/openclaw-audit-adapter.js +1 -1
  16. package/dist/audit/adapters/pi/pi-transcript-source.js +1 -1
  17. package/dist/audit/core/transcript-fingerprint.js +1 -0
  18. package/dist/audit/producer/ipc-audit-producer.js +1 -1
  19. package/dist/bridge/adapter-pool.js +1 -1
  20. package/dist/bridge/bridge.js +9 -9
  21. package/dist/bridge/cli-install-offer.js +1 -0
  22. package/dist/bridge/respawn-manager.js +1 -1
  23. package/dist/core/files/file-serve.js +1 -1
  24. package/dist/core/installer/installer.js +6 -6
  25. package/dist/core/installer/npm-registry.js +3 -3
  26. package/dist/core/installer/prereq-installer.js +1 -1
  27. package/dist/core/proxy/hermes-profile-relay.js +1 -1
  28. package/dist/core/runtime/spawn.js +3 -3
  29. package/dist/core/upgrade/npm-upgrader.js +2 -2
  30. package/dist/core/util/cli-probe.js +2 -2
  31. package/dist/core/util/git-project-root.js +1 -1
  32. package/dist/default-skills/grix-egg/SKILL.md +2 -2
  33. package/dist/default-skills/message-send/SKILL.md +1 -1
  34. package/dist/default-skills/message-unsend/SKILL.md +1 -1
  35. package/dist/manager.js +1 -1
  36. package/dist/service/process-control.js +1 -1
  37. package/openclaw-plugin/audit-recorder-worker.js +170 -44
  38. package/openclaw-plugin/index.js +88 -14
  39. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import{EventEmitter as f}from"node:events";import{stat as _}from"node:fs/promises";import{mkdirSync as E}from"node:fs";import{homedir as u}from"node:os";import{join as p,resolve as k}from"node:path";import{fileURLToPath as S}from"node:url";import{randomUUID as w}from"node:crypto";import{resolveCommandPath as x,spawnCommand as b,killProcessGroup as v,hasChildProcesses as y}from"../../core/runtime/spawn.js";import{InternalApiServer as $}from"../../core/mcp/internal-api-server.js";import{InboundMessageIdInjector as A}from"../shared/inbound-message-id-injector.js";import{syncDefaultSkillsToDir as C}from"../../default-skills/index.js";import{buildSimpleProbeReport as M}from"../shared/probe-util.js";import{resolveMcpServerName as P,LEGACY_MCP_SERVER_NAMES as L}from"../../core/mcp/server-name.js";import{OpenHumanTransport as D,readBearerToken as N}from"./openhuman-transport.js";import{IdentityInjector as F}from"../shared/identity-injector.js";import{log as n}from"../../core/log/index.js";import{splitTextForAibotProtocol as B}from"../../core/protocol/index.js";class O extends f{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitError(t){if(this.listenerCount("error")===0){n.warn("openhuman-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const U=200,j=2e3,q=12e4,m=1800*1e3,H=5e3,X=500,T=3e4,G=7788,W="127.0.0.1";class dt extends f{type="openhuman";config;callbacks;options;identity;inboundMessageIds=new A;process=null;transport=new D;alive=!1;stopped=!1;internalApi=null;clientId;activeRun=null;completedEvents=new Set;clientMsgSeq=0;idleTimer=null;lastProgressAt=0;constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.options=s??{},this.clientId=`grix-connector-${Date.now()}`,this.identity=new F("openhuman-adapter",e.getAgentProfile)}async start(){await this.startInternalApiAndInjectMcp();const t=this.options.host??W,e=this.options.port??G;await this.spawnProcess(t,e),await this.waitForServerReady(t,e);const s=this.resolveWorkspaceDir(),i=await N(s),o=`http://${t}:${e}`;if(await this.transport.connect(o,i),this.transport.on("event",a=>this.handleSocketEvent(a)),this.options.sessionToken)try{await this.transport.storeSession({token:this.options.sessionToken}),n.info("openhuman-adapter","Session token stored")}catch(a){n.warn("openhuman-adapter",`Failed to store session token: ${a}`)}this.transport.socketId&&(this.clientId=this.transport.socketId),n.info("openhuman-adapter",`Ready (pid=${this.process?.pid}, clientId=${this.clientId})`)}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.transport.disconnect(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.process){const t=this.process;try{v(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{v(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){return this.clientId}async resumeSession(t,e){}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.inboundMessageIds.forgetSession(t)}sendPrompt(t){const e=new O(t.adapterSessionId),s=this.buildPromptText(t);return this.doWebChat(t.adapterSessionId,s).then(i=>{this.activeRun&&(this.activeRun.requestId=i.request_id)}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){if(this.activeRun)try{await this.transport.webCancel({client_id:this.clientId,thread_id:this.activeRun.threadId})}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t;if(this.completedEvents.has(e)){n.info("openhuman-adapter",`Dropping duplicate event ${e}`),this.callbacks.sendEventAck(e,s),this.callbacks.sendEventResult(e,"responded");return}if(!this.alive){n.warn("openhuman-adapter",`Dropping event ${e}: process not alive`),this.callbacks.sendEventAck(e,s),this.callbacks.sendEventResult(e,"failed","Agent process not running");return}this.activeRun&&this.activeRun.eventId!==e&&(n.info("openhuman-adapter",`steer: ${this.activeRun.eventId} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(this.activeRun.eventId,"canceled","steered to new event"),this.clearRun());const o=this.buildPromptTextFromEvent(t);n.info("openhuman-adapter",`prompt: event=${e} session=${s}`),this.callbacks.sendEventAck(e,s),this.startRun(e,s,s),this.startComposing(s,e),this.resetIdleTimer(),this.doWebChat(s,o).then(a=>{this.activeRun&&this.activeRun.eventId===e&&(this.activeRun.requestId=a.request_id,a.accepted||(n.warn("openhuman-adapter",`web_chat not accepted: ${a.request_id}`),this.finishRun("failed","Chat request not accepted")))}).catch(a=>{n.error("openhuman-adapter",`web_chat failed: ${a}`),this.finishRun("failed",String(a))})}deliverStopEvent(t,e){this.activeRun&&this.activeRun.eventId===t&&(n.info("openhuman-adapter",`stop: event=${t}`),this.transport.webCancel({client_id:this.clientId,thread_id:this.activeRun.threadId}).catch(()=>{}),this.flushTextBuffer(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(t){}async ping(t){return this.transport.healthCheck()}getStatus(){return{alive:this.alive,busy:this.activeRun!==null,sessions:this.activeRun?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.stopIdleTimer(),this.stopTextFlush(),this.activeRun=null}getMcpConfig(){if(!this.internalApi)return null;const t=k(S(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:P(this.callbacks.getAgentId?.()),command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?y(t,[t]):!1}async probe(t){const e=this.getStatus();return M(this.config.command||"openhuman-core",{alive:e.alive,busy:e.busy,started:!!this.process},t)}async startInternalApiAndInjectMcp(){try{this.internalApi=new $,this.internalApi.setInvokeHandler(async(d,I,J,g)=>this.callbacks.agentInvoke(d,I,g)),await this.internalApi.start(0),n.info("openhuman-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=p(u(),".openhuman","users","local","workspace","mcp_clients"),s=p(e,"mcp_clients.db");E(e,{recursive:!0});const{execFileSync:i}=await import("node:child_process"),o=w(),a=JSON.stringify(t.args),c=["CREATE TABLE IF NOT EXISTS mcp_servers (server_id TEXT PRIMARY KEY, qualified_name TEXT NOT NULL, display_name TEXT NOT NULL, description TEXT, icon_url TEXT, command_kind TEXT NOT NULL DEFAULT 'node', command TEXT NOT NULL, args_json TEXT NOT NULL DEFAULT '[]', env_keys_json TEXT NOT NULL DEFAULT '[]', config_json TEXT, installed_at INTEGER NOT NULL, last_connected_at INTEGER);",`DELETE FROM mcp_servers WHERE qualified_name IN (${[...L,t.name].map(d=>`'${d}'`).join(", ")});`,`INSERT INTO mcp_servers (server_id, qualified_name, display_name, description, command_kind, command, args_json, installed_at) VALUES ('${o}', '${t.name}', 'Grix Connector Tools', 'Grix platform query and management tools', 'node', '${t.command}', '${a.replace(/'/g,"''")}', ${Date.now()});`].join(`
2
- `);i("sqlite3",[s,c],{timeout:1e4,stdio:"ignore"}),n.info("openhuman-adapter",`MCP server registered in SQLite: ${s}`);const l=p(u(),".openhuman","skills"),r=C(l);r.length>0&&n.info("openhuman-adapter",`Synced connector skills to ${l}: [${r.join(", ")}]`)}catch(t){n.warn("openhuman-adapter",`Failed to inject MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnProcess(t,e){const s=this.config.command||"openhuman-core",i=x(s,typeof process.env.PATH=="string"?process.env.PATH:void 0),o=this.config.args??[],h=o.includes("run")||o.includes("serve")?o:["run","--host",t,"--port",String(e),...o],c={...process.env,...this.config.env},l=this.resolveCwd();n.info("openhuman-adapter",`Spawning: ${i} ${h.join(" ")}`);try{if(!(await _(l)).isDirectory())throw new Error(`Bound path is not a directory: ${l}`)}catch(r){throw String(r?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${l}. Please rebind with /grix open <valid-directory>.`):r}try{this.process=b(i,h,{env:c,cwd:l}).process}catch(r){throw n.error("openhuman-adapter",`Spawn threw: ${r}`),this.alive=!1,r}this.process.on("error",r=>{n.error("openhuman-adapter",`Spawn error: ${r.message}`),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Spawn error: ${r.message}`),this.clearRun()),this.stopped||this.emit("exit",1)}),this.process.on("exit",r=>{n.info("openhuman-adapter",`Process exited (code=${r})`),this.alive=!1,this.transport.disconnect(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Process exited (code=${r})`),this.clearRun()),this.stopped||this.emit("exit",r??1)}),this.process.stderr?.on("data",r=>{const d=r.toString().trim();d&&n.info("openhuman-adapter",`[stderr] ${d}`)}),this.alive=!0}async waitForServerReady(t,e){const s=`http://${t}:${e}/health`,i=Date.now()+T;for(;Date.now()<i;){try{if((await fetch(s,{signal:AbortSignal.timeout(2e3)})).ok)return}catch{}await new Promise(o=>setTimeout(o,X))}throw new Error(`openhuman-core did not become ready at ${t}:${e} after ${T/1e3}s`)}async doWebChat(t,e){return this.transport.webChat({client_id:this.clientId,thread_id:t,message:e})}handleSocketEvent(t){if(this.stopped||!this.activeRun||t.request_id&&this.activeRun.requestId&&t.request_id!==this.activeRun.requestId)return;this.resetIdleTimer();const e=t.event;switch(e){case"text_delta":{t.delta&&t.delta_kind==="text"&&this.appendText(t.delta);break}case"thinking_delta":{t.delta&&t.delta_kind==="thinking"&&this.activeRun&&this.callbacks.sendThinking(this.activeRun.eventId,this.activeRun.sessionId,t.delta);break}case"tool_call":{if(this.flushTextBuffer(),this.activeRun&&t.tool_name){const s=typeof t.args=="string"?t.args:JSON.stringify(t.args??{});this.callbacks.sendToolUse(this.activeRun.eventId,this.activeRun.sessionId,t.tool_name,s)}break}case"tool_result":{if(this.activeRun&&t.tool_name){const s=t.output??"";this.callbacks.sendToolResult(this.activeRun.eventId,this.activeRun.sessionId,t.tool_name,s)}break}case"chat_done":{n.info("openhuman-adapter",`chat_done request=${t.request_id}`),this.activeRun&&this.activeRun.textBuffer.length===0&&t.full_response&&(this.activeRun.textBuffer=t.full_response),this.flushTextBuffer(),this.finishRun("responded");break}case"chat_error":{if(n.error("openhuman-adapter",`chat_error: ${t.error_type} ${t.message}`),this.flushTextBuffer(),this.activeRun){const s=t.message??t.error_type??"unknown error";this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,s)}this.finishRun("failed",t.message);break}case"chat_segment":case"inference_start":case"iteration_start":case"chat_accepted":break;default:{n.debug("openhuman-adapter",`Unhandled event: ${e}`);break}}}startRun(t,e,s){this.activeRun={eventId:t,sessionId:e,requestId:"",threadId:s,chunkSeq:0,clientMsgId:`oh_${++this.clientMsgSeq}_${Date.now()}`,textBuffer:"",flushTimer:null}}finishRun(t,e){const s=this.activeRun;if(!s)return;if(this.completedEvents.add(s.eventId),this.completedEvents.size>1e3){const a=this.completedEvents.values();for(let c=0;c<500;c++)a.next();const h=[...this.completedEvents].slice(-500);this.completedEvents=new Set(h)}this.activeRun=null,this.emit("eventDone",s.eventId),this.stopComposing(),this.stopIdleTimer();const i=++s.chunkSeq,o=s.clientMsgId;t==="failed"&&e&&this.callbacks.sendRunError(s.eventId,s.sessionId,e),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(s.eventId,s.sessionId,i,o).then(()=>{this.callbacks.sendEventResult(s.eventId,t,e)}).catch(a=>{const h=t==="responded"?"failed":t,c=t==="responded"?`final output delivery failed: ${a instanceof Error?a.message:String(a)}`:e;this.callbacks.sendEventResult(s.eventId,h,c)}):(this.callbacks.sendStreamChunk(s.eventId,s.sessionId,"",i,!0,o),this.callbacks.sendEventResult(s.eventId,t,e))}clearRun(){this.activeRun?.flushTimer&&clearTimeout(this.activeRun.flushTimer);const t=this.activeRun?.eventId;this.activeRun=null,this.lastProgressAt=0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}appendText(t){if(this.activeRun){if(this.activeRun.textBuffer+=t,this.activeRun.textBuffer.length>=j){this.flushTextBuffer();return}this.scheduleTextFlush()}}scheduleTextFlush(){!this.activeRun||this.activeRun.flushTimer||(this.activeRun.flushTimer=setTimeout(()=>{this.activeRun&&(this.activeRun.flushTimer=null),this.flushTextBuffer()},U))}flushTextBuffer(){if(this.stopTextFlush(),!(!this.activeRun||!this.activeRun.textBuffer)){for(const t of B(this.activeRun.textBuffer))this.activeRun.chunkSeq++,this.callbacks.sendStreamChunk(this.activeRun.eventId,this.activeRun.sessionId,t,this.activeRun.chunkSeq,!1,this.activeRun.clientMsgId);this.activeRun.textBuffer=""}}stopTextFlush(){this.activeRun?.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null)}startComposing(t,e){}stopComposing(){}resetIdleTimer(){this.lastProgressAt=Date.now(),this.armIdleTimer()}armIdleTimer(){if(this.stopIdleTimer(),this.stopped||!this.activeRun)return;const t=this.lastProgressAt+m-Date.now(),e=Math.max(0,Math.min(q,t)),s=this.activeRun.eventId;this.idleTimer=setTimeout(()=>{this.onIdleTimeout(s)},e)}async onIdleTimeout(t){if(this.stopped||this.activeRun?.eventId!==t)return;if(Date.now()-this.lastProgressAt>=m){this.failIdleRun(t,`liveness budget exhausted after ${m/6e4}min`);return}let s=!1;try{s=await this.ping(H)}catch{s=!1}if(!(this.stopped||this.activeRun?.eventId!==t)){if(s){n.info("openhuman-adapter",`Idle timer: transport healthy, extending until liveness cap (${m/6e4}min): ${t}`),this.armIdleTimer();return}this.failIdleRun(t,"transport unavailable")}}failIdleRun(t,e){this.activeRun?.eventId===t&&(n.error("openhuman-adapter",`Idle timeout (${e}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.callbacks.sendEventResult(t,"failed",`idle timeout: ${e}`),this.clearRun(),this.emit("exit",-1))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}resolveWorkspaceDir(){return this.options.workspaceDir?this.options.workspaceDir.replace(/^~/,u()):p(u(),".openhuman")}resolveCwd(){const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}buildPromptText(t){let e=t.text;return t.contextMessages&&t.contextMessages.length>0&&(e=t.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
1
+ import{EventEmitter as f}from"node:events";import{stat as _}from"node:fs/promises";import{mkdirSync as E}from"node:fs";import{homedir as u}from"node:os";import{join as p,resolve as k}from"node:path";import{fileURLToPath as w}from"node:url";import{randomUUID as S}from"node:crypto";import{resolveCommandPath as x,spawnCommand as b,killProcessGroup as v,hasChildProcesses as y}from"../../core/runtime/spawn.js";import{InternalApiServer as $}from"../../core/mcp/internal-api-server.js";import{InboundMessageIdInjector as A}from"../shared/inbound-message-id-injector.js";import{syncDefaultSkillsToDir as C}from"../../default-skills/index.js";import{buildSimpleProbeReport as M}from"../shared/probe-util.js";import{resolveMcpServerName as P,LEGACY_MCP_SERVER_NAMES as L}from"../../core/mcp/server-name.js";import{OpenHumanTransport as D,readBearerToken as N}from"./openhuman-transport.js";import{IdentityInjector as F}from"../shared/identity-injector.js";import{log as n}from"../../core/log/index.js";import{splitTextForAibotProtocol as B}from"../../core/protocol/index.js";class O extends f{adapterSessionId;constructor(t){super(),this.adapterSessionId=t}emitError(t){if(this.listenerCount("error")===0){n.warn("openhuman-adapter",`Prompt handle error (no listeners): ${t.message}`);return}this.emit("error",t)}async cancel(){}}const U=200,j=2e3,q=12e4,m=1800*1e3,H=5e3,X=500,T=3e4,G=7788,W="127.0.0.1";class dt extends f{type="openhuman";config;callbacks;options;identity;inboundMessageIds=new A;process=null;transport=new D;alive=!1;stopped=!1;internalApi=null;clientId;activeRun=null;completedEvents=new Set;clientMsgSeq=0;idleTimer=null;lastProgressAt=0;constructor(t,e,s){super(),this.config=t,this.callbacks=e,this.options=s??{},this.clientId=`grix-connector-${Date.now()}`,this.identity=new F("openhuman-adapter",e.getAgentProfile)}async start(){await this.startInternalApiAndInjectMcp();const t=this.options.host??W,e=this.options.port??G;await this.spawnProcess(t,e),await this.waitForServerReady(t,e);const s=this.resolveWorkspaceDir(),i=await N(s),o=`http://${t}:${e}`;if(await this.transport.connect(o,i),this.transport.on("event",r=>this.handleSocketEvent(r)),this.options.sessionToken)try{await this.transport.storeSession({token:this.options.sessionToken}),n.info("openhuman-adapter","Session token stored")}catch(r){n.warn("openhuman-adapter",`Failed to store session token: ${r}`)}this.transport.socketId&&(this.clientId=this.transport.socketId),n.info("openhuman-adapter",`Ready (pid=${this.process?.pid}, clientId=${this.clientId})`)}async stop(){if(this.stopped=!0,this.alive=!1,this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.transport.disconnect(),this.internalApi&&(await this.internalApi.stop(),this.internalApi=null),this.process){const t=this.process;try{v(t,"SIGTERM")}catch{}const e=setTimeout(()=>{try{v(t,"SIGKILL")}catch{}},5e3);t.on("exit",()=>clearTimeout(e)),this.process=null}}isAlive(){return this.alive}async createSession(t){return this.clientId}async resumeSession(t,e){}onAgentProfileChanged(){this.identity.onProfileChanged()}async destroySession(t){this.identity.forgetSession(t),this.inboundMessageIds.forgetSession(t)}sendPrompt(t){const e=new O(t.adapterSessionId),s=this.buildPromptText(t);return this.doWebChat(t.adapterSessionId,s).then(i=>{this.activeRun&&(this.activeRun.requestId=i.request_id)}).catch(i=>{e.emitError(i instanceof Error?i:new Error(String(i)))}),e}async cancel(t){if(this.activeRun)try{await this.transport.webCancel({client_id:this.clientId,thread_id:this.activeRun.threadId})}catch{}}deliverInboundEvent(t){const{event_id:e,session_id:s,content:i}=t;if(this.completedEvents.has(e)){n.info("openhuman-adapter",`Dropping duplicate event ${e}`),this.callbacks.sendEventAck(e,s),this.callbacks.sendEventResult(e,"responded");return}if(!this.alive){n.warn("openhuman-adapter",`Dropping event ${e}: process not alive`),this.callbacks.sendEventAck(e,s),this.callbacks.sendEventResult(e,"failed","Agent process not running");return}this.activeRun&&this.activeRun.eventId!==e&&(n.info("openhuman-adapter",`steer: ${this.activeRun.eventId} -> ${e}`),this.flushTextBuffer(),this.callbacks.sendEventResult(this.activeRun.eventId,"canceled","steered to new event"),this.clearRun());const o=this.buildPromptTextFromEvent(t);n.info("openhuman-adapter",`prompt: event=${e} session=${s}`),this.callbacks.sendEventAck(e,s),this.startRun(e,s,s),this.startComposing(s,e),this.resetIdleTimer(),this.doWebChat(s,o).then(r=>{this.activeRun&&this.activeRun.eventId===e&&(this.activeRun.requestId=r.request_id,r.accepted||(n.warn("openhuman-adapter",`web_chat not accepted: ${r.request_id}`),this.finishRun("failed","Chat request not accepted")))}).catch(r=>{n.error("openhuman-adapter",`web_chat failed: ${r}`),this.finishRun("failed",String(r))})}deliverStopEvent(t,e){this.activeRun&&this.activeRun.eventId===t&&(n.info("openhuman-adapter",`stop: event=${t}`),this.transport.webCancel({client_id:this.clientId,thread_id:this.activeRun.threadId}).catch(()=>{}),this.flushTextBuffer(),this.finishRun("canceled","stopped by user"))}setPermissionHandler(t){}async ping(t){return this.transport.healthCheck()}getStatus(){return{alive:this.alive,busy:this.activeRun!==null,sessions:this.activeRun?1:0}}getActiveEventIds(){return this.activeRun?[this.activeRun.eventId]:[]}clearActiveEventForShutdown(){this.stopIdleTimer(),this.stopTextFlush(),this.activeRun=null}getMcpConfig(){if(!this.internalApi)return null;const t=k(w(import.meta.url),"../../../mcp/acp-mcp-server.js");return{name:P(this.callbacks.getAgentId?.()),command:process.execPath,args:[t,"--api-url",this.internalApi.url]}}async hasBackgroundWork(){const t=this.process?.pid;return t?y(t,[t]):!1}async probe(t){const e=this.getStatus();return M(this.config.command||"openhuman-core",{alive:e.alive,busy:e.busy,started:!!this.process},t)}async startInternalApiAndInjectMcp(){try{this.internalApi=new $,this.internalApi.setInvokeHandler(async(d,I,J,g)=>this.callbacks.agentInvoke(d,I,g)),await this.internalApi.start(0),n.info("openhuman-adapter",`Internal API started at ${this.internalApi.url}`);const t=this.getMcpConfig(),e=p(u(),".openhuman","users","local","workspace","mcp_clients"),s=p(e,"mcp_clients.db");E(e,{recursive:!0});const{execFileSync:i}=await import("node:child_process"),o=S(),r=JSON.stringify(t.args),c=["CREATE TABLE IF NOT EXISTS mcp_servers (server_id TEXT PRIMARY KEY, qualified_name TEXT NOT NULL, display_name TEXT NOT NULL, description TEXT, icon_url TEXT, command_kind TEXT NOT NULL DEFAULT 'node', command TEXT NOT NULL, args_json TEXT NOT NULL DEFAULT '[]', env_keys_json TEXT NOT NULL DEFAULT '[]', config_json TEXT, installed_at INTEGER NOT NULL, last_connected_at INTEGER);",`DELETE FROM mcp_servers WHERE qualified_name IN (${[...L,t.name].map(d=>`'${d}'`).join(", ")});`,`INSERT INTO mcp_servers (server_id, qualified_name, display_name, description, command_kind, command, args_json, installed_at) VALUES ('${o}', '${t.name}', 'Grix Connector Tools', 'Grix platform query and management tools', 'node', '${t.command}', '${r.replace(/'/g,"''")}', ${Date.now()});`].join(`
2
+ `);i("sqlite3",[s,c],{timeout:1e4,stdio:"ignore",windowsHide:!0}),n.info("openhuman-adapter",`MCP server registered in SQLite: ${s}`);const l=p(u(),".openhuman","skills"),a=C(l);a.length>0&&n.info("openhuman-adapter",`Synced connector skills to ${l}: [${a.join(", ")}]`)}catch(t){n.warn("openhuman-adapter",`Failed to inject MCP tools (non-fatal): ${t instanceof Error?t.message:String(t)}`)}}async spawnProcess(t,e){const s=this.config.command||"openhuman-core",i=x(s,typeof process.env.PATH=="string"?process.env.PATH:void 0),o=this.config.args??[],h=o.includes("run")||o.includes("serve")?o:["run","--host",t,"--port",String(e),...o],c={...process.env,...this.config.env},l=this.resolveCwd();n.info("openhuman-adapter",`Spawning: ${i} ${h.join(" ")}`);try{if(!(await _(l)).isDirectory())throw new Error(`Bound path is not a directory: ${l}`)}catch(a){throw String(a?.code??"")==="ENOENT"?new Error(`Bound directory does not exist: ${l}. Please rebind with /grix open <valid-directory>.`):a}try{this.process=b(i,h,{env:c,cwd:l}).process}catch(a){throw n.error("openhuman-adapter",`Spawn threw: ${a}`),this.alive=!1,a}this.process.on("error",a=>{n.error("openhuman-adapter",`Spawn error: ${a.message}`),this.alive=!1,this.transport.disconnect(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Spawn error: ${a.message}`),this.clearRun()),this.stopped||this.emit("exit",1)}),this.process.on("exit",a=>{n.info("openhuman-adapter",`Process exited (code=${a})`),this.alive=!1,this.transport.disconnect(),this.stopComposing(),this.stopIdleTimer(),this.stopTextFlush(),this.activeRun&&(this.callbacks.sendEventResult(this.activeRun.eventId,"failed",`Process exited (code=${a})`),this.clearRun()),this.stopped||this.emit("exit",a??1)}),this.process.stderr?.on("data",a=>{const d=a.toString().trim();d&&n.info("openhuman-adapter",`[stderr] ${d}`)}),this.alive=!0}async waitForServerReady(t,e){const s=`http://${t}:${e}/health`,i=Date.now()+T;for(;Date.now()<i;){try{if((await fetch(s,{signal:AbortSignal.timeout(2e3)})).ok)return}catch{}await new Promise(o=>setTimeout(o,X))}throw new Error(`openhuman-core did not become ready at ${t}:${e} after ${T/1e3}s`)}async doWebChat(t,e){return this.transport.webChat({client_id:this.clientId,thread_id:t,message:e})}handleSocketEvent(t){if(this.stopped||!this.activeRun||t.request_id&&this.activeRun.requestId&&t.request_id!==this.activeRun.requestId)return;this.resetIdleTimer();const e=t.event;switch(e){case"text_delta":{t.delta&&t.delta_kind==="text"&&this.appendText(t.delta);break}case"thinking_delta":{t.delta&&t.delta_kind==="thinking"&&this.activeRun&&this.callbacks.sendThinking(this.activeRun.eventId,this.activeRun.sessionId,t.delta);break}case"tool_call":{if(this.flushTextBuffer(),this.activeRun&&t.tool_name){const s=typeof t.args=="string"?t.args:JSON.stringify(t.args??{});this.callbacks.sendToolUse(this.activeRun.eventId,this.activeRun.sessionId,t.tool_name,s)}break}case"tool_result":{if(this.activeRun&&t.tool_name){const s=t.output??"";this.callbacks.sendToolResult(this.activeRun.eventId,this.activeRun.sessionId,t.tool_name,s)}break}case"chat_done":{n.info("openhuman-adapter",`chat_done request=${t.request_id}`),this.activeRun&&this.activeRun.textBuffer.length===0&&t.full_response&&(this.activeRun.textBuffer=t.full_response),this.flushTextBuffer(),this.finishRun("responded");break}case"chat_error":{if(n.error("openhuman-adapter",`chat_error: ${t.error_type} ${t.message}`),this.flushTextBuffer(),this.activeRun){const s=t.message??t.error_type??"unknown error";this.callbacks.sendRunError(this.activeRun.eventId,this.activeRun.sessionId,s)}this.finishRun("failed",t.message);break}case"chat_segment":case"inference_start":case"iteration_start":case"chat_accepted":break;default:{n.debug("openhuman-adapter",`Unhandled event: ${e}`);break}}}startRun(t,e,s){this.activeRun={eventId:t,sessionId:e,requestId:"",threadId:s,chunkSeq:0,clientMsgId:`oh_${++this.clientMsgSeq}_${Date.now()}`,textBuffer:"",flushTimer:null}}finishRun(t,e){const s=this.activeRun;if(!s)return;if(this.completedEvents.add(s.eventId),this.completedEvents.size>1e3){const r=this.completedEvents.values();for(let c=0;c<500;c++)r.next();const h=[...this.completedEvents].slice(-500);this.completedEvents=new Set(h)}this.activeRun=null,this.emit("eventDone",s.eventId),this.stopComposing(),this.stopIdleTimer();const i=++s.chunkSeq,o=s.clientMsgId;t==="failed"&&e&&this.callbacks.sendRunError(s.eventId,s.sessionId,e),this.callbacks.sendFinalStreamChunkReliable?this.callbacks.sendFinalStreamChunkReliable(s.eventId,s.sessionId,i,o).then(()=>{this.callbacks.sendEventResult(s.eventId,t,e)}).catch(r=>{const h=t==="responded"?"failed":t,c=t==="responded"?`final output delivery failed: ${r instanceof Error?r.message:String(r)}`:e;this.callbacks.sendEventResult(s.eventId,h,c)}):(this.callbacks.sendStreamChunk(s.eventId,s.sessionId,"",i,!0,o),this.callbacks.sendEventResult(s.eventId,t,e))}clearRun(){this.activeRun?.flushTimer&&clearTimeout(this.activeRun.flushTimer);const t=this.activeRun?.eventId;this.activeRun=null,this.lastProgressAt=0,this.stopIdleTimer(),t&&this.emit("eventDone",t)}appendText(t){if(this.activeRun){if(this.activeRun.textBuffer+=t,this.activeRun.textBuffer.length>=j){this.flushTextBuffer();return}this.scheduleTextFlush()}}scheduleTextFlush(){!this.activeRun||this.activeRun.flushTimer||(this.activeRun.flushTimer=setTimeout(()=>{this.activeRun&&(this.activeRun.flushTimer=null),this.flushTextBuffer()},U))}flushTextBuffer(){if(this.stopTextFlush(),!(!this.activeRun||!this.activeRun.textBuffer)){for(const t of B(this.activeRun.textBuffer))this.activeRun.chunkSeq++,this.callbacks.sendStreamChunk(this.activeRun.eventId,this.activeRun.sessionId,t,this.activeRun.chunkSeq,!1,this.activeRun.clientMsgId);this.activeRun.textBuffer=""}}stopTextFlush(){this.activeRun?.flushTimer&&(clearTimeout(this.activeRun.flushTimer),this.activeRun.flushTimer=null)}startComposing(t,e){}stopComposing(){}resetIdleTimer(){this.lastProgressAt=Date.now(),this.armIdleTimer()}armIdleTimer(){if(this.stopIdleTimer(),this.stopped||!this.activeRun)return;const t=this.lastProgressAt+m-Date.now(),e=Math.max(0,Math.min(q,t)),s=this.activeRun.eventId;this.idleTimer=setTimeout(()=>{this.onIdleTimeout(s)},e)}async onIdleTimeout(t){if(this.stopped||this.activeRun?.eventId!==t)return;if(Date.now()-this.lastProgressAt>=m){this.failIdleRun(t,`liveness budget exhausted after ${m/6e4}min`);return}let s=!1;try{s=await this.ping(H)}catch{s=!1}if(!(this.stopped||this.activeRun?.eventId!==t)){if(s){n.info("openhuman-adapter",`Idle timer: transport healthy, extending until liveness cap (${m/6e4}min): ${t}`),this.armIdleTimer();return}this.failIdleRun(t,"transport unavailable")}}failIdleRun(t,e){this.activeRun?.eventId===t&&(n.error("openhuman-adapter",`Idle timeout (${e}) \u2014 emitting exit for respawn`),this.flushTextBuffer(),this.callbacks.sendEventResult(t,"failed",`idle timeout: ${e}`),this.clearRun(),this.emit("exit",-1))}stopIdleTimer(){this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=null)}resolveWorkspaceDir(){return this.options.workspaceDir?this.options.workspaceDir.replace(/^~/,u()):p(u(),".openhuman")}resolveCwd(){const t=(this.config.options??{}).cwd;return typeof t=="string"&&t?t:process.cwd()}buildPromptText(t){let e=t.text;return t.contextMessages&&t.contextMessages.length>0&&(e=t.contextMessages.map(i=>`[context] ${i.senderId}: ${i.content}`).join(`
3
3
  `)+`
4
4
 
5
- `+e),this.identity.injectOnce(t.adapterSessionId,e)}buildPromptTextFromEvent(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(a=>`[context] ${a.sender_id??"unknown"}: ${a.content}`).join(`
5
+ `+e),this.identity.injectOnce(t.adapterSessionId,e)}buildPromptTextFromEvent(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(`
6
6
  `)+`
7
7
 
8
8
  `+e)}catch{}const s=this.identity.injectOnce(t.session_id,e);return this.inboundMessageIds.inject(t.session_id,s,t.msg_id)}}export{dt as OpenHumanAdapter};
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "bridgeVersion": "3.33.2",
4
- "tarball": "grix-dsh-bridge-3.33.2.tgz",
5
- "size": 22013,
3
+ "bridgeVersion": "3.33.3",
4
+ "tarball": "grix-dsh-bridge-3.33.3.tgz",
5
+ "size": 22014,
6
6
  "unpackedSize": 87635,
7
- "shasum": "cd2abdd5139b7dbcbde5b80d303235312356fa30",
8
- "integrity": "sha512-ORbFYbP6RS6srGdsdKZSWAbOMLg4eAbTeVnQnVvIyvslv7HqvV0RDWX1kVfUVkcQzljf/PFIG+yONytagU04hA=="
7
+ "shasum": "41d351656451058f01d28e7ace0a8ec9b5674f96",
8
+ "integrity": "sha512-2iytp+1QkHrLu3AySM2YEXs6UXCBZ5UNQvyC42XCweszjOSpkS4MUEF6wbZrBd2lzTFrUdrGT8KunUhMIX2YUg=="
9
9
  }
@@ -1 +1 @@
1
- import{readFile as o}from"node:fs/promises";import{dirname as p,join as c}from"node:path";import{PiAuditAdapter as m}from"../pi/pi-audit-adapter.js";import{collectPiTranscript as u}from"../pi/pi-transcript-source.js";function a(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}async function l(t){if(typeof t.transcriptPath=="string")return t;const e=typeof t.sessionStorePath=="string"?t.sessionStorePath:void 0,r=typeof t.routeSessionKey=="string"?t.routeSessionKey:void 0;if(!e||!r)return t;try{const s=JSON.parse(await o(e,"utf8"));if(!a(s)||!a(s[r]))return t;const i=s[r],n=typeof i.sessionFile=="string"?i.sessionFile:typeof i.sessionId=="string"?c(p(e),`${i.sessionId}.jsonl`):void 0;return n?{...t,transcriptPath:n}:t}catch{return t}}function f(t){const e=typeof t.timestamp=="string"?Date.parse(t.timestamp):Number(t.timestamp);if(Number.isFinite(e)&&e>0)return e;const r=a(t.message)?t.message:void 0,s=Number(r?.timestamp);return Number.isFinite(s)&&s>0?s:void 0}function d(t,e){const r=Date.parse(e.startedAt),s=Date.parse(e.endedAt);return!Number.isFinite(r)||!Number.isFinite(s)?t:{...t,lines:t.lines.filter(i=>{const n=f(i.record);return n===void 0||n>=r-2e3&&n<=s+1e3})}}function g(t){return{...t,spans:t.spans.map(e=>({...e,provider:{...e.provider,provider:"openclaw"},...e.usage?{usage:{...e.usage,provider:"openclaw",normalization:{...e.usage.normalization,adapter:"openclaw-transcript"}}}:{}})),anomalies:t.anomalies.map(e=>({...e,code:e.code.replace(/^pi_/,"openclaw_")}))}}class F{provider="openclaw";transcript=new m;async collect(e){const r=await l(e.boundary);return d(await u(r),e)}async normalize(e,r){return g(await this.transcript.normalize(e,r))}async correlate(e,r){return this.transcript.correlate(e,r)}validate(e,r){return this.transcript.validate(e,r)}}export{F as OpenClawAuditAdapter};
1
+ import{readFile as l,readdir as m,stat as o}from"node:fs/promises";import{basename as h,dirname as p,join as u}from"node:path";import{PiAuditAdapter as w}from"../pi/pi-audit-adapter.js";import{collectPiTranscript as y}from"../pi/pi-transcript-source.js";function a(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function P(t,e){const r={...t,transcriptPath:e};return delete r.transcriptStartOffset,delete r.transcriptEndOffset,delete r.transcriptFileIdentity,delete r.transcriptPrefixFingerprint,r}async function f(t){try{const e=await o(t);return`${e.dev}:${e.ino}`}catch{return}}async function g(t,e){if(e)try{const r=p(t),n=`${h(t)}.reset`,i=(await m(r)).filter(s=>s.startsWith(n)).sort().reverse();for(const s of i){const c=u(r,s);if(await f(c)===e)return c}}catch{}}async function d(t){const e=typeof t.sessionStorePath=="string"?t.sessionStorePath:void 0,r=typeof t.routeSessionKey=="string"?t.routeSessionKey:void 0;if(!(!e||!r))try{const n=JSON.parse(await l(e,"utf8"));if(!a(n)||!a(n[r]))return;const i=n[r];return typeof i.sessionFile=="string"?i.sessionFile:typeof i.sessionId=="string"?u(p(e),`${i.sessionId}.jsonl`):void 0}catch{return}}async function F(t){const e=typeof t.transcriptPath=="string"?t.transcriptPath:void 0;if(!e){const r=await d(t);return r?P(t,r):t}try{return await o(e),t}catch{const r=typeof t.transcriptFileIdentity=="string"?t.transcriptFileIdentity:void 0,n=await g(e,r);if(n)return{...t,transcriptPath:n};const i=await d(t);return i&&r&&await f(i)===r?{...t,transcriptPath:i}:t}}function T(t){const e=typeof t.timestamp=="string"?Date.parse(t.timestamp):Number(t.timestamp);if(Number.isFinite(e)&&e>0)return e;const r=a(t.message)?t.message:void 0,n=Number(r?.timestamp);return Number.isFinite(n)&&n>0?n:void 0}function A(t,e){const r=Date.parse(e.startedAt),n=Date.parse(e.endedAt);return!Number.isFinite(r)||!Number.isFinite(n)?t:{...t,lines:t.lines.filter(i=>{const s=T(i.record);return s===void 0||s>=r-2e3&&s<=n+1e3})}}function v(t){return{...t,spans:t.spans.map(e=>({...e,provider:{...e.provider,provider:"openclaw"},...e.usage?{usage:{...e.usage,provider:"openclaw",normalization:{...e.usage.normalization,adapter:"openclaw-transcript"}}}:{}})),anomalies:t.anomalies.map(e=>({...e,code:e.code.replace(/^pi_/,"openclaw_")}))}}class z{provider="openclaw";transcript=new w;async collect(e){const r=await F(e.boundary);return A(await y(r),e)}async normalize(e,r){return v(await this.transcript.normalize(e,r))}async correlate(e,r){return this.transcript.correlate(e,r)}validate(e,r){return this.transcript.validate(e,r)}}export{z as OpenClawAuditAdapter};
@@ -1 +1 @@
1
- import{open as _,stat as y}from"node:fs/promises";function $(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function h(t,r){return typeof t=="number"&&Number.isSafeInteger(t)&&t>=0?t:r}async function P(t){const r=typeof t.transcriptPath=="string"?t.transcriptPath:void 0;if(!r)return{lines:[],anomalies:[{code:"pi_transcript_path_missing",message:"boundary.transcriptPath is required"}],gaps:[{category:"main_transcript",reason:"source_log_missing",detail:"boundary.transcriptPath is missing"}],sourcePaths:[]};const e={lines:[],anomalies:[],gaps:[],sourcePaths:[r]};try{const s=await y(r),c=h(t.transcriptStartOffset,0),g=typeof t.transcriptEndOffset=="number"?h(t.transcriptEndOffset,s.size):s.size;if(c>s.size)return e.anomalies.push({code:"pi_transcript_boundary_out_of_range",message:`${r}: start ${c} exceeds size ${s.size}`}),e.gaps.push({category:"main_transcript",reason:"correlation_failed",detail:`${r}:${c}-${g}`}),e;const p=c,f=Math.min(Math.max(g,p),s.size)-p;if(f===0)return e;const d=await _(r,"r");let n;try{n=Buffer.allocUnsafe(f);const{bytesRead:a}=await d.read(n,0,f,p);n=n.subarray(0,a)}finally{await d.close()}let o=0;for(;o<n.length;){const a=n.indexOf(10,o),m=a===-1?n.length:a+1,u=n.subarray(o,a===-1?n.length:a),l=p+o;if(o=m,u.length!==0)try{const i=JSON.parse(u.toString("utf8"));if(!$(i))throw new Error("JSONL row is not an object");e.lines.push({record:i,sourcePath:r,sourceOffset:l,sourceLength:u.length})}catch(i){e.anomalies.push({code:"pi_transcript_row_malformed",message:`${r}:${l}: ${i instanceof Error?i.message:String(i)}`}),e.gaps.push({category:"transcript_row",reason:"correlation_failed",detail:`${r}:${l}`})}}}catch(s){e.anomalies.push({code:"pi_transcript_source_unreadable",message:`${r}: ${s instanceof Error?s.message:String(s)}`}),e.gaps.push({category:"main_transcript",reason:"source_log_missing",detail:r})}return e}export{P as collectPiTranscript};
1
+ import{open as y}from"node:fs/promises";import{readAndHashFilePrefixRange as $}from"../../core/transcript-fingerprint.js";function w(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function m(t,r){return typeof t=="number"&&Number.isSafeInteger(t)&&t>=0?t:r}function _(t,r,e){t.anomalies.push({code:"pi_transcript_source_changed",message:`${r}: ${e}`}),t.gaps.push({category:"main_transcript",reason:"correlation_failed",detail:`${r}: ${e}`})}async function S(t){const r=typeof t.transcriptPath=="string"?t.transcriptPath:void 0;if(!r)return{lines:[],anomalies:[{code:"pi_transcript_path_missing",message:"boundary.transcriptPath is required"}],gaps:[{category:"main_transcript",reason:"source_log_missing",detail:"boundary.transcriptPath is missing"}],sourcePaths:[]};const e={lines:[],anomalies:[],gaps:[],sourcePaths:[r]};try{const a=await y(r,"r");let s,p;try{const n=await a.stat(),f=m(t.transcriptStartOffset,0),o=typeof t.transcriptEndOffset=="number"?m(t.transcriptEndOffset,n.size):n.size;if(f>n.size||o>n.size)return e.anomalies.push({code:"pi_transcript_boundary_out_of_range",message:`${r}: boundary ${f}-${o} exceeds size ${n.size}`}),e.gaps.push({category:"main_transcript",reason:"correlation_failed",detail:`${r}:${f}-${o}`}),e;const c=typeof t.transcriptFileIdentity=="string"?t.transcriptFileIdentity:void 0,i=`${n.dev}:${n.ino}`;if(c&&i!==c)return _(e,r,`identity ${i} does not match ${c}`),e;const u=typeof t.transcriptPrefixFingerprint=="string"?t.transcriptPrefixFingerprint:void 0;p=f;const h=Math.max(o,p),d=h-p;if(u){const l=await $(a,p,h);if(l.fingerprint!==u)return _(e,r,"closed transcript prefix fingerprint changed"),e;s=l.buffer}else{if(d===0)return e;s=Buffer.allocUnsafe(d);const{bytesRead:l}=await a.read(s,0,d,p);s=s.subarray(0,l)}if(d===0)return e}finally{await a.close()}let g=0;for(;g<s.length;){const n=s.indexOf(10,g),f=n===-1?s.length:n+1,o=s.subarray(g,n===-1?s.length:n),c=p+g;if(g=f,o.length!==0)try{const i=JSON.parse(o.toString("utf8"));if(!w(i))throw new Error("JSONL row is not an object");e.lines.push({record:i,sourcePath:r,sourceOffset:c,sourceLength:o.length})}catch(i){e.anomalies.push({code:"pi_transcript_row_malformed",message:`${r}:${c}: ${i instanceof Error?i.message:String(i)}`}),e.gaps.push({category:"transcript_row",reason:"correlation_failed",detail:`${r}:${c}`})}}}catch(a){e.anomalies.push({code:"pi_transcript_source_unreadable",message:`${r}: ${a instanceof Error?a.message:String(a)}`}),e.gaps.push({category:"main_transcript",reason:"source_log_missing",detail:r})}return e}export{S as collectPiTranscript};
@@ -0,0 +1 @@
1
+ import{createHash as p}from"node:crypto";const g=64*1024;async function d(o,a){return(await m(o,[a])).get(a)}async function m(o,a){const t=[...new Set(a)].sort((n,r)=>n-r);if(t.some(n=>!Number.isSafeInteger(n)||n<0))throw new Error("Transcript prefix lengths must be non-negative safe integers");const f=new Map(t.map(n=>[n,p("sha256").update(String(n)).update("\0")])),s=t.at(-1)??0,i=Buffer.allocUnsafe(Math.min(g,Math.max(1,s)));let e=0;for(;e<s;){const n=Math.min(i.length,s-e),{bytesRead:r}=await o.read(i,0,n,e);if(r===0)throw new Error(`Transcript ended before prefix offset ${s}`);for(const[c,u]of f)e>=c||u.update(i.subarray(0,Math.min(r,c-e)));e+=r}return new Map([...f].map(([n,r])=>[n,r.digest("hex")]))}async function w(o,a,t){if(!Number.isSafeInteger(a)||!Number.isSafeInteger(t)||a<0||t<a)throw new Error("Transcript range must contain safe non-negative offsets");const f=p("sha256").update(String(t)).update("\0"),s=Buffer.allocUnsafe(t-a),i=Buffer.allocUnsafe(Math.min(g,Math.max(1,t)));let e=0;for(;e<t;){const n=Math.min(i.length,t-e),{bytesRead:r}=await o.read(i,0,n,e);if(r===0)throw new Error(`Transcript ended before prefix offset ${t}`);const c=i.subarray(0,r);f.update(c);const u=Math.max(e,a),h=Math.min(e+r,t);u<h&&c.copy(s,u-a,u-e,h-e),e+=r}return{buffer:s,fingerprint:f.digest("hex")}}export{d as hashFilePrefix,m as hashFilePrefixes,w as readAndHashFilePrefixRange};
@@ -1 +1 @@
1
- import{fork as c}from"node:child_process";import{randomUUID as o}from"node:crypto";import{fileURLToPath as f}from"node:url";import{AUDIT_RECORDER_PROTOCOL_VERSION as p,isAuditRecorderChildMessage as m}from"../worker/audit-recorder-ipc-protocol.js";const w=128,y=3,k=1e3,v=5e3;class R{workerConfig;workerPath;maxQueueSize;maxDeliveryAttempts;restartDelayMs;onDiagnostic;jobSettledHandlers=new Set;queue=[];flushWaiters=new Map;child;childReady=!1;active;spawnScheduled=!1;restartTimer;workerFlushRequestId;shutdownWaiter;deliverySequence=0;accepting=!0;closing=!1;stopped=!1;stopPromise;constructor(e){this.maxQueueSize=n(e.maxQueueSize,w,"maxQueueSize"),this.maxDeliveryAttempts=n(e.maxDeliveryAttempts,y,"maxDeliveryAttempts"),this.restartDelayMs=g(e.restartDelayMs,k,"restartDelayMs"),this.workerConfig={...e.workerConfig},this.workerPath=e.workerPath??f(new URL("../worker/audit-recorder-worker-entry.js",import.meta.url)),this.onDiagnostic=e.onDiagnostic}get pendingCount(){return this.queue.length+(this.active?1:0)}addJobSettledHandler(e){return this.jobSettledHandlers.add(e),()=>{this.jobSettledHandlers.delete(e)}}tryEnqueue(e){try{return!this.accepting||this.closing||this.stopped?!1:this.pendingCount>=this.maxQueueSize?(this.diagnostic({type:"queue_full",message:`Audit Recorder queue is full (${this.maxQueueSize})`,pendingCount:this.pendingCount,jobId:e.jobId}),!1):(this.deliverySequence+=1,this.queue.push({deliveryId:`${e.jobId}:${this.deliverySequence}`,job:e,attempts:0}),this.wakeWorker(),!0)}catch{return!1}}flush(e={}){try{if(this.stopped)return Promise.resolve(this.pendingCount===0);const t=a(e.timeoutMs),i=o(),r=new Promise(h=>{const d=setTimeout(()=>{const l=this.flushWaiters.get(i);l&&(this.flushWaiters.delete(i),l.resolve(!1))},t);d.unref?.(),this.flushWaiters.set(i,{resolve:h,timer:d})});return this.pendingCount>0&&this.wakeWorker(),this.settleFlushWaiters(),r}catch{return Promise.resolve(!1)}}stop(e={}){if(this.stopPromise)return this.stopPromise;this.accepting=!1;const t=a(e.timeoutMs);return this.stopPromise=this.performStop(t).catch(()=>!1),this.stopPromise}async performStop(e){const t=await this.flush({timeoutMs:e});this.closing=!0,this.clearRestartTimer();let i=t;return t&&this.child&&this.childReady?i=await this.requestWorkerStop(e):this.child&&(i=!1,this.terminateChild(this.child)),this.stopped=!0,this.childReady=!1,this.queue.length=0,this.active=void 0,this.resolveFlushWaiters(!1),this.child&&!i&&this.terminateChild(this.child),t&&i}wakeWorker(){this.pump(),this.scheduleWorker()}scheduleWorker(){this.stopped||this.closing||this.child||this.spawnScheduled||this.restartTimer||this.pendingCount===0||(this.spawnScheduled=!0,queueMicrotask(()=>{this.spawnScheduled=!1,this.ensureWorker()}))}ensureWorker(){if(this.stopped||this.closing||this.child||this.pendingCount===0)return;let e;try{e=c(this.workerPath,[],{stdio:["ignore","ignore","ignore","ipc"],serialization:"json"})}catch(t){this.diagnostic({type:"worker_error",message:u(t)}),this.scheduleRestart();return}this.child=e,this.childReady=!1,e.on("message",t=>{this.child===e&&this.handleChildMessage(t)}),e.once("error",t=>{this.workerUnavailable(e,"worker_error",u(t))}),e.once("exit",(t,i)=>{const r=i?`signal ${i}`:`exit code ${t??"unknown"}`;this.workerUnavailable(e,"worker_exit",r)}),e.once("disconnect",()=>{this.workerUnavailable(e,"worker_exit","IPC channel disconnected")}),this.sendToChild({type:"audit_recorder_initialize",protocolVersion:p,config:this.workerConfig})}handleChildMessage(e){if(m(e))switch(e.type){case"audit_recorder_ready":this.childReady=!0,this.pump(),this.settleFlushWaiters();return;case"audit_recorder_job_result":this.handleJobResult(e);return;case"audit_recorder_flushed":if(e.requestId!==this.workerFlushRequestId)return;this.workerFlushRequestId=void 0,this.resolveFlushWaiters(!0);return;case"audit_recorder_stopped":if(e.requestId!==this.shutdownWaiter?.requestId)return;this.shutdownWaiter.acknowledged=!0;return;case"audit_recorder_fatal":{const t=this.child;this.diagnostic({type:"worker_fatal",message:e.error}),t&&(this.workerUnavailable(t,"worker_fatal",e.error),this.terminateChild(t));return}}}handleJobResult(e){const t=this.active;!t||t.deliveryId!==e.deliveryId||(this.active=void 0,e.ok?this.settle(t.job,{ok:!0,...e.revision===void 0?{}:{revision:e.revision},...e.quality===void 0?{}:{quality:e.quality},...e.truncated===void 0?{}:{truncated:e.truncated}}):(this.diagnostic({type:"job_failed",message:e.error??"Audit replay finalization failed",jobId:t.job.jobId,deliveryId:t.deliveryId}),e.retryable&&t.attempts<this.maxDeliveryAttempts?this.queue.unshift(t):e.retryable?this.dropDelivery(t,e.error??"retry limit reached"):this.settle(t.job,{ok:!1,error:e.error??"Audit replay finalization failed"})),this.pump(),this.settleFlushWaiters())}pump(){if(!this.child||!this.childReady||this.active||this.stopped||this.closing)return;const e=this.queue.shift();if(!e){this.settleFlushWaiters();return}if(e.attempts+=1,this.active=e,!this.sendToChild({type:"audit_recorder_job",deliveryId:e.deliveryId,job:e.job})){const t=this.child;t&&(this.workerUnavailable(t,"worker_error","Unable to send audit job to Recorder"),this.terminateChild(t))}}settleFlushWaiters(){if(this.flushWaiters.size===0||this.pendingCount>0)return;if(!this.child||!this.childReady){this.resolveFlushWaiters(!0);return}if(this.workerFlushRequestId)return;const e=o();if(this.workerFlushRequestId=e,!this.sendToChild({type:"audit_recorder_flush",requestId:e})){this.workerFlushRequestId=void 0;const t=this.child;t?(this.workerUnavailable(t,"worker_error","Unable to flush audit Recorder"),this.terminateChild(t)):this.resolveFlushWaiters(!0)}}resolveFlushWaiters(e){for(const[t,i]of this.flushWaiters)clearTimeout(i.timer),this.flushWaiters.delete(t),i.resolve(e)}requestWorkerStop(e){const t=this.child;if(!t||!this.childReady)return Promise.resolve(!0);const i=o();return new Promise(r=>{const h=setTimeout(()=>{this.shutdownWaiter?.requestId===i&&(this.shutdownWaiter=void 0,this.terminateChild(t),r(!1))},e);h.unref?.(),this.shutdownWaiter={requestId:i,acknowledged:!1,resolve:r,timer:h},this.sendToChild({type:"audit_recorder_stop",requestId:i})||(clearTimeout(h),this.shutdownWaiter=void 0,this.terminateChild(t),r(!1))})}sendToChild(e){const t=this.child;if(!t?.connected)return!1;try{return t.send(e,i=>{!i||this.child!==t||(this.workerUnavailable(t,"worker_error",i.message),this.terminateChild(t))}),!0}catch{return!1}}workerUnavailable(e,t,i){if(this.child!==e)return;if(this.child=void 0,this.childReady=!1,this.workerFlushRequestId=void 0,this.active){const h=this.active;this.active=void 0,h.attempts<this.maxDeliveryAttempts?this.queue.unshift(h):this.dropDelivery(h,i)}t!=="worker_fatal"&&this.diagnostic({type:t,message:i});const r=this.shutdownWaiter;r&&(clearTimeout(r.timer),this.shutdownWaiter=void 0,r.resolve(r.acknowledged)),this.pendingCount>0&&!this.closing&&!this.stopped?this.scheduleRestart():this.settleFlushWaiters()}scheduleRestart(){this.restartTimer||this.child||this.stopped||this.closing||this.pendingCount===0||(this.restartTimer=setTimeout(()=>{this.restartTimer=void 0,this.scheduleWorker()},this.restartDelayMs),this.restartTimer.unref?.())}clearRestartTimer(){this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=void 0)}dropDelivery(e,t){this.diagnostic({type:"job_dropped",message:t,jobId:e.job.jobId,deliveryId:e.deliveryId}),this.settle(e.job,{ok:!1,error:t})}settle(e,t){for(const i of this.jobSettledHandlers)try{i(e,t)}catch{}}terminateChild(e){try{e.kill("SIGTERM")}catch{}}diagnostic(e){try{this.onDiagnostic?.(e)}catch{}}}function n(s,e,t){const i=s??e;if(!Number.isSafeInteger(i)||i<=0)throw new Error(`${t} must be a positive integer`);return i}function g(s,e,t){const i=s??e;if(!Number.isSafeInteger(i)||i<0)throw new Error(`${t} must be a non-negative integer`);return i}function a(s){return n(s,v,"timeoutMs")}function u(s){return s instanceof Error?s.message:typeof s=="string"?s:"Unknown Recorder process error"}export{R as IpcAuditProducer};
1
+ import{fork as c}from"node:child_process";import{randomUUID as o}from"node:crypto";import{fileURLToPath as f}from"node:url";import{AUDIT_RECORDER_PROTOCOL_VERSION as p,isAuditRecorderChildMessage as m}from"../worker/audit-recorder-ipc-protocol.js";const w=128,y=3,k=1e3,v=5e3;class R{workerConfig;workerPath;maxQueueSize;maxDeliveryAttempts;restartDelayMs;onDiagnostic;jobSettledHandlers=new Set;queue=[];flushWaiters=new Map;child;childReady=!1;active;spawnScheduled=!1;restartTimer;workerFlushRequestId;shutdownWaiter;deliverySequence=0;accepting=!0;closing=!1;stopped=!1;stopPromise;constructor(e){this.maxQueueSize=n(e.maxQueueSize,w,"maxQueueSize"),this.maxDeliveryAttempts=n(e.maxDeliveryAttempts,y,"maxDeliveryAttempts"),this.restartDelayMs=g(e.restartDelayMs,k,"restartDelayMs"),this.workerConfig={...e.workerConfig},this.workerPath=e.workerPath??f(new URL("../worker/audit-recorder-worker-entry.js",import.meta.url)),this.onDiagnostic=e.onDiagnostic}get pendingCount(){return this.queue.length+(this.active?1:0)}addJobSettledHandler(e){return this.jobSettledHandlers.add(e),()=>{this.jobSettledHandlers.delete(e)}}tryEnqueue(e){try{return!this.accepting||this.closing||this.stopped?!1:this.pendingCount>=this.maxQueueSize?(this.diagnostic({type:"queue_full",message:`Audit Recorder queue is full (${this.maxQueueSize})`,pendingCount:this.pendingCount,jobId:e.jobId}),!1):(this.deliverySequence+=1,this.queue.push({deliveryId:`${e.jobId}:${this.deliverySequence}`,job:e,attempts:0}),this.wakeWorker(),!0)}catch{return!1}}flush(e={}){try{if(this.stopped)return Promise.resolve(this.pendingCount===0);const t=a(e.timeoutMs),i=o(),r=new Promise(h=>{const d=setTimeout(()=>{const l=this.flushWaiters.get(i);l&&(this.flushWaiters.delete(i),l.resolve(!1))},t);d.unref?.(),this.flushWaiters.set(i,{resolve:h,timer:d})});return this.pendingCount>0&&this.wakeWorker(),this.settleFlushWaiters(),r}catch{return Promise.resolve(!1)}}stop(e={}){if(this.stopPromise)return this.stopPromise;this.accepting=!1;const t=a(e.timeoutMs);return this.stopPromise=this.performStop(t).catch(()=>!1),this.stopPromise}async performStop(e){const t=await this.flush({timeoutMs:e});this.closing=!0,this.clearRestartTimer();let i=t;return t&&this.child&&this.childReady?i=await this.requestWorkerStop(e):this.child&&(i=!1,this.terminateChild(this.child)),this.stopped=!0,this.childReady=!1,this.queue.length=0,this.active=void 0,this.resolveFlushWaiters(!1),this.child&&!i&&this.terminateChild(this.child),t&&i}wakeWorker(){this.pump(),this.scheduleWorker()}scheduleWorker(){this.stopped||this.closing||this.child||this.spawnScheduled||this.restartTimer||this.pendingCount===0||(this.spawnScheduled=!0,queueMicrotask(()=>{this.spawnScheduled=!1,this.ensureWorker()}))}ensureWorker(){if(this.stopped||this.closing||this.child||this.pendingCount===0)return;let e;try{e=c(this.workerPath,[],{stdio:["ignore","ignore","ignore","ipc"],serialization:"json",windowsHide:!0})}catch(t){this.diagnostic({type:"worker_error",message:u(t)}),this.scheduleRestart();return}this.child=e,this.childReady=!1,e.on("message",t=>{this.child===e&&this.handleChildMessage(t)}),e.once("error",t=>{this.workerUnavailable(e,"worker_error",u(t))}),e.once("exit",(t,i)=>{const r=i?`signal ${i}`:`exit code ${t??"unknown"}`;this.workerUnavailable(e,"worker_exit",r)}),e.once("disconnect",()=>{this.workerUnavailable(e,"worker_exit","IPC channel disconnected")}),this.sendToChild({type:"audit_recorder_initialize",protocolVersion:p,config:this.workerConfig})}handleChildMessage(e){if(m(e))switch(e.type){case"audit_recorder_ready":this.childReady=!0,this.pump(),this.settleFlushWaiters();return;case"audit_recorder_job_result":this.handleJobResult(e);return;case"audit_recorder_flushed":if(e.requestId!==this.workerFlushRequestId)return;this.workerFlushRequestId=void 0,this.resolveFlushWaiters(!0);return;case"audit_recorder_stopped":if(e.requestId!==this.shutdownWaiter?.requestId)return;this.shutdownWaiter.acknowledged=!0;return;case"audit_recorder_fatal":{const t=this.child;this.diagnostic({type:"worker_fatal",message:e.error}),t&&(this.workerUnavailable(t,"worker_fatal",e.error),this.terminateChild(t));return}}}handleJobResult(e){const t=this.active;!t||t.deliveryId!==e.deliveryId||(this.active=void 0,e.ok?this.settle(t.job,{ok:!0,...e.revision===void 0?{}:{revision:e.revision},...e.quality===void 0?{}:{quality:e.quality},...e.truncated===void 0?{}:{truncated:e.truncated}}):(this.diagnostic({type:"job_failed",message:e.error??"Audit replay finalization failed",jobId:t.job.jobId,deliveryId:t.deliveryId}),e.retryable&&t.attempts<this.maxDeliveryAttempts?this.queue.unshift(t):e.retryable?this.dropDelivery(t,e.error??"retry limit reached"):this.settle(t.job,{ok:!1,error:e.error??"Audit replay finalization failed"})),this.pump(),this.settleFlushWaiters())}pump(){if(!this.child||!this.childReady||this.active||this.stopped||this.closing)return;const e=this.queue.shift();if(!e){this.settleFlushWaiters();return}if(e.attempts+=1,this.active=e,!this.sendToChild({type:"audit_recorder_job",deliveryId:e.deliveryId,job:e.job})){const t=this.child;t&&(this.workerUnavailable(t,"worker_error","Unable to send audit job to Recorder"),this.terminateChild(t))}}settleFlushWaiters(){if(this.flushWaiters.size===0||this.pendingCount>0)return;if(!this.child||!this.childReady){this.resolveFlushWaiters(!0);return}if(this.workerFlushRequestId)return;const e=o();if(this.workerFlushRequestId=e,!this.sendToChild({type:"audit_recorder_flush",requestId:e})){this.workerFlushRequestId=void 0;const t=this.child;t?(this.workerUnavailable(t,"worker_error","Unable to flush audit Recorder"),this.terminateChild(t)):this.resolveFlushWaiters(!0)}}resolveFlushWaiters(e){for(const[t,i]of this.flushWaiters)clearTimeout(i.timer),this.flushWaiters.delete(t),i.resolve(e)}requestWorkerStop(e){const t=this.child;if(!t||!this.childReady)return Promise.resolve(!0);const i=o();return new Promise(r=>{const h=setTimeout(()=>{this.shutdownWaiter?.requestId===i&&(this.shutdownWaiter=void 0,this.terminateChild(t),r(!1))},e);h.unref?.(),this.shutdownWaiter={requestId:i,acknowledged:!1,resolve:r,timer:h},this.sendToChild({type:"audit_recorder_stop",requestId:i})||(clearTimeout(h),this.shutdownWaiter=void 0,this.terminateChild(t),r(!1))})}sendToChild(e){const t=this.child;if(!t?.connected)return!1;try{return t.send(e,i=>{!i||this.child!==t||(this.workerUnavailable(t,"worker_error",i.message),this.terminateChild(t))}),!0}catch{return!1}}workerUnavailable(e,t,i){if(this.child!==e)return;if(this.child=void 0,this.childReady=!1,this.workerFlushRequestId=void 0,this.active){const h=this.active;this.active=void 0,h.attempts<this.maxDeliveryAttempts?this.queue.unshift(h):this.dropDelivery(h,i)}t!=="worker_fatal"&&this.diagnostic({type:t,message:i});const r=this.shutdownWaiter;r&&(clearTimeout(r.timer),this.shutdownWaiter=void 0,r.resolve(r.acknowledged)),this.pendingCount>0&&!this.closing&&!this.stopped?this.scheduleRestart():this.settleFlushWaiters()}scheduleRestart(){this.restartTimer||this.child||this.stopped||this.closing||this.pendingCount===0||(this.restartTimer=setTimeout(()=>{this.restartTimer=void 0,this.scheduleWorker()},this.restartDelayMs),this.restartTimer.unref?.())}clearRestartTimer(){this.restartTimer&&(clearTimeout(this.restartTimer),this.restartTimer=void 0)}dropDelivery(e,t){this.diagnostic({type:"job_dropped",message:t,jobId:e.job.jobId,deliveryId:e.deliveryId}),this.settle(e.job,{ok:!1,error:t})}settle(e,t){for(const i of this.jobSettledHandlers)try{i(e,t)}catch{}}terminateChild(e){try{e.kill("SIGTERM")}catch{}}diagnostic(e){try{this.onDiagnostic?.(e)}catch{}}}function n(s,e,t){const i=s??e;if(!Number.isSafeInteger(i)||i<=0)throw new Error(`${t} must be a positive integer`);return i}function g(s,e,t){const i=s??e;if(!Number.isSafeInteger(i)||i<0)throw new Error(`${t} must be a non-negative integer`);return i}function a(s){return n(s,v,"timeoutMs")}function u(s){return s instanceof Error?s.message:typeof s=="string"?s:"Unknown Recorder process error"}export{R as IpcAuditProducer};
@@ -1 +1 @@
1
- import{log as o}from"../core/log/index.js";import{RespawnManager as d}from"./respawn-manager.js";import{EventQueue as p}from"./event-queue.js";class c extends Error{constructor(e){super(`adapter pool full (maxSize=${e})`),this.name="PoolFullError"}}const v=6e4;class m{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;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){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);s.startPromise&&await s.startPromise,s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&o.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 c(this.config.maxPoolSize);this.sendAck(e.event_id,e.session_id);const n=this.createSlot(t);try{await n.startPromise}catch(r){throw r}this.submitToSlotQueue(n,e)}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&o.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);if(!s)return o.error("adapter-pool",`Cannot complete event ${e}: session slot not found (${t})`),!1;const n=s.eventQueue.complete(e);return n||o.error("adapter-pool",`Cannot complete event ${e}: not running in session ${t}`),n}cancelEvent(e,t){const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}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"}drainAllQueuedEvents(){const e=[];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);o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}o.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)){o.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const i=this.createSlot(s);try{await i.startPromise,n=i}catch(l){o.error("adapter-pool",`Failed to auto-create slot for ${s}: ${l}`)}}return n?.adapter?.handleLocalAction?(n.lastActivityAt=Date.now(),n.adapter.handleLocalAction(e)):s?{handled:!1,kind:""}:(await Promise.allSettled([...this.slots.values()].filter(i=>!i.stopping).map(i=>i.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(i=>i.status==="fulfilled"&&i.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 t;if(!this.stopped)return this.createSlot(e)}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.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(),v)}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){o.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?(o.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")}):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{o.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||(o.info("adapter-pool",`Evicting idle slot for session ${e} (${s})`),this.removeSlot(e).catch(r=>{o.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 o.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?(o.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:(o.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(){for(const e of this.slots.values())if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}hasPendingOrBackgroundWork(){for(const e of this.slots.values()){if(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 d,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(i){throw this.detachAllSlotEventListeners(r),this.slots.delete(e),i}finally{r.startPromise=null}})();return r.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(r)),r}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(o.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&&(o.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);o.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){o.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){o.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;return{name:`slot-${s}`,get stopped(){return t.stopped||!t.slots.has(s)},get agentAlive(){return e.adapter.isAlive()},pingAgent:n=>e.adapter.ping(n),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:n=>{}}}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,i)=>{this.onEventState?.(n,r,a,i)},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 p(t,s)}}export{m as AdapterPool,c as PoolFullError};
1
+ import{log as o}from"../core/log/index.js";import{RespawnManager as p}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 E{slots=new Map;config;factory;sendAck;onEventState=null;onQueueComposing=null;onInternalError=null;onEventStarted=null;onEventDone=null;onSessionActivity=null;stopped=!1;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){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);s.startPromise&&await s.startPromise,s.lastActivityAt=Date.now(),this.sendAck(e.event_id,e.session_id),s.state==="stopped"&&o.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);this.sendAck(e.event_id,e.session_id);const n=this.createSlot(t);try{await n.startPromise}catch(r){throw r}this.submitToSlotQueue(n,e)}submitToSlotQueue(e,t){e.eventQueue.submit(t)==="rejected"&&o.info("adapter-pool",`Event ${t.event_id} rejected by EventQueue (queue full)`)}eventComplete(e,t){const s=this.slots.get(t);if(!s)return o.error("adapter-pool",`Cannot complete event ${e}: session slot not found (${t})`),!1;const n=s.eventQueue.complete(e);return n||o.error("adapter-pool",`Cannot complete event ${e}: not running in session ${t}`),n}cancelEvent(e,t){const s=this.slots.get(t);return s?s.eventQueue.cancel(e):!1}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"}drainAllQueuedEvents(){const e=[];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);o.info("adapter-pool",`[stop-trace] pool.deliverStopEvent session=${t} event=${e} slotExists=${!!s} -> adapter.deliverStopEvent`),s?.adapter.deliverStopEvent(e,t);return}o.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)){o.info("adapter-pool",`Auto-creating slot for session ${s} (local_action trigger)`);const i=this.createSlot(s);try{await i.startPromise,n=i}catch(c){o.error("adapter-pool",`Failed to auto-create slot for ${s}: ${c}`)}}return n?.adapter?.handleLocalAction?(n.lastActivityAt=Date.now(),n.adapter.handleLocalAction(e)):s?{handled:!1,kind:""}:(await Promise.allSettled([...this.slots.values()].filter(i=>!i.stopping).map(i=>i.adapter.handleLocalAction?.(e)??Promise.resolve({handled:!1,kind:""})))).find(i=>i.status==="fulfilled"&&i.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.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){o.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?(o.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")}):(o.info("adapter-pool",`Evicting idle slot for session ${t}`),this.removeSlot(t).catch(r=>{o.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||(o.info("adapter-pool",`Evicting idle slot for session ${e} (${s})`),this.removeSlot(e).catch(r=>{o.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 o.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?(o.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:(o.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(){for(const e of this.slots.values())if(e.eventQueue.runningCount>0||e.eventQueue.queuedCount>0||e.state==="ready"&&e.adapter.getStatus().busy)return!0;return!1}hasPendingOrBackgroundWork(){for(const e of this.slots.values()){if(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 p,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(i){throw this.detachAllSlotEventListeners(r),this.slots.delete(e),i}finally{r.startPromise=null}})();return r.startPromise=a,s.startHealthCheck(this.slotRespawnCtx(r)),r}wireSlotEvents(e){e.adapter.on("exit",t=>{this.stopped||(o.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&&(o.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);o.error("adapter-pool",`Slot ${e.sessionId} adapter error: ${s}`)}),e.adapter.on("internalError",t=>{if(!this.stopped){if(this.slots.get(e.sessionId)!==e){o.warn("adapter-pool",`Ignored internalError from detached slot session=${e.sessionId} event=${t.eventId}`);return}if(!this.onInternalError){o.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,i)=>{this.onEventState?.(n,r,a,i)},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{E as AdapterPool,g as PoolFullError};