@automagik/genie 4.260421.28 → 4.260421.30

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
@@ -2107,7 +2107,37 @@ ${body}`;writeFileSync12(filePath,output,"utf-8")}function serializeSdkConfig(sd
2107
2107
  AND kind IN ('user', 'assistant', 'message')
2108
2108
  ORDER BY id ASC
2109
2109
  LIMIT 1
2110
- `)[0]?.text?.slice(0,TRANSCRIPT_PREVIEW_CAP)??""}async function matchFreshAgainstPeers(sql,fresh,peers,freshSeed,freshTokens){for(let peer of peers){let peerPreview=await loadArchivedPreview(sql,peer.team),peerTokens=topicTokens(peerPreview),similarity=jaccard(freshTokens,peerTokens);if(similarity>=TOPIC_MISMATCH_THRESHOLD)continue;return{new_agent_id:fresh.id,new_team:fresh.team,new_topic_seed:freshSeed.slice(0,256),conflicting_archived_agent_id:peer.agent_id,conflicting_archived_team:peer.team,conflicting_archived_last_transcript_preview:peerPreview.slice(0,256),jaccard_similarity:similarity}}return null}async function defaultLoadState4(){let sql=await getConnection(),cutoff=new Date(Date.now()-SPAWN_LOOKBACK_MS).toISOString(),recent=(await listAgents()).map((a)=>toFreshCandidate(a,cutoff)).filter((c)=>c!==null);if(recent.length===0)return{ghosts:[]};let ghosts=[];for(let fresh of recent){let peers=await findArchivedPeers(sql,fresh);if(peers.length===0)continue;let freshSeed=await loadFreshSeed(sql,fresh);if(freshSeed.length===0)continue;let freshTokens=topicTokens(freshSeed),match=await matchFreshAgainstPeers(sql,fresh,peers,freshSeed,freshTokens);if(match!==null)ghosts.push(match)}return{ghosts}}function makeSessionReuseGhostDetector(deps){return{id:"rot.session-reuse-ghost",version:"1.0.0",riskClass:"high",async query(){return deps.loadState()},shouldFire(state){return state.ghosts.length>0},render(state){let first=state.ghosts[0];return{type:"rot.detected",subject:first?.new_agent_id??"unknown",payload:{pattern_id:"pattern-8-session-reuse-ghost",entity_id:first?.new_agent_id??"unknown",observed_state_json:{new_agent_id:first?.new_agent_id??"unknown",new_team:first?.new_team??"unknown",new_topic_seed:first?.new_topic_seed??"",conflicting_archived_agent_id:first?.conflicting_archived_agent_id??"unknown",conflicting_archived_team:first?.conflicting_archived_team??"unknown",conflicting_archived_last_transcript_preview:first?.conflicting_archived_last_transcript_preview??"",jaccard_similarity:first?.jaccard_similarity??0,ghost_count:state.ghosts.length}}}}}}var SPAWN_LOOKBACK_MS=600000,TOPIC_SEED_TOKEN_CAP=8,TOPIC_MISMATCH_THRESHOLD=0.25,TRANSCRIPT_PREVIEW_CAP=2048,sessionReuseGhostDetector;var init_pattern_8_session_reuse_ghost=__esm(()=>{init_agent_registry();init_db();init_detectors();sessionReuseGhostDetector=makeSessionReuseGhostDetector({loadState:defaultLoadState4});registerDetector(sessionReuseGhostDetector)});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()});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});function start(options={}){let tickIntervalMs=options.tickIntervalMs??DEFAULT_TICK_INTERVAL_MS,jitterMs=options.jitterMs??DEFAULT_JITTER_MS,defaultBudget=options.defaultFireBudget??DEFAULT_FIRE_BUDGET,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=10,HOUR_MS=3600000;var init_detector_scheduler=__esm(()=>{init_detectors();init_emit();init_pattern_2_team_ls_drift()});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 getActivePort()+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`
2110
+ `)[0]?.text?.slice(0,TRANSCRIPT_PREVIEW_CAP)??""}async function matchFreshAgainstPeers(sql,fresh,peers,freshSeed,freshTokens){for(let peer of peers){let peerPreview=await loadArchivedPreview(sql,peer.team),peerTokens=topicTokens(peerPreview),similarity=jaccard(freshTokens,peerTokens);if(similarity>=TOPIC_MISMATCH_THRESHOLD)continue;return{new_agent_id:fresh.id,new_team:fresh.team,new_topic_seed:freshSeed.slice(0,256),conflicting_archived_agent_id:peer.agent_id,conflicting_archived_team:peer.team,conflicting_archived_last_transcript_preview:peerPreview.slice(0,256),jaccard_similarity:similarity}}return null}async function defaultLoadState4(){let sql=await getConnection(),cutoff=new Date(Date.now()-SPAWN_LOOKBACK_MS).toISOString(),recent=(await listAgents()).map((a)=>toFreshCandidate(a,cutoff)).filter((c)=>c!==null);if(recent.length===0)return{ghosts:[]};let ghosts=[];for(let fresh of recent){let peers=await findArchivedPeers(sql,fresh);if(peers.length===0)continue;let freshSeed=await loadFreshSeed(sql,fresh);if(freshSeed.length===0)continue;let freshTokens=topicTokens(freshSeed),match=await matchFreshAgainstPeers(sql,fresh,peers,freshSeed,freshTokens);if(match!==null)ghosts.push(match)}return{ghosts}}function makeSessionReuseGhostDetector(deps){return{id:"rot.session-reuse-ghost",version:"1.0.0",riskClass:"high",async query(){return deps.loadState()},shouldFire(state){return state.ghosts.length>0},render(state){let first=state.ghosts[0];return{type:"rot.detected",subject:first?.new_agent_id??"unknown",payload:{pattern_id:"pattern-8-session-reuse-ghost",entity_id:first?.new_agent_id??"unknown",observed_state_json:{new_agent_id:first?.new_agent_id??"unknown",new_team:first?.new_team??"unknown",new_topic_seed:first?.new_topic_seed??"",conflicting_archived_agent_id:first?.conflicting_archived_agent_id??"unknown",conflicting_archived_team:first?.conflicting_archived_team??"unknown",conflicting_archived_last_transcript_preview:first?.conflicting_archived_last_transcript_preview??"",jaccard_similarity:first?.jaccard_similarity??0,ghost_count:state.ghosts.length}}}}}}var SPAWN_LOOKBACK_MS=600000,TOPIC_SEED_TOKEN_CAP=8,TOPIC_MISMATCH_THRESHOLD=0.25,TRANSCRIPT_PREVIEW_CAP=2048,sessionReuseGhostDetector;var init_pattern_8_session_reuse_ghost=__esm(()=>{init_agent_registry();init_db();init_detectors();sessionReuseGhostDetector=makeSessionReuseGhostDetector({loadState:defaultLoadState4});registerDetector(sessionReuseGhostDetector)});import{execFile as execFile2}from"child_process";import{statSync as statSync5}from"fs";function defaultWorktreeExists(path3){try{return statSync5(path3).isDirectory()}catch{return!1}}function runGit(cwd,args,timeoutMs){return new Promise((resolve5,reject)=>{execFile2("git",args,{cwd,timeout:timeoutMs,windowsHide:!0},(err,stdout)=>{if(err)reject(err);else resolve5(String(stdout).trim())}).stderr?.resume()})}async function probeWorktree(worktreePath,baseBranch,timeoutMs){let aheadStr=await runGit(worktreePath,["rev-list","--count",`origin/${baseBranch}..HEAD`],timeoutMs),aheadCount=Number.parseInt(aheadStr,10);if(!Number.isFinite(aheadCount))return{ok:!1,branch_ahead_count:0,last_commit_ms:null,error:"parse_error"};if(aheadCount<=0)return{ok:!0,branch_ahead_count:0,last_commit_ms:null};let tipIso=await runGit(worktreePath,["log","-1","--format=%cI","HEAD"],timeoutMs),tipMs=tipIso?Date.parse(tipIso):Number.NaN;return{ok:!0,branch_ahead_count:aheadCount,last_commit_ms:Number.isFinite(tipMs)?tipMs:null}}function makeDefaultGitProbe(gitTimeoutMs){return async(row)=>{if(!row.base_branch||!row.worktree_path)return{ok:!1,branch_ahead_count:0,last_commit_ms:null,error:"malformed_path"};if(!defaultWorktreeExists(row.worktree_path))return{ok:!1,branch_ahead_count:0,last_commit_ms:null,error:"missing_worktree"};try{return await probeWorktree(row.worktree_path,row.base_branch,gitTimeoutMs)}catch(err){return{ok:!1,branch_ahead_count:0,last_commit_ms:null,error:err?.code??"probe_error"}}}}function createTeamUnpushedOrphanedWorktreeDetector(opts){let idleMinutes=opts?.idleMinutes??DEFAULT_IDLE_MINUTES2,thresholdMs=idleMinutes*60000,maxTeamsPerTick=opts?.maxTeamsPerTick??DEFAULT_MAX_TEAMS_PER_TICK,gitTimeoutMs=opts?.gitTimeoutMs??DEFAULT_GIT_TIMEOUT_MS,version=opts?.version??"0.1.0",gitProbe=opts?.gitProbe??makeDefaultGitProbe(gitTimeoutMs),defaultQuery=async()=>{let sql=await getConnection();return(await sql`
2111
+ WITH live_activity AS (
2112
+ SELECT a.team AS team, MAX(e.updated_at) AS last_active
2113
+ FROM agents a
2114
+ JOIN executors e ON e.agent_id = a.id
2115
+ WHERE a.team IS NOT NULL
2116
+ AND e.state = ANY(${sql.array([...LIVE_EXECUTOR_STATES])})
2117
+ GROUP BY a.team
2118
+ ),
2119
+ team_leads AS (
2120
+ SELECT DISTINCT ON (team) team, id AS lead_agent_id, state AS lead_state
2121
+ FROM agents
2122
+ WHERE role = 'team-lead'
2123
+ AND team IS NOT NULL
2124
+ ORDER BY team, created_at DESC
2125
+ )
2126
+ SELECT t.name AS team_name,
2127
+ t.status AS status,
2128
+ t.worktree_path AS worktree_path,
2129
+ t.base_branch AS base_branch,
2130
+ tl.lead_agent_id AS lead_agent_id,
2131
+ tl.lead_state AS lead_state,
2132
+ EXTRACT(EPOCH FROM la.last_active) * 1000 AS last_executor_active_ms,
2133
+ EXTRACT(EPOCH FROM now()) * 1000 AS now_ms
2134
+ FROM teams t
2135
+ LEFT JOIN live_activity la ON la.team = t.name
2136
+ LEFT JOIN team_leads tl ON tl.team = t.name
2137
+ WHERE t.status <> ALL(${sql.array([...EXEMPT_TEAM_STATUSES])})
2138
+ ORDER BY t.updated_at DESC
2139
+ LIMIT 500
2140
+ `).map((r)=>({team_name:r.team_name,status:r.status,worktree_path:r.worktree_path,base_branch:r.base_branch,lead_agent_id:r.lead_agent_id,lead_state:r.lead_state,last_executor_active_ms:r.last_executor_active_ms===null?null:Number(r.last_executor_active_ms),now_ms:Number(r.now_ms)}))},queryFn=opts?.query??defaultQuery,isIdlePastThreshold=(row)=>{if(row.last_executor_active_ms===null)return!0;return row.now_ms-row.last_executor_active_ms>thresholdMs},probeOne2=async(row)=>{let probe=await gitProbe(row);if(!probe.ok||probe.branch_ahead_count<=0)return null;return{row,branchAheadCount:probe.branch_ahead_count,lastCommitMs:probe.last_commit_ms}};return{id:"rot.team-unpushed-orphaned-worktree",version,riskClass:"low",async query(){let idle=(await queryFn()).filter(isIdlePastThreshold),batch=idle.slice(0,maxTeamsPerTick),idleUnprobed=idle.length-batch.length,stalled=[];for(let row of batch){let result2=await probeOne2(row);if(result2!==null)stalled.push(result2)}return{stalled,idleUnprobed,idleMinutes}},shouldFire(state){return state.stalled.length>0},render(state){let first=state.stalled[0],row=first.row,minutesSinceActive=row.last_executor_active_ms===null?null:Math.floor((row.now_ms-row.last_executor_active_ms)/60000),lastExecutorActiveIso=row.last_executor_active_ms===null?null:new Date(row.last_executor_active_ms).toISOString(),lastCommitIso=first.lastCommitMs===null?null:new Date(first.lastCommitMs).toISOString(),totalStalledTeams=state.stalled.length+state.idleUnprobed;return{type:"rot.detected",subject:row.team_name,payload:{pattern_id:"pattern-9-team-unpushed-orphaned-worktree",entity_id:row.team_name,observed_state_json:{team_name:row.team_name,team_status:row.status,worktree_path:row.worktree_path,base_branch:row.base_branch??"",branch_ahead_count:first.branchAheadCount,last_commit_at:lastCommitIso,last_executor_active_at:lastExecutorActiveIso,minutes_since_active:minutesSinceActive,threshold_minutes:state.idleMinutes,lead_agent_id:row.lead_agent_id,lead_state:row.lead_state,total_stalled_teams:totalStalledTeams}}}}}}var LIVE_EXECUTOR_STATES,EXEMPT_TEAM_STATUSES,DEFAULT_IDLE_MINUTES2=10,DEFAULT_MAX_TEAMS_PER_TICK=32,DEFAULT_GIT_TIMEOUT_MS=3000;var init_pattern_9_team_unpushed_orphaned_worktree=__esm(()=>{init_db();init_detectors();LIVE_EXECUTOR_STATES=["running","spawning"],EXEMPT_TEAM_STATUSES=["done","blocked","archived"];registerDetector(createTeamUnpushedOrphanedWorktreeDetector())});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()});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});function start(options={}){let tickIntervalMs=options.tickIntervalMs??DEFAULT_TICK_INTERVAL_MS,jitterMs=options.jitterMs??DEFAULT_JITTER_MS,defaultBudget=options.defaultFireBudget??DEFAULT_FIRE_BUDGET,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=10,HOUR_MS=3600000;var init_detector_scheduler=__esm(()=>{init_detectors();init_emit();init_pattern_2_team_ls_drift()});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 getActivePort()+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`
2111
2141
  SELECT id FROM approvals
2112
2142
  WHERE omni_message_id = ${messageId} AND decision = 'pending'
2113
2143
  `;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`
@@ -3679,7 +3709,7 @@ ${yamlStr}---`)}function buildDirResolveContext(agentName){let ctx={};try{let ws
3679
3709
  No agents registered. Add one with: genie dir add <name> --dir <path>`),console.log(`Use --builtins to also see built-in roles and council members.
3680
3710
  `);return}if(entries.length>0)printRegisteredTable(entries),printResolvedTable(entries);if(includeBuiltins)printBuiltinsTable()}function listEntriesJson(entries,includeBuiltins){let result2=entries.map((e)=>({...e,builtin:!1}));if(includeBuiltins)for(let b2 of ALL_BUILTINS)result2.push({name:b2.name,description:b2.description,model:b2.model,category:b2.category,scope:"built-in",builtin:!0});let{writeSync}=__require("fs"),data=`${JSON.stringify(result2,null,2)}
3681
3711
  `,CHUNK=4096;for(let i2=0;i2<data.length;i2+=CHUNK)writeSync(1,data.slice(i2,i2+CHUNK))}function registerSdkFlags(cmd){cmd.option("--sdk-permission-mode <mode>","SDK permission mode: default|acceptEdits|bypassPermissions|plan|dontAsk|auto").option("--sdk-tools <list>","SDK tools: comma-separated tool names").option("--sdk-allowed-tools <list>","SDK auto-approved tools: comma-separated").option("--sdk-disallowed-tools <list>","SDK blacklisted tools: comma-separated").option("--sdk-max-turns <n>","SDK max conversation turns").option("--sdk-max-budget <usd>","SDK max budget in USD").option("--sdk-effort <level>","SDK effort: low|medium|high|max").option("--sdk-thinking <config>","SDK thinking: adaptive|disabled|enabled[:budgetTokens]").option("--sdk-persist-session","SDK: enable session persistence").option("--no-sdk-persist-session","SDK: disable session persistence").option("--sdk-file-checkpointing","SDK: enable file checkpointing").option("--sdk-output-format <path>","SDK: path to JSON schema file for output format").option("--sdk-stream-partial","SDK: include partial messages in stream").option("--sdk-hook-events","SDK: include hook events in stream").option("--sdk-prompt-suggestions","SDK: enable prompt suggestions").option("--sdk-progress-summaries","SDK: enable agent progress summaries").option("--sdk-sandbox","SDK: enable sandbox").option("--sdk-betas <list>","SDK beta flags: comma-separated").option("--sdk-system-prompt <string>","SDK system prompt text").option("--sdk-mcp-server <spec>","SDK MCP server: name:command:args (repeatable)",collectRepeat,[]).option("--sdk-plugin <path>","SDK plugin path (repeatable)",collectRepeat,[]).option("--sdk-agent <name>","SDK main agent name").option("--sdk-subagent <spec>","SDK subagent: name:json (repeatable)",collectRepeat,[])}function collectRepeat(value,previous){return previous.concat([value])}function buildSdkConfig(options){let config={};return applyScalarSdkOptions(config,options),applyBooleanSdkOptions(config,options),applyRepeatableSdkOptions(config,options),Object.keys(config).length>0?config:void 0}function toSafeNumber(value,flagName){let n=Number(value);if(Number.isNaN(n))throw Error(`${flagName} must be a number, got: ${value}`);return n}function applyScalarSdkOptions(config,options){if(options.sdkPermissionMode!==void 0)config.permissionMode=options.sdkPermissionMode;if(options.sdkTools!==void 0)config.tools=splitComma(options.sdkTools);if(options.sdkAllowedTools!==void 0)config.allowedTools=splitComma(options.sdkAllowedTools);if(options.sdkDisallowedTools!==void 0)config.disallowedTools=splitComma(options.sdkDisallowedTools);if(options.sdkMaxTurns!==void 0)config.maxTurns=toSafeNumber(options.sdkMaxTurns,"--sdk-max-turns");if(options.sdkMaxBudget!==void 0)config.maxBudgetUsd=toSafeNumber(options.sdkMaxBudget,"--sdk-max-budget");if(options.sdkEffort!==void 0)config.effort=options.sdkEffort;if(options.sdkThinking!==void 0)config.thinking=parseThinkingConfig(options.sdkThinking);if(options.sdkBetas!==void 0)config.betas=splitComma(options.sdkBetas);if(options.sdkSystemPrompt!==void 0)config.systemPrompt=options.sdkSystemPrompt;if(options.sdkAgent!==void 0)config.agent=options.sdkAgent;if(options.sdkOutputFormat!==void 0)config.outputFormat={type:"json_schema",schema:{$ref:options.sdkOutputFormat}}}function applyBooleanSdkOptions(config,options){if(options.sdkPersistSession!==void 0)config.persistSession=options.sdkPersistSession;if(options.sdkFileCheckpointing===!0)config.enableFileCheckpointing=!0;if(options.sdkStreamPartial===!0)config.includePartialMessages=!0;if(options.sdkHookEvents===!0)config.includeHookEvents=!0;if(options.sdkPromptSuggestions===!0)config.promptSuggestions=!0;if(options.sdkProgressSummaries===!0)config.agentProgressSummaries=!0;if(options.sdkSandbox===!0)config.sandbox={enabled:!0}}function applyRepeatableSdkOptions(config,options){if(options.sdkMcpServer&&options.sdkMcpServer.length>0){config.mcpServers={};for(let spec of options.sdkMcpServer){let parsed=parseMcpServer(spec);config.mcpServers[parsed.name]=parsed.config}}if(options.sdkPlugin&&options.sdkPlugin.length>0)config.plugins=options.sdkPlugin.map((p)=>({type:"local",path:p}));if(options.sdkSubagent&&options.sdkSubagent.length>0)config.agents=parseSubagents(options.sdkSubagent)}function parseSubagents(specs){let agents={};for(let spec of specs){let colonIdx=spec.indexOf(":");if(colonIdx===-1)throw Error(`Invalid --sdk-subagent format: "${spec}". Expected "name:json".`);let agentName=spec.slice(0,colonIdx),jsonStr=spec.slice(colonIdx+1);try{agents[agentName]=JSON.parse(jsonStr)}catch{throw Error(`Invalid JSON in --sdk-subagent "${agentName}": ${jsonStr}`)}}return agents}function parseThinkingConfig(value){if(value==="adaptive")return{type:"adaptive"};if(value==="disabled")return{type:"disabled"};if(value==="enabled")return{type:"enabled"};if(value.startsWith("enabled:"))return{type:"enabled",budgetTokens:toSafeNumber(value.slice(8),"--sdk-thinking budgetTokens")};throw Error(`Invalid --sdk-thinking value: "${value}". Expected adaptive|disabled|enabled[:budgetTokens].`)}function parseMcpServer(spec){let firstColon=spec.indexOf(":");if(firstColon===-1)throw Error(`Invalid --sdk-mcp-server format: "${spec}". Expected "name:command:args".`);let name=spec.slice(0,firstColon),rest=spec.slice(firstColon+1),secondColon=rest.indexOf(":");if(secondColon===-1)throw Error(`Invalid --sdk-mcp-server format: "${spec}". Expected "name:command:args".`);let command=rest.slice(0,secondColon),argsStr=rest.slice(secondColon+1),args=argsStr?argsStr.split(",").map((s)=>s.trim()).filter(Boolean):[];return{name,config:{type:"stdio",command,args}}}function splitComma(value){return value.split(",").map((s)=>s.trim()).filter(Boolean)}function printSdkConfig(sdk){console.log(" SDK Config:");let lines=collectSdkDisplayLines(sdk);for(let line of lines)console.log(` ${line}`)}function collectSdkDisplayLines(sdk){let lines=[];if(sdk.permissionMode)lines.push(`Permission Mode: ${sdk.permissionMode}`);if(sdk.tools)lines.push(`Tools: ${Array.isArray(sdk.tools)?sdk.tools.join(", "):`preset:${sdk.tools.preset}`}`);if(sdk.allowedTools?.length)lines.push(`Allowed Tools: ${sdk.allowedTools.join(", ")}`);if(sdk.disallowedTools?.length)lines.push(`Disallowed Tools: ${sdk.disallowedTools.join(", ")}`);if(sdk.maxTurns!==void 0)lines.push(`Max Turns: ${sdk.maxTurns}`);if(sdk.maxBudgetUsd!==void 0)lines.push(`Max Budget: $${sdk.maxBudgetUsd.toFixed(2)}`);if(sdk.effort)lines.push(`Effort: ${sdk.effort}`);if(sdk.thinking)lines.push(`Thinking: ${formatThinking(sdk.thinking)}`);if(sdk.agent)lines.push(`Agent: ${sdk.agent}`);if(sdk.persistSession!==void 0)lines.push(`Persist Session: ${sdk.persistSession}`);if(sdk.enableFileCheckpointing)lines.push("File Checkpointing: enabled");if(sdk.outputFormat)lines.push(`Output Format: ${JSON.stringify(sdk.outputFormat.schema)}`);return collectSdkBooleanLines(sdk,lines),collectSdkComplexLines(sdk,lines),lines}function collectSdkBooleanLines(sdk,lines){if(sdk.includePartialMessages)lines.push("Stream Partial: enabled");if(sdk.includeHookEvents)lines.push("Hook Events: enabled");if(sdk.promptSuggestions)lines.push("Prompt Suggestions: enabled");if(sdk.agentProgressSummaries)lines.push("Progress Summaries: enabled");if(sdk.sandbox?.enabled)lines.push("Sandbox: enabled");if(sdk.betas?.length)lines.push(`Betas: ${sdk.betas.join(", ")}`)}function collectSdkComplexLines(sdk,lines){if(sdk.systemPrompt){let prompt2=typeof sdk.systemPrompt==="string"?sdk.systemPrompt:`preset:${sdk.systemPrompt.preset}`;lines.push(`System Prompt: ${prompt2.length>60?`${prompt2.slice(0,60)}...`:prompt2}`)}if(sdk.mcpServers)lines.push(`MCP Servers: ${Object.keys(sdk.mcpServers).join(", ")}`);if(sdk.plugins?.length)lines.push(`Plugins: ${sdk.plugins.map((p)=>p.path).join(", ")}`);if(sdk.agents)lines.push(`Subagents: ${Object.keys(sdk.agents).join(", ")}`)}function formatThinking(thinking){if(thinking.type==="enabled"&&thinking.budgetTokens)return`enabled:${thinking.budgetTokens}`;return thinking.type}function buildPermissions(permissionPreset,allow,bashAllow){if(!permissionPreset&&!allow&&!bashAllow)return;if(permissionPreset)return{preset:permissionPreset};return{...allow&&{allow:allow.split(",").map((s)=>s.trim()).filter(Boolean)},...bashAllow&&{bashAllowPatterns:bashAllow.split(",").map((s)=>s.trim()).filter(Boolean)}}}function normalizeRoles(roles){if(!roles)return;return roles.flatMap((r)=>r.split(",")).map((r)=>r.trim()).filter(Boolean)}function printRegisteredTable(entries){let repoValues=[],roleValues=[];for(let entry2 of entries)repoValues.push(entry2.repo?contractPath(entry2.repo):contractPath(entry2.dir)),roleValues.push(entry2.roles?.join(", ")||"-");let termW=process.stdout.columns||120,fixedW=54,maxRepoLen=Math.max(4,...repoValues.map((v)=>v.length)),repoW=Math.min(maxRepoLen+2,Math.max(30,termW-fixedW-20)),totalW=fixedW+repoW+20;console.log(""),console.log("REGISTERED AGENTS"),console.log("-".repeat(Math.max(90,totalW))),console.log(` ${"NAME".padEnd(22)}${"SCOPE".padEnd(10)}${"REPO".padEnd(repoW)}${"MODEL".padEnd(10)}${"PROVIDER".padEnd(10)}ROLES`),console.log(` ${"-".repeat(20)} ${"-".repeat(8)} ${"-".repeat(repoW-2)} ${"-".repeat(8)} ${"-".repeat(8)} ${"-".repeat(20)}`);for(let i2=0;i2<entries.length;i2++){let entry2=entries[i2],repo=repoValues[i2],roles=roleValues[i2];console.log(` ${entry2.name.padEnd(22)}${entry2.scope.padEnd(10)}${repo.padEnd(repoW)}${(entry2.model||"-").padEnd(10)}${(entry2.provider||"-").padEnd(10)}${roles}`)}console.log("")}function printResolvedTable(entries){if(entries.length===0)return;let nameW=22,declW=14,resolvedW=14;console.log("RESOLVED DEFAULTS"),console.log("-".repeat(70));for(let field of RESOLVED_FIELDS){let fieldUpper=field.toUpperCase();console.log(` ${"AGENT".padEnd(nameW)}${`${fieldUpper} (declared)`.padEnd(declW)}${`${fieldUpper} (resolved)`.padEnd(resolvedW)}SOURCE`),console.log(` ${"-".repeat(nameW-2)} ${"-".repeat(declW-2)} ${"-".repeat(resolvedW-2)} ${"-".repeat(16)}`);for(let entry2 of entries){let ctx=buildDirResolveContext(entry2.name),result2=resolveFieldWithSource(entry2,field,ctx),declared=entry2[field]||"-";console.log(` ${entry2.name.padEnd(nameW)}${declared.padEnd(declW)}${result2.value.padEnd(resolvedW)}${result2.source}`)}}console.log("")}function printBuiltinsTable(){console.log("BUILT-IN AGENTS"),console.log("-".repeat(80)),console.log(` ${"NAME".padEnd(22)}${"TYPE".padEnd(10)}${"MODEL".padEnd(8)}DESCRIPTION`),console.log(` ${"-".repeat(20)} ${"-".repeat(8)} ${"-".repeat(6)} ${"-".repeat(30)}`);for(let agent of ALL_BUILTINS)console.log(` ${agent.name.padEnd(22)}${agent.category.padEnd(10)}${(agent.model||"-").padEnd(8)}${agent.description}`);console.log("")}function validatePromptMode2(mode){if(mode!=="system"&&mode!=="append")throw Error(`Invalid prompt mode "${mode}". Must be "append" or "system".`);return mode}function normalizeRoles2(roles){if(!roles)return;return roles.flatMap((r)=>r.split(",")).map((r)=>r.trim()).filter(Boolean)}function printEntry2(entry2){if(console.log(` Name: ${entry2.name}`),console.log(` Dir: ${contractPath(entry2.dir)}`),entry2.repo)console.log(` Repo: ${contractPath(entry2.repo)}`);if(console.log(` Prompt mode: ${entry2.promptMode}`),entry2.model)console.log(` Model: ${entry2.model}`);if(entry2.roles?.length)console.log(` Roles: ${entry2.roles.join(", ")}`);console.log(` Registered: ${entry2.registeredAt}`)}async function handleOmniRegistration(name,options){let omniUrl=await resolveOmniApiUrl();if(!omniUrl)return;console.log(`
3682
- Registering in Omni (${omniUrl})...`);let existingId=await findOmniAgent(name);if(existingId){console.log(` Agent already exists in Omni: ${existingId}`),await edit(name,{omniAgentId:existingId},{global:options.global}),console.log(" Linked existing Omni agent to directory entry.");return}let omniAgentId=await registerAgentInOmni(name,{model:options.model,roles:options.roles});if(omniAgentId)await edit(name,{omniAgentId},{global:options.global}),console.log(` Omni agent created: ${omniAgentId}`),console.log(" Session isolation: per-person + per-channel")}async function handleAgentRegister(name,options){let promptMode=validatePromptMode2(options.promptMode);if(options.repo)validateRepoPath(options.repo);let roles=normalizeRoles2(options.roles),entry2=await add({name,dir:resolvePath2(options.dir),repo:options.repo?resolvePath2(options.repo):void 0,promptMode,model:options.model,roles},{global:options.global}),scope=options.global?"global":"project";if(console.log(`Agent "${entry2.name}" registered (${scope}).`),printEntry2(entry2),!options.skipOmni)await handleOmniRegistration(name,{...options,roles})}function registerAgentRegister(parent){parent.command("register <name>").description("Register an agent locally and auto-register in Omni when configured").requiredOption("--dir <path>","Agent folder (CWD + AGENTS.md)").option("--repo <path>","Default git repo (overridden by team)").option("--prompt-mode <mode>","Prompt mode: append or system","append").option("--model <model>","Default model (sonnet, opus, codex)").option("--roles <roles...>","Built-in roles this agent can orchestrate").option("--global","Write to global directory instead of project").option("--skip-omni","Skip Omni auto-registration").action(async(name,options)=>{try{await handleAgentRegister(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_agents();function registerAgentResume(parent){parent.command("resume [name]").description("Resume a suspended/failed agent with its Claude session").option("--all","Resume all eligible agents").option("--no-reset-attempts","Preserve resumeAttempts counter (scheduler auto-resume use)").action(async(name,options)=>{try{await handleWorkerResume(name,{all:options.all,noResetAttempts:options.resetAttempts===!1})}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_msg();function registerAgentSend(parent){parent.command("send <body>").description("Send a direct message to an agent (hierarchy-enforced)").option("--to <agent>","Recipient agent name (default: team leader)","team-lead").option("--from <sender>","Sender ID (auto-detected from context)").option("--team <name>","Explicit team context for sender/recipient resolution").option("--broadcast","Send to all direct reports").action(async(body,options)=>{try{let from=options.from??await detectSenderIdentity(options.team);if(options.broadcast){await handleBroadcast(from,body,options.team);return}await handleDirectMessage(from,options.to,body,options.team)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}async function isTeamLeader(agentName,teamName){try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leaderName=await resolveLeaderName2(teamName);return agentName===leaderName}catch{return!1}}async function checkHierarchy(from,to){if(from==="cli")return{allowed:!0};if(from===to)return{allowed:!0};try{let agents=await(await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry))).listAgents({}),sender=agents.find((a)=>a.customName===from||a.role===from||a.id===from),recipient=agents.find((a)=>a.customName===to||a.role===to||a.id===to);if(!sender||!recipient)return{allowed:!0};if(recipient.reportsTo===from||recipient.reportsTo===sender.id)return{allowed:!0};if(sender.reportsTo===to||sender.reportsTo===recipient.id)return{allowed:!0};if(sender.reportsTo&&sender.reportsTo===recipient.reportsTo)return{allowed:!0};if(sender.team&&sender.team===recipient.team&&await isTeamLeader(from,sender.team))return{allowed:!0};let manager=sender.reportsTo??"your manager";return{allowed:!1,reason:`Cannot reach "${to}". Escalate to ${manager}.`}}catch{return{allowed:!0}}}async function handleDirectMessage(from,to,body,team){let{allowed,reason}=await checkHierarchy(from,to);if(!allowed)console.error(`Error: ${reason}`),process.exit(1);let{checkSendScope:checkSendScope2}=await Promise.resolve().then(() => (init_msg(),exports_msg)),repoPath=process.cwd(),scopeError=await checkSendScope2(repoPath,from,to);if(scopeError)console.error(`Error: ${scopeError}`),process.exit(1);let taskService=await Promise.resolve().then(() => (init_task_service(),exports_task_service)),mailbox=await Promise.resolve().then(() => (init_mailbox(),exports_mailbox)),senderActor={actorType:"local",actorId:from},recipientActor={actorType:"local",actorId:to},conv=await taskService.findOrCreateConversation({type:"dm",members:[senderActor,recipientActor],createdBy:senderActor});await taskService.addMember(conv.id,senderActor),await taskService.addMember(conv.id,recipientActor),await mailbox.send(repoPath,from,to,body);let msg=await taskService.sendMessage(conv.id,senderActor,body);try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,`genie.msg.${to}`,{kind:"message",agent:from,direction:"out",peer:to,text:body,data:{messageId:msg.id,conversationId:conv.id,from,to},source:"mailbox"})}catch{}try{let nativeTeams=await Promise.resolve().then(() => (init_claude_native_teams(),exports_claude_native_teams)),nativeMsg={from,text:body,summary:body.length>50?`${body.substring(0,50)}...`:body,timestamp:new Date().toISOString(),color:"blue",read:!1},currentTeam=team??process.env.GENIE_TEAM;if(currentTeam){let nativeName=await nativeTeams.resolveNativeMemberName(currentTeam,to);if(nativeName)await nativeTeams.writeNativeInbox(currentTeam,nativeName,nativeMsg)}}catch{}console.log(`Message sent to "${to}".`),console.log(` ID: ${msg.id}`),console.log(` Conversation: ${conv.id}`)}async function handleBroadcast(from,body,team){let taskService=await Promise.resolve().then(() => (init_task_service(),exports_task_service)),repoPath=process.cwd(),teamName=team??process.env.GENIE_TEAM;if(!teamName)console.error("Error: Could not detect team. Use --team <name>."),process.exit(1);let senderActor={actorType:"local",actorId:from},conv=await taskService.findOrCreateConversation({type:"group",name:`Team: ${teamName}`,linkedEntity:"team",linkedEntityId:teamName,createdBy:senderActor,members:[senderActor]});await taskService.addMember(conv.id,senderActor);let msg=await taskService.sendMessage(conv.id,senderActor,body);try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,"genie.msg.broadcast",{kind:"message",agent:from,direction:"out",peer:teamName,text:body,data:{messageId:msg.id,conversationId:conv.id,from,team:teamName},source:"mailbox"})}catch{}console.log(`Broadcast sent to team "${teamName}".`),console.log(` Message ID: ${msg.id}`),console.log(` Conversation: ${conv.id}`)}init_term_format();function printAgentFields(agent){if(console.log(""),console.log(`AGENT: ${agent.customName??agent.role??agent.id}`),console.log("\u2500".repeat(60)),console.log(` ${padRight("ID:",20)} ${agent.id}`),agent.role)console.log(` ${padRight("Role:",20)} ${agent.role}`);if(agent.customName)console.log(` ${padRight("Name:",20)} ${agent.customName}`);if(agent.team)console.log(` ${padRight("Team:",20)} ${agent.team}`);console.log(` ${padRight("Started:",20)} ${agent.startedAt}`)}function printExecutorFields(executor){if(console.log(""),console.log("Current Executor:"),console.log("\u2500".repeat(60)),console.log(` ${padRight("Executor ID:",20)} ${executor.id}`),console.log(` ${padRight("Provider:",20)} ${executor.provider}`),console.log(` ${padRight("Transport:",20)} ${executor.transport}`),console.log(` ${padRight("State:",20)} ${executor.state}`),executor.pid)console.log(` ${padRight("PID:",20)} ${executor.pid}`);if(executor.tmuxSession)console.log(` ${padRight("Tmux Session:",20)} ${executor.tmuxSession}`);if(executor.tmuxPaneId)console.log(` ${padRight("Tmux Pane:",20)} ${executor.tmuxPaneId}`);if(executor.worktree)console.log(` ${padRight("Worktree:",20)} ${executor.worktree}`);if(console.log(` ${padRight("Started:",20)} ${executor.startedAt}`),executor.endedAt)console.log(` ${padRight("Ended:",20)} ${executor.endedAt}`)}async function showAgent(name,json2){let registry=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),executorRegistry=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),matches=(await registry.listAgents()).filter((a)=>a.customName===name||a.role===name||a.id===name),preferredTeam=process.env.GENIE_TEAM,agent=(preferredTeam&&matches.find((a)=>a.team===preferredTeam))??matches[0];if(!agent)console.error(`Agent "${name}" not found.`),process.exit(1);if(json2){let executor=agent.currentExecutorId?await executorRegistry.getExecutor(agent.currentExecutorId):null;console.log(JSON.stringify({agent,executor},null,2));return}if(printAgentFields(agent),agent.currentExecutorId){let executor=await executorRegistry.getExecutor(agent.currentExecutorId);if(executor)printExecutorFields(executor)}else console.log(`
3712
+ Registering in Omni (${omniUrl})...`);let existingId=await findOmniAgent(name);if(existingId){console.log(` Agent already exists in Omni: ${existingId}`),await edit(name,{omniAgentId:existingId},{global:options.global}),console.log(" Linked existing Omni agent to directory entry.");return}let omniAgentId=await registerAgentInOmni(name,{model:options.model,roles:options.roles});if(omniAgentId)await edit(name,{omniAgentId},{global:options.global}),console.log(` Omni agent created: ${omniAgentId}`),console.log(" Session isolation: per-person + per-channel")}async function handleAgentRegister(name,options){let promptMode=validatePromptMode2(options.promptMode);if(options.repo)validateRepoPath(options.repo);let roles=normalizeRoles2(options.roles),entry2=await add({name,dir:resolvePath2(options.dir),repo:options.repo?resolvePath2(options.repo):void 0,promptMode,model:options.model,roles},{global:options.global}),scope=options.global?"global":"project";if(console.log(`Agent "${entry2.name}" registered (${scope}).`),printEntry2(entry2),!options.skipOmni)await handleOmniRegistration(name,{...options,roles})}function registerAgentRegister(parent){parent.command("register <name>").description("Register an agent locally and auto-register in Omni when configured").requiredOption("--dir <path>","Agent folder (CWD + AGENTS.md)").option("--repo <path>","Default git repo (overridden by team)").option("--prompt-mode <mode>","Prompt mode: append or system","append").option("--model <model>","Default model (sonnet, opus, codex)").option("--roles <roles...>","Built-in roles this agent can orchestrate").option("--global","Write to global directory instead of project").option("--skip-omni","Skip Omni auto-registration").action(async(name,options)=>{try{await handleAgentRegister(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_agents();function registerAgentResume(parent){parent.command("resume [name]").description("Resume a suspended/failed agent with its Claude session").option("--all","Resume all eligible agents").option("--no-reset-attempts","Preserve resumeAttempts counter (scheduler auto-resume use)").action(async(name,options)=>{try{await handleWorkerResume(name,{all:options.all,noResetAttempts:options.resetAttempts===!1})}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_msg();function registerAgentSend(parent){parent.command("send <body>").description("Send a direct message to an agent (hierarchy-enforced)").option("--to <agent>","Recipient agent name (default: team leader)","team-lead").option("--from <sender>","Sender ID (auto-detected from context)").option("--team <name>","Explicit team context for sender/recipient resolution").option("--broadcast","Send to all direct reports").action(async(body,options)=>{try{let from=options.from??await detectSenderIdentity(options.team);if(options.broadcast){await handleBroadcast(from,body,options.team);return}await handleDirectMessage(from,options.to,body,options.team)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}async function isTeamLeader(agentName,teamName){try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),leaderName=await resolveLeaderName2(teamName);return agentName===leaderName}catch{return!1}}async function checkHierarchy(from,to){if(from==="cli")return{allowed:!0};if(from===to)return{allowed:!0};try{let agents=await(await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry))).listAgents({}),sender=agents.find((a)=>a.customName===from||a.role===from||a.id===from),recipient=agents.find((a)=>a.customName===to||a.role===to||a.id===to);if(!sender||!recipient)return{allowed:!0};if(recipient.reportsTo===from||recipient.reportsTo===sender.id)return{allowed:!0};if(sender.reportsTo===to||sender.reportsTo===recipient.id)return{allowed:!0};if(sender.reportsTo&&sender.reportsTo===recipient.reportsTo)return{allowed:!0};if(sender.team&&sender.team===recipient.team&&await isTeamLeader(from,sender.team))return{allowed:!0};let manager=sender.reportsTo??"your manager";return{allowed:!1,reason:`Cannot reach "${to}". Escalate to ${manager}.`}}catch{return{allowed:!0}}}async function handleDirectMessage(from,to,body,team){let{allowed,reason}=await checkHierarchy(from,to);if(!allowed)console.error(`Error: ${reason}`),process.exit(1);let{checkSendScope:checkSendScope2}=await Promise.resolve().then(() => (init_msg(),exports_msg)),repoPath=process.cwd(),scopeError=await checkSendScope2(repoPath,from,to);if(scopeError)console.error(`Error: ${scopeError}`),process.exit(1);let taskService=await Promise.resolve().then(() => (init_task_service(),exports_task_service)),mailbox=await Promise.resolve().then(() => (init_mailbox(),exports_mailbox)),senderActor={actorType:"local",actorId:from},recipientActor={actorType:"local",actorId:to},conv=await taskService.findOrCreateConversation({type:"dm",members:[senderActor,recipientActor],createdBy:senderActor});await taskService.addMember(conv.id,senderActor),await taskService.addMember(conv.id,recipientActor),await mailbox.send(repoPath,from,to,body);let msg=await taskService.sendMessage(conv.id,senderActor,body);try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,`genie.msg.${to}`,{kind:"message",agent:from,direction:"out",peer:to,text:body,data:{messageId:msg.id,conversationId:conv.id,from,to},source:"mailbox"})}catch{}try{let nativeTeams=await Promise.resolve().then(() => (init_claude_native_teams(),exports_claude_native_teams)),nativeMsg={from,text:body,summary:body.length>50?`${body.substring(0,50)}...`:body,timestamp:new Date().toISOString(),color:"blue",read:!1},currentTeam=team??process.env.GENIE_TEAM;if(currentTeam){let nativeName=await nativeTeams.resolveNativeMemberName(currentTeam,to);if(nativeName)await nativeTeams.writeNativeInbox(currentTeam,nativeName,nativeMsg)}}catch{}console.log(`Message sent to "${to}".`),console.log(` ID: ${msg.id}`),console.log(` Conversation: ${conv.id}`)}async function deliverBroadcastToMembers(deps,repoPath,from,teamName,body){let members=await deps.listMembers(teamName)??[],results=[];for(let member of members){if(member===from)continue;try{let r=await deps.sendMessage(repoPath,from,member,body,teamName);results.push({member,delivered:r.delivered,reason:r.reason})}catch(err){results.push({member,delivered:!1,reason:err instanceof Error?err.message:String(err)})}}return results}async function handleBroadcast(from,body,team){let taskService=await Promise.resolve().then(() => (init_task_service(),exports_task_service)),repoPath=process.cwd(),teamName=team??process.env.GENIE_TEAM;if(!teamName)console.error("Error: Could not detect team. Use --team <name>."),process.exit(1);let senderActor={actorType:"local",actorId:from},conv=await taskService.findOrCreateConversation({type:"group",name:`Team: ${teamName}`,linkedEntity:"team",linkedEntityId:teamName,createdBy:senderActor,members:[senderActor]});await taskService.addMember(conv.id,senderActor);let msg=await taskService.sendMessage(conv.id,senderActor,body);try{let{publishSubjectEvent:publishSubjectEvent2}=await Promise.resolve().then(() => (init_runtime_events(),exports_runtime_events));await publishSubjectEvent2(repoPath,"genie.msg.broadcast",{kind:"message",agent:from,direction:"out",peer:teamName,text:body,data:{messageId:msg.id,conversationId:conv.id,from,team:teamName},source:"mailbox"})}catch{}let{listMembers:listMembers2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager)),protocolRouter=await Promise.resolve().then(() => (init_protocol_router(),exports_protocol_router)),fanoutResults=await deliverBroadcastToMembers({listMembers:listMembers2,sendMessage:protocolRouter.sendMessage},repoPath,from,teamName,body);console.log(`Broadcast sent to team "${teamName}".`),console.log(` Message ID: ${msg.id}`),console.log(` Conversation: ${conv.id}`);let deliveredCount=fanoutResults.filter((r)=>r.delivered).length;console.log(` Fan-out: ${deliveredCount}/${fanoutResults.length} members reached`);for(let r of fanoutResults)if(!r.delivered)console.log(` \u26A0 ${r.member}: ${r.reason??"delivery failed"}`)}init_term_format();function printAgentFields(agent){if(console.log(""),console.log(`AGENT: ${agent.customName??agent.role??agent.id}`),console.log("\u2500".repeat(60)),console.log(` ${padRight("ID:",20)} ${agent.id}`),agent.role)console.log(` ${padRight("Role:",20)} ${agent.role}`);if(agent.customName)console.log(` ${padRight("Name:",20)} ${agent.customName}`);if(agent.team)console.log(` ${padRight("Team:",20)} ${agent.team}`);console.log(` ${padRight("Started:",20)} ${agent.startedAt}`)}function printExecutorFields(executor){if(console.log(""),console.log("Current Executor:"),console.log("\u2500".repeat(60)),console.log(` ${padRight("Executor ID:",20)} ${executor.id}`),console.log(` ${padRight("Provider:",20)} ${executor.provider}`),console.log(` ${padRight("Transport:",20)} ${executor.transport}`),console.log(` ${padRight("State:",20)} ${executor.state}`),executor.pid)console.log(` ${padRight("PID:",20)} ${executor.pid}`);if(executor.tmuxSession)console.log(` ${padRight("Tmux Session:",20)} ${executor.tmuxSession}`);if(executor.tmuxPaneId)console.log(` ${padRight("Tmux Pane:",20)} ${executor.tmuxPaneId}`);if(executor.worktree)console.log(` ${padRight("Worktree:",20)} ${executor.worktree}`);if(console.log(` ${padRight("Started:",20)} ${executor.startedAt}`),executor.endedAt)console.log(` ${padRight("Ended:",20)} ${executor.endedAt}`)}async function showAgent(name,json2){let registry=await Promise.resolve().then(() => (init_agent_registry(),exports_agent_registry)),executorRegistry=await Promise.resolve().then(() => (init_executor_registry(),exports_executor_registry)),matches=(await registry.listAgents()).filter((a)=>a.customName===name||a.role===name||a.id===name),preferredTeam=process.env.GENIE_TEAM,agent=(preferredTeam&&matches.find((a)=>a.team===preferredTeam))??matches[0];if(!agent)console.error(`Agent "${name}" not found.`),process.exit(1);if(json2){let executor=agent.currentExecutorId?await executorRegistry.getExecutor(agent.currentExecutorId):null;console.log(JSON.stringify({agent,executor},null,2));return}if(printAgentFields(agent),agent.currentExecutorId){let executor=await executorRegistry.getExecutor(agent.currentExecutorId);if(executor)printExecutorFields(executor)}else console.log(`
3683
3713
  No active executor.`);console.log("")}function registerAgentShow(parent){parent.command("show <name>").description("Show agent identity and current executor detail").option("--json","Output as JSON").action(async(name,options)=>{try{await showAgent(name,options.json)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_agents();function parseNumericFlag(flagName){return(value)=>{let n=Number(value);if(Number.isNaN(n))throw Error(`${flagName} must be a number, got: ${value}`);return n}}function registerAgentSpawn(parent){parent.command("spawn <name>").description("Spawn a new agent by name (resolves from directory or built-ins)").option("--provider <provider>","Provider: claude, codex, or claude-sdk").option("--team <team>","Team name").option("--model <model>","Model override (e.g., sonnet, opus)").option("--skill <skill>","Skill to load (optional)").option("--layout <layout>","Layout mode: mosaic (default) or vertical").option("--color <color>","Teammate pane border color").option("--plan-mode","Start teammate in plan mode").option("--permission-mode <mode>","Permission mode (e.g., acceptEdits)").option("--extra-args <args...>","Extra CLI args forwarded to provider").option("--cwd <path>","Working directory for the agent (overrides directory entry)").option("--session <session>","Tmux session name to spawn into").option("--role <role>","Override role name for registration (avoids duplicate guard)").option("--new-window","Create a new tmux window instead of splitting").option("--window <target>","Tmux window to split into (e.g., genie:3)").option("--no-auto-resume","Disable auto-resume on pane death").option("--prompt <prompt>","Initial prompt (first user message)").option("--sdk-max-turns <n>","SDK: max conversation turns",parseNumericFlag("--sdk-max-turns")).option("--sdk-max-budget <usd>","SDK: max budget in USD",parseNumericFlag("--sdk-max-budget")).option("--sdk-stream","SDK: enable streaming output (shortcut for --stream)").option("--sdk-effort <level>","SDK: reasoning effort level (low, medium, high, max)").option("--sdk-resume <session-id>","SDK: resume a previous session by ID").action(async(name,options)=>{if(options.prompt)options.initialPrompt=options.prompt;try{await handleWorkerSpawn(name,options)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}init_agents();function registerAgentStop(parent){parent.command("stop <name>").description("Stop an agent (preserves session for resume)").action(async(name)=>{try{await handleWorkerStop(name)}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}})}function registerAgentCommands(program2){let agent=program2.command("agent").description("Agent lifecycle management");registerAgentSpawn(agent),registerAgentStop(agent),registerAgentResume(agent),registerAgentKill(agent),registerAgentList(agent),registerAgentShow(agent),registerAgentAnswer(agent),registerAgentRegister(agent),registerAgentDirectory(agent),registerAgentInbox(agent),registerAgentBrief(agent),registerAgentLog(agent),registerAgentSend(agent),agent.on("command:*",(operands)=>{let cmd=operands[0],available=agent.commands.map((c)=>c.name()).join(", ");agent.error(`Unknown agent command '${cmd}'. Available: ${available}`)})}init_agents();async function handleTuiMode(){let{isServeRunning:isServeRunning2,autoStartServe:autoStartServe2}=await Promise.resolve().then(() => (init_serve(),exports_serve));if(!isServeRunning2())console.log("Starting genie serve..."),await autoStartServe2();let{attachTuiSession:attachTuiSession2}=await Promise.resolve().then(() => (init_tmux2(),exports_tmux2));attachTuiSession2()}async function findTauriBinary(){let{existsSync:existsSync38}=await import("fs"),{join:join45,dirname:dirname9}=await import("path"),{execSync:execSync11}=await import("child_process"),appName="genie-desktop",rootDir=join45(dirname9(new URL(import.meta.url).pathname),"..",".."),localBin=[join45(rootDir,"packages","genie-app","src-tauri","target","release","genie-desktop"),join45(rootDir,"packages","genie-app","src-tauri","target","debug","genie-desktop"),join45(rootDir,"dist","app","genie-desktop"),"/usr/local/bin/genie-desktop"].find((p)=>existsSync38(p));if(localBin)return localBin;try{return execSync11("which genie-desktop",{stdio:"ignore"}),"genie-desktop"}catch{return}}function registerAppCommand(program2){program2.command("app").description("Launch Genie desktop app (backend sidecar + views)").option("--backend-only","Start only the backend sidecar (IPC on stdin/stdout)").option("--tui","Fall back to terminal UI mode").option("--dev","Development mode").action(async(options)=>{if(options.tui){await handleTuiMode();return}if(options.backendOnly){await Promise.resolve().then(() => (init_src_backend(),exports_src_backend));return}let tauriBin=await findTauriBinary();if(tauriBin){console.log("\x1B[35m\u25C6 Genie App\x1B[0m Launching desktop...");let{execFileSync}=await import("child_process");try{execFileSync(tauriBin,[],{stdio:"inherit"})}catch{}return}console.log("\x1B[35m\u25C6 Genie App\x1B[0m Starting backend sidecar..."),console.log("\x1B[2mDesktop binary not found \u2014 running in sidecar mode.\x1B[0m"),console.log("\x1B[2mPG bridge + PTY manager + IPC on stdin/stdout\x1B[0m"),console.log(`\x1B[2mUse --tui for terminal UI, or pipe to a frontend shell.\x1B[0m
3684
3714
  `),await Promise.resolve().then(() => (init_src_backend(),exports_src_backend))})}init_claude_sdk_remote_approval();init_workspace();async function handleRequest(options){let ws=findWorkspace(),permissions=ws?getWorkspaceConfig(ws.root).permissions:void 0,timeoutSec=options.timeout?Number(options.timeout):permissions?.timeout??300,defaultAction=permissions?.defaultAction??"deny",timeoutAt=new Date(Date.now()+timeoutSec*1000),approvalId=await insertApproval(`cli-${process.pid}`,options.agent,options.tool,options.input,timeoutAt);if(console.log(`Approval created: ${approvalId}`),options.wait){console.log(`Waiting for resolution (timeout: ${timeoutSec}s, default: ${defaultAction})...`);let decision=await waitForResolution(approvalId,timeoutAt,defaultAction);if(console.log(`Decision: ${decision}`),decision==="deny")process.exit(1)}}async function handleResolve(id,options){if(options.decision!=="allow"&&options.decision!=="deny")console.error('Error: --decision must be "allow" or "deny"'),process.exit(1);let actor=process.env.GENIE_AGENT_NAME||options.by;if(await resolveApproval(id,options.decision,actor))console.log(`Approval ${id} resolved: ${options.decision} by ${options.by}`);else console.error(`Error: Approval ${id} not found or already resolved`),process.exit(1)}async function handleList(options){let rows=await listPendingApprovals(options.agent);if(options.json){console.log(JSON.stringify(rows,null,2));return}if(rows.length===0){console.log("No pending approvals.");return}console.log(` ${"ID".padEnd(38)} ${"AGENT".padEnd(20)} ${"TOOL".padEnd(15)} TIMEOUT`),console.log(` ${"\u2500".repeat(85)}`);for(let row of rows){let timeout=new Date(row.timeout_at).toLocaleTimeString();console.log(` ${String(row.id).padEnd(38)} ${String(row.agent_name).padEnd(20)} ${String(row.tool_name).padEnd(15)} ${timeout}`)}console.log(`
3685
3715
  ${rows.length} pending approval${rows.length===1?"":"s"}`)}function registerApprovalCommands(program2){let approval=program2.command("approval").description("Remote approval queue management");approval.command("request").description("Create an approval request (for tmux-path agents)").requiredOption("--tool <name>","Tool name requiring approval").requiredOption("--input <preview>","Tool input preview text").requiredOption("--agent <name>","Agent name requesting approval").option("--wait","Block until the approval is resolved").option("--timeout <seconds>","Timeout in seconds (overrides workspace config)").action(async(options)=>{try{await handleRequest(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),approval.command("resolve <id>").description("Resolve a pending approval").requiredOption("--decision <decision>","Decision: allow or deny").option("--by <actor>",'Display label for decision maker (defaults to GENIE_AGENT_NAME or "cli")',"cli").action(async(id,options)=>{try{await handleResolve(id,options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),approval.command("list").description("List pending approvals").option("--agent <name>","Filter by agent name").option("--json","Output as JSON").action(async(options)=>{try{await handleList(options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_audit();init_db();var V2_SELECT=`
@@ -3891,7 +3921,7 @@ Genie Scheduler Daemon`),console.log("\u2500".repeat(50)),console.log(` Status:
3891
3921
  (showing last ${lines} of ${allLines.length} entries)`)}async function tailFollow(filePath,initialLines){let{watch:watch2}=await import("fs");tailStatic(filePath,initialLines),console.log(`
3892
3922
  --- following (Ctrl+C to exit) ---
3893
3923
  `);let lastSize=existsSync41(filePath)?readFileSync28(filePath).length:0,watcher2=watch2(filePath,()=>{try{let content=readFileSync28(filePath,"utf-8");if(content.length>lastSize){let newLines=content.slice(lastSize).trim().split(`
3894
- `).filter(Boolean);for(let line of newLines)printLogLine(line);lastSize=content.length}}catch{}});process.on("SIGINT",()=>{watcher2.close(),process.exit(0)}),await new Promise(()=>{})}function printLogLine(raw){try{let entry2=JSON.parse(raw),ts3=entry2.timestamp?new Date(entry2.timestamp).toLocaleTimeString("en-US",{hour12:!1}):"??:??:??",level=(entry2.level??"info").toUpperCase().padEnd(5),event=entry2.event??"unknown",extras=Object.entries(entry2).filter(([k])=>!["timestamp","level","event"].includes(k)).map(([k,v])=>`${k}=${typeof v==="object"?JSON.stringify(v):v}`).join(" ");console.log(`${ts3} ${level} ${event}${extras?` ${extras}`:""}`)}catch{console.log(raw)}}function formatUptime(ms){let seconds=Math.floor(ms/1000),minutes=Math.floor(seconds/60),hours=Math.floor(minutes/60),days=Math.floor(hours/24);if(days>0)return`${days}d ${hours%24}h ${minutes%60}m`;if(hours>0)return`${hours}h ${minutes%60}m`;if(minutes>0)return`${minutes}m ${seconds%60}s`;return`${seconds}s`}function registerDaemonCommands(program2){let daemon=program2.command("daemon").description("Manage scheduler daemon lifecycle (redirects to genie serve --headless)");daemon.command("install").description("Generate systemd service unit and enable it").action(async()=>{await daemonInstallCommand()}),daemon.command("start").description("Start the scheduler daemon (alias for genie serve --headless)").option("--foreground","Run in foreground (for systemd ExecStart)").action(async(options)=>{await daemonStartCommand(options)}),daemon.command("stop").description("Stop genie serve gracefully").action(async()=>{await daemonStopCommand()}),daemon.command("status").description("Show daemon state, PID, uptime, and trigger stats").action(async()=>{await daemonStatusCommand()}),daemon.command("logs").description("Tail structured JSON scheduler log").option("--follow, -f","Follow log output").option("--lines <n>","Number of lines to show (default: 20)",Number.parseInt).action(async(options)=>{await daemonLogsCommand(options)})}init_cron();import{createInterface as createInterface3}from"readline";init_db();init_wish_state();import{spawnSync as spawnSync6}from"child_process";import{existsSync as existsSync42,mkdirSync as mkdirSync19,readFileSync as readFileSync29,renameSync as renameSync4,statSync as statSync5,writeFileSync as writeFileSync21}from"fs";import{homedir as homedir33}from"os";import{basename as basename8,join as join49,relative,resolve as resolve7}from"path";import{gunzipSync,gzipSync}from"zlib";var DB_NAME2="genie",DB_USER="postgres",DB_HOST="127.0.0.1",SNAPSHOT_FILE="snapshot.sql.gz";function getSnapshotPath(cwd){let repoRoot=resolveRepoPath(cwd),genieHome6=process.env.GENIE_HOME??join49(homedir33(),".genie");return join49(genieHome6,"backups",basename8(repoRoot),SNAPSHOT_FILE)}function assertOutsideRepo(snapshotPath,cwd){let repoRoot=resolveRepoPath(cwd),rel=relative(repoRoot,resolve7(snapshotPath));if(!rel.startsWith("..")&&rel!==""&&!rel.startsWith("/"))throw Error(`Refusing to write snapshot inside repo tree: ${snapshotPath}. Snapshots must live outside the repo (default: ~/.genie/backups/<repo>/).`)}function pgEnv(port){return{...process.env,PGHOST:DB_HOST,PGPORT:String(port),PGUSER:DB_USER,PGPASSWORD:DB_USER,PGDATABASE:DB_NAME2}}function backup(cwd){let port=getActivePort(),snapshotPath=getSnapshotPath(cwd);assertOutsideRepo(snapshotPath,cwd);let snapshotDir=snapshotPath.slice(0,snapshotPath.lastIndexOf("/")),tmpPath=`${snapshotPath}.tmp`;mkdirSync19(snapshotDir,{recursive:!0});let result2=spawnSync6("pg_dump",["--no-owner","--no-acl"],{env:pgEnv(port),stdio:["pipe","pipe","pipe"],timeout:120000,maxBuffer:1073741824});if(result2.status!==0){let stderr=result2.stderr?.toString().trim()||"unknown error";throw Error(`pg_dump failed (exit ${result2.status}): ${stderr}`)}let compressed=gzipSync(result2.stdout);writeFileSync21(tmpPath,compressed),renameSync4(tmpPath,snapshotPath);let compressedBytes=statSync5(snapshotPath).size,uncompressedBytes=0;try{let sizeResult=spawnSync6("psql",["-t","-A","-c","SELECT pg_database_size(current_database())"],{env:pgEnv(port),encoding:"utf-8",timeout:1e4});if(sizeResult.status===0)uncompressedBytes=Number.parseInt(sizeResult.stdout.trim(),10)||0}catch{}return{path:snapshotPath,compressedBytes,uncompressedBytes}}function restore(snapshotFile,cwd){let port=getActivePort(),filePath=snapshotFile??getSnapshotPath(cwd);if(!existsSync42(filePath))throw Error(`Snapshot not found: ${filePath}`);let env=pgEnv(port),adminEnv={...env,PGDATABASE:"postgres"};spawnSync6("psql",["-v",`target_db=${DB_NAME2}`,"-c","SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = :'target_db' AND pid <> pg_backend_pid()"],{env:adminEnv,stdio:["pipe","pipe","pipe"],timeout:1e4});let dropResult=spawnSync6("psql",["-v",`target_db=${DB_NAME2}`,"-c",'DROP DATABASE IF EXISTS :"target_db"'],{env:adminEnv,stdio:["pipe","pipe","pipe"],timeout:1e4});if(dropResult.status!==0)throw Error(`Failed to drop database: ${dropResult.stderr?.toString().trim()}`);let createResult=spawnSync6("psql",["-v",`target_db=${DB_NAME2}`,"-c",'CREATE DATABASE :"target_db"'],{env:adminEnv,stdio:["pipe","pipe","pipe"],timeout:1e4});if(createResult.status!==0)throw Error(`Failed to create database: ${createResult.stderr?.toString().trim()}`);let compressed=readFileSync29(filePath),sql=gunzipSync(compressed),restoreResult=spawnSync6("psql",[],{env:{...env,PGDATABASE:DB_NAME2},input:sql,stdio:["pipe","pipe","pipe"],timeout:300000,maxBuffer:1073741824});if(restoreResult.status!==0)throw Error(`psql restore failed (exit ${restoreResult.status}): ${restoreResult.stderr?.toString().trim()}`)}init_db_migrations();init_db();init_term_format();function printTable(rows){if(rows.length===0){console.log("(0 rows)");return}let columns=Object.keys(rows[0]),widths=columns.map((col)=>{let values2=rows.map((r)=>String(r[col]??"NULL"));return Math.max(col.length,...values2.map((v)=>v.length))}),header=columns.map((col,i2)=>padRight(col,widths[i2])).join(" | ");console.log(header),console.log(widths.map((w)=>"-".repeat(w)).join("-+-"));for(let row of rows){let line=columns.map((col,i2)=>padRight(String(row[col]??"NULL"),widths[i2])).join(" | ");console.log(line)}console.log(`(${rows.length} row${rows.length===1?"":"s"})`)}async function dbStatusCommand(){let port=getActivePort(),dataDir=getDataDir();if(console.log(`
3924
+ `).filter(Boolean);for(let line of newLines)printLogLine(line);lastSize=content.length}}catch{}});process.on("SIGINT",()=>{watcher2.close(),process.exit(0)}),await new Promise(()=>{})}function printLogLine(raw){try{let entry2=JSON.parse(raw),ts3=entry2.timestamp?new Date(entry2.timestamp).toLocaleTimeString("en-US",{hour12:!1}):"??:??:??",level=(entry2.level??"info").toUpperCase().padEnd(5),event=entry2.event??"unknown",extras=Object.entries(entry2).filter(([k])=>!["timestamp","level","event"].includes(k)).map(([k,v])=>`${k}=${typeof v==="object"?JSON.stringify(v):v}`).join(" ");console.log(`${ts3} ${level} ${event}${extras?` ${extras}`:""}`)}catch{console.log(raw)}}function formatUptime(ms){let seconds=Math.floor(ms/1000),minutes=Math.floor(seconds/60),hours=Math.floor(minutes/60),days=Math.floor(hours/24);if(days>0)return`${days}d ${hours%24}h ${minutes%60}m`;if(hours>0)return`${hours}h ${minutes%60}m`;if(minutes>0)return`${minutes}m ${seconds%60}s`;return`${seconds}s`}function registerDaemonCommands(program2){let daemon=program2.command("daemon").description("Manage scheduler daemon lifecycle (redirects to genie serve --headless)");daemon.command("install").description("Generate systemd service unit and enable it").action(async()=>{await daemonInstallCommand()}),daemon.command("start").description("Start the scheduler daemon (alias for genie serve --headless)").option("--foreground","Run in foreground (for systemd ExecStart)").action(async(options)=>{await daemonStartCommand(options)}),daemon.command("stop").description("Stop genie serve gracefully").action(async()=>{await daemonStopCommand()}),daemon.command("status").description("Show daemon state, PID, uptime, and trigger stats").action(async()=>{await daemonStatusCommand()}),daemon.command("logs").description("Tail structured JSON scheduler log").option("--follow, -f","Follow log output").option("--lines <n>","Number of lines to show (default: 20)",Number.parseInt).action(async(options)=>{await daemonLogsCommand(options)})}init_cron();import{createInterface as createInterface3}from"readline";init_db();init_wish_state();import{spawnSync as spawnSync6}from"child_process";import{existsSync as existsSync42,mkdirSync as mkdirSync19,readFileSync as readFileSync29,renameSync as renameSync4,statSync as statSync6,writeFileSync as writeFileSync21}from"fs";import{homedir as homedir33}from"os";import{basename as basename8,join as join49,relative,resolve as resolve7}from"path";import{gunzipSync,gzipSync}from"zlib";var DB_NAME2="genie",DB_USER="postgres",DB_HOST="127.0.0.1",SNAPSHOT_FILE="snapshot.sql.gz";function getSnapshotPath(cwd){let repoRoot=resolveRepoPath(cwd),genieHome6=process.env.GENIE_HOME??join49(homedir33(),".genie");return join49(genieHome6,"backups",basename8(repoRoot),SNAPSHOT_FILE)}function assertOutsideRepo(snapshotPath,cwd){let repoRoot=resolveRepoPath(cwd),rel=relative(repoRoot,resolve7(snapshotPath));if(!rel.startsWith("..")&&rel!==""&&!rel.startsWith("/"))throw Error(`Refusing to write snapshot inside repo tree: ${snapshotPath}. Snapshots must live outside the repo (default: ~/.genie/backups/<repo>/).`)}function pgEnv(port){return{...process.env,PGHOST:DB_HOST,PGPORT:String(port),PGUSER:DB_USER,PGPASSWORD:DB_USER,PGDATABASE:DB_NAME2}}function backup(cwd){let port=getActivePort(),snapshotPath=getSnapshotPath(cwd);assertOutsideRepo(snapshotPath,cwd);let snapshotDir=snapshotPath.slice(0,snapshotPath.lastIndexOf("/")),tmpPath=`${snapshotPath}.tmp`;mkdirSync19(snapshotDir,{recursive:!0});let result2=spawnSync6("pg_dump",["--no-owner","--no-acl"],{env:pgEnv(port),stdio:["pipe","pipe","pipe"],timeout:120000,maxBuffer:1073741824});if(result2.status!==0){let stderr=result2.stderr?.toString().trim()||"unknown error";throw Error(`pg_dump failed (exit ${result2.status}): ${stderr}`)}let compressed=gzipSync(result2.stdout);writeFileSync21(tmpPath,compressed),renameSync4(tmpPath,snapshotPath);let compressedBytes=statSync6(snapshotPath).size,uncompressedBytes=0;try{let sizeResult=spawnSync6("psql",["-t","-A","-c","SELECT pg_database_size(current_database())"],{env:pgEnv(port),encoding:"utf-8",timeout:1e4});if(sizeResult.status===0)uncompressedBytes=Number.parseInt(sizeResult.stdout.trim(),10)||0}catch{}return{path:snapshotPath,compressedBytes,uncompressedBytes}}function restore(snapshotFile,cwd){let port=getActivePort(),filePath=snapshotFile??getSnapshotPath(cwd);if(!existsSync42(filePath))throw Error(`Snapshot not found: ${filePath}`);let env=pgEnv(port),adminEnv={...env,PGDATABASE:"postgres"};spawnSync6("psql",["-v",`target_db=${DB_NAME2}`,"-c","SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = :'target_db' AND pid <> pg_backend_pid()"],{env:adminEnv,stdio:["pipe","pipe","pipe"],timeout:1e4});let dropResult=spawnSync6("psql",["-v",`target_db=${DB_NAME2}`,"-c",'DROP DATABASE IF EXISTS :"target_db"'],{env:adminEnv,stdio:["pipe","pipe","pipe"],timeout:1e4});if(dropResult.status!==0)throw Error(`Failed to drop database: ${dropResult.stderr?.toString().trim()}`);let createResult=spawnSync6("psql",["-v",`target_db=${DB_NAME2}`,"-c",'CREATE DATABASE :"target_db"'],{env:adminEnv,stdio:["pipe","pipe","pipe"],timeout:1e4});if(createResult.status!==0)throw Error(`Failed to create database: ${createResult.stderr?.toString().trim()}`);let compressed=readFileSync29(filePath),sql=gunzipSync(compressed),restoreResult=spawnSync6("psql",[],{env:{...env,PGDATABASE:DB_NAME2},input:sql,stdio:["pipe","pipe","pipe"],timeout:300000,maxBuffer:1073741824});if(restoreResult.status!==0)throw Error(`psql restore failed (exit ${restoreResult.status}): ${restoreResult.stderr?.toString().trim()}`)}init_db_migrations();init_db();init_term_format();function printTable(rows){if(rows.length===0){console.log("(0 rows)");return}let columns=Object.keys(rows[0]),widths=columns.map((col)=>{let values2=rows.map((r)=>String(r[col]??"NULL"));return Math.max(col.length,...values2.map((v)=>v.length))}),header=columns.map((col,i2)=>padRight(col,widths[i2])).join(" | ");console.log(header),console.log(widths.map((w)=>"-".repeat(w)).join("-+-"));for(let row of rows){let line=columns.map((col,i2)=>padRight(String(row[col]??"NULL"),widths[i2])).join(" | ");console.log(line)}console.log(`(${rows.length} row${rows.length===1?"":"s"})`)}async function dbStatusCommand(){let port=getActivePort(),dataDir=getDataDir();if(console.log(`
3895
3925
  Genie Database Status`),console.log("\u2500".repeat(50)),console.log(` Port: ${port}`),console.log(" Host: 127.0.0.1"),console.log(` Data dir: ${dataDir}`),!await isAvailable()){console.log(" Status: stopped"),console.log(`
3896
3926
  pgserve is not running. It will auto-start on first use.`),console.log("");return}console.log(" Status: running");try{let sql=await getConnection(),sizeResult=await sql`SELECT pg_size_pretty(pg_database_size(current_database())) AS size`;console.log(` DB size: ${sizeResult[0].size}`);let migrations=await getMigrationStatus(sql);console.log(`
3897
3927
  Migrations: ${migrations.applied.length} applied, ${migrations.pending.length} pending`);let tables=await sql`
@@ -3909,7 +3939,7 @@ Done. ${results.length} migration${results.length===1?"":"s"} applied.`),await s
3909
3939
  DELETE FROM genie_runtime_events
3910
3940
  WHERE created_at < now() - make_interval(secs => ${intervalSec})
3911
3941
  `,count=Number(result2.count);console.log(`Deleted ${count} event${count===1?"":"s"} older than ${options.olderThan}.`)}await shutdown()}catch(err){let message=err instanceof Error?err.message:String(err);console.error(`Prune failed: ${message}`),process.exit(1)}}function registerDbCommands(program2){let db=program2.command("db").description("Database management (pgserve)");db.command("status").description("Show pgserve health, port, data dir, and table counts").action(dbStatusCommand),db.command("migrate").description("Run pending database migrations").action(dbMigrateCommand),db.command("query <sql>").description("Execute arbitrary SQL and print results").action(dbQueryCommand),db.command("url").description("Print postgres connection URL for direct access").option("--quiet","Print URL only, no trailing newline (for scripts)").action((options)=>{let url=`postgres://postgres:postgres@127.0.0.1:${getActivePort()}/genie`;if(options.quiet)process.stdout.write(url);else console.log(url)}),db.command("prune-events").description("Prune old runtime events beyond retention period").option("--older-than <duration>","Delete events older than (e.g., 30d, 7d)","14d").option("--dry-run","Show count without deleting").action(dbPruneEventsCommand),db.command("backup").description("Dump database to .genie/snapshot.sql.gz").action(dbBackupCommand),db.command("restore [file]").description("Restore database from snapshot (default: .genie/snapshot.sql.gz)").option("-y, --yes","Skip confirmation prompt").action(dbRestoreCommand)}init_dispatch();function registerDispatchGroupCommands(program2){let dispatch2=program2.command("dispatch").description("Framework skill dispatch primitives (brainstorm/wish/review)");dispatch2.command("brainstorm <agent> <slug>").description("Spawn agent with brainstorm DRAFT.md context").action(async(agent,slug)=>{await brainstormCommand(agent,slug)}),dispatch2.command("wish <agent> <slug>").description("Spawn agent with wish DESIGN.md context").action(async(agent,slug)=>{await wishCommand(agent,slug)}),dispatch2.command("review <agent> <ref>").description("Spawn agent with review scope for a wish group (format: <slug>#<group>)").action(async(agent,ref)=>{await reviewCommand(agent,ref)})}init_dispatch();init_export_format();import{existsSync as existsSync49,mkdirSync as mkdirSync20,writeFileSync as writeFileSync22}from"fs";import{dirname as dirname12}from"path";async function getSql(){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return getConnection2()}async function getVersion(){let{VERSION:VERSION2}=await Promise.resolve().then(() => (init_version(),exports_version));return VERSION2}async function getActorName(){let{getActor:getActor2}=await Promise.resolve().then(() => (init_audit(),exports_audit));return getActor2()}async function detectTables(sql,tables){let{filterAvailableTables:filterAvailableTables2}=await Promise.resolve().then(() => exports_table_detect);return filterAvailableTables2(sql,tables)}function outputDocument(doc,options){let json2=options.pretty?JSON.stringify(doc,null,2):JSON.stringify(doc);if(options.output){let dir=dirname12(options.output);if(!existsSync49(dir))mkdirSync20(dir,{recursive:!0});writeFileSync22(options.output,`${json2}
3912
- `);let tables=Object.keys(doc.data),rows=Object.values(doc.data).reduce((sum,arr)=>sum+arr.length,0);if(console.log(`Exported ${tables.length} tables (${rows} rows) to ${options.output}`),doc.skippedTables.length>0)console.log(`Skipped tables (not found): ${doc.skippedTables.join(", ")}`)}else console.log(json2)}function autoOutputName(){let d=new Date;return`genie-backup-${`${d.getFullYear()}${String(d.getMonth()+1).padStart(2,"0")}${String(d.getDate()).padStart(2,"0")}`}.json`}async function exportGroup(sql,group,filter){let tables=GROUP_TABLES[group],{available,skipped}=await detectTables(sql,tables),data={};for(let table of available)if(filter)data[table]=[...await sql.unsafe(`SELECT * FROM ${table} WHERE ${filter.column} = $1`,[filter.value])];else data[table]=[...await sql.unsafe(`SELECT * FROM ${table}`)];return{data,skipped}}async function exportBoards(sql,name){let tables=GROUP_TABLES.boards,{available,skipped}=await detectTables(sql,tables),data={};for(let table of available)if(name&&table==="boards")data[table]=[...await sql`SELECT * FROM boards WHERE name = ${name}`];else if(table==="task_types")data[table]=[...await sql`SELECT * FROM task_types WHERE is_builtin = false`];else data[table]=[...await sql.unsafe(`SELECT * FROM ${table}`)];return{data,skipped}}var TASK_JOIN_ALIASES={task_tags:"tt",task_actors:"ta",task_dependencies:"td",task_stage_log:"tsl"};async function resolveProjectId2(sql,projectName){let projects=await sql`SELECT id FROM projects WHERE name = ${projectName}`;if(projects.length===0)throw Error(`Project not found: ${projectName}`);return projects[0].id}function stripEphemeralFields(rows){return rows.map((r)=>{let{checkout_run_id,execution_locked_at,session_id,pane_id,...rest}=r;return rest})}async function exportTaskTable(sql,table,projectId){let alias=TASK_JOIN_ALIASES[table];if(table==="tasks"){let rows=projectId?[...await sql.unsafe("SELECT * FROM tasks WHERE project_id = $1",[projectId])]:[...await sql`SELECT * FROM tasks`];return stripEphemeralFields(rows)}if(alias&&projectId)return[...await sql.unsafe(`SELECT ${alias}.* FROM ${table} ${alias} JOIN tasks t ON ${alias}.task_id = t.id WHERE t.project_id = $1`,[projectId])];return[...await sql.unsafe(`SELECT * FROM ${table}`)]}async function exportTasks(sql,projectName){let tables=GROUP_TABLES.tasks,{available,skipped}=await detectTables(sql,tables),data={},projectId=projectName?await resolveProjectId2(sql,projectName):null;for(let table of available)data[table]=await exportTaskTable(sql,table,projectId);return{data,skipped}}async function exportSchedules(sql,name){let{available,skipped}=await detectTables(sql,["schedules"]),data={};if(available.includes("schedules"))if(name)data.schedules=[...await sql`SELECT * FROM schedules WHERE name = ${name}`];else data.schedules=[...await sql`SELECT * FROM schedules`];return{data,skipped}}async function exportTags(sql){let{available,skipped}=await detectTables(sql,["tags"]),data={};if(available.includes("tags"))data.tags=[...await sql`SELECT * FROM tags WHERE name NOT LIKE 'test-%'`];return{data,skipped}}async function exportAll(sql){let allSkipped=[],allData={};for(let group of ALL_GROUPS){let result2;switch(group){case"boards":result2=await exportBoards(sql);break;case"tasks":result2=await exportTasks(sql);break;case"tags":result2=await exportTags(sql);break;case"schedules":result2=await exportSchedules(sql);break;default:result2=await exportGroup(sql,group);break}Object.assign(allData,result2.data),allSkipped.push(...result2.skipped)}return{data:allData,skipped:allSkipped}}async function runExport(groups,type2,exportFn,options){let sql=await getSql(),[version,actor]=await Promise.all([getVersion(),getActorName()]),doc=createExportDocument(type2,groups,version,actor),{data,skipped}=await exportFn(sql);doc.data=data,doc.skippedTables=skipped,outputDocument(doc,options)}function registerExportCommands(program2){let exp=program2.command("export").description("Export genie data as JSON").option("--output <file>","Write to file instead of stdout").option("-o <file>","Alias for --output").option("--pretty","Pretty-print JSON").action(async(options)=>{try{if(!options.output)options.output=autoOutputName();await runExport([...ALL_GROUPS],"full",(sql)=>exportAll(sql),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts=(cmd)=>cmd.option("--output <file>","Write to file instead of stdout").option("--pretty","Pretty-print JSON");sharedOpts(exp.command("all").description("Full backup (all present tables)")).action(async(options)=>{try{if(!options.output)options.output=autoOutputName();await runExport([...ALL_GROUPS],"full",(sql)=>exportAll(sql),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("boards [name]").description("Export boards, templates, and task types")).action(async(name,options)=>{try{await runExport(["boards"],"partial",(sql)=>exportBoards(sql,name),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("tasks").description("Export tasks with deps, actors, and stage log").option("--project <name>","Filter by project name")).action(async(options)=>{try{await runExport(["tasks"],"partial",(sql)=>exportTasks(sql,options.project),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("tags").description("Export tags")).action(async(options)=>{try{await runExport(["tags"],"partial",(sql)=>exportTags(sql),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("projects").description("Export projects")).action(async(options)=>{try{await runExport(["projects"],"partial",(sql)=>exportGroup(sql,"projects"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("schedules [name]").description("Export schedules with run_spec")).action(async(name,options)=>{try{await runExport(["schedules"],"partial",(sql)=>exportSchedules(sql,name),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("agents").description("Export agents, templates, and checkpoints")).action(async(options)=>{try{await runExport(["agents"],"partial",(sql)=>exportGroup(sql,"agents"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("comms").description("Export conversations, messages, mailbox")).action(async(options)=>{try{await runExport(["comms"],"partial",(sql)=>exportGroup(sql,"comms"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("config").description("Export OS config (graceful skip if missing)")).action(async(options)=>{try{await runExport(["config"],"partial",(sql)=>exportGroup(sql,"config"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_history();init_export_format();import{readFileSync as readFileSync31}from"fs";var IMPORT_LEVELS=[["schedules","sessions","projects","agent_templates","agent_checkpoints","tags","task_types","notification_preferences","os_config","golden_images","warm_pool","instances"],["triggers","boards","board_templates","agents","conversations"],["tasks","runs","messages","conversation_members","mailbox","team_chat"],["task_tags","task_actors","task_dependencies","task_stage_log","heartbeats","machine_snapshots"]],SELF_REFERENTIAL_COLUMNS={tasks:"parent_id",messages:"reply_to_id",conversations:"parent_message_id"};function getTableLevel(table){for(let i2=0;i2<IMPORT_LEVELS.length;i2++)if(IMPORT_LEVELS[i2].includes(table))return i2;return-1}function sortByImportOrder(tables){return[...tables].sort((a,b2)=>{let la=getTableLevel(a),lb=getTableLevel(b2);return(la===-1?999:la)-(lb===-1?999:lb)})}function getPrimaryKey(table){return{task_tags:["task_id","tag_id"],task_actors:["task_id","actor_type","actor_id","role"],task_dependencies:["task_id","depends_on_id"],conversation_members:["conversation_id","actor_type","actor_id"],notification_preferences:["actor_type","actor_id","channel"]}[table]??["id"]}var VALID_TABLES=new Set(Object.values(GROUP_TABLES).flat());function assertValidTable(name){if(!VALID_TABLES.has(name))throw Error(`Invalid table name: "${name}" is not in the schema whitelist`)}var VALID_COLUMN_RE=/^[a-zA-Z_][a-zA-Z0-9_]*$/;function assertValidColumnName(name){if(!VALID_COLUMN_RE.test(name))throw Error(`Invalid column name: "${name.slice(0,60)}" contains disallowed characters. Column names must match /^[a-zA-Z_][a-zA-Z0-9_]*$/.`)}async function getSql2(){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return getConnection2()}async function getActorName2(){let{getActor:getActor2}=await Promise.resolve().then(() => (init_audit(),exports_audit));return getActor2()}async function detectTables2(sql,tables){let{filterAvailableTables:filterAvailableTables2}=await Promise.resolve().then(() => exports_table_detect);return filterAvailableTables2(sql,tables)}async function detectConflicts(sql,table,rows){if(rows.length===0)return[];assertValidTable(table);let pk=getPrimaryKey(table);if(pk.length===1){let key=pk[0],ids=rows.map((r)=>r[key]),existing=await sql.unsafe(`SELECT ${key} FROM ${table} WHERE ${key} = ANY($1)`,[ids]),existingSet=new Set(existing.map((r)=>String(r[key])));return rows.filter((r)=>existingSet.has(String(r[key])))}let conflicts=[];for(let row of rows){let conditions=pk.map((col,i2)=>`${col} = $${i2+1}`).join(" AND "),values2=pk.map((col)=>row[col]);if((await sql.unsafe(`SELECT 1 FROM ${table} WHERE ${conditions} LIMIT 1`,values2)).length>0)conflicts.push(row)}return conflicts}function prepareRow(row,table,selfRefUpdates){let selfRefCol=SELF_REFERENTIAL_COLUMNS[table],entries=Object.entries(row),columns=entries.map(([k])=>k),values2=entries.map(([,v])=>v);for(let col of columns)assertValidColumnName(col);if(selfRefCol&&row[selfRefCol]!=null){let idx=columns.indexOf(selfRefCol);if(idx!==-1){let originalSelfRef=values2[idx];values2[idx]=null;let pk=getPrimaryKey(table);selfRefUpdates.push({pk:pk.length===1?row[pk[0]]:pk.map((k)=>row[k]),value:originalSelfRef})}}return{columns,values:values2,quotedCols:columns.map((c)=>`"${c}"`).join(", "),placeholders:values2.map((_,i2)=>`$${i2+1}`).join(", ")}}async function insertOneRow(tx,table,row,prepared,mode){assertValidTable(table);let{quotedCols,placeholders,values:values2}=prepared,pk=getPrimaryKey(table);if(mode==="overwrite"){let pkCondition=pk.map((col,i2)=>`"${col}" = $${values2.length+i2+1}`).join(" AND "),pkValues=pk.map((col)=>row[col]);await tx.unsafe(`DELETE FROM ${table} WHERE ${pkCondition}`,pkValues),await tx.unsafe(`INSERT INTO ${table} (${quotedCols}) VALUES (${placeholders})`,values2)}else if(mode==="merge"){let onConflict=pk.map((c)=>`"${c}"`).join(", ");await tx.unsafe(`INSERT INTO ${table} (${quotedCols}) VALUES (${placeholders}) ON CONFLICT (${onConflict}) DO NOTHING`,values2)}else await tx.unsafe(`INSERT INTO ${table} (${quotedCols}) VALUES (${placeholders})`,values2)}async function updateSelfRefs(tx,table,updates){assertValidTable(table);let selfRefCol=SELF_REFERENTIAL_COLUMNS[table],pk=getPrimaryKey(table);if(pk.length!==1)return;for(let{pk:pkVal,value}of updates)await tx.unsafe(`UPDATE ${table} SET "${selfRefCol}" = $1 WHERE "${pk[0]}" = $2`,[value,pkVal])}async function insertRows(tx,table,rows,mode){if(rows.length===0)return 0;let selfRefUpdates=[];for(let row of rows){let prepared=prepareRow(row,table,selfRefUpdates);await insertOneRow(tx,table,row,prepared,mode)}if(selfRefUpdates.length>0)await updateSelfRefs(tx,table,selfRefUpdates);return rows.length}function parseExportFile(filePath){let raw=readFileSync31(filePath,"utf-8"),parsed;try{parsed=JSON.parse(raw)}catch{throw Error(`Invalid JSON in ${filePath}`)}let validation=validateExportDocument(parsed);if(!validation.valid)throw Error(`Invalid export document: ${validation.error}`);return validation.doc}async function filterTablesByGroup(allTables,groupFilter){if(!groupFilter||groupFilter.length===0)return allTables;let{GROUP_TABLES:GROUP_TABLES2}=await Promise.resolve().then(() => (init_export_format(),exports_export_format)),allowedTables=new Set;for(let group of groupFilter){let tables=GROUP_TABLES2[group];if(tables)for(let t of tables)allowedTables.add(t);else console.warn(`Warning: Unknown group "${group}", skipping`)}return allTables.filter((t)=>allowedTables.has(t))}async function checkConflicts(sql,tables,data){for(let table of tables){let rows=data[table];if(!rows||rows.length===0)continue;let conflicts=await detectConflicts(sql,table,rows);if(conflicts.length>0){let pk=getPrimaryKey(table),ids=conflicts.slice(0,5).map((r)=>pk.map((k)=>r[k]).join(",")).join("; ");throw Error(`Conflict in table "${table}": ${conflicts.length} existing row(s) (e.g., ${ids}). Use --merge or --overwrite to resolve.`)}}}async function runImport(filePath,mode,groupFilter){let doc=parseExportFile(filePath),tablesToImport=await filterTablesByGroup(Object.keys(doc.data),groupFilter);if(tablesToImport.length===0){console.log("No tables to import.");return}tablesToImport=sortByImportOrder(tablesToImport);let sql=await getSql2(),{available}=await detectTables2(sql,tablesToImport),skippedTables=tablesToImport.filter((t)=>!available.includes(t));if(tablesToImport=available,skippedTables.length>0)console.log(`Skipping tables not in database: ${skippedTables.join(", ")}`);if(mode==="fail")await checkConflicts(sql,tablesToImport,doc.data);let totalInserted=0,tableStats={};await sql.begin(async(tx)=>{for(let table of tablesToImport){let rows=doc.data[table];if(!rows||rows.length===0)continue;let count=await insertRows(tx,table,rows,mode);tableStats[table]=count,totalInserted+=count}});let actor=await getActorName2(),{recordAuditEvent:recordAuditEvent3}=await Promise.resolve().then(() => (init_audit(),exports_audit));await recordAuditEvent3("import",filePath,"import_complete",actor,{mode,tables:tableStats,totalRows:totalInserted,skippedTables,sourceVersion:doc.version,sourceDate:doc.exportedAt}),console.log(`Import complete: ${totalInserted} rows across ${Object.keys(tableStats).length} tables`);for(let[table,count]of Object.entries(tableStats))if(count>0)console.log(` ${table}: ${count} rows`);if(skippedTables.length>0)console.log(`Skipped (not in DB): ${skippedTables.join(", ")}`)}function registerImportCommands(program2){program2.command("import <file>").description("Import genie data from JSON export").option("--fail","Abort on any conflict (default)").option("--merge","Skip existing rows, import new ones").option("--overwrite","Replace existing rows with imported data").option("--groups <list>","Comma-separated groups to import (e.g., boards,tags)").action(async(file,options)=>{try{let mode="fail";if(options.overwrite)mode="overwrite";else if(options.merge)mode="merge";let groupFilter=options.groups?.split(",").map((g)=>g.trim());await runImport(file,mode,groupFilter)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_esm14();import{existsSync as existsSync53,mkdirSync as mkdirSync23,symlinkSync,writeFileSync as writeFileSync25}from"fs";import{basename as basename10,join as join60,relative as relative4,resolve as resolve9,sep as sep2}from"path";import{cpSync,existsSync as existsSync51,mkdirSync as mkdirSync21,renameSync as renameSync5,rmSync as rmSync4}from"fs";import{join as join57,relative as relative3}from"path";var import_ignore=__toESM(require_ignore(),1);import{existsSync as existsSync50,readFileSync as readFileSync32,readdirSync as readdirSync10,statSync as statSync6}from"fs";import{join as join56,relative as relative2}from"path";var GENIEIGNORE_DEFAULTS=`node_modules
3942
+ `);let tables=Object.keys(doc.data),rows=Object.values(doc.data).reduce((sum,arr)=>sum+arr.length,0);if(console.log(`Exported ${tables.length} tables (${rows} rows) to ${options.output}`),doc.skippedTables.length>0)console.log(`Skipped tables (not found): ${doc.skippedTables.join(", ")}`)}else console.log(json2)}function autoOutputName(){let d=new Date;return`genie-backup-${`${d.getFullYear()}${String(d.getMonth()+1).padStart(2,"0")}${String(d.getDate()).padStart(2,"0")}`}.json`}async function exportGroup(sql,group,filter){let tables=GROUP_TABLES[group],{available,skipped}=await detectTables(sql,tables),data={};for(let table of available)if(filter)data[table]=[...await sql.unsafe(`SELECT * FROM ${table} WHERE ${filter.column} = $1`,[filter.value])];else data[table]=[...await sql.unsafe(`SELECT * FROM ${table}`)];return{data,skipped}}async function exportBoards(sql,name){let tables=GROUP_TABLES.boards,{available,skipped}=await detectTables(sql,tables),data={};for(let table of available)if(name&&table==="boards")data[table]=[...await sql`SELECT * FROM boards WHERE name = ${name}`];else if(table==="task_types")data[table]=[...await sql`SELECT * FROM task_types WHERE is_builtin = false`];else data[table]=[...await sql.unsafe(`SELECT * FROM ${table}`)];return{data,skipped}}var TASK_JOIN_ALIASES={task_tags:"tt",task_actors:"ta",task_dependencies:"td",task_stage_log:"tsl"};async function resolveProjectId2(sql,projectName){let projects=await sql`SELECT id FROM projects WHERE name = ${projectName}`;if(projects.length===0)throw Error(`Project not found: ${projectName}`);return projects[0].id}function stripEphemeralFields(rows){return rows.map((r)=>{let{checkout_run_id,execution_locked_at,session_id,pane_id,...rest}=r;return rest})}async function exportTaskTable(sql,table,projectId){let alias=TASK_JOIN_ALIASES[table];if(table==="tasks"){let rows=projectId?[...await sql.unsafe("SELECT * FROM tasks WHERE project_id = $1",[projectId])]:[...await sql`SELECT * FROM tasks`];return stripEphemeralFields(rows)}if(alias&&projectId)return[...await sql.unsafe(`SELECT ${alias}.* FROM ${table} ${alias} JOIN tasks t ON ${alias}.task_id = t.id WHERE t.project_id = $1`,[projectId])];return[...await sql.unsafe(`SELECT * FROM ${table}`)]}async function exportTasks(sql,projectName){let tables=GROUP_TABLES.tasks,{available,skipped}=await detectTables(sql,tables),data={},projectId=projectName?await resolveProjectId2(sql,projectName):null;for(let table of available)data[table]=await exportTaskTable(sql,table,projectId);return{data,skipped}}async function exportSchedules(sql,name){let{available,skipped}=await detectTables(sql,["schedules"]),data={};if(available.includes("schedules"))if(name)data.schedules=[...await sql`SELECT * FROM schedules WHERE name = ${name}`];else data.schedules=[...await sql`SELECT * FROM schedules`];return{data,skipped}}async function exportTags(sql){let{available,skipped}=await detectTables(sql,["tags"]),data={};if(available.includes("tags"))data.tags=[...await sql`SELECT * FROM tags WHERE name NOT LIKE 'test-%'`];return{data,skipped}}async function exportAll(sql){let allSkipped=[],allData={};for(let group of ALL_GROUPS){let result2;switch(group){case"boards":result2=await exportBoards(sql);break;case"tasks":result2=await exportTasks(sql);break;case"tags":result2=await exportTags(sql);break;case"schedules":result2=await exportSchedules(sql);break;default:result2=await exportGroup(sql,group);break}Object.assign(allData,result2.data),allSkipped.push(...result2.skipped)}return{data:allData,skipped:allSkipped}}async function runExport(groups,type2,exportFn,options){let sql=await getSql(),[version,actor]=await Promise.all([getVersion(),getActorName()]),doc=createExportDocument(type2,groups,version,actor),{data,skipped}=await exportFn(sql);doc.data=data,doc.skippedTables=skipped,outputDocument(doc,options)}function registerExportCommands(program2){let exp=program2.command("export").description("Export genie data as JSON").option("--output <file>","Write to file instead of stdout").option("-o <file>","Alias for --output").option("--pretty","Pretty-print JSON").action(async(options)=>{try{if(!options.output)options.output=autoOutputName();await runExport([...ALL_GROUPS],"full",(sql)=>exportAll(sql),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts=(cmd)=>cmd.option("--output <file>","Write to file instead of stdout").option("--pretty","Pretty-print JSON");sharedOpts(exp.command("all").description("Full backup (all present tables)")).action(async(options)=>{try{if(!options.output)options.output=autoOutputName();await runExport([...ALL_GROUPS],"full",(sql)=>exportAll(sql),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("boards [name]").description("Export boards, templates, and task types")).action(async(name,options)=>{try{await runExport(["boards"],"partial",(sql)=>exportBoards(sql,name),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("tasks").description("Export tasks with deps, actors, and stage log").option("--project <name>","Filter by project name")).action(async(options)=>{try{await runExport(["tasks"],"partial",(sql)=>exportTasks(sql,options.project),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("tags").description("Export tags")).action(async(options)=>{try{await runExport(["tags"],"partial",(sql)=>exportTags(sql),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("projects").description("Export projects")).action(async(options)=>{try{await runExport(["projects"],"partial",(sql)=>exportGroup(sql,"projects"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("schedules [name]").description("Export schedules with run_spec")).action(async(name,options)=>{try{await runExport(["schedules"],"partial",(sql)=>exportSchedules(sql,name),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("agents").description("Export agents, templates, and checkpoints")).action(async(options)=>{try{await runExport(["agents"],"partial",(sql)=>exportGroup(sql,"agents"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("comms").description("Export conversations, messages, mailbox")).action(async(options)=>{try{await runExport(["comms"],"partial",(sql)=>exportGroup(sql,"comms"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}}),sharedOpts(exp.command("config").description("Export OS config (graceful skip if missing)")).action(async(options)=>{try{await runExport(["config"],"partial",(sql)=>exportGroup(sql,"config"),options)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_history();init_export_format();import{readFileSync as readFileSync31}from"fs";var IMPORT_LEVELS=[["schedules","sessions","projects","agent_templates","agent_checkpoints","tags","task_types","notification_preferences","os_config","golden_images","warm_pool","instances"],["triggers","boards","board_templates","agents","conversations"],["tasks","runs","messages","conversation_members","mailbox","team_chat"],["task_tags","task_actors","task_dependencies","task_stage_log","heartbeats","machine_snapshots"]],SELF_REFERENTIAL_COLUMNS={tasks:"parent_id",messages:"reply_to_id",conversations:"parent_message_id"};function getTableLevel(table){for(let i2=0;i2<IMPORT_LEVELS.length;i2++)if(IMPORT_LEVELS[i2].includes(table))return i2;return-1}function sortByImportOrder(tables){return[...tables].sort((a,b2)=>{let la=getTableLevel(a),lb=getTableLevel(b2);return(la===-1?999:la)-(lb===-1?999:lb)})}function getPrimaryKey(table){return{task_tags:["task_id","tag_id"],task_actors:["task_id","actor_type","actor_id","role"],task_dependencies:["task_id","depends_on_id"],conversation_members:["conversation_id","actor_type","actor_id"],notification_preferences:["actor_type","actor_id","channel"]}[table]??["id"]}var VALID_TABLES=new Set(Object.values(GROUP_TABLES).flat());function assertValidTable(name){if(!VALID_TABLES.has(name))throw Error(`Invalid table name: "${name}" is not in the schema whitelist`)}var VALID_COLUMN_RE=/^[a-zA-Z_][a-zA-Z0-9_]*$/;function assertValidColumnName(name){if(!VALID_COLUMN_RE.test(name))throw Error(`Invalid column name: "${name.slice(0,60)}" contains disallowed characters. Column names must match /^[a-zA-Z_][a-zA-Z0-9_]*$/.`)}async function getSql2(){let{getConnection:getConnection2}=await Promise.resolve().then(() => (init_db(),exports_db));return getConnection2()}async function getActorName2(){let{getActor:getActor2}=await Promise.resolve().then(() => (init_audit(),exports_audit));return getActor2()}async function detectTables2(sql,tables){let{filterAvailableTables:filterAvailableTables2}=await Promise.resolve().then(() => exports_table_detect);return filterAvailableTables2(sql,tables)}async function detectConflicts(sql,table,rows){if(rows.length===0)return[];assertValidTable(table);let pk=getPrimaryKey(table);if(pk.length===1){let key=pk[0],ids=rows.map((r)=>r[key]),existing=await sql.unsafe(`SELECT ${key} FROM ${table} WHERE ${key} = ANY($1)`,[ids]),existingSet=new Set(existing.map((r)=>String(r[key])));return rows.filter((r)=>existingSet.has(String(r[key])))}let conflicts=[];for(let row of rows){let conditions=pk.map((col,i2)=>`${col} = $${i2+1}`).join(" AND "),values2=pk.map((col)=>row[col]);if((await sql.unsafe(`SELECT 1 FROM ${table} WHERE ${conditions} LIMIT 1`,values2)).length>0)conflicts.push(row)}return conflicts}function prepareRow(row,table,selfRefUpdates){let selfRefCol=SELF_REFERENTIAL_COLUMNS[table],entries=Object.entries(row),columns=entries.map(([k])=>k),values2=entries.map(([,v])=>v);for(let col of columns)assertValidColumnName(col);if(selfRefCol&&row[selfRefCol]!=null){let idx=columns.indexOf(selfRefCol);if(idx!==-1){let originalSelfRef=values2[idx];values2[idx]=null;let pk=getPrimaryKey(table);selfRefUpdates.push({pk:pk.length===1?row[pk[0]]:pk.map((k)=>row[k]),value:originalSelfRef})}}return{columns,values:values2,quotedCols:columns.map((c)=>`"${c}"`).join(", "),placeholders:values2.map((_,i2)=>`$${i2+1}`).join(", ")}}async function insertOneRow(tx,table,row,prepared,mode){assertValidTable(table);let{quotedCols,placeholders,values:values2}=prepared,pk=getPrimaryKey(table);if(mode==="overwrite"){let pkCondition=pk.map((col,i2)=>`"${col}" = $${values2.length+i2+1}`).join(" AND "),pkValues=pk.map((col)=>row[col]);await tx.unsafe(`DELETE FROM ${table} WHERE ${pkCondition}`,pkValues),await tx.unsafe(`INSERT INTO ${table} (${quotedCols}) VALUES (${placeholders})`,values2)}else if(mode==="merge"){let onConflict=pk.map((c)=>`"${c}"`).join(", ");await tx.unsafe(`INSERT INTO ${table} (${quotedCols}) VALUES (${placeholders}) ON CONFLICT (${onConflict}) DO NOTHING`,values2)}else await tx.unsafe(`INSERT INTO ${table} (${quotedCols}) VALUES (${placeholders})`,values2)}async function updateSelfRefs(tx,table,updates){assertValidTable(table);let selfRefCol=SELF_REFERENTIAL_COLUMNS[table],pk=getPrimaryKey(table);if(pk.length!==1)return;for(let{pk:pkVal,value}of updates)await tx.unsafe(`UPDATE ${table} SET "${selfRefCol}" = $1 WHERE "${pk[0]}" = $2`,[value,pkVal])}async function insertRows(tx,table,rows,mode){if(rows.length===0)return 0;let selfRefUpdates=[];for(let row of rows){let prepared=prepareRow(row,table,selfRefUpdates);await insertOneRow(tx,table,row,prepared,mode)}if(selfRefUpdates.length>0)await updateSelfRefs(tx,table,selfRefUpdates);return rows.length}function parseExportFile(filePath){let raw=readFileSync31(filePath,"utf-8"),parsed;try{parsed=JSON.parse(raw)}catch{throw Error(`Invalid JSON in ${filePath}`)}let validation=validateExportDocument(parsed);if(!validation.valid)throw Error(`Invalid export document: ${validation.error}`);return validation.doc}async function filterTablesByGroup(allTables,groupFilter){if(!groupFilter||groupFilter.length===0)return allTables;let{GROUP_TABLES:GROUP_TABLES2}=await Promise.resolve().then(() => (init_export_format(),exports_export_format)),allowedTables=new Set;for(let group of groupFilter){let tables=GROUP_TABLES2[group];if(tables)for(let t of tables)allowedTables.add(t);else console.warn(`Warning: Unknown group "${group}", skipping`)}return allTables.filter((t)=>allowedTables.has(t))}async function checkConflicts(sql,tables,data){for(let table of tables){let rows=data[table];if(!rows||rows.length===0)continue;let conflicts=await detectConflicts(sql,table,rows);if(conflicts.length>0){let pk=getPrimaryKey(table),ids=conflicts.slice(0,5).map((r)=>pk.map((k)=>r[k]).join(",")).join("; ");throw Error(`Conflict in table "${table}": ${conflicts.length} existing row(s) (e.g., ${ids}). Use --merge or --overwrite to resolve.`)}}}async function runImport(filePath,mode,groupFilter){let doc=parseExportFile(filePath),tablesToImport=await filterTablesByGroup(Object.keys(doc.data),groupFilter);if(tablesToImport.length===0){console.log("No tables to import.");return}tablesToImport=sortByImportOrder(tablesToImport);let sql=await getSql2(),{available}=await detectTables2(sql,tablesToImport),skippedTables=tablesToImport.filter((t)=>!available.includes(t));if(tablesToImport=available,skippedTables.length>0)console.log(`Skipping tables not in database: ${skippedTables.join(", ")}`);if(mode==="fail")await checkConflicts(sql,tablesToImport,doc.data);let totalInserted=0,tableStats={};await sql.begin(async(tx)=>{for(let table of tablesToImport){let rows=doc.data[table];if(!rows||rows.length===0)continue;let count=await insertRows(tx,table,rows,mode);tableStats[table]=count,totalInserted+=count}});let actor=await getActorName2(),{recordAuditEvent:recordAuditEvent3}=await Promise.resolve().then(() => (init_audit(),exports_audit));await recordAuditEvent3("import",filePath,"import_complete",actor,{mode,tables:tableStats,totalRows:totalInserted,skippedTables,sourceVersion:doc.version,sourceDate:doc.exportedAt}),console.log(`Import complete: ${totalInserted} rows across ${Object.keys(tableStats).length} tables`);for(let[table,count]of Object.entries(tableStats))if(count>0)console.log(` ${table}: ${count} rows`);if(skippedTables.length>0)console.log(`Skipped (not in DB): ${skippedTables.join(", ")}`)}function registerImportCommands(program2){program2.command("import <file>").description("Import genie data from JSON export").option("--fail","Abort on any conflict (default)").option("--merge","Skip existing rows, import new ones").option("--overwrite","Replace existing rows with imported data").option("--groups <list>","Comma-separated groups to import (e.g., boards,tags)").action(async(file,options)=>{try{let mode="fail";if(options.overwrite)mode="overwrite";else if(options.merge)mode="merge";let groupFilter=options.groups?.split(",").map((g)=>g.trim());await runImport(file,mode,groupFilter)}catch(error2){console.error(`Error: ${error2 instanceof Error?error2.message:String(error2)}`),process.exit(1)}})}init_esm14();import{existsSync as existsSync53,mkdirSync as mkdirSync23,symlinkSync,writeFileSync as writeFileSync25}from"fs";import{basename as basename10,join as join60,relative as relative4,resolve as resolve9,sep as sep2}from"path";import{cpSync,existsSync as existsSync51,mkdirSync as mkdirSync21,renameSync as renameSync5,rmSync as rmSync4}from"fs";import{join as join57,relative as relative3}from"path";var import_ignore=__toESM(require_ignore(),1);import{existsSync as existsSync50,readFileSync as readFileSync32,readdirSync as readdirSync10,statSync as statSync7}from"fs";import{join as join56,relative as relative2}from"path";var GENIEIGNORE_DEFAULTS=`node_modules
3913
3943
  .git
3914
3944
  .genie/worktrees
3915
3945
  dist
@@ -3922,7 +3952,7 @@ __pycache__
3922
3952
  target
3923
3953
  coverage
3924
3954
  .cache
3925
- `;function loadIgnoreRules(ignoreFilePath){let ig=import_ignore.default();if(existsSync50(ignoreFilePath)){let content=readFileSync32(ignoreFilePath,"utf-8");ig.add(content)}return ig}async function*scanForAgents(root,ignoreFilePath){let ig=loadIgnoreRules(ignoreFilePath??join56(root,".genieignore"));ig.add("agents"),yield*walkDir(root,root,ig)}function*walkDir(dir,root,ig){let names;try{names=readdirSync10(dir)}catch{return}for(let name of names){let fullPath=join56(dir,name);try{if(!statSync6(fullPath).isDirectory())continue}catch{continue}let relPath=`${relative2(root,fullPath)}/`;if(ig.ignores(relPath))continue;let agentsMdPath=join56(fullPath,"AGENTS.md");if(existsSync50(agentsMdPath)){let hasSubAgents=hasSubAgentDirs(fullPath);if(yield{path:fullPath,dirName:name,hasSubAgents,isSubAgent:!1},hasSubAgents)yield*scanSubAgents2(fullPath,name)}yield*walkDir(fullPath,root,ig)}}function hasSubAgentDirs(agentDir){let subAgentsDir=join56(agentDir,".genie","agents");if(!existsSync50(subAgentsDir))return!1;try{return readdirSync10(subAgentsDir).some((name)=>{let subPath=join56(subAgentsDir,name);try{return statSync6(subPath).isDirectory()&&existsSync50(join56(subPath,"AGENTS.md"))}catch{return!1}})}catch{return!1}}function*scanSubAgents2(parentDir,parentName){let subAgentsDir=join56(parentDir,".genie","agents");if(!existsSync50(subAgentsDir))return;let names;try{names=readdirSync10(subAgentsDir)}catch{return}for(let name of names){let subDir=join56(subAgentsDir,name);try{if(!statSync6(subDir).isDirectory())continue}catch{continue}if(!existsSync50(join56(subDir,"AGENTS.md")))continue;yield{path:subDir,dirName:name,hasSubAgents:!1,isSubAgent:!0,parentName}}}async function scanForAgentsAll(root,ignoreFilePath){let results=[];for await(let agent of scanForAgents(root,ignoreFilePath))results.push(agent);return results}init_workspace();async function discoverExternalAgents(workspaceRoot){let allScanned=await scanForAgentsAll(workspaceRoot),canonicalNames=new Set(scanAgents(workspaceRoot)),agentsDir=join57(workspaceRoot,"agents"),external2=[];for(let scanned of allScanned){if(scanned.path.startsWith(agentsDir))continue;if(canonicalNames.has(scanned.dirName))continue;external2.push({name:scanned.dirName,path:scanned.path,relativePath:relative3(workspaceRoot,scanned.path),isSubAgent:scanned.isSubAgent,parentName:scanned.parentName})}return external2}function importAgents(workspaceRoot,agents){let agentsDir=join57(workspaceRoot,"agents");mkdirSync21(agentsDir,{recursive:!0});let result2={imported:[],skipped:[],errors:[]};for(let agent of agents){let destName=resolveUniqueName(agentsDir,agent.name),destPath=join57(agentsDir,destName);if(existsSync51(destPath)){result2.skipped.push(agent.name);continue}try{moveDirectory(agent.path,destPath),result2.imported.push(destName)}catch(err){result2.errors.push({name:agent.name,error:err instanceof Error?err.message:String(err)})}}return result2}function moveDirectory(src,dest){try{renameSync5(src,dest)}catch{cpSync(src,dest,{recursive:!0}),rmSync4(src,{recursive:!0,force:!0})}}function resolveUniqueName(agentsDir,name){if(!existsSync51(join57(agentsDir,name)))return name;let suffix=2;while(existsSync51(join57(agentsDir,`${name}-${suffix}`)))suffix++;return`${name}-${suffix}`}init_interactivity();init_defaults();import{readFileSync as readFileSync33,writeFileSync as writeFileSync23}from"fs";import{join as join58}from"path";function formatDefaults(workspaceDefaults){let effective=computeEffectiveDefaults(workspaceDefaults),lines=[];for(let key of Object.keys(BUILTIN_DEFAULTS)){let value=effective[key],source=workspaceDefaults?.[key]!==void 0?"workspace":"built-in";lines.push(` ${key}: ${value} (${source})`)}return lines.join(`
3955
+ `;function loadIgnoreRules(ignoreFilePath){let ig=import_ignore.default();if(existsSync50(ignoreFilePath)){let content=readFileSync32(ignoreFilePath,"utf-8");ig.add(content)}return ig}async function*scanForAgents(root,ignoreFilePath){let ig=loadIgnoreRules(ignoreFilePath??join56(root,".genieignore"));ig.add("agents"),yield*walkDir(root,root,ig)}function*walkDir(dir,root,ig){let names;try{names=readdirSync10(dir)}catch{return}for(let name of names){let fullPath=join56(dir,name);try{if(!statSync7(fullPath).isDirectory())continue}catch{continue}let relPath=`${relative2(root,fullPath)}/`;if(ig.ignores(relPath))continue;let agentsMdPath=join56(fullPath,"AGENTS.md");if(existsSync50(agentsMdPath)){let hasSubAgents=hasSubAgentDirs(fullPath);if(yield{path:fullPath,dirName:name,hasSubAgents,isSubAgent:!1},hasSubAgents)yield*scanSubAgents2(fullPath,name)}yield*walkDir(fullPath,root,ig)}}function hasSubAgentDirs(agentDir){let subAgentsDir=join56(agentDir,".genie","agents");if(!existsSync50(subAgentsDir))return!1;try{return readdirSync10(subAgentsDir).some((name)=>{let subPath=join56(subAgentsDir,name);try{return statSync7(subPath).isDirectory()&&existsSync50(join56(subPath,"AGENTS.md"))}catch{return!1}})}catch{return!1}}function*scanSubAgents2(parentDir,parentName){let subAgentsDir=join56(parentDir,".genie","agents");if(!existsSync50(subAgentsDir))return;let names;try{names=readdirSync10(subAgentsDir)}catch{return}for(let name of names){let subDir=join56(subAgentsDir,name);try{if(!statSync7(subDir).isDirectory())continue}catch{continue}if(!existsSync50(join56(subDir,"AGENTS.md")))continue;yield{path:subDir,dirName:name,hasSubAgents:!1,isSubAgent:!0,parentName}}}async function scanForAgentsAll(root,ignoreFilePath){let results=[];for await(let agent of scanForAgents(root,ignoreFilePath))results.push(agent);return results}init_workspace();async function discoverExternalAgents(workspaceRoot){let allScanned=await scanForAgentsAll(workspaceRoot),canonicalNames=new Set(scanAgents(workspaceRoot)),agentsDir=join57(workspaceRoot,"agents"),external2=[];for(let scanned of allScanned){if(scanned.path.startsWith(agentsDir))continue;if(canonicalNames.has(scanned.dirName))continue;external2.push({name:scanned.dirName,path:scanned.path,relativePath:relative3(workspaceRoot,scanned.path),isSubAgent:scanned.isSubAgent,parentName:scanned.parentName})}return external2}function importAgents(workspaceRoot,agents){let agentsDir=join57(workspaceRoot,"agents");mkdirSync21(agentsDir,{recursive:!0});let result2={imported:[],skipped:[],errors:[]};for(let agent of agents){let destName=resolveUniqueName(agentsDir,agent.name),destPath=join57(agentsDir,destName);if(existsSync51(destPath)){result2.skipped.push(agent.name);continue}try{moveDirectory(agent.path,destPath),result2.imported.push(destName)}catch(err){result2.errors.push({name:agent.name,error:err instanceof Error?err.message:String(err)})}}return result2}function moveDirectory(src,dest){try{renameSync5(src,dest)}catch{cpSync(src,dest,{recursive:!0}),rmSync4(src,{recursive:!0,force:!0})}}function resolveUniqueName(agentsDir,name){if(!existsSync51(join57(agentsDir,name)))return name;let suffix=2;while(existsSync51(join57(agentsDir,`${name}-${suffix}`)))suffix++;return`${name}-${suffix}`}init_interactivity();init_defaults();import{readFileSync as readFileSync33,writeFileSync as writeFileSync23}from"fs";import{join as join58}from"path";function formatDefaults(workspaceDefaults){let effective=computeEffectiveDefaults(workspaceDefaults),lines=[];for(let key of Object.keys(BUILTIN_DEFAULTS)){let value=effective[key],source=workspaceDefaults?.[key]!==void 0?"workspace":"built-in";lines.push(` ${key}: ${value} (${source})`)}return lines.join(`
3926
3956
  `)}function formatWelcome(ctx){let lines=["",` Workspace: ${ctx.workspaceName}`,` Agents: ${ctx.canonicalAgentCount} registered`];if(ctx.discovered.length>0)lines.push(` Discovered: ${ctx.discovered.length} external agent(s) found`);return lines.push(""),lines.push(" Effective defaults:"),lines.push(formatDefaults(ctx.config.agents?.defaults)),lines.push(""),lines.join(`
3927
3957
  `)}function formatNextSteps(ctx){let lines=[""," Next steps:"];if(ctx.canonicalAgentCount===0)lines.push(" genie init agent <name> Scaffold your first agent");return lines.push(" genie spawn <agent> Launch an agent"),lines.push(" genie team create <name> Create a multi-agent team"),lines.push(" /wizard Full guided onboarding"),lines.push(""),lines.join(`
3928
3958
  `)}var MODEL_CHOICES=[{name:"opus (most capable)",value:"opus"},{name:"sonnet (balanced)",value:"sonnet"},{name:"haiku (fastest)",value:"haiku"}];async function runMiniWizard(ctx){let{confirm:confirm2}=await Promise.resolve().then(() => (init_esm14(),exports_esm));console.log(formatWelcome(ctx));let wantCustomize=await confirm2({message:"Customize workspace defaults?",default:!1}),result2={customized:!1,importedAgents:[],completed:!0};if(wantCustomize){let newDefaults=await customizeDefaults(ctx.config.agents?.defaults);if(newDefaults)result2.customized=!0,result2.defaults=newDefaults,persistDefaults(ctx.workspaceRoot,newDefaults)}if(ctx.pending.length>0){console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260421.28",
3
+ "version": "4.260421.30",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260421.28",
3
+ "version": "4.260421.30",
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.260421.28",
3
+ "version": "4.260421.30",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",