alchemylab 3.0.6 → 3.0.7

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/cli.js +448 -362
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1133,11 +1133,11 @@ let d,u
1133
1133
  if(a(s,c,e.health?.service),e.health){const t=await n(o,e.health)
1134
1134
  d=t.body||void 0,u=t.healthy}return{projectName:s,target:t,running:l,health:d,healthy:u}}
1135
1135
  const p=t(79896),f=h(t(16928)),m=u(t(7089)),g=t(55481),w=t(81618),y=t(28295),v=m.safeLoad||m.load,b=m.safeDump||m.dump},46508(e,exports,t){"use strict"
1136
- async function r(e,t,r){const s=(0,d.simpleGit)(e),n=`refs/heads/requests/${t}`
1136
+ async function r(e,t,r){const s=(0,u.simpleGit)(e),n=`refs/heads/requests/${t}`
1137
1137
  try{const t=(await s.raw(["rev-parse","--verify","--quiet",n])).trim()
1138
1138
  if(!t)return!1
1139
- const r=await(0,h.resolveDefaultBranch)(e)
1140
- return!!await(0,u.isAncestorCommit)(s,t,`refs/heads/${r}`)&&!(await s.raw(["worktree","list","--porcelain"])).split("\n").some(e=>e.trim()===`branch ${n}`)&&(await s.raw(["update-ref","-d",n,t]),!0)}catch(e){return/unable to resolve reference|not a valid ref|does not exist/i.test((0,a.getErrorMessage)(e))||r?.warn("could not drop the merged request branch; keeping it",{requestId:t,error:(0,a.getErrorMessage)(e)}),!1}}var s,n=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
1139
+ const r=await(0,p.resolveDefaultBranch)(e)
1140
+ return!!await(0,h.isAncestorCommit)(s,t,`refs/heads/${r}`)&&!(await s.raw(["worktree","list","--porcelain"])).split("\n").some(e=>e.trim()===`branch ${n}`)&&(await s.raw(["update-ref","-d",n,t]),!0)}catch(e){return/unable to resolve reference|not a valid ref|does not exist/i.test((0,a.getErrorMessage)(e))||r?.warn("could not drop the merged request branch; keeping it",{requestId:t,error:(0,a.getErrorMessage)(e)}),!1}}var s,n=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
1141
1141
  var n=Object.getOwnPropertyDescriptor(t,r)
1142
1142
  n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,s,n)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),i=this&&this.__importStar||(s=function(e){return s=Object.getOwnPropertyNames||function(e){var t=[]
1143
1143
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
@@ -1148,24 +1148,28 @@ return o(t,e),t})
1148
1148
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.dropBranchIfContained=r,exports.handlePruneWorktreeAction=async function(e,t){const{requestId:s}=e,{projectPath:n,logger:o}=t
1149
1149
  if(!s)return{success:!1,message:"requestId is required",error:"missing_params"}
1150
1150
  const i=(0,c.requestWorktreePath)(n,s)
1151
- try{const e=await(0,m.removeSetupWorktreesForRequest)(n,s,o)
1151
+ try{const e=await(0,w.removeSetupWorktreesForRequest)(n,s,o)
1152
1152
  e.length&&o.info("Removed the finished request's setup worktree",{requestId:s,setupRemoved:e})
1153
- try{await l.stat(i)}catch{return{success:!0,message:`no worktree for ${s}; nothing to prune${await r(n,s,o)?" (merged branch ref dropped)":""}`}}const a=t.workspaceManager
1154
- let c="workspaceManager"
1155
- if(a?.worktreeManager?.removeWorktree)await a.worktreeManager.removeWorktree(s,!0)
1156
- else{c="git"
1157
- const e=(0,d.simpleGit)(n)
1153
+ try{await l.stat(i)}catch{return{success:!0,message:`no worktree for ${s}; nothing to prune${await r(n,s,o)?" (merged branch ref dropped)":""}`}}const c=[],v=(0,f.sitesUnder)(i)
1154
+ if(v.length&&await async function(e,t){try{const r=(0,u.simpleGit)(e),s=(await r.raw(["rev-parse","--verify","--quiet",`refs/heads/requests/${t}`])).trim()
1155
+ return!!s&&await(0,h.isAncestorCommit)(r,s,`refs/heads/${await(0,p.resolveDefaultBranch)(e)}`)}catch{return!1}}(n,s)){try{const e=await(0,g.ensureDefaultBranchSnapshot)(n)
1156
+ for(const t of v){const r=d.relative(i,t.root),s=d.join(e,r),o=async e=>l.stat(d.join(e,"index.html")).then(e=>e.isFile()).catch(()=>!1)
1157
+ !await o(s)&&await o(t.root)&&await l.cp(t.root,s,{recursive:!0,force:!1,errorOnExist:!1}).catch(()=>{}),await o(s)&&(0,f.rehomeSite)(t.root,s,()=>(0,g.ensureDefaultBranchSnapshot)(n))&&c.push(`http://127.0.0.1:${t.port}`)}}catch(e){o.warn("a landed request's preview could not move to main; it stops with the working copy",{requestId:s,error:(0,a.getErrorMessage)(e)})}c.length&&o.info("Previews of the landed request now serve main",{requestId:s,previews:(0,m.previewsMovedToMain)(c)})}const b=t.workspaceManager
1158
+ let k="workspaceManager"
1159
+ if(b?.worktreeManager?.removeWorktree)await b.worktreeManager.removeWorktree(s,!0)
1160
+ else{k="git"
1161
+ const e=(0,u.simpleGit)(n)
1158
1162
  try{await e.raw(["worktree","remove",i,"--force"])}catch{await l.rm(i,{recursive:!0,force:!0})
1159
- try{await e.raw(["worktree","prune"])}catch{}}}let u=!1
1160
- try{await l.stat(i),u=!0}catch{}if(u)return{success:!1,message:`worktree for ${s} still present after prune (${c})`,error:"prune_ineffective"}
1161
- const h=(0,f.unregisterPreviewHandlesFor)((0,p.stopSitesUnder)(i))
1162
- h.length&&o.info("Stopped previews served from the pruned worktree",{requestId:s,previews:h})
1163
- const w=[]
1164
- for(const e of(0,g.experimentRegistry)().list(t.projectId))e.liveWorktree?.requestId===s&&await(0,g.experimentRegistry)().remove(e.handle,"stopped")&&w.push(e.handle)
1165
- w.length&&o.info("Stopped boxes over the pruned worktree",{requestId:s,boxes:w})
1166
- const y=await r(n,s,o)
1167
- return o.info(`Pruned worktree for retired request (${y?"merged branch ref dropped":"branch preserved"})`,{requestId:s,removedVia:c}),{success:!0,message:`pruned worktree for ${s} (branch requests/${s} ${y?"dropped: the default branch contains it":"kept"})`}}catch(e){return{success:!1,message:`prune-worktree failed: ${(0,a.getErrorMessage)(e)}`,error:"exception"}}}
1168
- const a=t(58824),c=t(7202),l=i(t(91943)),d=t(83807),u=t(27478),h=t(52232),p=t(3811),f=t(28295),m=t(48463),g=t(5452)},76634(e,exports,t){"use strict"
1163
+ try{await e.raw(["worktree","prune"])}catch{}}}let S=!1
1164
+ try{await l.stat(i),S=!0}catch{}if(S)return{success:!1,message:`worktree for ${s} still present after prune (${k})`,error:"prune_ineffective"}
1165
+ const I=(0,m.unregisterPreviewHandlesFor)((0,f.stopSitesUnder)(i))
1166
+ I.length&&o.info("Stopped previews served from the pruned worktree",{requestId:s,previews:I})
1167
+ const $=[]
1168
+ for(const e of(0,y.experimentRegistry)().list(t.projectId))e.liveWorktree?.requestId===s&&await(0,y.experimentRegistry)().remove(e.handle,"stopped")&&$.push(e.handle)
1169
+ $.length&&o.info("Stopped boxes over the pruned worktree",{requestId:s,boxes:$})
1170
+ const j=await r(n,s,o)
1171
+ return o.info(`Pruned worktree for retired request (${j?"merged branch ref dropped":"branch preserved"})`,{requestId:s,removedVia:k}),{success:!0,message:`pruned worktree for ${s} (branch requests/${s} ${j?"dropped: the default branch contains it":"kept"})`}}catch(e){return{success:!1,message:`prune-worktree failed: ${(0,a.getErrorMessage)(e)}`,error:"exception"}}}
1172
+ const a=t(58824),c=t(7202),l=i(t(91943)),d=i(t(16928)),u=t(83807),h=t(27478),p=t(52232),f=t(3811),m=t(28295),g=t(51857),w=t(48463),y=t(5452)},76634(e,exports,t){"use strict"
1169
1173
  exports.defaultRunner=function(){return(e,t,s)=>new Promise(n=>{let o
1170
1174
  try{o=(0,r.spawn)(e,t,{cwd:s.cwd,stdio:["ignore","pipe","pipe"]})}catch(e){return void n({ok:!1,stdout:"",stderr:String(e)})}let i="",a=""
1171
1175
  o.stdout?.on("data",e=>{i+=String(e)}),o.stderr?.on("data",e=>{a+=String(e)}),o.on("error",e=>n({ok:!1,stdout:i,stderr:a||String(e)})),o.on("close",e=>n({ok:0===e,stdout:i,stderr:a}))})},exports.publishRequestPr=async function(e,t){const r=`requests/${e.requestLocalId}`,i=e.projectPath,{run:a}=t
@@ -1979,25 +1983,28 @@ l=String((e?.data??e)?.type??"")
1979
1983
  const t=(e?.data??e)?.areas
1980
1984
  Array.isArray(t)&&(m=t.map(String))}catch{}const v=(0,E.sharedAreas)(t.areas,m),k=g.has(o);(n.localId??n.id)&&j.set(o,String(n.localId??n.id))
1981
1985
  const I=(b.get(o)??0)>0,$=c.areaReuse&&v>0,A=I||$?await p(o):{},_=Math.max(w.get(o)??0,A.atMs??0)||Date.parse(n.updatedAt??n.createdAt??"")||0,T=A.providerID&&A.modelID?f.get(`${A.providerID}/${A.modelID}`):void 0,x="archived"!==n.status&&(0,E.isCacheWarm)(Date.now(),{lastActiveMs:_,providerID:A.providerID},d),M=c.coldResume&&I&&!k&&!x?await(i.changedSince??(t=>h(e,t)))(y):void 0
1982
- C.push({...void 0!==A.contextTokens?{contextTokens:A.contextTokens}:{},...void 0!==M?{changedSinceLines:M}:{},...I||$?{contextCeiling:(0,E.contextCeilingFor)(T)}:{},providerID:A.providerID??null,externalSessionId:o,agentType:n.agentType,threadRequests:b.get(o)??0,sharedAreas:v,modelKey:(0,E.modelKeyOf)(t.kindModels,l),toolsKey:(0,E.toolsKeyOf)(l),lastActiveMs:_,busy:k,busyElsewhere:k&&(b.get(o)??0)>0?!!await u(e,o,t.requestId):void 0,archived:"archived"===n.status})}const _=(0,E.pickWarmCoder)(c,Date.now(),{worker:t.worker,modelKey:(0,E.modelKeyOf)(t.kindModels,t.kind),toolsKey:(0,E.toolsKeyOf)(t.kind)},C,d)
1986
+ C.push({...void 0!==A.contextTokens?{contextTokens:A.contextTokens}:{},...void 0!==M?{changedSinceLines:M}:{},...I||$?{contextCeiling:(0,E.contextCeilingFor)(T,A.providerID)}:{},providerID:A.providerID??null,externalSessionId:o,agentType:n.agentType,threadRequests:b.get(o)??0,sharedAreas:v,modelKey:(0,E.modelKeyOf)(t.kindModels,l),toolsKey:(0,E.toolsKeyOf)(l),lastActiveMs:_,busy:k,busyElsewhere:k&&(b.get(o)??0)>0?!!await u(e,o,t.requestId):void 0,archived:"archived"===n.status})}const _=(0,E.pickWarmCoder)(c,Date.now(),{worker:t.worker,modelKey:(0,E.modelKeyOf)(t.kindModels,t.kind),toolsKey:(0,E.toolsKeyOf)(t.kind)},C,d)
1983
1987
  if(!_.reuse){const e=C.filter(e=>(e.threadRequests??0)>0&&(e.archived||!(0,E.isCacheWarm)(Date.now(),e,d))&&(void 0!==e.contextTokens||void 0!==e.changedSinceLines))
1984
1988
  return e.length&&o.info("session routing: cold thread worker weighed and not resumed",{requestId:t.requestId,why:_.why,limits:{contextTokens:c.coldMaxContextTokens,changedLines:c.coldMaxChangedLines},weighed:e.map(e=>({sessionId:e.externalSessionId,contextTokens:e.contextTokens,changedSinceLines:e.changedSinceLines}))}),{why:_.why}}const T=C.find(e=>e.externalSessionId===_.sessionId)
1985
- return o.info(_.cold?"session routing: cold worker chosen (it built this thread and its code is still current)":_.area?"session routing: warm worker of the same area chosen (no thread link)":"session routing: warm worker chosen",{requestId:t.requestId,sessionId:_.sessionId,threadRequestsWorked:b.get(_.sessionId)??0,..._.area?{sharedAreas:T?.sharedAreas}:{},contextTokens:T?.contextTokens,contextCeiling:T?.contextCeiling,provider:T?.providerID??void 0,afterTurn:!!_.afterTurn,candidates:C.length,..._.cold?{changedSinceLines:T?.changedSinceLines,archived:!!T?.archived}:{}}),{sessionId:_.sessionId,previousRequestId:S.get(_.sessionId),..._.afterTurn?{afterTurn:!0}:{},..._.area?{area:!0}:{},..._.cold&&T?.archived?{reopenLocalId:j.get(_.sessionId)}:{}}}catch(e){return o.warn("session routing: warm-worker survey failed; spawning",{error:(0,$.getErrorMessage)(e)}),{why:"no-candidate"}}}exports.handleStartWorkerAction=async function(e,t){const s=e,{apiClient:i,projectId:a,logger:h}=t
1989
+ return o.info(_.cold?"session routing: cold worker chosen (it built this thread and its code is still current)":_.area?"session routing: warm worker of the same area chosen (no thread link)":"session routing: warm worker chosen",{requestId:t.requestId,sessionId:_.sessionId,threadRequestsWorked:b.get(_.sessionId)??0,..._.area?{sharedAreas:T?.sharedAreas}:{},contextTokens:T?.contextTokens,contextCeiling:T?.contextCeiling,provider:T?.providerID??void 0,afterTurn:!!_.afterTurn,candidates:C.length,..._.cold?{changedSinceLines:T?.changedSinceLines,archived:!!T?.archived}:{}}),{sessionId:_.sessionId,previousRequestId:S.get(_.sessionId),..._.afterTurn?{afterTurn:!0}:{},..._.area?{area:!0}:{},..._.cold&&T?.archived?{reopenLocalId:j.get(_.sessionId)}:{}}}catch(e){return o.warn("session routing: warm-worker survey failed; spawning",{error:(0,$.getErrorMessage)(e)}),{why:"no-candidate"}}}exports.ny=exports.PN=void 0,exports.handleStartWorkerAction=async function(e,t){const s=e,{apiClient:i,projectId:a,logger:h}=t
1986
1990
  if(!s.requestId)return{success:!1,message:"requestId is required",error:"missing_params"}
1987
1991
  const w=t.startWorkerDeps??{},y=await i.get(`/api/projects/${a}/requests/${s.requestId}`),v=y?.data??y
1988
1992
  if(!v||!["open"].includes(v.status))return{success:!0,message:`${s.requestId} is ${v?.status??"unknown"}: nothing to start`}
1989
- const k=M.get(s.requestId)
1990
- if(k)return{success:!0,message:`${s.requestId} starts on ${k} when its current turn ends`}
1991
- if(v.roundOf&&"string"==typeof s.unitWorker&&s.unitWorker&&null!==(0,f.kindProfile)(v.type).worker)try{const[e,r]=await Promise.all([(w.turnState??d)(t,s.unitWorker,s.requestId),(w.requestRecord??n)(t,s.requestId,v)]),i=e.busy?e.liveElsewhere:await u(t,s.unitWorker,s.requestId).then(e=>e&&e!==v.roundOf?e:null)
1992
- if(!e.busy&&!i){const e="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",n=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0}),i={p:s,req:v,worker:e,startPrompt:o(s,v,[],r),toolMask:n,deps:w}
1993
- if(await c(t,i,{sessionId:s.unitWorker,previousRequestId:v.roundOf}))return{success:!0,message:`${s.requestId} handed to ${v.roundOf}'s own ${e} ${s.unitWorker}`}}}catch(e){h.info("session routing: the unit did not go straight to its request's worker; the ordinary start takes it",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}try{const e=async e=>{const t=await i.get(`/api/projects/${a}/requests/${e}`)
1993
+ const S=M.get(s.requestId)
1994
+ if(S)return{success:!0,message:`${s.requestId} starts on ${S} when its current turn ends`}
1995
+ if(v.roundOf&&"string"==typeof s.unitWorker&&s.unitWorker&&null!==(0,f.kindProfile)(v.type).worker){const e=!0===s.unitWorkerPutAway,r="number"==typeof s.requestsSince?s.requestsSince:null,i=(0,k.getOpenCodeUrlFromConfig)(t.projectPath),a=await(w.lastCall??(e=>i?(0,A.readLastCall)(i,e):Promise.resolve({})))(s.unitWorker).catch(()=>({})),l=w.reloadedAt??(0,A.configReloadedAt)(t.projectPath),p=(0,E.isCacheWarm)(Date.now(),{lastActiveMs:a.atMs??0,providerID:a.providerID},l),m=!p&&"number"==typeof a.contextTokens&&a.contextTokens>exports.ny,y=e&&!(null!==r&&r<=exports.PN),b=!y&&!m
1996
+ if(!e&&p||h.info(b?"session routing: the unit wakes its request's worker (cold)":y?"session routing: the request's worker moved on; the ordinary start takes the unit":"session routing: the request's worker is cold and its context large; the ordinary start takes the unit",{requestId:s.requestId,of:v.roundOf,sessionId:s.unitWorker,putAway:e,warm:p,requestsSince:r,contextTokens:a.contextTokens,provider:a.providerID,model:a.modelID,idleMinutes:a.atMs?Math.round((Date.now()-a.atMs)/6e4):void 0,maxRequestsSince:exports.PN,maxColdTokens:exports.ny}),b)try{const[r,l]=await Promise.all([(w.turnState??d)(t,s.unitWorker,s.requestId),(w.requestRecord??n)(t,s.requestId,v)]),m=r.busy?r.liveElsewhere:await u(t,s.unitWorker,s.requestId).then(e=>e&&e!==v.roundOf?e:null)
1997
+ if(!r.busy&&!m){const r="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",n=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0}),d={p:s,req:v,worker:r,startPrompt:o(s,v,[],l),toolMask:n,deps:w},u=Date.now()
1998
+ if(await c(t,d,{sessionId:s.unitWorker,previousRequestId:v.roundOf,...e&&s.unitWorkerLocalId?{reopenLocalId:s.unitWorkerLocalId}:{}}))return i&&!w.lastCall&&(async()=>{for(let t=0;t<40;t++){await new Promise(e=>setTimeout(e,3e3))
1999
+ const t=await(0,A.firstCallAfter)(i,s.unitWorker,u-2e3)
2000
+ if(t)return void h.info("session routing: the woken worker's first call",{requestId:s.requestId,sessionId:s.unitWorker,guessedWarm:p,putAway:e,contextTokensBefore:a.contextTokens,idleMinutes:a.atMs?Math.round((u-a.atMs)/6e4):void 0,provider:a.providerID,model:a.modelID,cacheRead:t.cacheRead,uncachedInput:t.input,cacheWrite:t.cacheWrite,hitShare:t.hitShare,wasWarm:t.hitShare>=.5,guessRight:t.hitShare>=.5===p,secondsToFirstCall:Math.round((t.atMs-u)/1e3)})}})().catch(()=>{}),{success:!0,message:`${s.requestId} handed to ${v.roundOf}'s own ${r} ${s.unitWorker}`}}}catch(e){h.info("session routing: the unit did not go straight to its request's worker; the ordinary start takes it",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}}try{const e=async e=>{const t=await i.get(`/api/projects/${a}/requests/${e}`)
1994
2001
  return(t?.data??t)?.type}
1995
- t.projectPath&&await(0,x.defaultBranchHasCode)(t.projectPath,e)&&(await(0,x.reportProjectHasCode)(a,!0,(e,t)=>i.patch(e,t)),await i.post(`/api/projects/${a}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){h.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}const S=await(w.openDuplicates??r)(t,v,s.requestId),I=await(w.requestRecord??n)(t,s.requestId,v),j=o(s,v,S,I),A="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",C=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,g.hasMask)(C)&&h.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:A,off:Object.keys(C)})
1996
- const _={p:s,req:v,worker:A,startPrompt:j,toolMask:C,deps:w}
1997
- let T=null
1998
- const R=v?.filedBySession??v?.filed_by_session,P=v?.filedByAgent??v?.filed_by_agent
1999
- if("string"==typeof R&&R&&(0,E.sameWorkerRole)(P,A))try{if((await(0,m.readAllPages)(i,e=>`/api/projects/${a}/cli-sessions?status=active&limit=100&offset=${e}`)).some(e=>e?.externalSessionId===R)){const e=await(w.turnState??d)(t,R,s.requestId)
2000
- e.liveElsewhere?h.info("session routing: the filer is mid-turn on another live request; not handing it this one",{requestId:s.requestId,sessionId:R,on:e.liveElsewhere}):(T={sessionId:R},h.info("session routing: the session that filed this request keeps it",{requestId:s.requestId,sessionId:R}))}}catch{}if(T=T??await(w.pickWarm??p)(t,{worker:A,requestId:s.requestId,kind:v?.type,kindModels:s.kindModels,planDocumentId:v?.planDocumentId??v?.plan_document_id??null,areas:Array.isArray(v?.areas)?v.areas.map(String):[],waitedOn:Array.isArray(v?.waitedOn)?v.waitedOn.map(String):[]}),T?.sessionId){const e=T.sessionId,r=await(w.turnState??d)(t,e,s.requestId)
2002
+ t.projectPath&&await(0,x.defaultBranchHasCode)(t.projectPath,e)&&(await(0,x.reportProjectHasCode)(a,!0,(e,t)=>i.patch(e,t)),await i.post(`/api/projects/${a}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){h.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}const I=await(w.openDuplicates??r)(t,v,s.requestId),j=await(w.requestRecord??n)(t,s.requestId,v),C=o(s,v,I,j),_="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",T=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,g.hasMask)(T)&&h.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:_,off:Object.keys(T)})
2003
+ const R={p:s,req:v,worker:_,startPrompt:C,toolMask:T,deps:w}
2004
+ let P=null
2005
+ const O=v?.filedBySession??v?.filed_by_session,D=v?.filedByAgent??v?.filed_by_agent
2006
+ if("string"==typeof O&&O&&(0,E.sameWorkerRole)(D,_))try{if((await(0,m.readAllPages)(i,e=>`/api/projects/${a}/cli-sessions?status=active&limit=100&offset=${e}`)).some(e=>e?.externalSessionId===O)){const e=await(w.turnState??d)(t,O,s.requestId)
2007
+ e.liveElsewhere?h.info("session routing: the filer is mid-turn on another live request; not handing it this one",{requestId:s.requestId,sessionId:O,on:e.liveElsewhere}):(P={sessionId:O},h.info("session routing: the session that filed this request keeps it",{requestId:s.requestId,sessionId:O}))}}catch{}if(P=P??await(w.pickWarm??p)(t,{worker:_,requestId:s.requestId,kind:v?.type,kindModels:s.kindModels,planDocumentId:v?.planDocumentId??v?.plan_document_id??null,areas:Array.isArray(v?.areas)?v.areas.map(String):[],waitedOn:Array.isArray(v?.waitedOn)?v.waitedOn.map(String):[]}),P?.sessionId){const e=P.sessionId,r=await(w.turnState??d)(t,e,s.requestId)
2001
2008
  if(!r.busy&&!r.liveElsewhere&&v.roundOf){const n=await(w.liveElsewhere??u)(t,e,s.requestId)
2002
2009
  n&&n!==v.roundOf&&(r.liveElsewhere=n)}if(r.liveElsewhere)h.info("session routing: the chosen worker is mid-turn on another live request; spawning instead",{requestId:s.requestId,sessionId:e,on:r.liveElsewhere})
2003
2010
  else{if(r.busy)return M.set(s.requestId,e),(w.whenIdle??((e,r)=>(0,b.whenSessionIdle)(e,async()=>(await d(t,e,s.requestId)).busy,r)))(e,e=>{(async function(e,t,r,s){const{apiClient:n,projectId:o,logger:i}=e,{p:a}=t
@@ -2005,10 +2012,12 @@ try{const u=await n.get(`/api/projects/${o}/requests/${a.requestId}`),h=u?.data?
2005
2012
  if(!h||"open"!==h.status)return void i.info("session routing: the request moved on while its worker finished a turn; nothing to hand over",{requestId:a.requestId,status:h?.status??"unknown"})
2006
2013
  const p=await(t.deps.turnState??d)(e,r.sessionId,a.requestId)
2007
2014
  if("idle"===s&&!p.busy&&!p.liveElsewhere&&await c(e,{...t,req:h},r))return
2008
- i.info("session routing: the worker did not come free for this start; spawning",{requestId:a.requestId,sessionId:r.sessionId,why:s,busy:p.busy,on:p.liveElsewhere}),await l(e,{...t,req:h})}catch(e){i.warn("session routing: the start after a turn failed",{requestId:a.requestId,sessionId:r.sessionId,error:(0,$.getErrorMessage)(e)})}})(t,_,T,e).finally(()=>M.delete(s.requestId))}),h.info("session routing: the thread's worker is finishing a turn; the start is handed over when it ends",{requestId:s.requestId,sessionId:e,previous:T.previousRequestId}),{success:!0,message:`${s.requestId} starts on warm ${A} ${e} when its current turn ends (the thread shares the session)`}
2009
- if(await c(t,_,T))return{success:!0,message:`${s.requestId} handed to warm ${A} ${e} (${T.area?"same area, separate work":"the thread shares the session"})`}}}else T?.why&&h.info(`session routing: spawning, because ${(0,E.whyNotReused)(T.why)}`,{requestId:s.requestId,why:T.why})
2010
- return l(t,_)}
2011
- const f=t(87780),m=t(5641),g=t(45687),w=t(28148),y=t(17039),v=t(86116),b=t(67078),k=t(60833),S=t(2424),I=t(23687),$=t(58824),j=t(65454),E=t(64469),A=t(81570),C=t(76379),_=t(83807),T=t(47912),x=t(71879),M=new Map,R=new Set(["completed","canceled","reverted"])},82928(e,exports,t){"use strict"
2015
+ i.info("session routing: the worker did not come free for this start; spawning",{requestId:a.requestId,sessionId:r.sessionId,why:s,busy:p.busy,on:p.liveElsewhere}),await l(e,{...t,req:h})}catch(e){i.warn("session routing: the start after a turn failed",{requestId:a.requestId,sessionId:r.sessionId,error:(0,$.getErrorMessage)(e)})}})(t,R,P,e).finally(()=>M.delete(s.requestId))}),h.info("session routing: the thread's worker is finishing a turn; the start is handed over when it ends",{requestId:s.requestId,sessionId:e,previous:P.previousRequestId}),{success:!0,message:`${s.requestId} starts on warm ${_} ${e} when its current turn ends (the thread shares the session)`}
2016
+ if(await c(t,R,P))return{success:!0,message:`${s.requestId} handed to warm ${_} ${e} (${P.area?"same area, separate work":"the thread shares the session"})`}}}else P?.why&&h.info(`session routing: spawning, because ${(0,E.whyNotReused)(P.why)}`,{requestId:s.requestId,why:P.why})
2017
+ return l(t,R)}
2018
+ const f=t(87780),m=t(5641),g=t(45687),w=t(28148),y=t(17039),v=t(86116),b=t(67078),k=t(60833),S=t(2424),I=t(23687),$=t(58824),j=t(65454),E=t(64469),A=t(81570),C=t(76379),_=t(83807),T=t(47912),x=t(71879)
2019
+ exports.PN=1,exports.ny=15e4
2020
+ const M=new Map,R=new Set(["completed","canceled","reverted"])},82928(e,exports,t){"use strict"
2012
2021
  function r(e){let t=0
2013
2022
  for(const r of[...e??[]].reverse())if("user"===r?.info?.role&&!(r.parts??[]).some(e=>!0===e?.metadata?.alchemy?.payload?.nudge&&!0===e?.metadata?.alchemy?.payload?.restart)){if(!(r.parts??[]).some(e=>!0===e?.metadata?.alchemy?.payload?.nudge))break
2014
2023
  t++}return t}function s(e,t){return(e??[]).some(e=>"assistant"===e?.info?.role&&(e.info.time?.created??0)>t&&(e.parts??[]).some(e=>"tool"===e?.type))}function n(e,t){const r=String(e??"").match(/^ProviderQuotaError(?:\(([\w-]+)\))?/)
@@ -2854,7 +2863,9 @@ d||o||void 0===u?r.delete(c):r.add(c)
2854
2863
  const h={name:c,url:s,label:u,registeredAt:(new Date).toISOString(),...l?{requestId:l}:{}}
2855
2864
  return t.set(c,h),i.awaitingFrame?n.add(c):n.delete(c),h},exports.markPreviewFrameReady=function(e){n.delete(e)},exports.unregisterPreviewHandle=function(e){return n.delete(e),r.delete(e),t.delete(e)},exports.unregisterPreviewHandlesFor=function(e){const s=[]
2856
2865
  for(const[o,i]of t)e.includes(i.url)&&(t.delete(o),n.delete(o),r.delete(o),s.push(o))
2857
- return s},exports.getPreviewHandle=function(e){return t.get(e)},exports.listPreviewHandles=function(){return Array.from(t.values()).filter(e=>!n.has(e.name))}
2866
+ return s},exports.previewsMovedToMain=function(e){const r=[]
2867
+ for(const[s,n]of t)e.includes(n.url)&&(delete n.requestId,n.label&&(n.label=n.label.replace(/\s*\(not completed yet\)$/,"")),r.push(s))
2868
+ return r},exports.getPreviewHandle=function(e){return t.get(e)},exports.listPreviewHandles=function(){return Array.from(t.values()).filter(e=>!n.has(e.name))}
2858
2869
  const t=new Map,r=new Set
2859
2870
  let s
2860
2871
  const n=new Set},65232(e,exports,t){"use strict"
@@ -2939,7 +2950,61 @@ e.bytesOut+=s.length,this.lastActivity=Date.now(),this.sendData(r?3:2,e.id,s)}),
2939
2950
  const s="http"===e.kind?!!e.res:101===e.status
2940
2951
  e.done=!0,"ws"===e.kind&&s?this.send({t:"close",id:e.id,code:t>=1e3?t:1011,reason:r.slice(0,120)}):s?this.send({t:"error",id:e.id,status:t,reason:r}):this.send({t:"refused",id:e.id,status:t,reason:r}),this.audit(e,{outcome:s?"served":"refused",status:t,bytes:e.bytesOut,reason:r}),this.teardown(e),this.streams.delete(e.id)}teardown(e){e.done=!0
2941
2952
  try{e.req?.destroy()}catch{}try{e.res?.destroy()}catch{}try{e.ws&&e.ws.readyState!==d.default.CLOSED&&e.ws.terminate()}catch{}}openStreams(){return this.streams.size}}
2942
- const f=new Map},36799(e,exports){"use strict"
2953
+ const f=new Map},39175(e,exports,t){"use strict"
2954
+ function r(e,t){return new Promise(r=>{(0,u.execFile)("git",t,{cwd:e,maxBuffer:16777216},(e,t,s)=>{r({code:e?"number"==typeof e.code?e.code:1:0,stdout:String(t??""),stderr:String(s??"")})})})}function s(e,t){return p.join((0,f.workDir)(e,"snapshots"),`${v}${t.slice(0,12)}`)}function n(e,t){const r=p.resolve(t),s=p.resolve((0,m.snapshotPath)(e))
2955
+ if(r===s||r.startsWith(s+p.sep))return{kind:"latest",folder:(n=s,p.relative(n,r).split(p.sep).join("/")||".")}
2956
+ var n
2957
+ const o=p.resolve((0,f.requestWorktreesDir)(e))
2958
+ if(r.startsWith(o+p.sep)){const[e,...t]=p.relative(o,r).split(p.sep)
2959
+ if(/^req\d{6}$/.test(e))return{kind:"unmerged",requestId:e,folder:t.join("/")||"."}}const i=p.resolve((0,f.workDir)(e,"snapshots"))
2960
+ if(r.startsWith(i+p.sep)){const[e,...t]=p.relative(i,r).split(p.sep),s=e.startsWith(v)?b.get(p.join(i,e)):void 0
2961
+ if(s)return{kind:"version",requestId:s.requestId,commit:s.commit,folder:t.join("/")||"."}}return null}function o(e){const t=/^Merge requests\/(req\d{6})(?:-|$)/.exec(e.trim())
2962
+ return t?t[1]:null}function i(e,t){let r=0
2963
+ try{r=Number(new URL(t).port)}catch{return null}const s=(0,y.listServedSites)().find(e=>e.port===r)
2964
+ return s?n(e,s.root):null}var a,c=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
2965
+ var n=Object.getOwnPropertyDescriptor(t,r)
2966
+ n&&!("get"in n?!t.__esModule:n.writable||n.configurable)||(n={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,s,n)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),l=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),d=this&&this.__importStar||(a=function(e){return a=Object.getOwnPropertyNames||function(e){var t=[]
2967
+ for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
2968
+ return t},a(e)},function(e){if(e&&e.__esModule)return e
2969
+ var t={}
2970
+ if(null!=e)for(var r=a(e),s=0;s<r.length;s++)"default"!==r[s]&&c(t,e,r[s])
2971
+ return l(t,e),t})
2972
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.pinnedDir=s,exports.versionOfRoot=n,exports.requestOfLanding=o,exports.versionsOf=async function(e,t,s=30){const n=await(0,g.resolveDefaultBranch)(e),i="."===t?".":t.replace(/^\.?\/+/,""),a=await r(e,["log","--first-parent","--format=%H%x09%cI%x09%s","-n",String(3*s),`refs/heads/${n}`,"--",i])
2973
+ if(0!==a.code)return[]
2974
+ const c=[]
2975
+ for(const e of a.stdout.split("\n")){const[t,r,...n]=e.split("\t"),i=t?o(n.join("\t")):null
2976
+ if(i&&c.push({requestId:i,commit:t,date:r}),c.length>=s)break}return c},exports.landingOf=async function(e,t){if(!/^req\d{6}$/.test(t))return null
2977
+ const s=await(0,g.resolveDefaultBranch)(e),n=await r(e,["log","--first-parent","--format=%H%x09%cI%x09%s","-n","500",`refs/heads/${s}`])
2978
+ if(0!==n.code)return null
2979
+ const i=n.stdout.split("\n").map(e=>e.split("\t")).find(([e,,...r])=>e&&o(r.join("\t"))===t)
2980
+ if(!i)return null
2981
+ const[a,c]=i,l=await r(e,["diff","--name-only",`${a}^1`,a])
2982
+ return{commit:a,date:c,folders:Array.from(new Set(l.stdout.split("\n").filter(e=>/(^|\/)index\.html$/.test(e)).map(e=>p.posix.dirname(e)||".")))}},exports.pinnedRoot=async function(e,t,n,o){if(!/^[0-9a-f]{7,40}$/.test(t))throw new Error("not a commit")
2983
+ const i=(await r(e,["rev-parse","--verify","--quiet",`${t}^{commit}`])).stdout.trim()
2984
+ if(!i)throw new Error(`commit ${t.slice(0,8)} is not in this repository`)
2985
+ const a=s(e,i)
2986
+ if(!h.existsSync(p.join(a,".git"))){h.rmSync(a,{recursive:!0,force:!0}),h.mkdirSync(p.dirname(a),{recursive:!0}),await r(e,["worktree","prune"])
2987
+ const t=await r(e,["worktree","add","--detach",a,i])
2988
+ if(0!==t.code)throw new Error(`could not make a copy at ${i.slice(0,8)}: ${t.stderr.trim().slice(0,200)}`)}b.set(p.resolve(a),{requestId:o,commit:i})
2989
+ const c="."===n?"":n.replace(/^\.?\/+/,"")
2990
+ if(c.split("/").includes(".."))throw new Error("folder must stay inside the repository")
2991
+ const l=p.join(a,c)
2992
+ if(!h.existsSync(p.join(l,"index.html")))throw new Error(`${"."===n?"the top folder":n} has no page as ${o} left it: it was built into a folder the project does not keep, so this version can only be seen by building it again`)
2993
+ return l},exports.prunePinned=async function(e,t){const s=(0,f.workDir)(e,"snapshots")
2994
+ let n=[]
2995
+ try{n=h.readdirSync(s).filter(e=>e.startsWith(v))}catch{return[]}const o=t.map(e=>p.resolve(e)),i=[]
2996
+ for(const t of n){const n=p.resolve(p.join(s,t))
2997
+ o.some(e=>e===n||e.startsWith(n+p.sep))||(0!==(await r(e,["worktree","remove","--force",n])).code&&h.rmSync(n,{recursive:!0,force:!0}),b.delete(n),i.push(t))}return i.length&&await r(e,["worktree","prune"]),i},exports.versionOfUrl=i,exports.previewHandlesWithVersions=function(e){return(0,w.listPreviewHandles)().map(t=>{const r=i(e,t.url)
2998
+ return r?{...t,version:r}:t})},exports.pagesOf=async function(e,t=200){const s=await(0,g.resolveDefaultBranch)(e),n=await r(e,["log","--first-parent","--format=%x01%H%x09%cI%x09%s","--name-only","-m","--diff-merges=first-parent","-n",String(t),`refs/heads/${s}`])
2999
+ if(0!==n.code)return[]
3000
+ const i=new Map
3001
+ for(const e of n.stdout.split("\x01").slice(1)){const[t,...r]=e.split("\n").filter(e=>e.trim()),[s,n,...a]=(t??"").split("\t"),c=o(a.join("\t"))
3002
+ if(c)for(const e of r){if(!/(^|\/)index\.html$/.test(e))continue
3003
+ const t=p.posix.dirname(e)||".",r=i.get(t)??[]
3004
+ r.some(e=>e.requestId===c)||r.push({requestId:c,commit:s,date:n}),i.set(t,r)}}const a=[]
3005
+ for(const[t,s]of i){const n=await r(e,["show",`${s[0].commit}:${"."===t?"":`${t}/`}index.html`]),o=/<title[^>]*>([^<]{1,120})<\/title>/i.exec(n.stdout)?.[1]?.trim()
3006
+ a.push({folder:t,...o?{title:o}:{},versions:s})}return a.sort((e,t)=>(t.versions[0]?.date??"").localeCompare(e.versions[0]?.date??""))}
3007
+ const u=t(35317),h=d(t(79896)),p=d(t(16928)),f=t(7202),m=t(51857),g=t(52232),w=t(28295),y=t(3811),v="at-",b=new Map},36799(e,exports){"use strict"
2943
3008
  exports.RESUME_TEXT=void 0,exports.isProjectPaused=function(){return t},exports.setProjectPaused=function(e,s=Date.now()){if(e===t)return 0
2944
3009
  if(t=e,e)return r=s,0
2945
3010
  const n=r?s-r:0
@@ -3572,11 +3637,11 @@ try{const s=await e.apiClient.get(`/api/projects/${r}/session-request-associatio
3572
3637
  for(const t of n){const r=t?.requestLocalId??t?.request_local_id
3573
3638
  if("string"!=typeof r||!/^req\d+$/.test(r))continue
3574
3639
  const s=(0,w.requestWorktreePath)(e.projectPath,r)
3575
- if(await q.promises.stat(M.default.join(s,".git")).then(()=>!0).catch(()=>!1))return{requestId:r,worktree:s}}}catch{}return null}async function s(e,t){if(!t)return null
3576
- const s=Z.get(t)
3640
+ if(await N.promises.stat(M.default.join(s,".git")).then(()=>!0).catch(()=>!1))return{requestId:r,worktree:s}}}catch{}return null}async function s(e,t){if(!t)return null
3641
+ const s=ee.get(t)
3577
3642
  if(s&&Date.now()-s.at<=3e4)return s.own
3578
3643
  const n=await r(e,t)
3579
- return Z.size>=500&&Z.delete(Z.keys().next().value),Z.set(t,{at:Date.now(),own:n}),n}function n(e,t){const r=ee.get(e)
3644
+ return ee.size>=500&&ee.delete(ee.keys().next().value),ee.set(t,{at:Date.now(),own:n}),n}function n(e,t){const r=te.get(e)
3580
3645
  return!!r&&r.files===t.join("\n")&&Date.now()-r.at<=3e4}async function o(e,t){const r=e.getProjectId()
3581
3646
  if(!r)return[]
3582
3647
  const s=await e.apiClient.get(`/api/projects/${r}/test-processes/active${t?`?forRequest=${encodeURIComponent(t)}`:""}`),n=Array.isArray(s?.data)?s.data:Array.isArray(s)?s:[],o=[]
@@ -3595,7 +3660,7 @@ if(!r){const r=t.available?"not replayed (past the replay's limit of commands)":
3595
3660
  return{criterionId:e.criterionId,command:e.command,ok:!1,couldNotRun:!0,output:r.slice(0,200)}}return{criterionId:e.criterionId,command:e.command,ok:r.ok,..."number"==typeof r.exit?{exit:r.exit}:{},...r.checkedNothing?{checkedNothing:r.checkedNothing}:{},...r.timedOut?{timedOut:!0}:{},...r.couldNotRun?{couldNotRun:!0}:{},...r.cutShort?{cutShort:!0}:{},output:String(r.output??"").split("\n")[0].slice(0,200)}})}function l(e,t,r){const s=`it was still ${t.what} ${Math.round(r/1e3)} s after the run began, past that step's limit of ${Math.round(t.withinMs/1e3)} s; the request went on without it`
3596
3661
  return e.map(e=>({criterionId:e.criterionId,command:e.command,ok:!1,timedOut:!0,output:s.slice(0,200)}))}async function d(e,t,r,s,n={}){const o=Date.now()
3597
3662
  let i=null
3598
- try{const a=s.filter(e=>"string"==typeof e&&!!e.trim()),c=a.slice(0,20),l=a.length-c.length,d=(0,L.simpleGit)(e.projectPath)
3663
+ try{const a=s.filter(e=>"string"==typeof e&&!!e.trim()),c=a.slice(0,20),l=a.length-c.length,d=(0,U.simpleGit)(e.projectPath)
3599
3664
  n.onStep?.("finding the box each command runs in",12e4)
3600
3665
  const h=(await p(e,c,await u(e,t,n.proofs),t)).filter(e=>!e.gateEnv?.onHost)
3601
3666
  if(0===h.length){const s="this project has nowhere set up to run them automatically"
@@ -3606,22 +3671,22 @@ const g=[]
3606
3671
  let w={parent:"",subject:""}
3607
3672
  try{const[e,t]=(await d.raw(["log","-1","--format=%P%x00%s",r])).trim().split("\0")
3608
3673
  e&&!e.includes(" ")&&(w={parent:e,subject:t??""})}catch{}for(const e of h){const s=[]
3609
- for(const n of e.indices){const o=(0,U.passedProofRun)(t,c[n],e.environmentId,r)??(0,U.passedRunThatWroteHead)(t,c[n],e.environmentId,w)
3610
- o&&!(0,E.checkedNothing)({passed:!0,stdout:o.stdout,stderr:o.stderr})?(f.push({command:c[n],ok:!0,exit:0,output:(o.stderr||o.stdout).slice(0,400),...o.stdout?{printed:o.stdout.slice(-600)}:{},...o.stderr?{error:o.stderr.slice(-600)}:{},authoritative:e.authoritative,fromWorkerRun:!0}),m++):s.push(n)}s.length&&g.push({...e,indices:s})}g.length>0&&(n.onStep?.("checking out the commit",ne),i=M.default.join(e.projectPath,".alchemy","verify-preflight",`wt-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,7)}`),await d.raw(["worktree","add","--detach",i,r]))
3674
+ for(const n of e.indices){const o=(0,B.passedProofRun)(t,c[n],e.environmentId,r)??(0,B.passedRunThatWroteHead)(t,c[n],e.environmentId,w)
3675
+ o&&!(0,E.checkedNothing)({passed:!0,stdout:o.stdout,stderr:o.stderr})?(f.push({command:c[n],ok:!0,exit:0,output:(o.stderr||o.stdout).slice(0,400),...o.stdout?{printed:o.stdout.slice(-600)}:{},...o.stderr?{error:o.stderr.slice(-600)}:{},authoritative:e.authoritative,fromWorkerRun:!0}),m++):s.push(n)}s.length&&g.push({...e,indices:s})}g.length>0&&(n.onStep?.("checking out the commit",oe),i=M.default.join(e.projectPath,".alchemy","verify-preflight",`wt-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,7)}`),await d.raw(["worktree","add","--detach",i,r]))
3611
3676
  const y=n.budgetMs??exports.COMPLETION_REPLAY_BUDGET_MS,v=Date.now()+y,k=(e,t)=>f.push({command:c[e],ok:!1,exit:-1,timedOut:!0,output:"",authoritative:t})
3612
3677
  let S=0,I=""
3613
3678
  for(const t of g){const r=Math.floor((v-Date.now())/1e3)
3614
3679
  if(r<5){t.indices.forEach(e=>k(e,t.authoritative))
3615
- continue}const s=Math.min(j.VERIFY_TIMEOUT_SEC,r),o=await(n.runChecks??G.runChecks)(i,t.indices.map(e=>({name:`verify[${e+1}]`,command:c[e],required:!0,timeout:s})),{...t.gateEnv,projectId:e.getProjectId()},{setup:t.setup,deadlineAt:v,...n.onStep?{onStep:n.onStep}:{}})
3680
+ continue}const s=Math.min(j.VERIFY_TIMEOUT_SEC,r),o=await(n.runChecks??K.runChecks)(i,t.indices.map(e=>({name:`verify[${e+1}]`,command:c[e],required:!0,timeout:s})),{...t.gateEnv,projectId:e.getProjectId()},{setup:t.setup,deadlineAt:v,...n.onStep?{onStep:n.onStep}:{}})
3616
3681
  o.error?(S++,I=String(o.error),t.indices.forEach(e=>f.push({command:c[e],ok:!1,exit:-1,couldNotRun:!0,output:I.slice(0,400),authoritative:t.authoritative}))):o.commands.forEach((e,r)=>{const s=(e.stderr||e.stdout||"").slice(0,400),n=e.passed||e.timedOut?null:(0,E.missingPythonLibrary)(e.stdout||"",e.stderr||"",e=>["","src"].some(t=>A.default.existsSync(M.default.join(i,t,`${e}.py`))||A.default.existsSync(M.default.join(i,t,e)))),a=e.passed?(0,E.checkedNothing)(e):null,l=!e.passed&&!e.timedOut&&(!!t.lacks?.length||(0,b.exitMeansCouldNotRun)(e.exit)||(0,E.ranWithoutItsTools)(e.stdout||"",e.stderr||"")||!!n)||"missing-tool"===a
3617
3682
  f.push({command:c[t.indices[r]]??e.name,ok:e.passed&&!a,exit:e.exit,...a?{checkedNothing:a}:{},timedOut:e.timedOut,...l?{couldNotRun:!0,...t.lacks?.length?{needs:t.lacks}:{}}:{},...e.passed||e.timedOut||e.exit!==b.RUN_PARSE_FAILED_EXIT?{}:{unparsed:!0},output:s,...o.flaky?.includes(e.name)?{flaky:!0}:{},...e.stdout?{printed:e.stdout.slice(-600)}:{},...e.stderr?{error:e.stderr.slice(-600)}:{},authoritative:t.authoritative})})}if(S>0&&S===g.length&&0===m)return e.logger.info("verify-preflight",{requestId:t,sha:r.slice(0,8),commands:c.length,available:!1,reason:I}),{available:!1,reason:I,sha:r}
3618
- const $=h.every(e=>e.authoritative),C=(0,B.markCutShort)(f)
3683
+ const $=h.every(e=>e.authoritative),C=(0,H.markCutShort)(f)
3619
3684
  f.splice(0,f.length,...C)
3620
3685
  const _=f.filter(e=>!(e.ok||e.timedOut||e.couldNotRun||e.cutShort)).length
3621
3686
  e.logger.info("verify-preflight",{requestId:t,sha:r.slice(0,8),commands:c.length,failed:_,dropped:l,unfinished:f.filter(e=>e.timedOut).length,couldNotRun:f.filter(e=>e.couldNotRun).length,authoritative:$,boxes:g.length,reusedFromWorkerRuns:m,ms:Date.now()-o})
3622
3687
  const T=(await d.revparse([`${r}^{tree}`]).catch(()=>"")).trim()||void 0
3623
- return{available:!0,authoritative:$,sha:r,...T?{tree:T}:{},dropped:l,ms:Date.now()-o,results:f}}catch(t){return e.logger.debug("verify replay failed",{error:(0,P.getErrorMessage)(t)}),{available:!1,reason:(0,P.getErrorMessage)(t),sha:r}}finally{if(i){n.onStep?.("removing its checkout",ne)
3624
- try{await(0,L.simpleGit)(e.projectPath).raw(["worktree","remove","--force",i])}catch(t){e.logger.debug("verify-preflight worktree cleanup failed",{error:(0,P.getErrorMessage)(t)})}}}}async function u(e,t,r=[]){const s=e.getProjectId()
3688
+ return{available:!0,authoritative:$,sha:r,...T?{tree:T}:{},dropped:l,ms:Date.now()-o,results:f}}catch(t){return e.logger.debug("verify replay failed",{error:(0,P.getErrorMessage)(t)}),{available:!1,reason:(0,P.getErrorMessage)(t),sha:r}}finally{if(i){n.onStep?.("removing its checkout",oe)
3689
+ try{await(0,U.simpleGit)(e.projectPath).raw(["worktree","remove","--force",i])}catch(t){e.logger.debug("verify-preflight worktree cleanup failed",{error:(0,P.getErrorMessage)(t)})}}}}async function u(e,t,r=[]){const s=e.getProjectId()
3625
3690
  if(!s||!/^req\d{6}$/.test(t))return new Map
3626
3691
  try{const n=await e.apiClient.get(`/api/projects/${s}/requests/${t}`),o=n?.data??n,i=Array.isArray(o?.acceptanceCriteria)?o.acceptanceCriteria:[],a=(0,I.proofEnvironments)(i)
3627
3692
  for(const e of r){const t=(0,I.criterionEnvironment)(i.find(t=>t?.id===e.criterionId))
@@ -3630,7 +3695,7 @@ if(!t)return[]
3630
3695
  try{const r=await e.apiClient.get(`/api/projects/${t}/environments`)
3631
3696
  return(Array.isArray(r?.data)?r.data:Array.isArray(r)?r:[]).filter(e=>e?.config&&!i(e.config)&&"string"==typeof e.config.image&&e.config.image).map(e=>({id:String(e.id),name:"string"==typeof e.name?e.name:void 0,env:(0,k.gateEnvironmentFromRow)(e.config,t),has:(0,$.environmentToolchains)(e.config)}))}catch{return[]}}async function p(e,t,r=new Map,s){let n=[]
3632
3697
  try{n=await o(e,s)}catch{n=[]}const i=e=>{const t=(Array.isArray(e?.config?.setup)?e.config.setup:[]).filter(e=>"string"==typeof e&&e.trim())
3633
- return t.length?t:void 0},c=n.map(e=>e.proc).filter(e=>"experiment"!==e?.kind),l=e=>{const t=(0,K.setupOfEnvironment)(c,e)
3698
+ return t.length?t:void 0},c=n.map(e=>e.proc).filter(e=>"experiment"!==e?.kind),l=e=>{const t=(0,z.setupOfEnvironment)(c,e)
3634
3699
  return t.length?{setup:t}:{}},d=(e,t)=>Array.from(new Set([...(0,I.toolchainsOf)((e?.config?.commands??[]).map(e=>e?.command)),...(0,$.environmentToolchains)(t)])),u=n.length<=1?await a(e):null
3635
3700
  let p=null
3636
3701
  const f=new Map,m=(e,t,r)=>{f.has(e)||f.set(e,{...t,indices:[]}),f.get(e).indices.push(r)}
@@ -3647,7 +3712,20 @@ g={key:`${t.proc?.id}`,gateEnv:t.env,authoritative:s,has:d(t.proc,t.env),...t.pr
3647
3712
  continue}p??(p=await h(e))
3648
3713
  const y=p.find(e=>(0,$.covers)(e.has,f))
3649
3714
  y?m(`env:${y.id}`,{gateEnv:y.env,authoritative:!1,environmentId:y.id,...l(y.id)},s):m(`${g.key}|lacks:${f.join(",")}`,{gateEnv:g.gateEnv,authoritative:!1,lacks:f},s)}return[...f.values()]}var f=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
3650
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.COMPLETION_REPLAY_BUDGET_MS=void 0,exports.replayEnvironment=a,exports.proofEvidence=c,exports.overdueEvidence=l,exports.replayVerifyAt=d,exports.criterionPins=u,exports.replayPlan=p,exports.registerWorkspaceRoutes=function(e,o){e.get("/api/previews",(e,t)=>{t.json({success:!0,data:(0,O.listPreviewHandles)()})}),e.post("/api/previews",(e,t)=>{const{name:r,url:s,label:n}=e.body??{}
3715
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.COMPLETION_REPLAY_BUDGET_MS=void 0,exports.replayEnvironment=a,exports.proofEvidence=c,exports.overdueEvidence=l,exports.replayVerifyAt=d,exports.criterionPins=u,exports.replayPlan=p,exports.registerWorkspaceRoutes=function(e,o){const i=e=>(0,q.versionOfUrl)(o.projectPath,e);(0,q.prunePinned)(o.projectPath,(0,D.listServedSites)().map(e=>e.root)).catch(()=>{}),e.get("/api/previews",(e,t)=>{t.json({success:!0,data:(0,q.previewHandlesWithVersions)(o.projectPath)})}),e.get("/api/preview-versions",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:""
3716
+ if(r)return void t.json({success:!0,data:await(0,q.landingOf)(o.projectPath,r)})
3717
+ const s="string"==typeof e.query.name?e.query.name:"",n=s?(0,O.getPreviewHandle)(s):void 0,a=n?i(n.url):null
3718
+ if(!n||!a)return void t.status(404).json({success:!1,error:`No preview of a folder named ${s}`})
3719
+ t.json({success:!0,data:{version:a,label:n.label,versions:await(0,q.versionsOf)(o.projectPath,a.folder)}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.get("/api/preview-pages",async(e,t)=>{try{t.json({success:!0,data:await(0,q.pagesOf)(o.projectPath)})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.post("/api/preview-versions/open",async(e,t)=>{const{folder:r,requestId:s,commit:n,name:a,label:c}=e.body??{}
3720
+ if("string"!=typeof r||r.startsWith("/")||r.split("/").includes("..")||(0,D.refusedPreviewPath)("."===r?"":r))return void t.status(400).json({success:!1,error:"folder (repo-relative) required"})
3721
+ if(void 0!==s&&("string"!=typeof s||!/^req\d{6}$/.test(s)))return void t.status(400).json({success:!1,error:"requestId must be a request id like req000006"})
3722
+ const l=("string"==typeof a&&a.trim()?a.trim():r).replace(/@req\d{6}$/,"").replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,"")||"preview"
3723
+ try{let e,a
3724
+ if(s){const i="string"==typeof n&&n?n:(await(0,q.landingOf)(o.projectPath,s))?.commit
3725
+ if(!i)return void t.status(404).json({success:!1,error:`${s} has not landed on the default branch`})
3726
+ e=await(0,q.pinnedRoot)(o.projectPath,i,r,s)}else{e=(await(0,D.previewRootFor)(o.projectPath,r,()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath))).root,a=()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath)
3727
+ try{await N.promises.access(M.default.join(e,"index.html"))}catch{return void t.status(400).json({success:!1,error:`no index.html in ${r} on main`})}}const{url:d}=await(0,D.serveSite)(e,a?{refresh:a}:{}),u=(0,O.registerPreviewHandle)(s?`${l}@${s}`:l,d,"string"==typeof c&&c.trim()?c.trim():void 0,{fallbackLabel:r})
3728
+ t.json({success:!0,data:{...u,version:i(d)}})}catch(e){t.status(400).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.post("/api/previews",(e,t)=>{const{name:r,url:s,label:n}=e.body??{}
3651
3729
  if(!r||"string"!=typeof r||!s||"string"!=typeof s||!/^https?:\/\//.test(s))return void t.status(400).json({success:!1,error:"name and http(s) url required"})
3652
3730
  const o=(0,O.registerPreviewHandle)(r,s,"string"==typeof n?n:void 0)
3653
3731
  t.status(201).json({success:!0,data:o})}),e.post("/api/previews/:name/snapshot",async(e,r)=>{const s=(0,O.getPreviewHandle)(e.params.name)
@@ -3656,30 +3734,30 @@ let i
3656
3734
  try{i=await n.launch({channel:"chrome",args:["--no-sandbox"]})}catch{i=await n.launch({args:["--no-sandbox"]})}try{const t=await i.newPage({viewport:{width:1280,height:900},deviceScaleFactor:2,ignoreHTTPSErrors:!0})
3657
3735
  await t.goto(s.url,{waitUntil:"domcontentloaded",timeout:3e4}),await t.waitForLoadState("load",{timeout:1e4}).catch(()=>{}),await t.waitForLoadState("networkidle",{timeout:4e3}).catch(()=>{}),await t.waitForTimeout(300)
3658
3736
  const n=M.default.join(".alchemy","snapshots",`${e.params.name}-${Date.now()}.png`),a=M.default.resolve(o.projectPath,n)
3659
- await q.promises.mkdir(M.default.dirname(a),{recursive:!0}),await t.screenshot({path:a}),r.json({success:!0,data:{path:n,ref:`file:${n}`,url:s.url}})}finally{await i.close().catch(()=>{})}}catch(e){r.status(500).json({success:!1,error:e instanceof Error?e.message:String(e)})}else r.status(404).json({success:!1,error:`No preview named ${e.params.name}`})})
3660
- const i=e=>{if(e.startsWith("::ffff:")&&(e=e.slice(7)),e.includes(":")){const t=e.toLowerCase()
3737
+ await N.promises.mkdir(M.default.dirname(a),{recursive:!0}),await t.screenshot({path:a}),r.json({success:!0,data:{path:n,ref:`file:${n}`,url:s.url}})}finally{await i.close().catch(()=>{})}}catch(e){r.status(500).json({success:!1,error:e instanceof Error?e.message:String(e)})}else r.status(404).json({success:!1,error:`No preview named ${e.params.name}`})})
3738
+ const u=e=>{if(e.startsWith("::ffff:")&&(e=e.slice(7)),e.includes(":")){const t=e.toLowerCase()
3661
3739
  return"::1"!==t&&("::"===t||!!(t.startsWith("fe80")||t.startsWith("fc")||t.startsWith("fd")))}const t=e.split(".").map(Number)
3662
3740
  if(4!==t.length||t.some(e=>Number.isNaN(e)))return!0
3663
3741
  const[r,s]=t
3664
- return 127!==r&&(0===r||10===r||172===r&&s>=16&&s<=31||192===r&&168===s||169===r&&254===s||r>=224)},u=async e=>{let r
3742
+ return 127!==r&&(0===r||10===r||172===r&&s>=16&&s<=31||192===r&&168===s||169===r&&254===s||r>=224)},h=async e=>{let r
3665
3743
  try{r=new URL(e).hostname}catch{return"invalid URL"}const s=r.replace(/^\[|\]$/g,"")
3666
- if(t(69278).isIP(s))return i(s)?`refused: ${s} is a private/infrastructure address`:null
3744
+ if(t(69278).isIP(s))return u(s)?`refused: ${s} is a private/infrastructure address`:null
3667
3745
  if("localhost"===s)return null
3668
3746
  try{const e=t(72250).promises,r=await e.lookup(s,{all:!0})
3669
3747
  if(!r||0===r.length)return"refused: hostname did not resolve"
3670
- for(const e of r)if(i(e.address))return`refused: ${s} resolves to private/infrastructure address ${e.address}`
3748
+ for(const e of r)if(u(e.address))return`refused: ${s} resolves to private/infrastructure address ${e.address}`
3671
3749
  return null}catch{return"refused: hostname did not resolve"}}
3672
3750
  e.post("/api/site-shots",async(e,t)=>{const{url:r,fullPage:s,width:n}=e.body??{}
3673
3751
  if(!r||"string"!=typeof r||!/^https?:\/\//i.test(r))return void t.status(400).json({success:!1,error:"http(s) url required"})
3674
- const i=await u(r)
3752
+ const i=await h(r)
3675
3753
  if(i)t.status(403).json({success:!1,error:i})
3676
- else if(o.getProjectId())try{const{buf:e,mime:i}=await(0,V.shootUrl)(r,{fullPage:!!s,width:"number"==typeof n?n:void 0}),a=8388608
3754
+ else if(o.getProjectId())try{const{buf:e,mime:i}=await(0,Y.shootUrl)(r,{fullPage:!!s,width:"number"==typeof n?n:void 0}),a=8388608
3677
3755
  if(e.length>a)return void t.status(413).json({success:!1,error:`screenshot is ${e.length} bytes (cap ${a}); retry without fullPage or with a narrower width`})
3678
- const c=`data:${i};base64,${e.toString("base64")}`,l=await(0,z.storeLocalAsset)(o.projectPath,c).catch(()=>{})
3756
+ const c=`data:${i};base64,${e.toString("base64")}`,l=await(0,V.storeLocalAsset)(o.projectPath,c).catch(()=>{})
3679
3757
  if(!l)return void t.status(502).json({success:!1,error:"asset upload failed"})
3680
3758
  t.json({success:!0,data:{filename:l,dataUrl:c,url:r}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}else t.status(503).json({success:!1,error:"project not registered with the remote yet"})}),e.post("/api/site-text",async(e,r)=>{const{url:s}=e.body??{}
3681
3759
  if(!s||"string"!=typeof s||!/^https?:\/\//i.test(s))return void r.status(400).json({success:!1,error:"http(s) url required"})
3682
- const n=await u(s)
3760
+ const n=await h(s)
3683
3761
  if(n)r.status(403).json({success:!1,error:n})
3684
3762
  else try{const{chromium:e}=t(71257)
3685
3763
  let n
@@ -3689,28 +3767,28 @@ const o=await e.goto(s,{waitUntil:"domcontentloaded",timeout:3e4}),i=o?o.status(
3689
3767
  await e.waitForLoadState("load",{timeout:1e4}).catch(()=>{}),await e.waitForLoadState("networkidle",{timeout:6e3}).catch(()=>{}),await e.waitForTimeout(500)
3690
3768
  const a=await e.title().catch(()=>""),c=e.url(),l=await e.evaluate(()=>document.body&&document.body.innerText||"").catch(()=>""),d=6e4
3691
3769
  r.json({success:!0,data:{url:s,finalUrl:c,title:a,status:i,truncated:l.length>d,text:l.slice(0,d),consoleErrors:t.slice(0,20)}})}finally{await n.close().catch(()=>{})}}catch(e){r.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}})
3692
- const h=new Map,p=()=>{const e=Date.now()
3693
- for(const[t,r]of h)e-r.lastUsed>6e5&&(h.delete(t),r.browser.close().catch(()=>{}))}
3694
- setInterval(p,6e4).unref?.(),e.post("/api/site-drive",async(e,r)=>{const{url:s,steps:n,shots:i}=e.body??{}
3770
+ const p=new Map,f=()=>{const e=Date.now()
3771
+ for(const[t,r]of p)e-r.lastUsed>6e5&&(p.delete(t),r.browser.close().catch(()=>{}))}
3772
+ setInterval(f,6e4).unref?.(),e.post("/api/site-drive",async(e,r)=>{const{url:s,steps:n,shots:i}=e.body??{}
3695
3773
  if(!s||"string"!=typeof s||!/^https?:\/\//i.test(s))return void r.status(400).json({success:!1,error:"http(s) url required"})
3696
3774
  if(!Array.isArray(n)||0===n.length)return void r.status(400).json({success:!1,error:"steps: a non-empty array of { action, ... }"})
3697
3775
  if(n.length>20)return void r.status(400).json({success:!1,error:"at most 20 steps (a drive, not a suite)"})
3698
- const a=await u(s)
3776
+ const a=await h(s)
3699
3777
  if(a)return void r.status(403).json({success:!1,error:a})
3700
3778
  const c="each"===i,l="string"==typeof e.body?.session&&e.body.session.trim()?e.body.session.trim().slice(0,64):null,d=!0===e.body?.close
3701
3779
  try{const{chromium:i}=t(71257)
3702
3780
  let a,u
3703
- p()
3704
- let f=!1
3705
- const m=l?h.get(l):void 0
3706
- if(m)a=m.browser,u=m.page,f=!0
3781
+ f()
3782
+ let h=!1
3783
+ const m=l?p.get(l):void 0
3784
+ if(m)a=m.browser,u=m.page,h=!0
3707
3785
  else try{a=await i.launch({channel:"chrome",args:["--no-sandbox"]})}catch{a=await i.launch({args:["--no-sandbox"]})}const g=Date.now(),w=[]
3708
3786
  try{const t=m?.consoleErrors??[]
3709
- if(!f){const r=Number(e.body?.width),s=Number.isFinite(r)&&r>0?Math.min(1920,Math.max(320,Math.round(r))):1280,n=s<600?844:900
3710
- u=await a.newPage({viewport:{width:s,height:n},deviceScaleFactor:1,ignoreHTTPSErrors:!0}),u.on("console",e=>{"error"===e.type()&&t.push(String(e.text()).slice(0,300))}),u.on("pageerror",e=>t.push(String(e?.message??e).slice(0,300)))}f&&u.url()===s||(await u.goto(s,{waitUntil:"domcontentloaded",timeout:3e4}),await u.waitForLoadState("load",{timeout:1e4}).catch(()=>{}),await u.waitForTimeout(300))
3787
+ if(!h){const r=Number(e.body?.width),s=Number.isFinite(r)&&r>0?Math.min(1920,Math.max(320,Math.round(r))):1280,n=s<600?844:900
3788
+ u=await a.newPage({viewport:{width:s,height:n},deviceScaleFactor:1,ignoreHTTPSErrors:!0}),u.on("console",e=>{"error"===e.type()&&t.push(String(e.text()).slice(0,300))}),u.on("pageerror",e=>t.push(String(e?.message??e).slice(0,300)))}h&&u.url()===s||(await u.goto(s,{waitUntil:"domcontentloaded",timeout:3e4}),await u.waitForLoadState("load",{timeout:1e4}).catch(()=>{}),await u.waitForTimeout(300))
3711
3789
  let i=null
3712
- const p=async e=>{try{const t=`data:image/png;base64,${(await u.screenshot({type:"png"})).toString("base64")}`
3713
- return"final"===e&&(i=t),`asset:${await(0,z.storeLocalAsset)(o.projectPath,t)}`}catch{return null}}
3790
+ const f=async e=>{try{const t=`data:image/png;base64,${(await u.screenshot({type:"png"})).toString("base64")}`
3791
+ return"final"===e&&(i=t),`asset:${await(0,V.storeLocalAsset)(o.projectPath,t)}`}catch{return null}}
3714
3792
  for(let e=0;e<n.length;e++){if(Date.now()-g>9e4){w.push({step:e,action:"aborted",ok:!1,note:"drive exceeded 90s"})
3715
3793
  break}const t=n[e]??{},r=String(t.action||"").toLowerCase(),s="string"==typeof t.selector?t.selector:void 0,o="string"==typeof t.text?t.text:void 0,i={step:e,action:r,...s?{selector:s}:{},...o?{text:o}:{}}
3716
3794
  try{if("click"===r){if(!s)throw new Error("click needs a selector")
@@ -3723,9 +3801,9 @@ else if("wait"===r)await u.waitForTimeout(Math.min(Number(t.ms??500),5e3))
3723
3801
  else{if("media"!==r)throw new Error(`unknown action "${r}" (click, clickat, type, press, move, scroll, wait, media)`)
3724
3802
  {const e="screen"===t.media?"screen":"print"===t.media?"print":null
3725
3803
  if(!e)throw new Error('media needs media: "print" or "screen"')
3726
- await u.emulateMedia({media:e}),i.media=e}}i.ok=!0}catch(e){i.ok=!1,i.error=(0,P.getErrorMessage)(e)}await u.waitForTimeout(200),c&&(i.shot=await p(String(e))),w.push(i)}const y=await p("final"),v=await u.title().catch(()=>""),b=await u.evaluate(()=>document.body&&document.body.innerText||"").catch(()=>"")
3727
- if(r.json({success:!0,data:{url:s,finalUrl:u.url(),title:v,steps:w,finalShot:y,finalShotDataUrl:i,viewport:(()=>{try{return u.viewportSize()}catch{return null}})(),consoleErrors:t.slice(0,20),text:String(b).slice(0,2e4),ms:Date.now()-g,...l?{session:l,resumed:f,held:!d}:{}}}),l&&!d&&(h.set(l,{browser:a,page:u,lastUsed:Date.now(),consoleErrors:t}),h.size>4)){const e=[...h.entries()].sort((e,t)=>e[1].lastUsed-t[1].lastUsed)[0]
3728
- e&&(h.delete(e[0]),e[1].browser.close().catch(()=>{}))}}finally{l&&!d||(l&&h.delete(l),await a.close().catch(()=>{}))}}catch(e){r.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.post("/api/site-previews",async(e,t)=>{const{dir:r,name:s,label:n,requestId:i}=e.body??{}
3804
+ await u.emulateMedia({media:e}),i.media=e}}i.ok=!0}catch(e){i.ok=!1,i.error=(0,P.getErrorMessage)(e)}await u.waitForTimeout(200),c&&(i.shot=await f(String(e))),w.push(i)}const y=await f("final"),v=await u.title().catch(()=>""),b=await u.evaluate(()=>document.body&&document.body.innerText||"").catch(()=>"")
3805
+ if(r.json({success:!0,data:{url:s,finalUrl:u.url(),title:v,steps:w,finalShot:y,finalShotDataUrl:i,viewport:(()=>{try{return u.viewportSize()}catch{return null}})(),consoleErrors:t.slice(0,20),text:String(b).slice(0,2e4),ms:Date.now()-g,...l?{session:l,resumed:h,held:!d}:{}}}),l&&!d&&(p.set(l,{browser:a,page:u,lastUsed:Date.now(),consoleErrors:t}),p.size>4)){const e=[...p.entries()].sort((e,t)=>e[1].lastUsed-t[1].lastUsed)[0]
3806
+ e&&(p.delete(e[0]),e[1].browser.close().catch(()=>{}))}}finally{l&&!d||(l&&p.delete(l),await a.close().catch(()=>{}))}}catch(e){r.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.post("/api/site-previews",async(e,t)=>{const{dir:r,name:s,label:n,requestId:i}=e.body??{}
3729
3807
  if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"dir (repo-relative) required"})
3730
3808
  if(r.startsWith("/")||r.includes(".."))return void t.status(400).json({success:!1,error:'dir must be repo-relative, no ".."'})
3731
3809
  if(void 0!==i&&("string"!=typeof i||!/^req\d{6}$/.test(i)))return void t.status(400).json({success:!1,error:"requestId must be a request id like req000006"})
@@ -3733,38 +3811,38 @@ const a=i
3733
3811
  try{const e=M.default.normalize(r).split(M.default.sep).join("/").replace(/\/+$/,"")||"."
3734
3812
  if(e.startsWith("..")||(0,D.refusedPreviewPath)(e))return void t.status(400).json({success:!1,error:`${r} is not a site directory (hidden and AlchemyLab folders are not served)`})
3735
3813
  const i=await(0,D.previewRootFor)(o.projectPath,e,()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath),a),c=M.default.join(i.root,"index.html")
3736
- try{await q.promises.access(c)}catch{return void t.status(400).json({success:!1,error:i.main?`no index.html in ${r} on main (a preview of main serves what main has committed, plus build output from commands run for main; files only in the project folder are not served)`:`no index.html in ${r} (build it first, or point at the build output)`})}const{url:l}=await(0,D.serveSite)(i.root,i.main?{refresh:()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath)}:{}),d=a?`${a}/${"."===e?"":e}`.replace(/\/$/,""):r,u="string"==typeof s&&s.trim()?s.trim():d.replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,""),h=(0,O.registerPreviewHandle)(u,l,"string"==typeof n&&n.trim()?n.trim():void 0,{requestId:a,fallbackLabel:d})
3737
- t.json({success:!0,data:{...h,dir:r,ref:`live:${h.name}`}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.delete("/api/previews/:name",(e,t)=>{const r=(0,O.getPreviewHandle)(e.params.name),s=!!r&&(0,D.stopSiteAt)(r.url);(0,O.unregisterPreviewHandle)(e.params.name),t.json({success:!0,data:{name:e.params.name,deleted:!0,stopped:s}})}),e.get("/api/file",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)&&!e.query.ref.startsWith("-")?e.query.ref:void 0,n=(0,X.refusedRepoPath)(r)
3814
+ try{await N.promises.access(c)}catch{return void t.status(400).json({success:!1,error:i.main?`no index.html in ${r} on main (a preview of main serves what main has committed, plus build output from commands run for main; files only in the project folder are not served)`:`no index.html in ${r} (build it first, or point at the build output)`})}const{url:l}=await(0,D.serveSite)(i.root,i.main?{refresh:()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath)}:{}),d=a?`${a}/${"."===e?"":e}`.replace(/\/$/,""):r,u="string"==typeof s&&s.trim()?s.trim():d.replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,""),h=(0,O.registerPreviewHandle)(u,l,"string"==typeof n&&n.trim()?n.trim():void 0,{requestId:a,fallbackLabel:d})
3815
+ t.json({success:!0,data:{...h,dir:r,ref:`live:${h.name}`}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.delete("/api/previews/:name",(e,t)=>{const r=(0,O.getPreviewHandle)(e.params.name),s=!!r&&(0,D.stopSiteAt)(r.url);(0,O.unregisterPreviewHandle)(e.params.name),s&&(0,q.prunePinned)(o.projectPath,(0,D.listServedSites)().map(e=>e.root)).catch(()=>{}),t.json({success:!0,data:{name:e.params.name,deleted:!0,stopped:s}})}),e.get("/api/file",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)&&!e.query.ref.startsWith("-")?e.query.ref:void 0,n=(0,Q.refusedRepoPath)(r)
3738
3816
  if(n)return void t.status(400).json({success:!1,error:n})
3739
3817
  const i=524288,a=async e=>{const t=(0,w.requestWorktreesDir)(o.projectPath)
3740
3818
  let r=null
3741
- try{const s=await q.promises.readdir(t,{withFileTypes:!0})
3819
+ try{const s=await N.promises.readdir(t,{withFileTypes:!0})
3742
3820
  for(const n of s){if(!n.isDirectory())continue
3743
3821
  const s=M.default.join(t,n.name,e)
3744
- if(s.startsWith(M.default.join(t,n.name)+M.default.sep)&&await(0,X.realPathWithin)(M.default.join(t,n.name),s))try{const e=await q.promises.stat(s)
3822
+ if(s.startsWith(M.default.join(t,n.name)+M.default.sep)&&await(0,Q.realPathWithin)(M.default.join(t,n.name),s))try{const e=await N.promises.stat(s)
3745
3823
  e.isFile()&&(!r||e.mtimeMs>r.mtime)&&(r={abs:s,from:`requests/${n.name}`,mtime:e.mtimeMs})}catch{}}}catch{}return r?{abs:r.abs,from:r.from}:null}
3746
3824
  let c,l=null
3747
- if(s){const e=(0,L.simpleGit)(o.projectPath)
3825
+ if(s){const e=(0,U.simpleGit)(o.projectPath)
3748
3826
  c=await e.raw(["show",`${s}:${r}`])}else{const e=M.default.resolve(o.projectPath,r)
3749
3827
  if(!e.startsWith(M.default.resolve(o.projectPath)+M.default.sep))return void t.status(400).json({success:!1,error:"path escapes the project"})
3750
- if(!await(0,X.realPathWithin)(o.projectPath,e))return void t.status(400).json({success:!1,error:X.ESCAPES_ROOT})
3751
- try{c=await q.promises.readFile(e,"utf8")}catch(e){if("ENOENT"!==e?.code)throw e
3828
+ if(!await(0,Q.realPathWithin)(o.projectPath,e))return void t.status(400).json({success:!1,error:Q.ESCAPES_ROOT})
3829
+ try{c=await N.promises.readFile(e,"utf8")}catch(e){if("ENOENT"!==e?.code)throw e
3752
3830
  const t=await a(r)
3753
3831
  if(!t)throw e
3754
- c=await q.promises.readFile(t.abs,"utf8"),l=t.from}}const d=c.length>i
3832
+ c=await N.promises.readFile(t.abs,"utf8"),l=t.from}}const d=c.length>i
3755
3833
  if(d&&(c=c.slice(0,i)),c.includes("\0")){const e=M.default.extname(r).toLowerCase(),n={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml"}
3756
3834
  if(n[e]){const i=M.default.resolve(o.projectPath,r)
3757
3835
  let c
3758
- if(s)c=Buffer.from(await(0,L.simpleGit)(o.projectPath).binaryCatFile(["blob",`${s}:${r}`]))
3759
- else try{c=await q.promises.readFile(i)}catch(e){if("ENOENT"!==e?.code)throw e
3836
+ if(s)c=Buffer.from(await(0,U.simpleGit)(o.projectPath).binaryCatFile(["blob",`${s}:${r}`]))
3837
+ else try{c=await N.promises.readFile(i)}catch(e){if("ENOENT"!==e?.code)throw e
3760
3838
  const t=await a(r)
3761
3839
  if(!t)throw e
3762
- c=await q.promises.readFile(t.abs)}return t.setHeader("Content-Type",n[e]),t.setHeader("Cache-Control","no-cache"),void t.send(c)}return void t.status(415).json({success:!1,error:"binary file"})}t.json({success:!0,data:{path:r,ref:s??l,content:c,truncated:d,servedFrom:l}})}catch(r){const s=r instanceof Error?r.message:String(r)
3840
+ c=await N.promises.readFile(t.abs)}return t.setHeader("Content-Type",n[e]),t.setHeader("Cache-Control","no-cache"),void t.send(c)}return void t.status(415).json({success:!1,error:"binary file"})}t.json({success:!0,data:{path:r,ref:s??l,content:c,truncated:d,servedFrom:l}})}catch(r){const s=r instanceof Error?r.message:String(r)
3763
3841
  if(/ENOENT/.test(s)){const r="string"==typeof e.query.path?e.query.path:"",s=M.default.basename(r),n=[]
3764
- try{const e=(0,w.requestWorktreesDir)(o.projectPath),t=await q.promises.readdir(e,{withFileTypes:!0})
3842
+ try{const e=(0,w.requestWorktreesDir)(o.projectPath),t=await N.promises.readdir(e,{withFileTypes:!0})
3765
3843
  for(const r of t){if(!r.isDirectory())continue
3766
3844
  const t=M.default.join(e,r.name),o=async(e,i)=>{if(i>4||n.length>=5)return
3767
- const a=await q.promises.readdir(e,{withFileTypes:!0}).catch(()=>[])
3845
+ const a=await N.promises.readdir(e,{withFileTypes:!0}).catch(()=>[])
3768
3846
  for(const c of a){if(n.length>=5)return
3769
3847
  if(".git"===c.name||"node_modules"===c.name)continue
3770
3848
  const a=M.default.join(e,c.name)
@@ -3772,7 +3850,7 @@ c.isDirectory()?await o(a,i+1):c.name===s&&n.push(`${M.default.relative(t,a)} (r
3772
3850
  await o(t,0)}}catch{}const i=M.default.basename(o.projectPath)
3773
3851
  return void t.status(404).json({success:!1,error:n.length?`'${r}' is not in project '${i}' (default checkout or request worktrees). It may have MOVED -- found instead: ${n.join(", ")}`:`'${r}' is not in project '${i}' (default checkout or request worktrees). If you expected another project, this action server serves '${i}' -- reload so the UI re-discovers the right one.`,data:{suggestions:n,servingProject:i}})}t.status(404).json({success:!1,error:s})}}),e.get("/api/provenance",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s=Math.max(1,parseInt(String(e.query.start??"1"),10)||1),n=parseInt(String(e.query.end??String(s)),10)||s,i=Math.min(s+300,Math.max(s,n)),a="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)?e.query.ref:void 0
3774
3852
  if(!r||r.includes("..")||M.default.isAbsolute(r))return void t.status(400).json({success:!1,error:"path must be a repo-relative file path"})
3775
- const c=(0,L.simpleGit)(o.projectPath),l=["blame","--line-porcelain","-L",`${s},${i}`]
3853
+ const c=(0,U.simpleGit)(o.projectPath),l=["blame","--line-porcelain","-L",`${s},${i}`]
3776
3854
  a&&l.push(a),l.push("--",r)
3777
3855
  const d=await c.raw(l),u=[]
3778
3856
  let h=null
@@ -3793,84 +3871,84 @@ t.json({success:!0,data:{workdir:i,kind:"default-branch-snapshot"}})}catch(e){t.
3793
3871
  return Number.isFinite(t)&&t>=1&&r>=t?{path:e.path,lines:[Math.floor(t),Math.min(r,Number.MAX_SAFE_INTEGER)]}:{path:e.path}}),c=i&&a.length>0?await r(o,n):null
3794
3872
  if(!c||!i)return void t.json({success:!0,data:{note:null}})
3795
3873
  const l=await async function(e){const t=e.getProjectId(),r=Date.now()
3796
- if(!re||r-re.at>15e3){const s=t?await(0,v.readAllPages)(e.apiClient,e=>`/api/projects/${t}/requests?inFlight=true&limit=100&offset=${e}`):[]
3797
- re={at:r,rows:s}}const s=re.rows.map(se).filter(e=>/^req\d+$/.test(e.id))
3798
- return{projectPath:e.projectPath,defaultBranch:await(0,Y.resolveDefaultBranch)(e.projectPath),open:s}}(o),d=[]
3874
+ if(!se||r-se.at>15e3){const s=t?await(0,v.readAllPages)(e.apiClient,e=>`/api/projects/${t}/requests?inFlight=true&limit=100&offset=${e}`):[]
3875
+ se={at:r,rows:s}}const s=se.rows.map(ne).filter(e=>/^req\d+$/.test(e.id))
3876
+ return{projectPath:e.projectPath,defaultBranch:await(0,J.resolveDefaultBranch)(e.projectPath),open:s}}(o),d=[]
3799
3877
  for(const e of a){const t=M.default.resolve(c.worktree,e.path),r=M.default.relative(c.worktree,t)
3800
3878
  if(!r||r.startsWith("..")||M.default.isAbsolute(r)||".alchemy_lab"===r.split(M.default.sep)[0])continue
3801
- const s=await te.note({sessionId:n,requestId:c.requestId,worktree:c.worktree,relPath:r.split(M.default.sep).join("/"),tool:i,..."read"===i&&e.lines?{readRange:e.lines}:{}},l)
3879
+ const s=await re.note({sessionId:n,requestId:c.requestId,worktree:c.worktree,relPath:r.split(M.default.sep).join("/"),tool:i,..."read"===i&&e.lines?{readRange:e.lines}:{}},l)
3802
3880
  s&&d.push(a.length>1?`${r}:\n${s}`:s)}t.json({success:!0,data:{note:d.length>0?d.join("\n\n"):null}})}catch(e){o.logger.debug("parallel-note failed (the tool result stays as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{note:null}})}}),e.get("/api/sibling-overlap",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
3803
3881
  if(!r||!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"requestId required"})
3804
- const s=o.getProjectId(),n=(0,L.simpleGit)(o.projectPath),i=(await n.raw(["for-each-ref","--format=%(refname:short)","refs/heads/requests/"])).split("\n").map(e=>e.trim().replace(/^requests\//,"")).filter(e=>/^req\d+$/.test(e)&&e!==r),a=new Set(["open","in_progress","ready_for_review","completing","resolving_conflicts"]),c=new Set(i),l=[]
3882
+ const s=o.getProjectId(),n=(0,U.simpleGit)(o.projectPath),i=(await n.raw(["for-each-ref","--format=%(refname:short)","refs/heads/requests/"])).split("\n").map(e=>e.trim().replace(/^requests\//,"")).filter(e=>/^req\d+$/.test(e)&&e!==r),a=new Set(["open","in_progress","ready_for_review","completing","resolving_conflicts"]),c=new Set(i),l=[]
3805
3883
  if(s&&c.size>0){const e=await o.apiClient.get(`/api/projects/${s}/requests?inFlight=true&limit=100`),t=Array.isArray(e?.data)?e.data:[]
3806
3884
  for(const e of t){const t=String(e?.localId??"")
3807
- c.has(t)&&a.has(String(e.status))&&l.push({id:t,title:String(e.title??""),status:String(e.status)})}}const d=await(0,Y.resolveDefaultBranch)(o.projectPath),u=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:l.map(e=>e.id),defaultBranch:d,git:n}),h=new Map(l.map(e=>[e.id,e]))
3885
+ c.has(t)&&a.has(String(e.status))&&l.push({id:t,title:String(e.title??""),status:String(e.status)})}}const d=await(0,J.resolveDefaultBranch)(o.projectPath),u=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:l.map(e=>e.id),defaultBranch:d,git:n}),h=new Map(l.map(e=>[e.id,e]))
3808
3886
  t.json({success:!0,data:u.map(e=>({...e,siblingTitle:h.get(e.sibling)?.title??"",siblingStatus:h.get(e.sibling)?.status??""}))})}catch(e){o.logger.debug("sibling-overlap lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:[]})}}),e.get("/api/request-diff-stats",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
3809
3887
  if(!r)return void t.status(400).json({success:!1,error:"requestId required"})
3810
- const s=(0,L.simpleGit)(o.projectPath),n=await(0,F.resolveRequestDiffRange)(s,r,{status:"in_progress"}),i=n.headSha,a="string"==typeof e.query.since&&/^[0-9a-f]{7,40}$/.test(e.query.since)?e.query.since:"",c=a&&i&&await s.raw(["merge-base","--is-ancestor",a,i]).then(()=>!0,()=>!1)?a:n.baseSha,l=c&&i?await(0,F.computeDiffStats)(s,c,i):void 0,d=await(0,Y.resolveDefaultBranch)(o.projectPath).catch(()=>{})
3888
+ const s=(0,U.simpleGit)(o.projectPath),n=await(0,L.resolveRequestDiffRange)(s,r,{status:"in_progress"}),i=n.headSha,a="string"==typeof e.query.since&&/^[0-9a-f]{7,40}$/.test(e.query.since)?e.query.since:"",c=a&&i&&await s.raw(["merge-base","--is-ancestor",a,i]).then(()=>!0,()=>!1)?a:n.baseSha,l=c&&i?await(0,L.computeDiffStats)(s,c,i):void 0,d=await(0,J.resolveDefaultBranch)(o.projectPath).catch(()=>{})
3811
3889
  t.json({success:!0,data:l?{...l,baseSha:c,headSha:i,...d?{branch:d}:{}}:null})}catch(e){o.logger.debug("request-diff-stats lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:null})}}),e.post("/api/verify-preflight",async(e,t)=>{try{const{requestId:r,commands:s}=e.body??{},n=Array.isArray(s)?s.filter(e=>"string"==typeof e&&!!e.trim()):[]
3812
3890
  if(!r||0===n.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty commands array are required"})
3813
- const i=(await(0,L.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim(),a=Array.isArray(e.body?.proofs)?e.body.proofs.filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command):[]
3891
+ const i=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim(),a=Array.isArray(e.body?.proofs)?e.body.proofs.filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command):[]
3814
3892
  if(!0===e.body?.background){const e=JSON.stringify([[...n].sort(),a.map(e=>`${e.criterionId}\0${e.command}`).sort()])
3815
- return(0,B.startPendingProof)(r,i,async e=>{const t=await d(o,r,i,n,{proofs:a,onAdvisory:e.advisory,onStep:e.step}),s=o.getProjectId(),l=c(a,t)
3893
+ return(0,H.startPendingProof)(r,i,async e=>{const t=await d(o,r,i,n,{proofs:a,onAdvisory:e.advisory,onStep:e.step}),s=o.getProjectId(),l=c(a,t)
3816
3894
  if(e.step("recording what it found",6e4),s&&l.length>0&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:t.sha,tree:t.tree??null,proofs:l}).catch(e=>o.logger.warn("proof replay: evidence not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)})),!t.available||!Array.isArray(t.results))return null
3817
3895
  const u=t.results.filter(e=>!e.timedOut&&!e.couldNotRun&&!e.cutShort&&!1!==(e.authoritative??t.authoritative)).map(e=>({command:e.command,ok:e.ok,exit:e.exit,output:[e.printed,e.error].filter(Boolean).join("\n")||String(e.output??""),...e.flaky?{flaky:!0}:{}})),h=u.filter(e=>!e.ok).map(e=>({command:e.command,exit:e.exit,output:String(e.output??"")}))
3818
3896
  return o.logger.info("proof replay finished beside the sign-off check",{requestId:r,sha:i.slice(0,8),blocking:h.length,settled:u.length,ms:t.ms}),{sha:t.sha,...t.tree?{tree:t.tree}:{},blocking:h,settled:u}},{key:e,onOverdue:async(e,t)=>{const s=o.getProjectId()
3819
- o.logger.warn("proof replay: overdue, the merge goes on without it",{requestId:r,sha:i.slice(0,8),step:e.what,afterSec:Math.round(t/1e3)}),s&&0!==a.length&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:i,proofs:l(a,e,t)}).catch(e=>o.logger.warn("proof replay: its overdue end was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})||o.logger.info("proof replay: the same proofs at this commit are already replayed; joined",{requestId:r,sha:i.slice(0,8)}),void t.json({success:!0,data:{available:!0,pending:!0,sha:i}})}t.json({success:!0,data:await d(o,r,i,n,{proofs:a})})}catch(e){o.logger.debug("verify-preflight failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{available:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/showing-run",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(0,W.showCommands)(e.body?.commands),n=r&&/^req\d+$/.test(r)&&!0===e.body?.page?(0,W.servedPageOf)((0,w.requestWorktreePath)(o.projectPath,r),r,(0,O.listPreviewHandles)(),(0,D.listServedSites)()):null
3897
+ o.logger.warn("proof replay: overdue, the merge goes on without it",{requestId:r,sha:i.slice(0,8),step:e.what,afterSec:Math.round(t/1e3)}),s&&0!==a.length&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:i,proofs:l(a,e,t)}).catch(e=>o.logger.warn("proof replay: its overdue end was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})||o.logger.info("proof replay: the same proofs at this commit are already replayed; joined",{requestId:r,sha:i.slice(0,8)}),void t.json({success:!0,data:{available:!0,pending:!0,sha:i}})}t.json({success:!0,data:await d(o,r,i,n,{proofs:a})})}catch(e){o.logger.debug("verify-preflight failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{available:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/showing-run",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(0,G.showCommands)(e.body?.commands),n=r&&/^req\d+$/.test(r)&&!0===e.body?.page?(0,G.servedPageOf)((0,w.requestWorktreePath)(o.projectPath,r),r,(0,O.listPreviewHandles)(),(0,D.listServedSites)()):null
3820
3898
  if(!r||!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"requestId is required"})
3821
3899
  if(0===s.length&&!n)return void t.json({success:!0,data:{started:!1,reason:"no commands were named and no page is being served"}})
3822
- const i=(await(0,L.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(async()=>{const{runs:e,pages:t}=await(0,W.runShowing)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,i,s,{page:n}),c=o.getProjectId()
3900
+ const i=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(async()=>{const{runs:e,pages:t}=await(0,G.runShowing)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,i,s,{page:n}),c=o.getProjectId()
3823
3901
  if(!c)return
3824
3902
  const l=(0,m.randomUUID)()
3825
- for(const s of[...t.map(e=>(0,W.pageRecord)(r,e)),...e.map(e=>(0,W.showingRecord)(r,e))]){const e={...s,metadata:{...s.metadata,showing:l}}
3903
+ for(const s of[...t.map(e=>(0,G.pageRecord)(r,e)),...e.map(e=>(0,G.showingRecord)(r,e))]){const e={...s,metadata:{...s.metadata,showing:l}}
3826
3904
  await o.apiClient.post(`/api/projects/${c}/tool-records`,e).catch(e=>o.logger.warn("showing: a run or a picture was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})().catch(e=>o.logger.warn("showing runs failed",{requestId:r,error:(0,P.getErrorMessage)(e)})),t.json({success:!0,data:{started:!0,sha:i,commands:s.length,page:n?n.dir:null}})}catch(e){o.logger.warn("showing runs could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{started:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/baseline-measure",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(Array.isArray(e.body?.baselines)?e.body.baselines:[]).filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command&&e.command.trim()&&e?.baseline&&"object"==typeof e.baseline).slice(0,5)
3827
3905
  if(!r||0===s.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty baselines array ({ criterionId, command, baseline }) are required"})
3828
- const n=(await(0,L.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(0,B.startPendingProof)(r,n,async()=>{const{measured:e}=await(0,H.measureBaselines)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,n,s),t=o.getProjectId()
3906
+ const n=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(0,H.startPendingProof)(r,n,async()=>{const{measured:e}=await(0,W.measureBaselines)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,n,s),t=o.getProjectId()
3829
3907
  t&&e.length>0&&await o.apiClient.post(`/api/projects/${t}/requests/${r}/proof-evidence`,{commitSha:n,proofs:e.map(e=>({criterionId:e.criterionId,command:e.command,ok:e.ok,...e.couldNotRun?{couldNotRun:!0}:{},output:e.words.slice(0,200),words:e.words,baseline:e.metric}))}).catch(e=>o.logger.warn("baseline measurement: evidence not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))
3830
3908
  const i=e.filter(e=>!e.ok&&!e.couldNotRun).length
3831
- return o.logger.info("baselines measured beside the sign-off check",{requestId:r,sha:n.slice(0,8),measured:e.length,regressed:i}),{sha:n,blocking:[],baselines:{measured:e.length,regressed:i}}},{kind:"baselines",waitMs:H.BASELINE_BUDGET_MS+18e4}),t.json({success:!0,data:{pending:!0,sha:n,baselines:s.length}})}catch(e){o.logger.warn("baseline measurement could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{pending:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/bring-main-in",async(e,t)=>{try{const s="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await r(o,s)
3909
+ return o.logger.info("baselines measured beside the sign-off check",{requestId:r,sha:n.slice(0,8),measured:e.length,regressed:i}),{sha:n,blocking:[],baselines:{measured:e.length,regressed:i}}},{kind:"baselines",waitMs:W.BASELINE_BUDGET_MS+18e4}),t.json({success:!0,data:{pending:!0,sha:n,baselines:s.length}})}catch(e){o.logger.warn("baseline measurement could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{pending:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/bring-main-in",async(e,t)=>{try{const s="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await r(o,s)
3832
3910
  if(!i||!n)return void t.json({success:!0,data:{outcome:"failed",text:"This session works on no request with a worktree, so there is no branch to bring main into."}})
3833
3911
  const a="string"==typeof e.body?.requestId?e.body.requestId:""
3834
3912
  if(a&&a!==i.requestId)return void t.json({success:!0,data:{outcome:"failed",text:`Main can be brought only into your own request's branch (${i.requestId}), not ${a}'s.`}})
3835
- const c=await(0,J.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree})
3913
+ const c=await(0,X.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree})
3836
3914
  t.json({success:!0,data:{requestId:i.requestId,...c}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.get("/api/catch-up-due",async(e,t)=>{try{const r=await s(o,"string"==typeof e.query.sessionId?e.query.sessionId:"")
3837
3915
  if(!r)return void t.json({success:!0,data:{due:!1}})
3838
- const i=await te.catchUpDue(o.projectPath,await(0,Y.resolveDefaultBranch)(o.projectPath),r.requestId)
3916
+ const i=await re.catchUpDue(o.projectPath,await(0,J.resolveDefaultBranch)(o.projectPath),r.requestId)
3839
3917
  t.json({success:!0,data:{due:i.due&&!n(r.requestId,i.files)}})}catch(e){o.logger.debug("catch-up-due failed (the tool runs as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{due:!1}})}}),e.post("/api/catch-up",async(e,t)=>{try{const r="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await s(o,r)
3840
3918
  if(!i||!n)return void t.json({success:!0,data:{outcome:"none"}})
3841
- const a=await te.catchUpDue(o.projectPath,await(0,Y.resolveDefaultBranch)(o.projectPath),i.requestId)
3919
+ const a=await re.catchUpDue(o.projectPath,await(0,J.resolveDefaultBranch)(o.projectPath),i.requestId)
3842
3920
  if(!a.due)return void t.json({success:!0,data:{outcome:"none"}})
3843
- const c=await(0,J.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree,catchUp:{files:a.files}})
3844
- if("held"===c.outcome||"failed"===c.outcome)return ee.set(i.requestId,{at:Date.now(),files:a.files.join("\n")}),"failed"===c.outcome&&o.logger.warn("main could not come in at the worker's tool call",{requestId:i.requestId,text:c.text}),void t.json({success:!0,data:{outcome:c.outcome}})
3921
+ const c=await(0,X.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree,catchUp:{files:a.files}})
3922
+ if("held"===c.outcome||"failed"===c.outcome)return te.set(i.requestId,{at:Date.now(),files:a.files.join("\n")}),"failed"===c.outcome&&o.logger.warn("main could not come in at the worker's tool call",{requestId:i.requestId,text:c.text}),void t.json({success:!0,data:{outcome:c.outcome}})
3845
3923
  if("merged"===c.outcome||"conflict"===c.outcome){const e=c.from?.length?` (from ${c.from.join(", ")})`:"",t="merged"===c.outcome?`Brought in main's changes to ${a.files.join(", ")}${e}.`:`Brought in main's changes to ${a.files.join(", ")}${e}. They conflict with this work in ${(c.conflictedFiles??[]).join(", ")||"those files"}; its worker is resolving that now.`
3846
3924
  await o.apiClient.post(`/api/projects/${n}/session-messages/reports`,{fromSessionId:r,status:"success",summary:t,reportKind:"progress",requestId:i.requestId}).catch(e=>o.logger.warn("main came in, but the line on the request was not posted",{requestId:i.requestId,error:(0,P.getErrorMessage)(e)}))}t.json({success:!0,data:{outcome:c.outcome,text:c.text}})}catch(e){o.logger.warn("catch-up failed (the tool runs as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{outcome:"failed"}})}}),e.post("/api/commit-worktree-leftovers",async(e,t)=>{try{const r="string"==typeof e.body?.requestId?e.body.requestId:void 0
3847
3925
  if(!r||!/^req[0-9]{4,10}$/.test(r))return void t.status(400).json({success:!1,error:"a request local id (reqNNNNNN) is required"})
3848
3926
  const s=(0,w.requestWorktreesDir)(o.projectPath),n=M.default.join(s,r)
3849
3927
  if(n!==M.default.join(s,M.default.basename(n))||!n.startsWith(s+M.default.sep))return void t.status(400).json({success:!1,error:"requestId must name one of the project's request worktrees"})
3850
- if(!await q.promises.stat(n).then(e=>e.isDirectory()).catch(()=>!1))return void t.json({success:!0,data:{committed:!1,reason:"no worktree"}})
3851
- const i=(0,L.simpleGit)(n)
3928
+ if(!await N.promises.stat(n).then(e=>e.isDirectory()).catch(()=>!1))return void t.json({success:!0,data:{committed:!1,reason:"no worktree"}})
3929
+ const i=(0,U.simpleGit)(n)
3852
3930
  await(0,x.assertGitNotUserFolder)(i,"commit of worktree leftovers")
3853
3931
  const a=await i.status()
3854
3932
  if(a.isClean())return void t.json({success:!0,data:{committed:!1,reason:"clean"}})
3855
3933
  const c=[...a.modified,...a.not_added,...a.created,...a.deleted]
3856
3934
  await i.add(["-A"])
3857
- const{note:l}=await(0,Q.holdBackStagedCredentials)(n)
3935
+ const{note:l}=await(0,Z.holdBackStagedCredentials)(n)
3858
3936
  l&&o.logger.warn("commit-worktree-leftovers held key material back",{requestId:r,note:l}),await i.commit(`chore: commit uncommitted worktree changes at completion (${r})${l?`\n\n${l}`:""}`)
3859
3937
  const d=(await i.revparse(["HEAD"])).trim()
3860
3938
  o.logger.info("commit-worktree-leftovers",{requestId:r,files:c.length,sha:d.slice(0,8)}),t.json({success:!0,data:{committed:!0,sha:d,files:c}})}catch(e){o.logger.warn("commit-worktree-leftovers failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{committed:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.get("/api/branch-head",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0,s=("string"==typeof e.query.branch?e.query.branch:void 0)||(r?(0,R.generateRequestBranchName)(r,""):void 0)
3861
3939
  if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
3862
- const n=(await(0,L.simpleGit)(o.projectPath).revparse([s])).trim(),i=(await(0,L.simpleGit)(o.projectPath).revparse([`${s}^{tree}`]).catch(()=>"")).trim()
3940
+ const n=(await(0,U.simpleGit)(o.projectPath).revparse([s])).trim(),i=(await(0,U.simpleGit)(o.projectPath).revparse([`${s}^{tree}`]).catch(()=>"")).trim()
3863
3941
  t.json({success:!0,data:{branch:s,sha:n,.../^[0-9a-f]{40}$/.test(i)?{tree:i}:{}}})}catch(e){o.logger.debug("branch-head lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{branch:null,sha:null}})}}),e.post("/api/release-pool-slot",async(e,t)=>{try{const{worktreePath:r}=e.body||{}
3864
3942
  if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"})
3865
3943
  let s=null
3866
3944
  try{const e=o.getProjectId(),t=e?await(0,v.readAllPages)(o.apiClient,t=>`/api/projects/${e}/test-runs?limit=100&offset=${t}`,500):void 0
3867
3945
  if(t){const e=e=>"string"==typeof e?(0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),e):null
3868
- s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,N.releaseSlot)((0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),r)),o.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0,released:!0})}catch(e){o.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,P.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}})}
3869
- const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(79896),N=t(60489),F=t(92456),L=t(83807),U=t(45738),B=t(65856),H=t(82167),W=t(99970),G=t(24106),K=t(73775),z=t(27584),V=t(3031),Y=t(52232),J=t(62040),X=t(32559),Q=t(96511),Z=new Map,ee=new Map,te=new T.ParallelNotes
3870
- let re=null
3871
- const se=e=>({id:String(e?.localId??e?.id??""),title:String(e?.title??""),status:String(e?.status??""),description:"string"==typeof e?.description?e.description:null})
3946
+ s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,F.releaseSlot)((0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),r)),o.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0,released:!0})}catch(e){o.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,P.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}})}
3947
+ const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(39175),N=t(79896),F=t(60489),L=t(92456),U=t(83807),B=t(45738),H=t(65856),W=t(82167),G=t(99970),K=t(24106),z=t(73775),V=t(27584),Y=t(3031),J=t(52232),X=t(62040),Q=t(32559),Z=t(96511),ee=new Map,te=new Map,re=new T.ParallelNotes
3948
+ let se=null
3949
+ const ne=e=>({id:String(e?.localId??e?.id??""),title:String(e?.title??""),status:String(e?.status??""),description:"string"==typeof e?.description?e.description:null})
3872
3950
  exports.COMPLETION_REPLAY_BUDGET_MS=15e4
3873
- const ne=12e4},60885(e,exports,t){"use strict"
3951
+ const oe=12e4},60885(e,exports,t){"use strict"
3874
3952
  exports.makeRunOwn=function(e,t){return async o=>{n||(n=new r.Agent({headersTimeout:0,bodyTimeout:0}))
3875
3953
  const i="number"==typeof o.timeoutMs?o.timeoutMs:12e4,a=`http://127.0.0.1:${e}/api/run`,c={"Content-Type":"application/json"},l=(0,s.localSecretForUrl)(a,t)
3876
3954
  return l&&(c[s.LOCAL_SECRET_HEADER]=l),(await(0,r.fetch)(a,{method:"POST",headers:c,body:JSON.stringify(o),signal:AbortSignal.timeout(Math.max(18e4,i+6e4)),dispatcher:n})).json()}}
@@ -4775,37 +4853,37 @@ let h=null
4775
4853
  const p=new Map},62366(e,exports,t){"use strict"
4776
4854
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
4777
4855
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
4778
- const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(52640),T=t(65232),x=t(95671),M=t(44192),R=t(14139),P=t(41152),O=r(t(16928)),D=r(t(79896)),q=r(t(70857)),N=t(5607),F=t(76634),L=t(31),U=t(90524),B=t(18984),H=t(58824),W=t(29869),G=t(67078),K=t(60070),z=t(37210),V=t(87227),Y=t(82928),J=t(76616),X=t(15047),Q=t(90629),Z=t(42298),ee=t(67563),te=t(41950),re=t(88087),se=t(1285),ne=t(20564),oe=Object.keys(N.actionHandlers).join(","),ie=t(6850),ae=t(58836),ce=t(25146),le=t(23411),de=t(43218),ue=t(81570),he=t(18815),pe=t(73613),fe=t(49779),me=t(76982),ge=t(53340),we=t(89063),ye=t(61292),ve=t(11017),be=t(28148),ke=t(18649),Se=t(64306),Ie=t(96685),$e=t(49816),je=t(35419),Ee=t(33521),Ae=t(56844),Ce=t(84352),_e=t(40713),Te=t(75361),xe=t(75167),Me=t(72188),Re=t(6688),Pe=t(39068),Oe=t(3435),De=t(48463),qe=t(60489),Ne=t(24106),Fe=t(81618),Le=t(80211),Ue=t(2566),Be=t(83807),He=t(92456),We=t(95040),Ge=t(60355),Ke=t(31026),ze=t(73782),Ve=t(53608),Ye=t(63484),Je=t(31851),Xe=t(84102),Qe=t(75979),Ze=t(79329),et=t(60833),tt=t(42593),rt=t(69706),st=t(11773),nt=t(20990),ot=t(45956),it=t(36799),at=t(86116),ct=t(13751),lt=t(13791),dt=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ut=65536
4779
- class ht{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment":"on your machine",i=(0,Ee.isStandingRulePattern)(s),a=`You chose "Always allow" on the approval card for \`${t.askedCommand.slice(0,300)}\`${t.approvalId?` (card ${t.approvalId})`:""}${t.sessionId?`, asked by agent session ${t.sessionId}`:""}, on ${t.at.replace("T"," ").slice(0,16)} UTC.`;(async()=>{(await(0,$e.getRegisteredAllows)(this.apiClient,e,r??null).catch(()=>[])).some(e=>("string"==typeof e?e:e.exact).trim()===s.trim())||(await this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"ok:card",command:(0,n.commandAsStored)(s),description:`${a} ${i?`It allows exactly this shape, where <file> is any one file inside the project, ${o}`:`This exact command runs ${o}`} without asking. Delete it or switch it off to be asked again.`,timeout:120,isActive:!0,exactMatch:!0,...r?{environmentId:r}:{}}),(0,$e.invalidateRegisteredAllows)(e))})().catch(e=>this.logger.warn("Failed to remember approved command",{error:String(e)}))}rememberJudgeApprovedCommand(e,t,r){const s=new Date(Date.now()+ht.JUDGE_APPROVAL_TTL_MS).toISOString()
4780
- this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,$e.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
4856
+ const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(39175),T=t(52640),x=t(65232),M=t(95671),R=t(44192),P=t(14139),O=t(41152),D=r(t(16928)),q=r(t(79896)),N=r(t(70857)),F=t(5607),L=t(76634),U=t(31),B=t(90524),H=t(18984),W=t(58824),G=t(29869),K=t(67078),z=t(60070),V=t(37210),Y=t(87227),J=t(82928),X=t(76616),Q=t(15047),Z=t(90629),ee=t(42298),te=t(67563),re=t(41950),se=t(88087),ne=t(1285),oe=t(20564),ie=Object.keys(F.actionHandlers).join(","),ae=t(6850),ce=t(58836),le=t(25146),de=t(23411),ue=t(43218),he=t(81570),pe=t(18815),fe=t(73613),me=t(49779),ge=t(76982),we=t(53340),ye=t(89063),ve=t(61292),be=t(11017),ke=t(28148),Se=t(18649),Ie=t(64306),$e=t(96685),je=t(49816),Ee=t(35419),Ae=t(33521),Ce=t(56844),_e=t(84352),Te=t(40713),xe=t(75361),Me=t(75167),Re=t(72188),Pe=t(6688),Oe=t(39068),De=t(3435),qe=t(48463),Ne=t(60489),Fe=t(24106),Le=t(81618),Ue=t(80211),Be=t(2566),He=t(83807),We=t(92456),Ge=t(95040),Ke=t(60355),ze=t(31026),Ve=t(73782),Ye=t(53608),Je=t(63484),Xe=t(31851),Qe=t(84102),Ze=t(75979),et=t(79329),tt=t(60833),rt=t(42593),st=t(69706),nt=t(11773),ot=t(20990),it=t(45956),at=t(36799),ct=t(86116),lt=t(13751),dt=t(13791),ut=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ht=65536
4857
+ class pt{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment":"on your machine",i=(0,Ae.isStandingRulePattern)(s),a=`You chose "Always allow" on the approval card for \`${t.askedCommand.slice(0,300)}\`${t.approvalId?` (card ${t.approvalId})`:""}${t.sessionId?`, asked by agent session ${t.sessionId}`:""}, on ${t.at.replace("T"," ").slice(0,16)} UTC.`;(async()=>{(await(0,je.getRegisteredAllows)(this.apiClient,e,r??null).catch(()=>[])).some(e=>("string"==typeof e?e:e.exact).trim()===s.trim())||(await this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"ok:card",command:(0,n.commandAsStored)(s),description:`${a} ${i?`It allows exactly this shape, where <file> is any one file inside the project, ${o}`:`This exact command runs ${o}`} without asking. Delete it or switch it off to be asked again.`,timeout:120,isActive:!0,exactMatch:!0,...r?{environmentId:r}:{}}),(0,je.invalidateRegisteredAllows)(e))})().catch(e=>this.logger.warn("Failed to remember approved command",{error:String(e)}))}rememberJudgeApprovedCommand(e,t,r){const s=new Date(Date.now()+pt.JUDGE_APPROVAL_TTL_MS).toISOString()
4858
+ this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,je.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
4781
4859
  if(!this.parkController?.noteServeDeath(`exit code=${e.code} signal=${e.signal}`))return void this.logger.info("OpenCode serve exit observed during managed lifecycle -- no action",{code:e.code,signal:e.signal,pid:e.pid})
4782
4860
  const t=Date.now()
4783
4861
  this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.defaultModelUnchosen=!1,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
4784
- return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,te.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.followingDefaultTo=null,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,rt.installServeAuthFetch)(),(0,te.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
4785
- const r=(0,P.createMetricsCollector)(!1)
4786
- if((0,st.setFleetServerKey)(t.projectPath?t.projectPath.split("/").pop():void 0),this.apiClient=new M.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3,serverKey:t.projectPath?t.projectPath.split("/").pop():void 0}),t.tokenManager){const e=t.tokenManager
4862
+ return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,re.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.followingDefaultTo=null,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,st.installServeAuthFetch)(),(0,re.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
4863
+ const r=(0,O.createMetricsCollector)(!1)
4864
+ if((0,nt.setFleetServerKey)(t.projectPath?t.projectPath.split("/").pop():void 0),this.apiClient=new R.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3,serverKey:t.projectPath?t.projectPath.split("/").pop():void 0}),t.tokenManager){const e=t.tokenManager
4787
4865
  this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
4788
- return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new B.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new z.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,w.default)(),r=e=>(0,Z.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
4866
+ return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new H.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new V.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,w.default)(),r=e=>(0,ee.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
4789
4867
  e.use((e,t,r)=>{const n=e.headers.host
4790
- if((0,Z.isLoopbackHost)(n))return r()
4791
- s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:Z.HOST_REFUSED_MESSAGE})}),e.use((0,Z.inertActiveContent)()),e.use((e,t,s)=>{const n=e.headers.origin
4792
- if(!r(n))return this.logger.warn("Refused a browser request from an origin this project does not know",{origin:n,path:e.path,method:e.method}),void t.status(403).json({success:!1,error:Z.ORIGIN_REFUSED_MESSAGE})
4793
- n&&t.setHeader("Access-Control-Allow-Private-Network","true"),s()}),e.use((0,v.default)({origin:(e,t)=>t(null,r(e)),credentials:!0,allowedHeaders:["Content-Type","Authorization","Accept","X-Alchemy-Project"],maxAge:600})),e.use((0,re.localSecretGate)({secret:this.localSecret,logger:this.logger}))
4794
- const n=(0,ee.makeTicketVerifier)({apiClient:this.apiClient,projectId:()=>this.projectId,logger:this.logger})
4795
- e.use((0,ne.browserTicketGate)({verifier:n,secret:this.localSecret,logger:this.logger})),e.use((0,y.default)({filter:(e,t)=>!e.headers.accept?.includes("text/event-stream")&&y.default.filter(e,t)})),e.use(w.default.json({limit:"10mb"}))
4868
+ if((0,ee.isLoopbackHost)(n))return r()
4869
+ s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:ee.HOST_REFUSED_MESSAGE})}),e.use((0,ee.inertActiveContent)()),e.use((e,t,s)=>{const n=e.headers.origin
4870
+ if(!r(n))return this.logger.warn("Refused a browser request from an origin this project does not know",{origin:n,path:e.path,method:e.method}),void t.status(403).json({success:!1,error:ee.ORIGIN_REFUSED_MESSAGE})
4871
+ n&&t.setHeader("Access-Control-Allow-Private-Network","true"),s()}),e.use((0,v.default)({origin:(e,t)=>t(null,r(e)),credentials:!0,allowedHeaders:["Content-Type","Authorization","Accept","X-Alchemy-Project"],maxAge:600})),e.use((0,se.localSecretGate)({secret:this.localSecret,logger:this.logger}))
4872
+ const n=(0,te.makeTicketVerifier)({apiClient:this.apiClient,projectId:()=>this.projectId,logger:this.logger})
4873
+ e.use((0,oe.browserTicketGate)({verifier:n,secret:this.localSecret,logger:this.logger})),e.use((0,y.default)({filter:(e,t)=>!e.headers.accept?.includes("text/event-stream")&&y.default.filter(e,t)})),e.use(w.default.json({limit:"10mb"}))
4796
4874
  const o=["/api/opencode","/api/spawn-","/api/setup-","/api/run-tests-now","/api/flesh-out-request","/sync-agents","/api/stuck-sessions/"],i=["/api/opencode/session","/api/opencode/global/health"]
4797
4875
  e.use((e,t,r)=>{const s="string"==typeof e.query.projectId?e.query.projectId:void 0
4798
4876
  if(!s)return r()
4799
4877
  const n=this.projectId
4800
- n&&s!==n?t.status(409).json({success:!1,error:`project mismatch: this action server serves ${n} (${O.default.basename(this.options.projectPath)}), not ${s}. Re-discover the action server for your project -- ports shuffle on restart.`,code:"PROJECT_MISMATCH",data:{servingProjectId:n,servingProject:O.default.basename(this.options.projectPath)}}):r()}),e.use((e,t,r)=>{const s=!0===e.headers.accept?.includes("text/event-stream")
4878
+ n&&s!==n?t.status(409).json({success:!1,error:`project mismatch: this action server serves ${n} (${D.default.basename(this.options.projectPath)}), not ${s}. Re-discover the action server for your project -- ports shuffle on restart.`,code:"PROJECT_MISMATCH",data:{servingProjectId:n,servingProject:D.default.basename(this.options.projectPath)}}):r()}),e.use((e,t,r)=>{const s=!0===e.headers.accept?.includes("text/event-stream")
4801
4879
  if("GET"===e.method&&i.some(t=>e.path===t||"/api/opencode/session"===t&&e.path===`${t}/`)||s&&e.path.startsWith("/api/opencode"))return"active"!==this.parkController?.getState()&&this.parkController?void t.status(503).json({success:!1,parked:!0,error:"Project is parked (idle); serve is stopped"}):void r()
4802
- o.some(t=>e.path.startsWith(t))?this.ensureServeAlive(`route:${e.path}`).then(()=>r()):r()}),e.get("/health",this.handleHealth.bind(this)),e.get("/api/read-token",(e,t)=>{t.setHeader("Cache-Control","no-store"),t.json({success:!0,data:(0,se.mintReadToken)(this.localSecret)})}),this.options.projectPath&&(0,b.registerAssetRoutes)(e,{projectPath:this.options.projectPath,getProjectId:()=>this.projectId??null,logger:this.logger}),e.post("/admin/shutdown",(e,t)=>{this.logger.warn("Retiring: a newer action server for this project requested takeover",{reason:e.body?.reason}),t.json({ok:!0,pid:process.pid}),setTimeout(()=>{try{process.kill(process.pid,"SIGTERM")}catch{process.exit(0)}},150)}),(0,Te.initGateLogPersistence)(this.options.projectPath),(0,xe.initGateModes)(this.options.projectPath),(0,Me.setActivityRequestResolver)(async e=>{const t=this.projectId
4880
+ o.some(t=>e.path.startsWith(t))?this.ensureServeAlive(`route:${e.path}`).then(()=>r()):r()}),e.get("/health",this.handleHealth.bind(this)),e.get("/api/read-token",(e,t)=>{t.setHeader("Cache-Control","no-store"),t.json({success:!0,data:(0,ne.mintReadToken)(this.localSecret)})}),this.options.projectPath&&(0,b.registerAssetRoutes)(e,{projectPath:this.options.projectPath,getProjectId:()=>this.projectId??null,logger:this.logger}),e.post("/admin/shutdown",(e,t)=>{this.logger.warn("Retiring: a newer action server for this project requested takeover",{reason:e.body?.reason}),t.json({ok:!0,pid:process.pid}),setTimeout(()=>{try{process.kill(process.pid,"SIGTERM")}catch{process.exit(0)}},150)}),(0,xe.initGateLogPersistence)(this.options.projectPath),(0,Me.initGateModes)(this.options.projectPath),(0,Re.setActivityRequestResolver)(async e=>{const t=this.projectId
4803
4881
  if(!t)throw new Error("no project bound")
4804
4882
  const r=`/api/projects/${t}/session-request-associations`
4805
4883
  let s=!1
4806
4884
  const n=e=>e&&"object"==typeof e&&"data"in e?e.data:e,o={getCurrentForSession:async e=>{try{const t=n(await this.apiClient.get(`${r}/current/${encodeURIComponent(e)}`))
4807
4885
  return null===t?null:t&&"string"==typeof t.requestLocalId?{requestLocalId:t.requestLocalId,ended:!0===t.ended,withdrawn:!0===t.withdrawn}:void 0}catch{return}},getActiveRequestsForSession:async e=>{try{const t=n(await this.apiClient.get(`${r}/by-session/${encodeURIComponent(e)}?activeOnly=true`))
4808
- return Array.isArray(t)?t:[]}catch(e){throw s=!0,e}}},i=await Re.sessionIdentity.getSessionRequestId({getCLISessionClient:()=>null,getSessionRequestAssociationClient:()=>o},this.logger,e)
4886
+ return Array.isArray(t)?t:[]}catch(e){throw s=!0,e}}},i=await Pe.sessionIdentity.getSessionRequestId({getCLISessionClient:()=>null,getSessionRequestAssociationClient:()=>o},this.logger,e)
4809
4887
  if(s)throw new Error("session request lookup failed")
4810
4888
  return i})
4811
4889
  const a=async e=>Promise.all((0,$.listEntries)().map(async t=>{let r,s="",n=0,o=t.finishedAt??t.startedAt
@@ -4814,24 +4892,24 @@ try{const e=await k.promises.stat(t.logPath)
4814
4892
  n=e.size,o=e.mtime.toISOString(),r=await k.promises.open(t.logPath,"r")
4815
4893
  const i=Math.max(0,n-16384),a=Buffer.alloc(n-i)
4816
4894
  await r.read(a,0,a.length,i),s=a.toString("utf-8").split("\n").slice(-50).join("\n")}catch{}finally{await(r?.close().catch(()=>{}))}return{...t,tail:s,sizeBytes:n,modifiedAt:o}}))
4817
- return e.get("/api/commands/running",async(e,t)=>{try{t.json({success:!0,data:await a("0"!==e.query.tail)})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/model-switch",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{pending:this.pendingConfigReload,pendingSince:this.pendingConfigReload&&this.pendingConfigReloadSince?this.pendingConfigReloadSince:null,lastReloadAt:this.lastConfigReloadAt||null,capMs:ht.RELOAD_DEFER_CAP_MS,busySessionIds:await this.busySessionIds()}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/envs/running",async(e,t)=>{try{if(t.setHeader("Cache-Control","no-cache"),!this.projectId)return void t.json({success:!0,data:{at:Date.now(),dockerAvailable:!1,hostCores:null,hostMemBytes:null,computerCores:q.default.cpus().length,computerMemBytes:q.default.totalmem(),envs:[]}})
4818
- t.json({success:!0,data:await(0,Le.runningEnvs)(this.projectId)})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/commands/activity",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{runs:await a("0"!==e.query.tail),gate:(0,Te.listGateLog)(200),experiments:this.projectId?(0,Ie.listExperiments)(this.projectId):[],execs:(0,W.listRunningExecs)(),previews:(0,C.listPreviewHandles)()}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/commands/start",async(e,t)=>{try{const{id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:l}=e.body??{}
4819
- if(!(r&&s&&n&&o&&a&&c))return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.addStart)({id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:"number"==typeof l&&l>0?l:void 0}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/commands/end",async(e,t)=>{try{const{id:r,exitCode:s,timedOut:n,finishedAt:o}=e.body??{}
4895
+ return e.get("/api/commands/running",async(e,t)=>{try{t.json({success:!0,data:await a("0"!==e.query.tail)})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/model-switch",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{pending:this.pendingConfigReload,pendingSince:this.pendingConfigReload&&this.pendingConfigReloadSince?this.pendingConfigReloadSince:null,lastReloadAt:this.lastConfigReloadAt||null,capMs:pt.RELOAD_DEFER_CAP_MS,busySessionIds:await this.busySessionIds()}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/envs/running",async(e,t)=>{try{if(t.setHeader("Cache-Control","no-cache"),!this.projectId)return void t.json({success:!0,data:{at:Date.now(),dockerAvailable:!1,hostCores:null,hostMemBytes:null,computerCores:N.default.cpus().length,computerMemBytes:N.default.totalmem(),envs:[]}})
4896
+ t.json({success:!0,data:await(0,Ue.runningEnvs)(this.projectId)})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/commands/activity",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{runs:await a("0"!==e.query.tail),gate:(0,xe.listGateLog)(200),experiments:this.projectId?(0,$e.listExperiments)(this.projectId):[],execs:(0,G.listRunningExecs)(),previews:(0,_.previewHandlesWithVersions)(this.options.projectPath)}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/commands/start",async(e,t)=>{try{const{id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:l}=e.body??{}
4897
+ if(!(r&&s&&n&&o&&a&&c))return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.addStart)({id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:"number"==typeof l&&l>0?l:void 0}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/commands/end",async(e,t)=>{try{const{id:r,exitCode:s,timedOut:n,finishedAt:o}=e.body??{}
4820
4898
  if(!r||"number"!=typeof s||!o)return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.markEnd)({id:r,exitCode:s,timedOut:!!n,finishedAt:o})
4821
4899
  try{const e=(0,$.listEntries)().find(e=>e.id===r)
4822
- if(e){const t=Date.parse(o)-Date.parse(e.startedAt);(0,Te.recordGate)({source:"command",sessionId:e.sessionId,...void 0!==e.requestId?{requestId:e.requestId}:{},environmentName:"Workspace",command:e.command,action:"auto",ruleId:"registered",exit:s,ms:Number.isFinite(t)?t:void 0})}}catch{}t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.post("/api/auth-renew",async(e,t)=>{const r=this.options.tokenManager
4900
+ if(e){const t=Date.parse(o)-Date.parse(e.startedAt);(0,xe.recordGate)({source:"command",sessionId:e.sessionId,...void 0!==e.requestId?{requestId:e.requestId}:{},environmentName:"Workspace",command:e.command,action:"auto",ruleId:"registered",exit:s,ms:Number.isFinite(t)?t:void 0})}}catch{}t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.post("/api/auth-renew",async(e,t)=>{const r=this.options.tokenManager
4823
4901
  if(r)try{const e=await r.renewIfDue(Number.POSITIVE_INFINITY).catch(()=>null)
4824
4902
  if(e?.token)return this.updateAuthToken(e.token),void t.json({success:!0,renewed:!0})
4825
4903
  const s=r.getAuthStatus()
4826
- if(s.isPendingAuth&&s.authUrl)return void t.json({success:!0,opened:(0,Q.openBrowser)(s.authUrl),alreadyPending:!0})
4904
+ if(s.isPendingAuth&&s.authUrl)return void t.json({success:!0,opened:(0,Z.openBrowser)(s.authUrl),alreadyPending:!0})
4827
4905
  const{authUrl:n}=await r.startAuthenticationFlow()
4828
- t.json({success:!0,opened:(0,Q.openBrowser)(n)})}catch(e){this.logger.error("auth renew failed to start",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}else t.status(400).json({success:!1,error:"No token manager on this server"})}),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/opencode/restart",async(e,t)=>{try{if(!this.relaunchOpenCode)return void t.status(409).json({success:!1,error:"no serve child to restart (serve-only mode)"})
4906
+ t.json({success:!0,opened:(0,Z.openBrowser)(n)})}catch(e){this.logger.error("auth renew failed to start",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}else t.status(400).json({success:!1,error:"No token manager on this server"})}),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/opencode/restart",async(e,t)=>{try{if(!this.relaunchOpenCode)return void t.status(409).json({success:!1,error:"no serve child to restart (serve-only mode)"})
4829
4907
  for(const e of this.executePark())this.managedExitPids.add(e)
4830
- await this.executeRevive(),t.json({success:!0,data:{port:this.openCodePort??null}})}catch(e){this.logger.error("Manual serve restart failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/opencode/revive",async(e,t)=>{try{if(!this.parkController||"active"===this.parkController.getState())return void t.json({success:!0,parkState:"active"})
4831
- await this.parkController.ensureAlive(),t.json({success:!0,parkState:this.parkController.getState()})}catch(e){this.logger.error("Manual serve revive failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.get("/api/commits/:sha/diff",async(e,t)=>{const r=String(e.params.sha||"")
4908
+ await this.executeRevive(),t.json({success:!0,data:{port:this.openCodePort??null}})}catch(e){this.logger.error("Manual serve restart failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/opencode/revive",async(e,t)=>{try{if(!this.parkController||"active"===this.parkController.getState())return void t.json({success:!0,parkState:"active"})
4909
+ await this.parkController.ensureAlive(),t.json({success:!0,parkState:this.parkController.getState()})}catch(e){this.logger.error("Manual serve revive failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.get("/api/commits/:sha/diff",async(e,t)=>{const r=String(e.params.sha||"")
4832
4910
  if(!/^[0-9a-f]{7,64}$/i.test(r))return void t.status(400).json({success:!1,error:"sha must be a git commit hash"})
4833
4911
  const s="string"!=typeof e.query.path||e.query.path.includes("..")?void 0:e.query.path
4834
- try{const e=(0,Be.simpleGit)(this.options.projectPath),n=524288,o=(await e.raw(["rev-list","--parents","-n","1",r])).trim().split(/\s+/).length>2,i=[...(0,g.documentDiffGitArgs)(),"show","--format=%H%n%an%n%ad%n%s","--patch",...o?["--first-parent","-m"]:[],r]
4912
+ try{const e=(0,He.simpleGit)(this.options.projectPath),n=524288,o=(await e.raw(["rev-list","--parents","-n","1",r])).trim().split(/\s+/).length>2,i=[...(0,g.documentDiffGitArgs)(),"show","--format=%H%n%an%n%ad%n%s","--patch",...o?["--first-parent","-m"]:[],r]
4835
4913
  s&&i.push("--",s)
4836
4914
  let a=await(0,g.documentDiffGit)(this.options.projectPath).raw(i)
4837
4915
  const c=a.length>n
@@ -4842,79 +4920,79 @@ try{const s=this.projectId
4842
4920
  if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
4843
4921
  let n=null
4844
4922
  try{const e=await this.apiClient.get(`/api/projects/${s}/requests/${r}`)
4845
- n=e?.data??e??null}catch{}const o=(0,Be.simpleGit)(this.options.projectPath),i=async()=>{try{const e=await this.apiClient.get(`/api/projects/${s}/tool-records?requestId=${encodeURIComponent(r)}&sortBy=created_at&sortDirection=asc&limit=100`),t=e?.data??e??[],n=e=>"string"==typeof e&&/^[0-9a-f]{7,64}$/i.test(e)
4846
- return(Array.isArray(t)?t:[]).filter(e=>n(e?.metadata?.commitHash)).map(e=>({commitSha:e.metadata.commitHash,previousSha:n(e?.metadata?.previousCommitHash)?e.metadata.previousCommitHash:void 0}))}catch{return}},{baseSha:a,headSha:c}=await(0,He.resolveRequestDiffRange)(o,r,{mergeBaseSha:n?.mergeBaseSha,mergeCommitSha:n?.mergeCommitSha,status:n?.status},i),l=await(0,We.requestDocumentEdits)(this.apiClient,s,r).catch(()=>[])
4923
+ n=e?.data??e??null}catch{}const o=(0,He.simpleGit)(this.options.projectPath),i=async()=>{try{const e=await this.apiClient.get(`/api/projects/${s}/tool-records?requestId=${encodeURIComponent(r)}&sortBy=created_at&sortDirection=asc&limit=100`),t=e?.data??e??[],n=e=>"string"==typeof e&&/^[0-9a-f]{7,64}$/i.test(e)
4924
+ return(Array.isArray(t)?t:[]).filter(e=>n(e?.metadata?.commitHash)).map(e=>({commitSha:e.metadata.commitHash,previousSha:n(e?.metadata?.previousCommitHash)?e.metadata.previousCommitHash:void 0}))}catch{return}},{baseSha:a,headSha:c}=await(0,We.resolveRequestDiffRange)(o,r,{mergeBaseSha:n?.mergeBaseSha,mergeCommitSha:n?.mergeCommitSha,status:n?.status},i),l=await(0,Ge.requestDocumentEdits)(this.apiClient,s,r).catch(()=>[])
4847
4925
  if(!(a&&c||0!==l.length))return void t.status(404).json({success:!1,error:`No diff range found for ${r}.`})
4848
- if("1"===e.query.stats){const e=a&&c?await(0,He.computeDiffStats)(o,a,c):void 0
4926
+ if("1"===e.query.stats){const e=a&&c?await(0,We.computeDiffStats)(o,a,c):void 0
4849
4927
  if(!e&&0===l.length)return void t.status(404).json({success:!1,error:`No diff stats for ${r}.`})
4850
- const s=(0,We.withDocumentStats)(e,l)
4851
- return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,We.withDocumentEdits)(u,l),p=h.length>d
4852
- t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),(0,Ke.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,ze.registerLocalModelRoutes)(e),(0,Qe.registerExhibitPreviewRoutes)(e),e.get("/api/publish-capabilities",async(e,t)=>{try{const e=await(0,F.getPublishCapabilities)(this.options.projectPath,(0,F.defaultRunner)())
4853
- t.json({success:!0,data:e})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/requests/:id/publish-pr",async(e,t)=>{const r=e.params.id
4928
+ const s=(0,Ge.withDocumentStats)(e,l)
4929
+ return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,Ge.withDocumentEdits)(u,l),p=h.length>d
4930
+ t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),(0,ze.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,Ve.registerLocalModelRoutes)(e),(0,Ze.registerExhibitPreviewRoutes)(e),e.get("/api/publish-capabilities",async(e,t)=>{try{const e=await(0,L.getPublishCapabilities)(this.options.projectPath,(0,L.defaultRunner)())
4931
+ t.json({success:!0,data:e})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/requests/:id/publish-pr",async(e,t)=>{const r=e.params.id
4854
4932
  try{const s=this.projectId
4855
4933
  if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
4856
4934
  const{base:n,draft:o}=e.body??{},i=()=>{const e={"X-Client-Type":"alchemy-local-service"},t=this.apiClient.authToken||this.options.authToken
4857
- return t&&(e.Authorization=`Bearer ${t}`),e},a=(e.body??{}).mode??"pr",c={run:(0,F.defaultRunner)(),fetchRequest:async e=>{const t=await this.apiClient.get(`/api/projects/${s}/requests/${e}`)
4935
+ return t&&(e.Authorization=`Bearer ${t}`),e},a=(e.body??{}).mode??"pr",c={run:(0,L.defaultRunner)(),fetchRequest:async e=>{const t=await this.apiClient.get(`/api/projects/${s}/requests/${e}`)
4858
4936
  return t?.data??t??null},fetchReceiptMarkdown:async e=>{const t=await fetch(`${this.options.remoteServerUrl}/api/projects/${s}/requests/${e}/receipt?format=md`,{headers:i()})
4859
4937
  if(!t.ok)throw new Error(`Receipt fetch failed: HTTP ${t.status}`)
4860
4938
  return await t.text()}},l={},d=[]
4861
- if("pr"===a||"both"===a)try{l.pr=await(0,F.publishRequestPr)({projectPath:this.options.projectPath,requestLocalId:r,base:n,draft:o},c)}catch(e){d.push(`pr: ${(0,H.getErrorMessage)(e)}`)}if("notes"===a||"both"===a)try{l.note=await(0,F.publishReceiptNote)({projectPath:this.options.projectPath,requestLocalId:r},c)}catch(e){d.push(`notes: ${(0,H.getErrorMessage)(e)}`)}if(!l.pr&&!l.note)return void t.status(500).json({success:!1,error:d.join("; ")||"nothing published"})
4862
- d.length&&(l.warnings=d),t.json({success:!0,data:l})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),(0,Ge.registerHooksRoutes)(e,{apiClient:this.apiClient,logger:this.logger,getProjectId:()=>this.projectId}),e.get("/api/project-settings",async(e,t)=>{Date.now()-this.lastSettingsSyncAt>2e3&&(this.lastSettingsSyncAt=Date.now(),await this.syncProjectSettings().catch(()=>{})),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null,receipts:this.cachedReceipts,integrations:this.cachedIntegrations}})}),e.get("/api/agents/markdown",(e,t)=>{t.json({success:!0,data:this.configurer.getAgentMarkdowns()})}),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/after-compaction",async(e,t)=>{try{const r=this.projectId
4939
+ if("pr"===a||"both"===a)try{l.pr=await(0,L.publishRequestPr)({projectPath:this.options.projectPath,requestLocalId:r,base:n,draft:o},c)}catch(e){d.push(`pr: ${(0,W.getErrorMessage)(e)}`)}if("notes"===a||"both"===a)try{l.note=await(0,L.publishReceiptNote)({projectPath:this.options.projectPath,requestLocalId:r},c)}catch(e){d.push(`notes: ${(0,W.getErrorMessage)(e)}`)}if(!l.pr&&!l.note)return void t.status(500).json({success:!1,error:d.join("; ")||"nothing published"})
4940
+ d.length&&(l.warnings=d),t.json({success:!0,data:l})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),(0,Ke.registerHooksRoutes)(e,{apiClient:this.apiClient,logger:this.logger,getProjectId:()=>this.projectId}),e.get("/api/project-settings",async(e,t)=>{Date.now()-this.lastSettingsSyncAt>2e3&&(this.lastSettingsSyncAt=Date.now(),await this.syncProjectSettings().catch(()=>{})),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null,receipts:this.cachedReceipts,integrations:this.cachedIntegrations}})}),e.get("/api/agents/markdown",(e,t)=>{t.json({success:!0,data:this.configurer.getAgentMarkdowns()})}),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/after-compaction",async(e,t)=>{try{const r=this.projectId
4863
4941
  if(!r)return void t.json({success:!0,data:{text:""}})
4864
4942
  const s=await(0,p.afterCompactionBlock)({apiClient:this.apiClient,projectId:r,projectPath:this.options.projectPath},String(e.params.sessionId||""))
4865
4943
  t.json({success:!0,data:{text:s}})}catch(e){t.json({success:!0,data:{text:""}})}}),e.post("/api/sessions/:sessionId/deliver",async(e,r)=>{try{const s=String(e.params.sessionId||""),{message:n,kind:o,partPayload:i,noVerify:a,verifySeconds:c}=e.body||{}
4866
4944
  if(!s||"string"!=typeof n||!n.trim())return void r.status(400).json({success:!1,error:"sessionId and a non-empty message are required"});(0,u.wakeSession)(s)
4867
4945
  const{deliverPrompt:l}=await Promise.resolve().then(()=>t(17039)),d="worker-report"===o&&(0,h.isQuietReporter)(i?.fromSessionId)||"report-acknowledged"===o&&!0===i?.quiet,p=await l({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},s,n,{kind:o,partPayload:i,noVerify:!!a,...d?{noReply:d}:{},..."number"==typeof c?{verifySeconds:c}:{}})
4868
- p.ok||this.logger.warn("deliver door: delivery failed",{sessionId:s,kind:o,error:p.error,detail:p.detail}),r.json({success:!0,data:p})}catch(e){(0,Oe.respondWithError)(r,this.logger,"Deliver to session",e)}}),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",async(e,t)=>{try{const{sessionId:r}=e.params,s=this.projectId
4946
+ p.ok||this.logger.warn("deliver door: delivery failed",{sessionId:s,kind:o,error:p.error,detail:p.detail}),r.json({success:!0,data:p})}catch(e){(0,De.respondWithError)(r,this.logger,"Deliver to session",e)}}),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",async(e,t)=>{try{const{sessionId:r}=e.params,s=this.projectId
4869
4947
  if(!s)return void t.status(400).json({success:!1,error:"No project"})
4870
4948
  const n=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),o=(n?.data||n||[]).map(e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))
4871
- t.json({success:!0,ancestors:o})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/registry/projects",async(e,r)=>{try{const{listLive:e}=await Promise.resolve().then(()=>t(52640))
4872
- r.json({success:!0,data:e()})}catch(e){r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/mcp/tool-call-event",(e,t)=>{const{action:r,callId:s,sessionId:n,requestId:o,tool:i}=e.body||{}
4949
+ t.json({success:!0,ancestors:o})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/registry/projects",async(e,r)=>{try{const{listLive:e}=await Promise.resolve().then(()=>t(52640))
4950
+ r.json({success:!0,data:e()})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/tool-call-event",(e,t)=>{const{action:r,callId:s,sessionId:n,requestId:o,tool:i}=e.body||{}
4873
4951
  if("note-nudge"===r)return this.logger.info("note nudge appended",{tool:i??null,sessionId:n??null,requestId:o??null}),void t.json({success:!0})
4874
4952
  !s||"start"!==r&&"end"!==r?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(r,String(s)),t.json({success:!0}))}),e.post("/api/results/spill",async(e,r)=>{try{const{text:s,toolName:n,callId:o}=e.body??{}
4875
4953
  if("string"!=typeof s||!s)return void r.status(400).json({success:!1,error:"text is required"})
4876
4954
  const i=await Promise.resolve().then(()=>t(13080)),a=await i.putBuffer(this.options.projectPath,s)
4877
4955
  if(this.projectId){const{recordIdForCall:e}=await t.e(111).then(()=>t(56111)),r=e("string"==typeof o?o:void 0)
4878
- this.apiClient.post(`/api/projects/${this.projectId}/results`,{address:a.address,locator:a.locator,bytes:a.bytes,name:`${String(n??"tool")}-result`,mediaType:"text/plain",...r?{producedBy:r}:{},summary:{characters:s.length,head:s.slice(0,400),heldBackFromContext:!0},producerNote:`Result of ${String(n??"a tool")}, too large for context`}).catch(e=>this.logger.warn("Spilled artifact could not be recorded",{error:(0,H.getErrorMessage)(e)}))}this.logger.info("Oversized tool result spilled to the result store",{tool:n,bytes:a.bytes,deduped:a.deduped}),r.json({success:!0,data:a})}catch(e){this.logger.warn("Could not spill an oversized tool result",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/workflow-policy/reload",async(e,t)=>{try{if(!this.reconfigureOpenCode)return void t.status(503).json({success:!1,error:"This action server cannot reconfigure itself (serve-only mode). The change applies on the next start."})
4956
+ this.apiClient.post(`/api/projects/${this.projectId}/results`,{address:a.address,locator:a.locator,bytes:a.bytes,name:`${String(n??"tool")}-result`,mediaType:"text/plain",...r?{producedBy:r}:{},summary:{characters:s.length,head:s.slice(0,400),heldBackFromContext:!0},producerNote:`Result of ${String(n??"a tool")}, too large for context`}).catch(e=>this.logger.warn("Spilled artifact could not be recorded",{error:(0,W.getErrorMessage)(e)}))}this.logger.info("Oversized tool result spilled to the result store",{tool:n,bytes:a.bytes,deduped:a.deduped}),r.json({success:!0,data:a})}catch(e){this.logger.warn("Could not spill an oversized tool result",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/workflow-policy/reload",async(e,t)=>{try{if(!this.reconfigureOpenCode)return void t.status(503).json({success:!1,error:"This action server cannot reconfigure itself (serve-only mode). The change applies on the next start."})
4879
4957
  const e=await this.reconfigureOpenCode()
4880
4958
  if(!e.success)return void t.status(500).json({success:!1,error:e.error??"reconfigure failed"})
4881
- this.requestConfigReload("workflow-policy"),this.logger.info("Workflow tool policy pushed to the running project"),t.json({success:!0,data:{reconfigured:!0}})}catch(e){this.logger.error("Workflow policy reload failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/mcp/reload",async(e,r)=>{try{const s=String(e.body?.scope||"all"),{reloadOwnMcp:n,syncMcpConnections:o,syncMcpGrants:i}=await Promise.resolve().then(()=>t.t(73613,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729))
4959
+ this.requestConfigReload("workflow-policy"),this.logger.info("Workflow tool policy pushed to the running project"),t.json({success:!0,data:{reconfigured:!0}})}catch(e){this.logger.error("Workflow policy reload failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/reload",async(e,r)=>{try{const s=String(e.body?.scope||"all"),{reloadOwnMcp:n,syncMcpConnections:o,syncMcpGrants:i}=await Promise.resolve().then(()=>t.t(73613,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729))
4882
4960
  a()
4883
4961
  const c={}
4884
- "all"!==s&&"own"!==s||(c.own=await n({projectPath:this.options.projectPath,logger:this.logger})),"all"!==s&&"connections"!==s||!this.projectId||(c.connections=await o({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")}})),"grants"===s&&this.projectId&&(c.grants=await i({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-grants")}})),r.json({success:!0,data:c})}catch(e){this.logger.error("MCP reload failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/mcp/secrets/:name",async(e,r)=>{try{const{heldSecretKeys:s}=await Promise.resolve().then(()=>t.t(59373,23))
4885
- r.json({success:!0,data:{keys:s(this.options.projectPath,String(e.params.name))}})}catch(e){r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/mcp/secrets/:name",async(e,r)=>{try{const{key:s,value:n}=e.body||{}
4962
+ "all"!==s&&"own"!==s||(c.own=await n({projectPath:this.options.projectPath,logger:this.logger})),"all"!==s&&"connections"!==s||!this.projectId||(c.connections=await o({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")}})),"grants"===s&&this.projectId&&(c.grants=await i({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-grants")}})),r.json({success:!0,data:c})}catch(e){this.logger.error("MCP reload failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/mcp/secrets/:name",async(e,r)=>{try{const{heldSecretKeys:s}=await Promise.resolve().then(()=>t.t(59373,23))
4963
+ r.json({success:!0,data:{keys:s(this.options.projectPath,String(e.params.name))}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/secrets/:name",async(e,r)=>{try{const{key:s,value:n}=e.body||{}
4886
4964
  if(!s||"string"!=typeof s)return void r.status(400).json({success:!1,error:"key is required"})
4887
4965
  if("string"!=typeof n||!n)return void r.status(400).json({success:!1,error:"value is required"})
4888
4966
  const{setSecret:o,heldSecretKeys:i}=await Promise.resolve().then(()=>t.t(59373,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729)),c=String(e.params.name)
4889
4967
  if(o(this.options.projectPath,c,s,n),a(),this.logger.info("MCP secret stored on this host",{connection:c,key:s}),this.projectId){const{syncMcpConnections:e}=await Promise.resolve().then(()=>t.t(73613,23))
4890
- e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-secret-set")}})}r.json({success:!0,data:{keys:i(this.options.projectPath,c)}})}catch(e){r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.delete("/api/mcp/secrets/:name/:key",async(e,r)=>{try{const{clearSecret:s,heldSecretKeys:n}=await Promise.resolve().then(()=>t.t(59373,23)),o=String(e.params.name)
4891
- s(this.options.projectPath,o,String(e.params.key)),r.json({success:!0,data:{keys:n(this.options.projectPath,o)}})}catch(e){r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.get("/api/mcp/namespaces",async(e,r)=>{try{if(!this.projectId)return void r.json({success:!0,data:{namespaces:[]}})
4968
+ e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-secret-set")}})}r.json({success:!0,data:{keys:i(this.options.projectPath,c)}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.delete("/api/mcp/secrets/:name/:key",async(e,r)=>{try{const{clearSecret:s,heldSecretKeys:n}=await Promise.resolve().then(()=>t.t(59373,23)),o=String(e.params.name)
4969
+ s(this.options.projectPath,o,String(e.params.key)),r.json({success:!0,data:{keys:n(this.options.projectPath,o)}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/mcp/namespaces",async(e,r)=>{try{if(!this.projectId)return void r.json({success:!0,data:{namespaces:[]}})
4892
4970
  const{getRegisteredMcpServers:e,gatedNamespaces:s}=await t.e(729).then(()=>t(92729)),n=await e({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger})
4893
- r.json({success:!0,data:{namespaces:s(n)}})}catch(e){this.logger.error("MCP namespaces lookup failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/mcp/gate",async(e,r)=>{try{const{toolName:s,sessionId:n,args:o}=e.body||{}
4971
+ r.json({success:!0,data:{namespaces:s(n)}})}catch(e){this.logger.error("MCP namespaces lookup failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/gate",async(e,r)=>{try{const{toolName:s,sessionId:n,args:o}=e.body||{}
4894
4972
  if(!s)return void r.status(400).json({success:!1,error:"toolName is required"})
4895
4973
  if(!this.projectId)return void r.json({success:!0,data:{allow:!0,verdict:"not-external",reason:"No project"}})
4896
4974
  const{gateMcpCall:i}=await t.e(729).then(()=>t(92729)),a=await i({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{toolName:String(s),sessionId:n?String(n):void 0,args:o})
4897
4975
  if(a.allow||this.logger.info("MCP call refused at the gate",{toolName:s,verdict:a.verdict}),"not-external"!==a.verdict){const{getRegisteredMcpServers:r,gatedNamespaces:i,splitToolKey:c,summariseArgs:l}=await t.e(729).then(()=>t(92729)),{openMcpReceipt:d}=await t.e(111).then(()=>t(56111)),u=await r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}),h=c(String(s),i(u))
4898
- h&&await d({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{connection:h.server,tool:h.tool,target:l(o),sessionId:n?String(n):void 0,callId:e.body?.callId?String(e.body.callId):void 0,verdict:a.verdict,allowed:a.allow,reason:a.reason})}r.json({success:!0,data:a})}catch(e){this.logger.error("MCP gate failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/hook-event",async(e,r)=>{try{const{eventType:s,sessionId:n,variables:o,timing:i}=e.body
4976
+ h&&await d({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{connection:h.server,tool:h.tool,target:l(o),sessionId:n?String(n):void 0,callId:e.body?.callId?String(e.body.callId):void 0,verdict:a.verdict,allowed:a.allow,reason:a.reason})}r.json({success:!0,data:a})}catch(e){this.logger.error("MCP gate failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/hook-event",async(e,r)=>{try{const{eventType:s,sessionId:n,variables:o,timing:i}=e.body
4899
4977
  if(!s)return void r.status(400).json({success:!1,error:"eventType is required"})
4900
4978
  if("session_idle"===s&&n){const{notifyInboxChatSessionIdle:e}=await Promise.resolve().then(()=>t.t(86116,23))
4901
4979
  e(String(n))
4902
4980
  const{notifyPlannerHandoffSessionIdle:r}=await t.e(991).then(()=>t(18991))
4903
4981
  r(String(n))}const a=await this.hookExecutor.processEvent({eventType:s,sessionId:n||"",variables:o||{},timing:i})
4904
- r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/tool-event",async(e,r)=>{try{const{toolName:s,sessionId:n,input:o,output:i,timing:a}=e.body||{}
4982
+ r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/tool-event",async(e,r)=>{try{const{toolName:s,sessionId:n,input:o,output:i,timing:a}=e.body||{}
4905
4983
  if(!s||"before"!==a&&"after"!==a)return void r.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
4906
4984
  if(n){const{notifyPlannerHandoffToolEvent:e}=await t.e(991).then(()=>t(18991))
4907
4985
  e(String(n),String(s),o)}if("after"===a&&e.body?.callId&&this.projectId){const{closeMcpReceipt:r}=await t.e(111).then(()=>t(56111))
4908
- r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,V.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
4986
+ r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,Y.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
4909
4987
  let l=0
4910
4988
  for(const e of c)l+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:n||"",variables:e.variables,timing:e.timing})).triggered
4911
- this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/web-call",async(e,r)=>{try{const{tool:s,sessionId:n,callId:o,input:i,metadata:a,error:c}=e.body||{}
4989
+ this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/web-call",async(e,r)=>{try{const{tool:s,sessionId:n,callId:o,input:i,metadata:a,error:c}=e.body||{}
4912
4990
  if("string"!=typeof s||!this.projectId)return void r.status(400).json({success:!1,error:"tool is required"})
4913
4991
  const{writeWebReceipt:l}=await t.e(9).then(()=>t(82009)),d=await l({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{tool:s,sessionId:n?String(n):void 0,callId:o?String(o):void 0,input:i,metadata:a,error:"string"==typeof c?c:void 0})
4914
- r.json({success:!0,data:{recorded:d}})}catch(e){r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/spawn-planner",this.handleSpawnPlanner.bind(this)),e.post("/api/setup-run",this.handleSetupRun.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.post("/api/main-change-at-landing",async(e,r)=>{try{const{requestId:s,landingSha:n}=e.body??{}
4992
+ r.json({success:!0,data:{recorded:d}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/spawn-planner",this.handleSpawnPlanner.bind(this)),e.post("/api/setup-run",this.handleSetupRun.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.post("/api/main-change-at-landing",async(e,r)=>{try{const{requestId:s,landingSha:n}=e.body??{}
4915
4993
  if(!s||!n||!this.projectId)return void r.status(400).json({success:!1,error:"requestId and landingSha are required"})
4916
4994
  const{mainChangeAtLanding:o}=await Promise.resolve().then(()=>t(69689)),i=await o({projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient},s,n)
4917
- r.json({success:!0,data:i})}catch(e){r.json({success:!0,data:null,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/flesh-out-request",async(e,t)=>{try{if(!(0,Oe.validateRequest)(e,t,this.projectId,["requestId"]))return
4995
+ r.json({success:!0,data:i})}catch(e){r.json({success:!0,data:null,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/flesh-out-request",async(e,t)=>{try{if(!(0,De.validateRequest)(e,t,this.projectId,["requestId"]))return
4918
4996
  const r=this.projectId
4919
4997
  if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
4920
4998
  const{requestId:s,sourceSessionId:n}=e.body||{}
@@ -4922,87 +5000,87 @@ let o=""
4922
5000
  try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${s}`)
4923
5001
  o=(e?.data||e)?.title||""}catch{}let i=""
4924
5002
  if(n)try{const e=this.getOpenCodeUrl()
4925
- e&&(i=(await(0,tt.collectMessages)(e,n,this.logger)).filter(e=>e.text&&("user"===e.role||"assistant"===e.role)).slice(-8).map(e=>`${e.role}: ${e.text}`).join("\n").slice(0,4e3))}catch{}const a=["[Planner Flavor: request-shaping] You are fleshing out a DRAFT request in the background (no user is watching this session). This is a scoped single-request job: no plan document, no decomposition \u2014 do not use plan_write.",`Draft request id: ${s}${o?` | one-liner: "${o}"`:""}.`,i?`\nContext from the session that requested this:\n${i}\n`:"","Author ONE focused request:","1. Read the draft with get_request_information.","2. Using the one-liner (and any context above) plus a quick look at the project for grounding, write a clear DESCRIPTION and 1-5 specific, testable ACCEPTANCE CRITERIA (phrased so a confined test run could demonstrate them; any content or decision that must come from the user becomes its own criterion carrying the literal marker [user-input]).","3. Apply them to the draft with request_operations (update). Keep the title unless it is unclear. Do NOT register it and do NOT change its status; it stays a draft for the user to review and register.","4. BOUNDARY: if this is bigger than one focused request (several independent pieces, open-ended), do NOT cram it into one. Leave a short note in the description recommending a quest or a split, and stop.","Work autonomously from the information you have; do not wait for user input. When done, end your session."].filter(Boolean).join("\n"),c=await(0,be.spawnActionAgent)({agent:"planner",prompt:a,cwd:this.options.projectPath,projectPath:this.options.projectPath,projectId:r,title:o?`Flesh out: ${o.slice(0,60)}`:"Flesh out draft request",parentSessionId:n,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0,skipWorktree:!0,shapingRequestLocalId:s})
5003
+ e&&(i=(await(0,rt.collectMessages)(e,n,this.logger)).filter(e=>e.text&&("user"===e.role||"assistant"===e.role)).slice(-8).map(e=>`${e.role}: ${e.text}`).join("\n").slice(0,4e3))}catch{}const a=["[Planner Flavor: request-shaping] You are fleshing out a DRAFT request in the background (no user is watching this session). This is a scoped single-request job: no plan document, no decomposition \u2014 do not use plan_write.",`Draft request id: ${s}${o?` | one-liner: "${o}"`:""}.`,i?`\nContext from the session that requested this:\n${i}\n`:"","Author ONE focused request:","1. Read the draft with get_request_information.","2. Using the one-liner (and any context above) plus a quick look at the project for grounding, write a clear DESCRIPTION and 1-5 specific, testable ACCEPTANCE CRITERIA (phrased so a confined test run could demonstrate them; any content or decision that must come from the user becomes its own criterion carrying the literal marker [user-input]).","3. Apply them to the draft with request_operations (update). Keep the title unless it is unclear. Do NOT register it and do NOT change its status; it stays a draft for the user to review and register.","4. BOUNDARY: if this is bigger than one focused request (several independent pieces, open-ended), do NOT cram it into one. Leave a short note in the description recommending a quest or a split, and stop.","Work autonomously from the information you have; do not wait for user input. When done, end your session."].filter(Boolean).join("\n"),c=await(0,ke.spawnActionAgent)({agent:"planner",prompt:a,cwd:this.options.projectPath,projectPath:this.options.projectPath,projectId:r,title:o?`Flesh out: ${o.slice(0,60)}`:"Flesh out draft request",parentSessionId:n,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0,skipWorktree:!0,shapingRequestLocalId:s})
4926
5004
  if(!c.success||!c.sessionId)return void t.status(500).json({success:!1,error:c.error||"Failed to spawn request-shaping planner"})
4927
- t.json({success:!0,data:{sessionId:c.sessionId,requestId:s}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Flesh out request",e)}}),e.post("/api/environments/verify",async(e,t)=>{const{config:r,smoke:s,branchName:n,requestId:o}=e.body||{}
5005
+ t.json({success:!0,data:{sessionId:c.sessionId,requestId:s}})}catch(e){(0,De.respondWithError)(t,this.logger,"Flesh out request",e)}}),e.post("/api/environments/verify",async(e,t)=>{const{config:r,smoke:s,branchName:n,requestId:o}=e.body||{}
4928
5006
  if(!r||"object"!=typeof r)return void t.status(400).json({success:!1,error:"config (environment config) is required"})
4929
5007
  const i="string"==typeof s&&s.trim()?s.trim():"true",a=this.options.projectPath,c=this.projectId
4930
5008
  if(!c)return void t.status(400).json({success:!1,error:"project not registered"})
4931
5009
  const l={composeFile:r.composeFile,gateImage:r.image,connEnv:r.connEnv,install:r.install,artifacts:r.artifacts,shellInit:r.shellInit,projectId:c}
4932
5010
  let d
4933
- try{await(0,De.sweepStaleSetupWorktrees)(a,this.logger)
4934
- const e=await(0,De.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,De.generateSetupId)()
4935
- d=O.default.join((0,De.getSetupWorktreesBase)(a),`verify-${s}`),await(0,De.createSetupWorktree)(a,d,e,this.logger)
4936
- const u=await(0,Fe.withContainerLabels)({"alchemy.purpose":"verify","alchemy.request":"string"==typeof o?o:void 0},()=>(0,Ne.runConfinedGate)(d,[{name:"verify",command:i,required:!0,timeout:600}],l,{projectName:`verify-${s}`})),h=!String(r.shellInit??"").trim()&&(0,Ue.carriesOwnShellInit)(i),p=h?["The smoke command sources its own setup, and the environment declares no shellInit. This verify proves the smoke command, not the environment: docker exec runs no ENTRYPOINT and reads no login profile, so every LATER command gets a bare PATH. Move the preamble into config.shellInit and verify again with the command alone."]:[]
4937
- h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}finally{if(d&&D.default.existsSync(d))try{await(0,Be.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,Ve.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Xe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Je.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
5011
+ try{await(0,qe.sweepStaleSetupWorktrees)(a,this.logger)
5012
+ const e=await(0,qe.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,qe.generateSetupId)()
5013
+ d=D.default.join((0,qe.getSetupWorktreesBase)(a),`verify-${s}`),await(0,qe.createSetupWorktree)(a,d,e,this.logger)
5014
+ const u=await(0,Le.withContainerLabels)({"alchemy.purpose":"verify","alchemy.request":"string"==typeof o?o:void 0},()=>(0,Fe.runConfinedGate)(d,[{name:"verify",command:i,required:!0,timeout:600}],l,{projectName:`verify-${s}`})),h=!String(r.shellInit??"").trim()&&(0,Be.carriesOwnShellInit)(i),p=h?["The smoke command sources its own setup, and the environment declares no shellInit. This verify proves the smoke command, not the environment: docker exec runs no ENTRYPOINT and reads no login profile, so every LATER command gets a bare PATH. Move the preamble into config.shellInit and verify again with the command alone."]:[]
5015
+ h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}finally{if(d&&q.default.existsSync(d))try{await(0,He.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,Ye.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Qe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Xe.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
4938
5016
  if(!t||!/^[A-Za-z0-9_-]+$/.test(e))return null
4939
5017
  const r=await this.apiClient.get(`/api/projects/${t}/documents/${e}`).catch(()=>null),s=r?.data??r
4940
- return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,Ye.registerWorkspaceRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath}),e.get("/api/stuck-sessions",this.handleGetStuckSessions.bind(this)),e.post("/api/stuck-sessions/test-inject",this.handleTestInjectStuckSession.bind(this)),e.post("/api/stuck-sessions/:sessionId/retry",this.handleRetryStuckSession.bind(this)),e.get("/api/version-warnings",(e,t)=>{t.json({success:!0,data:this.versionWarnings})}),e.post("/api/self-update",async(e,r)=>{try{const{execSync:e}=await Promise.resolve().then(()=>t.t(35317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
4941
- try{D.default.accessSync(s,D.default.constants.W_OK)}catch{return void r.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:n}=await Promise.resolve().then(()=>t.t(35317,23))
4942
- r.json({success:!0,message:"Update started. The CLI will restart when complete."}),n("npm install -g alchemylab@latest",{timeout:12e4},(e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout(()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)},1e3))})}catch(e){r.json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.use("/api/opencode",(e,t)=>this.handleOpenCodeProxy(e,t)),e}handleHealth(e,t){if(!0!==t.locals.provenCaller){const e=this.remoteFailures>0&&this.isPolling
4943
- return void t.json({status:e?"degraded":"healthy",service:"alchemylab-action-server"})}const r=this.remoteLastOkAt?Date.now()-this.remoteLastOkAt:null,s=Date.now()-this.startTime.getTime(),n=this.remoteFailures>0?"degraded":null!==r?r<ht.REMOTE_STALE_MS?"ok":"degraded":s<ht.REMOTE_STALE_MS?"unknown":"degraded",o={url:this.options.remoteServerUrl??process.env.REMOTE_SERVER_URL??null,state:n,ok:"ok"===n,consecutiveFailures:this.remoteFailures,lastOkAt:this.remoteLastOkAt?new Date(this.remoteLastOkAt).toISOString():null,failingForMs:"degraded"===n?r??s:null,lastError:this.remoteLastError,polling:this.isPolling}
5018
+ return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,Je.registerWorkspaceRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath}),e.get("/api/stuck-sessions",this.handleGetStuckSessions.bind(this)),e.post("/api/stuck-sessions/test-inject",this.handleTestInjectStuckSession.bind(this)),e.post("/api/stuck-sessions/:sessionId/retry",this.handleRetryStuckSession.bind(this)),e.get("/api/version-warnings",(e,t)=>{t.json({success:!0,data:this.versionWarnings})}),e.post("/api/self-update",async(e,r)=>{try{const{execSync:e}=await Promise.resolve().then(()=>t.t(35317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
5019
+ try{q.default.accessSync(s,q.default.constants.W_OK)}catch{return void r.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:n}=await Promise.resolve().then(()=>t.t(35317,23))
5020
+ r.json({success:!0,message:"Update started. The CLI will restart when complete."}),n("npm install -g alchemylab@latest",{timeout:12e4},(e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout(()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)},1e3))})}catch(e){r.json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.use("/api/opencode",(e,t)=>this.handleOpenCodeProxy(e,t)),e}handleHealth(e,t){if(!0!==t.locals.provenCaller){const e=this.remoteFailures>0&&this.isPolling
5021
+ return void t.json({status:e?"degraded":"healthy",service:"alchemylab-action-server"})}const r=this.remoteLastOkAt?Date.now()-this.remoteLastOkAt:null,s=Date.now()-this.startTime.getTime(),n=this.remoteFailures>0?"degraded":null!==r?r<pt.REMOTE_STALE_MS?"ok":"degraded":s<pt.REMOTE_STALE_MS?"unknown":"degraded",o={url:this.options.remoteServerUrl??process.env.REMOTE_SERVER_URL??null,state:n,ok:"ok"===n,consecutiveFailures:this.remoteFailures,lastOkAt:this.remoteLastOkAt?new Date(this.remoteLastOkAt).toISOString():null,failingForMs:"degraded"===n?r??s:null,lastError:this.remoteLastError,polling:this.isPolling}
4944
5022
  t.json({status:"degraded"===n&&this.isPolling?"degraded":"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),build:(0,f.readBuildInfo)(),projectId:this.projectId,projectPath:this.options.projectPath,remote:o,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
4945
5023
  if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),n=e?await r.getToken():null
4946
- t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,H.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
4947
- try{if(!D.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
4948
- const e=D.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
4949
- t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,H.getErrorMessage)(e)})}}async followUnchosenDefault(){const e=(0,de.unchosenDefaultToFollow)(this.cachedDefaultModel,this.defaultModelUnchosen,this.configurer.getConnectedProviders())
5024
+ t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,W.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5025
+ try{if(!q.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
5026
+ const e=q.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
5027
+ t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,W.getErrorMessage)(e)})}}async followUnchosenDefault(){const e=(0,ue.unchosenDefaultToFollow)(this.cachedDefaultModel,this.defaultModelUnchosen,this.configurer.getConnectedProviders())
4950
5028
  if(!e||!this.projectId||this.followingDefaultTo===e)return
4951
5029
  this.followingDefaultTo=e
4952
5030
  const t=this.cachedDefaultModel
4953
- try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,H.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,H.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
5031
+ try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,W.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,W.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
4954
5032
  let t=0
4955
5033
  for(const[r,s]of this.activeMcpCalls)s<e&&(this.activeMcpCalls.delete(r),t++)
4956
- t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<ht.PROMPT_DELIVERY_BUSY_MS)return!0
5034
+ t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<pt.PROMPT_DELIVERY_BUSY_MS)return!0
4957
5035
  try{const e=this.getOpenCodeUrl()
4958
5036
  if(!e)return!1
4959
5037
  const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
4960
5038
  if(!t.ok)return!1
4961
5039
  const r=await t.json()
4962
- return Object.values(r).some(e=>!!e&&"idle"!==e.type)}catch(e){return this.logger.debug("Could not fetch OpenCode session status",{error:(0,H.getErrorMessage)(e)}),!1}}async busySessionIds(){try{const e=this.getOpenCodeUrl()
5040
+ return Object.values(r).some(e=>!!e&&"idle"!==e.type)}catch(e){return this.logger.debug("Could not fetch OpenCode session status",{error:(0,W.getErrorMessage)(e)}),!1}}async busySessionIds(){try{const e=this.getOpenCodeUrl()
4963
5041
  if(!e)return[]
4964
5042
  const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
4965
5043
  if(!t.ok)return[]
4966
5044
  const r=await t.json()
4967
- return Object.entries(r).filter(([,e])=>!!e&&"idle"!==e.type).map(([e])=>e)}catch{return[]}}notePendingConfigReload(e,t){this.pendingConfigReload||(this.pendingConfigReload=!0,this.pendingConfigReloadSince=Date.now()),this.startPendingReloadRecheck(),this.logger.info("Deferring OpenCode config reload until MCP calls and session turns are idle",{context:e,inFlightMcp:t})}startPendingReloadRecheck(){this.pendingReloadRecheckInterval||(this.pendingReloadRecheckInterval=setInterval(()=>{this.fireDeferredReloadIfIdle("recheck")},ht.RELOAD_RECHECK_MS),this.pendingReloadRecheckInterval.unref?.())}stopPendingReloadRecheck(){this.pendingReloadRecheckInterval&&(clearInterval(this.pendingReloadRecheckInterval),this.pendingReloadRecheckInterval=void 0)}async fireDeferredReloadIfIdle(e){if(this.pendingConfigReload){if(!this.deferredReloadCheckInFlight){this.deferredReloadCheckInFlight=!0
5045
+ return Object.entries(r).filter(([,e])=>!!e&&"idle"!==e.type).map(([e])=>e)}catch{return[]}}notePendingConfigReload(e,t){this.pendingConfigReload||(this.pendingConfigReload=!0,this.pendingConfigReloadSince=Date.now()),this.startPendingReloadRecheck(),this.logger.info("Deferring OpenCode config reload until MCP calls and session turns are idle",{context:e,inFlightMcp:t})}startPendingReloadRecheck(){this.pendingReloadRecheckInterval||(this.pendingReloadRecheckInterval=setInterval(()=>{this.fireDeferredReloadIfIdle("recheck")},pt.RELOAD_RECHECK_MS),this.pendingReloadRecheckInterval.unref?.())}stopPendingReloadRecheck(){this.pendingReloadRecheckInterval&&(clearInterval(this.pendingReloadRecheckInterval),this.pendingReloadRecheckInterval=void 0)}async fireDeferredReloadIfIdle(e){if(this.pendingConfigReload){if(!this.deferredReloadCheckInFlight){this.deferredReloadCheckInFlight=!0
4968
5046
  try{const t=Date.now()-this.pendingConfigReloadSince
4969
- if(t<ht.RELOAD_DEFER_CAP_MS){if(this.activeMcpCalls.size>0)return
5047
+ if(t<pt.RELOAD_DEFER_CAP_MS){if(this.activeMcpCalls.size>0)return
4970
5048
  if(await this.anySessionBusy())return}else this.logger.warn("Config reload deferred past cap; firing despite activity (staleness beats permanent deferral)",{trigger:e,waitedMs:t})
4971
5049
  this.pendingConfigReload=!1,this.stopPendingReloadRecheck(),this.logger.info("Firing deferred OpenCode config reload",{trigger:e,waitedMs:t}),await this.reloadOpenCodeConfigNow(`deferred-${e}`)}finally{this.deferredReloadCheckInFlight=!1}}}else this.stopPendingReloadRecheck()}async applyClaudeThinking(e){const t=this.claudeThinkingSeen
4972
- this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,H.getErrorMessage)(e)})))}async applyAnthropicCompaction(e){const t=(0,je.generatedOpencodeConfigPath)(this.options.projectPath)
4973
- try{const r=JSON.parse(await D.default.promises.readFile(t,"utf8"))
4974
- if(!r||"object"!=typeof r||!(0,B.withAnthropicCompaction)(r,e))return
4975
- await D.default.promises.writeFile(t,JSON.stringify(r,null,2),"utf8"),this.logger.info("Claude's own compaction setting changed; reloading the serve's config",{on:e}),await this.requestConfigReload("anthropic-compaction")}catch(e){this.logger.debug("Claude's own compaction setting not applied yet",{error:(0,H.getErrorMessage)(e)})}}async applyPause(e){const t=this.getOpenCodeUrl()?.replace(/\/$/,"")
5050
+ this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,W.getErrorMessage)(e)})))}async applyAnthropicCompaction(e){const t=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
5051
+ try{const r=JSON.parse(await q.default.promises.readFile(t,"utf8"))
5052
+ if(!r||"object"!=typeof r||!(0,H.withAnthropicCompaction)(r,e))return
5053
+ await q.default.promises.writeFile(t,JSON.stringify(r,null,2),"utf8"),this.logger.info("Claude's own compaction setting changed; reloading the serve's config",{on:e}),await this.requestConfigReload("anthropic-compaction")}catch(e){this.logger.debug("Claude's own compaction setting not applied yet",{error:(0,W.getErrorMessage)(e)})}}async applyPause(e){const t=this.getOpenCodeUrl()?.replace(/\/$/,"")
4976
5054
  if(t&&(this.pauseApplied?.url!==t||this.pauseApplied.paused!==e))try{const r=await fetch(`${t}/config/pause`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paused:e}),signal:AbortSignal.timeout(15e3)})
4977
5055
  if(!r.ok)return void this.logger.warn("The serve did not take the pause",{paused:e,status:r.status})
4978
5056
  const s=await r.json().catch(()=>({}))
4979
5057
  this.pauseApplied={url:t,paused:e}
4980
- const n=(0,it.setProjectPaused)(e)
5058
+ const n=(0,at.setProjectPaused)(e)
4981
5059
  if(e){const e=Array.isArray(s.stopped)?s.stopped:[]
4982
5060
  let t=0
4983
- for(const r of e)t+=await(0,W.cancelRunningExecs)({sessionId:r}).catch(()=>0)
5061
+ for(const r of e)t+=await(0,G.cancelRunningExecs)({sessionId:r}).catch(()=>0)
4984
5062
  const r=this.projectId?await(0,j.experimentRegistry)().releaseForPause(this.projectId).catch(()=>[]):[]
4985
- return void this.logger.info("Project paused: every turn stopped and held",{stopped:e.length,commandsCancelled:t,boxesReleased:r.length})}(0,at.extendChatWatches)(n)
5063
+ return void this.logger.info("Project paused: every turn stopped and held",{stopped:e.length,commandsCancelled:t,boxesReleased:r.length})}(0,ct.extendChatWatches)(n)
4986
5064
  const o=Array.isArray(s.waiting)?s.waiting:[],i={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel}
4987
5065
  let a=0
4988
- for(const e of o)(await(0,d.deliverPrompt)(i,e,it.RESUME_TEXT,{kind:"inserted-prompt"}).catch(()=>({ok:!1}))).ok?a++:this.logger.warn("A session held by the pause could not be woken",{sessionId:e})
4989
- this.logger.info("Project resumed",{waiting:o.length,woken:a,pausedForMs:n})}catch(t){this.logger.warn("Could not apply the pause to the serve",{paused:e,error:(0,H.getErrorMessage)(t)})}}async reloadOpenCodeConfigNow(e){try{const t=this.getOpenCodeUrl()
5066
+ for(const e of o)(await(0,d.deliverPrompt)(i,e,at.RESUME_TEXT,{kind:"inserted-prompt"}).catch(()=>({ok:!1}))).ok?a++:this.logger.warn("A session held by the pause could not be woken",{sessionId:e})
5067
+ this.logger.info("Project resumed",{waiting:o.length,woken:a,pausedForMs:n})}catch(t){this.logger.warn("Could not apply the pause to the serve",{paused:e,error:(0,W.getErrorMessage)(t)})}}async reloadOpenCodeConfigNow(e){try{const t=this.getOpenCodeUrl()
4990
5068
  if(!t)return!1
4991
5069
  const r=await fetch(`${t}/config/reload`,{method:"POST",signal:AbortSignal.timeout(5e3)})
4992
5070
  if(r.ok){this.logger.info("Triggered OpenCode config reload",{context:e})
4993
5071
  const t=await r.json().catch(()=>null)
4994
- return!0!==t?.deferred&&(0,a.noteServeLoadedLogins)(),this.stuckSessionWatchdog?.capacityChanged(`config-reloaded:${e}`),(0,ue.noteConfigReloaded)(this.options.projectPath),this.lastConfigReloadAt=Date.now(),!0}return this.logger.warn("OpenCode config reload returned non-OK status",{context:e,status:r.status}),!1}catch(t){return this.logger.warn("Could not reload OpenCode config",{context:e,error:(0,H.getErrorMessage)(t)}),!1}}async requestConfigReload(e){const t=this.activeMcpCalls.size
4995
- return t>0||await this.anySessionBusy()?(this.notePendingConfigReload(e,t),"deferred"):await this.reloadOpenCodeConfigNow(e)?"reloaded":"failed"}async syncOpencodeConfigModel(e){const t=(0,je.generatedOpencodeConfigPath)(this.options.projectPath)
4996
- try{const r=await D.default.promises.readFile(t,"utf8"),s=JSON.parse(r)
5072
+ return!0!==t?.deferred&&(0,a.noteServeLoadedLogins)(),this.stuckSessionWatchdog?.capacityChanged(`config-reloaded:${e}`),(0,he.noteConfigReloaded)(this.options.projectPath),this.lastConfigReloadAt=Date.now(),!0}return this.logger.warn("OpenCode config reload returned non-OK status",{context:e,status:r.status}),!1}catch(t){return this.logger.warn("Could not reload OpenCode config",{context:e,error:(0,W.getErrorMessage)(t)}),!1}}async requestConfigReload(e){const t=this.activeMcpCalls.size
5073
+ return t>0||await this.anySessionBusy()?(this.notePendingConfigReload(e,t),"deferred"):await this.reloadOpenCodeConfigNow(e)?"reloaded":"failed"}async syncOpencodeConfigModel(e){const t=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
5074
+ try{const r=await q.default.promises.readFile(t,"utf8"),s=JSON.parse(r)
4997
5075
  if(!s||"object"!=typeof s||s.model===e)return
4998
- s.model=e,await D.default.promises.writeFile(t,JSON.stringify(s,null,2),"utf8"),this.logger.info("opencode.json model field brought in step",{model:e})}catch(e){this.logger.debug("opencode.json model sync skipped",{error:(0,H.getErrorMessage)(e)})}}async resyncAgentMarkdowns(){if(await this.configurer.refreshConnectedProviders(this.options.projectPath).catch(()=>!1),!this.projectId)return
5076
+ s.model=e,await q.default.promises.writeFile(t,JSON.stringify(s,null,2),"utf8"),this.logger.info("opencode.json model field brought in step",{model:e})}catch(e){this.logger.debug("opencode.json model sync skipped",{error:(0,W.getErrorMessage)(e)})}}async resyncAgentMarkdowns(){if(await this.configurer.refreshConnectedProviders(this.options.projectPath).catch(()=>!1),!this.projectId)return
4999
5077
  const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
5000
5078
  if(!e?.length)return
5001
5079
  const t=await this.configurer.fetchProjectFromRemote(this.projectId)
5002
5080
  this.ownerGuidanceStale=!1
5003
- const r=await this.configurer.agentPreambleFor(this.projectId,t?.metadata?.globalSystemPrompt),s=O.default.join(this.options.projectPath,".opencode","agent"),n=this.cachedDefaultModel||x.FLEET_DEFAULT_MODEL,o=await this.configurer.resolveWorkflowEffort(this.projectId),i=await this.mcpGrantInputs(this.projectId),a=this.agentConfigFingerprint()
5081
+ const r=await this.configurer.agentPreambleFor(this.projectId,t?.metadata?.globalSystemPrompt),s=D.default.join(this.options.projectPath,".opencode","agent"),n=this.cachedDefaultModel||M.FLEET_DEFAULT_MODEL,o=await this.configurer.resolveWorkflowEffort(this.projectId),i=await this.mcpGrantInputs(this.projectId),a=this.agentConfigFingerprint()
5004
5082
  this.configurer.clearAgentMarkdowns()
5005
- for(const s of e)s.enabled&&this.configurer.setAgentMarkdown(s.name,this.configurer.generateAgentMarkdown(s,"alchemy",n,r,{effort:o,extraTools:(0,fe.agentToolGrants)(i.servers,s.name,i.permissions),limits:(0,x.modelLimitsOf)(t?.metadata),claudeThinking:(0,B.claudeThinkingOf)(t?.metadata)}))
5083
+ for(const s of e)s.enabled&&this.configurer.setAgentMarkdown(s.name,this.configurer.generateAgentMarkdown(s,"alchemy",n,r,{effort:o,extraTools:(0,me.agentToolGrants)(i.servers,s.name,i.permissions),limits:(0,M.modelLimitsOf)(t?.metadata),claudeThinking:(0,H.claudeThinkingOf)(t?.metadata)}))
5006
5084
  this.configurer.setAgentMarkdown("explore",this.configurer.buildExploreMarkdown(r)),await this.configurer.removeOnDiskAgentFiles(s),this.lastAgentResyncModel=n,await this.syncOpencodeConfigModel(n),this.logger.info("Re-synced agent markdowns after model change",{model:n,count:e.length}),this.agentConfigFingerprint()!==a?(this.stuckSessionWatchdog?.capacityChanged("agents-resynced"),await this.requestConfigReload("agent-resync")):this.logger.info("Agent definitions unchanged; no config reload",{context:"agent-resync"})}async checkOwnerGuidanceApply(){if(!this.ownerGuidanceStale||!this.projectId)return
5007
5085
  const e=this.getOpenCodeUrl()
5008
5086
  let t=!1,r=null
@@ -5011,10 +5089,10 @@ if(!s.ok)return
5011
5089
  const o=await s.json(),i=n.ok?await n.json():{}
5012
5090
  t=Object.values(i??{}).some(e=>"busy"===e?.type)||this.activeMcpCalls.size>0
5013
5091
  for(const e of o){const t=Number(e.time?.updated??e.time?.created??0)
5014
- t&&(null===r||t>r)&&(r=t)}}catch{return}"apply"===(0,ot.ownerGuidanceApplyDecision)({stale:this.ownerGuidanceStale,serveUp:!!e,anyBusy:t,lastTurnMs:r,now:Date.now(),coldMs:(0,ot.ownerGuidanceColdMs)()})&&(this.logger.info("Applying your changed guidance now: nothing here has had a turn for longer than a cache lives, so no cache is lost",{quietMs:(0,ot.ownerGuidanceColdMs)()}),await this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance failed; it stays pending",{error:(0,H.getErrorMessage)(e)})))}async mcpGrantInputs(e){const t=await this.configurer.fetchMcpServersFromRemote(e).catch(()=>[]),r=await this.configurer.fetchDefaultWorkflowFromRemote(e).catch(()=>null)
5015
- return{servers:t,permissions:r?.connectionPermissions??null}}agentConfigFingerprint(){const e=(0,me.createHash)("sha256")
5092
+ t&&(null===r||t>r)&&(r=t)}}catch{return}"apply"===(0,it.ownerGuidanceApplyDecision)({stale:this.ownerGuidanceStale,serveUp:!!e,anyBusy:t,lastTurnMs:r,now:Date.now(),coldMs:(0,it.ownerGuidanceColdMs)()})&&(this.logger.info("Applying your changed guidance now: nothing here has had a turn for longer than a cache lives, so no cache is lost",{quietMs:(0,it.ownerGuidanceColdMs)()}),await this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance failed; it stays pending",{error:(0,W.getErrorMessage)(e)})))}async mcpGrantInputs(e){const t=await this.configurer.fetchMcpServersFromRemote(e).catch(()=>[]),r=await this.configurer.fetchDefaultWorkflowFromRemote(e).catch(()=>null)
5093
+ return{servers:t,permissions:r?.connectionPermissions??null}}agentConfigFingerprint(){const e=(0,ge.createHash)("sha256")
5016
5094
  for(const t of[...this.configurer.getAgentMarkdowns()].sort((e,t)=>e.name.localeCompare(t.name)))e.update(t.name).update("\0").update(t.markdown).update("\x01")
5017
- try{e.update(D.default.readFileSync((0,je.generatedOpencodeConfigPath)(this.options.projectPath),"utf-8"))}catch{}return e.digest("hex")}async handleSyncAgents(e,t){try{const e=this.projectId
5095
+ try{e.update(q.default.readFileSync((0,Ee.generatedOpencodeConfigPath)(this.options.projectPath),"utf-8"))}catch{}return e.digest("hex")}async handleSyncAgents(e,t){try{const e=this.projectId
5018
5096
  if(!e)return void t.status(400).json({success:!1,error:"Project not registered"})
5019
5097
  this.logger.info("Syncing agents from remote",{projectId:e})
5020
5098
  const r=await this.configurer.fetchAgentsFromRemote(e)
@@ -5023,19 +5101,19 @@ const s=await this.configurer.fetchProjectFromRemote(e)
5023
5101
  this.logger.debug("Fetched project for global prompt",{projectId:e,hasProject:!!s,hasGlobalSystemPrompt:!!s?.metadata?.globalSystemPrompt})
5024
5102
  const n=await this.configurer.agentPreambleFor(e,s?.metadata?.globalSystemPrompt)
5025
5103
  n?this.logger.info("Using global system prompt from project metadata",{projectId:e,promptLength:n.length}):this.logger.debug("No global system prompt found in project metadata")
5026
- const o=O.default.join(this.options.projectPath,".opencode","agent")
5027
- await D.default.promises.mkdir(o,{recursive:!0})
5028
- const i="alchemy",a=this.cachedDefaultModel||x.FLEET_DEFAULT_MODEL,c=r.filter(e=>e.enabled),l=await this.configurer.resolveWorkflowEffort(e),d=await this.mcpGrantInputs(e),u=this.agentConfigFingerprint()
5104
+ const o=D.default.join(this.options.projectPath,".opencode","agent")
5105
+ await q.default.promises.mkdir(o,{recursive:!0})
5106
+ const i="alchemy",a=this.cachedDefaultModel||M.FLEET_DEFAULT_MODEL,c=r.filter(e=>e.enabled),l=await this.configurer.resolveWorkflowEffort(e),d=await this.mcpGrantInputs(e),u=this.agentConfigFingerprint()
5029
5107
  this.configurer.clearAgentMarkdowns()
5030
5108
  let h=0
5031
- for(const e of c)try{this.configurer.setAgentMarkdown(e.name,this.configurer.generateAgentMarkdown(e,i,a,n,{effort:l,extraTools:(0,fe.agentToolGrants)(d.servers,e.name,d.permissions),limits:(0,x.modelLimitsOf)(s?.metadata),claudeThinking:(0,B.claudeThinkingOf)(s?.metadata)})),h++}catch(t){this.logger.warn("Agent markdown not generated",{name:e.name,error:(0,H.getErrorMessage)(t)})}this.configurer.setAgentMarkdown("explore",this.configurer.buildExploreMarkdown(n)),await this.configurer.removeOnDiskAgentFiles(o),this.lastAgentResyncModel=a
5032
- try{const e=(0,je.generatedOpencodeConfigPath)(this.options.projectPath)
5033
- if(D.default.existsSync(e)){const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5109
+ for(const e of c)try{this.configurer.setAgentMarkdown(e.name,this.configurer.generateAgentMarkdown(e,i,a,n,{effort:l,extraTools:(0,me.agentToolGrants)(d.servers,e.name,d.permissions),limits:(0,M.modelLimitsOf)(s?.metadata),claudeThinking:(0,H.claudeThinkingOf)(s?.metadata)})),h++}catch(t){this.logger.warn("Agent markdown not generated",{name:e.name,error:(0,W.getErrorMessage)(t)})}this.configurer.setAgentMarkdown("explore",this.configurer.buildExploreMarkdown(n)),await this.configurer.removeOnDiskAgentFiles(o),this.lastAgentResyncModel=a
5110
+ try{const e=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
5111
+ if(q.default.existsSync(e)){const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5034
5112
  if(r.agent){for(const e of Object.values(r.agent))e&&"object"==typeof e&&"prompt"in e&&delete e.prompt
5035
- D.default.writeFileSync(e,JSON.stringify(r,null,2),"utf-8")}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,H.getErrorMessage)(e)})}const p=this.agentConfigFingerprint()===u
5113
+ q.default.writeFileSync(e,JSON.stringify(r,null,2),"utf-8")}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,W.getErrorMessage)(e)})}const p=this.agentConfigFingerprint()===u
5036
5114
  p&&this.logger.info("Agent definitions unchanged; no config reload",{context:"sync-agents"}),this.ownerGuidanceStale=!1
5037
5115
  const f=p?"unchanged":await this.requestConfigReload("sync-agents"),m="reloaded"===f
5038
- t.json({success:!0,message:`Synced ${h} agent(s) into the running serve${m?" - config reloaded":"deferred"===f?" - config reload deferred until MCP is idle":""}`,count:h,reloaded:m,reloadDeferred:"deferred"===f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
5116
+ t.json({success:!0,message:`Synced ${h} agent(s) into the running serve${m?" - config reloaded":"deferred"===f?" - config reload deferred until MCP is idle":""}`,count:h,reloaded:m,reloadDeferred:"deferred"===f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
5039
5117
  if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
5040
5118
  const{sessionId:s,requestId:n,summary:o,status:i}=e.body
5041
5119
  if(!s)return void t.status(400).json({success:!1,error:"sessionId is required"})
@@ -5043,26 +5121,26 @@ this.logger.info("Direct report creation",{sessionId:s,requestId:n,status:i})
5043
5121
  const a=[]
5044
5122
  try{const e=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),t=e?.data||e||[]
5045
5123
  let n=s
5046
- for(const e of t)a.push({ancestorId:e.externalSessionId,childId:n}),n=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,H.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
5124
+ for(const e of t)a.push({ancestorId:e.externalSessionId,childId:n}),n=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,W.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
5047
5125
  let c,l=o
5048
5126
  if(!l&&n)try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${n}`),t=e?.data||e
5049
5127
  c=t?.title
5050
5128
  const s=t?.completionSummary
5051
- s?l=s:c&&(l=`Done: ${c}`)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:n,error:(0,H.getErrorMessage)(e)})}l||(l=`Completed request ${n||"unknown"}`)
5129
+ s?l=s:c&&(l=`Done: ${c}`)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:n,error:(0,W.getErrorMessage)(e)})}l||(l=`Completed request ${n||"unknown"}`)
5052
5130
  const d=n||s
5053
5131
  if(this.directReportDedup.size>=1e3){const e=this.directReportDedup.keys().next().value
5054
5132
  void 0!==e&&this.directReportDedup.delete(e)}this.directReportDedup.has(d)||this.directReportDedup.set(d,new Set)
5055
5133
  const u=this.directReportDedup.get(d),h=a.filter(({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1)),p=await Promise.allSettled(h.map(async({ancestorId:e,childId:t})=>{const s=await this.apiClient.post(`/api/projects/${r}/session-messages/reports`,{fromSessionId:t,toSessionId:e,status:i||"success",summary:l}),n=s?.data||s
5056
- return u.add(e),n?.messageCode&&this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(n.messageCode)}/acknowledge`,{}).catch(e=>this.logger.debug("Direct report pre-ack failed (non-fatal)",{messageCode:n.messageCode,error:(0,H.getErrorMessage)(e)})),this.logger.info("Direct report created for ancestor",{messageCode:n?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:n?.messageCode,fromSessionId:t,content:n?.content}})),f=[]
5134
+ return u.add(e),n?.messageCode&&this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(n.messageCode)}/acknowledge`,{}).catch(e=>this.logger.debug("Direct report pre-ack failed (non-fatal)",{messageCode:n.messageCode,error:(0,W.getErrorMessage)(e)})),this.logger.info("Direct report created for ancestor",{messageCode:n?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:n?.messageCode,fromSessionId:t,content:n?.content}})),f=[]
5057
5135
  for(const e of p)"fulfilled"===e.status&&f.push(e.value)
5058
- for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,H.getErrorMessage)(e.reason)})
5136
+ for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,W.getErrorMessage)(e.reason)})
5059
5137
  if(setTimeout(()=>{this.directReportDedup.delete(d)},3e5),n)try{const e=await this.apiClient.get(`/api/projects/${r}/session-request-associations/by-session/${encodeURIComponent(s)}?activeOnly=true`),t=e?.data||e||[],n=(await Promise.allSettled(t.map(e=>this.apiClient.post(`/api/projects/${r}/session-request-associations/end`,{sessionId:s,requestLocalId:e.requestLocalId})))).filter(e=>"rejected"===e.status).length
5060
- this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:n})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,H.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:f,report:f[0]?{messageCode:f[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:r}=e.params,s=this.projectId
5138
+ this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:n})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,W.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:f,report:f[0]?{messageCode:f[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:r}=e.params,s=this.projectId
5061
5139
  if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),n=e?.data||e
5062
5140
  if(this.logger.info("Agent type DB lookup",{sessionId:r,dbAgentType:n?.agentType||null,dbId:n?.id||n?.localId||null}),n?.agentType){const e=this.configurer.agentModelOf(n.agentType)
5063
- return void t.json({success:!0,agentType:n.agentType,source:"db",...e?{model:e}:{}})}}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,H.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,s=this.projectId
5141
+ return void t.json({success:!0,agentType:n.agentType,source:"db",...e?{model:e}:{}})}}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,W.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,s=this.projectId
5064
5142
  if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/resolve/${encodeURIComponent(r)}`),n=e?.data?.parent?.externalSessionId||""
5065
- return void t.json({success:!0,parentSessionId:n})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,H.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
5143
+ return void t.json({success:!0,parentSessionId:n})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,W.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
5066
5144
  if(!r)return void t.status(502).json({error:"OpenCode not running"})
5067
5145
  const s=e.path.replace(/^\//,""),n=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",o=`${r}/${s}${n}`,i=e.headers.accept?.includes("text/event-stream")
5068
5146
  if(i){const r=new AbortController,s=()=>{this.proxyAbortControllers.delete(r)},n=(async()=>{try{const e=await fetch(o,{headers:{Accept:"text/event-stream"},signal:r.signal})
@@ -5083,18 +5161,18 @@ if(!t||t===r)throw e
5083
5161
  l=`${t}/${s}${n}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:r,newUrl:t}),c=await fetch(l,a)}if(204===c.status||"0"===c.headers.get("content-length"))return void t.status(c.status).end()
5084
5162
  const d=c.headers.get("content-type")||""
5085
5163
  let u
5086
- try{u=d.includes("application/json")?await c.json():await c.text()}catch{return void t.status(c.status).end()}t.status(c.status),d&&t.setHeader("Content-Type",d),"string"==typeof u?t.send(u):t.json(u)}finally{this.inFlightServeProxyRequests--}}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,H.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,H.getErrorMessage)(r)})}}async handleSpawnPlanner(e,t){try{if(!(0,Oe.validateRequest)(e,t,this.projectId,["planId","prompt"]))return
5164
+ try{u=d.includes("application/json")?await c.json():await c.text()}catch{return void t.status(c.status).end()}t.status(c.status),d&&t.setHeader("Content-Type",d),"string"==typeof u?t.send(u):t.json(u)}finally{this.inFlightServeProxyRequests--}}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,W.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,W.getErrorMessage)(r)})}}async handleSpawnPlanner(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId,["planId","prompt"]))return
5087
5165
  const{agent:r,planId:s,prompt:n,title:o,parentSessionId:i,flavor:a}=e.body||{},c="auto-planner"===r?"planner":r,l="auto-planner"===r||"autonomous"===a?"autonomous":void 0,d=new Set(["planner"])
5088
5166
  if(c&&!d.has(c))return void t.status(400).json({success:!1,error:`agent must be one of: ${[...d].join(", ")}`})
5089
- const u=await(0,ke.spawnPlannerWithDoc)({agent:c||"planner",flavor:l,planId:s,prompt:n,title:o||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
5167
+ const u=await(0,Se.spawnPlannerWithDoc)({agent:c||"planner",flavor:l,planId:s,prompt:n,title:o||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
5090
5168
  if(!u.success||!u.sessionId)return void t.status(500).json({success:!1,error:u.error||"spawn failed"})
5091
- t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Spawn planner",e)}}scheduleHubAlchemist(e){this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.hubRetryTimer=setTimeout(async()=>{this.hubRetryTimer=void 0,await this.ensureHubAlchemist()||this.scheduleHubAlchemist(Math.min(2*e,6e4))},e),this.hubRetryTimer.unref?.()}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
5092
- if(t&&t.externalSessionId){const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath),r=e?await this.serveHasSession(e,String(t.externalSessionId)):null
5169
+ t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,De.respondWithError)(t,this.logger,"Spawn planner",e)}}scheduleHubAlchemist(e){this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.hubRetryTimer=setTimeout(async()=>{this.hubRetryTimer=void 0,await this.ensureHubAlchemist()||this.scheduleHubAlchemist(Math.min(2*e,6e4))},e),this.hubRetryTimer.unref?.()}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
5170
+ if(t&&t.externalSessionId){const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath),r=e?await this.serveHasSession(e,String(t.externalSessionId)):null
5093
5171
  if(!1!==r)return this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r}),!0
5094
- this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,H.getErrorMessage)(e)}))}if(!this.projectId)return!0
5095
- const r=new ye.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}),s="Hub",n=await r.prepareSession({agent:"alchemist",prompt:s,title:s,projectPath:this.options.projectPath})
5096
- return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,H.getErrorMessage)(e)}),!1}}async reapVanishedSessions(){if(!this.projectId)return
5097
- const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
5172
+ this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,W.getErrorMessage)(e)}))}if(!this.projectId)return!0
5173
+ const r=new ve.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}),s="Hub",n=await r.prepareSession({agent:"alchemist",prompt:s,title:s,projectPath:this.options.projectPath})
5174
+ return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,W.getErrorMessage)(e)}),!1}}async reapVanishedSessions(){if(!this.projectId)return
5175
+ const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
5098
5176
  if(!e)return
5099
5177
  let t
5100
5178
  try{const r=await fetch(`${e.replace(/\/$/,"")}/session?limit=5000`,{signal:AbortSignal.timeout(15e3)})
@@ -5107,15 +5185,15 @@ try{for(let e=0;e<1e3;e+=100){const n=await this.apiClient.get(`/api/projects/${
5107
5185
  if(!Array.isArray(o)||0===o.length)break
5108
5186
  for(const e of o){s++
5109
5187
  const n=e?.externalSessionId??e?.external_session_id
5110
- n&&!t.has(String(n))&&r.push({id:String(e.id),ext:String(n)})}if(o.length<100)break}}catch(e){return void this.logger.warn("vanished-session sweep could not read the session list; ending nothing",{error:(0,H.getErrorMessage)(e)})}let n=0,o=0
5111
- for(const e of r)try{await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${e.id}`,{status:"completed"}),n++}catch(t){o++,o<=3&&this.logger.warn("could not end a vanished session row",{sessionId:e.id,externalSessionId:e.ext,error:(0,H.getErrorMessage)(t)})}o>0&&this.logger.warn("vanished-session sweep could not end every row it meant to",{ended:n,failed:o,scanned:s}),n>0&&this.logger.info("ended rows for sessions this serve no longer has",{ended:n,scanned:s,live:t.size})}async serveHasSession(e,t){try{const r=await fetch(`${e.replace(/\/$/,"")}/session/${encodeURIComponent(t)}`,{signal:AbortSignal.timeout(5e3)})
5112
- return!!r.ok||404!==r.status&&null}catch{return null}}async handleSpawnSession(e,t){try{if(!(0,Oe.validateRequest)(e,t,this.projectId,["agent","prompt"]))return
5113
- const{agent:r,prompt:s,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,shapingRequestLocalId:l,userOpened:d}=e.body,u=this.projectId,h=new ye.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),p=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,..."string"==typeof l&&/^req\d+$/.test(l)?{shapingRequestLocalId:l,skipWorktree:!0}:{}})
5114
- p?.sessionId&&o&&!0!==d&&this.watchSubagentForParent(String(p.sessionId),String(o),String(r)),t.json({success:!0,data:p})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Spawn session",e)}}async childTurnDeath(e,t){try{const r=await fetch(`${e}/session/${encodeURIComponent(t)}/message?limit=6`,{signal:AbortSignal.timeout(5e3)})
5115
- return r.ok?(0,J.lastTurnDeath)(await r.json()):null}catch{return null}}async endFinishedChildSession(e,t){try{const r=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=r?.data??r
5188
+ n&&!t.has(String(n))&&r.push({id:String(e.id),ext:String(n)})}if(o.length<100)break}}catch(e){return void this.logger.warn("vanished-session sweep could not read the session list; ending nothing",{error:(0,W.getErrorMessage)(e)})}let n=0,o=0
5189
+ for(const e of r)try{await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${e.id}`,{status:"completed"}),n++}catch(t){o++,o<=3&&this.logger.warn("could not end a vanished session row",{sessionId:e.id,externalSessionId:e.ext,error:(0,W.getErrorMessage)(t)})}o>0&&this.logger.warn("vanished-session sweep could not end every row it meant to",{ended:n,failed:o,scanned:s}),n>0&&this.logger.info("ended rows for sessions this serve no longer has",{ended:n,scanned:s,live:t.size})}async serveHasSession(e,t){try{const r=await fetch(`${e.replace(/\/$/,"")}/session/${encodeURIComponent(t)}`,{signal:AbortSignal.timeout(5e3)})
5190
+ return!!r.ok||404!==r.status&&null}catch{return null}}async handleSpawnSession(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId,["agent","prompt"]))return
5191
+ const{agent:r,prompt:s,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,shapingRequestLocalId:l,userOpened:d}=e.body,u=this.projectId,h=new ve.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),p=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,..."string"==typeof l&&/^req\d+$/.test(l)?{shapingRequestLocalId:l,skipWorktree:!0}:{}})
5192
+ p?.sessionId&&o&&!0!==d&&this.watchSubagentForParent(String(p.sessionId),String(o),String(r)),t.json({success:!0,data:p})}catch(e){(0,De.respondWithError)(t,this.logger,"Spawn session",e)}}async childTurnDeath(e,t){try{const r=await fetch(`${e}/session/${encodeURIComponent(t)}/message?limit=6`,{signal:AbortSignal.timeout(5e3)})
5193
+ return r.ok?(0,X.lastTurnDeath)(await r.json()):null}catch{return null}}async endFinishedChildSession(e,t){try{const r=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=r?.data??r
5116
5194
  if(!s?.id)return
5117
5195
  if(s.status&&"active"!==s.status)return
5118
- await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${s.id}`,{status:"completed"}),this.logger.info("subagent idle rail: child session ended",{childSessionId:e,agent:t})}catch(t){this.logger.warn("subagent idle rail: could not end the child session row",{childSessionId:e,error:(0,H.getErrorMessage)(t)})}}watchSubagentForParent(e,r,s){try{const{onNextSessionIdle:n}=t(67078),o=n(e,()=>{(async()=>{try{const n=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?sessionId=${encodeURIComponent(e)}&direction=outgoing&limit=20`).catch(()=>null)
5196
+ await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${s.id}`,{status:"completed"}),this.logger.info("subagent idle rail: child session ended",{childSessionId:e,agent:t})}catch(t){this.logger.warn("subagent idle rail: could not end the child session row",{childSessionId:e,error:(0,W.getErrorMessage)(t)})}}watchSubagentForParent(e,r,s){try{const{onNextSessionIdle:n}=t(67078),o=n(e,()=>{(async()=>{try{const n=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?sessionId=${encodeURIComponent(e)}&direction=outgoing&limit=20`).catch(()=>null)
5119
5197
  if((Array.isArray(n?.data)?n.data:[]).some(t=>String(t?.fromSessionId??t?.from_session_id??"")===e&&["report","question"].includes(String(t?.messageType??t?.message_type??""))))return this.logger.info("subagent idle rail: child already reported; parent not re-woken",{childSessionId:e,parentSessionId:r,agent:s}),void await this.endFinishedChildSession(e,s)
5120
5198
  const{getOpenCodeServerUrl:o}=await Promise.resolve().then(()=>t(28148)),i=o(this.options.projectPath,this.logger)
5121
5199
  if(!i)return
@@ -5128,11 +5206,11 @@ if(!u||!u.trim())return void await this.endFinishedChildSession(e,s)
5128
5206
  const{deliverPrompt:h}=await Promise.resolve().then(()=>t(17039)),p={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}
5129
5207
  if(d.test(u)){const t=(this.leakedCallNudges.get(e)??0)+1
5130
5208
  if(this.leakedCallNudges.set(e,t),t<=2)return await h(p,e,'[system] Your last reply wrote tool calls as plain text ("to=functions.\u2026"), so none of them ran: nothing you described was created. Make each of those calls now as real tool calls, then finish as you meant to.',{kind:"agent-context"}),this.logger.warn("subagent idle rail: tool calls came out as text; told the session and watching again",{childSessionId:e,agent:s,tries:t}),void this.watchSubagentForParent(e,r,s)
5131
- u=`It wrote its tool calls as plain text instead of making them, and did again after being told twice, so nothing it describes below was actually created. Its last message:\n\n${u}`}await h(p,r,`[${s} finished] It did not file a report, so here is what it produced:\n\n${u.slice(0,8e3)}`,{kind:"subagent-idle"}),this.logger.info("subagent idle rail: woke the parent with the child's final message",{childSessionId:e,parentSessionId:r,agent:s}),await this.endFinishedChildSession(e,s)}catch(t){this.logger.warn("subagent idle rail failed (the parent stays asleep)",{childSessionId:e,parentSessionId:r,error:(0,H.getErrorMessage)(t)})}})()}),i=setTimeout(()=>{try{o()}catch{}},36e5)
5132
- i.unref?.()}catch(t){this.logger.warn("subagent idle rail could not be armed",{childSessionId:e,parentSessionId:r,error:(0,H.getErrorMessage)(t)})}}visualBaselineDir(){return O.default.join(this.options.projectPath,".alchemy_lab",R.VISUAL_BASELINES_DIR_NAME)}async handleRunTestsNow(e,r){try{if(!(0,Oe.validateRequest)(e,r,this.projectId,["requestId"]))return
5209
+ u=`It wrote its tool calls as plain text instead of making them, and did again after being told twice, so nothing it describes below was actually created. Its last message:\n\n${u}`}await h(p,r,`[${s} finished] It did not file a report, so here is what it produced:\n\n${u.slice(0,8e3)}`,{kind:"subagent-idle"}),this.logger.info("subagent idle rail: woke the parent with the child's final message",{childSessionId:e,parentSessionId:r,agent:s}),await this.endFinishedChildSession(e,s)}catch(t){this.logger.warn("subagent idle rail failed (the parent stays asleep)",{childSessionId:e,parentSessionId:r,error:(0,W.getErrorMessage)(t)})}})()}),i=setTimeout(()=>{try{o()}catch{}},36e5)
5210
+ i.unref?.()}catch(t){this.logger.warn("subagent idle rail could not be armed",{childSessionId:e,parentSessionId:r,error:(0,W.getErrorMessage)(t)})}}visualBaselineDir(){return D.default.join(this.options.projectPath,".alchemy_lab",P.VISUAL_BASELINES_DIR_NAME)}async handleRunTestsNow(e,r){try{if(!(0,De.validateRequest)(e,r,this.projectId,["requestId"]))return
5133
5211
  const{requestId:s,parentSessionId:n,testProcessIds:o,commandNames:i,catchUp:a}=e.body||{},{handleRunTestsAction:c}=await Promise.resolve().then(()=>t(76555)),l={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},d=await c({requestId:s,parentSessionId:n,testProcessIds:o,commandNames:i,catchUp:a,mode:"auto"},l)
5134
5212
  if(!d.success)return void r.status(500).json({success:!1,error:d.error||d.message})
5135
- r.json({success:!0,data:d.data})}catch(e){(0,Oe.respondWithError)(r,this.logger,"Run tests now",e)}}async handleSetupRun(e,t){try{if(!(0,Oe.validateRequest)(e,t,this.projectId))return
5213
+ r.json({success:!0,data:d.data})}catch(e){(0,De.respondWithError)(r,this.logger,"Run tests now",e)}}async handleSetupRun(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId))return
5136
5214
  const r=this.projectId
5137
5215
  if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
5138
5216
  const s=String(e.body?.runId??"")
@@ -5141,21 +5219,21 @@ const n=await this.apiClient.get(`/api/projects/${r}/setup-runs/${s}`),o=n?.data
5141
5219
  if(!o?.id)return void t.status(404).json({success:!1,error:`no setup run ${s}`})
5142
5220
  if("running"!==o.state)return void t.json({success:!0,data:{skipped:`run is ${o.state}`}})
5143
5221
  if(o.architectSessionId)return void t.json({success:!0,data:{sessionId:o.architectSessionId,already:!0}})
5144
- const i=Array.isArray(o.parts)?o.parts:[],a=Array.isArray(o.asks)?o.asks:[],c=[o.askedBy,...a.map(e=>e?.from)].map(e=>e&&("request"===e.by||"worker"===e.by)&&/^req\d{6}$/.test(String(e.ref??""))?String(e.ref):void 0).find(Boolean),l=await(0,De.resolveSetupBranch)(this.apiClient,r,void 0,c,this.logger,this.options.projectPath),d=a.map(e=>String(e?.need??"")).filter(Boolean),u=a.find(e=>"checks"===e?.part&&("alchemist"===e?.from?.by||"user"===e?.from?.by))?.need,h=i.includes("checks")?"checks":i.includes("environment")?"environment":"commands"
5222
+ const i=Array.isArray(o.parts)?o.parts:[],a=Array.isArray(o.asks)?o.asks:[],c=[o.askedBy,...a.map(e=>e?.from)].map(e=>e&&("request"===e.by||"worker"===e.by)&&/^req\d{6}$/.test(String(e.ref??""))?String(e.ref):void 0).find(Boolean),l=await(0,qe.resolveSetupBranch)(this.apiClient,r,void 0,c,this.logger,this.options.projectPath),d=a.map(e=>String(e?.need??"")).filter(Boolean),u=a.find(e=>"checks"===e?.part&&("alchemist"===e?.from?.by||"user"===e?.from?.by))?.need,h=i.includes("checks")?"checks":i.includes("environment")?"environment":"commands"
5145
5223
  let p,f
5146
- if("checks"===h){const e=await(0,De.gateAsksOnRecord)(this.apiClient,r,this.logger)
5224
+ if("checks"===h){const e=await(0,qe.gateAsksOnRecord)(this.apiClient,r,this.logger)
5147
5225
  let t=!1
5148
5226
  try{const e=await this.apiClient.get(`/api/projects/${r}/test-processes`),s=Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[]
5149
5227
  t=s.length>0}catch{}let s=!1
5150
- try{s=await(0,De.testGateOffForSetup)(this.apiClient,r,c)}catch{}let n=!1
5151
- try{n=await(0,De.selfDrivingForSetup)(this.apiClient,r)}catch{}p=async(r,o,i,a)=>(0,De.buildSetupPrompt)(r,c,void 0,e,"string"==typeof u?u:void 0,s,a?await(0,Ae.declaredFloorsIn)(a).catch(()=>[]):[],{existingProcesses:t,selfDriving:n}),f="Test check setup"}else if("environment"===h){let e="interactive"
5228
+ try{s=await(0,qe.testGateOffForSetup)(this.apiClient,r,c)}catch{}let n=!1
5229
+ try{n=await(0,qe.selfDrivingForSetup)(this.apiClient,r)}catch{}p=async(r,o,i,a)=>(0,qe.buildSetupPrompt)(r,c,void 0,e,"string"==typeof u?u:void 0,s,a?await(0,Ce.declaredFloorsIn)(a).catch(()=>[]):[],{existingProcesses:t,selfDriving:n}),f="Test check setup"}else if("environment"===h){let e="interactive"
5152
5230
  try{const t=await this.apiClient.get(`/api/projects/${r}`),s=(t?.data||t)?.metadata?.environmentSetupMode
5153
- "auto"!==s&&"ask"!==s&&"interactive"!==s||(e=s)}catch{}p=async t=>(0,De.buildEnvironmentSetupPrompt)(t,c,d.join("\n"),e),f="Environment setup"}else p=async e=>(0,De.buildCommandSetupPrompt)(e,c,d.join("\n")),f="Commands setup"
5154
- const m=await(0,Se.setupAgentInWorktree)({agent:"setup-architect",promptBuilder:async(e,t,r,s)=>`${await p(e,t,r,s)}\n${(0,De.setupRunClosing)({id:o.id,parts:i,asks:a})}`,titleBuilder:()=>`[${o.id}] ${f}`,worktreeErrorLabel:`Failed to create setup worktree (${f})`,runId:o.id,parentSessionId:o.parentSessionId??void 0,branchName:l,apiClient:this.apiClient,logger:this.logger,projectId:r,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
5155
- if(!m.success||!m.sessionId)return await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/finish`,{system:!0,actor:"spawn",outcome:"stopped",summary:`The setup architect could not be started: ${String(m.error??"unknown error").slice(0,300)}`}).catch(e=>this.logger.warn("setup run spawn failed and the run could not be ended (the reconciler ends it)",{runId:o.id,error:(0,H.getErrorMessage)(e)})),void t.status(m.statusCode||500).json({success:!1,error:m.error})
5156
- await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/started`,{architectSessionId:m.sessionId}),this.logger.info("setup run architect spawned",{runId:o.id,parts:i,sessionId:m.sessionId,branch:m.branch}),t.json({success:!0,data:{sessionId:m.sessionId,worktreePath:m.worktreePath,branch:m.branch}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Setup run",e)}}async reconcileSetupRunPass(){if(!this.projectId)return
5157
- const e=this.projectId,t=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
5158
- try{const r=await(0,X.reconcileSetupRun)({runningRun:async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
5231
+ "auto"!==s&&"ask"!==s&&"interactive"!==s||(e=s)}catch{}p=async t=>(0,qe.buildEnvironmentSetupPrompt)(t,c,d.join("\n"),e),f="Environment setup"}else p=async e=>(0,qe.buildCommandSetupPrompt)(e,c,d.join("\n")),f="Commands setup"
5232
+ const m=await(0,Ie.setupAgentInWorktree)({agent:"setup-architect",promptBuilder:async(e,t,r,s)=>`${await p(e,t,r,s)}\n${(0,qe.setupRunClosing)({id:o.id,parts:i,asks:a})}`,titleBuilder:()=>`[${o.id}] ${f}`,worktreeErrorLabel:`Failed to create setup worktree (${f})`,runId:o.id,parentSessionId:o.parentSessionId??void 0,branchName:l,apiClient:this.apiClient,logger:this.logger,projectId:r,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
5233
+ if(!m.success||!m.sessionId)return await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/finish`,{system:!0,actor:"spawn",outcome:"stopped",summary:`The setup architect could not be started: ${String(m.error??"unknown error").slice(0,300)}`}).catch(e=>this.logger.warn("setup run spawn failed and the run could not be ended (the reconciler ends it)",{runId:o.id,error:(0,W.getErrorMessage)(e)})),void t.status(m.statusCode||500).json({success:!1,error:m.error})
5234
+ await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/started`,{architectSessionId:m.sessionId}),this.logger.info("setup run architect spawned",{runId:o.id,parts:i,sessionId:m.sessionId,branch:m.branch}),t.json({success:!0,data:{sessionId:m.sessionId,worktreePath:m.worktreePath,branch:m.branch}})}catch(e){(0,De.respondWithError)(t,this.logger,"Setup run",e)}}async reconcileSetupRunPass(){if(!this.projectId)return
5235
+ const e=this.projectId,t=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
5236
+ try{const r=await(0,Q.reconcileSetupRun)({runningRun:async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
5159
5237
  return r?.id?{id:r.id,architectSessionId:r.architectSessionId??null,startedAt:r.startedAt??null}:null},architectState:async e=>{if(!t)return"unknown"
5160
5238
  const r=await this.serveHasSession(t,e)
5161
5239
  if(!1===r)return"gone"
@@ -5163,37 +5241,37 @@ if(null===r)return"unknown"
5163
5241
  try{const r=await fetch(`${t.replace(/\/$/,"")}/session/status`,{signal:AbortSignal.timeout(5e3)})
5164
5242
  if(r.ok){const t=(await r.json())?.[e]
5165
5243
  if(t&&"idle"!==t.type)return"busy"}}catch{return"unknown"}try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}/message?limit=6`,{signal:AbortSignal.timeout(5e3)})
5166
- return r.ok?(0,X.architectTurnState)(await r.json()):"unknown"}catch{return"unknown"}},lastActivityAt:async e=>{if(!t)return null
5244
+ return r.ok?(0,Q.architectTurnState)(await r.json()):"unknown"}catch{return"unknown"}},lastActivityAt:async e=>{if(!t)return null
5167
5245
  try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(5e3)})
5168
5246
  if(!r.ok)return null
5169
5247
  const s=Number((await r.json())?.time?.updated)
5170
5248
  return Number.isFinite(s)&&s>0?s:null}catch{return null}},hasPendingQuestion:async t=>{const r=await this.apiClient.get(`/api/projects/${e}/session-messages?sessionId=${encodeURIComponent(t)}&direction=outgoing&messageType=question&status=pending&limit=5`).catch(()=>null)
5171
5249
  return(Array.isArray(r?.data)?r.data:[]).some(e=>"pending"===String(e?.status??""))},remind:async(t,r)=>{await(0,d.deliverPrompt)({projectPath:this.options.projectPath,projectId:e,apiClient:this.apiClient,logger:this.logger},t,`[${r} is still running] Your setup run has not ended: call \`setup_finish\` with its outcome and a summary that answers every ask the run carries. If you still need the user's answer, ask it as one question.`,{kind:"agent-context"})},stop:async(t,r)=>{await this.apiClient.post(`/api/projects/${e}/setup-runs/${t}/finish`,{system:!0,actor:"reconciler",outcome:"stopped",summary:r})}})
5172
- "none"!==r.action&&"running"!==r.action&&this.logger.info("setup run reconciled",r)}catch(e){this.logger.warn("setup run reconcile pass failed (the next pass looks again)",{error:(0,H.getErrorMessage)(e)})}await(0,De.removeEndedSetupRunWorktrees)(this.options.projectPath,async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
5250
+ "none"!==r.action&&"running"!==r.action&&this.logger.info("setup run reconciled",r)}catch(e){this.logger.warn("setup run reconcile pass failed (the next pass looks again)",{error:(0,W.getErrorMessage)(e)})}await(0,qe.removeEndedSetupRunWorktrees)(this.options.projectPath,async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
5173
5251
  return"string"==typeof r?.id?r.id:null},this.logger)}async handleGetAgentByName(e,t){try{const r=this.projectId
5174
5252
  if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
5175
- const{agentName:s}=e.params,n=(await(0,ve.getProjectAgents)(this.apiClient,r)).find(e=>e.name===s)
5253
+ const{agentName:s}=e.params,n=(await(0,be.getProjectAgents)(this.apiClient,r)).find(e=>e.name===s)
5176
5254
  if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
5177
- t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<ht.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
5178
- const e=(0,nt.identityPath)(this.options.projectPath)
5179
- try{if(!D.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
5180
- const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5181
- return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,H.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e,(0,rt.noteServe)(e)
5255
+ t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<pt.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
5256
+ const e=(0,ot.identityPath)(this.options.projectPath)
5257
+ try{if(!q.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
5258
+ const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5259
+ return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,W.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e,(0,st.noteServe)(e)
5182
5260
  const t=`http://127.0.0.1:${e}`
5183
5261
  this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.serveStartedAt=Date.now(),this.stuckSessionWatchdog?.serveStarted(this.serveStartedAt),(async()=>{for(let e=0;e<10;e++){try{const t=await this.configurer.refreshConnectedProviders(this.options.projectPath)
5184
- if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,H.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,H.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
5262
+ if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,W.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,W.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
5185
5263
  if(!t.ok)return
5186
- const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,x.primaryProviderOf)(s),o=r?.default||{},[i,a]=Object.entries(o)[0]||[],c=n?(0,x.defaultModelFor)(n):i&&a?`${i}/${a}`:null
5187
- c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,H.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.parkController?.isServeDown())return null
5264
+ const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,M.primaryProviderOf)(s),o=r?.default||{},[i,a]=Object.entries(o)[0]||[],c=n?(0,M.defaultModelFor)(n):i&&a?`${i}/${a}`:null
5265
+ c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,W.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.parkController?.isServeDown())return null
5188
5266
  if(this.openCodePort)return`http://127.0.0.1:${this.openCodePort}`
5189
5267
  if(this.openCodeUrlCache&&Date.now()-this.openCodeUrlCache.timestamp<3e4)return this.openCodeUrlCache.url
5190
- const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
5268
+ const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
5191
5269
  return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async register(){const e=this.readProjectConfig()
5192
5270
  if(!e?.id)throw new Error("No project configuration found")
5193
- this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.cachedDefaultModel&&this.lastAgentResyncModel!==this.cachedDefaultModel&&this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Post-registration agent markdown re-sync failed",{error:(0,H.getErrorMessage)(e)})),this.startParkController(),this.startProjectSubsystems("register").catch(e=>{this.logger.error("project subsystems failed to start from register()",{error:(0,H.getErrorMessage)(e)})}),this.clientVersion=(0,i.ownVersion)()
5194
- try{const e=(0,Pe.findOpencodeDir)()
5195
- if(e){const t=JSON.parse(D.default.readFileSync(O.default.join(e,"package.json"),"utf8"))
5196
- this.opencodeVersion=t.version}}catch{}const t={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:q.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion,...this.registeredOnce?{}:{boot:!0}}
5271
+ this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.cachedDefaultModel&&this.lastAgentResyncModel!==this.cachedDefaultModel&&this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Post-registration agent markdown re-sync failed",{error:(0,W.getErrorMessage)(e)})),this.startParkController(),this.startProjectSubsystems("register").catch(e=>{this.logger.error("project subsystems failed to start from register()",{error:(0,W.getErrorMessage)(e)})}),this.clientVersion=(0,i.ownVersion)()
5272
+ try{const e=(0,Oe.findOpencodeDir)()
5273
+ if(e){const t=JSON.parse(q.default.readFileSync(D.default.join(e,"package.json"),"utf8"))
5274
+ this.opencodeVersion=t.version}}catch{}const t={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:N.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion,...this.registeredOnce?{}:{boot:!0}}
5197
5275
  this.logger.info("Registering with remote",{projectId:this.projectId})
5198
5276
  const r=await this.apiClient.post("/api/servers/register",t)
5199
5277
  if(this.registeredOnce=!0,r.success&&r.data?.serverId){this.serverId=r.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId}),this.startHeartbeat()
@@ -5203,18 +5281,18 @@ for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] $
5203
5281
  this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}async applyVersionWarnings(e){if(e.map(e=>`${e.component}:${e.severity}`).join(",")!==this.versionWarnings.map(e=>`${e.component}:${e.severity}`).join(",")){for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] ${t.message}`),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${t.message}\x1b[0m\n`)):(this.logger.warn(`[UPDATE RECOMMENDED] ${t.message}`),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
5204
5282
  this.versionWarnings=e,!e.some(e=>"required"===e.severity)&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){if(!this.serverId)return
5205
5283
  if(this.heartbeatInterval)return
5206
- this.registryHeartbeatInterval=setInterval(()=>{if(this.projectId)try{(0,_.touchHeartbeat)(this.projectId,{projectId:this.projectId,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:this.startTime.toISOString()})}catch{}},3e4),"function"==typeof this.registryHeartbeatInterval.unref&&this.registryHeartbeatInterval.unref(),this.vanishedSessionSweepInterval=setInterval(()=>{this.reapVanishedSessions()},6e5),"function"==typeof this.vanishedSessionSweepInterval.unref&&this.vanishedSessionSweepInterval.unref()
5207
- const e=async()=>{if(this.projectId)try{const e=await(0,de.fetchProviderUsage)(this.options.projectPath)
5284
+ this.registryHeartbeatInterval=setInterval(()=>{if(this.projectId)try{(0,T.touchHeartbeat)(this.projectId,{projectId:this.projectId,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:this.startTime.toISOString()})}catch{}},3e4),"function"==typeof this.registryHeartbeatInterval.unref&&this.registryHeartbeatInterval.unref(),this.vanishedSessionSweepInterval=setInterval(()=>{this.reapVanishedSessions()},6e5),"function"==typeof this.vanishedSessionSweepInterval.unref&&this.vanishedSessionSweepInterval.unref()
5285
+ const e=async()=>{if(this.projectId)try{const e=await(0,ue.fetchProviderUsage)(this.options.projectPath)
5208
5286
  e.length&&await this.apiClient.post(`/api/projects/${this.projectId}/plan-usage-readings`,{providers:e})}catch{}}
5209
5287
  setTimeout(()=>{e()},2e4).unref?.(),this.planUsageInterval=setInterval(()=>{e()},6e5),"function"==typeof this.planUsageInterval.unref&&this.planUsageInterval.unref(),this.runningReportInterval=setInterval(()=>{this.reportRunningSessions(!0)},15e3),"function"==typeof this.runningReportInterval.unref&&this.runningReportInterval.unref(),this.heartbeatInterval=setInterval(async()=>{try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings??[]
5210
- await this.applyVersionWarnings(t)}catch(e){const t=(0,H.getErrorMessage)(e)
5288
+ await this.applyVersionWarnings(t)}catch(e){const t=(0,W.getErrorMessage)(e)
5211
5289
  if(t.includes("Server not found or inactive")){this.logger.warn("Heartbeat hit a stale registration; re-registering",{staleServerId:this.serverId})
5212
- try{await this.register(),this.logger.info("Re-registered after stale heartbeat",{serverId:this.serverId})}catch(e){this.logger.warn("Re-registration failed; will retry on next heartbeat",{error:(0,H.getErrorMessage)(e)})}return}this.logger.warn("Heartbeat failed",{error:t})}},3e5)}noteSessionRunning(e,t){t!==this.runningSessions.has(e)&&(t?this.runningSessions.add(e):this.runningSessions.delete(e),this.runningReportDebounce&&clearTimeout(this.runningReportDebounce),this.runningReportDebounce=setTimeout(()=>{this.reportRunningSessions()},300),"function"==typeof this.runningReportDebounce.unref&&this.runningReportDebounce.unref())}async reportRunningSessions(e=!1){if(!this.projectId)return
5290
+ try{await this.register(),this.logger.info("Re-registered after stale heartbeat",{serverId:this.serverId})}catch(e){this.logger.warn("Re-registration failed; will retry on next heartbeat",{error:(0,W.getErrorMessage)(e)})}return}this.logger.warn("Heartbeat failed",{error:t})}},3e5)}noteSessionRunning(e,t){t!==this.runningSessions.has(e)&&(t?this.runningSessions.add(e):this.runningSessions.delete(e),this.runningReportDebounce&&clearTimeout(this.runningReportDebounce),this.runningReportDebounce=setTimeout(()=>{this.reportRunningSessions()},300),"function"==typeof this.runningReportDebounce.unref&&this.runningReportDebounce.unref())}async reportRunningSessions(e=!1){if(!this.projectId)return
5213
5291
  const t=Array.from(this.runningSessions).sort(),r=t.join(",")
5214
5292
  if(e||r!==this.lastRunningReport){this.lastRunningReport=r
5215
- try{await this.apiClient.post(`/api/projects/${this.projectId}/session-activity`,{running:t,at:(new Date).toISOString()})}catch(e){this.lastRunningReport="",this.logger.debug("Running-sessions report failed",{error:(0,H.getErrorMessage)(e)})}}}startSSEEventForwarder(){this.sseForwarderInterval||(this.logger.info("Starting SSE event forwarder"),this.sseForwarderInterval=setInterval(async()=>{await this.flushSSEEvents()},2e3),this.connectToOpenCodeSSE())}stopSSEEventForwarder(){this.sseConnectGeneration++
5293
+ try{await this.apiClient.post(`/api/projects/${this.projectId}/session-activity`,{running:t,at:(new Date).toISOString()})}catch(e){this.lastRunningReport="",this.logger.debug("Running-sessions report failed",{error:(0,W.getErrorMessage)(e)})}}}startSSEEventForwarder(){this.sseForwarderInterval||(this.logger.info("Starting SSE event forwarder"),this.sseForwarderInterval=setInterval(async()=>{await this.flushSSEEvents()},2e3),this.connectToOpenCodeSSE())}stopSSEEventForwarder(){this.sseConnectGeneration++
5216
5294
  for(const e of this.sseReconnectTimers)clearTimeout(e)
5217
- this.sseReconnectTimers.clear(),this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch(e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,H.getErrorMessage)(e)}))}scheduleSSEReconnect(e){const t=setTimeout(()=>{this.sseReconnectTimers.delete(t),this.connectToOpenCodeSSE()},e)
5295
+ this.sseReconnectTimers.clear(),this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch(e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,W.getErrorMessage)(e)}))}scheduleSSEReconnect(e){const t=setTimeout(()=>{this.sseReconnectTimers.delete(t),this.connectToOpenCodeSSE()},e)
5218
5296
  this.sseReconnectTimers.add(t)}async connectToOpenCodeSSE(){const e=this.sseConnectGeneration,r=()=>e!==this.sseConnectGeneration,s=this.getOpenCodeUrl()
5219
5297
  if(!s)return void(this.sseForwarderInterval&&!r()&&(this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),this.scheduleSSEReconnect(5e3)))
5220
5298
  const n=`${s}/event`
@@ -5229,41 +5307,41 @@ if(r()){try{await s.cancel()}catch{}return}if(e)break
5229
5307
  const o=i.decode(n,{stream:!0})
5230
5308
  if(l){const e=o.indexOf("\n")
5231
5309
  if(-1===e)continue
5232
- l=!1,a=o.slice(e+1),u||this.logger.warn("Oversized SSE event discarded as it arrived",{type:d})}else if(a+=o,a.length>ut&&-1===a.indexOf("\n")&&!(a.length<=c.SESSION_DIFF_PARSE_CEILING&&(0,c.trimmableWhenWhole)(a.replace(/^data: /,"")))){d=(0,c.sseEventTypeOf)(a)
5310
+ l=!1,a=o.slice(e+1),u||this.logger.warn("Oversized SSE event discarded as it arrived",{type:d})}else if(a+=o,a.length>ht&&-1===a.indexOf("\n")&&!(a.length<=c.SESSION_DIFF_PARSE_CEILING&&(0,c.trimmableWhenWhole)(a.replace(/^data: /,"")))){d=(0,c.sseEventTypeOf)(a)
5233
5311
  const e=(0,c.trimOversizedSseEvent)(a.replace(/^data: /,""),Number.POSITIVE_INFINITY)
5234
5312
  u=!!e,e?(this.sseEventBuffer.push({type:e.type,data:e,at:Date.now()}),this.logger.debug("Oversized SSE event forwarded as a summary",{type:d})):(0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++),l=!0,a=""
5235
5313
  continue}const h=a.split("\n")
5236
5314
  a=h.pop()||""
5237
5315
  for(const e of h)if(e.startsWith("data: ")){let r=null
5238
- if(e.length>ut&&(r=(0,c.trimOversizedSseEvent)(e.slice(6)),r&&"session.diff"!==r.type&&JSON.stringify(r).length>ut&&(r=null)),r)this.logger.debug("Oversized SSE event forwarded as a summary",{type:r.type,chars:e.length})
5239
- else if(e.length>ut){const t=(0,c.sseEventTypeOf)(e)
5240
- 0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++,this.logger.warn("Oversized SSE event dropped before parsing",{type:t,chars:e.length,limit:ut})
5316
+ if(e.length>ht&&(r=(0,c.trimOversizedSseEvent)(e.slice(6)),r&&"session.diff"!==r.type&&JSON.stringify(r).length>ht&&(r=null)),r)this.logger.debug("Oversized SSE event forwarded as a summary",{type:r.type,chars:e.length})
5317
+ else if(e.length>ht){const t=(0,c.sseEventTypeOf)(e)
5318
+ 0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++,this.logger.warn("Oversized SSE event dropped before parsing",{type:t,chars:e.length,limit:ht})
5241
5319
  continue}try{const s=r??JSON.parse(e.slice(6)),n=s.type||"unknown"
5242
5320
  this.sseEventBuffer.push({type:n,data:s,at:Date.now()})
5243
5321
  {const e=s?.properties?.sessionID||s?.sessionID
5244
5322
  if(e)if("session.busy"===n?this.noteSessionRunning(e,!0):"session.idle"!==n&&"session.error"!==n&&"session.retry"!==n||this.noteSessionRunning(e,!1),"session.error"===n)Promise.resolve().then(()=>t.t(86116,23)).then(t=>t.notifyInboxChatSessionError(e,s?.properties?.error)).catch(()=>{}),this.stuckSessionWatchdog?.turnFailed(e,s?.properties?.error)
5245
5323
  else if("session.status"===n){const t=s?.properties?.status?.type
5246
- "running"===t||"busy"===t?this.noteSessionRunning(e,!0):"idle"!==t&&"retry"!==t&&"error"!==t||this.noteSessionRunning(e,!1)}}if("server.heartbeat"===n||"server.connected"===n||(0,le.isKeepWarmTick)(s?.properties?.sessionID??s?.properties?.info?.sessionID??s?.properties?.part?.sessionID??s?.sessionID)||this.parkController?.noteActivity(),this.handleSSEHookEvent(s).catch(e=>this.logger.warn("SSE hook event processing failed",{type:s?.type,error:(0,H.getErrorMessage)(e)})),"message.part.updated"===n&&"step-finish"===s?.properties?.part?.type){const e=String(s?.properties?.part?.sessionID??"")
5324
+ "running"===t||"busy"===t?this.noteSessionRunning(e,!0):"idle"!==t&&"retry"!==t&&"error"!==t||this.noteSessionRunning(e,!1)}}if("server.heartbeat"===n||"server.connected"===n||(0,de.isKeepWarmTick)(s?.properties?.sessionID??s?.properties?.info?.sessionID??s?.properties?.part?.sessionID??s?.sessionID)||this.parkController?.noteActivity(),this.handleSSEHookEvent(s).catch(e=>this.logger.warn("SSE hook event processing failed",{type:s?.type,error:(0,W.getErrorMessage)(e)})),"message.part.updated"===n&&"step-finish"===s?.properties?.part?.type){const e=String(s?.properties?.part?.sessionID??"")
5247
5325
  if(e&&!this.usageStepTimers.has(e)){const t=setTimeout(()=>{this.usageStepTimers.delete(e),this.recordUsageForSession(e).catch(()=>{})},2e3)
5248
- "function"==typeof t.unref&&t.unref(),this.usageStepTimers.set(e,t)}}if("session.idle"===n){this.flushSSEEvents().catch(e=>this.logger.debug("Immediate SSE flush failed",{error:(0,H.getErrorMessage)(e)})),this.recordUsageForSession(s?.properties?.sessionID||s?.sessionID||"").catch(()=>{})
5326
+ "function"==typeof t.unref&&t.unref(),this.usageStepTimers.set(e,t)}}if("session.idle"===n){this.flushSSEEvents().catch(e=>this.logger.debug("Immediate SSE flush failed",{error:(0,W.getErrorMessage)(e)})),this.recordUsageForSession(s?.properties?.sessionID||s?.sessionID||"").catch(()=>{})
5249
5327
  const e=s?.properties?.sessionID||s?.sessionID
5250
- e&&(0,G.notifySessionIdle)(e)}if("session.status"===n&&"idle"===s?.properties?.status?.type){const e=s?.properties?.sessionID||s?.sessionID
5251
- e&&(0,G.notifySessionIdle)(e)}"permission.asked"===n&&this.rejectPermissionAsk(s).catch(e=>this.logger.warn("Permission auto-reject failed",{error:(0,H.getErrorMessage)(e)}))}catch{}}}}catch(e){"AbortError"===e?.name||String(e?.message).includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,H.getErrorMessage)(e)})}this.sseForwarderInterval&&!r()&&(this.logger.info("SSE forwarder reconnecting in 3s"),this.scheduleSSEReconnect(3e3))})()}catch(e){"AbortError"===e?.name||String(e?.message).includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,H.getErrorMessage)(e)}),this.sseForwarderInterval&&!r()&&this.scheduleSSEReconnect(5e3))}}async handleSSEHookEvent(e){const t=e?.type
5328
+ e&&(0,K.notifySessionIdle)(e)}if("session.status"===n&&"idle"===s?.properties?.status?.type){const e=s?.properties?.sessionID||s?.sessionID
5329
+ e&&(0,K.notifySessionIdle)(e)}"permission.asked"===n&&this.rejectPermissionAsk(s).catch(e=>this.logger.warn("Permission auto-reject failed",{error:(0,W.getErrorMessage)(e)}))}catch{}}}}catch(e){"AbortError"===e?.name||String(e?.message).includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,W.getErrorMessage)(e)})}this.sseForwarderInterval&&!r()&&(this.logger.info("SSE forwarder reconnecting in 3s"),this.scheduleSSEReconnect(3e3))})()}catch(e){"AbortError"===e?.name||String(e?.message).includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,W.getErrorMessage)(e)}),this.sseForwarderInterval&&!r()&&this.scheduleSSEReconnect(5e3))}}async handleSSEHookEvent(e){const t=e?.type
5252
5330
  if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
5253
5331
  if(!t)return
5254
5332
  this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,r=t?.id||""
5255
5333
  if(!r)return
5256
5334
  const s=t?.metadata
5257
5335
  if(s?.agentType&&this.projectId){const e=t?.parentID||null
5258
- this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active"}).catch(e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,H.getErrorMessage)(e)})})}setTimeout(async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:r}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:r,variables:{sessionId:r}})},500)}}async recordUsageForSession(e){if(!this.projectId||!e)return
5336
+ this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active"}).catch(e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,W.getErrorMessage)(e)})})}setTimeout(async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:r}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:r,variables:{sessionId:r}})},500)}}async recordUsageForSession(e){if(!this.projectId||!e)return
5259
5337
  const t=this.getOpenCodeUrl()
5260
5338
  if(t)try{const r=await fetch(`${t}/session/${e}/message?limit=200`,{signal:AbortSignal.timeout(5e3)})
5261
5339
  if(!r.ok)return
5262
- const s=await r.json(),n=this.recordedUsageMsgs.get(e),o=(0,lt.turnSubjects)(s),i=[]
5340
+ const s=await r.json(),n=this.recordedUsageMsgs.get(e),o=(0,dt.turnSubjects)(s),i=[]
5263
5341
  for(const t of s){const r=t?.info
5264
5342
  if(!r||"assistant"!==r.role||!r.id)continue
5265
5343
  if(n&&n.has(String(r.id)))continue
5266
- const{input:s,output:a,reasoning:c,cacheRead:l,cacheWrite:d,cost:u,hasUsage:h}=(0,we.tokensOfMessage)(t)
5344
+ const{input:s,output:a,reasoning:c,cacheRead:l,cacheWrite:d,cost:u,hasUsage:h}=(0,ye.tokensOfMessage)(t)
5267
5345
  if(!h)continue
5268
5346
  const p=!!r.time?.completed
5269
5347
  i.push({finished:p,sessionId:e,messageId:String(r.id),agentType:r.agent??r.mode??null,provider:r.providerID??null,model:r.modelID??null,inputTokens:s,outputTokens:a,reasoningTokens:c,cacheReadTokens:l,cacheWriteTokens:d,cost:u,...r.parentID?{promptId:String(r.parentID)}:{},...r.parentID&&o.get(String(r.parentID))?{threadRef:o.get(String(r.parentID))}:{}})}if(0===i.length)return
@@ -5271,9 +5349,9 @@ await this.apiClient.post(`/api/projects/${this.projectId}/usage-events`,{events
5271
5349
  let a=n
5272
5350
  if(!a){if(a=new Set,this.recordedUsageMsgs.size>=300)for(const e of Array.from(this.recordedUsageMsgs.keys()).slice(0,50))this.recordedUsageMsgs.delete(e)
5273
5351
  this.recordedUsageMsgs.set(e,a)}for(const e of i)e.finished&&a.add(e.messageId)
5274
- this.logger.debug("Recorded session usage to remote",{sessionId:e,count:i.length})}catch(t){this.logger.debug("Usage recording failed",{sessionId:e,error:(0,H.getErrorMessage)(t)})}}async sweepUsage(e,t=8e3){const r=this.getOpenCodeUrl()
5352
+ this.logger.debug("Recorded session usage to remote",{sessionId:e,count:i.length})}catch(t){this.logger.debug("Usage recording failed",{sessionId:e,error:(0,W.getErrorMessage)(t)})}}async sweepUsage(e,t=8e3){const r=this.getOpenCodeUrl()
5275
5353
  if(!this.projectId||!r)return
5276
- const s=await(0,ct.sweepSessionUsage)(r,e,e=>this.recordUsageForSession(e),t)
5354
+ const s=await(0,lt.sweepSessionUsage)(r,e,e=>this.recordUsageForSession(e),t)
5277
5355
  s.error?this.logger.warn("usage sweep failed",{error:s.error}):s.outOfTime&&this.logger.warn("usage sweep ran out of time; some sessions not read",{read:s.read})}async rejectPermissionAsk(e){const t=e?.properties||{},r=t?.id
5278
5356
  if(!r)return
5279
5357
  const s=this.getOpenCodeUrl()
@@ -5286,7 +5364,7 @@ if(!r.ok)return
5286
5364
  const s=await r.json()
5287
5365
  if(!Array.isArray(s)||0===s.length)return
5288
5366
  this.logger.info("Sweeping pending permission asks",{count:s.length})
5289
- for(const e of s)await this.rejectPermissionAsk({properties:e}).catch(t=>this.logger.warn("Pending-permission reject failed",{permissionId:e?.id,sessionId:e?.sessionID,permission:e?.permission,patterns:Array.isArray(e?.patterns)?e.patterns.join(", "):void 0,error:(0,H.getErrorMessage)(t)}))}catch(e){this.logger.debug("Pending-permission sweep failed",{error:(0,H.getErrorMessage)(e)})}}async flushSSEEvents(){if(!this.projectId)return
5367
+ for(const e of s)await this.rejectPermissionAsk({properties:e}).catch(t=>this.logger.warn("Pending-permission reject failed",{permissionId:e?.id,sessionId:e?.sessionID,permission:e?.permission,patterns:Array.isArray(e?.patterns)?e.patterns.join(", "):void 0,error:(0,W.getErrorMessage)(t)}))}catch(e){this.logger.debug("Pending-permission sweep failed",{error:(0,W.getErrorMessage)(e)})}}async flushSSEEvents(){if(!this.projectId)return
5290
5368
  if(0===this.sseEventBuffer.length&&0===this.droppedEventCount)return
5291
5369
  const e=Date.now()-3e5
5292
5370
  let t=0
@@ -5294,36 +5372,36 @@ for(;t<this.sseEventBuffer.length&&this.sseEventBuffer[t].at<e;)t++
5294
5372
  if(t>0&&(this.sseEventBuffer.splice(0,t),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=t),!this.hasRemoteEventConsumers){if(this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
5295
5373
  this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}return}const r=this.sseEventBuffer.splice(0),s=this.droppedEventCount>0?{at:Date.now(),type:"alchemy.events.dropped",data:{count:this.droppedEventCount,firstDropAt:this.firstDropAt,reportedAt:Date.now()}}:null
5296
5374
  s&&r.unshift(s)
5297
- try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:r}),s&&(this.logger.warn("Reported dropped SSE events to remote",{count:s.data.count,firstDropAt:s.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(e){this.logger.debug("SSE event flush failed",{error:(0,H.getErrorMessage)(e),count:r.length})
5375
+ try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:r}),s&&(this.logger.warn("Reported dropped SSE events to remote",{count:s.data.count,firstDropAt:s.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(e){this.logger.debug("SSE event flush failed",{error:(0,W.getErrorMessage)(e),count:r.length})
5298
5376
  const t=s?r.slice(1):r
5299
5377
  if(this.sseEventBuffer.unshift(...t),this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
5300
5378
  this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce(),this.remoteLastOkAt=Date.now(),this.remoteFailures=0,this.remoteLastError=null}catch(e){if(!this.isPolling)return
5301
- this.remoteFailures+=1,this.remoteLastError=(0,H.getErrorMessage)(e),this.logger.debug("Long-poll error, retrying in 5s",{error:(0,H.getErrorMessage)(e)}),await new Promise(e=>setTimeout(e,5e3))}}async readMainSha(){try{this.mainBranchName||(this.mainBranchName=await(0,m.resolveDefaultBranch)(this.options.projectPath))
5302
- const e=(await(0,Be.simpleGit)(this.options.projectPath).revparse([this.mainBranchName])).trim()
5379
+ this.remoteFailures+=1,this.remoteLastError=(0,W.getErrorMessage)(e),this.logger.debug("Long-poll error, retrying in 5s",{error:(0,W.getErrorMessage)(e)}),await new Promise(e=>setTimeout(e,5e3))}}async readMainSha(){try{this.mainBranchName||(this.mainBranchName=await(0,m.resolveDefaultBranch)(this.options.projectPath))
5380
+ const e=(await(0,He.simpleGit)(this.options.projectPath).revparse([this.mainBranchName])).trim()
5303
5381
  return/^[0-9a-f]{40}$/.test(e)?e:null}catch{return this.mainBranchName=null,null}}async pollOnce(){if(this.isPolling&&this.projectId&&!this.updateBlocked)try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=30000`,t={"Content-Type":"application/json",Accept:"application/json"}
5304
- this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,st.machineCallerHeaders)(this.options.remoteServerUrl)),this.clientVersion&&(t["X-Client-Version"]=this.clientVersion),this.opencodeVersion&&(t["X-OpenCode-Version"]=this.opencodeVersion),this.port&&(t["X-Action-Server-Port"]=String(this.port)),t["X-Client-Action-Types"]=oe
5382
+ this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,nt.machineCallerHeaders)(this.options.remoteServerUrl)),this.clientVersion&&(t["X-Client-Version"]=this.clientVersion),this.opencodeVersion&&(t["X-OpenCode-Version"]=this.opencodeVersion),this.port&&(t["X-Action-Server-Port"]=String(this.port)),t["X-Client-Action-Types"]=ie
5305
5383
  const r=await this.readMainSha()
5306
5384
  r&&(t["X-Main-Sha"]=r)
5307
5385
  const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
5308
5386
  if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
5309
5387
  const n=await s.json(),o={success:!0,data:n.data||n}
5310
5388
  if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,anthropicCompaction:s,defaultModelUnchosen:n}=o.data.settings
5311
- "boolean"==typeof n&&(this.defaultModelUnchosen=n),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r),"boolean"==typeof s&&await this.applyAnthropicCompaction(s)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
5312
- this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,H.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
5389
+ "boolean"==typeof n&&(this.defaultModelUnchosen=n),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r),"boolean"==typeof s&&await this.applyAnthropicCompaction(s)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new x.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,nt.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,T.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
5390
+ this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,W.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
5313
5391
  if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
5314
5392
  this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
5315
5393
  for(const t of e)this.scheduleRunner?.nudge(t).catch(e=>{this.logger.warn("Schedule nudge dispatch failed",{docId:t,error:String(e)})})}o.success&&Array.isArray(o.data?.versionWarnings)&&this.applyVersionWarnings(o.data.versionWarnings),o.success&&o.data?.actions?.length>0&&this.dispatchActions(o.data.actions)}catch(e){throw e}}dispatchActions(e){const t=[],r=[]
5316
- for(const s of e)dt.has(s.type)?t.push(s):r.push(s)
5317
- for(const e of t)this.inFlightProxyActions++,this.processAction(e).catch(t=>this.logger.warn("Proxy action failed",{actionId:e.id,error:(0,H.getErrorMessage)(t)})).finally(()=>{this.inFlightProxyActions--})
5394
+ for(const s of e)ut.has(s.type)?t.push(s):r.push(s)
5395
+ for(const e of t)this.inFlightProxyActions++,this.processAction(e).catch(t=>this.logger.warn("Proxy action failed",{actionId:e.id,error:(0,W.getErrorMessage)(t)})).finally(()=>{this.inFlightProxyActions--})
5318
5396
  let s=0
5319
5397
  for(const e of r)this.queuedRegularActionIds.has(e.id)?this.logger.debug("Skipping re-delivered action already held locally",{actionId:e.id,type:e.type}):(this.queuedRegularActionIds.add(e.id),this.pendingRegularActions.push(e),s++)
5320
5398
  s>0&&this.drainRegularActions()}async drainRegularActions(){if(!this.isProcessingAction){this.isProcessingAction=!0
5321
5399
  try{for(;this.isPolling;){const e=this.pendingRegularActions.shift()
5322
5400
  if(!e)break
5323
- try{await this.processAction(e)}catch(t){this.logger.warn("Action processing failed",{actionId:e.id,type:e.type,error:(0,H.getErrorMessage)(t)})}finally{this.queuedRegularActionIds.delete(e.id)}}}finally{for(const e of this.pendingRegularActions)this.queuedRegularActionIds.delete(e.id)
5401
+ try{await this.processAction(e)}catch(t){this.logger.warn("Action processing failed",{actionId:e.id,type:e.type,error:(0,W.getErrorMessage)(t)})}finally{this.queuedRegularActionIds.delete(e.id)}}}finally{for(const e of this.pendingRegularActions)this.queuedRegularActionIds.delete(e.id)
5324
5402
  this.pendingRegularActions.length=0,this.isProcessingAction=!1}}}async syncProjectSettings(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}`)
5325
5403
  if(e.success&&e.data?.metadata){const{defaultModel:t}=e.data.metadata
5326
- this.cachedReceipts=e.data.metadata.receipts??null,this.cachedIntegrations=e.data.metadata.integrations??null,void 0!==t&&this.updateDefaultModel(t)}}catch(e){this.logger.debug("Settings sync failed",{error:(0,H.getErrorMessage)(e)})}}async sweepOrphanTestRuns(e){try{const r=(await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/test-runs?limit=100&offset=${e}`,500)).filter(t=>{if(!t||"pending"!==t.status&&"running"!==t.status)return!1
5404
+ this.cachedReceipts=e.data.metadata.receipts??null,this.cachedIntegrations=e.data.metadata.integrations??null,void 0!==t&&this.updateDefaultModel(t)}}catch(e){this.logger.debug("Settings sync failed",{error:(0,W.getErrorMessage)(e)})}}async sweepOrphanTestRuns(e){try{const r=(await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/test-runs?limit=100&offset=${e}`,500)).filter(t=>{if(!t||"pending"!==t.status&&"running"!==t.status)return!1
5327
5405
  const r=Date.parse(t.createdAt??t.created_at??"")
5328
5406
  return Number.isFinite(r)&&r<e})
5329
5407
  if(0===r.length)return
@@ -5331,22 +5409,22 @@ const s=new Map
5331
5409
  for(const e of r){const t=e.requestId??e.request_local_id
5332
5410
  t&&e.id&&s.set(t,[...s.get(t)??[],e.id])}const n={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},{handleRunTestsAction:o}=await Promise.resolve().then(()=>t(76555))
5333
5411
  for(const[e,t]of s){this.logger.warn("Re-dispatching test run(s) orphaned by restart",{requestId:e,testRunIds:t})
5334
- try{await o({requestId:e,testRunIds:t,mode:"auto"},n)}catch(t){this.logger.warn("Orphan test-run re-dispatch failed (stuck-run watchdog will backstop)",{requestId:e,error:(0,H.getErrorMessage)(t)})}}}catch(e){this.logger.warn("Orphan test-run sweep failed (non-fatal)",{error:(0,H.getErrorMessage)(e)})}}async processAction(e){this.parkController?.noteActivity(),this.logger.info("Processing action",{actionId:e.id,type:e.type})
5412
+ try{await o({requestId:e,testRunIds:t,mode:"auto"},n)}catch(t){this.logger.warn("Orphan test-run re-dispatch failed (stuck-run watchdog will backstop)",{requestId:e,error:(0,W.getErrorMessage)(t)})}}}catch(e){this.logger.warn("Orphan test-run sweep failed (non-fatal)",{error:(0,W.getErrorMessage)(e)})}}async processAction(e){this.parkController?.noteActivity(),this.logger.info("Processing action",{actionId:e.id,type:e.type})
5335
5413
  const t=Date.now()
5336
5414
  let r
5337
- try{const s={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},n=await(0,N.executeAction)(e.type,e.payload||{},s)
5338
- r={success:n.success,result:n.refused?{...n.data??{},refused:!0}:n.data,error:n.message||n.error,duration:Date.now()-t}}catch(e){r={success:!1,error:(0,H.getErrorMessage)(e),duration:Date.now()-t}}const n={...r,claimToken:e.claimToken}
5339
- try{await this.apiClient.post(`/api/actions/${e.id}/result`,n),r.success?(this.logger.info("Action processed",{actionId:e.id,type:e.type,success:!0,duration:r.duration}),"prune-worktree"===e.type&&this.sweepUsage(Date.now()-216e5).catch(()=>{})):r.result?.refused?this.logger.info("Action refused (by design)",{actionId:e.id,type:e.type,why:r.error}):this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r.error})}catch(t){this.logger.error("Failed to POST action result after retries; posting it again later",{actionId:e.id,type:e.type,workSucceeded:r.success,postError:(0,H.getErrorMessage)(t)}),(0,s.redeliverResult)({post:()=>this.apiClient.post(`/api/actions/${e.id}/result`,n)}).then(t=>this.logger[t.delivered?"info":"error"](t.delivered?"Action result posted on a later try":"Action result never reached the remote",{actionId:e.id,type:e.type,tries:t.tries,...t.delivered?{}:{postError:(0,H.getErrorMessage)(t.lastError)}}))}}startLinearAutoImportPoller(){const e=async()=>{try{if(!this.projectId)return
5340
- if(!await(0,L.readLinearKey)())return
5415
+ try{const s={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},n=await(0,F.executeAction)(e.type,e.payload||{},s)
5416
+ r={success:n.success,result:n.refused?{...n.data??{},refused:!0}:n.data,error:n.message||n.error,duration:Date.now()-t}}catch(e){r={success:!1,error:(0,W.getErrorMessage)(e),duration:Date.now()-t}}const n={...r,claimToken:e.claimToken}
5417
+ try{await this.apiClient.post(`/api/actions/${e.id}/result`,n),r.success?(this.logger.info("Action processed",{actionId:e.id,type:e.type,success:!0,duration:r.duration}),"prune-worktree"===e.type&&this.sweepUsage(Date.now()-216e5).catch(()=>{})):r.result?.refused?this.logger.info("Action refused (by design)",{actionId:e.id,type:e.type,why:r.error}):this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r.error})}catch(t){this.logger.error("Failed to POST action result after retries; posting it again later",{actionId:e.id,type:e.type,workSucceeded:r.success,postError:(0,W.getErrorMessage)(t)}),(0,s.redeliverResult)({post:()=>this.apiClient.post(`/api/actions/${e.id}/result`,n)}).then(t=>this.logger[t.delivered?"info":"error"](t.delivered?"Action result posted on a later try":"Action result never reached the remote",{actionId:e.id,type:e.type,tries:t.tries,...t.delivered?{}:{postError:(0,W.getErrorMessage)(t.lastError)}}))}}startLinearAutoImportPoller(){const e=async()=>{try{if(!this.projectId)return
5418
+ if(!await(0,U.readLinearKey)())return
5341
5419
  await this.syncProjectSettings().catch(()=>{})
5342
5420
  const e=this.cachedIntegrations?.linear?.autoImport
5343
5421
  if(!0!==e?.enabled)return
5344
- const t=e?.label?.trim()||"alchemylab",r=e?.team?.trim()||void 0,s=await(0,L.listLinearIssuesByLabel)(t,20,r)
5422
+ const t=e?.label?.trim()||"alchemylab",r=e?.team?.trim()||void 0,s=await(0,U.listLinearIssuesByLabel)(t,20,r)
5345
5423
  if(0===s.length)return
5346
- const n={projectId:this.projectId,apiClient:this.apiClient},o=await(0,U.knownLinearIssues)(n,s.map(e=>e.id)),i=new Set([...Object.keys(o.imported),...o.failed]),a=s.filter(e=>!i.has(e.id)).slice(0,3)
5347
- for(const e of a)try{const t=await(0,U.importLinearIssue)(n,e,{auto:!0})
5348
- this.logger.info("linear auto-import: filed a draft",{issue:e.identifier,requestId:t})}catch(t){if(t instanceof U.AlreadyImportedError)continue
5349
- this.logger.warn("linear auto-import: issue not imported",{issue:e.identifier,error:(0,H.getErrorMessage)(t)}),await this.apiClient.post(`/api/projects/${this.projectId}/integrations/linear/import-failures`,{issueId:e.id,issueKey:e.identifier,title:e.title,error:(0,H.getErrorMessage)(t)}).catch(t=>this.logger.warn("linear auto-import: the failure could not be recorded; it is tried again next tick",{issue:e.identifier,error:(0,H.getErrorMessage)(t)}))}}catch(e){this.logger.warn("linear auto-import tick failed",{error:(0,L.linearErrorInWords)(e)})}},t=setInterval(()=>{e()},6e4)
5424
+ const n={projectId:this.projectId,apiClient:this.apiClient},o=await(0,B.knownLinearIssues)(n,s.map(e=>e.id)),i=new Set([...Object.keys(o.imported),...o.failed]),a=s.filter(e=>!i.has(e.id)).slice(0,3)
5425
+ for(const e of a)try{const t=await(0,B.importLinearIssue)(n,e,{auto:!0})
5426
+ this.logger.info("linear auto-import: filed a draft",{issue:e.identifier,requestId:t})}catch(t){if(t instanceof B.AlreadyImportedError)continue
5427
+ this.logger.warn("linear auto-import: issue not imported",{issue:e.identifier,error:(0,W.getErrorMessage)(t)}),await this.apiClient.post(`/api/projects/${this.projectId}/integrations/linear/import-failures`,{issueId:e.id,issueKey:e.identifier,title:e.title,error:(0,W.getErrorMessage)(t)}).catch(t=>this.logger.warn("linear auto-import: the failure could not be recorded; it is tried again next tick",{issue:e.identifier,error:(0,W.getErrorMessage)(t)}))}}catch(e){this.logger.warn("linear auto-import tick failed",{error:(0,U.linearErrorInWords)(e)})}},t=setInterval(()=>{e()},6e4)
5350
5428
  "function"==typeof t.unref&&t.unref()}startExperimentReaper(){const e=async()=>{try{const e=this.getOpenCodeUrl()
5351
5429
  if(!e)return
5352
5430
  const r=await fetch(`${e}/session?limit=1000`,{signal:AbortSignal.timeout(5e3)})
@@ -5365,40 +5443,40 @@ const t=await e.json(),r=t?.data?.frontendUrl
5365
5443
  "string"==typeof r&&/^https?:\/\//.test(r)&&(this.frontendOrigin=new URL(r).origin,this.logger.info("Browser requests allowed from the app origin",{origin:this.frontendOrigin}))}catch{}})()
5366
5444
  const r=await(0,S.ensureSoleInstance)({projectPath:this.options.projectPath,logger:this.logger,retire:this.options.retireTwins})
5367
5445
  if(!r.ok)throw this.logger.error("Refusing to start: "+r.reason),Object.assign(new Error(r.reason),{runningElsewhere:!0===r.runningElsewhere})
5368
- try{await(0,I.migrateWorkCopies)(this.options.projectPath,this.logger)}catch(e){this.logger.warn("Working copies could not be moved out of the project folder (they stay where they are)",{error:(0,H.getErrorMessage)(e)})}this.workCopiesGate.resolve()
5446
+ try{await(0,I.migrateWorkCopies)(this.options.projectPath,this.logger)}catch(e){this.logger.warn("Working copies could not be moved out of the project folder (they stay where they are)",{error:(0,W.getErrorMessage)(e)})}this.workCopiesGate.resolve()
5369
5447
  for(let e=0;e<50;e++)try{await new Promise((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",()=>{this.logger.info("HTTP server started",{port:this.port}),(0,S.writeInstanceLock)(this.options.projectPath,this.port),e()}),this.server.on("error",e=>{e.code,t(e)})}catch(e){t(e)}})
5370
5448
  break}catch(t){if("EADDRINUSE"===t?.code&&e<49){if(0===e&&!1!==this.options.retireTwins&&await(0,S.askStaleTwinToRetire)({port:this.port,projectPath:this.options.projectPath,logger:this.logger})){this.logger.info("Stale twin retired; retrying our preferred port",{port:this.port})
5371
5449
  continue}this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
5372
- continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)}try{const e=O.default.join(this.options.projectPath,".alchemy_lab","action-server.json");(0,te.writeActionServerFile)(this.options.projectPath,this.port,this.localSecret),setTimeout(()=>{this.requestConfigReload("agents ready").catch(()=>{})},4e3),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,H.getErrorMessage)(e)})}(0,C.setPreviewSelfBaseUrl)(`http://localhost:${this.port}`)
5450
+ continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)}try{const e=D.default.join(this.options.projectPath,".alchemy_lab","action-server.json");(0,re.writeActionServerFile)(this.options.projectPath,this.port,this.localSecret),setTimeout(()=>{this.requestConfigReload("agents ready").catch(()=>{})},4e3),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,W.getErrorMessage)(e)})}(0,C.setPreviewSelfBaseUrl)(`http://localhost:${this.port}`)
5373
5451
  try{const e=this.readProjectConfig()
5374
5452
  if(e?.id){const{upsertEntry:r}=await Promise.resolve().then(()=>t(52640))
5375
- r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,H.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,H.getErrorMessage)(e)
5376
- t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,Ie.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
5453
+ r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,W.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,W.getErrorMessage)(e)
5454
+ t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,$e.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
5377
5455
  setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}this.startExperimentReaper(),this.startLinearAutoImportPoller()
5378
5456
  try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=0&settingsOnly=true`,t={"Content-Type":"application/json",Accept:"application/json"}
5379
- this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,st.machineCallerHeaders)(this.options.remoteServerUrl))
5457
+ this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,nt.machineCallerHeaders)(this.options.remoteServerUrl))
5380
5458
  const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
5381
5459
  if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
5382
- "boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,H.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Fe.setContainerProject)(this.projectId),this.startSSEEventForwarder()
5460
+ "boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,W.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Le.setContainerProject)(this.projectId),this.startSSEEventForwarder()
5383
5461
  try{const{resumeInboxChatWatches:e}=await Promise.resolve().then(()=>t.t(86116,23))
5384
- e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,H.getErrorMessage)(e)})}this.apiClient.post(`/api/projects/${this.projectId}/session-messages/expire-orphaned-approvals`,{}).then(e=>{const t=e?.data?.expired??0
5385
- t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,H.getErrorMessage)(e)})}),this.startStuckSessionWatchdog(),this.startScheduleRunner(),(0,ge.ensureOpsDiagnostics)(this.apiClient,this.projectId,this.options.projectPath,this.logger),(0,ae.startSessionCompactor)({getOpenCodeUrl:()=>this.getOpenCodeUrl()??void 0,apiClient:this.apiClient,projectId:this.projectId||void 0,projectName:this.options.projectPath?O.default.basename(this.options.projectPath):void 0,logger:this.logger}),this.keepWarmRunner=(0,le.startKeepWarmRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,getDefaultModel:()=>this.cachedDefaultModel}),this.ownerGuidanceTimer=setInterval(()=>{this.checkOwnerGuidanceApply()},6e4),this.ownerGuidanceTimer.unref?.(),this.workMetricsRunner||(this.workMetricsRunner=(0,he.startWorkMetricsRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})),this.mcpConnectionRunner||(this.mcpConnectionRunner=(0,pe.startMcpConnectionRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")},isBusy:async()=>this.activeMcpCalls.size>0||await this.anySessionBusy()})),(0,ce.startMonitorRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,serveReady:async()=>(await this.ensureServeAlive("monitor-wake"),null!=this.openCodePort&&!this.parkController?.isServeDown()),reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},runOwn:e=>this.runOwn(e),verifyDeliveryEnv:async e=>{let r
5462
+ e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,W.getErrorMessage)(e)})}this.apiClient.post(`/api/projects/${this.projectId}/session-messages/expire-orphaned-approvals`,{}).then(e=>{const t=e?.data?.expired??0
5463
+ t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,W.getErrorMessage)(e)})}),this.startStuckSessionWatchdog(),this.startScheduleRunner(),(0,we.ensureOpsDiagnostics)(this.apiClient,this.projectId,this.options.projectPath,this.logger),(0,ce.startSessionCompactor)({getOpenCodeUrl:()=>this.getOpenCodeUrl()??void 0,apiClient:this.apiClient,projectId:this.projectId||void 0,projectName:this.options.projectPath?D.default.basename(this.options.projectPath):void 0,logger:this.logger}),this.keepWarmRunner=(0,de.startKeepWarmRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,getDefaultModel:()=>this.cachedDefaultModel}),this.ownerGuidanceTimer=setInterval(()=>{this.checkOwnerGuidanceApply()},6e4),this.ownerGuidanceTimer.unref?.(),this.workMetricsRunner||(this.workMetricsRunner=(0,pe.startWorkMetricsRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})),this.mcpConnectionRunner||(this.mcpConnectionRunner=(0,fe.startMcpConnectionRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")},isBusy:async()=>this.activeMcpCalls.size>0||await this.anySessionBusy()})),(0,le.startMonitorRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,serveReady:async()=>(await this.ensureServeAlive("monitor-wake"),null!=this.openCodePort&&!this.parkController?.isServeDown()),reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},runOwn:e=>this.runOwn(e),verifyDeliveryEnv:async e=>{let r
5386
5464
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/environments/${e}`)
5387
5465
  r=t?.data||t}catch{throw new Error("environment fetch failed")}const s=r?.config?.delivery
5388
5466
  if(!r||!s?.targetPath)return{gone:!0,reason:"environment or its delivery config no longer exists"}
5389
5467
  const{verifyDelivery:n}=await Promise.resolve().then(()=>t.t(32504,23)),o=await n(s)
5390
- return{healthy:o.healthy,verified:!0===o.verified,failing:(o.checks||[]).filter(e=>!e.pass).map(e=>e.name).concat(!1===o.healthy?["health"]:[])}},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}}}).catch(e=>{this.logger.error("Monitor fleet-health report failed to post",{error:(0,H.getErrorMessage)(e)})})}})}}startParkController(){if(this.parkController)return
5468
+ return{healthy:o.healthy,verified:!0===o.verified,failing:(o.checks||[]).filter(e=>!e.pass).map(e=>e.name).concat(!1===o.healthy?["health"]:[])}},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}}}).catch(e=>{this.logger.error("Monitor fleet-health report failed to post",{error:(0,W.getErrorMessage)(e)})})}})}}startParkController(){if(this.parkController)return
5391
5469
  if(!this.relaunchOpenCode)return
5392
5470
  const e=this.options.onIdlePark
5393
- this.parkController=new K.ParkController({logger:this.logger,isIdle:()=>this.isProjectIdle(),park:()=>e?(e(),[]):this.executePark(),revive:()=>this.executeRevive()}),this.parkController.start()}parkVeto(e){return this.lastParkVetoes=[e],!1}async idleReport(){const e=await this.isProjectIdle({ignoreWarm:!0})
5394
- return{idle:e,warm:(0,le.keepWarmHoldsUntil)()>Date.now(),lastActivityAt:Date.now()-(this.parkController?.idleForMs()??0),...e?{}:{veto:this.lastParkVetoes[0]}}}async isProjectIdle(e={}){if(this.updateBlocked)return this.parkVeto("update-mode")
5471
+ this.parkController=new z.ParkController({logger:this.logger,isIdle:()=>this.isProjectIdle(),park:()=>e?(e(),[]):this.executePark(),revive:()=>this.executeRevive()}),this.parkController.start()}parkVeto(e){return this.lastParkVetoes=[e],!1}async idleReport(){const e=await this.isProjectIdle({ignoreWarm:!0})
5472
+ return{idle:e,warm:(0,de.keepWarmHoldsUntil)()>Date.now(),lastActivityAt:Date.now()-(this.parkController?.idleForMs()??0),...e?{}:{veto:this.lastParkVetoes[0]}}}async isProjectIdle(e={}){if(this.updateBlocked)return this.parkVeto("update-mode")
5395
5473
  if(this.isProcessingAction||this.inFlightProxyActions>0)return this.parkVeto("processing-action")
5396
5474
  if(this.inFlightServeProxyRequests>0)return this.parkVeto("serve-proxy-in-flight")
5397
5475
  if(this.hasRemoteEventConsumers)return this.parkVeto("ui-event-consumers")
5398
5476
  if(this.activeMcpCalls.size>0||this.pendingConfigReload)return this.parkVeto("mcp-or-config-activity")
5399
- if((0,qe.anyPoolActivity)())return this.parkVeto("pool-activity")
5400
- if((0,Ce.listPendingApprovals)().length>0||(0,_e.listPendingPaintRequests)().length>0)return this.parkVeto("pending-approvals")
5401
- if((0,W.listRunningExecs)().length>0)return this.parkVeto("running-execs")
5477
+ if((0,Ne.anyPoolActivity)())return this.parkVeto("pool-activity")
5478
+ if((0,_e.listPendingApprovals)().length>0||(0,Te.listPendingPaintRequests)().length>0)return this.parkVeto("pending-approvals")
5479
+ if((0,G.listRunningExecs)().length>0)return this.parkVeto("running-execs")
5402
5480
  if((0,C.listPreviewHandles)().length>0)return this.parkVeto("preview-handles")
5403
5481
  if(this.projectId&&(0,j.experimentRegistry)().countForProject(this.projectId)>0)return this.parkVeto("experiments")
5404
5482
  if(this.scheduleRunner?.hasInFlightFirings())return this.parkVeto("schedule-firing")
@@ -5407,55 +5485,55 @@ for(const e of[...this.trackedChildPids])if(e!==r)try{return process.kill(e,0),t
5407
5485
  this.trackedChildPids.delete(e)}const{hasActiveInboxChatTurns:s}=await Promise.resolve().then(()=>t.t(86116,23))
5408
5486
  if(s())return this.parkVeto("inbox-chat-turn")
5409
5487
  const{hasParkVetoingPlannerHandoffWatches:n}=await t.e(991).then(()=>t(18991))
5410
- return n()?this.parkVeto("planner-handoff"):await this.anySessionBusy()?this.parkVeto("session-busy"):!e.ignoreWarm&&(0,le.keepWarmHoldsUntil)()>Date.now()?this.parkVeto("keep-warm-window"):(this.lastParkVetoes=[],!0)}executePark(){const e=new Set(this.trackedChildPids),t=this.readServePid()
5488
+ return n()?this.parkVeto("planner-handoff"):await this.anySessionBusy()?this.parkVeto("session-busy"):!e.ignoreWarm&&(0,de.keepWarmHoldsUntil)()>Date.now()?this.parkVeto("keep-warm-window"):(this.lastParkVetoes=[],!0)}executePark(){const e=new Set(this.trackedChildPids),t=this.readServePid()
5411
5489
  t&&e.add(t),this.killTrackedProcesses()
5412
- try{const e=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5413
- if(D.default.existsSync(e)){const t=JSON.parse(D.default.readFileSync(e,"utf-8"))
5414
- delete t.pid,delete t.port,t.parkedAt=(new Date).toISOString(),D.default.writeFileSync(e,JSON.stringify(t,null,2))}}catch{}return this.stopSSEEventForwarder(),[...e]}async executeRevive(){if(!this.relaunchOpenCode)throw new Error("no serve relaunch path (serve-only mode)")
5490
+ try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5491
+ if(q.default.existsSync(e)){const t=JSON.parse(q.default.readFileSync(e,"utf-8"))
5492
+ delete t.pid,delete t.port,t.parkedAt=(new Date).toISOString(),q.default.writeFileSync(e,JSON.stringify(t,null,2))}}catch{}return this.stopSSEEventForwarder(),[...e]}async executeRevive(){if(!this.relaunchOpenCode)throw new Error("no serve relaunch path (serve-only mode)")
5415
5493
  const e=await this.relaunchOpenCode()
5416
5494
  if(this.parkController?.isStopped()||this.updateBlocked)throw e.pid&&this.killProcessGroup(e.pid),new Error("revival aborted: server stopping or in update mode")
5417
5495
  if(e.pid&&this.trackChildPid(e.pid),!e.success||!e.port)throw e.pid&&(this.killProcessGroup(e.pid),this.trackedChildPids.delete(e.pid)),new Error(e.success?"OpenCode serve relaunch did not announce a port":e.error||"OpenCode serve relaunch failed")
5418
5496
  this.setOpenCodePort(e.port)
5419
- try{const t=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5420
- D.default.writeFileSync(t,JSON.stringify({port:e.port,pid:e.pid,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("Could not write opencode.json after revival",{error:(0,H.getErrorMessage)(e)})}if(this.startSSEEventForwarder(),this.ownerGuidanceStale&&this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance after waking failed; it stays pending",{error:(0,H.getErrorMessage)(e)})),this.projectId){const e=Date.now()
5421
- setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}}async ensureServeAlive(e){if(this.parkController&&!this.updateBlocked)try{await this.parkController.ensureAlive()}catch(t){this.logger.warn("OpenCode serve revival failed",{context:e,error:(0,H.getErrorMessage)(t)})}}async awaitInFlightRevival(e=15e3){const t=this.parkController?.getInFlightRevival()
5422
- t&&await Promise.race([t.catch(()=>{}),new Promise(t=>{setTimeout(t,e).unref?.()})])}killProcessGroup(e){try{process.kill(-e,"SIGTERM")}catch(t){if("ESRCH"!==t?.code)try{process.kill(e,"SIGTERM")}catch{}}}readServePid(){try{const e=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5423
- if(!D.default.existsSync(e))return
5424
- const t=JSON.parse(D.default.readFileSync(e,"utf-8"))
5425
- return"number"==typeof t.pid?t.pid:void 0}catch{return}}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,H.getErrorMessage)(t)})}}this.trackedChildPids.clear()
5426
- try{const e=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5427
- if(D.default.existsSync(e)){const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5428
- if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,H.getErrorMessage)(e)})}}}}catch{}}async runOwn(e){return(0,E.makeRunOwn)(this.port,this.options.projectPath)(e)}startScheduleRunner(){this.scheduleRunner=new ie.ScheduleRunner({apiClient:this.apiClient,logger:this.logger,getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath,noteActivity:()=>this.parkController?.noteActivity(),ensureServe:()=>this.ensureServeAlive("schedule-firing"),runOwn:e=>this.runOwn(e)}),this.scheduleRunner.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new Y.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,reloadServe:e=>{this.requestConfigReload(e)},logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),deliver:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),i=await o({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},e,r,{kind:"inserted-prompt",...s?{partPayload:{requestId:s,nudge:!0,...n?.restart?{restart:!0}:{}}}:{}})
5497
+ try{const t=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5498
+ q.default.writeFileSync(t,JSON.stringify({port:e.port,pid:e.pid,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("Could not write opencode.json after revival",{error:(0,W.getErrorMessage)(e)})}if(this.startSSEEventForwarder(),this.ownerGuidanceStale&&this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance after waking failed; it stays pending",{error:(0,W.getErrorMessage)(e)})),this.projectId){const e=Date.now()
5499
+ setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}}async ensureServeAlive(e){if(this.parkController&&!this.updateBlocked)try{await this.parkController.ensureAlive()}catch(t){this.logger.warn("OpenCode serve revival failed",{context:e,error:(0,W.getErrorMessage)(t)})}}async awaitInFlightRevival(e=15e3){const t=this.parkController?.getInFlightRevival()
5500
+ t&&await Promise.race([t.catch(()=>{}),new Promise(t=>{setTimeout(t,e).unref?.()})])}killProcessGroup(e){try{process.kill(-e,"SIGTERM")}catch(t){if("ESRCH"!==t?.code)try{process.kill(e,"SIGTERM")}catch{}}}readServePid(){try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5501
+ if(!q.default.existsSync(e))return
5502
+ const t=JSON.parse(q.default.readFileSync(e,"utf-8"))
5503
+ return"number"==typeof t.pid?t.pid:void 0}catch{return}}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,W.getErrorMessage)(t)})}}this.trackedChildPids.clear()
5504
+ try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5505
+ if(q.default.existsSync(e)){const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5506
+ if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,W.getErrorMessage)(e)})}}}}catch{}}async runOwn(e){return(0,E.makeRunOwn)(this.port,this.options.projectPath)(e)}startScheduleRunner(){this.scheduleRunner=new ae.ScheduleRunner({apiClient:this.apiClient,logger:this.logger,getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath,noteActivity:()=>this.parkController?.noteActivity(),ensureServe:()=>this.ensureServeAlive("schedule-firing"),runOwn:e=>this.runOwn(e)}),this.scheduleRunner.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new J.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,reloadServe:e=>{this.requestConfigReload(e)},logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),deliver:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),i=await o({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},e,r,{kind:"inserted-prompt",...s?{partPayload:{requestId:s,nudge:!0,...n?.restart?{restart:!0}:{}}}:{}})
5429
5507
  return i.ok||this.logger.warn("watchdog nudge not delivered",{sessionId:e,requestId:s,error:i.error,detail:i.detail}),i.ok},getAgentModel:e=>this.configurer.agentModelOf(e),isAgentModelPinned:e=>this.configurer.agentPinnedOf(e),resumeHubTurn:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),{trackInboxChatTurn:i,resumeStalledWatchedTurn:a}=await Promise.resolve().then(()=>t.t(86116,23)),c={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel}
5430
5508
  if(n?.endTurn){const t=this.getOpenCodeUrl()?.replace(/\/$/,""),s=async()=>{if(!t)return
5431
5509
  const r=await(0,d.endOpenTurn)(t,e)
5432
5510
  "ended"!==r&&this.logger.warn("Stalled turn not ended before its resume",{sessionId:e,kind:r}),await new Promise(e=>setTimeout(e,1e3))},n=await a(e,r,s)
5433
5511
  if(null!==n)return n
5434
5512
  await s()}const l=await o(c,e,r,{kind:"inserted-prompt",partPayload:{nudge:!0}})
5435
- return l.ok&&l.messageId?(i(c,e,Date.now(),l.messageId,void 0,void 0,void 0,s),!0):(this.logger.warn("Alchemist resume not delivered",{sessionId:e,error:l.error,detail:l.detail}),!1)},recordProviderBlock:async(e,t,r,s,n)=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block`,{providerID:e,resetsAt:t,planType:r,reason:s,...n?{stoppedSessionId:n}:{}})}catch(t){this.logger.warn("provider cap not recorded (the report still went out)",{providerID:e,error:(0,H.getErrorMessage)(t)})}},clearCapStopped:async e=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block/resumed`,{sessionIds:e})}catch(t){this.logger.warn("could not clear cap-stopped sessions on the record",{sessionIds:e,error:(0,H.getErrorMessage)(t)})}},onStuckResolved:e=>this.handleStuckSessionResolved(e),getSessionAgentType:async e=>{if(!this.projectId)return null
5513
+ return l.ok&&l.messageId?(i(c,e,Date.now(),l.messageId,void 0,void 0,void 0,s),!0):(this.logger.warn("Alchemist resume not delivered",{sessionId:e,error:l.error,detail:l.detail}),!1)},recordProviderBlock:async(e,t,r,s,n)=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block`,{providerID:e,resetsAt:t,planType:r,reason:s,...n?{stoppedSessionId:n}:{}})}catch(t){this.logger.warn("provider cap not recorded (the report still went out)",{providerID:e,error:(0,W.getErrorMessage)(t)})}},clearCapStopped:async e=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block/resumed`,{sessionIds:e})}catch(t){this.logger.warn("could not clear cap-stopped sessions on the record",{sessionIds:e,error:(0,W.getErrorMessage)(t)})}},onStuckResolved:e=>this.handleStuckSessionResolved(e),getSessionAgentType:async e=>{if(!this.projectId)return null
5436
5514
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t
5437
- return"string"==typeof r?.agentType&&r.agentType?r.agentType:null}catch{return null}},hasLiveCommand:e=>(0,W.listRunningExecs)().some(t=>t.sessionId===e)||(0,$.listEntries)().some(t=>t.sessionId===e&&"running"===t.status),getSessionSettled:async e=>{if(!this.projectId)return null
5515
+ return"string"==typeof r?.agentType&&r.agentType?r.agentType:null}catch{return null}},hasLiveCommand:e=>(0,G.listRunningExecs)().some(t=>t.sessionId===e)||(0,$.listEntries)().some(t=>t.sessionId===e&&"running"===t.status),getSessionSettled:async e=>{if(!this.projectId)return null
5438
5516
  const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}/settled`),r=t?.data??t
5439
5517
  return"boolean"==typeof r?.settled?{settled:r.settled,..."string"==typeof r.why?{why:r.why}:{}}:null},getPendingQuestionAskerIds:async()=>{const e=new Set
5440
5518
  if(!this.projectId)return e
5441
5519
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?messageType=question&status=pending&limit=100`)
5442
- for(const r of t?.data||t||[]){if(!(0,Y.isWaitDeclaringQuestion)(r))continue
5520
+ for(const r of t?.data||t||[]){if(!(0,J.isWaitDeclaringQuestion)(r))continue
5443
5521
  const t=r?.fromSessionId||r?.from_external_session_id
5444
5522
  "string"==typeof t&&t&&e.add(t)}const r=await this.apiClient.get(`/api/projects/${this.projectId}/monitors?status=armed`)
5445
5523
  for(const t of r?.data||[]){const r=t?.owner_external_session_id
5446
- "string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,H.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,H.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
5524
+ "string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,W.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,W.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
5447
5525
  try{const o=await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask`,{sessionId:t,detail:r,...s?{refusedModel:s}:{},...n?{refusedPinned:!0}:{}}),i=o?.data??o
5448
- return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,H.getErrorMessage)(t)}),!1}},getStrandedCandidates:async()=>{if(!this.projectId)return[]
5526
+ return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,W.getErrorMessage)(t)}),!1}},getStrandedCandidates:async()=>{if(!this.projectId)return[]
5449
5527
  try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/stranded-candidates`),t=e?.data||e
5450
5528
  return Array.isArray(t?.candidates)?t.candidates:[]}catch{return[]}},getRequestWaitState:async e=>{if(!this.projectId)return null
5451
5529
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests/${encodeURIComponent(e)}/wait-state`)
5452
- return(0,Y.waitFactsFromRemote)(t?.data||t)}catch{return null}},fetchRecentReportDedupKeys:async()=>{if(!this.projectId)return[]
5530
+ return(0,J.waitFactsFromRemote)(t?.data||t)}catch{return null}},fetchRecentReportDedupKeys:async()=>{if(!this.projectId)return[]
5453
5531
  try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages/report-dedup-keys`),t=e?.data??e
5454
5532
  return Array.isArray(t)?t.filter(e=>"string"==typeof e&&!!e):[]}catch{return[]}},getOwnedRequestForSession:async e=>{if(!this.projectId)return null
5455
5533
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-request-associations/by-session/${encodeURIComponent(e)}?activeOnly=true`),r=(Array.isArray(t?.data)?t.data:Array.isArray(t)?t:[]).map(e=>e?.requestLocalId??e?.request_local_id).filter(e=>"string"==typeof e&&/^req\d{6}$/.test(e))
5456
5534
  return r.length>0?r[r.length-1]:null}catch{return null}},getLiveRequestLocalIds:async()=>{if(!this.projectId)return new Set
5457
5535
  const e=await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/requests?inFlight=true&limit=100&offset=${e}`),t=await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/requests?heldAtIntake=true&limit=100&offset=${e}`)
5458
- return(0,Y.requestsNotFinished)(e,t,A.TERMINAL_REQUEST_STATUSES)}}),this.stuckSessionWatchdog.start(),this.serveStartedAt&&this.stuckSessionWatchdog.serveStarted(this.serveStartedAt)}handleStuckSessionDetected(e){this.sseEventBuffer.push({at:Date.now(),type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({at:Date.now(),type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
5536
+ return(0,J.requestsNotFinished)(e,t,A.TERMINAL_REQUEST_STATUSES)}}),this.stuckSessionWatchdog.start(),this.serveStartedAt&&this.stuckSessionWatchdog.serveStarted(this.serveStartedAt)}handleStuckSessionDetected(e){this.sseEventBuffer.push({at:Date.now(),type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({at:Date.now(),type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
5459
5537
  t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
5460
5538
  const{sessionId:r,title:s}=e.body||{},n=this.stuckSessionWatchdog.injectTestStuck(r,s)
5461
5539
  this.logger.info("Test-injected stuck session",{sessionId:n.sessionId}),t.json({success:!0,data:n})}async handleRetryStuckSession(e,t){const{sessionId:r}=e.params
@@ -5464,13 +5542,13 @@ const s=await this.stuckSessionWatchdog.retrySession(r)
5464
5542
  s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
5465
5543
  for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
5466
5544
  console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
5467
- for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,Ze.closeExhibitPreviewBrowser)()
5545
+ for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,et.closeExhibitPreviewBrowser)()
5468
5546
  for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
5469
- try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,H.getErrorMessage)(e)})}this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0),this.stopPendingReloadRecheck()
5470
- try{const e=O.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
5471
- D.default.existsSync(e)&&D.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(()=>t(52640))
5547
+ try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,W.getErrorMessage)(e)})}this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0),this.stopPendingReloadRecheck()
5548
+ try{const e=D.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
5549
+ q.default.existsSync(e)&&q.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(()=>t(52640))
5472
5550
  e(this.projectId)}catch{}if(this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.runningReportInterval&&(clearInterval(this.runningReportInterval),this.runningReportInterval=void 0),this.runningReportDebounce&&(clearTimeout(this.runningReportDebounce),this.runningReportDebounce=void 0),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.registryHeartbeatInterval&&(clearInterval(this.registryHeartbeatInterval),this.registryHeartbeatInterval=void 0),this.vanishedSessionSweepInterval&&(clearInterval(this.vanishedSessionSweepInterval),this.vanishedSessionSweepInterval=void 0),this.planUsageInterval&&(clearInterval(this.planUsageInterval),this.planUsageInterval=void 0),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}await new Promise(e=>{if(this.server){const t=setTimeout(()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()},1e4)
5473
- this.server.close(()=>{clearTimeout(t),e()})}else e()}),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=ht,ht.JUDGE_APPROVAL_TTL_MS=864e5,ht.PROJECT_CONFIG_TTL=3e4,ht.REMOTE_STALE_MS=9e4,ht.PROMPT_DELIVERY_BUSY_MS=2e4,ht.RELOAD_RECHECK_MS=12e3,ht.RELOAD_DEFER_CAP_MS=3e5},61292(e,exports,t){"use strict"
5551
+ this.server.close(()=>{clearTimeout(t),e()})}else e()}),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=pt,pt.JUDGE_APPROVAL_TTL_MS=864e5,pt.PROJECT_CONFIG_TTL=3e4,pt.REMOTE_STALE_MS=9e4,pt.PROMPT_DELIVERY_BUSY_MS=2e4,pt.RELOAD_RECHECK_MS=12e3,pt.RELOAD_DEFER_CAP_MS=3e5},61292(e,exports,t){"use strict"
5474
5552
  function r(e,t){const r=String(e||"").replace(/\s+/g," ").trim().split(/(?<=[.!?])\s+/),s=/^(research only|read[- ]only|no edits?|do not|don't|never|only )/i,n=(r.find(e=>e.length>8&&!s.test(e.trim()))??r[0]??"").replace(/[.!?]+$/,"").trim()
5475
5553
  return n.length>t?`${n.slice(0,t)}...`:n}async function s(e,t){const r=v.get(t)
5476
5554
  if(r&&Date.now()-r.at<w)return{model:r.model,reason:r.model?"ok":"unconfigured"}
@@ -5794,7 +5872,12 @@ a&&(s.atMs=a)
5794
5872
  const c=i.providerID??i.model?.providerID,l=i.modelID??i.model?.modelID
5795
5873
  c&&(s.providerID=String(c)),l&&(s.modelID=String(l))
5796
5874
  const d=i.tokens
5797
- d&&(s.contextTokens=d.total||(d.input??0)+(d.output??0)+(d.cache?.read??0)+(d.cache?.write??0))}catch{}return s},exports.readModelLimits=async function(e,t=fetch){const s=r.get(e)
5875
+ d&&(s.contextTokens=d.total||(d.input??0)+(d.output??0)+(d.cache?.read??0)+(d.cache?.write??0))}catch{}return s},exports.firstCallAfter=async function(e,t,r,s=fetch){try{const n=await s(`${e.replace(/\/$/,"")}/session/${t}/message?limit=30`,{signal:AbortSignal.timeout(6e3)}),o=n.ok?await n.json():[]
5876
+ for(const e of Array.isArray(o)?o:[]){const t=e?.info
5877
+ if("assistant"!==t?.role||!t.time?.completed)continue
5878
+ if(Number(t.time?.created??0)<r)continue
5879
+ const s=t.tokens??{},n=Number(s.input??0),o=Number(s.cache?.read??0),i=Number(s.cache?.write??0),a=n+o+i
5880
+ return{atMs:Number(t.time.created),input:n,cacheRead:o,cacheWrite:i,hitShare:a>0?Math.round(o/a*100)/100:0}}}catch{}return null},exports.readModelLimits=async function(e,t=fetch){const s=r.get(e)
5798
5881
  if(s&&Date.now()-s.at<6e5)return s.value
5799
5882
  const n=new Map
5800
5883
  try{const s=await t(`${e.replace(/\/$/,"")}/config/providers`,{signal:AbortSignal.timeout(5e3)})
@@ -5903,23 +5986,26 @@ return{root:(0,i.resolve)((0,a.requestWorktreePath)(e,s),o),main:!1}}const c=awa
5903
5986
  return{root:(0,i.resolve)(c,o),main:!0}},exports.serveSite=async function(e,t={}){const a=(0,i.resolve)(e),d=l.get(a)
5904
5987
  if(d)return{port:d.port,url:`http://127.0.0.1:${d.port}`}
5905
5988
  let u=0
5906
- const h=(0,s.createServer)(async(e,s)=>{try{t.refresh&&Date.now()-u>1e3&&(u=Date.now(),await t.refresh().catch(()=>{}))
5907
- const l=decodeURIComponent((e.url||"/").split("?")[0])
5908
- let d=(0,i.resolve)((0,i.join)(a,l))
5909
- if(d!==a&&!d.startsWith(a+i.sep))return void s.writeHead(403).end("forbidden")
5910
- if(r((0,i.relative)(a,d)))return void s.writeHead(404).end("not found")
5911
- let h=await(0,o.stat)(d).catch(()=>null)
5912
- if(h?.isDirectory()&&(d=(0,i.join)(d,"index.html"),h=await(0,o.stat)(d).catch(()=>null)),h?.isFile()){const[e,t]=await Promise.all([(0,o.realpath)(a).catch(()=>a),(0,o.realpath)(d).catch(()=>"")])
5913
- if(!t||t!==e&&!t.startsWith(e+i.sep)||r((0,i.relative)(e,t)))return void s.writeHead(404).end("not found")}if(!h?.isFile()){if("/favicon.ico"===l)return void s.writeHead(204).end()
5914
- const e=await(0,o.stat)((0,i.join)(a,"index.html")).catch(()=>null)
5915
- return e?.isFile()?void s.writeHead(404).end("not found"):void s.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}).end(function(e){const t=(0,i.resolve)(e).split(i.sep),r=t.lastIndexOf("worktrees")
5989
+ const h={server:void 0,port:0,root:a,...t.refresh?{refresh:t.refresh}:{}},p=(0,s.createServer)(async(e,t)=>{try{const s=h.root
5990
+ h.refresh&&Date.now()-u>1e3&&(u=Date.now(),await h.refresh().catch(()=>{}))
5991
+ const a=decodeURIComponent((e.url||"/").split("?")[0])
5992
+ let l=(0,i.resolve)((0,i.join)(s,a))
5993
+ if(l!==s&&!l.startsWith(s+i.sep))return void t.writeHead(403).end("forbidden")
5994
+ if(r((0,i.relative)(s,l)))return void t.writeHead(404).end("not found")
5995
+ let d=await(0,o.stat)(l).catch(()=>null)
5996
+ if(d?.isDirectory()&&(l=(0,i.join)(l,"index.html"),d=await(0,o.stat)(l).catch(()=>null)),d?.isFile()){const[e,n]=await Promise.all([(0,o.realpath)(s).catch(()=>s),(0,o.realpath)(l).catch(()=>"")])
5997
+ if(!n||n!==e&&!n.startsWith(e+i.sep)||r((0,i.relative)(e,n)))return void t.writeHead(404).end("not found")}if(!d?.isFile()){if("/favicon.ico"===a)return void t.writeHead(204).end()
5998
+ const e=await(0,o.stat)((0,i.join)(s,"index.html")).catch(()=>null)
5999
+ return e?.isFile()?void t.writeHead(404).end("not found"):void t.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}).end(function(e){const t=(0,i.resolve)(e).split(i.sep),r=t.lastIndexOf("worktrees")
5916
6000
  return`<!doctype html><meta charset="utf-8"><title>Nothing to show</title><body style="font:15px/1.5 system-ui,sans-serif;max-width:36em;margin:3em auto;padding:0 1em;color:#333"><p><b>Nothing to show.</b> This preview serves ${s=r>=0&&/^req\d{6}$/.test(t[r+1]??"")?`${t.slice(r+2).join("/")||"the top folder"} in ${t[r+1]}'s working copy`:t.slice(-1)[0]||e,s.replace(/[&<>"]/g,e=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[e]))}, and that folder has no index.html any more: it was removed or rebuilt after the preview started.</p><p>Build output kept for a preview belongs in a folder git ignores (the project's build folder), so it stays on this machine and out of the request's branch.</p></body>`
5917
- var s}(a))}s.writeHead(200,{"Content-Type":c[(0,i.extname)(d).toLowerCase()]||"application/octet-stream","Content-Length":String(h.size),"Cache-Control":"no-store"}),(0,n.createReadStream)(d).pipe(s)}catch{s.writeHead(500).end("error")}}),p=await new Promise((e,t)=>{h.once("error",t),h.listen(0,"127.0.0.1",()=>e(h.address().port))})
5918
- return l.set(a,{server:h,port:p,root:a}),{port:p,url:`http://127.0.0.1:${p}`}},exports.stopSite=function(e){if(e){const t=(0,i.resolve)(e)
6001
+ var s}(s))}t.writeHead(200,{"Content-Type":c[(0,i.extname)(l).toLowerCase()]||"application/octet-stream","Content-Length":String(d.size),"Cache-Control":"no-store"}),(0,n.createReadStream)(l).pipe(t)}catch{t.writeHead(500).end("error")}}),f=await new Promise((e,t)=>{p.once("error",t),p.listen(0,"127.0.0.1",()=>e(p.address().port))})
6002
+ return h.server=p,h.port=f,l.set(a,h),{port:f,url:`http://127.0.0.1:${f}`}},exports.stopSite=function(e){if(e){const t=(0,i.resolve)(e)
5919
6003
  return l.get(t)?.server.close(),void l.delete(t)}for(const[,e]of l)e.server.close()
5920
6004
  l.clear()},exports.stopSitesUnder=function(e){const t=(0,i.resolve)(e),r=[]
5921
6005
  for(const[e,s]of l)(e===t||e.startsWith(t+i.sep))&&(s.server.close(),l.delete(e),r.push(`http://127.0.0.1:${s.port}`))
5922
- return r},exports.stopSiteAt=function(e){let t=0
6006
+ return r},exports.sitesUnder=function(e){const t=(0,i.resolve)(e)
6007
+ return Array.from(l.values()).filter(e=>e.root===t||e.root.startsWith(t+i.sep)).map(e=>({root:e.root,port:e.port}))},exports.rehomeSite=function(e,t,r){const s=(0,i.resolve)(e),n=(0,i.resolve)(t),o=l.get(s)
6008
+ return!(!o||n!==s&&l.has(n)||(l.delete(s),o.root=n,o.refresh=r,l.set(n,o),0))},exports.stopSiteAt=function(e){let t=0
5923
6009
  try{t=Number(new URL(e).port)}catch{return!1}for(const[e,r]of l)if(r.port===t)return r.server.close(),l.delete(e),!0
5924
6010
  return!1},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
5925
6011
  const s=t(58611),n=t(79896),o=t(91943),i=t(16928),a=t(7202),c={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".wasm":"application/wasm",".webmanifest":"application/manifest+json",".xml":"application/xml; charset=utf-8",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".mp4":"video/mp4",".webm":"video/webm",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".avif":"image/avif",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".otf":"font/otf",".txt":"text/plain; charset=utf-8",".pdf":"application/pdf"},l=new Map},3031(e,exports,t){"use strict"
@@ -10093,10 +10179,10 @@ for(const r of Array.isArray(e)?e:[]){const e=o(r)
10093
10179
  if(e)for(const n of s(r))t.set(n.trim(),e)}return t}
10094
10180
  const i=t(67071),a=t(65454),c={node:"node",npm:"node",npx:"node",yarn:"node",pnpm:"node",tsx:"node","ts-node":"node",corepack:"node",deno:"deno",bun:"bun",python:"python",python3:"python",pip:"python",pip3:"python",pytest:"python",uv:"python",poetry:"python",pipenv:"python",tox:"python",go:"go",gofmt:"go",cargo:"rust",rustc:"rust",ruby:"ruby",gem:"ruby",bundle:"ruby",rake:"ruby",rspec:"ruby",java:"java",javac:"java",mvn:"java",gradle:"java","./gradlew":"java",kotlin:"java",kotlinc:"java",php:"php",composer:"php",phpunit:"php",dotnet:"dotnet",swift:"swift",zig:"zig",elixir:"elixir",mix:"elixir",dart:"dart",flutter:"dart",perl:"perl",lua:"lua",julia:"julia",Rscript:"r",pdfinfo:"pdf",pdftotext:"pdf",pdftoppm:"pdf",pdfimages:"pdf",pdffonts:"pdf",pdfseparate:"pdf",pdfunite:"pdf",qpdf:"pdf",soffice:"office",libreoffice:"office",pandoc:"office","render-pdf":"office",zbarimg:"office",segno:"office"},l=new Set(["if","then","else","elif","do","while","until","!","time","exec","env","command","sudo","{"])},64469(e,exports,t){"use strict"
10095
10181
  function r(e,t,r=0){return!!t.lastActiveMs&&!(r&&t.lastActiveMs<r)&&e-t.lastActiveMs<=(0,i.cacheWarmForMs)(t.providerID)}function s(e,t){return!(!e||!t)&&(e===t||(0,o.isBranchWorkerAgentType)(e)&&(0,o.isBranchWorkerAgentType)(t))}exports.b6=exports.nZ=exports.WARM_CODER_DEFAULTS=void 0,exports.readWarmCoderPolicy=function(e){const t=e?.warmCoder??{},r=Number(t.maxContextTokens),s=Number(t.coldMaxContextTokens),n=Number(t.coldMaxChangedLines)
10096
- return{enabled:void 0===t.enabled?exports.WARM_CODER_DEFAULTS.enabled:!1!==t.enabled,maxContextTokens:Number.isFinite(r)&&r>0?r:exports.WARM_CODER_DEFAULTS.maxContextTokens,coldResume:void 0===t.coldResume?exports.WARM_CODER_DEFAULTS.coldResume:!1!==t.coldResume,coldMaxContextTokens:Number.isFinite(s)&&s>0?s:exports.WARM_CODER_DEFAULTS.coldMaxContextTokens,coldMaxChangedLines:Number.isFinite(n)&&n>=0?n:exports.WARM_CODER_DEFAULTS.coldMaxChangedLines,areaReuse:void 0===t.areaReuse?exports.WARM_CODER_DEFAULTS.areaReuse:!1!==t.areaReuse}},exports.isCacheWarm=r,exports.contextCeilingFor=function(e){const t=function(e){if(!(e&&e.context>0))return
10097
- const t=Math.min(e.output??0,a)||a
10098
- return e.input?e.input-Math.min(2e4,t):e.context-t}(e)
10099
- return void 0===t?e&&0===e.context?Number.POSITIVE_INFINITY:exports.b6:Math.max(0,t-exports.nZ)},exports.pickWarmCoder=function(e,t,n,o,i=0){if(!e.enabled)return{reuse:!1,why:"off"}
10182
+ return{enabled:void 0===t.enabled?exports.WARM_CODER_DEFAULTS.enabled:!1!==t.enabled,maxContextTokens:Number.isFinite(r)&&r>0?r:exports.WARM_CODER_DEFAULTS.maxContextTokens,coldResume:void 0===t.coldResume?exports.WARM_CODER_DEFAULTS.coldResume:!1!==t.coldResume,coldMaxContextTokens:Number.isFinite(s)&&s>0?s:exports.WARM_CODER_DEFAULTS.coldMaxContextTokens,coldMaxChangedLines:Number.isFinite(n)&&n>=0?n:exports.WARM_CODER_DEFAULTS.coldMaxChangedLines,areaReuse:void 0===t.areaReuse?exports.WARM_CODER_DEFAULTS.areaReuse:!1!==t.areaReuse}},exports.isCacheWarm=r,exports.contextCeilingFor=function(e,t){const r=function(e,t){if(!(e&&e.context>0))return
10183
+ const r=Math.min(e.output??0,a)||a
10184
+ return"openai"===String(t??"")&&e.input?e.input-Math.round(.05*e.input):e.input?e.input-Math.min(2e4,r):e.context-r}(e,t)
10185
+ return void 0===r?e&&0===e.context?Number.POSITIVE_INFINITY:exports.b6:Math.max(0,r-exports.nZ)},exports.pickWarmCoder=function(e,t,n,o,i=0){if(!e.enabled)return{reuse:!1,why:"off"}
10100
10186
  if(!o.length)return{reuse:!1,why:"no-candidate"}
10101
10187
  const a=["role","unrelated","model","tools","busy","context","archived","cold","cold-context","stale"]
10102
10188
  let c=0