@automagik/genie 4.260501.3 → 4.260501.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/genie.js CHANGED
@@ -2382,7 +2382,7 @@ ${body}`;writeFileSync16(filePath,output,"utf-8")}function serializeSdkConfig(sd
2382
2382
  `;return new Set(rows.map((r)=>r.id))}function makeAgentToolBypassDetector(deps){let windowMs=deps.activeWindowMs??DEFAULT_ACTIVE_WINDOW_MS,maxOrphans=deps.maxOrphansPerEvent??DEFAULT_MAX_ORPHANS_PER_EVENT;return{id:"rot.agent-tool-bypass",version:"1.0.0",riskClass:"medium",async query(){let nowMs=deps.now(),active=(await deps.listTranscripts()).filter((t)=>nowMs-t.mtime_ms<=windowMs),ids=active.map((t)=>t.agent_id),known=ids.length===0?new Set:await deps.checkAgentIds(ids),orphans=[];for(let t of active){if(known.has(t.agent_id))continue;orphans.push({agent_id:t.agent_id,transcript_path:t.transcript_path,size_bytes:t.size_bytes,mtime_iso:new Date(t.mtime_ms).toISOString()})}return{orphans,scanned_count:active.length}},shouldFire(state){return state.orphans.length>0},render(state){let head=state.orphans.slice(0,maxOrphans),first=head[0];return{type:"rot.detected",subject:first?.agent_id??"unknown",payload:{pattern_id:"pattern-10-agent-tool-bypass",entity_id:first?.agent_id??"unknown",observed_state_json:{agent_id:first?.agent_id??"unknown",transcript_path:first?.transcript_path??"",size_bytes:first?.size_bytes??0,mtime_iso:first?.mtime_iso??"",detected_at:new Date(deps.now()).toISOString(),orphan_count:state.orphans.length,scanned_count:state.scanned_count,agent_ids:head.map((o)=>o.agent_id),transcript_paths:head.map((o)=>o.transcript_path),sizes_bytes:head.map((o)=>o.size_bytes)}}}}}}var DEFAULT_ACTIVE_WINDOW_MS=600000,DEFAULT_MAX_ORPHANS_PER_EVENT=32,DEFAULT_SEARCH_ROOT="/tmp",DEFAULT_SEARCH_PREFIX="claude-",DEFAULT_WALK_DEPTH=4,agentToolBypassDetector;var init_pattern_10_agent_tool_bypass=__esm(()=>{init_db();init_detectors();agentToolBypassDetector=makeAgentToolBypassDetector({listTranscripts:defaultListTranscripts,checkAgentIds:defaultCheckAgentIds,now:()=>Date.now()});registerDetector(agentToolBypassDetector)});var exports_built_in={};var init_built_in=__esm(()=>{init_pattern_1_backfill_no_worktree();init_pattern_4_duplicate_agents();init_pattern_5_zombie_team_lead();init_pattern_2_team_ls_drift();init_pattern_3_anchor_orphan();init_pattern_6_subagent_cascade();init_pattern_7_dispatch_silent_drop();init_pattern_8_session_reuse_ghost();init_pattern_9_team_unpushed_orphaned_worktree();init_pattern_10_agent_tool_bypass()});var exports_detector_scheduler={};__export(exports_detector_scheduler,{start:()=>start,DEFAULT_TICK_INTERVAL_MS:()=>DEFAULT_TICK_INTERVAL_MS,DEFAULT_JITTER_MS:()=>DEFAULT_JITTER_MS,DEFAULT_FIRE_BUDGET:()=>DEFAULT_FIRE_BUDGET,DEFAULT_DETECTOR_BUDGETS:()=>DEFAULT_DETECTOR_BUDGETS});function start(options={}){let tickIntervalMs=options.tickIntervalMs??DEFAULT_TICK_INTERVAL_MS,jitterMs=options.jitterMs??DEFAULT_JITTER_MS,defaultBudget=options.defaultFireBudget??DEFAULT_FIRE_BUDGET,budgets={...DEFAULT_DETECTOR_BUDGETS,...options.fireBudgets??{}},now=options.now??(()=>Date.now()),setTimeoutFn=options.setTimeoutFn??((fn,ms)=>setTimeout(fn,ms)),clearTimeoutFn=options.clearTimeoutFn??((handle)=>{clearTimeout(handle)}),resolveDetectors=options.detectorSource??listDetectors,emit2=options.emitFn??emitEvent,state={ticks:0,fires:0,disables:0,budgetBuckets:{}},disabledBuckets=new Set,stopped=!1,currentTimer=null,tickInFlight=null;async function runTick(){if(stopped)return;state.ticks++;let detectors=resolveDetectors();for(let detector of detectors)await runOneDetector(detector)}async function safeCall(fn){try{return await fn()}catch{return null}}function emitFire(detector,event){emit2(event.type,event.payload,{detector_version:detector.version,source_subsystem:"detector-scheduler",entity_id:event.subject??detector.id,agent:process.env.GENIE_AGENT_NAME??"detector-scheduler"}),state.fires++}function emitDisable(detector,budget,current,bucketStart){state.disables++,emit2("detector.disabled",{detector_id:detector.id,cause:"fire_budget_exceeded",budget,fire_count:current,bucket_end_ts:new Date(bucketStart+HOUR_MS).toISOString()},{detector_version:detector.version,source_subsystem:"detector-scheduler",entity_id:detector.id,severity:"warn",agent:process.env.GENIE_AGENT_NAME??"detector-scheduler"})}async function runOneDetector(detector){let bucketStart=Math.floor(now()/HOUR_MS)*HOUR_MS,bucketKey=`${detector.id}:${bucketStart}`,budget=budgets[detector.id]??defaultBudget;if(disabledBuckets.has(bucketKey))return;let result2=await safeCall(()=>detector.query());if(result2===null)return;if(!await safeCall(()=>detector.shouldFire(result2)))return;let current=(state.budgetBuckets[bucketKey]??0)+1;state.budgetBuckets[bucketKey]=current;let event=await safeCall(()=>detector.render(result2));if(event===null)return;if(emitFire(detector,event),current>=budget&&!disabledBuckets.has(bucketKey))disabledBuckets.add(bucketKey),emitDisable(detector,budget,current,bucketStart)}function scheduleNext(){if(stopped)return;let jitter=jitterMs>0?Math.floor((Math.random()*2-1)*jitterMs):0,delay=Math.max(0,tickIntervalMs+jitter);currentTimer=setTimeoutFn(()=>{tickInFlight=runTick().finally(()=>{tickInFlight=null,scheduleNext()})},delay)}return scheduleNext(),{stop(){if(stopped)return;if(stopped=!0,currentTimer)clearTimeoutFn(currentTimer),currentTimer=null},async tickNow(){if(tickInFlight)await tickInFlight;await runTick()},stats(){return{...state,budgetBuckets:{...state.budgetBuckets}}}}}var DEFAULT_TICK_INTERVAL_MS=60000,DEFAULT_JITTER_MS=5000,DEFAULT_FIRE_BUDGET=100,DEFAULT_DETECTOR_BUDGETS,HOUR_MS=3600000;var init_detector_scheduler=__esm(()=>{init_detectors();init_emit();init_pattern_2_team_ls_drift();DEFAULT_DETECTOR_BUDGETS=Object.freeze({"rot.team-ls-drift":20,"rot.backfill-no-worktree":40})});var exports_executor_read={};__export(exports_executor_read,{stopExecutorReadEndpoint:()=>stopExecutorReadEndpoint,startExecutorReadEndpoint:()=>startExecutorReadEndpoint,readExecutorState:()=>readExecutorState,isExecutorReadEndpointRunning:()=>isExecutorReadEndpointRunning,getExecutorReadPort:()=>getExecutorReadPort});async function readExecutorState(id,sql){let rows=await(sql??await getConnection())`SELECT state, outcome, closed_at FROM executors WHERE id = ${id} LIMIT 1`;if(rows.length===0)return null;let row=rows[0];return{state:row.state,outcome:row.outcome??null,closed_at:row.closed_at==null?null:row.closed_at instanceof Date?row.closed_at.toISOString():row.closed_at}}function getExecutorReadPort(){let envPort=process.env.GENIE_EXECUTOR_READ_PORT;if(envPort){let parsed=Number.parseInt(envPort,10);if(!Number.isNaN(parsed)&&parsed>0&&parsed<65536)return parsed}return getAuxiliaryPortBase()+2}async function handleStateRoute(id){if(!UUID_RE.test(id))return Response.json({error:"invalid executor id"},{status:400});try{let reply=await readExecutorState(id);if(!reply)return Response.json({error:"not found"},{status:404});return Response.json(reply,{headers:{"Cache-Control":"no-store"}})}catch(err){let msg=err instanceof Error?err.message:String(err);return Response.json({error:msg},{status:500})}}async function routeRequest(req,port){let url=new URL(req.url);if(req.method==="GET"&&url.pathname==="/health")return Response.json({status:"ok",port});if(req.method!=="GET")return new Response("Method Not Allowed",{status:405});let match=ROUTE_RE.exec(url.pathname);if(!match)return new Response("Not Found",{status:404});return handleStateRoute(match[1])}async function startExecutorReadEndpoint(){if(server2)return!0;let port=getExecutorReadPort();try{return server2=Bun.serve({port,hostname:"127.0.0.1",fetch:(req)=>routeRequest(req,port)}),!0}catch(err){let message=err instanceof Error?err.message:String(err);if(message.includes("EADDRINUSE")||message.includes("address already in use"))console.warn(`Executor read endpoint: port ${port} already in use \u2014 skipping`);else console.warn(`Executor read endpoint: failed to start on port ${port}: ${message}`);return!1}}async function stopExecutorReadEndpoint(){if(server2)await server2.stop(!0),server2=null}function isExecutorReadEndpointRunning(){return server2!==null}var server2=null,UUID_RE,ROUTE_RE;var init_executor_read=__esm(()=>{init_db();UUID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ROUTE_RE=/^\/executors\/([^/]+)\/state\/?$/});var exports_omni_approval_handler={};__export(exports_omni_approval_handler,{startOmniApprovalHandler:()=>startOmniApprovalHandler});class OmniApprovalHandler{nc=null;subs=[];sc=import_nats2.StringCodec();permissions;natsUrl;approveTokens;denyTokens;constructor(config){this.permissions=config.permissions,this.natsUrl=config.natsUrl??"localhost:4222",this.approveTokens=(config.permissions.approveTokens??DEFAULT_APPROVE_TOKENS).map((t)=>t.toLowerCase()),this.denyTokens=(config.permissions.denyTokens??DEFAULT_DENY_TOKENS).map((t)=>t.toLowerCase())}async start(){let{omniChat,omniInstance}=this.permissions;if(!omniChat||!omniInstance)return;this.nc=await import_nats2.connect({servers:this.natsUrl});let messageTopic=`omni.message.${omniInstance}.>`,msgSub=this.nc.subscribe(messageTopic);this.subs.push(msgSub),this.processMessages(msgSub);let eventSub=this.nc.subscribe("omni.event.>");this.subs.push(eventSub),this.processEvents(eventSub),handlerInstance=this,console.log(`[omni-approval] Listening for approval replies on ${messageTopic}`)}async stop(){for(let sub of this.subs)sub.unsubscribe();if(this.subs=[],this.nc)await this.nc.close(),this.nc=null;if(handlerInstance===this)handlerInstance=null}async processMessages(sub){for await(let msg of sub)try{let data=JSON.parse(this.sc.decode(msg.data));if((data.chatId??this.extractChatIdFromSubject(msg.subject))!==this.permissions.omniChat)continue;if(data.content)await this.handleTextReply(data.content,data.sender??"whatsapp-user")}catch{}}async processEvents(sub){for await(let msg of sub)try{let data=JSON.parse(this.sc.decode(msg.data));if(data.type!=="reaction")continue;if(data.chatId!==this.permissions.omniChat||data.instanceId!==this.permissions.omniInstance)continue;if(data.emoji&&data.messageId)await this.handleReaction(data.emoji,data.messageId,data.sender??"whatsapp-user")}catch{}}extractChatIdFromSubject(subject){let parts=subject.split(".");if(parts.length>=4)return parts.slice(3).join(".");return}async handleTextReply(content,sender){let normalized=content.trim().toLowerCase();if(!normalized)return!1;let decision=null;if(this.approveTokens.includes(normalized))decision="allow";else if(this.denyTokens.includes(normalized))decision="deny";if(!decision)return!1;let pending=await listPendingApprovals();if(pending.length===0)return!1;let oldest=pending[0],resolved=await resolveApproval(oldest.id,decision,sender);if(resolved)console.log(`[omni-approval] Resolved ${oldest.id} as ${decision} by ${sender} (text: "${normalized}")`);return resolved}async handleReaction(emoji,messageId,sender){let decision=null;if(DEFAULT_APPROVE_REACTIONS.includes(emoji))decision="allow";else if(DEFAULT_DENY_REACTIONS.includes(emoji))decision="deny";if(!decision)return!1;try{let sql=await getConnection(),[approval]=await sql`
2383
2383
  SELECT id FROM approvals
2384
2384
  WHERE omni_message_id = ${messageId} AND decision = 'pending'
2385
- `;if(approval){let resolved2=await resolveApproval(approval.id,decision,sender);if(resolved2)console.log(`[omni-approval] Resolved ${approval.id} via reaction ${emoji} by ${sender}`);return resolved2}}catch{}let pending=await listPendingApprovals();if(pending.length===0)return!1;let resolved=await resolveApproval(pending[0].id,decision,sender);if(resolved)console.log(`[omni-approval] Resolved ${pending[0].id} via reaction ${emoji} by ${sender} (fallback)`);return resolved}}async function startOmniApprovalHandler(natsUrl){let ws=findWorkspace();if(!ws)return null;let config=getWorkspaceConfig(ws.root);if(!config.permissions?.omniChat||!config.permissions?.omniInstance)return null;let handler=new OmniApprovalHandler({natsUrl,permissions:config.permissions});return await handler.start(),handler}var import_nats2,DEFAULT_APPROVE_TOKENS,DEFAULT_DENY_TOKENS,DEFAULT_APPROVE_REACTIONS,DEFAULT_DENY_REACTIONS,handlerInstance=null;var init_omni_approval_handler=__esm(()=>{init_db();init_claude_sdk_remote_approval();init_workspace();import_nats2=__toESM(require_mod4(),1),DEFAULT_APPROVE_TOKENS=["y","yes","approve","sim"],DEFAULT_DENY_TOKENS=["n","no","deny","nao"],DEFAULT_APPROVE_REACTIONS=["\uD83D\uDC4D","\u2705","\uD83D\uDC4C"],DEFAULT_DENY_REACTIONS=["\uD83D\uDC4E","\u274C","\uD83D\uDEAB"]});function isValid2(value){return typeof value==="string"&&VALID.has(value)}function resolveExecutorType(override){if(isValid2(override))return override;let env=process.env.GENIE_EXECUTOR;if(isValid2(env))return env;try{let persisted=loadGenieConfigSync().omni?.executor;if(isValid2(persisted))return persisted}catch{}return"tmux"}var VALID;var init_executor_config=__esm(()=>{init_genie_config2();VALID=new Set(["tmux","sdk"])});class BridgeSessionStore{sql;constructor(sql){this.sql=sql}async create(opts){let[row]=await this.sql`
2385
+ `;if(approval){let resolved2=await resolveApproval(approval.id,decision,sender);if(resolved2)console.log(`[omni-approval] Resolved ${approval.id} via reaction ${emoji} by ${sender}`);return resolved2}}catch{}let pending=await listPendingApprovals();if(pending.length===0)return!1;let resolved=await resolveApproval(pending[0].id,decision,sender);if(resolved)console.log(`[omni-approval] Resolved ${pending[0].id} via reaction ${emoji} by ${sender} (fallback)`);return resolved}}async function startOmniApprovalHandler(natsUrl){let ws=findWorkspace();if(!ws)return null;let config=getWorkspaceConfig(ws.root);if(!config.permissions?.omniChat||!config.permissions?.omniInstance)return null;let handler=new OmniApprovalHandler({natsUrl,permissions:config.permissions});return await handler.start(),handler}var import_nats2,DEFAULT_APPROVE_TOKENS,DEFAULT_DENY_TOKENS,DEFAULT_APPROVE_REACTIONS,DEFAULT_DENY_REACTIONS,handlerInstance=null;var init_omni_approval_handler=__esm(()=>{init_db();init_claude_sdk_remote_approval();init_workspace();import_nats2=__toESM(require_mod4(),1),DEFAULT_APPROVE_TOKENS=["y","yes","approve","sim"],DEFAULT_DENY_TOKENS=["n","no","deny","nao"],DEFAULT_APPROVE_REACTIONS=["\uD83D\uDC4D","\u2705","\uD83D\uDC4C"],DEFAULT_DENY_REACTIONS=["\uD83D\uDC4E","\u274C","\uD83D\uDEAB"]});function isValid2(value){return typeof value==="string"&&VALID.has(value)}function resolveExecutorType(override){if(isValid2(override))return override;let env=process.env.GENIE_EXECUTOR;if(isValid2(env))return env;try{let persisted=loadGenieConfigSync().omni?.executor;if(isValid2(persisted))return persisted}catch{}return"tmux"}var VALID;var init_executor_config=__esm(()=>{init_genie_config2();VALID=new Set(["tmux","sdk"])});function resolveHeartbeatIntervalMs(explicit){let envRaw=process.env.OMNI_HEARTBEAT_INTERVAL_MS,envParsed=envRaw!==void 0?Number(envRaw):Number.NaN,candidate=explicit??(Number.isFinite(envParsed)&&envParsed>0?envParsed:DEFAULT_INTERVAL_MS);if(!Number.isFinite(candidate)||candidate<=0)return DEFAULT_INTERVAL_MS;return Math.min(Math.max(candidate,MIN_INTERVAL_MS),MAX_INTERVAL_MS)}function heartbeatSubject(instanceId,chatId){return`omni.agent.heartbeat.${instanceId}.${chatId}`}class HeartbeatPublisher{intervalMs;publishFn;setIntervalFn;clearIntervalFn;nowFn;active=new Map;constructor(options={}){if(this.intervalMs=resolveHeartbeatIntervalMs(options.intervalMs),this.nowFn=options.now??Date.now,this.setIntervalFn=options.setInterval??globalThis.setInterval,this.clearIntervalFn=options.clearInterval??globalThis.clearInterval,options.publish)this.publishFn=options.publish;else if(options.natsConnection){let sc=import_nats3.StringCodec(),nc=options.natsConnection;this.publishFn=(subject,payload)=>nc.publish(subject,sc.encode(payload))}else this.publishFn=()=>{}}getIntervalMs(){return this.intervalMs}size(){return this.active.size}start(sessionKey2,ctx){let existing=this.active.get(sessionKey2);if(existing)this.clearIntervalFn(existing.handle);let handle=this.setIntervalFn(()=>{this.tick(sessionKey2)},this.intervalMs);if(typeof handle.unref==="function")handle.unref?.();this.active.set(sessionKey2,{handle,ctx})}stop(sessionKey2){let entry2=this.active.get(sessionKey2);if(!entry2)return;this.clearIntervalFn(entry2.handle),this.active.delete(sessionKey2)}stopAll(){for(let[,entry2]of this.active)this.clearIntervalFn(entry2.handle);this.active.clear()}async tick(sessionKey2){let entry2=this.active.get(sessionKey2);if(!entry2)return;let{ctx}=entry2,busy;try{busy=await ctx.isBusy()}catch(err){console.warn(`[agent-heartbeat] isBusy threw for ${sessionKey2} \u2014 treating as idle (skip): ${err instanceof Error?err.message:String(err)}`);return}if(!busy)return;if(!this.active.has(sessionKey2))return;let event={turnId:ctx.turnId,instanceId:ctx.instanceId,chatId:ctx.chatId,timestamp:new Date(this.nowFn()).toISOString()};try{this.publishFn(heartbeatSubject(ctx.instanceId,ctx.chatId),JSON.stringify(event))}catch(err){console.warn(`[agent-heartbeat] publish failed for ${sessionKey2}: ${err instanceof Error?err.message:String(err)}`)}}}var import_nats3,DEFAULT_INTERVAL_MS=30000,MIN_INTERVAL_MS=5000,MAX_INTERVAL_MS=60000;var init_agent_heartbeat=__esm(()=>{import_nats3=__toESM(require_mod4(),1)});class BridgeSessionStore{sql;constructor(sql){this.sql=sql}async create(opts){let[row]=await this.sql`
2386
2386
  INSERT INTO genie_bridge_sessions (
2387
2387
  instance_id, chat_id, agent_name, executor_id,
2388
2388
  tmux_pane_id, claude_session_id, metadata
@@ -2451,7 +2451,7 @@ The user's message:
2451
2451
 
2452
2452
  ---
2453
2453
 
2454
- ${initialMessage}`:turnContext,permissions=entry2.permissions?.allow?.length||entry2.permissions?.deny?.length?{allow:entry2.permissions.allow,deny:entry2.permissions.deny}:void 0;return{provider:entry2.provider??"claude",team:agentName,role:agentName,sessionId:resumeClaudeSessionId?void 0:randomUUID9(),resume:resumeClaudeSessionId,model:entry2.model,promptMode:entry2.promptMode,systemPromptFile:join58(entry2.dir,"AGENTS.md"),initialPrompt:fullInitialPrompt,skipHooks:!0,permissions,disallowedTools:entry2.disallowedTools,nativeTeam:{enabled:!0,agentName,color:entry2.color??void 0}}}function resolveOmniPaneProcessName(provider){return provider==="codex"?"codex":"claude"}class ClaudeCodeOmniExecutor{sessions=new Map;safePgCall=null;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(_fn){}async injectNudge(session,text){let paneId=session.tmux?.paneId;if(!paneId)return;let nudgeText=`[system] ${text}`;await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(nudgeText)} Enter`)}async launchOmniProcessInPane(agentName,chatId,entry2,env,paneId,initialMessage,resumeClaudeSessionId){let omniEnv={...env,GENIE_OMNI_CHAT_ID:chatId,GENIE_OMNI_AGENT:agentName},params=buildOmniSpawnParams(agentName,chatId,entry2,omniEnv,initialMessage,resumeClaudeSessionId),claudeSessionId=resumeClaudeSessionId??params.sessionId??void 0,launch=buildLaunchCommand(params),allEnv={...omniEnv,...launch.env},envPrefix=Object.entries(allEnv).map(([k,v])=>`${k}=${shellQuote(v)}`).join(" "),cmd=envPrefix?`${envPrefix} ${launch.command}`:launch.command;return await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(cmd)} Enter`),claudeSessionId}async spawn(agentName,chatId,env,initialMessage){let resolved=await resolve6(agentName);if(!resolved)throw Error(`Agent "${agentName}" not found in genie directory`);let entry2=resolved.entry,tmuxSession=resolveBridgeTmuxSession(agentName,entry2.bridgeTmuxSession,env.GENIE_TMUX_SESSION),chatName=await lookupChatName(chatId,env.OMNI_INSTANCE??""),windowName=sanitizeWindowName2(chatId,chatName??void 0),{paneId,created}=await ensureTeamWindow(tmuxSession,windowName,entry2.dir),instanceId=env.OMNI_INSTANCE??"",agent=this.safePgCall?await this.safePgCall("tmux-find-or-create-agent",()=>findOrCreateAgent(`${agentName}:${chatId}`,"omni","omni"),null,{chatId}):null,existingExecutor=agent?await this.safePgCall?.("tmux-find-existing-executor",()=>findLatestByMetadata({agentId:agent.id,source:"omni",chatId}),null,{chatId})??null:null,resumeClaudeSessionId=existingExecutor?.claudeSessionId??void 0,processName=resolveOmniPaneProcessName(entry2.provider),processRunning=!created&&await isPaneProcessRunning(paneId,processName),claudeSessionId=created||!processRunning?await this.launchOmniProcessInPane(agentName,chatId,entry2,env,paneId,initialMessage,resumeClaudeSessionId):resumeClaudeSessionId,sessionKey2=`${agentName}:${chatId}`,registration=await this.registerOrRelinkExecutor(agent,existingExecutor,chatId,instanceId,tmuxSession,windowName,paneId,entry2.dir,claudeSessionId);if(this.sessions.set(sessionKey2,{executorId:registration?.executorId??null,agentId:registration?.agentId??null,repoPath:entry2.dir}),registration?.executorId)await this.updateState(registration.executorId,"running",chatId);let now=Date.now();return{id:sessionKey2,agentName,chatId,executorType:"tmux",createdAt:now,lastActivityAt:now,tmux:{session:tmuxSession,window:windowName,paneId,claudeSessionId,processName}}}async registerOrRelinkExecutor(agent,existingExecutor,chatId,instanceId,tmuxSession,tmuxWindow,tmuxPaneId,repoPath,claudeSessionId){if(!this.safePgCall||!agent)return null;if(await this.safePgCall("tmux-update-agent-pane",async()=>{await(await Promise.resolve().then(() => (init_db(),exports_db)).then((m)=>m.getConnection()))`
2454
+ ${initialMessage}`:turnContext,permissions=entry2.permissions?.allow?.length||entry2.permissions?.deny?.length?{allow:entry2.permissions.allow,deny:entry2.permissions.deny}:void 0;return{provider:entry2.provider??"claude",team:agentName,role:agentName,sessionId:resumeClaudeSessionId?void 0:randomUUID9(),resume:resumeClaudeSessionId,model:entry2.model,promptMode:entry2.promptMode,systemPromptFile:join58(entry2.dir,"AGENTS.md"),initialPrompt:fullInitialPrompt,skipHooks:!0,permissions,disallowedTools:entry2.disallowedTools,nativeTeam:{enabled:!0,agentName,color:entry2.color??void 0}}}function resolveOmniPaneProcessName(provider){return provider==="codex"?"codex":"claude"}class ClaudeCodeOmniExecutor{sessions=new Map;safePgCall=null;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(_fn){}async injectNudge(session,text){let paneId=session.tmux?.paneId;if(!paneId)return;let nudgeText=`[system] ${text}`;await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(nudgeText)} Enter`)}async launchOmniProcessInPane(agentName,chatId,entry2,env,paneId,initialMessage,resumeClaudeSessionId){let omniEnv={...env,GENIE_OMNI_CHAT_ID:chatId,GENIE_OMNI_AGENT:agentName},params=buildOmniSpawnParams(agentName,chatId,entry2,omniEnv,initialMessage,resumeClaudeSessionId),claudeSessionId=resumeClaudeSessionId??params.sessionId??void 0,launch=buildLaunchCommand(params),allEnv={...omniEnv,...launch.env},envPrefix=Object.entries(allEnv).map(([k,v])=>`${k}=${shellQuote(v)}`).join(" "),cmd=envPrefix?`${envPrefix} ${launch.command}`:launch.command;return await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(cmd)} Enter`),claudeSessionId}async spawn(agentName,chatId,env,initialMessage){let resolved=await resolve6(agentName);if(!resolved)throw Error(`Agent "${agentName}" not found in genie directory`);let entry2=resolved.entry,tmuxSession=resolveBridgeTmuxSession(agentName,entry2.bridgeTmuxSession,env.GENIE_TMUX_SESSION),chatName=await lookupChatName(chatId,env.OMNI_INSTANCE??""),windowName=sanitizeWindowName2(chatId,chatName??void 0),{paneId,created}=await ensureTeamWindow(tmuxSession,windowName,entry2.dir),instanceId=env.OMNI_INSTANCE??"",agent=this.safePgCall?await this.safePgCall("tmux-find-or-create-agent",()=>findOrCreateAgent(`${agentName}:${chatId}`,"omni","omni"),null,{chatId}):null,existingExecutor=agent?await this.safePgCall?.("tmux-find-existing-executor",()=>findLatestByMetadata({agentId:agent.id,source:"omni",chatId}),null,{chatId})??null:null,resumeClaudeSessionId=existingExecutor?.claudeSessionId??void 0,processName=resolveOmniPaneProcessName(entry2.provider),processRunning=!created&&await isPaneProcessRunning(paneId,processName),claudeSessionId=created||!processRunning?await this.launchOmniProcessInPane(agentName,chatId,entry2,env,paneId,initialMessage,resumeClaudeSessionId):resumeClaudeSessionId,sessionKey2=`${agentName}:${chatId}`,registration=await this.registerOrRelinkExecutor(agent,existingExecutor,chatId,instanceId,tmuxSession,windowName,paneId,entry2.dir,claudeSessionId);if(this.sessions.set(sessionKey2,{executorId:registration?.executorId??null,agentId:registration?.agentId??null,repoPath:entry2.dir,lastPaneFingerprint:null}),registration?.executorId)await this.updateState(registration.executorId,"running",chatId);let now=Date.now();return{id:sessionKey2,agentName,chatId,executorType:"tmux",createdAt:now,lastActivityAt:now,tmux:{session:tmuxSession,window:windowName,paneId,claudeSessionId,processName}}}async registerOrRelinkExecutor(agent,existingExecutor,chatId,instanceId,tmuxSession,tmuxWindow,tmuxPaneId,repoPath,claudeSessionId){if(!this.safePgCall||!agent)return null;if(await this.safePgCall("tmux-update-agent-pane",async()=>{await(await Promise.resolve().then(() => (init_db(),exports_db)).then((m)=>m.getConnection()))`
2455
2455
  UPDATE agents
2456
2456
  SET pane_id = ${tmuxPaneId},
2457
2457
  session = ${tmuxSession},
@@ -2473,9 +2473,9 @@ ${initialMessage}`:turnContext,permissions=entry2.permissions?.allow?.length||en
2473
2473
 
2474
2474
  ---
2475
2475
 
2476
- [${senderName}]: ${message.content}`,paneId=session.tmux?.paneId;if(paneId&&/^%\d+$/.test(paneId)&&await isPaneAlive(paneId))try{await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(body)}`),await new Promise((resolve11)=>setTimeout(resolve11,200)),await executeTmux2(`send-keys -t '${paneId}' Enter`)}catch(err){console.error(`[claude-code] deliver: send-keys failed for ${session.id} (pane ${paneId}):`,err instanceof Error?err.message:err)}else console.error(`[claude-code] deliver: pane unavailable for ${session.id} (paneId=${paneId??"null"}), message lost`);if(session.lastActivityAt=Date.now(),state?.executorId)await this.updateState(state.executorId,"idle",session.chatId)}async shutdown(session){let state=this.sessions.get(session.id);try{if(session.tmux)await killWindow(session.tmux.session,session.tmux.window)}finally{if(state?.executorId&&this.safePgCall)await this.safePgCall("tmux-terminate-executor",()=>terminateExecutor(state.executorId),void 0,{executorId:state.executorId,chatId:session.chatId});if(state?.agentId&&this.safePgCall)await this.safePgCall("tmux-unregister-agent",()=>unregister(state.agentId),void 0,{chatId:session.chatId});this.sessions.delete(session.id)}}async isAlive(session){let paneId=session.tmux?.paneId;if(!paneId)return!1;try{if(!await isPaneAlive(paneId))return!1;return await isPaneProcessRunning(paneId,session.tmux?.processName??"claude")}catch{return!1}}}var init_claude_code2=__esm(()=>{init_agent_directory();init_agent_registry();init_executor_registry();init_omni_signature();init_provider_adapters();init_team_lead_command();init_tmux()});async function recordAuditEvent2(safePgCall,type2,attrs){let entityType=attrs.entity_type??"executor",entityId=attrs.entity_id??attrs.executor_id??"",actor=attrs.actor??attrs.agent_id??null,{entity_type:_et,entity_id:_eid,actor:_a,...details}=attrs;await safePgCall(`audit:${type2}`,(sql)=>sql`INSERT INTO audit_events (entity_type, entity_id, event_type, actor, details)
2476
+ [${senderName}]: ${message.content}`,paneId=session.tmux?.paneId;if(paneId&&/^%\d+$/.test(paneId)&&await isPaneAlive(paneId))try{await executeTmux2(`send-keys -t '${paneId}' ${shellQuote(body)}`),await new Promise((resolve11)=>setTimeout(resolve11,200)),await executeTmux2(`send-keys -t '${paneId}' Enter`)}catch(err){console.error(`[claude-code] deliver: send-keys failed for ${session.id} (pane ${paneId}):`,err instanceof Error?err.message:err)}else console.error(`[claude-code] deliver: pane unavailable for ${session.id} (paneId=${paneId??"null"}), message lost`);if(session.lastActivityAt=Date.now(),state?.executorId)await this.updateState(state.executorId,"idle",session.chatId)}async shutdown(session){let state=this.sessions.get(session.id);try{if(session.tmux)await killWindow(session.tmux.session,session.tmux.window)}finally{if(state?.executorId&&this.safePgCall)await this.safePgCall("tmux-terminate-executor",()=>terminateExecutor(state.executorId),void 0,{executorId:state.executorId,chatId:session.chatId});if(state?.agentId&&this.safePgCall)await this.safePgCall("tmux-unregister-agent",()=>unregister(state.agentId),void 0,{chatId:session.chatId});this.sessions.delete(session.id)}}async isAlive(session){let paneId=session.tmux?.paneId;if(!paneId)return!1;try{if(!await isPaneAlive(paneId))return!1;return await isPaneProcessRunning(paneId,session.tmux?.processName??"claude")}catch{return!1}}async isBusy(session){let state=this.sessions.get(session.id),paneId=session.tmux?.paneId;if(!state||!paneId)return!1;let content;try{content=await capturePaneContent(paneId,200,!1)}catch{return!1}let fingerprint=`${content.length}:${content.slice(-128)}`,previous=state.lastPaneFingerprint;if(state.lastPaneFingerprint=fingerprint,previous===null)return!0;return previous!==fingerprint}}var init_claude_code2=__esm(()=>{init_agent_directory();init_agent_registry();init_executor_registry();init_omni_signature();init_provider_adapters();init_team_lead_command();init_tmux()});async function recordAuditEvent2(safePgCall,type2,attrs){let entityType=attrs.entity_type??"executor",entityId=attrs.entity_id??attrs.executor_id??"",actor=attrs.actor??attrs.agent_id??null,{entity_type:_et,entity_id:_eid,actor:_a,...details}=attrs;await safePgCall(`audit:${type2}`,(sql)=>sql`INSERT INTO audit_events (entity_type, entity_id, event_type, actor, details)
2477
2477
  VALUES (${entityType}, ${entityId}, ${type2}, ${actor}, ${JSON.stringify(details)})`,void 0,{executorId:entityId,chatId:attrs.chat_id??""})}async function loadSystemPrompt(entry2){let identityPath=loadIdentity(entry2);if(!identityPath)return;let{readFileSync:readFileSync28}=await import("fs");try{return readFileSync28(identityPath,"utf-8")}catch{return}}async function resolveSystemPrompt(entry2,state){let prompt2=await loadSystemPrompt(entry2),isTurnBased=Boolean(state.env.OMNI_INSTANCE);return{prompt:prompt2,isTurnBased}}function buildTurnContextPrefix(state,message,chatId){if(!state.env.OMNI_INSTANCE)return"";return buildTurnBasedPrompt(message.sender,state.env.OMNI_INSTANCE,state.env.OMNI_CHAT??chatId)}function extractTextFromAssistant(msg){if(!msg.message)return[];return msg.message.content.filter((b2)=>b2.type==="text"&&b2.text).map((b2)=>b2.text)}async function collectQueryResult(queryMessages){let textParts=[],sessionId;try{for await(let msg of queryMessages){if(msg.type==="assistant")textParts.push(...extractTextFromAssistant(msg));if(msg.type==="result"&&msg.subtype==="success")sessionId=msg.session_id}}catch(err){if(err.name==="AbortError")return{text:""};throw err}return{text:textParts.join(`
2478
- `).trim(),sessionId}}function buildReplyPayload(agent,chatId,instanceId,extra={}){return JSON.stringify({content:"",agent,chat_id:chatId,instance_id:instanceId,timestamp:new Date().toISOString(),...extra})}function resolveAction(params,env){if(params.skip)return{type:"skip",extra:{reason:params.reason},label:"Turn closed (skip)."};if(params.react)return{type:"react",extra:{react:String(params.react),message_id:env.OMNI_MESSAGE??""},label:`Reacted ${params.react} + turn closed.`};if(params.media)return{type:"media",extra:{content:params.caption?String(params.caption):params.text?String(params.text):"",media:String(params.media)},label:"Media sent + turn closed."};if(params.text)return{type:"text",extra:{content:String(params.text)},label:"Turn closed. Message delivered."};return{type:"skip",extra:{},label:"Turn closed (skip)."}}function handleDoneTool(params,env,natsPublish){let instanceId=env.OMNI_INSTANCE??"",chatId=env.OMNI_CHAT??"",agent=env.OMNI_AGENT??"",action=resolveAction(params,env);if(action.type==="skip"){if(natsPublish&&instanceId&&chatId)natsPublish(`omni.turn.done.${instanceId}.${chatId}`,JSON.stringify({action:"skip",...action.extra}));return action.label}if(!natsPublish||!instanceId||!chatId)return console.warn("[claude-sdk] No NATS publish available \u2014 reply dropped"),"Turn close attempted but NATS publish not available.";return natsPublish(`omni.reply.${instanceId}.${chatId}`,buildReplyPayload(agent,chatId,instanceId,action.extra)),action.label}function parseSendMessageInput(input){if(!input||typeof input!=="object")return{};let obj=input,recipient=typeof obj.recipient==="string"?obj.recipient:typeof obj.to==="string"?obj.to:void 0,body=typeof obj.message==="string"?obj.message:typeof obj.content==="string"?obj.content:void 0;return{recipient,body}}function createSendMessageOmniHook(env,natsPublish){return async(input)=>{let hookInput=input;if(hookInput.tool_name!=="SendMessage")return{};let{recipient,body}=parseSendMessageInput(hookInput.tool_input);if(recipient!=="omni")return{};let instanceId=env.OMNI_INSTANCE??"",chatId=env.OMNI_CHAT??"",agent=env.OMNI_AGENT??"";if(!instanceId||!chatId)return{};if(!body||body.trim()==="")return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:'SendMessage(recipient: "omni") requires a non-empty `message` field. Retry with the reply text.'}};if(!natsPublish)return console.warn("[claude-sdk] SendMessage(to: omni) intercepted but NATS publish unavailable \u2014 message dropped"),{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Omni bridge unavailable \u2014 message could not be delivered."}};return natsPublish(`omni.reply.${instanceId}.${chatId}`,buildReplyPayload(agent,chatId,instanceId,{content:body})),{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Message delivered to user via omni bridge."}}}}async function createDoneMcpServer(env,natsPublish){let{createSdkMcpServer,tool}=await import("@anthropic-ai/claude-agent-sdk");return createSdkMcpServer({name:"genie-omni-tools",tools:[tool("done","Close this turn. REQUIRED after processing the user message. Sends a final response, reacts, or skips. Call exactly once per turn.",{text:exports_external.string().optional().describe("Final message to the user"),media:exports_external.string().optional().describe("File path for media attachment"),caption:exports_external.string().optional().describe("Caption for media"),react:exports_external.string().optional().describe("Emoji reaction (instead of text)"),skip:exports_external.boolean().optional().describe("Close turn without sending anything"),reason:exports_external.string().optional().describe("Internal reason for skipping")},async(args)=>{return{content:[{type:"text",text:handleDoneTool(args,env,natsPublish)}]}})]})}class ClaudeSdkOmniExecutor{sessions=new Map;safePgCall=null;natsPublish=null;deliveryQueues=new Map;pendingNudges=new Map;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(fn){this.natsPublish=fn}async injectNudge(session,text){if(!this.sessions.has(session.id))return;this.pendingNudges.set(session.id,text)}async spawn(agentName,chatId,env,_initialMessage){if(!await resolve6(agentName))throw Error(`Agent "${agentName}" not found in genie directory`);let provider=new ClaudeSdkProvider,abortController=new AbortController,sessionId=`${agentName}:${chatId}`,registration=await this.registerInWorldA(agentName,chatId,env.OMNI_INSTANCE??"");if(this.sessions.set(sessionId,{abortController,running:!0,provider,executorId:registration?.executorId??null,claudeSessionId:registration?.claudeSessionId,dbSessionId:null,turnIndex:0,env}),registration?.executorId)await this.updateState(registration.executorId,"running",chatId);let now=Date.now();return{id:sessionId,agentName,chatId,executorType:"sdk",createdAt:now,lastActivityAt:now,sdk:{claudeSessionId:registration?.claudeSessionId,executorId:registration?.executorId}}}async registerInWorldA(agentName,chatId,instanceId){if(!this.safePgCall)return null;let agent=await this.safePgCall("sdk-find-or-create-agent",()=>findOrCreateAgent(agentName,"omni","omni"),null,{chatId});if(!agent)return null;let existing=await this.safePgCall("sdk-find-existing-executor",()=>findLatestByMetadata({agentId:agent.id,source:"omni",chatId}),null,{chatId});if(existing)return await this.safePgCall("sdk-relink-executor",()=>relinkExecutorToAgent(existing.id,agent.id),void 0,{executorId:existing.id,chatId}),await recordAuditEvent2(this.safePgCall,"session.resumed",{executor_id:existing.id,agent_id:agentName,chat_id:chatId,claude_session_id:existing.claudeSessionId}),{executorId:existing.id,claudeSessionId:existing.claudeSessionId??void 0};let executor=await this.safePgCall("sdk-create-executor",()=>createAndLinkExecutor(agent.id,"claude","api",{claudeSessionId:void 0,metadata:{source:"omni",chat_id:chatId,instance_id:instanceId}}),null,{chatId});if(executor)await recordAuditEvent2(this.safePgCall,"session.created_fresh",{executor_id:executor.id,agent_id:agentName,chat_id:chatId});return executor?{executorId:executor.id}:null}async updateState(executorId,state,chatId){if(!this.safePgCall)return;await this.safePgCall("sdk-update-executor-state",()=>updateExecutorState(executorId,state),void 0,{executorId,chatId})}async deliver(session,message){let state=this.sessions.get(session.id);if(!state)throw Error(`No SDK session found for ${session.id}`);let current=(this.deliveryQueues.get(session.id)??Promise.resolve()).then(()=>this._processDelivery(session,state,message));this.deliveryQueues.set(session.id,current.catch(()=>{}))}async _processDelivery(session,state,message){let resolved=await resolve6(session.agentName);if(!resolved)throw Error(`Agent "${session.agentName}" not found in genie directory`);let entry2=resolved.entry,permissionConfig=resolvePermissionConfig(entry2.permissions),{prompt:systemPrompt,isTurnBased}=await resolveSystemPrompt(entry2,state);if(state.executorId)await this.updateState(state.executorId,"working",session.chatId);if(this.safePgCall)await recordAuditEvent2(this.safePgCall,"deliver.start",{executor_id:state.executorId??session.id,agent_id:session.agentName,chat_id:message.chatId,instance_id:message.instanceId});let doneMcp=await createDoneMcpServer(state.env,this.natsPublish),sendMessageHooks=isTurnBased?{PreToolUse:[{matcher:"SendMessage",hooks:[createSendMessageOmniHook(state.env,this.natsPublish)]}]}:void 0,extraOptions={abortController:state.abortController,mcpServers:{"genie-omni-tools":doneMcp},...sendMessageHooks&&{hooks:sendMessageHooks}};if(state.claudeSessionId)extraOptions.resume=state.claudeSessionId;let turnPrefix=buildTurnContextPrefix(state,message,session.chatId),queryContent=message.content,pendingNudge=this.pendingNudges.get(session.id);if(pendingNudge)queryContent=`[system] ${pendingNudge}
2478
+ `).trim(),sessionId}}function buildReplyPayload(agent,chatId,instanceId,extra={}){return JSON.stringify({content:"",agent,chat_id:chatId,instance_id:instanceId,timestamp:new Date().toISOString(),...extra})}function resolveAction(params,env){if(params.skip)return{type:"skip",extra:{reason:params.reason},label:"Turn closed (skip)."};if(params.react)return{type:"react",extra:{react:String(params.react),message_id:env.OMNI_MESSAGE??""},label:`Reacted ${params.react} + turn closed.`};if(params.media)return{type:"media",extra:{content:params.caption?String(params.caption):params.text?String(params.text):"",media:String(params.media)},label:"Media sent + turn closed."};if(params.text)return{type:"text",extra:{content:String(params.text)},label:"Turn closed. Message delivered."};return{type:"skip",extra:{},label:"Turn closed (skip)."}}function handleDoneTool(params,env,natsPublish){let instanceId=env.OMNI_INSTANCE??"",chatId=env.OMNI_CHAT??"",agent=env.OMNI_AGENT??"",action=resolveAction(params,env);if(action.type==="skip"){if(natsPublish&&instanceId&&chatId)natsPublish(`omni.turn.done.${instanceId}.${chatId}`,JSON.stringify({action:"skip",...action.extra}));return action.label}if(!natsPublish||!instanceId||!chatId)return console.warn("[claude-sdk] No NATS publish available \u2014 reply dropped"),"Turn close attempted but NATS publish not available.";return natsPublish(`omni.reply.${instanceId}.${chatId}`,buildReplyPayload(agent,chatId,instanceId,action.extra)),action.label}function parseSendMessageInput(input){if(!input||typeof input!=="object")return{};let obj=input,recipient=typeof obj.recipient==="string"?obj.recipient:typeof obj.to==="string"?obj.to:void 0,body=typeof obj.message==="string"?obj.message:typeof obj.content==="string"?obj.content:void 0;return{recipient,body}}function createSendMessageOmniHook(env,natsPublish){return async(input)=>{let hookInput=input;if(hookInput.tool_name!=="SendMessage")return{};let{recipient,body}=parseSendMessageInput(hookInput.tool_input);if(recipient!=="omni")return{};let instanceId=env.OMNI_INSTANCE??"",chatId=env.OMNI_CHAT??"",agent=env.OMNI_AGENT??"";if(!instanceId||!chatId)return{};if(!body||body.trim()==="")return{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:'SendMessage(recipient: "omni") requires a non-empty `message` field. Retry with the reply text.'}};if(!natsPublish)return console.warn("[claude-sdk] SendMessage(to: omni) intercepted but NATS publish unavailable \u2014 message dropped"),{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Omni bridge unavailable \u2014 message could not be delivered."}};return natsPublish(`omni.reply.${instanceId}.${chatId}`,buildReplyPayload(agent,chatId,instanceId,{content:body})),{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Message delivered to user via omni bridge."}}}}async function createDoneMcpServer(env,natsPublish){let{createSdkMcpServer,tool}=await import("@anthropic-ai/claude-agent-sdk");return createSdkMcpServer({name:"genie-omni-tools",tools:[tool("done","Close this turn. REQUIRED after processing the user message. Sends a final response, reacts, or skips. Call exactly once per turn.",{text:exports_external.string().optional().describe("Final message to the user"),media:exports_external.string().optional().describe("File path for media attachment"),caption:exports_external.string().optional().describe("Caption for media"),react:exports_external.string().optional().describe("Emoji reaction (instead of text)"),skip:exports_external.boolean().optional().describe("Close turn without sending anything"),reason:exports_external.string().optional().describe("Internal reason for skipping")},async(args)=>{return{content:[{type:"text",text:handleDoneTool(args,env,natsPublish)}]}})]})}class ClaudeSdkOmniExecutor{sessions=new Map;safePgCall=null;natsPublish=null;deliveryQueues=new Map;pendingNudges=new Map;setSafePgCall(fn){this.safePgCall=fn}setNatsPublish(fn){this.natsPublish=fn}async injectNudge(session,text){if(!this.sessions.has(session.id))return;this.pendingNudges.set(session.id,text)}async spawn(agentName,chatId,env,_initialMessage){if(!await resolve6(agentName))throw Error(`Agent "${agentName}" not found in genie directory`);let provider=new ClaudeSdkProvider,abortController=new AbortController,sessionId=`${agentName}:${chatId}`,registration=await this.registerInWorldA(agentName,chatId,env.OMNI_INSTANCE??"");if(this.sessions.set(sessionId,{abortController,running:!0,provider,executorId:registration?.executorId??null,claudeSessionId:registration?.claudeSessionId,dbSessionId:null,turnIndex:0,env,inFlightDeliveries:0}),registration?.executorId)await this.updateState(registration.executorId,"running",chatId);let now=Date.now();return{id:sessionId,agentName,chatId,executorType:"sdk",createdAt:now,lastActivityAt:now,sdk:{claudeSessionId:registration?.claudeSessionId,executorId:registration?.executorId}}}async registerInWorldA(agentName,chatId,instanceId){if(!this.safePgCall)return null;let agent=await this.safePgCall("sdk-find-or-create-agent",()=>findOrCreateAgent(agentName,"omni","omni"),null,{chatId});if(!agent)return null;let existing=await this.safePgCall("sdk-find-existing-executor",()=>findLatestByMetadata({agentId:agent.id,source:"omni",chatId}),null,{chatId});if(existing)return await this.safePgCall("sdk-relink-executor",()=>relinkExecutorToAgent(existing.id,agent.id),void 0,{executorId:existing.id,chatId}),await recordAuditEvent2(this.safePgCall,"session.resumed",{executor_id:existing.id,agent_id:agentName,chat_id:chatId,claude_session_id:existing.claudeSessionId}),{executorId:existing.id,claudeSessionId:existing.claudeSessionId??void 0};let executor=await this.safePgCall("sdk-create-executor",()=>createAndLinkExecutor(agent.id,"claude","api",{claudeSessionId:void 0,metadata:{source:"omni",chat_id:chatId,instance_id:instanceId}}),null,{chatId});if(executor)await recordAuditEvent2(this.safePgCall,"session.created_fresh",{executor_id:executor.id,agent_id:agentName,chat_id:chatId});return executor?{executorId:executor.id}:null}async updateState(executorId,state,chatId){if(!this.safePgCall)return;await this.safePgCall("sdk-update-executor-state",()=>updateExecutorState(executorId,state),void 0,{executorId,chatId})}async deliver(session,message){let state=this.sessions.get(session.id);if(!state)throw Error(`No SDK session found for ${session.id}`);let current=(this.deliveryQueues.get(session.id)??Promise.resolve()).then(async()=>{state.inFlightDeliveries+=1;try{await this._processDelivery(session,state,message)}finally{state.inFlightDeliveries=Math.max(0,state.inFlightDeliveries-1)}});this.deliveryQueues.set(session.id,current.catch(()=>{}))}async isBusy(session){let state=this.sessions.get(session.id);if(!state)return!1;return state.running&&state.inFlightDeliveries>0}async _processDelivery(session,state,message){let resolved=await resolve6(session.agentName);if(!resolved)throw Error(`Agent "${session.agentName}" not found in genie directory`);let entry2=resolved.entry,permissionConfig=resolvePermissionConfig(entry2.permissions),{prompt:systemPrompt,isTurnBased}=await resolveSystemPrompt(entry2,state);if(state.executorId)await this.updateState(state.executorId,"working",session.chatId);if(this.safePgCall)await recordAuditEvent2(this.safePgCall,"deliver.start",{executor_id:state.executorId??session.id,agent_id:session.agentName,chat_id:message.chatId,instance_id:message.instanceId});let doneMcp=await createDoneMcpServer(state.env,this.natsPublish),sendMessageHooks=isTurnBased?{PreToolUse:[{matcher:"SendMessage",hooks:[createSendMessageOmniHook(state.env,this.natsPublish)]}]}:void 0,extraOptions={abortController:state.abortController,mcpServers:{"genie-omni-tools":doneMcp},...sendMessageHooks&&{hooks:sendMessageHooks}};if(state.claudeSessionId)extraOptions.resume=state.claudeSessionId;let turnPrefix=buildTurnContextPrefix(state,message,session.chatId),queryContent=message.content,pendingNudge=this.pendingNudges.get(session.id);if(pendingNudge)queryContent=`[system] ${pendingNudge}
2479
2479
 
2480
2480
  ${message.content}`,this.pendingNudges.delete(session.id);if(turnPrefix)queryContent=`${turnPrefix}
2481
2481
 
@@ -2524,10 +2524,10 @@ ${queryContent}`;let{messages:queryMessages}=state.provider.runQuery({agentId:se
2524
2524
  UPDATE omni_requests
2525
2525
  SET status = 'pending', started_at = NULL, next_retry_at = ${nextRetry}
2526
2526
  WHERE id = ${id}
2527
- `}}class TurnTracker{turns=new Map;open(sessionKey2,turnId,messageId){this.turns.set(sessionKey2,{turnId,sessionKey:sessionKey2,messageId,startedAt:Date.now(),closed:!1})}close(sessionKey2,action){let turn=this.turns.get(sessionKey2);if(turn&&!turn.closed)turn.closed=!0,turn.closedAction=action}isOpen(sessionKey2){let turn=this.turns.get(sessionKey2);return turn!==void 0&&!turn.closed}getTurnId(sessionKey2){return this.turns.get(sessionKey2)?.turnId}getByTurnId(turnId){for(let turn of this.turns.values())if(turn.turnId===turnId)return turn;return}delete(sessionKey2){this.turns.delete(sessionKey2)}}var exports_omni_bridge={};__export(exports_omni_bridge,{OmniBridge:()=>OmniBridge});import{closeSync as closeSync4,existsSync as existsSync46,mkdirSync as mkdirSync20,openSync as openSync4,readFileSync as readFileSync28,unlinkSync as unlinkSync9,writeSync}from"fs";import{dirname as dirname15}from"path";function withTimeout2(p,ms,label){return new Promise((resolve11,reject)=>{let timer2=setTimeout(()=>reject(Error(`${label} timed out after ${ms}ms`)),ms);timer2.unref?.(),p.then((v)=>{clearTimeout(timer2),resolve11(v)},(err)=>{clearTimeout(timer2),reject(err)})})}function isPgConnectionError(err){if(!err||typeof err!=="object")return!1;let e=err,code=e.code??"";if(["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENOTFOUND","EPIPE","EHOSTUNREACH"].includes(code))return!0;let msg=e.message??String(err);return/ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EPIPE|connection terminated|connection closed|server closed the connection|the database system is shutting down/i.test(msg)}class OmniBridge{nc=null;sub=null;executor;turnTracker=new TurnTracker;sessions=new Map;messageQueue=[];recentMessageIds=new Map;idleCheckTimer=null;sc=import_nats3.StringCodec();sql=null;pgAvailable=!1;pgProvider;natsConnectFn;queueConfig;queue=null;sessionStore=null;natsUrl;idleTimeoutMs;maxConcurrent;executorType;pidfilePath=null;startedAtMs=0;pingSub=null;signalCleanup=null;constructor(config={}){if(this.natsUrl=config.natsUrl??process.env.GENIE_NATS_URL??DEFAULT_NATS_URL2,this.idleTimeoutMs=config.idleTimeoutMs??(process.env.GENIE_IDLE_TIMEOUT_MS?Number(process.env.GENIE_IDLE_TIMEOUT_MS):DEFAULT_IDLE_TIMEOUT_MS2),this.maxConcurrent=config.maxConcurrent??(process.env.GENIE_MAX_CONCURRENT?Number(process.env.GENIE_MAX_CONCURRENT):DEFAULT_MAX_CONCURRENT),this.pgProvider=config.pgProvider??(async()=>{let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return await getConnection2()}),this.natsConnectFn=config.natsConnectFn??import_nats3.connect,this.queueConfig=config.queue??{},this.executorType=resolveExecutorType(config.executorType),this.executorType==="sdk")this.executor=new ClaudeSdkOmniExecutor;else this.executor=new ClaudeCodeOmniExecutor}async start(){if(this.nc){console.log("[omni-bridge] Already running");return}console.log(`[omni-bridge] Connecting to NATS at ${this.natsUrl}...`),this.nc=await this.natsConnectFn({servers:this.natsUrl,name:"genie-omni-bridge",reconnect:!0,maxReconnectAttempts:-1,reconnectTimeWait:2000}),console.log("[omni-bridge] Connected to NATS"),await this.setupPg(),this.wireExecutorHooks(),this.subscribeOmniChannels(),this.startedAtMs=Date.now(),this.subscribePingChannel(),await this.claimPidfile(),this.armSignalCleanup(),this.idleCheckTimer=setInterval(()=>this.checkIdleSessions(),IDLE_CHECK_INTERVAL_MS),console.log(`[omni-bridge] Listening on omni.message.> (max_concurrent=${this.maxConcurrent}, idle_timeout=${this.idleTimeoutMs}ms)`)}async setupPg(){if(await this.probePg(),this.pgAvailable&&this.sql)this.sessionStore=new BridgeSessionStore(this.sql),await this.recoverSessions();if(this.executorType==="sdk"&&this.pgAvailable&&this.sql)this.queue=new OmniQueue(this.sql,(_req,msg)=>this.routeMessage(msg),this.queueConfig),await this.queue.recoverStale(),this.queue.start()}wireExecutorHooks(){this.executor.setSafePgCall(this.safePgCall.bind(this));let sc=this.sc,nc=this.nc;if(!nc)return;this.executor.setNatsPublish((topic,payload)=>{nc.publish(topic,sc.encode(payload))})}subscribeOmniChannels(){if(!this.nc)return;this.sub=this.nc.subscribe("omni.message.>",{queue:"genie-bridge"}),this.processSubscription();let turnSubs=["omni.turn.open.>","omni.turn.done.>","omni.turn.nudge.>","omni.turn.timeout.>"];for(let topic of turnSubs){let sub=this.nc.subscribe(topic,{queue:"genie-bridge"});this.processTurnEvents(sub)}let sessionResetSub=this.nc.subscribe("omni.session.reset.>",{queue:"genie-bridge"});this.processSessionResetEvents(sessionResetSub)}subscribePingChannel(){if(!this.nc)return;this.pingSub=this.nc.subscribe(BRIDGE_PING_SUBJECT);let pingSub=this.pingSub,pingSc=this.sc,pingStart=this.startedAtMs;(async()=>{for await(let m of pingSub){let pong={ok:!0,pid:process.pid,uptimeMs:Date.now()-pingStart,subjects:["omni.message.>","omni.turn.open.>","omni.session.reset.>",BRIDGE_PING_SUBJECT]};try{m.respond(pingSc.encode(JSON.stringify(pong)))}catch{}}})().catch(()=>{})}async claimPidfile(){this.pidfilePath=getBridgePidfilePath();try{mkdirSync20(dirname15(this.pidfilePath),{recursive:!0}),this.evictStalePidfile(this.pidfilePath);let fd=openSync4(this.pidfilePath,"wx"),payload=JSON.stringify({pid:process.pid,startedAt:this.startedAtMs,subjects:["omni.message.>","omni.turn.open.>","omni.session.reset.>",BRIDGE_PING_SUBJECT],natsUrl:this.natsUrl});writeSync(fd,payload),closeSync4(fd)}catch(err){await this.rollbackStartOnPidfileError(err)}}evictStalePidfile(path3){if(!existsSync46(path3))return;let stalePid=null;try{let raw=readFileSync28(path3,"utf8"),parsed=JSON.parse(raw);if(typeof parsed.pid==="number"&&Number.isFinite(parsed.pid))stalePid=parsed.pid}catch{}if(stalePid!==null&&this.isPidAlive(stalePid))throw Error(`pidfile locked by PID ${stalePid}`);try{unlinkSync9(path3)}catch{}}isPidAlive(pid){try{return process.kill(pid,0),!0}catch(probeErr){return probeErr.code!=="ESRCH"}}async rollbackStartOnPidfileError(err){this.pidfilePath=null;let detail=err instanceof Error?err.message:String(err);try{if(this.pingSub)this.pingSub.unsubscribe()}catch{}this.pingSub=null;try{await this.nc?.drain()}catch{}throw this.nc=null,Error(`[omni-bridge] pidfile locked at ${getBridgePidfilePath()}: ${detail}`)}armSignalCleanup(){let onSignal=()=>{if(this.pidfilePath){try{unlinkSync9(this.pidfilePath)}catch{}this.pidfilePath=null}};process.once("SIGTERM",onSignal),process.once("SIGINT",onSignal),this.signalCleanup=()=>{process.removeListener("SIGTERM",onSignal),process.removeListener("SIGINT",onSignal)}}async stop(){if(!this.nc){console.log("[omni-bridge] Not running");return}if(console.log("[omni-bridge] Shutting down..."),this.queue)this.queue.stop(),this.queue=null;if(this.idleCheckTimer)clearInterval(this.idleCheckTimer),this.idleCheckTimer=null;if(await this.shutdownActiveSessions(),this.unsubscribeChannels(),this.clearPidfileOnStop(),this.signalCleanup)this.signalCleanup(),this.signalCleanup=null;try{await this.nc.drain()}catch{}this.nc=null,this.sql=null,this.pgAvailable=!1,this.sessionStore=null,console.log("[omni-bridge] Stopped")}async shutdownActiveSessions(){for(let[key,entry2]of this.sessions){if(entry2.idleTimer)clearTimeout(entry2.idleTimer);if(entry2.spawning||!entry2.session)continue;if(entry2.session.executorType==="tmux"){console.log(`[omni-bridge] Detaching from tmux session ${key} (pane stays alive)`);continue}try{await this.executor.shutdown(entry2.session)}catch(err){console.warn(`[omni-bridge] Error shutting down session ${key}:`,err)}let closeId=entry2.pgBridgeSessionId;if(closeId&&this.sessionStore)await this.safePgCall("session_close_sdk",(sql)=>new BridgeSessionStore(sql).close(closeId),void 0)}this.sessions.clear()}unsubscribeChannels(){if(this.sub)this.sub.unsubscribe(),this.sub=null;if(this.pingSub){try{this.pingSub.unsubscribe()}catch{}this.pingSub=null}}clearPidfileOnStop(){if(!this.pidfilePath)return;try{unlinkSync9(this.pidfilePath)}catch{}this.pidfilePath=null}async status(){let now=Date.now(),activeFromPg=null,executorIds=[];if(this.pgAvailable&&this.sql){let rows=await this.safePgCall("status_active_count",async(sql)=>sql`
2527
+ `}}class TurnTracker{turns=new Map;open(sessionKey2,turnId,messageId){this.turns.set(sessionKey2,{turnId,sessionKey:sessionKey2,messageId,startedAt:Date.now(),closed:!1})}close(sessionKey2,action){let turn=this.turns.get(sessionKey2);if(turn&&!turn.closed)turn.closed=!0,turn.closedAction=action}isOpen(sessionKey2){let turn=this.turns.get(sessionKey2);return turn!==void 0&&!turn.closed}getTurnId(sessionKey2){return this.turns.get(sessionKey2)?.turnId}getByTurnId(turnId){for(let turn of this.turns.values())if(turn.turnId===turnId)return turn;return}delete(sessionKey2){this.turns.delete(sessionKey2)}}var exports_omni_bridge={};__export(exports_omni_bridge,{OmniBridge:()=>OmniBridge});import{closeSync as closeSync4,existsSync as existsSync46,mkdirSync as mkdirSync20,openSync as openSync4,readFileSync as readFileSync28,unlinkSync as unlinkSync9,writeSync}from"fs";import{dirname as dirname15}from"path";function withTimeout2(p,ms,label){return new Promise((resolve11,reject)=>{let timer2=setTimeout(()=>reject(Error(`${label} timed out after ${ms}ms`)),ms);timer2.unref?.(),p.then((v)=>{clearTimeout(timer2),resolve11(v)},(err)=>{clearTimeout(timer2),reject(err)})})}function isPgConnectionError(err){if(!err||typeof err!=="object")return!1;let e=err,code=e.code??"";if(["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENOTFOUND","EPIPE","EHOSTUNREACH"].includes(code))return!0;let msg=e.message??String(err);return/ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EPIPE|connection terminated|connection closed|server closed the connection|the database system is shutting down/i.test(msg)}class OmniBridge{nc=null;sub=null;executor;turnTracker=new TurnTracker;heartbeatPublisher=null;sessions=new Map;messageQueue=[];recentMessageIds=new Map;idleCheckTimer=null;sc=import_nats4.StringCodec();sql=null;pgAvailable=!1;pgProvider;natsConnectFn;queueConfig;queue=null;sessionStore=null;natsUrl;idleTimeoutMs;maxConcurrent;executorType;pidfilePath=null;startedAtMs=0;pingSub=null;signalCleanup=null;constructor(config={}){if(this.natsUrl=config.natsUrl??process.env.GENIE_NATS_URL??DEFAULT_NATS_URL2,this.idleTimeoutMs=config.idleTimeoutMs??(process.env.GENIE_IDLE_TIMEOUT_MS?Number(process.env.GENIE_IDLE_TIMEOUT_MS):DEFAULT_IDLE_TIMEOUT_MS2),this.maxConcurrent=config.maxConcurrent??(process.env.GENIE_MAX_CONCURRENT?Number(process.env.GENIE_MAX_CONCURRENT):DEFAULT_MAX_CONCURRENT),this.pgProvider=config.pgProvider??(async()=>{let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return await getConnection2()}),this.natsConnectFn=config.natsConnectFn??import_nats4.connect,this.queueConfig=config.queue??{},this.executorType=resolveExecutorType(config.executorType),this.executorType==="sdk")this.executor=new ClaudeSdkOmniExecutor;else this.executor=new ClaudeCodeOmniExecutor}async start(){if(this.nc){console.log("[omni-bridge] Already running");return}console.log(`[omni-bridge] Connecting to NATS at ${this.natsUrl}...`),this.nc=await this.natsConnectFn({servers:this.natsUrl,name:"genie-omni-bridge",reconnect:!0,maxReconnectAttempts:-1,reconnectTimeWait:2000}),console.log("[omni-bridge] Connected to NATS"),await this.setupPg(),this.wireExecutorHooks(),this.subscribeOmniChannels(),this.startedAtMs=Date.now(),this.subscribePingChannel(),await this.claimPidfile(),this.armSignalCleanup(),this.idleCheckTimer=setInterval(()=>this.checkIdleSessions(),IDLE_CHECK_INTERVAL_MS),console.log(`[omni-bridge] Listening on omni.message.> (max_concurrent=${this.maxConcurrent}, idle_timeout=${this.idleTimeoutMs}ms)`)}async setupPg(){if(await this.probePg(),this.pgAvailable&&this.sql)this.sessionStore=new BridgeSessionStore(this.sql),await this.recoverSessions();if(this.executorType==="sdk"&&this.pgAvailable&&this.sql)this.queue=new OmniQueue(this.sql,(_req,msg)=>this.routeMessage(msg),this.queueConfig),await this.queue.recoverStale(),this.queue.start()}wireExecutorHooks(){this.executor.setSafePgCall(this.safePgCall.bind(this));let sc=this.sc,nc=this.nc;if(!nc)return;let publish=(topic,payload)=>{nc.publish(topic,sc.encode(payload))};this.executor.setNatsPublish(publish),this.heartbeatPublisher=new HeartbeatPublisher({publish})}subscribeOmniChannels(){if(!this.nc)return;this.sub=this.nc.subscribe("omni.message.>",{queue:"genie-bridge"}),this.processSubscription();let turnSubs=["omni.turn.open.>","omni.turn.done.>","omni.turn.nudge.>","omni.turn.timeout.>"];for(let topic of turnSubs){let sub=this.nc.subscribe(topic,{queue:"genie-bridge"});this.processTurnEvents(sub)}let sessionResetSub=this.nc.subscribe("omni.session.reset.>",{queue:"genie-bridge"});this.processSessionResetEvents(sessionResetSub)}subscribePingChannel(){if(!this.nc)return;this.pingSub=this.nc.subscribe(BRIDGE_PING_SUBJECT);let pingSub=this.pingSub,pingSc=this.sc,pingStart=this.startedAtMs;(async()=>{for await(let m of pingSub){let pong={ok:!0,pid:process.pid,uptimeMs:Date.now()-pingStart,subjects:["omni.message.>","omni.turn.open.>","omni.session.reset.>",BRIDGE_PING_SUBJECT]};try{m.respond(pingSc.encode(JSON.stringify(pong)))}catch{}}})().catch(()=>{})}async claimPidfile(){this.pidfilePath=getBridgePidfilePath();try{mkdirSync20(dirname15(this.pidfilePath),{recursive:!0}),this.evictStalePidfile(this.pidfilePath);let fd=openSync4(this.pidfilePath,"wx"),payload=JSON.stringify({pid:process.pid,startedAt:this.startedAtMs,subjects:["omni.message.>","omni.turn.open.>","omni.session.reset.>",BRIDGE_PING_SUBJECT],natsUrl:this.natsUrl});writeSync(fd,payload),closeSync4(fd)}catch(err){await this.rollbackStartOnPidfileError(err)}}evictStalePidfile(path3){if(!existsSync46(path3))return;let stalePid=null;try{let raw=readFileSync28(path3,"utf8"),parsed=JSON.parse(raw);if(typeof parsed.pid==="number"&&Number.isFinite(parsed.pid))stalePid=parsed.pid}catch{}if(stalePid!==null&&this.isPidAlive(stalePid))throw Error(`pidfile locked by PID ${stalePid}`);try{unlinkSync9(path3)}catch{}}isPidAlive(pid){try{return process.kill(pid,0),!0}catch(probeErr){return probeErr.code!=="ESRCH"}}async rollbackStartOnPidfileError(err){this.pidfilePath=null;let detail=err instanceof Error?err.message:String(err);try{if(this.pingSub)this.pingSub.unsubscribe()}catch{}this.pingSub=null;try{await this.nc?.drain()}catch{}throw this.nc=null,Error(`[omni-bridge] pidfile locked at ${getBridgePidfilePath()}: ${detail}`)}armSignalCleanup(){let onSignal=()=>{if(this.pidfilePath){try{unlinkSync9(this.pidfilePath)}catch{}this.pidfilePath=null}};process.once("SIGTERM",onSignal),process.once("SIGINT",onSignal),this.signalCleanup=()=>{process.removeListener("SIGTERM",onSignal),process.removeListener("SIGINT",onSignal)}}async stop(){if(!this.nc){console.log("[omni-bridge] Not running");return}if(console.log("[omni-bridge] Shutting down..."),this.queue)this.queue.stop(),this.queue=null;if(this.idleCheckTimer)clearInterval(this.idleCheckTimer),this.idleCheckTimer=null;if(this.heartbeatPublisher?.stopAll(),this.heartbeatPublisher=null,await this.shutdownActiveSessions(),this.unsubscribeChannels(),this.clearPidfileOnStop(),this.signalCleanup)this.signalCleanup(),this.signalCleanup=null;try{await this.nc.drain()}catch{}this.nc=null,this.sql=null,this.pgAvailable=!1,this.sessionStore=null,console.log("[omni-bridge] Stopped")}async shutdownActiveSessions(){for(let[key,entry2]of this.sessions){if(entry2.idleTimer)clearTimeout(entry2.idleTimer);if(entry2.spawning||!entry2.session)continue;if(entry2.session.executorType==="tmux"){console.log(`[omni-bridge] Detaching from tmux session ${key} (pane stays alive)`);continue}try{await this.executor.shutdown(entry2.session)}catch(err){console.warn(`[omni-bridge] Error shutting down session ${key}:`,err)}let closeId=entry2.pgBridgeSessionId;if(closeId&&this.sessionStore)await this.safePgCall("session_close_sdk",(sql)=>new BridgeSessionStore(sql).close(closeId),void 0)}this.sessions.clear()}unsubscribeChannels(){if(this.sub)this.sub.unsubscribe(),this.sub=null;if(this.pingSub){try{this.pingSub.unsubscribe()}catch{}this.pingSub=null}}clearPidfileOnStop(){if(!this.pidfilePath)return;try{unlinkSync9(this.pidfilePath)}catch{}this.pidfilePath=null}async status(){let now=Date.now(),activeFromPg=null,executorIds=[];if(this.pgAvailable&&this.sql){let rows=await this.safePgCall("status_active_count",async(sql)=>sql`
2528
2528
  SELECT id FROM executors
2529
2529
  WHERE ended_at IS NULL AND metadata->>'source' = 'omni'
2530
- `,null);if(rows)activeFromPg=rows.length,executorIds=rows.map((r)=>r.id)}let pgQueue=null;if(this.queue)pgQueue=await this.safePgCall("status_queue_stats",(_sql)=>this.queue?.stats()??Promise.resolve(null),null);return{connected:this.nc!==null,natsUrl:this.natsUrl,pgAvailable:this.pgAvailable,activeSessions:activeFromPg??this.sessions.size,maxConcurrent:this.maxConcurrent,idleTimeoutMs:this.idleTimeoutMs,queueDepth:pgQueue?pgQueue.pending+pgQueue.processing:this.messageQueue.length,executorType:this.executorType,executorIds,pgQueue,sessions:Array.from(this.sessions.entries()).map(([key,entry2])=>({id:key,agentName:entry2.session.agentName,chatId:entry2.session.chatId,instanceId:entry2.instanceId,executorType:entry2.session.executorType,spawning:entry2.spawning,idleMs:now-entry2.session.lastActivityAt,bufferSize:entry2.buffer.length}))}}async probePg(){try{let sql=await withTimeout2(this.pgProvider(),PG_STARTUP_PROBE_TIMEOUT_MS,"PG provider startup");await withTimeout2(Promise.resolve(sql`SELECT 1`),PG_STARTUP_PROBE_TIMEOUT_MS,"PG SELECT 1 probe"),this.sql=sql,this.pgAvailable=!0,console.log("[omni-bridge] PG reachable \u2014 session recovery enabled")}catch(err){this.sql=null,this.pgAvailable=!1;let msg=err instanceof Error?err.message:String(err);if(isPgConnectionError(err)){console.warn(`[omni-bridge] PG unavailable \u2014 session recovery disabled (${msg})`);return}throw Error(`[omni-bridge] PG schema mismatch or setup error: ${msg}. ${"Run `bun run migrate` (or the equivalent migration command) and retry."}`)}}async recoverSessions(){if(!this.sessionStore)return;let orphanedCount=await this.safePgCall("recover_orphan_all",(sql)=>new BridgeSessionStore(sql).markAllOrphaned(),0);if(orphanedCount>0)console.log(`[omni-bridge] Startup cleanup: orphaned ${orphanedCount} stale session(s) from previous run`)}async safePgCall(op,fn,fallback,ctx){if(!this.pgAvailable||!this.sql)return fallback;let sql=this.sql;try{return await withTimeout2(fn(sql),PG_RUNTIME_QUERY_TIMEOUT_MS,`safePgCall(${op})`)}catch(err){let msg=err instanceof Error?err.message:String(err),execPart=ctx?.executorId?` executor_id=${ctx.executorId}`:"",chatPart=ctx?.chatId?` chat_id=${ctx.chatId}`:"";if(console.warn(`[omni-bridge] safePgCall(${op}) failed${execPart}${chatPart}: ${msg}`),isPgConnectionError(err))this.pgAvailable=!1,this.sql=null,console.warn("[omni-bridge] PG connection lost \u2014 switching to degraded mode");return fallback}}fillSubjectMetadata(parsed,subject){let parts=subject.split(".");if(parts.length<4)return;parsed.instanceId=parsed.instanceId||parts[2],parsed.chatId=parsed.chatId||parts[3]}isDuplicateMessage(messageId){if(!messageId)return!1;if(this.recentMessageIds.has(messageId))return!0;if(this.recentMessageIds.set(messageId,Date.now()),this.recentMessageIds.size>1000){let cutoff=Date.now()-60000;for(let[id,ts3]of this.recentMessageIds)if(ts3<cutoff)this.recentMessageIds.delete(id)}return!1}async dispatchMessage(parsed){if(this.queue){let env=parsed.env??{};await this.queue.enqueue(parsed,env);return}let key=`${parsed.agent}:${parsed.chatId}`,hasSession=this.sessions.has(key);console.log(`[omni-bridge] Routing message for ${key} (hasSession=${hasSession}, queue=${!!this.queue})`),await this.routeMessage(parsed),console.log(`[omni-bridge] routeMessage done for ${key}`)}async processSubscription(){if(!this.sub)return;for await(let msg of this.sub)try{let data=this.sc.decode(msg.data),parsed=JSON.parse(data);this.fillSubjectMetadata(parsed,msg.subject),console.log(`[omni-bridge] NATS message received: ${msg.subject} agent=${parsed.agent} chat=${parsed.chatId}`);let messageId=parsed.messageId;if(this.isDuplicateMessage(messageId)){console.log(`[omni-bridge] Dedup: skipping duplicate messageId=${messageId}`);continue}if(!parsed.chatId||!parsed.agent){console.warn("[omni-bridge] Dropping message: missing chatId or agent",msg.subject);continue}await this.dispatchMessage(parsed)}catch(err){console.error("[omni-bridge] Error processing message:",err)}}async processTurnEvents(sub){for await(let msg of sub)try{let payload=JSON.parse(this.sc.decode(msg.data)),parts=msg.subject.split("."),eventType=parts[2],instanceId=parts[3],chatId=parts.slice(4).join(".");console.log(`[omni-bridge] Turn event: ${eventType} instance=${instanceId} chat=${chatId}`);let sessionKey2=this.findSessionKey(instanceId,chatId);if(!sessionKey2&&payload.turnId){if(sessionKey2=this.findSessionKeyByTurnId(payload.turnId),sessionKey2)console.log(`[omni-bridge] Matched session via turnId fallback: ${sessionKey2}`)}if(sessionKey2)await this.routeTurnEvent(eventType,sessionKey2,payload);else console.log(`[omni-bridge] No session found for turn.${eventType} (instance=${instanceId}, chat=${chatId})`)}catch(err){console.warn("[omni-bridge] Error processing turn event:",err)}}async routeTurnEvent(eventType,sessionKey2,payload){switch(eventType){case"open":this.turnTracker.open(sessionKey2,payload.turnId,payload.messageId);break;case"done":this.turnTracker.close(sessionKey2,payload.action),await this.handleTurnDone(sessionKey2);break;case"nudge":await this.handleTurnNudge(sessionKey2,payload.message);break;case"timeout":await this.handleTurnTimeout(sessionKey2);break}}findSessionKeyByTurnId(turnId){for(let[key]of this.sessions)if(this.turnTracker.getTurnId(key)===turnId)return key;return}chatIdMap=new Map;findSessionKey(instanceId,chatId){let resolvedChatId=this.chatIdMap.get(chatId);for(let[key,entry2]of this.sessions){if(entry2.instanceId!==instanceId)continue;if(entry2.session?.chatId===chatId)return key;if(resolvedChatId&&entry2.session?.chatId===resolvedChatId)return key;if(entry2.spawning&&key.endsWith(`:${chatId}`))return key;if(resolvedChatId&&entry2.spawning&&key.endsWith(`:${resolvedChatId}`))return key}return}async handleTurnNudge(sessionKey2,nudgeText){let entry2=this.sessions.get(sessionKey2);if(!entry2?.session)return;try{await this.executor.injectNudge(entry2.session,nudgeText)}catch(err){console.warn(`[omni-bridge] Failed to inject nudge for ${sessionKey2}:`,err)}}async processSessionResetEvents(sub){for await(let msg of sub)try{let parts=msg.subject.split(".");if(parts.length<5){console.warn(`[omni-bridge] Malformed session-reset subject: ${msg.subject}`);continue}let instanceId=parts[3],chatId=parts.slice(4).join("."),action;try{action=JSON.parse(this.sc.decode(msg.data)).action}catch{}await this.handleSessionReset(instanceId,chatId,action)}catch(err){console.warn("[omni-bridge] Error processing session reset event:",err)}}async handleSessionReset(instanceId,chatId,action){let sessionKey2=this.findSessionKey(instanceId,chatId);if(!sessionKey2){console.log(`[omni-bridge] Session reset for cold chat ${instanceId}/${chatId} \u2014 no-op`);return}let entry2=this.sessions.get(sessionKey2);if(!entry2)return;let actionTag=action?` (action=${action})`:"";if(entry2.spawning){console.log(`[omni-bridge] Session reset for spawning ${sessionKey2}${actionTag}, marking cancelled`),entry2.cancelled=!0,entry2.buffer=[],this.turnTracker.close(sessionKey2,"reset"),await this.removeSession(sessionKey2),await this.drainQueue();return}if(!entry2.session)return;console.log(`[omni-bridge] Session reset for ${sessionKey2}${actionTag}, evicting`),this.turnTracker.close(sessionKey2,"reset");try{await this.executor.shutdown(entry2.session)}catch(err){console.warn(`[omni-bridge] Error shutting down reset session ${sessionKey2}:`,err)}await this.removeSession(sessionKey2),await this.drainQueue()}async handleTurnDone(sessionKey2){if(!this.sessions.get(sessionKey2)?.session)return;console.log(`[omni-bridge] Turn done for ${sessionKey2}, session stays alive for next message`),this.resetIdleTimer(sessionKey2)}async handleTurnTimeout(sessionKey2){let entry2=this.sessions.get(sessionKey2);if(!entry2?.session)return;console.warn(`[omni-bridge] Turn timed out for ${sessionKey2}, evicting session`),this.turnTracker.close(sessionKey2,"timeout");try{await this.executor.shutdown(entry2.session)}catch(err){console.warn(`[omni-bridge] Error shutting down timed-out session ${sessionKey2}:`,err)}this.sessions.delete(sessionKey2)}async routeMessage(message){let key=`${message.agent}:${message.chatId}`,entry2=this.sessions.get(key);if(entry2){if(entry2.spawning){if(entry2.buffer.length<MAX_BUFFER_PER_CHAT)entry2.buffer.push(message);else console.warn(`[omni-bridge] Buffer full (${MAX_BUFFER_PER_CHAT}) for ${key}, dropping message from ${message.sender}`),await this.publishBufferFullReply(message);return}if(await this.executor.isAlive(entry2.session)){await this.executor.deliver(entry2.session,message),this.resetIdleTimer(key);let bsId=entry2.pgBridgeSessionId;if(bsId&&this.sessionStore)this.safePgCall("session_activity",(sql)=>new BridgeSessionStore(sql).recordActivity(bsId),void 0,{chatId:message.chatId});return}await this.removeSession(key)}await this.spawnSession(message)}async spawnSession(message){let key=`${message.agent}:${message.chatId}`,existing=this.sessions.get(key);if(existing){if(existing.buffer.length<MAX_BUFFER_PER_CHAT)existing.buffer.push(message),console.log(`[omni-bridge] Buffered message for existing session ${key} (buffer=${existing.buffer.length})`);return}if(this.sessions.size>=this.maxConcurrent){this.messageQueue.push(message),await this.publishAutoReply(message),console.log(`[omni-bridge] Max concurrent (${this.maxConcurrent}) reached, queued message for ${key}`);return}let placeholder={session:null,instanceId:message.instanceId,spawning:!0,buffer:[message],idleTimer:null};this.sessions.set(key,placeholder);try{let raw=message,payloadEnv=raw.env,spawnEnv={OMNI_API_KEY:payloadEnv?.OMNI_API_KEY??process.env.OMNI_API_KEY??"",OMNI_INSTANCE:payloadEnv?.OMNI_INSTANCE??message.instanceId,OMNI_CHAT:payloadEnv?.OMNI_CHAT??message.chatId,OMNI_MESSAGE:payloadEnv?.OMNI_MESSAGE??raw.messageId??"",OMNI_TURN_ID:payloadEnv?.OMNI_TURN_ID||"",OMNI_SENDER_NAME:payloadEnv?.OMNI_SENDER_NAME??message.sender??""};console.log(`[omni-bridge] Spawning session for ${key}...`);let session=await this.executor.spawn(message.agent,message.chatId,spawnEnv,message.content);if(placeholder.cancelled){console.log(`[omni-bridge] Spawn for ${key} completed but was cancelled by reset, shutting down`);try{await this.executor.shutdown(session)}catch(err){console.warn(`[omni-bridge] Error shutting down cancelled spawn for ${key}:`,err)}return}if(placeholder.session=session,placeholder.spawning=!1,this.sessionStore){let claudeSessionId=session.sdk?.claudeSessionId??session.tmux?.claudeSessionId,pgId=await this.safePgCall("session_create",(sql)=>new BridgeSessionStore(sql).create({instanceId:message.instanceId,chatId:message.chatId,agentName:message.agent,executorId:session.sdk?.executorId,tmuxPaneId:session.tmux?.paneId,claudeSessionId}),void 0,{chatId:message.chatId});if(pgId)placeholder.pgBridgeSessionId=pgId}for(let buffered of placeholder.buffer)await this.executor.deliver(session,buffered);placeholder.buffer=[],this.resetIdleTimer(key);let sessionTag=session.executorType==="tmux"?`(tmux pane=${session.tmux?.paneId})`:"(executor=sdk)";console.log(`[omni-bridge] Session active: ${key} ${sessionTag}`)}catch(err){console.error(`[omni-bridge] Failed to spawn session for ${key}:`,err);let lostMessages=placeholder.buffer;if(lostMessages.length>0)console.warn(`[omni-bridge] Re-queuing ${lostMessages.length} buffered message(s) from failed spawn for ${key}`),this.messageQueue.push(...lostMessages);this.sessions.delete(key)}}resetIdleTimer(key){let entry2=this.sessions.get(key);if(!entry2)return;if(entry2.idleTimer)clearTimeout(entry2.idleTimer);entry2.idleTimer=setTimeout(async()=>{console.log(`[omni-bridge] Idle timeout for ${key}, shutting down...`);try{await this.executor.shutdown(entry2.session)}catch{}await this.removeSession(key),await this.drainQueue()},this.idleTimeoutMs)}async checkIdleSessions(){let now=Date.now();for(let[key,entry2]of this.sessions){if(entry2.spawning)continue;if(!await this.executor.isAlive(entry2.session)){console.log(`[omni-bridge] Dead session detected: ${key}`),await this.removeSession(key);continue}let idleMs=now-entry2.session.lastActivityAt;if(idleMs>this.idleTimeoutMs){console.log(`[omni-bridge] Forcing idle shutdown: ${key} (idle ${Math.round(idleMs/1000)}s)`);try{await this.executor.shutdown(entry2.session)}catch{}await this.removeSession(key)}}}async removeSession(key){let entry2=this.sessions.get(key);if(entry2?.idleTimer)clearTimeout(entry2.idleTimer);let closeId=entry2?.pgBridgeSessionId;if(closeId&&this.sessionStore)await this.safePgCall("session_close",(sql)=>new BridgeSessionStore(sql).close(closeId),void 0);this.sessions.delete(key)}async drainQueue(){while(this.messageQueue.length>0){if(this.sessions.size>=this.maxConcurrent)break;let message=this.messageQueue.shift();if(message)await this.spawnSession(message)}}async publishBufferFullReply(message){if(!this.nc)return;let topic=`omni.reply.${message.instanceId}.${message.chatId}`,reply={content:"Fila de mensagens cheia, por favor aguarde e tente novamente.",agent:message.agent,chat_id:message.chatId,instance_id:message.instanceId,timestamp:new Date().toISOString(),auto_reply:!0};this.nc.publish(topic,this.sc.encode(JSON.stringify(reply)))}async publishAutoReply(message){if(!this.nc)return;let topic=`omni.reply.${message.instanceId}.${message.chatId}`,reply={content:"Aguarde um momento, estou atendendo outros clientes.",agent:message.agent,chat_id:message.chatId,instance_id:message.instanceId,timestamp:new Date().toISOString(),auto_reply:!0};this.nc.publish(topic,this.sc.encode(JSON.stringify(reply)))}}var import_nats3,DEFAULT_NATS_URL2="localhost:4222",DEFAULT_IDLE_TIMEOUT_MS2=900000,DEFAULT_MAX_CONCURRENT=20,MAX_BUFFER_PER_CHAT=50,IDLE_CHECK_INTERVAL_MS=30000,PG_STARTUP_PROBE_TIMEOUT_MS=5000,PG_RUNTIME_QUERY_TIMEOUT_MS=2000;var init_omni_bridge=__esm(()=>{init_bridge_status();init_executor_config();init_claude_code2();init_claude_sdk2();import_nats3=__toESM(require_mod4(),1)});import{existsSync as existsSync47,mkdirSync as mkdirSync21,readFileSync as readFileSync29,readdirSync as readdirSync10,renameSync as renameSync7,statSync as statSync12,writeFileSync as writeFileSync18}from"fs";import{homedir as homedir38}from"os";import{dirname as dirname16,join as join59,resolve as resolvePath4}from"path";import{pathToFileURL as pathToFileURL2}from"url";function scanDir(dirAbs){if(!existsSync47(dirAbs))return[];return readdirSync10(dirAbs).filter((name)=>name.endsWith(".ts")&&!name.startsWith("_")&&!name.endsWith(".test.ts")).map((name)=>join59(dirAbs,name)).filter((p)=>{try{return statSync12(p).isFile()}catch{return!1}})}function discoverHooks(opts){let home=homedir38(),discovered=[],teamsRoot=join59(home,".claude","teams");if(existsSync47(teamsRoot))for(let teamName of readdirSync10(teamsRoot)){if(teamName.startsWith("_"))continue;let teamHooksDir=join59(teamsRoot,teamName,"hooks");for(let path3 of scanDir(teamHooksDir))discovered.push({path:path3,scope:"team",teamName})}if(opts.repoRoot){let repoHooksDir=join59(opts.repoRoot,".genie","hooks"),repoRemoteUrl=opts.resolveRepoRemoteUrl?.(opts.repoRoot)??void 0;for(let path3 of scanDir(repoHooksDir))discovered.push({path:path3,scope:"repo",repoRemoteUrl})}let globalHooksDir=join59(home,".genie","hooks");for(let path3 of scanDir(globalHooksDir))discovered.push({path:path3,scope:"global"});return discovered}function quarantine(filePath,errorMessage){let dir=dirname16(filePath),quarantineDir=join59(dir,"_quarantine");if(!existsSync47(quarantineDir))mkdirSync21(quarantineDir,{recursive:!0});let basename11=filePath.split("/").pop()??"unknown.ts",target=join59(quarantineDir,basename11);try{renameSync7(filePath,target)}catch{}try{writeFileSync18(`${target}.error`,errorMessage,"utf-8")}catch{}return target}function validateHandler(imported){if(typeof imported!=="object"||imported===null)return"expected default export to be an object";let handler=imported;if(handler.version!=="1")return`unknown handler version: ${String(handler.version)} (expected '1')`;if(typeof handler.name!=="string"||handler.name.length===0)return"handler.name must be a non-empty string";if(typeof handler.event!=="string")return"handler.event must be a HookEventName string";if(typeof handler.priority!=="number"||Number.isNaN(handler.priority))return"handler.priority must be a finite number";if(typeof handler.fn!=="function")return"handler.fn must be a function";return null}async function loadExternalHooks(opts={}){let trustPath=opts.trustPath??defaultTrustPath(),trustFile;try{trustFile=readTrustFile(trustPath)}catch(err){let msg=err instanceof Error?err.message:String(err);return console.warn(`[hook-loader] trust file unreadable at ${trustPath}: ${msg} \u2014 refusing to load any external hooks`),[]}let importer=opts.importer??((url)=>import(url.href)),discovered=discoverHooks(opts),loaded=[],seenNames=new Map,outcomes=[];for(let source of discovered){let verify=verifyTrust(source.path,trustFile,{currentRepoRemoteUrl:source.repoRemoteUrl});if(!verify.trusted){outcomes.push({kind:"untrusted",reason:verify.reason,source});continue}let imported;try{imported=await importer(pathToFileURL2(resolvePath4(source.path)))}catch(err){let msg=err instanceof Error?`${err.name}: ${err.message}`:String(err),quarantined=quarantine(source.path,msg);outcomes.push({kind:"broken",error:msg,source,quarantined});continue}let handlerCandidate=imported.default,validationError=validateHandler(handlerCandidate);if(validationError!==null){let quarantined=quarantine(source.path,validationError);outcomes.push({kind:"broken",error:validationError,source,quarantined});continue}let stamped={...handlerCandidate,source:source.scope,manifest_path:source.path},existing=seenNames.get(stamped.name);if(existing){if(console.warn(`[hook-loader] handler name collision: "${stamped.name}" registered from ${existing.path} (${existing.scope}) shadows ${source.path} (${source.scope})`),opts.strict)throw Error(`--strict-hooks: handler name collision on "${stamped.name}" between ${existing.path} and ${source.path}`);outcomes.push({kind:"shadowed",handler:stamped,source,shadowedBy:existing});continue}parseCapabilities(readFileSync29(source.path,"utf-8")),seenNames.set(stamped.name,source),loaded.push(stamped),outcomes.push({kind:"loaded",handler:stamped,source})}let builtins=getRegistry().filter((h)=>h.source==="builtin");return setRegistry([...builtins,...loaded]),outcomes}var init_loader=__esm(()=>{init_hooks();init_trust()});var exports_hook_socket={};__export(exports_hook_socket,{startHookSocket:()=>startHookSocket,defaultHookSocketPath:()=>defaultHookSocketPath});import{existsSync as existsSync48,unlinkSync as unlinkSync10}from"fs";import{mkdir as mkdir7}from"fs/promises";import{createServer,connect as netConnect}from"net";import{homedir as homedir39}from"os";import{dirname as dirname17,join as join60}from"path";function defaultHookSocketPath(){if(process.env.GENIE_HOOK_SOCK)return process.env.GENIE_HOOK_SOCK;let home=process.env.GENIE_HOME??join60(homedir39(),".genie");return join60(home,"hook.sock")}async function detectStaleAndCleanup(socketPath){if(!existsSync48(socketPath))return"clean";if(await new Promise((resolve11)=>{let probe=netConnect(socketPath),finish=(alive)=>{try{probe.destroy()}catch{}resolve11(alive)};probe.once("connect",()=>finish(!0)),probe.once("error",()=>finish(!1)),setTimeout(()=>finish(!1),200).unref()}))return"live";try{return unlinkSync10(socketPath),"stale-removed"}catch(err){throw Error(`Failed to remove stale hook socket at ${socketPath}: ${err.message}`)}}function parseFrame(acc,length){if(length===-1){if(acc.length<4)return{kind:"incomplete",length:-1};let declared=acc.readUInt32BE(0);if(declared>MAX_FRAME_BYTES2)return{kind:"error",reason:`frame length ${declared} exceeds MAX_FRAME_BYTES`};if(declared===0)return{kind:"done",body:Buffer.alloc(0)};if(acc.length>=4+declared)return{kind:"done",body:acc.subarray(4,4+declared)};return{kind:"incomplete",length:declared}}if(acc.length>=4+length)return{kind:"done",body:acc.subarray(4,4+length)};return{kind:"incomplete",length}}function readOneFrame(socket){return new Promise((resolve11,reject)=>{let acc=Buffer.alloc(0),length=-1,cleanup=()=>{socket.off("data",onData),socket.off("end",onEnd),socket.off("error",onError)},onData=(chunk)=>{acc=acc.length===0?Buffer.from(chunk):Buffer.concat([acc,Buffer.from(chunk)],acc.length+chunk.length);let result2=parseFrame(acc,length);if(result2.kind==="incomplete"){length=result2.length;return}if(cleanup(),result2.kind==="done")resolve11(result2.body);else reject(Error(result2.reason))},onEnd=()=>{cleanup(),reject(Error(`socket closed after ${acc.length} bytes (expected ${length===-1?"4+":4+length})`))},onError=(err)=>{cleanup(),reject(err)};socket.on("data",onData),socket.on("end",onEnd),socket.on("error",onError)})}function writeFrame(socket,payload){let body=Buffer.from(payload,"utf-8");if(body.length>MAX_FRAME_BYTES2){let empty=Buffer.alloc(4);socket.end(empty);return}let header=Buffer.alloc(4);header.writeUInt32BE(body.length,0),socket.end(Buffer.concat([header,body]))}async function handleConnection(socket){socket.setNoDelay(!0);try{let body=await readOneFrame(socket),stdin=body.length===0?"":body.toString("utf-8"),reply=await dispatch(stdin);writeFrame(socket,reply??"")}catch(err){try{writeFrame(socket,"")}catch{}if(process.env.GENIE_HOOK_SOCK_DEBUG==="1")console.warn(`[hook-socket] connection error: ${err.message}`)}}async function startHookSocket(options={}){if(process.env.GENIE_WIDE_EMIT===void 0)process.env.GENIE_WIDE_EMIT="1";let loaderOutcomes=[];if(options.loadExternal!==!1)loaderOutcomes=await loadExternalHooks({repoRoot:options.repoRoot,strict:options.strict}),summarizeLoaderOutcomes(loaderOutcomes);let socketPath=defaultHookSocketPath();await mkdir7(dirname17(socketPath),{recursive:!0});let state=await detectStaleAndCleanup(socketPath);if(state==="live")throw Error(`hook socket at ${socketPath} is already live \u2014 another genie serve daemon is running. Refusing to start.`);if(state==="stale-removed")console.log(`hook-socket: removed stale socket at ${socketPath}`);let liveSockets=new Set,server3=createServer((socket)=>{liveSockets.add(socket),socket.once("close",()=>liveSockets.delete(socket)),handleConnection(socket)});await new Promise((resolve11,reject)=>{let onError=(err)=>{server3.off("listening",onListening),reject(err)},onListening=()=>{server3.off("error",onError),resolve11()};server3.once("error",onError),server3.once("listening",onListening),server3.listen(socketPath)}),console.log(`hook-socket: listening at ${socketPath}`);let stopped=!1;return{path:socketPath,stop:async()=>{if(stopped)return;stopped=!0;for(let sock of liveSockets)try{sock.destroy()}catch{}if(liveSockets.clear(),await new Promise((resolve11)=>{server3.close(()=>resolve11())}),existsSync48(socketPath))try{unlinkSync10(socketPath)}catch{}},loaderOutcomes}}function summarizeLoaderOutcomes(outcomes){if(outcomes.length===0)return;let loaded=0,untrusted=0,broken=0,shadowed=0;for(let outcome of outcomes)if(outcome.kind==="loaded")loaded+=1;else if(outcome.kind==="untrusted")untrusted+=1;else if(outcome.kind==="broken")broken+=1;else shadowed+=1;let parts=[`hook-loader: ${loaded} loaded`];if(untrusted>0)parts.push(`${untrusted} untrusted`);if(broken>0)parts.push(`${broken} broken (quarantined)`);if(shadowed>0)parts.push(`${shadowed} shadowed`);console.log(parts.join(", "))}var MAX_FRAME_BYTES2=1048576;var init_hook_socket=__esm(()=>{init_hooks();init_loader()});var exports_serve={};__export(exports_serve,{writeStoppingLockSync:()=>writeStoppingLockSync,startBrainServerIfEnabled:()=>startBrainServerIfEnabled,registerServeCommands:()=>registerServeCommands,isTuiSessionReady:()=>isTuiSessionReady,isStoppingLockActive:()=>isStoppingLockActive,isServeRunning:()=>isServeRunning,getTuiQuitBindingArgs:()=>getTuiQuitBindingArgs,getTuiKeybindings:()=>getTuiKeybindings,ensureTuiSession:()=>ensureTuiSession,clearStoppingLock:()=>clearStoppingLock,autoStartServe:()=>autoStartServe});import{execSync as execSync11,spawn as spawn4,spawnSync as spawnSync6}from"child_process";import{closeSync as closeSync5,existsSync as existsSync49,mkdirSync as mkdirSync22,openSync as openSync5,readFileSync as readFileSync30,unlinkSync as unlinkSync11,writeFileSync as writeFileSync19,writeSync as writeSync2}from"fs";import{homedir as homedir40}from"os";import{dirname as dirname18,join as join61}from"path";function genieHome3(){return process.env.GENIE_HOME??join61(homedir40(),".genie")}function servePidPath(){return join61(genieHome3(),"serve.pid")}function serveStartupStatusPath(){return join61(genieHome3(),"state",`serve-startup-${process.pid}-${Date.now()}.json`)}function readServePid(){let path3=servePidPath();if(!existsSync49(path3))return null;let raw=readFileSync30(path3,"utf-8").trim();if(raw==="")return null;let sepIdx=raw.indexOf(":");if(sepIdx<0){let pid2=Number.parseInt(raw,10);if(Number.isNaN(pid2)||pid2<=0)return null;return{pid:pid2,startTime:null}}let pidPart=raw.slice(0,sepIdx),startTimePart=raw.slice(sepIdx+1).trim(),pid=Number.parseInt(pidPart,10);if(Number.isNaN(pid)||pid<=0)return null;return{pid,startTime:startTimePart===""||startTimePart==="unknown"?null:startTimePart}}function removeServePid(){let path3=servePidPath();if(!existsSync49(path3))return;try{let current=readServePid();if(current&&current.pid!==process.pid)return;unlinkSync11(path3)}catch{}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function stoppingLockPath(){return join61(genieHome3(),"serve.stopping.lock")}function writeStoppingLockSync(ttlMs=STOPPING_LOCK_TTL_MS){mkdirSync22(genieHome3(),{recursive:!0}),writeFileSync19(stoppingLockPath(),String(Date.now()+ttlMs),"utf-8")}function clearStoppingLock(){try{unlinkSync11(stoppingLockPath())}catch{}}function isStoppingLockActive(){let path3=stoppingLockPath();if(!existsSync49(path3))return!1;let raw;try{raw=readFileSync30(path3,"utf-8").trim()}catch{return!1}let expiresAt=Number.parseInt(raw,10);if(raw===""||Number.isNaN(expiresAt)||expiresAt<=Date.now())return clearStoppingLock(),!1;return!0}function tuiTmuxConf(){return[join61(genieHome3(),"tui-tmux.conf")].find((p)=>existsSync49(p))??"/dev/null"}function tuiTmux(subcmd){return`${tmuxBin()} -L genie-tui -f ${tuiTmuxConf()} ${subcmd}`}function isGenieTmuxRunning(){try{return execSync11(genieTmuxCmd("list-sessions"),{stdio:"ignore"}),!0}catch{return!1}}function getTuiKeybindings(sessionName=TUI_SESSION){return[`bind-key -T root Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${sessionName}:0.1" "select-pane -t ${sessionName}:0.0"`,`bind-key -T root C-1 select-pane -t ${sessionName}:0.0`,`bind-key -T root C-2 select-pane -t ${sessionName}:0.1`,`bind-key -T root C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${sessionName}:0.0) -gt 5 ]" "resize-pane -t ${sessionName}:0.0 -x 0" "resize-pane -t ${sessionName}:0.0 -x ${NAV_WIDTH}"`,`bind-key -T root C-t select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-t`,"bind-key -T root C-d detach-client",`bind-key -T root C-q select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-q`]}function getTuiQuitBindingArgs(sessionName=TUI_SESSION){return["bind-key","-T","root","C-q","select-pane","-t",`${sessionName}:0.0`,"\\;","send-keys","-t",`${sessionName}:0.0`,"C-q"]}function applyTuiStyle(){let cmds=[`set-option -t ${TUI_SESSION} pane-border-style 'fg=${TUI_STYLE.inactiveBorder}'`,`set-option -t ${TUI_SESSION} pane-active-border-style 'fg=${TUI_STYLE.activeBorder}'`,...process.env.GENIE_TMUX_MOUSE!=="off"?[`set-option -t ${TUI_SESSION} mouse on`]:[],`set-option -t ${TUI_SESSION} status off`,`set-option -t ${TUI_SESSION} pane-border-status off`];for(let cmd of cmds)try{execSync11(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function setupTuiKeybindings(){for(let cmd of getTuiKeybindings())try{if(cmd.startsWith("bind-key -T root C-q "))spawnSync6(tmuxBin(),["-L","genie-tui","-f",tuiTmuxConf(),...getTuiQuitBindingArgs()],{stdio:"ignore"});else execSync11(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function startTuiTmuxServer(){try{execSync11(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"});let panes2=execSync11(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
2530
+ `,null);if(rows)activeFromPg=rows.length,executorIds=rows.map((r)=>r.id)}let pgQueue=null;if(this.queue)pgQueue=await this.safePgCall("status_queue_stats",(_sql)=>this.queue?.stats()??Promise.resolve(null),null);return{connected:this.nc!==null,natsUrl:this.natsUrl,pgAvailable:this.pgAvailable,activeSessions:activeFromPg??this.sessions.size,maxConcurrent:this.maxConcurrent,idleTimeoutMs:this.idleTimeoutMs,queueDepth:pgQueue?pgQueue.pending+pgQueue.processing:this.messageQueue.length,executorType:this.executorType,executorIds,pgQueue,sessions:Array.from(this.sessions.entries()).map(([key,entry2])=>({id:key,agentName:entry2.session.agentName,chatId:entry2.session.chatId,instanceId:entry2.instanceId,executorType:entry2.session.executorType,spawning:entry2.spawning,idleMs:now-entry2.session.lastActivityAt,bufferSize:entry2.buffer.length}))}}async probePg(){try{let sql=await withTimeout2(this.pgProvider(),PG_STARTUP_PROBE_TIMEOUT_MS,"PG provider startup");await withTimeout2(Promise.resolve(sql`SELECT 1`),PG_STARTUP_PROBE_TIMEOUT_MS,"PG SELECT 1 probe"),this.sql=sql,this.pgAvailable=!0,console.log("[omni-bridge] PG reachable \u2014 session recovery enabled")}catch(err){this.sql=null,this.pgAvailable=!1;let msg=err instanceof Error?err.message:String(err);if(isPgConnectionError(err)){console.warn(`[omni-bridge] PG unavailable \u2014 session recovery disabled (${msg})`);return}throw Error(`[omni-bridge] PG schema mismatch or setup error: ${msg}. ${"Run `bun run migrate` (or the equivalent migration command) and retry."}`)}}async recoverSessions(){if(!this.sessionStore)return;let orphanedCount=await this.safePgCall("recover_orphan_all",(sql)=>new BridgeSessionStore(sql).markAllOrphaned(),0);if(orphanedCount>0)console.log(`[omni-bridge] Startup cleanup: orphaned ${orphanedCount} stale session(s) from previous run`)}async safePgCall(op,fn,fallback,ctx){if(!this.pgAvailable||!this.sql)return fallback;let sql=this.sql;try{return await withTimeout2(fn(sql),PG_RUNTIME_QUERY_TIMEOUT_MS,`safePgCall(${op})`)}catch(err){let msg=err instanceof Error?err.message:String(err),execPart=ctx?.executorId?` executor_id=${ctx.executorId}`:"",chatPart=ctx?.chatId?` chat_id=${ctx.chatId}`:"";if(console.warn(`[omni-bridge] safePgCall(${op}) failed${execPart}${chatPart}: ${msg}`),isPgConnectionError(err))this.pgAvailable=!1,this.sql=null,console.warn("[omni-bridge] PG connection lost \u2014 switching to degraded mode");return fallback}}fillSubjectMetadata(parsed,subject){let parts=subject.split(".");if(parts.length<4)return;parsed.instanceId=parsed.instanceId||parts[2],parsed.chatId=parsed.chatId||parts[3]}isDuplicateMessage(messageId){if(!messageId)return!1;if(this.recentMessageIds.has(messageId))return!0;if(this.recentMessageIds.set(messageId,Date.now()),this.recentMessageIds.size>1000){let cutoff=Date.now()-60000;for(let[id,ts3]of this.recentMessageIds)if(ts3<cutoff)this.recentMessageIds.delete(id)}return!1}async dispatchMessage(parsed){if(this.queue){let env=parsed.env??{};await this.queue.enqueue(parsed,env);return}let key=`${parsed.agent}:${parsed.chatId}`,hasSession=this.sessions.has(key);console.log(`[omni-bridge] Routing message for ${key} (hasSession=${hasSession}, queue=${!!this.queue})`),await this.routeMessage(parsed),console.log(`[omni-bridge] routeMessage done for ${key}`)}async processSubscription(){if(!this.sub)return;for await(let msg of this.sub)try{let data=this.sc.decode(msg.data),parsed=JSON.parse(data);this.fillSubjectMetadata(parsed,msg.subject),console.log(`[omni-bridge] NATS message received: ${msg.subject} agent=${parsed.agent} chat=${parsed.chatId}`);let messageId=parsed.messageId;if(this.isDuplicateMessage(messageId)){console.log(`[omni-bridge] Dedup: skipping duplicate messageId=${messageId}`);continue}if(!parsed.chatId||!parsed.agent){console.warn("[omni-bridge] Dropping message: missing chatId or agent",msg.subject);continue}await this.dispatchMessage(parsed)}catch(err){console.error("[omni-bridge] Error processing message:",err)}}async processTurnEvents(sub){for await(let msg of sub)try{let payload=JSON.parse(this.sc.decode(msg.data)),parts=msg.subject.split("."),eventType=parts[2],instanceId=parts[3],chatId=parts.slice(4).join(".");console.log(`[omni-bridge] Turn event: ${eventType} instance=${instanceId} chat=${chatId}`);let sessionKey2=this.findSessionKey(instanceId,chatId);if(!sessionKey2&&payload.turnId){if(sessionKey2=this.findSessionKeyByTurnId(payload.turnId),sessionKey2)console.log(`[omni-bridge] Matched session via turnId fallback: ${sessionKey2}`)}if(sessionKey2)await this.routeTurnEvent(eventType,sessionKey2,payload);else console.log(`[omni-bridge] No session found for turn.${eventType} (instance=${instanceId}, chat=${chatId})`)}catch(err){console.warn("[omni-bridge] Error processing turn event:",err)}}async routeTurnEvent(eventType,sessionKey2,payload){switch(eventType){case"open":this.turnTracker.open(sessionKey2,payload.turnId,payload.messageId),this.startHeartbeat(sessionKey2,payload);break;case"done":this.turnTracker.close(sessionKey2,payload.action),this.heartbeatPublisher?.stop(sessionKey2),await this.handleTurnDone(sessionKey2);break;case"nudge":await this.handleTurnNudge(sessionKey2,payload.message);break;case"timeout":this.heartbeatPublisher?.stop(sessionKey2),await this.handleTurnTimeout(sessionKey2);break}}startHeartbeat(sessionKey2,payload){let publisher=this.heartbeatPublisher;if(!publisher)return;let entry2=this.sessions.get(sessionKey2);if(!entry2)return;let turnId=payload.turnId;if(!turnId)return;publisher.start(sessionKey2,{instanceId:entry2.instanceId,chatId:entry2.session?.chatId??"",turnId,isBusy:async()=>{let live=this.sessions.get(sessionKey2);if(!live?.session)return!1;try{return await this.executor.isBusy(live.session)}catch{return!1}}})}findSessionKeyByTurnId(turnId){for(let[key]of this.sessions)if(this.turnTracker.getTurnId(key)===turnId)return key;return}chatIdMap=new Map;findSessionKey(instanceId,chatId){let resolvedChatId=this.chatIdMap.get(chatId);for(let[key,entry2]of this.sessions){if(entry2.instanceId!==instanceId)continue;if(entry2.session?.chatId===chatId)return key;if(resolvedChatId&&entry2.session?.chatId===resolvedChatId)return key;if(entry2.spawning&&key.endsWith(`:${chatId}`))return key;if(resolvedChatId&&entry2.spawning&&key.endsWith(`:${resolvedChatId}`))return key}return}async handleTurnNudge(sessionKey2,nudgeText){let entry2=this.sessions.get(sessionKey2);if(!entry2?.session)return;try{await this.executor.injectNudge(entry2.session,nudgeText)}catch(err){console.warn(`[omni-bridge] Failed to inject nudge for ${sessionKey2}:`,err)}}async processSessionResetEvents(sub){for await(let msg of sub)try{let parts=msg.subject.split(".");if(parts.length<5){console.warn(`[omni-bridge] Malformed session-reset subject: ${msg.subject}`);continue}let instanceId=parts[3],chatId=parts.slice(4).join("."),action;try{action=JSON.parse(this.sc.decode(msg.data)).action}catch{}await this.handleSessionReset(instanceId,chatId,action)}catch(err){console.warn("[omni-bridge] Error processing session reset event:",err)}}async handleSessionReset(instanceId,chatId,action){let sessionKey2=this.findSessionKey(instanceId,chatId);if(!sessionKey2){console.log(`[omni-bridge] Session reset for cold chat ${instanceId}/${chatId} \u2014 no-op`);return}let entry2=this.sessions.get(sessionKey2);if(!entry2)return;let actionTag=action?` (action=${action})`:"";if(entry2.spawning){console.log(`[omni-bridge] Session reset for spawning ${sessionKey2}${actionTag}, marking cancelled`),entry2.cancelled=!0,entry2.buffer=[],this.turnTracker.close(sessionKey2,"reset"),this.heartbeatPublisher?.stop(sessionKey2),await this.removeSession(sessionKey2),await this.drainQueue();return}if(!entry2.session)return;console.log(`[omni-bridge] Session reset for ${sessionKey2}${actionTag}, evicting`),this.turnTracker.close(sessionKey2,"reset"),this.heartbeatPublisher?.stop(sessionKey2);try{await this.executor.shutdown(entry2.session)}catch(err){console.warn(`[omni-bridge] Error shutting down reset session ${sessionKey2}:`,err)}await this.removeSession(sessionKey2),await this.drainQueue()}async handleTurnDone(sessionKey2){if(!this.sessions.get(sessionKey2)?.session)return;console.log(`[omni-bridge] Turn done for ${sessionKey2}, session stays alive for next message`),this.resetIdleTimer(sessionKey2)}async handleTurnTimeout(sessionKey2){let entry2=this.sessions.get(sessionKey2);if(!entry2?.session)return;console.warn(`[omni-bridge] Turn timed out for ${sessionKey2}, evicting session`),this.turnTracker.close(sessionKey2,"timeout"),this.heartbeatPublisher?.stop(sessionKey2);try{await this.executor.shutdown(entry2.session)}catch(err){console.warn(`[omni-bridge] Error shutting down timed-out session ${sessionKey2}:`,err)}this.sessions.delete(sessionKey2)}async routeMessage(message){let key=`${message.agent}:${message.chatId}`,entry2=this.sessions.get(key);if(entry2){if(entry2.spawning){if(entry2.buffer.length<MAX_BUFFER_PER_CHAT)entry2.buffer.push(message);else console.warn(`[omni-bridge] Buffer full (${MAX_BUFFER_PER_CHAT}) for ${key}, dropping message from ${message.sender}`),await this.publishBufferFullReply(message);return}if(await this.executor.isAlive(entry2.session)){await this.executor.deliver(entry2.session,message),this.resetIdleTimer(key);let bsId=entry2.pgBridgeSessionId;if(bsId&&this.sessionStore)this.safePgCall("session_activity",(sql)=>new BridgeSessionStore(sql).recordActivity(bsId),void 0,{chatId:message.chatId});return}await this.removeSession(key)}await this.spawnSession(message)}async spawnSession(message){let key=`${message.agent}:${message.chatId}`,existing=this.sessions.get(key);if(existing){if(existing.buffer.length<MAX_BUFFER_PER_CHAT)existing.buffer.push(message),console.log(`[omni-bridge] Buffered message for existing session ${key} (buffer=${existing.buffer.length})`);return}if(this.sessions.size>=this.maxConcurrent){this.messageQueue.push(message),await this.publishAutoReply(message),console.log(`[omni-bridge] Max concurrent (${this.maxConcurrent}) reached, queued message for ${key}`);return}let placeholder={session:null,instanceId:message.instanceId,spawning:!0,buffer:[message],idleTimer:null};this.sessions.set(key,placeholder);try{let raw=message,payloadEnv=raw.env,spawnEnv={OMNI_API_KEY:payloadEnv?.OMNI_API_KEY??process.env.OMNI_API_KEY??"",OMNI_INSTANCE:payloadEnv?.OMNI_INSTANCE??message.instanceId,OMNI_CHAT:payloadEnv?.OMNI_CHAT??message.chatId,OMNI_MESSAGE:payloadEnv?.OMNI_MESSAGE??raw.messageId??"",OMNI_TURN_ID:payloadEnv?.OMNI_TURN_ID||"",OMNI_SENDER_NAME:payloadEnv?.OMNI_SENDER_NAME??message.sender??""};console.log(`[omni-bridge] Spawning session for ${key}...`);let session=await this.executor.spawn(message.agent,message.chatId,spawnEnv,message.content);if(placeholder.cancelled){console.log(`[omni-bridge] Spawn for ${key} completed but was cancelled by reset, shutting down`);try{await this.executor.shutdown(session)}catch(err){console.warn(`[omni-bridge] Error shutting down cancelled spawn for ${key}:`,err)}return}if(placeholder.session=session,placeholder.spawning=!1,this.sessionStore){let claudeSessionId=session.sdk?.claudeSessionId??session.tmux?.claudeSessionId,pgId=await this.safePgCall("session_create",(sql)=>new BridgeSessionStore(sql).create({instanceId:message.instanceId,chatId:message.chatId,agentName:message.agent,executorId:session.sdk?.executorId,tmuxPaneId:session.tmux?.paneId,claudeSessionId}),void 0,{chatId:message.chatId});if(pgId)placeholder.pgBridgeSessionId=pgId}for(let buffered of placeholder.buffer)await this.executor.deliver(session,buffered);placeholder.buffer=[],this.resetIdleTimer(key);let sessionTag=session.executorType==="tmux"?`(tmux pane=${session.tmux?.paneId})`:"(executor=sdk)";console.log(`[omni-bridge] Session active: ${key} ${sessionTag}`)}catch(err){console.error(`[omni-bridge] Failed to spawn session for ${key}:`,err);let lostMessages=placeholder.buffer;if(lostMessages.length>0)console.warn(`[omni-bridge] Re-queuing ${lostMessages.length} buffered message(s) from failed spawn for ${key}`),this.messageQueue.push(...lostMessages);this.sessions.delete(key)}}resetIdleTimer(key){let entry2=this.sessions.get(key);if(!entry2)return;if(entry2.idleTimer)clearTimeout(entry2.idleTimer);entry2.idleTimer=setTimeout(async()=>{console.log(`[omni-bridge] Idle timeout for ${key}, shutting down...`);try{await this.executor.shutdown(entry2.session)}catch{}await this.removeSession(key),await this.drainQueue()},this.idleTimeoutMs)}async checkIdleSessions(){let now=Date.now();for(let[key,entry2]of this.sessions){if(entry2.spawning)continue;if(!await this.executor.isAlive(entry2.session)){console.log(`[omni-bridge] Dead session detected: ${key}`),await this.removeSession(key);continue}let idleMs=now-entry2.session.lastActivityAt;if(idleMs>this.idleTimeoutMs){console.log(`[omni-bridge] Forcing idle shutdown: ${key} (idle ${Math.round(idleMs/1000)}s)`);try{await this.executor.shutdown(entry2.session)}catch{}await this.removeSession(key)}}}async removeSession(key){let entry2=this.sessions.get(key);if(entry2?.idleTimer)clearTimeout(entry2.idleTimer);this.heartbeatPublisher?.stop(key);let closeId=entry2?.pgBridgeSessionId;if(closeId&&this.sessionStore)await this.safePgCall("session_close",(sql)=>new BridgeSessionStore(sql).close(closeId),void 0);this.sessions.delete(key)}async drainQueue(){while(this.messageQueue.length>0){if(this.sessions.size>=this.maxConcurrent)break;let message=this.messageQueue.shift();if(message)await this.spawnSession(message)}}async publishBufferFullReply(message){if(!this.nc)return;let topic=`omni.reply.${message.instanceId}.${message.chatId}`,reply={content:"Fila de mensagens cheia, por favor aguarde e tente novamente.",agent:message.agent,chat_id:message.chatId,instance_id:message.instanceId,timestamp:new Date().toISOString(),auto_reply:!0};this.nc.publish(topic,this.sc.encode(JSON.stringify(reply)))}async publishAutoReply(message){if(!this.nc)return;let topic=`omni.reply.${message.instanceId}.${message.chatId}`,reply={content:"Aguarde um momento, estou atendendo outros clientes.",agent:message.agent,chat_id:message.chatId,instance_id:message.instanceId,timestamp:new Date().toISOString(),auto_reply:!0};this.nc.publish(topic,this.sc.encode(JSON.stringify(reply)))}}var import_nats4,DEFAULT_NATS_URL2="localhost:4222",DEFAULT_IDLE_TIMEOUT_MS2=900000,DEFAULT_MAX_CONCURRENT=20,MAX_BUFFER_PER_CHAT=50,IDLE_CHECK_INTERVAL_MS=30000,PG_STARTUP_PROBE_TIMEOUT_MS=5000,PG_RUNTIME_QUERY_TIMEOUT_MS=2000;var init_omni_bridge=__esm(()=>{init_bridge_status();init_executor_config();init_agent_heartbeat();init_claude_code2();init_claude_sdk2();import_nats4=__toESM(require_mod4(),1)});import{existsSync as existsSync47,mkdirSync as mkdirSync21,readFileSync as readFileSync29,readdirSync as readdirSync10,renameSync as renameSync7,statSync as statSync12,writeFileSync as writeFileSync18}from"fs";import{homedir as homedir38}from"os";import{dirname as dirname16,join as join59,resolve as resolvePath4}from"path";import{pathToFileURL as pathToFileURL2}from"url";function scanDir(dirAbs){if(!existsSync47(dirAbs))return[];return readdirSync10(dirAbs).filter((name)=>name.endsWith(".ts")&&!name.startsWith("_")&&!name.endsWith(".test.ts")).map((name)=>join59(dirAbs,name)).filter((p)=>{try{return statSync12(p).isFile()}catch{return!1}})}function discoverHooks(opts){let home=homedir38(),discovered=[],teamsRoot=join59(home,".claude","teams");if(existsSync47(teamsRoot))for(let teamName of readdirSync10(teamsRoot)){if(teamName.startsWith("_"))continue;let teamHooksDir=join59(teamsRoot,teamName,"hooks");for(let path3 of scanDir(teamHooksDir))discovered.push({path:path3,scope:"team",teamName})}if(opts.repoRoot){let repoHooksDir=join59(opts.repoRoot,".genie","hooks"),repoRemoteUrl=opts.resolveRepoRemoteUrl?.(opts.repoRoot)??void 0;for(let path3 of scanDir(repoHooksDir))discovered.push({path:path3,scope:"repo",repoRemoteUrl})}let globalHooksDir=join59(home,".genie","hooks");for(let path3 of scanDir(globalHooksDir))discovered.push({path:path3,scope:"global"});return discovered}function quarantine(filePath,errorMessage){let dir=dirname16(filePath),quarantineDir=join59(dir,"_quarantine");if(!existsSync47(quarantineDir))mkdirSync21(quarantineDir,{recursive:!0});let basename11=filePath.split("/").pop()??"unknown.ts",target=join59(quarantineDir,basename11);try{renameSync7(filePath,target)}catch{}try{writeFileSync18(`${target}.error`,errorMessage,"utf-8")}catch{}return target}function validateHandler(imported){if(typeof imported!=="object"||imported===null)return"expected default export to be an object";let handler=imported;if(handler.version!=="1")return`unknown handler version: ${String(handler.version)} (expected '1')`;if(typeof handler.name!=="string"||handler.name.length===0)return"handler.name must be a non-empty string";if(typeof handler.event!=="string")return"handler.event must be a HookEventName string";if(typeof handler.priority!=="number"||Number.isNaN(handler.priority))return"handler.priority must be a finite number";if(typeof handler.fn!=="function")return"handler.fn must be a function";return null}async function loadExternalHooks(opts={}){let trustPath=opts.trustPath??defaultTrustPath(),trustFile;try{trustFile=readTrustFile(trustPath)}catch(err){let msg=err instanceof Error?err.message:String(err);return console.warn(`[hook-loader] trust file unreadable at ${trustPath}: ${msg} \u2014 refusing to load any external hooks`),[]}let importer=opts.importer??((url)=>import(url.href)),discovered=discoverHooks(opts),loaded=[],seenNames=new Map,outcomes=[];for(let source of discovered){let verify=verifyTrust(source.path,trustFile,{currentRepoRemoteUrl:source.repoRemoteUrl});if(!verify.trusted){outcomes.push({kind:"untrusted",reason:verify.reason,source});continue}let imported;try{imported=await importer(pathToFileURL2(resolvePath4(source.path)))}catch(err){let msg=err instanceof Error?`${err.name}: ${err.message}`:String(err),quarantined=quarantine(source.path,msg);outcomes.push({kind:"broken",error:msg,source,quarantined});continue}let handlerCandidate=imported.default,validationError=validateHandler(handlerCandidate);if(validationError!==null){let quarantined=quarantine(source.path,validationError);outcomes.push({kind:"broken",error:validationError,source,quarantined});continue}let stamped={...handlerCandidate,source:source.scope,manifest_path:source.path},existing=seenNames.get(stamped.name);if(existing){if(console.warn(`[hook-loader] handler name collision: "${stamped.name}" registered from ${existing.path} (${existing.scope}) shadows ${source.path} (${source.scope})`),opts.strict)throw Error(`--strict-hooks: handler name collision on "${stamped.name}" between ${existing.path} and ${source.path}`);outcomes.push({kind:"shadowed",handler:stamped,source,shadowedBy:existing});continue}parseCapabilities(readFileSync29(source.path,"utf-8")),seenNames.set(stamped.name,source),loaded.push(stamped),outcomes.push({kind:"loaded",handler:stamped,source})}let builtins=getRegistry().filter((h)=>h.source==="builtin");return setRegistry([...builtins,...loaded]),outcomes}var init_loader=__esm(()=>{init_hooks();init_trust()});var exports_hook_socket={};__export(exports_hook_socket,{startHookSocket:()=>startHookSocket,defaultHookSocketPath:()=>defaultHookSocketPath});import{existsSync as existsSync48,unlinkSync as unlinkSync10}from"fs";import{mkdir as mkdir7}from"fs/promises";import{createServer,connect as netConnect}from"net";import{homedir as homedir39}from"os";import{dirname as dirname17,join as join60}from"path";function defaultHookSocketPath(){if(process.env.GENIE_HOOK_SOCK)return process.env.GENIE_HOOK_SOCK;let home=process.env.GENIE_HOME??join60(homedir39(),".genie");return join60(home,"hook.sock")}async function detectStaleAndCleanup(socketPath){if(!existsSync48(socketPath))return"clean";if(await new Promise((resolve11)=>{let probe=netConnect(socketPath),finish=(alive)=>{try{probe.destroy()}catch{}resolve11(alive)};probe.once("connect",()=>finish(!0)),probe.once("error",()=>finish(!1)),setTimeout(()=>finish(!1),200).unref()}))return"live";try{return unlinkSync10(socketPath),"stale-removed"}catch(err){throw Error(`Failed to remove stale hook socket at ${socketPath}: ${err.message}`)}}function parseFrame(acc,length){if(length===-1){if(acc.length<4)return{kind:"incomplete",length:-1};let declared=acc.readUInt32BE(0);if(declared>MAX_FRAME_BYTES2)return{kind:"error",reason:`frame length ${declared} exceeds MAX_FRAME_BYTES`};if(declared===0)return{kind:"done",body:Buffer.alloc(0)};if(acc.length>=4+declared)return{kind:"done",body:acc.subarray(4,4+declared)};return{kind:"incomplete",length:declared}}if(acc.length>=4+length)return{kind:"done",body:acc.subarray(4,4+length)};return{kind:"incomplete",length}}function readOneFrame(socket){return new Promise((resolve11,reject)=>{let acc=Buffer.alloc(0),length=-1,cleanup=()=>{socket.off("data",onData),socket.off("end",onEnd),socket.off("error",onError)},onData=(chunk)=>{acc=acc.length===0?Buffer.from(chunk):Buffer.concat([acc,Buffer.from(chunk)],acc.length+chunk.length);let result2=parseFrame(acc,length);if(result2.kind==="incomplete"){length=result2.length;return}if(cleanup(),result2.kind==="done")resolve11(result2.body);else reject(Error(result2.reason))},onEnd=()=>{cleanup(),reject(Error(`socket closed after ${acc.length} bytes (expected ${length===-1?"4+":4+length})`))},onError=(err)=>{cleanup(),reject(err)};socket.on("data",onData),socket.on("end",onEnd),socket.on("error",onError)})}function writeFrame(socket,payload){let body=Buffer.from(payload,"utf-8");if(body.length>MAX_FRAME_BYTES2){let empty=Buffer.alloc(4);socket.end(empty);return}let header=Buffer.alloc(4);header.writeUInt32BE(body.length,0),socket.end(Buffer.concat([header,body]))}async function handleConnection(socket){socket.setNoDelay(!0);try{let body=await readOneFrame(socket),stdin=body.length===0?"":body.toString("utf-8"),reply=await dispatch(stdin);writeFrame(socket,reply??"")}catch(err){try{writeFrame(socket,"")}catch{}if(process.env.GENIE_HOOK_SOCK_DEBUG==="1")console.warn(`[hook-socket] connection error: ${err.message}`)}}async function startHookSocket(options={}){if(process.env.GENIE_WIDE_EMIT===void 0)process.env.GENIE_WIDE_EMIT="1";let loaderOutcomes=[];if(options.loadExternal!==!1)loaderOutcomes=await loadExternalHooks({repoRoot:options.repoRoot,strict:options.strict}),summarizeLoaderOutcomes(loaderOutcomes);let socketPath=defaultHookSocketPath();await mkdir7(dirname17(socketPath),{recursive:!0});let state=await detectStaleAndCleanup(socketPath);if(state==="live")throw Error(`hook socket at ${socketPath} is already live \u2014 another genie serve daemon is running. Refusing to start.`);if(state==="stale-removed")console.log(`hook-socket: removed stale socket at ${socketPath}`);let liveSockets=new Set,server3=createServer((socket)=>{liveSockets.add(socket),socket.once("close",()=>liveSockets.delete(socket)),handleConnection(socket)});await new Promise((resolve11,reject)=>{let onError=(err)=>{server3.off("listening",onListening),reject(err)},onListening=()=>{server3.off("error",onError),resolve11()};server3.once("error",onError),server3.once("listening",onListening),server3.listen(socketPath)}),console.log(`hook-socket: listening at ${socketPath}`);let stopped=!1;return{path:socketPath,stop:async()=>{if(stopped)return;stopped=!0;for(let sock of liveSockets)try{sock.destroy()}catch{}if(liveSockets.clear(),await new Promise((resolve11)=>{server3.close(()=>resolve11())}),existsSync48(socketPath))try{unlinkSync10(socketPath)}catch{}},loaderOutcomes}}function summarizeLoaderOutcomes(outcomes){if(outcomes.length===0)return;let loaded=0,untrusted=0,broken=0,shadowed=0;for(let outcome of outcomes)if(outcome.kind==="loaded")loaded+=1;else if(outcome.kind==="untrusted")untrusted+=1;else if(outcome.kind==="broken")broken+=1;else shadowed+=1;let parts=[`hook-loader: ${loaded} loaded`];if(untrusted>0)parts.push(`${untrusted} untrusted`);if(broken>0)parts.push(`${broken} broken (quarantined)`);if(shadowed>0)parts.push(`${shadowed} shadowed`);console.log(parts.join(", "))}var MAX_FRAME_BYTES2=1048576;var init_hook_socket=__esm(()=>{init_hooks();init_loader()});var exports_serve={};__export(exports_serve,{writeStoppingLockSync:()=>writeStoppingLockSync,startBrainServerIfEnabled:()=>startBrainServerIfEnabled,registerServeCommands:()=>registerServeCommands,isTuiSessionReady:()=>isTuiSessionReady,isStoppingLockActive:()=>isStoppingLockActive,isServeRunning:()=>isServeRunning,getTuiQuitBindingArgs:()=>getTuiQuitBindingArgs,getTuiKeybindings:()=>getTuiKeybindings,ensureTuiSession:()=>ensureTuiSession,clearStoppingLock:()=>clearStoppingLock,autoStartServe:()=>autoStartServe});import{execSync as execSync11,spawn as spawn4,spawnSync as spawnSync6}from"child_process";import{closeSync as closeSync5,existsSync as existsSync49,mkdirSync as mkdirSync22,openSync as openSync5,readFileSync as readFileSync30,unlinkSync as unlinkSync11,writeFileSync as writeFileSync19,writeSync as writeSync2}from"fs";import{homedir as homedir40}from"os";import{dirname as dirname18,join as join61}from"path";function genieHome3(){return process.env.GENIE_HOME??join61(homedir40(),".genie")}function servePidPath(){return join61(genieHome3(),"serve.pid")}function serveStartupStatusPath(){return join61(genieHome3(),"state",`serve-startup-${process.pid}-${Date.now()}.json`)}function readServePid(){let path3=servePidPath();if(!existsSync49(path3))return null;let raw=readFileSync30(path3,"utf-8").trim();if(raw==="")return null;let sepIdx=raw.indexOf(":");if(sepIdx<0){let pid2=Number.parseInt(raw,10);if(Number.isNaN(pid2)||pid2<=0)return null;return{pid:pid2,startTime:null}}let pidPart=raw.slice(0,sepIdx),startTimePart=raw.slice(sepIdx+1).trim(),pid=Number.parseInt(pidPart,10);if(Number.isNaN(pid)||pid<=0)return null;return{pid,startTime:startTimePart===""||startTimePart==="unknown"?null:startTimePart}}function removeServePid(){let path3=servePidPath();if(!existsSync49(path3))return;try{let current=readServePid();if(current&&current.pid!==process.pid)return;unlinkSync11(path3)}catch{}}function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch{return!1}}function stoppingLockPath(){return join61(genieHome3(),"serve.stopping.lock")}function writeStoppingLockSync(ttlMs=STOPPING_LOCK_TTL_MS){mkdirSync22(genieHome3(),{recursive:!0}),writeFileSync19(stoppingLockPath(),String(Date.now()+ttlMs),"utf-8")}function clearStoppingLock(){try{unlinkSync11(stoppingLockPath())}catch{}}function isStoppingLockActive(){let path3=stoppingLockPath();if(!existsSync49(path3))return!1;let raw;try{raw=readFileSync30(path3,"utf-8").trim()}catch{return!1}let expiresAt=Number.parseInt(raw,10);if(raw===""||Number.isNaN(expiresAt)||expiresAt<=Date.now())return clearStoppingLock(),!1;return!0}function tuiTmuxConf(){return[join61(genieHome3(),"tui-tmux.conf")].find((p)=>existsSync49(p))??"/dev/null"}function tuiTmux(subcmd){return`${tmuxBin()} -L genie-tui -f ${tuiTmuxConf()} ${subcmd}`}function isGenieTmuxRunning(){try{return execSync11(genieTmuxCmd("list-sessions"),{stdio:"ignore"}),!0}catch{return!1}}function getTuiKeybindings(sessionName=TUI_SESSION){return[`bind-key -T root Tab if-shell "[ '#{pane_index}' = '0' ]" "select-pane -t ${sessionName}:0.1" "select-pane -t ${sessionName}:0.0"`,`bind-key -T root C-1 select-pane -t ${sessionName}:0.0`,`bind-key -T root C-2 select-pane -t ${sessionName}:0.1`,`bind-key -T root C-b if-shell "[ $(tmux display-message -p '#\\{pane_width\\}' -t ${sessionName}:0.0) -gt 5 ]" "resize-pane -t ${sessionName}:0.0 -x 0" "resize-pane -t ${sessionName}:0.0 -x ${NAV_WIDTH}"`,`bind-key -T root C-t select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-t`,"bind-key -T root C-d detach-client",`bind-key -T root C-q select-pane -t ${sessionName}:0.0 \\; send-keys -t ${sessionName}:0.0 C-q`]}function getTuiQuitBindingArgs(sessionName=TUI_SESSION){return["bind-key","-T","root","C-q","select-pane","-t",`${sessionName}:0.0`,"\\;","send-keys","-t",`${sessionName}:0.0`,"C-q"]}function applyTuiStyle(){let cmds=[`set-option -t ${TUI_SESSION} pane-border-style 'fg=${TUI_STYLE.inactiveBorder}'`,`set-option -t ${TUI_SESSION} pane-active-border-style 'fg=${TUI_STYLE.activeBorder}'`,...process.env.GENIE_TMUX_MOUSE!=="off"?[`set-option -t ${TUI_SESSION} mouse on`]:[],`set-option -t ${TUI_SESSION} status off`,`set-option -t ${TUI_SESSION} pane-border-status off`];for(let cmd of cmds)try{execSync11(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function setupTuiKeybindings(){for(let cmd of getTuiKeybindings())try{if(cmd.startsWith("bind-key -T root C-q "))spawnSync6(tmuxBin(),["-L","genie-tui","-f",tuiTmuxConf(),...getTuiQuitBindingArgs()],{stdio:"ignore"});else execSync11(tuiTmux(cmd),{stdio:"ignore"})}catch{}}function startTuiTmuxServer(){try{execSync11(tuiTmux(`has-session -t ${TUI_SESSION}`),{stdio:"ignore"});let panes2=execSync11(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
2531
2531
  `);if(panes2.length>=2){try{execSync11(tuiTmux(`respawn-pane -k -t ${panes2[1]} 'cat'`),{stdio:"ignore"})}catch{}return{leftPane:panes2[0],rightPane:panes2[1]}}let cols2=Number.parseInt(execSync11(tuiTmux(`display-message -t ${TUI_SESSION}:0 -p '#{window_width}'`),{encoding:"utf-8"}).trim(),10)||120;execSync11(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols2-NAV_WIDTH-1}`),{stdio:"ignore"});let refreshed=execSync11(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
2532
2532
  `);applyTuiStyle(),setupTuiKeybindings();try{execSync11(tuiTmux(`select-pane -t ${refreshed[0]}`),{stdio:"ignore"})}catch{}return{leftPane:refreshed[0],rightPane:refreshed[1]||refreshed[0]}}catch{}let cols=120;execSync11(tuiTmux(`new-session -d -s ${TUI_SESSION} -x ${cols} -y ${40}`),{stdio:"ignore"}),execSync11(tuiTmux(`split-window -h -t ${TUI_SESSION}:0 -l ${cols-NAV_WIDTH-1}`),{stdio:"ignore"});let panes=execSync11(tuiTmux(`list-panes -t ${TUI_SESSION}:0 -F '#{pane_id}'`),{encoding:"utf-8"}).trim().split(`
2533
2533
  `);applyTuiStyle(),setupTuiKeybindings();try{execSync11(tuiTmux(`select-pane -t ${panes[0]}`),{stdio:"ignore"})}catch{}return{leftPane:panes[0],rightPane:panes[1]||panes[0]}}function sendTuiLaunchScript(leftPane,rightPane,workspaceRoot){let home=genieHome3(),bunPath=process.execPath||"bun",genieBin=process.argv[1]||"genie",scriptPath=join61(home,"tui-launch.sh"),logsDir=join61(home,"logs"),crashLog=join61(logsDir,"tui-crash.log"),envVars=["GENIE_TUI_PANE=left",`GENIE_TUI_RIGHT=${rightPane}`];if(workspaceRoot)envVars.push(`GENIE_TUI_WORKSPACE=${workspaceRoot}`);let content=["#!/bin/sh",`mkdir -p '${logsDir}'`,`exec 2>> '${crashLog}'`,`printf -- '--- tui-launch %s pid=%s ---\\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$$" >&2`,`export ${envVars.join(`
@@ -2540,8 +2540,8 @@ genie serve is running (${mode}). ${headless?"Send SIGTERM to stop.":"Press Ctrl
2540
2540
  `))}function printHookSocketStatus(){let sock=hookSocketPath();if(existsSync49(sock))console.log(` hook UDS: listening at ${sock}`);else console.log(` hook UDS: MISSING at ${sock} (F1 fallback active \u2014 see #1490)`)}function resolveBrainPortFromWorkspace(brain){try{let{findWorkspace:findWorkspace2}=(init_workspace(),__toCommonJS(exports_workspace)),ws=findWorkspace2();if(ws?.root&&brain.readServerInfo){let info=brain.readServerInfo(join61(ws.root,"brain"));if(info?.port)return info.port}}catch{}return null}async function probeBrainHealth(brainPort){try{let resp=await fetch(`http://127.0.0.1:${brainPort}/healthz`);console.log(resp.ok?` brain: running (port ${brainPort})`:` brain: unhealthy (port ${brainPort}, status ${resp.status})`)}catch{console.log(` brain: stopped (port ${brainPort} unreachable)`)}}async function printBrainStatus(){try{let brain=await import("@khal-os/brain");if(handles.brainHandles.length>0){for(let handle of handles.brainHandles)await probeBrainHealth(handle.port);return}let brainPort=resolveBrainPortFromWorkspace(brain);if(brainPort)await probeBrainHealth(brainPort);else console.log(" brain: stopped")}catch{console.log(" brain: not installed")}}async function printPgserveStatus(){await printPgserveHealth(),printHookSocketStatus(),await printBrainStatus()}function printTmuxStatus(){let agentRunning=isGenieTmuxRunning(),sessions=agentRunning?listAgentSessions():[];if(console.log(` tmux -L genie: ${agentRunning?`running (${sessions.length} sessions)`:"stopped"}`),sessions.length>0)console.log(` ${sessions.join(", ")}`);let tuiReady=isTuiSessionReady();console.log(` tmux -L genie-tui: ${tuiReady?"running":"stopped"}`)}async function printDaemonStatus(serveRunning){try{let schedulerPidPath=join61(genieHome3(),"scheduler.pid");if(existsSync49(schedulerPidPath)){let sPid=Number.parseInt(readFileSync30(schedulerPidPath,"utf-8").trim(),10),sAlive=!Number.isNaN(sPid)&&isProcessAlive(sPid);console.log(` scheduler: ${sAlive?`running (PID ${sPid})`:"stopped"}`)}else if(serveRunning)console.log(" scheduler: integrated (in-process)");else console.log(" scheduler: stopped")}catch{console.log(" scheduler: unknown")}try{let{getInboxPollIntervalMs:getInboxPollIntervalMs2}=await Promise.resolve().then(() => (init_inbox_watcher(),exports_inbox_watcher)),pollMs=getInboxPollIntervalMs2();if(pollMs===0)console.log(" inbox: disabled");else console.log(` inbox: ${serveRunning?"watching":"stopped"} (poll ${pollMs/1000}s)`)}catch{console.log(" inbox: unavailable")}}async function printBridgeStatus(){try{let{getBridgeStatus:getBridgeStatus2}=await Promise.resolve().then(() => (init_bridge_status(),exports_bridge_status)),res=await getBridgeStatus2();if(res.state==="running"&&res.pong){let uptimeSec=Math.round(res.pong.uptimeMs/1000),latency=res.latencyMs??0;console.log(` omni-bridge: running (pid ${res.pong.pid}, uptime ${uptimeSec}s, ping ${latency}ms)`)}else if(res.state==="stale")console.log(` omni-bridge: stale \u2014 ${res.detail}`);else console.log(" omni-bridge: stopped")}catch{console.log(" omni-bridge: unavailable")}}async function statusServe(){let entry2=readServePid(),running2=entry2!==null&&isProcessAlive(entry2.pid);if(console.log(`
2541
2541
  Genie Serve`),console.log("\u2500".repeat(50)),console.log(` Status: ${running2?"running":"stopped"}`),running2&&entry2)console.log(` PID: ${entry2.pid}`);await printPgserveStatus(),printTmuxStatus(),await printDaemonStatus(running2),await printBridgeStatus(),console.log(` PID file: ${servePidPath()}`),console.log("")}async function runStartPreconditions(autoFix,log2){if(process.env.GENIE_SKIP_PRECONDITIONS==="1")return!0;let{ensureServeReady:ensureServeReady2}=await Promise.resolve().then(() => (init_ensure_ready(),exports_ensure_ready)),report=await ensureServeReady2({autoFix,deps:log2?{log:log2}:void 0});if(autoFix)return!0;if(!report.ok){if(log2)log2("genie serve start refused: one or more preconditions are not ok (--no-fix mode).");else console.error("genie serve start refused: one or more preconditions are not ok (--no-fix mode).");return!1}return!0}function registerServeCommands(program2){let serve=program2.command("serve").description("Start all genie infrastructure (pgserve, tmux, scheduler)");serve.command("start",{isDefault:!0}).description("Start genie serve").option("--daemon","Run in background").option("--foreground","Run in foreground (default)").option("--headless","Run without TUI (services only: pgserve, scheduler, inbox-watcher)").option("--no-fix","Refuse to start when any precondition is not ok (default: auto-fix)").option("--strict-hooks","Refuse to start on any same-name external hook collision (default: warn + continue)").action(async(options)=>{let autoFix=options.fix!==!1;if(options.strictHooks)process.env.GENIE_STRICT_HOOKS="1";if(options.daemon)await startBackground(options.headless,autoFix);else await startForeground(options.headless,autoFix)}),serve.command("stop").description("Stop genie serve and all services").action(async()=>{await stopServe()}),serve.command("status").description("Show service health").action(async()=>{await statusServe()})}var STOPPING_LOCK_TTL_MS=30000,TUI_SESSION="genie-tui",NAV_WIDTH=30,TUI_STYLE,handles,operatorCwd;var init_serve=__esm(()=>{init_genie_tokens();init_brain_vaults();init_ensure_tmux();init_process_identity();init_tmux_wrapper();init_tui_disable();TUI_STYLE={activeBorder:palette.borderActive,inactiveBorder:palette.border};handles={schedulerHandle:null,agentWatcher:null,brainHandles:[],omniApprovalHandler:null,omniBridge:null,detectorScheduler:null,derivedSignals:null,hookSocket:null};operatorCwd=process.cwd()});var exports_tmux2={};__export(exports_tmux2,{newAgentWindow:()=>newAgentWindow,hasProjectSession:()=>hasProjectSession,attachTuiSession:()=>attachTuiSession,attachProjectWindow:()=>attachProjectWindow});import{spawnSync as spawnSync7}from"child_process";function runTuiTmux(args,stdio="ignore"){return spawnSync7(TMUX_BIN,["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,...args],{stdio})}function runTuiTmuxOutput(args){let result2=spawnSync7(TMUX_BIN,["-L",TMUX_SOCKET,"-f",TUI_TMUX_CONF,...args],{encoding:"utf-8"});return result2.status===0?result2.stdout.trim():null}function runAgentTmux(args,stdio="ignore"){return spawnSync7(TMUX_BIN,["-L",GENIE_AGENT_SOCKET,...args],{stdio})}function shellQuote3(value){return`'${value.replace(/'/g,"'\\''")}'`}function buildAttachLoop(targetSession){return`while true; do TMUX='' ${[TMUX_BIN,"-L",GENIE_AGENT_SOCKET,"attach-session","-t",targetSession].map(shellQuote3).join(" ")} 2>/dev/null; sleep 0.3; done`}function resolveRightPane(rightPane){if(runTuiTmux(["display-message","-t",rightPane,"-p",""]).status===0)return rightPane;let panes=runTuiTmuxOutput(["list-panes","-t",`${SESSION_NAME}:0`,"-F","#{pane_id}"])?.split(`
2542
2542
  `)??[];return panes[1]||panes[0]||rightPane}function hasProjectSession(targetSession){return runAgentTmux(["has-session","-t",targetSession]).status===0}function attachProjectWindow(rightPane,targetSession,windowIndex){if(targetSession===SESSION_NAME)return;let pane=resolveRightPane(rightPane);if(!hasProjectSession(targetSession))return;if(windowIndex!==void 0)runAgentTmux(["select-window","-t",`${targetSession}:${windowIndex}`]);runAgentTmux(["set-option","-t",targetSession,"status","off"]),runTuiTmux(["respawn-pane","-k","-t",pane,"sh","-lc",buildAttachLoop(targetSession)]),runTuiTmux(["select-pane","-t",`${SESSION_NAME}:0.0`])}function attachTuiSession(){if(runTuiTmux(["select-pane","-t",`${SESSION_NAME}:0.0`]),process.env.TMUX)runTuiTmux(["switch-client","-t",SESSION_NAME],"inherit");else runTuiTmux(["attach-session","-t",SESSION_NAME],"inherit")}function nextRoleSuffix(baseName){let sessionName=baseName.replace(/\//g,"-"),output=spawnSync7(TMUX_BIN,["-L",GENIE_AGENT_SOCKET,"list-windows","-t",`=${sessionName}`,"-F","#{window_name}"],{encoding:"utf-8"}),names=output.status===0?output.stdout.trim().split(`
2543
- `):[],max=names.length+1,re=new RegExp(`^${baseName}-(\\d+)$`);for(let n of names){let m=n.match(re);if(m)max=Math.max(max,Number.parseInt(m[1],10)+1)}return max}function newAgentWindow(agentName){(async()=>{try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch{}let{spawn:spawn5}=__require("child_process"),{join:join62,resolve:resolve11}=__require("path"),{existsSync:existsSync50}=__require("fs"),bunPath=process.execPath||"bun",genieBin=process.argv[1],wsRoot=process.env.GENIE_TUI_WORKSPACE,cwd;if(wsRoot){let parentName=agentName.includes("/")?agentName.slice(0,agentName.indexOf("/")):agentName,agentDir=resolve11(join62(wsRoot,"agents",parentName));if(existsSync50(agentDir))cwd=agentDir}let suffix=nextRoleSuffix(agentName),role=`${agentName}-${suffix}`,sessionName=agentName.replace(/\//g,"-"),args=["spawn",agentName,"--role",role,"--session",sessionName,"--new-window"];(genieBin&&genieBin!=="genie"?spawn5(bunPath,[genieBin,...args],{detached:!0,stdio:"ignore",cwd}):spawn5("genie",args,{detached:!0,stdio:"ignore",cwd})).unref()})()}var SESSION_NAME="genie-tui",TMUX_SOCKET="genie-tui",GENIE_AGENT_SOCKET="genie",TUI_TMUX_CONF,TMUX_BIN;var init_tmux2=__esm(()=>{init_ensure_tmux();TUI_TMUX_CONF=(()=>{let{existsSync:existsSync50}=__require("fs"),tuiConf=`${process.env.GENIE_HOME??`${process.env.HOME}/.genie`}/tui-tmux.conf`;return existsSync50(tuiConf)?tuiConf:"/dev/null"})(),TMUX_BIN=tmuxBin()});function s(orgId,path3){return`khal.${orgId}.genie.${path3}`}var GENIE_SUBJECTS;var init_subjects=__esm(()=>{GENIE_SUBJECTS={dashboard:{stats:(orgId)=>s(orgId,"dashboard.stats")},agents:{list:(orgId)=>s(orgId,"agents.list"),show:(orgId)=>s(orgId,"agents.show")},sessions:{list:(orgId)=>s(orgId,"sessions.list"),content:(orgId)=>s(orgId,"sessions.content"),search:(orgId)=>s(orgId,"sessions.search")},tasks:{list:(orgId)=>s(orgId,"tasks.list"),show:(orgId)=>s(orgId,"tasks.show")},boards:{list:(orgId)=>s(orgId,"boards.list"),show:(orgId)=>s(orgId,"boards.show")},costs:{summary:(orgId)=>s(orgId,"costs.summary"),sessions:(orgId)=>s(orgId,"costs.sessions"),tokens:(orgId)=>s(orgId,"costs.tokens"),efficiency:(orgId)=>s(orgId,"costs.efficiency")},schedules:{list:(orgId)=>s(orgId,"schedules.list"),history:(orgId)=>s(orgId,"schedules.history")},system:{health:(orgId)=>s(orgId,"system.health"),snapshots:(orgId)=>s(orgId,"system.snapshots"),tables:(orgId)=>s(orgId,"system.tables"),channels:(orgId)=>s(orgId,"system.channels")},settings:{get:(orgId)=>s(orgId,"settings.get"),set:(orgId)=>s(orgId,"settings.set"),templates:(orgId)=>s(orgId,"settings.templates"),templateSave:(orgId)=>s(orgId,"settings.templates.save"),skills:(orgId)=>s(orgId,"settings.skills"),rules:(orgId)=>s(orgId,"settings.rules"),workspace:(orgId)=>s(orgId,"settings.workspace"),testPg:(orgId)=>s(orgId,"settings.test_pg")},pty:{create:(orgId)=>s(orgId,"pty.create"),input:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.input`),data:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.data`),resize:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.resize`),kill:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.kill`)},fs:{list:(orgId)=>s(orgId,"fs.list"),read:(orgId)=>s(orgId,"fs.read"),write:(orgId)=>s(orgId,"fs.write")},approval:{resolve:(orgId)=>s(orgId,"approval.resolve"),list:(orgId)=>s(orgId,"approval.list")},events:{agentState:(orgId)=>s(orgId,"events.agent_state"),executorState:(orgId)=>s(orgId,"events.executor_state"),taskStage:(orgId)=>s(orgId,"events.task_stage"),runtime:(orgId)=>s(orgId,"events.runtime"),audit:(orgId)=>s(orgId,"events.audit"),message:(orgId)=>s(orgId,"events.message"),mailbox:(orgId)=>s(orgId,"events.mailbox"),taskDep:(orgId)=>s(orgId,"events.task_dep"),trigger:(orgId)=>s(orgId,"events.trigger"),approvalRequest:(orgId)=>s(orgId,"events.approval_request"),approvalResolved:(orgId)=>s(orgId,"events.approval_resolved")}}});function emit2(event){for(let handler of listeners)handler(event)}async function startListening(nats,orgId){if(listenersActive)return;if(listenersActive=!0,nats)natsConn=nats,natsOrgId=orgId??"default";let sql=await getConnection();for(let mapping of CHANNEL_MAP){let listener=await sql.listen(mapping.channel,(payload)=>{let parsed;try{parsed=JSON.parse(payload)}catch{parsed={raw:payload}}if(emit2({type:mapping.eventType,payload:parsed}),natsConn){let subject=mapping.natsSubject(natsOrgId);natsConn.publish(subject,sc.encode(JSON.stringify(parsed)))}});stopFns.push(()=>listener.unlisten())}}async function stopListening(){listenersActive=!1,natsConn=null,await Promise.allSettled(stopFns.map((fn)=>fn())),stopFns=[]}var import_nats4,listeners,natsConn=null,natsOrgId="default",sc,CHANNEL_MAP,listenersActive=!1,stopFns;var init_pg_bridge=__esm(()=>{init_db();init_subjects();import_nats4=__toESM(require_mod4(),1),listeners=new Set;sc=import_nats4.StringCodec(),CHANNEL_MAP=[{channel:"genie_agent_state",eventType:"agent-state-changed",natsSubject:GENIE_SUBJECTS.events.agentState},{channel:"genie_executor_state",eventType:"executor-state-changed",natsSubject:GENIE_SUBJECTS.events.executorState},{channel:"genie_task_stage",eventType:"task-stage-changed",natsSubject:GENIE_SUBJECTS.events.taskStage},{channel:"genie_runtime_event",eventType:"runtime-event",natsSubject:GENIE_SUBJECTS.events.runtime},{channel:"genie_audit_event",eventType:"audit-event",natsSubject:GENIE_SUBJECTS.events.audit},{channel:"genie_message",eventType:"message",natsSubject:GENIE_SUBJECTS.events.message},{channel:"genie_mailbox_delivery",eventType:"mailbox-delivery",natsSubject:GENIE_SUBJECTS.events.mailbox},{channel:"genie_task_dep",eventType:"task-dep-changed",natsSubject:GENIE_SUBJECTS.events.taskDep},{channel:"genie_trigger_due",eventType:"trigger-due",natsSubject:GENIE_SUBJECTS.events.trigger},{channel:"genie_approval_request",eventType:"approval-request",natsSubject:GENIE_SUBJECTS.events.approvalRequest},{channel:"genie_approval_resolved",eventType:"approval-resolved",natsSubject:GENIE_SUBJECTS.events.approvalResolved}],stopFns=[]});import{randomUUID as randomUUID10}from"crypto";function onPtyData(cb){dataCallback=cb}function onPtyExit(cb){exitCallback=cb}async function spawnForAgent(agentName,opts={}){let{buildClaudeCommand:buildClaudeCommand3}=await Promise.resolve().then(() => (init_provider_adapters(),exports_provider_adapters)),{createExecutor:createExecutor2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),{findOrCreateAgent:findOrCreateAgent2,setCurrentExecutor:setCurrentExecutor2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),cols=opts.cols??120,rows=opts.rows??40,cwd=opts.cwd??process.cwd(),launch=buildClaudeCommand3({provider:"claude",team:"app",role:"engineer",name:agentName}),agent=await findOrCreateAgent2(agentName,"app","engineer"),executor=await createExecutor2(agent.id,"app-pty","process",{repoPath:cwd,state:"spawning",metadata:{command:launch.command,source:"genie-app"}});await setCurrentExecutor2(agent.id,executor.id);let env={...process.env,...launch.env,GENIE_APP_PTY:"true"},session=spawnPty(launch.command,{cwd,cols,rows,env,agentId:agent.id,executorId:executor.id,taskId:opts.taskId??null}),{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));return await updateExecutorState2(executor.id,"running"),session}function spawnBash(cwd){let shell=process.env.SHELL??"/bin/bash";return spawnPty(shell,{cwd:cwd??process.cwd(),cols:120,rows:40,env:process.env,agentId:null,executorId:null,taskId:null})}function writeTerminal(sessionId,data){let session=sessions.get(sessionId);if(!session||session.state!=="running")return!1;return session.pty.write(data),!0}function resizeTerminal(sessionId,cols,rows){let session=sessions.get(sessionId);if(!session||session.state!=="running")return!1;return session.pty.resize(cols,rows),session.cols=cols,session.rows=rows,!0}async function killTerminal(sessionId){let session=sessions.get(sessionId);if(!session)return!1;if(session.pty.kill(),session.state="exited",session.executorId)try{let{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));await updateExecutorState2(session.executorId,"terminated")}catch{}return sessions.delete(sessionId),!0}async function killAll(){let ids=[...sessions.keys()];await Promise.allSettled(ids.map((id)=>killTerminal(id)))}async function pipeStdout(stdout,sessionId,ptyHandle){let reader=stdout.getReader(),decoder=new TextDecoder;try{while(!0){let{done,value}=await reader.read();if(done)break;let text=decoder.decode(value);if(dataCallback)dataCallback(sessionId,text);if(ptyHandle.onData)ptyHandle.onData(text)}}catch{}}function onProcExit(sessionId,code,ptyHandle){let session=sessions.get(sessionId);if(session)session.state="exited";if(exitCallback)exitCallback(sessionId,code);if(ptyHandle.onExit)ptyHandle.onExit(code);handlePtyExit(sessionId,code)}function spawnPty(command,opts){let sessionId=randomUUID10(),ptyHandle;try{let bunPty=(()=>{throw new Error("Cannot require module "+"bun-pty");})(),parts=command.split(" "),raw=bunPty.spawn(parts,{cwd:opts.cwd,env:opts.env,cols:opts.cols,rows:opts.rows});ptyHandle={write:(data)=>raw.write(data),resize:(cols,rows)=>raw.resize(cols,rows),kill:()=>raw.kill(),onData:null,onExit:null},raw.onData=(data)=>{if(dataCallback)dataCallback(sessionId,data);if(ptyHandle.onData)ptyHandle.onData(data)},raw.onExit=(code)=>{let session2=sessions.get(sessionId);if(session2)session2.state="exited";if(exitCallback)exitCallback(sessionId,code);if(ptyHandle.onExit)ptyHandle.onExit(code);handlePtyExit(sessionId,code)}}catch{let parts=command.split(" "),proc=Bun.spawn(parts,{cwd:opts.cwd,env:opts.env,stdin:"pipe",stdout:"pipe",stderr:"pipe"});ptyHandle={write:(data)=>proc.stdin.write(data),resize:()=>{},kill:()=>proc.kill(),onData:null,onExit:null},pipeStdout(proc.stdout,sessionId,ptyHandle),proc.exited.then((code)=>onProcExit(sessionId,code,ptyHandle))}let session={id:sessionId,pty:ptyHandle,agentId:opts.agentId,executorId:opts.executorId,taskId:opts.taskId,command,state:"running",cols:opts.cols,rows:opts.rows,createdAt:new Date().toISOString()};return sessions.set(sessionId,session),session}function handlePtyExit(sessionId,code){let session=sessions.get(sessionId);if(!session?.executorId)return;(async()=>{try{let{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));await updateExecutorState2(session.executorId,code===0?"done":"error")}catch{}})()}var sessions,dataCallback=null,exitCallback=null;var init_pty=__esm(()=>{sessions=new Map});var exports_src_backend={};import{existsSync as existsSync50,readFileSync as readFileSync31,readdirSync as readdirSync11,writeFileSync as writeFileSync20}from"fs";import{homedir as homedir41}from"os";import{join as join62,resolve as resolve11}from"path";function findSkillsDir(){let genieHome4=process.env.GENIE_HOME??join62(homedir41(),".genie"),candidateDirs=[join62(process.cwd(),"skills"),join62(genieHome4,"..","skills")];for(let d of candidateDirs)if(existsSync50(d))return d;return null}function parseSkillMd(content,fallbackName){let descMatch=content.match(/^description:\s*["']?(.+?)["']?\s*$/m),description="";if(descMatch)description=descMatch[1].trim();else description=content.split(`
2544
- `).filter((l)=>l.trim()&&!l.startsWith("---")&&!l.startsWith("#"))[0]?.trim()??"";let nameMatch=content.match(/^name:\s*(.+?)\s*$/m);return{name:nameMatch?nameMatch[1].trim():fallbackName,description}}function scanSkillsDirectory(skillsDir){try{let entries=readdirSync11(skillsDir,{withFileTypes:!0}),skills=[];for(let entry2 of entries){if(!entry2.isDirectory())continue;let skillMdPath=join62(skillsDir,entry2.name,"SKILL.md");if(!existsSync50(skillMdPath))continue;let content=readFileSync31(skillMdPath,"utf-8"),{name,description}=parseSkillMd(content,entry2.name);skills.push({name,slug:entry2.name,description,path:skillMdPath})}return skills.sort((a,b2)=>a.name.localeCompare(b2.name))}catch{return[]}}function scanRuleDirs(){let genieHome4=process.env.GENIE_HOME??join62(homedir41(),".genie"),ruleDirs=[join62(genieHome4,"rules"),join62(homedir41(),".claude","rules")],allRules=[];for(let rulesDir of ruleDirs){if(!existsSync50(rulesDir))continue;try{let files=readdirSync11(rulesDir).filter((f)=>f.endsWith(".md"));for(let f of files){let filePath=join62(rulesDir,f);allRules.push({name:f.replace(".md",""),path:filePath,content:readFileSync31(filePath,"utf-8"),source:rulesDir.includes(".claude")?"claude":"genie"})}}catch{}}return allRules}async function start2(){console.log("[genie-app] Starting backend service..."),console.log(`[genie-app] NATS: ${NATS_URL}`),console.log(`[genie-app] Org: ${ORG_ID}`);let sql=await getConnection();console.log("[genie-app] PG connected"),nc=await import_nats5.connect({servers:NATS_URL,name:"genie-app-backend",reconnect:!0,maxReconnectAttempts:-1,reconnectTimeWait:2000}),console.log("[genie-app] NATS connected"),registerHandlers(sql),await startListening(nc,ORG_ID),console.log("[genie-app] PG LISTEN/NOTIFY active (11 channels)"),onPtyData((sessionId,data)=>{if(!nc)return;nc.publish(GENIE_SUBJECTS.pty.data(ORG_ID,sessionId),sc2.encode(JSON.stringify({data})))}),onPtyExit((sessionId,code)=>{if(!nc)return;nc.publish(GENIE_SUBJECTS.pty.data(ORG_ID,sessionId),sc2.encode(JSON.stringify({code,type:"exit"})))}),console.log("[genie-app] Backend ready")}function registerHandlers(sql){if(!nc)return;let sub=GENIE_SUBJECTS;reply(sub.dashboard.stats(ORG_ID),async()=>{let[agentRows,taskRows,teamRows,costRows,snapshot]=await Promise.all([sql`
2543
+ `):[],max=names.length+1,re=new RegExp(`^${baseName}-(\\d+)$`);for(let n of names){let m=n.match(re);if(m)max=Math.max(max,Number.parseInt(m[1],10)+1)}return max}function newAgentWindow(agentName){(async()=>{try{let{reconcileStaleSpawns:reconcileStaleSpawns2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry));await reconcileStaleSpawns2()}catch{}let{spawn:spawn5}=__require("child_process"),{join:join62,resolve:resolve11}=__require("path"),{existsSync:existsSync50}=__require("fs"),bunPath=process.execPath||"bun",genieBin=process.argv[1],wsRoot=process.env.GENIE_TUI_WORKSPACE,cwd;if(wsRoot){let parentName=agentName.includes("/")?agentName.slice(0,agentName.indexOf("/")):agentName,agentDir=resolve11(join62(wsRoot,"agents",parentName));if(existsSync50(agentDir))cwd=agentDir}let suffix=nextRoleSuffix(agentName),role=`${agentName}-${suffix}`,sessionName=agentName.replace(/\//g,"-"),args=["spawn",agentName,"--role",role,"--session",sessionName,"--new-window"];(genieBin&&genieBin!=="genie"?spawn5(bunPath,[genieBin,...args],{detached:!0,stdio:"ignore",cwd}):spawn5("genie",args,{detached:!0,stdio:"ignore",cwd})).unref()})()}var SESSION_NAME="genie-tui",TMUX_SOCKET="genie-tui",GENIE_AGENT_SOCKET="genie",TUI_TMUX_CONF,TMUX_BIN;var init_tmux2=__esm(()=>{init_ensure_tmux();TUI_TMUX_CONF=(()=>{let{existsSync:existsSync50}=__require("fs"),tuiConf=`${process.env.GENIE_HOME??`${process.env.HOME}/.genie`}/tui-tmux.conf`;return existsSync50(tuiConf)?tuiConf:"/dev/null"})(),TMUX_BIN=tmuxBin()});function s(orgId,path3){return`khal.${orgId}.genie.${path3}`}var GENIE_SUBJECTS;var init_subjects=__esm(()=>{GENIE_SUBJECTS={dashboard:{stats:(orgId)=>s(orgId,"dashboard.stats")},agents:{list:(orgId)=>s(orgId,"agents.list"),show:(orgId)=>s(orgId,"agents.show")},sessions:{list:(orgId)=>s(orgId,"sessions.list"),content:(orgId)=>s(orgId,"sessions.content"),search:(orgId)=>s(orgId,"sessions.search")},tasks:{list:(orgId)=>s(orgId,"tasks.list"),show:(orgId)=>s(orgId,"tasks.show")},boards:{list:(orgId)=>s(orgId,"boards.list"),show:(orgId)=>s(orgId,"boards.show")},costs:{summary:(orgId)=>s(orgId,"costs.summary"),sessions:(orgId)=>s(orgId,"costs.sessions"),tokens:(orgId)=>s(orgId,"costs.tokens"),efficiency:(orgId)=>s(orgId,"costs.efficiency")},schedules:{list:(orgId)=>s(orgId,"schedules.list"),history:(orgId)=>s(orgId,"schedules.history")},system:{health:(orgId)=>s(orgId,"system.health"),snapshots:(orgId)=>s(orgId,"system.snapshots"),tables:(orgId)=>s(orgId,"system.tables"),channels:(orgId)=>s(orgId,"system.channels")},settings:{get:(orgId)=>s(orgId,"settings.get"),set:(orgId)=>s(orgId,"settings.set"),templates:(orgId)=>s(orgId,"settings.templates"),templateSave:(orgId)=>s(orgId,"settings.templates.save"),skills:(orgId)=>s(orgId,"settings.skills"),rules:(orgId)=>s(orgId,"settings.rules"),workspace:(orgId)=>s(orgId,"settings.workspace"),testPg:(orgId)=>s(orgId,"settings.test_pg")},pty:{create:(orgId)=>s(orgId,"pty.create"),input:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.input`),data:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.data`),resize:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.resize`),kill:(orgId,sessionId)=>s(orgId,`pty.${sessionId}.kill`)},fs:{list:(orgId)=>s(orgId,"fs.list"),read:(orgId)=>s(orgId,"fs.read"),write:(orgId)=>s(orgId,"fs.write")},approval:{resolve:(orgId)=>s(orgId,"approval.resolve"),list:(orgId)=>s(orgId,"approval.list")},events:{agentState:(orgId)=>s(orgId,"events.agent_state"),executorState:(orgId)=>s(orgId,"events.executor_state"),taskStage:(orgId)=>s(orgId,"events.task_stage"),runtime:(orgId)=>s(orgId,"events.runtime"),audit:(orgId)=>s(orgId,"events.audit"),message:(orgId)=>s(orgId,"events.message"),mailbox:(orgId)=>s(orgId,"events.mailbox"),taskDep:(orgId)=>s(orgId,"events.task_dep"),trigger:(orgId)=>s(orgId,"events.trigger"),approvalRequest:(orgId)=>s(orgId,"events.approval_request"),approvalResolved:(orgId)=>s(orgId,"events.approval_resolved")}}});function emit2(event){for(let handler of listeners)handler(event)}async function startListening(nats,orgId){if(listenersActive)return;if(listenersActive=!0,nats)natsConn=nats,natsOrgId=orgId??"default";let sql=await getConnection();for(let mapping of CHANNEL_MAP){let listener=await sql.listen(mapping.channel,(payload)=>{let parsed;try{parsed=JSON.parse(payload)}catch{parsed={raw:payload}}if(emit2({type:mapping.eventType,payload:parsed}),natsConn){let subject=mapping.natsSubject(natsOrgId);natsConn.publish(subject,sc.encode(JSON.stringify(parsed)))}});stopFns.push(()=>listener.unlisten())}}async function stopListening(){listenersActive=!1,natsConn=null,await Promise.allSettled(stopFns.map((fn)=>fn())),stopFns=[]}var import_nats5,listeners,natsConn=null,natsOrgId="default",sc,CHANNEL_MAP,listenersActive=!1,stopFns;var init_pg_bridge=__esm(()=>{init_db();init_subjects();import_nats5=__toESM(require_mod4(),1),listeners=new Set;sc=import_nats5.StringCodec(),CHANNEL_MAP=[{channel:"genie_agent_state",eventType:"agent-state-changed",natsSubject:GENIE_SUBJECTS.events.agentState},{channel:"genie_executor_state",eventType:"executor-state-changed",natsSubject:GENIE_SUBJECTS.events.executorState},{channel:"genie_task_stage",eventType:"task-stage-changed",natsSubject:GENIE_SUBJECTS.events.taskStage},{channel:"genie_runtime_event",eventType:"runtime-event",natsSubject:GENIE_SUBJECTS.events.runtime},{channel:"genie_audit_event",eventType:"audit-event",natsSubject:GENIE_SUBJECTS.events.audit},{channel:"genie_message",eventType:"message",natsSubject:GENIE_SUBJECTS.events.message},{channel:"genie_mailbox_delivery",eventType:"mailbox-delivery",natsSubject:GENIE_SUBJECTS.events.mailbox},{channel:"genie_task_dep",eventType:"task-dep-changed",natsSubject:GENIE_SUBJECTS.events.taskDep},{channel:"genie_trigger_due",eventType:"trigger-due",natsSubject:GENIE_SUBJECTS.events.trigger},{channel:"genie_approval_request",eventType:"approval-request",natsSubject:GENIE_SUBJECTS.events.approvalRequest},{channel:"genie_approval_resolved",eventType:"approval-resolved",natsSubject:GENIE_SUBJECTS.events.approvalResolved}],stopFns=[]});import{randomUUID as randomUUID10}from"crypto";function onPtyData(cb){dataCallback=cb}function onPtyExit(cb){exitCallback=cb}async function spawnForAgent(agentName,opts={}){let{buildClaudeCommand:buildClaudeCommand3}=await Promise.resolve().then(() => (init_provider_adapters(),exports_provider_adapters)),{createExecutor:createExecutor2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),{findOrCreateAgent:findOrCreateAgent2,setCurrentExecutor:setCurrentExecutor2}=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),cols=opts.cols??120,rows=opts.rows??40,cwd=opts.cwd??process.cwd(),launch=buildClaudeCommand3({provider:"claude",team:"app",role:"engineer",name:agentName}),agent=await findOrCreateAgent2(agentName,"app","engineer"),executor=await createExecutor2(agent.id,"app-pty","process",{repoPath:cwd,state:"spawning",metadata:{command:launch.command,source:"genie-app"}});await setCurrentExecutor2(agent.id,executor.id);let env={...process.env,...launch.env,GENIE_APP_PTY:"true"},session=spawnPty(launch.command,{cwd,cols,rows,env,agentId:agent.id,executorId:executor.id,taskId:opts.taskId??null}),{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));return await updateExecutorState2(executor.id,"running"),session}function spawnBash(cwd){let shell=process.env.SHELL??"/bin/bash";return spawnPty(shell,{cwd:cwd??process.cwd(),cols:120,rows:40,env:process.env,agentId:null,executorId:null,taskId:null})}function writeTerminal(sessionId,data){let session=sessions.get(sessionId);if(!session||session.state!=="running")return!1;return session.pty.write(data),!0}function resizeTerminal(sessionId,cols,rows){let session=sessions.get(sessionId);if(!session||session.state!=="running")return!1;return session.pty.resize(cols,rows),session.cols=cols,session.rows=rows,!0}async function killTerminal(sessionId){let session=sessions.get(sessionId);if(!session)return!1;if(session.pty.kill(),session.state="exited",session.executorId)try{let{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));await updateExecutorState2(session.executorId,"terminated")}catch{}return sessions.delete(sessionId),!0}async function killAll(){let ids=[...sessions.keys()];await Promise.allSettled(ids.map((id)=>killTerminal(id)))}async function pipeStdout(stdout,sessionId,ptyHandle){let reader=stdout.getReader(),decoder=new TextDecoder;try{while(!0){let{done,value}=await reader.read();if(done)break;let text=decoder.decode(value);if(dataCallback)dataCallback(sessionId,text);if(ptyHandle.onData)ptyHandle.onData(text)}}catch{}}function onProcExit(sessionId,code,ptyHandle){let session=sessions.get(sessionId);if(session)session.state="exited";if(exitCallback)exitCallback(sessionId,code);if(ptyHandle.onExit)ptyHandle.onExit(code);handlePtyExit(sessionId,code)}function spawnPty(command,opts){let sessionId=randomUUID10(),ptyHandle;try{let bunPty=(()=>{throw new Error("Cannot require module "+"bun-pty");})(),parts=command.split(" "),raw=bunPty.spawn(parts,{cwd:opts.cwd,env:opts.env,cols:opts.cols,rows:opts.rows});ptyHandle={write:(data)=>raw.write(data),resize:(cols,rows)=>raw.resize(cols,rows),kill:()=>raw.kill(),onData:null,onExit:null},raw.onData=(data)=>{if(dataCallback)dataCallback(sessionId,data);if(ptyHandle.onData)ptyHandle.onData(data)},raw.onExit=(code)=>{let session2=sessions.get(sessionId);if(session2)session2.state="exited";if(exitCallback)exitCallback(sessionId,code);if(ptyHandle.onExit)ptyHandle.onExit(code);handlePtyExit(sessionId,code)}}catch{let parts=command.split(" "),proc=Bun.spawn(parts,{cwd:opts.cwd,env:opts.env,stdin:"pipe",stdout:"pipe",stderr:"pipe"});ptyHandle={write:(data)=>proc.stdin.write(data),resize:()=>{},kill:()=>proc.kill(),onData:null,onExit:null},pipeStdout(proc.stdout,sessionId,ptyHandle),proc.exited.then((code)=>onProcExit(sessionId,code,ptyHandle))}let session={id:sessionId,pty:ptyHandle,agentId:opts.agentId,executorId:opts.executorId,taskId:opts.taskId,command,state:"running",cols:opts.cols,rows:opts.rows,createdAt:new Date().toISOString()};return sessions.set(sessionId,session),session}function handlePtyExit(sessionId,code){let session=sessions.get(sessionId);if(!session?.executorId)return;(async()=>{try{let{updateExecutorState:updateExecutorState2}=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry));await updateExecutorState2(session.executorId,code===0?"done":"error")}catch{}})()}var sessions,dataCallback=null,exitCallback=null;var init_pty=__esm(()=>{sessions=new Map});var exports_src_backend={};import{existsSync as existsSync50,readFileSync as readFileSync31,readdirSync as readdirSync11,writeFileSync as writeFileSync20}from"fs";import{homedir as homedir41}from"os";import{join as join62,resolve as resolve11}from"path";function findSkillsDir(){let genieHome4=process.env.GENIE_HOME??join62(homedir41(),".genie"),candidateDirs=[join62(process.cwd(),"skills"),join62(genieHome4,"..","skills")];for(let d of candidateDirs)if(existsSync50(d))return d;return null}function parseSkillMd(content,fallbackName){let descMatch=content.match(/^description:\s*["']?(.+?)["']?\s*$/m),description="";if(descMatch)description=descMatch[1].trim();else description=content.split(`
2544
+ `).filter((l)=>l.trim()&&!l.startsWith("---")&&!l.startsWith("#"))[0]?.trim()??"";let nameMatch=content.match(/^name:\s*(.+?)\s*$/m);return{name:nameMatch?nameMatch[1].trim():fallbackName,description}}function scanSkillsDirectory(skillsDir){try{let entries=readdirSync11(skillsDir,{withFileTypes:!0}),skills=[];for(let entry2 of entries){if(!entry2.isDirectory())continue;let skillMdPath=join62(skillsDir,entry2.name,"SKILL.md");if(!existsSync50(skillMdPath))continue;let content=readFileSync31(skillMdPath,"utf-8"),{name,description}=parseSkillMd(content,entry2.name);skills.push({name,slug:entry2.name,description,path:skillMdPath})}return skills.sort((a,b2)=>a.name.localeCompare(b2.name))}catch{return[]}}function scanRuleDirs(){let genieHome4=process.env.GENIE_HOME??join62(homedir41(),".genie"),ruleDirs=[join62(genieHome4,"rules"),join62(homedir41(),".claude","rules")],allRules=[];for(let rulesDir of ruleDirs){if(!existsSync50(rulesDir))continue;try{let files=readdirSync11(rulesDir).filter((f)=>f.endsWith(".md"));for(let f of files){let filePath=join62(rulesDir,f);allRules.push({name:f.replace(".md",""),path:filePath,content:readFileSync31(filePath,"utf-8"),source:rulesDir.includes(".claude")?"claude":"genie"})}}catch{}}return allRules}async function start2(){console.log("[genie-app] Starting backend service..."),console.log(`[genie-app] NATS: ${NATS_URL}`),console.log(`[genie-app] Org: ${ORG_ID}`);let sql=await getConnection();console.log("[genie-app] PG connected"),nc=await import_nats6.connect({servers:NATS_URL,name:"genie-app-backend",reconnect:!0,maxReconnectAttempts:-1,reconnectTimeWait:2000}),console.log("[genie-app] NATS connected"),registerHandlers(sql),await startListening(nc,ORG_ID),console.log("[genie-app] PG LISTEN/NOTIFY active (11 channels)"),onPtyData((sessionId,data)=>{if(!nc)return;nc.publish(GENIE_SUBJECTS.pty.data(ORG_ID,sessionId),sc2.encode(JSON.stringify({data})))}),onPtyExit((sessionId,code)=>{if(!nc)return;nc.publish(GENIE_SUBJECTS.pty.data(ORG_ID,sessionId),sc2.encode(JSON.stringify({code,type:"exit"})))}),console.log("[genie-app] Backend ready")}function registerHandlers(sql){if(!nc)return;let sub=GENIE_SUBJECTS;reply(sub.dashboard.stats(ORG_ID),async()=>{let[agentRows,taskRows,teamRows,costRows,snapshot]=await Promise.all([sql`
2545
2545
  SELECT
2546
2546
  COUNT(*) FILTER (WHERE a.state IN ('working', 'idle', 'permission', 'question')) AS online,
2547
2547
  COUNT(*) FILTER (WHERE a.state = 'error') AS errored,
@@ -2770,7 +2770,7 @@ Genie Serve`),console.log("\u2500".repeat(50)),console.log(` Status: ${runn
2770
2770
  cwd = EXCLUDED.cwd,
2771
2771
  extra_args = EXCLUDED.extra_args,
2772
2772
  native_team_enabled = EXCLUDED.native_team_enabled
2773
- `,{ok:!0}}),reply(sub.settings.testPg(ORG_ID),async()=>{try{return await sql`SELECT 1 AS ok`,{ok:!0,message:"Connection successful"}}catch(err){return{ok:!1,message:err instanceof Error?err.message:String(err)}}}),reply(sub.pty.create(ORG_ID),async(params)=>{if(params.agentName){let session2=await spawnForAgent(params.agentName,{cwd:params.cwd,cols:params.cols,rows:params.rows});return{sessionId:session2.id,agentId:session2.agentId,executorId:session2.executorId}}return{sessionId:spawnBash(params.cwd).id,agentId:null,executorId:null}}),subscribePtyWildcard(`khal.${ORG_ID}.genie.pty.*.input`,async(sessionId,params)=>{writeTerminal(sessionId,params.data)}),subscribePtyWildcard(`khal.${ORG_ID}.genie.pty.*.resize`,async(sessionId,params)=>{resizeTerminal(sessionId,params.cols,params.rows)}),subscribePtyWildcard(`khal.${ORG_ID}.genie.pty.*.kill`,async(sessionId)=>{await killTerminal(sessionId)}),reply(sub.fs.list(ORG_ID),async(params)=>{let targetPath=resolve11(params.path);if(!existsSync50(targetPath))return{error:"not_found"};try{return readdirSync11(targetPath,{withFileTypes:!0}).map((e)=>({name:e.name,isDirectory:e.isDirectory(),path:join62(targetPath,e.name)}))}catch(err){return{error:err instanceof Error?err.message:String(err)}}}),reply(sub.fs.read(ORG_ID),async(params)=>{let targetPath=resolve11(params.path);if(!existsSync50(targetPath))return{error:"not_found"};try{return{content:readFileSync31(targetPath,"utf-8")}}catch(err){return{error:err instanceof Error?err.message:String(err)}}}),reply(sub.fs.write(ORG_ID),async(params)=>{let targetPath=resolve11(params.path);try{return writeFileSync20(targetPath,params.content),{ok:!0}}catch(err){return{error:err instanceof Error?err.message:String(err)}}}),reply(sub.approval.resolve(ORG_ID),async(params)=>{return{ok:await resolveApproval(params.id,params.decision,params.decided_by)}}),reply(sub.approval.list(ORG_ID),async(params)=>{return listPendingApprovals(params.agent_name)}),console.log("[genie-app] All request/reply handlers registered")}function decodeParams(data){if(data.length===0)return{};try{return JSON.parse(sc2.decode(data))}catch{return{}}}function subscribePtyWildcard(subject,handler){if(!nc)return;let subscription=nc.subscribe(subject);(async()=>{for await(let msg of subscription)try{let params=decodeParams(msg.data),sessionId=msg.subject.split(".")[4];if(sessionId)await handler(sessionId,params)}catch(err){console.error(`[genie-app] PTY wildcard error on ${msg.subject}:`,err instanceof Error?err.message:String(err))}})()}function reply(subject,handler){if(!nc)return;let subscription=nc.subscribe(subject);processSubscription(subscription,subject,handler)}async function processSubscription(subscription,subject,handler){for await(let msg of subscription)try{let params=decodeParams(msg.data),result2=await handler(params);if(msg.reply)msg.respond(sc2.encode(JSON.stringify(result2)))}catch(err){let errorMsg=err instanceof Error?err.message:String(err);if(console.error(`[genie-app] Handler error on ${subject}:`,errorMsg),msg.reply)msg.respond(sc2.encode(JSON.stringify({error:errorMsg})))}}async function shutdown2(){if(shutdownRequested)return;if(shutdownRequested=!0,console.log("[genie-app] Shutting down..."),await killAll(),await stopListening(),nc){try{await nc.drain()}catch{}nc=null}console.log("[genie-app] Shutdown complete"),process.exit(0)}var import_nats5,NATS_URL,ORG_ID,_PG_URL,sc2,nc=null,shutdownRequested=!1;var init_src_backend=__esm(()=>{init_db();init_claude_sdk_remote_approval();init_subjects();init_pg_bridge();init_pty();import_nats5=__toESM(require_mod4(),1);process.env.GENIE_APP="1";NATS_URL=process.env.GENIE_NATS_URL??"nats://localhost:4222",ORG_ID=process.env.GENIE_ORG_ID??"default",_PG_URL=process.env.GENIE_PG_URL??"postgresql://localhost:19642/genie",sc2=import_nats5.StringCodec();process.on("SIGTERM",()=>void shutdown2());process.on("SIGINT",()=>void shutdown2());start2().catch((err)=>{console.error("[genie-app] Fatal error:",err),process.exit(1)})});var exports_event_renderer={};__export(exports_event_renderer,{renderRuntimeEvent:()=>renderRuntimeEvent,renderAuditEvent:()=>renderAuditEvent,formatEventLine:()=>formatEventLine});function renderAuditEvent(input){let details=input.details;if(typeof details==="string")try{details=JSON.parse(details)}catch{details={}}let normalized={...input,details:details??{}},renderer=auditRenderers[input.event_type];if(renderer)return renderer(normalized);let event=normalized,json2=JSON.stringify(event.details);return{indicator:color("dim","\u25CB"),content:`${color("gray",event.event_type)} ${json2==="{}"?"":color("dim",json2)}`,context:`${event.entity_type}:${event.entity_id}`}}function renderRuntimeEvent(input){let renderer=runtimeRenderers[input.kind];if(renderer)return renderer(input);return{indicator:color("dim","\u25CB"),content:`${color("gray",input.kind)} ${input.text}`,context:input.agent}}function formatEventLine(timeStr,rendered){let termWidth=process.stdout.columns||120,timeCol=color("gray",timeStr),timeWidth=stripAnsi2(timeCol).length,iconWidth=stripAnsi2(rendered.indicator).length,prefixWidth=timeWidth+2+iconWidth+2,contextStr=rendered.context?color("dim",rendered.context):"",contextWidth=contextStr?stripAnsi2(contextStr).length+2:0,contentWidth=Math.max(20,termWidth-prefixWidth-contextWidth),content=stripAnsi2(rendered.content).replace(/[\r\n]+/g," ");if(content.length<=contentWidth){let paddedContent=padToWidth(rendered.content,contentWidth);return`${timeCol} ${rendered.indicator} ${paddedContent}${contextStr?` ${contextStr}`:""}`}let chunks=wrapText(content,contentWidth),indent2=" ".repeat(prefixWidth),firstLine=`${timeCol} ${rendered.indicator} ${padToWidth(chunks[0],contentWidth)}${contextStr?` ${contextStr}`:""}`,rest=chunks.slice(1).map((c)=>`${indent2}${c}`);return[firstLine,...rest].join(`
2773
+ `,{ok:!0}}),reply(sub.settings.testPg(ORG_ID),async()=>{try{return await sql`SELECT 1 AS ok`,{ok:!0,message:"Connection successful"}}catch(err){return{ok:!1,message:err instanceof Error?err.message:String(err)}}}),reply(sub.pty.create(ORG_ID),async(params)=>{if(params.agentName){let session2=await spawnForAgent(params.agentName,{cwd:params.cwd,cols:params.cols,rows:params.rows});return{sessionId:session2.id,agentId:session2.agentId,executorId:session2.executorId}}return{sessionId:spawnBash(params.cwd).id,agentId:null,executorId:null}}),subscribePtyWildcard(`khal.${ORG_ID}.genie.pty.*.input`,async(sessionId,params)=>{writeTerminal(sessionId,params.data)}),subscribePtyWildcard(`khal.${ORG_ID}.genie.pty.*.resize`,async(sessionId,params)=>{resizeTerminal(sessionId,params.cols,params.rows)}),subscribePtyWildcard(`khal.${ORG_ID}.genie.pty.*.kill`,async(sessionId)=>{await killTerminal(sessionId)}),reply(sub.fs.list(ORG_ID),async(params)=>{let targetPath=resolve11(params.path);if(!existsSync50(targetPath))return{error:"not_found"};try{return readdirSync11(targetPath,{withFileTypes:!0}).map((e)=>({name:e.name,isDirectory:e.isDirectory(),path:join62(targetPath,e.name)}))}catch(err){return{error:err instanceof Error?err.message:String(err)}}}),reply(sub.fs.read(ORG_ID),async(params)=>{let targetPath=resolve11(params.path);if(!existsSync50(targetPath))return{error:"not_found"};try{return{content:readFileSync31(targetPath,"utf-8")}}catch(err){return{error:err instanceof Error?err.message:String(err)}}}),reply(sub.fs.write(ORG_ID),async(params)=>{let targetPath=resolve11(params.path);try{return writeFileSync20(targetPath,params.content),{ok:!0}}catch(err){return{error:err instanceof Error?err.message:String(err)}}}),reply(sub.approval.resolve(ORG_ID),async(params)=>{return{ok:await resolveApproval(params.id,params.decision,params.decided_by)}}),reply(sub.approval.list(ORG_ID),async(params)=>{return listPendingApprovals(params.agent_name)}),console.log("[genie-app] All request/reply handlers registered")}function decodeParams(data){if(data.length===0)return{};try{return JSON.parse(sc2.decode(data))}catch{return{}}}function subscribePtyWildcard(subject,handler){if(!nc)return;let subscription=nc.subscribe(subject);(async()=>{for await(let msg of subscription)try{let params=decodeParams(msg.data),sessionId=msg.subject.split(".")[4];if(sessionId)await handler(sessionId,params)}catch(err){console.error(`[genie-app] PTY wildcard error on ${msg.subject}:`,err instanceof Error?err.message:String(err))}})()}function reply(subject,handler){if(!nc)return;let subscription=nc.subscribe(subject);processSubscription(subscription,subject,handler)}async function processSubscription(subscription,subject,handler){for await(let msg of subscription)try{let params=decodeParams(msg.data),result2=await handler(params);if(msg.reply)msg.respond(sc2.encode(JSON.stringify(result2)))}catch(err){let errorMsg=err instanceof Error?err.message:String(err);if(console.error(`[genie-app] Handler error on ${subject}:`,errorMsg),msg.reply)msg.respond(sc2.encode(JSON.stringify({error:errorMsg})))}}async function shutdown2(){if(shutdownRequested)return;if(shutdownRequested=!0,console.log("[genie-app] Shutting down..."),await killAll(),await stopListening(),nc){try{await nc.drain()}catch{}nc=null}console.log("[genie-app] Shutdown complete"),process.exit(0)}var import_nats6,NATS_URL,ORG_ID,_PG_URL,sc2,nc=null,shutdownRequested=!1;var init_src_backend=__esm(()=>{init_db();init_claude_sdk_remote_approval();init_subjects();init_pg_bridge();init_pty();import_nats6=__toESM(require_mod4(),1);process.env.GENIE_APP="1";NATS_URL=process.env.GENIE_NATS_URL??"nats://localhost:4222",ORG_ID=process.env.GENIE_ORG_ID??"default",_PG_URL=process.env.GENIE_PG_URL??"postgresql://localhost:19642/genie",sc2=import_nats6.StringCodec();process.on("SIGTERM",()=>void shutdown2());process.on("SIGINT",()=>void shutdown2());start2().catch((err)=>{console.error("[genie-app] Fatal error:",err),process.exit(1)})});var exports_event_renderer={};__export(exports_event_renderer,{renderRuntimeEvent:()=>renderRuntimeEvent,renderAuditEvent:()=>renderAuditEvent,formatEventLine:()=>formatEventLine});function renderAuditEvent(input){let details=input.details;if(typeof details==="string")try{details=JSON.parse(details)}catch{details={}}let normalized={...input,details:details??{}},renderer=auditRenderers[input.event_type];if(renderer)return renderer(normalized);let event=normalized,json2=JSON.stringify(event.details);return{indicator:color("dim","\u25CB"),content:`${color("gray",event.event_type)} ${json2==="{}"?"":color("dim",json2)}`,context:`${event.entity_type}:${event.entity_id}`}}function renderRuntimeEvent(input){let renderer=runtimeRenderers[input.kind];if(renderer)return renderer(input);return{indicator:color("dim","\u25CB"),content:`${color("gray",input.kind)} ${input.text}`,context:input.agent}}function formatEventLine(timeStr,rendered){let termWidth=process.stdout.columns||120,timeCol=color("gray",timeStr),timeWidth=stripAnsi2(timeCol).length,iconWidth=stripAnsi2(rendered.indicator).length,prefixWidth=timeWidth+2+iconWidth+2,contextStr=rendered.context?color("dim",rendered.context):"",contextWidth=contextStr?stripAnsi2(contextStr).length+2:0,contentWidth=Math.max(20,termWidth-prefixWidth-contextWidth),content=stripAnsi2(rendered.content).replace(/[\r\n]+/g," ");if(content.length<=contentWidth){let paddedContent=padToWidth(rendered.content,contentWidth);return`${timeCol} ${rendered.indicator} ${paddedContent}${contextStr?` ${contextStr}`:""}`}let chunks=wrapText(content,contentWidth),indent2=" ".repeat(prefixWidth),firstLine=`${timeCol} ${rendered.indicator} ${padToWidth(chunks[0],contentWidth)}${contextStr?` ${contextStr}`:""}`,rest=chunks.slice(1).map((c)=>`${indent2}${c}`);return[firstLine,...rest].join(`
2774
2774
  `)}function padToWidth(coloredText,width){let visible=stripAnsi2(coloredText).length,pad=Math.max(0,width-visible);return coloredText+" ".repeat(pad)}function wrapText(text,width){let chunks=[],remaining=text;while(remaining.length>width){let breakAt=remaining.lastIndexOf(" ",width);if(breakAt<=0||breakAt<width/2)breakAt=width;chunks.push(remaining.slice(0,breakAt).trimEnd()),remaining=remaining.slice(breakAt).trimStart()}if(remaining.length>0)chunks.push(remaining);return chunks}function formatDuration3(ms){if(ms<1000)return`${ms}ms`;if(ms<60000)return`${(ms/1000).toFixed(1)}s`;let s2=Math.floor(ms/1000);return`${Math.floor(s2/60)}m${s2%60}s`}var shortEntity=(id)=>id.split(":")[0]??id,auditRenderers,runtimeRenderers;var init_event_renderer=__esm(()=>{init_term_format();auditRenderers={"sdk.assistant.message":(e)=>({indicator:color("brightCyan","\uD83D\uDCAC"),content:color("brightCyan",`"${e.details.textPreview??""}"`),context:shortEntity(e.entity_id)}),"sdk.user.message":(e)=>{let d=e.details,text=d.textPreview;if(typeof text==="string"&&text.length>0){let tag=d.isReplay?color("dim"," (replay)"):d.isSynthetic?color("dim"," (synthetic)"):"";return{indicator:color("cyan","\uD83D\uDC64"),content:`${color("cyan",`"${text}"`)}${tag}`,context:shortEntity(e.entity_id)}}let kind=d.isReplay?"replay":d.isSynthetic?"synthetic":"turn";return{indicator:color("cyan","\uD83D\uDC64"),content:color("dim",`user ${kind}`),context:shortEntity(e.entity_id)}},"sdk.hook.started":(e)=>({indicator:color("yellow","\uD83E\uDE9D"),content:`${e.details.hookName??"?"} ${color("dim","started")}`,context:shortEntity(e.entity_id)}),"sdk.hook.response":(e)=>{let outcome=e.details.outcome==="success"?color("green","\u2713"):color("red","\u2717"),hookId=e.details.hookId?color("dim",` ${String(e.details.hookId).slice(0,8)}`):"";return{indicator:color("yellow","\uD83E\uDE9D"),content:`${e.details.hookName??"?"} ${outcome}${hookId}`,context:shortEntity(e.entity_id)}},"sdk.system":(e)=>{let model=String(e.details.model??"?").replace(/^claude-/,""),tools=e.details.tools??0,session=e.details.sessionId?` \xB7 ${color("dim",String(e.details.sessionId).slice(0,8))}`:"";return{indicator:color("gray","\u2699"),content:`init ${color("cyan",model)} \xB7 ${tools} tools${session}`,context:shortEntity(e.entity_id)}},"sdk.result.success":(e)=>{let d=e.details,dur=d.durationMs?formatDuration3(d.durationMs):"",cost=typeof d.totalCostUsd==="number"?`$${d.totalCostUsd.toFixed(4)}`:"",preview=d.resultPreview?` \xB7 "${d.resultPreview}"`:"";return{indicator:color("green","\u2728"),content:`${color("brightGreen",dur)} \xB7 ${color("yellow",cost)}${color("dim",preview)}`,context:shortEntity(e.entity_id)}},"sdk.rate_limit":(e)=>({indicator:color("gray","\u23F1"),content:color("dim",`rate limit: ${e.details.status??"?"}`),context:shortEntity(e.entity_id)}),"sdk.api.retry":(e)=>{let d=e.details,attempt=d.attempt??"?",max=d.maxRetries??"?",delay=d.retryDelayMs?`${Math.round(d.retryDelayMs)}ms`:"";return{indicator:color("yellow","\u21BB"),content:color("yellow",`retry ${attempt}/${max} \xB7 ${d.error??"?"}${delay?` \xB7 ${delay}`:""}`),context:shortEntity(e.entity_id)}},"session.created_fresh":(e)=>({indicator:color("green","\u2726"),content:color("green",`session created \xB7 ${e.details.agent_id??"?"}`),context:shortEntity(e.entity_id)}),"session.resumed":(e)=>({indicator:color("cyan","\u21BB"),content:color("cyan",`session resumed \xB7 ${e.details.agent_id??"?"}`),context:shortEntity(e.entity_id)}),"deliver.start":(e)=>({indicator:color("blue","\u25B8"),content:color("dim",`deliver \u2192 ${e.details.agent_id??"?"}`),context:shortEntity(e.entity_id)}),"deliver.end":(e)=>{let turns=e.details.turn_count?` \xB7 turn ${e.details.turn_count}`:"";return{indicator:color("blue","\u25C2"),content:color("dim",`deliver done${turns}`),context:shortEntity(e.entity_id)}},state_changed:(e)=>({indicator:color("gray","\u25C9"),content:color("dim",`state \u2192 ${e.details.state??"?"}`),context:shortEntity(e.entity_id)}),"executor.spawn":(e)=>({indicator:color("green","\u25B6"),content:color("green",`spawn ${e.details.source??""}`),context:e.entity_id}),"executor.terminate":(e)=>({indicator:color("yellow","\u25A0"),content:color("yellow",`terminate ${e.details.source??""}`),context:e.entity_id}),"executor.deliver":(e)=>{let d=e.details,dur=d.durationMs?formatDuration3(d.durationMs):"",tokens2=d.tokens,tokStr=tokens2?`${tokens2.input??0}\u2192${tokens2.output??0}`:"",parts=[dur,tokStr].filter(Boolean);return{indicator:color("blue","\u2192"),content:parts.join(" \xB7 "),context:e.entity_id}},"task.error":(e)=>({indicator:color("red","\u2717"),content:color("red",String(e.details.error??"unknown error")),context:e.entity_id}),command_success:(e)=>({indicator:color("dim","\xB7"),content:color("dim",`${e.details.duration_ms??0}ms`),context:e.entity_id})};runtimeRenderers={user:(e)=>({indicator:color("brightCyan","\uD83D\uDC64"),content:e.text,context:e.agent}),assistant:(e)=>({indicator:color("cyan","\uD83E\uDD16"),content:color("cyan",e.text),context:e.agent}),message:(e)=>({indicator:color("magenta","\u2709"),content:e.text,context:e.agent}),tool_call:(e)=>({indicator:color("yellow","\uD83D\uDD27"),content:color("yellow",e.text),context:e.agent}),tool_result:(e)=>({indicator:color("gray","\u2B90"),content:color("dim",e.text),context:e.agent}),state:(e)=>({indicator:color("gray","\u25C9"),content:color("dim",e.text),context:e.agent}),system:(e)=>({indicator:color("gray","\u2699"),content:color("dim",e.text),context:e.agent}),qa:(e)=>({indicator:color("magenta","\uD83E\uDDEA"),content:e.text,context:e.agent})}});var exports_board_service={};__export(exports_board_service,{updateColumn:()=>updateColumn,updateBoard:()=>updateBoard,reorderColumns:()=>reorderColumns,removeColumn:()=>removeColumn,reconcileBoard:()=>reconcileBoard,listBoards:()=>listBoards,importBoard:()=>importBoard,getColumns:()=>getColumns,getBoard:()=>getBoard,exportBoard:()=>exportBoard,deleteBoard:()=>deleteBoard,createBoard:()=>createBoard,addColumn:()=>addColumn});function str3(v){return v!=null?String(v):null}function strOrDefault2(v,def){return v!=null?String(v):def}function parseJsonb(val,fallback){if(val==null)return fallback;if(typeof val==="string")try{return JSON.parse(val)}catch{return fallback}return val}function mapBoard(row){return{id:row.id,name:row.name,projectId:str3(row.project_id),description:str3(row.description),status:strOrDefault2(row.status,"active"),archivedAt:str3(row.archived_at),columns:parseJsonb(row.columns,[]),config:parseJsonb(row.config,{}),createdAt:strOrDefault2(row.created_at,""),updatedAt:strOrDefault2(row.updated_at,"")}}function generateColumnId(){return crypto.randomUUID()}function fillColumnDefaults(col,position){return{id:col.id??generateColumnId(),name:col.name??`column-${position}`,label:col.label??col.name??`Column ${position}`,gate:col.gate??"human",action:col.action??null,auto_advance:col.auto_advance??!1,transitions:col.transitions??[],roles:col.roles??["*"],color:col.color??palette.textDim,parallel:col.parallel??!1,on_fail:col.on_fail??null,position}}async function createBoard(input){let sql=await getConnection(),columns=[];if(input.fromTemplate){let tmplRows=await sql`
2775
2775
  SELECT columns FROM board_templates WHERE name = ${input.fromTemplate} OR id = ${input.fromTemplate} LIMIT 1
2776
2776
  `;if(tmplRows.length===0)throw Error(`Template not found: ${input.fromTemplate}`);columns=tmplRows[0].columns.map((col,i2)=>({...col,id:generateColumnId(),position:i2}))}else if(input.columns)columns=input.columns.map((col,i2)=>fillColumnDefaults(col,i2));let rows=await sql`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260501.3",
3
+ "version": "4.260501.4",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows. NOTE: the npm distribution is being soft-deprecated — the canonical install is `curl -fsSL https://get.automagik.dev/genie | bash` (cosign + SLSA verified). See https://automagik.dev/genie/security/distribution-sovereignty",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260501.3",
3
+ "version": "4.260501.4",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260501.3",
3
+ "version": "4.260501.4",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",