@sma1lboy/rove 0.9.254 → 0.9.256

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/cli/{chunk-5b7pd8j5.js → chunk-09v8w5jq.js} +1 -1
  2. package/dist/cli/{chunk-d48g9zzm.js → chunk-0yyvfpcg.js} +1 -1
  3. package/dist/cli/{chunk-1yzg4hzy.js → chunk-1s8ctwmv.js} +1 -1
  4. package/dist/cli/{chunk-sxt12set.js → chunk-372yekdz.js} +1 -1
  5. package/dist/cli/{chunk-x7ryv67y.js → chunk-3aecx6za.js} +2 -2
  6. package/dist/cli/{chunk-v9fgdd90.js → chunk-3vq0ywzf.js} +1 -1
  7. package/dist/cli/{chunk-mpdfhvgs.js → chunk-3x0hyz6y.js} +2 -2
  8. package/dist/cli/{chunk-k9ahks28.js → chunk-5csm9ffj.js} +1 -1
  9. package/dist/cli/{chunk-fwt0t2h7.js → chunk-73chbrb4.js} +1 -1
  10. package/dist/cli/{chunk-n3wdy4z4.js → chunk-7yb48mar.js} +1 -1
  11. package/dist/cli/{chunk-vzn94t0z.js → chunk-8vh57pq0.js} +1 -1
  12. package/dist/cli/{chunk-68pcd00x.js → chunk-8wf3p9er.js} +1 -1
  13. package/dist/cli/{chunk-qkd4eweb.js → chunk-9qny0zke.js} +1 -1
  14. package/dist/cli/{chunk-sxhjvr0w.js → chunk-adkk89dp.js} +1 -1
  15. package/dist/cli/{chunk-mxkm0snt.js → chunk-bampejxv.js} +1 -1
  16. package/dist/cli/{chunk-pvah9q79.js → chunk-btpp4geb.js} +1 -1
  17. package/dist/cli/chunk-c9xg9dn0.js +3 -0
  18. package/dist/cli/{chunk-6zvh002j.js → chunk-ccqs21dj.js} +1 -1
  19. package/dist/cli/{chunk-e5vnptx5.js → chunk-cqdebtbr.js} +1 -1
  20. package/dist/cli/{chunk-ykdpg0zd.js → chunk-ekre1aps.js} +2 -2
  21. package/dist/cli/{chunk-wn2w5ar9.js → chunk-em6vwq07.js} +1 -1
  22. package/dist/cli/{chunk-vjvkkrzc.js → chunk-f5n0tgky.js} +1 -1
  23. package/dist/cli/{chunk-j9c9tdfg.js → chunk-fhrs8ja3.js} +1 -1
  24. package/dist/cli/{chunk-4myck788.js → chunk-fvksjtsk.js} +1 -1
  25. package/dist/cli/{chunk-kwd59v51.js → chunk-ggzfard0.js} +1 -1
  26. package/dist/cli/{chunk-j19gkhzh.js → chunk-gpcxqdmq.js} +1 -1
  27. package/dist/cli/{chunk-6g5wvt5m.js → chunk-h0h51emh.js} +1 -1
  28. package/dist/cli/{chunk-8bd054vx.js → chunk-hkyn3kme.js} +1 -1
  29. package/dist/cli/{chunk-x40h0f1w.js → chunk-ksk3j88n.js} +1 -1
  30. package/dist/cli/{chunk-6wxnkz3c.js → chunk-m3pn99w9.js} +3 -3
  31. package/dist/cli/{chunk-tbq3t259.js → chunk-mk1xzp2k.js} +1 -1
  32. package/dist/cli/{chunk-ddc4t38t.js → chunk-mvsvmfsf.js} +1 -1
  33. package/dist/cli/{chunk-x951qvk5.js → chunk-pfx1tg6q.js} +1 -1
  34. package/dist/cli/{chunk-rn97jntv.js → chunk-ptaj848y.js} +1 -1
  35. package/dist/cli/{chunk-0g1me47p.js → chunk-pw10r9fk.js} +1 -1
  36. package/dist/cli/{chunk-yebn33mf.js → chunk-pw1twn8f.js} +1 -1
  37. package/dist/cli/{chunk-tnft1xw4.js → chunk-q800jxzk.js} +1 -1
  38. package/dist/cli/{chunk-ef7ckm5b.js → chunk-q9f24pkx.js} +1 -1
  39. package/dist/cli/{chunk-9dendh71.js → chunk-rc2nwsdd.js} +1 -1
  40. package/dist/cli/{chunk-gqpka00s.js → chunk-rzd7xpx2.js} +1 -1
  41. package/dist/cli/{chunk-5fhrns31.js → chunk-se20ckc5.js} +1 -1
  42. package/dist/cli/{chunk-cm923k09.js → chunk-vbv6v4hs.js} +1 -1
  43. package/dist/cli/{chunk-13msr643.js → chunk-vydwyjz2.js} +1 -1
  44. package/dist/cli/{chunk-r7q7s63y.js → chunk-w9grtqm8.js} +2 -2
  45. package/dist/cli/{chunk-xdhs40fx.js → chunk-wy5xx1ez.js} +1 -1
  46. package/dist/cli/{chunk-wsv6tw1b.js → chunk-xd4qfcm6.js} +1 -1
  47. package/dist/cli/{chunk-pyv2ywxr.js → chunk-y4vxge6s.js} +1 -1
  48. package/dist/cli/{chunk-n5xj11ps.js → chunk-ypzac0kj.js} +1 -1
  49. package/dist/cli/{chunk-j84zbw79.js → chunk-z41s748k.js} +2 -2
  50. package/dist/cli/index.js +4 -4
  51. package/dist/cli/pty-host-node.mjs +5 -2
  52. package/dist/cli/rove.js +4 -4
  53. package/package.json +6 -4
  54. package/dist/cli/chunk-m0cbndc2.js +0 -3
@@ -1,7 +1,7 @@
1
1
  // @bun
2
- import{classifyTier,describeTierBlock,isAutoRoutingTier,readAutoRoutingTable,readClassifierConfig,screenErrorLine,taskGroupOf,tierBlock}from"./chunk-d4mvprvq.js";import{ensurePluginEnginesLoaded,registeredEngineIds}from"./chunk-5j6z32fh.js";import{resolveActiveTaskId}from"./chunk-p5902z3e.js";import{listPaneLaunches}from"./chunk-9dendh71.js";import{deriveTitleFromPrompt}from"./chunk-cbc1h345.js";import{DIRTY_WORKTREE_CODE,EMPTY_BRANCH_DIRTY_WORKTREE_CODE,canonicalize}from"./chunk-1mpheecn.js";import{ENGINE_ACTIVITY_KINDS,TASK_ACTIVITY_STATES}from"./chunk-17e2tgyg.js";import{detectEngineStatus}from"./chunk-4mxn5jg6.js";import{ISSUE_STATUSES}from"./chunk-4hestsjp.js";import{resolveEditorLaunch}from"./chunk-ghyz3fd7.js";import{ROW_TOKEN_MAX_TEXT,ROW_TOKEN_TONES}from"./chunk-atn06ncf.js";import{DEFAULT_FEEDBACK_CATEGORY_SLUG,submitFeedback}from"./chunk-kwd59v51.js";import{ulid}from"./chunk-qkehg2c1.js";import{ENGINE_NOT_OBSERVED_REASON,ENGINE_START_POLL_MS,ENGINE_START_PROBE_MS,allocateTab,closeTabsSnapshot,deliverHostedPrompt,deliverToKey,ensurePtyHost,findEngineKey,joinTaskTabs,killTaskSessions,listSessions,listSessionsOrNull,markCliTabSession,mintCliTab,openAdmittedEngine,openPtyHost,outcomeFields,publishCliTabSnapshot,readTabsSnapshot,renameTabsSnapshot,restoredTabLaunch,taskEngineArgv,taskKeys,unregisteredTabIds,wireSplit}from"./chunk-j19gkhzh.js";import{collapseSplit,findEditorTab,initialSplit,leaves,removeLeaf,renameLeaf,setTabSplit,shellIdentityInput,splitActive,splitLeafPtyKey,tabPtyKeyFor,terminalTabsKey}from"./chunk-8bd054vx.js";import{awaitEngineProcess,buildEngineSessionLaunch,enginePresence,findHostedEngineKey,hostedSessionFailureLine,isHostedTaskKey,listHostedSessions,openHostedSessionHost,readPersistedTerminalDefaultColors,sessionArgvNamesEngine,trustEngineWorktree}from"./chunk-ddc4t38t.js";import{GENERIC_PROTOCOL,describePreset,engineLaunchArgv,listEnginePresets,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol,withPinnedSessionId}from"./chunk-rf4z7b6g.js";import{resolveLoginShell}from"./chunk-vjvkkrzc.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-j7831wcj.js";import{engineEntry,getCapabilities,supportsStructuredHistory,vendorsWithEffortLevels,vendorsWithTurnReader}from"./chunk-tewt7vre.js";import{CONTRIB_ENGINE_IDS,installedEngineIds}from"./chunk-9g0vz9dk.js";import{terminalRows}from"./chunk-etfh8f4z.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{ApiError,VERB_GROUP_IDS,cliCaller,helpStep,promptMayHaveLanded,splitDaemonCode}from"./chunk-x7r75e3p.js";import{parseBoolLiteral}from"./chunk-zsfyj36g.js";import{expandTilde}from"./chunk-ag3txfg2.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{DEFAULT_TASK_VENDOR}from"./chunk-xy8be792.js";import{ALL_VENDORS,DEFAULT_VENDOR,coerceVendorId}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-m0cbndc2.js";import{isRemoteRepoKey}from"./chunk-xkr7fwvb.js";import{loadStateFile,updateStateFile}from"./chunk-axxf5dkq.js";import{homeDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{DELIVERY_OUTCOMES,DELIVERY_SUMMARY_MAX_CHARS,MAX_DELIVERIES_PER_TASK,ROUTINE_RESPONSE_MAX_CHARS,SHOULD_RUN_SKIP_EXIT_CODE,SKIP_REASONS,deliveryFooter}from"./chunk-fnsch8kr.js";import{ROVE_API_VERSION}from"./chunk-278sc87n.js";import{pathWithin,samePath}from"./chunk-pn5ygqn6.js";import{defaultDaemonSocketPath}from"./chunk-z7zm8h1q.js";import{__require}from"./chunk-91sykzqq.js";import{readFileSync as readFileSync2}from"fs";import{resolve as resolve2}from"path";import{readFileSync}from"fs";import{isAbsolute,resolve}from"path";var FANOUT_CAP=10;function parsePositiveInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>0?n:void 0}function parseNonNegativeInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>=0?n:void 0}var FANOUT_STEP=helpStep("add"),F={repo:(required=!0)=>({name:"repo",type:"string",required,placeholder:"PATH",description:"Repo root (git toplevel). Relative paths resolve against $PWD."}),taskId:(required=!0)=>({name:"task-id",type:"string",required,placeholder:"ID",description:"Target task id (from `list` / `add`)."}),vendor:()=>({name:"vendor",type:"enum",values:ALL_VENDORS,placeholder:"V",description:"Engine vendor for the task."}),command:()=>({name:"command",type:"string",placeholder:"CMD",description:"Engine launch command, verbatim \u2014 an engine id from `engine-list` (e.g. claude) or a full command line (e.g. 'codex --search'). Unvalidated: probe an unfamiliar engine's flags with `<cmd> --help` first. Omitted = the repo's default engine."}),title:()=>({name:"title",type:"string",placeholder:"T",description:"Human task title."}),prompt:(required,desc)=>({name:"prompt",type:"string",required,placeholder:"TEXT",description:required?`${desc} Required unless --prompt-file is given.`:desc}),promptFile:()=>({name:"prompt-file",type:"string",placeholder:"PATH",description:"Read the prompt from this file instead of --prompt (`-` = stdin). Use it whenever the text has backticks, $vars, or quotes you don't want the shell to touch. Exactly one of --prompt / --prompt-file."})},REPEAT_SEP="\x00";function setFlag(flags,key,value,repeatable){let prev=flags.get(key);flags.set(key,repeatable.has(key)&&prev!==void 0?`${prev}${REPEAT_SEP}${value}`:value)}function parseFlags(argv,booleanFlags=new Set,repeatableFlags=new Set){let flags=new Map,pretty=!1,help=!1;for(let i=0;i<argv.length;i++){let arg=argv[i];if(!arg.startsWith("--")&&arg!=="-h")throw new ApiError(`unexpected positional arg: ${arg}`,"BAD_FLAG");if(arg==="-h"){help=!0;continue}let eq=arg.indexOf("=");if(eq!==-1){let key2=arg.slice(2,eq),value=arg.slice(eq+1);if(key2==="envelope")continue;if(key2==="pretty")pretty=value!=="false"&&value!=="0";else if(key2==="help")help=value!=="false"&&value!=="0";else setFlag(flags,key2,value,repeatableFlags);continue}let key=arg.slice(2);if(key==="envelope")continue;if(key==="pretty"){pretty=!0;continue}if(key==="help"){help=!0;continue}if(booleanFlags.has(key)){let next2=argv[i+1];if(next2!==void 0&&parseBoolLiteral(next2)!==void 0)flags.set(key,next2),i+=1;else flags.set(key,"true");continue}let next=argv[i+1];if(next===void 0||next.startsWith("--"))throw new ApiError(`flag --${key} requires a value`,"BAD_FLAG");setFlag(flags,key,next,repeatableFlags),i+=1}return{flags,pretty,help}}function validateAgainstSpec(verb,flags){let known=new Set(verb.flags.map((f)=>f.name));for(let key of flags.keys())if(!known.has(key))throw new ApiError(`unknown flag --${key} for "${verb.name}"`,"BAD_FLAG",helpStep(verb.name));for(let f of verb.flags){let satisfied=flags.get(f.name)||f.name==="prompt"&&flags.get("prompt-file");if(f.required&&!satisfied)throw new ApiError(`--${f.name} is required for "${verb.name}"`,"MISSING_FLAG",helpStep(verb.name));if(f.type==="enum"&&f.values){let raw=flags.get(f.name);if(raw!==void 0&&!f.values.includes(raw)){if(f.name==="vendor"&&registeredEngineIds().includes(raw))continue;throw new ApiError(`--${f.name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(verb.name))}}if(f.type==="int"){let raw=flags.get(f.name);if(raw!==void 0&&parsePositiveInt(raw)===void 0)throw new ApiError(`--${f.name} must be a positive integer`,"BAD_FLAG")}if(f.type==="uint"){let raw=flags.get(f.name);if(raw!==void 0&&parseNonNegativeInt(raw)===void 0)throw new ApiError(`--${f.name} must be a non-negative integer`,"BAD_FLAG")}}}class VerbArgs{verb;flags;caller;constructor(verb,flags,caller=cliCaller()){this.verb=verb;this.flags=flags;this.caller=caller}resolvePath(name,raw){let expanded=expandTilde(raw);if(isAbsolute(expanded))return expanded;if(this.caller.cwd===void 0)throw new ApiError(`--${name} must be an absolute path: this caller has no working directory`,"BAD_FLAG");return resolve(this.caller.cwd,expanded)}spec(name){let f=this.verb.flags.find((s)=>s.name===name);if(!f)throw Error(`internal: --${name} is not declared on verb "${this.verb.name}"`);return f}str(name){this.spec(name);let v=this.flags.get(name);return v&&v.length>0?v:void 0}present(name){return this.spec(name),this.flags.get(name)!==void 0}list(name){if(!this.spec(name).repeatable)throw Error(`internal: --${name} is not repeatable on verb "${this.verb.name}"`);let raw=this.flags.get(name);return raw===void 0?[]:raw.split(REPEAT_SEP).filter((v)=>v.length>0)}promptText(){if(this.promptMemo!==void 0)return this.promptMemo.value;let value=this.readPromptText();return this.promptMemo={value},value}promptMemo;readPromptText(){let inline=this.verb.flags.some((f)=>f.name==="prompt")?this.str("prompt"):void 0,file=this.str("prompt-file");if(inline!==void 0&&file!==void 0)throw new ApiError("pass --prompt or --prompt-file, not both","BAD_FLAG",helpStep(this.verb.name));if(file===void 0)return inline;if(file==="-"&&!this.caller.cli)throw new ApiError("--prompt-file - reads stdin, which this caller cannot send; pass --prompt","BAD_FLAG");let text=readFileSync(file==="-"?0:this.resolvePath("prompt-file",file),"utf8");if(text.trim().length===0)throw new ApiError(`--prompt-file ${file} is empty`,"BAD_FLAG");return text}require(name){let v=this.str(name);if(v===void 0)throw new ApiError(`--${name} is required`,"MISSING_FLAG");return v}enumOf(name){let f=this.spec(name),v=this.str(name);if(v===void 0)return;if(f.values&&!f.values.includes(v))throw new ApiError(`--${name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(this.verb.name));return v}requireEnum(name){return this.require(name),this.enumOf(name)}vendor(){let value=this.str("vendor");if(value===void 0)return;let builtins=this.spec("vendor").values??ALL_VENDORS;if(builtins.includes(value))return value;if(registeredEngineIds().includes(value))return value;throw new ApiError(`--vendor must be a built-in (${builtins.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",helpStep(this.verb.name))}bool(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let value=parseBoolLiteral(raw);if(value===void 0)throw new ApiError(`--${name} must be a boolean (true/false)`,"BAD_FLAG");return value}int(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parsePositiveInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a positive integer`,"BAD_FLAG");return n}nonNegativeInt(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parseNonNegativeInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a non-negative integer`,"BAD_FLAG");return n}path(name){let v=this.str(name);return v===void 0?void 0:this.resolvePath(name,v)}requirePath(name){return this.resolvePath(name,this.require(name))}requireRepo(name){let raw=this.require(name);return isRemoteRepoKey(raw)?raw:this.resolvePath(name,raw)}}function parseAgentsSpec(spec){let out=[];for(let part of spec.split(",")){let trimmed=part.trim();if(!trimmed)continue;let colon=trimmed.indexOf(":");if(colon===-1)throw new ApiError(`--agents entry "${trimmed}" must be engine:count`,"BAD_FLAG",FANOUT_STEP);let vendor=trimmed.slice(0,colon);if(!ALL_VENDORS.includes(vendor)&&!registeredEngineIds().includes(vendor))throw new ApiError(`--agents engine "${vendor}" must be a built-in (${ALL_VENDORS.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",FANOUT_STEP);let count=parsePositiveInt(trimmed.slice(colon+1));if(count===void 0)throw new ApiError(`--agents count for "${vendor}" must be a positive integer`,"BAD_FLAG",FANOUT_STEP);if(out.length+count>FANOUT_CAP)throw new ApiError(`--agents requests ${out.length+count} agents, exceeds the cap of ${FANOUT_CAP}`,"BAD_FLAG",FANOUT_STEP);for(let i=0;i<count;i++)out.push(vendor)}if(out.length===0)throw new ApiError('--agents specified no agents (e.g. "claude:2,codex:1")',"BAD_FLAG",FANOUT_STEP);return out}function buildCountPlan(count,vendor){if(count>FANOUT_CAP)throw new ApiError(`--count ${count} exceeds the parallel cap of ${FANOUT_CAP} \u2014 spawn in batches`,"BAD_FLAG",FANOUT_STEP);return Array(count).fill(vendor)}function daemonOf(ctx){if(!ctx.client)throw new ApiError("daemon required","BAD_DAEMON");return ctx.client}async function repoFilter(runtime,repoFlag,repos){let target=await runtime.resolveRepoRoot(repoFlag);if(!await runtime.isUsableRepo(target))throw new ApiError(`--repo ${repoFlag} is not a readable git repository (resolved to ${target}) \u2014 the project may have moved or been deleted; \`rove api list\` still shows its tasks`,"REPO_UNRESOLVABLE");let resolved=new Map,unresolvable=new Set;for(let repo of new Set(repos)){let root=await runtime.resolveRepoRoot(repo);if(resolved.set(repo,root),!samePath(root,target)&&!await runtime.isUsableRepo(root))unresolvable.add(repo)}return{target,matches:(repo)=>samePath(resolved.get(repo),target),unresolvableRepos:[...unresolvable].sort()}}async function simpleRpc(ctx,name,payload){return daemonOf(ctx).request(name,payload)}async function handlePtyList(){let[{RoveDaemonClient},{defaultPtyHostSocketPath}]=await Promise.all([import("./chunk-3r8v2gza.js"),import("./chunk-ax7wzcam.js")]),client=new RoveDaemonClient(defaultPtyHostSocketPath());try{return await client.connect(),await client.request("pty.list",{})}catch{return{sessions:null}}finally{client.close()}}function readJsonBody(source){let raw=readFileSync2(source==="-"?0:resolve2(process.cwd(),expandTilde(source)),"utf8"),parsed;try{parsed=JSON.parse(raw)}catch{throw new ApiError(`--json ${source} is not valid JSON`,"BAD_FLAG")}if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))throw new ApiError("--json must be an object: {delivery, outcome, summary, evidence?, next?}","BAD_FLAG");let body=parsed,evidence=body.evidence;if(evidence!==void 0&&(!Array.isArray(evidence)||evidence.some((e)=>typeof e!=="string")))throw new ApiError("evidence must be an array of strings","BAD_FLAG");for(let key of["delivery","outcome","summary","next"])if(body[key]!==void 0&&typeof body[key]!=="string")throw new ApiError(`${key} must be a string`,"BAD_FLAG");return body}var FIELD_FLAGS=["delivery","outcome","summary","next"];function readBody(ctx){let json=ctx.args.str("json"),evidence=ctx.args.list("evidence");if(json===void 0)return{delivery:ctx.args.str("delivery"),outcome:ctx.args.str("outcome"),summary:ctx.args.str("summary"),evidence,next:ctx.args.str("next")};if(FIELD_FLAGS.some((f)=>ctx.args.str(f)!==void 0)||evidence.length>0)throw new ApiError("pass --json OR the individual flags, not both","BAD_FLAG");return readJsonBody(json)}async function report(ctx){let body=readBody(ctx);if(!body.delivery)throw new ApiError("--delivery is required","MISSING_FLAG");if(!body.outcome||!DELIVERY_OUTCOMES.includes(body.outcome))throw new ApiError(`--outcome must be one of ${DELIVERY_OUTCOMES.join("|")}`,"MISSING_FLAG");if(!body.summary?.trim())throw new ApiError("--summary is required","MISSING_FLAG");if(body.summary.length>DELIVERY_SUMMARY_MAX_CHARS)throw new ApiError(`summary is ${body.summary.length} characters; the cap is ${DELIVERY_SUMMARY_MAX_CHARS}`,"RESPONSE_TOO_LARGE",{hint:"Summarize: lead with the conclusion, put the detail in --evidence (commits, PR urls, paths)."});let res=await simpleRpc(ctx,"delivery.report",{deliveryId:body.delivery,outcome:body.outcome,summary:body.summary,...body.evidence?.length?{evidence:body.evidence}:{},...body.next?{next:body.next}:{}});if(res.delivery===null)throw new ApiError(`no delivery ${body.delivery}`,"DELIVERY_NOT_FOUND",{hint:"Copy the id from the [ROVE] report line of the prompt you were given; `deliveries --task-id <id>` lists them."});return res}var REPORT_VERB={name:"report",group:"drive",summary:`Close the report a delivered prompt asked for. Run it as the LAST step of the turn, before you stop: a turn that ends without it is flagged \`report_missing\` in the owner's Inbox. --delivery is the id in the prompt's [ROVE] / [ROVE ROUTINE] line. Re-reporting replaces the earlier report; a late report on a flagged delivery clears the flag. --summary \u2264 ${DELIVERY_SUMMARY_MAX_CHARS} chars; put commits, PR urls, test results and paths in repeatable --evidence, and what the next tick should do in --next. Use --json - (stdin) for text with quotes or backticks.`,flags:[{name:"delivery",type:"string",placeholder:"ID",description:"Delivery id, e.g. d-41."},{name:"outcome",type:"enum",values:DELIVERY_OUTCOMES,placeholder:"O",description:"done = finished what was asked; partial = some of it; blocked = needs the owner or an external fix; failed = tried and could not."},{name:"summary",type:"string",placeholder:"TEXT",description:"What was done (or why not)."},{name:"evidence",type:"string",placeholder:"X",repeatable:!0,description:"A commit, PR url, test command + result, or file path. Repeat the flag for several."},{name:"next",type:"string",placeholder:"TEXT",description:"What the next tick should do."},{name:"json",type:"string",placeholder:"PATH",description:"Read {delivery, outcome, summary, evidence?, next?} as JSON from this file (`-` = stdin) instead of the flags."}],handler:report},DELIVERIES_VERB={name:"deliveries",group:"read",summary:`A task's armed deliveries, newest first (newest ${MAX_DELIVERIES_PER_TASK} are kept): status awaiting|reported|missing|skipped, the report (outcome, summary, evidence, next) and, for missing, why (cause turn-complete|turn-failed|engine-dead|stalled|interrupted|session-end|rate-limited + note; rate-limited raises no Inbox row and does not block) plus dismissedAt once the owner dismissed it (a missing row blocks \`send --if-runnable\` and routine ticks until a late report or that dismissal). A skipped row is a \`send --if-runnable\` tick should-run refused: \`skipped\` is the verdict {reason, detail?, until?, gateIds?}, \`repeats\` how many identical refusals (same tab, same reason) the one row stands for. The per-tick log a loop prompt should read instead of a growing MEMO file.`,flags:[F.taskId(!0),{name:"limit",type:"int",placeholder:"N",description:"At most N entries (default: all retained)."}],handler:async(ctx)=>simpleRpc(ctx,"delivery.list",{taskId:ctx.args.require("task-id"),...ctx.args.int("limit")!==void 0?{limit:ctx.args.int("limit")}:{}})};async function listAllEnginePresets(){let presets=await enginePresetsInList(),byProtocol=new Map,modelsOf=(protocol)=>{let pending=byProtocol.get(protocol);if(!pending){let list=engineEntry(protocol).listModels;pending=list?list().catch(()=>null):Promise.resolve(null),byProtocol.set(protocol,pending)}return pending};return Promise.all(presets.map(async(preset)=>({...preset,models:await modelsOf(preset.protocol)})))}async function enginePresetsInList(){ensurePluginEnginesLoaded();let presets=[...listEnginePresets()],seen=new Set(presets.map((p)=>p.id));for(let id of await installedEngineIds())if(!seen.has(id))presets.push(describePreset(id));return presets}async function engineListIds(){return(await enginePresetsInList()).map((p)=>p.id)}var ENGINE_LIST_VERB={name:"engine-list",group:"discover",summary:`List every engine Rove can launch \u2014 built-ins, registered presets, the shipped contrib engines whose CLI is on PATH (${CONTRIB_ENGINE_IDS.join(", ")}), and engines contributed by enabled plugins \u2014 each with its RAW launch command, exactly as it runs. Copy one into \`add --command\` / \`send --tab new --command\` verbatim, or edit its flags first. \`protocol\` is the adapter Rove speaks to it (history, trust, delivery); \`generic\` = none, which still runs fine but loses transcript reads. \`models\` is what the engine can name for \`--model\` (suggestions, not a closed set); null = Rove cannot list them for this engine. Returns { engines }.`,flags:[],offline:!0,handler:async()=>({engines:await listAllEnginePresets()})};function taskEngine(task){let command=task.command?.trim();if(command){let resolved=resolveCommandProtocol(command);if(resolved!==GENERIC_PROTOCOL)return resolved}return sessionProtocol(coerceVendorId(task.vendor))}function assertEngineAcceptsEffort(engine,level,recover){let levels=engineEntry(engine).effortLevels??[];if(levels.length===0)throw new ApiError(`engine ${engine} declares no reasoning effort levels`,"BAD_EFFORT",{engine,hint:`Only engines with declared reasoning levels accept one (${vendorsWithEffortLevels().join(", ")} today). Check the task's engine with \`get-task\`.`,nextCommandArgs:[...recover]});if(!levels.includes(level))throw new ApiError(`engine ${engine} does not accept effort level ${JSON.stringify(level)} \u2014 it declares ${levels.join(", ")}`,"BAD_EFFORT",{engine,levels,hint:`Pass one of: ${levels.join(", ")}.`,nextCommandArgs:[...recover]})}function assertEngineAcceptsModel(engine,model,recover){if(engineEntry(engine).modelArgv)return;throw new ApiError(`engine ${engine} declares no model flag \u2014 it does not accept a model`,"BAD_MODEL",{engine,hint:"Only engines that declare a model flag accept one (claude, codex, kimi, pi, omp today); `engine-list` shows `models` per engine.",nextCommandArgs:[...recover]})}function vendorToRecord(task,engine){let recorded=coerceVendorId(task.vendor);return sessionProtocol(recorded)===engine?recorded:engine}async function readRunnability(daemon,taskId){try{return await daemon.request("task.runnability",{taskId})}catch{return}}async function shouldRun(ctx){let tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let verdict=await daemonOf(ctx).request("task.runnability",{taskId:ctx.args.require("task-id"),...tab?{tabId:tab}:{}});if(!verdict.run)throw new ApiError(`skip: ${verdict.reason}`,"SKIPPED",{...verdict});return verdict}var SHOULD_RUN_VERB={name:"should-run",group:"read",summary:`Would a tick sent to this task be used? Prints the verdict {run:true} or {run:false, reason, detail?, until?, gateIds?} and exits 0 on run, ${SHOULD_RUN_SKIP_EXIT_CODE} on skip (the verdict is still on stdout). reason, first match wins: ${SKIP_REASONS.join(" | ")}. \`awaiting-owner\` = an open gate (answer or withdraw it), \`report-missing\` = the last delivery ended unreported (late \`report\`, or dismiss its Inbox row), \`quota\` clears itself at \`until\`, \`engine-error\` / \`engine-dead\` clear on the engine's next turn or a restart, \`awaiting-permission\` = the engine waits on an approval, \`busy\` = a turn is running or the last delivery still awaits its report. A task whose only gate is answered-but-undelivered is runnable: the next tick carries the answer. Without --tab the whole task is judged and a dead engine is not a skip (\`send\` revives it); --tab tab-N judges that tab and a dead engine is \`engine-dead\`; --tab new judges only task-level state. To check and send in one step use \`send --if-runnable\`.`,flags:[F.taskId(!0),{name:"tab",type:"string",required:!1,placeholder:"TAB",description:'Judge exactly this tab (e.g. tab-2), or "new" for a tab about to be created. Omitted = the whole task.'}],handler:shouldRun};async function attentionDismiss(ctx){let delivery=ctx.args.require("delivery");if((await daemonOf(ctx).request("attention.dismiss",{deliveryId:delivery})).found===!1)throw new ApiError(`no delivery ${delivery}`,"DELIVERY_NOT_FOUND",{hint:"`deliveries --task-id <id>` lists a task's delivery ids; the Inbox row names it as d-N."});return{ok:!0,delivery,cleared:!0}}var ATTENTION_DISMISS_VERB={name:"attention-dismiss",group:"drive",summary:'Dismiss a `report_missing` Inbox row by its delivery id (d-N): the owner\'s deliberate "seen, carry on" for a tick that ended without a report. It unblocks `should-run` for the task. Opening or visiting the row never does this, and a late `report` clears it on its own. Idempotent; an unknown id is DELIVERY_NOT_FOUND.',flags:[{name:"delivery",type:"string",required:!0,placeholder:"ID",description:"Delivery id, e.g. d-41."}],handler:attentionDismiss};var API_SCHEMA_VERSION=ROVE_API_VERSION,GLOBAL_FLAGS=[{name:"pretty",type:"bool",description:"Pretty-print stdout JSON."},{name:"help",type:"bool",description:"Show usage for the verb and exit."},{name:"envelope",type:"bool",description:"Print every outcome as one { apiVersion, ok, result | error } on stdout \u2014 what the daemon's and the bridge's api.invoke return."},{name:"machine",type:"string",placeholder:"ALIAS",description:"Run the verb in that registered machine's daemon (`rove machine list`); paths are paths there. A --task-id on another machine goes there without it."}];function displayValues(f){if(!f.values)return;if(f.name!=="vendor")return f.values;let registered=registeredEngineIds().filter((id)=>!f.values.includes(id));return registered.length>0?[...f.values,...registered]:f.values}function flagJson(f){let values=displayValues(f);return{name:f.name,type:f.type,required:f.required??!1,...values?{values}:{},...f.default!==void 0?{default:f.default}:{},...f.placeholder?{placeholder:f.placeholder}:{},...f.repeatable?{repeatable:!0}:{},description:f.description}}function verbSchema(v){return{name:v.name,group:v.group,summary:v.summary,offline:v.offline??!1,cliOnly:v.cliOnly??!1,flags:v.flags.map(flagJson)}}function schemaIndex(){let cliName=ROVE_PRODUCT_NAME;return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,hint:`Compact index. Drill into ONE verb: \`${cliName} api schema --verb <name>\` (or \`${cliName} api <verb> --help\`). One group: \`--group <g>\`. Whole spec: \`--all\`.`,groups:VERB_GROUPS,verbs:VERBS.map((v)=>({name:v.name,group:v.group,summary:v.summary})),globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES}}function groupSchema(group){let verbs=VERBS.filter((v)=>v.group===group);if(verbs.length===0)throw new ApiError(`unknown group: ${group}. Groups: ${Object.keys(VERB_GROUPS).join(", ")}`,"BAD_VERB",{hint:"that group does not exist \u2014 the compact index lists every group and the verbs in it",nextCommandArgs:["api","schema"]});return{group,verbs:verbs.map((v)=>({name:v.name,summary:v.summary}))}}function fullSchema(){return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,output:{success:"one JSON object on stdout, newline-terminated, exit 0",error:'{"error":{"message","code"}} on stderr, exit != 0',pretty:"--pretty indents stdout JSON",envelope:'--envelope prints {"apiVersion","ok","result"} or {"apiVersion","ok","error"} on stdout instead, exit codes unchanged'},transports:{cli:"rove api <verb> --flag value",daemon:'socket request "api.invoke" {verb, params, machine?} \u2192 the envelope; params are keyed by flag name',bridge:'WebSocket op "api.invoke" {verb, params, machine?} \u2192 result, or the full error object'},globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES,groups:VERB_GROUPS,verbs:VERBS.map(verbSchema)}}function flagSignature(verb){return verb.flags.map((f)=>{let meta=f.type==="enum"&&f.values?displayValues(f).join("|"):f.placeholder??(f.type==="bool"?"":"X"),core=meta?`--${f.name} ${meta}`:`--${f.name}`;return f.required?core:`[${core}]`}).join(" ")}function verbHelp(verb){let lines=[`${ROVE_PRODUCT_NAME} api ${verb.name} ${flagSignature(verb)}`.trimEnd(),"",verb.summary,""],alias=Object.entries(VERB_ALIASES).find(([,canon])=>canon===verb.name)?.[0];if(alias)lines.push(`Alias: ${alias}`,"");if(verb.flags.length>0){lines.push("Flags:");for(let f of verb.flags){let req=f.required?" (required)":f.repeatable?" (repeatable)":"",def=f.default!==void 0?` [default: ${f.default}]`:"",vals=f.type==="enum"&&f.values?` {${displayValues(f).join("|")}}`:"";lines.push(` --${f.name}${vals}${req}${def} ${f.description}`)}lines.push("")}return lines.push("Global: [--pretty] [--help] [--envelope] [--machine ALIAS]"),lines.join(`
2
+ import{classifyTier,describeTierBlock,isAutoRoutingTier,readAutoRoutingTable,readClassifierConfig,screenErrorLine,taskGroupOf,tierBlock}from"./chunk-d4mvprvq.js";import{ensurePluginEnginesLoaded,registeredEngineIds}from"./chunk-5j6z32fh.js";import{resolveActiveTaskId}from"./chunk-p5902z3e.js";import{listPaneLaunches}from"./chunk-rc2nwsdd.js";import{deriveTitleFromPrompt}from"./chunk-cbc1h345.js";import{DIRTY_WORKTREE_CODE,EMPTY_BRANCH_DIRTY_WORKTREE_CODE,canonicalize}from"./chunk-1mpheecn.js";import{ENGINE_ACTIVITY_KINDS,TASK_ACTIVITY_STATES}from"./chunk-17e2tgyg.js";import{detectEngineStatus}from"./chunk-4mxn5jg6.js";import{ISSUE_STATUSES}from"./chunk-4hestsjp.js";import{resolveEditorLaunch}from"./chunk-ghyz3fd7.js";import{ROW_TOKEN_MAX_TEXT,ROW_TOKEN_TONES}from"./chunk-atn06ncf.js";import{DEFAULT_FEEDBACK_CATEGORY_SLUG,submitFeedback}from"./chunk-ggzfard0.js";import{ulid}from"./chunk-qkehg2c1.js";import{ENGINE_NOT_OBSERVED_REASON,ENGINE_START_POLL_MS,ENGINE_START_PROBE_MS,allocateTab,closeTabsSnapshot,deliverHostedPrompt,deliverToKey,ensurePtyHost,findEngineKey,joinTaskTabs,killTaskSessions,listSessions,listSessionsOrNull,markCliTabSession,mintCliTab,openAdmittedEngine,openPtyHost,outcomeFields,publishCliTabSnapshot,readTabsSnapshot,renameTabsSnapshot,restoredTabLaunch,taskEngineArgv,taskKeys,unregisteredTabIds,wireSplit}from"./chunk-gpcxqdmq.js";import{collapseSplit,findEditorTab,initialSplit,leaves,removeLeaf,renameLeaf,setTabSplit,shellIdentityInput,splitActive,splitLeafPtyKey,tabPtyKeyFor,terminalTabsKey}from"./chunk-hkyn3kme.js";import{awaitEngineProcess,buildEngineSessionLaunch,enginePresence,findHostedEngineKey,hostedSessionFailureLine,isHostedTaskKey,listHostedSessions,openHostedSessionHost,readPersistedTerminalDefaultColors,sessionArgvNamesEngine,trustEngineWorktree}from"./chunk-mvsvmfsf.js";import{GENERIC_PROTOCOL,describePreset,engineLaunchArgv,listEnginePresets,protocolEntry,resolveCommandProtocol,roveApiInvocation,sessionProtocol,withPinnedSessionId}from"./chunk-rf4z7b6g.js";import{resolveLoginShell}from"./chunk-f5n0tgky.js";import{foregroundEngineIn,parsePsSnapshot,psSnapshot}from"./chunk-j7831wcj.js";import{engineEntry,getCapabilities,supportsStructuredHistory,vendorsWithEffortLevels,vendorsWithTurnReader}from"./chunk-tewt7vre.js";import{CONTRIB_ENGINE_IDS,installedEngineIds}from"./chunk-9g0vz9dk.js";import{terminalRows}from"./chunk-etfh8f4z.js";import{readOnlyGitProcessEnv}from"./chunk-6xppe9bj.js";import{ApiError,VERB_GROUP_IDS,cliCaller,helpStep,promptMayHaveLanded,splitDaemonCode}from"./chunk-x7r75e3p.js";import{parseBoolLiteral}from"./chunk-zsfyj36g.js";import{expandTilde}from"./chunk-ag3txfg2.js";import{errorMessage}from"./chunk-2brn8e1y.js";import{DEFAULT_TASK_VENDOR}from"./chunk-xy8be792.js";import{ALL_VENDORS,DEFAULT_VENDOR,coerceVendorId}from"./chunk-h2m7em1e.js";import{CURRENT_VERSION}from"./chunk-c9xg9dn0.js";import{isRemoteRepoKey}from"./chunk-xkr7fwvb.js";import{loadStateFile,updateStateFile}from"./chunk-axxf5dkq.js";import{homeDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import{DELIVERY_OUTCOMES,DELIVERY_SUMMARY_MAX_CHARS,MAX_DELIVERIES_PER_TASK,ROUTINE_RESPONSE_MAX_CHARS,SHOULD_RUN_SKIP_EXIT_CODE,SKIP_REASONS,deliveryFooter}from"./chunk-fnsch8kr.js";import{ROVE_API_VERSION}from"./chunk-278sc87n.js";import{pathWithin,samePath}from"./chunk-pn5ygqn6.js";import{defaultDaemonSocketPath}from"./chunk-z7zm8h1q.js";import{__require}from"./chunk-91sykzqq.js";import{readFileSync as readFileSync2}from"fs";import{resolve as resolve2}from"path";import{readFileSync}from"fs";import{isAbsolute,resolve}from"path";var FANOUT_CAP=10;function parsePositiveInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>0?n:void 0}function parseNonNegativeInt(raw){if(!/^\d+$/.test(raw.trim()))return;let n=Number.parseInt(raw,10);return Number.isSafeInteger(n)&&n>=0?n:void 0}var FANOUT_STEP=helpStep("add"),F={repo:(required=!0)=>({name:"repo",type:"string",required,placeholder:"PATH",description:"Repo root (git toplevel). Relative paths resolve against $PWD."}),taskId:(required=!0)=>({name:"task-id",type:"string",required,placeholder:"ID",description:"Target task id (from `list` / `add`)."}),vendor:()=>({name:"vendor",type:"enum",values:ALL_VENDORS,placeholder:"V",description:"Engine vendor for the task."}),command:()=>({name:"command",type:"string",placeholder:"CMD",description:"Engine launch command, verbatim \u2014 an engine id from `engine-list` (e.g. claude) or a full command line (e.g. 'codex --search'). Unvalidated: probe an unfamiliar engine's flags with `<cmd> --help` first. Omitted = the repo's default engine."}),title:()=>({name:"title",type:"string",placeholder:"T",description:"Human task title."}),prompt:(required,desc)=>({name:"prompt",type:"string",required,placeholder:"TEXT",description:required?`${desc} Required unless --prompt-file is given.`:desc}),promptFile:()=>({name:"prompt-file",type:"string",placeholder:"PATH",description:"Read the prompt from this file instead of --prompt (`-` = stdin). Use it whenever the text has backticks, $vars, or quotes you don't want the shell to touch. Exactly one of --prompt / --prompt-file."})},REPEAT_SEP="\x00";function setFlag(flags,key,value,repeatable){let prev=flags.get(key);flags.set(key,repeatable.has(key)&&prev!==void 0?`${prev}${REPEAT_SEP}${value}`:value)}function parseFlags(argv,booleanFlags=new Set,repeatableFlags=new Set){let flags=new Map,pretty=!1,help=!1;for(let i=0;i<argv.length;i++){let arg=argv[i];if(!arg.startsWith("--")&&arg!=="-h")throw new ApiError(`unexpected positional arg: ${arg}`,"BAD_FLAG");if(arg==="-h"){help=!0;continue}let eq=arg.indexOf("=");if(eq!==-1){let key2=arg.slice(2,eq),value=arg.slice(eq+1);if(key2==="envelope")continue;if(key2==="pretty")pretty=value!=="false"&&value!=="0";else if(key2==="help")help=value!=="false"&&value!=="0";else setFlag(flags,key2,value,repeatableFlags);continue}let key=arg.slice(2);if(key==="envelope")continue;if(key==="pretty"){pretty=!0;continue}if(key==="help"){help=!0;continue}if(booleanFlags.has(key)){let next2=argv[i+1];if(next2!==void 0&&parseBoolLiteral(next2)!==void 0)flags.set(key,next2),i+=1;else flags.set(key,"true");continue}let next=argv[i+1];if(next===void 0||next.startsWith("--"))throw new ApiError(`flag --${key} requires a value`,"BAD_FLAG");setFlag(flags,key,next,repeatableFlags),i+=1}return{flags,pretty,help}}function validateAgainstSpec(verb,flags){let known=new Set(verb.flags.map((f)=>f.name));for(let key of flags.keys())if(!known.has(key))throw new ApiError(`unknown flag --${key} for "${verb.name}"`,"BAD_FLAG",helpStep(verb.name));for(let f of verb.flags){let satisfied=flags.get(f.name)||f.name==="prompt"&&flags.get("prompt-file");if(f.required&&!satisfied)throw new ApiError(`--${f.name} is required for "${verb.name}"`,"MISSING_FLAG",helpStep(verb.name));if(f.type==="enum"&&f.values){let raw=flags.get(f.name);if(raw!==void 0&&!f.values.includes(raw)){if(f.name==="vendor"&&registeredEngineIds().includes(raw))continue;throw new ApiError(`--${f.name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(verb.name))}}if(f.type==="int"){let raw=flags.get(f.name);if(raw!==void 0&&parsePositiveInt(raw)===void 0)throw new ApiError(`--${f.name} must be a positive integer`,"BAD_FLAG")}if(f.type==="uint"){let raw=flags.get(f.name);if(raw!==void 0&&parseNonNegativeInt(raw)===void 0)throw new ApiError(`--${f.name} must be a non-negative integer`,"BAD_FLAG")}}}class VerbArgs{verb;flags;caller;constructor(verb,flags,caller=cliCaller()){this.verb=verb;this.flags=flags;this.caller=caller}resolvePath(name,raw){let expanded=expandTilde(raw);if(isAbsolute(expanded))return expanded;if(this.caller.cwd===void 0)throw new ApiError(`--${name} must be an absolute path: this caller has no working directory`,"BAD_FLAG");return resolve(this.caller.cwd,expanded)}spec(name){let f=this.verb.flags.find((s)=>s.name===name);if(!f)throw Error(`internal: --${name} is not declared on verb "${this.verb.name}"`);return f}str(name){this.spec(name);let v=this.flags.get(name);return v&&v.length>0?v:void 0}present(name){return this.spec(name),this.flags.get(name)!==void 0}list(name){if(!this.spec(name).repeatable)throw Error(`internal: --${name} is not repeatable on verb "${this.verb.name}"`);let raw=this.flags.get(name);return raw===void 0?[]:raw.split(REPEAT_SEP).filter((v)=>v.length>0)}promptText(){if(this.promptMemo!==void 0)return this.promptMemo.value;let value=this.readPromptText();return this.promptMemo={value},value}promptMemo;readPromptText(){let inline=this.verb.flags.some((f)=>f.name==="prompt")?this.str("prompt"):void 0,file=this.str("prompt-file");if(inline!==void 0&&file!==void 0)throw new ApiError("pass --prompt or --prompt-file, not both","BAD_FLAG",helpStep(this.verb.name));if(file===void 0)return inline;if(file==="-"&&!this.caller.cli)throw new ApiError("--prompt-file - reads stdin, which this caller cannot send; pass --prompt","BAD_FLAG");let text=readFileSync(file==="-"?0:this.resolvePath("prompt-file",file),"utf8");if(text.trim().length===0)throw new ApiError(`--prompt-file ${file} is empty`,"BAD_FLAG");return text}require(name){let v=this.str(name);if(v===void 0)throw new ApiError(`--${name} is required`,"MISSING_FLAG");return v}enumOf(name){let f=this.spec(name),v=this.str(name);if(v===void 0)return;if(f.values&&!f.values.includes(v))throw new ApiError(`--${name} must be one of ${f.values.join(", ")}`,"BAD_FLAG",helpStep(this.verb.name));return v}requireEnum(name){return this.require(name),this.enumOf(name)}vendor(){let value=this.str("vendor");if(value===void 0)return;let builtins=this.spec("vendor").values??ALL_VENDORS;if(builtins.includes(value))return value;if(registeredEngineIds().includes(value))return value;throw new ApiError(`--vendor must be a built-in (${builtins.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",helpStep(this.verb.name))}bool(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let value=parseBoolLiteral(raw);if(value===void 0)throw new ApiError(`--${name} must be a boolean (true/false)`,"BAD_FLAG");return value}int(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parsePositiveInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a positive integer`,"BAD_FLAG");return n}nonNegativeInt(name){this.spec(name);let raw=this.str(name);if(raw===void 0)return;let n=parseNonNegativeInt(raw);if(n===void 0)throw new ApiError(`--${name} must be a non-negative integer`,"BAD_FLAG");return n}path(name){let v=this.str(name);return v===void 0?void 0:this.resolvePath(name,v)}requirePath(name){return this.resolvePath(name,this.require(name))}requireRepo(name){let raw=this.require(name);return isRemoteRepoKey(raw)?raw:this.resolvePath(name,raw)}}function parseAgentsSpec(spec){let out=[];for(let part of spec.split(",")){let trimmed=part.trim();if(!trimmed)continue;let colon=trimmed.indexOf(":");if(colon===-1)throw new ApiError(`--agents entry "${trimmed}" must be engine:count`,"BAD_FLAG",FANOUT_STEP);let vendor=trimmed.slice(0,colon);if(!ALL_VENDORS.includes(vendor)&&!registeredEngineIds().includes(vendor))throw new ApiError(`--agents engine "${vendor}" must be a built-in (${ALL_VENDORS.join(", ")}) or a registered engine id \u2014 see \`engine-list\``,"BAD_FLAG",FANOUT_STEP);let count=parsePositiveInt(trimmed.slice(colon+1));if(count===void 0)throw new ApiError(`--agents count for "${vendor}" must be a positive integer`,"BAD_FLAG",FANOUT_STEP);if(out.length+count>FANOUT_CAP)throw new ApiError(`--agents requests ${out.length+count} agents, exceeds the cap of ${FANOUT_CAP}`,"BAD_FLAG",FANOUT_STEP);for(let i=0;i<count;i++)out.push(vendor)}if(out.length===0)throw new ApiError('--agents specified no agents (e.g. "claude:2,codex:1")',"BAD_FLAG",FANOUT_STEP);return out}function buildCountPlan(count,vendor){if(count>FANOUT_CAP)throw new ApiError(`--count ${count} exceeds the parallel cap of ${FANOUT_CAP} \u2014 spawn in batches`,"BAD_FLAG",FANOUT_STEP);return Array(count).fill(vendor)}function daemonOf(ctx){if(!ctx.client)throw new ApiError("daemon required","BAD_DAEMON");return ctx.client}async function repoFilter(runtime,repoFlag,repos){let target=await runtime.resolveRepoRoot(repoFlag);if(!await runtime.isUsableRepo(target))throw new ApiError(`--repo ${repoFlag} is not a readable git repository (resolved to ${target}) \u2014 the project may have moved or been deleted; \`rove api list\` still shows its tasks`,"REPO_UNRESOLVABLE");let resolved=new Map,unresolvable=new Set;for(let repo of new Set(repos)){let root=await runtime.resolveRepoRoot(repo);if(resolved.set(repo,root),!samePath(root,target)&&!await runtime.isUsableRepo(root))unresolvable.add(repo)}return{target,matches:(repo)=>samePath(resolved.get(repo),target),unresolvableRepos:[...unresolvable].sort()}}async function simpleRpc(ctx,name,payload){return daemonOf(ctx).request(name,payload)}async function handlePtyList(){let[{RoveDaemonClient},{defaultPtyHostSocketPath}]=await Promise.all([import("./chunk-3r8v2gza.js"),import("./chunk-ax7wzcam.js")]),client=new RoveDaemonClient(defaultPtyHostSocketPath());try{return await client.connect(),await client.request("pty.list",{})}catch{return{sessions:null}}finally{client.close()}}function readJsonBody(source){let raw=readFileSync2(source==="-"?0:resolve2(process.cwd(),expandTilde(source)),"utf8"),parsed;try{parsed=JSON.parse(raw)}catch{throw new ApiError(`--json ${source} is not valid JSON`,"BAD_FLAG")}if(!parsed||typeof parsed!=="object"||Array.isArray(parsed))throw new ApiError("--json must be an object: {delivery, outcome, summary, evidence?, next?}","BAD_FLAG");let body=parsed,evidence=body.evidence;if(evidence!==void 0&&(!Array.isArray(evidence)||evidence.some((e)=>typeof e!=="string")))throw new ApiError("evidence must be an array of strings","BAD_FLAG");for(let key of["delivery","outcome","summary","next"])if(body[key]!==void 0&&typeof body[key]!=="string")throw new ApiError(`${key} must be a string`,"BAD_FLAG");return body}var FIELD_FLAGS=["delivery","outcome","summary","next"];function readBody(ctx){let json=ctx.args.str("json"),evidence=ctx.args.list("evidence");if(json===void 0)return{delivery:ctx.args.str("delivery"),outcome:ctx.args.str("outcome"),summary:ctx.args.str("summary"),evidence,next:ctx.args.str("next")};if(FIELD_FLAGS.some((f)=>ctx.args.str(f)!==void 0)||evidence.length>0)throw new ApiError("pass --json OR the individual flags, not both","BAD_FLAG");return readJsonBody(json)}async function report(ctx){let body=readBody(ctx);if(!body.delivery)throw new ApiError("--delivery is required","MISSING_FLAG");if(!body.outcome||!DELIVERY_OUTCOMES.includes(body.outcome))throw new ApiError(`--outcome must be one of ${DELIVERY_OUTCOMES.join("|")}`,"MISSING_FLAG");if(!body.summary?.trim())throw new ApiError("--summary is required","MISSING_FLAG");if(body.summary.length>DELIVERY_SUMMARY_MAX_CHARS)throw new ApiError(`summary is ${body.summary.length} characters; the cap is ${DELIVERY_SUMMARY_MAX_CHARS}`,"RESPONSE_TOO_LARGE",{hint:"Summarize: lead with the conclusion, put the detail in --evidence (commits, PR urls, paths)."});let res=await simpleRpc(ctx,"delivery.report",{deliveryId:body.delivery,outcome:body.outcome,summary:body.summary,...body.evidence?.length?{evidence:body.evidence}:{},...body.next?{next:body.next}:{}});if(res.delivery===null)throw new ApiError(`no delivery ${body.delivery}`,"DELIVERY_NOT_FOUND",{hint:"Copy the id from the [ROVE] report line of the prompt you were given; `deliveries --task-id <id>` lists them."});return res}var REPORT_VERB={name:"report",group:"drive",summary:`Close the report a delivered prompt asked for. Run it as the LAST step of the turn, before you stop: a turn that ends without it is flagged \`report_missing\` in the owner's Inbox. --delivery is the id in the prompt's [ROVE] / [ROVE ROUTINE] line. Re-reporting replaces the earlier report; a late report on a flagged delivery clears the flag. --summary \u2264 ${DELIVERY_SUMMARY_MAX_CHARS} chars; put commits, PR urls, test results and paths in repeatable --evidence, and what the next tick should do in --next. Use --json - (stdin) for text with quotes or backticks.`,flags:[{name:"delivery",type:"string",placeholder:"ID",description:"Delivery id, e.g. d-41."},{name:"outcome",type:"enum",values:DELIVERY_OUTCOMES,placeholder:"O",description:"done = finished what was asked; partial = some of it; blocked = needs the owner or an external fix; failed = tried and could not."},{name:"summary",type:"string",placeholder:"TEXT",description:"What was done (or why not)."},{name:"evidence",type:"string",placeholder:"X",repeatable:!0,description:"A commit, PR url, test command + result, or file path. Repeat the flag for several."},{name:"next",type:"string",placeholder:"TEXT",description:"What the next tick should do."},{name:"json",type:"string",placeholder:"PATH",description:"Read {delivery, outcome, summary, evidence?, next?} as JSON from this file (`-` = stdin) instead of the flags."}],handler:report},DELIVERIES_VERB={name:"deliveries",group:"read",summary:`A task's armed deliveries, newest first (newest ${MAX_DELIVERIES_PER_TASK} are kept): status awaiting|reported|missing|skipped, the report (outcome, summary, evidence, next) and, for missing, why (cause turn-complete|turn-failed|engine-dead|stalled|interrupted|session-end|rate-limited + note; rate-limited raises no Inbox row and does not block) plus dismissedAt once the owner dismissed it (a missing row blocks \`send --if-runnable\` and routine ticks until a late report or that dismissal). A skipped row is a \`send --if-runnable\` tick should-run refused: \`skipped\` is the verdict {reason, detail?, until?, gateIds?}, \`repeats\` how many identical refusals (same tab, same reason) the one row stands for. The per-tick log a loop prompt should read instead of a growing MEMO file.`,flags:[F.taskId(!0),{name:"limit",type:"int",placeholder:"N",description:"At most N entries (default: all retained)."}],handler:async(ctx)=>simpleRpc(ctx,"delivery.list",{taskId:ctx.args.require("task-id"),...ctx.args.int("limit")!==void 0?{limit:ctx.args.int("limit")}:{}})};async function listAllEnginePresets(){let presets=await enginePresetsInList(),byProtocol=new Map,modelsOf=(protocol)=>{let pending=byProtocol.get(protocol);if(!pending){let list=engineEntry(protocol).listModels;pending=list?list().catch(()=>null):Promise.resolve(null),byProtocol.set(protocol,pending)}return pending};return Promise.all(presets.map(async(preset)=>({...preset,models:await modelsOf(preset.protocol)})))}async function enginePresetsInList(){ensurePluginEnginesLoaded();let presets=[...listEnginePresets()],seen=new Set(presets.map((p)=>p.id));for(let id of await installedEngineIds())if(!seen.has(id))presets.push(describePreset(id));return presets}async function engineListIds(){return(await enginePresetsInList()).map((p)=>p.id)}var ENGINE_LIST_VERB={name:"engine-list",group:"discover",summary:`List every engine Rove can launch \u2014 built-ins, registered presets, the shipped contrib engines whose CLI is on PATH (${CONTRIB_ENGINE_IDS.join(", ")}), and engines contributed by enabled plugins \u2014 each with its RAW launch command, exactly as it runs. Copy one into \`add --command\` / \`send --tab new --command\` verbatim, or edit its flags first. \`protocol\` is the adapter Rove speaks to it (history, trust, delivery); \`generic\` = none, which still runs fine but loses transcript reads. \`models\` is what the engine can name for \`--model\` (suggestions, not a closed set); null = Rove cannot list them for this engine. Returns { engines }.`,flags:[],offline:!0,handler:async()=>({engines:await listAllEnginePresets()})};function taskEngine(task){let command=task.command?.trim();if(command){let resolved=resolveCommandProtocol(command);if(resolved!==GENERIC_PROTOCOL)return resolved}return sessionProtocol(coerceVendorId(task.vendor))}function assertEngineAcceptsEffort(engine,level,recover){let levels=engineEntry(engine).effortLevels??[];if(levels.length===0)throw new ApiError(`engine ${engine} declares no reasoning effort levels`,"BAD_EFFORT",{engine,hint:`Only engines with declared reasoning levels accept one (${vendorsWithEffortLevels().join(", ")} today). Check the task's engine with \`get-task\`.`,nextCommandArgs:[...recover]});if(!levels.includes(level))throw new ApiError(`engine ${engine} does not accept effort level ${JSON.stringify(level)} \u2014 it declares ${levels.join(", ")}`,"BAD_EFFORT",{engine,levels,hint:`Pass one of: ${levels.join(", ")}.`,nextCommandArgs:[...recover]})}function assertEngineAcceptsModel(engine,model,recover){if(engineEntry(engine).modelArgv)return;throw new ApiError(`engine ${engine} declares no model flag \u2014 it does not accept a model`,"BAD_MODEL",{engine,hint:"Only engines that declare a model flag accept one (claude, codex, kimi, pi, omp today); `engine-list` shows `models` per engine.",nextCommandArgs:[...recover]})}function vendorToRecord(task,engine){let recorded=coerceVendorId(task.vendor);return sessionProtocol(recorded)===engine?recorded:engine}async function readRunnability(daemon,taskId){try{return await daemon.request("task.runnability",{taskId})}catch{return}}async function shouldRun(ctx){let tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let verdict=await daemonOf(ctx).request("task.runnability",{taskId:ctx.args.require("task-id"),...tab?{tabId:tab}:{}});if(!verdict.run)throw new ApiError(`skip: ${verdict.reason}`,"SKIPPED",{...verdict});return verdict}var SHOULD_RUN_VERB={name:"should-run",group:"read",summary:`Would a tick sent to this task be used? Prints the verdict {run:true} or {run:false, reason, detail?, until?, gateIds?} and exits 0 on run, ${SHOULD_RUN_SKIP_EXIT_CODE} on skip (the verdict is still on stdout). reason, first match wins: ${SKIP_REASONS.join(" | ")}. \`awaiting-owner\` = an open gate (answer or withdraw it), \`report-missing\` = the last delivery ended unreported (late \`report\`, or dismiss its Inbox row), \`quota\` clears itself at \`until\`, \`engine-error\` / \`engine-dead\` clear on the engine's next turn or a restart, \`awaiting-permission\` = the engine waits on an approval, \`busy\` = a turn is running or the last delivery still awaits its report. A task whose only gate is answered-but-undelivered is runnable: the next tick carries the answer. Without --tab the whole task is judged and a dead engine is not a skip (\`send\` revives it); --tab tab-N judges that tab and a dead engine is \`engine-dead\`; --tab new judges only task-level state. To check and send in one step use \`send --if-runnable\`.`,flags:[F.taskId(!0),{name:"tab",type:"string",required:!1,placeholder:"TAB",description:'Judge exactly this tab (e.g. tab-2), or "new" for a tab about to be created. Omitted = the whole task.'}],handler:shouldRun};async function attentionDismiss(ctx){let delivery=ctx.args.require("delivery");if((await daemonOf(ctx).request("attention.dismiss",{deliveryId:delivery})).found===!1)throw new ApiError(`no delivery ${delivery}`,"DELIVERY_NOT_FOUND",{hint:"`deliveries --task-id <id>` lists a task's delivery ids; the Inbox row names it as d-N."});return{ok:!0,delivery,cleared:!0}}var ATTENTION_DISMISS_VERB={name:"attention-dismiss",group:"drive",summary:'Dismiss a `report_missing` Inbox row by its delivery id (d-N): the owner\'s deliberate "seen, carry on" for a tick that ended without a report. It unblocks `should-run` for the task. Opening or visiting the row never does this, and a late `report` clears it on its own. Idempotent; an unknown id is DELIVERY_NOT_FOUND.',flags:[{name:"delivery",type:"string",required:!0,placeholder:"ID",description:"Delivery id, e.g. d-41."}],handler:attentionDismiss};var API_SCHEMA_VERSION=ROVE_API_VERSION,GLOBAL_FLAGS=[{name:"pretty",type:"bool",description:"Pretty-print stdout JSON."},{name:"help",type:"bool",description:"Show usage for the verb and exit."},{name:"envelope",type:"bool",description:"Print every outcome as one { apiVersion, ok, result | error } on stdout \u2014 what the daemon's and the bridge's api.invoke return."},{name:"machine",type:"string",placeholder:"ALIAS",description:"Run the verb in that registered machine's daemon (`rove machine list`); paths are paths there. A --task-id on another machine goes there without it."}];function displayValues(f){if(!f.values)return;if(f.name!=="vendor")return f.values;let registered=registeredEngineIds().filter((id)=>!f.values.includes(id));return registered.length>0?[...f.values,...registered]:f.values}function flagJson(f){let values=displayValues(f);return{name:f.name,type:f.type,required:f.required??!1,...values?{values}:{},...f.default!==void 0?{default:f.default}:{},...f.placeholder?{placeholder:f.placeholder}:{},...f.repeatable?{repeatable:!0}:{},description:f.description}}function verbSchema(v){return{name:v.name,group:v.group,summary:v.summary,offline:v.offline??!1,cliOnly:v.cliOnly??!1,flags:v.flags.map(flagJson)}}function schemaIndex(){let cliName=ROVE_PRODUCT_NAME;return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,hint:`Compact index. Drill into ONE verb: \`${cliName} api schema --verb <name>\` (or \`${cliName} api <verb> --help\`). One group: \`--group <g>\`. Whole spec: \`--all\`.`,groups:VERB_GROUPS,verbs:VERBS.map((v)=>({name:v.name,group:v.group,summary:v.summary})),globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES}}function groupSchema(group){let verbs=VERBS.filter((v)=>v.group===group);if(verbs.length===0)throw new ApiError(`unknown group: ${group}. Groups: ${Object.keys(VERB_GROUPS).join(", ")}`,"BAD_VERB",{hint:"that group does not exist \u2014 the compact index lists every group and the verbs in it",nextCommandArgs:["api","schema"]});return{group,verbs:verbs.map((v)=>({name:v.name,summary:v.summary}))}}function fullSchema(){return{apiVersion:API_SCHEMA_VERSION,roveVersion:CURRENT_VERSION,output:{success:"one JSON object on stdout, newline-terminated, exit 0",error:'{"error":{"message","code"}} on stderr, exit != 0',pretty:"--pretty indents stdout JSON",envelope:'--envelope prints {"apiVersion","ok","result"} or {"apiVersion","ok","error"} on stdout instead, exit codes unchanged'},transports:{cli:"rove api <verb> --flag value",daemon:'socket request "api.invoke" {verb, params, machine?} \u2192 the envelope; params are keyed by flag name',bridge:'WebSocket op "api.invoke" {verb, params, machine?} \u2192 result, or the full error object'},globalFlags:GLOBAL_FLAGS,aliases:VERB_ALIASES,groups:VERB_GROUPS,verbs:VERBS.map(verbSchema)}}function flagSignature(verb){return verb.flags.map((f)=>{let meta=f.type==="enum"&&f.values?displayValues(f).join("|"):f.placeholder??(f.type==="bool"?"":"X"),core=meta?`--${f.name} ${meta}`:`--${f.name}`;return f.required?core:`[${core}]`}).join(" ")}function verbHelp(verb){let lines=[`${ROVE_PRODUCT_NAME} api ${verb.name} ${flagSignature(verb)}`.trimEnd(),"",verb.summary,""],alias=Object.entries(VERB_ALIASES).find(([,canon])=>canon===verb.name)?.[0];if(alias)lines.push(`Alias: ${alias}`,"");if(verb.flags.length>0){lines.push("Flags:");for(let f of verb.flags){let req=f.required?" (required)":f.repeatable?" (repeatable)":"",def=f.default!==void 0?` [default: ${f.default}]`:"",vals=f.type==="enum"&&f.values?` {${displayValues(f).join("|")}}`:"";lines.push(` --${f.name}${vals}${req}${def} ${f.description}`)}lines.push("")}return lines.push("Global: [--pretty] [--help] [--envelope] [--machine ALIAS]"),lines.join(`
3
3
  `)}function apiUsage(){let cliName=ROVE_PRODUCT_NAME,rows=VERBS.map((v)=>` ${v.name.padEnd(18)} ${v.summary}`);return[`usage: ${cliName} api <verb> [flags] [--pretty] [--help]`,"",`Explore the full surface (names, flags, types) with: ${cliName} api schema`,"","verbs:",...rows,"","Output is one JSON object on stdout (exit 0); errors are JSON on stderr (exit != 0)."].join(`
4
- `)}var DEFAULT_PAGE_MESSAGES=40,MAX_PAGE_MESSAGES=50,PAGE_BYTE_BUDGET=131072,STRING_CLIP_CHARS=16384,DEFAULT_TAIL_LINES=40,TERMINAL_TAIL_LINES=200,TERMINAL_TAIL_BYTES=65536;function encodeCursor(cursor){return Buffer.from(JSON.stringify(cursor),"utf8").toString("base64url")}function decodeCursor(raw,taskId){let parsed;try{parsed=JSON.parse(Buffer.from(raw,"base64url").toString("utf8"))}catch{throw new ApiError("invalid cursor (not a read-output cursor)","CURSOR_INVALID")}let c=parsed;if(!(c!==null&&typeof c==="object"&&c.v===1&&typeof c.task==="string"&&(c.tab===void 0||typeof c.tab==="string")&&(c.src==="history"&&typeof c.sid==="string"&&typeof c.idx==="number"||c.src==="terminal"&&typeof c.off==="number")))throw new ApiError("invalid cursor (unknown version or shape)","CURSOR_INVALID");if(c.task!==taskId)throw new ApiError(`cursor belongs to task ${c.task}, not ${taskId}`,"CURSOR_TASK_MISMATCH");return c}function clipStrings(value){if(typeof value==="string"){if(value.length<=STRING_CLIP_CHARS)return value;let points=[...value];if(points.length<=STRING_CLIP_CHARS)return value;return`${points.slice(0,STRING_CLIP_CHARS).join("")}\u2026[+${points.length-STRING_CLIP_CHARS} chars clipped]`}if(Array.isArray(value))return value.map(clipStrings);if(value!==null&&typeof value==="object"){let out={};for(let[k,v]of Object.entries(value))out[k]=clipStrings(v);return out}return value}function buildHistoryPage(messages,at,limit){let forward="from"in at,page=[],bytes=0,limited=!1,i=forward?at.from:at.before-1;for(;forward?i<messages.length:i>=0;forward?i++:i--){if(page.length>=limit)break;let clipped=clipStrings(messages[i]),size=JSON.stringify(clipped).length;if(page.length>0&&bytes+size>PAGE_BYTE_BUDGET){limited=!0;break}page.push(clipped),bytes+=size}if(forward)return{page,start:at.from,end:i,limited};return page.reverse(),{page,start:i+1,end:at.before,limited}}function terminalLines(text){return terminalRows(text)}function boundedTail(text,maxLines=TERMINAL_TAIL_LINES){let lines=terminalLines(text),start=Math.max(0,lines.length-Math.min(maxLines,TERMINAL_TAIL_LINES)),bytes=0;for(let i=lines.length-1;i>=start;i--)if(bytes+=(lines[i]?.length??0)+1,bytes>TERMINAL_TAIL_BYTES&&i<lines.length-1){start=i+1;break}return{tail:lines.slice(start),truncated:start>0}}async function peekTaskTerminal(taskId,engineBin,tab,sinceOffset){let host=await openPtyHost();if(!host)return"host-unreachable";try{let key;if(tab)key=`${taskId}::${tab}`;else{let sessions=await listSessionsOrNull(host.rpc);if(sessions===null)return"host-unreachable";key=findEngineKey(sessions,taskId,engineBin)??sessions.find((s)=>s.key===`${taskId}::tab-1`)?.key}if(!key)return null;let res;try{res=await host.rpc.request("pty.peek",{key,sinceOffset})}catch{return"host-unreachable"}if(!res.exists){if(tab)throw new ApiError(`tab ${tab} has no hosted session on task ${taskId} \u2014 see \`rove api pty-list\` for live tabs`,"TAB_NOT_FOUND");return null}return{pid:res.pid,offset:res.offset,text:Buffer.from(res.data,"base64").toString("utf8"),sinceValid:res.sinceValid,live:res.alive,exit:res.exit??null}}catch(err){if(err instanceof ApiError)throw err;return null}finally{host.close()}}async function engineScreenError(task,opts={}){let pattern=protocolEntry(task.vendor).errorLine;if(!pattern)return;try{let page=await peekTaskTerminal(task.id,taskEngineArgv(task)[0],void 0);if(!page||page==="host-unreachable")return;return screenErrorLine(pattern,boundedTail(page.text).tail,opts)}catch{return}}var ACTIVITY_STATES_DOC="`.activity.state` is one of: `idle` (at rest), `running` (a turn is in progress), `turn_complete` (a turn finished \u2014 also at rest; treat it like idle when deciding whether the engine is free), `permission_needed` (blocked on an approval prompt), `rate_limited` (clears on its own), `error` (the last turn failed; `.activity.detail.note` carries the engine's text, and `.activity.source: \"screen\"` means it was read off the engine's own screen because the engine reported nothing), `dead` (the engine process exited; the tab's exit record says how).",AT_REST=new Set(["idle","turn_complete"]);async function readActivityRegistry(daemon){try{let dbg=await daemon.request("debug.inspect");return{tasks:dbg?.activity?.tasks??{},tabs:dbg?.activity?.tabs??{}}}catch{return null}}async function activityView(registry,task,tab){let entry=tab?registry?.tabs[task.id]?.[tab]:registry?.tasks[task.id];if(!entry)return null;let base={state:entry.state,at:new Date(entry.at).toISOString(),forMs:Math.max(0,Date.now()-entry.at),...entry.detail?{detail:entry.detail}:{}},atRest=AT_REST.has(entry.state);if(tab||!(atRest||entry.state==="running"))return base;let note=await engineScreenError(task,{last:!atRest});return note?{...base,state:"error",detail:{failure:"other",note},source:"screen"}:base}var selfSessionOnce,identityWarning=null;async function realProbe(){let[{openPtyHost:openPtyHost2,listSessions:listSessions2},{psSnapshot:psSnapshot2}]=await Promise.all([import("./chunk-yebn33mf.js"),import("./chunk-18dpws0y.js")]);return{sessions:async()=>{let host=await openPtyHost2();if(!host)return[];try{return await listSessions2(host.rpc)}finally{host.close()}},ps:psSnapshot2,pid:process.pid}}async function verifiedSelfSession(env=process.env,probe){if(!probe&&selfSessionOnce)return selfSessionOnce;let run=resolveSelfSession(env,probe);return selfSessionOnce=run,run}async function resolveSelfSession(env,probe){let taskId=env.ROVE_TASK_ID;if(!taskId)return null;let tabId=env.ROVE_TAB_ID||"tab-1";try{let p=probe??await realProbe(),key=`${taskId}::${tabId}`,session=(await p.sessions()).find((s)=>s.key===key&&s.alive);if(session?.pid){let{hasAncestor,parsePsSnapshot:parsePsSnapshot2}=await import("./chunk-18dpws0y.js");if(hasAncestor(parsePsSnapshot2(await p.ps([session.pid,p.pid])),p.pid,session.pid))return identityWarning=null,{taskId,tabId}}}catch{}return identityWarning=`$ROVE_TASK_ID/$ROVE_TASK_ID names task ${taskId} ${tabId}, but this process is not running inside that tab (an inherited env, not an identity) \u2014 dispatcher/peer provenance omitted`,null}function takeIdentityWarning(){let warning=identityWarning;return identityWarning=null,warning}async function dispatcherEnvPayload(env=process.env,probe){let self=await verifiedSelfSession(env,probe);if(!self)return{};return{dispatcherTaskId:self.taskId,dispatcherTabId:self.tabId}}async function readOwnDispatcher(daemon){let self=await verifiedSelfSession();if(!self)return null;try{return(await daemon.request("task.get",{taskId:self.taskId})).task.dispatcher??null}catch{return null}}async function resolveDispatcherTab(runtime,dispatcher){let{tabs,running}=await runtime.taskTabs(dispatcher.taskId);if(tabs.some((t)=>t.id===dispatcher.tabId&&t.alive))return dispatcher.tabId;if(running)return;throw new ApiError(`dispatcher tab ${dispatcher.tabId} on task ${dispatcher.taskId} is dead and the task has no live engine tab \u2014 the reply has nowhere to land`,"DISPATCHER_UNREACHABLE",{dispatcher,hint:`address an alive target explicitly with --task-id/--tab (see \`${ROVE_PRODUCT_NAME} api pty-list\`), or notify the user with \`${ROVE_PRODUCT_NAME} api notify\``,nextCommandArgs:["api","pty-list"]})}async function withPeerProvenance(daemon,targetTaskId,prompt){let self=await verifiedSelfSession(),senderId=self?.taskId;if(!senderId||senderId===targetTaskId)return prompt;let label=senderId;try{let res=await daemon.request("task.get",{taskId:senderId});label=res.task.title||res.task.branch||senderId}catch{}let api=roveApiInvocation(),replyTarget=`--task-id ${senderId} --tab ${self.tabId}`;return`[ROVE PEER] from "${label}" (task ${senderId} \u2014 Rove agent skill /rove, read it once per session; reply only if it changes what I do next: \`${api} send ${replyTarget} --prompt "<text>"\`; verb reference: \`${api} schema\`)
4
+ `)}var DEFAULT_PAGE_MESSAGES=40,MAX_PAGE_MESSAGES=50,PAGE_BYTE_BUDGET=131072,STRING_CLIP_CHARS=16384,DEFAULT_TAIL_LINES=40,TERMINAL_TAIL_LINES=200,TERMINAL_TAIL_BYTES=65536;function encodeCursor(cursor){return Buffer.from(JSON.stringify(cursor),"utf8").toString("base64url")}function decodeCursor(raw,taskId){let parsed;try{parsed=JSON.parse(Buffer.from(raw,"base64url").toString("utf8"))}catch{throw new ApiError("invalid cursor (not a read-output cursor)","CURSOR_INVALID")}let c=parsed;if(!(c!==null&&typeof c==="object"&&c.v===1&&typeof c.task==="string"&&(c.tab===void 0||typeof c.tab==="string")&&(c.src==="history"&&typeof c.sid==="string"&&typeof c.idx==="number"||c.src==="terminal"&&typeof c.off==="number")))throw new ApiError("invalid cursor (unknown version or shape)","CURSOR_INVALID");if(c.task!==taskId)throw new ApiError(`cursor belongs to task ${c.task}, not ${taskId}`,"CURSOR_TASK_MISMATCH");return c}function clipStrings(value){if(typeof value==="string"){if(value.length<=STRING_CLIP_CHARS)return value;let points=[...value];if(points.length<=STRING_CLIP_CHARS)return value;return`${points.slice(0,STRING_CLIP_CHARS).join("")}\u2026[+${points.length-STRING_CLIP_CHARS} chars clipped]`}if(Array.isArray(value))return value.map(clipStrings);if(value!==null&&typeof value==="object"){let out={};for(let[k,v]of Object.entries(value))out[k]=clipStrings(v);return out}return value}function buildHistoryPage(messages,at,limit){let forward="from"in at,page=[],bytes=0,limited=!1,i=forward?at.from:at.before-1;for(;forward?i<messages.length:i>=0;forward?i++:i--){if(page.length>=limit)break;let clipped=clipStrings(messages[i]),size=JSON.stringify(clipped).length;if(page.length>0&&bytes+size>PAGE_BYTE_BUDGET){limited=!0;break}page.push(clipped),bytes+=size}if(forward)return{page,start:at.from,end:i,limited};return page.reverse(),{page,start:i+1,end:at.before,limited}}function terminalLines(text){return terminalRows(text)}function boundedTail(text,maxLines=TERMINAL_TAIL_LINES){let lines=terminalLines(text),start=Math.max(0,lines.length-Math.min(maxLines,TERMINAL_TAIL_LINES)),bytes=0;for(let i=lines.length-1;i>=start;i--)if(bytes+=(lines[i]?.length??0)+1,bytes>TERMINAL_TAIL_BYTES&&i<lines.length-1){start=i+1;break}return{tail:lines.slice(start),truncated:start>0}}async function peekTaskTerminal(taskId,engineBin,tab,sinceOffset){let host=await openPtyHost();if(!host)return"host-unreachable";try{let key;if(tab)key=`${taskId}::${tab}`;else{let sessions=await listSessionsOrNull(host.rpc);if(sessions===null)return"host-unreachable";key=findEngineKey(sessions,taskId,engineBin)??sessions.find((s)=>s.key===`${taskId}::tab-1`)?.key}if(!key)return null;let res;try{res=await host.rpc.request("pty.peek",{key,sinceOffset})}catch{return"host-unreachable"}if(!res.exists){if(tab)throw new ApiError(`tab ${tab} has no hosted session on task ${taskId} \u2014 see \`rove api pty-list\` for live tabs`,"TAB_NOT_FOUND");return null}return{pid:res.pid,offset:res.offset,text:Buffer.from(res.data,"base64").toString("utf8"),sinceValid:res.sinceValid,live:res.alive,exit:res.exit??null}}catch(err){if(err instanceof ApiError)throw err;return null}finally{host.close()}}async function engineScreenError(task,opts={}){let pattern=protocolEntry(task.vendor).errorLine;if(!pattern)return;try{let page=await peekTaskTerminal(task.id,taskEngineArgv(task)[0],void 0);if(!page||page==="host-unreachable")return;return screenErrorLine(pattern,boundedTail(page.text).tail,opts)}catch{return}}var ACTIVITY_STATES_DOC="`.activity.state` is one of: `idle` (at rest), `running` (a turn is in progress), `turn_complete` (a turn finished \u2014 also at rest; treat it like idle when deciding whether the engine is free), `permission_needed` (blocked on an approval prompt), `rate_limited` (clears on its own), `error` (the last turn failed; `.activity.detail.note` carries the engine's text, and `.activity.source: \"screen\"` means it was read off the engine's own screen because the engine reported nothing), `dead` (the engine process exited; the tab's exit record says how).",AT_REST=new Set(["idle","turn_complete"]);async function readActivityRegistry(daemon){try{let dbg=await daemon.request("debug.inspect");return{tasks:dbg?.activity?.tasks??{},tabs:dbg?.activity?.tabs??{}}}catch{return null}}async function activityView(registry,task,tab){let entry=tab?registry?.tabs[task.id]?.[tab]:registry?.tasks[task.id];if(!entry)return null;let base={state:entry.state,at:new Date(entry.at).toISOString(),forMs:Math.max(0,Date.now()-entry.at),...entry.detail?{detail:entry.detail}:{}},atRest=AT_REST.has(entry.state);if(tab||!(atRest||entry.state==="running"))return base;let note=await engineScreenError(task,{last:!atRest});return note?{...base,state:"error",detail:{failure:"other",note},source:"screen"}:base}var selfSessionOnce,identityWarning=null;async function realProbe(){let[{openPtyHost:openPtyHost2,listSessions:listSessions2},{psSnapshot:psSnapshot2}]=await Promise.all([import("./chunk-pw1twn8f.js"),import("./chunk-18dpws0y.js")]);return{sessions:async()=>{let host=await openPtyHost2();if(!host)return[];try{return await listSessions2(host.rpc)}finally{host.close()}},ps:psSnapshot2,pid:process.pid}}async function verifiedSelfSession(env=process.env,probe){if(!probe&&selfSessionOnce)return selfSessionOnce;let run=resolveSelfSession(env,probe);return selfSessionOnce=run,run}async function resolveSelfSession(env,probe){let taskId=env.ROVE_TASK_ID;if(!taskId)return null;let tabId=env.ROVE_TAB_ID||"tab-1";try{let p=probe??await realProbe(),key=`${taskId}::${tabId}`,session=(await p.sessions()).find((s)=>s.key===key&&s.alive);if(session?.pid){let{hasAncestor,parsePsSnapshot:parsePsSnapshot2}=await import("./chunk-18dpws0y.js");if(hasAncestor(parsePsSnapshot2(await p.ps([session.pid,p.pid])),p.pid,session.pid))return identityWarning=null,{taskId,tabId}}}catch{}return identityWarning=`$ROVE_TASK_ID/$ROVE_TASK_ID names task ${taskId} ${tabId}, but this process is not running inside that tab (an inherited env, not an identity) \u2014 dispatcher/peer provenance omitted`,null}function takeIdentityWarning(){let warning=identityWarning;return identityWarning=null,warning}async function dispatcherEnvPayload(env=process.env,probe){let self=await verifiedSelfSession(env,probe);if(!self)return{};return{dispatcherTaskId:self.taskId,dispatcherTabId:self.tabId}}async function readOwnDispatcher(daemon){let self=await verifiedSelfSession();if(!self)return null;try{return(await daemon.request("task.get",{taskId:self.taskId})).task.dispatcher??null}catch{return null}}async function resolveDispatcherTab(runtime,dispatcher){let{tabs,running}=await runtime.taskTabs(dispatcher.taskId);if(tabs.some((t)=>t.id===dispatcher.tabId&&t.alive))return dispatcher.tabId;if(running)return;throw new ApiError(`dispatcher tab ${dispatcher.tabId} on task ${dispatcher.taskId} is dead and the task has no live engine tab \u2014 the reply has nowhere to land`,"DISPATCHER_UNREACHABLE",{dispatcher,hint:`address an alive target explicitly with --task-id/--tab (see \`${ROVE_PRODUCT_NAME} api pty-list\`), or notify the user with \`${ROVE_PRODUCT_NAME} api notify\``,nextCommandArgs:["api","pty-list"]})}async function withPeerProvenance(daemon,targetTaskId,prompt){let self=await verifiedSelfSession(),senderId=self?.taskId;if(!senderId||senderId===targetTaskId)return prompt;let label=senderId;try{let res=await daemon.request("task.get",{taskId:senderId});label=res.task.title||res.task.branch||senderId}catch{}let api=roveApiInvocation(),replyTarget=`--task-id ${senderId} --tab ${self.tabId}`;return`[ROVE PEER] from "${label}" (task ${senderId} \u2014 Rove agent skill /rove, read it once per session; reply only if it changes what I do next: \`${api} send ${replyTarget} --prompt "<text>"\`; verb reference: \`${api} schema\`)
5
5
 
6
6
  ${prompt}`}function parseOption(raw){let eq=raw.indexOf("=");if(eq<=0||eq===raw.length-1)throw new ApiError(`--option must be key=label (got ${JSON.stringify(raw)})`,"BAD_FLAG",helpStep("gate-raise"));return{key:raw.slice(0,eq).trim(),label:raw.slice(eq+1).trim()}}async function gateRaise(ctx){let{args}=ctx,daemon=daemonOf(ctx),taskId=args.require("task-id"),tabId=args.str("tab");if(!tabId){let self=await verifiedSelfSession();if(self?.taskId===taskId)tabId=self.tabId}return{ok:!0,gate:(await daemon.request("gate.raise",{taskId,...tabId?{tabId}:{},question:args.require("question"),options:args.list("option").map(parseOption),...args.str("recommend")?{recommended:args.str("recommend")}:{},...args.str("context")?{context:args.str("context")}:{}})).gate}}async function gateAnswer(ctx){let{args}=ctx,option=args.str("option"),text=args.str("text");if(option===void 0&&text===void 0)throw new ApiError("pass --option KEY and/or --text TEXT","MISSING_FLAG",helpStep("gate-answer"));let res=await daemonOf(ctx).request("gate.answer",{gateId:args.require("gate"),...option!==void 0?{option}:{},...text!==void 0?{text}:{},by:"cli"});return{ok:!0,gate:res.gate,delivered:res.delivered}}async function gateWithdraw(ctx){return{ok:!0,gate:(await daemonOf(ctx).request("gate.withdraw",{gateId:ctx.args.require("gate")})).gate}}async function gates(ctx){let{args}=ctx,repo=args.str("repo")?args.requireRepo("repo"):void 0;return{gates:(await daemonOf(ctx).request("gate.list",{...repo?{repo}:{},...args.str("task-id")?{taskId:args.str("task-id")}:{},status:args.enumOf("status")??"open"})).gates}}async function takeOwedGateAnswers(daemon,taskId,tab){if(tab==="new")return null;try{let owed=await daemon.request("gate.takeUndelivered",{taskId,...tab?{tabId:tab}:{}});return owed.text?{text:owed.text,gateIds:owed.gateIds}:null}catch{return null}}async function markGateAnswersDelivered(daemon,gateIds){await daemon.request("gate.markDelivered",{gateIds}).catch(()=>{})}var TASK_STATUSES=["backlog","in_progress","in_review","done","canceled","error"];function csvOf(ctx,name,allowed,verb="list"){let raw=ctx.args.str(name);if(raw===void 0)return;let values=raw.split(",").map((s)=>s.trim()).filter(Boolean);if(values.find((v)=>!allowed.includes(v))!==void 0||values.length===0)throw new ApiError(`--${name} takes a comma list of ${allowed.join(", ")}`,"BAD_FLAG",helpStep(verb));return new Set(values)}function listFilters(ctx){let repo=ctx.args.path("repo"),statuses=csvOf(ctx,"status",TASK_STATUSES),activities=csvOf(ctx,"activity",TASK_ACTIVITY_STATES);if(!repo&&!statuses&&!activities)return null;return{repo,statuses,activities}}async function filterTaskList(ctx,daemon,tasks,filters){let isLocal=(t)=>!t.origin||t.origin.machineId==="local",repo=filters.repo?await repoFilter(ctx.runtime,filters.repo,tasks.filter(isLocal).map((t)=>t.repo)):null,kept=tasks.filter((t)=>(!repo||isLocal(t)&&repo.matches(t.repo))&&(!filters.statuses||filters.statuses.has(t.status))),out=kept;if(filters.activities){let wanted=filters.activities,registry=await readActivityRegistry(daemon),local=kept.filter(isLocal),views=await Promise.all(local.map((t)=>activityView(registry,t)));out=local.flatMap((task,i)=>{let activity=views[i];return activity&&wanted.has(activity.state)?[{...task,activity}]:[]})}return{tasks:out,...repo&&repo.unresolvableRepos.length>0?{unresolvableRepos:repo.unresolvableRepos}:{}}}var MAX_LIVE_ENGINES_KEY="engine.maxLiveSessions";function normalizeMaxLiveEngines(value){let n=typeof value==="number"?value:typeof value==="string"&&value.trim()!==""?Number(value.trim()):Number.NaN;return Number.isInteger(n)&&n>0?n:void 0}function persistedMaxLiveEngines(){return normalizeMaxLiveEngines(loadStateFile()[MAX_LIVE_ENGINES_KEY])}async function deliverToExactTab(rpc,taskId,tabId,cwd,prompt,opts){let key=`${taskId}::${tabId}`,{sessions=[]}=await rpc.request("pty.list",{}),session=sessions.find((s)=>s.key===key);if(!session?.alive){if(session?.restored===!0){if(!opts?.respawn)throw restoredTabError(taskId,tabId,prompt);let launch=opts.respawn(),open=await openAdmittedEngine(rpc,{key,cwd,...launch?{command:launch.command}:{},defaultColors:readPersistedTerminalDefaultColors()},{maxLiveEngines:opts.maxLiveEngines,snapshot:opts.snapshot,taskId,retryArgs:["api","send","--task-id",taskId,"--tab",tabId,"--respawn","--plain","--prompt",prompt]});if(open.respawned!==!0&&open.alive!==!0)throw new ApiError(`tab ${tabId} on task ${taskId} could not be respawned`,"SESSION_FAILED",{taskId,tabId,hint:"the frozen record is still on disk; open the task in the TUI to see the session's own output",nextCommandArgs:["api","read-output","--task-id",taskId,"--tab",tabId,"--source","terminal"]});let enginePid=await awaitEngineProcess(rpc,key,opts.engineBin,{timeoutMs:ENGINE_START_PROBE_MS,intervalMs:ENGINE_START_POLL_MS,snapshot:opts.snapshot});if(enginePid===null)return{session:key,pane:key,started:!1,respawned:!0,engineReady:!1,delivered:!1,reason:await hostedSessionFailureLine(rpc,key)??ENGINE_NOT_OBSERVED_REASON};return await deliverRespawned(rpc,key,prompt,enginePid.vendor)}throw new ApiError(`tab ${tabId} has no live session on task ${taskId} \u2014 see \`rove api pty-list\` for alive tabs`,"TAB_NOT_FOUND")}let presence=await enginePresence(session.pid,opts?.engineBin,opts?.snapshot);if(presence.kind==="unknown")throw new ApiError(`could not read the process table, so tab ${tabId} on task ${taskId} could not be checked for a live engine`,"ENGINE_PROBE_FAILED",{hint:"the `ps` probe failed or timed out; retry, or check the machine's process table",nextCommandArgs:["api","pty-list"]});if(presence.kind!=="engine")throw new ApiError(`tab ${tabId} on task ${taskId} has no live engine process \u2014 it is a plain shell right now`,"ENGINE_NOT_RUNNING",{hint:"spawn a fresh engine tab for this prompt with --tab new, or pick an engine tab from pty-list",nextCommandArgs:["api","pty-list"]});let outcome=await deliverToKey(rpc,key,prompt,{vendor:presence.vendor});return{session:key,pane:key,started:!1,...outcomeFields(outcome)}}async function deliverRespawned(rpc,key,prompt,vendor){let outcome=await deliverToKey(rpc,key,prompt,{vendor});return{session:key,pane:key,started:!1,respawned:!0,...outcomeFields(outcome)}}function restoredTabError(taskId,tabId,prompt){return new ApiError(`tab ${tabId} on task ${taskId} is a freeze-restored session \u2014 its pty host restarted, so the tab exists with its scrollback but nothing is running in it`,"TAB_RESTORED",{taskId,tabId,hint:"revive it with --respawn (resumes the tab's pinned conversation when it has one \u2014 `get-task` shows `sessionId`; a tab without one replays its recorded launch command)",nextCommandArgs:["api","send","--task-id",taskId,"--tab",tabId,"--respawn","--plain","--prompt",prompt]})}function sessionFailed(taskId,message,hint,extra){return new ApiError(message,"SESSION_FAILED",{taskId,...extra,hint,nextCommandArgs:["api","read-output","--task-id",taskId,"--source","terminal"]})}async function ensureHostFor(taskId,hint){try{return await ensurePtyHost()}catch(error){throw sessionFailed(taskId,`failed to start PTY host for ${taskId}: ${error instanceof Error?error.message:String(error)}`,hint)}}import{realpathSync,statSync}from"fs";import{join}from"path";function hostTabIds(sessions,taskId){let ids=new Set;for(let{key}of sessions){let[task,tab,...rest]=key.split("::");if(task===taskId&&tab&&rest.length===0)ids.add(tab)}return ids}function collision(taskId,key,retryArgs){return new ApiError(`session ${key} already existed; no new tab was created`,"TAB_COLLISION",{taskId,hint:"another process took this tab id between allocation and spawn; retry to get a fresh id",nextCommandArgs:retryArgs})}var createdFresh=(open)=>open.created??open.replay.length===0;async function openShell(rpc,taskId,cwd,taken){let shell=resolveLoginShell({fallback:"/bin/zsh"}),tab=allocateTab(taskId,(id,ordinal)=>({kind:"command",id,title:null,ordinal,command:[shell]}),taken),key=`${taskId}::${tab.id}`;try{let open;try{open=await rpc.request("pty.open",{key,cwd,command:[shell],defaultColors:readPersistedTerminalDefaultColors()})}catch(error){throw closeTabsSnapshot(taskId,tab.id),error}if(!createdFresh(open))throw collision(taskId,key,["api","tab-new","--task-id",taskId,"--kind","shell"]);await rpc.request("pty.write",{key,data:shellIdentityInput(taskId,tab.id)})}finally{await rpc.request("pty.detach",{key}).catch(()=>{})}return{tab,key}}async function openEngine(rpc,target,cwd,taken){let taskId=target.id,tab=allocateTab(taskId,(id,ordinal)=>({kind:"engine",id,title:null,ordinal,...target.tabVendor?{vendor:target.tabVendor}:{},...target.tabCommand?{engineCommand:target.tabCommand}:{}}),taken),key=`${taskId}::${tab.id}`,retryArgs=["api","tab-new","--task-id",taskId,"--kind","engine",...target.tabCommand?["--command",target.tabCommand]:[]];try{let open,sessionId;try{let launchVendor=target.tabVendor??target.vendor,launchCommand=target.tabCommand??(target.tabVendor?void 0:target.command),pinned=withPinnedSessionId(engineLaunchArgv({command:launchCommand,vendor:launchVendor,effort:target.modelEffort,model:target.model}),launchVendor);sessionId=pinned.sessionId,trustEngineWorktree(launchVendor,cwd);let launch=buildEngineSessionLaunch({task:{id:taskId,kind:target.kind,vendor:launchVendor,repo:target.repo},worktreePath:cwd,shell:resolveLoginShell({fallback:"/bin/zsh"}),argv:pinned.argv,promptIntent:{kind:"none"},tabId:tab.id});open=await openAdmittedEngine(rpc,{key:launch.key,cwd,command:launch.command,defaultColors:readPersistedTerminalDefaultColors()},{maxLiveEngines:persistedMaxLiveEngines(),taskId,retryArgs})}catch(error){throw closeTabsSnapshot(taskId,tab.id),error}if(!createdFresh(open)&&open.respawned!==!0)throw collision(taskId,key,retryArgs);if(!open.alive)throw sessionFailed(taskId,`the new engine tab ${tab.id} exited as it started`,"the session was created but nothing stayed running in it \u2014 fix the task's launch command (`api update --command`), then open another tab",{session:key});markCliTabSession(taskId,tab.id,sessionId)}finally{await rpc.request("pty.detach",{key}).catch(()=>{})}return{tab,key}}async function openNewTab(target,worktree,kind){let host=await ensureHostFor(target.id,"no PTY host to run the tab in \u2014 nothing was started; check `rove daemon status`, then retry");try{let taken=hostTabIds(await listSessions(host.rpc),target.id),opened=kind==="shell"?await openShell(host.rpc,target.id,worktree,taken):await openEngine(host.rpc,target,worktree,taken);return{tab:readTabsSnapshot(target.id)?.tabs.find((t)=>t.id===opened.tab.id)??opened.tab,session:opened.key}}catch(error){if(error instanceof ApiError)throw error;throw sessionFailed(target.id,`opening a ${kind} tab failed for ${target.id}: ${error instanceof Error?error.message:String(error)}`,"the PTY host or state.json refused the new tab \u2014 check `rove daemon status` and that state.json is writable, then retry")}finally{host.close()}}function fileInside(worktree,rel){if(!rel||rel.startsWith("/")||rel.includes("\x00")||rel.split(/[\\/]/).includes(".."))throw new ApiError("--file must be a path relative to the worktree","BAD_FLAG");let abs,root;try{root=realpathSync(worktree),abs=realpathSync(join(worktree,rel))}catch{throw new ApiError(`${rel} does not exist in the worktree`,"FILE_NOT_FOUND")}if(!pathWithin(root,abs))throw new ApiError(`${rel} resolves outside the worktree`,"BAD_FLAG");if(!statSync(abs).isFile())throw new ApiError(`${rel} is not a file`,"BAD_FLAG");return abs}async function spawnCommandTab(taskId,cwd,command,title,purpose,retryArgs){let host=await ensureHostFor(taskId,"no PTY host to run the tab in \u2014 nothing was started; check `rove daemon status`, then retry");try{let taken=hostTabIds(await listSessions(host.rpc),taskId),tab=allocateTab(taskId,(id,ordinal)=>({kind:"command",id,title,ordinal,command,...purpose?{purpose}:{}}),taken),key=`${taskId}::${tab.id}`;try{let open;try{open=await host.rpc.request("pty.open",{key,cwd,command,defaultColors:readPersistedTerminalDefaultColors()})}catch(error){throw closeTabsSnapshot(taskId,tab.id),error}if(!createdFresh(open))throw collision(taskId,key,retryArgs)}finally{await host.rpc.request("pty.detach",{key}).catch(()=>{})}return{tab:readTabsSnapshot(taskId)?.tabs.find((t)=>t.id===tab.id)??tab,session:key}}catch(error){if(error instanceof ApiError)throw error;throw sessionFailed(taskId,`opening the tab failed for ${taskId}: ${error instanceof Error?error.message:String(error)}`,"the PTY host or state.json refused the new tab \u2014 check `rove daemon status` and that state.json is writable, then retry")}finally{host.close()}}async function launchEditor(spec){let abs=fileInside(spec.worktree,spec.file),launch=await resolveEditorLaunch(spec.worktree,abs,spec.line);if(!launch)throw new ApiError("no editor found \u2014 set $VISUAL/$EDITOR, or Settings \u2192 General \u2192 Editor","NO_EDITOR",{hint:"an editor chosen in Settings wins over $EDITOR; `rove config` shows what resolves"});let snapshot=readTabsSnapshot(spec.taskId),previous=snapshot?findEditorTab(snapshot)?.id:void 0;return{...await spawnCommandTab(spec.taskId,spec.worktree,["sh","-c",launch.command],launch.label,"editor",["api","tab-new","--task-id",spec.taskId,"--kind","editor",`--file=${spec.file}`,...spec.line===void 0?[]:[`--line=${spec.line}`]]),...previous?{replaces:previous}:{},...launch.atLine===void 0?{}:{atLine:launch.atLine}}}function findPane(taskId,pane){let found=listPaneLaunches({socketPath:defaultDaemonSocketPath(),binPath:ROVE_PRODUCT_NAME,taskId}).find((p)=>`${p.pluginId}.${p.paneId}`===pane);if(!found)throw new ApiError(`no enabled plugin pane named ${pane}`,"PANE_NOT_FOUND",{hint:"panes of enabled plugins only; `rove plugin list` shows what is installed"});return found}async function launchPane(spec,split){let pane=findPane(spec.taskId,spec.pane),snapshot=readTabsSnapshot(spec.taskId),host=snapshot?.tabs.find((t)=>t.id===(spec.hostTab??snapshot.activeId));if(pane.placement==="split"&&host&&host.kind!=="content"){let out=await split(spec.taskId,host.id,spec.worktree,pane);if(out.changed)return{tab:out.tab,session:`${spec.taskId}::${host.id}::${out.leaf}`,leaf:out.leaf}}return spawnCommandTab(spec.taskId,spec.worktree,pane.argv,pane.title,void 0,["api","tab-new","--task-id",spec.taskId,"--kind","pane",`--pane=${spec.pane}`])}function launchViewport(spec){let shown=readTabsSnapshot(spec.view.id)?.tabs.find((t)=>t.id==="tab-1"),engine=shown?.kind==="engine"?shown:void 0;return{tab:allocateTab(spec.taskId,(id,ordinal)=>({kind:"engine",id,title:null,ordinal,...engine?.vendor?{vendor:engine.vendor}:{},sessionId:engine?.sessionId??null,spawned:!0,ptyTask:{id:spec.view.id,worktree:spec.view.worktree}})),session:`${spec.view.id}::tab-1`}}function launchTab(spec,split){switch(spec.kind){case"editor":return launchEditor(spec);case"pane":return launchPane(spec,split);case"viewport":return Promise.resolve(launchViewport(spec))}}function locate(taskId,tabId,state){let tab=state&&Array.isArray(state.tabs)?state.tabs.find((t)=>t.id===tabId):void 0;if(!state||!tab)throw new ApiError(`tab ${tabId} does not exist on task ${taskId}`,"TAB_NOT_FOUND",{hint:"refresh the task's tab ids with get-task, then retry with one of its .tabs[].id values",nextCommandArgs:["api","get-task","--task-id",taskId]});if(tab.kind==="content")throw new ApiError(`tab ${tabId} is a content tab; only terminal tabs split`,"TAB_NOT_SPLITTABLE");return{state,tab}}function editTree(taskId,tabId,edit){let out={},key=terminalTabsKey(taskId);if(updateStateFile((store)=>{let{state,tab}=locate(taskId,tabId,store[key]),before=tab.splitTree??initialSplit(null),after=edit(before);if(out.before=before,out.after=after,after===before)return out.tab=tab,!1;let next=setTabSplit(state,tabId,collapseSplit(after));store[key]=next,out.tab=next.tabs.find((t)=>t.id===tabId);return}),!out.tab||!out.before||!out.after)throw Error(`split edit for ${taskId}/${tabId} did not run`);return{tab:out.tab,before:out.before,after:out.after}}async function openLeafShell(rpc,key,cwd,opts={}){if(await rpc.request("pty.open",{key,cwd,command:opts.command??[resolveLoginShell({fallback:"/bin/zsh"})],defaultColors:readPersistedTerminalDefaultColors(),...opts.size?{cols:opts.size.cols,rows:opts.size.rows}:{}}),opts.identity)await rpc.request("pty.write",{key,data:opts.identity});await rpc.request("pty.detach",{key}).catch(()=>{})}async function splitTab(taskId,tabId,worktree,edit){let host=await ensureHostFor(taskId,"no PTY host to run the split in \u2014 nothing was changed; check `rove daemon status`");try{switch(edit.kind){case"open":{let{tab,before,after}=editTree(taskId,tabId,(tree)=>{let from=leaves(tree.root).some((l)=>l.id===edit.from)?edit.from:tree.activeLeafId,base=from===tree.activeLeafId?tree:{...tree,activeLeafId:from},argv=edit.command?.argv??[resolveLoginShell({fallback:"/bin/zsh"})],next=splitActive(base,edit.orientation,argv,edit.size??null);if(next===base)return tree;return edit.command?renameLeaf(next,next.activeLeafId,edit.command.title):next});if(after===before)return{tab,leaf:before.activeLeafId,changed:!1};let leaf=after.activeLeafId;try{await openLeafShell(host.rpc,splitLeafPtyKey(tabPtyKeyFor(taskId,tab),leaf),worktree,{size:edit.size,command:edit.command?.argv})}catch(error){throw editTree(taskId,tabId,(tree)=>removeLeaf(tree,leaf)??tree),sessionFailed(taskId,`opening a split shell failed: ${error instanceof Error?error.message:String(error)}`,"the PTY host refused the shell \u2014 the split was taken back; check `rove daemon status`, then retry")}return{tab,leaf,changed:!0}}case"close":{let{tab,before,after}=editTree(taskId,tabId,(tree)=>{let next=removeLeaf(tree,edit.leaf);if(next===null)throw new ApiError(`${edit.leaf} is the only pane; close the tab instead`,"LAST_LEAF");return next});if(after===before)return{tab,leaf:edit.leaf,changed:!1};return await killTaskSessions(host.rpc,[splitLeafPtyKey(tabPtyKeyFor(taskId,tab),edit.leaf)]),{tab,leaf:edit.leaf,changed:!0}}case"rename":{let{tab,before,after}=editTree(taskId,tabId,(tree)=>renameLeaf(tree,edit.leaf,edit.title));return{tab,leaf:edit.leaf,changed:after!==before}}case"reset":{let{tab}=editTree(taskId,tabId,(tree)=>tree),shellTab=tab.kind==="command";if(!shellTab&&edit.leaf==="leaf-1")throw new ApiError("reset respawns a login shell; this pane runs an engine","RESET_ENGINE",{hint:"close the engine tab and open a new one; shell tabs and split shells reset"});let key=splitLeafPtyKey(tabPtyKeyFor(taskId,tab),edit.leaf);return await killTaskSessions(host.rpc,[key],{wait:!0}),await openLeafShell(host.rpc,key,worktree,{size:edit.size,identity:shellTab&&edit.leaf==="leaf-1"?shellIdentityInput(taskId,tab.id):void 0}),{tab,leaf:edit.leaf,changed:!1}}}}finally{host.close()}}function hasLiveEngineTab(snapshot,taskId,sessions,engineAlive,engineBin){let labelled=new Set((snapshot?.tabs??[]).filter((t)=>t.kind==="engine").map((t)=>t.id));return sessions.some((s)=>{if(s.alive!==!0||!isHostedTaskKey(s.key,taskId))return!1;if(engineAlive?.get(s.key)===!1)return!1;if(s.key===`${taskId}::tab-1`)return!0;let tabId=s.key.slice(taskId.length+2);return labelled.has(tabId)||sessionArgvNamesEngine(s.command,engineBin)})}async function ensureWorktree(client,target){if(target.worktreePath)return target.worktreePath;let res=await client.request("task.ensureWorktree",{taskId:target.id});if(!res.worktreePath)throw new ApiError(`task ${target.id} has no worktree`,"NO_WORKTREE");return res.worktreePath}async function deliverHosted(target,worktree,prompt){let host=await ensureHostFor(target.id,"no PTY host to run the engine in \u2014 nothing was started; check `rove daemon status`, then retry the same send"),newTab;try{if(target.tab&&target.tab!=="new"){let engineBin=engineLaunchArgv({command:target.command,vendor:target.vendor,effort:target.modelEffort,model:target.model})[0],tabId=target.tab;return await deliverToExactTab(host.rpc,target.id,tabId,worktree,prompt,{engineBin,maxLiveEngines:persistedMaxLiveEngines(),...target.respawn?{respawn:()=>restoredTabLaunch(target,tabId,worktree,resolveLoginShell({fallback:"/bin/zsh"}))}:{}})}newTab=target.tab==="new"?mintCliTab(target.id,target.tabVendor,target.tabCommand):void 0;let launchVendor=target.tabVendor??target.vendor,launchCommand=target.tabCommand??(target.tabVendor?void 0:target.command),{argv,sessionId}=withPinnedSessionId(engineLaunchArgv({command:launchCommand,vendor:launchVendor,effort:target.modelEffort,model:target.model}),launchVendor);trustEngineWorktree(launchVendor,worktree);let launch=buildEngineSessionLaunch({task:{id:target.id,kind:target.kind,vendor:launchVendor,repo:target.repo},worktreePath:worktree,shell:resolveLoginShell({fallback:"/bin/zsh"}),argv,promptIntent:target.newTask?{kind:"new-task",prompt}:{kind:"explicit",prompt},tabId:newTab}),result=await deliverHostedPrompt(host.rpc,{id:target.id,engineBin:argv[0]},worktree,prompt,launch,{forceNew:newTab!==void 0,maxLiveEngines:persistedMaxLiveEngines(),retryArgs:["api","send","--task-id",target.id,...target.tab?["--tab",target.tab]:[],...target.tabCommand?["--command",target.tabCommand]:[],"--plain","--prompt",prompt]});if(result.started&&!result.delivered)throw sessionFailed(target.id,`failed to start hosted engine session for ${target.id}`,"the session was created but no engine ran in it \u2014 fix the task's launch command (`api update --command`), then retry with `api send --tab new`",{session:result.session,engineReady:result.engineReady,...result.reason?{reason:result.reason}:{}});if(!newTab)publishCliTabSnapshot(target.id,result.started?sessionId:void 0);if(result.started&&sessionId){let startedTab=newTab??result.session.split("::")[1];if(startedTab)markCliTabSession(target.id,startedTab,sessionId)}return result}catch(error){if(error instanceof ApiError&&newTab&&["ENGINE_LIMIT","ENGINE_ADMISSION_BUSY","ENGINE_ADMISSION_UNAVAILABLE"].includes(error.code))closeTabsSnapshot(target.id,newTab);if(error instanceof ApiError)throw error;throw sessionFailed(target.id,`hosted engine session failed for ${target.id}: ${error instanceof Error?error.message:String(error)}`,"the hosted session threw while starting or delivering \u2014 read its terminal tail for the cause, then retry the send")}finally{host.close()}}var realPromptDeliveryOps={deliverHosted};async function closeHeadlessTerminalTab(taskId,tabId){let host=await openPtyHost();try{let sessions=host?await listSessions(host.rpc):[],saved=readTabsSnapshot(taskId)?.tabs.find((tab)=>tab.id===tabId),directKey=`${taskId}::${tabId}`,unregisteredAlive=sessions.some((session)=>session.key===directKey&&session.alive);if(!saved&&!unregisteredAlive)throw new ApiError(`tab ${tabId} does not exist on task ${taskId}`,"TAB_NOT_FOUND",{hint:"refresh the task's tab ids with get-task, then retry with one of its .tabs[].id values",nextCommandArgs:["api","get-task","--task-id",taskId]});let closing=saved?closeTabsSnapshot(taskId,tabId):void 0;if(saved&&!closing)throw new ApiError(`tab ${tabId} no longer exists on task ${taskId}`,"TAB_NOT_FOUND",{hint:"the tab closed while this command was running; refresh with get-task before retrying",nextCommandArgs:["api","get-task","--task-id",taskId]});let baseKey=closing?tabPtyKeyFor(taskId,closing):directKey,keys=!(closing?.kind==="engine"&&closing.ptyTask)?sessions.filter((session)=>session.key===baseKey||session.key.startsWith(`${baseKey}::`)).map((session)=>session.key):[],wasAlive=sessions.some((session)=>keys.includes(session.key)&&session.alive);if(host)await killTaskSessions(host.rpc,keys);return{kind:closing?.kind??"engine",wasAlive}}finally{host?.close()}}async function deliverPrompt(client,target,prompt,ops=realPromptDeliveryOps){let worktree=await ensureWorktree(client,target);if(target.newTask)await client.request("task.observeLanguage",{taskId:target.id,text:prompt}).catch(()=>{});let hosted=await ops.deliverHosted(target,worktree,prompt);if(!hosted)throw sessionFailed(target.id,`failed to start hosted engine session for ${target.id}`,"delivery returned nothing \u2014 the task exists but has no session; retry with `api send --tab new`");return hosted}async function readLiveTaskIds(){let listed=await handlePtyList();if(!listed?.sessions)return null;let live=new Set;for(let row of listed.sessions){if(row.alive===!1||typeof row.key!=="string")continue;let taskId=row.key.split("::")[0];if(taskId)live.add(taskId)}return live}var defaultApiRuntime={liveTaskIds:readLiveTaskIds,isTaskRunning:async(taskId,engineArgv)=>(await defaultApiRuntime.taskTabs(taskId,engineArgv)).running,taskTabs:async(taskId,engineArgv)=>{let listed=null,host=await openPtyHost();if(host)try{listed=await listSessionsOrNull(host.rpc)}finally{host.close()}let sessions=listed??[],hostReachable=listed!==null,liveVendors,engineAlive;try{let{engineProcessIn,foregroundEngineIn:foregroundEngineIn2,parsePsSnapshot:parsePsSnapshot2,psSnapshot:psSnapshot2}=await import("./chunk-18dpws0y.js"),walkable=sessions.filter((s)=>s.alive&&typeof s.pid==="number"&&s.pid>0);if(walkable.length>0){let rows=parsePsSnapshot2(await psSnapshot2(walkable.map((s)=>s.pid)));liveVendors=new Map(walkable.map((s)=>[s.key,foregroundEngineIn2(rows,s.pid)?.vendor??null])),engineAlive=new Map(walkable.map((s)=>[s.key,engineProcessIn(rows,s.pid,engineArgv)]))}else engineAlive=new Map}catch{}let exits={};try{exits=(await import("./chunk-4zhmbqt3.js")).readPtyExitRecords()}catch{}let snapshot=readTabsSnapshot(taskId),sessionIds=new Map((snapshot?.tabs??[]).map((t)=>[t.id,t.sessionId??void 0]));return{tabs:joinTaskTabs(snapshot,taskId,listed,exits,liveVendors,engineAlive).map((row)=>{let sessionId=sessionIds.get(row.id);return sessionId?{...row,sessionId}:row}),running:hostReachable?hasLiveEngineTab(snapshot,taskId,sessions,engineAlive,engineArgv?.[0]):null}},closeTerminalTab:closeHeadlessTerminalTab,deliverPrompt:(client,target,prompt)=>deliverPrompt(client,target,prompt),openTab:async(client,target,kind)=>openNewTab(target,await ensureWorktree(client,target),kind),launchTab:(spec)=>launchTab(spec,(taskId,tabId,worktree,pane)=>splitTab(taskId,tabId,worktree,{kind:"open",orientation:"row",from:"",command:{argv:pane.argv,title:pane.title}})),splitTab,resolveRepoRoot:async(absPath)=>(await import("./chunk-afwh7as3.js")).resolveMainRepoRoot(absPath),isUsableRepo:async(absPath)=>{let{isGitRepo,isRemoteRepoKey:isRemoteRepoKey2}=await import("./chunk-afwh7as3.js");return isRemoteRepoKey2(absPath)||isGitRepo(absPath)},isValidBranchName:async(branch)=>(await import("./chunk-afwh7as3.js")).isValidBranchName(branch),defaultVendor:async(repo)=>{let{getGlobalDefaultVendor,getRepoLastActiveVendor}=await import("./chunk-gb3qb83h.js");return(repo?getRepoLastActiveVendor(repo):void 0)??getGlobalDefaultVendor()},readWorktreeChanges:async(worktreePath)=>(await import("./chunk-11wyec8z.js")).readWorktreeChanges(worktreePath),readBranchSignals:async(worktreePath,recordedBaseRef)=>(await import("./chunk-9xtg2k54.js")).readBranchSignals(worktreePath,recordedBaseRef),tearDownSession:async(taskId)=>{let host=await openPtyHost();if(host)try{await killTaskSessions(host.rpc,taskKeys(await listSessions(host.rpc),taskId))}catch{}finally{host.close()}}};async function issueUpdate(ctx){let title=ctx.args.str("title"),body=ctx.args.str("body"),task=ctx.args.str("task"),status=ctx.args.enumOf("status");if(title===void 0&&body===void 0&&task===void 0&&status===void 0)throw new ApiError("issue-update requires --title, --body, --task, and/or --status","MISSING_FLAG");let repoRoot=ctx.args.requireRepo("repo"),id=ctx.args.int("id"),state;if(title!==void 0||body!==void 0||task!==void 0)state=await simpleRpc(ctx,"issue.mutate",{repoRoot,op:{type:"update",id,title,body,...task===void 0?{}:{taskId:task==="none"?null:task}}});if(status!==void 0)state=await simpleRpc(ctx,"issue.mutate",{repoRoot,op:{type:"setStatus",id,status}});return state}async function assertNotEmptySuccess(daemon,ctx,prompt){if(ctx.args.bool("allow-empty"))return;if(!/^\s*succeeded\s*[:\uff1a]/i.test(prompt))return;let self=await verifiedSelfSession();if(!self)return;let sender;try{sender=(await daemon.request("task.get",{taskId:self.taskId})).task}catch{return}if(sender.kind==="main"||sender.kind==="dir")return;if(!sender.worktreePath)return;let ahead;try{ahead=(await ctx.runtime.readBranchSignals(sender.worktreePath,sender.baseRef)).ahead}catch{return}if(ahead!==0)return;let branch=sender.branch||"your branch";throw new ApiError(`refusing to report success: ${branch} has 0 commits \u2014 "succeeded" means COMMITTED, and this report would reach the coordinator as a clean success with nothing to land`,"EMPTY_SUCCESS_REPORT",{taskId:self.taskId,branch,hint:"commit your work with a real message and send again \u2014 or, if this task genuinely produced no commits (an investigation or a review), re-send with --allow-empty to say so explicitly",nextCommandArgs:["api","send",...ctx.args.str("task-id")?["--task-id",ctx.args.str("task-id")]:[],...ctx.args.str("tab")?["--tab",ctx.args.str("tab")]:[],"--allow-empty","--prompt",prompt]})}function requirePromptText(ctx,verb){let text=ctx.args.promptText();if(text===void 0)throw new ApiError("--prompt (or --prompt-file) is required","MISSING_FLAG",helpStep(verb));return text}async function send(ctx){let daemon=daemonOf(ctx),prompt=requirePromptText(ctx,"send"),tab=ctx.args.str("tab");if(tab&&tab!=="new"&&!/^tab-[A-Za-z0-9-]+$/.test(tab))throw new ApiError(`--tab must be "new" or a tab id like tab-2 (got ${JSON.stringify(tab)})`,"BAD_TAB");let tabCommand=ctx.args.str("command");if(tabCommand&&tab!=="new")throw new ApiError(`--command only applies to a new tab; pass --tab new (got --tab ${tab??"<canonical>"})`,"BAD_FLAG",helpStep("send"));let respawn=ctx.args.bool("respawn");if(respawn&&(tab===void 0||tab==="new"))throw new ApiError(`--respawn addresses one frozen tab; pass --tab tab-N (got --tab ${tab??"<canonical>"})`,"BAD_FLAG",helpStep("send"));let tabVendor=tabCommand?resolveCommandProtocol(tabCommand):void 0,taskId=ctx.args.str("task-id");if(!taskId){let dispatcher=await readOwnDispatcher(daemon);if(dispatcher){if(taskId=dispatcher.taskId,tab===void 0)tab=await resolveDispatcherTab(ctx.runtime,dispatcher)}else{let active=await resolveActiveTaskId(daemon);if(!active)throw new ApiError("no --task-id given and no active task \u2014 open a task first or pass --task-id","MISSING_TARGET");taskId=active}}let res=await daemon.request("task.get",{taskId});await assertNotEmptySuccess(daemon,ctx,prompt);let plain=ctx.args.bool("plain")?prompt:await withPeerProvenance(daemon,taskId,prompt),owed=await takeOwedGateAnswers(daemon,taskId,tab),withAnswers=owed?`${owed.text}
7
7
 
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{booleanFlagsOf,callVerb,errorBody,errorEnvelope,executeVerb,paramsToFlags,repeatableFlagsOf,runVerb,successEnvelope,toApiError}from"./chunk-pyv2ywxr.js";import"./chunk-ykdpg0zd.js";import"./chunk-d4mvprvq.js";import"./chunk-5j6z32fh.js";import"./chunk-p5902z3e.js";import"./chunk-9dendh71.js";import"./chunk-cbc1h345.js";import"./chunk-1mpheecn.js";import"./chunk-17e2tgyg.js";import"./chunk-4mxn5jg6.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-ghyz3fd7.js";import"./chunk-atn06ncf.js";import"./chunk-kwd59v51.js";import"./chunk-qkehg2c1.js";import"./chunk-j19gkhzh.js";import"./chunk-8bd054vx.js";import"./chunk-ddc4t38t.js";import"./chunk-dehnv06q.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-rf4z7b6g.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-vjvkkrzc.js";import"./chunk-z3zaa155.js";import"./chunk-08ycv55d.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-j7831wcj.js";import"./chunk-tewt7vre.js";import"./chunk-2tfnscxk.js";import"./chunk-9g0vz9dk.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-pahcf9td.js";import"./chunk-k7ke95vy.js";import"./chunk-avz72132.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-m0cbndc2.js";import"./chunk-xkr7fwvb.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import"./chunk-pn5ygqn6.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{toApiError,successEnvelope,runVerb,repeatableFlagsOf,paramsToFlags,executeVerb,errorEnvelope,errorBody,callVerb,booleanFlagsOf};
2
+ import{booleanFlagsOf,callVerb,errorBody,errorEnvelope,executeVerb,paramsToFlags,repeatableFlagsOf,runVerb,successEnvelope,toApiError}from"./chunk-y4vxge6s.js";import"./chunk-ekre1aps.js";import"./chunk-d4mvprvq.js";import"./chunk-5j6z32fh.js";import"./chunk-p5902z3e.js";import"./chunk-rc2nwsdd.js";import"./chunk-cbc1h345.js";import"./chunk-1mpheecn.js";import"./chunk-17e2tgyg.js";import"./chunk-4mxn5jg6.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-ghyz3fd7.js";import"./chunk-atn06ncf.js";import"./chunk-ggzfard0.js";import"./chunk-qkehg2c1.js";import"./chunk-gpcxqdmq.js";import"./chunk-hkyn3kme.js";import"./chunk-mvsvmfsf.js";import"./chunk-dehnv06q.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-rf4z7b6g.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import"./chunk-z3zaa155.js";import"./chunk-08ycv55d.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-j7831wcj.js";import"./chunk-tewt7vre.js";import"./chunk-2tfnscxk.js";import"./chunk-9g0vz9dk.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-pahcf9td.js";import"./chunk-k7ke95vy.js";import"./chunk-avz72132.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-c9xg9dn0.js";import"./chunk-xkr7fwvb.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import"./chunk-pn5ygqn6.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{toApiError,successEnvelope,runVerb,repeatableFlagsOf,paramsToFlags,executeVerb,errorEnvelope,errorBody,callVerb,booleanFlagsOf};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{existsSync}from"fs";function windowsBashCandidates(env){return[env.ProgramFiles,env["ProgramFiles(x86)"],env.LOCALAPPDATA&&`${env.LOCALAPPDATA}\\Programs`].filter(Boolean).map((root)=>`${root}\\Git\\bin\\bash.exe`)}var WINDOWS_BASH_FALLBACK="C:\\Program Files\\Git\\bin\\bash.exe";function isWindowsSpawnable(shellPath){return/^[A-Za-z]:[\\/]/.test(shellPath)||shellPath.startsWith("\\\\")}var POSIX_SHELL_NAMES=new Set(["bash","sh","zsh","dash","ash","ksh","mksh"]);function isPosixShell(shellPath){let base=(shellPath.split(/[\\/]/).pop()??"").toLowerCase();return POSIX_SHELL_NAMES.has(base.endsWith(".exe")?base.slice(0,-4):base)}var cache=new Map;function resolveLoginShell(options={}){let fallback=options.fallback??"/bin/bash",platform=options.platform??process.platform,env=options.env??process.env,exists=options.exists??existsSync,cacheable=platform===process.platform&&env===process.env&&options.exists===void 0;if(cacheable){let hit=cache.get(fallback);if(hit)return hit}let resolved=resolveUncached(fallback,platform,env,exists);if(cacheable)cache.set(fallback,resolved);return resolved}function resolveUncached(fallback,platform,env,exists){let explicit=env.SHELL?.trim();if(platform!=="win32")return explicit||fallback;if(explicit&&isPosixShell(explicit)&&isWindowsSpawnable(explicit)&&exists(explicit))return explicit;for(let candidate of windowsBashCandidates(env))if(exists(candidate))return candidate;return WINDOWS_BASH_FALLBACK}function toPosixPath(filePath,platform=process.platform){if(platform!=="win32")return filePath;let slashed=filePath.replace(/\\/g,"/"),drive=/^([A-Za-z]):\//.exec(slashed);return drive?`/${drive[1].toLowerCase()}${slashed.slice(2)}`:slashed}
2
+ import{existsSync}from"fs";function windowsBashCandidates(env){return[env.ProgramFiles,env["ProgramFiles(x86)"],env.LOCALAPPDATA&&`${env.LOCALAPPDATA}\\Programs`].filter(Boolean).map((root)=>`${root}\\Git\\bin\\bash.exe`)}var WINDOWS_BASH_FALLBACK="C:\\Program Files\\Git\\bin\\bash.exe";function isWindowsSpawnable(shellPath){return/^[A-Za-z]:[\\/]/.test(shellPath)||shellPath.startsWith("\\\\")}var POSIX_SHELL_NAMES=new Set(["bash","sh","zsh","dash","ash","ksh","mksh"]);function isPosixShell(shellPath){let base=(shellPath.split(/[\\/]/).pop()??"").toLowerCase();return POSIX_SHELL_NAMES.has(base.endsWith(".exe")?base.slice(0,-4):base)}var cache=new Map;function resolveLoginShell(options={}){let fallback=options.fallback??"/bin/bash",platform=options.platform??process.platform,env=options.env??process.env,exists=options.exists??existsSync,cacheable=platform===process.platform&&env===process.env&&options.exists===void 0;if(cacheable){let hit=cache.get(fallback);if(hit)return hit}let resolved=resolveUncached(fallback,platform,env,exists);if(cacheable)cache.set(fallback,resolved);return resolved}function resolveUncached(fallback,platform,env,exists){let explicit=env.SHELL?.trim();if(platform!=="win32"){if(explicit)return explicit;return[fallback,"/bin/bash","/bin/sh"].find((candidate)=>exists(candidate))??fallback}if(explicit&&isPosixShell(explicit)&&isWindowsSpawnable(explicit)&&exists(explicit))return explicit;for(let candidate of windowsBashCandidates(env))if(exists(candidate))return candidate;return WINDOWS_BASH_FALLBACK}function toPosixPath(filePath,platform=process.platform){if(platform!=="win32")return filePath;let slashed=filePath.replace(/\\/g,"/"),drive=/^([A-Za-z]):\//.exec(slashed);return drive?`/${drive[1].toLowerCase()}${slashed.slice(2)}`:slashed}
3
3
  export{resolveLoginShell,toPosixPath};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{API_VERBS,VERBS,VERB_ALIASES,VERB_GROUPS,findVerb,unknownVerbError}from"./chunk-ykdpg0zd.js";import"./chunk-d4mvprvq.js";import"./chunk-5j6z32fh.js";import"./chunk-p5902z3e.js";import"./chunk-9dendh71.js";import"./chunk-cbc1h345.js";import"./chunk-1mpheecn.js";import"./chunk-17e2tgyg.js";import"./chunk-4mxn5jg6.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-ghyz3fd7.js";import"./chunk-atn06ncf.js";import"./chunk-kwd59v51.js";import"./chunk-qkehg2c1.js";import"./chunk-j19gkhzh.js";import"./chunk-8bd054vx.js";import"./chunk-ddc4t38t.js";import"./chunk-dehnv06q.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-rf4z7b6g.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-vjvkkrzc.js";import"./chunk-z3zaa155.js";import"./chunk-08ycv55d.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-j7831wcj.js";import"./chunk-tewt7vre.js";import"./chunk-2tfnscxk.js";import"./chunk-9g0vz9dk.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-pahcf9td.js";import"./chunk-k7ke95vy.js";import"./chunk-avz72132.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-m0cbndc2.js";import"./chunk-xkr7fwvb.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import"./chunk-pn5ygqn6.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{unknownVerbError,findVerb,VERB_GROUPS,VERB_ALIASES,VERBS,API_VERBS};
2
+ import{API_VERBS,VERBS,VERB_ALIASES,VERB_GROUPS,findVerb,unknownVerbError}from"./chunk-ekre1aps.js";import"./chunk-d4mvprvq.js";import"./chunk-5j6z32fh.js";import"./chunk-p5902z3e.js";import"./chunk-rc2nwsdd.js";import"./chunk-cbc1h345.js";import"./chunk-1mpheecn.js";import"./chunk-17e2tgyg.js";import"./chunk-4mxn5jg6.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-ghyz3fd7.js";import"./chunk-atn06ncf.js";import"./chunk-ggzfard0.js";import"./chunk-qkehg2c1.js";import"./chunk-gpcxqdmq.js";import"./chunk-hkyn3kme.js";import"./chunk-mvsvmfsf.js";import"./chunk-dehnv06q.js";import"./chunk-sak1tphp.js";import"./chunk-4250pw6r.js";import"./chunk-rf4z7b6g.js";import"./chunk-rxqdw6s5.js";import"./chunk-jetsx9zr.js";import"./chunk-f5n0tgky.js";import"./chunk-z3zaa155.js";import"./chunk-08ycv55d.js";import"./chunk-hpay0ax8.js";import"./chunk-catrr0z3.js";import"./chunk-j7831wcj.js";import"./chunk-tewt7vre.js";import"./chunk-2tfnscxk.js";import"./chunk-9g0vz9dk.js";import"./chunk-w7zv47mp.js";import"./chunk-etfh8f4z.js";import"./chunk-6xppe9bj.js";import"./chunk-b7xjhgts.js";import"./chunk-x7r75e3p.js";import"./chunk-zsfyj36g.js";import"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import"./chunk-ag3txfg2.js";import"./chunk-2brn8e1y.js";import"./chunk-pahcf9td.js";import"./chunk-k7ke95vy.js";import"./chunk-avz72132.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import"./chunk-c9xg9dn0.js";import"./chunk-xkr7fwvb.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import"./chunk-b31cte10.js";import"./chunk-prqeddy7.js";import"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import"./chunk-pn5ygqn6.js";import"./chunk-z7zm8h1q.js";import"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import"./chunk-91sykzqq.js";export{unknownVerbError,findVerb,VERB_GROUPS,VERB_ALIASES,VERBS,API_VERBS};
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{BREAKING_VERSIONS,CURRENT_VERSION,compareSemver}from"./chunk-m0cbndc2.js";import{loadStateFile,patchStateFile}from"./chunk-axxf5dkq.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";var LAST_RUN_VERSION_KEY="app.lastRunVersion";function resetGateBlockers(lastRun,current=CURRENT_VERSION,breaking=BREAKING_VERSIONS){if(typeof lastRun!=="string"||lastRun.length===0)return[];let[lo,hi]=compareSemver(lastRun,current)<=0?[lastRun,current]:[current,lastRun];return breaking.filter((b)=>compareSemver(b,lo)>0&&compareSemver(b,hi)<=0)}function enforceResetGate(){let cliName=ROVE_PRODUCT_NAME,lastRun=loadStateFile()[LAST_RUN_VERSION_KEY],blockers=resetGateBlockers(lastRun);if(blockers.length>0){let from=typeof lastRun==="string"?lastRun:"unknown";console.error([`${cliName} ${CURRENT_VERSION}: cannot start \u2014 version ${blockers.join(", ")} introduced breaking changes`,`(last run: ${from}). Your daemon/session state may be incompatible.`,"","Run:",` ${cliName} reset # stop daemon + PTY host + sessions (tasks kept)`,` ${cliName} reset --hard # additionally wipe the task index + UI state`,"","Then relaunch Rove. Worktrees are never touched."].join(`
2
+ import{BREAKING_VERSIONS,CURRENT_VERSION,compareSemver}from"./chunk-c9xg9dn0.js";import{loadStateFile,patchStateFile}from"./chunk-axxf5dkq.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";var LAST_RUN_VERSION_KEY="app.lastRunVersion";function resetGateBlockers(lastRun,current=CURRENT_VERSION,breaking=BREAKING_VERSIONS){if(typeof lastRun!=="string"||lastRun.length===0)return[];let[lo,hi]=compareSemver(lastRun,current)<=0?[lastRun,current]:[current,lastRun];return breaking.filter((b)=>compareSemver(b,lo)>0&&compareSemver(b,hi)<=0)}function enforceResetGate(){let cliName=ROVE_PRODUCT_NAME,lastRun=loadStateFile()[LAST_RUN_VERSION_KEY],blockers=resetGateBlockers(lastRun);if(blockers.length>0){let from=typeof lastRun==="string"?lastRun:"unknown";console.error([`${cliName} ${CURRENT_VERSION}: cannot start \u2014 version ${blockers.join(", ")} introduced breaking changes`,`(last run: ${from}). Your daemon/session state may be incompatible.`,"","Run:",` ${cliName} reset # stop daemon + PTY host + sessions (tasks kept)`,` ${cliName} reset --hard # additionally wipe the task index + UI state`,"","Then relaunch Rove. Worktrees are never touched."].join(`
3
3
  `)),process.exit(1)}if(lastRun!==CURRENT_VERSION)stampResetGate()}function stampResetGate(){try{patchStateFile({[LAST_RUN_VERSION_KEY]:CURRENT_VERSION})}catch{}}
4
4
  export{LAST_RUN_VERSION_KEY,enforceResetGate,stampResetGate};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{CURRENT_VERSION,repoSlug}from"./chunk-m0cbndc2.js";import{spawnSync}from"child_process";var DEFAULT_FEEDBACK_CATEGORY_SLUG="feedback",DISCUSSION_CATEGORY_QUERY=`
2
+ import{CURRENT_VERSION,repoSlug}from"./chunk-c9xg9dn0.js";import{spawnSync}from"child_process";var DEFAULT_FEEDBACK_CATEGORY_SLUG="feedback",DISCUSSION_CATEGORY_QUERY=`
3
3
  query($owner: String!, $name: String!) {
4
4
  repository(owner: $owner, name: $name) {
5
5
  id
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{closeTab,engineTabArgv,initialTabs,setTabTitle,tabPtyKeyFor,terminalTabsKey}from"./chunk-8bd054vx.js";import{awaitEngineProcess,buildEngineSessionLaunch,deliverToHostedKey,enginePresence,ensureHostedSessionHost,findHostedEngineKey,hostedSessionFailureLine,hostedTaskKeys,initMarkerSaysFinished,isHostedTaskKey,killHostedSessions,listHostedSessions,listHostedSessionsOrNull,openHostedSessionHost,pastePromptWhenEngineUp,readPersistedTerminalDefaultColors,sessionArgvNamesEngine}from"./chunk-ddc4t38t.js";import{engineLaunchArgv}from"./chunk-rf4z7b6g.js";import{engineProcessIn,parsePsSnapshot,psSnapshot,vendorFromArgv}from"./chunk-j7831wcj.js";import{ApiError}from"./chunk-x7r75e3p.js";import{loadStateFile,patchStateFile,updateStateFile}from"./chunk-axxf5dkq.js";import{basename}from"path";function wireNode(node){if(node.kind==="leaf")return{kind:"leaf",id:node.id,title:node.title??null,own:node.content===null};return{kind:"group",orientation:node.orientation,children:node.children.map(wireNode)}}function wireSplit(tree){return tree?{root:wireNode(tree.root),activeLeafId:tree.activeLeafId}:null}function taskEngineArgv(task){return engineLaunchArgv({command:task.command,vendor:task.vendor})}function readTabsSnapshot(taskId){try{let snap=loadStateFile()[terminalTabsKey(taskId)];return snap&&Array.isArray(snap.tabs)?snap:void 0}catch{return}}function closeTabsSnapshot(taskId,tabId){let closing,key=terminalTabsKey(taskId);return updateStateFile((store)=>{let state=store[key];if(!state||!Array.isArray(state.tabs))return!1;if(closing=state.tabs.find((tab)=>tab.id===tabId),!closing)return!1;let{state:next,closedId}=closeTab(state,tabId,{allowEmpty:!0});if(!closedId)return closing=void 0,!1;store[key]=next;return}),closing}function renameTabsSnapshot(taskId,tabId,title){let found=!1,key=terminalTabsKey(taskId);return updateStateFile((store)=>{let state=store[key];if(!state||!Array.isArray(state.tabs))return!1;if(!state.tabs.some((tab)=>tab.id===tabId))return!1;found=!0;let next=setTabTitle(state,tabId,title);if(next===state)return!1;store[key]=next;return}),found}var aliveKeysOf=(sessions)=>new Set(sessions.filter((s)=>s.alive).map((s)=>s.key));function unregisteredTabIds(snapshot,taskId,sessions){let known=new Set((snapshot?.tabs??[]).map((t)=>t.id)),prefix=`${taskId}::`,out=[];for(let s of sessions){if(!s.alive||!s.key.startsWith(prefix))continue;let tabId=s.key.slice(prefix.length);if(tabId.includes("::"))continue;if(!known.has(tabId)&&!out.includes(tabId))out.push(tabId)}return out}var abnormalExit=(exit)=>exit&&(exit.code!==0||exit.signal!==null)?exit:null;function joinTaskTabs(snapshot,taskId,sessions,persistedExits={},liveVendors,engineAlive){let unknown=sessions===null,rows0=sessions??[],alive=aliveKeysOf(rows0),sessionExits=new Map(rows0.map((s)=>[s.key,s.exit])),deadExit=(key)=>{let ex=abnormalExit(sessionExits.get(key)??persistedExits[key]);if(!ex)return null;let record=persistedExits[key],sameDeath=record?.at===ex.at,tail=sameDeath?record?.tail:void 0,code=ex.code??(sameDeath?record?.code??null:null),layer=record?.layer??"pty";return{code,signal:ex.signal,at:ex.at,layer,...tail&&tail.length>0?{tail}:{}}},engineExit=(key)=>{let record=persistedExits[`${key}#engine`];if(!record)return null;let{code,signal,at,tail,atApproximate}=record;return{code,signal,at,layer:"engine",...atApproximate?{atApproximate}:{},...tail&&tail.length>0?{tail}:{}}},rows=(snapshot?.tabs??[]).map((t)=>{let key=tabPtyKeyFor(taskId,t),isAlive=unknown?null:alive.has(key),walked=isAlive===!0&&liveVendors?.has(key)===!0?liveVendors.get(key)??null:void 0,engineIsAlive=engineAliveOf(key,isAlive,engineAlive);return{id:t.id,kind:t.kind,title:t.title??null,vendor:t.vendor??null,liveVendor:walked!==void 0?walked:t.liveVendor??null,lastTitle:t.lastTitle??null,autoTitle:t.autoTitle??null,alive:isAlive,engineAlive:engineIsAlive,exit:isAlive===!1?deadExit(key):engineIsAlive===!1?engineExit(key):null,...t.splitTree?{split:wireSplit(t.splitTree)}:{},...t.kind==="engine"&&t.ptyTask?{viewOf:t.ptyTask.id}:{}}});for(let tabId of unregisteredTabIds(snapshot,taskId,rows0))rows.push({id:tabId,kind:"engine",title:null,vendor:null,liveVendor:null,lastTitle:null,autoTitle:null,alive:!0,engineAlive:engineAliveOf(`${taskId}::${tabId}`,!0,engineAlive),exit:null,unregistered:!0});return rows}function engineAliveOf(key,isAlive,engineAlive){if(isAlive===null)return null;if(!isAlive)return!1;return engineAlive?.has(key)===!0?engineAlive.get(key)??null:null}function publishCliTabSnapshot(taskId,sessionId){if(!taskId)return;try{let key=terminalTabsKey(taskId);if(loadStateFile()[key]!==void 0)return;let seeded=initialTabs();patchStateFile({[key]:sessionId?{...seeded,tabs:seeded.tabs.map((t)=>t.kind==="engine"?{...t,sessionId,spawned:!0}:t)}:seeded})}catch{}}function markCliTabSession(taskId,tabId,sessionId){try{let key=terminalTabsKey(taskId);updateStateFile((store)=>{let existing=store[key];if(!existing||!Array.isArray(existing.tabs))return!1;store[key]={...existing,tabs:existing.tabs.map((t)=>t.id===tabId&&t.kind==="engine"?{...t,...sessionId?{sessionId}:{},spawned:!0}:t)};return})}catch{}}function allocateTab(taskId,build,taken=new Set){let key=terminalTabsKey(taskId),out={};if(updateStateFile((store)=>{let existing=store[key],state=existing&&Array.isArray(existing.tabs)&&existing.tabs.length>0?existing:initialTabs(),ordinal=typeof state.nextOrdinal==="number"&&state.nextOrdinal>1?state.nextOrdinal:2;while(taken.has(`tab-${ordinal}`)||state.tabs.some((t)=>t.id===`tab-${ordinal}`))ordinal++;let tab=build(`tab-${ordinal}`,ordinal);out.tab=tab,store[key]={...state,tabs:[...state.tabs,tab],activeId:tab.id,nextOrdinal:ordinal+1};return}),!out.tab)throw Error(`tab allocation for ${taskId} did not run`);return out.tab}function mintCliTab(taskId,vendor,command){try{return allocateTab(taskId,(id,ordinal)=>({kind:"engine",id,title:null,ordinal,...vendor?{vendor}:{},...command?{engineCommand:command}:{}})).id}catch{return`tab-cli-${Date.now().toString(36)}`}}async function countLiveEngineSessions(sessions,snapshot=psSnapshot){let tabs=sessions.filter((s)=>s.alive&&s.key.split("::").length===2);if(!tabs.length)return 0;let rows;try{if(rows=parsePsSnapshot(await snapshot(tabs.flatMap((s)=>s.pid?[s.pid]:[]))),!rows.length)return tabs.length}catch{return tabs.length}return tabs.filter((s)=>{if(!s.pid)return!0;let root=rows.find((row)=>row.pid===s.pid);if(!root)return!0;if(engineProcessIn(rows,s.pid)||vendorFromArgv(root.args))return!0;let[taskId,tabId]=s.key.split("::"),labelled=readTabsSnapshot(taskId)?.tabs.some((tab)=>tab.id===tabId&&tab.kind==="engine");if(tabId!=="tab-1"&&!labelled&&!sessionArgvNamesEngine(s.command))return!1;let executable=basename(root.args.split(/\s+/)[0]).toLowerCase().replace(/^-/,"").replace(/\.exe$/,"");if(["sh","bash","zsh","fish","dash","ash","ksh","mksh"].includes(executable))return/^\S+\s+-[a-zA-Z]*c\b/.test(root.args);return!0}).length}async function openAdmittedEngine(rpc,spec,opts){let limit=opts.maxLiveEngines;if(limit===void 0)return rpc.request("pty.open",spec);for(let attempt=0;attempt<20;attempt++){let{sessions,admissionToken}=await rpc.request("pty.list",{});if(typeof admissionToken!=="string")throw new ApiError("the PTY host must be updated before enforcing engine.maxLiveSessions","ENGINE_ADMISSION_UNAVAILABLE",{taskId:opts.taskId,hint:"the running PTY host predates atomic admission; update and restart the PTY host before retrying",nextCommandArgs:opts.retryArgs});let live=await countLiveEngineSessions(sessions,opts.snapshot);if(!sessions.some((s)=>s.key===spec.key&&s.alive)&&live>=limit)throw new ApiError(`${live} engine sessions are already running (engine.maxLiveSessions is ${limit})`,"ENGINE_LIMIT",{taskId:opts.taskId,live,limit,hint:"wait for an engine to finish or close a tab, then retry; raise engine.maxLiveSessions in state.json to allow more",nextCommandArgs:opts.retryArgs});let result=await rpc.request("pty.openChecked",{...spec,admissionToken});if(result.kind==="opened")return result.open}throw new ApiError("PTY inventory kept changing during engine admission; retry the send","ENGINE_ADMISSION_BUSY",{taskId:opts.taskId,nextCommandArgs:opts.retryArgs})}function restoredTabsOf(sessions,taskId,exceptKey){let prefix=`${taskId}::`,ids=[];for(let s of sessions){if(s.alive||s.restored!==!0||s.key===exceptKey||!s.key.startsWith(prefix))continue;let tab=s.key.slice(prefix.length);if(tab.includes("::"))continue;if(!ids.includes(tab))ids.push(tab)}let tabs=readTabsSnapshot(taskId)?.tabs??[];return ids.map((tab)=>{let sessionId=tabs.find((t)=>t.id===tab)?.sessionId;return sessionId?{tab,sessionId}:{tab}})}function restoredTabLaunch(task,tabId,worktreePath,shell){let tab=readTabsSnapshot(task.id)?.tabs.find((t)=>t.id===tabId&&t.kind==="engine");if(!tab)return null;let base=engineLaunchArgv({command:tab.engineCommand??(tab.vendor?void 0:task.command),vendor:tab.vendor??task.vendor,effort:task.modelEffort,model:tab.engineCommand||tab.vendor?void 0:task.model});return buildEngineSessionLaunch({task:{id:task.id,kind:task.kind??"task",vendor:tab.vendor??task.vendor,repo:task.repo},worktreePath,shell,argv:engineTabArgv(tab,base,!1,task.vendor),promptIntent:{kind:"none"},tabId})}var isTaskKey=isHostedTaskKey,findEngineKey=findHostedEngineKey,taskKeys=hostedTaskKeys,openPtyHost=openHostedSessionHost,ensurePtyHost=ensureHostedSessionHost,listSessions=listHostedSessions,listSessionsOrNull=listHostedSessionsOrNull,deliverToKey=deliverToHostedKey,ENGINE_START_PROBE_MS=3000,ENGINE_START_POLL_MS=150,ENGINE_NOT_OBSERVED_REASON=`no engine process appeared in the session within ${ENGINE_START_PROBE_MS}ms`;function outcomeFields(outcome){if(!outcome)return{engineReady:!1,delivered:!1};return{engineReady:outcome.ready,delivered:!0,bytes:outcome.bytes,promptEcho:outcome.confirmed?"confirmed":"unconfirmed"}}async function deliverHostedPrompt(rpc,target,cwd,prompt,launch,opts){let{sessions=[]}=await rpc.request("pty.list",{}),existingKey=opts?.forceNew?null:findEngineKey(sessions,target.id,target.engineBin);if(existingKey){let pid=sessions.find((s)=>s.key===existingKey)?.pid,presence=await enginePresence(pid,target.engineBin,opts?.snapshot);if(presence.kind==="unknown")throw new ApiError(`could not read the process table, so task ${target.id}'s engine tab (${existingKey}) could not be checked for a live engine`,"ENGINE_PROBE_FAILED",{hint:"the `ps` probe failed or timed out; retry, or check the machine's process table",nextCommandArgs:["api","pty-list"]});if(presence.kind!=="engine")throw new ApiError(`task ${target.id}'s engine tab (${existingKey}) has no live engine process \u2014 its engine exited into a plain shell`,"ENGINE_NOT_RUNNING",{hint:"spawn a fresh engine tab for this prompt with --tab new",nextCommandArgs:["api","send","--task-id",target.id,"--tab","new","--plain","--prompt",prompt]});let outcome=await deliverToKey(rpc,existingKey,prompt,{vendor:presence.vendor});return{session:existingKey,pane:existingKey,started:!1,...outcomeFields(outcome)}}if(!opts?.forceNew){let aliveTabs=sessions.filter((s)=>s.alive&&isTaskKey(s.key,target.id)).map((s)=>s.key);if(aliveTabs.length>0)throw new ApiError(`task ${target.id} has live tabs (${aliveTabs.join(", ")}) but none resolves as its engine tab \u2014 refusing to spawn a new engine`,"NO_ENGINE_TAB",{hint:"address a live engine tab explicitly with --tab <tab-N> (see pty-list), or spawn a fresh engine tab with --tab new",nextCommandArgs:["api","pty-list"]})}let frozen=restoredTabsOf(sessions,target.id,launch.key),disclose=frozen.length>0?{frozenTabs:frozen}:{},staleCanonical=sessions.find((session)=>session.key===launch.key&&!session.alive);if(staleCanonical&&staleCanonical.restored!==!0)await rpc.request("pty.kill",{key:launch.key});let open=await openAdmittedEngine(rpc,{key:launch.key,cwd,command:launch.command,defaultColors:readPersistedTerminalDefaultColors()},{maxLiveEngines:opts?.maxLiveEngines,snapshot:opts?.snapshot,taskId:target.id,retryArgs:opts?.retryArgs??["api","send","--task-id",target.id,...opts?.forceNew?["--tab","new"]:[],"--plain","--prompt",prompt]});try{if(!open.alive)return{session:launch.key,pane:launch.key,started:open.created!==!1||open.respawned===!0,engineReady:!1,delivered:!1,...disclose};if(launch.firstMessage){let outcome=await pastePromptWhenEngineUp(rpc,launch.key,target.engineBin,launch.firstMessage,{initMarkerPath:launch.initMarkerPath,initTimeoutMs:launch.initTimeoutMs});return{session:launch.key,pane:launch.key,started:open.created!==!1||open.respawned===!0,...outcomeFields(outcome),...disclose}}let started=open.created!==!1||open.respawned===!0;if(open.created===!1&&open.respawned!==!0){let outcome=await pastePromptWhenEngineUp(rpc,launch.key,target.engineBin,prompt,{snapshot:opts?.snapshot});return{session:launch.key,pane:launch.key,started,...outcomeFields(outcome),...disclose}}let pendingInit={session:launch.key,pane:launch.key,started,engineReady:!1,delivered:!0,reason:"repo init script is still running; the engine has not started yet",...disclose};if(launch.initMarkerPath&&!initMarkerSaysFinished(launch.initMarkerPath))return pendingInit;if(await awaitEngineProcess(rpc,launch.key,target.engineBin,{timeoutMs:ENGINE_START_PROBE_MS,intervalMs:ENGINE_START_POLL_MS,snapshot:opts?.snapshot})===null){if(launch.initMarkerPath&&!initMarkerSaysFinished(launch.initMarkerPath)&&(await listSessions(rpc)).some((session)=>session.key===launch.key&&session.alive)&&!initMarkerSaysFinished(launch.initMarkerPath))return pendingInit;return{session:launch.key,pane:launch.key,started,engineReady:!1,delivered:!1,reason:await hostedSessionFailureLine(rpc,launch.key)??ENGINE_NOT_OBSERVED_REASON,...disclose}}return{session:launch.key,pane:launch.key,started,engineReady:!0,delivered:!0,...disclose}}finally{await rpc.request("pty.detach",{key:launch.key}).catch(()=>{})}}var killTaskSessions=killHostedSessions;
2
+ import{closeTab,engineTabArgv,initialTabs,setTabTitle,tabPtyKeyFor,terminalTabsKey}from"./chunk-hkyn3kme.js";import{awaitEngineProcess,buildEngineSessionLaunch,deliverToHostedKey,enginePresence,ensureHostedSessionHost,findHostedEngineKey,hostedSessionFailureLine,hostedTaskKeys,initMarkerSaysFinished,isHostedTaskKey,killHostedSessions,listHostedSessions,listHostedSessionsOrNull,openHostedSessionHost,pastePromptWhenEngineUp,readPersistedTerminalDefaultColors,sessionArgvNamesEngine}from"./chunk-mvsvmfsf.js";import{engineLaunchArgv}from"./chunk-rf4z7b6g.js";import{engineProcessIn,parsePsSnapshot,psSnapshot,vendorFromArgv}from"./chunk-j7831wcj.js";import{ApiError}from"./chunk-x7r75e3p.js";import{loadStateFile,patchStateFile,updateStateFile}from"./chunk-axxf5dkq.js";import{basename}from"path";function wireNode(node){if(node.kind==="leaf")return{kind:"leaf",id:node.id,title:node.title??null,own:node.content===null};return{kind:"group",orientation:node.orientation,children:node.children.map(wireNode)}}function wireSplit(tree){return tree?{root:wireNode(tree.root),activeLeafId:tree.activeLeafId}:null}function taskEngineArgv(task){return engineLaunchArgv({command:task.command,vendor:task.vendor})}function readTabsSnapshot(taskId){try{let snap=loadStateFile()[terminalTabsKey(taskId)];return snap&&Array.isArray(snap.tabs)?snap:void 0}catch{return}}function closeTabsSnapshot(taskId,tabId){let closing,key=terminalTabsKey(taskId);return updateStateFile((store)=>{let state=store[key];if(!state||!Array.isArray(state.tabs))return!1;if(closing=state.tabs.find((tab)=>tab.id===tabId),!closing)return!1;let{state:next,closedId}=closeTab(state,tabId,{allowEmpty:!0});if(!closedId)return closing=void 0,!1;store[key]=next;return}),closing}function renameTabsSnapshot(taskId,tabId,title){let found=!1,key=terminalTabsKey(taskId);return updateStateFile((store)=>{let state=store[key];if(!state||!Array.isArray(state.tabs))return!1;if(!state.tabs.some((tab)=>tab.id===tabId))return!1;found=!0;let next=setTabTitle(state,tabId,title);if(next===state)return!1;store[key]=next;return}),found}var aliveKeysOf=(sessions)=>new Set(sessions.filter((s)=>s.alive).map((s)=>s.key));function unregisteredTabIds(snapshot,taskId,sessions){let known=new Set((snapshot?.tabs??[]).map((t)=>t.id)),prefix=`${taskId}::`,out=[];for(let s of sessions){if(!s.alive||!s.key.startsWith(prefix))continue;let tabId=s.key.slice(prefix.length);if(tabId.includes("::"))continue;if(!known.has(tabId)&&!out.includes(tabId))out.push(tabId)}return out}var abnormalExit=(exit)=>exit&&(exit.code!==0||exit.signal!==null)?exit:null;function joinTaskTabs(snapshot,taskId,sessions,persistedExits={},liveVendors,engineAlive){let unknown=sessions===null,rows0=sessions??[],alive=aliveKeysOf(rows0),sessionExits=new Map(rows0.map((s)=>[s.key,s.exit])),deadExit=(key)=>{let ex=abnormalExit(sessionExits.get(key)??persistedExits[key]);if(!ex)return null;let record=persistedExits[key],sameDeath=record?.at===ex.at,tail=sameDeath?record?.tail:void 0,code=ex.code??(sameDeath?record?.code??null:null),layer=record?.layer??"pty";return{code,signal:ex.signal,at:ex.at,layer,...tail&&tail.length>0?{tail}:{}}},engineExit=(key)=>{let record=persistedExits[`${key}#engine`];if(!record)return null;let{code,signal,at,tail,atApproximate}=record;return{code,signal,at,layer:"engine",...atApproximate?{atApproximate}:{},...tail&&tail.length>0?{tail}:{}}},rows=(snapshot?.tabs??[]).map((t)=>{let key=tabPtyKeyFor(taskId,t),isAlive=unknown?null:alive.has(key),walked=isAlive===!0&&liveVendors?.has(key)===!0?liveVendors.get(key)??null:void 0,engineIsAlive=engineAliveOf(key,isAlive,engineAlive);return{id:t.id,kind:t.kind,title:t.title??null,vendor:t.vendor??null,liveVendor:walked!==void 0?walked:t.liveVendor??null,lastTitle:t.lastTitle??null,autoTitle:t.autoTitle??null,alive:isAlive,engineAlive:engineIsAlive,exit:isAlive===!1?deadExit(key):engineIsAlive===!1?engineExit(key):null,...t.splitTree?{split:wireSplit(t.splitTree)}:{},...t.kind==="engine"&&t.ptyTask?{viewOf:t.ptyTask.id}:{}}});for(let tabId of unregisteredTabIds(snapshot,taskId,rows0))rows.push({id:tabId,kind:"engine",title:null,vendor:null,liveVendor:null,lastTitle:null,autoTitle:null,alive:!0,engineAlive:engineAliveOf(`${taskId}::${tabId}`,!0,engineAlive),exit:null,unregistered:!0});return rows}function engineAliveOf(key,isAlive,engineAlive){if(isAlive===null)return null;if(!isAlive)return!1;return engineAlive?.has(key)===!0?engineAlive.get(key)??null:null}function publishCliTabSnapshot(taskId,sessionId){if(!taskId)return;try{let key=terminalTabsKey(taskId);if(loadStateFile()[key]!==void 0)return;let seeded=initialTabs();patchStateFile({[key]:sessionId?{...seeded,tabs:seeded.tabs.map((t)=>t.kind==="engine"?{...t,sessionId,spawned:!0}:t)}:seeded})}catch{}}function markCliTabSession(taskId,tabId,sessionId){try{let key=terminalTabsKey(taskId);updateStateFile((store)=>{let existing=store[key];if(!existing||!Array.isArray(existing.tabs))return!1;store[key]={...existing,tabs:existing.tabs.map((t)=>t.id===tabId&&t.kind==="engine"?{...t,...sessionId?{sessionId}:{},spawned:!0}:t)};return})}catch{}}function allocateTab(taskId,build,taken=new Set){let key=terminalTabsKey(taskId),out={};if(updateStateFile((store)=>{let existing=store[key],state=existing&&Array.isArray(existing.tabs)&&existing.tabs.length>0?existing:initialTabs(),ordinal=typeof state.nextOrdinal==="number"&&state.nextOrdinal>1?state.nextOrdinal:2;while(taken.has(`tab-${ordinal}`)||state.tabs.some((t)=>t.id===`tab-${ordinal}`))ordinal++;let tab=build(`tab-${ordinal}`,ordinal);out.tab=tab,store[key]={...state,tabs:[...state.tabs,tab],activeId:tab.id,nextOrdinal:ordinal+1};return}),!out.tab)throw Error(`tab allocation for ${taskId} did not run`);return out.tab}function mintCliTab(taskId,vendor,command){try{return allocateTab(taskId,(id,ordinal)=>({kind:"engine",id,title:null,ordinal,...vendor?{vendor}:{},...command?{engineCommand:command}:{}})).id}catch{return`tab-cli-${Date.now().toString(36)}`}}async function countLiveEngineSessions(sessions,snapshot=psSnapshot){let tabs=sessions.filter((s)=>s.alive&&s.key.split("::").length===2);if(!tabs.length)return 0;let rows;try{if(rows=parsePsSnapshot(await snapshot(tabs.flatMap((s)=>s.pid?[s.pid]:[]))),!rows.length)return tabs.length}catch{return tabs.length}return tabs.filter((s)=>{if(!s.pid)return!0;let root=rows.find((row)=>row.pid===s.pid);if(!root)return!0;if(engineProcessIn(rows,s.pid)||vendorFromArgv(root.args))return!0;let[taskId,tabId]=s.key.split("::"),labelled=readTabsSnapshot(taskId)?.tabs.some((tab)=>tab.id===tabId&&tab.kind==="engine");if(tabId!=="tab-1"&&!labelled&&!sessionArgvNamesEngine(s.command))return!1;let executable=basename(root.args.split(/\s+/)[0]).toLowerCase().replace(/^-/,"").replace(/\.exe$/,"");if(["sh","bash","zsh","fish","dash","ash","ksh","mksh"].includes(executable))return/^\S+\s+-[a-zA-Z]*c\b/.test(root.args);return!0}).length}async function openAdmittedEngine(rpc,spec,opts){let limit=opts.maxLiveEngines;if(limit===void 0)return rpc.request("pty.open",spec);for(let attempt=0;attempt<20;attempt++){let{sessions,admissionToken}=await rpc.request("pty.list",{});if(typeof admissionToken!=="string")throw new ApiError("the PTY host must be updated before enforcing engine.maxLiveSessions","ENGINE_ADMISSION_UNAVAILABLE",{taskId:opts.taskId,hint:"the running PTY host predates atomic admission; update and restart the PTY host before retrying",nextCommandArgs:opts.retryArgs});let live=await countLiveEngineSessions(sessions,opts.snapshot);if(!sessions.some((s)=>s.key===spec.key&&s.alive)&&live>=limit)throw new ApiError(`${live} engine sessions are already running (engine.maxLiveSessions is ${limit})`,"ENGINE_LIMIT",{taskId:opts.taskId,live,limit,hint:"wait for an engine to finish or close a tab, then retry; raise engine.maxLiveSessions in state.json to allow more",nextCommandArgs:opts.retryArgs});let result=await rpc.request("pty.openChecked",{...spec,admissionToken});if(result.kind==="opened")return result.open}throw new ApiError("PTY inventory kept changing during engine admission; retry the send","ENGINE_ADMISSION_BUSY",{taskId:opts.taskId,nextCommandArgs:opts.retryArgs})}function restoredTabsOf(sessions,taskId,exceptKey){let prefix=`${taskId}::`,ids=[];for(let s of sessions){if(s.alive||s.restored!==!0||s.key===exceptKey||!s.key.startsWith(prefix))continue;let tab=s.key.slice(prefix.length);if(tab.includes("::"))continue;if(!ids.includes(tab))ids.push(tab)}let tabs=readTabsSnapshot(taskId)?.tabs??[];return ids.map((tab)=>{let sessionId=tabs.find((t)=>t.id===tab)?.sessionId;return sessionId?{tab,sessionId}:{tab}})}function restoredTabLaunch(task,tabId,worktreePath,shell){let tab=readTabsSnapshot(task.id)?.tabs.find((t)=>t.id===tabId&&t.kind==="engine");if(!tab)return null;let base=engineLaunchArgv({command:tab.engineCommand??(tab.vendor?void 0:task.command),vendor:tab.vendor??task.vendor,effort:task.modelEffort,model:tab.engineCommand||tab.vendor?void 0:task.model});return buildEngineSessionLaunch({task:{id:task.id,kind:task.kind??"task",vendor:tab.vendor??task.vendor,repo:task.repo},worktreePath,shell,argv:engineTabArgv(tab,base,!1,task.vendor),promptIntent:{kind:"none"},tabId})}var isTaskKey=isHostedTaskKey,findEngineKey=findHostedEngineKey,taskKeys=hostedTaskKeys,openPtyHost=openHostedSessionHost,ensurePtyHost=ensureHostedSessionHost,listSessions=listHostedSessions,listSessionsOrNull=listHostedSessionsOrNull,deliverToKey=deliverToHostedKey,ENGINE_START_PROBE_MS=3000,ENGINE_START_POLL_MS=150,ENGINE_NOT_OBSERVED_REASON=`no engine process appeared in the session within ${ENGINE_START_PROBE_MS}ms`;function outcomeFields(outcome){if(!outcome)return{engineReady:!1,delivered:!1};return{engineReady:outcome.ready,delivered:!0,bytes:outcome.bytes,promptEcho:outcome.confirmed?"confirmed":"unconfirmed"}}async function deliverHostedPrompt(rpc,target,cwd,prompt,launch,opts){let{sessions=[]}=await rpc.request("pty.list",{}),existingKey=opts?.forceNew?null:findEngineKey(sessions,target.id,target.engineBin);if(existingKey){let pid=sessions.find((s)=>s.key===existingKey)?.pid,presence=await enginePresence(pid,target.engineBin,opts?.snapshot);if(presence.kind==="unknown")throw new ApiError(`could not read the process table, so task ${target.id}'s engine tab (${existingKey}) could not be checked for a live engine`,"ENGINE_PROBE_FAILED",{hint:"the `ps` probe failed or timed out; retry, or check the machine's process table",nextCommandArgs:["api","pty-list"]});if(presence.kind!=="engine")throw new ApiError(`task ${target.id}'s engine tab (${existingKey}) has no live engine process \u2014 its engine exited into a plain shell`,"ENGINE_NOT_RUNNING",{hint:"spawn a fresh engine tab for this prompt with --tab new",nextCommandArgs:["api","send","--task-id",target.id,"--tab","new","--plain","--prompt",prompt]});let outcome=await deliverToKey(rpc,existingKey,prompt,{vendor:presence.vendor});return{session:existingKey,pane:existingKey,started:!1,...outcomeFields(outcome)}}if(!opts?.forceNew){let aliveTabs=sessions.filter((s)=>s.alive&&isTaskKey(s.key,target.id)).map((s)=>s.key);if(aliveTabs.length>0)throw new ApiError(`task ${target.id} has live tabs (${aliveTabs.join(", ")}) but none resolves as its engine tab \u2014 refusing to spawn a new engine`,"NO_ENGINE_TAB",{hint:"address a live engine tab explicitly with --tab <tab-N> (see pty-list), or spawn a fresh engine tab with --tab new",nextCommandArgs:["api","pty-list"]})}let frozen=restoredTabsOf(sessions,target.id,launch.key),disclose=frozen.length>0?{frozenTabs:frozen}:{},staleCanonical=sessions.find((session)=>session.key===launch.key&&!session.alive);if(staleCanonical&&staleCanonical.restored!==!0)await rpc.request("pty.kill",{key:launch.key});let open=await openAdmittedEngine(rpc,{key:launch.key,cwd,command:launch.command,defaultColors:readPersistedTerminalDefaultColors()},{maxLiveEngines:opts?.maxLiveEngines,snapshot:opts?.snapshot,taskId:target.id,retryArgs:opts?.retryArgs??["api","send","--task-id",target.id,...opts?.forceNew?["--tab","new"]:[],"--plain","--prompt",prompt]});try{if(!open.alive)return{session:launch.key,pane:launch.key,started:open.created!==!1||open.respawned===!0,engineReady:!1,delivered:!1,...disclose};if(launch.firstMessage){let outcome=await pastePromptWhenEngineUp(rpc,launch.key,target.engineBin,launch.firstMessage,{initMarkerPath:launch.initMarkerPath,initTimeoutMs:launch.initTimeoutMs});return{session:launch.key,pane:launch.key,started:open.created!==!1||open.respawned===!0,...outcomeFields(outcome),...disclose}}let started=open.created!==!1||open.respawned===!0;if(open.created===!1&&open.respawned!==!0){let outcome=await pastePromptWhenEngineUp(rpc,launch.key,target.engineBin,prompt,{snapshot:opts?.snapshot});return{session:launch.key,pane:launch.key,started,...outcomeFields(outcome),...disclose}}let pendingInit={session:launch.key,pane:launch.key,started,engineReady:!1,delivered:!0,reason:"repo init script is still running; the engine has not started yet",...disclose};if(launch.initMarkerPath&&!initMarkerSaysFinished(launch.initMarkerPath))return pendingInit;if(await awaitEngineProcess(rpc,launch.key,target.engineBin,{timeoutMs:ENGINE_START_PROBE_MS,intervalMs:ENGINE_START_POLL_MS,snapshot:opts?.snapshot})===null){if(launch.initMarkerPath&&!initMarkerSaysFinished(launch.initMarkerPath)&&(await listSessions(rpc)).some((session)=>session.key===launch.key&&session.alive)&&!initMarkerSaysFinished(launch.initMarkerPath))return pendingInit;return{session:launch.key,pane:launch.key,started,engineReady:!1,delivered:!1,reason:await hostedSessionFailureLine(rpc,launch.key)??ENGINE_NOT_OBSERVED_REASON,...disclose}}return{session:launch.key,pane:launch.key,started,engineReady:!0,delivered:!0,...disclose}}finally{await rpc.request("pty.detach",{key:launch.key}).catch(()=>{})}}var killTaskSessions=killHostedSessions;
3
3
  export{wireSplit,taskEngineArgv,readTabsSnapshot,closeTabsSnapshot,renameTabsSnapshot,unregisteredTabIds,joinTaskTabs,publishCliTabSnapshot,markCliTabSession,allocateTab,mintCliTab,openAdmittedEngine,restoredTabLaunch,isTaskKey,findEngineKey,taskKeys,openPtyHost,ensurePtyHost,listSessions,listSessionsOrNull,deliverToKey,ENGINE_START_PROBE_MS,ENGINE_START_POLL_MS,ENGINE_NOT_OBSERVED_REASON,outcomeFields,deliverHostedPrompt,killTaskSessions};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{formatBytes}from"./chunk-my20q579.js";import{hookConfigIssues}from"./chunk-hzzq6pdb.js";import{inspectLegacyTmux,legacyTmuxDoctorLines}from"./chunk-gkdfct7t.js";import"./chunk-gqpka00s.js";import"./chunk-r3s2t9af.js";import"./chunk-a4hsqj1b.js";import"./chunk-dqprpnd6.js";import"./chunk-k67vbszb.js";import{applyFixes,daemonRestartFix,defaultFixRuntime,engineTabsManualFix,humanOnlyFix,killOrphansManualFix,noEngineFix,reinstallManualFix,resetManualFix,skillInstallFix,spawnHelperFix}from"./chunk-80zzjcvp.js";import{describeAccount,detectEngineStatuses,probeableEngineIds,summarizeEngines}from"./chunk-4mxn5jg6.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-atn06ncf.js";import{resolveNodeBinary}from"./chunk-rf4z7b6g.js";import{formatDaemonInfo}from"./chunk-rxqdw6s5.js";import"./chunk-vjvkkrzc.js";import"./chunk-j7831wcj.js";import"./chunk-tewt7vre.js";import"./chunk-2tfnscxk.js";import"./chunk-9g0vz9dk.js";import"./chunk-w7zv47mp.js";import"./chunk-k3wzznqw.js";import"./chunk-etfh8f4z.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-zsfyj36g.js";import{isStaleInstallError,resolveRoveSpawn}from"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import"./chunk-2brn8e1y.js";import"./chunk-pahcf9td.js";import"./chunk-k7ke95vy.js";import{readableLegacyIndexPath}from"./chunk-avz72132.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import{CURRENT_VERSION,package_default}from"./chunk-m0cbndc2.js";import{roveSkillState,skillInstallCommand}from"./chunk-hqp8b49k.js";import"./chunk-xkr7fwvb.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import{readPidFile}from"./chunk-b31cte10.js";import{homeDir,kvStatePath,roveStateDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import{t}from"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient,isForeignDaemonHome}from"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import"./chunk-pn5ygqn6.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath,defaultPtyHostLogPath,defaultPtyHostPidPath,defaultPtyHostSocketPath}from"./chunk-z7zm8h1q.js";import{preRenameStateDir}from"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";import{existsSync,readFileSync as readFileSync2,statSync as statSync2}from"fs";import{join as join2}from"path";var MIN_BUN_VERSION=package_default.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0";function parseBunVersion(raw){return raw.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function isBunAtLeast(raw,minimum=MIN_BUN_VERSION){let found=parseBunVersion(raw),floor=parseBunVersion(minimum);if(!found||!floor)return!0;let a=found.split(".").map(Number),b=floor.split(".").map(Number);for(let i=0;i<3;i++){let av=a[i]??0,bv=b[i]??0;if(av!==bv)return av>bv}return!0}function classifyHookChannel(input){let total=0,hooked=0;for(let tabs of Object.values(input.tabs))for(let entry of Object.values(tabs))if(total++,entry.source==="hook")hooked++;if(total===0)return{kind:"no-tabs"};if(hooked===0)return{kind:"down",totalTabs:total};return{kind:"live",hookTabs:hooked,totalTabs:total}}function hookChannelDoctorLines(verdict,input,cliName){let configLines=(input.configIssues??[]).flatMap((issue)=>[` \u26A0 hook install skipped: ${issue.file}`,` ${issue.reason} \u2014 fix the file, then relaunch Rove`]);if(verdict.kind==="no-tabs")return["hooks: \u2014 no engine tabs yet (nothing to check)",...configLines];if(verdict.kind==="live")return[`hooks: \u2713 engine hook channel live (${verdict.hookTabs}/${verdict.totalTabs} tab(s) hook-sourced)`,...configLines];let out=[`hooks: \u2717 NO hook events reaching the daemon (0/${verdict.totalTabs} tab(s) hook-sourced)`," badges fall back to a ~10s poll, so activity looks seconds late",` daemon socket: ${input.socketPath}`,...configLines];return out.push(' \u2192 compare with an engine tab\'s own path: `ps eww -p <engine-pid> | tr " " "\\n" | grep DAEMON_SOCKET_PATH`',` \u2192 restart the engine tabs (they may hold a stale socket path), or run \`${cliName} daemon restart\``,` \u2192 debug one hook directly: \`ROVE_HOOK_DEBUG=1 echo '{}' | ${cliName} hook turn-start --engine claude\``),out}import{readdirSync,statSync}from"fs";import{createRequire}from"module";import{dirname,join}from"path";function installedSpawnHelpers(){let pkg;try{pkg=dirname(createRequire(import.meta.url).resolve("node-pty/package.json"))}catch{return[]}let prebuilds=join(pkg,"prebuilds"),arches;try{arches=readdirSync(prebuilds).filter((name)=>name.startsWith("darwin-"))}catch{return[]}let helpers=[];for(let arch of arches){let path=join(prebuilds,arch,"spawn-helper");try{helpers.push({path,executable:(statSync(path).mode&64)!==0})}catch{}}return helpers}function spawnHelperDoctorLines(helpers){let broken=helpers.filter((helper)=>!helper.executable).map((helper)=>helper.path);if(helpers.length===0)return{lines:["node-pty: ? no darwin spawn-helper found next to node-pty"],broken};if(broken.length===0)return{lines:[`node-pty: \u2713 spawn-helper executable (${helpers.length} arch)`],broken};return{lines:["node-pty: \u2717 spawn-helper is not executable \u2014 every node-pty PTY spawn fails",...broken.map((path)=>` ${path}`),` \u2192 chmod 755 ${broken.join(" ")}`],broken}}import{appendFileSync,readFileSync}from"fs";var PTY_MARKER="ROVE_TERMINAL_PTY=1",run=async(argv)=>{try{let proc=Bun.spawn([...argv],{stdin:"ignore",stdout:"pipe",stderr:"ignore"}),[stdout,code]=await Promise.all([new Response(proc.stdout).text().catch(()=>""),proc.exited]);return{code,stdout}}catch{return{code:127,stdout:""}}};function parsePsRows(output){let rows=[];for(let line of output.split(`
2
+ import{formatBytes}from"./chunk-my20q579.js";import{hookConfigIssues}from"./chunk-hzzq6pdb.js";import{inspectLegacyTmux,legacyTmuxDoctorLines}from"./chunk-gkdfct7t.js";import"./chunk-rzd7xpx2.js";import"./chunk-r3s2t9af.js";import"./chunk-a4hsqj1b.js";import"./chunk-dqprpnd6.js";import"./chunk-k67vbszb.js";import{applyFixes,daemonRestartFix,defaultFixRuntime,engineTabsManualFix,humanOnlyFix,killOrphansManualFix,noEngineFix,reinstallManualFix,resetManualFix,skillInstallFix,spawnHelperFix}from"./chunk-80zzjcvp.js";import{describeAccount,detectEngineStatuses,probeableEngineIds,summarizeEngines}from"./chunk-4mxn5jg6.js";import"./chunk-4hestsjp.js";import"./chunk-41x1hrqe.js";import"./chunk-5mhvjytn.js";import"./chunk-stcvcsfm.js";import"./chunk-2bgmby1d.js";import"./chunk-edsm15ht.js";import"./chunk-atn06ncf.js";import{resolveNodeBinary}from"./chunk-rf4z7b6g.js";import{formatDaemonInfo}from"./chunk-rxqdw6s5.js";import"./chunk-f5n0tgky.js";import"./chunk-j7831wcj.js";import"./chunk-tewt7vre.js";import"./chunk-2tfnscxk.js";import"./chunk-9g0vz9dk.js";import"./chunk-w7zv47mp.js";import"./chunk-k3wzznqw.js";import"./chunk-etfh8f4z.js";import"./chunk-dmzg9zdc.js";import"./chunk-kg9kmwt9.js";import"./chunk-zsfyj36g.js";import{isStaleInstallError,resolveRoveSpawn}from"./chunk-ant4k6dm.js";import"./chunk-1e6jbj70.js";import"./chunk-y330t2bt.js";import"./chunk-2brn8e1y.js";import"./chunk-pahcf9td.js";import"./chunk-k7ke95vy.js";import{readableLegacyIndexPath}from"./chunk-avz72132.js";import"./chunk-xy8be792.js";import"./chunk-7kysxgbq.js";import"./chunk-h2m7em1e.js";import"./chunk-7ghwdjdc.js";import"./chunk-tdhk4dz8.js";import{CURRENT_VERSION,package_default}from"./chunk-c9xg9dn0.js";import{roveSkillState,skillInstallCommand}from"./chunk-hqp8b49k.js";import"./chunk-xkr7fwvb.js";import"./chunk-40dgv828.js";import"./chunk-3342qzve.js";import"./chunk-axxf5dkq.js";import"./chunk-qk5f6p3e.js";import{readPidFile}from"./chunk-b31cte10.js";import{homeDir,kvStatePath,roveStateDir}from"./chunk-prqeddy7.js";import{ROVE_PRODUCT_NAME}from"./chunk-zt7wfnva.js";import"./chunk-yt15y085.js";import{t}from"./chunk-tq6pq1ws.js";import"./chunk-sma961wh.js";import"./chunk-cmp64q4j.js";import{RoveDaemonClient,isForeignDaemonHome}from"./chunk-fnsch8kr.js";import"./chunk-278sc87n.js";import"./chunk-y55zpren.js";import"./chunk-pn5ygqn6.js";import{defaultDaemonLogPath,defaultDaemonPidPath,defaultDaemonSocketPath,defaultPtyHostLogPath,defaultPtyHostPidPath,defaultPtyHostSocketPath}from"./chunk-z7zm8h1q.js";import{preRenameStateDir}from"./chunk-b39sdtt0.js";import"./chunk-nc46gddg.js";import"./chunk-pxk2c2vj.js";import{__require}from"./chunk-91sykzqq.js";import{existsSync,readFileSync as readFileSync2,statSync as statSync2}from"fs";import{join as join2}from"path";var MIN_BUN_VERSION=package_default.engines.bun.match(/\d+\.\d+\.\d+/)?.[0]??"0.0.0";function parseBunVersion(raw){return raw.trim().match(/^v?(\d+\.\d+\.\d+)/)?.[1]??null}function isBunAtLeast(raw,minimum=MIN_BUN_VERSION){let found=parseBunVersion(raw),floor=parseBunVersion(minimum);if(!found||!floor)return!0;let a=found.split(".").map(Number),b=floor.split(".").map(Number);for(let i=0;i<3;i++){let av=a[i]??0,bv=b[i]??0;if(av!==bv)return av>bv}return!0}function classifyHookChannel(input){let total=0,hooked=0;for(let tabs of Object.values(input.tabs))for(let entry of Object.values(tabs))if(total++,entry.source==="hook")hooked++;if(total===0)return{kind:"no-tabs"};if(hooked===0)return{kind:"down",totalTabs:total};return{kind:"live",hookTabs:hooked,totalTabs:total}}function hookChannelDoctorLines(verdict,input,cliName){let configLines=(input.configIssues??[]).flatMap((issue)=>[` \u26A0 hook install skipped: ${issue.file}`,` ${issue.reason} \u2014 fix the file, then relaunch Rove`]);if(verdict.kind==="no-tabs")return["hooks: \u2014 no engine tabs yet (nothing to check)",...configLines];if(verdict.kind==="live")return[`hooks: \u2713 engine hook channel live (${verdict.hookTabs}/${verdict.totalTabs} tab(s) hook-sourced)`,...configLines];let out=[`hooks: \u2717 NO hook events reaching the daemon (0/${verdict.totalTabs} tab(s) hook-sourced)`," badges fall back to a ~10s poll, so activity looks seconds late",` daemon socket: ${input.socketPath}`,...configLines];return out.push(' \u2192 compare with an engine tab\'s own path: `ps eww -p <engine-pid> | tr " " "\\n" | grep DAEMON_SOCKET_PATH`',` \u2192 restart the engine tabs (they may hold a stale socket path), or run \`${cliName} daemon restart\``,` \u2192 debug one hook directly: \`ROVE_HOOK_DEBUG=1 echo '{}' | ${cliName} hook turn-start --engine claude\``),out}import{readdirSync,statSync}from"fs";import{createRequire}from"module";import{dirname,join}from"path";function installedSpawnHelpers(){let pkg;try{pkg=dirname(createRequire(import.meta.url).resolve("node-pty/package.json"))}catch{return[]}let prebuilds=join(pkg,"prebuilds"),arches;try{arches=readdirSync(prebuilds).filter((name)=>name.startsWith("darwin-"))}catch{return[]}let helpers=[];for(let arch of arches){let path=join(prebuilds,arch,"spawn-helper");try{helpers.push({path,executable:(statSync(path).mode&64)!==0})}catch{}}return helpers}function spawnHelperDoctorLines(helpers){let broken=helpers.filter((helper)=>!helper.executable).map((helper)=>helper.path);if(helpers.length===0)return{lines:["node-pty: ? no darwin spawn-helper found next to node-pty"],broken};if(broken.length===0)return{lines:[`node-pty: \u2713 spawn-helper executable (${helpers.length} arch)`],broken};return{lines:["node-pty: \u2717 spawn-helper is not executable \u2014 every node-pty PTY spawn fails",...broken.map((path)=>` ${path}`),` \u2192 chmod 755 ${broken.join(" ")}`],broken}}import{appendFileSync,readFileSync}from"fs";var PTY_MARKER="ROVE_TERMINAL_PTY=1",run=async(argv)=>{try{let proc=Bun.spawn([...argv],{stdin:"ignore",stdout:"pipe",stderr:"ignore"}),[stdout,code]=await Promise.all([new Response(proc.stdout).text().catch(()=>""),proc.exited]);return{code,stdout}}catch{return{code:127,stdout:""}}};function parsePsRows(output){let rows=[];for(let line of output.split(`
3
3
  `)){let parts=line.trim().split(/\s+/);if(parts.length<6)continue;let[pid,ppid,pgid]=[parts[0],parts[1],parts[2]].map((value)=>Number.parseInt(value??"",10)),rssKb=Number.parseInt(parts[4]??"",10);if(!Number.isFinite(pid)||!Number.isFinite(ppid)||!Number.isFinite(pgid)||!Number.isFinite(rssKb))continue;rows.push({pid,ppid,pgid,etime:parts[3]??"",rssKb,command:parts.slice(5).join(" ")})}return rows}function pidAlive(pid,rows){return rows.some((row)=>row.pid===pid)}function orphanCandidates(rows,selfPgid,liveSessionPids){return rows.filter((row)=>row.ppid===1&&row.pgid!==selfPgid&&!liveSessionPids.has(row.pgid)&&!pidAlive(row.pgid,rows))}async function markedPids(pids,deps={}){let runProbe=deps.run??run,readEnviron=deps.readEnviron??((pid)=>readFileSync(`/proc/${pid}/environ`,"utf8")),marked=new Set;if(pids.length===0)return{marked,failed:null};if((deps.platform??process.platform)==="linux"){let refused=null;for(let pid of pids)try{if(readEnviron(pid).split("\x00").includes(PTY_MARKER))marked.add(pid)}catch(err){let code=err.code;if(code!=="ENOENT"&&code!=="ESRCH")refused??=`/proc/${pid}/environ: ${code??"unreadable"}`}return{marked,failed:refused}}let result=await runProbe(["ps","eww","-o","pid=,command=","-p",pids.join(",")]);if(result.code!==0)return{marked,failed:`ps eww exited ${result.code}`};let marker=new RegExp(`(^|\\s)${PTY_MARKER}(\\s|$)`);for(let line of result.stdout.split(`
4
4
  `)){let pid=Number.parseInt(line.trim().split(/\s+/)[0]??"",10);if(Number.isFinite(pid)&&marker.test(line))marked.add(pid)}return{marked,failed:null}}async function ownPgid(runProbe){let result=await runProbe(["ps","-o","pgid=","-p",String(process.pid)]),pgid=Number.parseInt(result.stdout.trim(),10);return Number.isFinite(pgid)?pgid:-1}async function collectOrphans(liveSessionPids,deps={}){let runProbe=deps.run??run;if((deps.platform??process.platform)==="win32")return{orphans:[],error:null};let ps=await runProbe(["ps","-A","-o","pid=,ppid=,pgid=,etime=,rss=,command="]);if(ps.code!==0)return{orphans:[],error:`could not read the process table \u2014 ps exited ${ps.code}`};let rows=parsePsRows(ps.stdout),candidates=orphanCandidates(rows,await ownPgid(runProbe),liveSessionPids),{marked,failed}=await markedPids(candidates.map((row)=>row.pid),deps);if(failed)return{orphans:[],error:`could not read process environments \u2014 ${failed}`};return{orphans:candidates.filter((row)=>marked.has(row.pid)),error:null}}function formatMb(rssKb){return`${(rssKb/1024).toFixed(0)} MB`}function orphanDoctorLines(orphans,error,cliName,killing=!1){if(error)return[`orphans: \u2717 ${error}`];if(orphans.length===0)return["orphans: \u2713 none \u2014 no processes left behind by a dead PTY session"];let totalMb=orphans.reduce((sum,row)=>sum+row.rssKb,0)/1024,lines=[`orphans: \u26A0 ${orphans.length} process(es) outlived the PTY session that spawned them (${totalMb.toFixed(0)} MB RSS)`," each is reparented to init, carries Rove's PTY marker, and its process"," group leader is gone \u2014 no live task owns them"];for(let row of[...orphans].sort((a,b)=>b.rssKb-a.rssKb))lines.push(` pid ${row.pid} (group ${row.pgid}) up ${row.etime}, ${formatMb(row.rssKb)}: ${row.command.slice(0,90)}`);if(!killing)lines.push(` \u2192 \`${cliName} doctor --kill-orphans\` ends those process groups (SIGTERM, then SIGKILL)`," read the list first: something you backgrounded from a Rove terminal and then"," closed the tab on looks exactly like a leak, and doctor cannot tell them apart");return lines}var GROUP_EXIT_GRACE_MS=2000,GROUP_POLL_MS=100;function logOrphanKill(pgid,signal,logPath=defaultDaemonLogPath()){try{appendFileSync(logPath,formatDaemonInfo("doctor-kill-orphans",`${signal} process group ${pgid}`))}catch{}}function groupAlive(pgid){try{return process.kill(-pgid,0),!0}catch(err){return err.code!=="ESRCH"}}async function killOrphanGroups(orphans){let groups=[...new Set(orphans.map((row)=>row.pgid))];for(let pgid of groups)try{process.kill(-pgid,"SIGTERM"),logOrphanKill(pgid,"SIGTERM")}catch{}let deadline=Date.now()+GROUP_EXIT_GRACE_MS,survivors=groups.filter(groupAlive);while(survivors.length>0&&Date.now()<deadline)await new Promise((resolve)=>setTimeout(resolve,GROUP_POLL_MS)),survivors=survivors.filter(groupAlive);for(let pgid of survivors)try{process.kill(-pgid,"SIGKILL"),logOrphanKill(pgid,"SIGKILL")}catch{}return await new Promise((resolve)=>setTimeout(resolve,GROUP_POLL_MS)),{groups,survivors:survivors.filter(groupAlive)}}function multiplexerLabel(env){if(env.TMUX)return"tmux";if(env.ZELLIJ)return"zellij";if(env.STY)return"screen";return"no"}function terminalEnvLines(env){let show=(v)=>v&&v.length>0?v:"(unset)",program=env.TERM_PROGRAM?`${env.TERM_PROGRAM}${env.TERM_PROGRAM_VERSION?` v${env.TERM_PROGRAM_VERSION}`:""}`:"(unset)";return[`terminal: TERM=${show(env.TERM)} TERM_PROGRAM=${program} COLORTERM=${show(env.COLORTERM)}`,` running inside a multiplexer: ${multiplexerLabel(env)}`]}function parseKittyProbeReply(data){let kitty=data.match(/\x1b\[\?(\d+)u/);if(kitty?.[1]!==void 0)return{kind:"supported",flags:Number.parseInt(kitty[1],10)};if(/\x1b\[\?[\d;]*c/.test(data))return{kind:"unsupported"};return null}function kittyProbeLine(result){switch(result.kind){case"supported":return` kitty keyboard protocol: \u2713 answered (flags=${result.flags})`;case"unsupported":return[" kitty keyboard protocol: \u2717 not supported \u2014 legacy key path"," (ctrl+h/ctrl+j arrive as C0 backspace/linefeed bytes; the"," split chords ctrl+\\ and ctrl+= cannot be encoded at all)"].join(`
5
5
  `);case"no-response":return" kitty keyboard protocol: ? no reply (terminal ignored both the kitty query and DA1)";case"skipped":return` kitty keyboard protocol: skipped (${result.reason})`}}async function probeKittyKeyboard(timeoutMs=300){let stdin=process.stdin;if(!stdin.isTTY||!process.stdout.isTTY)return{kind:"skipped",reason:"not an interactive terminal"};let wasRaw=stdin.isRaw===!0,buffer="";return await new Promise((resolve)=>{let done=!1,finish=(result)=>{if(done)return;if(done=!0,clearTimeout(timer),stdin.off("data",onData),stdin.pause(),!wasRaw)stdin.setRawMode(!1);resolve(result)},onData=(chunk)=>{buffer+=chunk.toString("latin1");let decided=parseKittyProbeReply(buffer);if(decided)finish(decided)},timer=setTimeout(()=>finish({kind:"no-response"}),timeoutMs);stdin.setRawMode(!0),stdin.resume(),stdin.on("data",onData),process.stdout.write("\x1B[?u\x1B[c")})}async function terminalDoctorLines(){return[...terminalEnvLines(process.env),kittyProbeLine(await probeKittyKeyboard())]}async function probeGit(){try{let proc=Bun.spawn(["git","--version"],{stdin:"ignore",stdout:"pipe",stderr:"ignore"}),text=(await new Response(proc.stdout).text()).trim();if(await proc.exited===0&&text)return{line:`git: \u2713 ${text}`,found:!0}}catch{}return{line:"git: \u2717 not found on PATH",found:!1}}function binaryLabel(binary){return binary.found?`\u2713 ${binary.path}`:`\u2717 ${binary.error}`}async function probeEngines(){let statuses=await detectEngineStatuses(await probeableEngineIds()),lines=["engines:"];for(let status of statuses){let account=describeAccount(status.account),name=`${status.vendor.padEnd(7)} `;if(lines.push(` ${name}${binaryLabel(status.binary)}${status.binary.found?` \u2014 ${account}`:""}`),status.accountError)lines.push(` \u26A0 ${status.accountError}`)}let{usable,signedOut}=summarizeEngines(statuses);return{lines,anyUsable:usable.length>0,signedOut}}var CLI_NAME=ROVE_PRODUCT_NAME;function isProcessAlive(pid){try{return process.kill(pid,0),!0}catch(err){return err.code==="EPERM"}}async function requestIfReachable(socketPath,name){let client=new RoveDaemonClient(socketPath);try{return await client.request(name,{})}catch{return null}finally{client.close()}}function fmtDuration(ms){let seconds=Math.floor(ms/1000);if(seconds<60)return`${seconds}s`;let minutes=Math.floor(seconds/60);if(minutes<60)return`${minutes}m ${seconds%60}s`;return`${Math.floor(minutes/60)}h ${minutes%60}m`}function describeFile(path){try{let stat=statSync2(path);return`present (${formatBytes(stat.size)}, modified ${stat.mtime.toISOString()})`}catch{return"absent"}}function taskCount(path){try{let parsed=JSON.parse(readFileSync2(path,"utf8"));return Array.isArray(parsed.tasks)?parsed.tasks.length:null}catch{return null}}function tailFile(path,count){try{return readFileSync2(path,"utf8").split(`
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{buildEngineSessionLaunch,trustEngineWorktree}from"./chunk-ddc4t38t.js";import{engineForkArgv,engineResumeArgv,withPinnedSessionId}from"./chunk-rf4z7b6g.js";import{engineEntry,engineStatusPrefixes,isEnginePlaceholderTitle,stripEngineStatusPrefix}from"./chunk-tewt7vre.js";import{tildify}from"./chunk-ag3txfg2.js";import{t}from"./chunk-tq6pq1ws.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";function engineTabArgv(tab,base,live,fallbackVendor){if(tab.forkFrom&&!tab.spawned&&!live){let forked=tab.vendor?engineForkArgv(base,tab.vendor,tab.forkFrom,tab.sessionId??null):null;if(forked)return forked}if(!tab.sessionId)return base;let vendor=tab.vendor??fallbackVendor;if(tab.spawned&&!live)return engineResumeArgv(base,vendor,tab.sessionId)??base;return withPinnedSessionId(base,vendor,()=>tab.sessionId).argv}function engineTabSpawnFor(state,tab,base,opts){let{live,shell,prompt}=opts,firstEngine=state.tabs.find((t2)=>t2.kind==="engine"),fresh=!tab.spawned&&!live,tabPrompt=fresh?tab.initialPrompt?.trim():void 0,wantsPrompt=!!prompt&&tab.id===firstEngine?.id&&fresh,isFreshFirstEngine=tab.id===firstEngine?.id&&fresh,promptIntent=tabPrompt?{kind:"explicit",prompt:tabPrompt}:wantsPrompt?{kind:"new-task",prompt}:isFreshFirstEngine?{kind:"repo-init"}:{kind:"none"},ref=tab.ptyTask;trustEngineWorktree(tab.vendor??opts.task.vendor,ref?.worktree??opts.worktreePath);let launch=buildEngineSessionLaunch({task:ref?{...opts.task,id:ref.id,kind:"task"}:opts.task,worktreePath:ref?.worktree??opts.worktreePath,shell,argv:engineTabArgv(tab,base,live,opts.task.vendor),promptIntent,protocolGates:opts.protocolGates,inlineImages:opts.inlineImages,tabId:ref?"tab-1":tab.id});return{command:launch.command,...launch.firstMessage?{firstMessage:launch.firstMessage,engineBin:base[0]}:{}}}function tabExitAction(tab,deadOnAttach,resumeTried){if(tab.kind==="engine"&&deadOnAttach&&!!tab.sessionId&&tab.spawned&&!resumeTried)return"resume";return"close"}import*as fs from"fs";import*as os from"os";function pathLeaf(p){return p.slice(lastSeparator(p)+1)}function lastSeparator(p){return pathSyntax(p).sep==="\\"||process.platform==="win32"&&!p.startsWith("/")?Math.max(p.lastIndexOf("/"),p.lastIndexOf("\\")):p.lastIndexOf("/")}function expandHome(p){if(p==="~")return os.homedir();if(p.startsWith("~/")||process.platform==="win32"&&p.startsWith("~\\"))return os.homedir()+p.slice(1);return p}function stripTrailingSlash(p){let root=pathSyntax(p).parse(p).root,end=p.length;while(end>root.length&&lastSeparator(p.slice(0,end))===end-1)end--;return p.slice(0,end)}function splitPathForDirSuggest(value){if(!value)return{base:"",filter:""};let expanded=expandHome(value==="~"?"~/":value),lastSlash=lastSeparator(expanded);if(lastSlash===-1)return{base:"",filter:expanded};return{base:expanded.slice(0,lastSlash+1),filter:expanded.slice(lastSlash+1)}}function listSubdirs(base){if(!base)return[];try{let entries=fs.readdirSync(base,{withFileTypes:!0}),out=[];for(let e of entries)if(e.isDirectory())out.push(e.name);return out.sort((a,b)=>a.localeCompare(b))}catch{return[]}}function filterSubdirs(all,filter){let f=filter.toLowerCase(),visible=f.startsWith(".")?all:all.filter((n)=>!n.startsWith("."));if(!f)return visible;return visible.filter((n)=>n.toLowerCase().startsWith(f))}function joinDrill(typedValue,baseExpanded,name){let out=`${baseExpanded+name}/`;if(typedValue.startsWith("~")){let short=tildify(out);if(short.startsWith("~"))return short==="~"?"~/":`${short}/`}return out}function joinPicked(typedValue,baseExpanded,name){let out=baseExpanded+name;if(typedValue.startsWith("~"))return tildify(out);return out}function depth(node){return node.kind==="leaf"?0:1+Math.max(...node.children.map(depth))}function siblingCount(root,id,orientation){let find=(node)=>{if(node.kind==="leaf")return null;if(node.orientation===orientation&&node.children.some((c)=>c.kind==="leaf"&&c.id===id))return node.children.length;for(let child of node.children){let n=find(child);if(n!==null)return n}return null};return find(root)??1}function splitFits(state,orientation,activeSize){let extent=orientation==="row"?activeSize.cols:activeSize.rows,min=orientation==="row"?20:6,n=siblingCount(state.root,state.activeLeafId,orientation);return Math.floor(extent*n/(n+1))-1>=min}function initialSplit(content){return{root:{kind:"leaf",id:"leaf-1",content},activeLeafId:"leaf-1",nextOrdinal:2}}function leaves(node){return node.kind==="leaf"?[node]:node.children.flatMap(leaves)}function splitActive(state,orientation,content,activeSize){if(activeSize&&!splitFits(state,orientation,activeSize))return state;let leaf={kind:"leaf",id:`leaf-${state.nextOrdinal}`,content},insert=(node)=>{if(node.kind==="leaf"){if(node.id!==state.activeLeafId)return node;return{kind:"group",orientation,children:[node,leaf]}}if(node.orientation===orientation){let i=node.children.findIndex((c)=>c.kind==="leaf"&&c.id===state.activeLeafId);if(i>=0){let children=[...node.children.slice(0,i+1),leaf,...node.children.slice(i+1)];return{...node,children}}}return{...node,children:node.children.map(insert)}},root=insert(state.root);if(!activeSize&&depth(root)>4)return state;return{root,activeLeafId:leaf.id,nextOrdinal:state.nextOrdinal+1}}function removeLeaf(state,id){let all=leaves(state.root);if(all.length<=1)return null;let prune=(node)=>{if(node.kind==="leaf")return node.id===id?null:node;let children=node.children.map(prune).filter((c)=>c!==null);if(children.length===0)return null;if(children.length===1)return children[0];return{...node,children}},root=prune(state.root);if(root===null)return null;if(leaves(root).length===all.length)return state;let order=all.map((l)=>l.id),removedIdx=order.indexOf(id),fallback=order[removedIdx>0?removedIdx-1:removedIdx+1],activeLeafId=state.activeLeafId===id?fallback:state.activeLeafId;return{...state,root,activeLeafId}}function renameLeaf(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,walk=(node)=>node.kind==="leaf"?node.id===id?{...node,title:next}:node:{...node,children:node.children.map(walk)};if(!leaves(state.root).some((l)=>l.id===id))return state;return{...state,root:walk(state.root)}}function cycleLeaf(state,delta){let order=leaves(state.root).map((l)=>l.id);if(order.length<=1)return state;let i=order.indexOf(state.activeLeafId),next=order[(i+delta+order.length)%order.length];return{...state,activeLeafId:next}}function hasEngineLeaf(tree){return!tree||leaves(tree.root).some((l)=>l.id==="leaf-1")}function isTabSplit(tree){return tree?leaves(tree.root).length>1:!1}function collapseSplit(next){let ls=leaves(next.root);return ls.length===1&&ls[0]?.id==="leaf-1"?null:next}function splitLeafPtyKey(tabKey,leafId){return leafId==="leaf-1"?tabKey:`${tabKey}::${leafId}`}var SHELL_LEAF_NAME="shell";function splitLeafNames(leafList,tabCommand,engineTitle,liveTitles){let basename=(argv)=>{let head=(argv??tabCommand)[0]??"",name=pathLeaf(head);return name.length>0?name:"?"},seen=new Map,out=new Map;for(let leaf of leafList){if(leaf.title){out.set(leaf.id,leaf.title);continue}let name=leaf.content===null?engineTitle||liveTitles?.get(leaf.id)||basename(leaf.content):liveTitles?.get(leaf.id)||SHELL_LEAF_NAME,n=(seen.get(name)??0)+1;seen.set(name,n),out.set(leaf.id,n===1?name:`${name} ${n}`)}return out}function meaningfulAutoTitle(autoTitle){let trimmed=(autoTitle??"").trim();if(trimmed.length<3)return null;if(/^[\d\s\p{P}\p{S}]+$/u.test(trimmed))return null;return trimmed}function stableRecordedTitle(raw,vendor){let recorded=raw?.trim();if(!recorded)return null;let cleaned=stripEngineStatusPrefix(recorded,vendor);if(cleaned===recorded&&isEngineDecoration(recorded,vendor))return null;if(isEnginePlaceholderTitle(cleaned,vendor))return null;return cleaned||null}function isEngineDecoration(text,vendor){let glyphs=new Set(engineStatusPrefixes(vendor));return[...text].every((ch)=>ch.trim().length===0||glyphs.has(ch))}function tabTitleStable(tab,taskVendor,liveVendor,liveTitle){if(liveVendor===null&&tab.kind==="engine")return tabTitle({...tab,kind:"command",lastTitle:null},taskVendor);let vendor=liveVendor??tab.liveVendor??(tab.kind==="engine"?tab.vendor??taskVendor:void 0)??void 0,source=liveTitle?.trim()||tab.lastTitle,named=vendor?stableRecordedTitle(source,vendor):source??null;if(!vendor||engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return tabTitle({...tab,...vendor?{kind:"engine",vendor}:{},lastTitle:named},taskVendor);return tabTitle({...tab,kind:"engine",vendor,lastTitle:named},taskVendor)}function titleVendor(tab,taskVendor){return tab.liveVendor??(tab.kind==="engine"?tab.vendor:void 0)??taskVendor}function tabTitle(tab,taskVendor,liveName){if(tab.title)return tab.title;let ls=tab.splitTree?leaves(tab.splitTree.root):[];if(ls.length>1)return t("terminal.tab.groupTitle",{n:tab.ordinal});let sole=ls.length===1?ls[0]:void 0;if(sole&&sole.id!=="leaf-1")return sole.title??`${liveName??SHELL_LEAF_NAME} ${tab.ordinal}`;let vendor=titleVendor(tab,taskVendor);if(liveName&&!isEnginePlaceholderTitle(liveName,vendor))return`${liveName} ${tab.ordinal}`;if(tab.lastTitle&&!isEnginePlaceholderTitle(tab.lastTitle,vendor))return`${tab.lastTitle} ${tab.ordinal}`;let auto=meaningfulAutoTitle(tab.autoTitle);if(auto)return auto;return`${tab.kind==="engine"?engineEntry(tab.vendor??taskVendor).defaultCommand[0]??SHELL_LEAF_NAME:SHELL_LEAF_NAME} ${tab.ordinal}`}function visibleNativeStatus(tab,taskVendor,vendor,liveName){if(!vendor||!liveName)return!1;if(engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return!1;return tabTitle(tab,taskVendor,liveName)===`${liveName} ${tab.ordinal}`}function initialTabs(){return{tabs:[{kind:"engine",id:"tab-1",title:null,ordinal:1}],activeId:"tab-1",nextOrdinal:2}}function reopenHintFor(closed){if(closed?.kind==="command")return{kind:"command"};if(closed?.kind==="engine"&&closed.vendor)return{kind:"engine",vendor:closed.vendor};return{kind:"engine"}}function reopenTabs(state,shell,engine){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,next=state.nextOrdinal+1;if(!engine&&state.reopenAs?.kind==="command")return{tabs:[{kind:"command",id,title:null,ordinal,command:[shell]}],activeId:id,nextOrdinal:next};let vendor=state.reopenAs?.kind==="engine"?state.reopenAs.vendor:void 0;return{tabs:[{kind:"engine",id,title:null,ordinal,...engine??(vendor?{vendor}:{})}],activeId:id,nextOrdinal:next}}function initialShellTabs(shell){return{tabs:[{kind:"command",id:"tab-1",title:null,ordinal:1,command:[shell]}],activeId:"tab-1",nextOrdinal:2}}function rehydrateTabs(persisted,shell,opts={}){let tabs=persisted.tabs.map((t2)=>t2.kind==="command"?{...t2,command:shell,purpose:void 0}:t2);if(tabs.length===0)return opts.allowEmpty?persisted:initialTabs();let activeId=tabs.some((t2)=>t2.id===persisted.activeId)?persisted.activeId:tabs[0].id,maxOrdinal=tabs.reduce((max,t2)=>Math.max(max,t2.ordinal),0);return{tabs,activeId,nextOrdinal:Math.max(persisted.nextOrdinal,maxOrdinal+1)}}function recycleTabs(state,prev){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,pinned=prev.kind==="engine"?prev:void 0;return{tabs:[{kind:"engine",id,ordinal,title:prev.title,autoTitle:prev.autoTitle,...pinned?.vendor?{vendor:pinned.vendor}:{},...pinned?.engineCommand?{engineCommand:pinned.engineCommand}:{}}],activeId:id,nextOrdinal:ordinal+1}}var SHELL_SAFE_ARG=/^[A-Za-z0-9@%+=:,./_-]+$/;function shellCommandLine(argv){return argv.map((a)=>SHELL_SAFE_ARG.test(a)?a:`'${a.replaceAll("'","'\\''")}'`).join(" ")}function shellIdentityInput(taskId,tabId){let task=shellCommandLine([taskId]),tab=shellCommandLine([tabId]);return` export ROVE_TASK_ID=${task} ROVE_TAB_ID=${tab} && clear\r`}function insertAfterActive(state,tab){let i=state.tabs.findIndex((t2)=>t2.id===state.activeId);return{tabs:[...state.tabs.slice(0,i+1),tab,...state.tabs.slice(i+1)],activeId:tab.id,nextOrdinal:state.nextOrdinal+1}}function addTab(state,vendor){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"engine",id:`tab-${ordinal}`,title:null,ordinal,vendor})}function openCommandTab(state,command,label){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command})}function findEditorTab(state){return state.tabs.find((tab)=>tab.kind==="command"&&tab.purpose==="editor")}function openEditorTab(state,command,label){let existing=findEditorTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command,purpose:"editor"})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,command,splitTree:null}:tab);return{...state,tabs,activeId:existing.id}}function findContentTab(state){return state.tabs.find((tab)=>tab.kind==="content")}function openContentTab(state,relPath,label,base){let existing=findContentTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"content",id:`tab-${ordinal}`,title:label,ordinal,relPath,base})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,relPath,base}:tab);return{...state,tabs,activeId:existing.id}}function closeTab(state,id,opts={}){if(state.tabs.length<=1&&!opts.allowEmpty)return{state,closedId:null};let i=state.tabs.findIndex((t2)=>t2.id===id);if(i<0)return{state,closedId:null};let tabs=state.tabs.filter((t2)=>t2.id!==id);if(state.activeId!==id)return{state:{...state,tabs},closedId:id};if(tabs.length===0)return{state:{...state,tabs,activeId:id,reopenAs:reopenHintFor(state.tabs[i])},closedId:id};let next=tabs[Math.max(0,i-1)];return{state:{...state,tabs,activeId:(next??tabs[0]).id},closedId:id}}function closeActiveTab(state){return closeTab(state,state.activeId)}function renameActiveTab(state,title){return setTabTitle(state,state.activeId,title)}function setTabTitle(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.title??null)===next)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,title:next}:t2);return{...state,tabs}}function setTabSessionId(state,id,sessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,sessionId}:t2);return{...state,tabs}}function setTabForkFrom(state,id,sourceSessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,forkFrom:sourceSessionId}:t2);return{...state,tabs}}function setTabEngineCommand(state,id,command){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,engineCommand:command}:t2);return{...state,tabs}}function setTabInitialPrompt(state,id,prompt){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,initialPrompt:prompt}:t2);return{...state,tabs}}function setTabLastTitle(state,id,lastTitle){if(lastTitle.length===0)return state;let current=state.tabs.find((t2)=>t2.id===id);if(!current||current.lastTitle===lastTitle)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,lastTitle}:t2);return{...state,tabs}}function setTabLiveVendor(state,id,liveVendor){let current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.liveVendor??null)===liveVendor)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,liveVendor}:t2);return{...state,tabs}}function setTabAutoTitle(state,id,autoTitle){let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,autoTitle}:t2);return{...state,tabs}}function setTabSpawned(state,id,spawned){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"&&!t2.spawned!==!spawned?{...t2,spawned}:t2);return{...state,tabs}}function cycleTab(state,delta){let n=state.tabs.length;if(n<=1)return state;let i=state.tabs.findIndex((t2)=>t2.id===state.activeId),next=state.tabs[(i+delta+n)%n];return{...state,activeId:next.id}}function gotoTab(state,index){let target=state.tabs[index];return target&&target.id!==state.activeId?{...state,activeId:target.id}:state}function moveTab(state,id,delta){let i=state.tabs.findIndex((t2)=>t2.id===id),j=i+delta;if(i<0||j<0||j>=state.tabs.length)return state;let tabs=[...state.tabs],a=tabs[i];return tabs[i]=tabs[j],tabs[j]=a,{...state,tabs}}function selectTab(state,id){if(state.activeId===id||!state.tabs.some((t2)=>t2.id===id))return state;return{...state,activeId:id}}function setTabSplit(state,id,tree){if(!state.tabs.some((t2)=>t2.id===id))return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,splitTree:tree}:t2);return{...state,tabs}}function tabPtyKey(taskId,tabId){return`${taskId}::${tabId}`}function tabPtyKeyFor(taskId,tab){if(tab.kind==="engine"&&tab.ptyTask)return tabPtyKey(tab.ptyTask.id,"tab-1");return tabPtyKey(taskId,tab.id)}function tabCwdFor(tab,taskWorktree){if(tab.kind==="engine"&&tab.ptyTask)return tab.ptyTask.worktree;return taskWorktree}function terminalTabsKey(taskId){return`terminalTabs.${taskId}`}function forgetTaskTabsSnapshot(kv,taskId){let key=terminalTabsKey(taskId);if(kv.store[key]===void 0)return;kv.set(key,void 0)}function sweepOrphanTabsSnapshots(kv,liveTaskIds){let live=new Set(liveTaskIds),swept=0;for(let key of Object.keys(kv.store)){if(!key.startsWith("terminalTabs."))continue;let taskId=key.slice(13);if(live.has(taskId))continue;kv.set(key,void 0),swept++}return swept}
2
+ import{buildEngineSessionLaunch,trustEngineWorktree}from"./chunk-mvsvmfsf.js";import{engineForkArgv,engineResumeArgv,withPinnedSessionId}from"./chunk-rf4z7b6g.js";import{engineEntry,engineStatusPrefixes,isEnginePlaceholderTitle,stripEngineStatusPrefix}from"./chunk-tewt7vre.js";import{tildify}from"./chunk-ag3txfg2.js";import{t}from"./chunk-tq6pq1ws.js";import{pathSyntax}from"./chunk-pn5ygqn6.js";function engineTabArgv(tab,base,live,fallbackVendor){if(tab.forkFrom&&!tab.spawned&&!live){let forked=tab.vendor?engineForkArgv(base,tab.vendor,tab.forkFrom,tab.sessionId??null):null;if(forked)return forked}if(!tab.sessionId)return base;let vendor=tab.vendor??fallbackVendor;if(tab.spawned&&!live)return engineResumeArgv(base,vendor,tab.sessionId)??base;return withPinnedSessionId(base,vendor,()=>tab.sessionId).argv}function engineTabSpawnFor(state,tab,base,opts){let{live,shell,prompt}=opts,firstEngine=state.tabs.find((t2)=>t2.kind==="engine"),fresh=!tab.spawned&&!live,tabPrompt=fresh?tab.initialPrompt?.trim():void 0,wantsPrompt=!!prompt&&tab.id===firstEngine?.id&&fresh,isFreshFirstEngine=tab.id===firstEngine?.id&&fresh,promptIntent=tabPrompt?{kind:"explicit",prompt:tabPrompt}:wantsPrompt?{kind:"new-task",prompt}:isFreshFirstEngine?{kind:"repo-init"}:{kind:"none"},ref=tab.ptyTask;trustEngineWorktree(tab.vendor??opts.task.vendor,ref?.worktree??opts.worktreePath);let launch=buildEngineSessionLaunch({task:ref?{...opts.task,id:ref.id,kind:"task"}:opts.task,worktreePath:ref?.worktree??opts.worktreePath,shell,argv:engineTabArgv(tab,base,live,opts.task.vendor),promptIntent,protocolGates:opts.protocolGates,inlineImages:opts.inlineImages,tabId:ref?"tab-1":tab.id});return{command:launch.command,...launch.firstMessage?{firstMessage:launch.firstMessage,engineBin:base[0]}:{}}}function tabExitAction(tab,deadOnAttach,resumeTried){if(tab.kind==="engine"&&deadOnAttach&&!!tab.sessionId&&tab.spawned&&!resumeTried)return"resume";return"close"}import*as fs from"fs";import*as os from"os";function pathLeaf(p){return p.slice(lastSeparator(p)+1)}function lastSeparator(p){return pathSyntax(p).sep==="\\"||process.platform==="win32"&&!p.startsWith("/")?Math.max(p.lastIndexOf("/"),p.lastIndexOf("\\")):p.lastIndexOf("/")}function expandHome(p){if(p==="~")return os.homedir();if(p.startsWith("~/")||process.platform==="win32"&&p.startsWith("~\\"))return os.homedir()+p.slice(1);return p}function stripTrailingSlash(p){let root=pathSyntax(p).parse(p).root,end=p.length;while(end>root.length&&lastSeparator(p.slice(0,end))===end-1)end--;return p.slice(0,end)}function splitPathForDirSuggest(value){if(!value)return{base:"",filter:""};let expanded=expandHome(value==="~"?"~/":value),lastSlash=lastSeparator(expanded);if(lastSlash===-1)return{base:"",filter:expanded};return{base:expanded.slice(0,lastSlash+1),filter:expanded.slice(lastSlash+1)}}function listSubdirs(base){if(!base)return[];try{let entries=fs.readdirSync(base,{withFileTypes:!0}),out=[];for(let e of entries)if(e.isDirectory())out.push(e.name);return out.sort((a,b)=>a.localeCompare(b))}catch{return[]}}function filterSubdirs(all,filter){let f=filter.toLowerCase(),visible=f.startsWith(".")?all:all.filter((n)=>!n.startsWith("."));if(!f)return visible;return visible.filter((n)=>n.toLowerCase().startsWith(f))}function joinDrill(typedValue,baseExpanded,name){let out=`${baseExpanded+name}/`;if(typedValue.startsWith("~")){let short=tildify(out);if(short.startsWith("~"))return short==="~"?"~/":`${short}/`}return out}function joinPicked(typedValue,baseExpanded,name){let out=baseExpanded+name;if(typedValue.startsWith("~"))return tildify(out);return out}function depth(node){return node.kind==="leaf"?0:1+Math.max(...node.children.map(depth))}function siblingCount(root,id,orientation){let find=(node)=>{if(node.kind==="leaf")return null;if(node.orientation===orientation&&node.children.some((c)=>c.kind==="leaf"&&c.id===id))return node.children.length;for(let child of node.children){let n=find(child);if(n!==null)return n}return null};return find(root)??1}function splitFits(state,orientation,activeSize){let extent=orientation==="row"?activeSize.cols:activeSize.rows,min=orientation==="row"?20:6,n=siblingCount(state.root,state.activeLeafId,orientation);return Math.floor(extent*n/(n+1))-1>=min}function initialSplit(content){return{root:{kind:"leaf",id:"leaf-1",content},activeLeafId:"leaf-1",nextOrdinal:2}}function leaves(node){return node.kind==="leaf"?[node]:node.children.flatMap(leaves)}function splitActive(state,orientation,content,activeSize){if(activeSize&&!splitFits(state,orientation,activeSize))return state;let leaf={kind:"leaf",id:`leaf-${state.nextOrdinal}`,content},insert=(node)=>{if(node.kind==="leaf"){if(node.id!==state.activeLeafId)return node;return{kind:"group",orientation,children:[node,leaf]}}if(node.orientation===orientation){let i=node.children.findIndex((c)=>c.kind==="leaf"&&c.id===state.activeLeafId);if(i>=0){let children=[...node.children.slice(0,i+1),leaf,...node.children.slice(i+1)];return{...node,children}}}return{...node,children:node.children.map(insert)}},root=insert(state.root);if(!activeSize&&depth(root)>4)return state;return{root,activeLeafId:leaf.id,nextOrdinal:state.nextOrdinal+1}}function removeLeaf(state,id){let all=leaves(state.root);if(all.length<=1)return null;let prune=(node)=>{if(node.kind==="leaf")return node.id===id?null:node;let children=node.children.map(prune).filter((c)=>c!==null);if(children.length===0)return null;if(children.length===1)return children[0];return{...node,children}},root=prune(state.root);if(root===null)return null;if(leaves(root).length===all.length)return state;let order=all.map((l)=>l.id),removedIdx=order.indexOf(id),fallback=order[removedIdx>0?removedIdx-1:removedIdx+1],activeLeafId=state.activeLeafId===id?fallback:state.activeLeafId;return{...state,root,activeLeafId}}function renameLeaf(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,walk=(node)=>node.kind==="leaf"?node.id===id?{...node,title:next}:node:{...node,children:node.children.map(walk)};if(!leaves(state.root).some((l)=>l.id===id))return state;return{...state,root:walk(state.root)}}function cycleLeaf(state,delta){let order=leaves(state.root).map((l)=>l.id);if(order.length<=1)return state;let i=order.indexOf(state.activeLeafId),next=order[(i+delta+order.length)%order.length];return{...state,activeLeafId:next}}function hasEngineLeaf(tree){return!tree||leaves(tree.root).some((l)=>l.id==="leaf-1")}function isTabSplit(tree){return tree?leaves(tree.root).length>1:!1}function collapseSplit(next){let ls=leaves(next.root);return ls.length===1&&ls[0]?.id==="leaf-1"?null:next}function splitLeafPtyKey(tabKey,leafId){return leafId==="leaf-1"?tabKey:`${tabKey}::${leafId}`}var SHELL_LEAF_NAME="shell";function splitLeafNames(leafList,tabCommand,engineTitle,liveTitles){let basename=(argv)=>{let head=(argv??tabCommand)[0]??"",name=pathLeaf(head);return name.length>0?name:"?"},seen=new Map,out=new Map;for(let leaf of leafList){if(leaf.title){out.set(leaf.id,leaf.title);continue}let name=leaf.content===null?engineTitle||liveTitles?.get(leaf.id)||basename(leaf.content):liveTitles?.get(leaf.id)||SHELL_LEAF_NAME,n=(seen.get(name)??0)+1;seen.set(name,n),out.set(leaf.id,n===1?name:`${name} ${n}`)}return out}function meaningfulAutoTitle(autoTitle){let trimmed=(autoTitle??"").trim();if(trimmed.length<3)return null;if(/^[\d\s\p{P}\p{S}]+$/u.test(trimmed))return null;return trimmed}function stableRecordedTitle(raw,vendor){let recorded=raw?.trim();if(!recorded)return null;let cleaned=stripEngineStatusPrefix(recorded,vendor);if(cleaned===recorded&&isEngineDecoration(recorded,vendor))return null;if(isEnginePlaceholderTitle(cleaned,vendor))return null;return cleaned||null}function isEngineDecoration(text,vendor){let glyphs=new Set(engineStatusPrefixes(vendor));return[...text].every((ch)=>ch.trim().length===0||glyphs.has(ch))}function tabTitleStable(tab,taskVendor,liveVendor,liveTitle){if(liveVendor===null&&tab.kind==="engine")return tabTitle({...tab,kind:"command",lastTitle:null},taskVendor);let vendor=liveVendor??tab.liveVendor??(tab.kind==="engine"?tab.vendor??taskVendor:void 0)??void 0,source=liveTitle?.trim()||tab.lastTitle,named=vendor?stableRecordedTitle(source,vendor):source??null;if(!vendor||engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return tabTitle({...tab,...vendor?{kind:"engine",vendor}:{},lastTitle:named},taskVendor);return tabTitle({...tab,kind:"engine",vendor,lastTitle:named},taskVendor)}function titleVendor(tab,taskVendor){return tab.liveVendor??(tab.kind==="engine"?tab.vendor:void 0)??taskVendor}function tabTitle(tab,taskVendor,liveName){if(tab.title)return tab.title;let ls=tab.splitTree?leaves(tab.splitTree.root):[];if(ls.length>1)return t("terminal.tab.groupTitle",{n:tab.ordinal});let sole=ls.length===1?ls[0]:void 0;if(sole&&sole.id!=="leaf-1")return sole.title??`${liveName??SHELL_LEAF_NAME} ${tab.ordinal}`;let vendor=titleVendor(tab,taskVendor);if(liveName&&!isEnginePlaceholderTitle(liveName,vendor))return`${liveName} ${tab.ordinal}`;if(tab.lastTitle&&!isEnginePlaceholderTitle(tab.lastTitle,vendor))return`${tab.lastTitle} ${tab.ordinal}`;let auto=meaningfulAutoTitle(tab.autoTitle);if(auto)return auto;return`${tab.kind==="engine"?engineEntry(tab.vendor??taskVendor).defaultCommand[0]??SHELL_LEAF_NAME:SHELL_LEAF_NAME} ${tab.ordinal}`}function visibleNativeStatus(tab,taskVendor,vendor,liveName){if(!vendor||!liveName)return!1;if(engineEntry(vendor).terminalTitle?.ownsStatus!==!0)return!1;return tabTitle(tab,taskVendor,liveName)===`${liveName} ${tab.ordinal}`}function initialTabs(){return{tabs:[{kind:"engine",id:"tab-1",title:null,ordinal:1}],activeId:"tab-1",nextOrdinal:2}}function reopenHintFor(closed){if(closed?.kind==="command")return{kind:"command"};if(closed?.kind==="engine"&&closed.vendor)return{kind:"engine",vendor:closed.vendor};return{kind:"engine"}}function reopenTabs(state,shell,engine){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,next=state.nextOrdinal+1;if(!engine&&state.reopenAs?.kind==="command")return{tabs:[{kind:"command",id,title:null,ordinal,command:[shell]}],activeId:id,nextOrdinal:next};let vendor=state.reopenAs?.kind==="engine"?state.reopenAs.vendor:void 0;return{tabs:[{kind:"engine",id,title:null,ordinal,...engine??(vendor?{vendor}:{})}],activeId:id,nextOrdinal:next}}function initialShellTabs(shell){return{tabs:[{kind:"command",id:"tab-1",title:null,ordinal:1,command:[shell]}],activeId:"tab-1",nextOrdinal:2}}function rehydrateTabs(persisted,shell,opts={}){let tabs=persisted.tabs.map((t2)=>t2.kind==="command"?{...t2,command:shell,purpose:void 0}:t2);if(tabs.length===0)return opts.allowEmpty?persisted:initialTabs();let activeId=tabs.some((t2)=>t2.id===persisted.activeId)?persisted.activeId:tabs[0].id,maxOrdinal=tabs.reduce((max,t2)=>Math.max(max,t2.ordinal),0);return{tabs,activeId,nextOrdinal:Math.max(persisted.nextOrdinal,maxOrdinal+1)}}function recycleTabs(state,prev){let ordinal=state.nextOrdinal,id=`tab-${ordinal}`,pinned=prev.kind==="engine"?prev:void 0;return{tabs:[{kind:"engine",id,ordinal,title:prev.title,autoTitle:prev.autoTitle,...pinned?.vendor?{vendor:pinned.vendor}:{},...pinned?.engineCommand?{engineCommand:pinned.engineCommand}:{}}],activeId:id,nextOrdinal:ordinal+1}}var SHELL_SAFE_ARG=/^[A-Za-z0-9@%+=:,./_-]+$/;function shellCommandLine(argv){return argv.map((a)=>SHELL_SAFE_ARG.test(a)?a:`'${a.replaceAll("'","'\\''")}'`).join(" ")}function shellIdentityInput(taskId,tabId){let task=shellCommandLine([taskId]),tab=shellCommandLine([tabId]);return` export ROVE_TASK_ID=${task} ROVE_TAB_ID=${tab} && clear\r`}function insertAfterActive(state,tab){let i=state.tabs.findIndex((t2)=>t2.id===state.activeId);return{tabs:[...state.tabs.slice(0,i+1),tab,...state.tabs.slice(i+1)],activeId:tab.id,nextOrdinal:state.nextOrdinal+1}}function addTab(state,vendor){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"engine",id:`tab-${ordinal}`,title:null,ordinal,vendor})}function openCommandTab(state,command,label){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command})}function findEditorTab(state){return state.tabs.find((tab)=>tab.kind==="command"&&tab.purpose==="editor")}function openEditorTab(state,command,label){let existing=findEditorTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"command",id:`tab-${ordinal}`,title:label,ordinal,command,purpose:"editor"})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,command,splitTree:null}:tab);return{...state,tabs,activeId:existing.id}}function findContentTab(state){return state.tabs.find((tab)=>tab.kind==="content")}function openContentTab(state,relPath,label,base){let existing=findContentTab(state);if(!existing){let ordinal=state.nextOrdinal;return insertAfterActive(state,{kind:"content",id:`tab-${ordinal}`,title:label,ordinal,relPath,base})}let tabs=state.tabs.map((tab)=>tab.id===existing.id?{...existing,title:label,relPath,base}:tab);return{...state,tabs,activeId:existing.id}}function closeTab(state,id,opts={}){if(state.tabs.length<=1&&!opts.allowEmpty)return{state,closedId:null};let i=state.tabs.findIndex((t2)=>t2.id===id);if(i<0)return{state,closedId:null};let tabs=state.tabs.filter((t2)=>t2.id!==id);if(state.activeId!==id)return{state:{...state,tabs},closedId:id};if(tabs.length===0)return{state:{...state,tabs,activeId:id,reopenAs:reopenHintFor(state.tabs[i])},closedId:id};let next=tabs[Math.max(0,i-1)];return{state:{...state,tabs,activeId:(next??tabs[0]).id},closedId:id}}function closeActiveTab(state){return closeTab(state,state.activeId)}function renameActiveTab(state,title){return setTabTitle(state,state.activeId,title)}function setTabTitle(state,id,title){let trimmed=title.trim(),next=trimmed.length>0?trimmed:null,current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.title??null)===next)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,title:next}:t2);return{...state,tabs}}function setTabSessionId(state,id,sessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,sessionId}:t2);return{...state,tabs}}function setTabForkFrom(state,id,sourceSessionId){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,forkFrom:sourceSessionId}:t2);return{...state,tabs}}function setTabEngineCommand(state,id,command){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,engineCommand:command}:t2);return{...state,tabs}}function setTabInitialPrompt(state,id,prompt){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"?{...t2,initialPrompt:prompt}:t2);return{...state,tabs}}function setTabLastTitle(state,id,lastTitle){if(lastTitle.length===0)return state;let current=state.tabs.find((t2)=>t2.id===id);if(!current||current.lastTitle===lastTitle)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,lastTitle}:t2);return{...state,tabs}}function setTabLiveVendor(state,id,liveVendor){let current=state.tabs.find((t2)=>t2.id===id);if(!current||(current.liveVendor??null)===liveVendor)return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,liveVendor}:t2);return{...state,tabs}}function setTabAutoTitle(state,id,autoTitle){let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,autoTitle}:t2);return{...state,tabs}}function setTabSpawned(state,id,spawned){let tabs=state.tabs.map((t2)=>t2.id===id&&t2.kind==="engine"&&!t2.spawned!==!spawned?{...t2,spawned}:t2);return{...state,tabs}}function cycleTab(state,delta){let n=state.tabs.length;if(n<=1)return state;let i=state.tabs.findIndex((t2)=>t2.id===state.activeId),next=state.tabs[(i+delta+n)%n];return{...state,activeId:next.id}}function gotoTab(state,index){let target=state.tabs[index];return target&&target.id!==state.activeId?{...state,activeId:target.id}:state}function moveTab(state,id,delta){let i=state.tabs.findIndex((t2)=>t2.id===id),j=i+delta;if(i<0||j<0||j>=state.tabs.length)return state;let tabs=[...state.tabs],a=tabs[i];return tabs[i]=tabs[j],tabs[j]=a,{...state,tabs}}function selectTab(state,id){if(state.activeId===id||!state.tabs.some((t2)=>t2.id===id))return state;return{...state,activeId:id}}function setTabSplit(state,id,tree){if(!state.tabs.some((t2)=>t2.id===id))return state;let tabs=state.tabs.map((t2)=>t2.id===id?{...t2,splitTree:tree}:t2);return{...state,tabs}}function tabPtyKey(taskId,tabId){return`${taskId}::${tabId}`}function tabPtyKeyFor(taskId,tab){if(tab.kind==="engine"&&tab.ptyTask)return tabPtyKey(tab.ptyTask.id,"tab-1");return tabPtyKey(taskId,tab.id)}function tabCwdFor(tab,taskWorktree){if(tab.kind==="engine"&&tab.ptyTask)return tab.ptyTask.worktree;return taskWorktree}function terminalTabsKey(taskId){return`terminalTabs.${taskId}`}function forgetTaskTabsSnapshot(kv,taskId){let key=terminalTabsKey(taskId);if(kv.store[key]===void 0)return;kv.set(key,void 0)}function sweepOrphanTabsSnapshots(kv,liveTaskIds){let live=new Set(liveTaskIds),swept=0;for(let key of Object.keys(kv.store)){if(!key.startsWith("terminalTabs."))continue;let taskId=key.slice(13);if(live.has(taskId))continue;kv.set(key,void 0),swept++}return swept}
3
3
  export{terminalTabsKey,forgetTaskTabsSnapshot,sweepOrphanTabsSnapshots,pathLeaf,expandHome,stripTrailingSlash,splitPathForDirSuggest,listSubdirs,filterSubdirs,joinDrill,joinPicked,initialSplit,leaves,splitActive,removeLeaf,renameLeaf,cycleLeaf,hasEngineLeaf,isTabSplit,collapseSplit,splitLeafPtyKey,splitLeafNames,tabTitleStable,tabTitle,visibleNativeStatus,initialTabs,reopenTabs,initialShellTabs,rehydrateTabs,recycleTabs,engineTabArgv,engineTabSpawnFor,tabExitAction,shellIdentityInput,addTab,openCommandTab,findEditorTab,openEditorTab,openContentTab,closeTab,closeActiveTab,renameActiveTab,setTabTitle,setTabSessionId,setTabForkFrom,setTabEngineCommand,setTabInitialPrompt,setTabLastTitle,setTabLiveVendor,setTabAutoTitle,setTabSpawned,cycleTab,gotoTab,moveTab,selectTab,setTabSplit,tabPtyKey,tabPtyKeyFor,tabCwdFor};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{__require}from"./chunk-91sykzqq.js";var welcomeWrite=Promise.resolve();function trackWelcomeWrite(write){welcomeWrite=write}async function launchTui(){let{startTui}=await import("./chunk-68pcd00x.js");await startTui(),await welcomeWrite;let{runPendingWelcomeInstalls}=await import("./chunk-n3wdy4z4.js");runPendingWelcomeInstalls()}
2
+ import{__require}from"./chunk-91sykzqq.js";var welcomeWrite=Promise.resolve();function trackWelcomeWrite(write){welcomeWrite=write}async function launchTui(){let{startTui}=await import("./chunk-8wf3p9er.js");await startTui(),await welcomeWrite;let{runPendingWelcomeInstalls}=await import("./chunk-7yb48mar.js");runPendingWelcomeInstalls()}
3
3
  export{trackWelcomeWrite,launchTui};