@automagik/genie 4.260427.5 → 4.260427.6
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
|
@@ -1666,11 +1666,11 @@ ${otherList}
|
|
|
1666
1666
|
`,await sql`
|
|
1667
1667
|
DELETE FROM task_actors WHERE task_id = ANY(${ids}) AND role = 'assignee'
|
|
1668
1668
|
`,ids.length}var GroupStatusSchema,GroupStateSchema,WishStateSchema,WishStateMismatchError;var init_wish_state=__esm(()=>{init_zod();init_db();GroupStatusSchema=exports_external.enum(["blocked","ready","in_progress","done"]),GroupStateSchema=exports_external.object({status:GroupStatusSchema,assignee:exports_external.string().optional(),dependsOn:exports_external.array(exports_external.string()).default([]),startedAt:exports_external.string().optional(),completedAt:exports_external.string().optional()}),WishStateSchema=exports_external.object({wish:exports_external.string(),groups:exports_external.record(exports_external.string(),GroupStateSchema),createdAt:exports_external.string(),updatedAt:exports_external.string()});WishStateMismatchError=class WishStateMismatchError extends Error{slug;added;removed;changed;constructor(slug,added,removed,changed){let lines=[`Wish "${slug}" group structure has changed since state was created.`];if(added.length>0)lines.push(` + added: ${added.join(", ")}`);if(removed.length>0)lines.push(` - removed: ${removed.join(", ")}`);if(changed.length>0)lines.push(` ~ changed deps: ${changed.join(", ")}`);lines.push(""),lines.push(`Run \`genie reset ${slug}\` to recreate state from the current WISH.md.`);super(lines.join(`
|
|
1669
|
-
`));this.name="WishStateMismatchError",this.slug=slug,this.added=added,this.removed=removed,this.changed=changed}}});var exports_protocol_router_spawn={};__export(exports_protocol_router_spawn,{spawnWorkerFromTemplate:()=>spawnWorkerFromTemplate,injectResumeContext:()=>injectResumeContext,_deps:()=>_deps2});import{exec as exec2}from"child_process";import{readFile as readFile9}from"fs/promises";import{join as join40}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let leaderName=await resolveLeaderName(team),sanitized=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitized).catch(()=>null);if(leaderAgent){let decision=await shouldResume(leaderAgent.id).catch(()=>null);if(decision?.sessionId)return decision.sessionId}return await discoverClaudeParentSessionId()??`genie-${team}`}function buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId){let isClaude=template.provider==="claude"||template.provider==="claude-sdk",sessionName=template.role?`${template.team}-${template.role}`:void 0,newSessionId=isClaude&&!resumeSessionId?crypto.randomUUID():void 0,params={provider:template.provider,team:template.team,role:template.role,skill:template.skill,extraArgs:template.extraArgs,sessionId:newSessionId,resume:isClaude?resumeSessionId:void 0,name:sessionName};if(isClaude)params.nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:template.role??"general-purpose",agentName:template.role};return params}function buildFullCommand(launch){if(launch.env&&Object.keys(launch.env).length>0)return`env ${Object.entries(launch.env).map(([k,v])=>`${k}=${v}`).join(" ")} ${launch.command}`;return launch.command}async function generateWorkerId(team,role){let base=role?`${team}-${role}`:team;return(await list()).some((w)=>w.id===base)?`${base}-${crypto.randomUUID().slice(0,8)}`:base}async function terminateActiveExecutorIfRunning(agentId){let currentExec=await getCurrentExecutor(agentId);if(!currentExec||currentExec.state==="terminated"||currentExec.state==="done")return;let providerImpl=getProvider(currentExec.provider);if(providerImpl)try{await providerImpl.terminate(currentExec)}catch{}await terminateActiveExecutor(agentId)}async function capturePanePid(paneId){try{let{stdout:pidOut}=await execAsync(genieTmuxCmd(`display -t '${paneId}' -p '#{pane_pid}'`)),parsed=Number.parseInt(pidOut.trim(),10);return parsed>0?parsed:null}catch{return null}}function resolveExecutorTransport(provider){if(provider==="codex")return"api";if(provider==="claude-sdk")return"process";return"tmux"}async function createExecutorForAutoSpawn(template,paneId,session,repoPath,effectiveSessionId,spawnColor,teamWindow){let agentName=template.role??"worker",agentIdentity=await findOrCreateAgent(agentName,template.team,template.role);await(await getConnection()).begin(async(tx)=>{await tx`SELECT pg_advisory_xact_lock(hashtext(${agentIdentity.id}))`,await terminateActiveExecutorIfRunning(agentIdentity.id);let pid=await capturePanePid(paneId),executor=await createExecutor(agentIdentity.id,template.provider,resolveExecutorTransport(template.provider),{pid,tmuxSession:session,tmuxPaneId:paneId,tmuxWindow:teamWindow?.windowName??null,tmuxWindowId:teamWindow?.windowId??null,claudeSessionId:effectiveSessionId??null,state:"spawning",repoPath,paneColor:spawnColor??null});await setCurrentExecutor(agentIdentity.id,executor.id)})}async function spawnPaneInSession(session,team,repoPath,fullCommand){let teamWindow=null;try{teamWindow=await ensureTeamWindow(session,team,repoPath)}catch{}let splitTarget=teamWindow?`-t '${teamWindow.windowId}'`:"",escapedCmd=fullCommand.replace(/'/g,"'\\''"),{stdout}=await execAsync(genieTmuxCmd(`split-window -d ${splitTarget} -P -F '#{pane_id}' '${escapedCmd}'`)),paneId=stdout.trim(),layoutTarget=`${session}:${teamWindow?.windowName??""}`;if(!teamWindow){let wins=await listWindows(session);layoutTarget=wins[0]?wins[0].id:`${session}:`}try{await execAsync(genieTmuxCmd(buildLayoutCommand(layoutTarget,resolveLayoutMode())))}catch{}return{paneId,teamWindow}}async function registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId){let now=new Date().toISOString();await registerNativeMember(team,{agentName,agentType:template.role??"general-purpose",color:spawnColor??"blue",tmuxPaneId:paneId,cwd:repoPath});let leaderInboxTarget;try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));leaderInboxTarget=await resolveLeaderName2(team)}catch{leaderInboxTarget=team}try{await _deps2.writeNativeInbox(team,leaderInboxTarget,{from:agentName,text:`Worker ${agentName} (${template.provider}) auto-spawned${resumeSessionId?" with --resume":""}. Ready for tasks.`,summary:`${agentName} auto-spawned`,timestamp:now,color:spawnColor??"blue",read:!1})}catch(err){let msg=err instanceof Error?err.message:String(err);console.warn(`[protocol-router] Native inbox write failed for team="${team}" target="${leaderInboxTarget}": ${msg}`)}}async function tryAutoBrain(workerId,repoPath){try{let brain=await import("@khal-os/brain");if(brain.autoBrain)await brain.autoBrain({agentId:workerId,workdir:repoPath})}catch{}}async function spawnWorkerFromTemplate(template,resumeSessionId){let repoPath=template.cwd??process.cwd(),team=template.team,parentSessionId=await resolveParentSession(repoPath,team);await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=await assignColor(team),params=buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId),launch=buildLaunchCommand(validateSpawnParams(params)),fullCommand=buildFullCommand(launch),workerId=await generateWorkerId(team,template.role),teamConfig=await getTeam(team),session=(Boolean(process.env.TMUX)?await getCurrentSessionName():null)??teamConfig?.tmuxSessionName??team,{paneId,teamWindow}=await spawnPaneInSession(session,team,repoPath,fullCommand),now=new Date().toISOString(),agentName=template.role??"worker",isClaude=template.provider==="claude"||template.provider==="claude-sdk",effectiveSessionId=resumeSessionId??params.sessionId,workerEntry={id:workerId,paneId,session,provider:template.provider,transport:"tmux",role:template.role,skill:template.skill,team,worktree:null,startedAt:now,state:"spawning",lastStateChange:now,repoPath,nativeTeamEnabled:isClaude,nativeAgentId:`${agentName}@${team}`,nativeColor:spawnColor,parentSessionId,window:teamWindow?.windowName,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId};await register(workerEntry);try{await createExecutorForAutoSpawn(template,paneId,session,repoPath,effectiveSessionId,spawnColor,teamWindow)}catch{}if(isClaude)await registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId);if(spawnColor)await applyPaneColor(paneId,spawnColor,teamWindow?.windowId);return await injectResumeContext(repoPath,workerId,agentName,team),await tryAutoBrain(workerId,repoPath),{worker:workerEntry,paneId,workerId}}function extractGroupSection(content,groupName){let escaped=groupName.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),pattern=new RegExp(`^### Group ${escaped}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}async function getRecentGitLog(repoPath,count=3){try{let{stdout}=await execAsync(`git -C '${repoPath}' log --oneline -${count} 2>/dev/null`);return stdout.trim()}catch{return""}}async function getGitStatus(repoPath){try{let{stdout}=await execAsync(`git -C '${repoPath}' status --short 2>/dev/null`);return stdout.trim()}catch{return""}}async function injectResumeContext(repoPath,workerId,agentName,_team){try{let match=await _deps2.findAnyGroupByAssignee(workerId,repoPath)??await _deps2.findAnyGroupByAssignee(agentName,repoPath);if(!match)return;let{slug,groupName,group}=match,wishPath=join40(repoPath,".genie","wishes",slug,"WISH.md"),groupSection="";try{let wishContent=await readFile9(wishPath,"utf-8");groupSection=extractGroupSection(wishContent,groupName)??""}catch{}let gitLog=await getRecentGitLog(repoPath),gitStatus=await getGitStatus(repoPath),resumePrompt=[`RESUME CONTEXT: You were working on wish "${slug}", group "${groupName}".`,`Status: ${group.status}. Started at: ${group.startedAt??"unknown"}.`,`Wish file: .genie/wishes/${slug}/WISH.md`,"",groupSection?`Group section:
|
|
1669
|
+
`));this.name="WishStateMismatchError",this.slug=slug,this.added=added,this.removed=removed,this.changed=changed}}});var exports_protocol_router_spawn={};__export(exports_protocol_router_spawn,{spawnWorkerFromTemplate:()=>spawnWorkerFromTemplate,injectResumeContext:()=>injectResumeContext,_deps:()=>_deps2});import{exec as exec2}from"child_process";import{readFile as readFile9}from"fs/promises";import{join as join40}from"path";import{promisify as promisify2}from"util";async function resolveParentSession(_repoPath,team){let leaderName=await resolveLeaderName(team),sanitized=sanitizeTeamName(team),leaderAgent=await getAgentByName(leaderName,sanitized).catch(()=>null);if(leaderAgent){let decision=await shouldResume(leaderAgent.id).catch(()=>null);if(decision?.sessionId)return decision.sessionId}return await discoverClaudeParentSessionId()??`genie-${team}`}function buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId){let isClaude=template.provider==="claude"||template.provider==="claude-sdk",sessionName=template.role?`${template.team}-${template.role}`:void 0,newSessionId=isClaude&&!resumeSessionId?crypto.randomUUID():void 0,params={provider:template.provider,team:template.team,role:template.role,skill:template.skill,extraArgs:template.extraArgs,sessionId:newSessionId,resume:isClaude?resumeSessionId:void 0,name:sessionName};if(isClaude)params.nativeTeam={enabled:!0,parentSessionId,color:spawnColor,agentType:template.role??"general-purpose",agentName:template.role};return params}function buildFullCommand(launch){if(launch.env&&Object.keys(launch.env).length>0)return`env ${Object.entries(launch.env).map(([k,v])=>`${k}=${v}`).join(" ")} ${launch.command}`;return launch.command}async function generateWorkerId(team,role){let base=role?`${team}-${role}`:team;return(await list()).some((w)=>w.id===base)?`${base}-${crypto.randomUUID().slice(0,8)}`:base}async function terminateActiveExecutorIfRunning(agentId){let currentExec=await getCurrentExecutor(agentId);if(!currentExec||currentExec.state==="terminated"||currentExec.state==="done")return;let liveStates=new Set(["running","idle","working","permission","question"]);if(currentExec.tmuxPaneId&&liveStates.has(currentExec.state)&&await isPaneAlive(currentExec.tmuxPaneId)){recordAuditEvent("executor",currentExec.id,"tried_to_kill_live_executor",process.env.GENIE_AGENT_NAME??"cli",{agent_id:agentId,state:currentExec.state,pane_id:currentExec.tmuxPaneId,reason:"live_pane_guard"}).catch(()=>{});return}let providerImpl=getProvider(currentExec.provider);if(providerImpl)try{await providerImpl.terminate(currentExec)}catch{}await terminateActiveExecutor(agentId)}async function capturePanePid(paneId){try{let{stdout:pidOut}=await execAsync(genieTmuxCmd(`display -t '${paneId}' -p '#{pane_pid}'`)),parsed=Number.parseInt(pidOut.trim(),10);return parsed>0?parsed:null}catch{return null}}function resolveExecutorTransport(provider){if(provider==="codex")return"api";if(provider==="claude-sdk")return"process";return"tmux"}async function createExecutorForAutoSpawn(template,paneId,session,repoPath,effectiveSessionId,spawnColor,teamWindow){let agentName=template.role??"worker",agentIdentity=await findOrCreateAgent(agentName,template.team,template.role);await(await getConnection()).begin(async(tx)=>{await tx`SELECT pg_advisory_xact_lock(hashtext(${agentIdentity.id}))`,await terminateActiveExecutorIfRunning(agentIdentity.id);let pid=await capturePanePid(paneId),executor=await createExecutor(agentIdentity.id,template.provider,resolveExecutorTransport(template.provider),{pid,tmuxSession:session,tmuxPaneId:paneId,tmuxWindow:teamWindow?.windowName??null,tmuxWindowId:teamWindow?.windowId??null,claudeSessionId:effectiveSessionId??null,state:"spawning",repoPath,paneColor:spawnColor??null});await setCurrentExecutor(agentIdentity.id,executor.id)})}async function spawnPaneInSession(session,team,repoPath,fullCommand){let teamWindow=null;try{teamWindow=await ensureTeamWindow(session,team,repoPath)}catch{}let splitTarget=teamWindow?`-t '${teamWindow.windowId}'`:"",escapedCmd=fullCommand.replace(/'/g,"'\\''"),{stdout}=await execAsync(genieTmuxCmd(`split-window -d ${splitTarget} -P -F '#{pane_id}' '${escapedCmd}'`)),paneId=stdout.trim(),layoutTarget=`${session}:${teamWindow?.windowName??""}`;if(!teamWindow){let wins=await listWindows(session);layoutTarget=wins[0]?wins[0].id:`${session}:`}try{await execAsync(genieTmuxCmd(buildLayoutCommand(layoutTarget,resolveLayoutMode())))}catch{}return{paneId,teamWindow}}async function registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId){let now=new Date().toISOString();await registerNativeMember(team,{agentName,agentType:template.role??"general-purpose",color:spawnColor??"blue",tmuxPaneId:paneId,cwd:repoPath});let leaderInboxTarget;try{let{resolveLeaderName:resolveLeaderName2}=await Promise.resolve().then(() => (init_team_manager(),exports_team_manager));leaderInboxTarget=await resolveLeaderName2(team)}catch{leaderInboxTarget=team}try{await _deps2.writeNativeInbox(team,leaderInboxTarget,{from:agentName,text:`Worker ${agentName} (${template.provider}) auto-spawned${resumeSessionId?" with --resume":""}. Ready for tasks.`,summary:`${agentName} auto-spawned`,timestamp:now,color:spawnColor??"blue",read:!1})}catch(err){let msg=err instanceof Error?err.message:String(err);console.warn(`[protocol-router] Native inbox write failed for team="${team}" target="${leaderInboxTarget}": ${msg}`)}}async function tryAutoBrain(workerId,repoPath){try{let brain=await import("@khal-os/brain");if(brain.autoBrain)await brain.autoBrain({agentId:workerId,workdir:repoPath})}catch{}}async function spawnWorkerFromTemplate(template,resumeSessionId){let repoPath=template.cwd??process.cwd(),team=template.team,parentSessionId=await resolveParentSession(repoPath,team);await ensureNativeTeam(team,`Genie team: ${team}`,parentSessionId);let spawnColor=await assignColor(team),params=buildSpawnParams(template,parentSessionId,spawnColor,resumeSessionId),launch=buildLaunchCommand(validateSpawnParams(params)),fullCommand=buildFullCommand(launch),workerId=await generateWorkerId(team,template.role),teamConfig=await getTeam(team),session=(Boolean(process.env.TMUX)?await getCurrentSessionName():null)??teamConfig?.tmuxSessionName??team,{paneId,teamWindow}=await spawnPaneInSession(session,team,repoPath,fullCommand),now=new Date().toISOString(),agentName=template.role??"worker",isClaude=template.provider==="claude"||template.provider==="claude-sdk",effectiveSessionId=resumeSessionId??params.sessionId,workerEntry={id:workerId,paneId,session,provider:template.provider,transport:"tmux",role:template.role,skill:template.skill,team,worktree:null,startedAt:now,state:"spawning",lastStateChange:now,repoPath,nativeTeamEnabled:isClaude,nativeAgentId:`${agentName}@${team}`,nativeColor:spawnColor,parentSessionId,window:teamWindow?.windowName,windowName:teamWindow?.windowName,windowId:teamWindow?.windowId};await register(workerEntry);try{await createExecutorForAutoSpawn(template,paneId,session,repoPath,effectiveSessionId,spawnColor,teamWindow)}catch{}if(isClaude)await registerNativeTeamMember(team,agentName,template,paneId,repoPath,spawnColor,resumeSessionId);if(spawnColor)await applyPaneColor(paneId,spawnColor,teamWindow?.windowId);return await injectResumeContext(repoPath,workerId,agentName,team),await tryAutoBrain(workerId,repoPath),{worker:workerEntry,paneId,workerId}}function extractGroupSection(content,groupName){let escaped=groupName.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),pattern=new RegExp(`^### Group ${escaped}:`,"m"),match=content.match(pattern);if(!match||match.index===void 0)return null;let start=match.index,nextBoundary=content.slice(start).slice(1).search(/^### Group \d|^---$/m),end=nextBoundary!==-1?start+1+nextBoundary:content.length;return content.slice(start,end).trim()}async function getRecentGitLog(repoPath,count=3){try{let{stdout}=await execAsync(`git -C '${repoPath}' log --oneline -${count} 2>/dev/null`);return stdout.trim()}catch{return""}}async function getGitStatus(repoPath){try{let{stdout}=await execAsync(`git -C '${repoPath}' status --short 2>/dev/null`);return stdout.trim()}catch{return""}}async function injectResumeContext(repoPath,workerId,agentName,_team){try{let match=await _deps2.findAnyGroupByAssignee(workerId,repoPath)??await _deps2.findAnyGroupByAssignee(agentName,repoPath);if(!match)return;let{slug,groupName,group}=match,wishPath=join40(repoPath,".genie","wishes",slug,"WISH.md"),groupSection="";try{let wishContent=await readFile9(wishPath,"utf-8");groupSection=extractGroupSection(wishContent,groupName)??""}catch{}let gitLog=await getRecentGitLog(repoPath),gitStatus=await getGitStatus(repoPath),resumePrompt=[`RESUME CONTEXT: You were working on wish "${slug}", group "${groupName}".`,`Status: ${group.status}. Started at: ${group.startedAt??"unknown"}.`,`Wish file: .genie/wishes/${slug}/WISH.md`,"",groupSection?`Group section:
|
|
1670
1670
|
${groupSection}`:"","",gitLog?`Last git log:
|
|
1671
1671
|
${gitLog}`:"","",gitStatus?`Uncommitted changes:
|
|
1672
1672
|
${gitStatus}`:"","","Pick up where you left off. Read the wish file for full context."].filter(Boolean).join(`
|
|
1673
|
-
`);await _deps2.mailboxSend(repoPath,"genie",workerId,resumePrompt)}catch(err){let msg=err instanceof Error?err.message:String(err);console.warn(`[protocol-router] Resume context injection failed: ${msg}`)}}var execAsync,_deps2;var init_protocol_router_spawn=__esm(()=>{init_agent_registry();init_claude_native_teams();init_db();init_executor_registry();init_mailbox();init_provider_adapters();init_registry2();init_should_resume();init_team_manager();init_tmux_wrapper();init_tmux();init_wish_state();execAsync=promisify2(exec2),_deps2={findAnyGroupByAssignee,mailboxSend:send,writeNativeInbox}});async function waitForAgentReady(paneId,opts){let timeoutMs=opts?.timeoutMs??(process.env.GENIE_SPAWN_TIMEOUT_MS?Number(process.env.GENIE_SPAWN_TIMEOUT_MS):DEFAULT_SPAWN_TIMEOUT_MS),pollIntervalMs=opts?.pollIntervalMs??READINESS_POLL_INTERVAL_MS,start=Date.now();while(Date.now()-start<timeoutMs){try{let content=await capturePaneContent(paneId,50);if(content){let state=detectState(content);if(state.type==="idle"||state.type==="tool_use")return{ready:!0,elapsedMs:Date.now()-start}}}catch{}await new Promise((r)=>setTimeout(r,pollIntervalMs))}return{ready:!1,elapsedMs:Date.now()-start}}async function waitForExecutorReady(executorId,opts){let timeoutMs=opts?.timeoutMs??DEFAULT_SPAWN_TIMEOUT_MS,start=Date.now();if(!await _pgDeps.isAvailable())return{ready:!1,elapsedMs:0};try{let executor=await _pgDeps.getExecutor(executorId);if(executor&&(executor.state==="running"||executor.state==="idle"))return{ready:!0,elapsedMs:Date.now()-start}}catch{return{ready:!1,elapsedMs:Date.now()-start}}let sql;try{sql=await _pgDeps.getConnection()}catch{return{ready:!1,elapsedMs:Date.now()-start}}return new Promise((resolve6)=>{let resolved=!1,listener=null,pollInterval=null,timeout=null,cleanup=async()=>{if(pollInterval)clearInterval(pollInterval);if(timeout)clearTimeout(timeout);if(listener)try{await listener.unlisten()}catch{}},finish=(ready)=>{if(resolved)return;resolved=!0,cleanup().then(()=>resolve6({ready,elapsedMs:Date.now()-start}))};timeout=setTimeout(()=>finish(!1),timeoutMs),sql.listen("genie_executor_state",(payload)=>{let parts=payload.split(":");if(parts.length<4)return;let[notifyExecId,,,newState]=parts;if(notifyExecId===executorId&&(newState==="running"||newState==="idle"))finish(!0)}).then((l)=>{if(listener=l,resolved)l.unlisten().catch(()=>{})}).catch(()=>{}),pollInterval=setInterval(async()=>{if(resolved)return;try{let executor=await _pgDeps.getExecutor(executorId);if(executor&&(executor.state==="running"||executor.state==="idle"))finish(!0)}catch{}},READINESS_POLL_INTERVAL_MS)})}var DEFAULT_SPAWN_TIMEOUT_MS=30000,READINESS_POLL_INTERVAL_MS=2000,_pgDeps;var init_spawn_command=__esm(()=>{init_db();init_executor_registry();init_orchestrator();init_tmux();_pgDeps={isAvailable,getConnection,getExecutor}});var exports_protocol_router={};__export(exports_protocol_router,{sendMessage:()=>sendMessage2,resolveResumeSessionId:()=>resolveResumeSessionId,getInbox:()=>getInbox,deliverToPane:()=>deliverToPane,_deps:()=>_deps3,MissingResumeSessionError:()=>MissingResumeSessionError});async function waitForWorkerReady(paneId,timeoutMs=AUTO_SPAWN_READY_TIMEOUT_MS){try{let executor=await findExecutorByPane(paneId);if(executor&&executor.state!=="terminated"&&executor.state!=="error"){if((await waitForExecutorReady(executor.id,{timeoutMs})).ready)return!0}}catch{}let start=Date.now();while(Date.now()-start<timeoutMs){try{let content=await capturePaneContent(paneId,30);if(detectState(content).type==="idle")return!0}catch{}await new Promise((r)=>setTimeout(r,AUTO_SPAWN_POLL_INTERVAL_MS))}return!1}async function isExecutorCompleted(worker){if(!worker?.currentExecutorId)return!1;let executor=await getCurrentExecutor(worker.id);return executor!=null&&(executor.state==="done"||executor.state==="terminated")}async function isExecutorResumable(worker){if(!worker.currentExecutorId)return!1;let executor=await getCurrentExecutor(worker.id);if(!executor)return!1;return!["done","error","terminated"].includes(executor.state)}async function isWorkerDead(w){if(w.currentExecutorId){let state=await getAgentEffectiveState(w.id);return state==="terminated"||state==="offline"}return w.state==="suspended"}async function resolveRecipient(recipientId){let allWorkers=await list(),byId=[],byRole=[],byTeamRole=[];for(let w of allWorkers){if(await isWorkerDead(w))continue;if(!await _deps3.isPaneAlive(w.paneId))continue;if(w.id===recipientId)byId.push(w);else if(w.role===recipientId)byRole.push(w);else if(`${w.team}:${w.role}`===recipientId)byTeamRole.push(w)}if(byId.length>0)return byId;if(byRole.length>0)return byRole;return byTeamRole}async function findLiveWorkerFuzzy(recipientId){let matches=await resolveRecipient(recipientId);return matches.length===1?matches[0]:null}async function findSpawnTemplate(worker,recipientId){let templates=await listTemplates(),candidates=[worker?.role,worker?.id,recipientId].filter((v)=>Boolean(v)),uniqueCandidates=[...new Set(candidates)],workerTeam=worker?.team;return templates.find((t)=>{if(workerTeam&&t.team!==workerTeam)return!1;return uniqueCandidates.some((q)=>t.id===q||t.role===q||`${t.team}:${t.role}`===q)})??null}async function lockedSpawnWorker(recipientId,worker,template,resumeSessionId){let sql=await getConnection(),workerTeam=worker?.team,lockResult=await sql.begin(async(tx)=>{await tx`SELECT pg_advisory_xact_lock(hashtext(${recipientId}))`;let postLockLive=await findLiveWorkerFuzzy(recipientId);if(postLockLive)return{type:"existing",worker:postLockLive};if(await cleanupDeadWorkers(recipientId,workerTeam),worker)await unregister(worker.id);let{spawnWorkerFromTemplate:spawnWorkerFromTemplate2}=await Promise.resolve().then(() => (init_protocol_router_spawn(),exports_protocol_router_spawn));return{type:"spawned",...await spawnWorkerFromTemplate2(template,resumeSessionId)}});if(lockResult.type==="existing")return{worker:lockResult.worker,respawned:!1};if(await saveTemplate({...template,lastSpawnedAt:new Date().toISOString()}),await(_deps3.waitForWorkerReady??waitForWorkerReady)(lockResult.paneId),!await _deps3.isPaneAlive(lockResult.paneId))return await unregister(lockResult.worker.id),null;return{worker:lockResult.worker,respawned:!0}}async function resolveResumeSessionId(worker,template,recipientId){if(template.provider!=="claude")return;let agentIdToProbe=worker?.id??`dir:${recipientId}`,decision=await shouldResume(agentIdToProbe);if(worker&&await isExecutorResumable(worker)){if(!decision.sessionId)throw new MissingResumeSessionError(worker.id,recipientId)}return decision.sessionId}async function handleSpawnError(err,worker,recipientId){if(err instanceof MissingResumeSessionError)throw err;let msg=err instanceof Error?err.message:String(err);if(console.error(`[protocol-router] Spawn failed for "${recipientId}": ${msg}`),worker)await update(worker.id,{state:"error"}).catch(()=>{});return null}async function ensureWorkerAlive(worker,recipientId){if(worker&&worker.state!=="suspended"&&await _deps3.isPaneAlive(worker.paneId))return{worker,respawned:!1};let live=await findLiveWorkerFuzzy(recipientId);if(live)return{worker:live,respawned:!1};if(await isExecutorCompleted(worker))return null;if(!process.env.TMUX)return null;let template=await findSpawnTemplate(worker,recipientId);if(!template)return null;let resumeSessionId=await resolveResumeSessionId(worker,template,recipientId);try{return await lockedSpawnWorker(recipientId,worker,template,resumeSessionId)}catch(err){return handleSpawnError(err,worker,recipientId)}}async function cleanupDeadWorkers(recipientId,team){let allWorkers=await list();for(let w of allWorkers){if(team&&w.team!==team)continue;if(!(w.role===recipientId||w.id===recipientId))continue;if(await _deps3.isPaneAlive(w.paneId))continue;await unregister(w.id)}}async function deliverToWorker(repoPath,from,worker,body){let message=await send(repoPath,from,worker.id,body),delivered=!1;if(worker.nativeTeamEnabled&&worker.team&&worker.role)delivered=await writeToNativeInbox(worker,message);else delivered=await injectToTmuxPane(worker,message);if(!delivered&&worker.team){let agentName=worker.role||worker.id.split("-").slice(-1)[0]||worker.id;try{let nativeMsg=toNativeInboxMessage(message,worker.nativeColor??"blue");await writeNativeInbox(worker.team,agentName,nativeMsg),delivered=!0}catch{}}if(delivered)await markDelivered(repoPath,worker.id,message.id);else console.error(`[protocol-router] Delivery failed: all paths exhausted (worker=${worker.id}, pane=${worker.paneId}, msg="${body.slice(0,50)}")`);return{messageId:message.id,workerId:worker.id,delivered}}async function deliverViaNativeInbox(repoPath,from,to,body,teamName){let resolvedTeam=teamName??await discoverTeamName();if(!resolvedTeam)return null;let config=await loadConfig(resolvedTeam).catch(()=>null);if(!config)return null;let sanitizedTo=sanitizeTeamName(to),matchedMember=config.members?.find((m)=>m.name===to||m.name===sanitizedTo||m.agentId===`${to}@${resolvedTeam}`||m.agentId===`${sanitizedTo}@${resolvedTeam}`);if(!matchedMember)return null;let inboxName=matchedMember.name??to;try{let message=await send(repoPath,from,to,body),nativeMsg={from,text:body,summary:body.length>50?`${body.substring(0,50)}...`:body,timestamp:new Date().toISOString(),color:"blue",read:!1};return await writeNativeInbox(resolvedTeam,inboxName,nativeMsg),await markDelivered(repoPath,to,message.id),{messageId:message.id,workerId:to,delivered:!0}}catch{return null}}async function deliverAfterPaneRecheck(repoPath,from,worker,body,paneDeadReason){if(!await _deps3.isPaneAlive(worker.paneId)){let message=await send(repoPath,from,worker.id,body);return console.error(`[protocol-router] Delivery failed: ${paneDeadReason} (worker=${worker.id}, msg="${body.slice(0,50)}")`),{messageId:message.id,workerId:worker.id,delivered:!1,reason:paneDeadReason}}return deliverToWorker(repoPath,from,worker,body)}async function findKnownWorker(to){let worker=await get(to);if(worker)return worker;let allWorkers=await list();return allWorkers.find((w)=>w.role===to&&w.state==="suspended")??allWorkers.find((w)=>w.role===to)??null}async function attemptAutoSpawnDelivery(repoPath,from,to,body,worker){let alive;try{alive=await ensureWorkerAlive(worker,to)}catch(err){if(err instanceof MissingResumeSessionError)return console.error(`[protocol-router] ${err.message}`),{messageId:"",workerId:worker?.id??to,delivered:!1,reason:err.message};throw err}if(!alive)return null;return deliverAfterPaneRecheck(repoPath,from,alive.worker,body,"pane dead after spawn")}async function sendMessage2(repoPath,from,to,body,teamName){if(from===to)return{messageId:"",workerId:to,delivered:!0,reason:"Self-delivery suppressed"};let liveMatches=await resolveRecipient(to);if(liveMatches.length===1)return deliverAfterPaneRecheck(repoPath,from,liveMatches[0],body,"Pane died before delivery");if(liveMatches.length>1)return{messageId:"",workerId:to,delivered:!1,reason:`Worker "${to}" is ambiguous. Found ${liveMatches.length} live matches: ${liveMatches.map((m)=>m.id).join(", ")}. Use exact worker ID.`};let{resolve:resolve6}=await Promise.resolve().then(() => (init_agent_directory(),exports_agent_directory)),dirResolved=await resolve6(to),worker=await findKnownWorker(to);if(dirResolved||worker){let result2=await attemptAutoSpawnDelivery(repoPath,from,to,body,worker);if(result2)return result2}let nativeResult=await deliverViaNativeInbox(repoPath,from,to,body,teamName);if(nativeResult)return nativeResult;return{messageId:"",workerId:to,delivered:!1,reason:`Worker "${to}" not found or not alive`}}async function writeToNativeInbox(worker,message){try{let nativeMsg=toNativeInboxMessage(message,worker.nativeColor??"blue"),agentName=worker.role??worker.id;return await writeNativeInbox(worker.team??"",agentName,nativeMsg),!0}catch{return!1}}async function injectToTmuxPane(worker,message){if(!worker.paneId)return!1;if(!/^%\d+$/.test(worker.paneId))return!1;if(!await _deps3.isPaneAlive(worker.paneId))return!1;try{let escaped=message.body.replace(/'/g,"'\\''");return await executeTmux2(`send-keys -t '${worker.paneId}' '${escaped}'`),await new Promise((resolve6)=>setTimeout(resolve6,200)),await executeTmux2(`send-keys -t '${worker.paneId}' Enter`),!0}catch{return!1}}async function deliverToPane(toWorker,messageId){let worker=await get(toWorker);if(!worker||!worker.paneId)return await markFailed(messageId),!1;if(!await _deps3.isPaneAlive(worker.paneId))return await markFailed(messageId),!1;let message=await getById(messageId);if(!message||message.deliveredAt)return!1;let injected=await injectToTmuxPane(worker,message);if(injected&&worker.repoPath)await markDelivered(worker.repoPath,worker.id,messageId);else await markFailed(messageId);return injected}async function getInbox(repoPath,workerId){return inbox(repoPath,workerId)}var MissingResumeSessionError,_deps3,AUTO_SPAWN_READY_TIMEOUT_MS=15000,AUTO_SPAWN_POLL_INTERVAL_MS=1000;var init_protocol_router=__esm(()=>{init_agent_registry();init_claude_native_teams();init_db();init_executor_registry();init_mailbox();init_orchestrator();init_should_resume();init_spawn_command();init_tmux();MissingResumeSessionError=class MissingResumeSessionError extends Error{workerId;entityId;recipientId;reason;constructor(workerId,recipientId,reason="null_session"){let suffix=recipientId?` (recipient "${recipientId}")`:"";super(`Cannot resume worker "${workerId}"${suffix}: executor has no claude_session_id recorded (reason: ${reason}). This usually means the worker predates the session-sync hook. Run \`genie reset ${workerId}\` or re-spawn the worker to recover.`);this.name="MissingResumeSessionError",this.workerId=workerId,this.entityId=workerId,this.recipientId=recipientId,this.reason=reason}};_deps3={isPaneAlive,waitForWorkerReady:null}});var exports_sdk_session_capture={};__export(exports_sdk_session_capture,{updateTurnCount:()=>updateTurnCount,startSession:()=>startSession,recordTurn:()=>recordTurn,endSession:()=>endSession});async function startSession(safePgCall,executorId,claudeSessionId,agentId,team,role,wishSlug){let sessionId=claudeSessionId??`sdk-${executorId}-${Date.now()}`;if(!await safePgCall("sdk-session-start",(sql)=>sql`INSERT INTO sessions (id, agent_id, executor_id, team, role, wish_slug, status, jsonl_path, project_path)
|
|
1673
|
+
`);await _deps2.mailboxSend(repoPath,"genie",workerId,resumePrompt)}catch(err){let msg=err instanceof Error?err.message:String(err);console.warn(`[protocol-router] Resume context injection failed: ${msg}`)}}var execAsync,_deps2;var init_protocol_router_spawn=__esm(()=>{init_agent_registry();init_audit();init_claude_native_teams();init_db();init_executor_registry();init_mailbox();init_provider_adapters();init_registry2();init_should_resume();init_team_manager();init_tmux_wrapper();init_tmux();init_wish_state();execAsync=promisify2(exec2),_deps2={findAnyGroupByAssignee,mailboxSend:send,writeNativeInbox}});async function waitForAgentReady(paneId,opts){let timeoutMs=opts?.timeoutMs??(process.env.GENIE_SPAWN_TIMEOUT_MS?Number(process.env.GENIE_SPAWN_TIMEOUT_MS):DEFAULT_SPAWN_TIMEOUT_MS),pollIntervalMs=opts?.pollIntervalMs??READINESS_POLL_INTERVAL_MS,start=Date.now();while(Date.now()-start<timeoutMs){try{let content=await capturePaneContent(paneId,50);if(content){let state=detectState(content);if(state.type==="idle"||state.type==="tool_use")return{ready:!0,elapsedMs:Date.now()-start}}}catch{}await new Promise((r)=>setTimeout(r,pollIntervalMs))}return{ready:!1,elapsedMs:Date.now()-start}}async function waitForExecutorReady(executorId,opts){let timeoutMs=opts?.timeoutMs??DEFAULT_SPAWN_TIMEOUT_MS,start=Date.now();if(!await _pgDeps.isAvailable())return{ready:!1,elapsedMs:0};try{let executor=await _pgDeps.getExecutor(executorId);if(executor&&(executor.state==="running"||executor.state==="idle"))return{ready:!0,elapsedMs:Date.now()-start}}catch{return{ready:!1,elapsedMs:Date.now()-start}}let sql;try{sql=await _pgDeps.getConnection()}catch{return{ready:!1,elapsedMs:Date.now()-start}}return new Promise((resolve6)=>{let resolved=!1,listener=null,pollInterval=null,timeout=null,cleanup=async()=>{if(pollInterval)clearInterval(pollInterval);if(timeout)clearTimeout(timeout);if(listener)try{await listener.unlisten()}catch{}},finish=(ready)=>{if(resolved)return;resolved=!0,cleanup().then(()=>resolve6({ready,elapsedMs:Date.now()-start}))};timeout=setTimeout(()=>finish(!1),timeoutMs),sql.listen("genie_executor_state",(payload)=>{let parts=payload.split(":");if(parts.length<4)return;let[notifyExecId,,,newState]=parts;if(notifyExecId===executorId&&(newState==="running"||newState==="idle"))finish(!0)}).then((l)=>{if(listener=l,resolved)l.unlisten().catch(()=>{})}).catch(()=>{}),pollInterval=setInterval(async()=>{if(resolved)return;try{let executor=await _pgDeps.getExecutor(executorId);if(executor&&(executor.state==="running"||executor.state==="idle"))finish(!0)}catch{}},READINESS_POLL_INTERVAL_MS)})}var DEFAULT_SPAWN_TIMEOUT_MS=30000,READINESS_POLL_INTERVAL_MS=2000,_pgDeps;var init_spawn_command=__esm(()=>{init_db();init_executor_registry();init_orchestrator();init_tmux();_pgDeps={isAvailable,getConnection,getExecutor}});var exports_protocol_router={};__export(exports_protocol_router,{sendMessage:()=>sendMessage2,resolveResumeSessionId:()=>resolveResumeSessionId,getInbox:()=>getInbox,deliverToPane:()=>deliverToPane,_deps:()=>_deps3,MissingResumeSessionError:()=>MissingResumeSessionError});async function waitForWorkerReady(paneId,timeoutMs=AUTO_SPAWN_READY_TIMEOUT_MS){try{let executor=await findExecutorByPane(paneId);if(executor&&executor.state!=="terminated"&&executor.state!=="error"){if((await waitForExecutorReady(executor.id,{timeoutMs})).ready)return!0}}catch{}let start=Date.now();while(Date.now()-start<timeoutMs){try{let content=await capturePaneContent(paneId,30);if(detectState(content).type==="idle")return!0}catch{}await new Promise((r)=>setTimeout(r,AUTO_SPAWN_POLL_INTERVAL_MS))}return!1}async function isExecutorCompleted(worker){if(!worker?.currentExecutorId)return!1;let executor=await getCurrentExecutor(worker.id);return executor!=null&&(executor.state==="done"||executor.state==="terminated")}async function isExecutorResumable(worker){if(!worker.currentExecutorId)return!1;let executor=await getCurrentExecutor(worker.id);if(!executor)return!1;return!["done","error","terminated"].includes(executor.state)}async function isWorkerDead(w){if(w.currentExecutorId){let state=await getAgentEffectiveState(w.id);return state==="terminated"||state==="offline"}return w.state==="suspended"}async function resolveRecipient(recipientId){let allWorkers=await list(),byId=[],byRole=[],byTeamRole=[];for(let w of allWorkers){if(await isWorkerDead(w))continue;if(!await _deps3.isPaneAlive(w.paneId))continue;if(w.id===recipientId)byId.push(w);else if(w.role===recipientId)byRole.push(w);else if(`${w.team}:${w.role}`===recipientId)byTeamRole.push(w)}if(byId.length>0)return byId;if(byRole.length>0)return byRole;return byTeamRole}function dedupShadowsForSend(workers){let groups=new Map;for(let w of workers){let key=`${w.customName??w.id}\x00${w.team??""}`,arr=groups.get(key);if(arr)arr.push(w);else groups.set(key,[w])}let out=[];for(let arr of groups.values()){if(arr.length===1){out.push(arr[0]);continue}arr.sort((a,b2)=>{let aExec=a.currentExecutorId!=null?1:0,bExec=b2.currentExecutorId!=null?1:0;if(aExec!==bExec)return bExec-aExec;return(b2.startedAt??"").localeCompare(a.startedAt??"")}),out.push(arr[0])}return out}async function findLiveWorkerFuzzy(recipientId){let matches=dedupShadowsForSend(await resolveRecipient(recipientId));return matches.length===1?matches[0]:null}async function findSpawnTemplate(worker,recipientId){let templates=await listTemplates(),candidates=[worker?.role,worker?.id,recipientId].filter((v)=>Boolean(v)),uniqueCandidates=[...new Set(candidates)],workerTeam=worker?.team;return templates.find((t)=>{if(workerTeam&&t.team!==workerTeam)return!1;return uniqueCandidates.some((q)=>t.id===q||t.role===q||`${t.team}:${t.role}`===q)})??null}async function lockedSpawnWorker(recipientId,worker,template,resumeSessionId){let sql=await getConnection(),workerTeam=worker?.team,lockResult=await sql.begin(async(tx)=>{await tx`SELECT pg_advisory_xact_lock(hashtext(${recipientId}))`;let postLockLive=await findLiveWorkerFuzzy(recipientId);if(postLockLive)return{type:"existing",worker:postLockLive};if(await cleanupDeadWorkers(recipientId,workerTeam),worker)await unregister(worker.id);let{spawnWorkerFromTemplate:spawnWorkerFromTemplate2}=await Promise.resolve().then(() => (init_protocol_router_spawn(),exports_protocol_router_spawn));return{type:"spawned",...await spawnWorkerFromTemplate2(template,resumeSessionId)}});if(lockResult.type==="existing")return{worker:lockResult.worker,respawned:!1};if(await saveTemplate({...template,lastSpawnedAt:new Date().toISOString()}),await(_deps3.waitForWorkerReady??waitForWorkerReady)(lockResult.paneId),!await _deps3.isPaneAlive(lockResult.paneId))return await unregister(lockResult.worker.id),null;return{worker:lockResult.worker,respawned:!0}}async function resolveResumeSessionId(worker,template,recipientId){if(template.provider!=="claude")return;let agentIdToProbe=worker?.id??`dir:${recipientId}`,decision=await shouldResume(agentIdToProbe);if(worker&&await isExecutorResumable(worker)){if(!decision.sessionId)throw new MissingResumeSessionError(worker.id,recipientId)}return decision.sessionId}async function handleSpawnError(err,worker,recipientId){if(err instanceof MissingResumeSessionError)throw err;let msg=err instanceof Error?err.message:String(err);if(console.error(`[protocol-router] Spawn failed for "${recipientId}": ${msg}`),worker)await update(worker.id,{state:"error"}).catch(()=>{});return null}async function ensureWorkerAlive(worker,recipientId){if(worker&&worker.state!=="suspended"&&await _deps3.isPaneAlive(worker.paneId))return{worker,respawned:!1};let live=await findLiveWorkerFuzzy(recipientId);if(live)return{worker:live,respawned:!1};if(await isExecutorCompleted(worker))return null;if(!process.env.TMUX)return null;let template=await findSpawnTemplate(worker,recipientId);if(!template)return null;let resumeSessionId=await resolveResumeSessionId(worker,template,recipientId);try{return await lockedSpawnWorker(recipientId,worker,template,resumeSessionId)}catch(err){return handleSpawnError(err,worker,recipientId)}}async function cleanupDeadWorkers(recipientId,team){let allWorkers=await list();for(let w of allWorkers){if(team&&w.team!==team)continue;if(!(w.role===recipientId||w.id===recipientId))continue;if(await _deps3.isPaneAlive(w.paneId))continue;await unregister(w.id)}}async function deliverToWorker(repoPath,from,worker,body){let message=await send(repoPath,from,worker.id,body),delivered=!1;if(worker.nativeTeamEnabled&&worker.team&&worker.role)delivered=await writeToNativeInbox(worker,message);else delivered=await injectToTmuxPane(worker,message);if(!delivered&&worker.team){let agentName=worker.role||worker.id.split("-").slice(-1)[0]||worker.id;try{let nativeMsg=toNativeInboxMessage(message,worker.nativeColor??"blue");await writeNativeInbox(worker.team,agentName,nativeMsg),delivered=!0}catch{}}if(delivered)await markDelivered(repoPath,worker.id,message.id);else console.error(`[protocol-router] Delivery failed: all paths exhausted (worker=${worker.id}, pane=${worker.paneId}, msg="${body.slice(0,50)}")`);return{messageId:message.id,workerId:worker.id,delivered}}async function deliverViaNativeInbox(repoPath,from,to,body,teamName){let resolvedTeam=teamName??await discoverTeamName();if(!resolvedTeam)return null;let config=await loadConfig(resolvedTeam).catch(()=>null);if(!config)return null;let sanitizedTo=sanitizeTeamName(to),matchedMember=config.members?.find((m)=>m.name===to||m.name===sanitizedTo||m.agentId===`${to}@${resolvedTeam}`||m.agentId===`${sanitizedTo}@${resolvedTeam}`);if(!matchedMember)return null;let inboxName=matchedMember.name??to;try{let message=await send(repoPath,from,to,body),nativeMsg={from,text:body,summary:body.length>50?`${body.substring(0,50)}...`:body,timestamp:new Date().toISOString(),color:"blue",read:!1};return await writeNativeInbox(resolvedTeam,inboxName,nativeMsg),await markDelivered(repoPath,to,message.id),{messageId:message.id,workerId:to,delivered:!0}}catch{return null}}async function deliverAfterPaneRecheck(repoPath,from,worker,body,paneDeadReason){if(!await _deps3.isPaneAlive(worker.paneId)){let message=await send(repoPath,from,worker.id,body);return console.error(`[protocol-router] Delivery failed: ${paneDeadReason} (worker=${worker.id}, msg="${body.slice(0,50)}")`),{messageId:message.id,workerId:worker.id,delivered:!1,reason:paneDeadReason}}return deliverToWorker(repoPath,from,worker,body)}async function findKnownWorker(to){let worker=await get(to);if(worker)return worker;let allWorkers=await list();return allWorkers.find((w)=>w.role===to&&w.state==="suspended")??allWorkers.find((w)=>w.role===to)??null}async function attemptAutoSpawnDelivery(repoPath,from,to,body,worker){let alive;try{alive=await ensureWorkerAlive(worker,to)}catch(err){if(err instanceof MissingResumeSessionError)return console.error(`[protocol-router] ${err.message}`),{messageId:"",workerId:worker?.id??to,delivered:!1,reason:err.message};throw err}if(!alive)return null;return deliverAfterPaneRecheck(repoPath,from,alive.worker,body,"pane dead after spawn")}async function sendMessage2(repoPath,from,to,body,teamName){if(from===to)return{messageId:"",workerId:to,delivered:!0,reason:"Self-delivery suppressed"};let liveMatches=await resolveRecipient(to);if(liveMatches.length===1)return deliverAfterPaneRecheck(repoPath,from,liveMatches[0],body,"Pane died before delivery");if(liveMatches.length>1)return{messageId:"",workerId:to,delivered:!1,reason:`Worker "${to}" is ambiguous. Found ${liveMatches.length} live matches: ${liveMatches.map((m)=>m.id).join(", ")}. Use exact worker ID.`};let{resolve:resolve6}=await Promise.resolve().then(() => (init_agent_directory(),exports_agent_directory)),dirResolved=await resolve6(to),worker=await findKnownWorker(to);if(dirResolved||worker){let result2=await attemptAutoSpawnDelivery(repoPath,from,to,body,worker);if(result2)return result2}let nativeResult=await deliverViaNativeInbox(repoPath,from,to,body,teamName);if(nativeResult)return nativeResult;return{messageId:"",workerId:to,delivered:!1,reason:`Worker "${to}" not found or not alive`}}async function writeToNativeInbox(worker,message){try{let nativeMsg=toNativeInboxMessage(message,worker.nativeColor??"blue"),agentName=worker.role??worker.id;return await writeNativeInbox(worker.team??"",agentName,nativeMsg),!0}catch{return!1}}async function injectToTmuxPane(worker,message){if(!worker.paneId)return!1;if(!/^%\d+$/.test(worker.paneId))return!1;if(!await _deps3.isPaneAlive(worker.paneId))return!1;try{let escaped=message.body.replace(/'/g,"'\\''");return await executeTmux2(`send-keys -t '${worker.paneId}' '${escaped}'`),await new Promise((resolve6)=>setTimeout(resolve6,200)),await executeTmux2(`send-keys -t '${worker.paneId}' Enter`),!0}catch{return!1}}async function deliverToPane(toWorker,messageId){let worker=await get(toWorker);if(!worker||!worker.paneId)return await markFailed(messageId),!1;if(!await _deps3.isPaneAlive(worker.paneId))return await markFailed(messageId),!1;let message=await getById(messageId);if(!message||message.deliveredAt)return!1;let injected=await injectToTmuxPane(worker,message);if(injected&&worker.repoPath)await markDelivered(worker.repoPath,worker.id,messageId);else await markFailed(messageId);return injected}async function getInbox(repoPath,workerId){return inbox(repoPath,workerId)}var MissingResumeSessionError,_deps3,AUTO_SPAWN_READY_TIMEOUT_MS=15000,AUTO_SPAWN_POLL_INTERVAL_MS=1000;var init_protocol_router=__esm(()=>{init_agent_registry();init_claude_native_teams();init_db();init_executor_registry();init_mailbox();init_orchestrator();init_should_resume();init_spawn_command();init_tmux();MissingResumeSessionError=class MissingResumeSessionError extends Error{workerId;entityId;recipientId;reason;constructor(workerId,recipientId,reason="null_session"){let suffix=recipientId?` (recipient "${recipientId}")`:"";super(`Cannot resume worker "${workerId}"${suffix}: executor has no claude_session_id recorded (reason: ${reason}). This usually means the worker predates the session-sync hook. Run \`genie reset ${workerId}\` or re-spawn the worker to recover.`);this.name="MissingResumeSessionError",this.workerId=workerId,this.entityId=workerId,this.recipientId=recipientId,this.reason=reason}};_deps3={isPaneAlive,waitForWorkerReady:null}});var exports_sdk_session_capture={};__export(exports_sdk_session_capture,{updateTurnCount:()=>updateTurnCount,startSession:()=>startSession,recordTurn:()=>recordTurn,endSession:()=>endSession});async function startSession(safePgCall,executorId,claudeSessionId,agentId,team,role,wishSlug){let sessionId=claudeSessionId??`sdk-${executorId}-${Date.now()}`;if(!await safePgCall("sdk-session-start",(sql)=>sql`INSERT INTO sessions (id, agent_id, executor_id, team, role, wish_slug, status, jsonl_path, project_path)
|
|
1674
1674
|
VALUES (${sessionId}, ${agentId}, ${executorId}, ${team??null}, ${role??null}, ${wishSlug??null}, 'active', '', '')
|
|
1675
1675
|
ON CONFLICT (id) DO NOTHING
|
|
1676
1676
|
RETURNING id`,null,{executorId,chatId:""}))return null;return sessionId}async function recordTurn(safePgCall,sessionId,turnIndex,role,content,toolName,timestamp2){let ts3=timestamp2??new Date().toISOString();await safePgCall("sdk-session-turn",(sql)=>sql`INSERT INTO session_content (session_id, turn_index, role, content, tool_name, timestamp)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260427.
|
|
3
|
+
"version": "4.260427.6",
|
|
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"
|