@sma1lboy/rove 0.9.160 → 0.9.161
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/cli/{chunk-zmvsqfad.js → chunk-07m61r5j.js} +5 -5
- package/dist/cli/chunk-0j4d6w8x.js +2 -0
- package/dist/cli/{chunk-ttnpek4s.js → chunk-0rsh0hk3.js} +1 -1
- package/dist/cli/{chunk-wdhcrcre.js → chunk-0zsechc5.js} +1 -1
- package/dist/cli/{chunk-144wtkqt.js → chunk-109g1rks.js} +1 -1
- package/dist/cli/chunk-13mw2v47.js +2 -0
- package/dist/cli/{chunk-kt1ppyje.js → chunk-1kz2syvp.js} +1 -1
- package/dist/cli/chunk-1pht7ydx.js +2 -0
- package/dist/cli/{chunk-cz7qj1p8.js → chunk-2h9nz0zv.js} +1 -1
- package/dist/cli/{chunk-39x1ztey.js → chunk-2qkj3wsg.js} +1 -1
- package/dist/cli/chunk-3bcx9dyb.js +2 -0
- package/dist/cli/{chunk-5w26jqne.js → chunk-44cy59kn.js} +1 -1
- package/dist/cli/chunk-4wswdvzj.js +2 -0
- package/dist/cli/{chunk-91f68r6e.js → chunk-5ec9ydqp.js} +1 -1
- package/dist/cli/{chunk-0am6gj8j.js → chunk-5emg1pra.js} +1 -1
- package/dist/cli/{chunk-56wver0a.js → chunk-5q3gsvne.js} +1 -1
- package/dist/cli/{chunk-vj2vdx3w.js → chunk-632q2pg7.js} +1 -1
- package/dist/cli/{chunk-t0y9ty67.js → chunk-6ndddtg5.js} +1 -1
- package/dist/cli/{chunk-8vec42df.js → chunk-6rc52z3j.js} +1 -1
- package/dist/cli/{chunk-a2xndedd.js → chunk-6tvrvekq.js} +1 -1
- package/dist/cli/{chunk-ztx1pfqv.js → chunk-7027e3rb.js} +1 -1
- package/dist/cli/{chunk-qy855bd5.js → chunk-74h1frdb.js} +1 -1
- package/dist/cli/{chunk-z49thwax.js → chunk-7zmsb99k.js} +1 -1
- package/dist/cli/{chunk-wdq5p44p.js → chunk-89843565.js} +1 -1
- package/dist/cli/{chunk-caqpt09m.js → chunk-932ymm0m.js} +1 -1
- package/dist/cli/{chunk-y7szq7xb.js → chunk-97h1gbh8.js} +2 -2
- package/dist/cli/{chunk-sfv15era.js → chunk-9esrwvqc.js} +1 -1
- package/dist/cli/{chunk-yrawhw9y.js → chunk-9fvdqhj9.js} +1 -1
- package/dist/cli/{chunk-qpd817w4.js → chunk-a6jcy2y3.js} +1 -1
- package/dist/cli/chunk-aedbpxbr.js +2 -0
- package/dist/cli/{chunk-ynknvkrw.js → chunk-amt0tw20.js} +1 -1
- package/dist/cli/chunk-ayhhgfsz.js +3 -0
- package/dist/cli/{chunk-6pt23vas.js → chunk-b3pxf5xd.js} +1 -1
- package/dist/cli/{chunk-gkmpfpmn.js → chunk-bpkg51p5.js} +2 -2
- package/dist/cli/{chunk-9h4kbsk5.js → chunk-bt5s6ft2.js} +1 -1
- package/dist/cli/{chunk-ksqdd9zh.js → chunk-bybwrdrn.js} +1 -1
- package/dist/cli/{chunk-ds9qf1w1.js → chunk-dx4xyegr.js} +1 -1
- package/dist/cli/{chunk-0hn7ctj6.js → chunk-ea0tf083.js} +1 -1
- package/dist/cli/{chunk-0bayctej.js → chunk-em5cnbrc.js} +3 -3
- package/dist/cli/{chunk-myewzjst.js → chunk-ep7exzsb.js} +1 -1
- package/dist/cli/{chunk-pas1rh6m.js → chunk-fex98vjg.js} +1 -1
- package/dist/cli/{chunk-hj08heah.js → chunk-ffj7jp3k.js} +1 -1
- package/dist/cli/{chunk-ny31s73y.js → chunk-ftpbjbg9.js} +1 -1
- package/dist/cli/{chunk-9wn8m2rt.js → chunk-gs8kf0me.js} +1 -1
- package/dist/cli/chunk-gyc4fj15.js +3 -0
- package/dist/cli/{chunk-aak5ttgt.js → chunk-hfh1wgtr.js} +1 -1
- package/dist/cli/{chunk-5v893waj.js → chunk-j75axe29.js} +1 -1
- package/dist/cli/{chunk-w68k2kdb.js → chunk-k1qe5qqa.js} +1 -1
- package/dist/cli/{chunk-y8yyk9hq.js → chunk-k43kf7qa.js} +1 -1
- package/dist/cli/{chunk-39fjx5fq.js → chunk-mnk7byzm.js} +2 -2
- package/dist/cli/chunk-mv7ptt5w.js +3 -0
- package/dist/cli/{chunk-j91yq51m.js → chunk-mw1x9nh0.js} +3 -3
- package/dist/cli/chunk-n4cwdgv2.js +2 -0
- package/dist/cli/chunk-n55jst34.js +7 -0
- package/dist/cli/{chunk-cfg0s90c.js → chunk-n5c3k1dg.js} +1 -1
- package/dist/cli/{chunk-zftxrnwh.js → chunk-naexhh9c.js} +1 -1
- package/dist/cli/chunk-njzwyy8v.js +2 -0
- package/dist/cli/{chunk-47nevjck.js → chunk-pc3djc2b.js} +2 -2
- package/dist/cli/{chunk-1ckc04zj.js → chunk-pfvpvxz2.js} +1 -1
- package/dist/cli/{chunk-zz788rt4.js → chunk-q0s362r7.js} +2 -2
- package/dist/cli/chunk-q16pxc1j.js +2 -0
- package/dist/cli/{chunk-cs50jnwn.js → chunk-qs80x1cd.js} +1 -1
- package/dist/cli/chunk-r79ht9kg.js +2 -0
- package/dist/cli/chunk-rqwx8wqx.js +2 -0
- package/dist/cli/chunk-s6mcrm5g.js +2 -0
- package/dist/cli/{chunk-wbt845s6.js → chunk-sx8fw2qd.js} +1 -1
- package/dist/cli/{chunk-4502n1bk.js → chunk-tngnng9q.js} +1 -1
- package/dist/cli/chunk-tp48wamv.js +3 -0
- package/dist/cli/{chunk-t7xhb2jx.js → chunk-tqat428f.js} +1 -1
- package/dist/cli/{chunk-ntt7gy5a.js → chunk-ttd78mrr.js} +1 -1
- package/dist/cli/{chunk-84s62bex.js → chunk-v8mscj31.js} +1 -1
- package/dist/cli/chunk-wc0g8kzq.js +2 -0
- package/dist/cli/chunk-wzdrq5es.js +2 -0
- package/dist/cli/{chunk-t27t2x9b.js → chunk-xcgb5q15.js} +1 -1
- package/dist/cli/{chunk-x6hevxs5.js → chunk-y9xr3c66.js} +1 -1
- package/dist/cli/chunk-ycdeav89.js +2 -0
- package/dist/cli/{chunk-zvfgzpw6.js → chunk-ygnfyfq9.js} +1 -1
- package/dist/cli/chunk-ykrvv6bc.js +4 -0
- package/dist/cli/{chunk-1aqmmea7.js → chunk-yrwkh92f.js} +1 -1
- package/dist/cli/{chunk-qr3e1m00.js → chunk-ytxw3rvn.js} +1 -1
- package/dist/cli/{chunk-g48s150x.js → chunk-yv913can.js} +1 -1
- package/dist/cli/{chunk-ccjmtajy.js → chunk-ywcx78mv.js} +1 -1
- package/dist/cli/{chunk-s59cymtd.js → chunk-zf45ap00.js} +1 -1
- package/dist/cli/{chunk-e85ttr8r.js → chunk-zk9yzb09.js} +1 -1
- package/dist/cli/index.js +7 -7
- package/dist/cli/kobe.js +1 -1
- package/dist/cli/pty-host-node.mjs +0 -13
- package/dist/cli/rove.js +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-0fhab4tq.js +0 -2
- package/dist/cli/chunk-1y37w7zq.js +0 -4
- package/dist/cli/chunk-22yhtgcy.js +0 -7
- package/dist/cli/chunk-4jf0yq4v.js +0 -2
- package/dist/cli/chunk-6b474j5g.js +0 -2
- package/dist/cli/chunk-7x88845e.js +0 -2
- package/dist/cli/chunk-959a71qj.js +0 -2
- package/dist/cli/chunk-a3a9gdc4.js +0 -3
- package/dist/cli/chunk-azsmk821.js +0 -2
- package/dist/cli/chunk-cdxme5y4.js +0 -2
- package/dist/cli/chunk-cfghdnev.js +0 -2
- package/dist/cli/chunk-dmdpsw2y.js +0 -2
- package/dist/cli/chunk-dzd1wy5a.js +0 -2
- package/dist/cli/chunk-kw0nnzdp.js +0 -2
- package/dist/cli/chunk-mhs6yywx.js +0 -2
- package/dist/cli/chunk-mp8v5gn8.js +0 -2
- package/dist/cli/chunk-mqqnk8w7.js +0 -3
- package/dist/cli/chunk-nm0pq5r6.js +0 -3
- package/dist/cli/chunk-rc668r26.js +0 -2
- package/dist/cli/chunk-w06vgba4.js +0 -3
- package/dist/cli/chunk-yzmvmt89.js +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{IssuesStore,countCronBetween,defaultIssuesStorePath,isValidCron,latestCronAtOrBefore,nextCronAfter,resolveRepoRoot,serialized,tightenInstalledPluginPermissions,writeJsonAtomic}from"./chunk-
|
|
2
|
+
import{IssuesStore,countCronBetween,defaultIssuesStorePath,isValidCron,latestCronAtOrBefore,nextCronAfter,resolveRepoRoot,serialized,tightenInstalledPluginPermissions,writeJsonAtomic}from"./chunk-2qkj3wsg.js";import{buildPluginEnv}from"./chunk-41ahvk51.js";import{ClientWriter,LineReceiver,linkLegacyRuntimePath,objectPayload,optionalActivityDetail,optionalBoolean,optionalNumber,optionalString,optionalVendor,requireNumber,requireString}from"./chunk-rz8zs7j8.js";import{parsePorcelainRows}from"./chunk-h4n8ffqy.js";import{worktreeFingerprint}from"./chunk-vyb5a2rh.js";import{STICKY_STATES,activityStillWorking,reduceActivity,resolveEngineStateTtlMs}from"./chunk-r2pztgm6.js";import{currentPluginPlatform,loadPluginRegistry,pluginConfigDir,pluginLogPath,pluginManifestPath,pluginRegistryPath,pluginStateDir,readPluginManifest,supportsPlatform}from"./chunk-38272eqv.js";import{managedWorktreeRootsFor,readWorktreeBaseOverride}from"./chunk-5f80ggc3.js";import{ptyHostHasLiveSessions,sweepPtyHostSessions}from"./chunk-0zsechc5.js";import{logDaemonError,logDaemonInfo}from"./chunk-1cjd7h8p.js";import{resolveLoginShell}from"./chunk-b99tf0jr.js";import{probeDaemonSocket}from"./chunk-tngnng9q.js";import{KobeDaemonClient}from"./chunk-0wnfq5sx.js";import{CHANNEL_NAMES,DAEMON_PROTOCOL_VERSION,MIN_COMPATIBLE_PROTOCOL_VERSION,attentionInboxItemKey,automationRunNeedsAttention,displayTaskTitle,frameToLine,isAttentionInboxState,isProtocolCompatible,normalizeChannelFilter,serializeTask}from"./chunk-7j5372wb.js";import{maybeStartScheduledRun}from"./chunk-gwcneg6c.js";import{defaultKeybindingsPath,defaultUiPrefsStatePath}from"./chunk-ch9b1d5y.js";import{createSocketOwnershipGuard,ensureOwnerOnlyStateDir,isProcessAlive,listenOnUnixSocket}from"./chunk-mv7ptt5w.js";import{rotateLogIfNeeded}from"./chunk-pw6yfj10.js";import{ENGINE_EXIT_BANNER,engineExitCodeFromTail,plainTail,readPtyExitRecords,readPtyExitStore,recordEngineExit}from"./chunk-nyv7a76k.js";import{defaultDaemonPidPath,defaultDaemonSocketPath,defaultPtyExitsPath,defaultPtyHostSocketPath,legacyDaemonPidPath,legacyDaemonSocketPath,resolveDaemonHomeDir}from"./chunk-mcdv19rs.js";import{ROVE_STATE_DIR_BASENAME,readRoveEnv,readRoveHomeDirEnv}from"./chunk-r3k9rbwm.js";import{__require}from"./chunk-wqfp8d9w.js";import{mkdir,unlink as unlink2,writeFile as writeFile2}from"fs/promises";import{createServer}from"net";import{dirname as dirname2}from"path";import{statSync}from"fs";var WATCHED_FIELDS=["title","branch","worktreePath","status","pinned","vendor","command","modelEffort","linkedWorkItem","scratch"];function same(a,b){if(a===b)return!0;if(a==null||b==null)return a==null&&b==null;if(typeof a==="object"||typeof b==="object")return JSON.stringify(a)===JSON.stringify(b);return!1}function samePr(a,b){let strip=(v)=>{if(v==null||typeof v!=="object"||Array.isArray(v))return v;let{lastCheckedAt:_a,lastError:_b,...rest}=v;return rest};return same(strip(a),strip(b))}function diffTask(prev,next){let fields=[],from={},to={};for(let field of WATCHED_FIELDS){let a=prev[field],b=next[field];if(same(a,b))continue;if(fields.push(field),a!==void 0)from[field]=a;if(b!==void 0)to[field]=b}let prChanged=!samePr(prev.prStatus,next.prStatus);if(fields.length===0&&!prChanged)return null;return{fields,from,to,prChanged,worktreeCreated:next.kind==="task"&&!prev.worktreePath&&!!next.worktreePath}}function bornWithWorktree(task){return task.kind==="task"&&!!task.worktreePath}function lifecycleEventFor(kind,detail){switch(kind){case"session-start":return"session.start";case"session-end":return"session.end";case"turn-start":return"turn.prompt";case"turn-complete":return"turn.complete";case"turn-failed":return"turn.failed";case"turn-interrupted":return"turn.interrupted";case"awaiting-input":return detail?.waiting==="input"?"attention.question":"attention.permission";case"tool-pre":return"tool.pre";case"tool-post":return"tool.post";case"tool-failed":return"tool.failed";case"pre-compact":return"context.pre-compact";case"post-compact":return"context.post-compact";case"subagent-start":return"subagent.start";case"subagent-stop":return"subagent.stop";default:return}}var AGENT_EVENT_BY_STATE={turn_complete:"agent.turn-complete",permission_needed:"agent.permission-needed",rate_limited:"agent.rate-limited",error:"agent.error",running:"agent.running",idle:"agent.idle"};function taskContext(task){if(!task)return;let{id,title,repo,branch,worktreePath,vendor,status}=task;return{id,title,repo,branch,worktreePath,vendor,status}}class PluginEventReducer{tasks;agentState=new Map;now;constructor(now=Date.now){this.now=now}contextFor(taskId){return taskContext(this.tasks?.get(taskId))}reduce(event){switch(event.channel){case"task.snapshot":return this.reduceTasks(event.payload.tasks);case"engine-state":return this.reduceEngine(event.payload);default:return[]}}reduceTasks(tasks){let next=new Map(tasks.map((t)=>[t.id,t])),prev=this.tasks;if(this.tasks=next,!prev)return[];let at=this.now(),out=[];for(let[id,task]of next){let before=prev.get(id);if(!before){if(out.push({event:"task.created",taskId:id,task:taskContext(task),at}),bornWithWorktree(task))out.push({event:"worktree.created",taskId:id,task:taskContext(task),at});continue}let diff=diffTask(before,task);if(!diff)continue;let ctx=taskContext(task);if(diff.fields.length>0)out.push({event:"task.changed",taskId:id,task:ctx,detail:{fields:diff.fields,from:diff.from,to:diff.to},at});if(diff.prChanged)out.push({event:"task.pr-changed",taskId:id,task:ctx,detail:{...before.prStatus!==void 0?{from:before.prStatus}:{},...task.prStatus!==void 0?{to:task.prStatus}:{}},at});if(diff.worktreeCreated)out.push({event:"worktree.created",taskId:id,task:ctx,at})}for(let[id,task]of prev)if(!next.has(id))out.push({event:"task.deleted",taskId:id,task:taskContext(task),at});return out}reduceEngine(payload){let key=`${payload.taskId}\x00${payload.tabId??""}`,prev=this.agentState.get(key);if(this.agentState.set(key,payload.state),prev===payload.state)return[];let event=AGENT_EVENT_BY_STATE[payload.state];if(!event)return[];return[{event,taskId:payload.taskId,task:taskContext(this.tasks?.get(payload.taskId)),...payload.tabId?{tabId:payload.tabId}:{},at:this.now()}]}}import{spawn}from"child_process";import{appendFileSync,mkdirSync}from"fs";var OUTPUT_CAP=8192,PLUGIN_LOG_CAP_BYTES=4194304,SHUTDOWN_GRACE_MS=3000,HOOK_TIMEOUT_MS=30000,HOOK_SLOW_MS=2000;function hookTimeoutMs(spec,kind){return spec.timeoutMs??(kind==="shutdown"?SHUTDOWN_GRACE_MS:HOOK_TIMEOUT_MS)}function appendRecord(pluginId,homeDir,record){try{let logPath=pluginLogPath(pluginId,homeDir);rotateLogIfNeeded(logPath,PLUGIN_LOG_CAP_BYTES),appendFileSync(logPath,`${JSON.stringify(record)}
|
|
3
3
|
`,{mode:384})}catch{}}async function runPluginHook(opts){let{pluginId,spec,kind,label,homeDir}=opts,timeoutMs=hookTimeoutMs(spec,kind),startedAt=Date.now();try{mkdirSync(pluginConfigDir(pluginId,homeDir),{recursive:!0,mode:448}),mkdirSync(pluginStateDir(pluginId,homeDir),{recursive:!0,mode:448})}catch(err){appendRecord(pluginId,homeDir,{at:startedAt,kind,label,command:spec.command,exitCode:null,durationMs:Date.now()-startedAt,spawnError:String(err)}),opts.log?.(`plugin ${pluginId} ${label}: ${String(err)}`);return}let exitCode=null,stdout="",stderr="",spawnError,timedOut=!1;if(await new Promise((resolve)=>{let[cmd,...args]=spec.command,child=spawn(cmd,args,{cwd:opts.pluginRoot,env:buildPluginEnv({homeDir,socketPath:opts.socketPath,binPath:opts.binPath,pluginId,pluginRoot:opts.pluginRoot,extra:opts.extraEnv}),stdio:["ignore","pipe","pipe"],detached:!0}),kill=()=>{try{if(child.pid)process.kill(-child.pid,"SIGKILL");else child.kill("SIGKILL")}catch{child.kill("SIGKILL")}},deadline=setTimeout(()=>{timedOut=!0,kill()},timeoutMs);deadline.unref?.();let slow=setTimeout(()=>{appendRecord(pluginId,homeDir,{at:startedAt,kind,label,command:spec.command,phase:"running",runningMs:Date.now()-startedAt,timeoutMs})},Math.min(HOOK_SLOW_MS,Math.floor(timeoutMs/2)));slow.unref?.(),opts.inFlight?.add(kill);let finish=()=>{clearTimeout(deadline),clearTimeout(slow),opts.inFlight?.delete(kill),resolve()};child.stdout.on("data",(chunk)=>{if(stdout.length<OUTPUT_CAP)stdout+=chunk.toString().slice(0,OUTPUT_CAP-stdout.length)}),child.stderr.on("data",(chunk)=>{if(stderr.length<OUTPUT_CAP)stderr+=chunk.toString().slice(0,OUTPUT_CAP-stderr.length)}),child.on("error",(err)=>{spawnError=String(err),finish()}),child.on("close",(code)=>{exitCode=code,finish()})}),appendRecord(pluginId,homeDir,{at:startedAt,kind,label,command:spec.command,exitCode,durationMs:Date.now()-startedAt,...timedOut?{timedOut:!0,timeoutMs}:{},...stdout?{stdout}:{},...stderr?{stderr}:{},...spawnError?{spawnError}:{}}),timedOut)opts.log?.(`plugin ${pluginId} ${label}: killed after ${timeoutMs}ms (hook did not exit)`);else if(spawnError||exitCode!==null&&exitCode!==0)opts.log?.(`plugin ${pluginId} ${label}: ${spawnError??`exit ${exitCode}`}`)}var RELOAD_DEBOUNCE_MS=150,REGISTRY_POLL_MS=200;function fileStamp(path){if(!path)return"absent";try{let s=statSync(path,{bigint:!0});return`${s.mtimeNs}:${s.size}:${s.ino}`}catch{return"absent"}}function startPluginHost(bus,opts){let host=new PluginHost(opts);bus.onPublish((event)=>host.handleChannel(event));for(let event of bus.snapshot())host.handleChannel(event);return host.start(),host}function maybeStartPluginHost(bus,options,socketPath,log){if(!options.plugins)return null;return startPluginHost(bus,{homeDir:options.homeDir,socketPath,binPath:options.plugins.binPath,log})}class PluginHost{opts;reducer=new PluginEventReducer;plugins=[];enabledIds=new Set;manifestStamps=new Map;pollTimer;registryStamp="";reloadTimer;inFlight=new Set;stopped=!1;constructor(opts){this.opts=opts}start(){this.watchRegistry(),this.plugins=this.loadPlugins();for(let plugin of this.plugins)for(let[i,hook]of plugin.manifest.startup.entries()){if(!supportsPlatform(hook,plugin.manifest,currentPluginPlatform()))continue;this.run(plugin,hook,"startup",{ROVE_PLUGIN_EVENT:"startup"},`startup[${i}]`)}}async stop(){if(this.stopped)return;if(this.stopped=!0,this.reloadTimer)clearTimeout(this.reloadTimer);if(this.pollTimer)clearInterval(this.pollTimer);for(let kill of[...this.inFlight])kill();let runs=[];for(let plugin of this.plugins)for(let[i,hook]of plugin.manifest.shutdown.entries()){if(!supportsPlatform(hook,plugin.manifest,currentPluginPlatform()))continue;runs.push(this.run(plugin,hook,"shutdown",{ROVE_PLUGIN_EVENT:"shutdown"},`shutdown[${i}]`))}await Promise.allSettled(runs)}handleChannel(event){if(this.stopped)return;try{for(let derived of this.reducer.reduce(event))this.dispatch(derived)}catch(err){this.opts.log?.(`plugin event reduce failed on ${event.channel} \u2014 ${String(err)}`)}}handleUiReport(report){if(this.stopped)return;try{this.dispatch({event:report.kind,...report.taskId?{taskId:report.taskId,task:this.reducer.contextFor(report.taskId)}:{},...report.detail?{detail:report.detail}:{},at:Date.now()})}catch(err){this.opts.log?.(`plugin ui-report dispatch failed for ${report.kind} \u2014 ${String(err)}`)}}handleEngineReport(report){if(this.stopped)return;let event=lifecycleEventFor(report.kind,report.detail);if(!event)return;try{this.dispatch({event,taskId:report.taskId,task:this.reducer.contextFor(report.taskId),...report.vendor?{vendor:report.vendor}:{},...report.tabId?{tabId:report.tabId}:{},...report.sessionId?{sessionId:report.sessionId}:{},...report.detail?{detail:report.detail}:{},at:Date.now()})}catch(err){this.opts.log?.(`plugin engine-report dispatch failed for ${event} \u2014 ${String(err)}`)}}dispatchTo(plugin,event){let platform=currentPluginPlatform();try{for(let hook of plugin.manifest.events){if(hook.on!==event.event)continue;if(!supportsPlatform(hook,plugin.manifest,platform))continue;this.run(plugin,hook,"event",{ROVE_PLUGIN_EVENT:event.event,ROVE_PLUGIN_EVENT_JSON:JSON.stringify(event)},hook.on)}}catch(err){this.opts.log?.(`plugin ${plugin.manifest.id}: ${event.event} dispatch failed \u2014 ${String(err)}`)}}dispatch(event){let platform=currentPluginPlatform();for(let plugin of this.plugins)try{for(let hook of plugin.manifest.events){if(hook.on!==event.event)continue;if(!supportsPlatform(hook,plugin.manifest,platform))continue;this.run(plugin,hook,"event",{ROVE_PLUGIN_EVENT:event.event,ROVE_PLUGIN_EVENT_JSON:JSON.stringify(event),...event.taskId?{ROVE_PLUGIN_TASK_ID:event.taskId}:{},...event.task?.title?{ROVE_PLUGIN_TASK_TITLE:event.task.title}:{}},hook.on)}}catch(err){this.opts.log?.(`plugin ${plugin.manifest.id}: ${event.event} dispatch failed \u2014 ${String(err)}`)}}loadPlugins(){let registry=loadPluginRegistry(this.opts.homeDir),platform=currentPluginPlatform(),out=[],enabled=new Set,stamps=new Map;for(let entry of registry.plugins){if(!entry.enabled)continue;enabled.add(entry.id),stamps.set(entry.root,fileStamp(pluginManifestPath(entry.root)));let manifest;try{manifest=readPluginManifest(entry.root).manifest}catch(err){this.opts.log?.(`plugin ${entry.id}: manifest unreadable, skipping \u2014 ${String(err)}`);continue}if(!supportsPlatform({},manifest,platform))continue;out.push({manifest,root:entry.root})}return this.enabledIds=enabled,this.manifestStamps=stamps,out}sourcesChanged(){let stamp=fileStamp(pluginRegistryPath(this.opts.homeDir));if(stamp!==this.registryStamp)return this.registryStamp=stamp,!0;for(let[root,was]of this.manifestStamps)if(fileStamp(pluginManifestPath(root))!==was)return!0;return!1}watchRegistry(){this.registryStamp=fileStamp(pluginRegistryPath(this.opts.homeDir)),this.pollTimer=setInterval(()=>{if(!this.sourcesChanged())return;if(this.reloadTimer)clearTimeout(this.reloadTimer);this.reloadTimer=setTimeout(()=>{if(this.stopped)return;try{let loadedBefore=new Map(this.plugins.map((p)=>[p.manifest.id,p])),enabledBefore=this.enabledIds;this.plugins=this.loadPlugins(),this.opts.log?.(`plugin registry reloaded (${this.plugins.length} enabled)`);let at=Date.now();for(let plugin of this.plugins)if(!enabledBefore.has(plugin.manifest.id))this.dispatchTo(plugin,{event:"plugin.enabled",detail:{pluginId:plugin.manifest.id},at});for(let[id,plugin]of loadedBefore)if(!this.enabledIds.has(id))this.dispatchTo(plugin,{event:"plugin.disabled",detail:{pluginId:id},at})}catch(err){this.opts.log?.(`plugin registry reload failed \u2014 ${String(err)}`)}},RELOAD_DEBOUNCE_MS)},REGISTRY_POLL_MS),this.pollTimer.unref?.()}run(plugin,spec,kind,extraEnv,label){return runPluginHook({pluginId:plugin.manifest.id,pluginRoot:plugin.root,spec,kind,label,extraEnv,homeDir:this.opts.homeDir,socketPath:this.opts.socketPath,binPath:this.opts.binPath,log:this.opts.log,inFlight:this.inFlight}).catch((err)=>{this.opts.log?.(`plugin ${plugin.manifest.id} ${label}: ${String(err)}`)})}}function replacementKey(frame){if(frame.type!=="event")return null;switch(frame.name){case"task.snapshot":case"active-task":case"update":case"attention.inbox":case"ui-prefs":case"worktree.changes":case"transcript.activity":case"usage.snapshot":case"usage.context":return frame.name;default:return null}}function writeFrame(client,frame){client.writer.write(frameToLine(frame),replacementKey(frame))}function broadcast(clients,frame){let channel=frame.type==="event"&&frame.name!=="daemon.stopping"?frame.name:null,replaceKey=replacementKey(frame),line=null;for(let client of clients){if(!client.subscribed&&frame.type==="event")continue;if(channel&&client.channels&&!client.channels.has(channel))continue;line??=frameToLine(frame),client.writer.write(line,replaceKey)}}function handleClientLine(client,line,onRequest){if(line.trim().length===0)return;try{let frame=JSON.parse(line);if(frame.type!=="request")throw Error("daemon only accepts request frames from clients");onRequest(frame,client)}catch(err){writeFrame(client,{type:"response",id:"parse-error",error:{message:err instanceof Error?err.message:String(err)}})}}import{statSync as statSync2}from"fs";import{basename,dirname,join}from"path";var POLL_MS=200;function startFileWatchTrigger(opts){if(opts.debounceMs<=0)return()=>{};let dir=dirname(opts.filePath),names=[...new Set([basename(opts.filePath),...opts.matchBasenames??[]])],timer=null,trigger=()=>{try{opts.onTrigger()}catch(err){opts.onError(err)}},schedule=()=>{if(timer)clearTimeout(timer);timer=setTimeout(()=>{timer=null,trigger()},opts.debounceMs),timer.unref?.()},stampOf=(name)=>{try{let s=statSync2(join(dir,name),{bigint:!0});return`${s.mtimeNs}:${s.size}:${s.ino}`}catch{return"absent"}},stamps=new Map;for(let name of names)stamps.set(name,stampOf(name));let poll=setInterval(()=>{for(let name of names){let stamp=stampOf(name);if(stamp===stamps.get(name))continue;stamps.set(name,stamp),schedule()}},POLL_MS);return poll.unref?.(),()=>{if(clearInterval(poll),timer)clearTimeout(timer),timer=null}}var DEBOUNCE_MS=250,KEY_RE=/^(.+?)::(.+)$/;function startPtyExitWatch(opts){let path=opts.path??defaultPtyExitsPath(opts.homeDir),seen=new Map,baselined=!1,sweep=()=>{let host=opts.plugins();if(!host)return;let{records,readable}=readPtyExitStore(path);if(!readable)return;for(let[storeKey,record]of Object.entries(records)){if(seen.get(storeKey)===record.at)continue;if(seen.set(storeKey,record.at),!baselined)continue;host.handleUiReport({kind:"session.exited",...exitReport(record)}),publishDeath(record)}baselined=!0;for(let key of seen.keys())if(!(key in records))seen.delete(key)},publishDeath=(record)=>{let registry=opts.activity;if(!registry&&!opts.inbox)return;let death=engineDeathOf(record);if(!death)return;let{taskId,tabId,exit,at}=death;registry?.recordEngineDeath(taskId,tabId,exit,at),opts.inbox?.recordEngineDeath(taskId,tabId,{exit},at).catch((err)=>opts.log?.(`pty-exit inbox: ${String(err)}`))},stop=startFileWatchTrigger({filePath:path,debounceMs:DEBOUNCE_MS,onTrigger:sweep,onError:(err)=>opts.log?.(`pty-exit watch: ${String(err)}`)}),initial=readPtyExitStore(path);if(initial.readable){for(let[storeKey,record]of Object.entries(initial.records))seen.set(storeKey,record.at);baselined=!0}return stop}function engineDeathOf(record){let match=KEY_RE.exec(record.key),taskId=match?.[1],tabId=match?.[2];if(!taskId||!tabId)return null;let at=Date.parse(record.at);if(!Number.isFinite(at))return null;let lastLine=lastErrorLine(record.tail);return{taskId,tabId,exit:{code:record.code,signal:record.signal,...lastLine?{lastLine}:{}},at}}function lastErrorLine(tail){let last;for(let i=tail.length-1;i>=0;i--){let line=(tail[i]??"").trim();if(line.length===0)continue;if(ENGINE_EXIT_BANNER.test(line))return line;last??=line}return last}function exitReport(record){let match=KEY_RE.exec(record.key);return{...match?.[1]?{taskId:match[1]}:{},detail:{key:record.key,...match?.[2]?{tabId:match[2]}:{},pid:record.pid,code:record.code,signal:record.signal,exitedAt:record.at,tail:record.tail}}}function createActivityObserverIo(homeDir,runtime,activity){let peek=async(key)=>{let client=new KobeDaemonClient(defaultPtyHostSocketPath(homeDir));try{await client.connect();let result=await client.request("pty.peek",{key});return Buffer.from(result.data??"","base64").toString("utf8")}catch{return""}finally{client.close()}};return{onEngineExit({taskId,tabId,vendor,pid}){let key=`${taskId}::${tabId}`,pending=peek(key).then((tail)=>recordEngineExit({key,vendor,pid,at:new Date().toISOString(),tail},defaultPtyExitsPath(homeDir))).catch((err)=>logDaemonInfo("engine-exit",`record failed for ${key}: ${String(err)}`));return logDaemonInfo("engine-exit",`${vendor} (pid ${pid??"?"}) gone from live session ${key}`),pending},onEngineAbsentAtStart({taskId,tabId}){let key=`${taskId}::${tabId}`,path=defaultPtyExitsPath(homeDir),existing=readPtyExitRecords(path)[`${key}#engine`];if(existing){let death=engineDeathOf(existing);if(death)activity?.recordEngineDeath(death.taskId,death.tabId,death.exit,death.at);return}return peek(key).then((raw)=>{if(engineExitCodeFromTail(plainTail(raw))===null)return;recordEngineExit({key,pid:null,at:new Date().toISOString(),tail:raw,atApproximate:!0},path),logDaemonInfo("engine-exit",`recorded an unwatched death in ${key} (exit time unknown)`)}).catch((err)=>logDaemonInfo("engine-exit",`boot record failed for ${key}: ${String(err)}`))},async listSessions(){let client=new KobeDaemonClient(defaultPtyHostSocketPath(homeDir));try{return await client.connect(),((await client.request("pty.list")).sessions??[]).map((s)=>({key:s.key??"",alive:s.alive===!0,pid:typeof s.pid==="number"?s.pid:null,title:typeof s.title==="string"?s.title:"",totalBytes:typeof s.totalBytes==="number"?s.totalBytes:0}))}catch{return null}finally{client.close()}},foregroundEngines:(pids)=>runtime.foregroundEngines(pids),titleTurnHint:(vendor,title)=>runtime.titleTurnHint(vendor,title)}}var DEFAULT_OBSERVER_POLL_MS=1e4,DEFAULT_SILENCE_MS=30000,DEFAULT_WALK_EVERY_TICKS=6,DEFAULT_UNSUBSCRIBED_EVERY_TICKS=6,DEFAULT_CORRECT_AFTER_MS=20000;function startActivityObserver(activity,io,hasSubscribers,options={}){let pollMs=options.pollMs??DEFAULT_OBSERVER_POLL_MS,silenceMs=options.silenceMs??DEFAULT_SILENCE_MS,walkEvery=Math.max(1,options.walkEveryTicks??DEFAULT_WALK_EVERY_TICKS),unsubscribedEvery=Math.max(1,options.unsubscribedEveryTicks??DEFAULT_UNSUBSCRIBED_EVERY_TICKS),correctAfterMs=options.correctAfterMs??DEFAULT_CORRECT_AFTER_MS,log=options.log??logDaemonInfo,tracks=new Map,tickCount=0,unsubscribedTicks=0,inFlight,stopped=!1,bootWalkDone=!1,applyRest=(track,why)=>{if(activity.observeTab(track.taskId,track.tabId,"rest",{...track.vendor?{vendor:track.vendor}:{},correctHookRunningAfterMs:correctAfterMs})==="corrected-hook-running")log("activity-observe",`idled hook-claimed running for ${track.taskId}/${track.tabId} (${why})`)},tick=async()=>{let effects=[];try{let subscribed=hasSubscribers();if(!subscribed&&unsubscribedTicks++%unsubscribedEvery!==0)return;let walkTick=!subscribed||tickCount%walkEvery===0;tickCount++;let listed=await io.listSessions();if(stopped)return;if(listed===null){for(let[key,track]of tracks)activity.observeTab(track.taskId,track.tabId,"rest",{}),tracks.delete(key);return}let sessions=listed,seen=new Set,now=Date.now(),live=[],newborn=new Set;for(let s of sessions){let parts=s.key.split("::");if(parts.length!==2||!parts[0]||!parts[1])continue;if(seen.add(s.key),!s.alive){let track2=tracks.get(s.key);applyRest(track2??{taskId:parts[0],tabId:parts[1],vendor:void 0},"session exited"),tracks.delete(s.key);continue}let track=tracks.get(s.key);if(!track)track={taskId:parts[0],tabId:parts[1],firstSeenAt:now,lastTotalBytes:s.totalBytes,lastTitle:s.title,lastActivityAt:null,vendor:void 0,enginePid:null},tracks.set(s.key,track),newborn.add(s.key);else if(s.totalBytes!==track.lastTotalBytes||s.title!==track.lastTitle)track.lastTotalBytes=s.totalBytes,track.lastTitle=s.title,track.lastActivityAt=now;live.push(track)}for(let[key,track]of tracks){if(seen.has(key)&&sessions.some((s)=>s.key===key&&s.alive))continue;applyRest(track,"session gone"),tracks.delete(key)}let toWalk=live.filter((t)=>walkTick||newborn.has(`${t.taskId}::${t.tabId}`)||t.vendor===void 0);if(toWalk.length>0){let pidByKey=new Map;for(let s of sessions)if(s.alive&&s.pid!==null)pidByKey.set(s.key,s.pid);try{let pids=toWalk.map((t)=>pidByKey.get(`${t.taskId}::${t.tabId}`)).filter((pid)=>pid!==void 0),verdicts=await io.foregroundEngines(pids);if(stopped)return;let bootWalk=!bootWalkDone;bootWalkDone=!0;for(let track of toWalk){let pid=pidByKey.get(`${track.taskId}::${track.tabId}`);if(pid===void 0||!verdicts.has(pid))continue;let found=verdicts.get(pid)??null;if(found===null&&typeof track.vendor==="string")effects.push(io.onEngineExit?.({taskId:track.taskId,tabId:track.tabId,vendor:track.vendor,pid:track.enginePid}));else if(found===null&&track.vendor===void 0&&bootWalk)effects.push(io.onEngineAbsentAtStart?.({taskId:track.taskId,tabId:track.tabId}));track.vendor=found?.vendor??null,track.enginePid=found?.pid??null}}catch{}}if(stopped)return;for(let track of live){let key=`${track.taskId}::${track.tabId}`,session=sessions.find((s)=>s.key===key);if(!session||track.vendor===void 0)continue;if(effects.push(io.onEngineEvidence?.(track.taskId,track.tabId,{walkVendor:track.vendor,title:session.title})),track.vendor===null){applyRest(track,"no engine in foreground");continue}let silentForMs=now-(track.lastActivityAt??track.firstSeenAt),hint=io.titleTurnHint(track.vendor,session.title),claimWorking=(why)=>{if(activity.observeTab(track.taskId,track.tabId,"working",{vendor:track.vendor??void 0})==="observed-running")log("activity-observe",`seeded running for ${track.taskId}/${track.tabId} (${why})`)};if(hint==="rest")applyRest(track,"engine title at rest");else if(track.lastActivityAt!==null&&now-track.lastActivityAt<silenceMs)claimWorking("live output");else if(hint==="working"&&silentForMs<silenceMs)claimWorking("working title frame");else if(silentForMs>=silenceMs)applyRest(track,`no output for ${Math.round(silentForMs/1000)}s`)}}catch{}finally{await Promise.allSettled(effects)}},schedule=()=>{if(stopped||inFlight)return;inFlight=tick().finally(()=>{inFlight=void 0})};schedule();let timer=setInterval(schedule,pollMs);return timer.unref?.(),async()=>{stopped=!0,clearInterval(timer),await inFlight}}function isThenable(value){return typeof value?.then==="function"}function startTicker(opts){if(opts.tickMs<=0)return async()=>{};let running=null,stopped=!1,tick=()=>{if(stopped||opts.gate&&!opts.gate())return;if(running)return;let result;try{result=opts.run()}catch(err){logDaemonError(opts.name,err);return}if(!isThenable(result))return;running=Promise.resolve(result).then(()=>{return}).catch((err)=>logDaemonError(opts.name,err)).finally(()=>{running=null})};if(opts.immediate)tick();let timer=setInterval(tick,opts.tickMs);return timer.unref?.(),async()=>{try{if(!stopped)stopped=!0,clearInterval(timer),opts.onStop?.()}finally{await running}}}var DEFAULT_AUTO_TITLE_POLL_MS=4000;async function runAutoTitlePass(orch,derive,placeholderTitle="(new task)",defaultVendor="claude"){let renamed=[],snapshot=orch.listTasks();for(let task of snapshot){if(task.title!==placeholderTitle||!task.worktreePath)continue;try{let derived=await derive(task.worktreePath,task.vendor??defaultVendor);if(!derived)continue;let title=withGroupOrdinal(derived,task.id,task.groupId,snapshot),current=orch.getTask(task.id);if(!current||current.title!==placeholderTitle)continue;await orch.setTitle(task.id,title),renamed.push({id:task.id,title})}catch(err){logDaemonError("auto-title-poller",err)}}return renamed}function withGroupOrdinal(title,taskId,groupId,snapshot){if(!groupId)return title;let siblings=snapshot.filter((t)=>t.groupId===groupId);if(siblings.length<2)return title;let ordinal=siblings.findIndex((t)=>t.id===taskId);if(ordinal<0)return title;return`${title} #${ordinal+1}/${siblings.length}`}function startAutoTitlePoller(orch,runtime,intervalMs=DEFAULT_AUTO_TITLE_POLL_MS,hasSubscribers){return startTicker({name:"auto-title-poller",tickMs:intervalMs,...hasSubscribers?{gate:hasSubscribers}:{},run:()=>runAutoTitlePass(orch,runtime.deriveTitleFromSession,runtime.placeholderTaskTitle,runtime.defaultTaskVendor)})}import{realpath}from"fs/promises";function automationTarget(automation){if(automation.target)return automation.target;return automation.persistentSession?{kind:"standing",taskId:automation.sessionTaskId}:{kind:"fresh"}}function readAutomationTarget(value){if(value===null)return null;if(typeof value!=="object"||!value||!("kind"in value)||value.kind!=="existing-tab"||!("taskId"in value)||typeof value.taskId!=="string"||!value.taskId.trim()||!("tabId"in value)||typeof value.tabId!=="string"||!/^tab-[\w-]+$/.test(value.tabId))throw Error("target must be { kind: 'existing-tab', taskId, tabId } or null");return{kind:"existing-tab",taskId:value.taskId.trim(),tabId:value.tabId}}function assertAutomationTargetOptions(value){if(!value.target)return;if(value.vendor||value.baseRef||value.persistentSession)throw Error("an existing-tab target cannot set vendor, baseRef or persistentSession; clear them when binding")}function mergeAutomationTargetOptions(automation,patch){return{target:patch.target===null?void 0:patch.target??automation.target,vendor:patch.vendor===null?void 0:patch.vendor??automation.vendor,baseRef:patch.baseRef===null?void 0:patch.baseRef??automation.baseRef,persistentSession:patch.persistentSession??automation.persistentSession,sessionTaskId:patch.target||patch.sessionTaskId===null?void 0:patch.sessionTaskId??automation.sessionTaskId}}async function assertAutomationTargetTask(value,orch){if(!value.target)return;let task=orch.getTask(value.target.taskId);if(!task||task.deletion||!task.worktreePath)throw Error(`target task unavailable: ${value.target.taskId}`);let[repo,taskRepo]=await Promise.all([realpath(value.repo),realpath(task.repo)]);if(repo!==taskRepo)throw Error(`routine repo must match target task repo: ${task.repo}`)}import{randomUUID}from"crypto";import{readFile}from"fs/promises";import{homedir}from"os";import{join as join2}from"path";var DEFERRED_PROMPT_TTL_MS=86400000;class DeferredPromptPendingError extends Error{existing;constructor(existing){super(`task ${existing.taskId} tab ${existing.tabId} already has a deferred prompt (${existing.id})`);this.existing=existing;this.name="DeferredPromptPendingError"}}function defaultDeferredPromptsPath(homeDir=readRoveHomeDirEnv()??homedir()){return join2(homeDir,ROVE_STATE_DIR_BASENAME,"deferred-prompts.json")}function normalizeRecord(value){if(!value||typeof value!=="object"||Array.isArray(value))return null;let raw=value;if(typeof raw.id!=="string"||raw.id.length===0)return null;if(typeof raw.taskId!=="string"||raw.taskId.length===0)return null;if(typeof raw.tabId!=="string"||raw.tabId.length===0)return null;if(typeof raw.prompt!=="string"||raw.prompt.length===0)return null;if(raw.layer!=="recent-human-write"&&raw.layer!=="composer-not-empty")return null;if(typeof raw.at!=="number"||!Number.isFinite(raw.at))return null;return{id:raw.id,taskId:raw.taskId,tabId:raw.tabId,prompt:raw.prompt,layer:raw.layer,...typeof raw.senderLabel==="string"?{senderLabel:raw.senderLabel}:{},...typeof raw.senderTaskId==="string"?{senderTaskId:raw.senderTaskId}:{},at:raw.at,...typeof raw.deliveredAt==="number"&&Number.isFinite(raw.deliveredAt)?{deliveredAt:raw.deliveredAt}:{},...typeof raw.deliveryStartedAt==="number"&&Number.isFinite(raw.deliveryStartedAt)?{deliveryStartedAt:raw.deliveryStartedAt}:{},...typeof raw.dismissedAt==="number"&&Number.isFinite(raw.dismissedAt)?{dismissedAt:raw.dismissedAt}:{}}}async function readStore(path){try{let parsed=JSON.parse(await readFile(path,"utf8"));if(!Array.isArray(parsed.records))return{version:1,records:[]};return{version:1,records:parsed.records.map(normalizeRecord).filter((r)=>r!==null)}}catch(err){if(err.code==="ENOENT")return{version:1,records:[]};throw err}}async function writeStore(path,records){let body={version:1,records:[...records]};await writeJsonAtomic(path,body)}var SUBSYSTEM="deferred-prompts";function layerCode(layer){return layer==="recent-human-write"?"A":"B"}class DeferredPromptsStore{path;now;claims=new Map;constructor(path=defaultDeferredPromptsPath(),now=()=>Date.now()){this.path=path;this.now=now}enqueue(operation){return serialized(this.path,operation)}async file(record){return await this.enqueue(async()=>{let now=this.now(),store=await readStore(this.path),kept=[],occupied=null;for(let existing of store.records){if(now-existing.at>DEFERRED_PROMPT_TTL_MS){if(this.claims.has(existing.id)){if(existing.taskId===record.taskId&&existing.tabId===record.tabId)throw Error(`deferred prompt ${existing.id} expiry cleanup is in flight`);kept.push(existing);continue}if(existing.taskId===record.taskId&&existing.tabId===record.tabId){logDaemonInfo(SUBSYSTEM,`expired deferred prompt ${existing.id} for ${existing.taskId}::${existing.tabId} replaced by a new deferral`);continue}kept.push(existing);continue}if(existing.taskId===record.taskId&&existing.tabId===record.tabId&&existing.dismissedAt===void 0)occupied=existing;kept.push(existing)}if(occupied)throw new DeferredPromptPendingError(occupied);let next={...record,id:randomUUID()};return kept.push(next),await writeStore(this.path,kept),logDaemonInfo(SUBSYSTEM,`held ${next.id} for ${next.taskId}::${next.tabId} layer=${layerCode(next.layer)} from=${next.senderLabel??"unknown"}`),next})}async get(id){return await this.enqueue(async()=>(await readStore(this.path)).records.find((r)=>r.id===id)??null)}async claim(id){return await this.enqueue(async()=>{if(this.claims.has(id))return{kind:"in-flight"};let record=(await readStore(this.path)).records.find((candidate)=>candidate.id===id);if(!record)return{kind:"missing"};let claimId=randomUUID(),finish=()=>{},done=new Promise((resolve)=>{finish=resolve});return this.claims.set(id,{claimId,done,finish}),{kind:"claimed",claim:{claimId,record}}})}async beginDelivery(claim){return await this.enqueue(async()=>{this.requireClaim(claim);let store=await readStore(this.path),current=store.records.find((record)=>record.id===claim.record.id);if(!current)throw Error(`deferred prompt ${claim.record.id} disappeared while claimed`);let started=current.deliveryStartedAt!==void 0?current:{...current,deliveryStartedAt:this.now()};if(current.deliveryStartedAt===void 0)await writeStore(this.path,store.records.map((record)=>record.id===current.id?started:record));return started})}async resetDelivery(claim){await this.enqueue(async()=>{this.requireClaim(claim);let store=await readStore(this.path),current=store.records.find((record)=>record.id===claim.record.id);if(!current)throw Error(`deferred prompt ${claim.record.id} disappeared while claimed`);if(current.deliveryStartedAt===void 0)return;let{deliveryStartedAt:_deliveryStartedAt,...reset}=current;await writeStore(this.path,store.records.map((record)=>record.id===current.id?reset:record))})}async markDelivered(claim){return await this.enqueue(async()=>{this.requireClaim(claim);let store=await readStore(this.path),current=store.records.find((record)=>record.id===claim.record.id);if(!current)throw Error(`deferred prompt ${claim.record.id} disappeared while claimed`);let delivered=current.deliveredAt?current:{...current,deliveredAt:this.now()};if(!current.deliveredAt)logDaemonInfo(SUBSYSTEM,`released ${current.id} for ${current.taskId}::${current.tabId}`),await writeStore(this.path,store.records.map((record)=>record.id===current.id?delivered:record));return delivered})}async completeClaim(claim){return await this.enqueue(async()=>{this.requireClaim(claim);let store=await readStore(this.path),kept=store.records.filter((record)=>record.id!==claim.record.id);if(kept.length!==store.records.length)await writeStore(this.path,kept);return this.finishClaim(claim.record.id),kept.length!==store.records.length})}async releaseClaim(claim){await this.enqueue(async()=>{this.requireClaim(claim),this.finishClaim(claim.record.id)})}requireClaim(claim){if(this.claims.get(claim.record.id)?.claimId!==claim.claimId)throw Error(`deferred prompt ${claim.record.id} claim is no longer owned`)}finishClaim(id){let active=this.claims.get(id);this.claims.delete(id),active?.finish()}async waitForClaims(taskId,tabId){for(;;){let waiting=await this.enqueue(async()=>{return(await readStore(this.path)).records.filter((record)=>record.taskId===taskId&&(tabId===void 0||record.tabId===tabId)).flatMap((record)=>{let active=this.claims.get(record.id);return active?[active.done]:[]})});if(waiting.length===0)return;await Promise.all(waiting)}}async list(){return await this.enqueue(async()=>{let now=this.now(),store=await readStore(this.path),expired=[],dismissed=[];return{records:store.records.filter((record)=>{if(now-record.at<=DEFERRED_PROMPT_TTL_MS){if(record.dismissedAt!==void 0)return dismissed.push(record),!1;return!0}return expired.push(record),logDaemonInfo(SUBSYSTEM,`expired deferred prompt ${record.id} for ${record.taskId}::${record.tabId} (age ${Math.round((now-record.at)/1000)}s) \u2014 cleanup requested`),!1}),dismissed,expired}})}async resolve(id){return await this.enqueue(async()=>{let store=await readStore(this.path),kept=store.records.filter((r)=>r.id!==id);if(kept.length===store.records.length)return!1;return await writeStore(this.path,kept),!0})}async discardTab(taskId,tabId,reason){return await this.waitForClaims(taskId,tabId),await this.enqueue(async()=>{let store=await readStore(this.path),dropped=store.records.filter((record)=>record.taskId===taskId&&record.tabId===tabId);if(dropped.length===0)return[];for(let record of dropped)logDaemonInfo(SUBSYSTEM,`dropped deferred prompt ${record.id} for ${taskId}::${tabId} \u2014 ${reason}`);return await writeStore(this.path,store.records.filter((record)=>record.taskId!==taskId||record.tabId!==tabId)),dropped})}async discard(id,reason){for(;;){let[waiting]=await this.enqueue(async()=>{let active=this.claims.get(id);return active?[active.done]:[]});if(!waiting)break;await waiting}return await this.enqueue(async()=>{let store=await readStore(this.path),found=store.records.find((record)=>record.id===id);if(!found)return null;if(found.dismissedAt!==void 0)return found;let dismissed={...found,dismissedAt:this.now()},expiresAt=new Date(found.at+DEFERRED_PROMPT_TTL_MS).toISOString();return logDaemonInfo(SUBSYSTEM,`dismissed ${found.id} for ${found.taskId}::${found.tabId} \u2014 ${reason}; text retained until ${expiresAt}`),await writeStore(this.path,store.records.map((record)=>record.id===id?dismissed:record)),dismissed})}async listForTask(taskId){return await this.enqueue(async()=>(await readStore(this.path)).records.filter((r)=>r.taskId===taskId))}async deleteTask(taskId){await this.waitForClaims(taskId),await this.enqueue(async()=>{let store=await readStore(this.path),kept=store.records.filter((r)=>r.taskId!==taskId);if(kept.length===store.records.length)return;for(let dropped of store.records)if(dropped.taskId===taskId)logDaemonInfo(SUBSYSTEM,`dropped deferred prompt ${dropped.id} \u2014 task ${taskId} deleted`);await writeStore(this.path,kept)})}}function resolveStandingTask(orch,automation){let id=automation.sessionTaskId;if(!id)return{task:null,hadStaleLink:!1};let task=orch.getTask(id);if(!task||task.deletion||!task.worktreePath)return{task:null,hadStaleLink:!0};return{task}}async function createRunTask(deps,automation){return await deps.orch.createTask({repo:automation.repo,title:automation.name,...automation.vendor?{vendor:automation.vendor}:{},...automation.baseRef?{baseRef:automation.baseRef}:{},...automation.persistentSession?{routine:{automationId:automation.id}}:{}})}async function spawnWithPrompt(deps,automation,taskId,status){if(deps.canDeliver?.()===!1)return{status:"skipped_cancelled",taskId};let outcome=await deps.runtime.startTaskSessionWithPrompt(deps.link(),taskId,automation.prompt);if(!outcome.started)return{status:"dispatch_failed",taskId,error:outcome.error??"engine session did not start"};return{status,taskId}}async function deferBlockedPrompt(deps,automation,taskId,tabId,layer){if(!deps.deferred||!deps.inbox)return{status:"dispatch_failed",taskId,error:`composer busy (${layer}) and no deferred-prompt store`};let record;try{record=await deps.deferred.file({taskId,tabId,prompt:automation.prompt,layer,senderLabel:`routine: ${automation.name}`,at:(deps.now??Date.now)()})}catch(error){if(!(error instanceof DeferredPromptPendingError))throw error;return await deps.inbox.recordPromptDeferred(error.existing.taskId,error.existing.tabId,error.existing.id,error.existing.layer,error.existing.at+DEFERRED_PROMPT_TTL_MS),{status:"dispatch_failed",taskId,error:`tab ${tabId} already has a deferred prompt (${error.existing.id})`}}let notificationError;return await deps.inbox.recordPromptDeferred(taskId,tabId,record.id,layer,record.at+DEFERRED_PROMPT_TTL_MS).catch((error)=>{logDaemonError("automation-deferred-inbox",error),notificationError=`prompt queued; Inbox notification failed: ${String(error)}`}),logDaemonInfo("automation",`deferred ${automation.name} task=${taskId} tab=${tabId} layer=${layer}`),{status:"deferred",taskId,tabId,deferredId:record.id,...notificationError?{error:notificationError}:{}}}async function dispatchAutomation(deps,automation){if(deps.canDeliver?.()===!1)return{status:"skipped_cancelled"};let target=automationTarget(automation);if(target.kind==="existing-tab"){try{await assertAutomationTargetTask(automation,deps.orch)}catch(error){return{status:"skipped_unavailable",taskId:target.taskId,tabId:target.tabId,error:String(error)}}let task2=deps.orch.getTask(target.taskId);if(!task2||task2.deletion||!task2.worktreePath)return{status:"skipped_unavailable",taskId:target.taskId,tabId:target.tabId,error:"target task unavailable"};if(deps.canDeliver?.()===!1)return{status:"skipped_cancelled",taskId:task2.id,tabId:target.tabId};let pending=(await deps.deferred?.listForTask(task2.id))?.find((record)=>record.tabId===target.tabId);if(pending)return{status:"dispatch_failed",taskId:task2.id,tabId:target.tabId,error:`tab ${target.tabId} already has a deferred prompt (${pending.id})`};if(deps.canDeliver?.()===!1)return{status:"skipped_cancelled",taskId:task2.id,tabId:target.tabId};let currentTask=deps.orch.getTask(task2.id);if(!currentTask||currentTask.deletion||!currentTask.worktreePath)return{status:"skipped_unavailable",taskId:task2.id,tabId:target.tabId,error:"target task unavailable"};let result2=await deps.runtime.deliverPromptToLiveEngineTabDetailed({id:task2.id,tabId:target.tabId,vendor:task2.vendor,command:task2.command,worktreePath:task2.worktreePath},automation.prompt);return await liveDeliveryOutcome(deps,automation,task2.id,result2)}if(target.kind==="fresh"){let task2=await createRunTask(deps,automation);return await spawnWithPrompt(deps,automation,task2.id,"dispatched")}let standing=resolveStandingTask(deps.orch,automation);if(standing.task===null){let task2=await createRunTask(deps,automation);return{...await spawnWithPrompt(deps,automation,task2.id,"dispatched"),sessionTaskIdToSet:task2.id,...standing.hadStaleLink?{sessionTaskIdToClear:!0}:{}}}let task=standing.task,result=await deps.runtime.deliverPromptToLiveEngineDetailed({id:task.id,vendor:task.vendor,command:task.command,worktreePath:task.worktreePath},automation.prompt);if(result.outcome==="delivered"||result.outcome==="busy")return await liveDeliveryOutcome(deps,automation,task.id,result);return await spawnWithPrompt(deps,automation,task.id,"revived")}async function liveDeliveryOutcome(deps,automation,taskId,result){switch(result.outcome){case"delivered":return logDaemonInfo("automation",`delivered ${automation.name} task=${taskId} tab=${result.tabId}`),{status:"dispatched",taskId,tabId:result.tabId};case"busy":return await deferBlockedPrompt(deps,automation,taskId,result.tabId,result.layer);case"no-engine":case"no-session":return{status:"dispatch_failed",taskId,tabId:automation.target?.tabId,error:result.outcome==="no-engine"?"target engine exited; restart it explicitly":"target tab has no live session"}}}import{spawn as spawn2}from"child_process";import{existsSync}from"fs";var MAX_OUTPUT_CHARS=4000,MAX_TIMEOUT_SECONDS=600;function killGroup(child){let pid=child.pid;if(pid===void 0)return;try{process.kill(-pid,"SIGKILL")}catch{try{child.kill("SIGKILL")}catch{}}}function withWatchdog(command,timeoutSeconds){return["set +m 2>/dev/null",`{ sleep ${timeoutSeconds} && { kill -9 -$$ 2>/dev/null || kill -9 $$ ; } ; } >/dev/null 2>&1 </dev/null &`,"__rove_watchdog=$!",`trap 'kill "$__rove_watchdog" 2>/dev/null' EXIT`,command].join(`
|
|
4
|
-
`)}function tail(chunks){let text=Buffer.concat(chunks.map((c)=>typeof c==="string"?Buffer.from(c):c)).toString("utf8");if(text.length<=MAX_OUTPUT_CHARS)return text;let points=Array.from(text);return points.length<=MAX_OUTPUT_CHARS?text:points.slice(-MAX_OUTPUT_CHARS).join("")}function runAutomationPrecheck(precheck,cwd,shell=resolveLoginShell({fallback:"/bin/sh"})){let startedAt=Date.now(),timeoutMs=Math.min(Math.max(precheck.timeoutSeconds,1),MAX_TIMEOUT_SECONDS)*1000;if(!existsSync(cwd))return Promise.resolve({exitCode:null,timedOut:!1,stdout:"",stderr:`working directory does not exist: ${cwd}`,durationMs:0});return new Promise((resolve)=>{let out=[],err=[],settled=!1,timedOut=!1,finish=(exitCode,timedOut2)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({exitCode,timedOut:timedOut2,stdout:tail(out),stderr:tail(err),durationMs:Date.now()-startedAt})},timer=setTimeout(()=>{if(timedOut=!0,child)killGroup(child);finish(null,!0)},timeoutMs);timer.unref?.();let child;try{child=spawn2(shell,["-ilc",withWatchdog(precheck.command,timeoutMs/1000+1)],{cwd,stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32"})}catch(spawnError){err.push(spawnError instanceof Error?spawnError.message:String(spawnError)),finish(null,!1);return}child.stdout?.on("data",(chunk)=>out.push(chunk)),child.stderr?.on("data",(chunk)=>err.push(chunk)),child.on("error",(spawnError)=>{if(timedOut){finish(null,!0);return}err.push(spawnError.message),finish(null,!1)}),child.on("close",(code)=>finish(code,timedOut))})}function precheckPassed(result){return result.exitCode===0&&!result.timedOut}function formatPrecheckSkip(result){if(result.timedOut)return`precheck timed out after ${result.durationMs}ms`;if(result.exitCode===null)return`precheck could not run: ${result.stderr.trim()||"unknown error"}`;return`precheck exited ${result.exitCode}`}var DEFAULT_AUTOMATION_TICK_MS=60000;function dueAutomations(automations,nowMs){return automations.filter((a)=>{if(!a.enabled)return!1;let at=Date.parse(a.nextRunAt);return Number.isFinite(at)&&at<=nowMs})}function resolveDueOccurrence(automation,nowMs,tickMs=DEFAULT_AUTOMATION_TICK_MS){let notBefore=Date.parse(automation.createdAt),scheduledFor=latestCronAtOrBefore(automation.schedule,nowMs,Number.isFinite(notBefore)?notBefore:0);if(scheduledFor===null)return null;let graceMs=automation.missedRunGraceMinutes*60000+Math.max(tickMs,0);return{scheduledFor,missed:nowMs-scheduledFor>graceMs}}function runEventFor(status){if(status==="dispatched"||status==="revived")return"automation.dispatched";if(status==="dispatch_failed")return"automation.failed";return"automation.skipped"}function resolveLink(link){return typeof link==="function"?link():link}function emitRunEvent(deps,automation,status,args,extra){deps.plugins?.()?.handleUiReport({kind:runEventFor(status),...extra.taskId?{taskId:extra.taskId}:{},detail:{automationId:automation.id,name:automation.name,repo:automation.repo,status,trigger:args.trigger,scheduledFor:new Date(args.scheduledFor).toISOString(),...extra.tabId?{tabId:extra.tabId}:{},...extra.deferredId?{deferredId:extra.deferredId}:{},...extra.error?{error:extra.error}:{}}})}async function raiseOrClearInboxEpisode(deps,automation,status,extra){let inbox=deps.inbox;if(!inbox)return;let now=deps.now??Date.now;await(automationRunNeedsAttention(status)?inbox.recordRoutineFailure({automationId:automation.id,name:automation.name,status,...extra.error?{error:extra.error}:{}},extra.taskId??null,now()):inbox.deleteRoutineEpisode?.(automation.id))?.catch((err)=>logDaemonError("automation-inbox",err))}async function recordSkip(deps,automation,status,scheduledFor,error){let now=deps.now??Date.now;await deps.store.recordRun({automationId:automation.id,scheduledFor:new Date(scheduledFor).toISOString(),status,trigger:"scheduled",at:new Date(now()).toISOString(),error}),emitRunEvent(deps,automation,status,{scheduledFor,trigger:"scheduled"},{error})}function droppedOccurrences(automation,scheduledFor){let armedAt=Date.parse(automation.nextRunAt);if(!Number.isFinite(armedAt)||armedAt>=scheduledFor)return null;let count=countCronBetween(automation.schedule,armedAt,scheduledFor);return count>0?{count,firstMs:armedAt}:null}async function runAutomationOnce(deps,automation,args){let now=deps.now??Date.now,record=async(status,extra={})=>{return await deps.store.recordRun({automationId:automation.id,scheduledFor:new Date(args.scheduledFor).toISOString(),status,trigger:args.trigger,at:new Date(now()).toISOString(),...extra}),emitRunEvent(deps,automation,status,args,extra),await raiseOrClearInboxEpisode(deps,automation,status,extra),status},cancelled=()=>deps.stopped?.()===!0||deps.store.get(automation.id)!==automation||args.trigger==="scheduled"&&!automation.enabled;if(cancelled())return await record("skipped_cancelled",{error:"routine disabled, changed, deleted or runner stopped before delivery"});if(args.trigger==="scheduled"&&automation.precheck){let result=await runAutomationPrecheck(automation.precheck,automation.repo);if(!precheckPassed(result))return logDaemonInfo("automation",`skip ${automation.name}: ${formatPrecheckSkip(result)}`),await record("skipped_precheck",{precheckResult:result,error:formatPrecheckSkip(result)})}if(cancelled())return await record("skipped_cancelled",{error:"routine disabled, changed, deleted or runner stopped before delivery"});let outcome;try{outcome=await dispatchAutomation({canDeliver:()=>!cancelled(),orch:deps.orch,runtime:deps.runtime,link:()=>resolveLink(deps.link),...deps.deferred?{deferred:deps.deferred}:{},...deps.inbox?{inbox:deps.inbox}:{},...deps.now?{now:deps.now}:{}},automation)}catch(err){let error=err instanceof Error?err.message:String(err);return logDaemonError("automation-dispatch",err),await record("skipped_unavailable",{error})}if(outcome.sessionTaskIdToSet)await deps.store.update(automation.id,{sessionTaskId:outcome.sessionTaskIdToSet}).catch((err)=>logDaemonError("automation-session-link",err));else if(outcome.sessionTaskIdToClear)await deps.store.update(automation.id,{sessionTaskId:null}).catch((err)=>logDaemonError("automation-session-unlink",err));if(outcome.status==="dispatched"||outcome.status==="revived")logDaemonInfo("automation",`${outcome.status} ${automation.name} task=${outcome.taskId}`);return await record(outcome.status,{...outcome.taskId?{taskId:outcome.taskId}:{},...outcome.tabId?{tabId:outcome.tabId}:{},...outcome.deferredId?{deferredId:outcome.deferredId}:{},...outcome.error?{error:outcome.error}:{}})}async function sweepAutomations(deps,tickMs=DEFAULT_AUTOMATION_TICK_MS){let now=deps.now??Date.now;for(let automation of dueAutomations(deps.store.list(),now())){if(deps.stopped?.())return;let nowMs=now(),occurrence=resolveDueOccurrence(automation,nowMs,tickMs);if(!occurrence){await deps.store.advanceNextRun(automation.id,nowMs);continue}let dropped=droppedOccurrences(automation,occurrence.scheduledFor),claimed=await deps.store.advanceNextRun(automation.id,occurrence.scheduledFor,automation.nextRunAt);if(!claimed)continue;if(dropped){let first=new Date(dropped.firstMs).toISOString();await recordSkip(deps,automation,"skipped_missed",dropped.firstMs,`${dropped.count} earlier occurrence${dropped.count===1?"":"s"} never ran (from ${first})`).catch((err)=>logDaemonError("automation-dropped",err))}if(occurrence.missed){await recordSkip(deps,automation,"skipped_missed",occurrence.scheduledFor,`missed by more than the ${automation.missedRunGraceMinutes}m grace window`);continue}await runAutomationOnce(deps,claimed,{scheduledFor:occurrence.scheduledFor,trigger:"scheduled"}).catch((err)=>logDaemonError("automation-run",err))}}function startAutomationRunner(deps,tickMs=DEFAULT_AUTOMATION_TICK_MS){let stopped=!1;return startTicker({name:"automation-sweep",tickMs,run:()=>sweepAutomations({...deps,stopped:()=>stopped},tickMs),onStop:()=>{stopped=!0}})}var DEFAULT_CONTEXT_USAGE_TICK_MS=1e4,CONTEXT_USAGE_TIMEOUT_MS=4000,CONTEXT_USAGE_SLOW_RETRY_MS=60000,CONTEXT_USAGE_MIN_INTERVAL_MS=5000;function sameContextUsage(a,b){return a.contextTokens===b.contextTokens&&a.contextWindowTokens===b.contextWindowTokens&&a.approximate===b.approximate&&a.inputTokens===b.inputTokens&&a.outputTokens===b.outputTokens&&a.cacheReadTokens===b.cacheReadTokens&&a.cacheCreationTokens===b.cacheCreationTokens}function contextUsageTargets(states,getVendor){let seen=new Set,targets=[];for(let state of states){if(!state.tabId||!state.sessionId)continue;let vendor=getVendor(state.taskId);if(!vendor)continue;let key=`${state.taskId}::${state.tabId}`;if(seen.has(key))continue;seen.add(key),targets.push({key,vendor,sessionId:state.sessionId})}return targets}class ContextUsageCollector{registry;orch;bus;runtime;options;entries=new Map;stopped=!1;constructor(registry,orch,bus,runtime,options={}){this.registry=registry;this.orch=orch;this.bus=bus;this.runtime=runtime;this.options=options}tick(){if(this.stopped)return;if(this.options.hasSubscribers&&!this.options.hasSubscribers())return;try{let targets=contextUsageTargets(this.registry.currentNonIdle(),(id)=>this.orch.getTask(id)?.vendor),live=new Set(targets.map((t)=>t.key)),pruned=!1;for(let key of[...this.entries.keys()]){if(live.has(key))continue;if(this.entries.get(key)?.value)pruned=!0;this.entries.delete(key)}if(pruned)this.publish();for(let target of targets)this.collect(target)}catch(err){logDaemonError("context-usage",err)}}stop(){this.stopped=!0}collect(target){let entry=this.entries.get(target.key);if(!entry)entry={inFlight:!1,nextAllowedAt:0},this.entries.set(target.key,entry);if(entry.sessionId!==void 0&&entry.sessionId!==target.sessionId){let had=entry.value!==void 0;if(entry.value=void 0,entry.nextAllowedAt=0,entry.sessionId=target.sessionId,had)this.publish()}entry.sessionId=target.sessionId;let cadence=this.options.cadence??{timeoutMs:CONTEXT_USAGE_TIMEOUT_MS,slowRetryMs:CONTEXT_USAGE_SLOW_RETRY_MS,minIntervalMs:CONTEXT_USAGE_MIN_INTERVAL_MS},read=this.options.read??((t)=>this.runtime.readEngineContextUsage(t.vendor,t.sessionId)),startedAt=Date.now();if(entry.inFlight||startedAt<entry.nextAllowedAt)return;entry.inFlight=!0;let controller=new AbortController,timer=setTimeout(()=>controller.abort(),cadence.timeoutMs),held=entry;read(target,controller.signal).then((value)=>{if(controller.signal.aborted||this.stopped)return;if(this.entries.get(target.key)!==held)return;if(!value)return;if(held.value&&sameContextUsage(held.value,value))return;held.value=value,this.publish()}).catch(()=>{}).finally(()=>{clearTimeout(timer);let finishedAt=Date.now();held.nextAllowedAt=controller.signal.aborted?startedAt+cadence.slowRetryMs:finishedAt+Math.max(cadence.minIntervalMs,(finishedAt-startedAt)*5),held.inFlight=!1})}publish(){let context={};for(let[key,entry]of this.entries)if(entry.value)context[key]=entry.value;this.bus.publish("usage.context",{context})}}function startContextUsageCollector(registry,orch,bus,runtime,tickMs=DEFAULT_CONTEXT_USAGE_TICK_MS,hasSubscribers){if(tickMs<=0)return()=>{};let collector=new ContextUsageCollector(registry,orch,bus,runtime,{hasSubscribers});collector.tick();let timer=setInterval(()=>collector.tick(),tickMs);return timer.unref?.(),()=>{clearInterval(timer),collector.stop()}}var DEFAULT_DEFERRED_SWEEP_TICK_MS=3600000;function errorText(error){return error instanceof Error?error.message:String(error)}async function sweepExpiredDeferredPrompts(deps){let report={expired:[],cleanupPending:[]},listed=await deps.store.list();for(let expired of listed.expired){let claimed=await deps.store.claim(expired.id);if(claimed.kind!=="claimed"){report.cleanupPending.push(failure(expired,claimed.kind));continue}try{if(expired.dismissedAt===void 0)await deps.inbox.recordPromptExpired(expired.taskId,expired.tabId,expired.id,(deps.now??Date.now)());await deps.store.completeClaim(claimed.claim),report.expired.push(expired.id)}catch(error){await deps.store.releaseClaim(claimed.claim).catch(()=>{}),report.cleanupPending.push(failure(expired,errorText(error)))}}return report}function failure(record,error){return{id:record.id,taskId:record.taskId,tabId:record.tabId,error}}function startDeferredPromptSweep(deps,tickMs=DEFAULT_DEFERRED_SWEEP_TICK_MS){return startTicker({name:"deferred-prompt-sweep",tickMs,immediate:!0,run:()=>sweepExpiredDeferredPrompts(deps)})}import{basename as basename2}from"path";var DEFAULT_KEYBINDINGS_DEBOUNCE_MS=200;function startKeybindingsWatcher(bus,options={}){let debounceMs=options.debounceMs??DEFAULT_KEYBINDINGS_DEBOUNCE_MS;if(debounceMs<=0)return()=>{};let filePath=options.path??defaultKeybindingsPath(),baseYaml=basename2(filePath),baseYml=baseYaml.replace(/\.yaml$/,".yml"),rev=0;return bus.publish("keybindings",{rev}),startFileWatchTrigger({filePath,matchBasenames:[baseYaml,baseYml],debounceMs,onTrigger:()=>{try{rev+=1,bus.publish("keybindings",{rev})}catch(err){logDaemonError("keybindings-watcher",err)}},onError:(err)=>logDaemonError("keybindings-watcher",err)})}import{spawn as spawn3}from"child_process";var PR_STATE_RANK={OPEN:2,MERGED:1,CLOSED:1};function pickPr(views){let best,bestRank=-1;for(let view of views){let rank=PR_STATE_RANK[(view.state??"").toUpperCase()]??0;if(rank>bestRank)best=view,bestRank=rank}return best}var DEFAULT_PR_STATUS_POLL_MS=30000,NO_PR_BACKOFF_MS=300000,SETTLED_BACKOFF_MS=600000,PR_FAILURE_BASE_MS=DEFAULT_PR_STATUS_POLL_MS,PR_FAILURE_CAP_MS=900000,NO_REMOTE_BACKOFF_MS=1800000,PR_POLL_JITTER_RATIO=0.2,PR_VIEW_TIMEOUT_MS=1e4,PR_POLL_CONCURRENCY=8;function decodeSpawnChunks(chunks){return Buffer.concat(chunks.map((c)=>typeof c==="string"?Buffer.from(c):c)).toString("utf8")}function spawnGh(args,cwd,signal){return new Promise((resolve)=>{let outChunks=[],errChunks=[],settled=!1,finish=(status,spawnError)=>{if(settled)return;settled=!0,resolve({status,stdout:decodeSpawnChunks(outChunks),stderr:decodeSpawnChunks(errChunks),spawnError})},child=spawn3("gh",args.slice(),{cwd,stdio:["ignore","pipe","pipe"],signal,killSignal:"SIGKILL"});child.stdout?.on("data",(chunk)=>{outChunks.push(chunk)}),child.stderr?.on("data",(chunk)=>{errChunks.push(chunk)}),child.on("error",()=>finish(null,!signal.aborted)),child.on("close",(code)=>finish(code,!1))})}function makeGhPrViewRunner(classify,viewFields){return async(worktreePath,branch)=>{let controller=new AbortController,timedOut=!1,timer=setTimeout(()=>{timedOut=!0,controller.abort()},PR_VIEW_TIMEOUT_MS);try{let res=await spawnGh(["pr","list","--head",branch,"--state","all","--json",viewFields],worktreePath,controller.signal);if(res.status===0&&!timedOut)try{let views=JSON.parse(res.stdout),picked=Array.isArray(views)?pickPr(views):void 0;return picked?{kind:"pr",view:picked}:{kind:"empty"}}catch{return classify({parseError:!0})}return classify({spawnError:res.spawnError,timedOut,exitCode:res.status,stderr:res.stderr})}finally{clearTimeout(timer)}}}function isPrPollable(task){if(task.kind==="main")return!1;if(!task.branch||!task.worktreePath)return!1;if(task.repo.startsWith("ssh://")||task.worktreePath.startsWith("ssh://"))return!1;return!0}async function setPrStaleMarker(orch,taskId,error){let prev=orch.getTask(taskId)?.prStatus;if(!prev||prev.lastError===error)return!1;let{lastError:_cleared,...rest}=prev;return await orch.setPRStatus(taskId,error===void 0?rest:{...rest,lastError:error}),!0}async function runPrStatusPass(orch,opts){let tickMs=opts.tickMs??DEFAULT_PR_STATUS_POLL_MS,rand=opts.rand,cfg={tickMs,settledMs:SETTLED_BACKOFF_MS,noPrMs:NO_PR_BACKOFF_MS,noRemoteMs:NO_REMOTE_BACKOFF_MS,failureBaseMs:PR_FAILURE_BASE_MS,failureCapMs:PR_FAILURE_CAP_MS,jitterRatio:PR_POLL_JITTER_RATIO},tasks=orch.listTasks(),live=new Set(tasks.map((task)=>task.id));for(let id of opts.schedule.keys())if(!live.has(id))opts.schedule.delete(id);let due=[];for(let task of tasks){if(!isPrPollable(task)){opts.schedule.delete(task.id);continue}let entry=opts.schedule.get(task.id);if(entry&&opts.now<entry.nextAllowedAt)continue;due.push({task,prevFailures:entry?.failures??0})}let pollOne=async(task,prevFailures)=>{try{let result=await opts.run(task.worktreePath,task.branch);if(result.kind==="error"){logDaemonInfo("pr-status-poller",`gh pr list failed (${result.error}) for task ${task.id} [${task.branch}] \u2014 keeping last PR status, backing off`);let marked=await setPrStaleMarker(orch,task.id,result.error);return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"error",error:result.error},prevFailures,opts.now,cfg,rand)),marked?task.id:void 0}if(result.kind==="empty"){let marked=await setPrStaleMarker(orch,task.id,void 0);return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"empty"},prevFailures,opts.now,cfg,rand)),marked?task.id:void 0}let next=opts.runtime.prStatus.mapView(result.view,opts.at),current=orch.getTask(task.id);if(!current){opts.schedule.delete(task.id);return}let wasStale=current.prStatus?.lastError!==void 0,changedId;if(wasStale||!opts.runtime.prStatus.sameStatus(current.prStatus,next??void 0))await orch.setPRStatus(task.id,next),changedId=task.id;let settled=next?.lifecycle==="merged"||next?.lifecycle==="closed";return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"pr",settled},prevFailures,opts.now,cfg,rand)),changedId}catch(err){logDaemonError("pr-status-poller",err);let marked=await setPrStaleMarker(orch,task.id,"network").catch(()=>!1);return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"error",error:"network"},prevFailures,opts.now,cfg,rand)),marked?task.id:void 0}},slots=Array(due.length),cursor=0,worker=async()=>{for(let i=cursor++;i<due.length;i=cursor++){let item=due[i];if(item)slots[i]=await pollOne(item.task,item.prevFailures)}};return await Promise.all(Array.from({length:Math.min(PR_POLL_CONCURRENCY,due.length)},worker)),slots.filter((id)=>id!==void 0)}function startPrStatusPoller(orch,runtime,intervalMs=DEFAULT_PR_STATUS_POLL_MS,hasSubscribers,run=makeGhPrViewRunner(runtime.prStatus.classify,runtime.prStatus.viewFields),hasWorkingAgent){let schedule=new Map;return startTicker({name:"pr-status-poller",tickMs:intervalMs,...hasSubscribers?{gate:()=>hasSubscribers()||(hasWorkingAgent?.()??!1)}:{},run:()=>runPrStatusPass(orch,{runtime,run,now:Date.now(),at:new Date().toISOString(),schedule,tickMs:intervalMs})})}function quotaResumeContinuePrompt(language){return language==="zh"?"\u4ECE\u4E2D\u65AD\u7684\u5730\u65B9\u7EE7\u7EED\u8FD9\u4E2A\u4EFB\u52A1\u3002\u4E0A\u9762\u7684\u5BF9\u8BDD\u8BB0\u5F55\u91CC\u5DF2\u7ECF\u5B8C\u6210\u7684\u5DE5\u4F5C\u4E0D\u8981\u91CD\u505A\u3002":"Continue the task from where it stopped. The transcript above shows the work already completed \u2014 do not redo it."}var DEFAULT_QUOTA_RESUME_TICK_MS=60000;function dueQuotaResumes(tasks,nowMs){return tasks.filter((task)=>{if(!task.quotaResume||!task.worktreePath)return!1;if(task.deletion)return!1;let at=Date.parse(task.quotaResume.resumeAt);return Number.isFinite(at)&&at<=nowMs})}function exhaustedResetAtMs(usage,nowMs){let candidates=usage.windows.filter((w)=>w.percent>=100&&w.resetsAt!=null&&w.resetsAt>nowMs).map((w)=>w.resetsAt);return candidates.length>0?Math.min(...candidates):null}async function scheduleQuotaResume(orch,runtime,cache,taskId,now=Date.now,plugins){let task=orch.getTask(taskId);if(!task||!task.worktreePath||task.deletion)return;let usage=await cache.get(task.vendor??runtime.defaultTaskVendor,0),resetAtMs=usage?exhaustedResetAtMs(usage,now()):null;if(resetAtMs==null)return;let resumeAt=new Date(resetAtMs).toISOString();await orch.setQuotaResume(taskId,{resumeAt,requestedAt:new Date(now()).toISOString()}),logDaemonInfo("quota-resume",`armed task=${taskId} resumeAt=${resumeAt}`),plugins?.()?.handleUiReport({kind:"quota.exhausted",taskId,detail:{vendor:task.vendor??runtime.defaultTaskVendor,resumeAt}})}async function resumeDueTask(orch,runtime,task,plugins){await orch.setQuotaResume(task.id,null);let delivered=await runtime.deliverPromptToLiveEngine({id:task.id,vendor:task.vendor,command:task.command,worktreePath:task.worktreePath},quotaResumeContinuePrompt(task.observedLanguage));logDaemonInfo("quota-resume",`resume task=${task.id} delivered=${delivered}`),plugins?.()?.handleUiReport({kind:"quota.resumed",taskId:task.id,detail:{delivered}})}function startQuotaResumeRunner(orch,runtime,tickMs=DEFAULT_QUOTA_RESUME_TICK_MS,now=Date.now,plugins){return startTicker({name:"quota-resume",tickMs,run:async()=>{for(let task of dueQuotaResumes(orch.listTasks(),now()))await resumeDueTask(orch,runtime,task,plugins).catch((err)=>logDaemonError("quota-resume",err))}})}var FRESH_POLL_MS=900000,RETRY_BASE_MS=60000,MIN_FETCH_INTERVAL_MS=60000;class QuotaUsageCache{runtime;bus;now;entries=new Map;constructor(runtime,bus,now=Date.now){this.runtime=runtime;this.bus=bus;this.now=now}peek(vendor){return this.entries.get(vendor)?.usage??null}async get(vendor,maxAgeMs){let entry=this.entry(vendor),age=this.now()-(entry.usage?.capturedAt??0);if(entry.usage&&age<=maxAgeMs)return entry.usage;if(entry.inFlight)return entry.inFlight;if(this.now()-entry.attemptedAt<MIN_FETCH_INTERVAL_MS)return entry.usage;return this.fetch(vendor,entry)}async refreshIfDue(vendor){let entry=this.entry(vendor),retryAfter=entry.failures===0?0:Math.min(RETRY_BASE_MS*2**(entry.failures-1),FRESH_POLL_MS),dueAfter=entry.usage?FRESH_POLL_MS:retryAfter;if(this.now()-entry.attemptedAt<dueAfter)return;await this.fetch(vendor,entry)}entry(vendor){let existing=this.entries.get(vendor);if(existing)return existing;let fresh={usage:null,attemptedAt:0,failures:0,inFlight:null};return this.entries.set(vendor,fresh),fresh}fetch(vendor,entry){if(entry.inFlight)return entry.inFlight;return entry.attemptedAt=this.now(),entry.inFlight=this.runtime.quotaUsage(vendor).catch(()=>null).then((usage)=>{if(entry.inFlight=null,usage){entry.failures=0;let changed=JSON.stringify(usage.windows)!==JSON.stringify(entry.usage?.windows);if(entry.usage=usage,changed)this.publish()}else entry.failures+=1;return entry.usage}),entry.inFlight}publish(){let usage={};for(let[vendor,entry]of this.entries)if(entry.usage)usage[vendor]=entry.usage;this.bus.publish("usage.snapshot",{usage})}}function startQuotaUsagePoller(cache,listVendors,hasSubscribers,tickMs=MIN_FETCH_INTERVAL_MS){return startTicker({name:"quota-usage-poller",tickMs,gate:hasSubscribers,immediate:!0,run:()=>{for(let vendor of new Set(listVendors()))cache.refreshIfDue(vendor)}})}function isRemoteRepoKey(value){return value.startsWith("ssh://")}var DEFAULT_TRANSCRIPT_ACTIVITY_TICK_MS=1500,TRANSCRIPT_ACTIVITY_TIMEOUT_MS=4000,TRANSCRIPT_ACTIVITY_SLOW_RETRY_MS=60000,TRANSCRIPT_ACTIVITY_MIN_INTERVAL_MS=1500;function sameTranscriptActivityEntry(a,b){return a.mtimeMs===b.mtimeMs&&a.completionId===b.completionId&&a.completionAt===b.completionAt}async function runTranscriptActivity(worktreePath,vendor,detector,_signal){let{marker,mtimeMs}=await detector.latestActivity(worktreePath);return{mtimeMs:detector.supportsCompletionMarkers()?mtimeMs:0,completionId:marker?.id??null,completionAt:marker?.timestampMs??0}}function trackedWorktrees(tasks,defaultVendor="claude"){let map=new Map;for(let task of tasks){if(!task.worktreePath)continue;if(isRemoteRepoKey(task.repo)||isRemoteRepoKey(task.worktreePath))continue;if(map.has(task.worktreePath))continue;map.set(task.worktreePath,task.vendor??defaultVendor)}return map}class TranscriptActivityCollector{orch;bus;options;entries=new Map;stopped=!1;constructor(orch,bus,options={}){this.orch=orch;this.bus=bus;this.options=options}tick(){if(this.stopped)return;if(this.options.hasSubscribers&&!this.options.hasSubscribers())return;try{let tracked=trackedWorktrees(this.orch.listTasks(),this.options.defaultVendor),pruned=!1;for(let path of this.entries.keys()){if(tracked.has(path))continue;if(this.entries.get(path)?.value)pruned=!0;this.entries.delete(path)}if(pruned)this.publish();for(let[path,vendor]of tracked)this.maybeCollect(path,vendor)}catch(err){logDaemonError("transcript-activity",err)}}stop(){this.stopped=!0}maybeCollect(worktreePath,vendor){let entry=this.entries.get(worktreePath);if(!entry)entry={inFlight:!1,nextAllowedAt:0,vendor,detector:this.createDetector(vendor)},this.entries.set(worktreePath,entry);else if(entry.vendor!==vendor)entry.vendor=vendor,entry.detector=this.createDetector(vendor);let cadence=this.options.cadence??{timeoutMs:TRANSCRIPT_ACTIVITY_TIMEOUT_MS,slowRetryMs:TRANSCRIPT_ACTIVITY_SLOW_RETRY_MS,minIntervalMs:TRANSCRIPT_ACTIVITY_MIN_INTERVAL_MS},run=this.options.run??runTranscriptActivity,current=entry;maybeStartScheduledRun(current,cadence,(signal)=>run(worktreePath,current.vendor,current.detector,signal),(value)=>{if(this.stopped)return;if(this.entries.get(worktreePath)!==current)return;if(current.value&&sameTranscriptActivityEntry(current.value,value))return;current.value=value,this.publish()})}publish(){let activity={};for(let[path,entry]of this.entries)if(entry.value)activity[path]=entry.value;this.bus.publish("transcript.activity",{activity})}createDetector(vendor){return this.options.createDetector?.(vendor)??{latestActivity:async()=>({marker:null,mtimeMs:0}),latestActivityInFile:async()=>null,supportsCompletionMarkers:()=>!1}}}function startTranscriptActivityCollector(orch,runtime,bus,tickMs=DEFAULT_TRANSCRIPT_ACTIVITY_TICK_MS,hasSubscribers){let collector=new TranscriptActivityCollector(orch,bus,{hasSubscribers,createDetector:runtime.createEngineTurnDetector,defaultVendor:runtime.defaultTaskVendor,run:async(path,vendor,detector,signal)=>{let{marker,mtimeMs}=await detector.latestActivity(path);return{mtimeMs:detector.supportsCompletionMarkers()?mtimeMs:await runtime.latestTranscriptMtime(vendor,path),completionId:marker?.id??null,completionAt:marker?.timestampMs??0}}});return startTicker({name:"transcript-activity-collector",tickMs,immediate:!0,run:()=>collector.tick(),onStop:()=>collector.stop()})}import{readFileSync}from"fs";import{basename as basename3}from"path";var DEFAULT_UI_PREFS_DEBOUNCE_MS=200,FOCUS_ACCENT_SLOT_NAMES=["primary","success","info"];function readUiPrefsFromStateFile(statePath){let parsed={};try{let raw=JSON.parse(readFileSync(statePath,"utf8"));if(raw&&typeof raw==="object"&&!Array.isArray(raw))parsed=raw}catch{}let theme=typeof parsed.activeTheme==="string"&&parsed.activeTheme.length>0?parsed.activeTheme:"claude",transparentBackground=parsed.transparentBackground!==!1,focusAccent=typeof parsed.focusAccent==="string"&&FOCUS_ACCENT_SLOT_NAMES.includes(parsed.focusAccent)?parsed.focusAccent:null,sortMode=parsed.activeSortMode==="recent"?"recent":"default",keysCollapsed=parsed["tasksPane.keysCollapsed"]===!0,projectFilter=typeof parsed["tasksPane.projectFilter"]==="string"&&parsed["tasksPane.projectFilter"].length>0?parsed["tasksPane.projectFilter"]:null,locale=typeof parsed.locale==="string"&&parsed.locale.length>0?parsed.locale:"en";return{theme,transparentBackground,focusAccent,locale,sortMode,keysCollapsed,projectFilter}}function samePrefs(a,b){return a.theme===b.theme&&a.transparentBackground===b.transparentBackground&&a.focusAccent===b.focusAccent&&a.locale===b.locale&&a.sortMode===b.sortMode&&a.keysCollapsed===b.keysCollapsed&&a.projectFilter===b.projectFilter}function startUiPrefsWatcher(bus,options={}){let debounceMs=options.debounceMs??DEFAULT_UI_PREFS_DEBOUNCE_MS;if(debounceMs<=0)return()=>{};let statePath=options.statePath??defaultUiPrefsStatePath(),stateFile=basename3(statePath),last=null,publishIfChanged=()=>{try{let next=readUiPrefsFromStateFile(statePath);if(last&&samePrefs(last,next))return;last=next,bus.publish("ui-prefs",next)}catch(err){logDaemonError("ui-prefs-watcher",err)}},stop=startFileWatchTrigger({filePath:statePath,matchBasenames:[stateFile],debounceMs,onTrigger:publishIfChanged,onError:(err)=>logDaemonError("ui-prefs-watcher",err)});return publishIfChanged(),stop}import{spawn as spawn4}from"child_process";function runGit(worktreePath,args,signal){return new Promise((resolve)=>{let stdout="",settled=!1,finish=(status)=>{if(settled)return;settled=!0,resolve(status===0?stdout:null)},child=spawn4("git",args.slice(),{cwd:worktreePath,stdio:["ignore","pipe","ignore"],env:{...process.env,GIT_OPTIONAL_LOCKS:"0"},signal,killSignal:"SIGKILL"});child.stdout?.on("data",(chunk)=>{stdout+=String(chunk)}),child.on("error",()=>finish(null)),child.on("close",finish)})}async function countDrift(worktreePath,baseRef,signal){return parseAheadBehind(await runGit(worktreePath,["rev-list","--left-right","--count",`${baseRef}...HEAD`],signal))}function parseAheadBehind(stdout){if(stdout===null)return null;let parts=stdout.trim().split(/\s+/);if(parts.length!==2)return null;let behind=Number.parseInt(parts[0]??"",10),ahead=Number.parseInt(parts[1]??"",10);if(!Number.isInteger(behind)||behind<0||!Number.isInteger(ahead)||ahead<0)return null;return{behind,ahead}}function countPorcelain(stdout){let added=0,deleted=0;for(let row of parsePorcelainRows(stdout))if(row.x==="D"||row.y==="D")deleted++;else added++;return{added,deleted}}async function runGitStatus(worktreePath,signal,baseRef){let stdout=await runGit(worktreePath,["status","--porcelain=v1"],signal);if(stdout===null)throw Error("git status failed");let counts=countPorcelain(stdout);if(!baseRef)return counts;let drift=await countDrift(worktreePath,baseRef,signal);return drift===null?counts:{...counts,...drift}}var EMPTY_PATHS=new Set;function isRemoteRepoKey2(value){return value.startsWith("ssh://")}function sameWorktreeChanges(a,b){return a.added===b.added&&a.deleted===b.deleted&&a.behind===b.behind&&a.ahead===b.ahead}var DEFAULT_WORKTREE_CHANGES_TICK_MS=2000,WORKTREE_CHANGES_CONCURRENCY=4,WORKTREE_CHANGES_TIMEOUT_MS=4000,WORKTREE_CHANGES_SLOW_RETRY_MS=60000,WORKTREE_CHANGES_MIN_INTERVAL_MS=1500,WORKTREE_CHANGES_QUIET_INTERVAL_MS=60000;function trackedWorktreePaths(tasks){let paths=new Map;for(let task of tasks){if(!task.worktreePath)continue;if(isRemoteRepoKey2(task.repo)||isRemoteRepoKey2(task.worktreePath))continue;if(paths.get(task.worktreePath)===void 0)paths.set(task.worktreePath,task.baseRef)}return paths}var UNREADABLE=Symbol("worktree-changes-unreadable");class WorktreeChangesCollector{orch;bus;options;entries=new Map;stopped=!1;queued=new Map;active=0;activePaths=new Set;drainHandle;publishTimer;controllers=new Set;constructor(orch,bus,options={}){this.orch=orch;this.bus=bus;this.options=options}tick(){if(this.stopped)return;if(this.options.hasSubscribers&&!this.options.hasSubscribers())return;try{let tasks=this.orch.listTasks(),tracked=trackedWorktreePaths(tasks),pruned=!1;for(let path of this.entries.keys()){if(tracked.has(path))continue;if(this.entries.get(path)?.value)pruned=!0;this.entries.delete(path),this.queued.delete(path)}for(let path of this.queued.keys())if(!tracked.has(path))this.queued.delete(path);if(pruned)this.publish();let busy=this.busyPaths(tasks);for(let[path,baseRef]of tracked)if(!this.entries.get(path)?.inFlight)this.queued.set(path,{baseRef,busy:busy.has(path)});this.drain()}catch(err){logDaemonError("worktree-changes",err)}}stop(){if(this.stopped=!0,this.queued.clear(),this.drainHandle)clearImmediate(this.drainHandle);if(this.publishTimer)clearTimeout(this.publishTimer);for(let controller of this.controllers)controller.abort()}drain(){if(this.stopped||this.options.hasSubscribers?.()===!1)return;let limit=WORKTREE_CHANGES_CONCURRENCY;for(let[path,demand]of this.queued){if(this.active>=limit)break;if(this.activePaths.has(path))continue;this.queued.delete(path);try{this.maybeCollect(path,demand.baseRef,demand.busy)}catch(err){logDaemonError("worktree-changes",err)}}}runFinished(path){if(this.active--,this.activePaths.delete(path),this.stopped||this.drainHandle)return;this.drainHandle=setImmediate(()=>{this.drainHandle=void 0,this.drain()})}busyPaths(tasks){let ids=this.options.activeTaskIds?.();if(!ids)return EMPTY_PATHS;let active=new Set(ids);if(active.size===0)return EMPTY_PATHS;let paths=new Set;for(let task of tasks)if(task.worktreePath&&active.has(task.id))paths.add(task.worktreePath);return paths}maybeCollect(worktreePath,baseRef,busy=!1){let entry=this.entries.get(worktreePath);if(!entry)entry={inFlight:!1,nextAllowedAt:0},this.entries.set(worktreePath,entry);let now=Date.now(),probe=(this.options.probe??worktreeFingerprint)(worktreePath,baseRef),quietUntil=entry.quietUntil??0;if(!busy&&probe!==null&&probe===entry.probe&&now<quietUntil)return;let quietIntervalMs=this.options.quietIntervalMs??WORKTREE_CHANGES_QUIET_INTERVAL_MS,cadence=this.options.cadence??{timeoutMs:WORKTREE_CHANGES_TIMEOUT_MS,slowRetryMs:WORKTREE_CHANGES_SLOW_RETRY_MS,minIntervalMs:WORKTREE_CHANGES_MIN_INTERVAL_MS},run=this.options.run??runGitStatus;maybeStartScheduledRun(entry,cadence,(signal)=>{entry.probe=probe,entry.quietUntil=Date.now()+quietIntervalMs,this.active++,this.activePaths.add(worktreePath);let controller=new AbortController;this.controllers.add(controller);let abort=()=>controller.abort();return signal.addEventListener("abort",abort,{once:!0}),(async()=>{try{return await run(worktreePath,controller.signal,baseRef)}catch{return UNREADABLE}finally{signal.removeEventListener("abort",abort),this.controllers.delete(controller),this.runFinished(worktreePath)}})()},(value)=>{if(this.stopped)return;if(this.entries.get(worktreePath)!==entry)return;if(value===UNREADABLE&&entry.value!==void 0&&entry.value!==UNREADABLE)return;if(entry.value===value)return;if(entry.value&&entry.value!==UNREADABLE&&value!==UNREADABLE&&sameWorktreeChanges(entry.value,value))return;entry.value=value,this.publish()})}publish(){if(this.publishTimer||this.stopped)return;this.publishTimer=setTimeout(()=>{if(this.publishTimer=void 0,!this.stopped)this.publishNow()},this.options.publishDelayMs??10)}publishNow(){let changes={},unreadable=[];for(let[path,entry]of this.entries)if(entry.value===UNREADABLE)unreadable.push(path);else if(entry.value)changes[path]=entry.value;this.bus.publish("worktree.changes",unreadable.length>0?{changes,unreadable}:{changes})}}function startWorktreeChangesCollector(orch,runtime,bus,tickMs=DEFAULT_WORKTREE_CHANGES_TICK_MS,hasSubscribers,activeTaskIds){let collector=new WorktreeChangesCollector(orch,bus,{hasSubscribers,run:runtime.runWorktreeStatus,...activeTaskIds?{activeTaskIds}:{}});return startTicker({name:"worktree-changes-collector",tickMs,immediate:!0,run:()=>collector.tick(),onStop:()=>collector.stop()})}var DEFAULT_UPDATE_POLL_MS=21600000;function createProtocolUpgradeReporter(orch,runtime){return(taskId,tabId,evidence)=>{if(tabId!=="tab-1")return;let task=orch.getTask(taskId);if(!task)return;let upgrade=runtime.resolveProtocolUpgrade?.(task,evidence);if(!upgrade)return;return orch.setCommand(taskId,upgrade.command,upgrade.vendor).then(()=>logDaemonInfo("protocol-sniff",`upgraded task ${taskId} to the ${upgrade.vendor} protocol from its live session`)).catch((err)=>logDaemonError("protocol-sniff",err))}}function startDaemonCollectors(orch,runtime,bus,hasSubscribersFor,options,quotaUsage,automations,activity,deferredSweep){let stopActivityObserver=activity?startActivityObserver(activity,{...createActivityObserverIo(options.homeDir,runtime,activity),onEngineEvidence:createProtocolUpgradeReporter(orch,runtime)},()=>hasSubscribersFor("engine-state")):()=>{},checkUpdate=options.checkUpdate??runtime.checkLatestVersion,updatePollMs=options.updatePollMs??DEFAULT_UPDATE_POLL_MS,pollUpdate=()=>{checkUpdate().then((info)=>bus.publish("update",{info})).catch((err)=>logDaemonError("update-poller",err))},updateTimer=null;if(updatePollMs>0)pollUpdate(),updateTimer=setInterval(pollUpdate,updatePollMs),updateTimer.unref?.();let stopAutoTitlePoller=startAutoTitlePoller(orch,runtime,options.autoTitlePollMs??DEFAULT_AUTO_TITLE_POLL_MS,()=>hasSubscribersFor("task.snapshot")),stopUiPrefsWatcher=startUiPrefsWatcher(bus,{statePath:defaultUiPrefsStatePath(options.homeDir),debounceMs:options.uiPrefsDebounceMs??DEFAULT_UI_PREFS_DEBOUNCE_MS}),stopKeybindingsWatcher=startKeybindingsWatcher(bus,{path:defaultKeybindingsPath(options.homeDir),debounceMs:options.keybindingsDebounceMs??DEFAULT_KEYBINDINGS_DEBOUNCE_MS}),stopWorktreeChangesCollector=startWorktreeChangesCollector(orch,runtime,bus,options.worktreeChangesTickMs??DEFAULT_WORKTREE_CHANGES_TICK_MS,()=>hasSubscribersFor("worktree.changes"),activity?()=>activity.currentNonIdle().map((e)=>e.taskId):void 0),stopTranscriptActivityCollector=startTranscriptActivityCollector(orch,runtime,bus,options.transcriptActivityTickMs??DEFAULT_TRANSCRIPT_ACTIVITY_TICK_MS,()=>hasSubscribersFor("transcript.activity")),stopContextUsageCollector=activity?startContextUsageCollector(activity,orch,bus,runtime,options.contextUsageTickMs??DEFAULT_CONTEXT_USAGE_TICK_MS,()=>hasSubscribersFor("usage.context")):()=>{},stopPrStatusPoller=startPrStatusPoller(orch,runtime,options.prStatusPollMs??DEFAULT_PR_STATUS_POLL_MS,()=>hasSubscribersFor("task.snapshot"),void 0,activity?()=>activity.currentNonIdle().some((e)=>e.state!=="idle"):void 0),stopQuotaResumeRunner=startQuotaResumeRunner(orch,runtime,options.quotaResumeTickMs??DEFAULT_QUOTA_RESUME_TICK_MS,void 0,automations?.plugins),stopAutomationRunner=automations?startAutomationRunner({store:automations.store,orch,runtime,link:automations.link,...automations.plugins?{plugins:automations.plugins}:{},...automations.deferred?{deferred:automations.deferred}:{},...automations.inbox?{inbox:automations.inbox}:{}},options.automationTickMs??DEFAULT_AUTOMATION_TICK_MS):()=>{},stopDeferredPromptSweep=deferredSweep?startDeferredPromptSweep(deferredSweep,options.deferredSweepTickMs??DEFAULT_DEFERRED_SWEEP_TICK_MS):()=>{},stopQuotaUsagePoller=quotaUsage?startQuotaUsagePoller(quotaUsage,()=>runtime.vendorsWithQuotaProbe(),()=>hasSubscribersFor("usage.snapshot"),options.quotaUsageTickMs):()=>{};return async()=>{if(updateTimer)clearInterval(updateTimer);await Promise.allSettled([stopActivityObserver,stopAutoTitlePoller,stopPrStatusPoller,stopQuotaResumeRunner,stopAutomationRunner,stopDeferredPromptSweep,stopQuotaUsagePoller,stopUiPrefsWatcher,stopKeybindingsWatcher,stopWorktreeChangesCollector,stopTranscriptActivityCollector,stopContextUsageCollector].map(async(stop)=>stop()))}}var AGENT_TURN_HANDLERS=[{name:"agentTurn.list",handle(payload,ctx){let taskId=optionalString(payload,"taskId"),repo=optionalString(payload,"repoRoot"),since=optionalNumber(payload,"since"),limit=optionalNumber(payload,"limit");return{turns:ctx.agentTurns?.list({...taskId?{taskId}:{},...repo?{repo}:{},...since!==void 0?{since}:{},...limit!==void 0?{limit}:{}})??[]}}}];async function discardMatchingDeferredPrompt(ctx,taskId,tabId,at,reason){if(!tabId||!ctx.deferredPrompts)return;let deferredId=ctx.inbox.snapshot().find((item)=>item.taskId===taskId&&item.tabId===tabId&&item.state==="prompt_deferred"&&(at===void 0||item.at===at))?.detail?.deferredPrompt?.id;if(deferredId)await ctx.deferredPrompts.discard(deferredId,reason);return deferredId}var ATTENTION_HANDLERS=[{name:"attention.dismissRoutine",async handle(payload,ctx){return await ctx.inbox.deleteRoutineEpisode(requireString(payload,"automationId")),{deleted:!0}}},{name:"attention.dismiss",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),tabId=optionalString(payload,"tabId")??null,at=payload.at===void 0?void 0:requireNumber(payload,"at"),deferredId=await discardMatchingDeferredPrompt(ctx,taskId,tabId,at,"Inbox item dismissed"),deleted=await ctx.inbox.deleteEpisode(taskId,tabId,at,deferredId?"prompt_deferred":void 0,deferredId);if(deleted)ctx.plugins?.handleUiReport({kind:"attention.handled",taskId,detail:{how:"dismissed",...tabId?{tabId}:{}}});return{deleted}}},{name:"attention.read",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),tabId=optionalString(payload,"tabId")??null,at=requireNumber(payload,"at"),deferredId=await discardMatchingDeferredPrompt(ctx,taskId,tabId,at,"Inbox item read by legacy client"),updated=await ctx.inbox.markRead(taskId,tabId,at,deferredId?"prompt_deferred":void 0,deferredId);if(updated)ctx.plugins?.handleUiReport({kind:"attention.handled",taskId,detail:{how:"read",...tabId?{tabId}:{}}});return{updated}}}];import{execFile}from"child_process";import{stat}from"fs/promises";import{promisify}from"util";var execFileAsync=promisify(execFile);function isProbeable(repo){return!repo.startsWith("ssh://")}async function isGitWorkTree(repo){try{let{stdout}=await execFileAsync("git",["-C",repo,"rev-parse","--is-inside-work-tree"]);return stdout.trim()==="true"}catch{return!1}}async function assertRoutineRepo(repo){if(!isProbeable(repo))return;if(!(await stat(repo).catch(()=>null))?.isDirectory())throw Error(`repo does not exist: ${repo}`);if(!await isGitWorkTree(repo))throw Error(`not a git repository: ${repo}`)}async function assertRoutineBaseRef(repo,ref){if(!ref||!isProbeable(repo))return;if(!await isGitWorkTree(repo))return;try{await execFileAsync("git",["-C",repo,"rev-parse","--verify","--quiet",`${ref}^{commit}`])}catch{throw Error(`base branch does not resolve in ${repo}: ${ref}`)}}function requireSchedule(payload,key){let schedule=requireString(payload,key);if(!isValidCron(schedule))throw Error(`invalid cron expression: ${schedule}`);return schedule}function readPrecheck(payload){if(!("precheck"in payload))return;let raw=payload.precheck;if(raw===null)return null;if(!raw||typeof raw!=="object")throw Error("precheck must be an object or null");let command=requireString(raw,"command"),timeoutSeconds=optionalNumber(raw,"timeoutSeconds")??120;if(timeoutSeconds<=0)throw Error("precheck.timeoutSeconds must be greater than zero");return{command,timeoutSeconds}}function readGraceMinutes(payload){let value=optionalNumber(payload,"missedRunGraceMinutes");if(value!==void 0&&value<0)throw Error("missedRunGraceMinutes must be zero or more minutes");return value}var AUTOMATION_HANDLERS=[{name:"automation.list",async handle(_payload,ctx){let automations=ctx.automations.list(),lastRunStatus={};for(let automation of automations){let latest=ctx.automations.runsFor(automation.id,1)[0];if(latest)lastRunStatus[automation.id]=latest.status}return{automations,lastRunStatus,keepsDaemonAlive:ctx.automations.hasEnabled()}}},{name:"automation.create",async handle(payload,ctx){let precheck=readPrecheck(payload),repo=requireString(payload,"repo"),baseRef=optionalString(payload,"baseRef"),target="target"in payload?readAutomationTarget(payload.target):void 0;if(!target)await assertRoutineRepo(repo);if(baseRef)await assertRoutineBaseRef(repo,baseRef);let targetOptions={target:target??void 0,vendor:optionalVendor(payload,"vendor"),baseRef,persistentSession:optionalBoolean(payload,"persistentSession")};assertAutomationTargetOptions(targetOptions),await assertAutomationTargetTask({repo,target:target??void 0},ctx.orch);let automation=await ctx.automations.create({name:requireString(payload,"name"),repo,...target?{target}:{},prompt:requireString(payload,"prompt"),schedule:requireSchedule(payload,"schedule"),missedRunGraceMinutes:readGraceMinutes(payload)??60,...optionalVendor(payload,"vendor")?{vendor:optionalVendor(payload,"vendor")}:{},...precheck?{precheck}:{},...baseRef?{baseRef}:{},...optionalBoolean(payload,"persistentSession")===!0?{persistentSession:!0}:{},...optionalBoolean(payload,"enabled")!==void 0?{enabled:optionalBoolean(payload,"enabled")}:{}});return ctx.daemon.reevaluateIdle(),{automation}}},{name:"automation.update",async handle(payload,ctx){let id=requireString(payload,"id"),nextBaseRef="baseRef"in payload?optionalString(payload,"baseRef"):void 0,currentRepo=ctx.automations.get(id)?.repo;if(nextBaseRef&¤tRepo)await assertRoutineBaseRef(currentRepo,nextBaseRef);let patch={...optionalString(payload,"name")!==void 0?{name:optionalString(payload,"name")}:{},...optionalString(payload,"prompt")!==void 0?{prompt:optionalString(payload,"prompt")}:{},...payload.vendor===null?{vendor:null}:optionalVendor(payload,"vendor")!==void 0?{vendor:optionalVendor(payload,"vendor")}:{},..."target"in payload?{target:readAutomationTarget(payload.target)}:{},..."schedule"in payload?{schedule:requireSchedule(payload,"schedule")}:{},...readPrecheck(payload)!==void 0?{precheck:readPrecheck(payload)}:{},..."baseRef"in payload?{baseRef:optionalString(payload,"baseRef")??null}:{},...optionalBoolean(payload,"enabled")!==void 0?{enabled:optionalBoolean(payload,"enabled")}:{},...readGraceMinutes(payload)!==void 0?{missedRunGraceMinutes:readGraceMinutes(payload)}:{},...optionalBoolean(payload,"persistentSession")!==void 0?{persistentSession:optionalBoolean(payload,"persistentSession")}:{}},current=ctx.automations.get(id);if(!current)throw Error(`automation not found: ${id}`);let targetOptions=mergeAutomationTargetOptions(current,patch);if(assertAutomationTargetOptions(targetOptions),patch.target)await assertAutomationTargetTask({repo:current.repo,target:patch.target},ctx.orch);let automation=await ctx.automations.update(id,patch);if(!automation)throw Error(`automation not found: ${id}`);return ctx.daemon.reevaluateIdle(),{automation}}},{name:"automation.delete",async handle(payload,ctx){let id=requireString(payload,"id"),deleted=await ctx.automations.delete(id);if(deleted)await ctx.inbox.deleteRoutineEpisode(id).catch(()=>{}),ctx.daemon.reevaluateIdle();return{deleted}}},{name:"automation.runs",async handle(payload,ctx){let automationId=requireString(payload,"id");if(!ctx.automations.get(automationId))throw Error(`automation not found: ${automationId}`);return{runs:ctx.automations.runsFor(automationId)}}},{name:"automation.runNow",blocking:!0,async handle(payload,ctx){let id=requireString(payload,"id"),automation=ctx.automations.get(id);if(!automation)throw Error(`automation not found: ${id}`);return{status:await runAutomationOnce({store:ctx.automations,orch:ctx.orch,runtime:ctx.runtime,link:ctx.selfLink,...ctx.plugins?{plugins:()=>ctx.plugins??null}:{},...ctx.deferredPrompts?{deferred:ctx.deferredPrompts}:{},inbox:ctx.inbox},automation,{scheduledFor:Date.now(),trigger:"manual"})}}}];var PEER_HEADER=/^\[(?:ROVE|KOBE) PEER\]\s+from\s+"([^"]{1,200})"/;function deferredPromptSender(prompt){let label=PEER_HEADER.exec(prompt)?.[1]?.trim();return label?label:void 0}function deferredPromptInput(payload){let taskId=requireString(payload,"taskId"),tabId=requireString(payload,"tabId"),prompt=requireString(payload,"prompt"),layer=requireString(payload,"layer");if(layer!=="recent-human-write"&&layer!=="composer-not-empty")throw Error('layer must be "recent-human-write" or "composer-not-empty"');let senderLabel=optionalString(payload,"senderLabel")??deferredPromptSender(prompt),senderTaskId=optionalString(payload,"senderTaskId");return{taskId,tabId,prompt,layer,...senderLabel!==void 0?{senderLabel}:{},...senderTaskId!==void 0?{senderTaskId}:{},at:Date.now()}}async function fileWithInbox(input,ctx){if(!ctx.orch.getTask(input.taskId))throw Error(`task not found: ${input.taskId}`);if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let kind="filed",record;try{record=await ctx.deferredPrompts.file(input)}catch(error){if(!(error instanceof DeferredPromptPendingError))throw error;kind="occupied",record=error.existing}return await ctx.inbox.recordPromptDeferred(record.taskId,record.tabId,record.id,record.layer,record.at+DEFERRED_PROMPT_TTL_MS,record.senderLabel),{kind,record}}function errorText2(error){return error instanceof Error?error.message:String(error)}function publicRecord(record){return{id:record.id,taskId:record.taskId,tabId:record.tabId,prompt:record.prompt,layer:record.layer,at:new Date(record.at).toISOString(),expiresAt:new Date(record.at+DEFERRED_PROMPT_TTL_MS).toISOString(),...record.senderLabel!==void 0?{senderLabel:record.senderLabel}:{},...record.senderTaskId!==void 0?{senderTaskId:record.senderTaskId}:{},...record.dismissedAt!==void 0?{dismissedAt:new Date(record.dismissedAt).toISOString()}:{}}}async function deleteDeferredInboxPointer(record,ctx){await ctx.inbox.deleteEpisode(record.taskId,record.tabId,void 0,"prompt_deferred",record.id)}async function cleanupDeliveredClaim(claim,ctx,report){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let{record}=claim;try{await deleteDeferredInboxPointer(record,ctx),await ctx.deferredPrompts.completeClaim(claim),report.cleaned.push(record.id)}catch(error){await ctx.deferredPrompts.releaseClaim(claim),report.cleanupPending.push({id:record.id,taskId:record.taskId,tabId:record.tabId,error:errorText2(error)})}}async function deliverClaim(claim,ctx,report){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let{record}=claim;if(record.deliveredAt||record.deliveryStartedAt){await cleanupDeliveredClaim(claim,ctx,report);return}let task=ctx.orch.getTask(record.taskId);if(!task?.worktreePath){await ctx.deferredPrompts.releaseClaim(claim),report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"unavailable"});return}let deliveryStarted=!1;try{await ctx.deferredPrompts.beginDelivery(claim),deliveryStarted=!0;let outcome=await ctx.runtime.deliverPromptToLiveEngineTabDetailed({id:task.id,tabId:record.tabId,vendor:task.vendor,command:task.command,worktreePath:task.worktreePath},record.prompt);if(outcome.outcome==="delivered")await ctx.deferredPrompts.markDelivered(claim),report.delivered.push(record.id),await cleanupDeliveredClaim(claim,ctx,report);else await ctx.deferredPrompts.resetDelivery(claim),await ctx.deferredPrompts.releaseClaim(claim),report.retained.push(outcome.outcome==="busy"?{id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"busy",layer:outcome.layer}:{id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"unavailable"})}catch(error){if(await ctx.deferredPrompts.releaseClaim(claim).catch(()=>{}),deliveryStarted)report.cleanupPending.push({id:record.id,taskId:record.taskId,tabId:record.tabId,error:errorText2(error)});else report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"error",error:errorText2(error)})}}async function flushDeferredPrompts(ctx){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let report={delivered:[],cleaned:[],expired:[],retained:[],cleanupPending:[]},swept=await sweepExpiredDeferredPrompts({store:ctx.deferredPrompts,inbox:ctx.inbox});report.expired.push(...swept.expired),report.cleanupPending.push(...swept.cleanupPending);let listed=await ctx.deferredPrompts.list();for(let record of listed.records){if(record.deliveredAt||record.deliveryStartedAt){let claimed2=await ctx.deferredPrompts.claim(record.id);if(claimed2.kind==="claimed")await cleanupDeliveredClaim(claimed2.claim,ctx,report);continue}if(ctx.runtime.deliveryGuard()==="on"){report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"gate-enabled"});continue}let claimed=await ctx.deferredPrompts.claim(record.id);if(claimed.kind==="in-flight"){report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"in-flight"});continue}if(claimed.kind==="claimed")await deliverClaim(claimed.claim,ctx,report)}return report}var DEFERRED_PROMPT_HANDLERS=[{name:"deferredPrompt.file",async handle(payload,ctx){let result=await fileWithInbox(deferredPromptInput(payload),ctx);if(result.kind==="occupied")throw new DeferredPromptPendingError(result.record);return{id:result.record.id}}},{name:"deferredPrompt.fileIfVacant",async handle(payload,ctx){let result=await fileWithInbox(deferredPromptInput(payload),ctx);return{kind:result.kind,id:result.record.id,expiresAt:publicRecord(result.record).expiresAt,...result.kind==="occupied"?{layer:result.record.layer}:{}}}},{name:"deferredPrompt.get",async handle(){throw Error("legacy deferred prompt release is unsafe; restart Rove to update the client")}},{name:"deferredPrompt.list",async handle(payload,ctx){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let listed=await ctx.deferredPrompts.list();return{records:(payload.includeDismissed===!0?[...listed.records,...listed.dismissed]:listed.records).map(publicRecord)}}},{name:"deferredPrompt.dismiss",async handle(payload,ctx){let id=requireString(payload,"id");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let dropped=await ctx.deferredPrompts.discard(id,"Inbox item dismissed");if(!dropped)return{dismissed:!1};return await deleteDeferredInboxPointer(dropped,ctx),{dismissed:!0,record:publicRecord(dropped)}}},{name:"deferredPrompt.resolve",async handle(payload,ctx){let id=requireString(payload,"id");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let claimed=await ctx.deferredPrompts.claim(id);if(claimed.kind!=="claimed")return{removed:!1,kind:claimed.kind};let report={delivered:[],cleaned:[],expired:[],retained:[],cleanupPending:[]};return await ctx.deferredPrompts.markDelivered(claimed.claim),await cleanupDeliveredClaim(claimed.claim,ctx,report),{removed:report.cleaned.includes(id),cleanupPending:report.cleanupPending}}},{name:"deferredPrompt.release",blocking:!0,async handle(payload,ctx){let id=requireString(payload,"id");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let report={delivered:[],cleaned:[],expired:[],retained:[],cleanupPending:[]},claimed=await ctx.deferredPrompts.claim(id);if(claimed.kind==="claimed")await deliverClaim(claimed.claim,ctx,report);return{kind:claimed.kind,...report}}},{name:"deferredPrompt.discardTab",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),tabId=requireString(payload,"tabId");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let dropped=await ctx.deferredPrompts.discardTab(taskId,tabId,"tab closed");for(let record of dropped)await deleteDeferredInboxPointer(record,ctx);return{dropped:dropped.map((record)=>record.id)}}},{name:"deferredPrompt.flush",blocking:!0,async handle(_payload,ctx){return await flushDeferredPrompts(ctx)}}];async function ingestAgentTurns(store,runtime,orch,input){if(!input.transcriptPath)return{recorded:0,latest:void 0};let task=orch.getTask(input.taskId),vendor=input.vendor??task?.vendor;if(!vendor)return{recorded:0,latest:void 0};let turns=await runtime.readEngineTurns(vendor,input.transcriptPath);if(turns.length===0)return{recorded:0,latest:void 0};let records=turns.map((turn)=>({...turn,taskId:input.taskId,...input.tabId?{tabId:input.tabId}:{},vendor,...task?.repo?{repo:task.repo}:{}})),recorded=await store.record(records),last=records[records.length-1],latest=last?{id:last.id,model:last.model,usage:last.usage,startedAt:last.startedAt,endedAt:last.endedAt}:void 0;return{recorded,latest}}function ingestAgentTurnsBestEffort(store,runtime,orch,input,onDone){if(!store){onDone?.(void 0);return}ingestAgentTurns(store,runtime,orch,input).then(({latest})=>onDone?.(latest)).catch((err)=>{logDaemonError("agent-turns-ingest",err),onDone?.(void 0)})}import{existsSync as existsSync2}from"fs";import path from"path";function normalize(p){return p.length>1&&p.endsWith("/")?p.slice(0,-1):p}function isAncestorOrSelf(wt,cwd){return cwd===wt||cwd.startsWith(`${wt}/`)}function crossesRepoBoundary(wt,cwd){for(let dir=cwd;dir.length>wt.length;dir=path.dirname(dir))if(existsSync2(path.join(dir,".git")))return!0;return!1}function matchTaskByCwd(tasks,cwd){let target=normalize(cwd),bestId,bestWt="",bestLen=-1;for(let t of tasks){if(!t.worktreePath)continue;let wt=normalize(t.worktreePath);if(isAncestorOrSelf(wt,target)&&wt.length>bestLen)bestLen=wt.length,bestWt=wt,bestId=t.id}if(bestId&&crossesRepoBoundary(bestWt,target))return;return bestId}function matchTaskByWorktreePath(tasks,worktreePath){let target=normalize(worktreePath);for(let t of tasks)if(t.worktreePath&&normalize(t.worktreePath)===target)return t.id;return}function findAdoptableWorktree(tasks,cwd){let target=normalize(cwd),repos=new Set,known=new Set;for(let t of tasks){if(t.repo&&path.isAbsolute(t.repo))repos.add(normalize(t.repo));if(t.worktreePath)known.add(normalize(t.worktreePath))}for(let repo of repos)for(let root of managedWorktreeRootsFor(repo,readWorktreeBaseOverride(repo)).map(normalize)){let prefix=`${root}/`;if(!target.startsWith(prefix))continue;let name=target.slice(prefix.length).split("/")[0];if(!name)continue;let worktreePath=`${prefix}${name}`;if(known.has(worktreePath))return;return{repo,worktreePath}}return}var ENGINE_REPORT_HANDLER={name:"engine.reportEvent",async handle(payload,ctx){let kind=requireString(payload,"kind");if(!ctx.runtime.isEngineActivityKind(kind))throw Error(`BAD_EVENT_KIND: unknown engine event kind: ${kind}`);let explicitId=optionalString(payload,"taskId"),cwd=optionalString(payload,"cwd");if(!explicitId&&cwd&&kind==="session-start"){let cand=findAdoptableWorktree(ctx.orch.listTasks(),cwd);if(cand)try{await ctx.orch.adoptWorktree({repo:cand.repo,worktreePath:cand.worktreePath,ifExists:"return"})}catch(err){logDaemonError("worktree-autosync",err)}}let taskId=explicitId??(cwd?matchTaskByCwd(ctx.orch.listTasks(),cwd):void 0);if(!taskId)return{};let detail=optionalActivityDetail(payload),tabId=optionalString(payload,"tabId"),sessionId=optionalString(payload,"sessionId"),transcriptPath=optionalString(payload,"transcriptPath"),session=sessionId?{id:sessionId,transcriptPath}:void 0,isStateKind=ctx.runtime.affectsActivityState(kind),vendor=optionalString(payload,"engine");if(isStateKind){if(ctx.activity.report(taskId,kind,detail,tabId,session,vendor),explicitId)await ctx.inbox.record(taskId,kind,detail,tabId??null).catch((err)=>logDaemonError("attention-inbox-record",err))}if(ctx.engineEvents?.append(taskId,{kind,...tabId?{tabId}:{},...vendor?{vendor}:{},...detail?{detail}:{},at:Date.now()}),kind==="pre-compact"||kind==="post-compact"||kind==="subagent-start"||kind==="subagent-stop")ctx.bus.publish("engine.lifecycle",{taskId,kind,...tabId?{tabId}:{},at:Date.now()});let pluginReport={kind,taskId,...detail?{detail}:{},...vendor?{vendor}:{},...tabId?{tabId}:{},...sessionId?{sessionId}:{}};if(kind!=="turn-complete")ctx.plugins?.handleEngineReport(pluginReport);if(kind==="turn-complete")ingestAgentTurnsBestEffort(ctx.agentTurns,ctx.runtime,ctx.orch,{taskId,...tabId?{tabId}:{},...vendor?{vendor}:{},...transcriptPath?{transcriptPath}:{}},(latest)=>ctx.plugins?.handleEngineReport(latest?{...pluginReport,detail:{...pluginReport.detail??{},turn:latest}}:pluginReport));if(kind==="turn-start"){if(ctx.runtime.maybeAutoStart(ctx.orch,taskId).then((result)=>{if(result==="moved")console.log(`[status-rules] task ${taskId} auto-moved backlog \u2192 in_progress`)}).catch((err)=>logDaemonError("status-rules",err)),ctx.orch.getTask(taskId)?.quotaResume)ctx.orch.setQuotaResume(taskId,null).catch((err)=>logDaemonError("quota-resume",err))}if(kind==="turn-failed"&&detail?.failure==="rate_limit")scheduleQuotaResume(ctx.orch,ctx.runtime,ctx.quotaUsage,taskId,void 0,()=>ctx.plugins??null).catch((err)=>logDaemonError("quota-resume",err));return{}}};var ISSUE_HANDLERS=[{name:"issue.list",async handle(payload,ctx){return ctx.issues.list(requireString(payload,"repoRoot"))}},{name:"issue.repos",async handle(_payload,ctx){return{repos:await ctx.issues.repos()}}},{name:"issue.mutate",async handle(payload,ctx){let repoRoot=requireString(payload,"repoRoot"),op=payload.op,opType=op&&typeof op==="object"?op.type:void 0;if(opType==="link"||opType==="update"){let taskId=op.taskId;if(typeof taskId==="string"&&taskId.length>0&&!ctx.orch.getTask(taskId))throw Error(`task not found: ${taskId}`)}let state=await ctx.issues.mutate(repoRoot,payload.op);return ctx.bus.publish("issue.snapshot",state),ctx.plugins?.handleUiReport({kind:"issue.changed",detail:{repo:repoRoot,...payload.op&&typeof payload.op==="object"?{op:payload.op}:{}}}),state}}];var MAX_FAILING_JOBS=3,MAX_TAIL_LINES=200,RUN_LOG_TIMEOUT_MS=30000;function stringField(value,key){if(typeof value!=="object"||value===null)return"";let field=value[key];return typeof field==="string"?field:""}function runIdFromDetailsUrl(url){return/\/actions\/runs\/(\d+)\b/.exec(url)?.[1]??null}function failingCheckTargets(rollup){let targets=[];for(let entry of rollup){let status=stringField(entry,"status").toUpperCase(),conclusion=stringField(entry,"conclusion").toUpperCase(),contextState=stringField(entry,"state").toUpperCase(),failedAs=null;if(status&&status!=="COMPLETED")failedAs=null;else if(conclusion)failedAs=conclusion==="SUCCESS"||conclusion==="NEUTRAL"||conclusion==="SKIPPED"?null:conclusion;else if(contextState==="FAILURE"||contextState==="ERROR")failedAs=contextState;if(failedAs===null)continue;let url=stringField(entry,"detailsUrl")||stringField(entry,"targetUrl");targets.push({jobName:stringField(entry,"name")||stringField(entry,"context")||"check",conclusion:failedAs,url,runId:runIdFromDetailsUrl(url)})}return targets}function parseFailedRunLog(text,maxLines=MAX_TAIL_LINES){let byJob=new Map;for(let raw of text.split(`
|
|
4
|
+
`)}function tail(chunks){let text=Buffer.concat(chunks.map((c)=>typeof c==="string"?Buffer.from(c):c)).toString("utf8");if(text.length<=MAX_OUTPUT_CHARS)return text;let points=Array.from(text);return points.length<=MAX_OUTPUT_CHARS?text:points.slice(-MAX_OUTPUT_CHARS).join("")}function runAutomationPrecheck(precheck,cwd,shell=resolveLoginShell({fallback:"/bin/sh"})){let startedAt=Date.now(),timeoutMs=Math.min(Math.max(precheck.timeoutSeconds,1),MAX_TIMEOUT_SECONDS)*1000;if(!existsSync(cwd))return Promise.resolve({exitCode:null,timedOut:!1,stdout:"",stderr:`working directory does not exist: ${cwd}`,durationMs:0});return new Promise((resolve)=>{let out=[],err=[],settled=!1,timedOut=!1,finish=(exitCode,timedOut2)=>{if(settled)return;settled=!0,clearTimeout(timer),resolve({exitCode,timedOut:timedOut2,stdout:tail(out),stderr:tail(err),durationMs:Date.now()-startedAt})},timer=setTimeout(()=>{if(timedOut=!0,child)killGroup(child);finish(null,!0)},timeoutMs);timer.unref?.();let child;try{child=spawn2(shell,["-ilc",withWatchdog(precheck.command,timeoutMs/1000+1)],{cwd,stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32"})}catch(spawnError){err.push(spawnError instanceof Error?spawnError.message:String(spawnError)),finish(null,!1);return}child.stdout?.on("data",(chunk)=>out.push(chunk)),child.stderr?.on("data",(chunk)=>err.push(chunk)),child.on("error",(spawnError)=>{if(timedOut){finish(null,!0);return}err.push(spawnError.message),finish(null,!1)}),child.on("close",(code)=>finish(code,timedOut))})}function precheckPassed(result){return result.exitCode===0&&!result.timedOut}function formatPrecheckSkip(result){if(result.timedOut)return`precheck timed out after ${result.durationMs}ms`;if(result.exitCode===null)return`precheck could not run: ${result.stderr.trim()||"unknown error"}`;return`precheck exited ${result.exitCode}`}var DEFAULT_AUTOMATION_TICK_MS=60000;function dueAutomations(automations,nowMs){return automations.filter((a)=>{if(!a.enabled)return!1;let at=Date.parse(a.nextRunAt);return Number.isFinite(at)&&at<=nowMs})}function resolveDueOccurrence(automation,nowMs,tickMs=DEFAULT_AUTOMATION_TICK_MS){let notBefore=Date.parse(automation.createdAt),scheduledFor=latestCronAtOrBefore(automation.schedule,nowMs,Number.isFinite(notBefore)?notBefore:0);if(scheduledFor===null)return null;let graceMs=automation.missedRunGraceMinutes*60000+Math.max(tickMs,0);return{scheduledFor,missed:nowMs-scheduledFor>graceMs}}function runEventFor(status){if(status==="dispatched"||status==="revived")return"automation.dispatched";if(status==="dispatch_failed")return"automation.failed";return"automation.skipped"}function resolveLink(link){return typeof link==="function"?link():link}function emitRunEvent(deps,automation,status,args,extra){deps.plugins?.()?.handleUiReport({kind:runEventFor(status),...extra.taskId?{taskId:extra.taskId}:{},detail:{automationId:automation.id,name:automation.name,repo:automation.repo,status,trigger:args.trigger,scheduledFor:new Date(args.scheduledFor).toISOString(),...extra.tabId?{tabId:extra.tabId}:{},...extra.deferredId?{deferredId:extra.deferredId}:{},...extra.error?{error:extra.error}:{}}})}async function raiseOrClearInboxEpisode(deps,automation,status,extra){let inbox=deps.inbox;if(!inbox)return;let now=deps.now??Date.now;await(automationRunNeedsAttention(status)?inbox.recordRoutineFailure({automationId:automation.id,name:automation.name,status,...extra.error?{error:extra.error}:{}},extra.taskId??null,now()):inbox.deleteRoutineEpisode?.(automation.id))?.catch((err)=>logDaemonError("automation-inbox",err))}async function recordSkip(deps,automation,status,scheduledFor,error){let now=deps.now??Date.now;await deps.store.recordRun({automationId:automation.id,scheduledFor:new Date(scheduledFor).toISOString(),status,trigger:"scheduled",at:new Date(now()).toISOString(),error}),emitRunEvent(deps,automation,status,{scheduledFor,trigger:"scheduled"},{error})}function droppedOccurrences(automation,scheduledFor){let armedAt=Date.parse(automation.nextRunAt);if(!Number.isFinite(armedAt)||armedAt>=scheduledFor)return null;let count=countCronBetween(automation.schedule,armedAt,scheduledFor);return count>0?{count,firstMs:armedAt}:null}async function runAutomationOnce(deps,automation,args){let now=deps.now??Date.now,record=async(status,extra={})=>{return await deps.store.recordRun({automationId:automation.id,scheduledFor:new Date(args.scheduledFor).toISOString(),status,trigger:args.trigger,at:new Date(now()).toISOString(),...extra}),emitRunEvent(deps,automation,status,args,extra),await raiseOrClearInboxEpisode(deps,automation,status,extra),status},cancelled=()=>deps.stopped?.()===!0||deps.store.get(automation.id)!==automation||args.trigger==="scheduled"&&!automation.enabled;if(cancelled())return await record("skipped_cancelled",{error:"routine disabled, changed, deleted or runner stopped before delivery"});if(args.trigger==="scheduled"&&automation.precheck){let result=await runAutomationPrecheck(automation.precheck,automation.repo);if(!precheckPassed(result))return logDaemonInfo("automation",`skip ${automation.name}: ${formatPrecheckSkip(result)}`),await record("skipped_precheck",{precheckResult:result,error:formatPrecheckSkip(result)})}if(cancelled())return await record("skipped_cancelled",{error:"routine disabled, changed, deleted or runner stopped before delivery"});let outcome;try{outcome=await dispatchAutomation({canDeliver:()=>!cancelled(),orch:deps.orch,runtime:deps.runtime,link:()=>resolveLink(deps.link),...deps.deferred?{deferred:deps.deferred}:{},...deps.inbox?{inbox:deps.inbox}:{},...deps.now?{now:deps.now}:{}},automation)}catch(err){let error=err instanceof Error?err.message:String(err);return logDaemonError("automation-dispatch",err),await record("skipped_unavailable",{error})}if(outcome.sessionTaskIdToSet)await deps.store.update(automation.id,{sessionTaskId:outcome.sessionTaskIdToSet}).catch((err)=>logDaemonError("automation-session-link",err));else if(outcome.sessionTaskIdToClear)await deps.store.update(automation.id,{sessionTaskId:null}).catch((err)=>logDaemonError("automation-session-unlink",err));if(outcome.status==="dispatched"||outcome.status==="revived")logDaemonInfo("automation",`${outcome.status} ${automation.name} task=${outcome.taskId}`);return await record(outcome.status,{...outcome.taskId?{taskId:outcome.taskId}:{},...outcome.tabId?{tabId:outcome.tabId}:{},...outcome.deferredId?{deferredId:outcome.deferredId}:{},...outcome.error?{error:outcome.error}:{}})}async function sweepAutomations(deps,tickMs=DEFAULT_AUTOMATION_TICK_MS){let now=deps.now??Date.now;for(let automation of dueAutomations(deps.store.list(),now())){if(deps.stopped?.())return;let nowMs=now(),occurrence=resolveDueOccurrence(automation,nowMs,tickMs);if(!occurrence){await deps.store.advanceNextRun(automation.id,nowMs);continue}let dropped=droppedOccurrences(automation,occurrence.scheduledFor),claimed=await deps.store.advanceNextRun(automation.id,occurrence.scheduledFor,automation.nextRunAt);if(!claimed)continue;if(dropped){let first=new Date(dropped.firstMs).toISOString();await recordSkip(deps,automation,"skipped_missed",dropped.firstMs,`${dropped.count} earlier occurrence${dropped.count===1?"":"s"} never ran (from ${first})`).catch((err)=>logDaemonError("automation-dropped",err))}if(occurrence.missed){await recordSkip(deps,automation,"skipped_missed",occurrence.scheduledFor,`missed by more than the ${automation.missedRunGraceMinutes}m grace window`);continue}await runAutomationOnce(deps,claimed,{scheduledFor:occurrence.scheduledFor,trigger:"scheduled"}).catch((err)=>logDaemonError("automation-run",err))}}function startAutomationRunner(deps,tickMs=DEFAULT_AUTOMATION_TICK_MS){let stopped=!1;return startTicker({name:"automation-sweep",tickMs,run:()=>sweepAutomations({...deps,stopped:()=>stopped},tickMs),onStop:()=>{stopped=!0}})}var DEFAULT_CONTEXT_USAGE_TICK_MS=1e4,CONTEXT_USAGE_TIMEOUT_MS=4000,CONTEXT_USAGE_SLOW_RETRY_MS=60000,CONTEXT_USAGE_MIN_INTERVAL_MS=5000;function sameContextUsage(a,b){return a.contextTokens===b.contextTokens&&a.contextWindowTokens===b.contextWindowTokens&&a.approximate===b.approximate&&a.inputTokens===b.inputTokens&&a.outputTokens===b.outputTokens&&a.cacheReadTokens===b.cacheReadTokens&&a.cacheCreationTokens===b.cacheCreationTokens}function contextUsageTargets(states,getVendor){let seen=new Set,targets=[];for(let state of states){if(!state.tabId||!state.sessionId)continue;let vendor=getVendor(state.taskId);if(!vendor)continue;let key=`${state.taskId}::${state.tabId}`;if(seen.has(key))continue;seen.add(key),targets.push({key,vendor,sessionId:state.sessionId})}return targets}class ContextUsageCollector{registry;orch;bus;runtime;options;entries=new Map;stopped=!1;constructor(registry,orch,bus,runtime,options={}){this.registry=registry;this.orch=orch;this.bus=bus;this.runtime=runtime;this.options=options}tick(){if(this.stopped)return;if(this.options.hasSubscribers&&!this.options.hasSubscribers())return;try{let targets=contextUsageTargets(this.registry.currentNonIdle(),(id)=>this.orch.getTask(id)?.vendor),live=new Set(targets.map((t)=>t.key)),pruned=!1;for(let key of[...this.entries.keys()]){if(live.has(key))continue;if(this.entries.get(key)?.value)pruned=!0;this.entries.delete(key)}if(pruned)this.publish();for(let target of targets)this.collect(target)}catch(err){logDaemonError("context-usage",err)}}stop(){this.stopped=!0}collect(target){let entry=this.entries.get(target.key);if(!entry)entry={inFlight:!1,nextAllowedAt:0},this.entries.set(target.key,entry);if(entry.sessionId!==void 0&&entry.sessionId!==target.sessionId){let had=entry.value!==void 0;if(entry.value=void 0,entry.nextAllowedAt=0,entry.sessionId=target.sessionId,had)this.publish()}entry.sessionId=target.sessionId;let cadence=this.options.cadence??{timeoutMs:CONTEXT_USAGE_TIMEOUT_MS,slowRetryMs:CONTEXT_USAGE_SLOW_RETRY_MS,minIntervalMs:CONTEXT_USAGE_MIN_INTERVAL_MS},read=this.options.read??((t)=>this.runtime.readEngineContextUsage(t.vendor,t.sessionId)),startedAt=Date.now();if(entry.inFlight||startedAt<entry.nextAllowedAt)return;entry.inFlight=!0;let controller=new AbortController,timer=setTimeout(()=>controller.abort(),cadence.timeoutMs),held=entry;read(target,controller.signal).then((value)=>{if(controller.signal.aborted||this.stopped)return;if(this.entries.get(target.key)!==held)return;if(!value)return;if(held.value&&sameContextUsage(held.value,value))return;held.value=value,this.publish()}).catch(()=>{}).finally(()=>{clearTimeout(timer);let finishedAt=Date.now();held.nextAllowedAt=controller.signal.aborted?startedAt+cadence.slowRetryMs:finishedAt+Math.max(cadence.minIntervalMs,(finishedAt-startedAt)*5),held.inFlight=!1})}publish(){let context={};for(let[key,entry]of this.entries)if(entry.value)context[key]=entry.value;this.bus.publish("usage.context",{context})}}function startContextUsageCollector(registry,orch,bus,runtime,tickMs=DEFAULT_CONTEXT_USAGE_TICK_MS,hasSubscribers){if(tickMs<=0)return()=>{};let collector=new ContextUsageCollector(registry,orch,bus,runtime,{hasSubscribers});collector.tick();let timer=setInterval(()=>collector.tick(),tickMs);return timer.unref?.(),()=>{clearInterval(timer),collector.stop()}}var DEFAULT_DEFERRED_SWEEP_TICK_MS=3600000;function errorText(error){return error instanceof Error?error.message:String(error)}async function sweepExpiredDeferredPrompts(deps){let report={expired:[],cleanupPending:[]},listed=await deps.store.list();for(let expired of listed.expired){let claimed=await deps.store.claim(expired.id);if(claimed.kind!=="claimed"){report.cleanupPending.push(failure(expired,claimed.kind));continue}try{if(expired.dismissedAt===void 0)await deps.inbox.recordPromptExpired(expired.taskId,expired.tabId,expired.id,(deps.now??Date.now)());await deps.store.completeClaim(claimed.claim),report.expired.push(expired.id)}catch(error){await deps.store.releaseClaim(claimed.claim).catch(()=>{}),report.cleanupPending.push(failure(expired,errorText(error)))}}return report}function failure(record,error){return{id:record.id,taskId:record.taskId,tabId:record.tabId,error}}function startDeferredPromptSweep(deps,tickMs=DEFAULT_DEFERRED_SWEEP_TICK_MS){return startTicker({name:"deferred-prompt-sweep",tickMs,immediate:!0,run:()=>sweepExpiredDeferredPrompts(deps)})}import{basename as basename2}from"path";var DEFAULT_KEYBINDINGS_DEBOUNCE_MS=200;function startKeybindingsWatcher(bus,options={}){let debounceMs=options.debounceMs??DEFAULT_KEYBINDINGS_DEBOUNCE_MS;if(debounceMs<=0)return()=>{};let filePath=options.path??defaultKeybindingsPath(),baseYaml=basename2(filePath),baseYml=baseYaml.replace(/\.yaml$/,".yml"),rev=0;return bus.publish("keybindings",{rev}),startFileWatchTrigger({filePath,matchBasenames:[baseYaml,baseYml],debounceMs,onTrigger:()=>{try{rev+=1,bus.publish("keybindings",{rev})}catch(err){logDaemonError("keybindings-watcher",err)}},onError:(err)=>logDaemonError("keybindings-watcher",err)})}import{spawn as spawn3}from"child_process";var PR_STATE_RANK={OPEN:2,MERGED:1,CLOSED:1};function pickPr(views){let best,bestRank=-1;for(let view of views){let rank=PR_STATE_RANK[(view.state??"").toUpperCase()]??0;if(rank>bestRank)best=view,bestRank=rank}return best}var DEFAULT_PR_STATUS_POLL_MS=30000,NO_PR_BACKOFF_MS=300000,SETTLED_BACKOFF_MS=600000,PR_FAILURE_BASE_MS=DEFAULT_PR_STATUS_POLL_MS,PR_FAILURE_CAP_MS=900000,NO_REMOTE_BACKOFF_MS=1800000,PR_POLL_JITTER_RATIO=0.2,PR_VIEW_TIMEOUT_MS=1e4,PR_POLL_CONCURRENCY=8;function decodeSpawnChunks(chunks){return Buffer.concat(chunks.map((c)=>typeof c==="string"?Buffer.from(c):c)).toString("utf8")}function spawnGh(args,cwd,signal){return new Promise((resolve)=>{let outChunks=[],errChunks=[],settled=!1,finish=(status,spawnError)=>{if(settled)return;settled=!0,resolve({status,stdout:decodeSpawnChunks(outChunks),stderr:decodeSpawnChunks(errChunks),spawnError})},child=spawn3("gh",args.slice(),{cwd,stdio:["ignore","pipe","pipe"],signal,killSignal:"SIGKILL"});child.stdout?.on("data",(chunk)=>{outChunks.push(chunk)}),child.stderr?.on("data",(chunk)=>{errChunks.push(chunk)}),child.on("error",()=>finish(null,!signal.aborted)),child.on("close",(code)=>finish(code,!1))})}function makeGhPrViewRunner(classify,viewFields){return async(worktreePath,branch)=>{let controller=new AbortController,timedOut=!1,timer=setTimeout(()=>{timedOut=!0,controller.abort()},PR_VIEW_TIMEOUT_MS);try{let res=await spawnGh(["pr","list","--head",branch,"--state","all","--json",viewFields],worktreePath,controller.signal);if(res.status===0&&!timedOut)try{let views=JSON.parse(res.stdout),picked=Array.isArray(views)?pickPr(views):void 0;return picked?{kind:"pr",view:picked}:{kind:"empty"}}catch{return classify({parseError:!0})}return classify({spawnError:res.spawnError,timedOut,exitCode:res.status,stderr:res.stderr})}finally{clearTimeout(timer)}}}function isPrPollable(task){if(task.kind==="main")return!1;if(!task.branch||!task.worktreePath)return!1;if(task.repo.startsWith("ssh://")||task.worktreePath.startsWith("ssh://"))return!1;return!0}async function setPrStaleMarker(orch,taskId,error){let prev=orch.getTask(taskId)?.prStatus;if(!prev||prev.lastError===error)return!1;let{lastError:_cleared,...rest}=prev;return await orch.setPRStatus(taskId,error===void 0?rest:{...rest,lastError:error}),!0}async function runPrStatusPass(orch,opts){let tickMs=opts.tickMs??DEFAULT_PR_STATUS_POLL_MS,rand=opts.rand,cfg={tickMs,settledMs:SETTLED_BACKOFF_MS,noPrMs:NO_PR_BACKOFF_MS,noRemoteMs:NO_REMOTE_BACKOFF_MS,failureBaseMs:PR_FAILURE_BASE_MS,failureCapMs:PR_FAILURE_CAP_MS,jitterRatio:PR_POLL_JITTER_RATIO},tasks=orch.listTasks(),live=new Set(tasks.map((task)=>task.id));for(let id of opts.schedule.keys())if(!live.has(id))opts.schedule.delete(id);let due=[];for(let task of tasks){if(!isPrPollable(task)){opts.schedule.delete(task.id);continue}let entry=opts.schedule.get(task.id);if(entry&&opts.now<entry.nextAllowedAt)continue;due.push({task,prevFailures:entry?.failures??0})}let pollOne=async(task,prevFailures)=>{try{let result=await opts.run(task.worktreePath,task.branch);if(result.kind==="error"){logDaemonInfo("pr-status-poller",`gh pr list failed (${result.error}) for task ${task.id} [${task.branch}] \u2014 keeping last PR status, backing off`);let marked=await setPrStaleMarker(orch,task.id,result.error);return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"error",error:result.error},prevFailures,opts.now,cfg,rand)),marked?task.id:void 0}if(result.kind==="empty"){let marked=await setPrStaleMarker(orch,task.id,void 0);return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"empty"},prevFailures,opts.now,cfg,rand)),marked?task.id:void 0}let next=opts.runtime.prStatus.mapView(result.view,opts.at),current=orch.getTask(task.id);if(!current){opts.schedule.delete(task.id);return}let wasStale=current.prStatus?.lastError!==void 0,changedId;if(wasStale||!opts.runtime.prStatus.sameStatus(current.prStatus,next??void 0))await orch.setPRStatus(task.id,next),changedId=task.id;let settled=next?.lifecycle==="merged"||next?.lifecycle==="closed";return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"pr",settled},prevFailures,opts.now,cfg,rand)),changedId}catch(err){logDaemonError("pr-status-poller",err);let marked=await setPrStaleMarker(orch,task.id,"network").catch(()=>!1);return opts.schedule.set(task.id,opts.runtime.prStatus.nextPoll({kind:"error",error:"network"},prevFailures,opts.now,cfg,rand)),marked?task.id:void 0}},slots=Array(due.length),cursor=0,worker=async()=>{for(let i=cursor++;i<due.length;i=cursor++){let item=due[i];if(item)slots[i]=await pollOne(item.task,item.prevFailures)}};return await Promise.all(Array.from({length:Math.min(PR_POLL_CONCURRENCY,due.length)},worker)),slots.filter((id)=>id!==void 0)}function startPrStatusPoller(orch,runtime,intervalMs=DEFAULT_PR_STATUS_POLL_MS,hasSubscribers,run=makeGhPrViewRunner(runtime.prStatus.classify,runtime.prStatus.viewFields),hasWorkingAgent){let schedule=new Map;return startTicker({name:"pr-status-poller",tickMs:intervalMs,...hasSubscribers?{gate:()=>hasSubscribers()||(hasWorkingAgent?.()??!1)}:{},run:()=>runPrStatusPass(orch,{runtime,run,now:Date.now(),at:new Date().toISOString(),schedule,tickMs:intervalMs})})}function quotaResumeContinuePrompt(language){return language==="zh"?"\u4ECE\u4E2D\u65AD\u7684\u5730\u65B9\u7EE7\u7EED\u8FD9\u4E2A\u4EFB\u52A1\u3002\u4E0A\u9762\u7684\u5BF9\u8BDD\u8BB0\u5F55\u91CC\u5DF2\u7ECF\u5B8C\u6210\u7684\u5DE5\u4F5C\u4E0D\u8981\u91CD\u505A\u3002":"Continue the task from where it stopped. The transcript above shows the work already completed \u2014 do not redo it."}var DEFAULT_QUOTA_RESUME_TICK_MS=60000;function dueQuotaResumes(tasks,nowMs){return tasks.filter((task)=>{if(!task.quotaResume||!task.worktreePath)return!1;if(task.deletion)return!1;let at=Date.parse(task.quotaResume.resumeAt);return Number.isFinite(at)&&at<=nowMs})}function exhaustedResetAtMs(usage,nowMs){let candidates=usage.windows.filter((w)=>w.percent>=100&&w.resetsAt!=null&&w.resetsAt>nowMs).map((w)=>w.resetsAt);return candidates.length>0?Math.min(...candidates):null}async function scheduleQuotaResume(orch,runtime,cache,taskId,now=Date.now,plugins){let task=orch.getTask(taskId);if(!task||!task.worktreePath||task.deletion)return;let usage=await cache.get(task.vendor??runtime.defaultTaskVendor,0),resetAtMs=usage?exhaustedResetAtMs(usage,now()):null;if(resetAtMs==null)return;let resumeAt=new Date(resetAtMs).toISOString();await orch.setQuotaResume(taskId,{resumeAt,requestedAt:new Date(now()).toISOString()}),logDaemonInfo("quota-resume",`armed task=${taskId} resumeAt=${resumeAt}`),plugins?.()?.handleUiReport({kind:"quota.exhausted",taskId,detail:{vendor:task.vendor??runtime.defaultTaskVendor,resumeAt}})}async function resumeDueTask(orch,runtime,task,plugins){await orch.setQuotaResume(task.id,null);let delivered=await runtime.deliverPromptToLiveEngine({id:task.id,vendor:task.vendor,command:task.command,worktreePath:task.worktreePath},quotaResumeContinuePrompt(task.observedLanguage));logDaemonInfo("quota-resume",`resume task=${task.id} delivered=${delivered}`),plugins?.()?.handleUiReport({kind:"quota.resumed",taskId:task.id,detail:{delivered}})}function startQuotaResumeRunner(orch,runtime,tickMs=DEFAULT_QUOTA_RESUME_TICK_MS,now=Date.now,plugins){return startTicker({name:"quota-resume",tickMs,run:async()=>{for(let task of dueQuotaResumes(orch.listTasks(),now()))await resumeDueTask(orch,runtime,task,plugins).catch((err)=>logDaemonError("quota-resume",err))}})}var FRESH_POLL_MS=900000,RETRY_BASE_MS=60000,MIN_FETCH_INTERVAL_MS=60000;class QuotaUsageCache{runtime;bus;now;entries=new Map;constructor(runtime,bus,now=Date.now){this.runtime=runtime;this.bus=bus;this.now=now}peek(vendor){return this.entries.get(vendor)?.usage??null}async get(vendor,maxAgeMs){let entry=this.entry(vendor),age=this.now()-(entry.usage?.capturedAt??0);if(entry.usage&&age<=maxAgeMs)return entry.usage;if(entry.inFlight)return entry.inFlight;if(this.now()-entry.attemptedAt<MIN_FETCH_INTERVAL_MS)return entry.usage;return this.fetch(vendor,entry)}async refreshIfDue(vendor){let entry=this.entry(vendor),retryAfter=entry.failures===0?0:Math.min(RETRY_BASE_MS*2**(entry.failures-1),FRESH_POLL_MS),dueAfter=entry.usage?FRESH_POLL_MS:retryAfter;if(this.now()-entry.attemptedAt<dueAfter)return;await this.fetch(vendor,entry)}entry(vendor){let existing=this.entries.get(vendor);if(existing)return existing;let fresh={usage:null,attemptedAt:0,failures:0,inFlight:null};return this.entries.set(vendor,fresh),fresh}fetch(vendor,entry){if(entry.inFlight)return entry.inFlight;return entry.attemptedAt=this.now(),entry.inFlight=this.runtime.quotaUsage(vendor).catch(()=>null).then((usage)=>{if(entry.inFlight=null,usage){entry.failures=0;let changed=JSON.stringify(usage.windows)!==JSON.stringify(entry.usage?.windows);if(entry.usage=usage,changed)this.publish()}else entry.failures+=1;return entry.usage}),entry.inFlight}publish(){let usage={};for(let[vendor,entry]of this.entries)if(entry.usage)usage[vendor]=entry.usage;this.bus.publish("usage.snapshot",{usage})}}function startQuotaUsagePoller(cache,listVendors,hasSubscribers,tickMs=MIN_FETCH_INTERVAL_MS){return startTicker({name:"quota-usage-poller",tickMs,gate:hasSubscribers,immediate:!0,run:()=>{for(let vendor of new Set(listVendors()))cache.refreshIfDue(vendor)}})}function isRemoteRepoKey(value){return value.startsWith("ssh://")}var DEFAULT_TRANSCRIPT_ACTIVITY_TICK_MS=1500,TRANSCRIPT_ACTIVITY_TIMEOUT_MS=4000,TRANSCRIPT_ACTIVITY_SLOW_RETRY_MS=60000,TRANSCRIPT_ACTIVITY_MIN_INTERVAL_MS=1500;function sameTranscriptActivityEntry(a,b){return a.mtimeMs===b.mtimeMs&&a.completionId===b.completionId&&a.completionAt===b.completionAt}async function runTranscriptActivity(worktreePath,vendor,detector,_signal){let{marker,mtimeMs}=await detector.latestActivity(worktreePath);return{mtimeMs:detector.supportsCompletionMarkers()?mtimeMs:0,completionId:marker?.id??null,completionAt:marker?.timestampMs??0}}function trackedWorktrees(tasks,defaultVendor="claude"){let map=new Map;for(let task of tasks){if(!task.worktreePath)continue;if(isRemoteRepoKey(task.repo)||isRemoteRepoKey(task.worktreePath))continue;if(map.has(task.worktreePath))continue;map.set(task.worktreePath,task.vendor??defaultVendor)}return map}class TranscriptActivityCollector{orch;bus;options;entries=new Map;stopped=!1;constructor(orch,bus,options={}){this.orch=orch;this.bus=bus;this.options=options}tick(){if(this.stopped)return;if(this.options.hasSubscribers&&!this.options.hasSubscribers())return;try{let tracked=trackedWorktrees(this.orch.listTasks(),this.options.defaultVendor),pruned=!1;for(let path of this.entries.keys()){if(tracked.has(path))continue;if(this.entries.get(path)?.value)pruned=!0;this.entries.delete(path)}if(pruned)this.publish();for(let[path,vendor]of tracked)this.maybeCollect(path,vendor)}catch(err){logDaemonError("transcript-activity",err)}}stop(){this.stopped=!0}maybeCollect(worktreePath,vendor){let entry=this.entries.get(worktreePath);if(!entry)entry={inFlight:!1,nextAllowedAt:0,vendor,detector:this.createDetector(vendor)},this.entries.set(worktreePath,entry);else if(entry.vendor!==vendor)entry.vendor=vendor,entry.detector=this.createDetector(vendor);let cadence=this.options.cadence??{timeoutMs:TRANSCRIPT_ACTIVITY_TIMEOUT_MS,slowRetryMs:TRANSCRIPT_ACTIVITY_SLOW_RETRY_MS,minIntervalMs:TRANSCRIPT_ACTIVITY_MIN_INTERVAL_MS},run=this.options.run??runTranscriptActivity,current=entry;maybeStartScheduledRun(current,cadence,(signal)=>run(worktreePath,current.vendor,current.detector,signal),(value)=>{if(this.stopped)return;if(this.entries.get(worktreePath)!==current)return;if(current.value&&sameTranscriptActivityEntry(current.value,value))return;current.value=value,this.publish()})}publish(){let activity={};for(let[path,entry]of this.entries)if(entry.value)activity[path]=entry.value;this.bus.publish("transcript.activity",{activity})}createDetector(vendor){return this.options.createDetector?.(vendor)??{latestActivity:async()=>({marker:null,mtimeMs:0}),latestActivityInFile:async()=>null,supportsCompletionMarkers:()=>!1}}}function startTranscriptActivityCollector(orch,runtime,bus,tickMs=DEFAULT_TRANSCRIPT_ACTIVITY_TICK_MS,hasSubscribers){let collector=new TranscriptActivityCollector(orch,bus,{hasSubscribers,createDetector:runtime.createEngineTurnDetector,defaultVendor:runtime.defaultTaskVendor,run:async(path,vendor,detector,signal)=>{let{marker,mtimeMs}=await detector.latestActivity(path);return{mtimeMs:detector.supportsCompletionMarkers()?mtimeMs:await runtime.latestTranscriptMtime(vendor,path),completionId:marker?.id??null,completionAt:marker?.timestampMs??0}}});return startTicker({name:"transcript-activity-collector",tickMs,immediate:!0,run:()=>collector.tick(),onStop:()=>collector.stop()})}import{readFileSync}from"fs";import{basename as basename3}from"path";var DEFAULT_UI_PREFS_DEBOUNCE_MS=200,FOCUS_ACCENT_SLOT_NAMES=["primary","success","info"];function readUiPrefsFromStateFile(statePath){let parsed={};try{let raw=JSON.parse(readFileSync(statePath,"utf8"));if(raw&&typeof raw==="object"&&!Array.isArray(raw))parsed=raw}catch{}let theme=typeof parsed.activeTheme==="string"&&parsed.activeTheme.length>0?parsed.activeTheme:"claude",transparentBackground=parsed.transparentBackground!==!1,focusAccent=typeof parsed.focusAccent==="string"&&FOCUS_ACCENT_SLOT_NAMES.includes(parsed.focusAccent)?parsed.focusAccent:null,sortMode=parsed.activeSortMode==="recent"?"recent":"default",keysCollapsed=parsed["tasksPane.keysCollapsed"]===!0,projectFilter=typeof parsed["tasksPane.projectFilter"]==="string"&&parsed["tasksPane.projectFilter"].length>0?parsed["tasksPane.projectFilter"]:null,locale=typeof parsed.locale==="string"&&parsed.locale.length>0?parsed.locale:"en";return{theme,transparentBackground,focusAccent,locale,sortMode,keysCollapsed,projectFilter}}function samePrefs(a,b){return a.theme===b.theme&&a.transparentBackground===b.transparentBackground&&a.focusAccent===b.focusAccent&&a.locale===b.locale&&a.sortMode===b.sortMode&&a.keysCollapsed===b.keysCollapsed&&a.projectFilter===b.projectFilter}function startUiPrefsWatcher(bus,options={}){let debounceMs=options.debounceMs??DEFAULT_UI_PREFS_DEBOUNCE_MS;if(debounceMs<=0)return()=>{};let statePath=options.statePath??defaultUiPrefsStatePath(),stateFile=basename3(statePath),last=null,publishIfChanged=()=>{try{let next=readUiPrefsFromStateFile(statePath);if(last&&samePrefs(last,next))return;last=next,bus.publish("ui-prefs",next)}catch(err){logDaemonError("ui-prefs-watcher",err)}},stop=startFileWatchTrigger({filePath:statePath,matchBasenames:[stateFile],debounceMs,onTrigger:publishIfChanged,onError:(err)=>logDaemonError("ui-prefs-watcher",err)});return publishIfChanged(),stop}import{spawn as spawn4}from"child_process";function runGit(worktreePath,args,signal){return new Promise((resolve)=>{let stdout="",settled=!1,finish=(status)=>{if(settled)return;settled=!0,resolve(status===0?stdout:null)},child=spawn4("git",args.slice(),{cwd:worktreePath,stdio:["ignore","pipe","ignore"],env:{...process.env,GIT_OPTIONAL_LOCKS:"0"},signal,killSignal:"SIGKILL"});child.stdout?.on("data",(chunk)=>{stdout+=String(chunk)}),child.on("error",()=>finish(null)),child.on("close",finish)})}async function countDrift(worktreePath,baseRef,signal){return parseAheadBehind(await runGit(worktreePath,["rev-list","--left-right","--count",`${baseRef}...HEAD`],signal))}function parseAheadBehind(stdout){if(stdout===null)return null;let parts=stdout.trim().split(/\s+/);if(parts.length!==2)return null;let behind=Number.parseInt(parts[0]??"",10),ahead=Number.parseInt(parts[1]??"",10);if(!Number.isInteger(behind)||behind<0||!Number.isInteger(ahead)||ahead<0)return null;return{behind,ahead}}function countPorcelain(stdout){let added=0,deleted=0;for(let row of parsePorcelainRows(stdout))if(row.x==="D"||row.y==="D")deleted++;else added++;return{added,deleted}}async function runGitStatus(worktreePath,signal,baseRef){let stdout=await runGit(worktreePath,["status","--porcelain=v1"],signal);if(stdout===null)throw Error("git status failed");let counts=countPorcelain(stdout);if(!baseRef)return counts;let drift=await countDrift(worktreePath,baseRef,signal);return drift===null?counts:{...counts,...drift}}var EMPTY_PATHS=new Set;function isRemoteRepoKey2(value){return value.startsWith("ssh://")}function sameWorktreeChanges(a,b){return a.added===b.added&&a.deleted===b.deleted&&a.behind===b.behind&&a.ahead===b.ahead}var DEFAULT_WORKTREE_CHANGES_TICK_MS=2000,WORKTREE_CHANGES_CONCURRENCY=4,WORKTREE_CHANGES_TIMEOUT_MS=4000,WORKTREE_CHANGES_SLOW_RETRY_MS=60000,WORKTREE_CHANGES_MIN_INTERVAL_MS=1500,WORKTREE_CHANGES_QUIET_INTERVAL_MS=60000;function trackedWorktreePaths(tasks){let paths=new Map;for(let task of tasks){if(!task.worktreePath)continue;if(isRemoteRepoKey2(task.repo)||isRemoteRepoKey2(task.worktreePath))continue;if(paths.get(task.worktreePath)===void 0)paths.set(task.worktreePath,task.baseRef)}return paths}var UNREADABLE=Symbol("worktree-changes-unreadable");class WorktreeChangesCollector{orch;bus;options;entries=new Map;stopped=!1;queued=new Map;active=0;activePaths=new Set;drainHandle;publishTimer;controllers=new Set;constructor(orch,bus,options={}){this.orch=orch;this.bus=bus;this.options=options}tick(){if(this.stopped)return;if(this.options.hasSubscribers&&!this.options.hasSubscribers())return;try{let tasks=this.orch.listTasks(),tracked=trackedWorktreePaths(tasks),pruned=!1;for(let path of this.entries.keys()){if(tracked.has(path))continue;if(this.entries.get(path)?.value)pruned=!0;this.entries.delete(path),this.queued.delete(path)}for(let path of this.queued.keys())if(!tracked.has(path))this.queued.delete(path);if(pruned)this.publish();let busy=this.busyPaths(tasks);for(let[path,baseRef]of tracked)if(!this.entries.get(path)?.inFlight)this.queued.set(path,{baseRef,busy:busy.has(path)});this.drain()}catch(err){logDaemonError("worktree-changes",err)}}stop(){if(this.stopped=!0,this.queued.clear(),this.drainHandle)clearImmediate(this.drainHandle);if(this.publishTimer)clearTimeout(this.publishTimer);for(let controller of this.controllers)controller.abort()}drain(){if(this.stopped||this.options.hasSubscribers?.()===!1)return;let limit=WORKTREE_CHANGES_CONCURRENCY;for(let[path,demand]of this.queued){if(this.active>=limit)break;if(this.activePaths.has(path))continue;this.queued.delete(path);try{this.maybeCollect(path,demand.baseRef,demand.busy)}catch(err){logDaemonError("worktree-changes",err)}}}runFinished(path){if(this.active--,this.activePaths.delete(path),this.stopped||this.drainHandle)return;this.drainHandle=setImmediate(()=>{this.drainHandle=void 0,this.drain()})}busyPaths(tasks){let ids=this.options.activeTaskIds?.();if(!ids)return EMPTY_PATHS;let active=new Set(ids);if(active.size===0)return EMPTY_PATHS;let paths=new Set;for(let task of tasks)if(task.worktreePath&&active.has(task.id))paths.add(task.worktreePath);return paths}maybeCollect(worktreePath,baseRef,busy=!1){let entry=this.entries.get(worktreePath);if(!entry)entry={inFlight:!1,nextAllowedAt:0},this.entries.set(worktreePath,entry);let now=Date.now(),probe=(this.options.probe??worktreeFingerprint)(worktreePath,baseRef),quietUntil=entry.quietUntil??0;if(!busy&&probe!==null&&probe===entry.probe&&now<quietUntil)return;let quietIntervalMs=this.options.quietIntervalMs??WORKTREE_CHANGES_QUIET_INTERVAL_MS,cadence=this.options.cadence??{timeoutMs:WORKTREE_CHANGES_TIMEOUT_MS,slowRetryMs:WORKTREE_CHANGES_SLOW_RETRY_MS,minIntervalMs:WORKTREE_CHANGES_MIN_INTERVAL_MS},run=this.options.run??runGitStatus;maybeStartScheduledRun(entry,cadence,(signal)=>{entry.probe=probe,entry.quietUntil=Date.now()+quietIntervalMs,this.active++,this.activePaths.add(worktreePath);let controller=new AbortController;this.controllers.add(controller);let abort=()=>controller.abort();return signal.addEventListener("abort",abort,{once:!0}),(async()=>{try{return await run(worktreePath,controller.signal,baseRef)}catch{return UNREADABLE}finally{signal.removeEventListener("abort",abort),this.controllers.delete(controller),this.runFinished(worktreePath)}})()},(value)=>{if(this.stopped)return;if(this.entries.get(worktreePath)!==entry)return;if(value===UNREADABLE&&entry.value!==void 0&&entry.value!==UNREADABLE)return;if(entry.value===value)return;if(entry.value&&entry.value!==UNREADABLE&&value!==UNREADABLE&&sameWorktreeChanges(entry.value,value))return;entry.value=value,this.publish()})}publish(){if(this.publishTimer||this.stopped)return;this.publishTimer=setTimeout(()=>{if(this.publishTimer=void 0,!this.stopped)this.publishNow()},this.options.publishDelayMs??10)}publishNow(){let changes={},unreadable=[];for(let[path,entry]of this.entries)if(entry.value===UNREADABLE)unreadable.push(path);else if(entry.value)changes[path]=entry.value;this.bus.publish("worktree.changes",unreadable.length>0?{changes,unreadable}:{changes})}}function startWorktreeChangesCollector(orch,runtime,bus,tickMs=DEFAULT_WORKTREE_CHANGES_TICK_MS,hasSubscribers,activeTaskIds){let collector=new WorktreeChangesCollector(orch,bus,{hasSubscribers,run:runtime.runWorktreeStatus,...activeTaskIds?{activeTaskIds}:{}});return startTicker({name:"worktree-changes-collector",tickMs,immediate:!0,run:()=>collector.tick(),onStop:()=>collector.stop()})}var DEFAULT_UPDATE_POLL_MS=21600000;function createProtocolUpgradeReporter(orch,runtime){return(taskId,tabId,evidence)=>{if(tabId!=="tab-1")return;let task=orch.getTask(taskId);if(!task)return;let upgrade=runtime.resolveProtocolUpgrade?.(task,evidence);if(!upgrade)return;return orch.setCommand(taskId,upgrade.command,upgrade.vendor).then(()=>logDaemonInfo("protocol-sniff",`upgraded task ${taskId} to the ${upgrade.vendor} protocol from its live session`)).catch((err)=>logDaemonError("protocol-sniff",err))}}function startDaemonCollectors(orch,runtime,bus,hasSubscribersFor,options,quotaUsage,automations,activity,deferredSweep){let stopActivityObserver=activity?startActivityObserver(activity,{...createActivityObserverIo(options.homeDir,runtime,activity),onEngineEvidence:createProtocolUpgradeReporter(orch,runtime)},()=>hasSubscribersFor("engine-state")):()=>{},checkUpdate=options.checkUpdate??runtime.checkLatestVersion,updatePollMs=options.updatePollMs??DEFAULT_UPDATE_POLL_MS,pollUpdate=()=>{checkUpdate().then((info)=>bus.publish("update",{info})).catch((err)=>logDaemonError("update-poller",err))},updateTimer=null;if(updatePollMs>0)pollUpdate(),updateTimer=setInterval(pollUpdate,updatePollMs),updateTimer.unref?.();let stopAutoTitlePoller=startAutoTitlePoller(orch,runtime,options.autoTitlePollMs??DEFAULT_AUTO_TITLE_POLL_MS,()=>hasSubscribersFor("task.snapshot")),stopUiPrefsWatcher=startUiPrefsWatcher(bus,{statePath:defaultUiPrefsStatePath(options.homeDir),debounceMs:options.uiPrefsDebounceMs??DEFAULT_UI_PREFS_DEBOUNCE_MS}),stopKeybindingsWatcher=startKeybindingsWatcher(bus,{path:defaultKeybindingsPath(options.homeDir),debounceMs:options.keybindingsDebounceMs??DEFAULT_KEYBINDINGS_DEBOUNCE_MS}),stopWorktreeChangesCollector=startWorktreeChangesCollector(orch,runtime,bus,options.worktreeChangesTickMs??DEFAULT_WORKTREE_CHANGES_TICK_MS,()=>hasSubscribersFor("worktree.changes"),activity?()=>activity.currentNonIdle().map((e)=>e.taskId):void 0),stopTranscriptActivityCollector=startTranscriptActivityCollector(orch,runtime,bus,options.transcriptActivityTickMs??DEFAULT_TRANSCRIPT_ACTIVITY_TICK_MS,()=>hasSubscribersFor("transcript.activity")),stopContextUsageCollector=activity?startContextUsageCollector(activity,orch,bus,runtime,options.contextUsageTickMs??DEFAULT_CONTEXT_USAGE_TICK_MS,()=>hasSubscribersFor("usage.context")):()=>{},stopPrStatusPoller=startPrStatusPoller(orch,runtime,options.prStatusPollMs??DEFAULT_PR_STATUS_POLL_MS,()=>hasSubscribersFor("task.snapshot"),void 0,activity?()=>activity.currentNonIdle().some((e)=>e.state!=="idle"):void 0),stopQuotaResumeRunner=startQuotaResumeRunner(orch,runtime,options.quotaResumeTickMs??DEFAULT_QUOTA_RESUME_TICK_MS,void 0,automations?.plugins),stopAutomationRunner=automations?startAutomationRunner({store:automations.store,orch,runtime,link:automations.link,...automations.plugins?{plugins:automations.plugins}:{},...automations.deferred?{deferred:automations.deferred}:{},...automations.inbox?{inbox:automations.inbox}:{}},options.automationTickMs??DEFAULT_AUTOMATION_TICK_MS):()=>{},stopDeferredPromptSweep=deferredSweep?startDeferredPromptSweep(deferredSweep,options.deferredSweepTickMs??DEFAULT_DEFERRED_SWEEP_TICK_MS):()=>{},stopQuotaUsagePoller=quotaUsage?startQuotaUsagePoller(quotaUsage,()=>runtime.vendorsWithQuotaProbe(),()=>hasSubscribersFor("usage.snapshot"),options.quotaUsageTickMs):()=>{};return async()=>{if(updateTimer)clearInterval(updateTimer);await Promise.allSettled([stopActivityObserver,stopAutoTitlePoller,stopPrStatusPoller,stopQuotaResumeRunner,stopAutomationRunner,stopDeferredPromptSweep,stopQuotaUsagePoller,stopUiPrefsWatcher,stopKeybindingsWatcher,stopWorktreeChangesCollector,stopTranscriptActivityCollector,stopContextUsageCollector].map(async(stop)=>stop()))}}var AGENT_TURN_HANDLERS=[{name:"agentTurn.list",handle(payload,ctx){let taskId=optionalString(payload,"taskId"),repo=optionalString(payload,"repoRoot"),since=optionalNumber(payload,"since"),limit=optionalNumber(payload,"limit");return{turns:ctx.agentTurns?.list({...taskId?{taskId}:{},...repo?{repo}:{},...since!==void 0?{since}:{},...limit!==void 0?{limit}:{}})??[]}}}];async function discardMatchingDeferredPrompt(ctx,taskId,tabId,at,reason){if(!tabId||!ctx.deferredPrompts)return;let deferredId=ctx.inbox.snapshot().find((item)=>item.taskId===taskId&&item.tabId===tabId&&item.state==="prompt_deferred"&&(at===void 0||item.at===at))?.detail?.deferredPrompt?.id;if(deferredId)await ctx.deferredPrompts.discard(deferredId,reason);return deferredId}var ATTENTION_HANDLERS=[{name:"attention.dismissRoutine",async handle(payload,ctx){return await ctx.inbox.deleteRoutineEpisode(requireString(payload,"automationId")),{deleted:!0}}},{name:"attention.dismiss",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),tabId=optionalString(payload,"tabId")??null,at=payload.at===void 0?void 0:requireNumber(payload,"at"),deferredId=await discardMatchingDeferredPrompt(ctx,taskId,tabId,at,"Inbox item dismissed"),deleted=await ctx.inbox.deleteEpisode(taskId,tabId,at,deferredId?"prompt_deferred":void 0,deferredId);if(deleted)ctx.plugins?.handleUiReport({kind:"attention.handled",taskId,detail:{how:"dismissed",...tabId?{tabId}:{}}});return{deleted}}},{name:"attention.read",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),tabId=optionalString(payload,"tabId")??null,at=requireNumber(payload,"at"),deferredId=await discardMatchingDeferredPrompt(ctx,taskId,tabId,at,"Inbox item read by legacy client"),updated=await ctx.inbox.markRead(taskId,tabId,at,deferredId?"prompt_deferred":void 0,deferredId);if(updated)ctx.plugins?.handleUiReport({kind:"attention.handled",taskId,detail:{how:"read",...tabId?{tabId}:{}}});return{updated}}}];import{execFile}from"child_process";import{stat}from"fs/promises";import{promisify}from"util";var execFileAsync=promisify(execFile);function isProbeable(repo){return!repo.startsWith("ssh://")}async function isGitWorkTree(repo){try{let{stdout}=await execFileAsync("git",["-C",repo,"rev-parse","--is-inside-work-tree"]);return stdout.trim()==="true"}catch{return!1}}async function assertRoutineRepo(repo){if(!isProbeable(repo))return;if(!(await stat(repo).catch(()=>null))?.isDirectory())throw Error(`repo does not exist: ${repo}`);if(!await isGitWorkTree(repo))throw Error(`not a git repository: ${repo}`)}async function assertRoutineBaseRef(repo,ref){if(!ref||!isProbeable(repo))return;if(!await isGitWorkTree(repo))return;try{await execFileAsync("git",["-C",repo,"rev-parse","--verify","--quiet",`${ref}^{commit}`])}catch{throw Error(`base branch does not resolve in ${repo}: ${ref}`)}}function requireSchedule(payload,key){let schedule=requireString(payload,key);if(!isValidCron(schedule))throw Error(`invalid cron expression: ${schedule}`);return schedule}function readPrecheck(payload){if(!("precheck"in payload))return;let raw=payload.precheck;if(raw===null)return null;if(!raw||typeof raw!=="object")throw Error("precheck must be an object or null");let command=requireString(raw,"command"),timeoutSeconds=optionalNumber(raw,"timeoutSeconds")??120;if(timeoutSeconds<=0)throw Error("precheck.timeoutSeconds must be greater than zero");return{command,timeoutSeconds}}function readGraceMinutes(payload){let value=optionalNumber(payload,"missedRunGraceMinutes");if(value!==void 0&&value<0)throw Error("missedRunGraceMinutes must be zero or more minutes");return value}var AUTOMATION_HANDLERS=[{name:"automation.list",async handle(_payload,ctx){let automations=ctx.automations.list(),lastRunStatus={};for(let automation of automations){let latest=ctx.automations.runsFor(automation.id,1)[0];if(latest)lastRunStatus[automation.id]=latest.status}return{automations,lastRunStatus,keepsDaemonAlive:ctx.automations.hasEnabled()}}},{name:"automation.create",async handle(payload,ctx){let precheck=readPrecheck(payload),repo=requireString(payload,"repo"),baseRef=optionalString(payload,"baseRef"),target="target"in payload?readAutomationTarget(payload.target):void 0;if(!target)await assertRoutineRepo(repo);if(baseRef)await assertRoutineBaseRef(repo,baseRef);let targetOptions={target:target??void 0,vendor:optionalVendor(payload,"vendor"),baseRef,persistentSession:optionalBoolean(payload,"persistentSession")};assertAutomationTargetOptions(targetOptions),await assertAutomationTargetTask({repo,target:target??void 0},ctx.orch);let automation=await ctx.automations.create({name:requireString(payload,"name"),repo,...target?{target}:{},prompt:requireString(payload,"prompt"),schedule:requireSchedule(payload,"schedule"),missedRunGraceMinutes:readGraceMinutes(payload)??60,...optionalVendor(payload,"vendor")?{vendor:optionalVendor(payload,"vendor")}:{},...precheck?{precheck}:{},...baseRef?{baseRef}:{},...optionalBoolean(payload,"persistentSession")===!0?{persistentSession:!0}:{},...optionalBoolean(payload,"enabled")!==void 0?{enabled:optionalBoolean(payload,"enabled")}:{}});return ctx.daemon.reevaluateIdle(),{automation}}},{name:"automation.update",async handle(payload,ctx){let id=requireString(payload,"id"),nextBaseRef="baseRef"in payload?optionalString(payload,"baseRef"):void 0,currentRepo=ctx.automations.get(id)?.repo;if(nextBaseRef&¤tRepo)await assertRoutineBaseRef(currentRepo,nextBaseRef);let patch={...optionalString(payload,"name")!==void 0?{name:optionalString(payload,"name")}:{},...optionalString(payload,"prompt")!==void 0?{prompt:optionalString(payload,"prompt")}:{},...payload.vendor===null?{vendor:null}:optionalVendor(payload,"vendor")!==void 0?{vendor:optionalVendor(payload,"vendor")}:{},..."target"in payload?{target:readAutomationTarget(payload.target)}:{},..."schedule"in payload?{schedule:requireSchedule(payload,"schedule")}:{},...readPrecheck(payload)!==void 0?{precheck:readPrecheck(payload)}:{},..."baseRef"in payload?{baseRef:optionalString(payload,"baseRef")??null}:{},...optionalBoolean(payload,"enabled")!==void 0?{enabled:optionalBoolean(payload,"enabled")}:{},...readGraceMinutes(payload)!==void 0?{missedRunGraceMinutes:readGraceMinutes(payload)}:{},...optionalBoolean(payload,"persistentSession")!==void 0?{persistentSession:optionalBoolean(payload,"persistentSession")}:{}},current=ctx.automations.get(id);if(!current)throw Error(`automation not found: ${id}`);let targetOptions=mergeAutomationTargetOptions(current,patch);if(assertAutomationTargetOptions(targetOptions),patch.target)await assertAutomationTargetTask({repo:current.repo,target:patch.target},ctx.orch);let automation=await ctx.automations.update(id,patch);if(!automation)throw Error(`automation not found: ${id}`);return ctx.daemon.reevaluateIdle(),{automation}}},{name:"automation.delete",async handle(payload,ctx){let id=requireString(payload,"id"),deleted=await ctx.automations.delete(id);if(deleted)await ctx.inbox.deleteRoutineEpisode(id).catch(()=>{}),ctx.daemon.reevaluateIdle();return{deleted}}},{name:"automation.runs",async handle(payload,ctx){let automationId=requireString(payload,"id");if(!ctx.automations.get(automationId))throw Error(`automation not found: ${automationId}`);return{runs:ctx.automations.runsFor(automationId)}}},{name:"automation.runNow",blocking:!0,async handle(payload,ctx){let id=requireString(payload,"id"),automation=ctx.automations.get(id);if(!automation)throw Error(`automation not found: ${id}`);return{status:await runAutomationOnce({store:ctx.automations,orch:ctx.orch,runtime:ctx.runtime,link:ctx.selfLink,...ctx.plugins?{plugins:()=>ctx.plugins??null}:{},...ctx.deferredPrompts?{deferred:ctx.deferredPrompts}:{},inbox:ctx.inbox},automation,{scheduledFor:Date.now(),trigger:"manual"})}}}];var PEER_HEADER=/^\[(?:ROVE|KOBE) PEER\]\s+from\s+"([^"]{1,200})"/;function deferredPromptSender(prompt){let label=PEER_HEADER.exec(prompt)?.[1]?.trim();return label?label:void 0}function deferredPromptInput(payload){let taskId=requireString(payload,"taskId"),tabId=requireString(payload,"tabId"),prompt=requireString(payload,"prompt"),layer=requireString(payload,"layer");if(layer!=="recent-human-write"&&layer!=="composer-not-empty")throw Error('layer must be "recent-human-write" or "composer-not-empty"');let senderLabel=optionalString(payload,"senderLabel")??deferredPromptSender(prompt),senderTaskId=optionalString(payload,"senderTaskId");return{taskId,tabId,prompt,layer,...senderLabel!==void 0?{senderLabel}:{},...senderTaskId!==void 0?{senderTaskId}:{},at:Date.now()}}async function fileWithInbox(input,ctx){if(!ctx.orch.getTask(input.taskId))throw Error(`task not found: ${input.taskId}`);if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let kind="filed",record;try{record=await ctx.deferredPrompts.file(input)}catch(error){if(!(error instanceof DeferredPromptPendingError))throw error;kind="occupied",record=error.existing}return await ctx.inbox.recordPromptDeferred(record.taskId,record.tabId,record.id,record.layer,record.at+DEFERRED_PROMPT_TTL_MS,record.senderLabel),{kind,record}}function errorText2(error){return error instanceof Error?error.message:String(error)}function publicRecord(record){return{id:record.id,taskId:record.taskId,tabId:record.tabId,prompt:record.prompt,layer:record.layer,at:new Date(record.at).toISOString(),expiresAt:new Date(record.at+DEFERRED_PROMPT_TTL_MS).toISOString(),...record.senderLabel!==void 0?{senderLabel:record.senderLabel}:{},...record.senderTaskId!==void 0?{senderTaskId:record.senderTaskId}:{},...record.dismissedAt!==void 0?{dismissedAt:new Date(record.dismissedAt).toISOString()}:{}}}async function deleteDeferredInboxPointer(record,ctx){await ctx.inbox.deleteEpisode(record.taskId,record.tabId,void 0,"prompt_deferred",record.id)}async function cleanupDeliveredClaim(claim,ctx,report){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let{record}=claim;try{await deleteDeferredInboxPointer(record,ctx),await ctx.deferredPrompts.completeClaim(claim),report.cleaned.push(record.id)}catch(error){await ctx.deferredPrompts.releaseClaim(claim),report.cleanupPending.push({id:record.id,taskId:record.taskId,tabId:record.tabId,error:errorText2(error)})}}async function deliverClaim(claim,ctx,report){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let{record}=claim;if(record.deliveredAt||record.deliveryStartedAt){await cleanupDeliveredClaim(claim,ctx,report);return}let task=ctx.orch.getTask(record.taskId);if(!task?.worktreePath){await ctx.deferredPrompts.releaseClaim(claim),report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"unavailable"});return}let deliveryStarted=!1;try{await ctx.deferredPrompts.beginDelivery(claim),deliveryStarted=!0;let outcome=await ctx.runtime.deliverPromptToLiveEngineTabDetailed({id:task.id,tabId:record.tabId,vendor:task.vendor,command:task.command,worktreePath:task.worktreePath},record.prompt);if(outcome.outcome==="delivered")await ctx.deferredPrompts.markDelivered(claim),report.delivered.push(record.id),await cleanupDeliveredClaim(claim,ctx,report);else await ctx.deferredPrompts.resetDelivery(claim),await ctx.deferredPrompts.releaseClaim(claim),report.retained.push(outcome.outcome==="busy"?{id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"busy",layer:outcome.layer}:{id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"unavailable"})}catch(error){if(await ctx.deferredPrompts.releaseClaim(claim).catch(()=>{}),deliveryStarted)report.cleanupPending.push({id:record.id,taskId:record.taskId,tabId:record.tabId,error:errorText2(error)});else report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"error",error:errorText2(error)})}}async function flushDeferredPrompts(ctx){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let report={delivered:[],cleaned:[],expired:[],retained:[],cleanupPending:[]},swept=await sweepExpiredDeferredPrompts({store:ctx.deferredPrompts,inbox:ctx.inbox});report.expired.push(...swept.expired),report.cleanupPending.push(...swept.cleanupPending);let listed=await ctx.deferredPrompts.list();for(let record of listed.records){if(record.deliveredAt||record.deliveryStartedAt){let claimed2=await ctx.deferredPrompts.claim(record.id);if(claimed2.kind==="claimed")await cleanupDeliveredClaim(claimed2.claim,ctx,report);continue}if(ctx.runtime.deliveryGuard()==="on"){report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"gate-enabled"});continue}let claimed=await ctx.deferredPrompts.claim(record.id);if(claimed.kind==="in-flight"){report.retained.push({id:record.id,taskId:record.taskId,tabId:record.tabId,reason:"in-flight"});continue}if(claimed.kind==="claimed")await deliverClaim(claimed.claim,ctx,report)}return report}function retiredDeferredPromptRpc(name,replacement){return{name,handle(){throw Error(`RETIRED_RPC: ${name} is gone; use ${replacement}. This client is an older build than the daemon \u2014 restart Rove to update the client (restarting the daemon will not help).`)}}}var DEFERRED_PROMPT_HANDLERS=[{name:"deferredPrompt.fileIfVacant",async handle(payload,ctx){let result=await fileWithInbox(deferredPromptInput(payload),ctx);return{kind:result.kind,id:result.record.id,expiresAt:publicRecord(result.record).expiresAt,...result.kind==="occupied"?{layer:result.record.layer}:{}}}},{name:"deferredPrompt.list",async handle(payload,ctx){if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let listed=await ctx.deferredPrompts.list();return{records:(payload.includeDismissed===!0?[...listed.records,...listed.dismissed]:listed.records).map(publicRecord)}}},{name:"deferredPrompt.dismiss",async handle(payload,ctx){let id=requireString(payload,"id");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let dropped=await ctx.deferredPrompts.discard(id,"Inbox item dismissed");if(!dropped)return{dismissed:!1};return await deleteDeferredInboxPointer(dropped,ctx),{dismissed:!0,record:publicRecord(dropped)}}},{name:"deferredPrompt.release",blocking:!0,async handle(payload,ctx){let id=requireString(payload,"id");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let report={delivered:[],cleaned:[],expired:[],retained:[],cleanupPending:[]},claimed=await ctx.deferredPrompts.claim(id);if(claimed.kind==="claimed")await deliverClaim(claimed.claim,ctx,report);return{kind:claimed.kind,...report}}},{name:"deferredPrompt.discardTab",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),tabId=requireString(payload,"tabId");if(!ctx.deferredPrompts)throw Error("deferred prompt store unavailable");let dropped=await ctx.deferredPrompts.discardTab(taskId,tabId,"tab closed");for(let record of dropped)await deleteDeferredInboxPointer(record,ctx);return{dropped:dropped.map((record)=>record.id)}}},{name:"deferredPrompt.flush",blocking:!0,async handle(_payload,ctx){return await flushDeferredPrompts(ctx)}},retiredDeferredPromptRpc("deferredPrompt.file","deferredPrompt.fileIfVacant"),retiredDeferredPromptRpc("deferredPrompt.get","deferredPrompt.release"),retiredDeferredPromptRpc("deferredPrompt.resolve","deferredPrompt.release")];async function ingestAgentTurns(store,runtime,orch,input){if(!input.transcriptPath)return{recorded:0,latest:void 0};let task=orch.getTask(input.taskId),vendor=input.vendor??task?.vendor;if(!vendor)return{recorded:0,latest:void 0};let turns=await runtime.readEngineTurns(vendor,input.transcriptPath);if(turns.length===0)return{recorded:0,latest:void 0};let records=turns.map((turn)=>({...turn,taskId:input.taskId,...input.tabId?{tabId:input.tabId}:{},vendor,...task?.repo?{repo:task.repo}:{}})),recorded=await store.record(records),last=records[records.length-1],latest=last?{id:last.id,model:last.model,usage:last.usage,startedAt:last.startedAt,endedAt:last.endedAt}:void 0;return{recorded,latest}}function ingestAgentTurnsBestEffort(store,runtime,orch,input,onDone){if(!store){onDone?.(void 0);return}ingestAgentTurns(store,runtime,orch,input).then(({latest})=>onDone?.(latest)).catch((err)=>{logDaemonError("agent-turns-ingest",err),onDone?.(void 0)})}import{existsSync as existsSync2}from"fs";import path from"path";function normalize(p){return p.length>1&&p.endsWith("/")?p.slice(0,-1):p}function isAncestorOrSelf(wt,cwd){return cwd===wt||cwd.startsWith(`${wt}/`)}function crossesRepoBoundary(wt,cwd){for(let dir=cwd;dir.length>wt.length;dir=path.dirname(dir))if(existsSync2(path.join(dir,".git")))return!0;return!1}function matchTaskByCwd(tasks,cwd){let target=normalize(cwd),bestId,bestWt="",bestLen=-1;for(let t of tasks){if(!t.worktreePath)continue;let wt=normalize(t.worktreePath);if(isAncestorOrSelf(wt,target)&&wt.length>bestLen)bestLen=wt.length,bestWt=wt,bestId=t.id}if(bestId&&crossesRepoBoundary(bestWt,target))return;return bestId}function matchTaskByWorktreePath(tasks,worktreePath){let target=normalize(worktreePath);for(let t of tasks)if(t.worktreePath&&normalize(t.worktreePath)===target)return t.id;return}function findAdoptableWorktree(tasks,cwd){let target=normalize(cwd),repos=new Set,known=new Set;for(let t of tasks){if(t.repo&&path.isAbsolute(t.repo))repos.add(normalize(t.repo));if(t.worktreePath)known.add(normalize(t.worktreePath))}for(let repo of repos)for(let root of managedWorktreeRootsFor(repo,readWorktreeBaseOverride(repo)).map(normalize)){let prefix=`${root}/`;if(!target.startsWith(prefix))continue;let name=target.slice(prefix.length).split("/")[0];if(!name)continue;let worktreePath=`${prefix}${name}`;if(known.has(worktreePath))return;return{repo,worktreePath}}return}var ENGINE_REPORT_HANDLER={name:"engine.reportEvent",async handle(payload,ctx){let kind=requireString(payload,"kind");if(!ctx.runtime.isEngineActivityKind(kind))throw Error(`BAD_EVENT_KIND: unknown engine event kind: ${kind}`);let explicitId=optionalString(payload,"taskId"),cwd=optionalString(payload,"cwd");if(!explicitId&&cwd&&kind==="session-start"){let cand=findAdoptableWorktree(ctx.orch.listTasks(),cwd);if(cand)try{await ctx.orch.adoptWorktree({repo:cand.repo,worktreePath:cand.worktreePath,ifExists:"return"})}catch(err){logDaemonError("worktree-autosync",err)}}let taskId=explicitId??(cwd?matchTaskByCwd(ctx.orch.listTasks(),cwd):void 0);if(!taskId)return{};let detail=optionalActivityDetail(payload),tabId=optionalString(payload,"tabId"),sessionId=optionalString(payload,"sessionId"),transcriptPath=optionalString(payload,"transcriptPath"),session=sessionId?{id:sessionId,transcriptPath}:void 0,isStateKind=ctx.runtime.affectsActivityState(kind),vendor=optionalString(payload,"engine");if(isStateKind){if(ctx.activity.report(taskId,kind,detail,tabId,session,vendor),explicitId)await ctx.inbox.record(taskId,kind,detail,tabId??null).catch((err)=>logDaemonError("attention-inbox-record",err))}if(ctx.engineEvents?.append(taskId,{kind,...tabId?{tabId}:{},...vendor?{vendor}:{},...detail?{detail}:{},at:Date.now()}),kind==="pre-compact"||kind==="post-compact"||kind==="subagent-start"||kind==="subagent-stop")ctx.bus.publish("engine.lifecycle",{taskId,kind,...tabId?{tabId}:{},at:Date.now()});let pluginReport={kind,taskId,...detail?{detail}:{},...vendor?{vendor}:{},...tabId?{tabId}:{},...sessionId?{sessionId}:{}};if(kind!=="turn-complete")ctx.plugins?.handleEngineReport(pluginReport);if(kind==="turn-complete")ingestAgentTurnsBestEffort(ctx.agentTurns,ctx.runtime,ctx.orch,{taskId,...tabId?{tabId}:{},...vendor?{vendor}:{},...transcriptPath?{transcriptPath}:{}},(latest)=>ctx.plugins?.handleEngineReport(latest?{...pluginReport,detail:{...pluginReport.detail??{},turn:latest}}:pluginReport));if(kind==="turn-start"){if(ctx.runtime.maybeAutoStart(ctx.orch,taskId).then((result)=>{if(result==="moved")console.log(`[status-rules] task ${taskId} auto-moved backlog \u2192 in_progress`)}).catch((err)=>logDaemonError("status-rules",err)),ctx.orch.getTask(taskId)?.quotaResume)ctx.orch.setQuotaResume(taskId,null).catch((err)=>logDaemonError("quota-resume",err))}if(kind==="turn-failed"&&detail?.failure==="rate_limit")scheduleQuotaResume(ctx.orch,ctx.runtime,ctx.quotaUsage,taskId,void 0,()=>ctx.plugins??null).catch((err)=>logDaemonError("quota-resume",err));return{}}};var ISSUE_HANDLERS=[{name:"issue.list",async handle(payload,ctx){return ctx.issues.list(requireString(payload,"repoRoot"))}},{name:"issue.repos",async handle(_payload,ctx){return{repos:await ctx.issues.repos()}}},{name:"issue.mutate",async handle(payload,ctx){let repoRoot=requireString(payload,"repoRoot"),op=payload.op,opType=op&&typeof op==="object"?op.type:void 0;if(opType==="link"||opType==="update"){let taskId=op.taskId;if(typeof taskId==="string"&&taskId.length>0&&!ctx.orch.getTask(taskId))throw Error(`task not found: ${taskId}`)}let state=await ctx.issues.mutate(repoRoot,payload.op);return ctx.bus.publish("issue.snapshot",state),ctx.plugins?.handleUiReport({kind:"issue.changed",detail:{repo:repoRoot,...payload.op&&typeof payload.op==="object"?{op:payload.op}:{}}}),state}}];var MAX_FAILING_JOBS=3,MAX_TAIL_LINES=200,RUN_LOG_TIMEOUT_MS=30000;function stringField(value,key){if(typeof value!=="object"||value===null)return"";let field=value[key];return typeof field==="string"?field:""}function runIdFromDetailsUrl(url){return/\/actions\/runs\/(\d+)\b/.exec(url)?.[1]??null}function failingCheckTargets(rollup){let targets=[];for(let entry of rollup){let status=stringField(entry,"status").toUpperCase(),conclusion=stringField(entry,"conclusion").toUpperCase(),contextState=stringField(entry,"state").toUpperCase(),failedAs=null;if(status&&status!=="COMPLETED")failedAs=null;else if(conclusion)failedAs=conclusion==="SUCCESS"||conclusion==="NEUTRAL"||conclusion==="SKIPPED"?null:conclusion;else if(contextState==="FAILURE"||contextState==="ERROR")failedAs=contextState;if(failedAs===null)continue;let url=stringField(entry,"detailsUrl")||stringField(entry,"targetUrl");targets.push({jobName:stringField(entry,"name")||stringField(entry,"context")||"check",conclusion:failedAs,url,runId:runIdFromDetailsUrl(url)})}return targets}function parseFailedRunLog(text,maxLines=MAX_TAIL_LINES){let byJob=new Map;for(let raw of text.split(`
|
|
5
5
|
`)){let line=raw.replace(/\r$/,"");if(line.length===0)continue;let firstTab=line.indexOf("\t");if(firstTab<=0)continue;let job=line.slice(0,firstTab),secondTab=line.indexOf("\t",firstTab+1),body=(secondTab===-1?line.slice(firstTab+1):line.slice(secondTab+1)).replace(/^\uFEFF/,""),lines=byJob.get(job);if(!lines)lines=[],byJob.set(job,lines);if(lines.push(body),lines.length>maxLines)lines.shift()}return byJob}function joinFailingChecks(targets,logsByJob){return{checks:targets.slice(0,MAX_FAILING_JOBS).map((target)=>({jobName:target.jobName,conclusion:target.conclusion,url:target.url,tail:(logsByJob.get(target.jobName)??[]).join(`
|
|
6
6
|
`)})),totalFailing:targets.length}}async function ghText(args,cwd,timeoutMs){let controller=new AbortController,timer=setTimeout(()=>controller.abort(),timeoutMs);try{let res=await spawnGh(args,cwd,controller.signal);if(res.status===0)return{ok:!0,stdout:res.stdout};let stderr=res.stderr.trim();if(res.spawnError)return{ok:!1,detail:stderr||"gh could not be started \u2014 is the GitHub CLI installed?"};if(controller.signal.aborted)return{ok:!1,detail:`gh timed out after ${timeoutMs}ms`};return{ok:!1,detail:stderr||`gh exited ${res.status}`}}finally{clearTimeout(timer)}}async function readFailingChecks(opts){try{let rollupRead=await ghText(["pr","view",String(opts.prNumber),"--json","statusCheckRollup"],opts.worktreePath,PR_VIEW_TIMEOUT_MS);if(!rollupRead.ok)return logDaemonInfo("pr-failing-checks",`gh could not read PR #${opts.prNumber}: ${rollupRead.detail}`),{checks:[],totalFailing:0,unavailable:{reason:"gh_failed",detail:rollupRead.detail}};let parsed=JSON.parse(rollupRead.stdout),rollup=Array.isArray(parsed.statusCheckRollup)?parsed.statusCheckRollup:[],targets=failingCheckTargets(rollup);if(targets.length===0)return{checks:[],totalFailing:0};let runIds=[];for(let target of targets.slice(0,MAX_FAILING_JOBS))if(target.runId&&!runIds.includes(target.runId))runIds.push(target.runId);let logsByJob=new Map;for(let runId of runIds){let log=await ghText(["run","view",runId,"--log-failed"],opts.worktreePath,RUN_LOG_TIMEOUT_MS);if(!log.ok)continue;for(let[job,lines]of parseFailedRunLog(log.stdout))if(!logsByJob.has(job))logsByJob.set(job,lines)}return joinFailingChecks(targets,logsByJob)}catch(err){return logDaemonError("pr-failing-checks",err),{checks:[],totalFailing:0,unavailable:{reason:"gh_error",detail:err instanceof Error?err.message:String(err)}}}}var PR_HANDLERS=[{name:"pr.failingChecks",async handle(payload,ctx){let taskId=requireString(payload,"taskId"),task=ctx.orch?.getTask(taskId),prNumber=task?.prStatus?.number;if(!task?.worktreePath||typeof prNumber!=="number")return{checks:[],totalFailing:0};return await readFailingChecks({worktreePath:task.worktreePath,prNumber})}}];var SUBSYSTEM2="task-deletion-audit";function originText(origin,task){let parts=[];if(origin)parts.push(`client=${origin.clientId}`);if(origin?.requestedBy)parts.push(`by=${origin.requestedBy.taskId}::${origin.requestedBy.tabId}`);let dispatcher=task?.dispatcher;if(dispatcher)parts.push(`spawnedBy=${dispatcher.taskId}::${dispatcher.tabId}`);return parts.length>0?` (${parts.join(" ")})`:""}function subjectText(task,taskId){if(!task)return`task ${taskId}`;let bits=[`task ${taskId}`,`title=${JSON.stringify(task.title)}`,`kind=${task.kind}`];if(task.branch)bits.push(`branch=${task.branch}`);if(task.worktreePath)bits.push(`worktree=${task.worktreePath}`);return bits.join(" ")}function auditDeletionRequested(taskId,task,origin,opts={}){logDaemonInfo(SUBSYSTEM2,`requested ${subjectText(task,taskId)} force=${opts.force===!0} deleteBranch=${opts.deleteBranch===!0}${originText(origin,task)}`)}function auditDeletionRemoved(taskId,task){logDaemonInfo(SUBSYSTEM2,`removed ${subjectText(task,taskId)}`)}function auditDeletionBranchKept(taskId,branch,reason){logDaemonInfo(SUBSYSTEM2,`branch kept task ${taskId} branch=${branch} \u2014 git refused the delete: ${reason.replace(/\s+/g," ").trim()}. Nothing else will remove it; \`git branch -D ${branch}\` does, and drops its reflog with it.`)}function auditDeletionFailed(taskId,task,err){let cause=err instanceof Error?err:Error(String(err)),context=`failed ${subjectText(task,taskId)} \u2014 session teardown and activity/inbox cleanup ALREADY ran; the worktree directory and task entry remain. Reason: `,line=Error(`${context}${cause.message}`);line.name=cause.name;let frames=cause.stack?.split(`
|
|
7
7
|
`).slice(1)??[];if(frames.length>0)line.stack=[`${line.name}: ${line.message}`,...frames].join(`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{claudeGlobalConfigPath,codexAuthPath,copilotConfigPath,kimiCredentialsPath,readTextFileSyncBounded}from"./chunk-7jkf3tvr.js";import{getCustomEngineIds,getDisabledEngineIds}from"./chunk-
|
|
2
|
+
import{claudeGlobalConfigPath,codexAuthPath,copilotConfigPath,kimiCredentialsPath,readTextFileSyncBounded}from"./chunk-7jkf3tvr.js";import{getCustomEngineIds,getDisabledEngineIds}from"./chunk-1kz2syvp.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{__require}from"./chunk-wqfp8d9w.js";var pluginEngines=new Map;function registerPluginEngine(id,spec){if(Object.hasOwn(CONTRIB_ENGINES,id))return!1;return pluginEngines.set(id,spec),!0}function clearPluginEngines(){pluginEngines.clear()}function pluginEngineIds(){return[...pluginEngines.keys()]}var GEMINI={rules:[{state:"blocked",any:["\u2502 apply this change","\u2502 allow execution","waiting for user confirmation"]},{state:"blocked",all:["do you want to proceed"],any:["yes"]},{state:"working",any:["esc to cancel"]}]},OPENCODE={rules:[{state:"blocked",any:["\u25B3 permission required"]},{state:"blocked",all:["esc dismiss"],any:["enter confirm","enter submit","enter toggle"]},{state:"working",any:["esc interrupt","esc to interrupt","ctrl+c to interrupt","esc again to interrupt"]},{state:"idle",any:["enter send"]}]},CURSOR={rules:[{state:"blocked",any:["press any key to log in"]},{state:"blocked",all:["proceed (y)"]},{state:"blocked",any:["run this command?","waiting for approval","skip (esc or n)","(y) (enter)"]},{state:"working",any:["esc to cancel","ctrl+c to stop"]}]},GROK={rules:[{state:"blocked",any:["\u26A0 action required","ctrl+o:yolo"]},{state:"blocked",all:["\u2503"],lineRegex:["^\\s*\u2503\\s+\\S+\\s+\\(\u25CB\\)"]},{state:"working",any:["[stop]"],lineRegex:["^\\s*[\\u2800-\\u28FF]"]}]},DROID={rules:[{state:"blocked",all:["enter to select","esc to cancel"],any:["> yes, allow","> no, cancel"]},{state:"blocked",all:["enter select","esc cancel"]},{state:"working",any:["esc to stop"]}]},AMP={rules:[{state:"blocked",any:["waiting for approval","run this command?","allow editing file:","allow creating file:","confirm tool call"]},{state:"working",lineRegex:["^\\s*\u2570\\s+\\S+\\s+(thinking|streaming|running tools|waiting)\\s+\u2500"]}]},CONTRIB_ENGINES={gemini:{displayName:"Gemini CLI",defaultCommand:["gemini"],screenManifest:GEMINI},opencode:{displayName:"OpenCode",defaultCommand:["opencode"],screenManifest:OPENCODE,firstMessageDelivery:"paste"},cursor:{displayName:"Cursor Agent",defaultCommand:["cursor-agent"],screenManifest:CURSOR},grok:{displayName:"Grok CLI",defaultCommand:["grok"],screenManifest:GROK},droid:{displayName:"Droid",defaultCommand:["droid"],screenManifest:DROID},amp:{displayName:"Amp",defaultCommand:["amp"],screenManifest:AMP}};function isContribEngine(id){return Object.hasOwn(CONTRIB_ENGINES,id)||pluginEngines.has(id)}var CONTRIB_ENGINE_IDS=Object.keys(CONTRIB_ENGINES);function contribEngineEntry(id,base){let spec=CONTRIB_ENGINES[id]??pluginEngines.get(id);if(!spec)return base;return{...base,displayName:spec.displayName,defaultCommand:spec.defaultCommand,...spec.processNames?{processNames:spec.processNames}:{},screenManifest:spec.screenManifest,...spec.identity?{identity:spec.identity}:{},...spec.firstMessageDelivery?{firstMessageDelivery:spec.firstMessageDelivery}:{}}}import{homedir as homedir2}from"os";import{spawnSync}from"child_process";import{existsSync,statSync}from"fs";import{homedir}from"os";var defaultBinaryDeps={fileExists(p){try{return statSync(p).isFile()}catch{return!1}},env(name){return process.env[name]},home(){return homedir()},which(name){let cmd=process.platform==="win32"?"where":"which",out=spawnSync(cmd,[name],{encoding:"utf8"});if(out.status!==0)return;let first=out.stdout.split(`
|
|
3
3
|
`).map((l)=>l.trim()).filter(Boolean)[0];if(!first)return;if(first.startsWith(`${name}:`)&&first.includes("aliased to")){let aliasTarget=first.split("aliased to")[1]?.trim();return aliasTarget&&existsSync(aliasTarget)?aliasTarget:void 0}return first},readdir(p){try{return __require("fs").readdirSync(p)}catch{return[]}}};class BinaryNotFoundError extends Error{checkedPaths;constructor(label,hint,checkedPaths){super(`${label} not found. Checked: ${checkedPaths.join(", ")}. ${hint}`);this.name="BinaryNotFoundError",this.checkedPaths=checkedPaths}}function createBinaryFinder(spec){return async function(deps=defaultBinaryDeps){let checked=[],whichResult=deps.which(spec.name);if(whichResult){if(checked.push(`which:${whichResult}`),deps.fileExists(whichResult))return whichResult}for(let candidate of spec.candidates({deps,home:deps.home()}))if(checked.push(candidate),deps.fileExists(candidate))return candidate;throw spec.notFound(checked)}}import path from"path";class ClaudeBinaryNotFoundError extends BinaryNotFoundError{constructor(checkedPaths){super("Claude Code binary","Ensure 'claude' is on PATH, or install at ~/.claude/local/claude.",checkedPaths);this.name="ClaudeBinaryNotFoundError"}}var findClaudeBinary=createBinaryFinder({name:"claude",candidates({deps,home}){let out=[path.join(home,".claude","local","claude")],nvmBin=deps.env("NVM_BIN");if(nvmBin)out.push(path.join(nvmBin,"claude"));let nvmRoot=path.join(home,".nvm","versions","node"),versions=(deps.readdir?.(nvmRoot)??[]).sort((a,b)=>b.localeCompare(a,void 0,{numeric:!0}));for(let v of versions)out.push(path.join(nvmRoot,v,"bin","claude"));out.push("/opt/homebrew/bin/claude","/usr/local/bin/claude","/usr/bin/claude","/bin/claude");for(let rel of[".local/bin",".npm-global/bin",".yarn/bin",".bun/bin","bin"])out.push(path.join(home,rel,"claude"));return out},notFound:(checked)=>new ClaudeBinaryNotFoundError(checked)});import path2 from"path";class CodexBinaryNotFoundError extends BinaryNotFoundError{constructor(checkedPaths){super("Codex CLI binary","Ensure 'codex' is on PATH (e.g. `brew install codex` or the official installer).",checkedPaths);this.name="CodexBinaryNotFoundError"}}var findCodexBinary=createBinaryFinder({name:"codex",candidates({deps,home}){let out=["/opt/homebrew/bin/codex","/usr/local/bin/codex","/usr/bin/codex","/bin/codex"],nvmBin=deps.env("NVM_BIN");if(nvmBin)out.push(path2.join(nvmBin,"codex"));for(let rel of[".local/bin",".bun/bin","bin"])out.push(path2.join(home,rel,"codex"));return out},notFound:(checked)=>new CodexBinaryNotFoundError(checked)});import path3 from"path";class CopilotBinaryNotFoundError extends BinaryNotFoundError{constructor(checkedPaths){super("GitHub Copilot CLI binary","Ensure 'copilot' is on PATH (for example `npm install -g @github/copilot` or `brew install copilot-cli`).",checkedPaths);this.name="CopilotBinaryNotFoundError"}}var findCopilotBinary=createBinaryFinder({name:"copilot",candidates({deps,home}){let win32=(deps.platform?.()??process.platform)==="win32",names=win32?["copilot.exe","copilot.cmd","copilot"]:["copilot"],dirs=["/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"],nvmBin=deps.env("NVM_BIN");if(nvmBin)dirs.push(nvmBin);let homeDirs=[path3.join(home,".npm-global/bin"),path3.join(home,".local/bin"),path3.join(home,".bun/bin"),path3.join(home,"bin")];if(win32){let appData=deps.env("APPDATA"),localAppData=deps.env("LOCALAPPDATA");if(homeDirs.unshift(path3.join(home,"AppData/Roaming/npm")),appData)homeDirs.unshift(path3.join(appData,"npm"));if(localAppData)homeDirs.unshift(path3.join(localAppData,"npm"))}return[...dirs,...homeDirs].flatMap((dir)=>names.map((name)=>path3.join(dir,name)))},notFound:(checked)=>new CopilotBinaryNotFoundError(checked)});import path4 from"path";class KimiBinaryNotFoundError extends BinaryNotFoundError{constructor(checkedPaths){super("Kimi Code CLI binary","Ensure 'kimi' is on PATH or installed at ~/.kimi-code/bin/kimi.",checkedPaths);this.name="KimiBinaryNotFoundError"}}var findKimiBinary=createBinaryFinder({name:"kimi",candidates:({home})=>[path4.join(home,".kimi-code/bin"),path4.join(home,".local/bin"),path4.join(home,"bin"),"/opt/homebrew/bin","/usr/local/bin"].map((dir)=>path4.join(dir,"kimi")),notFound:(checked)=>new KimiBinaryNotFoundError(checked)});var defaultDeps={readFile(p){return readTextFileSyncBounded(p)},env(name){return process.env[name]},home(){return homedir2()},findClaudeBinary(){return findClaudeBinary()},findCodexBinary(){return findCodexBinary()},findCopilotBinary(){return findCopilotBinary()},findKimiBinary(){return findKimiBinary()}};function decodeJwtPayload(jwt){let parts=jwt.split(".");if(parts.length!==3)return null;let payload=parts[1];if(!payload)return null;let b64=payload.replace(/-/g,"+").replace(/_/g,"/"),padded=b64+"=".repeat((4-b64.length%4)%4);try{let json=Buffer.from(padded,"base64").toString("utf8"),obj=JSON.parse(json);return typeof obj==="object"&&obj!==null?obj:null}catch{return null}}async function probeBinary(probe){try{return{found:!0,path:await probe()}}catch(err){if(err instanceof BinaryNotFoundError)return{found:!1,error:"not found on PATH"};return{found:!1,error:errorMessage(err)}}}var cachedDefaultVendors=null;async function probeAvailableVendors(deps){return(await Promise.all([["claude",()=>deps.findClaudeBinary()],["codex",()=>deps.findCodexBinary()],["copilot",()=>deps.findCopilotBinary()],["kimi",()=>deps.findKimiBinary()]].map(async([vendor,probe])=>(await probeBinary(probe)).found?vendor:null))).filter((v)=>v!==null)}function detectAvailableVendors(deps=defaultDeps){if(deps!==defaultDeps)return probeAvailableVendors(deps);if(cachedDefaultVendors)return cachedDefaultVendors;let pending=probeAvailableVendors(deps).catch((err)=>{throw cachedDefaultVendors=null,err});return cachedDefaultVendors=pending,pending}function resetAvailableVendorsCache(){cachedDefaultVendors=null,cachedContribEngines=null}async function installedEngineIds(deps=defaultDeps){let builtins=await detectAvailableVendors(deps),contrib=await detectContribEngines();return[...new Set([...builtins,...getCustomEngineIds(),...contrib])]}async function availableEngineIds(deps=defaultDeps){let disabled=new Set(getDisabledEngineIds());return(await installedEngineIds(deps)).filter((id)=>!disabled.has(id))}var cachedContribEngines=null;function detectContribEngines(){if(cachedContribEngines)return cachedContribEngines;let which=globalThis.Bun?.which;return cachedContribEngines=Promise.resolve([...which?CONTRIB_ENGINE_IDS.filter((id)=>{let bin=CONTRIB_ENGINES[id]?.defaultCommand[0];return bin?which(bin)!==null:!1}):[],...pluginEngineIds()]),cachedContribEngines}async function detectClaudeAccount(deps=defaultDeps){let binary=await probeBinary(()=>deps.findClaudeBinary()),configPath=claudeGlobalConfigPath(deps.env,deps.home()),raw;try{raw=deps.readFile(configPath)}catch(err){return{binary,account:{kind:"none"},accountError:`read ${configPath}: ${errorMessage(err)}`}}if(raw===null)return{binary,account:{kind:"none"}};let parsed;try{parsed=JSON.parse(raw)}catch(err){return{binary,account:{kind:"none"},accountError:`parse ${configPath}: ${errorMessage(err)}`}}let oauth=parsed?.oauthAccount;if(!oauth||typeof oauth!=="object")return{binary,account:{kind:"none"}};let o=oauth,email=typeof o.emailAddress==="string"?o.emailAddress:void 0;if(!email)return{binary,account:{kind:"none"}};return{binary,account:{kind:"oauth",email,organization:typeof o.organizationName==="string"?o.organizationName:void 0,displayName:typeof o.displayName==="string"?o.displayName:void 0,billingType:typeof o.billingType==="string"?o.billingType:void 0}}}async function detectCodexAccount(deps=defaultDeps){let binary=await probeBinary(()=>deps.findCodexBinary()),authPath=codexAuthPath(deps.env,deps.home()),raw;try{raw=deps.readFile(authPath)}catch(err){return{binary,account:{kind:"none"},accountError:`read ${authPath}: ${errorMessage(err)}`}}if(raw===null)return{binary,account:{kind:"none"}};let parsed;try{parsed=JSON.parse(raw)}catch(err){return{binary,account:{kind:"none"},accountError:`parse ${authPath}: ${errorMessage(err)}`}}let obj=parsed??{},tokens=obj.tokens,idToken=typeof tokens?.id_token==="string"?tokens.id_token:void 0;if(idToken){let payload=decodeJwtPayload(idToken);if(!payload)return{binary,account:{kind:"none"},accountError:"codex id_token: malformed JWT"};let email=typeof payload.email==="string"?payload.email:void 0,authClaimRaw=payload["https://api.openai.com/auth"],authClaim=typeof authClaimRaw==="object"&&authClaimRaw!==null&&!Array.isArray(authClaimRaw)?authClaimRaw:void 0,plan=typeof authClaim?.chatgpt_plan_type==="string"?authClaim.chatgpt_plan_type:void 0;if(email)return{binary,account:{kind:"chatgpt",email,plan}};return{binary,account:{kind:"none"},accountError:"codex id_token: no email claim"}}let apiKey=obj.OPENAI_API_KEY;if(typeof apiKey==="string"&&apiKey.length>0)return{binary,account:{kind:"apikey"}};return{binary,account:{kind:"none"}}}async function detectCopilotAccount(deps=defaultDeps){let binary=await probeBinary(()=>deps.findCopilotBinary());for(let source of["COPILOT_GITHUB_TOKEN","GH_TOKEN","GITHUB_TOKEN"])if(deps.env(source)?.trim())return{binary,account:{kind:"token",source}};let configPath=copilotConfigPath(deps.env,deps.home()),raw;try{raw=deps.readFile(configPath)}catch(err){return{binary,account:{kind:"none"},accountError:`read ${configPath}: ${errorMessage(err)}`}}if(raw===null)return{binary,account:{kind:"none"}};let parsed;try{parsed=JSON.parse(raw)}catch(err){return{binary,account:{kind:"none"},accountError:`parse ${configPath}: ${errorMessage(err)}`}}if(!isRecord(parsed))return{binary,account:{kind:"none"}};if(hasStringDeep(parsed,["github_token","oauth_token","access_token","token","selectedUser","currentUser","user"]))return{binary,account:{kind:"oauth"}};return{binary,account:{kind:"none"}}}async function detectKimiAccount(deps=defaultDeps){let binary=await probeBinary(()=>deps.findKimiBinary()),credPath=kimiCredentialsPath(deps.env,deps.home()),raw;try{raw=deps.readFile(credPath)}catch(err){return{binary,account:{kind:"none"},accountError:`read ${credPath}: ${errorMessage(err)}`}}if(raw===null)return{binary,account:{kind:"none"}};let parsed;try{parsed=JSON.parse(raw)}catch(err){return{binary,account:{kind:"none"},accountError:`parse ${credPath}: ${errorMessage(err)}`}}if(!isRecord(parsed))return{binary,account:{kind:"none"}};let token=parsed.access_token;if(typeof token==="string"&&token.length>0)return{binary,account:{kind:"oauth"}};return{binary,account:{kind:"none"}}}function isRecord(v){return typeof v==="object"&&v!==null&&!Array.isArray(v)}function hasStringDeep(value,interestingKeys,depth=0){if(depth>4||!isRecord(value))return!1;for(let[key,entry]of Object.entries(value)){if(interestingKeys.includes(key)&&typeof entry==="string"&&entry.length>0)return!0;if(isRecord(entry)&&hasStringDeep(entry,interestingKeys,depth+1))return!0}return!1}
|
|
4
4
|
export{registerPluginEngine,clearPluginEngines,pluginEngineIds,isContribEngine,CONTRIB_ENGINE_IDS,contribEngineEntry,resetAvailableVendorsCache,installedEngineIds,availableEngineIds,detectClaudeAccount,detectCodexAccount,detectCopilotAccount,detectKimiAccount};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailedAdapter,deriveTitleFromSession,ensureTaskSessionAdapter,startTaskSessionWithPromptAdapter,tearDownTaskSessionAdapter}from"./chunk-
|
|
2
|
+
import{deliverPromptToLiveEngineAdapter,deliverPromptToLiveEngineDetailedAdapter,deliverPromptToLiveEngineTabDetailedAdapter,deriveTitleFromSession,ensureTaskSessionAdapter,startTaskSessionWithPromptAdapter,tearDownTaskSessionAdapter}from"./chunk-7zmsb99k.js";import{resolvePluginBinPath}from"./chunk-d4vadrmy.js";import{Orchestrator}from"./chunk-sx8fw2qd.js";import{GH_PR_VIEW_FIELDS,classifyGhFailure,mapGhPrView,nextPrPoll,samePrStatus}from"./chunk-632q2pg7.js";import"./chunk-tqat428f.js";import{GitWorktreeManager,parseDirtyPaths}from"./chunk-ffj7jp3k.js";import{auditDeletionBranchKept,auditDeletionResidue,auditDeletionSalvaged,auditWorktreeResidue,auditWorktreeSalvaged,parseAheadBehind,startDaemonServer}from"./chunk-97h1gbh8.js";import"./chunk-2qkj3wsg.js";import"./chunk-41ahvk51.js";import"./chunk-rz8zs7j8.js";import"./chunk-932ymm0m.js";import{parsePorcelain}from"./chunk-xqw5d5rv.js";import"./chunk-h4n8ffqy.js";import{DEFAULT_BASE_REF_CANDIDATES,readHeadSha,readRefSha,resolveGitDirs}from"./chunk-vyb5a2rh.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{affectsActivityState,isEngineActivityKind}from"./chunk-3e5n28t6.js";import{PLACEHOLDER_TASK_TITLE}from"./chunk-r05r01nv.js";import"./chunk-b3pxf5xd.js";import"./chunk-r2pztgm6.js";import"./chunk-38272eqv.js";import"./chunk-5f80ggc3.js";import{SUBCOMMAND_VERBS}from"./chunk-s76r56rj.js";import{TaskIndexStore}from"./chunk-qs80x1cd.js";import"./chunk-qkehg2c1.js";import{CURRENT_VERSION,checkLatestVersion}from"./chunk-zk9yzb09.js";import{autoStatusEnabled,deliveryGuard}from"./chunk-fex98vjg.js";import{execHostForWorktreePath}from"./chunk-dx4xyegr.js";import"./chunk-bkba9c4n.js";import"./chunk-g2advs8t.js";import"./chunk-0zsechc5.js";import{GENERIC_PROTOCOL,engineProtocolKey,getEngineProtocol,protocolEntry,resolveCommandProtocol}from"./chunk-k43kf7qa.js";import"./chunk-7q67dz4c.js";import{installDaemonCrashHandlers}from"./chunk-1cjd7h8p.js";import"./chunk-b99tf0jr.js";import"./chunk-xcgb5q15.js";import"./chunk-hape5vwj.js";import{engineDisplayName,kobeApiInvocation}from"./chunk-y9xr3c66.js";import"./chunk-amt0tw20.js";import"./chunk-tp48wamv.js";import"./chunk-sdyzxayr.js";import"./chunk-zqfbyq0a.js";import"./chunk-jvxh93nz.js";import{connectOrStartDaemon}from"./chunk-tngnng9q.js";import{stopDaemonProcess}from"./chunk-gyc4fj15.js";import{KobeDaemonClient}from"./chunk-0wnfq5sx.js";import"./chunk-7j5372wb.js";import"./chunk-mnqxgvyb.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-7027e3rb.js";import{createEngineTurnDetector,engineEntry,engineTitleTurnHint,vendorsWithQuotaProbe}from"./chunk-07m61r5j.js";import{availableEngineIds}from"./chunk-9esrwvqc.js";import"./chunk-w7zv47mp.js";import{spawnCapture}from"./chunk-gwcneg6c.js";import"./chunk-6tvrvekq.js";import"./chunk-7jkf3tvr.js";import"./chunk-2fypr41w.js";import{backfillSavedReposFromProjects,getCustomEngineIds,getPersistedString,getRemoteRepoConfig,getSavedRepos,setPersistedString}from"./chunk-1kz2syvp.js";import"./chunk-j75axe29.js";import"./chunk-k1qe5qqa.js";import{DEFAULT_TASK_VENDOR,isTaskStatus}from"./chunk-xy8be792.js";import{releaseSync}from"./chunk-ayhhgfsz.js";import"./chunk-2brn8e1y.js";import"./chunk-3wj69b0a.js";import"./chunk-ch9b1d5y.js";import{BUILTIN_VENDORS,isBuiltinVendor}from"./chunk-bq3fyc0p.js";import{isProcessAlive,readPidFile}from"./chunk-mv7ptt5w.js";import{activeCliName}from"./chunk-v0zxyqrq.js";import{migrateRoveDaemonStateLayout}from"./chunk-e29hx18x.js";import"./chunk-g56kvet6.js";import"./chunk-k2ckx8zw.js";import{rotateLogIfNeeded}from"./chunk-pw6yfj10.js";import"./chunk-nyv7a76k.js";import"./chunk-rxvbm4vt.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath}from"./chunk-mcdv19rs.js";import{readRoveHomeDirEnv}from"./chunk-r3k9rbwm.js";import"./chunk-wqfp8d9w.js";function sniffProtocolFromTitle(title){let trimmed=title?.trim();if(!trimmed)return null;let owners=new Map;for(let vendor of BUILTIN_VENDORS)for(let glyph of engineEntry(vendor).terminalTitle?.statusPrefixes??[])owners.set(glyph,[...owners.get(glyph)??[],vendor]);for(let glyph of[...owners.keys()].sort((a,b)=>b.length-a.length)){if(!trimmed.startsWith(glyph))continue;if(trimmed.slice(glyph.length).trim().length===0)continue;let vendors=owners.get(glyph)??[];if(vendors.length===1)return vendors[0];return null}return null}function protocolUpgradeFromLiveSession(task,evidence){let command=task.command?.trim();if(!command)return null;if(task.vendor&&isBuiltinVendor(task.vendor))return null;if(resolveCommandProtocol(command)!==GENERIC_PROTOCOL)return null;let walk=evidence.walkVendor,vendor=walk!==null&&isBuiltinVendor(walk)?walk:sniffProtocolFromTitle(evidence.title);return vendor?{command,vendor}:null}function protocolWriteBackFromLiveSession(task,evidence,customEngineIds){let id=task.vendor?.trim();if(!id)return null;if(getEngineProtocol(id)!==void 0)return null;if(!customEngineIds.includes(id))return null;let walk=evidence.walkVendor;return walk!==null&&isBuiltinVendor(walk)?{id,protocol:walk}:null}async function latestTranscriptMtime(vendor,worktree){if(!worktree)return 0;return protocolEntry(vendor).history.latestTranscriptMtimeForWorktree(worktree)}async function maybeAutoStart(orch,taskId,enabled=autoStatusEnabled){if(!enabled())return"skipped";let task=orch.getTask(taskId);if(!task)return"skipped";if((task.kind??"task")==="main")return"skipped";if(task.status!=="backlog")return"skipped";return await orch.setStatus(taskId,"in_progress"),"moved"}var SYNC_TIMEOUT_MS=60000,SYNC_CONFLICT="SYNC_CONFLICT",SYNC_DIRTY="SYNC_WORKTREE_DIRTY";async function git(cwd,args,signal,write=!1){return spawnCapture("git",args,{cwd,env:write?process.env:readOnlyGitProcessEnv(),signal})}function parseConflictedPaths(stdout){return stdout.split(`
|
|
3
3
|
`).map((line)=>line.trim()).filter((line)=>line.length>0)}async function syncWorktreeWithBase(worktreePath,baseRef,signal){let status=await git(worktreePath,["status","--porcelain=v1"],signal);if(status.status!==0)throw Error("git status failed");let dirty=parseDirtyPaths(status.stdout);if(dirty.length>0)throw Error(`${SYNC_DIRTY}: ${dirty.join(", ")}`);let before=await git(worktreePath,["rev-parse","HEAD"],signal),merge=await git(worktreePath,["merge","--no-edit",baseRef],signal,!0);if(merge.status!==0){let conflicted=await git(worktreePath,["diff","--name-only","--diff-filter=U"],signal),paths=parseConflictedPaths(conflicted.stdout);if(paths.length>0)throw Error(`${SYNC_CONFLICT}: ${paths.join(", ")}`);let detail=merge.stderr?.trim();throw Error(detail?`git merge ${baseRef} failed \u2014 ${detail}`:`git merge ${baseRef} failed`)}let after=await git(worktreePath,["rev-parse","HEAD"],signal);return{baseRef,alreadyCurrent:before.status===0&&before.stdout.trim()===after.stdout.trim()}}import{readFileSync}from"fs";import{join}from"path";var BASE_REF_TTL_MS=300000,cache=new Map;async function git2(worktreePath,args,signal){return await spawnCapture("git",[...args],{cwd:worktreePath,env:readOnlyGitProcessEnv(),signal})}async function refExists(worktreePath,ref,signal){return(await git2(worktreePath,["rev-parse","--verify","--quiet",ref],signal)).status===0}async function sharesHistory(worktreePath,ref,signal){return(await git2(worktreePath,["merge-base",ref,"HEAD"],signal)).status===0}async function baseCheckoutBranchFromGit(worktreePath,signal){let out=await git2(worktreePath,["worktree","list","--porcelain"],signal);if(out.status!==0)return null;return(out.stdout.split(`
|
|
4
4
|
|
|
5
5
|
`,1)[0]??"").split(`
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{envReadyForTasks}from"./chunk-
|
|
2
|
+
import{envReadyForTasks}from"./chunk-pc3djc2b.js";import{wizardKeyLines}from"./chunk-v8mscj31.js";import"./chunk-yv913can.js";import"./chunk-ftpbjbg9.js";import"./chunk-bpkg51p5.js";import"./chunk-2h9nz0zv.js";import{$Fragment,$jsx,$jsxs,bootPaneHost,currentPrefixConfiguration,pageCloseBindings,require_react_production,useBindings,useRenderer,useT,useTheme}from"./chunk-ytxw3rvn.js";import"./chunk-764bsyb0.js";import"./chunk-xqw5d5rv.js";import"./chunk-h4n8ffqy.js";import"./chunk-6xppe9bj.js";import"./chunk-zk9yzb09.js";import"./chunk-k43kf7qa.js";import"./chunk-y9xr3c66.js";import"./chunk-amt0tw20.js";import"./chunk-gs8kf0me.js";import"./chunk-tp48wamv.js";import"./chunk-sdyzxayr.js";import"./chunk-zqfbyq0a.js";import"./chunk-jvxh93nz.js";import"./chunk-tngnng9q.js";import"./chunk-gyc4fj15.js";import"./chunk-0wnfq5sx.js";import"./chunk-7j5372wb.js";import"./chunk-mnqxgvyb.js";import"./chunk-7027e3rb.js";import"./chunk-07m61r5j.js";import"./chunk-9esrwvqc.js";import"./chunk-w7zv47mp.js";import"./chunk-gwcneg6c.js";import"./chunk-6tvrvekq.js";import"./chunk-7jkf3tvr.js";import"./chunk-2fypr41w.js";import"./chunk-1kz2syvp.js";import"./chunk-j75axe29.js";import"./chunk-k1qe5qqa.js";import"./chunk-xy8be792.js";import"./chunk-ayhhgfsz.js";import"./chunk-2brn8e1y.js";import"./chunk-3wj69b0a.js";import"./chunk-ch9b1d5y.js";import"./chunk-bq3fyc0p.js";import"./chunk-mv7ptt5w.js";import"./chunk-v0zxyqrq.js";import"./chunk-e29hx18x.js";import"./chunk-g56kvet6.js";import"./chunk-k2ckx8zw.js";import"./chunk-pw6yfj10.js";import"./chunk-mcdv19rs.js";import"./chunk-r3k9rbwm.js";import{__toESM}from"./chunk-wqfp8d9w.js";import{TextAttributes}from"@opentui/core";var import_react2=__toESM(require_react_production(),1);function inlineRowsFor(env){return 15+env.engines.lines.length+envActionKeys(env).length}function envActionKeys(env){let keys=[];if(!env.engines.anyUsable)keys.push("doctor.fix.noEngineAction");if(!env.git.found)keys.push("doctor.fix.gitAction");return keys}function WizardPage(props){let{theme}=useTheme(),t=useT(),renderer=useRenderer(),steps=(props.mode??"full")==="primer"?[]:props.shell===null?["skill"]:["completions","skill"],[stepIndex,setStepIndex]=import_react2.useState(0),[yes,setYes]=import_react2.useState(!0),[answers,setAnswers]=import_react2.useState({}),[page,setPage]=import_react2.useState(steps.length===0?"env":"questions"),step=steps[stepIndex];function finish(finalAnswers){renderer?.destroy(),props.onDone({completions:finalAnswers.completions??!1,skill:finalAnswers.skill??!1})}function confirm(choice=yes){if(page==="env"){setPage("keys");return}if(page==="keys"){finish(answers);return}let next={...answers,[step]:choice};if(setAnswers(next),stepIndex+1>=steps.length){setPage("env");return}setStepIndex(stepIndex+1),setYes(!0)}useBindings(()=>({bindings:[{key:"up",cmd:()=>setYes(!0)},{key:"k",cmd:()=>setYes(!0)},{key:"down",cmd:()=>setYes(!1)},{key:"j",cmd:()=>setYes(!1)},{key:"return",cmd:()=>confirm()},...pageCloseBindings(()=>finish(answers))]}));function questionFor(s){return s==="completions"?t("onboarding.completionsQuestion",{shell:props.shell??""}):t("onboarding.skillQuestion")}let explain=step==="completions"?t("onboarding.completionsExplain"):t("onboarding.skillExplain"),ready=envReadyForTasks(props.env);return $jsxs("box",{flexDirection:"column",flexGrow:1,paddingTop:1,paddingLeft:2,paddingRight:2,children:[$jsx("text",{fg:theme.primary,attributes:TextAttributes.BOLD,wrapMode:"none",children:t("onboarding.title")}),$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("onboarding.subtitle")}),$jsxs("box",{flexDirection:"column",paddingTop:1,children:[steps.slice(0,page==="questions"?stepIndex:steps.length).map((answered)=>$jsxs("box",{flexDirection:"row",gap:1,children:[$jsx("text",{fg:theme.success,wrapMode:"none",children:"\u2713"}),$jsx("text",{fg:theme.textMuted,wrapMode:"none",children:questionFor(answered)}),$jsx("text",{fg:theme.text,wrapMode:"none",children:answers[answered]?t("onboarding.optionYes"):t("onboarding.optionNo")})]},answered)),page==="env"?$jsxs("box",{flexDirection:"column",onMouseUp:()=>confirm(),children:[$jsx("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"word",children:t("onboarding.envTitle")}),$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t("onboarding.envExplain")}),$jsxs("box",{flexDirection:"column",paddingTop:1,children:[$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:props.env.git.line}),props.env.engines.lines.map((line)=>$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:line},line))]}),$jsxs("box",{paddingTop:1,flexDirection:"column",children:[$jsx("text",{fg:ready?theme.success:theme.error,wrapMode:"word",children:ready?t("onboarding.envReady"):t("onboarding.envNotReady")}),envActionKeys(props.env).map((key)=>$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:`\u2192 ${t(key)}`},key))]})]}):null,page==="keys"?$jsxs("box",{flexDirection:"column",onMouseUp:()=>confirm(),children:[$jsx("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"word",children:t("onboarding.keysTitle")}),wizardKeyLines(currentPrefixConfiguration().key).map((line)=>$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:t(`onboarding.${line.msg}`,line.params)},line.msg))]}):null,page==="questions"?$jsxs($Fragment,{children:[$jsx("text",{fg:theme.text,attributes:TextAttributes.BOLD,wrapMode:"word",children:questionFor(step)}),$jsx("text",{fg:theme.textMuted,wrapMode:"word",children:explain}),[!0,!1].map((option)=>{let active=yes===option;return $jsxs("box",{flexDirection:"row",gap:1,paddingLeft:1,onMouseUp:()=>confirm(option),children:[$jsx("text",{fg:active?theme.primary:theme.textMuted,wrapMode:"none",children:active?"\u276F":" "}),$jsx("text",{fg:active?theme.primary:theme.textMuted,attributes:active?TextAttributes.BOLD:void 0,wrapMode:"none",children:option?t("onboarding.optionYes"):t("onboarding.optionNo")})]},String(option))})]}):null]}),$jsx("box",{paddingTop:1,children:$jsx("text",{fg:theme.textMuted,attributes:TextAttributes.DIM,wrapMode:"none",children:t(page==="keys"?"onboarding.keysLegend":page==="env"?"onboarding.envLegend":"onboarding.legend")})})]})}async function runOnboardingWizard(shell,env,mode){return await new Promise((resolve)=>{bootPaneHost({inlineRows:inlineRowsFor(env),setup:()=>({root:()=>$jsx(WizardPage,{shell,env,mode,onDone:resolve})})})})}export{runOnboardingWizard,WizardPage};
|