oh-my-pr 4.2.0 → 4.2.1

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 (2) hide show
  1. package/dist/index.cjs +5 -5
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1056,7 +1056,7 @@ ${n}`:n;return{commentDatabaseId:(await be("status reply fallback",e,()=>t.issue
1056
1056
  `)).join(`
1057
1057
  `):"None",u=s.length?s.map((l,p)=>`${p+1}. ${l.context}: ${l.description}${l.targetUrl?` (${l.targetUrl})`:""}`).join(`
1058
1058
  `):"None",c=a?["Documentation updates are required for this PR.",`Assessment summary: ${a}`,"Update the appropriate repository documentation for these changes.","Choose the right docs files for this repository (for example README, docs pages, API/config/operator docs).","If, after inspection, the repository documentation is already accurate or there is no appropriate docs target, leave docs unchanged and report that using the docs summary block with `no_change`."].join(`
1059
- `):"None";return[`You are acting as an autonomous PR babysitter for ${e.repo} PR #${e.number}.`,`PR URL: ${e.url}`,`Base repository: ${r.repoFullName}`,`Head repository: ${r.headRepoFullName}`,`Head branch: ${r.headRef}`,`Head remote: ${n}`,"You are running inside an isolated app-owned worktree under ~/.oh-my-pr.","Make only targeted changes that resolve the approved tasks.","Do not wait for user input, confirmation, or approval at any point.","Do not rewrite unrelated files.","Use the available git tooling for inspection and verification only.","Leave file edits uncommitted; the babysitter app will handle Git finalization after your run.","GitHub follow-up replies and review-thread resolution will be handled by the babysitter after your run.","If a task is invalid after inspection, explain it in your final response and include the exact audit token.","","Approved review-comment tasks:",o,"","Approved status-check tasks:",u,"","Approved documentation tasks:",c,"","When done:","1) Run the relevant verification for your changes.","2) Leave any changed files in the worktree for the babysitter app to finalize.","3) For each feedback item you addressed or were blocked on, emit a summary block in the following format:"," FEEDBACK_SUMMARY_START <auditToken>"," <A concise 1-2 sentence summary of what you did or why you were blocked>"," FEEDBACK_SUMMARY_END"," Include one block per audit token. These summaries will be posted as follow-up comments on the PR.","4) If documentation tasks were assigned, emit exactly one docs summary block in the following format:"," DOCS_SUMMARY_START <changed|no_change>"," <A concise 1-2 sentence summary of the docs you updated, or why no docs changes were necessary after inspection>"," DOCS_SUMMARY_END"].join(`
1059
+ `):"None";return[`You are acting as an autonomous PR babysitter for ${e.repo} PR #${e.number}.`,`PR URL: ${e.url}`,`Base repository: ${r.repoFullName}`,`Head repository: ${r.headRepoFullName}`,`Head branch: ${r.headRef}`,`Head remote: ${n}`,"You are running inside an isolated app-owned worktree under ~/.oh-my-pr.","Make only targeted changes that resolve the approved tasks.","Do not wait for user input, confirmation, or approval at any point.","Do not rewrite unrelated files.","Use the available git tooling for inspection and verification only.","If dependencies are missing, install them using the repository's lockfile/package manager as needed inside this isolated worktree.","Leave file edits uncommitted; the babysitter app will handle Git finalization after your run.","GitHub follow-up replies and review-thread resolution will be handled by the babysitter after your run.","If a task is invalid after inspection, explain it in your final response and include the exact audit token.","","Approved review-comment tasks:",o,"","Approved status-check tasks:",u,"","Approved documentation tasks:",c,"","When done:","1) Run the relevant verification for your changes.","2) Leave any changed files in the worktree for the babysitter app to finalize.","3) For each feedback item you addressed or were blocked on, emit a summary block in the following format:"," FEEDBACK_SUMMARY_START <auditToken>"," <A concise 1-2 sentence summary of what you did or why you were blocked>"," FEEDBACK_SUMMARY_END"," Include one block per audit token. These summaries will be posted as follow-up comments on the PR.","4) If documentation tasks were assigned, emit exactly one docs summary block in the following format:"," DOCS_SUMMARY_START <changed|no_change>"," <A concise 1-2 sentence summary of the docs you updated, or why no docs changes were necessary after inspection>"," DOCS_SUMMARY_END"].join(`
1060
1060
  `)}function nY(t){let e=t.match(zQ);return e?Array.from(new Set(e)):[]}var gP=/FEEDBACK_SUMMARY_START\s+(codefactory-feedback:[^\s]+)\s*\n([\s\S]*?)FEEDBACK_SUMMARY_END/g,iY=/DOCS_SUMMARY_START\s+(changed|no_change)\s*\n([\s\S]*?)DOCS_SUMMARY_END/;function sY(t){let e=new Map,r,n=new RegExp(gP.source,gP.flags);for(;(r=n.exec(t))!==null;){let i=r[1],s=r[2].trim();i&&s&&e.set(i,s)}return e}function aY(t){let e=iY.exec(t);if(!e)return null;let r=e[1],n=e[2]?.trim();return!n||r!=="changed"&&r!=="no_change"?null:{outcome:r,summary:n}}function oY(t,e){let r=nY(t.body).filter(i=>i!==t.auditToken);if(r.length===0)return!1;let n=new Date(t.createdAt).getTime();return r.some(i=>e.some(s=>{if(s.id===t.id||s.auditToken!==i)return!1;let a=new Date(s.createdAt).getTime();return Number.isNaN(n)||Number.isNaN(a)?!1:a<=n}))}function D0(t,e,r){return e.some(n=>{if(n.id===t.id||!n.body.includes(t.auditToken)||t.replyKind==="review_thread"&&(!t.threadId||n.threadId!==t.threadId))return!1;let i=new Date(n.createdAt).getTime();return Number.isNaN(i)?!1:typeof r=="number"?i>=r:!0})}function uY(t){return t.includes("**Accepted**")||t.includes("**Agent running**")||t.includes("**Agent failed**")||t.includes("**Agent completed**")||t.includes("**Resolved**")}function hP(t){let e=t?.statusReplyRefs;if(!e||typeof e!="object")return{};let r={};for(let[n,i]of Object.entries(e)){if(!i||typeof i!="object")continue;let s=i;typeof s.commentDatabaseId=="number"&&(s.replyKind==="review_thread"||s.replyKind==="review"||s.replyKind==="general_comment")&&typeof s.body=="string"&&(r[n]={commentDatabaseId:s.commentDatabaseId,replyKind:s.replyKind,body:s.body})}return r}function bP(t,e){for(let r of e){if(r.id===t.id||!uY(r.body))continue;let n=Number(r.sourceId);if(Number.isFinite(n)){if(t.replyKind==="review_thread"){if(r.type!=="review_comment"||!t.threadId||r.threadId!==t.threadId)continue;return{commentDatabaseId:n,replyKind:"review_thread",body:r.body}}if(r.type==="general_comment")return{commentDatabaseId:n,replyKind:t.replyKind,body:r.body}}}return null}function cY(t){let{pr:e,followUpTasks:r,runStartedAtMs:n}=t,i=[];for(let s of r)D0(s,e.feedbackItems,n)||i.push(`missing audit trail for ${s.id}`),s.replyKind==="review_thread"&&(e.feedbackItems.find(o=>o.id===s.id)?.threadResolved||i.push(`review thread not resolved for ${s.id}`));return i}function lY(t,e){return t.decision!=="accept"||t.status!=="queued"&&t.status!=="in_progress"?!1:D0(t,e)?Uo(Ql(t)):!0}function pY(t){return t.feedbackItems.filter(e=>lY(e,t.feedbackItems))}function yP(t){return[t.prId,t.sha,t.provider,t.context,t.status,t.conclusion||"",t.description,t.targetUrl||""].join("|")}async function vP(t,e){if(e.length===0)return;let r=await t.listCheckSnapshots({prId:e[0].prId,sha:e[0].sha}),n=new Set(r.map(i=>yP(i)));for(let i of e){let s=yP(i);if(n.has(s))continue;let{id:a,...o}=i;await t.createCheckSnapshot(o),n.add(s)}}async function wP(t,e,r,n){let i=await t.listFailureFingerprints({sessionId:e,sha:r}),s=new Set(i.map(a=>a.fingerprint));for(let a of n)s.has(a.fingerprint)||(await t.createFailureFingerprint({sessionId:e,sha:r,fingerprint:a.fingerprint,category:a.category,classification:a.classification,summary:a.summary,selectedEvidence:a.selectedEvidence}),s.add(a.fingerprint))}function dY(t,e){let r=new Set(t),n=new Set(e.map(u=>u.fingerprint)),i=t.filter(u=>!n.has(u)),s=t.filter(u=>n.has(u)),a=e.map(u=>u.fingerprint).filter(u=>!r.has(u)),o=i.length*2-s.length*2-a.length*3;return{removedTargetFingerprints:i,remainingTargetFingerprints:s,newUnrelatedFingerprints:a,improvementScore:o}}function PP(t){return t?RP:kP}function fY(t,e,r,n){let i=t.trim()?t.trim().slice(0,7):"",a=[i?`Addressed in commit \`${i}\` by the latest babysitter run.`:"Addressed in the latest babysitter run."];if(e.replyKind!=="review_thread"&&e.sourceUrl){let o=(e.body||"").split(`
1061
1061
  `)[0]||"",u=o.length>120?o.slice(0,120)+"\u2026":o;a.push("",`> Responding to [comment by @${e.author}](${e.sourceUrl}):`,`> ${u}`)}return n&&a.push("",n),a.push("",`<!-- ${e.auditToken} -->`),r&&a.push("",In),a.join(`
1062
1062
  `)}var CP="<!-- codefactory-agent-command -->";function mY(t){let e=0,r=t.match(/`{3,}/g);if(r)for(let i of r)i.length>e&&(e=i.length);let n="`".repeat(Math.max(3,e+1));return{open:`${n}text`,close:n}}function gY(t){return t.includes(CP)}function hY(t,e,r){let n=mY(e);return[CP,`\u{1F916} **${PP(r)}** dispatched \`${t}\` with the following prompt:`,"","<details>","<summary>Agent prompt (click to expand)</summary>","",n.open,e,n.close,"","</details>",...r?["",In]:[]].join(`
@@ -1067,8 +1067,8 @@ ${In}`:i}function OP(t){let e=t.trimEnd();if(e===In)return{bodyWithoutFooter:"",
1067
1067
 
1068
1068
  `+In;return e.endsWith(r)?{bodyWithoutFooter:e.slice(0,-r.length),hadFooter:!0}:{bodyWithoutFooter:t,hadFooter:!1}}function bY(t,e){if(!e)return t;let{bodyWithoutFooter:r}=OP(t);return r?`${r}
1069
1069
 
1070
- ${In}`:In}function Yl(t,e){return[t,...e].join(" ")}function qi(t){return t.stderr.trim()||t.stdout.trim()||"no output"}function yY(t){return/\b(Could not resolve host|ENOTFOUND|EAI_AGAIN|ECONNRESET|network error|Failed to connect|Connection timed out)\b/i.test(t)}function Mi(t,e){let r=qi(e);return yY(r)?`Infrastructure Git failure while ${t}: ${r}`:`${t} failed: ${r}`}function St(t){return t instanceof Error?t.message:String(t)}function vY(t){return t==="claude"?"codex":"claude"}function _P(t){let n=(t.replace(/^(?:claude|codex|agent) apply failed \(\d+\):\s*/i,"").replace(/^Agent failed to resolve merge conflicts \(\d+\):\s*/i,"").replace(/^Error:\s*/i,"").split(/\r?\n/).map(s=>s.trim()).find(s=>s.length>0)??"No failure details were reported").replace(/\s+/g," "),i=n.length>180?`${n.slice(0,177).trimEnd()}...`:n;return/[.!?]$/.test(i)?i:`${i}.`}function wY(t,e){let n=`${t}${e}`.split(/\r?\n/);return{lines:n.slice(0,-1),buffer:n.at(-1)??""}}async function xY(t,e){let r=await e("git",["config","--get","user.name"],{cwd:t,timeoutMs:3e3});(r.code!==0||!r.stdout.trim())&&await e("git",["config","user.name",GQ],{cwd:t,timeoutMs:3e3});let n=await e("git",["config","--get","user.email"],{cwd:t,timeoutMs:3e3});(n.code!==0||!n.stdout.trim())&&await e("git",["config","user.email",BQ],{cwd:t,timeoutMs:3e3})}async function ia(t){try{return await(0,SP.access)(t),!0}catch(e){if(e instanceof Error&&"code"in e&&(e.code==="ENOENT"||e.code==="ENOTDIR"))return!1;throw e}}async function IP(t){if(!t.requiresVerification)return{ok:!0};let e=ji.default.join(t.cwd,"package.json");if(!await ia(e))return{ok:!0};let r={packageLock:await ia(ji.default.join(t.cwd,"package-lock.json")),npmShrinkwrap:await ia(ji.default.join(t.cwd,"npm-shrinkwrap.json")),pnpmLock:await ia(ji.default.join(t.cwd,"pnpm-lock.yaml")),yarnLock:await ia(ji.default.join(t.cwd,"yarn.lock"))};if(!r.packageLock&&!r.npmShrinkwrap&&!r.pnpmLock&&!r.yarnLock)return{ok:!0};if(!await ia(ji.default.join(t.cwd,"node_modules"))){if((await t.runCommand("git",["check-ignore","-q","node_modules"],{cwd:t.cwd,timeoutMs:5e3})).code!==0)return{ok:!1,reason:"Node dependency cache is missing, but node_modules is not ignored by git; refusing to install dependencies automatically"};let i=r.pnpmLock?{command:"pnpm",args:["install","--frozen-lockfile"]}:r.yarnLock?{command:"yarn",args:["install","--frozen-lockfile"]}:{command:"npm",args:["ci"]},s=await t.runCommand(i.command,i.args,{cwd:t.cwd,timeoutMs:3e5});if(s.code!==0)return{ok:!1,reason:pn(s,`Dependency install failed while running ${i.command} ${i.args.join(" ")}`)}}return{ok:!0}}function EP(t){return new Promise(e=>setTimeout(e,t))}var Zl=class{storage;inProgress=new Set;feedbackMutationLocks=new Map;github;runtime;releaseManager;deploymentHealingManager;scheduleBackgroundJob;clock;agentHealthCache=new Map;constructor(e,r=WQ,n=VQ,i,s,a){this.storage=e,this.github=r,this.runtime=n,this.releaseManager=i,this.deploymentHealingManager=a,this.scheduleBackgroundJob=s,this.clock=n.now??(()=>new Date)}now(){return this.clock()}async readAgentHealth(e){let r=this.now().getTime(),n=this.agentHealthCache.get(e);if(n&&r-n.checkedAtMs<HQ)return n.result;let i=this.runtime.checkAgentHealth?await this.runtime.checkAgentHealth(e):{ok:!0};return this.agentHealthCache.set(e,{checkedAtMs:r,result:i}),i}async pauseAutomationForAgentFailure(e,r,n){let i=`Agent health check failed for ${e}: ${r}`;await this.storage.updateRuntimeState({drainMode:!0,drainRequestedAt:this.now().toISOString(),drainReason:i}),await Promise.all(n.map(async s=>{let a=await this.storage.getPR(s);if(a){let o=a.feedbackItems.map(u=>u.decision==="accept"&&(u.status==="queued"||u.status==="in_progress")?I0(u,i):u);if(o.some((u,c)=>u!==a.feedbackItems[c])){let u=Fi(o);await this.storage.updatePR(a.id,{feedbackItems:o,accepted:u.accepted,rejected:u.rejected,flagged:u.flagged})}}await this.storage.addLog(s,"error",`Automation paused: ${i}`,{phase:"agent.health"})}))}async ensureAgentHealthy(e,r){let n=await this.readAgentHealth(e);if(!n.ok)throw await this.pauseAutomationForAgentFailure(e,n.reason,r),new Error(`Agent health check failed for ${e}: ${n.reason}`)}async supersedeActiveHealingSessionsForArchivedPr(e){let r=await this.storage.listHealingSessions({prId:e}),n=this.now().toISOString();for(let i of r)ea(i.state)||await this.storage.updateHealingSession(i.id,{state:"superseded",endedAt:n,escalationReason:"PR archived on GitHub"})}getActiveRunCount(){return this.inProgress.size}async waitForIdle(e=12e4){let r=Date.now();for(;this.inProgress.size>0;){if(Date.now()-r>=e)return!1;await EP(100)}return!0}async runQueuedBabysitPR(e,r){let n=(await this.storage.listAgentRuns({status:"running",prId:e})).slice().sort((s,a)=>Date.parse(a.updatedAt)-Date.parse(s.updatedAt))[0];if(!n){await this.babysitPR(e,r,{allowDuringDrain:!0,rethrowOnFailure:!0});return}if(!!!(n.prompt&&n.resolvedAgent&&n.initialHeadSha)){let s=new Date().toISOString();await this.storage.upsertAgentRun({...n,status:"failed",phase:"run.failed",lastError:"Interrupted run missing replay context",updatedAt:s}),await this.babysitPR(e,r,{allowDuringDrain:!0,rethrowOnFailure:!0});return}await this.babysitPR(e,n.preferredAgent,{runId:n.id,recoveryMode:!0,forceAgentPrompt:n.prompt,forceResolvedAgent:n.resolvedAgent,replayInitialHeadSha:n.initialHeadSha,allowDuringDrain:!0,rethrowOnFailure:!0})}async resumeInterruptedRuns(){let e=await this.storage.listAgentRuns({status:"running"});if(e.length!==0){if(this.scheduleBackgroundJob){let r=new Set;for(let n of e)r.has(n.prId)||(r.add(n.prId),await this.scheduleBackgroundJob("babysit_pr",n.prId,Or("babysit_pr",n.prId),{preferredAgent:n.preferredAgent}));return}for(let r of e){if(!!!(r.prompt&&r.resolvedAgent&&r.initialHeadSha)){let i=new Date().toISOString();await this.storage.upsertAgentRun({...r,status:"failed",phase:"run.failed",lastError:"Interrupted run missing replay context",updatedAt:i}),await this.babysitPR(r.prId,r.preferredAgent,{allowDuringDrain:!0});continue}await this.babysitPR(r.prId,r.preferredAgent,{runId:r.id,recoveryMode:!0,forceAgentPrompt:r.prompt,forceResolvedAgent:r.resolvedAgent,replayInitialHeadSha:r.initialHeadSha,allowDuringDrain:!0})}}}async withFeedbackMutationLock(e,r){let n=this.feedbackMutationLocks.get(e)??Promise.resolve(),i,s=new Promise(o=>{i=()=>o()}),a=n.then(()=>s);this.feedbackMutationLocks.set(e,a),await n;try{return await r()}finally{i?.(),this.feedbackMutationLocks.get(e)===a&&this.feedbackMutationLocks.delete(e)}}async retryFeedbackItem(e,r){return this.withFeedbackMutationLock(e,async()=>{let n=await this.storage.getPR(e);if(!n)return{kind:"pr_not_found"};let i=n.feedbackItems.find(u=>u.id===r);if(!i)return{kind:"feedback_not_found"};if(i.status!=="failed"&&i.status!=="warning")return{kind:"feedback_not_retryable"};let s=n.feedbackItems.map(u=>u.id===r?mP(u):u),a=Fi(s),o=await this.storage.updatePR(n.id,{feedbackItems:s,accepted:a.accepted,rejected:a.rejected,flagged:a.flagged});if(!o)throw new Error(`Failed to queue retry for feedback item ${r} on PR ${e}`);return{kind:"ok",updated:o}})}async syncFeedbackForPR(e,r){let n=await this.storage.getPR(e);if(!n)throw new Error("PR not found");let i=dt(n.repo);if(!i)throw new Error(`Invalid repository slug: ${n.repo}`);let s={owner:i.owner,repo:i.repo,number:n.number},a=await this.storage.getConfig(),o=await this.github.buildOctokit(a),u=r?.phase??"sync";r?.logStart&&await this.storage.addLog(n.id,"info","Syncing GitHub comments/reviews...",{runId:r.runId??null,phase:u});let c=await this.github.fetchFeedbackItemsForPR(o,s,a),{merged:l,newCount:p}=JQ(n.feedbackItems,c),d=Fi(l),m=await this.storage.updatePR(n.id,{title:n.title,status:n.status,lastChecked:new Date().toISOString(),feedbackItems:l,accepted:d.accepted,rejected:d.rejected,flagged:d.flagged});if(!m)throw new Error("Failed to update PR after feedback sync");return await this.storage.addLog(n.id,"info",KQ(c.length,p),{runId:r?.runId??null,phase:u,metadata:{total:c.length,newCount:p}}),m}async syncAndBabysitTrackedRepos(){if((await this.storage.getRuntimeState()).drainMode)return;let r=[...await this.storage.getPRs(),...await this.storage.getArchivedPRs()].filter(XQ);for(let f of r)try{await this.syncFeedbackForPR(f.id,{phase:"repair"})}catch(g){await this.storage.addLog(f.id,"warn",`Could not repair missing GitHub review-thread metadata: ${St(g)}`,{phase:"repair"})}let n=await this.storage.getConfig(),i=await this.storage.getPRs(),s=new Set([...i.map(f=>f.repo),...n.watchedRepos]),a=n.codingAgent,o=i.filter(f=>f.watchEnabled!==!1&&f.status!=="archived").map(f=>f.id);if(s.size>0)try{await this.ensureAgentHealthy(a,o)}catch{return}let u=new Map((await this.storage.listRepoSettings()).map(f=>[f.repo,f])),c=await this.github.buildOctokit(n),l=Array.from(s).some(f=>u.get(f)?.ownPrsOnly??!0),p=null,d=async()=>l?(p||(p=(this.github.getAuthenticatedLogin?.(c)??Promise.resolve(null)).catch(f=>(Go.warn({err:f instanceof Error?f.message:String(f)},"Failed to determine authenticated GitHub login for watcher filtering"),null))),p):null,m=Array.from(s).map(f=>dt(f)).filter(f=>!!f);for(let f of m){let g=Pn(f),h;try{h=await this.github.listOpenPullsForRepo(c,f)}catch(I){Go.warn({err:I instanceof Error?I.message:String(I),repo:g},"Failed to list open PRs");continue}let T=new Set(h.map(I=>I.number)),R=u.get(g)?.ownPrsOnly??!0,S=R?await d():null,P=new Set(h.filter(I=>!R||S!==null&&I.author.trim().toLowerCase()===S).map(I=>I.number)),F=i.filter(I=>I.repo===g);for(let I of F)if(!T.has(I.number)&&I.status!=="archived"){let A;if(this.github.fetchPullCloseState)try{A=await this.github.fetchPullCloseState(c,{owner:f.owner,repo:f.repo,number:I.number})}catch(v){await this.storage.addLog(I.id,"warn",`Could not confirm merge state before archival: ${St(v)}`,{phase:"watcher"})}await this.storage.updatePR(I.id,{status:"archived"}),await this.supersedeActiveHealingSessionsForArchivedPr(I.id),await this.storage.addLog(I.id,"info",`PR #${I.number} is no longer open on GitHub \u2014 archived`,{phase:"watcher"});let j=u.get(g)?.autoCreateReleases??!0;if(A?.merged&&this.releaseManager&&n.autoCreateReleases)if(!j)await this.storage.addLog(I.id,"info",`PR #${I.number} was merged, but auto-release is disabled for ${g}`,{phase:"watcher"});else{let v=A.baseRef.trim(),L=A.mergeCommitSha||A.headSha,w=A.mergedAt||A.closedAt;if(!v||!L||!w){let k=[v?null:"a base branch",L?null:"a commit SHA",w?null:"a merge timestamp"].filter(x=>!!x);await this.storage.addLog(I.id,"warn",`PR #${I.number} was merged, but release evaluation was not queued because GitHub did not return ${k.join(" and ")}.`,{phase:"watcher",metadata:{baseBranch:v,headSha:A.headSha,mergeCommitSha:A.mergeCommitSha,mergedAt:A.mergedAt,closedAt:A.closedAt}})}else try{await this.releaseManager.enqueueMergedPullReleaseEvaluation({repo:g,baseBranch:v,triggerPrNumber:A.number,triggerPrTitle:A.title,triggerPrUrl:A.url,triggerMergeSha:L,triggerMergedAt:w}),await this.storage.addLog(I.id,"info",`PR #${I.number} was merged \u2014 queued release evaluation`,{phase:"watcher",metadata:{baseBranch:v,triggerMergeSha:L}})}catch(k){await this.storage.addLog(I.id,"warn",`PR #${I.number} was merged, but release evaluation could not be queued: ${St(k)}`,{phase:"watcher"})}}else A&&!A.merged&&await this.storage.addLog(I.id,"info",`PR #${I.number} closed without merge`,{phase:"watcher"});if(A?.merged&&this.deploymentHealingManager&&this.scheduleBackgroundJob&&n.autoHealDeployments){let v=A.baseRef.trim(),L=A.mergeCommitSha||A.headSha;if(v&&L)try{let w=await this.github.resolveGitHubAuthToken(n),k=ql(g,w),{repoCacheDir:x}=await Wl({repoFullName:g,repoCloneUrl:k,runCommand:this.runtime.runCommand}),C=await uP(x);C&&(await this.scheduleBackgroundJob("heal_deployment",`${g}:${L}`,Or("heal_deployment",`${g}:${L}`),{repo:g,platform:C.platform,mergeSha:L,triggerPrNumber:I.number,triggerPrTitle:I.title,triggerPrUrl:I.url,baseBranch:v,...On({label:`Healing ${C.platform} deployment`,detail:`${g} PR #${I.number} - ${I.title}`,targetUrl:I.url})}),await this.storage.addLog(I.id,"info",`PR #${I.number} merged \u2014 queued deployment healing (${C.platform})`,{phase:"watcher",metadata:{platform:C.platform,mergeSha:L}}))}catch(w){await this.storage.addLog(I.id,"warn",`Failed to queue deployment healing: ${St(w)}`,{phase:"watcher"})}}}for(let I of h){let A=await this.storage.getPRByRepoAndNumber(g,I.number);if(!A){if(!P.has(I.number))continue;A=await this.storage.addPR({number:I.number,title:I.title,repo:g,branch:I.branch,author:I.author,url:I.url,status:"watching",feedbackItems:[],accepted:0,rejected:0,flagged:0,testsPassed:null,lintPassed:null,lastChecked:null}),await this.storage.addLog(A.id,"info",`Auto-registered open PR #${I.number} from ${g}`)}P.has(I.number)&&A.watchEnabled&&(await this.storage.addLog(A.id,"info","Watcher queued autonomous babysitter run",{phase:"watcher",metadata:{repo:g}}),this.scheduleBackgroundJob?await this.scheduleBackgroundJob("babysit_pr",A.id,Or("babysit_pr",A.id),{preferredAgent:n.codingAgent,...On({label:`Babysitting PR #${A.number}`,detail:`${A.repo} - ${A.title}`,targetUrl:A.url})}):await this.babysitPR(A.id,n.codingAgent))}}}async pollForCICompletion(e,r,n,i,s,a){let u=this.runtime.ciPollIntervalMs??3e4;for(let c=1;c<=10;c++){await EP(u);try{let l=await this.github.checkCISettled(e,r,i),p=await this.github.listFailingStatuses(e,r,i);if(await a(s,"info",`CI poll attempt ${c}/10: ${p.length} failure(s), settled=${l}`,{phase:"verify.ci",metadata:{attempt:c,failures:p.length,settled:l}}),l)return p.length>0?{status:"failure",failures:p}:{status:"success",failures:[]}}catch(l){await a(s,"warn",`CI poll attempt ${c} failed: ${St(l)}`,{phase:"verify.ci",metadata:{attempt:c}})}}try{let c=await this.github.listFailingStatuses(e,r,i);if(c.length>0)return{status:"failure",failures:c}}catch(c){await a(s,"warn",`Final CI status check after timeout failed: ${St(c)}`,{phase:"verify.ci"})}return{status:"timeout",failures:[]}}async babysitPR(e,r,n){if((await this.storage.getRuntimeState()).drainMode&&!n?.allowDuringDrain){let A=await this.storage.getPR(e);A&&await this.storage.addLog(A.id,"warn","Babysitter run skipped because drain mode is enabled",{phase:"run"});return}if(this.inProgress.has(e)){let A=await this.storage.getPR(e);A&&await this.storage.addLog(A.id,"warn","Babysitter run skipped because another run is already in progress",{phase:"run"});return}this.inProgress.add(e);let s=n?.runId||(0,TP.randomUUID)(),a=Math.floor(Date.now()/1e3)*1e3-1e3,o=Promise.resolve(),u=new Date().toISOString(),c=await this.storage.getAgentRun(s)||{id:s,prId:e,preferredAgent:r,resolvedAgent:n?.forceResolvedAgent??null,status:"running",phase:"run.started",prompt:n?.forceAgentPrompt??null,initialHeadSha:n?.replayInitialHeadSha??null,metadata:{recoveryMode:!!n?.recoveryMode},lastError:null,createdAt:u,updatedAt:u};await this.storage.upsertAgentRun(c);let l=async A=>{c={...c,...A,updatedAt:new Date().toISOString()},await this.storage.upsertAgentRun(c)},p=(A,j,v,L)=>(o=o.then(async()=>{await this.storage.addLog(A,j,v,{runId:s,phase:L?.phase??null,metadata:L?.metadata??null})}).catch(w=>{Go.warn({err:w instanceof Error?w.message:String(w)},"Babysitter log write failed")}),o),d=async(A,j,v,L)=>{await p(A,"warn",v,{phase:j,metadata:L??null})},m=(A,j,v,L,w)=>{let k="",x=0,C=!1,_=D=>w!==void 0&&x>=w?C?o:(C=!0,p(A,L,`[${v}] output truncated after ${w} line(s)`,{phase:j,metadata:{stream:v,truncated:!0,maxLines:w}})):(x+=1,p(A,L,`[${v}] ${D}`,{phase:j,metadata:{stream:v}}));return{onChunk:D=>{let E=wY(k,D);k=E.buffer;for(let U of E.lines){let Z=U.trim();Z&&_(Z)}},flush:async()=>{let D=k.trim();if(k="",D){await _(D);return}await o}}},f=async A=>{let{currentPrId:j,command:v,args:L,cwd:w,timeoutMs:k,phase:x,successMessage:C,maxOutputLogLines:_}=A;await p(j,"info",`Running ${Yl(v,L)}`,{phase:x});let D=m(j,x,"stdout","info",_),E=m(j,x,"stderr","warn",_),U=await this.runtime.runCommand(v,L,{cwd:w,timeoutMs:k,onStdoutChunk:D.onChunk,onStderrChunk:E.onChunk});return await D.flush(),await E.flush(),U.code===0?await p(j,"info",C,{phase:x,metadata:{command:Yl(v,L),code:U.code}}):await p(j,"error",`${Yl(v,L)} failed (${U.code})`,{phase:x,metadata:{command:Yl(v,L),code:U.code,summary:qi(U)}}),U},g=async A=>{let j=await f({currentPrId:A.currentPrId,command:"git",args:["status","--porcelain"],cwd:A.cwd,timeoutMs:5e3,phase:A.phase,successMessage:"Collected worktree git status"});if(j.code!==0)throw new Error(Mi("checking worktree status",j));if(!j.stdout.trim())return await p(A.currentPrId,"info",`No worktree changes to commit after ${A.context}`,{phase:A.phase}),!1;let v=await f({currentPrId:A.currentPrId,command:"git",args:["add","-A"],cwd:A.cwd,timeoutMs:3e4,phase:A.phase,successMessage:"Staged worktree changes"});if(v.code!==0)throw new Error(Mi("staging worktree changes",v));let L=await f({currentPrId:A.currentPrId,command:"git",args:A.commitArgs,cwd:A.cwd,timeoutMs:6e4,phase:A.phase,successMessage:"Committed worktree changes"});if(L.code!==0)throw new Error(Mi("committing worktree changes",L));return!0},h=[],T=n?.forceAgentPrompt??null,R=n?.forceResolvedAgent??null,S=n?.replayInitialHeadSha??null,P=!!n?.recoveryMode,F=!1,I=!1;try{await l({status:"running",phase:"run.started",metadata:{...c.metadata??{},recoveryMode:P},lastError:null}),await this.storage.updatePR(e,{status:"processing",lastChecked:new Date().toISOString()}),await p(e,"info",`Babysitter run started using preferred agent ${r}${P?" (recovery)":""}`,{phase:"run",metadata:{preferredAgent:r,recoveryMode:P}});let A=await this.storage.getConfig(),j=R||r;await this.ensureAgentHealthy(j,[e]),await l({phase:"run.sync"});let v=await this.syncFeedbackForPR(e,{runId:s,logStart:!0,phase:"sync"}),L=R||await this.runtime.resolveAgent(r,{allowFallback:A.fallbackToNextCodingAgent});L!==j&&await this.ensureAgentHealthy(L,[v.id]),await l({resolvedAgent:L});let w=dt(v.repo);if(!w)throw new Error(`Invalid repository slug: ${v.repo}`);await p(v.id,"info",`Resolved coding agent to ${L}`,{phase:"run"});let k=!1;if(!R&&A.fallbackToNextCodingAgent&&L!==r){k=!0;let O=`Configured coding agent ${r} CLI is not installed`;await p(v.id,"warn",`Falling back from ${r} to ${L} because ${r} is not working: ${O}`,{phase:"run",metadata:{failedAgent:r,fallbackAgent:L}}),await l({metadata:{...c.metadata??{},fallbackFromAgent:r,fallbackReason:O}})}let x=async(O,J)=>{if(!A.fallbackToNextCodingAgent||R||k||!nE(O))return!1;let H=L,me=vY(H),K;try{K=await this.runtime.resolveAgent(me,{allowFallback:!1})}catch{return!1}k=!0,L=K;let Te=St(O);return await p(v.id,"warn",`Falling back from ${H} to ${L} because ${H} is not working: ${Te}`,{phase:J,metadata:{failedAgent:H,fallbackAgent:L}}),await l({resolvedAgent:L,metadata:{...c.metadata??{},fallbackFromAgent:H,fallbackReason:Te}}),!0},C=async O=>{try{return await this.runtime.evaluateFixNecessityWithAgent({agent:L,cwd:O.cwd,prompt:O.prompt})}catch(J){if(await x(J,O.phase))return this.runtime.evaluateFixNecessityWithAgent({agent:L,cwd:O.cwd,prompt:O.prompt});throw J}},_=await this.github.buildOctokit(A),D={owner:w.owner,repo:w.repo,number:v.number},E=await this.github.fetchPullSummary(_,D);T&&S&&E.headSha!==S&&(F=!0,await p(v.id,"warn",`Skipping forced prompt replay because PR head moved (${S.slice(0,7)} -> ${E.headSha.slice(0,7)})`,{phase:"run.replay",metadata:{replayInitialHeadSha:S,currentHeadSha:E.headSha}}));let U=await this.github.listFailingStatuses(_,w,E.headSha),Z=this.now().toISOString(),Ee=(this.github.fetchCheckSnapshotsForRef?await this.github.fetchCheckSnapshotsForRef(_,w,v.id,E.headSha):U.map(O=>({id:`${O.context}:${O.description}:${E.headSha}`,prId:v.id,sha:E.headSha,provider:"github.commit_status",context:O.context,status:"failure",conclusion:null,description:O.description,targetUrl:O.targetUrl,observedAt:Z}))).filter(O=>Sm(O)),fe=k0(Ee),oe=fe.filter(O=>O.classification==="healable_in_branch"),pe=fe.filter(O=>O.classification==="blocked_external"),Ie=new Bl(this.storage,()=>this.now()),G=null;if(Ee.length>0)G=await Ie.ensureSessionForHead({prId:v.id,repo:v.repo,prNumber:v.number,headSha:E.headSha}),await vP(this.storage,Ee),await wP(this.storage,G.id,E.headSha,fe),ea(G.state)?await p(v.id,"info",`CI healing session ${G.id} is already ${G.state}; skipping repair transition`,{phase:"healing.state",metadata:{healingSessionId:G.id,healingState:G.state,headSha:E.headSha}}):pe.length>0?G=await Ie.markBlocked(G.id,pe[0]?.summary??"CI failure classified as blocked_external",{latestFingerprint:pe[0]?.fingerprint??null,currentHeadSha:E.headSha}):oe.length>0?G=await Ie.markAwaitingRepairSlot(G.id,{latestFingerprint:oe[0]?.fingerprint??null,currentHeadSha:E.headSha}):G=await Ie.markEscalated(G.id,"CI failure could not be classified as healable in branch",{latestFingerprint:fe[0]?.fingerprint??null,currentHeadSha:E.headSha});else{let O=await Ie.getSessionByPrAndHead(v.id,E.headSha);O&&["triaging","awaiting_repair_slot","repairing","awaiting_ci","verifying","cooldown"].includes(O.state)?(G=await Ie.markVerifying(O.id,{currentHeadSha:E.headSha}),G=await Ie.markHealed(G.id,{currentHeadSha:E.headSha,lastImprovementScore:G.lastImprovementScore??0})):G=O??null}let le=A.includeRepositoryLinksInGitHubComments,Ir=A.postGitHubProgressReplies,mt=new Map(Object.entries(hP(c.metadata))),it=async(O,J)=>{let H={...hP(c.metadata),[O]:J};await l({metadata:{...c.metadata??{},statusReplyRefs:H}})},sp=async O=>{let J=mt.get(O);if(J)return J;let H=v.feedbackItems.find(K=>K.id===O);if(!H)return null;let me=bP(H,v.feedbackItems);return me?(mt.set(O,me),await it(O,me),me):null},Zr=async(O,J)=>{let H=await sp(O);if(H)try{let me=xP(H.body,J);await this.github.updateStatusReply(_,D,H,me),await it(O,H)}catch(me){let K=v.feedbackItems.find(je=>je.id===O),Te=K?bP(K,v.feedbackItems):null;if(Te&&Te.commentDatabaseId!==H.commentDatabaseId)try{let je=xP(Te.body,J);await this.github.updateStatusReply(_,D,Te,je),mt.set(O,Te),await it(O,Te);return}catch(je){await d(v.id,"github.status",`Failed to update recovered status reply for ${O}: ${St(je)}`,{feedbackId:O});return}await d(v.id,"github.status",`Failed to update status reply for ${O}: ${St(me)}`,{feedbackId:O})}},aa=async(O,J)=>{try{await this.github.postPRComment(_,D,hY(O,J,le))}catch(H){await d(v.id,"github.agent-command",`Failed to post agent command comment: ${St(H)}`)}},U0=async O=>{let{phase:J,onFallback:H,...me}=O,K=await this.runtime.applyFixesWithAgent({agent:L,...me});if(K.code===0)return K;let Te=new Error(`${L} apply failed (${K.code}): ${K.stderr||K.stdout}`);return await x(Te,J)?(await H?.(L),await p(v.id,"info",`Launching ${L} after fallback`,{phase:J,metadata:{agent:L,prompt:O.prompt}}),await aa(L,O.prompt),this.runtime.applyFixesWithAgent({agent:L,...me})):K},G0=v.feedbackItems.filter(O=>O.status==="pending");await p(v.id,"info",`Evaluating ${G0.length} pending feedback item(s)`,{phase:"evaluate.comments"});let oa=new Map;for(let O of G0){if(await p(v.id,"info",`Inspecting feedback from ${O.author}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,file:O.file,line:O.line}}),gY(O.body)){oa.set(O.id,Ni(O,!1,"oh-my-pr-authored agent command comment; no code change required")),await p(v.id,"info",`Ignored self-authored agent command comment ${O.id}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"reject"}});continue}if(oY(O,v.feedbackItems)){oa.set(O.id,Ni(O,!1,"Automation audit trail follow-up; no code change required")),await p(v.id,"info",`Ignored audit-trail follow-up comment ${O.id}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"reject"}});continue}let J=QQ({pr:v,item:O});await p(v.id,"info",`Evaluating feedback ${O.id} with ${L}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,agent:L,prompt:J}});let H=await C({cwd:process.cwd(),prompt:J,phase:"evaluate.comments"}),me=Ni(O,H.needsFix,H.reason);if(oa.set(O.id,me),H.needsFix){await p(v.id,"info",`Accepted feedback ${O.id}: ${H.reason}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"accept"}});try{await this.github.addReactionToComment(_,D,O,"eyes")}catch(K){await d(v.id,"github.reaction",`Failed to add reaction for ${O.id}: ${St(K)}`,{feedbackId:O.id})}if(Ir)try{let K=await this.github.postStatusReplyForFeedbackItem(_,D,O,bY(na.accepted,le));K&&(mt.set(O.id,K),await it(O.id,K))}catch(K){await d(v.id,"github.status",`Failed to post status reply for ${O.id}: ${St(K)}`,{feedbackId:O.id})}}else await p(v.id,"info",`Rejected feedback ${O.id}: ${H.reason}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"reject"}})}let Dn=[];if(A.autoHealCI&&G)await p(v.id,"info",`Skipping legacy status-task evaluation because CI healing session ${G.id} is active`,{phase:"evaluate.status",metadata:{healingSessionId:G.id,healingState:G.state}});else{await p(v.id,"info",`Evaluating ${U.length} failing status check(s)`,{phase:"evaluate.status"});for(let O of U){let J=YQ({pr:v,context:O.context,description:O.description,targetUrl:O.targetUrl});await p(v.id,"info",`Evaluating failing status ${O.context} with ${L}`,{phase:"evaluate.status",metadata:{context:O.context,agent:L,prompt:J}});let H=await C({cwd:process.cwd(),prompt:J,phase:"evaluate.status"});H.needsFix?(Dn.push(O),await p(v.id,"info",`Accepted failing status ${O.context}: ${H.reason}`,{phase:"evaluate.status",metadata:{context:O.context,decision:"accept"}})):await p(v.id,"info",`Rejected failing status ${O.context}: ${H.reason}`,{phase:"evaluate.status",metadata:{context:O.context,decision:"reject"}})}}if(oa.size>0){let O=v.feedbackItems.map(me=>oa.get(me.id)??me),J=Fi(O),H=await this.storage.updatePR(v.id,{feedbackItems:O,accepted:J.accepted,rejected:J.rejected,flagged:J.flagged});H&&(v=H)}let mC=v.feedbackItems.filter(O=>O.status==="queued"&&O.decision==="accept"),B0=T?v.feedbackItems.filter(O=>(O.status==="queued"||O.status==="in_progress")&&O.decision==="accept"):[],Jt=B0.length>0?B0:mC;h=pY(v);let H0=E.mergeable===!1,pr=H0&&A.autoResolveMergeConflicts;H0&&!A.autoResolveMergeConflicts&&await p(v.id,"warn",`PR #${v.number} has merge conflicts but auto-resolve is disabled in settings`,{phase:"conflict",metadata:{baseRef:E.baseRef,mergeable:E.mergeable}});let Bi=!!(T&&!F),ap=!!(T&&F),op=!ap&&(Jt.length>0||Dn.length>0||Bi),ua=AP(v,E.headSha),Nn=!ap&&!Bi&&eY(v,E.headSha,A.autoUpdateDocs),Bo=A.autoUpdateDocs&&ua?.status==="needed"?ua.summary:null,Xt=!!Bo,$0=op||pr||Nn||Xt,Ho=null,z0=!!(A.autoHealCI&&G&&G.state==="awaiting_repair_slot"&&!ap&&!op&&!pr&&!Nn&&!Xt);if(z0&&G){let O=oe[0]?.fingerprint??G.latestFingerprint??void 0,J=await Ie.canRetry(G.id,O);J.canRetry||(Ho=J.reason??"retry budget rejected the healing attempt",G=await Ie.markEscalated(G.id,Ho,{currentHeadSha:E.headSha,latestFingerprint:O??G.latestFingerprint}),await p(v.id,"warn",`CI healing retry skipped: ${Ho}`,{phase:"healing.retry",metadata:{healingSessionId:G.id,fingerprint:O??null,sessionAttempts:J.sessionAttempts,fingerprintAttempts:J.fingerprintAttempts,maxSessionAttempts:J.maxSessionAttempts,maxFingerprintAttempts:J.maxFingerprintAttempts}}))}let W0=!!(z0&&G?.state==="awaiting_repair_slot"&&!Ho);if(A.autoUpdateDocs&&ua&&!Nn&&await p(v.id,"info",`Documentation assessment already recorded for ${E.headSha.slice(0,7)} (${ua.status})`,{phase:"evaluate.docs",metadata:{headSha:E.headSha,status:ua.status}}),!$0&&!W0&&h.length===0&&!pr&&Ee.length===0){await p(v.id,"info",`Babysitter checked PR #${v.number}; no necessary fixes identified`,{phase:"run"}),await this.storage.updatePR(v.id,{status:"watching",lastChecked:new Date().toISOString()}),await l({status:"completed",phase:"run.completed",lastError:null});return}let Le=E.headSha;I=!1;let $o=null,up=new Map,Kt=null,cp=!1,ca=null,Pe=null;if(pr&&await p(v.id,"info",`PR #${v.number} has merge conflicts with base branch ${E.baseRef}`,{phase:"conflict",metadata:{baseRef:E.baseRef,mergeable:E.mergeable}}),$0||pr){await p(v.id,"info",`Babysitter preparing fix run with ${Jt.length} comment task(s), ${Dn.length} status task(s), ${Xt?1:0} documentation task(s), and ${h.length} GitHub follow-up task(s)${Nn?", with documentation assessment":""}${pr?", plus merge conflict resolution":""}${Bi?", with forced prompt replay":""} using ${L}`,{phase:"run",metadata:{commentTasks:Jt.length,statusTasks:Dn.length,docsTasks:Xt?1:0,docsAssessmentNeeded:Nn,followUpTasks:h.length,hasConflicts:pr,shouldRunForcedReplay:Bi,agent:L}});let O=Ct();await p(v.id,"info",`Preparing worktree in ${O.rootDir}`,{phase:"worktree"});let{repoCacheDir:J,worktreePath:H,healed:me,remoteName:K}=await Vl({rootDir:O.rootDir,repoFullName:E.repoFullName,repoCloneUrl:E.repoCloneUrl,headRepoFullName:E.headRepoFullName,headRepoCloneUrl:E.headRepoCloneUrl,headRef:E.headRef,prNumber:v.number,runId:s,runCommand:this.runtime.runCommand});$o=K;try{await p(v.id,"info",`Worktree ready at ${H}`,{phase:"worktree",metadata:{remoteName:K,healed:me}}),me&&await p(v.id,"info","Repo cache required auto-heal before the worktree was created",{phase:"worktree",metadata:{repoCacheDir:J}}),await p(v.id,"info",`Prepared PR head from remote ${K}`,{phase:"worktree",metadata:{remoteName:K,headRef:E.headRef}}),await p(v.id,"info","Ensuring git identity",{phase:"git.identity"}),await xY(H,this.runtime.runCommand),await p(v.id,"info","Git identity ready",{phase:"git.identity"});let Te=await(this.runtime.checkDependencyPreflight??IP)({cwd:H,pr:v,pullSummary:E,requiresVerification:op||pr||Nn||Xt,runCommand:this.runtime.runCommand});if(!Te.ok)throw await p(v.id,"error",`Dependency preflight failed: ${Te.reason}`,{phase:"preflight.dependencies",metadata:{headSha:E.headSha,reason:Te.reason}}),new Error(`Dependency preflight failed: ${Te.reason}`);if(await p(v.id,"info","Dependency preflight passed",{phase:"preflight.dependencies"}),Nn){await p(v.id,"info","Documentation assessment started",{phase:"evaluate.docs",metadata:{headSha:E.headSha}});try{let ze=await f({currentPrId:v.id,command:"git",args:["fetch","origin",E.baseRef],cwd:H,timeoutMs:12e4,phase:"evaluate.docs",successMessage:`Fetched origin/${E.baseRef} for docs assessment`});if(ze.code!==0)throw new Error(`Failed to fetch origin/${E.baseRef} for docs assessment: ${qi(ze)}`);let Ue=await f({currentPrId:v.id,command:"git",args:["diff","--name-only",`origin/${E.baseRef}...HEAD`],cwd:H,timeoutMs:1e4,phase:"evaluate.docs",successMessage:"Collected changed files for docs assessment"});if(Ue.code!==0)throw new Error(`Failed to collect changed files for docs assessment: ${qi(Ue)}`);let Ge=await f({currentPrId:v.id,command:"git",args:["diff","--stat",`origin/${E.baseRef}...HEAD`],cwd:H,timeoutMs:1e4,phase:"evaluate.docs",successMessage:"Collected diff stat for docs assessment"});if(Ge.code!==0)throw new Error(`Failed to collect diff stat for docs assessment: ${qi(Ge)}`);let st=await f({currentPrId:v.id,command:"git",args:["diff","--no-color","--unified=0",`origin/${E.baseRef}...HEAD`],cwd:H,timeoutMs:1e4,phase:"evaluate.docs",successMessage:"Collected diff preview for docs assessment",maxOutputLogLines:20});if(st.code!==0)throw new Error(`Failed to collect diff preview for docs assessment: ${qi(st)}`);let at=ZQ({pr:v,pullSummary:E,changedFiles:Ue.stdout.trim(),diffStat:Ge.stdout.trim(),diffPreview:st.stdout.trim()});await p(v.id,"info",`Evaluating documentation needs with ${L}`,{phase:"evaluate.docs",metadata:{agent:L,prompt:at}});let gt=await C({cwd:H,prompt:at,phase:"evaluate.docs"}),kt={headSha:E.headSha,status:gt.needsFix?"needed":"not_needed",summary:gt.reason,assessedAt:new Date().toISOString()},Rt=await this.storage.updatePR(v.id,{docsAssessment:kt});Rt&&(v=Rt),Xt=gt.needsFix,Bo=gt.needsFix?gt.reason:null,await p(v.id,"info",gt.needsFix?`Documentation updates required: ${gt.reason}`:`Documentation updates not required: ${gt.reason}`,{phase:"evaluate.docs",metadata:{decision:gt.needsFix?"needed":"not_needed",headSha:E.headSha}})}catch(ze){let Ue=St(ze),Ge={headSha:E.headSha,status:"failed",summary:Ue,assessedAt:new Date().toISOString()},st=await this.storage.updatePR(v.id,{docsAssessment:Ge});st&&(v=st),Xt=!1,Bo=null,await p(v.id,"warn",`Documentation assessment failed: ${Ue}`,{phase:"evaluate.docs",metadata:{headSha:E.headSha}})}}if(pr){await p(v.id,"info",`Fetching base branch origin/${E.baseRef} for merge`,{phase:"conflict"});let ze=await f({currentPrId:v.id,command:"git",args:["fetch","origin",E.baseRef],cwd:H,timeoutMs:12e4,phase:"conflict",successMessage:`Fetched origin/${E.baseRef}`});if(ze.code!==0)throw new Error(`Failed to fetch base branch origin/${E.baseRef}: ${qi(ze)}`);await p(v.id,"info",`Attempting merge of origin/${E.baseRef} into ${E.headRef}`,{phase:"conflict"});let Ue=await this.runtime.runCommand("git",["merge","FETCH_HEAD","--no-edit"],{cwd:H,timeoutMs:6e4});if(Ue.code!==0){await p(v.id,"info","Merge produced conflicts; invoking agent to resolve them",{phase:"conflict"});let st=(await this.runtime.runCommand("git",["diff","--name-only","--diff-filter=U"],{cwd:H,timeoutMs:1e4})).stdout.trim().split(`
1071
- `).filter(pp=>pp.trim().length>0);if(st.length===0)throw new Error(`Merge failed but no conflict files detected: ${Ue.stderr||Ue.stdout}`);await p(v.id,"info",`Found ${st.length} file(s) with merge conflicts`,{phase:"conflict",metadata:{conflictFiles:st}});let at=m(v.id,"conflict.agent","stdout","info"),gt=m(v.id,"conflict.agent","stderr","warn"),kt=await this.github.resolveGitHubAuthToken(A),Rt=kt?{...process.env,GITHUB_TOKEN:kt,GH_TOKEN:kt}:void 0,Fn=tY({pr:v,pullSummary:E,remoteName:K,conflictFiles:st});await p(v.id,"info",`Launching ${L} to resolve merge conflicts`,{phase:"conflict.agent",metadata:{agent:L,prompt:Fn}}),await aa(L,Fn);let Mn=await U0({cwd:H,prompt:Fn,env:Rt,onStdoutChunk:at.onChunk,onStderrChunk:gt.onChunk,phase:"conflict.agent"});if(await at.flush(),await gt.flush(),Mn.code!==0){let pp=_P(Mn.stderr||Mn.stdout);throw new Error(`Agent failed to resolve merge conflicts (${Mn.code}): ${pp}`)}await p(v.id,"info","Agent completed merge conflict resolution",{phase:"conflict.agent",metadata:{code:Mn.code}});let Wo=await this.runtime.runCommand("git",["diff","--name-only","--diff-filter=U"],{cwd:H,timeoutMs:5e3});if(Wo.code!==0)throw new Error(Mi("checking unresolved merge conflicts",Wo));if(Wo.stdout.trim())throw new Error(`Agent left unresolved merge conflicts: ${Wo.stdout.trim()}`);await g({currentPrId:v.id,cwd:H,commitArgs:["commit","--no-edit"],phase:"conflict",context:"merge conflict resolution"}),await p(v.id,"info","Merge conflicts resolved and committed by babysitter",{phase:"conflict"})}else await p(v.id,"info","Merge completed without conflicts (GitHub mergeability may have been stale)",{phase:"conflict"})}if(Jt.length>0){let ze=new Set(Jt.map(at=>at.id)),Ue=v.feedbackItems.map(at=>ze.has(at.id)?dP(at):at),Ge=Fi(Ue),st=await this.storage.updatePR(v.id,{feedbackItems:Ue,accepted:Ge.accepted,rejected:Ge.rejected,flagged:Ge.flagged});st&&(v=st)}if(Bi||Jt.length>0||Dn.length>0||Xt){let ze=m(v.id,"agent","stdout","info"),Ue=m(v.id,"agent","stderr","warn"),Ge=await this.github.resolveGitHubAuthToken(A),st=Ge?{...process.env,GITHUB_TOKEN:Ge,GH_TOKEN:Ge}:void 0,at=Bi&&T?T:rY({pr:v,pullSummary:E,remoteName:K,commentTasks:Jt,statusTasks:Dn,docsTaskSummary:Bo});await l({phase:"run.prompt-prepared",prompt:at,initialHeadSha:S||E.headSha}),await p(v.id,"info",`Launching ${L} in autonomous mode`,{phase:"agent",metadata:{githubAuth:!!Ge,prompt:at}}),await l({phase:"run.agent-running"}),await aa(L,at);let gt=na.agentRunning(L);await Promise.all(Jt.map(Rt=>Zr(Rt.id,gt)));let kt=await U0({cwd:H,prompt:at,env:st,onStdoutChunk:ze.onChunk,onStderrChunk:Ue.onChunk,phase:"agent",onFallback:async Rt=>{let Fn=na.agentRunning(Rt);await Promise.all(Jt.map(Mn=>Zr(Mn.id,Fn)))}});if(await ze.flush(),await Ue.flush(),kt.code!==0){let Rt=_P(kt.stderr||kt.stdout);throw await Promise.all(Jt.map(Fn=>Zr(Fn.id,na.agentFailed(L,Rt)))),new Error(`${L} apply failed (${kt.code}): ${Rt}`)}await Promise.all(Jt.map(Rt=>Zr(Rt.id,na.agentCompleted))),up=sY(kt.stdout),Kt=aY(kt.stdout),await p(v.id,"info",`${L} completed successfully`,{phase:"agent",metadata:{code:kt.code,extractedSummaries:up.size,docsTaskOutcome:Kt?.outcome??null}}),await l({phase:"run.agent-finished"})}await g({currentPrId:v.id,cwd:H,commitArgs:["commit","-m",`Apply babysitter fixes for PR #${v.number}`],phase:"verify.git.status",context:"agent run"}),await p(v.id,"info","Worktree is clean after babysitter finalization",{phase:"verify.git.status"});let je=await f({currentPrId:v.id,command:"git",args:["rev-parse","HEAD"],cwd:H,timeoutMs:5e3,phase:"verify.git.local-head",successMessage:"Collected worktree HEAD"});if(je.code!==0)throw new Error(`git rev-parse HEAD failed: ${je.stderr||je.stdout}`);let Qt=await f({currentPrId:v.id,command:"git",args:["-C",J,"fetch",K,E.headRef],timeoutMs:12e4,phase:"verify.git.fetch-head",successMessage:`Fetched ${K}/${E.headRef} for verification`});if(Qt.code!==0)throw new Error(`git fetch ${K} ${E.headRef} failed: ${Qt.stderr||Qt.stdout}`);let zo=await f({currentPrId:v.id,command:"git",args:["-C",J,"rev-parse","FETCH_HEAD"],timeoutMs:5e3,phase:"verify.git.remote-head",successMessage:"Collected remote PR head SHA"});if(zo.code!==0)throw new Error(`git rev-parse FETCH_HEAD failed: ${zo.stderr||zo.stdout}`);let la=je.stdout.trim(),pa=zo.stdout.trim(),lp=la!==E.headSha;if(lp&&pa!==la){let ze=await f({currentPrId:v.id,command:"git",args:["push",K,`HEAD:${E.headRef}`],cwd:H,timeoutMs:12e4,phase:"verify.git.push",successMessage:`Pushed babysitter commit to ${K}/${E.headRef}`});if(ze.code!==0)throw new Error(Mi(`pushing ${K}/${E.headRef}`,ze));let Ue=await f({currentPrId:v.id,command:"git",args:["-C",J,"fetch",K,E.headRef],timeoutMs:12e4,phase:"verify.git.fetch-head",successMessage:`Fetched ${K}/${E.headRef} after babysitter push`});if(Ue.code!==0)throw new Error(Mi(`fetching ${K}/${E.headRef} after push`,Ue));let Ge=await f({currentPrId:v.id,command:"git",args:["-C",J,"rev-parse","FETCH_HEAD"],timeoutMs:5e3,phase:"verify.git.remote-head",successMessage:"Collected remote PR head SHA after babysitter push"});if(Ge.code!==0)throw new Error(Mi("reading remote PR head after push",Ge));pa=Ge.stdout.trim()}if(lp&&pa!==la)throw new Error("Babysitter created a local commit but could not verify it on the PR head branch");if(I=pa!==E.headSha,Dn.length>0&&!I)throw new Error("Agent did not update the PR head branch for accepted failing status tasks");if(Xt&&!Kt)throw new Error("Agent did not report documentation task outcome");if(Xt&&!I&&Kt?.outcome!=="no_change")throw new Error("Agent did not update the PR head branch for required documentation tasks");if(pr&&!I)throw new Error("Conflict resolution was not pushed to the PR head branch");if(Xt&&Kt?.outcome==="no_change"){let ze=await this.storage.updatePR(v.id,{docsAssessment:{headSha:E.headSha,status:"not_needed",summary:Kt.summary,assessedAt:new Date().toISOString()}});ze&&(v=ze)}Le=la,await p(v.id,"info","Verified git branch state after agent run",{phase:"verify.git",metadata:{initialHeadSha:E.headSha,localHeadSha:la,remoteHeadSha:pa,branchMoved:I,localCommitCreated:lp,remoteName:K,docsTaskOutcome:Kt?.outcome??null}}),Kt&&await p(v.id,"info",`Documentation task outcome: ${Kt.outcome} - ${Kt.summary}`,{phase:"verify.docs",metadata:{outcome:Kt.outcome,branchMoved:I}})}finally{try{await p(v.id,"info","Cleaning up worktree",{phase:"cleanup"}),await Jl({repoCacheDir:J,worktreePath:H,runCommand:this.runtime.runCommand}),await p(v.id,"info","Worktree cleanup complete",{phase:"cleanup"})}catch(Te){let je=Te instanceof Error?Te.message:String(Te);await p(v.id,"error",`Worktree cleanup failed: ${je}`,{phase:"cleanup"})}}}else await p(v.id,"info",`Babysitter found ${h.length} accepted feedback item(s) awaiting GitHub follow-up`,{phase:"run",metadata:{followUpTasks:h.length,agent:L}});if(W0&&G){let O=oe.map(K=>K.fingerprint);await p(v.id,"info",`Launching dedicated CI healing attempt for ${O.length} fingerprint(s)`,{phase:"healing.run",metadata:{healingSessionId:G.id,targetFingerprints:O}}),G=await Ie.markRepairing(G.id,{currentHeadSha:E.headSha,latestFingerprint:O[0]??G.latestFingerprint});let J=await this.github.resolveGitHubAuthToken(A),H=J?{...process.env,GITHUB_TOKEN:J,GH_TOKEN:J}:void 0;if(Pe=await this.runtime.runCIHealingRepairAttempt?.({prNumber:v.number,repoFullName:E.repoFullName,repoCloneUrl:E.repoCloneUrl,headRepoFullName:E.headRepoFullName,headRepoCloneUrl:E.headRepoCloneUrl,headRef:E.headRef,baseRef:E.baseRef,headSha:E.headSha,title:E.title,url:E.url,author:E.author,branch:v.branch,agent:L,failures:oe,runId:`${s}-ci-healing`,rootDir:Ct().rootDir,env:H})??null,!Pe)throw new Error("CI healing runtime is unavailable");ca=(await this.storage.createHealingAttempt({sessionId:G.id,attemptNumber:G.attemptCount,inputSha:E.headSha,outputSha:Pe.accepted?Pe.verification.remoteHeadSha:null,status:Pe.accepted?"awaiting_ci":"failed",endedAt:Pe.accepted?null:new Date().toISOString(),agent:L,promptDigest:Pe.promptDigest,targetFingerprints:Pe.targetFingerprints,summary:Pe.summary,improvementScore:null,error:Pe.rejectionReason})).id,cp=!0,Pe.accepted?(G=await Ie.markAwaitingCi(G.id,{currentHeadSha:Pe.verification.remoteHeadSha,latestFingerprint:Pe.targetFingerprints[0]??G.latestFingerprint}),I=Pe.verification.pushedNewSha,Le=Pe.verification.remoteHeadSha,$o=Pe.remoteName):G=await Ie.markEscalated(G.id,Pe.rejectionReason??"CI healing attempt failed",{currentHeadSha:E.headSha,latestFingerprint:Pe.targetFingerprints[0]??G.latestFingerprint})}await l({phase:"run.reconcile"});for(let O of h){let J=!D0(O,v.feedbackItems),H=Uo(Ql(O));if(J){await p(v.id,"info",`Posting GitHub follow-up for ${O.id}${H?" and resolving conversation":""}`,{phase:"github.followup",metadata:{feedbackId:O.id,replyKind:O.replyKind,resolve:H}});let me=fY(Le,O,le,up.get(O.auditToken));await this.github.postFollowUpForFeedbackItem(_,D,O,me,{resolve:H})}else if(H){if(!O.threadId){await p(v.id,"warn",`Cannot resolve review thread for ${O.id}: thread ID unavailable (skipping)`,{phase:"github.followup",metadata:{feedbackId:O.id}});continue}await this.github.resolveReviewThread(_,D,O.threadId)}await p(v.id,"info",`GitHub follow-up complete for ${O.id}`,{phase:"github.followup",metadata:{feedbackId:O.id,replyKind:O.replyKind,posted:J,resolved:H}}),await Zr(O.id,na.resolved(Le))}v=await this.syncFeedbackForPR(v.id,{runId:s,logStart:!0,phase:"verify.sync"});let V0=cY({pr:v,followUpTasks:h,runStartedAtMs:a});if(V0.length>0)throw new Me(`GitHub audit trail verification failed: ${V0.join("; ")}`,502);if(await p(v.id,"info","GitHub audit trail verified",{phase:"verify.github",metadata:{verifiedComments:h.length,remoteName:$o,branchMoved:I}}),h.length>0){let O=new Set(h.map(K=>K.id)),J=v.feedbackItems.map(K=>O.has(K.id)?Ql(K):K),H=Fi(J),me=await this.storage.updatePR(v.id,{feedbackItems:J,accepted:H.accepted,rejected:H.rejected,flagged:H.flagged});me&&(v=me)}if(I&&Le){await p(v.id,"info","Waiting for CI/CD checks on new commit...",{phase:"verify.ci",metadata:{headSha:Le}});let O=await this.pollForCICompletion(_,w,D,Le,v.id,p);if(O.status==="failure"){let J=O.failures.map(H=>`${H.context}: ${H.description}`).join("; ");await p(v.id,"warn",`CI/CD still failing after agent fix: ${J}`,{phase:"verify.ci",metadata:{failures:O.failures}});try{let H=[`## \u26A0\uFE0F ${PP(le)} CI Alert`,"",`The babysitter pushed changes (commit \`${Le.slice(0,7)}\`), but CI/CD checks are still failing:`,"",...O.failures.map(me=>`- **${me.context}**: ${me.description}${me.targetUrl?` ([details](${me.targetUrl}))`:""}`),"","Manual investigation may be required.",...le?["",In]:[]].join(`
1070
+ ${In}`:In}function Yl(t,e){return[t,...e].join(" ")}function qi(t){return t.stderr.trim()||t.stdout.trim()||"no output"}function yY(t){return/\b(Could not resolve host|ENOTFOUND|EAI_AGAIN|ECONNRESET|network error|Failed to connect|Connection timed out)\b/i.test(t)}function Mi(t,e){let r=qi(e);return yY(r)?`Infrastructure Git failure while ${t}: ${r}`:`${t} failed: ${r}`}function St(t){return t instanceof Error?t.message:String(t)}function vY(t){return t==="claude"?"codex":"claude"}function _P(t){let n=(t.replace(/^(?:claude|codex|agent) apply failed \(\d+\):\s*/i,"").replace(/^Agent failed to resolve merge conflicts \(\d+\):\s*/i,"").replace(/^Error:\s*/i,"").split(/\r?\n/).map(s=>s.trim()).find(s=>s.length>0)??"No failure details were reported").replace(/\s+/g," "),i=n.length>180?`${n.slice(0,177).trimEnd()}...`:n;return/[.!?]$/.test(i)?i:`${i}.`}function wY(t,e){let n=`${t}${e}`.split(/\r?\n/);return{lines:n.slice(0,-1),buffer:n.at(-1)??""}}async function xY(t,e){let r=await e("git",["config","--get","user.name"],{cwd:t,timeoutMs:3e3});(r.code!==0||!r.stdout.trim())&&await e("git",["config","user.name",GQ],{cwd:t,timeoutMs:3e3});let n=await e("git",["config","--get","user.email"],{cwd:t,timeoutMs:3e3});(n.code!==0||!n.stdout.trim())&&await e("git",["config","user.email",BQ],{cwd:t,timeoutMs:3e3})}async function ia(t){try{return await(0,SP.access)(t),!0}catch(e){if(e instanceof Error&&"code"in e&&(e.code==="ENOENT"||e.code==="ENOTDIR"))return!1;throw e}}async function IP(t){if(!t.requiresVerification)return{ok:!0};let e=ji.default.join(t.cwd,"package.json");if(!await ia(e))return{ok:!0};let r={packageLock:await ia(ji.default.join(t.cwd,"package-lock.json")),npmShrinkwrap:await ia(ji.default.join(t.cwd,"npm-shrinkwrap.json")),pnpmLock:await ia(ji.default.join(t.cwd,"pnpm-lock.yaml")),yarnLock:await ia(ji.default.join(t.cwd,"yarn.lock"))};if(!r.packageLock&&!r.npmShrinkwrap&&!r.pnpmLock&&!r.yarnLock)return{ok:!0};if(!await ia(ji.default.join(t.cwd,"node_modules"))){if((await t.runCommand("git",["check-ignore","-q","node_modules/"],{cwd:t.cwd,timeoutMs:5e3})).code!==0)return{ok:!1,reason:"Node dependency cache is missing, but node_modules is not ignored by git; refusing to install dependencies automatically"};let i=r.pnpmLock?{command:"pnpm",args:["install","--frozen-lockfile"]}:r.yarnLock?{command:"yarn",args:["install","--frozen-lockfile"]}:{command:"npm",args:["ci"]},s=await t.runCommand(i.command,i.args,{cwd:t.cwd,timeoutMs:3e5});if(s.code!==0)return{ok:!1,reason:pn(s,`Dependency install failed while running ${i.command} ${i.args.join(" ")}`)}}return{ok:!0}}function EP(t){return new Promise(e=>setTimeout(e,t))}var Zl=class{storage;inProgress=new Set;feedbackMutationLocks=new Map;github;runtime;releaseManager;deploymentHealingManager;scheduleBackgroundJob;clock;agentHealthCache=new Map;constructor(e,r=WQ,n=VQ,i,s,a){this.storage=e,this.github=r,this.runtime=n,this.releaseManager=i,this.deploymentHealingManager=a,this.scheduleBackgroundJob=s,this.clock=n.now??(()=>new Date)}now(){return this.clock()}async readAgentHealth(e){let r=this.now().getTime(),n=this.agentHealthCache.get(e);if(n&&r-n.checkedAtMs<HQ)return n.result;let i=this.runtime.checkAgentHealth?await this.runtime.checkAgentHealth(e):{ok:!0};return this.agentHealthCache.set(e,{checkedAtMs:r,result:i}),i}async pauseAutomationForAgentFailure(e,r,n){let i=`Agent health check failed for ${e}: ${r}`;await this.storage.updateRuntimeState({drainMode:!0,drainRequestedAt:this.now().toISOString(),drainReason:i}),await Promise.all(n.map(async s=>{let a=await this.storage.getPR(s);if(a){let o=a.feedbackItems.map(u=>u.decision==="accept"&&(u.status==="queued"||u.status==="in_progress")?I0(u,i):u);if(o.some((u,c)=>u!==a.feedbackItems[c])){let u=Fi(o);await this.storage.updatePR(a.id,{feedbackItems:o,accepted:u.accepted,rejected:u.rejected,flagged:u.flagged})}}await this.storage.addLog(s,"error",`Automation paused: ${i}`,{phase:"agent.health"})}))}async ensureAgentHealthy(e,r){let n=await this.readAgentHealth(e);if(!n.ok)throw await this.pauseAutomationForAgentFailure(e,n.reason,r),new Error(`Agent health check failed for ${e}: ${n.reason}`)}async supersedeActiveHealingSessionsForArchivedPr(e){let r=await this.storage.listHealingSessions({prId:e}),n=this.now().toISOString();for(let i of r)ea(i.state)||await this.storage.updateHealingSession(i.id,{state:"superseded",endedAt:n,escalationReason:"PR archived on GitHub"})}getActiveRunCount(){return this.inProgress.size}async waitForIdle(e=12e4){let r=Date.now();for(;this.inProgress.size>0;){if(Date.now()-r>=e)return!1;await EP(100)}return!0}async runQueuedBabysitPR(e,r){let n=(await this.storage.listAgentRuns({status:"running",prId:e})).slice().sort((s,a)=>Date.parse(a.updatedAt)-Date.parse(s.updatedAt))[0];if(!n){await this.babysitPR(e,r,{allowDuringDrain:!0,rethrowOnFailure:!0});return}if(!!!(n.prompt&&n.resolvedAgent&&n.initialHeadSha)){let s=new Date().toISOString();await this.storage.upsertAgentRun({...n,status:"failed",phase:"run.failed",lastError:"Interrupted run missing replay context",updatedAt:s}),await this.babysitPR(e,r,{allowDuringDrain:!0,rethrowOnFailure:!0});return}await this.babysitPR(e,n.preferredAgent,{runId:n.id,recoveryMode:!0,forceAgentPrompt:n.prompt,forceResolvedAgent:n.resolvedAgent,replayInitialHeadSha:n.initialHeadSha,allowDuringDrain:!0,rethrowOnFailure:!0})}async resumeInterruptedRuns(){let e=await this.storage.listAgentRuns({status:"running"});if(e.length!==0){if(this.scheduleBackgroundJob){let r=new Set;for(let n of e)r.has(n.prId)||(r.add(n.prId),await this.scheduleBackgroundJob("babysit_pr",n.prId,Or("babysit_pr",n.prId),{preferredAgent:n.preferredAgent}));return}for(let r of e){if(!!!(r.prompt&&r.resolvedAgent&&r.initialHeadSha)){let i=new Date().toISOString();await this.storage.upsertAgentRun({...r,status:"failed",phase:"run.failed",lastError:"Interrupted run missing replay context",updatedAt:i}),await this.babysitPR(r.prId,r.preferredAgent,{allowDuringDrain:!0});continue}await this.babysitPR(r.prId,r.preferredAgent,{runId:r.id,recoveryMode:!0,forceAgentPrompt:r.prompt,forceResolvedAgent:r.resolvedAgent,replayInitialHeadSha:r.initialHeadSha,allowDuringDrain:!0})}}}async withFeedbackMutationLock(e,r){let n=this.feedbackMutationLocks.get(e)??Promise.resolve(),i,s=new Promise(o=>{i=()=>o()}),a=n.then(()=>s);this.feedbackMutationLocks.set(e,a),await n;try{return await r()}finally{i?.(),this.feedbackMutationLocks.get(e)===a&&this.feedbackMutationLocks.delete(e)}}async retryFeedbackItem(e,r){return this.withFeedbackMutationLock(e,async()=>{let n=await this.storage.getPR(e);if(!n)return{kind:"pr_not_found"};let i=n.feedbackItems.find(u=>u.id===r);if(!i)return{kind:"feedback_not_found"};if(i.status!=="failed"&&i.status!=="warning")return{kind:"feedback_not_retryable"};let s=n.feedbackItems.map(u=>u.id===r?mP(u):u),a=Fi(s),o=await this.storage.updatePR(n.id,{feedbackItems:s,accepted:a.accepted,rejected:a.rejected,flagged:a.flagged});if(!o)throw new Error(`Failed to queue retry for feedback item ${r} on PR ${e}`);return{kind:"ok",updated:o}})}async syncFeedbackForPR(e,r){let n=await this.storage.getPR(e);if(!n)throw new Error("PR not found");let i=dt(n.repo);if(!i)throw new Error(`Invalid repository slug: ${n.repo}`);let s={owner:i.owner,repo:i.repo,number:n.number},a=await this.storage.getConfig(),o=await this.github.buildOctokit(a),u=r?.phase??"sync";r?.logStart&&await this.storage.addLog(n.id,"info","Syncing GitHub comments/reviews...",{runId:r.runId??null,phase:u});let c=await this.github.fetchFeedbackItemsForPR(o,s,a),{merged:l,newCount:p}=JQ(n.feedbackItems,c),d=Fi(l),m=await this.storage.updatePR(n.id,{title:n.title,status:n.status,lastChecked:new Date().toISOString(),feedbackItems:l,accepted:d.accepted,rejected:d.rejected,flagged:d.flagged});if(!m)throw new Error("Failed to update PR after feedback sync");return await this.storage.addLog(n.id,"info",KQ(c.length,p),{runId:r?.runId??null,phase:u,metadata:{total:c.length,newCount:p}}),m}async syncAndBabysitTrackedRepos(){if((await this.storage.getRuntimeState()).drainMode)return;let r=[...await this.storage.getPRs(),...await this.storage.getArchivedPRs()].filter(XQ);for(let f of r)try{await this.syncFeedbackForPR(f.id,{phase:"repair"})}catch(g){await this.storage.addLog(f.id,"warn",`Could not repair missing GitHub review-thread metadata: ${St(g)}`,{phase:"repair"})}let n=await this.storage.getConfig(),i=await this.storage.getPRs(),s=new Set([...i.map(f=>f.repo),...n.watchedRepos]),a=n.codingAgent,o=i.filter(f=>f.watchEnabled!==!1&&f.status!=="archived").map(f=>f.id);if(s.size>0)try{await this.ensureAgentHealthy(a,o)}catch{return}let u=new Map((await this.storage.listRepoSettings()).map(f=>[f.repo,f])),c=await this.github.buildOctokit(n),l=Array.from(s).some(f=>u.get(f)?.ownPrsOnly??!0),p=null,d=async()=>l?(p||(p=(this.github.getAuthenticatedLogin?.(c)??Promise.resolve(null)).catch(f=>(Go.warn({err:f instanceof Error?f.message:String(f)},"Failed to determine authenticated GitHub login for watcher filtering"),null))),p):null,m=Array.from(s).map(f=>dt(f)).filter(f=>!!f);for(let f of m){let g=Pn(f),h;try{h=await this.github.listOpenPullsForRepo(c,f)}catch(I){Go.warn({err:I instanceof Error?I.message:String(I),repo:g},"Failed to list open PRs");continue}let T=new Set(h.map(I=>I.number)),R=u.get(g)?.ownPrsOnly??!0,S=R?await d():null,P=new Set(h.filter(I=>!R||S!==null&&I.author.trim().toLowerCase()===S).map(I=>I.number)),F=i.filter(I=>I.repo===g);for(let I of F)if(!T.has(I.number)&&I.status!=="archived"){let A;if(this.github.fetchPullCloseState)try{A=await this.github.fetchPullCloseState(c,{owner:f.owner,repo:f.repo,number:I.number})}catch(v){await this.storage.addLog(I.id,"warn",`Could not confirm merge state before archival: ${St(v)}`,{phase:"watcher"})}await this.storage.updatePR(I.id,{status:"archived"}),await this.supersedeActiveHealingSessionsForArchivedPr(I.id),await this.storage.addLog(I.id,"info",`PR #${I.number} is no longer open on GitHub \u2014 archived`,{phase:"watcher"});let j=u.get(g)?.autoCreateReleases??!0;if(A?.merged&&this.releaseManager&&n.autoCreateReleases)if(!j)await this.storage.addLog(I.id,"info",`PR #${I.number} was merged, but auto-release is disabled for ${g}`,{phase:"watcher"});else{let v=A.baseRef.trim(),L=A.mergeCommitSha||A.headSha,w=A.mergedAt||A.closedAt;if(!v||!L||!w){let k=[v?null:"a base branch",L?null:"a commit SHA",w?null:"a merge timestamp"].filter(x=>!!x);await this.storage.addLog(I.id,"warn",`PR #${I.number} was merged, but release evaluation was not queued because GitHub did not return ${k.join(" and ")}.`,{phase:"watcher",metadata:{baseBranch:v,headSha:A.headSha,mergeCommitSha:A.mergeCommitSha,mergedAt:A.mergedAt,closedAt:A.closedAt}})}else try{await this.releaseManager.enqueueMergedPullReleaseEvaluation({repo:g,baseBranch:v,triggerPrNumber:A.number,triggerPrTitle:A.title,triggerPrUrl:A.url,triggerMergeSha:L,triggerMergedAt:w}),await this.storage.addLog(I.id,"info",`PR #${I.number} was merged \u2014 queued release evaluation`,{phase:"watcher",metadata:{baseBranch:v,triggerMergeSha:L}})}catch(k){await this.storage.addLog(I.id,"warn",`PR #${I.number} was merged, but release evaluation could not be queued: ${St(k)}`,{phase:"watcher"})}}else A&&!A.merged&&await this.storage.addLog(I.id,"info",`PR #${I.number} closed without merge`,{phase:"watcher"});if(A?.merged&&this.deploymentHealingManager&&this.scheduleBackgroundJob&&n.autoHealDeployments){let v=A.baseRef.trim(),L=A.mergeCommitSha||A.headSha;if(v&&L)try{let w=await this.github.resolveGitHubAuthToken(n),k=ql(g,w),{repoCacheDir:x}=await Wl({repoFullName:g,repoCloneUrl:k,runCommand:this.runtime.runCommand}),C=await uP(x);C&&(await this.scheduleBackgroundJob("heal_deployment",`${g}:${L}`,Or("heal_deployment",`${g}:${L}`),{repo:g,platform:C.platform,mergeSha:L,triggerPrNumber:I.number,triggerPrTitle:I.title,triggerPrUrl:I.url,baseBranch:v,...On({label:`Healing ${C.platform} deployment`,detail:`${g} PR #${I.number} - ${I.title}`,targetUrl:I.url})}),await this.storage.addLog(I.id,"info",`PR #${I.number} merged \u2014 queued deployment healing (${C.platform})`,{phase:"watcher",metadata:{platform:C.platform,mergeSha:L}}))}catch(w){await this.storage.addLog(I.id,"warn",`Failed to queue deployment healing: ${St(w)}`,{phase:"watcher"})}}}for(let I of h){let A=await this.storage.getPRByRepoAndNumber(g,I.number);if(!A){if(!P.has(I.number))continue;A=await this.storage.addPR({number:I.number,title:I.title,repo:g,branch:I.branch,author:I.author,url:I.url,status:"watching",feedbackItems:[],accepted:0,rejected:0,flagged:0,testsPassed:null,lintPassed:null,lastChecked:null}),await this.storage.addLog(A.id,"info",`Auto-registered open PR #${I.number} from ${g}`)}P.has(I.number)&&A.watchEnabled&&(await this.storage.addLog(A.id,"info","Watcher queued autonomous babysitter run",{phase:"watcher",metadata:{repo:g}}),this.scheduleBackgroundJob?await this.scheduleBackgroundJob("babysit_pr",A.id,Or("babysit_pr",A.id),{preferredAgent:n.codingAgent,...On({label:`Babysitting PR #${A.number}`,detail:`${A.repo} - ${A.title}`,targetUrl:A.url})}):await this.babysitPR(A.id,n.codingAgent))}}}async pollForCICompletion(e,r,n,i,s,a){let u=this.runtime.ciPollIntervalMs??3e4;for(let c=1;c<=10;c++){await EP(u);try{let l=await this.github.checkCISettled(e,r,i),p=await this.github.listFailingStatuses(e,r,i);if(await a(s,"info",`CI poll attempt ${c}/10: ${p.length} failure(s), settled=${l}`,{phase:"verify.ci",metadata:{attempt:c,failures:p.length,settled:l}}),l)return p.length>0?{status:"failure",failures:p}:{status:"success",failures:[]}}catch(l){await a(s,"warn",`CI poll attempt ${c} failed: ${St(l)}`,{phase:"verify.ci",metadata:{attempt:c}})}}try{let c=await this.github.listFailingStatuses(e,r,i);if(c.length>0)return{status:"failure",failures:c}}catch(c){await a(s,"warn",`Final CI status check after timeout failed: ${St(c)}`,{phase:"verify.ci"})}return{status:"timeout",failures:[]}}async babysitPR(e,r,n){if((await this.storage.getRuntimeState()).drainMode&&!n?.allowDuringDrain){let A=await this.storage.getPR(e);A&&await this.storage.addLog(A.id,"warn","Babysitter run skipped because drain mode is enabled",{phase:"run"});return}if(this.inProgress.has(e)){let A=await this.storage.getPR(e);A&&await this.storage.addLog(A.id,"warn","Babysitter run skipped because another run is already in progress",{phase:"run"});return}this.inProgress.add(e);let s=n?.runId||(0,TP.randomUUID)(),a=Math.floor(Date.now()/1e3)*1e3-1e3,o=Promise.resolve(),u=new Date().toISOString(),c=await this.storage.getAgentRun(s)||{id:s,prId:e,preferredAgent:r,resolvedAgent:n?.forceResolvedAgent??null,status:"running",phase:"run.started",prompt:n?.forceAgentPrompt??null,initialHeadSha:n?.replayInitialHeadSha??null,metadata:{recoveryMode:!!n?.recoveryMode},lastError:null,createdAt:u,updatedAt:u};await this.storage.upsertAgentRun(c);let l=async A=>{c={...c,...A,updatedAt:new Date().toISOString()},await this.storage.upsertAgentRun(c)},p=(A,j,v,L)=>(o=o.then(async()=>{await this.storage.addLog(A,j,v,{runId:s,phase:L?.phase??null,metadata:L?.metadata??null})}).catch(w=>{Go.warn({err:w instanceof Error?w.message:String(w)},"Babysitter log write failed")}),o),d=async(A,j,v,L)=>{await p(A,"warn",v,{phase:j,metadata:L??null})},m=(A,j,v,L,w)=>{let k="",x=0,C=!1,_=D=>w!==void 0&&x>=w?C?o:(C=!0,p(A,L,`[${v}] output truncated after ${w} line(s)`,{phase:j,metadata:{stream:v,truncated:!0,maxLines:w}})):(x+=1,p(A,L,`[${v}] ${D}`,{phase:j,metadata:{stream:v}}));return{onChunk:D=>{let E=wY(k,D);k=E.buffer;for(let U of E.lines){let Z=U.trim();Z&&_(Z)}},flush:async()=>{let D=k.trim();if(k="",D){await _(D);return}await o}}},f=async A=>{let{currentPrId:j,command:v,args:L,cwd:w,timeoutMs:k,phase:x,successMessage:C,maxOutputLogLines:_}=A;await p(j,"info",`Running ${Yl(v,L)}`,{phase:x});let D=m(j,x,"stdout","info",_),E=m(j,x,"stderr","warn",_),U=await this.runtime.runCommand(v,L,{cwd:w,timeoutMs:k,onStdoutChunk:D.onChunk,onStderrChunk:E.onChunk});return await D.flush(),await E.flush(),U.code===0?await p(j,"info",C,{phase:x,metadata:{command:Yl(v,L),code:U.code}}):await p(j,"error",`${Yl(v,L)} failed (${U.code})`,{phase:x,metadata:{command:Yl(v,L),code:U.code,summary:qi(U)}}),U},g=async A=>{let j=await f({currentPrId:A.currentPrId,command:"git",args:["status","--porcelain"],cwd:A.cwd,timeoutMs:5e3,phase:A.phase,successMessage:"Collected worktree git status"});if(j.code!==0)throw new Error(Mi("checking worktree status",j));if(!j.stdout.trim())return await p(A.currentPrId,"info",`No worktree changes to commit after ${A.context}`,{phase:A.phase}),!1;let v=await f({currentPrId:A.currentPrId,command:"git",args:["add","-A"],cwd:A.cwd,timeoutMs:3e4,phase:A.phase,successMessage:"Staged worktree changes"});if(v.code!==0)throw new Error(Mi("staging worktree changes",v));let L=await f({currentPrId:A.currentPrId,command:"git",args:A.commitArgs,cwd:A.cwd,timeoutMs:6e4,phase:A.phase,successMessage:"Committed worktree changes"});if(L.code!==0)throw new Error(Mi("committing worktree changes",L));return!0},h=[],T=n?.forceAgentPrompt??null,R=n?.forceResolvedAgent??null,S=n?.replayInitialHeadSha??null,P=!!n?.recoveryMode,F=!1,I=!1;try{await l({status:"running",phase:"run.started",metadata:{...c.metadata??{},recoveryMode:P},lastError:null}),await this.storage.updatePR(e,{status:"processing",lastChecked:new Date().toISOString()}),await p(e,"info",`Babysitter run started using preferred agent ${r}${P?" (recovery)":""}`,{phase:"run",metadata:{preferredAgent:r,recoveryMode:P}});let A=await this.storage.getConfig(),j=R||r;await this.ensureAgentHealthy(j,[e]),await l({phase:"run.sync"});let v=await this.syncFeedbackForPR(e,{runId:s,logStart:!0,phase:"sync"}),L=R||await this.runtime.resolveAgent(r,{allowFallback:A.fallbackToNextCodingAgent});L!==j&&await this.ensureAgentHealthy(L,[v.id]),await l({resolvedAgent:L});let w=dt(v.repo);if(!w)throw new Error(`Invalid repository slug: ${v.repo}`);await p(v.id,"info",`Resolved coding agent to ${L}`,{phase:"run"});let k=!1;if(!R&&A.fallbackToNextCodingAgent&&L!==r){k=!0;let O=`Configured coding agent ${r} CLI is not installed`;await p(v.id,"warn",`Falling back from ${r} to ${L} because ${r} is not working: ${O}`,{phase:"run",metadata:{failedAgent:r,fallbackAgent:L}}),await l({metadata:{...c.metadata??{},fallbackFromAgent:r,fallbackReason:O}})}let x=async(O,J)=>{if(!A.fallbackToNextCodingAgent||R||k||!nE(O))return!1;let H=L,me=vY(H),K;try{K=await this.runtime.resolveAgent(me,{allowFallback:!1})}catch{return!1}k=!0,L=K;let Te=St(O);return await p(v.id,"warn",`Falling back from ${H} to ${L} because ${H} is not working: ${Te}`,{phase:J,metadata:{failedAgent:H,fallbackAgent:L}}),await l({resolvedAgent:L,metadata:{...c.metadata??{},fallbackFromAgent:H,fallbackReason:Te}}),!0},C=async O=>{try{return await this.runtime.evaluateFixNecessityWithAgent({agent:L,cwd:O.cwd,prompt:O.prompt})}catch(J){if(await x(J,O.phase))return this.runtime.evaluateFixNecessityWithAgent({agent:L,cwd:O.cwd,prompt:O.prompt});throw J}},_=await this.github.buildOctokit(A),D={owner:w.owner,repo:w.repo,number:v.number},E=await this.github.fetchPullSummary(_,D);T&&S&&E.headSha!==S&&(F=!0,await p(v.id,"warn",`Skipping forced prompt replay because PR head moved (${S.slice(0,7)} -> ${E.headSha.slice(0,7)})`,{phase:"run.replay",metadata:{replayInitialHeadSha:S,currentHeadSha:E.headSha}}));let U=await this.github.listFailingStatuses(_,w,E.headSha),Z=this.now().toISOString(),Ee=(this.github.fetchCheckSnapshotsForRef?await this.github.fetchCheckSnapshotsForRef(_,w,v.id,E.headSha):U.map(O=>({id:`${O.context}:${O.description}:${E.headSha}`,prId:v.id,sha:E.headSha,provider:"github.commit_status",context:O.context,status:"failure",conclusion:null,description:O.description,targetUrl:O.targetUrl,observedAt:Z}))).filter(O=>Sm(O)),fe=k0(Ee),oe=fe.filter(O=>O.classification==="healable_in_branch"),pe=fe.filter(O=>O.classification==="blocked_external"),Ie=new Bl(this.storage,()=>this.now()),G=null;if(Ee.length>0)G=await Ie.ensureSessionForHead({prId:v.id,repo:v.repo,prNumber:v.number,headSha:E.headSha}),await vP(this.storage,Ee),await wP(this.storage,G.id,E.headSha,fe),ea(G.state)?await p(v.id,"info",`CI healing session ${G.id} is already ${G.state}; skipping repair transition`,{phase:"healing.state",metadata:{healingSessionId:G.id,healingState:G.state,headSha:E.headSha}}):pe.length>0?G=await Ie.markBlocked(G.id,pe[0]?.summary??"CI failure classified as blocked_external",{latestFingerprint:pe[0]?.fingerprint??null,currentHeadSha:E.headSha}):oe.length>0?G=await Ie.markAwaitingRepairSlot(G.id,{latestFingerprint:oe[0]?.fingerprint??null,currentHeadSha:E.headSha}):G=await Ie.markEscalated(G.id,"CI failure could not be classified as healable in branch",{latestFingerprint:fe[0]?.fingerprint??null,currentHeadSha:E.headSha});else{let O=await Ie.getSessionByPrAndHead(v.id,E.headSha);O&&["triaging","awaiting_repair_slot","repairing","awaiting_ci","verifying","cooldown"].includes(O.state)?(G=await Ie.markVerifying(O.id,{currentHeadSha:E.headSha}),G=await Ie.markHealed(G.id,{currentHeadSha:E.headSha,lastImprovementScore:G.lastImprovementScore??0})):G=O??null}let le=A.includeRepositoryLinksInGitHubComments,Ir=A.postGitHubProgressReplies,mt=new Map(Object.entries(hP(c.metadata))),it=async(O,J)=>{let H={...hP(c.metadata),[O]:J};await l({metadata:{...c.metadata??{},statusReplyRefs:H}})},sp=async O=>{let J=mt.get(O);if(J)return J;let H=v.feedbackItems.find(K=>K.id===O);if(!H)return null;let me=bP(H,v.feedbackItems);return me?(mt.set(O,me),await it(O,me),me):null},Zr=async(O,J)=>{let H=await sp(O);if(H)try{let me=xP(H.body,J);await this.github.updateStatusReply(_,D,H,me),await it(O,H)}catch(me){let K=v.feedbackItems.find(je=>je.id===O),Te=K?bP(K,v.feedbackItems):null;if(Te&&Te.commentDatabaseId!==H.commentDatabaseId)try{let je=xP(Te.body,J);await this.github.updateStatusReply(_,D,Te,je),mt.set(O,Te),await it(O,Te);return}catch(je){await d(v.id,"github.status",`Failed to update recovered status reply for ${O}: ${St(je)}`,{feedbackId:O});return}await d(v.id,"github.status",`Failed to update status reply for ${O}: ${St(me)}`,{feedbackId:O})}},aa=async(O,J)=>{try{await this.github.postPRComment(_,D,hY(O,J,le))}catch(H){await d(v.id,"github.agent-command",`Failed to post agent command comment: ${St(H)}`)}},U0=async O=>{let{phase:J,onFallback:H,...me}=O,K=await this.runtime.applyFixesWithAgent({agent:L,...me});if(K.code===0)return K;let Te=new Error(`${L} apply failed (${K.code}): ${K.stderr||K.stdout}`);return await x(Te,J)?(await H?.(L),await p(v.id,"info",`Launching ${L} after fallback`,{phase:J,metadata:{agent:L,prompt:O.prompt}}),await aa(L,O.prompt),this.runtime.applyFixesWithAgent({agent:L,...me})):K},G0=v.feedbackItems.filter(O=>O.status==="pending");await p(v.id,"info",`Evaluating ${G0.length} pending feedback item(s)`,{phase:"evaluate.comments"});let oa=new Map;for(let O of G0){if(await p(v.id,"info",`Inspecting feedback from ${O.author}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,file:O.file,line:O.line}}),gY(O.body)){oa.set(O.id,Ni(O,!1,"oh-my-pr-authored agent command comment; no code change required")),await p(v.id,"info",`Ignored self-authored agent command comment ${O.id}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"reject"}});continue}if(oY(O,v.feedbackItems)){oa.set(O.id,Ni(O,!1,"Automation audit trail follow-up; no code change required")),await p(v.id,"info",`Ignored audit-trail follow-up comment ${O.id}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"reject"}});continue}let J=QQ({pr:v,item:O});await p(v.id,"info",`Evaluating feedback ${O.id} with ${L}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,agent:L,prompt:J}});let H=await C({cwd:process.cwd(),prompt:J,phase:"evaluate.comments"}),me=Ni(O,H.needsFix,H.reason);if(oa.set(O.id,me),H.needsFix){await p(v.id,"info",`Accepted feedback ${O.id}: ${H.reason}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"accept"}});try{await this.github.addReactionToComment(_,D,O,"eyes")}catch(K){await d(v.id,"github.reaction",`Failed to add reaction for ${O.id}: ${St(K)}`,{feedbackId:O.id})}if(Ir)try{let K=await this.github.postStatusReplyForFeedbackItem(_,D,O,bY(na.accepted,le));K&&(mt.set(O.id,K),await it(O.id,K))}catch(K){await d(v.id,"github.status",`Failed to post status reply for ${O.id}: ${St(K)}`,{feedbackId:O.id})}}else await p(v.id,"info",`Rejected feedback ${O.id}: ${H.reason}`,{phase:"evaluate.comments",metadata:{feedbackId:O.id,decision:"reject"}})}let Dn=[];if(A.autoHealCI&&G)await p(v.id,"info",`Skipping legacy status-task evaluation because CI healing session ${G.id} is active`,{phase:"evaluate.status",metadata:{healingSessionId:G.id,healingState:G.state}});else{await p(v.id,"info",`Evaluating ${U.length} failing status check(s)`,{phase:"evaluate.status"});for(let O of U){let J=YQ({pr:v,context:O.context,description:O.description,targetUrl:O.targetUrl});await p(v.id,"info",`Evaluating failing status ${O.context} with ${L}`,{phase:"evaluate.status",metadata:{context:O.context,agent:L,prompt:J}});let H=await C({cwd:process.cwd(),prompt:J,phase:"evaluate.status"});H.needsFix?(Dn.push(O),await p(v.id,"info",`Accepted failing status ${O.context}: ${H.reason}`,{phase:"evaluate.status",metadata:{context:O.context,decision:"accept"}})):await p(v.id,"info",`Rejected failing status ${O.context}: ${H.reason}`,{phase:"evaluate.status",metadata:{context:O.context,decision:"reject"}})}}if(oa.size>0){let O=v.feedbackItems.map(me=>oa.get(me.id)??me),J=Fi(O),H=await this.storage.updatePR(v.id,{feedbackItems:O,accepted:J.accepted,rejected:J.rejected,flagged:J.flagged});H&&(v=H)}let mC=v.feedbackItems.filter(O=>O.status==="queued"&&O.decision==="accept"),B0=T?v.feedbackItems.filter(O=>(O.status==="queued"||O.status==="in_progress")&&O.decision==="accept"):[],Jt=B0.length>0?B0:mC;h=pY(v);let H0=E.mergeable===!1,pr=H0&&A.autoResolveMergeConflicts;H0&&!A.autoResolveMergeConflicts&&await p(v.id,"warn",`PR #${v.number} has merge conflicts but auto-resolve is disabled in settings`,{phase:"conflict",metadata:{baseRef:E.baseRef,mergeable:E.mergeable}});let Bi=!!(T&&!F),ap=!!(T&&F),op=!ap&&(Jt.length>0||Dn.length>0||Bi),ua=AP(v,E.headSha),Nn=!ap&&!Bi&&eY(v,E.headSha,A.autoUpdateDocs),Bo=A.autoUpdateDocs&&ua?.status==="needed"?ua.summary:null,Xt=!!Bo,$0=op||pr||Nn||Xt,Ho=null,z0=!!(A.autoHealCI&&G&&G.state==="awaiting_repair_slot"&&!ap&&!op&&!pr&&!Nn&&!Xt);if(z0&&G){let O=oe[0]?.fingerprint??G.latestFingerprint??void 0,J=await Ie.canRetry(G.id,O);J.canRetry||(Ho=J.reason??"retry budget rejected the healing attempt",G=await Ie.markEscalated(G.id,Ho,{currentHeadSha:E.headSha,latestFingerprint:O??G.latestFingerprint}),await p(v.id,"warn",`CI healing retry skipped: ${Ho}`,{phase:"healing.retry",metadata:{healingSessionId:G.id,fingerprint:O??null,sessionAttempts:J.sessionAttempts,fingerprintAttempts:J.fingerprintAttempts,maxSessionAttempts:J.maxSessionAttempts,maxFingerprintAttempts:J.maxFingerprintAttempts}}))}let W0=!!(z0&&G?.state==="awaiting_repair_slot"&&!Ho);if(A.autoUpdateDocs&&ua&&!Nn&&await p(v.id,"info",`Documentation assessment already recorded for ${E.headSha.slice(0,7)} (${ua.status})`,{phase:"evaluate.docs",metadata:{headSha:E.headSha,status:ua.status}}),!$0&&!W0&&h.length===0&&!pr&&Ee.length===0){await p(v.id,"info",`Babysitter checked PR #${v.number}; no necessary fixes identified`,{phase:"run"}),await this.storage.updatePR(v.id,{status:"watching",lastChecked:new Date().toISOString()}),await l({status:"completed",phase:"run.completed",lastError:null});return}let Le=E.headSha;I=!1;let $o=null,up=new Map,Kt=null,cp=!1,ca=null,Pe=null;if(pr&&await p(v.id,"info",`PR #${v.number} has merge conflicts with base branch ${E.baseRef}`,{phase:"conflict",metadata:{baseRef:E.baseRef,mergeable:E.mergeable}}),$0||pr){await p(v.id,"info",`Babysitter preparing fix run with ${Jt.length} comment task(s), ${Dn.length} status task(s), ${Xt?1:0} documentation task(s), and ${h.length} GitHub follow-up task(s)${Nn?", with documentation assessment":""}${pr?", plus merge conflict resolution":""}${Bi?", with forced prompt replay":""} using ${L}`,{phase:"run",metadata:{commentTasks:Jt.length,statusTasks:Dn.length,docsTasks:Xt?1:0,docsAssessmentNeeded:Nn,followUpTasks:h.length,hasConflicts:pr,shouldRunForcedReplay:Bi,agent:L}});let O=Ct();await p(v.id,"info",`Preparing worktree in ${O.rootDir}`,{phase:"worktree"});let{repoCacheDir:J,worktreePath:H,healed:me,remoteName:K}=await Vl({rootDir:O.rootDir,repoFullName:E.repoFullName,repoCloneUrl:E.repoCloneUrl,headRepoFullName:E.headRepoFullName,headRepoCloneUrl:E.headRepoCloneUrl,headRef:E.headRef,prNumber:v.number,runId:s,runCommand:this.runtime.runCommand});$o=K;try{await p(v.id,"info",`Worktree ready at ${H}`,{phase:"worktree",metadata:{remoteName:K,healed:me}}),me&&await p(v.id,"info","Repo cache required auto-heal before the worktree was created",{phase:"worktree",metadata:{repoCacheDir:J}}),await p(v.id,"info",`Prepared PR head from remote ${K}`,{phase:"worktree",metadata:{remoteName:K,headRef:E.headRef}}),await p(v.id,"info","Ensuring git identity",{phase:"git.identity"}),await xY(H,this.runtime.runCommand),await p(v.id,"info","Git identity ready",{phase:"git.identity"});let Te=await(this.runtime.checkDependencyPreflight??IP)({cwd:H,pr:v,pullSummary:E,requiresVerification:op||pr||Nn||Xt,runCommand:this.runtime.runCommand});if(!Te.ok)throw await p(v.id,"error",`Dependency preflight failed: ${Te.reason}`,{phase:"preflight.dependencies",metadata:{headSha:E.headSha,reason:Te.reason}}),new Error(`Dependency preflight failed: ${Te.reason}`);if(await p(v.id,"info","Dependency preflight passed",{phase:"preflight.dependencies"}),Nn){await p(v.id,"info","Documentation assessment started",{phase:"evaluate.docs",metadata:{headSha:E.headSha}});try{let ze=await f({currentPrId:v.id,command:"git",args:["fetch","origin",E.baseRef],cwd:H,timeoutMs:12e4,phase:"evaluate.docs",successMessage:`Fetched origin/${E.baseRef} for docs assessment`});if(ze.code!==0)throw new Error(`Failed to fetch origin/${E.baseRef} for docs assessment: ${qi(ze)}`);let Ue=await f({currentPrId:v.id,command:"git",args:["diff","--name-only",`origin/${E.baseRef}...HEAD`],cwd:H,timeoutMs:1e4,phase:"evaluate.docs",successMessage:"Collected changed files for docs assessment"});if(Ue.code!==0)throw new Error(`Failed to collect changed files for docs assessment: ${qi(Ue)}`);let Ge=await f({currentPrId:v.id,command:"git",args:["diff","--stat",`origin/${E.baseRef}...HEAD`],cwd:H,timeoutMs:1e4,phase:"evaluate.docs",successMessage:"Collected diff stat for docs assessment"});if(Ge.code!==0)throw new Error(`Failed to collect diff stat for docs assessment: ${qi(Ge)}`);let st=await f({currentPrId:v.id,command:"git",args:["diff","--no-color","--unified=0",`origin/${E.baseRef}...HEAD`],cwd:H,timeoutMs:1e4,phase:"evaluate.docs",successMessage:"Collected diff preview for docs assessment",maxOutputLogLines:20});if(st.code!==0)throw new Error(`Failed to collect diff preview for docs assessment: ${qi(st)}`);let at=ZQ({pr:v,pullSummary:E,changedFiles:Ue.stdout.trim(),diffStat:Ge.stdout.trim(),diffPreview:st.stdout.trim()});await p(v.id,"info",`Evaluating documentation needs with ${L}`,{phase:"evaluate.docs",metadata:{agent:L,prompt:at}});let gt=await C({cwd:H,prompt:at,phase:"evaluate.docs"}),kt={headSha:E.headSha,status:gt.needsFix?"needed":"not_needed",summary:gt.reason,assessedAt:new Date().toISOString()},Rt=await this.storage.updatePR(v.id,{docsAssessment:kt});Rt&&(v=Rt),Xt=gt.needsFix,Bo=gt.needsFix?gt.reason:null,await p(v.id,"info",gt.needsFix?`Documentation updates required: ${gt.reason}`:`Documentation updates not required: ${gt.reason}`,{phase:"evaluate.docs",metadata:{decision:gt.needsFix?"needed":"not_needed",headSha:E.headSha}})}catch(ze){let Ue=St(ze),Ge={headSha:E.headSha,status:"failed",summary:Ue,assessedAt:new Date().toISOString()},st=await this.storage.updatePR(v.id,{docsAssessment:Ge});st&&(v=st),Xt=!1,Bo=null,await p(v.id,"warn",`Documentation assessment failed: ${Ue}`,{phase:"evaluate.docs",metadata:{headSha:E.headSha}})}}if(pr){await p(v.id,"info",`Fetching base branch origin/${E.baseRef} for merge`,{phase:"conflict"});let ze=await f({currentPrId:v.id,command:"git",args:["fetch","origin",E.baseRef],cwd:H,timeoutMs:12e4,phase:"conflict",successMessage:`Fetched origin/${E.baseRef}`});if(ze.code!==0)throw new Error(`Failed to fetch base branch origin/${E.baseRef}: ${qi(ze)}`);await p(v.id,"info",`Attempting merge of origin/${E.baseRef} into ${E.headRef}`,{phase:"conflict"});let Ue=await this.runtime.runCommand("git",["merge","FETCH_HEAD","--no-edit"],{cwd:H,timeoutMs:6e4});if(Ue.code!==0){await p(v.id,"info","Merge produced conflicts; invoking agent to resolve them",{phase:"conflict"});let st=(await this.runtime.runCommand("git",["diff","--name-only","--diff-filter=U"],{cwd:H,timeoutMs:1e4})).stdout.trim().split(`
1071
+ `).filter(pp=>pp.trim().length>0);if(st.length===0)throw new Error(`Merge failed but no conflict files detected: ${Ue.stderr||Ue.stdout}`);await p(v.id,"info",`Found ${st.length} file(s) with merge conflicts`,{phase:"conflict",metadata:{conflictFiles:st}});let at=m(v.id,"conflict.agent","stdout","info"),gt=m(v.id,"conflict.agent","stderr","warn"),kt=await this.github.resolveGitHubAuthToken(A),Rt=kt?{...process.env,GITHUB_TOKEN:kt,GH_TOKEN:kt}:void 0,Fn=tY({pr:v,pullSummary:E,remoteName:K,conflictFiles:st});await p(v.id,"info",`Launching ${L} to resolve merge conflicts`,{phase:"conflict.agent",metadata:{agent:L,prompt:Fn}}),await aa(L,Fn);let Mn=await U0({cwd:H,prompt:Fn,env:Rt,onStdoutChunk:at.onChunk,onStderrChunk:gt.onChunk,phase:"conflict.agent"});if(await at.flush(),await gt.flush(),Mn.code!==0){let pp=_P(Mn.stderr||Mn.stdout);throw new Error(`Agent failed to resolve merge conflicts (${Mn.code}): ${pp}`)}await p(v.id,"info","Agent completed merge conflict resolution",{phase:"conflict.agent",metadata:{code:Mn.code}});let Wo=await this.runtime.runCommand("git",["diff","--name-only","--diff-filter=U"],{cwd:H,timeoutMs:5e3});if(Wo.code!==0)throw new Error(Mi("checking unresolved merge conflicts",Wo));if(Wo.stdout.trim())throw new Error(`Agent left unresolved merge conflicts: ${Wo.stdout.trim()}`);await g({currentPrId:v.id,cwd:H,commitArgs:["commit","--no-verify","--no-edit"],phase:"conflict",context:"merge conflict resolution"}),await p(v.id,"info","Merge conflicts resolved and committed by babysitter",{phase:"conflict"})}else await p(v.id,"info","Merge completed without conflicts (GitHub mergeability may have been stale)",{phase:"conflict"})}if(Jt.length>0){let ze=new Set(Jt.map(at=>at.id)),Ue=v.feedbackItems.map(at=>ze.has(at.id)?dP(at):at),Ge=Fi(Ue),st=await this.storage.updatePR(v.id,{feedbackItems:Ue,accepted:Ge.accepted,rejected:Ge.rejected,flagged:Ge.flagged});st&&(v=st)}if(Bi||Jt.length>0||Dn.length>0||Xt){let ze=m(v.id,"agent","stdout","info"),Ue=m(v.id,"agent","stderr","warn"),Ge=await this.github.resolveGitHubAuthToken(A),st=Ge?{...process.env,GITHUB_TOKEN:Ge,GH_TOKEN:Ge}:void 0,at=Bi&&T?T:rY({pr:v,pullSummary:E,remoteName:K,commentTasks:Jt,statusTasks:Dn,docsTaskSummary:Bo});await l({phase:"run.prompt-prepared",prompt:at,initialHeadSha:S||E.headSha}),await p(v.id,"info",`Launching ${L} in autonomous mode`,{phase:"agent",metadata:{githubAuth:!!Ge,prompt:at}}),await l({phase:"run.agent-running"}),await aa(L,at);let gt=na.agentRunning(L);await Promise.all(Jt.map(Rt=>Zr(Rt.id,gt)));let kt=await U0({cwd:H,prompt:at,env:st,onStdoutChunk:ze.onChunk,onStderrChunk:Ue.onChunk,phase:"agent",onFallback:async Rt=>{let Fn=na.agentRunning(Rt);await Promise.all(Jt.map(Mn=>Zr(Mn.id,Fn)))}});if(await ze.flush(),await Ue.flush(),kt.code!==0){let Rt=_P(kt.stderr||kt.stdout);throw await Promise.all(Jt.map(Fn=>Zr(Fn.id,na.agentFailed(L,Rt)))),new Error(`${L} apply failed (${kt.code}): ${Rt}`)}await Promise.all(Jt.map(Rt=>Zr(Rt.id,na.agentCompleted))),up=sY(kt.stdout),Kt=aY(kt.stdout),await p(v.id,"info",`${L} completed successfully`,{phase:"agent",metadata:{code:kt.code,extractedSummaries:up.size,docsTaskOutcome:Kt?.outcome??null}}),await l({phase:"run.agent-finished"})}await g({currentPrId:v.id,cwd:H,commitArgs:["commit","--no-verify","-m",`Apply babysitter fixes for PR #${v.number}`],phase:"verify.git.status",context:"agent run"}),await p(v.id,"info","Worktree is clean after babysitter finalization",{phase:"verify.git.status"});let je=await f({currentPrId:v.id,command:"git",args:["rev-parse","HEAD"],cwd:H,timeoutMs:5e3,phase:"verify.git.local-head",successMessage:"Collected worktree HEAD"});if(je.code!==0)throw new Error(`git rev-parse HEAD failed: ${je.stderr||je.stdout}`);let Qt=await f({currentPrId:v.id,command:"git",args:["-C",J,"fetch",K,E.headRef],timeoutMs:12e4,phase:"verify.git.fetch-head",successMessage:`Fetched ${K}/${E.headRef} for verification`});if(Qt.code!==0)throw new Error(`git fetch ${K} ${E.headRef} failed: ${Qt.stderr||Qt.stdout}`);let zo=await f({currentPrId:v.id,command:"git",args:["-C",J,"rev-parse","FETCH_HEAD"],timeoutMs:5e3,phase:"verify.git.remote-head",successMessage:"Collected remote PR head SHA"});if(zo.code!==0)throw new Error(`git rev-parse FETCH_HEAD failed: ${zo.stderr||zo.stdout}`);let la=je.stdout.trim(),pa=zo.stdout.trim(),lp=la!==E.headSha;if(lp&&pa!==la){let ze=await f({currentPrId:v.id,command:"git",args:["push",K,`HEAD:${E.headRef}`],cwd:H,timeoutMs:12e4,phase:"verify.git.push",successMessage:`Pushed babysitter commit to ${K}/${E.headRef}`});if(ze.code!==0)throw new Error(Mi(`pushing ${K}/${E.headRef}`,ze));let Ue=await f({currentPrId:v.id,command:"git",args:["-C",J,"fetch",K,E.headRef],timeoutMs:12e4,phase:"verify.git.fetch-head",successMessage:`Fetched ${K}/${E.headRef} after babysitter push`});if(Ue.code!==0)throw new Error(Mi(`fetching ${K}/${E.headRef} after push`,Ue));let Ge=await f({currentPrId:v.id,command:"git",args:["-C",J,"rev-parse","FETCH_HEAD"],timeoutMs:5e3,phase:"verify.git.remote-head",successMessage:"Collected remote PR head SHA after babysitter push"});if(Ge.code!==0)throw new Error(Mi("reading remote PR head after push",Ge));pa=Ge.stdout.trim()}if(lp&&pa!==la)throw new Error("Babysitter created a local commit but could not verify it on the PR head branch");if(I=pa!==E.headSha,Dn.length>0&&!I)throw new Error("Agent did not update the PR head branch for accepted failing status tasks");if(Xt&&!Kt)throw new Error("Agent did not report documentation task outcome");if(Xt&&!I&&Kt?.outcome!=="no_change")throw new Error("Agent did not update the PR head branch for required documentation tasks");if(pr&&!I)throw new Error("Conflict resolution was not pushed to the PR head branch");if(Xt&&Kt?.outcome==="no_change"){let ze=await this.storage.updatePR(v.id,{docsAssessment:{headSha:E.headSha,status:"not_needed",summary:Kt.summary,assessedAt:new Date().toISOString()}});ze&&(v=ze)}Le=la,await p(v.id,"info","Verified git branch state after agent run",{phase:"verify.git",metadata:{initialHeadSha:E.headSha,localHeadSha:la,remoteHeadSha:pa,branchMoved:I,localCommitCreated:lp,remoteName:K,docsTaskOutcome:Kt?.outcome??null}}),Kt&&await p(v.id,"info",`Documentation task outcome: ${Kt.outcome} - ${Kt.summary}`,{phase:"verify.docs",metadata:{outcome:Kt.outcome,branchMoved:I}})}finally{try{await p(v.id,"info","Cleaning up worktree",{phase:"cleanup"}),await Jl({repoCacheDir:J,worktreePath:H,runCommand:this.runtime.runCommand}),await p(v.id,"info","Worktree cleanup complete",{phase:"cleanup"})}catch(Te){let je=Te instanceof Error?Te.message:String(Te);await p(v.id,"error",`Worktree cleanup failed: ${je}`,{phase:"cleanup"})}}}else await p(v.id,"info",`Babysitter found ${h.length} accepted feedback item(s) awaiting GitHub follow-up`,{phase:"run",metadata:{followUpTasks:h.length,agent:L}});if(W0&&G){let O=oe.map(K=>K.fingerprint);await p(v.id,"info",`Launching dedicated CI healing attempt for ${O.length} fingerprint(s)`,{phase:"healing.run",metadata:{healingSessionId:G.id,targetFingerprints:O}}),G=await Ie.markRepairing(G.id,{currentHeadSha:E.headSha,latestFingerprint:O[0]??G.latestFingerprint});let J=await this.github.resolveGitHubAuthToken(A),H=J?{...process.env,GITHUB_TOKEN:J,GH_TOKEN:J}:void 0;if(Pe=await this.runtime.runCIHealingRepairAttempt?.({prNumber:v.number,repoFullName:E.repoFullName,repoCloneUrl:E.repoCloneUrl,headRepoFullName:E.headRepoFullName,headRepoCloneUrl:E.headRepoCloneUrl,headRef:E.headRef,baseRef:E.baseRef,headSha:E.headSha,title:E.title,url:E.url,author:E.author,branch:v.branch,agent:L,failures:oe,runId:`${s}-ci-healing`,rootDir:Ct().rootDir,env:H})??null,!Pe)throw new Error("CI healing runtime is unavailable");ca=(await this.storage.createHealingAttempt({sessionId:G.id,attemptNumber:G.attemptCount,inputSha:E.headSha,outputSha:Pe.accepted?Pe.verification.remoteHeadSha:null,status:Pe.accepted?"awaiting_ci":"failed",endedAt:Pe.accepted?null:new Date().toISOString(),agent:L,promptDigest:Pe.promptDigest,targetFingerprints:Pe.targetFingerprints,summary:Pe.summary,improvementScore:null,error:Pe.rejectionReason})).id,cp=!0,Pe.accepted?(G=await Ie.markAwaitingCi(G.id,{currentHeadSha:Pe.verification.remoteHeadSha,latestFingerprint:Pe.targetFingerprints[0]??G.latestFingerprint}),I=Pe.verification.pushedNewSha,Le=Pe.verification.remoteHeadSha,$o=Pe.remoteName):G=await Ie.markEscalated(G.id,Pe.rejectionReason??"CI healing attempt failed",{currentHeadSha:E.headSha,latestFingerprint:Pe.targetFingerprints[0]??G.latestFingerprint})}await l({phase:"run.reconcile"});for(let O of h){let J=!D0(O,v.feedbackItems),H=Uo(Ql(O));if(J){await p(v.id,"info",`Posting GitHub follow-up for ${O.id}${H?" and resolving conversation":""}`,{phase:"github.followup",metadata:{feedbackId:O.id,replyKind:O.replyKind,resolve:H}});let me=fY(Le,O,le,up.get(O.auditToken));await this.github.postFollowUpForFeedbackItem(_,D,O,me,{resolve:H})}else if(H){if(!O.threadId){await p(v.id,"warn",`Cannot resolve review thread for ${O.id}: thread ID unavailable (skipping)`,{phase:"github.followup",metadata:{feedbackId:O.id}});continue}await this.github.resolveReviewThread(_,D,O.threadId)}await p(v.id,"info",`GitHub follow-up complete for ${O.id}`,{phase:"github.followup",metadata:{feedbackId:O.id,replyKind:O.replyKind,posted:J,resolved:H}}),await Zr(O.id,na.resolved(Le))}v=await this.syncFeedbackForPR(v.id,{runId:s,logStart:!0,phase:"verify.sync"});let V0=cY({pr:v,followUpTasks:h,runStartedAtMs:a});if(V0.length>0)throw new Me(`GitHub audit trail verification failed: ${V0.join("; ")}`,502);if(await p(v.id,"info","GitHub audit trail verified",{phase:"verify.github",metadata:{verifiedComments:h.length,remoteName:$o,branchMoved:I}}),h.length>0){let O=new Set(h.map(K=>K.id)),J=v.feedbackItems.map(K=>O.has(K.id)?Ql(K):K),H=Fi(J),me=await this.storage.updatePR(v.id,{feedbackItems:J,accepted:H.accepted,rejected:H.rejected,flagged:H.flagged});me&&(v=me)}if(I&&Le){await p(v.id,"info","Waiting for CI/CD checks on new commit...",{phase:"verify.ci",metadata:{headSha:Le}});let O=await this.pollForCICompletion(_,w,D,Le,v.id,p);if(O.status==="failure"){let J=O.failures.map(H=>`${H.context}: ${H.description}`).join("; ");await p(v.id,"warn",`CI/CD still failing after agent fix: ${J}`,{phase:"verify.ci",metadata:{failures:O.failures}});try{let H=[`## \u26A0\uFE0F ${PP(le)} CI Alert`,"",`The babysitter pushed changes (commit \`${Le.slice(0,7)}\`), but CI/CD checks are still failing:`,"",...O.failures.map(me=>`- **${me.context}**: ${me.description}${me.targetUrl?` ([details](${me.targetUrl}))`:""}`),"","Manual investigation may be required.",...le?["",In]:[]].join(`
1072
1072
  `);await this.github.postPRComment(_,D,H)}catch(H){await d(v.id,"verify.ci",`Failed to post CI failure alert comment: ${St(H)}`)}if(await this.storage.updatePR(v.id,{testsPassed:!1,lastChecked:new Date().toISOString()}),cp&&Pe&&ca&&G){G=await Ie.markVerifying(G.id,{currentHeadSha:Le});let H=this.now().toISOString(),K=(this.github.fetchCheckSnapshotsForRef?await this.github.fetchCheckSnapshotsForRef(_,w,v.id,Le):O.failures.map(Qt=>({id:`${Qt.context}:${Qt.description}:${Le}`,prId:v.id,sha:Le,provider:"github.commit_status",context:Qt.context,status:"failure",conclusion:null,description:Qt.description,targetUrl:Qt.targetUrl,observedAt:H}))).filter(Qt=>Sm(Qt)),Te=k0(K);await vP(this.storage,K),await wP(this.storage,G.id,Le,Te);let je=dY(Pe.targetFingerprints,Te);await this.storage.updateHealingAttempt(ca,{outputSha:Le,status:"verified",endedAt:new Date().toISOString(),improvementScore:je.improvementScore,error:je.improvementScore>0?null:`CI failures remained unchanged or worsened after repair: ${J}`}),je.improvementScore>0?G=await Ie.markAwaitingRepairSlot(G.id,{currentHeadSha:Le,latestFingerprint:je.remainingTargetFingerprints[0]??Te[0]?.fingerprint??null,lastImprovementScore:je.improvementScore}):G=await Ie.markEscalated(G.id,`CI failures remained unchanged or worsened after repair: ${J}`,{currentHeadSha:Le,latestFingerprint:je.remainingTargetFingerprints[0]??Te[0]?.fingerprint??null,lastImprovementScore:je.improvementScore})}}else O.status==="success"?(await p(v.id,"info","All CI/CD checks passed on new commit",{phase:"verify.ci",metadata:{headSha:Le}}),await this.storage.updatePR(v.id,{testsPassed:!0,lastChecked:new Date().toISOString()}),cp&&Pe&&ca&&G&&(G=await Ie.markVerifying(G.id,{currentHeadSha:Le}),await this.storage.updateHealingAttempt(ca,{outputSha:Le,status:"verified",endedAt:new Date().toISOString(),improvementScore:Math.max(2,Pe.targetFingerprints.length*2),error:null}),G=await Ie.markHealed(G.id,{currentHeadSha:Le,latestFingerprint:null,lastImprovementScore:Math.max(2,Pe.targetFingerprints.length*2)}))):await p(v.id,"info","CI/CD checks did not complete within polling window; will re-check on next cycle",{phase:"verify.ci",metadata:{headSha:Le}})}await this.storage.updatePR(v.id,{status:"watching",lastChecked:new Date().toISOString()}),await p(v.id,"info","Babysitter run complete",{phase:"run",metadata:{remoteName:$o,branchMoved:I}}),await l({status:"completed",phase:"run.completed",lastError:null});return}catch(A){let j=A instanceof Error?A.message:String(A);await l({status:"failed",phase:"run.failed",lastError:j});let v=await this.storage.getPR(e);if(v){let k=A instanceof Me&&I,x=k?"warn":"error",C=k?"Babysitter warning":"Babysitter error";if(await p(v.id,x,`${C}: ${j}`,{phase:"run"}),h.length>0){let _=new Set(h.map(U=>U.id)),D=v.feedbackItems.map(U=>_.has(U.id)?k?fP(U,`GitHub comment could not be posted: ${j}`):I0(U,j):U),E=Fi(D);await this.storage.updatePR(v.id,{feedbackItems:D,accepted:E.accepted,rejected:E.rejected,flagged:E.flagged,status:k?"watching":"error",lastChecked:new Date().toISOString()})}else await this.storage.updatePR(v.id,{status:k?"watching":"error",lastChecked:new Date().toISOString()})}let L=A instanceof Error?A.message:String(A);if(Go.warn({err:L,prId:e},"Babysitter failure"),A instanceof Error&&A.stack&&Go.debug({stack:A.stack,prId:e},"Babysitter failure stack"),n?.rethrowOnFailure)throw A}finally{await o,this.inProgress.delete(e)}}};var _Y={buildOctokit:lr,resolveReviewThread:qo};function EY(t){return{accepted:t.filter(e=>e.decision==="accept").length,rejected:t.filter(e=>e.decision==="reject").length,flagged:t.filter(e=>e.decision==="flag").length}}async function LP(t){let{storage:e,pr:r,feedbackId:n,decision:i,github:s=_Y}=t,a=r.feedbackItems.map(c=>c.id===n?lP(c,i):c),o=a.find(c=>c.id===n);if(o?.threadId&&Uo(o)){let c=jl(r.url);if(!c)throw new Me(`Invalid PR URL: ${r.url}`,500);let l=await e.getConfig(),p=await s.buildOctokit(l);await s.resolveReviewThread(p,c,o.threadId),a=a.map(d=>d.id===n?cP(d):d)}let u=EY(a);return e.updatePR(r.id,{feedbackItems:a,accepted:u.accepted,rejected:u.rejected,flagged:u.flagged})}var NP=require("crypto");var TY=zt("jobs"),Vt=class extends Error{constructor(e){super(e),this.name="CancelBackgroundJobError"}},ep=class{storage;queue;handlers;handledKinds;workerId;pollIntervalMs;leaseMs;heartbeatIntervalMs;maxAttempts;retryBackoffMs;now;onError;onReclaimedJobs;activeJobs=new Map;running=!1;polling=!1;pollTimer=null;constructor(e){this.storage=e.storage,this.queue=e.queue,this.handlers=e.handlers,this.handledKinds=Object.keys(e.handlers),this.workerId=e.workerId??(0,NP.randomUUID)(),this.pollIntervalMs=e.pollIntervalMs??1e3,this.leaseMs=e.leaseMs??3e4,this.heartbeatIntervalMs=e.heartbeatIntervalMs??1e4,this.maxAttempts=Math.max(1,e.maxAttempts??3),this.retryBackoffMs=Math.max(0,e.retryBackoffMs??3e4),this.now=e.now??(()=>new Date),this.onError=e.onError??(r=>{TY.warn({err:r instanceof Error?r.message:String(r)},"Background job dispatcher error")}),this.onReclaimedJobs=e.onReclaimedJobs??(()=>{})}async start(){this.running||(this.running=!0,await this.requeueExpiredAndNotify(),this.wake())}stop(){this.running=!1,this.pollTimer&&(clearTimeout(this.pollTimer),this.pollTimer=null)}wake(){this.running&&this.schedulePoll(0)}getActiveRunCount(){return this.activeJobs.size}async waitForIdle(e=12e4){let r=Date.now();for(;this.activeJobs.size>0;){if(Date.now()-r>=e)return!1;await SY(25)}return!0}schedulePoll(e){this.running&&(this.pollTimer&&clearTimeout(this.pollTimer),this.pollTimer=setTimeout(()=>{this.pollTimer=null,this.pollOnce()},e))}async pollOnce(){if(!(!this.running||this.polling)){this.polling=!0;try{if(this.handledKinds.length===0||(await this.storage.getRuntimeState()).drainMode)return;await this.requeueExpiredAndNotify();let r=await this.queue.claimNext({workerId:this.workerId,leaseMs:this.leaseMs,now:this.now(),kinds:this.handledKinds});if(!r)return;this.runJob(r),this.schedulePoll(0)}catch(e){this.onError(e)}finally{this.polling=!1,this.running&&!this.pollTimer&&this.schedulePoll(this.pollIntervalMs)}}}runJob(e){let r=e.leaseToken,n=this.handlers[e.kind],i=(async()=>{if(!r)throw new Error(`Claimed background job ${e.id} is missing a lease token`);let s=this.heartbeatIntervalMs>0?setInterval(()=>{this.queue.heartbeat({jobId:e.id,leaseToken:r,leaseMs:this.leaseMs,now:this.now()}).catch(a=>{this.onError(a)})},this.heartbeatIntervalMs):null;try{if(!n)throw new Error(`No background job handler registered for ${e.kind}`);await n(e),await this.queue.complete({jobId:e.id,leaseToken:r,now:this.now()})}catch(a){await this.finalizeFailedJob(e,r,a)}finally{s&&clearInterval(s),this.activeJobs.delete(e.id)}})().catch(s=>{this.onError(s)}).finally(()=>{this.running&&this.schedulePoll(0)});this.activeJobs.set(e.id,i)}async requeueExpiredAndNotify(){let e=await this.queue.requeueExpiredWithDetails(this.now());e.length>0&&this.onReclaimedJobs(e)}async finalizeFailedJob(e,r,n){let i=this.now(),s=this.resolveFailureAction(e,n);try{switch(s){case"cancel":await this.queue.cancel({jobId:e.id,leaseToken:r,error:n instanceof Vt&&n.message||null,now:i});return;case"retry":await this.queue.retry({jobId:e.id,leaseToken:r,error:DP(n),now:i,availableAt:new Date(i.getTime()+this.retryBackoffMs)});return;case"fail":await this.queue.fail({jobId:e.id,leaseToken:r,error:DP(n),now:i});return}}catch(a){this.onError(new Error(`Failed to ${s} background job ${e.id} (kind=${e.kind}, attempt=${e.attemptCount}): ${a instanceof Error?a.message:String(a)}`,{cause:a}))}}resolveFailureAction(e,r){return r instanceof Vt?"cancel":e.attemptCount<this.maxAttempts?"retry":"fail"}};function DP(t){return(t instanceof Error?t.message:String(t)).trim().slice(0,2e3)}function SY(t){return new Promise(e=>setTimeout(e,t))}var MP=qe(require("path"),1);function kY(t){switch(t.toUpperCase()){case"READY":return"ready";case"BUILDING":return"building";case"DEPLOYING":case"QUEUED":case"INITIALIZING":return"deploying";case"ERROR":case"CANCELED":return"error";default:return"error"}}function RY(t){switch(t.toUpperCase()){case"SUCCESS":return"ready";case"BUILDING":return"building";case"DEPLOYING":return"deploying";case"INITIALIZING":case"QUEUED":return"deploying";case"FAILED":case"CRASHED":case"REMOVED":return"error";default:return"error"}}function FP(t){return MP.default.join(Ct().repoRootDir,zl(t))}var N0=class{platform="vercel";run;constructor(e){this.run=e??vt}async getDeploymentStatus(e){let{sha:r}=e,n=await this.run("vercel",["list","--meta",`githubCommitSha=${r}`,"--json"]);if(n.code!==0)return{state:"error",deploymentId:null,url:null,error:n.stderr||"vercel list failed"};let i;try{i=JSON.parse(n.stdout)}catch{return{state:"error",deploymentId:null,url:null,error:"Failed to parse vercel list output"}}let s=i.deployments??[];if(s.length===0)return{state:"not_found",deploymentId:null,url:null,error:null};let a=s[0],o=a.state.toUpperCase();return{state:kY(a.state),deploymentId:a.uid,url:a.url??null,error:o==="ERROR"?a.errorMessage??"Deployment failed":null}}async getDeploymentLogs(e){let{deploymentId:r}=e,n=await this.run("vercel",["inspect",r,"--json"]),i=r;if(n.code===0)try{let a=JSON.parse(n.stdout);a.url&&(i=a.url)}catch{}let s=await this.run("vercel",["logs",i]);return s.stdout||s.stderr}},F0=class{platform="railway";run;constructor(e){this.run=e??vt}async getDeploymentStatus(e){let r=await this.run("railway",["status","--json"],{cwd:FP(e.repo)});if(r.code!==0)return{state:"error",deploymentId:null,url:null,error:r.stderr||"railway status failed"};let n;try{n=JSON.parse(r.stdout)}catch{return{state:"error",deploymentId:null,url:null,error:"Failed to parse railway status output"}}return{state:RY(n.status??""),deploymentId:n.deploymentId??null,url:n.url??null,error:null}}async getDeploymentLogs(e){let{repo:r,deploymentId:n}=e,i=await this.run("railway",["logs","--deployment",n],{cwd:FP(r)});return i.stdout||i.stderr}};function jP(t,e){switch(t.toLowerCase()){case"vercel":return new N0(e);case"railway":return new F0(e);default:throw new Error(`Unsupported deployment platform: ${t}`)}}function AY(t){return{ensureRepoCache:t?.ensureRepoCache??Wl,applyFixesWithAgent:t?.applyFixesWithAgent??Ts,runCommand:t?.runCommand??vt}}function PY(t){return["You are fixing a failed deployment for a repository.","A pull request was merged but the deployment to the platform failed.","Your task is to diagnose the deployment failure and apply the minimal fix.","Create a new branch named deploy-fix/<platform>-<timestamp> from the merge SHA.","Leave any file edits unstaged and uncommitted. The app will stage, commit, push, and create the PR.","At the end of your response, include exactly one line in this format:","DEPLOYMENT_FIX_SUMMARY: <one short sentence describing what was fixed>","",`Repository: ${t.repo}`,`Platform: ${t.platform}`,`Merge SHA: ${t.mergeSha}`,`Base branch: ${t.baseBranch}`,`Trigger PR: #${t.triggerPrNumber}`,`Trigger PR title: ${t.triggerPrTitle}`,`Trigger PR URL: ${t.triggerPrUrl}`,"","Deployment failure log:","```",t.deploymentLog,"```","","Instructions:","1. Analyze the deployment log to identify the root cause of the failure.","2. Apply the minimal code change needed to fix the deployment error.","3. Do not run git commit or git push.","4. Leave the fix branch checked out with your file edits in the worktree.","5. Do not merge the branch \u2014 a PR will be created automatically.","6. Include the DEPLOYMENT_FIX_SUMMARY line at the end of your output."].join(`
1073
1073
  `)}function Ui(t){let e=t.match(/^DEPLOYMENT_FIX_SUMMARY:\s*(.+)$/m);if(e?.[1])return e[1].trim();let r=t.trim();if(!r)return"No agent summary provided";let n=r.split(/\r?\n/).map(i=>i.trim()).filter(Boolean);return n[n.length-1]??"No agent summary provided"}async function qP(t){let e=AY(t.dependencies),r=`deploy-fix/${t.platform}-${Math.floor(Date.now()/1e3)}`,n=PY(t),{repoCacheDir:i}=await e.ensureRepoCache({rootDir:t.rootDir,repoFullName:t.repo,repoCloneUrl:t.repoCloneUrl,runCommand:e.runCommand}),s=await e.runCommand("git",["-C",i,"checkout","-b",r,t.mergeSha],{timeoutMs:3e4});if(s.code!==0)return{accepted:!1,rejectionReason:`branch creation failed: ${s.stderr||s.stdout}`,summary:"No agent summary provided",fixBranch:r,agentResult:s};try{let a=await e.applyFixesWithAgent({agent:t.agent,cwd:i,prompt:n,env:t.env});if(a.code!==0)return{accepted:!1,rejectionReason:pn(a,`agent failed (${a.code})`),summary:Ui(a.stdout),fixBranch:r,agentResult:a};let o=await e.runCommand("git",["-C",i,"status","--porcelain"],{timeoutMs:1e4});if(o.code!==0)return{accepted:!1,rejectionReason:`git status failed: ${o.stderr||o.stdout}`,summary:Ui(a.stdout),fixBranch:r,agentResult:a};if(o.stdout.trim()){let p=await e.runCommand("git",["-C",i,"add","-A"],{timeoutMs:3e4});if(p.code!==0)return{accepted:!1,rejectionReason:`git add failed: ${p.stderr||p.stdout}`,summary:Ui(a.stdout),fixBranch:r,agentResult:a};let d=await e.runCommand("git",["-C",i,"commit","-m",`Fix ${t.platform} deployment`],{timeoutMs:6e4});if(d.code!==0)return{accepted:!1,rejectionReason:`git commit failed: ${d.stderr||d.stdout}`,summary:Ui(a.stdout),fixBranch:r,agentResult:a}}let u=await e.runCommand("git",["-C",i,"log",`${t.mergeSha}..HEAD`,"--oneline"],{timeoutMs:1e4});if(!(u.code===0&&u.stdout.trim().length>0))return{accepted:!1,rejectionReason:"agent did not produce any new commits",summary:Ui(a.stdout),fixBranch:r,agentResult:a};let l=await e.runCommand("git",["-C",i,"push","origin",r],{timeoutMs:6e4});return l.code!==0?{accepted:!1,rejectionReason:`push failed: ${l.stderr||l.stdout}`,summary:Ui(a.stdout),fixBranch:r,agentResult:a}:{accepted:!0,rejectionReason:null,summary:Ui(a.stdout),fixBranch:r,agentResult:a}}finally{await e.runCommand("git",["-C",i,"checkout","--detach"],{timeoutMs:15e3})}}async function UP(t){let{storage:e,prId:r,questionId:n,question:i,preferredAgent:s}=t;await e.updateQuestion(n,{status:"answering"});try{let a=await CY(e,r),o=await Es(s),u=OY(a,i),c=await tr(o,o==="claude"?["-p","--output-format","text",u]:["exec","--skip-git-repo-check","--sandbox","read-only",u],{timeoutMs:18e4});if(c.code!==0){let p=pn(c,`Agent exited with code ${c.code}`);await e.updateQuestion(n,{status:"error",error:p.slice(0,2e3)});return}let l=c.stdout.trim();if(!l){await e.updateQuestion(n,{status:"error",error:"Agent returned an empty response",answer:null});return}await e.updateQuestion(n,{status:"answered",answer:l,answeredAt:new Date().toISOString()})}catch(a){let o=a instanceof Error?a.message:String(a);await e.updateQuestion(n,{status:"error",error:o.slice(0,2e3)})}}async function CY(t,e){let r=await t.getPR(e);if(!r)throw new Error("PR not found");let i=(await t.getLogs(e)).slice(-50).map(a=>`[${a.timestamp}] ${a.level.toUpperCase()} ${a.phase?`[${a.phase}]`:""} ${a.message}`).join(`
1074
1074
  `),s=r.feedbackItems.map(a=>[`- [${a.status}]`,a.decision?`decision=${a.decision}`:"",`by ${a.author}`,a.file?`on ${a.file}${a.line?`:${a.line}`:""}`:"",`:: ${a.body.slice(0,200)}`].filter(Boolean).join(" "));return{title:r.title,number:r.number,repo:r.repo,branch:r.branch,author:r.author,url:r.url,status:r.status,testsPassed:r.testsPassed,lintPassed:r.lintPassed,lastChecked:r.lastChecked,feedbackSummary:s.length>0?s.join(`
@@ -1079,8 +1079,8 @@ ${In}`:In}function Yl(t,e){return[t,...e].join(" ")}function qi(t){return t.stde
1079
1079
  `);return["Respond with ONLY valid JSON and nothing else.","Schema:",'{"shouldRelease":boolean,"reason":string,"bump":"patch"|"minor"|"major"|null,"title":string|null,"notes":string|null}',"","You are deciding whether a merged set of pull requests should be published as a GitHub release.","Be conservative. Release only when the merged changes are meaningful enough for users or operators to care about.","",`Repository: ${t.repo}`,`Release branch: ${t.baseBranch}`,`Latest release tag: ${t.latestTag??"none"}`,"",`Trigger PR: #${t.triggerPr.number} "${t.triggerPr.title}"`,`Trigger merged at: ${t.triggerPr.mergedAt}`,`Trigger merge SHA: ${t.triggerPr.mergeSha}`,"","Merged PRs included in this candidate release:",e||"(none)","","Decision rules:","- shouldRelease=true only when the merged changes are worth announcing as a GitHub release.","- bump must be null when shouldRelease=false.","- bump must be one of patch, minor, or major when shouldRelease=true.","- Use patch for fixes or small improvements, minor for additive user-facing features, and major for breaking changes.","- title should be short and release-note ready when shouldRelease=true, else null.","- notes should be GitHub-release Markdown focused on user-visible/operator-visible impact when shouldRelease=true, else null.","- notes must have exactly TWO sections in this order:"," 1. '## Why This Matters' \u2014 a user-friendly, value-driven summary at the top that explains how the release makes users' lives better."," 2. '## Detailed Changes' \u2014 a line-by-line changelog of the included changes in plain English, more detailed than a headline but not deeply technical.","- notes should mention the key merged PRs in plain language, not internal process commentary.","- Prefer user outcomes, workflow improvements, and visible behavior over implementation details."].join(`
1080
1080
  `)}function $P(t){let e=NY(t.trim());if(!e||typeof e!="object")throw new Error(`Could not parse release evaluation JSON: ${t.slice(0,500)}`);let r=e;if(typeof r.shouldRelease!="boolean")throw new Error("Release evaluation missing boolean 'shouldRelease'");if(typeof r.reason!="string"||r.reason.trim().length===0)throw new Error("Release evaluation missing string 'reason'");if(!r.shouldRelease)return{shouldRelease:!1,reason:r.reason.trim(),bump:null,title:null,notes:null};if(r.bump!=="patch"&&r.bump!=="minor"&&r.bump!=="major")throw new Error("Release evaluation returned invalid 'bump'");if(typeof r.title!="string"||r.title.trim().length===0)throw new Error("Release evaluation missing non-empty 'title'");if(typeof r.notes!="string"||r.notes.trim().length===0)throw new Error("Release evaluation missing non-empty 'notes'");return{shouldRelease:!0,reason:r.reason.trim(),bump:r.bump,title:r.title.trim(),notes:r.notes.trim()}}function NY(t){try{return JSON.parse(t)}catch{}let e=t.indexOf("{"),r=t.lastIndexOf("}");if(e===-1||r===-1||r<=e)return null;try{return JSON.parse(t.slice(e,r+1))}catch{return null}}var FY=zt("release"),tp=class{storage;github;evaluateRelease;scheduleBackgroundJob;inProgress=new Set;repoLocks=new Map;constructor(e,r){this.storage=e,this.github=r.github,this.evaluateRelease=r.evaluateRelease??WP,this.scheduleBackgroundJob=r.scheduleBackgroundJob}getActiveRunCount(){return this.inProgress.size}async waitForIdle(e=12e4){let r=Date.now();for(;this.inProgress.size>0;){if(Date.now()-r>=e)return!1;await HY(50)}return!0}async enqueueMergedPullReleaseEvaluation(e){let r=await this.storage.getReleaseRunByTrigger(e.repo,e.triggerPrNumber,e.triggerMergeSha);if(r)return VP(r.status)||this.scheduleProcessing(r),r;let n=await this.storage.createReleaseRun({repo:e.repo,baseBranch:e.baseBranch,triggerPrNumber:e.triggerPrNumber,triggerPrTitle:e.triggerPrTitle,triggerPrUrl:e.triggerPrUrl,triggerMergeSha:e.triggerMergeSha,triggerMergedAt:e.triggerMergedAt,status:"detected",decisionReason:null,recommendedBump:null,proposedVersion:null,releaseTitle:null,releaseNotes:null,includedPrs:[],targetSha:e.triggerMergeSha,githubReleaseId:null,githubReleaseUrl:null,error:null,completedAt:null});return this.scheduleProcessing(n),n}async enqueueManualRepoRelease(e){let r=dt(e);if(!r)throw new Error(`Invalid repository slug: ${e}`);let n=`${r.owner}/${r.repo}`,i=await this.storage.getConfig(),s=await this.github.buildOctokit(i),a=await this.github.getDefaultBranch(s,r),u=[...await this.github.listUnreleasedMergedPulls(s,r,{baseBranch:a})].sort((p,d)=>Date.parse(p.mergedAt)-Date.parse(d.mergedAt)).at(-1);if(!u)return null;let c=await this.storage.getReleaseRunByTrigger(n,u.number,u.mergeSha);if(c){let p=c.status==="skipped"||c.status==="error",m=await this.storage.updateReleaseRun(c.id,{source:"manual",...p?{status:"detected",decisionReason:null,recommendedBump:null,proposedVersion:null,releaseTitle:null,releaseNotes:null,includedPrs:[],targetSha:u.mergeSha,githubReleaseId:null,githubReleaseUrl:null,error:null,completedAt:null}:{}})??c;return VP(m.status)||this.scheduleProcessing(m),m}let l=await this.storage.createReleaseRun({repo:n,baseBranch:a,triggerPrNumber:u.number,triggerPrTitle:u.title,triggerPrUrl:u.url,triggerMergeSha:u.mergeSha,triggerMergedAt:u.mergedAt,source:"manual",status:"detected",decisionReason:null,recommendedBump:null,proposedVersion:null,releaseTitle:null,releaseNotes:null,includedPrs:[],targetSha:u.mergeSha,githubReleaseId:null,githubReleaseUrl:null,error:null,completedAt:null});return this.scheduleProcessing(l),l}async retryReleaseRun(e){if(!await this.storage.getReleaseRun(e))return;let n=await this.storage.updateReleaseRun(e,{status:"detected",error:null,completedAt:null});if(n)return this.scheduleProcessing(n),n}async processReleaseRun(e){let r=await this.storage.getReleaseRun(e);if(r)return this.withRepoLock(r.repo,async()=>{if(this.inProgress.has(e))return this.storage.getReleaseRun(e);this.inProgress.add(e);try{let n=await this.storage.getReleaseRun(e);if(!n)return;if(n.status==="published"||n.status==="skipped")return n;let i=dt(n.repo);if(!i)return this.failRun(e,`Invalid repository slug: ${n.repo}`);let s=await this.storage.getConfig(),a=n.source==="manual";if(!a&&!s.autoCreateReleases)return await this.storage.updateReleaseRun(e,{status:"skipped",decisionReason:"Automatic release creation is disabled in settings",completedAt:new Date().toISOString()})??void 0;let o=await this.storage.getRepoSettings(n.repo);if(!a&&o&&!o.autoCreateReleases)return await this.storage.updateReleaseRun(e,{status:"skipped",decisionReason:`Automatic release creation is disabled for ${n.repo}`,completedAt:new Date().toISOString()})??void 0;await this.storage.updateReleaseRun(e,{status:"evaluating",error:null,completedAt:null});let u=await this.github.buildOctokit(s),c=await this.github.findLatestSemverReleaseTag(u,i),l=MY(n),p=await this.loadIncludedPulls(u,i,n,l),d=p.map(jY),m=await this.evaluateRelease({preferredAgent:s.codingAgent,repo:n.repo,baseBranch:n.baseBranch,latestTag:c,triggerPr:l,includedPulls:p}),f=m.shouldRelease||!a?m:UY(m,p);if(!f.shouldRelease)return await this.storage.updateReleaseRun(e,{status:"skipped",decisionReason:f.reason,recommendedBump:null,proposedVersion:null,releaseTitle:null,releaseNotes:null,includedPrs:d,targetSha:n.triggerMergeSha,completedAt:new Date().toISOString()})??void 0;if(!f.bump)throw new Error("Release evaluation approved publishing but did not provide a semver bump");let g=this.github.bumpReleaseTag(c,f.bump),h=qY(f,g),T=f.notes??`Release ${g}`;await this.storage.updateReleaseRun(e,{status:"proposed",decisionReason:f.reason,recommendedBump:f.bump,proposedVersion:g,releaseTitle:h,releaseNotes:T,includedPrs:d,targetSha:n.triggerMergeSha});let R=this.github.findReleaseByTag?await this.github.findReleaseByTag(u,i,g):null;if(R)return await this.storage.updateReleaseRun(e,{status:"published",githubReleaseId:R.id,githubReleaseUrl:R.url,completedAt:new Date().toISOString()})??void 0;await this.storage.updateReleaseRun(e,{status:"publishing"});let S=await this.github.createGitHubRelease(u,i,{tagName:g,targetCommitish:n.triggerMergeSha,name:h,body:T});return await this.storage.updateReleaseRun(e,{status:"published",githubReleaseId:S.id,githubReleaseUrl:S.url,completedAt:new Date().toISOString()})??void 0}catch(n){return this.failRun(e,BY(n))}finally{this.inProgress.delete(e)}})}async loadIncludedPulls(e,r,n,i){if(!this.github.listMergedPullsForReleaseCandidate)return[i];let s=await this.github.listMergedPullsForReleaseCandidate(e,r,{baseBranch:n.baseBranch,untilMergedAt:n.triggerMergedAt,triggerPr:i}),a=new Map;for(let o of s)a.set(o.mergeSha||`${o.repo}#${o.number}`,o);return a.has(i.mergeSha)||a.set(i.mergeSha,i),Array.from(a.values()).sort((o,u)=>o.mergedAt.localeCompare(u.mergedAt))}async failRun(e,r){return this.storage.updateReleaseRun(e,{status:"error",error:r,completedAt:new Date().toISOString()})}async withRepoLock(e,r){let n=this.repoLocks.get(e)??Promise.resolve(),i,s=new Promise(o=>{i=()=>o()}),a=n.then(()=>s);this.repoLocks.set(e,a),await n;try{return await r()}finally{i?.(),this.repoLocks.get(e)===a&&this.repoLocks.delete(e)}}scheduleProcessing(e){let r=e.id;if(this.scheduleBackgroundJob){this.scheduleBackgroundJob("process_release_run",r,Or("process_release_run",r),{releaseRunId:r,...On({label:`Processing release for ${e.repo}`,detail:`PR #${e.triggerPrNumber} - ${e.triggerPrTitle}`,targetUrl:e.triggerPrUrl})}).catch(n=>{FY.warn({err:n instanceof Error?n.message:String(n),runId:r},"Failed to schedule release run")});return}this.processReleaseRun(r).catch(()=>{})}};function MY(t){return{number:t.triggerPrNumber,title:t.triggerPrTitle,url:t.triggerPrUrl,author:"unknown",repo:t.repo,mergedAt:t.triggerMergedAt,mergeSha:t.triggerMergeSha}}function jY(t){return{number:t.number,title:t.title,url:t.url,author:t.author,mergedAt:t.mergedAt,mergeSha:t.mergeSha}}function qY(t,e){let r=t.title?.trim();return r?r.startsWith(e)?r:`${e} - ${r}`:e}function UY(t,e){return{shouldRelease:!0,reason:`Manual release requested. Evaluator recommendation: ${t.reason}`,bump:"patch",title:"Manual release",notes:GY(e)}}function GY(t){return`## Changes
1081
1081
  ${t.length>0?t.map(r=>`- #${r.number} ${r.title} (@${r.author})`).join(`
1082
- `):"- Manual release requested."}`}function BY(t){return(t instanceof Error?t.message:String(t)).trim().slice(0,2e3)}function VP(t){return t==="skipped"||t==="published"}function HY(t){return new Promise(e=>setTimeout(e,t))}var $Y=["fix_submitted","escalated"],zY={monitoring:["failed","escalated"],failed:["fixing","escalated"],fixing:["fix_submitted","escalated"],fix_submitted:[],escalated:[]},rp=class{constructor(e,r=()=>new Date){this.storage=e;this.clock=r}async createSession(e){return this.storage.createDeploymentHealingSession({...e,deploymentId:null,deploymentLog:null,fixBranch:null,fixPrNumber:null,fixPrUrl:null,state:"monitoring",error:null,completedAt:null})}async ensureSession(e){let r=await this.storage.getDeploymentHealingSessionByRepoAndMergeSha(e.repo,e.mergeSha);return r||this.createSession(e)}async transitionTo(e,r,n={}){let i=await this.storage.getDeploymentHealingSession(e);if(!i)throw new Error(`Deployment healing session not found: ${e}`);if(i.state===r){let o=await this.storage.updateDeploymentHealingSession(e,n);if(!o)throw new Error(`Deployment healing session not found: ${e}`);return o}if(!zY[i.state].includes(r))throw new Error(`Illegal deployment healing transition: ${i.state} -> ${r}`);let s={...n,state:r};$Y.includes(r)&&(s.completedAt=n.completedAt??this.clock().toISOString());let a=await this.storage.updateDeploymentHealingSession(e,s);if(!a)throw new Error(`Deployment healing session not found: ${e}`);return a}};var JP=zt("runtime"),$e=class extends Error{statusCode;constructor(e,r){super(r),this.name="AppRuntimeError",this.statusCode=e}};function XP(t){return t instanceof Error?t.message:String(t)}function nt(t,e){if(t===void 0)throw new $e(404,e);return t}function WY(t){switch(t.kind){case"sync_watched_repos":return"Sync watched repositories";case"babysit_pr":return"Babysitting PR";case"process_release_run":return"Processing release";case"answer_pr_question":return"Answering PR question";case"generate_social_changelog":return"Social changelog generation removed";case"heal_deployment":return"Healing deployment"}}function np(t,e){let r=t.payload[e];return typeof r=="string"&&r.trim()?r:null}var VY={Claude:{auth:["Run `claude auth login` on this machine.","Restart oh-my-pr if it was launched before you refreshed credentials.","Rerun the babysitter for this PR."],cli_missing:["Install the Claude Code CLI on this machine.","Restart oh-my-pr after installing.","Rerun the babysitter for this PR."]},Codex:{auth:["Run `codex login` on this machine.","Restart oh-my-pr if it was launched before you refreshed credentials.","Rerun the babysitter for this PR."],cli_missing:["Install the Codex CLI on this machine.","Restart oh-my-pr after installing.","Rerun the babysitter for this PR."]}};function j0(t,e){return{agentLabel:t,kind:e,fixSteps:VY[t][e]}}function JY(t){let e=t.toLowerCase();return e.includes("claude evaluation failed")||e.includes("claude apply failed")?"Claude":e.includes("codex evaluation failed")||e.includes("codex apply failed")?"Codex":null}function KP(t){if(t.kind!=="babysit_pr"||!t.lastError)return null;let e=lm(t.lastError);if(!e)return null;let r=JY(t.lastError);if(r)return j0(r,e);let n=np(t,"preferredAgent");return n==="claude"?j0("Claude",e):n==="codex"?j0("Codex",e):null}function QP(t){return t.flatMap(e=>{let r=e.mergeCommitSha?.trim();return r?[{number:e.number,title:e.title,url:e.url,author:e.author,repo:e.repo,mergedAt:e.mergedAt,mergeSha:r}]:[]})}function ZP(t={}){let e=t.storage??Q_(),r=new YP.EventEmitter,n=t.backgroundJobQueue??new Xl(e),i,s=async(...w)=>{let k=await n.enqueue(...w);return i.wake(),k},a=t.deploymentHealingManager??new rp(e),o=t.releaseManager??new tp(e,{github:{buildOctokit:lr,getDefaultBranch:E0,findLatestSemverReleaseTag:P4,bumpReleaseTag:A4,listUnreleasedMergedPulls:async(w,k,x)=>{let C=await T0(w,k,{baseRef:x.baseBranch});return QP(C)},listMergedPullsForReleaseCandidate:async(w,k,x)=>{let C=await T0(w,k,{baseRef:x.baseBranch}),_=Date.parse(x.untilMergedAt);return QP(C.filter(D=>!Number.isFinite(_)||Date.parse(D.mergedAt)<=_))},findReleaseByTag:async(w,k,x)=>{let _=(await Gl(w,k)).find(D=>!D.draft&&D.tagName===x);return _?{id:_.id,url:_.htmlUrl,tagName:_.tagName,name:_.name}:null},createGitHubRelease:async(w,k,x)=>{let C=await C4(w,k,{tagName:x.tagName,targetCommitish:x.targetCommitish,name:x.name,body:x.body});return{id:C.id,url:C.htmlUrl,tagName:C.tagName,name:C.name}}},scheduleBackgroundJob:s}),u=t.babysitter??new Zl(e,void 0,void 0,o,s,a);i=t.backgroundJobDispatcher??new ep({storage:e,queue:n,handlers:BP({storage:e,babysitter:u,releaseManager:o,deploymentHealingManager:a}),onReclaimedJobs:w=>{for(let k of w)k.kind==="babysit_pr"&&e.addLog(k.targetId,"warn",`Reclaimed expired background job ${k.id} for PR ${k.targetId}`,{phase:"background.job",metadata:{jobId:k.id,kind:k.kind,leaseOwner:k.leaseOwner,leaseExpiresAt:k.leaseExpiresAt,attemptCount:k.attemptCount}}).catch(x=>{JP.warn({err:x instanceof Error?x.message:String(x)},"Failed to log reclaimed background job")})}});let c=null,l=0,p=t.watcherScheduler??HP(async()=>{await s("sync_watched_repos","runtime:1",Or("sync_watched_repos","runtime:1"))},w=>{JP.warn({err:w instanceof Error?w.message:String(w)},"Repository babysitter watcher failed")}),d=p.run,m=t.startBackgroundServices??!0,f=t.startWatcher??m,g=!1,h=()=>{r.emit("change")},T=async()=>({...await e.getRuntimeState(),activeRuns:i.getActiveRunCount()}),R=async w=>{let k=new Set,x=new Set,C=new Set,_=new Set;for(let fe of w)if(fe.kind==="babysit_pr")k.add(fe.targetId);else if(fe.kind==="answer_pr_question"){let oe=np(fe,"prId");oe&&k.add(oe)}else fe.kind==="process_release_run"?x.add(fe.targetId):fe.kind==="generate_social_changelog"?C.add(fe.targetId):fe.kind==="heal_deployment"&&_.add(fe.targetId);let[D,E,U,Z,ge]=await Promise.all([k.size>0?e.getPRs():Promise.resolve([]),k.size>0?e.getArchivedPRs():Promise.resolve([]),x.size>0?e.listReleaseRuns():Promise.resolve([]),C.size>0?e.getSocialChangelogs():Promise.resolve([]),_.size>0?e.listDeploymentHealingSessions():Promise.resolve([])]),Ee=new Map;for(let fe of ge)Ee.set(fe.id,fe),Ee.set(`${fe.repo}:${fe.mergeSha}`,fe);return{prsById:new Map([...D,...E].map(fe=>[fe.id,fe])),releaseRunsById:new Map(U.map(fe=>[fe.id,fe])),socialChangelogsById:new Map(Z.map(fe=>[fe.id,fe])),deploymentHealingSessionsByTarget:Ee}},S=(w,k)=>{let x=oP(w.payload);if(x)return x;if(w.kind==="sync_watched_repos")return{label:"Sync watched repositories",detail:null,targetUrl:null};if(w.kind==="babysit_pr"){let C=k.prsById.get(w.targetId);if(C)return{label:`Babysitting PR #${C.number}`,detail:`${C.repo} - ${C.title}`,targetUrl:C.url}}if(w.kind==="answer_pr_question"){let C=np(w,"prId"),_=C?k.prsById.get(C):void 0;if(_)return{label:`Answering question for PR #${_.number}`,detail:`${_.repo} - ${_.title}`,targetUrl:_.url}}if(w.kind==="process_release_run"){let C=k.releaseRunsById.get(w.targetId);if(C)return{label:`Processing release for ${C.repo}`,detail:`PR #${C.triggerPrNumber} - ${C.triggerPrTitle}`,targetUrl:C.triggerPrUrl}}if(w.kind==="generate_social_changelog"){let C=k.socialChangelogsById.get(w.targetId);if(C)return{label:"Social changelog generation removed",detail:`${C.date} - ${C.triggerCount} merged PRs`,targetUrl:null}}if(w.kind==="heal_deployment"){let C=k.deploymentHealingSessionsByTarget.get(w.targetId);if(C)return{label:`Healing ${C.platform} deployment`,detail:`${C.repo} PR #${C.triggerPrNumber} - ${C.triggerPrTitle}`,targetUrl:C.triggerPrUrl}}return{label:WY(w),detail:w.targetId,targetUrl:null}},P=(w,k)=>{let x=S(w,k);return{id:w.id,kind:w.kind,status:w.status==="leased"?"in_progress":w.status==="failed"?"failed":"queued",label:x.label,detail:x.detail,targetId:w.targetId,targetUrl:x.targetUrl,queuedAt:w.createdAt,availableAt:w.availableAt,startedAt:w.heartbeatAt,updatedAt:w.updatedAt,attemptCount:w.attemptCount,lastError:w.lastError}},F=(w,k)=>{if(w.kind==="babysit_pr")return k.prsById.get(w.targetId)?.status==="archived";if(w.kind==="answer_pr_question"){let x=np(w,"prId");return x?k.prsById.get(x)?.status==="archived":!1}return!1},I=(w,k)=>{let x=KP(w);if(!x)return null;let C=k.prsById.get(w.targetId);if(!C||C.status!=="error")return null;let _=x.kind==="auth"?"authentication failed":"CLI not installed",D=x.kind==="auth"?"local agent credentials are invalid or expired":"the agent CLI is not installed on this machine";return{id:w.id,severity:"warning",title:`${x.agentLabel} ${_}`,message:`Babysitter could not run ${x.agentLabel} for PR #${C.number} in ${C.repo} because ${D}.`,fixSteps:x.fixSteps,targetId:w.targetId,targetUrl:C.url,createdAt:w.createdAt,updatedAt:w.updatedAt}},A=async w=>{let[k,x,C]=await Promise.all([i.waitForIdle(w),u.waitForIdle(w),o.waitForIdle(w)]);return k&&x&&C},j=async()=>{let w=await e.getConfig(),k=Math.max(1e4,w.pollIntervalMs||12e4);c&&l===k||(c&&(clearInterval(c),c=null),l=k,c=setInterval(()=>{d()},k))},v=async(w,k)=>{await s("babysit_pr",w.id,Or("babysit_pr",w.id),{preferredAgent:k,...On({label:`Babysitting PR #${w.number}`,detail:`${w.repo} - ${w.title}`,targetUrl:w.url})})},L={async start(){g||(g=!0,m&&await i.start(),f&&(await j(),u.resumeInterruptedRuns(),d()))},stop(){g=!1,i.stop(),c&&(clearInterval(c),c=null)},subscribe(w){return r.on("change",w),()=>{r.off("change",w)}},getRuntimeSnapshot:T,async listActivities(){let[w,k,x]=await Promise.all([e.listBackgroundJobs({status:"failed"}),e.listBackgroundJobs({status:"leased"}),e.listBackgroundJobs({status:"queued"})]),C=await R([...w,...k,...x]),_=w.filter(Ee=>!F(Ee,C)),D=_.filter(Ee=>KP(Ee)),E=_.map(Ee=>P(Ee,C)),U=k.map(Ee=>P(Ee,C)),Z=x.map(Ee=>P(Ee,C)),ge=D.map(Ee=>I(Ee,C)).filter(Ee=>!!Ee).sort((Ee,fe)=>Date.parse(fe.updatedAt)-Date.parse(Ee.updatedAt)).slice(0,5);return{failed:E,inProgress:U,queued:Z,warnings:ge,generatedAt:new Date().toISOString()}},async clearFailedActivities(){let w=await e.clearFailedBackgroundJobs();return w>0&&h(),{cleared:w}},async setDrainMode(w){let k=await e.updateRuntimeState({drainMode:w.enabled,drainRequestedAt:w.enabled?new Date().toISOString():null,drainReason:w.enabled?w.reason??null:null});if(w.enabled&&w.waitForIdle){let C=await A(w.timeoutMs??12e4),_=await T();return h(),{...k,..._,drained:C}}let x=await T();return h(),{...k,...x}},async listRepos(){let w=await e.getConfig(),k=await e.getPRs();return Array.from(new Set([...w.watchedRepos,...k.map(x=>x.repo)])).sort((x,C)=>x.localeCompare(C))},async listRepoSettings(){let[w,k]=await Promise.all([e.listRepoSettings(),e.getPRs()]),x=new Map(w.map(C=>[C.repo,C]));for(let C of k)x.has(C.repo)||x.set(C.repo,{repo:C.repo,autoCreateReleases:!0,ownPrsOnly:!0});return Array.from(x.values()).sort((C,_)=>C.repo.localeCompare(_.repo))},async addRepo(w){let k=dt(w);if(!k)throw new $e(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let x=Pn(k),C=await e.getConfig();return C.watchedRepos.includes(x)||await e.updateConfig({watchedRepos:[...C.watchedRepos,x].sort((_,D)=>_.localeCompare(D))}),d(),h(),{repo:x}},async updateRepoSettings(w,k){let x=dt(w);if(!x)throw new $e(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let C=Pn(x),_=await e.updateRepoSettings(C,k);return h(),_},async syncRepos(){if((await e.getRuntimeState()).drainMode)throw new $e(409,"Drain mode is enabled. Sync-triggered runs are blocked until drain mode is disabled.");return await p.runAndReportErrors(),h(),{ok:!0}},async createManualRelease(w){let k=dt(w);if(!k)throw new $e(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let x=Pn(k),C=await o.enqueueManualRepoRelease(x);if(!C)throw new $e(409,`No unreleased merged pull requests found for ${x}`);return h(),C},async listPRs(w="active"){return w==="archived"?e.getArchivedPRs():e.getPRs()},async getPR(w){return await e.getPR(w)??null},async addPR(w){let k;try{({url:k}=U_.parse({url:w}))}catch(ge){throw ge instanceof b.ZodError?new $e(400,ge.errors[0]?.message??"Invalid PR URL"):ge}let x=jl(k);if(!x)throw new $e(400,"Invalid GitHub PR URL. Expected: https://github.com/owner/repo/pull/123");let C=`${x.owner}/${x.repo}`,_=await e.getPRByRepoAndNumber(C,x.number);if(_)return _;let D=await e.getConfig(),E=await lr(D),U=await Ul(E,x),Z=await e.addPR({number:x.number,title:U.title,repo:C,branch:U.branch,author:U.author,url:U.url,status:"watching",feedbackItems:[],accepted:0,rejected:0,flagged:0,testsPassed:null,lintPassed:null,lastChecked:null});return await e.addLog(Z.id,"info",`Registered PR #${x.number} from ${C}`),await e.addLog(Z.id,"info",`Repository ${C} added to auto-babysit watch list`),D.watchedRepos.includes(C)||await e.updateConfig({watchedRepos:[...D.watchedRepos,C].sort((ge,Ee)=>ge.localeCompare(Ee))}),await v(Z,D.codingAgent),h(),Z},async removePR(w){if(!await e.removePR(w))throw new $e(404,"PR not found");return h(),{ok:!0}},async setPRWatchEnabled(w,k){let x=nt(await e.getPR(w),"PR not found"),C=await e.updatePR(x.id,{watchEnabled:k}),_=nt(C,"PR not found");return x.watchEnabled!==k&&(await e.addLog(x.id,"info",k?"Background watch resumed":"Background watch paused"),k&&d()),h(),_},async setWatchEnabled(w,k){return L.setPRWatchEnabled(w,k)},async fetchPRFeedback(w){let k=nt(await e.getPR(w),"PR not found");await e.updatePR(k.id,{status:"processing",lastChecked:new Date().toISOString()}),await e.addLog(k.id,"info","Syncing GitHub comments/reviews...");try{let x=await u.syncFeedbackForPR(k.id);return h(),x}catch(x){let C=XP(x);throw await e.updatePR(k.id,{status:"error",lastChecked:new Date().toISOString()}),await e.addLog(k.id,"error",`Fetch failed: ${C}`),x}},async triagePR(w){let k=nt(await e.getPR(w),"PR not found");await e.updatePR(k.id,{status:"processing"}),await e.addLog(k.id,"info","Triaging feedback...");let x=k.feedbackItems.map(U=>{if(U.decision)return U;let Z=U.body.toLowerCase();return Z.includes("lgtm")||Z.includes("looks good")?Ni(U,!1,"Acknowledgement, no code change requested"):Z.includes("please")||Z.includes("should")||Z.includes("fix")||Z.includes("error")||Z.includes("fail")?{...Ni(U,!0,"Likely actionable request"),action:U.body}:pP(U,"Unclear actionability, flagged for manual review")}),C=x.filter(U=>U.decision==="accept").length,_=x.filter(U=>U.decision==="reject").length,D=x.filter(U=>U.decision==="flag").length,E=await e.updatePR(k.id,{feedbackItems:x,accepted:C,rejected:_,flagged:D,status:"watching"});return await e.addLog(k.id,"info",`Triage complete: ${C} accept, ${_} reject, ${D} flag`),h(),nt(E,"PR not found")},async applyPR(w){let k=nt(await e.getPR(w),"PR not found");if((await e.getRuntimeState()).drainMode)throw new $e(409,"Drain mode is enabled. Manual runs are blocked until drain mode is disabled.");let C=await e.getConfig();await e.updatePR(k.id,{status:"processing"}),await e.addLog(k.id,"info",`Launching autonomous babysitter run using ${C.codingAgent}`),await v(k,C.codingAgent);let _=await e.getPR(k.id);return h(),nt(_,"PR disappeared after apply run")},async babysitPR(w){let k=nt(await e.getPR(w),"PR not found");if((await e.getRuntimeState()).drainMode)throw new $e(409,"Drain mode is enabled. Manual runs are blocked until drain mode is disabled.");let C=await e.getConfig();await e.addLog(k.id,"info",`Manual babysitter trigger using ${C.codingAgent}`),await v(k,C.codingAgent);let _=await e.getPR(k.id);return h(),nt(_,"PR disappeared after babysit run")},async queueBabysit(w){return L.babysitPR(w)},async setFeedbackDecision(w,k,x){let C=nt(await e.getPR(w),"PR not found"),_=await LP({storage:e,pr:C,feedbackId:k,decision:x});return h(),nt(_,"PR not found")},async retryFeedback(w,k){let x=await u.retryFeedbackItem(w,k);if(x.kind==="pr_not_found")throw new $e(404,"PR not found");if(x.kind==="feedback_not_found")throw new $e(404,"Feedback item not found");if(x.kind==="feedback_not_retryable")throw new $e(400,"Only failed or warning items can be retried");await e.addLog(w,"info",`Feedback item ${k} queued for retry`);let C=await e.getConfig();return await v(x.updated,C.codingAgent),h(),x.updated},async listPRQuestions(w){return nt(await e.getPR(w),"PR not found"),e.getQuestions(w)},async askQuestion(w,k){let x=nt(await e.getPR(w),"PR not found"),C;try{C=H_.parse({question:k})}catch(D){throw D instanceof b.ZodError?new $e(400,D.errors[0]?.message??"Invalid question"):D}let _=await e.addQuestion(w,C.question);try{await s("answer_pr_question",_.id,Or("answer_pr_question",_.id),{prId:w,...On({label:`Answering question for PR #${x.number}`,detail:`${x.repo} - ${x.title}`,targetUrl:x.url})})}catch(D){let E=XP(D);throw await e.updateQuestion(_.id,{status:"error",error:E.trim().slice(0,2e3)}),D}return h(),_},async listLogs(w){return e.getLogs(w)},async getOnboardingStatus(){let w=await e.getConfig();return S4(w,w.watchedRepos)},async installReviewWorkflow(w,k){let x=await e.getConfig();return k4(x,w,k)},async listHealingSessions(){return e.listHealingSessions()},async getHealingSession(w){return nt(await e.getHealingSession(w),"Healing session not found")},async listDeploymentHealingSessions(w){return e.listDeploymentHealingSessions(w?{repo:w}:void 0)},async getDeploymentHealingSession(w){return nt(await e.getDeploymentHealingSession(w),"Deployment healing session not found")},async getConfig(){return e.getConfig()},async updateConfig(w){let k=await e.updateConfig(w);return f&&g&&await j(),h(),k},async listSocialChangelogs(){return e.getSocialChangelogs()},async getSocialChangelog(w){return nt(await e.getSocialChangelog(w),"Changelog not found")},async listReleaseRuns(){return e.listReleaseRuns()},async getReleaseRun(w){return nt(await e.getReleaseRun(w),"Release run not found")},async retryReleaseRun(w){let k=await o.retryReleaseRun(w);if(!k)throw new $e(404,"Release run not found");return h(),k}};return L}function eC(t){return t instanceof $e}var tC="https://github.com/yungookim/oh-my-pr/releases",XY="https://api.github.com/repos/yungookim/oh-my-pr/releases/latest",KY=3600*1e3;function QY(t,e){let r=Ii(t),n=Ii(e);return!r||!n?0:r.major!==n.major?r.major-n.major:r.minor!==n.minor?r.minor-n.minor:r.patch-n.patch}function YY(t){return{currentVersion:t,latestVersion:null,latestReleaseUrl:tC,updateAvailable:!1}}async function ZY(t,e=fetch){let r=t.trim(),n=YY(r);if(!Ii(r))return n;try{let i=await e(XY,{headers:{accept:"application/vnd.github+json","user-agent":`oh-my-pr/${r}`}});if(!i.ok)return n;let s=await i.json(),a=s.tag_name?.trim()??null;return s.draft||s.prerelease||!a||!Ii(a)?n:{currentVersion:r,latestVersion:a,latestReleaseUrl:s.html_url?.trim()||tC,updateAvailable:QY(a,r)>0}}catch{return n}}function rC(t=fetch,e={}){let r=e.cacheTtlMs??KY,n=e.now??Date.now,i=new Map,s=new Map;return async a=>{let o=a.trim(),u=i.get(o);if(u&&u.expiresAt>n())return u.status;let c=s.get(o);if(c)return c;let l=ZY(o,t).then(p=>(i.set(o,{status:p,expiresAt:n()+r}),s.delete(o),p),p=>{throw s.delete(o),p});return s.set(o,l),l}}var iC="***";function eZ(t){return t instanceof Error?t.message:String(t)}function ve(t,e){if(e instanceof b.ZodError){t.status(400).json({error:e.errors[0]?.message??"Invalid request"});return}if(e instanceof Me){t.status(e.statusCode).json({error:e.message});return}if(eC(e)){t.status(e.statusCode).json({error:e.message});return}t.status(500).json({error:eZ(e)})}function sC(t){return t?`${iC}${t.slice(-4)}`:""}function tZ(t,e){let r=t.map(n=>({token:n,masked:sC(n),used:!1}));return e.map(n=>{let i=n.trim();if(!i)return"";if(i.startsWith(iC)){let s=r.find(a=>!a.used&&a.masked===i);if(s)return s.used=!0,s.token}return i}).filter(Boolean)}function rZ(t,e){let r=e.githubTokens??(e.githubToken!==void 0?[e.githubToken]:void 0);if(r===void 0)return e;let{githubToken:n,...i}=e;return{...i,githubTokens:tZ(t.githubTokens,r)}}function nC(t){let e=t.githubTokens.map(sC);return{...t,githubTokens:e,githubToken:e[0]??""}}async function aC(t,e,r={}){let n=r.runtime??ZP(r),i=r.appUpdateChecker??rC();return await n.start(),t.on("close",()=>{n.stop()}),e.get("/api/runtime",async(s,a)=>{a.json(await n.getRuntimeSnapshot())}),e.get("/api/activities",async(s,a)=>{a.json(await n.listActivities())}),e.delete("/api/activities/failed",async(s,a)=>{try{a.json(await n.clearFailedActivities())}catch(o){ve(a,o)}}),e.post("/api/runtime/drain",async(s,a)=>{try{let o=b.object({enabled:b.boolean(),reason:b.string().optional(),waitForIdle:b.boolean().optional(),timeoutMs:b.number().int().positive().max(6e5).optional()}).parse(s.body),u=await n.setDrainMode(o);if(o.enabled&&o.waitForIdle&&u.drained===!1)return a.status(202).json(u);a.json(u)}catch(o){ve(a,o)}}),e.get("/api/repos",async(s,a)=>{a.json(await n.listRepos())}),e.get("/api/repos/settings",async(s,a)=>{a.json(await n.listRepoSettings())}),e.post("/api/repos",async(s,a)=>{try{let{repo:o}=b.object({repo:b.string().min(1)}).parse(s.body);a.status(201).json(await n.addRepo(o))}catch(o){ve(a,o)}}),e.patch("/api/repos/settings",async(s,a)=>{try{let o=b.object({repo:b.string().min(1),autoCreateReleases:b.boolean().optional(),ownPrsOnly:b.boolean().optional()}).refine(l=>l.autoCreateReleases!==void 0||l.ownPrsOnly!==void 0,"At least one repository setting must be provided").parse(s.body),{repo:u,...c}=o;a.json(await n.updateRepoSettings(u,c))}catch(o){ve(a,o)}}),e.post("/api/repos/sync",async(s,a)=>{try{a.json(await n.syncRepos())}catch(o){ve(a,o)}}),e.post("/api/repos/release",async(s,a)=>{try{let{repo:o}=b.object({repo:b.string().min(1)}).parse(s.body);a.status(201).json(await n.createManualRelease(o))}catch(o){ve(a,o)}}),e.get("/api/prs",async(s,a)=>{a.json(await n.listPRs("active"))}),e.get("/api/prs/archived",async(s,a)=>{a.json(await n.listPRs("archived"))}),e.get("/api/prs/:id",async(s,a)=>{let o=await n.getPR(s.params.id);if(!o)return a.status(404).json({error:"PR not found"});a.json(o)}),e.post("/api/prs",async(s,a)=>{try{a.status(201).json(await n.addPR(s.body?.url))}catch(o){ve(a,o)}}),e.delete("/api/prs/:id",async(s,a)=>{try{a.json(await n.removePR(s.params.id))}catch(o){ve(a,o)}}),e.patch("/api/prs/:id/watch",async(s,a)=>{try{let{enabled:o}=b.object({enabled:b.boolean()}).parse(s.body);a.json(await n.setPRWatchEnabled(s.params.id,o))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/fetch",async(s,a)=>{try{a.json(await n.fetchPRFeedback(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/triage",async(s,a)=>{try{a.json(await n.triagePR(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/apply",async(s,a)=>{try{a.json(await n.applyPR(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/babysit",async(s,a)=>{try{a.json(await n.babysitPR(s.params.id))}catch(o){ve(a,o)}}),e.patch("/api/prs/:id/feedback/:feedbackId",async(s,a)=>{try{let{decision:o}=b.object({decision:b.enum(["accept","reject","flag"])}).parse(s.body);a.json(await n.setFeedbackDecision(s.params.id,s.params.feedbackId,o))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/feedback/:feedbackId/retry",async(s,a)=>{try{a.json(await n.retryFeedback(s.params.id,s.params.feedbackId))}catch(o){ve(a,o)}}),e.get("/api/prs/:id/questions",async(s,a)=>{try{a.json(await n.listPRQuestions(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/questions",async(s,a)=>{try{a.status(201).json(await n.askQuestion(s.params.id,s.body?.question))}catch(o){ve(a,o)}}),e.get("/api/logs",async(s,a)=>{let o=typeof s.query.prId=="string"?s.query.prId:void 0;a.json(await n.listLogs(o))}),e.get("/api/onboarding/status",async(s,a)=>{try{a.json(await n.getOnboardingStatus())}catch(o){ve(a,o)}}),e.post("/api/onboarding/install-review",async(s,a)=>{try{let{repo:o,tool:u}=b.object({repo:b.string().min(1),tool:b.enum(["claude","codex"])}).parse(s.body);a.json(await n.installReviewWorkflow(o,u))}catch(o){ve(a,o)}}),e.get("/api/healing-sessions",async(s,a)=>{try{a.json(await n.listHealingSessions())}catch(o){ve(a,o)}}),e.get("/api/healing-sessions/:id",async(s,a)=>{try{a.json(await n.getHealingSession(s.params.id))}catch(o){ve(a,o)}}),e.get("/api/deployment-healing-sessions",async(s,a)=>{try{let o=typeof s.query.repo=="string"?s.query.repo:void 0;a.json(await n.listDeploymentHealingSessions(o))}catch(o){ve(a,o)}}),e.get("/api/deployment-healing-sessions/:id",async(s,a)=>{try{a.json(await n.getDeploymentHealingSession(s.params.id))}catch(o){ve(a,o)}}),e.get("/api/config",async(s,a)=>{a.json(nC(await n.getConfig()))}),e.get("/api/app-update",async(s,a)=>{try{a.json(await i("4.2.0"))}catch(o){ve(a,o)}}),e.get("/api/changelogs",async(s,a)=>{try{a.json(await n.listSocialChangelogs())}catch(o){ve(a,o)}}),e.get("/api/changelogs/:id",async(s,a)=>{try{a.json(await n.getSocialChangelog(s.params.id))}catch(o){ve(a,o)}}),e.get("/api/releases",async(s,a)=>{try{a.json(await n.listReleaseRuns())}catch(o){ve(a,o)}}),e.get("/api/releases/:id",async(s,a)=>{try{a.json(await n.getReleaseRun(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/releases/:id/retry",async(s,a)=>{try{a.json(await n.retryReleaseRun(s.params.id))}catch(o){ve(a,o)}}),e.patch("/api/config",async(s,a)=>{try{let o=ic.partial().parse(s.body),u=await n.getConfig();a.json(nC(await n.updateConfig(rZ(u,o))))}catch(o){ve(a,o)}}),t}var oC=qe(Sf(),1),uC=qe(require("fs"),1),q0=qe(require("path"),1);function cC(t){let e=q0.default.resolve(__dirname,"public");if(!uC.default.existsSync(e))throw new Error(`Could not find the build directory: ${e}, make sure to build the client first`);t.use(oC.default.static(e)),t.use("/{*path}",(r,n)=>{n.sendFile(q0.default.resolve(e,"index.html"))})}var nZ=new Set(["127.0.0.1","::1","::ffff:127.0.0.1","localhost"]);function iZ(t){if(!t)return!1;let e=t.replace(/^\[|\]$/g,"");return nZ.has(e)?!0:e.startsWith("::ffff:")?e.slice(7).startsWith("127."):!!e.startsWith("127.")}function lC(t,e,r){let n=t.ip??t.socket?.remoteAddress;if(!iZ(n)){e.status(403).json({error:"Forbidden",message:"oh-my-pr only accepts connections from the local machine. External access is not permitted."});return}r()}var dC=require("http");var sZ=zt("server"),Gi=(0,ip.default)(),pC=(0,dC.createServer)(Gi);Gi.use(ip.default.json({verify:(t,e,r)=>{t.rawBody=r}}));Gi.use(ip.default.urlencoded({extended:!1}));Gi.use("/api",lC);function fC(t,e="express"){Jg.info({source:e},t)}async function aZ(t){let{default:e}=await import("open");await e(t)}(async()=>{await aC(pC,Gi),Gi.use((e,r,n,i)=>{let s=e,a=s.status||s.statusCode||500,o=s.message||"Internal Server Error";return sZ.error({err:e instanceof Error?e.message:String(e),status:a},"Internal Server Error"),n.headersSent?i(e):n.status(a).json({message:o})}),cC(Gi);let t=parseInt(process.env.PORT||"5001",10);pC.listen({port:t,host:"0.0.0.0"},()=>{let e=`http://localhost:${t}`;console.log(`
1083
- oh-my-pr v4.2.0
1082
+ `):"- Manual release requested."}`}function BY(t){return(t instanceof Error?t.message:String(t)).trim().slice(0,2e3)}function VP(t){return t==="skipped"||t==="published"}function HY(t){return new Promise(e=>setTimeout(e,t))}var $Y=["fix_submitted","escalated"],zY={monitoring:["failed","escalated"],failed:["fixing","escalated"],fixing:["fix_submitted","escalated"],fix_submitted:[],escalated:[]},rp=class{constructor(e,r=()=>new Date){this.storage=e;this.clock=r}async createSession(e){return this.storage.createDeploymentHealingSession({...e,deploymentId:null,deploymentLog:null,fixBranch:null,fixPrNumber:null,fixPrUrl:null,state:"monitoring",error:null,completedAt:null})}async ensureSession(e){let r=await this.storage.getDeploymentHealingSessionByRepoAndMergeSha(e.repo,e.mergeSha);return r||this.createSession(e)}async transitionTo(e,r,n={}){let i=await this.storage.getDeploymentHealingSession(e);if(!i)throw new Error(`Deployment healing session not found: ${e}`);if(i.state===r){let o=await this.storage.updateDeploymentHealingSession(e,n);if(!o)throw new Error(`Deployment healing session not found: ${e}`);return o}if(!zY[i.state].includes(r))throw new Error(`Illegal deployment healing transition: ${i.state} -> ${r}`);let s={...n,state:r};$Y.includes(r)&&(s.completedAt=n.completedAt??this.clock().toISOString());let a=await this.storage.updateDeploymentHealingSession(e,s);if(!a)throw new Error(`Deployment healing session not found: ${e}`);return a}};var JP=zt("runtime"),$e=class extends Error{statusCode;constructor(e,r){super(r),this.name="AppRuntimeError",this.statusCode=e}};function XP(t){return t instanceof Error?t.message:String(t)}function nt(t,e){if(t===void 0)throw new $e(404,e);return t}function WY(t){switch(t.kind){case"sync_watched_repos":return"Sync watched repositories";case"babysit_pr":return"Babysitting PR";case"process_release_run":return"Processing release";case"answer_pr_question":return"Answering PR question";case"generate_social_changelog":return"Social changelog generation removed";case"heal_deployment":return"Healing deployment"}}function np(t,e){let r=t.payload[e];return typeof r=="string"&&r.trim()?r:null}var VY={Claude:{auth:["Run `claude auth login` on this machine.","Restart oh-my-pr if it was launched before you refreshed credentials.","Rerun the babysitter for this PR."],cli_missing:["Install the Claude Code CLI on this machine.","Restart oh-my-pr after installing.","Rerun the babysitter for this PR."]},Codex:{auth:["Run `codex login` on this machine.","Restart oh-my-pr if it was launched before you refreshed credentials.","Rerun the babysitter for this PR."],cli_missing:["Install the Codex CLI on this machine.","Restart oh-my-pr after installing.","Rerun the babysitter for this PR."]}};function j0(t,e){return{agentLabel:t,kind:e,fixSteps:VY[t][e]}}function JY(t){let e=t.toLowerCase();return e.includes("claude evaluation failed")||e.includes("claude apply failed")?"Claude":e.includes("codex evaluation failed")||e.includes("codex apply failed")?"Codex":null}function KP(t){if(t.kind!=="babysit_pr"||!t.lastError)return null;let e=lm(t.lastError);if(!e)return null;let r=JY(t.lastError);if(r)return j0(r,e);let n=np(t,"preferredAgent");return n==="claude"?j0("Claude",e):n==="codex"?j0("Codex",e):null}function QP(t){return t.flatMap(e=>{let r=e.mergeCommitSha?.trim();return r?[{number:e.number,title:e.title,url:e.url,author:e.author,repo:e.repo,mergedAt:e.mergedAt,mergeSha:r}]:[]})}function ZP(t={}){let e=t.storage??Q_(),r=new YP.EventEmitter,n=t.backgroundJobQueue??new Xl(e),i,s=async(...w)=>{let k=await n.enqueue(...w);return i.wake(),k},a=t.deploymentHealingManager??new rp(e),o=t.releaseManager??new tp(e,{github:{buildOctokit:lr,getDefaultBranch:E0,findLatestSemverReleaseTag:P4,bumpReleaseTag:A4,listUnreleasedMergedPulls:async(w,k,x)=>{let C=await T0(w,k,{baseRef:x.baseBranch});return QP(C)},listMergedPullsForReleaseCandidate:async(w,k,x)=>{let C=await T0(w,k,{baseRef:x.baseBranch}),_=Date.parse(x.untilMergedAt);return QP(C.filter(D=>!Number.isFinite(_)||Date.parse(D.mergedAt)<=_))},findReleaseByTag:async(w,k,x)=>{let _=(await Gl(w,k)).find(D=>!D.draft&&D.tagName===x);return _?{id:_.id,url:_.htmlUrl,tagName:_.tagName,name:_.name}:null},createGitHubRelease:async(w,k,x)=>{let C=await C4(w,k,{tagName:x.tagName,targetCommitish:x.targetCommitish,name:x.name,body:x.body});return{id:C.id,url:C.htmlUrl,tagName:C.tagName,name:C.name}}},scheduleBackgroundJob:s}),u=t.babysitter??new Zl(e,void 0,void 0,o,s,a);i=t.backgroundJobDispatcher??new ep({storage:e,queue:n,handlers:BP({storage:e,babysitter:u,releaseManager:o,deploymentHealingManager:a}),onReclaimedJobs:w=>{for(let k of w)k.kind==="babysit_pr"&&e.addLog(k.targetId,"warn",`Reclaimed expired background job ${k.id} for PR ${k.targetId}`,{phase:"background.job",metadata:{jobId:k.id,kind:k.kind,leaseOwner:k.leaseOwner,leaseExpiresAt:k.leaseExpiresAt,attemptCount:k.attemptCount}}).catch(x=>{JP.warn({err:x instanceof Error?x.message:String(x)},"Failed to log reclaimed background job")})}});let c=null,l=0,p=t.watcherScheduler??HP(async()=>{await s("sync_watched_repos","runtime:1",Or("sync_watched_repos","runtime:1"))},w=>{JP.warn({err:w instanceof Error?w.message:String(w)},"Repository babysitter watcher failed")}),d=p.run,m=t.startBackgroundServices??!0,f=t.startWatcher??m,g=!1,h=()=>{r.emit("change")},T=async()=>({...await e.getRuntimeState(),activeRuns:i.getActiveRunCount()}),R=async w=>{let k=new Set,x=new Set,C=new Set,_=new Set;for(let fe of w)if(fe.kind==="babysit_pr")k.add(fe.targetId);else if(fe.kind==="answer_pr_question"){let oe=np(fe,"prId");oe&&k.add(oe)}else fe.kind==="process_release_run"?x.add(fe.targetId):fe.kind==="generate_social_changelog"?C.add(fe.targetId):fe.kind==="heal_deployment"&&_.add(fe.targetId);let[D,E,U,Z,ge]=await Promise.all([k.size>0?e.getPRs():Promise.resolve([]),k.size>0?e.getArchivedPRs():Promise.resolve([]),x.size>0?e.listReleaseRuns():Promise.resolve([]),C.size>0?e.getSocialChangelogs():Promise.resolve([]),_.size>0?e.listDeploymentHealingSessions():Promise.resolve([])]),Ee=new Map;for(let fe of ge)Ee.set(fe.id,fe),Ee.set(`${fe.repo}:${fe.mergeSha}`,fe);return{prsById:new Map([...D,...E].map(fe=>[fe.id,fe])),releaseRunsById:new Map(U.map(fe=>[fe.id,fe])),socialChangelogsById:new Map(Z.map(fe=>[fe.id,fe])),deploymentHealingSessionsByTarget:Ee}},S=(w,k)=>{let x=oP(w.payload);if(x)return x;if(w.kind==="sync_watched_repos")return{label:"Sync watched repositories",detail:null,targetUrl:null};if(w.kind==="babysit_pr"){let C=k.prsById.get(w.targetId);if(C)return{label:`Babysitting PR #${C.number}`,detail:`${C.repo} - ${C.title}`,targetUrl:C.url}}if(w.kind==="answer_pr_question"){let C=np(w,"prId"),_=C?k.prsById.get(C):void 0;if(_)return{label:`Answering question for PR #${_.number}`,detail:`${_.repo} - ${_.title}`,targetUrl:_.url}}if(w.kind==="process_release_run"){let C=k.releaseRunsById.get(w.targetId);if(C)return{label:`Processing release for ${C.repo}`,detail:`PR #${C.triggerPrNumber} - ${C.triggerPrTitle}`,targetUrl:C.triggerPrUrl}}if(w.kind==="generate_social_changelog"){let C=k.socialChangelogsById.get(w.targetId);if(C)return{label:"Social changelog generation removed",detail:`${C.date} - ${C.triggerCount} merged PRs`,targetUrl:null}}if(w.kind==="heal_deployment"){let C=k.deploymentHealingSessionsByTarget.get(w.targetId);if(C)return{label:`Healing ${C.platform} deployment`,detail:`${C.repo} PR #${C.triggerPrNumber} - ${C.triggerPrTitle}`,targetUrl:C.triggerPrUrl}}return{label:WY(w),detail:w.targetId,targetUrl:null}},P=(w,k)=>{let x=S(w,k);return{id:w.id,kind:w.kind,status:w.status==="leased"?"in_progress":w.status==="failed"?"failed":"queued",label:x.label,detail:x.detail,targetId:w.targetId,targetUrl:x.targetUrl,queuedAt:w.createdAt,availableAt:w.availableAt,startedAt:w.heartbeatAt,updatedAt:w.updatedAt,attemptCount:w.attemptCount,lastError:w.lastError}},F=(w,k)=>{if(w.kind==="babysit_pr")return k.prsById.get(w.targetId)?.status==="archived";if(w.kind==="answer_pr_question"){let x=np(w,"prId");return x?k.prsById.get(x)?.status==="archived":!1}return!1},I=(w,k)=>{let x=KP(w);if(!x)return null;let C=k.prsById.get(w.targetId);if(!C||C.status!=="error")return null;let _=x.kind==="auth"?"authentication failed":"CLI not installed",D=x.kind==="auth"?"local agent credentials are invalid or expired":"the agent CLI is not installed on this machine";return{id:w.id,severity:"warning",title:`${x.agentLabel} ${_}`,message:`Babysitter could not run ${x.agentLabel} for PR #${C.number} in ${C.repo} because ${D}.`,fixSteps:x.fixSteps,targetId:w.targetId,targetUrl:C.url,createdAt:w.createdAt,updatedAt:w.updatedAt}},A=async w=>{let[k,x,C]=await Promise.all([i.waitForIdle(w),u.waitForIdle(w),o.waitForIdle(w)]);return k&&x&&C},j=async()=>{let w=await e.getConfig(),k=Math.max(1e4,w.pollIntervalMs||12e4);c&&l===k||(c&&(clearInterval(c),c=null),l=k,c=setInterval(()=>{d()},k))},v=async(w,k)=>{await s("babysit_pr",w.id,Or("babysit_pr",w.id),{preferredAgent:k,...On({label:`Babysitting PR #${w.number}`,detail:`${w.repo} - ${w.title}`,targetUrl:w.url})})},L={async start(){g||(g=!0,m&&await i.start(),f&&(await j(),u.resumeInterruptedRuns(),d()))},stop(){g=!1,i.stop(),c&&(clearInterval(c),c=null)},subscribe(w){return r.on("change",w),()=>{r.off("change",w)}},getRuntimeSnapshot:T,async listActivities(){let[w,k,x]=await Promise.all([e.listBackgroundJobs({status:"failed"}),e.listBackgroundJobs({status:"leased"}),e.listBackgroundJobs({status:"queued"})]),C=await R([...w,...k,...x]),_=w.filter(Ee=>!F(Ee,C)),D=_.filter(Ee=>KP(Ee)),E=_.map(Ee=>P(Ee,C)),U=k.map(Ee=>P(Ee,C)),Z=x.map(Ee=>P(Ee,C)),ge=D.map(Ee=>I(Ee,C)).filter(Ee=>!!Ee).sort((Ee,fe)=>Date.parse(fe.updatedAt)-Date.parse(Ee.updatedAt)).slice(0,5);return{failed:E,inProgress:U,queued:Z,warnings:ge,generatedAt:new Date().toISOString()}},async clearFailedActivities(){let w=await e.clearFailedBackgroundJobs();return w>0&&h(),{cleared:w}},async setDrainMode(w){let k=await e.updateRuntimeState({drainMode:w.enabled,drainRequestedAt:w.enabled?new Date().toISOString():null,drainReason:w.enabled?w.reason??null:null});if(w.enabled&&w.waitForIdle){let C=await A(w.timeoutMs??12e4),_=await T();return h(),{...k,..._,drained:C}}let x=await T();return h(),{...k,...x}},async listRepos(){let w=await e.getConfig(),k=await e.getPRs();return Array.from(new Set([...w.watchedRepos,...k.map(x=>x.repo)])).sort((x,C)=>x.localeCompare(C))},async listRepoSettings(){let[w,k]=await Promise.all([e.listRepoSettings(),e.getPRs()]),x=new Map(w.map(C=>[C.repo,C]));for(let C of k)x.has(C.repo)||x.set(C.repo,{repo:C.repo,autoCreateReleases:!0,ownPrsOnly:!0});return Array.from(x.values()).sort((C,_)=>C.repo.localeCompare(_.repo))},async addRepo(w){let k=dt(w);if(!k)throw new $e(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let x=Pn(k),C=await e.getConfig();return C.watchedRepos.includes(x)||await e.updateConfig({watchedRepos:[...C.watchedRepos,x].sort((_,D)=>_.localeCompare(D))}),d(),h(),{repo:x}},async updateRepoSettings(w,k){let x=dt(w);if(!x)throw new $e(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let C=Pn(x),_=await e.updateRepoSettings(C,k);return h(),_},async syncRepos(){if((await e.getRuntimeState()).drainMode)throw new $e(409,"Drain mode is enabled. Sync-triggered runs are blocked until drain mode is disabled.");return await p.runAndReportErrors(),h(),{ok:!0}},async createManualRelease(w){let k=dt(w);if(!k)throw new $e(400,"Invalid repository. Use owner/repo or https://github.com/owner/repo");let x=Pn(k),C=await o.enqueueManualRepoRelease(x);if(!C)throw new $e(409,`No unreleased merged pull requests found for ${x}`);return h(),C},async listPRs(w="active"){return w==="archived"?e.getArchivedPRs():e.getPRs()},async getPR(w){return await e.getPR(w)??null},async addPR(w){let k;try{({url:k}=U_.parse({url:w}))}catch(ge){throw ge instanceof b.ZodError?new $e(400,ge.errors[0]?.message??"Invalid PR URL"):ge}let x=jl(k);if(!x)throw new $e(400,"Invalid GitHub PR URL. Expected: https://github.com/owner/repo/pull/123");let C=`${x.owner}/${x.repo}`,_=await e.getPRByRepoAndNumber(C,x.number);if(_)return _;let D=await e.getConfig(),E=await lr(D),U=await Ul(E,x),Z=await e.addPR({number:x.number,title:U.title,repo:C,branch:U.branch,author:U.author,url:U.url,status:"watching",feedbackItems:[],accepted:0,rejected:0,flagged:0,testsPassed:null,lintPassed:null,lastChecked:null});return await e.addLog(Z.id,"info",`Registered PR #${x.number} from ${C}`),await e.addLog(Z.id,"info",`Repository ${C} added to auto-babysit watch list`),D.watchedRepos.includes(C)||await e.updateConfig({watchedRepos:[...D.watchedRepos,C].sort((ge,Ee)=>ge.localeCompare(Ee))}),await v(Z,D.codingAgent),h(),Z},async removePR(w){if(!await e.removePR(w))throw new $e(404,"PR not found");return h(),{ok:!0}},async setPRWatchEnabled(w,k){let x=nt(await e.getPR(w),"PR not found"),C=await e.updatePR(x.id,{watchEnabled:k}),_=nt(C,"PR not found");return x.watchEnabled!==k&&(await e.addLog(x.id,"info",k?"Background watch resumed":"Background watch paused"),k&&d()),h(),_},async setWatchEnabled(w,k){return L.setPRWatchEnabled(w,k)},async fetchPRFeedback(w){let k=nt(await e.getPR(w),"PR not found");await e.updatePR(k.id,{status:"processing",lastChecked:new Date().toISOString()}),await e.addLog(k.id,"info","Syncing GitHub comments/reviews...");try{let x=await u.syncFeedbackForPR(k.id);return h(),x}catch(x){let C=XP(x);throw await e.updatePR(k.id,{status:"error",lastChecked:new Date().toISOString()}),await e.addLog(k.id,"error",`Fetch failed: ${C}`),x}},async triagePR(w){let k=nt(await e.getPR(w),"PR not found");await e.updatePR(k.id,{status:"processing"}),await e.addLog(k.id,"info","Triaging feedback...");let x=k.feedbackItems.map(U=>{if(U.decision)return U;let Z=U.body.toLowerCase();return Z.includes("lgtm")||Z.includes("looks good")?Ni(U,!1,"Acknowledgement, no code change requested"):Z.includes("please")||Z.includes("should")||Z.includes("fix")||Z.includes("error")||Z.includes("fail")?{...Ni(U,!0,"Likely actionable request"),action:U.body}:pP(U,"Unclear actionability, flagged for manual review")}),C=x.filter(U=>U.decision==="accept").length,_=x.filter(U=>U.decision==="reject").length,D=x.filter(U=>U.decision==="flag").length,E=await e.updatePR(k.id,{feedbackItems:x,accepted:C,rejected:_,flagged:D,status:"watching"});return await e.addLog(k.id,"info",`Triage complete: ${C} accept, ${_} reject, ${D} flag`),h(),nt(E,"PR not found")},async applyPR(w){let k=nt(await e.getPR(w),"PR not found");if((await e.getRuntimeState()).drainMode)throw new $e(409,"Drain mode is enabled. Manual runs are blocked until drain mode is disabled.");let C=await e.getConfig();await e.updatePR(k.id,{status:"processing"}),await e.addLog(k.id,"info",`Launching autonomous babysitter run using ${C.codingAgent}`),await v(k,C.codingAgent);let _=await e.getPR(k.id);return h(),nt(_,"PR disappeared after apply run")},async babysitPR(w){let k=nt(await e.getPR(w),"PR not found");if((await e.getRuntimeState()).drainMode)throw new $e(409,"Drain mode is enabled. Manual runs are blocked until drain mode is disabled.");let C=await e.getConfig();await e.addLog(k.id,"info",`Manual babysitter trigger using ${C.codingAgent}`),await v(k,C.codingAgent);let _=await e.getPR(k.id);return h(),nt(_,"PR disappeared after babysit run")},async queueBabysit(w){return L.babysitPR(w)},async setFeedbackDecision(w,k,x){let C=nt(await e.getPR(w),"PR not found"),_=await LP({storage:e,pr:C,feedbackId:k,decision:x});return h(),nt(_,"PR not found")},async retryFeedback(w,k){let x=await u.retryFeedbackItem(w,k);if(x.kind==="pr_not_found")throw new $e(404,"PR not found");if(x.kind==="feedback_not_found")throw new $e(404,"Feedback item not found");if(x.kind==="feedback_not_retryable")throw new $e(400,"Only failed or warning items can be retried");await e.addLog(w,"info",`Feedback item ${k} queued for retry`);let C=await e.getConfig();return await v(x.updated,C.codingAgent),h(),x.updated},async listPRQuestions(w){return nt(await e.getPR(w),"PR not found"),e.getQuestions(w)},async askQuestion(w,k){let x=nt(await e.getPR(w),"PR not found"),C;try{C=H_.parse({question:k})}catch(D){throw D instanceof b.ZodError?new $e(400,D.errors[0]?.message??"Invalid question"):D}let _=await e.addQuestion(w,C.question);try{await s("answer_pr_question",_.id,Or("answer_pr_question",_.id),{prId:w,...On({label:`Answering question for PR #${x.number}`,detail:`${x.repo} - ${x.title}`,targetUrl:x.url})})}catch(D){let E=XP(D);throw await e.updateQuestion(_.id,{status:"error",error:E.trim().slice(0,2e3)}),D}return h(),_},async listLogs(w){return e.getLogs(w)},async getOnboardingStatus(){let w=await e.getConfig();return S4(w,w.watchedRepos)},async installReviewWorkflow(w,k){let x=await e.getConfig();return k4(x,w,k)},async listHealingSessions(){return e.listHealingSessions()},async getHealingSession(w){return nt(await e.getHealingSession(w),"Healing session not found")},async listDeploymentHealingSessions(w){return e.listDeploymentHealingSessions(w?{repo:w}:void 0)},async getDeploymentHealingSession(w){return nt(await e.getDeploymentHealingSession(w),"Deployment healing session not found")},async getConfig(){return e.getConfig()},async updateConfig(w){let k=await e.updateConfig(w);return f&&g&&await j(),h(),k},async listSocialChangelogs(){return e.getSocialChangelogs()},async getSocialChangelog(w){return nt(await e.getSocialChangelog(w),"Changelog not found")},async listReleaseRuns(){return e.listReleaseRuns()},async getReleaseRun(w){return nt(await e.getReleaseRun(w),"Release run not found")},async retryReleaseRun(w){let k=await o.retryReleaseRun(w);if(!k)throw new $e(404,"Release run not found");return h(),k}};return L}function eC(t){return t instanceof $e}var tC="https://github.com/yungookim/oh-my-pr/releases",XY="https://api.github.com/repos/yungookim/oh-my-pr/releases/latest",KY=3600*1e3;function QY(t,e){let r=Ii(t),n=Ii(e);return!r||!n?0:r.major!==n.major?r.major-n.major:r.minor!==n.minor?r.minor-n.minor:r.patch-n.patch}function YY(t){return{currentVersion:t,latestVersion:null,latestReleaseUrl:tC,updateAvailable:!1}}async function ZY(t,e=fetch){let r=t.trim(),n=YY(r);if(!Ii(r))return n;try{let i=await e(XY,{headers:{accept:"application/vnd.github+json","user-agent":`oh-my-pr/${r}`}});if(!i.ok)return n;let s=await i.json(),a=s.tag_name?.trim()??null;return s.draft||s.prerelease||!a||!Ii(a)?n:{currentVersion:r,latestVersion:a,latestReleaseUrl:s.html_url?.trim()||tC,updateAvailable:QY(a,r)>0}}catch{return n}}function rC(t=fetch,e={}){let r=e.cacheTtlMs??KY,n=e.now??Date.now,i=new Map,s=new Map;return async a=>{let o=a.trim(),u=i.get(o);if(u&&u.expiresAt>n())return u.status;let c=s.get(o);if(c)return c;let l=ZY(o,t).then(p=>(i.set(o,{status:p,expiresAt:n()+r}),s.delete(o),p),p=>{throw s.delete(o),p});return s.set(o,l),l}}var iC="***";function eZ(t){return t instanceof Error?t.message:String(t)}function ve(t,e){if(e instanceof b.ZodError){t.status(400).json({error:e.errors[0]?.message??"Invalid request"});return}if(e instanceof Me){t.status(e.statusCode).json({error:e.message});return}if(eC(e)){t.status(e.statusCode).json({error:e.message});return}t.status(500).json({error:eZ(e)})}function sC(t){return t?`${iC}${t.slice(-4)}`:""}function tZ(t,e){let r=t.map(n=>({token:n,masked:sC(n),used:!1}));return e.map(n=>{let i=n.trim();if(!i)return"";if(i.startsWith(iC)){let s=r.find(a=>!a.used&&a.masked===i);if(s)return s.used=!0,s.token}return i}).filter(Boolean)}function rZ(t,e){let r=e.githubTokens??(e.githubToken!==void 0?[e.githubToken]:void 0);if(r===void 0)return e;let{githubToken:n,...i}=e;return{...i,githubTokens:tZ(t.githubTokens,r)}}function nC(t){let e=t.githubTokens.map(sC);return{...t,githubTokens:e,githubToken:e[0]??""}}async function aC(t,e,r={}){let n=r.runtime??ZP(r),i=r.appUpdateChecker??rC();return await n.start(),t.on("close",()=>{n.stop()}),e.get("/api/runtime",async(s,a)=>{a.json(await n.getRuntimeSnapshot())}),e.get("/api/activities",async(s,a)=>{a.json(await n.listActivities())}),e.delete("/api/activities/failed",async(s,a)=>{try{a.json(await n.clearFailedActivities())}catch(o){ve(a,o)}}),e.post("/api/runtime/drain",async(s,a)=>{try{let o=b.object({enabled:b.boolean(),reason:b.string().optional(),waitForIdle:b.boolean().optional(),timeoutMs:b.number().int().positive().max(6e5).optional()}).parse(s.body),u=await n.setDrainMode(o);if(o.enabled&&o.waitForIdle&&u.drained===!1)return a.status(202).json(u);a.json(u)}catch(o){ve(a,o)}}),e.get("/api/repos",async(s,a)=>{a.json(await n.listRepos())}),e.get("/api/repos/settings",async(s,a)=>{a.json(await n.listRepoSettings())}),e.post("/api/repos",async(s,a)=>{try{let{repo:o}=b.object({repo:b.string().min(1)}).parse(s.body);a.status(201).json(await n.addRepo(o))}catch(o){ve(a,o)}}),e.patch("/api/repos/settings",async(s,a)=>{try{let o=b.object({repo:b.string().min(1),autoCreateReleases:b.boolean().optional(),ownPrsOnly:b.boolean().optional()}).refine(l=>l.autoCreateReleases!==void 0||l.ownPrsOnly!==void 0,"At least one repository setting must be provided").parse(s.body),{repo:u,...c}=o;a.json(await n.updateRepoSettings(u,c))}catch(o){ve(a,o)}}),e.post("/api/repos/sync",async(s,a)=>{try{a.json(await n.syncRepos())}catch(o){ve(a,o)}}),e.post("/api/repos/release",async(s,a)=>{try{let{repo:o}=b.object({repo:b.string().min(1)}).parse(s.body);a.status(201).json(await n.createManualRelease(o))}catch(o){ve(a,o)}}),e.get("/api/prs",async(s,a)=>{a.json(await n.listPRs("active"))}),e.get("/api/prs/archived",async(s,a)=>{a.json(await n.listPRs("archived"))}),e.get("/api/prs/:id",async(s,a)=>{let o=await n.getPR(s.params.id);if(!o)return a.status(404).json({error:"PR not found"});a.json(o)}),e.post("/api/prs",async(s,a)=>{try{a.status(201).json(await n.addPR(s.body?.url))}catch(o){ve(a,o)}}),e.delete("/api/prs/:id",async(s,a)=>{try{a.json(await n.removePR(s.params.id))}catch(o){ve(a,o)}}),e.patch("/api/prs/:id/watch",async(s,a)=>{try{let{enabled:o}=b.object({enabled:b.boolean()}).parse(s.body);a.json(await n.setPRWatchEnabled(s.params.id,o))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/fetch",async(s,a)=>{try{a.json(await n.fetchPRFeedback(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/triage",async(s,a)=>{try{a.json(await n.triagePR(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/apply",async(s,a)=>{try{a.json(await n.applyPR(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/babysit",async(s,a)=>{try{a.json(await n.babysitPR(s.params.id))}catch(o){ve(a,o)}}),e.patch("/api/prs/:id/feedback/:feedbackId",async(s,a)=>{try{let{decision:o}=b.object({decision:b.enum(["accept","reject","flag"])}).parse(s.body);a.json(await n.setFeedbackDecision(s.params.id,s.params.feedbackId,o))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/feedback/:feedbackId/retry",async(s,a)=>{try{a.json(await n.retryFeedback(s.params.id,s.params.feedbackId))}catch(o){ve(a,o)}}),e.get("/api/prs/:id/questions",async(s,a)=>{try{a.json(await n.listPRQuestions(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/prs/:id/questions",async(s,a)=>{try{a.status(201).json(await n.askQuestion(s.params.id,s.body?.question))}catch(o){ve(a,o)}}),e.get("/api/logs",async(s,a)=>{let o=typeof s.query.prId=="string"?s.query.prId:void 0;a.json(await n.listLogs(o))}),e.get("/api/onboarding/status",async(s,a)=>{try{a.json(await n.getOnboardingStatus())}catch(o){ve(a,o)}}),e.post("/api/onboarding/install-review",async(s,a)=>{try{let{repo:o,tool:u}=b.object({repo:b.string().min(1),tool:b.enum(["claude","codex"])}).parse(s.body);a.json(await n.installReviewWorkflow(o,u))}catch(o){ve(a,o)}}),e.get("/api/healing-sessions",async(s,a)=>{try{a.json(await n.listHealingSessions())}catch(o){ve(a,o)}}),e.get("/api/healing-sessions/:id",async(s,a)=>{try{a.json(await n.getHealingSession(s.params.id))}catch(o){ve(a,o)}}),e.get("/api/deployment-healing-sessions",async(s,a)=>{try{let o=typeof s.query.repo=="string"?s.query.repo:void 0;a.json(await n.listDeploymentHealingSessions(o))}catch(o){ve(a,o)}}),e.get("/api/deployment-healing-sessions/:id",async(s,a)=>{try{a.json(await n.getDeploymentHealingSession(s.params.id))}catch(o){ve(a,o)}}),e.get("/api/config",async(s,a)=>{a.json(nC(await n.getConfig()))}),e.get("/api/app-update",async(s,a)=>{try{a.json(await i("4.2.1"))}catch(o){ve(a,o)}}),e.get("/api/changelogs",async(s,a)=>{try{a.json(await n.listSocialChangelogs())}catch(o){ve(a,o)}}),e.get("/api/changelogs/:id",async(s,a)=>{try{a.json(await n.getSocialChangelog(s.params.id))}catch(o){ve(a,o)}}),e.get("/api/releases",async(s,a)=>{try{a.json(await n.listReleaseRuns())}catch(o){ve(a,o)}}),e.get("/api/releases/:id",async(s,a)=>{try{a.json(await n.getReleaseRun(s.params.id))}catch(o){ve(a,o)}}),e.post("/api/releases/:id/retry",async(s,a)=>{try{a.json(await n.retryReleaseRun(s.params.id))}catch(o){ve(a,o)}}),e.patch("/api/config",async(s,a)=>{try{let o=ic.partial().parse(s.body),u=await n.getConfig();a.json(nC(await n.updateConfig(rZ(u,o))))}catch(o){ve(a,o)}}),t}var oC=qe(Sf(),1),uC=qe(require("fs"),1),q0=qe(require("path"),1);function cC(t){let e=q0.default.resolve(__dirname,"public");if(!uC.default.existsSync(e))throw new Error(`Could not find the build directory: ${e}, make sure to build the client first`);t.use(oC.default.static(e)),t.use("/{*path}",(r,n)=>{n.sendFile(q0.default.resolve(e,"index.html"))})}var nZ=new Set(["127.0.0.1","::1","::ffff:127.0.0.1","localhost"]);function iZ(t){if(!t)return!1;let e=t.replace(/^\[|\]$/g,"");return nZ.has(e)?!0:e.startsWith("::ffff:")?e.slice(7).startsWith("127."):!!e.startsWith("127.")}function lC(t,e,r){let n=t.ip??t.socket?.remoteAddress;if(!iZ(n)){e.status(403).json({error:"Forbidden",message:"oh-my-pr only accepts connections from the local machine. External access is not permitted."});return}r()}var dC=require("http");var sZ=zt("server"),Gi=(0,ip.default)(),pC=(0,dC.createServer)(Gi);Gi.use(ip.default.json({verify:(t,e,r)=>{t.rawBody=r}}));Gi.use(ip.default.urlencoded({extended:!1}));Gi.use("/api",lC);function fC(t,e="express"){Jg.info({source:e},t)}async function aZ(t){let{default:e}=await import("open");await e(t)}(async()=>{await aC(pC,Gi),Gi.use((e,r,n,i)=>{let s=e,a=s.status||s.statusCode||500,o=s.message||"Internal Server Error";return sZ.error({err:e instanceof Error?e.message:String(e),status:a},"Internal Server Error"),n.headersSent?i(e):n.status(a).json({message:o})}),cC(Gi);let t=parseInt(process.env.PORT||"5001",10);pC.listen({port:t,host:"0.0.0.0"},()=>{let e=`http://localhost:${t}`;console.log(`
1083
+ oh-my-pr v4.2.1
1084
1084
  Dashboard: ${e}
1085
1085
  `),!process.env.TAURI_DEV&&!process.env.OH_MY_PR_DESKTOP&&aZ(e).catch(n=>{fC(`Could not open browser automatically: ${n.message}`)})})})();0&&(module.exports={log});
1086
1086
  /*! Bundled license information:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-pr",
3
- "version": "4.2.0",
3
+ "version": "4.2.1",
4
4
  "description": "Autonomous GitHub PR babysitter that watches repos, triages review feedback, and dispatches AI agents to fix code locally",
5
5
  "keywords": [
6
6
  "github",