alchemylab 3.0.5 → 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 +594 -489
  2. package/package.json +2 -2
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,83 +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=[]
3805
- for(const e of i)try{const t=s?await o.apiClient.get(`/api/projects/${s}/requests/${e}`):null,r=t?.data??t
3806
- r&&a.has(String(r.status))&&c.push({id:e,title:String(r.title??""),status:String(r.status)})}catch{}const l=await(0,Y.resolveDefaultBranch)(o.projectPath),d=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:c.map(e=>e.id),defaultBranch:l,git:n}),u=new Map(c.map(e=>[e.id,e]))
3807
- t.json({success:!0,data:d.map(e=>({...e,siblingTitle:u.get(e.sibling)?.title??"",siblingStatus:u.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
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=[]
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:[]
3884
+ for(const e of t){const t=String(e?.localId??"")
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]))
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
3808
3887
  if(!r)return void t.status(400).json({success:!1,error:"requestId required"})
3809
- 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(()=>{})
3810
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()):[]
3811
3890
  if(!r||0===n.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty commands array are required"})
3812
- 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):[]
3813
3892
  if(!0===e.body?.background){const e=JSON.stringify([[...n].sort(),a.map(e=>`${e.criterionId}\0${e.command}`).sort()])
3814
- 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)
3815
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
3816
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??"")}))
3817
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()
3818
- 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
3819
3898
  if(!r||!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"requestId is required"})
3820
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"}})
3821
- 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()
3822
3901
  if(!c)return
3823
3902
  const l=(0,m.randomUUID)()
3824
- 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}}
3825
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)
3826
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"})
3827
- 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()
3828
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)}))
3829
3908
  const i=e.filter(e=>!e.ok&&!e.couldNotRun).length
3830
- 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)
3831
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."}})
3832
3911
  const a="string"==typeof e.body?.requestId?e.body.requestId:""
3833
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.`}})
3834
- 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})
3835
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:"")
3836
3915
  if(!r)return void t.json({success:!0,data:{due:!1}})
3837
- 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)
3838
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)
3839
3918
  if(!i||!n)return void t.json({success:!0,data:{outcome:"none"}})
3840
- 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)
3841
3920
  if(!a.due)return void t.json({success:!0,data:{outcome:"none"}})
3842
- 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}})
3843
- 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}})
3844
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.`
3845
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
3846
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"})
3847
3926
  const s=(0,w.requestWorktreesDir)(o.projectPath),n=M.default.join(s,r)
3848
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"})
3849
- if(!await q.promises.stat(n).then(e=>e.isDirectory()).catch(()=>!1))return void t.json({success:!0,data:{committed:!1,reason:"no worktree"}})
3850
- 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)
3851
3930
  await(0,x.assertGitNotUserFolder)(i,"commit of worktree leftovers")
3852
3931
  const a=await i.status()
3853
3932
  if(a.isClean())return void t.json({success:!0,data:{committed:!1,reason:"clean"}})
3854
3933
  const c=[...a.modified,...a.not_added,...a.created,...a.deleted]
3855
3934
  await i.add(["-A"])
3856
- const{note:l}=await(0,Q.holdBackStagedCredentials)(n)
3935
+ const{note:l}=await(0,Z.holdBackStagedCredentials)(n)
3857
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}`:""}`)
3858
3937
  const d=(await i.revparse(["HEAD"])).trim()
3859
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)
3860
3939
  if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
3861
- 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()
3862
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||{}
3863
3942
  if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"})
3864
3943
  let s=null
3865
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
3866
3945
  if(t){const e=e=>"string"==typeof e?(0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),e):null
3867
- 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)})}})}
3868
- 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
3869
- let re=null
3870
- 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})
3871
3950
  exports.COMPLETION_REPLAY_BUDGET_MS=15e4
3872
- const ne=12e4},60885(e,exports,t){"use strict"
3951
+ const oe=12e4},60885(e,exports,t){"use strict"
3873
3952
  exports.makeRunOwn=function(e,t){return async o=>{n||(n=new r.Agent({headersTimeout:0,bodyTimeout:0}))
3874
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)
3875
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()}}
@@ -4774,37 +4853,37 @@ let h=null
4774
4853
  const p=new Map},62366(e,exports,t){"use strict"
4775
4854
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
4776
4855
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
4777
- 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
4778
- 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()
4779
- 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})
4780
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})
4781
4860
  const t=Date.now()
4782
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
4783
- 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
4784
- const r=(0,P.createMetricsCollector)(!1)
4785
- 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
4786
4865
  this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
4787
- 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
4788
4867
  e.use((e,t,r)=>{const n=e.headers.host
4789
- if((0,Z.isLoopbackHost)(n))return r()
4790
- 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
4791
- 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})
4792
- 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}))
4793
- const n=(0,ee.makeTicketVerifier)({apiClient:this.apiClient,projectId:()=>this.projectId,logger:this.logger})
4794
- 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"}))
4795
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"]
4796
4875
  e.use((e,t,r)=>{const s="string"==typeof e.query.projectId?e.query.projectId:void 0
4797
4876
  if(!s)return r()
4798
4877
  const n=this.projectId
4799
- 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")
4800
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()
4801
- 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
4802
4881
  if(!t)throw new Error("no project bound")
4803
4882
  const r=`/api/projects/${t}/session-request-associations`
4804
4883
  let s=!1
4805
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)}`))
4806
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`))
4807
- 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)
4808
4887
  if(s)throw new Error("session request lookup failed")
4809
4888
  return i})
4810
4889
  const a=async e=>Promise.all((0,$.listEntries)().map(async t=>{let r,s="",n=0,o=t.finishedAt??t.startedAt
@@ -4813,24 +4892,24 @@ try{const e=await k.promises.stat(t.logPath)
4813
4892
  n=e.size,o=e.mtime.toISOString(),r=await k.promises.open(t.logPath,"r")
4814
4893
  const i=Math.max(0,n-16384),a=Buffer.alloc(n-i)
4815
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}}))
4816
- 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:[]}})
4817
- 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??{}
4818
- 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??{}
4819
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})
4820
4899
  try{const e=(0,$.listEntries)().find(e=>e.id===r)
4821
- 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
4822
4901
  if(r)try{const e=await r.renewIfDue(Number.POSITIVE_INFINITY).catch(()=>null)
4823
4902
  if(e?.token)return this.updateAuthToken(e.token),void t.json({success:!0,renewed:!0})
4824
4903
  const s=r.getAuthStatus()
4825
- 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})
4826
4905
  const{authUrl:n}=await r.startAuthenticationFlow()
4827
- 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)"})
4828
4907
  for(const e of this.executePark())this.managedExitPids.add(e)
4829
- 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"})
4830
- 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||"")
4831
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"})
4832
4911
  const s="string"!=typeof e.query.path||e.query.path.includes("..")?void 0:e.query.path
4833
- 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]
4834
4913
  s&&i.push("--",s)
4835
4914
  let a=await(0,g.documentDiffGit)(this.options.projectPath).raw(i)
4836
4915
  const c=a.length>n
@@ -4841,79 +4920,79 @@ try{const s=this.projectId
4841
4920
  if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
4842
4921
  let n=null
4843
4922
  try{const e=await this.apiClient.get(`/api/projects/${s}/requests/${r}`)
4844
- 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)
4845
- 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(()=>[])
4846
4925
  if(!(a&&c||0!==l.length))return void t.status(404).json({success:!1,error:`No diff range found for ${r}.`})
4847
- 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
4848
4927
  if(!e&&0===l.length)return void t.status(404).json({success:!1,error:`No diff stats for ${r}.`})
4849
- const s=(0,We.withDocumentStats)(e,l)
4850
- 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
4851
- 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)())
4852
- 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
4853
4932
  try{const s=this.projectId
4854
4933
  if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
4855
4934
  const{base:n,draft:o}=e.body??{},i=()=>{const e={"X-Client-Type":"alchemy-local-service"},t=this.apiClient.authToken||this.options.authToken
4856
- 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}`)
4857
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()})
4858
4937
  if(!t.ok)throw new Error(`Receipt fetch failed: HTTP ${t.status}`)
4859
4938
  return await t.text()}},l={},d=[]
4860
- 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"})
4861
- 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
4862
4941
  if(!r)return void t.json({success:!0,data:{text:""}})
4863
4942
  const s=await(0,p.afterCompactionBlock)({apiClient:this.apiClient,projectId:r,projectPath:this.options.projectPath},String(e.params.sessionId||""))
4864
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||{}
4865
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)
4866
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}:{}})
4867
- 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
4868
4947
  if(!s)return void t.status(400).json({success:!1,error:"No project"})
4869
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}))
4870
- 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))
4871
- 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||{}
4872
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})
4873
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??{}
4874
4953
  if("string"!=typeof s||!s)return void r.status(400).json({success:!1,error:"text is required"})
4875
4954
  const i=await Promise.resolve().then(()=>t(13080)),a=await i.putBuffer(this.options.projectPath,s)
4876
4955
  if(this.projectId){const{recordIdForCall:e}=await t.e(111).then(()=>t(56111)),r=e("string"==typeof o?o:void 0)
4877
- 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."})
4878
4957
  const e=await this.reconfigureOpenCode()
4879
4958
  if(!e.success)return void t.status(500).json({success:!1,error:e.error??"reconfigure failed"})
4880
- 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))
4881
4960
  a()
4882
4961
  const c={}
4883
- "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))
4884
- 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||{}
4885
4964
  if(!s||"string"!=typeof s)return void r.status(400).json({success:!1,error:"key is required"})
4886
4965
  if("string"!=typeof n||!n)return void r.status(400).json({success:!1,error:"value is required"})
4887
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)
4888
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))
4889
- 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)
4890
- 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:[]}})
4891
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})
4892
- 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||{}
4893
4972
  if(!s)return void r.status(400).json({success:!1,error:"toolName is required"})
4894
4973
  if(!this.projectId)return void r.json({success:!0,data:{allow:!0,verdict:"not-external",reason:"No project"}})
4895
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})
4896
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))
4897
- 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
4898
4977
  if(!s)return void r.status(400).json({success:!1,error:"eventType is required"})
4899
4978
  if("session_idle"===s&&n){const{notifyInboxChatSessionIdle:e}=await Promise.resolve().then(()=>t.t(86116,23))
4900
4979
  e(String(n))
4901
4980
  const{notifyPlannerHandoffSessionIdle:r}=await t.e(991).then(()=>t(18991))
4902
4981
  r(String(n))}const a=await this.hookExecutor.processEvent({eventType:s,sessionId:n||"",variables:o||{},timing:i})
4903
- 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||{}
4904
4983
  if(!s||"before"!==a&&"after"!==a)return void r.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
4905
4984
  if(n){const{notifyPlannerHandoffToolEvent:e}=await t.e(991).then(()=>t(18991))
4906
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))
4907
- 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})
4908
4987
  let l=0
4909
4988
  for(const e of c)l+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:n||"",variables:e.variables,timing:e.timing})).triggered
4910
- 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||{}
4911
4990
  if("string"!=typeof s||!this.projectId)return void r.status(400).json({success:!1,error:"tool is required"})
4912
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})
4913
- 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??{}
4914
4993
  if(!s||!n||!this.projectId)return void r.status(400).json({success:!1,error:"requestId and landingSha are required"})
4915
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)
4916
- 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
4917
4996
  const r=this.projectId
4918
4997
  if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
4919
4998
  const{requestId:s,sourceSessionId:n}=e.body||{}
@@ -4921,84 +5000,87 @@ let o=""
4921
5000
  try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${s}`)
4922
5001
  o=(e?.data||e)?.title||""}catch{}let i=""
4923
5002
  if(n)try{const e=this.getOpenCodeUrl()
4924
- 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})
4925
5004
  if(!c.success||!c.sessionId)return void t.status(500).json({success:!1,error:c.error||"Failed to spawn request-shaping planner"})
4926
- 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||{}
4927
5006
  if(!r||"object"!=typeof r)return void t.status(400).json({success:!1,error:"config (environment config) is required"})
4928
5007
  const i="string"==typeof s&&s.trim()?s.trim():"true",a=this.options.projectPath,c=this.projectId
4929
5008
  if(!c)return void t.status(400).json({success:!1,error:"project not registered"})
4930
5009
  const l={composeFile:r.composeFile,gateImage:r.image,connEnv:r.connEnv,install:r.install,artifacts:r.artifacts,shellInit:r.shellInit,projectId:c}
4931
5010
  let d
4932
- try{await(0,De.sweepStaleSetupWorktrees)(a,this.logger)
4933
- const e=await(0,De.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,De.generateSetupId)()
4934
- d=O.default.join((0,De.getSetupWorktreesBase)(a),`verify-${s}`),await(0,De.createSetupWorktree)(a,d,e,this.logger)
4935
- 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."]:[]
4936
- 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
4937
5016
  if(!t||!/^[A-Za-z0-9_-]+$/.test(e))return null
4938
5017
  const r=await this.apiClient.get(`/api/projects/${t}/documents/${e}`).catch(()=>null),s=r?.data??r
4939
- 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()
4940
- 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))
4941
- 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
4942
- 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}
4943
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
4944
5023
  if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),n=e?await r.getToken():null
4945
- 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")
4946
- try{if(!D.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
4947
- const e=D.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
4948
- 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())
4949
5028
  if(!e||!this.projectId||this.followingDefaultTo===e)return
4950
5029
  this.followingDefaultTo=e
4951
5030
  const t=this.cachedDefaultModel
4952
- 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
4953
5032
  let t=0
4954
5033
  for(const[r,s]of this.activeMcpCalls)s<e&&(this.activeMcpCalls.delete(r),t++)
4955
- 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
4956
5035
  try{const e=this.getOpenCodeUrl()
4957
5036
  if(!e)return!1
4958
5037
  const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
4959
5038
  if(!t.ok)return!1
4960
5039
  const r=await t.json()
4961
- 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()
4962
5041
  if(!e)return[]
4963
5042
  const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
4964
5043
  if(!t.ok)return[]
4965
5044
  const r=await t.json()
4966
- 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
4967
5046
  try{const t=Date.now()-this.pendingConfigReloadSince
4968
- 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
4969
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})
4970
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
4971
- 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 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(/\/$/,"")
4972
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)})
4973
5055
  if(!r.ok)return void this.logger.warn("The serve did not take the pause",{paused:e,status:r.status})
4974
5056
  const s=await r.json().catch(()=>({}))
4975
5057
  this.pauseApplied={url:t,paused:e}
4976
- const n=(0,it.setProjectPaused)(e)
5058
+ const n=(0,at.setProjectPaused)(e)
4977
5059
  if(e){const e=Array.isArray(s.stopped)?s.stopped:[]
4978
5060
  let t=0
4979
- 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)
4980
5062
  const r=this.projectId?await(0,j.experimentRegistry)().releaseForPause(this.projectId).catch(()=>[]):[]
4981
- 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)
4982
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}
4983
5065
  let a=0
4984
- 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})
4985
- 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()
4986
5068
  if(!t)return!1
4987
5069
  const r=await fetch(`${t}/config/reload`,{method:"POST",signal:AbortSignal.timeout(5e3)})
4988
5070
  if(r.ok){this.logger.info("Triggered OpenCode config reload",{context:e})
4989
5071
  const t=await r.json().catch(()=>null)
4990
- 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
4991
- 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)
4992
- 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)
4993
5075
  if(!s||"object"!=typeof s||s.model===e)return
4994
- 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
4995
5077
  const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
4996
5078
  if(!e?.length)return
4997
5079
  const t=await this.configurer.fetchProjectFromRemote(this.projectId)
4998
5080
  this.ownerGuidanceStale=!1
4999
- 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()
5000
5082
  this.configurer.clearAgentMarkdowns()
5001
- 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)}))
5002
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
5003
5085
  const e=this.getOpenCodeUrl()
5004
5086
  let t=!1,r=null
@@ -5007,10 +5089,10 @@ if(!s.ok)return
5007
5089
  const o=await s.json(),i=n.ok?await n.json():{}
5008
5090
  t=Object.values(i??{}).some(e=>"busy"===e?.type)||this.activeMcpCalls.size>0
5009
5091
  for(const e of o){const t=Number(e.time?.updated??e.time?.created??0)
5010
- 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)
5011
- 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")
5012
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")
5013
- 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
5014
5096
  if(!e)return void t.status(400).json({success:!1,error:"Project not registered"})
5015
5097
  this.logger.info("Syncing agents from remote",{projectId:e})
5016
5098
  const r=await this.configurer.fetchAgentsFromRemote(e)
@@ -5019,19 +5101,19 @@ const s=await this.configurer.fetchProjectFromRemote(e)
5019
5101
  this.logger.debug("Fetched project for global prompt",{projectId:e,hasProject:!!s,hasGlobalSystemPrompt:!!s?.metadata?.globalSystemPrompt})
5020
5102
  const n=await this.configurer.agentPreambleFor(e,s?.metadata?.globalSystemPrompt)
5021
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")
5022
- const o=O.default.join(this.options.projectPath,".opencode","agent")
5023
- await D.default.promises.mkdir(o,{recursive:!0})
5024
- 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()
5025
5107
  this.configurer.clearAgentMarkdowns()
5026
5108
  let h=0
5027
- 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
5028
- try{const e=(0,je.generatedOpencodeConfigPath)(this.options.projectPath)
5029
- 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)
5030
5112
  if(r.agent){for(const e of Object.values(r.agent))e&&"object"==typeof e&&"prompt"in e&&delete e.prompt
5031
- 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
5032
5114
  p&&this.logger.info("Agent definitions unchanged; no config reload",{context:"sync-agents"}),this.ownerGuidanceStale=!1
5033
5115
  const f=p?"unchanged":await this.requestConfigReload("sync-agents"),m="reloaded"===f
5034
- 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
5035
5117
  if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
5036
5118
  const{sessionId:s,requestId:n,summary:o,status:i}=e.body
5037
5119
  if(!s)return void t.status(400).json({success:!1,error:"sessionId is required"})
@@ -5039,26 +5121,26 @@ this.logger.info("Direct report creation",{sessionId:s,requestId:n,status:i})
5039
5121
  const a=[]
5040
5122
  try{const e=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),t=e?.data||e||[]
5041
5123
  let n=s
5042
- 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"})
5043
5125
  let c,l=o
5044
5126
  if(!l&&n)try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${n}`),t=e?.data||e
5045
5127
  c=t?.title
5046
5128
  const s=t?.completionSummary
5047
- 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"}`)
5048
5130
  const d=n||s
5049
5131
  if(this.directReportDedup.size>=1e3){const e=this.directReportDedup.keys().next().value
5050
5132
  void 0!==e&&this.directReportDedup.delete(e)}this.directReportDedup.has(d)||this.directReportDedup.set(d,new Set)
5051
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
5052
- 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=[]
5053
5135
  for(const e of p)"fulfilled"===e.status&&f.push(e.value)
5054
- 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)})
5055
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
5056
- 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
5057
5139
  if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),n=e?.data||e
5058
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)
5059
- 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
5060
5142
  if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/resolve/${encodeURIComponent(r)}`),n=e?.data?.parent?.externalSessionId||""
5061
- 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()
5062
5144
  if(!r)return void t.status(502).json({error:"OpenCode not running"})
5063
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")
5064
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})
@@ -5079,18 +5161,18 @@ if(!t||t===r)throw e
5079
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()
5080
5162
  const d=c.headers.get("content-type")||""
5081
5163
  let u
5082
- 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
5083
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"])
5084
5166
  if(c&&!d.has(c))return void t.status(400).json({success:!1,error:`agent must be one of: ${[...d].join(", ")}`})
5085
- 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)})
5086
5168
  if(!u.success||!u.sessionId)return void t.status(500).json({success:!1,error:u.error||"spawn failed"})
5087
- 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
5088
- 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
5089
5171
  if(!1!==r)return this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r}),!0
5090
- 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
5091
- 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})
5092
- 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
5093
- 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)
5094
5176
  if(!e)return
5095
5177
  let t
5096
5178
  try{const r=await fetch(`${e.replace(/\/$/,"")}/session?limit=5000`,{signal:AbortSignal.timeout(15e3)})
@@ -5103,15 +5185,15 @@ try{for(let e=0;e<1e3;e+=100){const n=await this.apiClient.get(`/api/projects/${
5103
5185
  if(!Array.isArray(o)||0===o.length)break
5104
5186
  for(const e of o){s++
5105
5187
  const n=e?.externalSessionId??e?.external_session_id
5106
- 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
5107
- 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)})
5108
- 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
5109
- 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}:{}})
5110
- 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)})
5111
- 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
5112
5194
  if(!s?.id)return
5113
5195
  if(s.status&&"active"!==s.status)return
5114
- 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)
5115
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)
5116
5198
  const{getOpenCodeServerUrl:o}=await Promise.resolve().then(()=>t(28148)),i=o(this.options.projectPath,this.logger)
5117
5199
  if(!i)return
@@ -5124,11 +5206,11 @@ if(!u||!u.trim())return void await this.endFinishedChildSession(e,s)
5124
5206
  const{deliverPrompt:h}=await Promise.resolve().then(()=>t(17039)),p={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}
5125
5207
  if(d.test(u)){const t=(this.leakedCallNudges.get(e)??0)+1
5126
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)
5127
- 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)
5128
- 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
5129
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)
5130
5212
  if(!d.success)return void r.status(500).json({success:!1,error:d.error||d.message})
5131
- 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
5132
5214
  const r=this.projectId
5133
5215
  if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
5134
5216
  const s=String(e.body?.runId??"")
@@ -5137,21 +5219,21 @@ const n=await this.apiClient.get(`/api/projects/${r}/setup-runs/${s}`),o=n?.data
5137
5219
  if(!o?.id)return void t.status(404).json({success:!1,error:`no setup run ${s}`})
5138
5220
  if("running"!==o.state)return void t.json({success:!0,data:{skipped:`run is ${o.state}`}})
5139
5221
  if(o.architectSessionId)return void t.json({success:!0,data:{sessionId:o.architectSessionId,already:!0}})
5140
- 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"
5141
5223
  let p,f
5142
- 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)
5143
5225
  let t=!1
5144
5226
  try{const e=await this.apiClient.get(`/api/projects/${r}/test-processes`),s=Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[]
5145
5227
  t=s.length>0}catch{}let s=!1
5146
- try{s=await(0,De.testGateOffForSetup)(this.apiClient,r,c)}catch{}let n=!1
5147
- 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"
5148
5230
  try{const t=await this.apiClient.get(`/api/projects/${r}`),s=(t?.data||t)?.metadata?.environmentSetupMode
5149
- "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"
5150
- 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})
5151
- 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})
5152
- 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
5153
- const e=this.projectId,t=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
5154
- 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
5155
5237
  return r?.id?{id:r.id,architectSessionId:r.architectSessionId??null,startedAt:r.startedAt??null}:null},architectState:async e=>{if(!t)return"unknown"
5156
5238
  const r=await this.serveHasSession(t,e)
5157
5239
  if(!1===r)return"gone"
@@ -5159,37 +5241,37 @@ if(null===r)return"unknown"
5159
5241
  try{const r=await fetch(`${t.replace(/\/$/,"")}/session/status`,{signal:AbortSignal.timeout(5e3)})
5160
5242
  if(r.ok){const t=(await r.json())?.[e]
5161
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)})
5162
- 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
5163
5245
  try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(5e3)})
5164
5246
  if(!r.ok)return null
5165
5247
  const s=Number((await r.json())?.time?.updated)
5166
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)
5167
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})}})
5168
- "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
5169
5251
  return"string"==typeof r?.id?r.id:null},this.logger)}async handleGetAgentByName(e,t){try{const r=this.projectId
5170
5252
  if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
5171
- 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)
5172
5254
  if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
5173
- 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
5174
- const e=(0,nt.identityPath)(this.options.projectPath)
5175
- try{if(!D.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
5176
- const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5177
- 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)
5178
5260
  const t=`http://127.0.0.1:${e}`
5179
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)
5180
- 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)})
5181
5263
  if(!t.ok)return
5182
- 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
5183
- 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
5184
5266
  if(this.openCodePort)return`http://127.0.0.1:${this.openCodePort}`
5185
5267
  if(this.openCodeUrlCache&&Date.now()-this.openCodeUrlCache.timestamp<3e4)return this.openCodeUrlCache.url
5186
- const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
5268
+ const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
5187
5269
  return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async register(){const e=this.readProjectConfig()
5188
5270
  if(!e?.id)throw new Error("No project configuration found")
5189
- 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)()
5190
- try{const e=(0,Pe.findOpencodeDir)()
5191
- if(e){const t=JSON.parse(D.default.readFileSync(O.default.join(e,"package.json"),"utf8"))
5192
- 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}}
5193
5275
  this.logger.info("Registering with remote",{projectId:this.projectId})
5194
5276
  const r=await this.apiClient.post("/api/servers/register",t)
5195
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()
@@ -5199,18 +5281,18 @@ for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] $
5199
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`))
5200
5282
  this.versionWarnings=e,!e.some(e=>"required"===e.severity)&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){if(!this.serverId)return
5201
5283
  if(this.heartbeatInterval)return
5202
- 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()
5203
- 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)
5204
5286
  e.length&&await this.apiClient.post(`/api/projects/${this.projectId}/plan-usage-readings`,{providers:e})}catch{}}
5205
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??[]
5206
- 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)
5207
5289
  if(t.includes("Server not found or inactive")){this.logger.warn("Heartbeat hit a stale registration; re-registering",{staleServerId:this.serverId})
5208
- 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
5209
5291
  const t=Array.from(this.runningSessions).sort(),r=t.join(",")
5210
5292
  if(e||r!==this.lastRunningReport){this.lastRunningReport=r
5211
- 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++
5212
5294
  for(const e of this.sseReconnectTimers)clearTimeout(e)
5213
- 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)
5214
5296
  this.sseReconnectTimers.add(t)}async connectToOpenCodeSSE(){const e=this.sseConnectGeneration,r=()=>e!==this.sseConnectGeneration,s=this.getOpenCodeUrl()
5215
5297
  if(!s)return void(this.sseForwarderInterval&&!r()&&(this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),this.scheduleSSEReconnect(5e3)))
5216
5298
  const n=`${s}/event`
@@ -5225,41 +5307,41 @@ if(r()){try{await s.cancel()}catch{}return}if(e)break
5225
5307
  const o=i.decode(n,{stream:!0})
5226
5308
  if(l){const e=o.indexOf("\n")
5227
5309
  if(-1===e)continue
5228
- 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)
5229
5311
  const e=(0,c.trimOversizedSseEvent)(a.replace(/^data: /,""),Number.POSITIVE_INFINITY)
5230
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=""
5231
5313
  continue}const h=a.split("\n")
5232
5314
  a=h.pop()||""
5233
5315
  for(const e of h)if(e.startsWith("data: ")){let r=null
5234
- 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})
5235
- else if(e.length>ut){const t=(0,c.sseEventTypeOf)(e)
5236
- 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})
5237
5319
  continue}try{const s=r??JSON.parse(e.slice(6)),n=s.type||"unknown"
5238
5320
  this.sseEventBuffer.push({type:n,data:s,at:Date.now()})
5239
5321
  {const e=s?.properties?.sessionID||s?.sessionID
5240
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)
5241
5323
  else if("session.status"===n){const t=s?.properties?.status?.type
5242
- "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??"")
5243
5325
  if(e&&!this.usageStepTimers.has(e)){const t=setTimeout(()=>{this.usageStepTimers.delete(e),this.recordUsageForSession(e).catch(()=>{})},2e3)
5244
- "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(()=>{})
5245
5327
  const e=s?.properties?.sessionID||s?.sessionID
5246
- e&&(0,G.notifySessionIdle)(e)}if("session.status"===n&&"idle"===s?.properties?.status?.type){const e=s?.properties?.sessionID||s?.sessionID
5247
- 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
5248
5330
  if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
5249
5331
  if(!t)return
5250
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||""
5251
5333
  if(!r)return
5252
5334
  const s=t?.metadata
5253
5335
  if(s?.agentType&&this.projectId){const e=t?.parentID||null
5254
- 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
5255
5337
  const t=this.getOpenCodeUrl()
5256
5338
  if(t)try{const r=await fetch(`${t}/session/${e}/message?limit=200`,{signal:AbortSignal.timeout(5e3)})
5257
5339
  if(!r.ok)return
5258
- 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=[]
5259
5341
  for(const t of s){const r=t?.info
5260
5342
  if(!r||"assistant"!==r.role||!r.id)continue
5261
5343
  if(n&&n.has(String(r.id)))continue
5262
- 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)
5263
5345
  if(!h)continue
5264
5346
  const p=!!r.time?.completed
5265
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
@@ -5267,9 +5349,9 @@ await this.apiClient.post(`/api/projects/${this.projectId}/usage-events`,{events
5267
5349
  let a=n
5268
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)
5269
5351
  this.recordedUsageMsgs.set(e,a)}for(const e of i)e.finished&&a.add(e.messageId)
5270
- 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()
5271
5353
  if(!this.projectId||!r)return
5272
- 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)
5273
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
5274
5356
  if(!r)return
5275
5357
  const s=this.getOpenCodeUrl()
@@ -5282,7 +5364,7 @@ if(!r.ok)return
5282
5364
  const s=await r.json()
5283
5365
  if(!Array.isArray(s)||0===s.length)return
5284
5366
  this.logger.info("Sweeping pending permission asks",{count:s.length})
5285
- 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
5286
5368
  if(0===this.sseEventBuffer.length&&0===this.droppedEventCount)return
5287
5369
  const e=Date.now()-3e5
5288
5370
  let t=0
@@ -5290,36 +5372,36 @@ for(;t<this.sseEventBuffer.length&&this.sseEventBuffer[t].at<e;)t++
5290
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
5291
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
5292
5374
  s&&r.unshift(s)
5293
- 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})
5294
5376
  const t=s?r.slice(1):r
5295
5377
  if(this.sseEventBuffer.unshift(...t),this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
5296
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
5297
- 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))
5298
- 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()
5299
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"}
5300
- 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
5301
5383
  const r=await this.readMainSha()
5302
5384
  r&&(t["X-Main-Sha"]=r)
5303
5385
  const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
5304
5386
  if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
5305
5387
  const n=await s.json(),o={success:!0,data:n.data||n}
5306
- 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,defaultModelUnchosen:s}=o.data.settings
5307
- "boolean"==typeof s&&(this.defaultModelUnchosen=s),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r)}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
5308
- 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
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
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
5309
5391
  if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
5310
5392
  this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
5311
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=[]
5312
- for(const s of e)dt.has(s.type)?t.push(s):r.push(s)
5313
- 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--})
5314
5396
  let s=0
5315
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++)
5316
5398
  s>0&&this.drainRegularActions()}async drainRegularActions(){if(!this.isProcessingAction){this.isProcessingAction=!0
5317
5399
  try{for(;this.isPolling;){const e=this.pendingRegularActions.shift()
5318
5400
  if(!e)break
5319
- 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)
5320
5402
  this.pendingRegularActions.length=0,this.isProcessingAction=!1}}}async syncProjectSettings(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}`)
5321
5403
  if(e.success&&e.data?.metadata){const{defaultModel:t}=e.data.metadata
5322
- 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
5323
5405
  const r=Date.parse(t.createdAt??t.created_at??"")
5324
5406
  return Number.isFinite(r)&&r<e})
5325
5407
  if(0===r.length)return
@@ -5327,22 +5409,22 @@ const s=new Map
5327
5409
  for(const e of r){const t=e.requestId??e.request_local_id
5328
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))
5329
5411
  for(const[e,t]of s){this.logger.warn("Re-dispatching test run(s) orphaned by restart",{requestId:e,testRunIds:t})
5330
- 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})
5331
5413
  const t=Date.now()
5332
5414
  let r
5333
- 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)
5334
- 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}
5335
- 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
5336
- 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
5337
5419
  await this.syncProjectSettings().catch(()=>{})
5338
5420
  const e=this.cachedIntegrations?.linear?.autoImport
5339
5421
  if(!0!==e?.enabled)return
5340
- 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)
5341
5423
  if(0===s.length)return
5342
- 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)
5343
- for(const e of a)try{const t=await(0,U.importLinearIssue)(n,e,{auto:!0})
5344
- this.logger.info("linear auto-import: filed a draft",{issue:e.identifier,requestId:t})}catch(t){if(t instanceof U.AlreadyImportedError)continue
5345
- 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)
5346
5428
  "function"==typeof t.unref&&t.unref()}startExperimentReaper(){const e=async()=>{try{const e=this.getOpenCodeUrl()
5347
5429
  if(!e)return
5348
5430
  const r=await fetch(`${e}/session?limit=1000`,{signal:AbortSignal.timeout(5e3)})
@@ -5361,40 +5443,40 @@ const t=await e.json(),r=t?.data?.frontendUrl
5361
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{}})()
5362
5444
  const r=await(0,S.ensureSoleInstance)({projectPath:this.options.projectPath,logger:this.logger,retire:this.options.retireTwins})
5363
5445
  if(!r.ok)throw this.logger.error("Refusing to start: "+r.reason),Object.assign(new Error(r.reason),{runningElsewhere:!0===r.runningElsewhere})
5364
- 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()
5365
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)}})
5366
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})
5367
5449
  continue}this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
5368
- 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}`)
5369
5451
  try{const e=this.readProjectConfig()
5370
5452
  if(e?.id){const{upsertEntry:r}=await Promise.resolve().then(()=>t(52640))
5371
- 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)
5372
- 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()
5373
5455
  setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}this.startExperimentReaper(),this.startLinearAutoImportPoller()
5374
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"}
5375
- 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))
5376
5458
  const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
5377
5459
  if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
5378
- "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()
5379
5461
  try{const{resumeInboxChatWatches:e}=await Promise.resolve().then(()=>t.t(86116,23))
5380
- 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
5381
- 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
5382
5464
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/environments/${e}`)
5383
5465
  r=t?.data||t}catch{throw new Error("environment fetch failed")}const s=r?.config?.delivery
5384
5466
  if(!r||!s?.targetPath)return{gone:!0,reason:"environment or its delivery config no longer exists"}
5385
5467
  const{verifyDelivery:n}=await Promise.resolve().then(()=>t.t(32504,23)),o=await n(s)
5386
- 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
5387
5469
  if(!this.relaunchOpenCode)return
5388
5470
  const e=this.options.onIdlePark
5389
- 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})
5390
- 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")
5391
5473
  if(this.isProcessingAction||this.inFlightProxyActions>0)return this.parkVeto("processing-action")
5392
5474
  if(this.inFlightServeProxyRequests>0)return this.parkVeto("serve-proxy-in-flight")
5393
5475
  if(this.hasRemoteEventConsumers)return this.parkVeto("ui-event-consumers")
5394
5476
  if(this.activeMcpCalls.size>0||this.pendingConfigReload)return this.parkVeto("mcp-or-config-activity")
5395
- if((0,qe.anyPoolActivity)())return this.parkVeto("pool-activity")
5396
- if((0,Ce.listPendingApprovals)().length>0||(0,_e.listPendingPaintRequests)().length>0)return this.parkVeto("pending-approvals")
5397
- 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")
5398
5480
  if((0,C.listPreviewHandles)().length>0)return this.parkVeto("preview-handles")
5399
5481
  if(this.projectId&&(0,j.experimentRegistry)().countForProject(this.projectId)>0)return this.parkVeto("experiments")
5400
5482
  if(this.scheduleRunner?.hasInFlightFirings())return this.parkVeto("schedule-firing")
@@ -5403,55 +5485,55 @@ for(const e of[...this.trackedChildPids])if(e!==r)try{return process.kill(e,0),t
5403
5485
  this.trackedChildPids.delete(e)}const{hasActiveInboxChatTurns:s}=await Promise.resolve().then(()=>t.t(86116,23))
5404
5486
  if(s())return this.parkVeto("inbox-chat-turn")
5405
5487
  const{hasParkVetoingPlannerHandoffWatches:n}=await t.e(991).then(()=>t(18991))
5406
- 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()
5407
5489
  t&&e.add(t),this.killTrackedProcesses()
5408
- try{const e=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5409
- if(D.default.existsSync(e)){const t=JSON.parse(D.default.readFileSync(e,"utf-8"))
5410
- 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)")
5411
5493
  const e=await this.relaunchOpenCode()
5412
5494
  if(this.parkController?.isStopped()||this.updateBlocked)throw e.pid&&this.killProcessGroup(e.pid),new Error("revival aborted: server stopping or in update mode")
5413
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")
5414
5496
  this.setOpenCodePort(e.port)
5415
- try{const t=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5416
- 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()
5417
- 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()
5418
- 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")
5419
- if(!D.default.existsSync(e))return
5420
- const t=JSON.parse(D.default.readFileSync(e,"utf-8"))
5421
- 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()
5422
- try{const e=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
5423
- if(D.default.existsSync(e)){const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5424
- 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}:{}}}:{}})
5425
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}
5426
5508
  if(n?.endTurn){const t=this.getOpenCodeUrl()?.replace(/\/$/,""),s=async()=>{if(!t)return
5427
5509
  const r=await(0,d.endOpenTurn)(t,e)
5428
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)
5429
5511
  if(null!==n)return n
5430
5512
  await s()}const l=await o(c,e,r,{kind:"inserted-prompt",partPayload:{nudge:!0}})
5431
- 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
5432
5514
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t
5433
- 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
5434
5516
  const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}/settled`),r=t?.data??t
5435
5517
  return"boolean"==typeof r?.settled?{settled:r.settled,..."string"==typeof r.why?{why:r.why}:{}}:null},getPendingQuestionAskerIds:async()=>{const e=new Set
5436
5518
  if(!this.projectId)return e
5437
5519
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?messageType=question&status=pending&limit=100`)
5438
- 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
5439
5521
  const t=r?.fromSessionId||r?.from_external_session_id
5440
5522
  "string"==typeof t&&t&&e.add(t)}const r=await this.apiClient.get(`/api/projects/${this.projectId}/monitors?status=armed`)
5441
5523
  for(const t of r?.data||[]){const r=t?.owner_external_session_id
5442
- "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
5443
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
5444
- 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[]
5445
5527
  try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/stranded-candidates`),t=e?.data||e
5446
5528
  return Array.isArray(t?.candidates)?t.candidates:[]}catch{return[]}},getRequestWaitState:async e=>{if(!this.projectId)return null
5447
5529
  try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests/${encodeURIComponent(e)}/wait-state`)
5448
- 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[]
5449
5531
  try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages/report-dedup-keys`),t=e?.data??e
5450
5532
  return Array.isArray(t)?t.filter(e=>"string"==typeof e&&!!e):[]}catch{return[]}},getOwnedRequestForSession:async e=>{if(!this.projectId)return null
5451
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))
5452
5534
  return r.length>0?r[r.length-1]:null}catch{return null}},getLiveRequestLocalIds:async()=>{if(!this.projectId)return new Set
5453
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}`)
5454
- 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()??[]
5455
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"})
5456
5538
  const{sessionId:r,title:s}=e.body||{},n=this.stuckSessionWatchdog.injectTestStuck(r,s)
5457
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
@@ -5460,13 +5542,13 @@ const s=await this.stuckSessionWatchdog.retrySession(r)
5460
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")
5461
5543
  for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
5462
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)
5463
- 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)()
5464
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()
5465
- 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()
5466
- try{const e=O.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
5467
- 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))
5468
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)
5469
- 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"
5470
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()
5471
5553
  return n.length>t?`${n.slice(0,t)}...`:n}async function s(e,t){const r=v.get(t)
5472
5554
  if(r&&Date.now()-r.at<w)return{model:r.model,reason:r.model?"ok":"unconfigured"}
@@ -5790,7 +5872,12 @@ a&&(s.atMs=a)
5790
5872
  const c=i.providerID??i.model?.providerID,l=i.modelID??i.model?.modelID
5791
5873
  c&&(s.providerID=String(c)),l&&(s.modelID=String(l))
5792
5874
  const d=i.tokens
5793
- 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)
5794
5881
  if(s&&Date.now()-s.at<6e5)return s.value
5795
5882
  const n=new Map
5796
5883
  try{const s=await t(`${e.replace(/\/$/,"")}/config/providers`,{signal:AbortSignal.timeout(5e3)})
@@ -5802,30 +5889,30 @@ if("number"!=typeof t?.context)continue
5802
5889
  const o={context:t.context,..."number"==typeof t.input?{input:t.input}:{},..."number"==typeof t.output?{output:t.output}:{}}
5803
5890
  n.set(`${e.id}/${r}`,o),s?.id&&s.id!==r&&n.set(`${e.id}/${s.id}`,o)}}r.set(e,{at:Date.now(),value:n})}catch{}return n}
5804
5891
  const t=new Map,r=new Map},58836(e,exports,t){"use strict"
5805
- function r(e){return"alchemist"===e?1e5:6e4}exports.startSessionCompactor=function(e){let t,i,a=0
5806
- const c=r=>e.apiClient.post("/api/liveness/report",{name:"session-compactor",project:e.projectName,description:`Compacts the hub session idle past its provider's cache window (~60-90 min, per-provider heuristic) with >${Math.round(60)}k context -> summarize so reuse is cheap`,intervalMs:6e5,lastRunAt:(new Date).toISOString(),runs:a,lastAction:t,lastActionAt:i,...r}).catch(()=>{}),l=async()=>{if((0,o.isProjectPaused)())return
5807
- const l=Date.now()
5808
- a++
5892
+ function r(e,t){const r=e?.info??e??{}
5893
+ if((0,n.isCompactionSummary)(r))return{compact:!1,why:"already compacted"}
5894
+ const s=(0,n.contextTokensOf)(r)
5895
+ return s<("alchemist"===t.agentType?1e5:6e4)?{compact:!1,why:"small"}:r.time&&!r.time.completed?{compact:!1,why:"mid-turn"}:r.providerID&&r.modelID?t.idleMs<(0,n.cacheRetentionMsFor)(r.providerID)?{compact:!1,why:"cache plausibly still warm"}:{compact:!0,ctx:s}:{compact:!1,why:"no model"}}exports.startSessionCompactor=function(e){let t,n,i=0
5896
+ const a=r=>e.apiClient.post("/api/liveness/report",{name:"session-compactor",project:e.projectName,description:`Compacts the hub session idle past its provider's cache window (~60-90 min, per-provider heuristic) with >${Math.round(60)}k context -> summarize so reuse is cheap`,intervalMs:6e5,lastRunAt:(new Date).toISOString(),runs:i,lastAction:t,lastActionAt:n,...r}).catch(()=>{}),c=async()=>{if((0,o.isProjectPaused)())return
5897
+ const c=Date.now()
5898
+ i++
5809
5899
  try{const o=e.getOpenCodeUrl()
5810
- if(!o||!e.projectId)return void await c({lastDurationMs:Date.now()-l})
5811
- const a=await(0,s.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/cli-sessions?status=active&limit=100&offset=${t}`),d=new Map
5812
- for(const e of a){const t=e.externalSessionId||e.external_session_id,r=e.agentType||e.agent_type
5813
- t&&"alchemist"===r&&"active"===(e.status??"active")&&d.set(t,{title:e.title||t,agentType:r})}if(0===d.size)return void await c({lastDurationMs:Date.now()-l})
5814
- const u=await fetch(`${o}/session?limit=1000`,{signal:AbortSignal.timeout(1e4)}),h=await u.json().catch(()=>[]),p=Date.now()
5815
- for(const s of Array.isArray(h)?h:[]){const a=d.get(s.id)
5816
- if(!a)continue
5817
- const c=a.title,l=s.time?.updated??s.time?.created??0
5818
- if(p-l<27e5)continue
5819
- let u
5900
+ if(!o||!e.projectId)return void await a({lastDurationMs:Date.now()-c})
5901
+ const i=await(0,s.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/cli-sessions?status=active&limit=100&offset=${t}`),l=new Map
5902
+ for(const e of i){const t=e.externalSessionId||e.external_session_id,r=e.agentType||e.agent_type
5903
+ t&&"alchemist"===r&&"active"===(e.status??"active")&&l.set(t,{title:e.title||t,agentType:r})}if(0===l.size)return void await a({lastDurationMs:Date.now()-c})
5904
+ const d=await fetch(`${o}/session?limit=1000`,{signal:AbortSignal.timeout(1e4)}),u=await d.json().catch(()=>[]),h=Date.now()
5905
+ for(const s of Array.isArray(u)?u:[]){const i=l.get(s.id)
5906
+ if(!i)continue
5907
+ const a=i.title,c=s.time?.updated??s.time?.created??0
5908
+ if(h-c<27e5)continue
5909
+ let d
5820
5910
  try{const e=await fetch(`${o}/session/${s.id}/message?limit=1`,{signal:AbortSignal.timeout(1e4)}),t=await e.json()
5821
- u=Array.isArray(t)?t[t.length-1]:void 0}catch{continue}const h=u?.info??u??{},f=(0,n.contextTokensOf)(h)
5822
- if(f<r(a.agentType))continue
5823
- if(h.time&&!h.time.completed)continue
5824
- if(!h.providerID||!h.modelID)continue
5825
- if(p-l<(0,n.cacheRetentionMsFor)(h.providerID))continue
5826
- const m=await fetch(`${o}/session/${s.id}/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerID:h.providerID,modelID:h.modelID}),signal:AbortSignal.timeout(12e4)})
5827
- m.ok?(t=`compacted ${a.agentType} ${c} (${Math.round(f/1e3)}k ctx, idle ${Math.round((p-l)/6e4)}m)`,i=(new Date).toISOString(),e.logger.info("Session compactor: compacted an idle session",{sessionId:s.id,title:c,ctx:f})):e.logger.warn("Session compactor: summarize failed",{sessionId:s.id,status:m.status})}await c({lastDurationMs:Date.now()-l})}catch(e){await c({lastError:String(e).slice(0,200),lastDurationMs:Date.now()-l})}}
5828
- setInterval(l,6e5),l()}
5911
+ d=Array.isArray(t)?t[t.length-1]:void 0}catch{continue}const u=d?.info??d??{},p=r(d,{agentType:i.agentType,idleMs:h-c})
5912
+ if(!p.compact)continue
5913
+ const f=p.ctx,m=await fetch(`${o}/session/${s.id}/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerID:u.providerID,modelID:u.modelID}),signal:AbortSignal.timeout(12e4)})
5914
+ m.ok?(t=`compacted ${i.agentType} ${a} (${Math.round(f/1e3)}k ctx, idle ${Math.round((h-c)/6e4)}m)`,n=(new Date).toISOString(),e.logger.info("Session compactor: compacted an idle session",{sessionId:s.id,title:a,ctx:f})):e.logger.warn("Session compactor: summarize failed",{sessionId:s.id,status:m.status})}await a({lastDurationMs:Date.now()-c})}catch(e){await a({lastError:String(e).slice(0,200),lastDurationMs:Date.now()-c})}}
5915
+ setInterval(c,6e5),c()}
5829
5916
  const s=t(5641),n=t(89063),o=t(36799)},67078(e,exports){"use strict"
5830
5917
  function t(e,t){let s=r.get(e)
5831
5918
  return s||(s=new Set,r.set(e,s)),s.add(t),()=>{const s=r.get(e)
@@ -5899,23 +5986,26 @@ return{root:(0,i.resolve)((0,a.requestWorktreePath)(e,s),o),main:!1}}const c=awa
5899
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)
5900
5987
  if(d)return{port:d.port,url:`http://127.0.0.1:${d.port}`}
5901
5988
  let u=0
5902
- const h=(0,s.createServer)(async(e,s)=>{try{t.refresh&&Date.now()-u>1e3&&(u=Date.now(),await t.refresh().catch(()=>{}))
5903
- const l=decodeURIComponent((e.url||"/").split("?")[0])
5904
- let d=(0,i.resolve)((0,i.join)(a,l))
5905
- if(d!==a&&!d.startsWith(a+i.sep))return void s.writeHead(403).end("forbidden")
5906
- if(r((0,i.relative)(a,d)))return void s.writeHead(404).end("not found")
5907
- let h=await(0,o.stat)(d).catch(()=>null)
5908
- 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(()=>"")])
5909
- 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()
5910
- const e=await(0,o.stat)((0,i.join)(a,"index.html")).catch(()=>null)
5911
- 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")
5912
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>`
5913
- 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))})
5914
- 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)
5915
6003
  return l.get(t)?.server.close(),void l.delete(t)}for(const[,e]of l)e.server.close()
5916
6004
  l.clear()},exports.stopSitesUnder=function(e){const t=(0,i.resolve)(e),r=[]
5917
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}`))
5918
- 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
5919
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
5920
6010
  return!1},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
5921
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"
@@ -6745,23 +6835,35 @@ else{const r=await(0,i.resolveDefaultBranch)(e)
6745
6835
  await u.raw(["worktree","add","-b",c,d,r]),a.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:c,mainBranch:r,worktreePath:d})}}catch(e){const r=e instanceof Error?e.message:String(e)
6746
6836
  a.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
6747
6837
  const r=t(79896),s=t(16928),n=t(83807),o=t(7202),i=t(52232)},35419(e,exports,t){"use strict"
6748
- function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){return{ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
6838
+ function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){const n=process.env.ALCHEMYLAB_ANTHROPIC_COMPACTION
6839
+ return{...void 0===n?{}:{ALCHEMYLAB_ANTHROPIC_COMPACTION:n},ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
6749
6840
  const n=t(70857),o=t(16928),i=t(14139)
6750
6841
  exports.t="2.1.280"},39068(e,exports,t){"use strict"
6751
6842
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
6752
6843
  if(e)return e
6753
6844
  try{const e=("undefined"!=typeof require?require:t(54245)).resolve("alchemylab-opencode/package.json")
6754
- return(0,n.dirname)(e)}catch{}}function s(e,t){return{command:function(){if(c)return c
6755
- try{const e=(0,i.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
6756
- if((0,a.isSupportedNode)(e))return c="node",c}catch{}const e=process.env.NVM_DIR||(0,n.join)(process.env.HOME||"",".nvm"),t=(0,n.join)(e,"versions","node")
6757
- if((0,o.existsSync)(t))try{const e=(0,o.readdirSync)(t).filter(e=>(0,a.isSupportedNode)(e)).sort((e,t)=>{const r=e.replace("v","").split(".").map(Number),s=t.replace("v","").split(".").map(Number)
6845
+ return(0,a.dirname)(e)}catch{}}function s(){if(u)return u
6846
+ try{const e=(0,l.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
6847
+ if((0,d.isSupportedNode)(e))return u="node",u}catch{}const e=process.env.NVM_DIR||(0,a.join)(process.env.HOME||"",".nvm"),t=(0,a.join)(e,"versions","node")
6848
+ if((0,c.existsSync)(t))try{const e=(0,c.readdirSync)(t).filter(e=>(0,d.isSupportedNode)(e)).sort((e,t)=>{const r=e.replace("v","").split(".").map(Number),s=t.replace("v","").split(".").map(Number)
6758
6849
  return s[0]-r[0]||s[1]-r[1]||s[2]-r[2]})
6759
- if(e.length>0){const r=(0,n.join)(t,e[0],"bin","node")
6760
- if((0,o.existsSync)(r))return c=r,c}}catch{}throw new Error((0,a.unsupportedNodeMessage)(process.version))}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
6761
- return e?s((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
6762
- return e?s((0,n.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
6763
- const n=t(16928),o=t(79896),i=t(35317),a=t(84973)
6764
- let c},69706(e,exports,t){"use strict"
6850
+ if(e.length>0){const r=(0,a.join)(t,e[0],"bin","node")
6851
+ if((0,c.existsSync)(r))return u=r,u}}catch{}throw new Error((0,d.unsupportedNodeMessage)(process.version))}function n(e,t){return{command:s(),prependArgs:["--no-warnings",(0,a.join)(e,t)]}}function o(e,t,r){const s=(0,l.spawnSync)(e,[t],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:12e4}),n=`${s.stdout??""}${s.stderr??""}`
6852
+ if(s.error||0!==s.status)throw Object.assign(new Error(s.error?.message??`exit ${s.status}`),{stderr:n})
6853
+ return n}function i(e,t=o){if(!e||process.env.ALCHEMY_OPENCODE_DIR)return"not-needed"
6854
+ if(h?.dir===e)return h.state
6855
+ const r=(0,a.join)(e,"postinstall.cjs"),n=(t,r="")=>(h={dir:e,state:t,detail:r},t)
6856
+ if(!(0,c.existsSync)(r)||!(0,c.existsSync)((0,a.join)(e,"patches")))return n("not-needed")
6857
+ let i=""
6858
+ try{i=String(JSON.parse((0,c.readFileSync)((0,a.join)(e,"package.json"),"utf8")).version??"")}catch{}const l=(0,a.join)(e,exports.BY)
6859
+ try{if(i&&(0,c.readFileSync)(l,"utf8")===i)return n("done")}catch{}try{const o=t(s(),r,e)
6860
+ return i&&(0,c.writeFileSync)(l,i),n("applied",String(o??"").trim())}catch(t){const r=String(t?.stderr||t?.message||t).trim().split("\n").slice(-3).join(" ")
6861
+ return process.stderr.write(`AlchemyLab could not apply its engine's dependency patches in ${e} (${r}); the engine starts without them.\n`),n("failed",r)}}exports.BY=void 0,exports.findOpencodeDir=r,exports.lastEnginePatchState=function(){return h},exports.resolveServeCommand=function(){const e=r()
6862
+ return e?(i(e),n((0,a.join)(e,"dist"),"serve.js")):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
6863
+ return e?(i(e),n((0,a.join)(e,"dist"),"run.js")):{command:"opencode",prependArgs:[]}}
6864
+ const a=t(16928),c=t(79896),l=t(35317),d=t(84973)
6865
+ let u,h
6866
+ exports.BY=".alchemylab-patches-applied"},69706(e,exports,t){"use strict"
6765
6867
  function r(e){try{const t=JSON.parse(a.default.readFileSync(c.default.join(e,l),"utf8"))
6766
6868
  return"string"==typeof t.password&&t.password?t.password:null}catch{return null}}function s(e){const t=process.env.OPENCODE_SERVER_USERNAME||exports.SERVE_USERNAME
6767
6869
  return`Basic ${Buffer.from(`${t}:${e}`).toString("base64")}`}function n(e){if(!d.port)return!1
@@ -7781,17 +7883,20 @@ const d=Number(s.prepare("SELECT v FROM meta WHERE k = 'generation'").get()?.v??
7781
7883
  i.prepare("UPDATE meta SET v = max(v, ?) WHERE k = 'generation'").run(d),i.exec("COMMIT")}finally{s.close()}}}finally{i.close()}if(t===p.items+e)return{projectId:s,restored:!1,unchanged:!0,items:t,keptFromLive:e}
7782
7884
  if(n.default.chmodSync(u,384),n.default.existsSync(d)){n.default.renameSync(d,`${d}.before-restore-${a}`)
7783
7885
  for(const e of["-wal","-shm"])n.default.existsSync(d+e)&&n.default.renameSync(d+e,`${d}.before-restore-${a}${e}`)}return n.default.renameSync(u,d),{projectId:s,restored:!0,items:p.items+e,keptFromLive:e}}finally{o.unhold(s),n.default.rmSync(u,{force:!0})}}}},18984(e,exports,t){"use strict"
7784
- function r(e){return"summary"===e?.claudeThinking?"summary":"omitted"}exports.MCPClientConfigurer=exports.Vr=exports.Es=exports.R1=exports.NK=void 0,exports.claudeThinkingOf=r
7785
- const s=t(75632),n=t(58824),o=t(89796),i=t(95671),a=t(43218),c=t(49779),l=t(11773),d=t(79896),u=t(16928),h=t(35317),p=t(91943),f=t(39023),m=t(35419)
7886
+ function r(e){return"summary"===e?.claudeThinking?"summary":"omitted"}function s(e,t){const r=void 0!==e.compaction?.anthropic
7887
+ if(t?!0===e.compaction?.anthropic:!r)return!1
7888
+ const{anthropic:s,...n}=e.compaction??{}
7889
+ return t?e.compaction={...n,anthropic:!0}:Object.keys(n).length>0?e.compaction=n:delete e.compaction,!0}exports.MCPClientConfigurer=exports.Vr=exports.Es=exports.R1=exports.NK=void 0,exports.claudeThinkingOf=r,exports.withAnthropicCompaction=s
7890
+ const n=t(75632),o=t(58824),i=t(89796),a=t(95671),c=t(43218),l=t(49779),d=t(11773),u=t(79896),h=t(16928),p=t(35317),f=t(91943),m=t(39023),g=t(35419)
7786
7891
  exports.NK="medium"
7787
- const g=new Set(["low","medium","high","xhigh","max"])
7788
- let w=exports.NK,y=[]
7892
+ const w=new Set(["low","medium","high","xhigh","max"])
7893
+ let y=exports.NK,v=[]
7789
7894
  exports.R1=new Set(["$schema","model","mcp","agent","mode","permission","plugin","tools","small_model","share","autoshare"]),exports.Es=new Set(["theme","keybinds","tui","layout","username","watcher","instructions"]),exports.Vr=new Set(["reviewer","acceptance-judge","command-judge","guidance-judge","intake-reviewer"])
7790
- class v{constructor(e,t,r){this.logger=e,this.agentMarkdowns=new Map,this.pinnedAgents=new Set,this.connectedProviders=null,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
7895
+ class b{constructor(e,t,r){this.logger=e,this.agentMarkdowns=new Map,this.pinnedAgents=new Set,this.connectedProviders=null,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
7791
7896
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot initialize agents"),!1
7792
7897
  try{const r=`${t}/api/agents/seed`
7793
7898
  this.logger.info("Ensuring built-in agents exist",{url:r})
7794
- const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,l.machineCallerHeaders)(t)}
7899
+ const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,d.machineCallerHeaders)(t)}
7795
7900
  this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
7796
7901
  const n=await fetch(r,{method:"POST",headers:s})
7797
7902
  n.ok||this.logger.warn("Failed to seed global agents",{status:n.status,statusText:n.statusText})
@@ -7800,16 +7905,16 @@ this.logger.info("Initializing project agent activations",{url:o})
7800
7905
  const i=await fetch(o,{method:"POST",headers:s})
7801
7906
  if(!i.ok)return this.logger.warn("Failed to initialize project agents",{status:i.status,statusText:i.statusText}),!1
7802
7907
  const a=await i.json()
7803
- return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,n.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
7908
+ return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,o.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
7804
7909
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, using baseline plugin manifest"),{...e}
7805
7910
  const r=`${t}/api/distribution/plugin-versions`
7806
- try{const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,l.machineCallerHeaders)(t)}
7911
+ try{const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,d.machineCallerHeaders)(t)}
7807
7912
  this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
7808
7913
  const n=await fetch(r,{method:"GET",headers:s})
7809
7914
  if(!n.ok)return this.logger.warn("Plugin manifest fetch returned non-OK; using baseline",{status:n.status}),{...e}
7810
7915
  const o=await n.json(),i=o?.data?.plugins??o?.plugins??{},a={...e}
7811
7916
  for(const[e,t]of Object.entries(i)){const r=t?.version
7812
- "string"==typeof r&&r.trim()&&(a[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:a}),a}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,n.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(()=>t.t(91943,23)),s=await Promise.resolve().then(()=>t.t(16928,23)),n=(await Promise.resolve().then(()=>t.t(70857,23))).homedir(),o=new Set
7917
+ "string"==typeof r&&r.trim()&&(a[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:a}),a}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,o.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(()=>t.t(91943,23)),s=await Promise.resolve().then(()=>t.t(16928,23)),n=(await Promise.resolve().then(()=>t.t(70857,23))).homedir(),o=new Set
7813
7918
  let i=e
7814
7919
  for(let e=0;e<6;e++){o.add(s.join(i,".opencode","plugin","anthropic-auth.js"))
7815
7920
  const e=s.dirname(i)
@@ -7832,17 +7937,17 @@ this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
7832
7937
  const n=await fetch(r,{method:"GET",headers:s})
7833
7938
  if(!n.ok)return this.logger.warn("Failed to fetch agents from remote",{status:n.status,statusText:n.statusText}),null
7834
7939
  const o=await n.json(),i=o.data||o
7835
- return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,n.getErrorMessage)(e)}),null}}async fetchDefaultWorkflowFromRemote(e){const t=this.remoteServerUrl
7940
+ return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,o.getErrorMessage)(e)}),null}}async fetchDefaultWorkflowFromRemote(e){const t=this.remoteServerUrl
7836
7941
  if(!t)return null
7837
7942
  try{const r=await fetch(`${t}/api/projects/${e}/default-workflow`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7838
7943
  if(!r.ok)return this.logger.warn("Failed to fetch default workflow from remote",{projectId:e,status:r.status}),null
7839
7944
  const s=await r.json()
7840
7945
  return s.data||s||null}catch(t){return this.logger.warn("Error fetching default workflow from remote",{projectId:e,error:t instanceof Error?t.message:String(t)}),null}}async fetchMcpServersFromRemote(e){const t=this.remoteServerUrl
7841
- if(!t)return y
7946
+ if(!t)return v
7842
7947
  try{const r=await fetch(`${t}/api/projects/${e}/mcp-servers`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7843
- if(!r.ok)return this.logger.warn("Failed to fetch MCP connections; keeping the last known denies",{projectId:e,status:r.status,known:y.length}),y
7948
+ if(!r.ok)return this.logger.warn("Failed to fetch MCP connections; keeping the last known denies",{projectId:e,status:r.status,known:v.length}),v
7844
7949
  const s=await r.json(),n=s.data||s||[]
7845
- return Array.isArray(n)?(y=n,n):y}catch(t){return this.logger.warn("Error fetching MCP connections; keeping the last known denies",{projectId:e,error:t instanceof Error?t.message:String(t),known:y.length}),y}}async fetchOwnerGuidanceFromRemote(e){const t=this.remoteServerUrl
7950
+ return Array.isArray(n)?(v=n,n):v}catch(t){return this.logger.warn("Error fetching MCP connections; keeping the last known denies",{projectId:e,error:t instanceof Error?t.message:String(t),known:v.length}),v}}async fetchOwnerGuidanceFromRemote(e){const t=this.remoteServerUrl
7846
7951
  if(!t)return[]
7847
7952
  try{const r=await fetch(`${t}/api/projects/${e}/owner-guidance`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7848
7953
  if(!r.ok)return[]
@@ -7854,97 +7959,97 @@ if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch project f
7854
7959
  try{const r=await fetch(`${t}/api/projects/${e}`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7855
7960
  if(!r.ok)return this.logger.warn("Failed to fetch project from remote",{projectId:e,status:r.status,statusText:r.statusText}),null
7856
7961
  const s=await r.json()
7857
- return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,n.getErrorMessage)(t)}),null}}getAgentMarkdowns(){return[...this.agentMarkdowns].map(([e,t])=>({name:e,markdown:t}))}setAgentMarkdown(e,t){this.agentMarkdowns.set(e,t)}agentModelOf(e){const t=this.agentMarkdowns.get(e),r=t?/^model:\s*"([^"\n]+)"\s*$/m.exec(t):null
7962
+ return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,o.getErrorMessage)(t)}),null}}getAgentMarkdowns(){return[...this.agentMarkdowns].map(([e,t])=>({name:e,markdown:t}))}setAgentMarkdown(e,t){this.agentMarkdowns.set(e,t)}agentModelOf(e){const t=this.agentMarkdowns.get(e),r=t?/^model:\s*"([^"\n]+)"\s*$/m.exec(t):null
7858
7963
  return r&&r[1].includes("/")?r[1]:null}agentPinnedOf(e){return this.pinnedAgents.has(e)}clearAgentMarkdowns(){this.agentMarkdowns.clear()}async removeOnDiskAgentFiles(e,r=[]){const s=await Promise.resolve().then(()=>t.t(91943,23)),n=await Promise.resolve().then(()=>t.t(16928,23)),o=new Set(r.map(e=>`${e}.md`)),i=(new Date).toISOString().slice(0,10)
7859
7964
  try{for(const t of await s.readdir(e)){if(!t.endsWith(".md"))continue
7860
7965
  const r=n.join(e,t)
7861
7966
  let a=""
7862
7967
  try{a=(await s.readFile(r,"utf8")).slice(0,400)}catch{}if(o.has(t)&&/^---[\s\S]*\bmode:\s*(primary|subagent|all)\b/m.test(a)){await s.unlink(r)
7863
7968
  continue}const c=`${r}.removed-by-alchemy-${i}`
7864
- try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&g.has(t.effort)?t.effort:exports.NK
7865
- r!==w&&this.logger.info("Effort dial read from the workflow",{effort:r,was:w}),w=r}catch{}return w}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,a.fetchConnectedProviders)(e)
7969
+ try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&w.has(t.effort)?t.effort:exports.NK
7970
+ r!==y&&this.logger.info("Effort dial read from the workflow",{effort:r,was:y}),y=r}catch{}return y}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,c.fetchConnectedProviders)(e)
7866
7971
  if(!t)return!1
7867
7972
  const r=JSON.stringify(t)!==JSON.stringify(this.connectedProviders)
7868
- return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,n,o={}){let c=r
7869
- if(e.model?.providerId&&e.model?.modelId?this.pinnedAgents.add(e.name):this.pinnedAgents.delete(e.name),e.model?.providerId&&e.model?.modelId)c=`${e.model.providerId}/${e.model.modelId}`
7870
- else if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,i.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0},o.limits)
7871
- t.resolved&&(c=t.fullModel)}const l=(0,a.runnableModel)(c,this.connectedProviders)
7872
- if(l&&l!==c){const t=`${e.name}:${c}->${l}`
7873
- v.rerouteLogged.has(t)||(v.rerouteLogged.add(t),this.logger.warn("agent model provider not connected; the agent runs on a connected provider's default until it is",{agent:e.name,configured:c,running:l})),c=l}const d=[...Object.entries(e.tools||{}).map(([e,r])=>{const s=e.replace(/-/g,"_")
7874
- return` ${v.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`}),...Object.entries(o.extraTools||{}).map(([e,t])=>` ${e}: ${t}`)].join("\n")
7875
- let u=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${c}"\n`
7876
- u+=`variant: "${e.variant||o.effort||w}"\n`
7973
+ return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,s,o={}){let i=r
7974
+ if(e.model?.providerId&&e.model?.modelId?this.pinnedAgents.add(e.name):this.pinnedAgents.delete(e.name),e.model?.providerId&&e.model?.modelId)i=`${e.model.providerId}/${e.model.modelId}`
7975
+ else if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,a.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0},o.limits)
7976
+ t.resolved&&(i=t.fullModel)}const l=(0,c.runnableModel)(i,this.connectedProviders)
7977
+ if(l&&l!==i){const t=`${e.name}:${i}->${l}`
7978
+ b.rerouteLogged.has(t)||(b.rerouteLogged.add(t),this.logger.warn("agent model provider not connected; the agent runs on a connected provider's default until it is",{agent:e.name,configured:i,running:l})),i=l}const d=[...Object.entries(e.tools||{}).map(([e,r])=>{const s=e.replace(/-/g,"_")
7979
+ return` ${b.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`}),...Object.entries(o.extraTools||{}).map(([e,t])=>` ${e}: ${t}`)].join("\n")
7980
+ let u=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${i}"\n`
7981
+ u+=`variant: "${e.variant||o.effort||y}"\n`
7877
7982
  const h=[]
7878
- "summary"===o.claudeThinking&&c.startsWith("anthropic/")&&h.push(' thinking:\n display: "summarized"'),exports.Vr.has(e.name)&&c.startsWith("anthropic/")&&h.push(' cacheTtl: "5m"')
7879
- const p=(0,s.toolScopesFor)(e.name,t,e.tools||{})
7983
+ "summary"===o.claudeThinking&&i.startsWith("anthropic/")&&h.push(' thinking:\n display: "summarized"'),exports.Vr.has(e.name)&&i.startsWith("anthropic/")&&h.push(' cacheTtl: "5m"')
7984
+ const p=(0,n.toolScopesFor)(e.name,t,e.tools||{})
7880
7985
  if(Object.keys(p).length>0&&h.push(` toolScopes: ${JSON.stringify(p)}`),h.length>0&&(u+=`options:\n${h.join("\n")}\n`),e.color&&(u+=`color: "${e.color}"\n`),u+=`tools:\n${d}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){u+="permission:\n"
7881
7986
  for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){u+=` ${t}:\n`
7882
- for(const[e,t]of Object.entries(r))u+=` "${e}": ${t}\n`}else u+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(u+=`temperature: ${e.temperature}\n`),"number"==typeof e.maxSteps&&Number.isInteger(e.maxSteps)&&e.maxSteps>0&&(u+=`steps: ${e.maxSteps}\n`),u+="---\n\n",n&&(u+=n.trim()+"\n\n"),e.prompt&&(u+=e.prompt),e.promptAddition&&(u+="\n\n"+e.promptAddition),u}async configureOpenCode(e,s,a="node",l,g){try{const w=await Promise.resolve().then(()=>t.t(91943,23)),y=await Promise.resolve().then(()=>t.t(16928,23)),b=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),k="alchemy",S=y.join(e,"mcp-direct.js")
7883
- this.logger.info("Configuring OpenCode",{isDevMode:b,serverName:k,scriptPath:S,projectPath:s}),await(0,o.ensureAlchemyIgnoreEntries)(s,this.logger),await this.sweepLegacyAuthPluginFiles(s)
7884
- const I=(0,m.generatedOpencodeConfigPath)(s),$=y.join(s,"opencode.json")
7885
- let j=null,E=null,A=null
7886
- const C={}
7887
- let _=null,T=!1
7888
- const x=y.join(s,".alchemy_lab","config.json")
7889
- if(!l)try{const e=await w.readFile(x,"utf-8"),t=JSON.parse(e)
7890
- t?.openCodeTools?(j=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:j})):T=!0,t?.openCodeModel&&(E=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),T=!0}{let e=null
7891
- try{e=JSON.parse(await w.readFile(I,"utf-8"))}catch{}const t=await async function(e,t){const r=(0,u.join)(e,"opencode.json")
7987
+ for(const[e,t]of Object.entries(r))u+=` "${e}": ${t}\n`}else u+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(u+=`temperature: ${e.temperature}\n`),"number"==typeof e.maxSteps&&Number.isInteger(e.maxSteps)&&e.maxSteps>0&&(u+=`steps: ${e.maxSteps}\n`),u+="---\n\n",s&&(u+=s.trim()+"\n\n"),e.prompt&&(u+=e.prompt),e.promptAddition&&(u+="\n\n"+e.promptAddition),u}async configureOpenCode(e,n,c="node",d,w){try{const v=await Promise.resolve().then(()=>t.t(91943,23)),k=await Promise.resolve().then(()=>t.t(16928,23)),S=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),I="alchemy",$=k.join(e,"mcp-direct.js")
7988
+ this.logger.info("Configuring OpenCode",{isDevMode:S,serverName:I,scriptPath:$,projectPath:n}),await(0,i.ensureAlchemyIgnoreEntries)(n,this.logger),await this.sweepLegacyAuthPluginFiles(n)
7989
+ const j=(0,g.generatedOpencodeConfigPath)(n),E=k.join(n,"opencode.json")
7990
+ let A=null,C=null,_=null
7991
+ const T={}
7992
+ let x=null,M=!1
7993
+ const R=k.join(n,".alchemy_lab","config.json")
7994
+ if(!d)try{const e=await v.readFile(R,"utf-8"),t=JSON.parse(e)
7995
+ t?.openCodeTools?(A=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:A})):M=!0,t?.openCodeModel&&(C=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),M=!0}{let e=null
7996
+ try{e=JSON.parse(await v.readFile(j,"utf-8"))}catch{}const t=await async function(e,t){const r=(0,h.join)(e,"opencode.json")
7892
7997
  let s
7893
- try{s=JSON.parse(await(0,p.readFile)(r,"utf8"))}catch{return null}if(!s||"object"!=typeof s||Array.isArray(s)||!s.mcp||"object"!=typeof s.mcp||!("alchemy"in s.mcp))return null
7894
- try{return await(0,f.promisify)(h.execFile)("git",["ls-files","--error-unmatch","--","opencode.json"],{cwd:e}),null}catch{}const o=Object.fromEntries(Object.entries(s).filter(([e])=>!exports.R1.has(e)&&"instructions"!==e&&"skills"!==e))
7895
- try{0===Object.keys(o).length?await(0,p.unlink)(r):await(0,p.writeFile)(r,JSON.stringify(o,null,2),"utf8"),t?.info("Moved the generated OpenCode config out of the project folder",{removed:0===Object.keys(o).length,keptUserKeys:Object.keys(o)})}catch(e){t?.warn("Could not clear the old generated opencode.json from the project folder",{error:(0,n.getErrorMessage)(e)})}return s}(s,this.logger)
7896
- e=e??t,!l&&e?.mode?.alchemy?.tools&&(A=e.mode.alchemy.tools,this.logger.info("Found existing tool settings in the generated OpenCode config",{existingTools:A})),!l&&e?.model&&(_=e.model)
7897
- try{const e=JSON.parse(await w.readFile($,"utf-8")),{kept:t,setAside:r}=function(e){const t={},r={}
7998
+ try{s=JSON.parse(await(0,f.readFile)(r,"utf8"))}catch{return null}if(!s||"object"!=typeof s||Array.isArray(s)||!s.mcp||"object"!=typeof s.mcp||!("alchemy"in s.mcp))return null
7999
+ try{return await(0,m.promisify)(p.execFile)("git",["ls-files","--error-unmatch","--","opencode.json"],{cwd:e}),null}catch{}const n=Object.fromEntries(Object.entries(s).filter(([e])=>!exports.R1.has(e)&&"instructions"!==e&&"skills"!==e))
8000
+ try{0===Object.keys(n).length?await(0,f.unlink)(r):await(0,f.writeFile)(r,JSON.stringify(n,null,2),"utf8"),t?.info("Moved the generated OpenCode config out of the project folder",{removed:0===Object.keys(n).length,keptUserKeys:Object.keys(n)})}catch(e){t?.warn("Could not clear the old generated opencode.json from the project folder",{error:(0,o.getErrorMessage)(e)})}return s}(n,this.logger)
8001
+ e=e??t,!d&&e?.mode?.alchemy?.tools&&(_=e.mode.alchemy.tools,this.logger.info("Found existing tool settings in the generated OpenCode config",{existingTools:_})),!d&&e?.model&&(x=e.model)
8002
+ try{const e=JSON.parse(await v.readFile(E,"utf-8")),{kept:t,setAside:r}=function(e){const t={},r={}
7898
8003
  if(!e||"object"!=typeof e||Array.isArray(e))return{kept:t,setAside:r}
7899
8004
  for(const[s,n]of Object.entries(e))if(!exports.R1.has(s))if(exports.Es.has(s)){if("instructions"===s&&Array.isArray(n)){const e=n.filter(e=>"string"==typeof e&&!/^[a-z][a-z0-9+.-]*:\/\//i.test(e)),o=n.filter(t=>!e.includes(t))
7900
8005
  e.length>0&&(t[s]=e),o.length>0&&(r[s]=o)
7901
8006
  continue}t[s]=n}else r[s]=n
7902
8007
  return{kept:t,setAside:r}}(e)
7903
- Object.assign(C,t),Object.keys(t).length>0&&this.logger.info("Keeping opencode.json keys AlchemyLab does not manage",{keys:Object.keys(t)}),Object.keys(r).length>0&&this.logger.warn("Not loading opencode.json keys: they run commands, change the runtime or send data elsewhere (providers are connected in Settings); the file is left as it is",{keys:Object.keys(r)})}catch{this.logger.info("No opencode.json of the user's in the project folder")}}const M=l?.openCodeTools||j||A||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===l?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==g,todoread:!1,todowrite:!1},R={tools:M}
7904
- let P=null,O=null
7905
- if(l?.projectId&&!l?.openCodeModel)try{O=await this.fetchProjectFromRemote(l.projectId)
7906
- const e=O?.metadata?.defaultModel
7907
- "string"==typeof e&&e.includes("/")&&(P=e)}catch(e){this.logger.warn("Could not fetch project default model; falling back",{error:e instanceof Error?e.message:String(e)})}const D=l?.openCodeModel||P||E||_||i.FLEET_DEFAULT_MODEL
7908
- D!==i.FLEET_DEFAULT_MODEL||l?.openCodeModel||P||E||_||this.logger.warn("No default model configured anywhere for this project; agent files fall back to the fleet default model",{projectId:l?.projectId??null}),l?.openCodeModel?R.model=l.openCodeModel:P?R.model=P:E?R.model=E:_&&(R.model=_)
7909
- const q=y.join(s,".opencode","agent")
7910
- await w.mkdir(q,{recursive:!0})
7911
- const N=D||i.FLEET_DEFAULT_MODEL
7912
- let F,L=null,U=null,B=[],H=null
7913
- if(l?.projectId)if(await this.seedAgentsFromRemote(l.projectId),U=O??await this.fetchProjectFromRemote(l.projectId),F=U?.metadata?.globalSystemPrompt,F&&this.logger.info("Using global system prompt from project metadata",{projectId:l.projectId,promptLength:F.length}),F=await this.agentPreambleFor(l.projectId,F),B=await this.fetchMcpServersFromRemote(l.projectId),H=(await this.fetchDefaultWorkflowFromRemote(l.projectId))?.connectionPermissions??null,L=await this.fetchAgentsFromRemote(l.projectId),L&&L.length>0){this.logger.info("Using agents from remote database",{count:L.length})
7914
- const e=await this.resolveWorkflowEffort(l.projectId)
8008
+ Object.assign(T,t),Object.keys(t).length>0&&this.logger.info("Keeping opencode.json keys AlchemyLab does not manage",{keys:Object.keys(t)}),Object.keys(r).length>0&&this.logger.warn("Not loading opencode.json keys: they run commands, change the runtime or send data elsewhere (providers are connected in Settings); the file is left as it is",{keys:Object.keys(r)})}catch{this.logger.info("No opencode.json of the user's in the project folder")}}const P=d?.openCodeTools||A||_||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===d?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==w,todoread:!1,todowrite:!1},O={tools:P}
8009
+ let D=null,q=null
8010
+ if(d?.projectId&&!d?.openCodeModel)try{q=await this.fetchProjectFromRemote(d.projectId)
8011
+ const e=q?.metadata?.defaultModel
8012
+ "string"==typeof e&&e.includes("/")&&(D=e)}catch(e){this.logger.warn("Could not fetch project default model; falling back",{error:e instanceof Error?e.message:String(e)})}const N=d?.openCodeModel||D||C||x||a.FLEET_DEFAULT_MODEL
8013
+ N!==a.FLEET_DEFAULT_MODEL||d?.openCodeModel||D||C||x||this.logger.warn("No default model configured anywhere for this project; agent files fall back to the fleet default model",{projectId:d?.projectId??null}),d?.openCodeModel?O.model=d.openCodeModel:D?O.model=D:C?O.model=C:x&&(O.model=x)
8014
+ const F=k.join(n,".opencode","agent")
8015
+ await v.mkdir(F,{recursive:!0})
8016
+ const L=N||a.FLEET_DEFAULT_MODEL
8017
+ let U,B=null,H=null,W=[],G=null
8018
+ if(d?.projectId)if(await this.seedAgentsFromRemote(d.projectId),H=q??await this.fetchProjectFromRemote(d.projectId),U=H?.metadata?.globalSystemPrompt,U&&this.logger.info("Using global system prompt from project metadata",{projectId:d.projectId,promptLength:U.length}),U=await this.agentPreambleFor(d.projectId,U),W=await this.fetchMcpServersFromRemote(d.projectId),G=(await this.fetchDefaultWorkflowFromRemote(d.projectId))?.connectionPermissions??null,B=await this.fetchAgentsFromRemote(d.projectId),B&&B.length>0){this.logger.info("Using agents from remote database",{count:B.length})
8019
+ const e=await this.resolveWorkflowEffort(d.projectId)
7915
8020
  this.clearAgentMarkdowns()
7916
- for(const t of L)t.enabled&&this.setAgentMarkdown(t.name,this.generateAgentMarkdown(t,k,N,F,{effort:e,extraTools:(0,c.agentToolGrants)(B,t.name,H),limits:(0,i.modelLimitsOf)(U?.metadata),claudeThinking:r(U?.metadata)}))
7917
- await this.removeOnDiskAgentFiles(q,[...L.filter(e=>e.enabled).map(e=>e.name),"explore"]),this.logger.info("Agent prompts held in memory (served to OpenCode over the action server)",{count:this.agentMarkdowns.size})
7918
- try{const e=new Set([...L.filter(e=>e.enabled).map(e=>`${e.name}.md`),"explore.md"])
7919
- for(const t of await w.readdir(q))if(t.endsWith(".md")&&!e.has(t)){const e=y.join(q,t),r=`${e}.removed-by-alchemy-${(new Date).toISOString().slice(0,10)}`
7920
- try{await w.rename(e,r),this.logger.info(`Moved agent file ${t} aside (no agent by that name)`,{movedTo:r})}catch(e){this.logger.warn(`Could not move agent file ${t} aside; leaving it`,{error:e instanceof Error?e.message:String(e)})}}}catch(e){this.logger.warn("Orphaned agent-file cleanup failed",{error:e instanceof Error?e.message:String(e)})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
8021
+ for(const t of B)t.enabled&&this.setAgentMarkdown(t.name,this.generateAgentMarkdown(t,I,L,U,{effort:e,extraTools:(0,l.agentToolGrants)(W,t.name,G),limits:(0,a.modelLimitsOf)(H?.metadata),claudeThinking:r(H?.metadata)}))
8022
+ await this.removeOnDiskAgentFiles(F,[...B.filter(e=>e.enabled).map(e=>e.name),"explore"]),this.logger.info("Agent prompts held in memory (served to OpenCode over the action server)",{count:this.agentMarkdowns.size})
8023
+ try{const e=new Set([...B.filter(e=>e.enabled).map(e=>`${e.name}.md`),"explore.md"])
8024
+ for(const t of await v.readdir(F))if(t.endsWith(".md")&&!e.has(t)){const e=k.join(F,t),r=`${e}.removed-by-alchemy-${(new Date).toISOString().slice(0,10)}`
8025
+ try{await v.rename(e,r),this.logger.info(`Moved agent file ${t} aside (no agent by that name)`,{movedTo:r})}catch(e){this.logger.warn(`Could not move agent file ${t} aside; leaving it`,{error:e instanceof Error?e.message:String(e)})}}}catch(e){this.logger.warn("Orphaned agent-file cleanup failed",{error:e instanceof Error?e.message:String(e)})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
7921
8026
  else this.logger.warn("No projectId provided - agents must be configured via database")
7922
- const W={$schema:"https://opencode.ai/config.json",share:"disabled",model:D,mcp:{[k]:{type:"local",command:[a,S,s],enabled:!0,timeout:6e5}}}
7923
- b&&process.env.REMOTE_SERVER_URL&&(W.mcp[k].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL})
7924
- const G=l?.projectId?await this.fetchDefaultWorkflowFromRemote(l.projectId):null,K=!1!==G?.webAccess
7925
- if(W.permission={...W.permission??{},bash:"deny"},Object.assign(W.permission,(0,c.globalDenyRules)(B)),L&&L.length>0){W.agent={}
7926
- for(const e of L){if(!e.enabled)continue
8027
+ const K={$schema:"https://opencode.ai/config.json",share:"disabled",model:N,mcp:{[I]:{type:"local",command:[c,$,n],enabled:!0,timeout:6e5}}}
8028
+ S&&process.env.REMOTE_SERVER_URL&&(K.mcp[I].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL})
8029
+ const z=d?.projectId?await this.fetchDefaultWorkflowFromRemote(d.projectId):null,V=!1!==z?.webAccess
8030
+ if(K.permission={...K.permission??{},bash:"deny"},Object.assign(K.permission,(0,l.globalDenyRules)(W)),B&&B.length>0){K.agent={}
8031
+ for(const e of B){if(!e.enabled)continue
7927
8032
  const t={}
7928
8033
  for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
7929
- t[v.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${k}_${n}`]=e}if(!K)for(const e of v.WEB_TOOLS)t[v.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${k}_${e}`]=!1
7930
- Object.assign(t,(0,c.agentToolGrants)(B,e.name,H)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),W.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}W.agent.general={...W.agent.general,disable:!0},W.agent.explore={...W.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${k}_search`]:!0,[`${k}_read_files`]:!0,[`${k}_view_asset`]:!0,[`${k}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
7931
- const e=this.buildExploreMarkdown(F)
7932
- this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(W.agent).length})}const z=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),V=(await this.fetchPluginManifest())["opencode-claude-auth"],Y=W.plugin,J=Array.isArray(Y)?Y.filter(e=>!z(e)):[]
7933
- V&&this.applyAuthPluginPin(J,V),J.length>0?W.plugin=J:delete W.plugin
7934
- try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,d.existsSync)(y.join(s,e)))
7935
- e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}W.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,u.join)(e,t)).find(e=>(0,d.existsSync)(e))??(0,u.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,u.isAbsolute)(t)?t:(0,u.join)(e,t))
7936
- return[...new Set([r,...s])]}(s,C.instructions)
7937
- const X=[".opencode/skill",".opencode/skills",".claude/skills",".agents/skills"].map(e=>y.join(s,e)).filter(e=>(0,d.existsSync)(e))
7938
- X.length>0&&(W.skills={paths:X})
7939
- const Q={...C,...W}
7940
- await w.mkdir(y.dirname(I),{recursive:!0}),await w.writeFile(I,JSON.stringify(Q,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:I})
7941
- try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=y.join(e.homedir(),".alchemy_lab","opencode","config"),s=y.join(r,"opencode.json")
8034
+ t[b.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${I}_${n}`]=e}if(!V)for(const e of b.WEB_TOOLS)t[b.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${I}_${e}`]=!1
8035
+ Object.assign(t,(0,l.agentToolGrants)(W,e.name,G)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),K.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}K.agent.general={...K.agent.general,disable:!0},K.agent.explore={...K.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${I}_search`]:!0,[`${I}_read_files`]:!0,[`${I}_view_asset`]:!0,[`${I}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
8036
+ const e=this.buildExploreMarkdown(U)
8037
+ this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(K.agent).length})}const Y=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),J=(await this.fetchPluginManifest())["opencode-claude-auth"],X=K.plugin,Q=Array.isArray(X)?X.filter(e=>!Y(e)):[]
8038
+ J&&this.applyAuthPluginPin(Q,J),Q.length>0?K.plugin=Q:delete K.plugin
8039
+ try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,u.existsSync)(k.join(n,e)))
8040
+ e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}K.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,h.join)(e,t)).find(e=>(0,u.existsSync)(e))??(0,h.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,h.isAbsolute)(t)?t:(0,h.join)(e,t))
8041
+ return[...new Set([r,...s])]}(n,T.instructions)
8042
+ const Z=[".opencode/skill",".opencode/skills",".claude/skills",".agents/skills"].map(e=>k.join(n,e)).filter(e=>(0,u.existsSync)(e))
8043
+ Z.length>0&&(K.skills={paths:Z})
8044
+ const ee={...T,...K}
8045
+ s(ee,(y=H?.metadata,!0===y?.anthropicCompaction)),await v.mkdir(k.dirname(j),{recursive:!0}),await v.writeFile(j,JSON.stringify(ee,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:j})
8046
+ try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=k.join(e.homedir(),".alchemy_lab","opencode","config"),s=k.join(r,"opencode.json")
7942
8047
  let n=null
7943
- try{n=JSON.parse(await w.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!z(e))
7944
- V&&this.applyAuthPluginPin(a,V),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await w.mkdir(r,{recursive:!0}),await w.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(T&&!l)try{let e={}
7945
- try{const t=await w.readFile(x,"utf-8")
7946
- e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=M,await w.writeFile(x,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:x}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:n}=await t.e(939).then(()=>t.t(49939,23)),o=await r(b??!1,this.logger,e)
7947
- o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await n(s,this.logger)}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:I}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,n.getErrorMessage)(e)}),{success:!1,error:(0,n.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=v,v.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],v.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),v.rerouteLogged=new Set},6688(e,exports,t){"use strict"
8048
+ try{n=JSON.parse(await v.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!Y(e))
8049
+ J&&this.applyAuthPluginPin(a,J),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await v.mkdir(r,{recursive:!0}),await v.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(M&&!d)try{let e={}
8050
+ try{const t=await v.readFile(R,"utf-8")
8051
+ e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=P,await v.writeFile(R,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:R}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:s}=await t.e(939).then(()=>t.t(49939,23)),o=await r(S??!1,this.logger,e)
8052
+ o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await s(n,this.logger)}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:j}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,o.getErrorMessage)(e)}),{success:!1,error:(0,o.getErrorMessage)(e)}}var y}}exports.MCPClientConfigurer=b,b.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],b.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),b.rerouteLogged=new Set},6688(e,exports,t){"use strict"
7948
8053
  exports.sessionIdentity=void 0
7949
8054
  const r=t(16698),s=t(79896),n=t(16928),o=t(43545),i=t(79414),a=t(87280)
7950
8055
  class c{constructor(){this.parentCache=new Map,this.sessionRequestCache=new Map,this.endedUnderSession=new Map,this.callScope=new r.AsyncLocalStorage,this.agentTypeCache=new Map}setIdentity(e){const t=this.identity
@@ -8019,10 +8124,10 @@ t.info("UnifiedLauncher: reaping stale serve from a previous action-server life"
8019
8124
  try{process.kill(-n,"SIGTERM")}catch{try{process.kill(n,"SIGTERM")}catch{}}await new Promise(e=>setTimeout(e,1500))
8020
8125
  try{process.kill(n,0)
8021
8126
  try{process.kill(-n,"SIGKILL")}catch{try{process.kill(n,"SIGKILL")}catch{}}t.warn("UnifiedLauncher: stale serve needed SIGKILL",{pid:n})}catch{}}catch(e){t.warn("UnifiedLauncher: stale-serve reap failed (continuing with launch)",{error:(0,m.getErrorMessage)(e)})}}(w,this.logger)
8022
- const{resolveServeCommand:v,findOpencodeDir:k}=await Promise.resolve().then(()=>t(39068))
8127
+ const{resolveServeCommand:v,findOpencodeDir:k,lastEnginePatchState:S}=await Promise.resolve().then(()=>t(39068))
8023
8128
  if(!k())try{(0,f.execFileSync)("win32"===process.platform?"where":"which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"alchemylab-opencode package not installed and no system `opencode` on PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:w})
8024
- const S=v()
8025
- return new Promise(t=>{const v=l.default.join(w,".alchemy_lab","opencode"),k=process.env.XDG_DATA_HOME||l.default.join(d.default.homedir(),".local","share")
8129
+ const I=v(),$=S()
8130
+ return"applied"===$?.state?this.logger.info("UnifiedLauncher: applied the engine's dependency patches (its install script had not run)",{dir:$.dir,output:$.detail}):"failed"===$?.state&&this.logger.warn("UnifiedLauncher: the engine's dependency patches could not be applied; it starts without them",{dir:$.dir,error:$.detail}),new Promise(t=>{const v=l.default.join(w,".alchemy_lab","opencode"),k=process.env.XDG_DATA_HOME||l.default.join(d.default.homedir(),".local","share")
8026
8131
  try{u.default.mkdirSync(l.default.join(k,"opencode"),{recursive:!0})}catch(e){this.logger.warn("UnifiedLauncher: Could not pre-create opencode globalData dir",{error:(0,m.getErrorMessage)(e)})}try{!function(e){const t=function(){const e=l.default.join(d.default.homedir(),".alchemy_lab","opencode","config","opencode.json")
8027
8132
  try{const t=JSON.parse(u.default.readFileSync(e,"utf-8"))
8028
8133
  if(Array.isArray(t.plugin)){const e=t.plugin.find(e=>"string"==typeof e&&/^opencode-claude-auth@/.test(e))
@@ -8031,15 +8136,15 @@ if(-1!==t)return e.slice(t+1)}}}catch{}return"1.5.3"}(),i=l.default.join(d.defau
8031
8136
  if(!u.default.existsSync(c))try{u.default.mkdirSync(a,{recursive:!0}),u.default.writeFileSync(l.default.join(a,"package.json"),JSON.stringify({name:"alchemylab-claude-auth-wrapper",private:!0,version:"0.0.0"},null,2)),e.info("UnifiedLauncher: Pre-installing opencode-claude-auth plugin",{version:t,targetDir:a}),(0,f.execFileSync)("npm",["install",`opencode-claude-auth@${t}`,"--no-audit","--no-fund","--silent","--ignore-scripts"],{cwd:a,stdio:"pipe",timeout:12e4}),e.info("UnifiedLauncher: Pre-installed opencode-claude-auth plugin",{version:t})}catch(t){e.warn("UnifiedLauncher: claude-auth pre-install failed; opencode will install lazily and first OAuth click may crash",{error:(0,m.getErrorMessage)(t)})}if(u.default.existsSync(c)&&(r(c,e,`opencode-claude-auth@${t}`),s(l.default.join(l.default.dirname(c),"credentials.js"),e,`opencode-claude-auth@${t}`),n(c,e,`opencode-claude-auth@${t}`),o(c,e,`opencode-claude-auth@${t}`)),u.default.existsSync(i)){let t
8032
8137
  try{t=u.default.readdirSync(i)}catch{return}for(const a of t){if(!a.startsWith("opencode-claude-auth@"))continue
8033
8138
  const t=l.default.join(i,a,"node_modules","opencode-claude-auth","dist","index.js")
8034
- t!==c&&(r(t,e,a),s(l.default.join(l.default.dirname(t),"credentials.js"),e,a),n(t,e,a),o(t,e,a))}}}(this.logger)}catch(e){this.logger.warn("UnifiedLauncher: claude-auth ensure-patched failed",{error:(0,m.getErrorMessage)(e)})}const I=l.default.join(v,"log"),$=l.default.join(I,"opencode-serve.log")
8139
+ t!==c&&(r(t,e,a),s(l.default.join(l.default.dirname(t),"credentials.js"),e,a),n(t,e,a),o(t,e,a))}}}(this.logger)}catch(e){this.logger.warn("UnifiedLauncher: claude-auth ensure-patched failed",{error:(0,m.getErrorMessage)(e)})}const S=l.default.join(v,"log"),$=l.default.join(S,"opencode-serve.log")
8035
8140
  let j=null,E=0
8036
8141
  const A=e=>{if(!j)return
8037
8142
  const t=j
8038
8143
  j=null,t.end(e)},C=e=>{u.default.writeFileSync($,e),j=u.default.createWriteStream($,{flags:"a"}),E=e.length},_=e=>{if(j&&(E+=e.length,j.write(e),E>=20971520))try{const e=j
8039
8144
  j=null,e.end(),u.default.renameSync($,`${$}.1`),C(`# opencode-serve.log \u2014 rotated ${(new Date).toISOString()} (previous in opencode-serve.log.1)\n`)}catch(e){this.logger.warn("UnifiedLauncher: opencode log rotation failed; logging stops for this launch",{error:(0,m.getErrorMessage)(e)})}}
8040
- try{u.default.mkdirSync(I,{recursive:!0}),C(`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`)}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,m.getErrorMessage)(e)})}const T=process.env.ALCHEMY_OPENCODE_LOG_LEVEL||"WARN",x=(0,g.newServePassword)()
8145
+ try{u.default.mkdirSync(S,{recursive:!0}),C(`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`)}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,m.getErrorMessage)(e)})}const T=process.env.ALCHEMY_OPENCODE_LOG_LEVEL||"WARN",x=(0,g.newServePassword)()
8041
8146
  try{(0,g.writeServeAuth)(w,x)}catch(e){this.logger.warn("UnifiedLauncher: could not record the serve password on disk; only this process can reach the serve",{error:(0,m.getErrorMessage)(e)})}(0,g.noteServe)(null,x),(0,c.noteServeLoadedLogins)()
8042
- const M=(0,f.spawn)(S.command,[...S.prependArgs,"--port","0","--data-dir",v,"--log-level",T],{stdio:["ignore","pipe","pipe"],cwd:w,detached:!0,env:{...process.env,...(0,p.getOpencodeSpawnEnv)(w,(0,h.isDevMode)()),...y,OPENCODE_SERVER_PASSWORD:x}})
8147
+ const M=(0,f.spawn)(I.command,[...I.prependArgs,"--port","0","--data-dir",v,"--log-level",T],{stdio:["ignore","pipe","pipe"],cwd:w,detached:!0,env:{...process.env,...(0,p.getOpencodeSpawnEnv)(w,(0,h.isDevMode)()),...y,OPENCODE_SERVER_PASSWORD:x}})
8043
8148
  let R="",P=!1
8044
8149
  if(M.pid)try{const e=l.default.join(w,b,"opencode.json")
8045
8150
  u.default.mkdirSync(l.default.dirname(e),{recursive:!0}),u.default.writeFileSync(e,JSON.stringify({pid:M.pid,port:null,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("UnifiedLauncher: could not record the serve pid (an orphan would survive a crash)",{error:(0,m.getErrorMessage)(e)})}const O=e=>{if(P)return
@@ -8165,16 +8270,16 @@ return e.ok?e.version??"":a(3e4)}():await async function(){if(console.log(" In
8165
8270
  const e=await o()
8166
8271
  return e.ok?e.version??"":e.permissionDenied?(c(),null):a(15e3)}()}return null!==h?(console.log("\u2705 Docker ready"+(h?` (server v${h})`:"")),{status:"ready",version:h}):{status:"unavailable",reason:"setup did not complete"}}
8167
8272
  const d=t(35317),u=t(79896),h=t(23785)},89063(e,exports){"use strict"
8168
- function t(e){return exports.lS[String(e??"").toLowerCase()]??exports.Ey}function r(e){const r=t(e).atLeast
8169
- return r-Math.min(5*s,r/10)}exports.Ey=exports.lS=void 0,exports.tokensOfMessage=function(e){const t={input:0,output:0,reasoning:0,cacheRead:0,cacheWrite:0,cost:0,hasUsage:!1}
8273
+ function t(e){return"assistant"===e?.role&&!0===e?.summary}function r(e){return exports.lS[String(e??"").toLowerCase()]??exports.Ey}function s(e){const t=r(e).atLeast
8274
+ return t-Math.min(5*n,t/10)}exports.Ey=exports.lS=void 0,exports.tokensOfMessage=function(e){const t={input:0,output:0,reasoning:0,cacheRead:0,cacheWrite:0,cost:0,hasUsage:!1}
8170
8275
  for(const r of e?.parts||[])if("step-finish"===r?.type&&r?.tokens){t.hasUsage=!0
8171
8276
  const e=r.tokens
8172
8277
  t.input+=e.input||0,t.output+=e.output||0,t.reasoning+=e.reasoning||0,t.cacheRead+=e.cache?.read||0,t.cacheWrite+=e.cache?.write||0,t.cost+=r.cost||0}const r=e?.info
8173
8278
  if(!t.hasUsage&&r?.tokens){const e=r.tokens
8174
- t.input=e.input||0,t.output=e.output||0,t.reasoning=e.reasoning||0,t.cacheRead=e.cache?.read||0,t.cacheWrite=e.cache?.write||0,t.cost=r.cost||0,t.hasUsage=t.input>0||t.output>0||t.cacheRead>0||t.cacheWrite>0||t.cost>0}return t},exports.contextTokensOf=function(e){const t=e?.tokens||{}
8175
- return(t.input??0)+(t.cache?.read??0)},exports.cacheRetentionMsFor=function(e){return t(e).atMost},exports.cacheWarmForMs=r,exports.longestCacheWarmMs=function(){return Math.max(r(null),...Object.keys(exports.lS).map(r))}
8176
- const s=6e4
8177
- exports.lS={anthropic:{atLeast:60*s,atMost:65*s},openai:{atLeast:60*s,atMost:90*s}},exports.Ey={atLeast:5*s,atMost:60*s}},75632(e,exports){"use strict"
8279
+ t.input=e.input||0,t.output=e.output||0,t.reasoning=e.reasoning||0,t.cacheRead=e.cache?.read||0,t.cacheWrite=e.cache?.write||0,t.cost=r.cost||0,t.hasUsage=t.input>0||t.output>0||t.cacheRead>0||t.cacheWrite>0||t.cost>0}return t},exports.contextTokensOf=function(e){const r=e?.tokens||{}
8280
+ return t(e)?r.output??0:(r.input??0)+(r.cache?.read??0)},exports.isCompactionSummary=t,exports.cacheRetentionMsFor=function(e){return r(e).atMost},exports.cacheWarmForMs=s,exports.longestCacheWarmMs=function(){return Math.max(s(null),...Object.keys(exports.lS).map(s))}
8281
+ const n=6e4
8282
+ exports.lS={anthropic:{atLeast:60*n,atMost:65*n},openai:{atLeast:60*n,atMost:90*n}},exports.Ey={atLeast:5*n,atMost:60*n}},75632(e,exports){"use strict"
8178
8283
  exports.REGISTER_BY_SETUP_RUN=exports.b=void 0,exports.toolScopesFor=function(e,t,r){const s=exports.b[e]
8179
8284
  if(!s)return{}
8180
8285
  const n={}
@@ -10074,10 +10179,10 @@ for(const r of Array.isArray(e)?e:[]){const e=o(r)
10074
10179
  if(e)for(const n of s(r))t.set(n.trim(),e)}return t}
10075
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"
10076
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)
10077
- 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
10078
- const t=Math.min(e.output??0,a)||a
10079
- return e.input?e.input-Math.min(2e4,t):e.context-t}(e)
10080
- 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"}
10081
10186
  if(!o.length)return{reuse:!1,why:"no-candidate"}
10082
10187
  const a=["role","unrelated","model","tools","busy","context","archived","cold","cold-context","stale"]
10083
10188
  let c=0