alchemylab 3.0.6 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +448 -362
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1133,11 +1133,11 @@ let d,u
|
|
|
1133
1133
|
if(a(s,c,e.health?.service),e.health){const t=await n(o,e.health)
|
|
1134
1134
|
d=t.body||void 0,u=t.healthy}return{projectName:s,target:t,running:l,health:d,healthy:u}}
|
|
1135
1135
|
const p=t(79896),f=h(t(16928)),m=u(t(7089)),g=t(55481),w=t(81618),y=t(28295),v=m.safeLoad||m.load,b=m.safeDump||m.dump},46508(e,exports,t){"use strict"
|
|
1136
|
-
async function r(e,t,r){const s=(0,
|
|
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,
|
|
1140
|
-
return!!await(0,
|
|
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,
|
|
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
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
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
|
|
1160
|
-
try{await l.stat(i),
|
|
1161
|
-
const
|
|
1162
|
-
|
|
1163
|
-
const
|
|
1164
|
-
for(const e of(0,
|
|
1165
|
-
|
|
1166
|
-
const
|
|
1167
|
-
return o.info(`Pruned worktree for retired request (${
|
|
1168
|
-
const a=t(58824),c=t(7202),l=i(t(91943)),d=t(
|
|
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
|
|
1990
|
-
if(
|
|
1991
|
-
if(v.roundOf&&"string"==typeof s.unitWorker&&s.unitWorker&&null!==(0,f.kindProfile)(v.type).worker)
|
|
1992
|
-
if(!e.
|
|
1993
|
-
if(
|
|
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
|
|
1996
|
-
const
|
|
1997
|
-
let
|
|
1998
|
-
const
|
|
1999
|
-
if("string"==typeof
|
|
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:
|
|
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,
|
|
2009
|
-
if(await c(t,
|
|
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)
|
|
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.
|
|
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},
|
|
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
|
|
3576
|
-
const s=
|
|
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
|
|
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,
|
|
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,
|
|
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",
|
|
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??
|
|
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,
|
|
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",
|
|
3624
|
-
try{await(0,
|
|
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,
|
|
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,
|
|
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
|
|
3660
|
-
const
|
|
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)},
|
|
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
|
|
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(
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
3693
|
-
for(const[t,r]of
|
|
3694
|
-
setInterval(
|
|
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
|
|
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
|
-
|
|
3704
|
-
let
|
|
3705
|
-
const m=l?
|
|
3706
|
-
if(m)a=m.browser,u=m.page,
|
|
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(!
|
|
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)))}
|
|
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
|
|
3713
|
-
return"final"===e&&(i=t),`asset:${await(0,
|
|
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
|
|
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:
|
|
3728
|
-
e&&(
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
3751
|
-
try{c=await
|
|
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
|
|
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,
|
|
3759
|
-
else try{c=await
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
3853
|
+
const c=(0,U.simpleGit)(o.projectPath),l=["blame","--line-porcelain","-L",`${s},${i}`]
|
|
3776
3854
|
a&&l.push(a),l.push("--",r)
|
|
3777
3855
|
const d=await c.raw(l),u=[]
|
|
3778
3856
|
let h=null
|
|
@@ -3793,84 +3871,84 @@ t.json({success:!0,data:{workdir:i,kind:"default-branch-snapshot"}})}catch(e){t.
|
|
|
3793
3871
|
return Number.isFinite(t)&&t>=1&&r>=t?{path:e.path,lines:[Math.floor(t),Math.min(r,Number.MAX_SAFE_INTEGER)]}:{path:e.path}}),c=i&&a.length>0?await r(o,n):null
|
|
3794
3872
|
if(!c||!i)return void t.json({success:!0,data:{note:null}})
|
|
3795
3873
|
const l=await async function(e){const t=e.getProjectId(),r=Date.now()
|
|
3796
|
-
if(!
|
|
3797
|
-
|
|
3798
|
-
return{projectPath:e.projectPath,defaultBranch:await(0,
|
|
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
|
|
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,
|
|
3882
|
+
const s=o.getProjectId(),n=(0,U.simpleGit)(o.projectPath),i=(await n.raw(["for-each-ref","--format=%(refname:short)","refs/heads/requests/"])).split("\n").map(e=>e.trim().replace(/^requests\//,"")).filter(e=>/^req\d+$/.test(e)&&e!==r),a=new Set(["open","in_progress","ready_for_review","completing","resolving_conflicts"]),c=new Set(i),l=[]
|
|
3805
3883
|
if(s&&c.size>0){const e=await o.apiClient.get(`/api/projects/${s}/requests?inFlight=true&limit=100`),t=Array.isArray(e?.data)?e.data:[]
|
|
3806
3884
|
for(const e of t){const t=String(e?.localId??"")
|
|
3807
|
-
c.has(t)&&a.has(String(e.status))&&l.push({id:t,title:String(e.title??""),status:String(e.status)})}}const d=await(0,
|
|
3885
|
+
c.has(t)&&a.has(String(e.status))&&l.push({id:t,title:String(e.title??""),status:String(e.status)})}}const d=await(0,J.resolveDefaultBranch)(o.projectPath),u=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:l.map(e=>e.id),defaultBranch:d,git:n}),h=new Map(l.map(e=>[e.id,e]))
|
|
3808
3886
|
t.json({success:!0,data:u.map(e=>({...e,siblingTitle:h.get(e.sibling)?.title??"",siblingStatus:h.get(e.sibling)?.status??""}))})}catch(e){o.logger.debug("sibling-overlap lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:[]})}}),e.get("/api/request-diff-stats",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
|
|
3809
3887
|
if(!r)return void t.status(400).json({success:!1,error:"requestId required"})
|
|
3810
|
-
const s=(0,
|
|
3888
|
+
const s=(0,U.simpleGit)(o.projectPath),n=await(0,L.resolveRequestDiffRange)(s,r,{status:"in_progress"}),i=n.headSha,a="string"==typeof e.query.since&&/^[0-9a-f]{7,40}$/.test(e.query.since)?e.query.since:"",c=a&&i&&await s.raw(["merge-base","--is-ancestor",a,i]).then(()=>!0,()=>!1)?a:n.baseSha,l=c&&i?await(0,L.computeDiffStats)(s,c,i):void 0,d=await(0,J.resolveDefaultBranch)(o.projectPath).catch(()=>{})
|
|
3811
3889
|
t.json({success:!0,data:l?{...l,baseSha:c,headSha:i,...d?{branch:d}:{}}:null})}catch(e){o.logger.debug("request-diff-stats lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:null})}}),e.post("/api/verify-preflight",async(e,t)=>{try{const{requestId:r,commands:s}=e.body??{},n=Array.isArray(s)?s.filter(e=>"string"==typeof e&&!!e.trim()):[]
|
|
3812
3890
|
if(!r||0===n.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty commands array are required"})
|
|
3813
|
-
const i=(await(0,
|
|
3891
|
+
const i=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim(),a=Array.isArray(e.body?.proofs)?e.body.proofs.filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command):[]
|
|
3814
3892
|
if(!0===e.body?.background){const e=JSON.stringify([[...n].sort(),a.map(e=>`${e.criterionId}\0${e.command}`).sort()])
|
|
3815
|
-
return(0,
|
|
3893
|
+
return(0,H.startPendingProof)(r,i,async e=>{const t=await d(o,r,i,n,{proofs:a,onAdvisory:e.advisory,onStep:e.step}),s=o.getProjectId(),l=c(a,t)
|
|
3816
3894
|
if(e.step("recording what it found",6e4),s&&l.length>0&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:t.sha,tree:t.tree??null,proofs:l}).catch(e=>o.logger.warn("proof replay: evidence not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)})),!t.available||!Array.isArray(t.results))return null
|
|
3817
3895
|
const u=t.results.filter(e=>!e.timedOut&&!e.couldNotRun&&!e.cutShort&&!1!==(e.authoritative??t.authoritative)).map(e=>({command:e.command,ok:e.ok,exit:e.exit,output:[e.printed,e.error].filter(Boolean).join("\n")||String(e.output??""),...e.flaky?{flaky:!0}:{}})),h=u.filter(e=>!e.ok).map(e=>({command:e.command,exit:e.exit,output:String(e.output??"")}))
|
|
3818
3896
|
return o.logger.info("proof replay finished beside the sign-off check",{requestId:r,sha:i.slice(0,8),blocking:h.length,settled:u.length,ms:t.ms}),{sha:t.sha,...t.tree?{tree:t.tree}:{},blocking:h,settled:u}},{key:e,onOverdue:async(e,t)=>{const s=o.getProjectId()
|
|
3819
|
-
o.logger.warn("proof replay: overdue, the merge goes on without it",{requestId:r,sha:i.slice(0,8),step:e.what,afterSec:Math.round(t/1e3)}),s&&0!==a.length&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:i,proofs:l(a,e,t)}).catch(e=>o.logger.warn("proof replay: its overdue end was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})||o.logger.info("proof replay: the same proofs at this commit are already replayed; joined",{requestId:r,sha:i.slice(0,8)}),void t.json({success:!0,data:{available:!0,pending:!0,sha:i}})}t.json({success:!0,data:await d(o,r,i,n,{proofs:a})})}catch(e){o.logger.debug("verify-preflight failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{available:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/showing-run",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(0,
|
|
3897
|
+
o.logger.warn("proof replay: overdue, the merge goes on without it",{requestId:r,sha:i.slice(0,8),step:e.what,afterSec:Math.round(t/1e3)}),s&&0!==a.length&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:i,proofs:l(a,e,t)}).catch(e=>o.logger.warn("proof replay: its overdue end was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})||o.logger.info("proof replay: the same proofs at this commit are already replayed; joined",{requestId:r,sha:i.slice(0,8)}),void t.json({success:!0,data:{available:!0,pending:!0,sha:i}})}t.json({success:!0,data:await d(o,r,i,n,{proofs:a})})}catch(e){o.logger.debug("verify-preflight failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{available:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/showing-run",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(0,G.showCommands)(e.body?.commands),n=r&&/^req\d+$/.test(r)&&!0===e.body?.page?(0,G.servedPageOf)((0,w.requestWorktreePath)(o.projectPath,r),r,(0,O.listPreviewHandles)(),(0,D.listServedSites)()):null
|
|
3820
3898
|
if(!r||!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"requestId is required"})
|
|
3821
3899
|
if(0===s.length&&!n)return void t.json({success:!0,data:{started:!1,reason:"no commands were named and no page is being served"}})
|
|
3822
|
-
const i=(await(0,
|
|
3900
|
+
const i=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(async()=>{const{runs:e,pages:t}=await(0,G.runShowing)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,i,s,{page:n}),c=o.getProjectId()
|
|
3823
3901
|
if(!c)return
|
|
3824
3902
|
const l=(0,m.randomUUID)()
|
|
3825
|
-
for(const s of[...t.map(e=>(0,
|
|
3903
|
+
for(const s of[...t.map(e=>(0,G.pageRecord)(r,e)),...e.map(e=>(0,G.showingRecord)(r,e))]){const e={...s,metadata:{...s.metadata,showing:l}}
|
|
3826
3904
|
await o.apiClient.post(`/api/projects/${c}/tool-records`,e).catch(e=>o.logger.warn("showing: a run or a picture was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})().catch(e=>o.logger.warn("showing runs failed",{requestId:r,error:(0,P.getErrorMessage)(e)})),t.json({success:!0,data:{started:!0,sha:i,commands:s.length,page:n?n.dir:null}})}catch(e){o.logger.warn("showing runs could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{started:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/baseline-measure",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(Array.isArray(e.body?.baselines)?e.body.baselines:[]).filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command&&e.command.trim()&&e?.baseline&&"object"==typeof e.baseline).slice(0,5)
|
|
3827
3905
|
if(!r||0===s.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty baselines array ({ criterionId, command, baseline }) are required"})
|
|
3828
|
-
const n=(await(0,
|
|
3906
|
+
const n=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(0,H.startPendingProof)(r,n,async()=>{const{measured:e}=await(0,W.measureBaselines)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,n,s),t=o.getProjectId()
|
|
3829
3907
|
t&&e.length>0&&await o.apiClient.post(`/api/projects/${t}/requests/${r}/proof-evidence`,{commitSha:n,proofs:e.map(e=>({criterionId:e.criterionId,command:e.command,ok:e.ok,...e.couldNotRun?{couldNotRun:!0}:{},output:e.words.slice(0,200),words:e.words,baseline:e.metric}))}).catch(e=>o.logger.warn("baseline measurement: evidence not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))
|
|
3830
3908
|
const i=e.filter(e=>!e.ok&&!e.couldNotRun).length
|
|
3831
|
-
return o.logger.info("baselines measured beside the sign-off check",{requestId:r,sha:n.slice(0,8),measured:e.length,regressed:i}),{sha:n,blocking:[],baselines:{measured:e.length,regressed:i}}},{kind:"baselines",waitMs:
|
|
3909
|
+
return o.logger.info("baselines measured beside the sign-off check",{requestId:r,sha:n.slice(0,8),measured:e.length,regressed:i}),{sha:n,blocking:[],baselines:{measured:e.length,regressed:i}}},{kind:"baselines",waitMs:W.BASELINE_BUDGET_MS+18e4}),t.json({success:!0,data:{pending:!0,sha:n,baselines:s.length}})}catch(e){o.logger.warn("baseline measurement could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{pending:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/bring-main-in",async(e,t)=>{try{const s="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await r(o,s)
|
|
3832
3910
|
if(!i||!n)return void t.json({success:!0,data:{outcome:"failed",text:"This session works on no request with a worktree, so there is no branch to bring main into."}})
|
|
3833
3911
|
const a="string"==typeof e.body?.requestId?e.body.requestId:""
|
|
3834
3912
|
if(a&&a!==i.requestId)return void t.json({success:!0,data:{outcome:"failed",text:`Main can be brought only into your own request's branch (${i.requestId}), not ${a}'s.`}})
|
|
3835
|
-
const c=await(0,
|
|
3913
|
+
const c=await(0,X.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree})
|
|
3836
3914
|
t.json({success:!0,data:{requestId:i.requestId,...c}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.get("/api/catch-up-due",async(e,t)=>{try{const r=await s(o,"string"==typeof e.query.sessionId?e.query.sessionId:"")
|
|
3837
3915
|
if(!r)return void t.json({success:!0,data:{due:!1}})
|
|
3838
|
-
const i=await
|
|
3916
|
+
const i=await re.catchUpDue(o.projectPath,await(0,J.resolveDefaultBranch)(o.projectPath),r.requestId)
|
|
3839
3917
|
t.json({success:!0,data:{due:i.due&&!n(r.requestId,i.files)}})}catch(e){o.logger.debug("catch-up-due failed (the tool runs as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{due:!1}})}}),e.post("/api/catch-up",async(e,t)=>{try{const r="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await s(o,r)
|
|
3840
3918
|
if(!i||!n)return void t.json({success:!0,data:{outcome:"none"}})
|
|
3841
|
-
const a=await
|
|
3919
|
+
const a=await re.catchUpDue(o.projectPath,await(0,J.resolveDefaultBranch)(o.projectPath),i.requestId)
|
|
3842
3920
|
if(!a.due)return void t.json({success:!0,data:{outcome:"none"}})
|
|
3843
|
-
const c=await(0,
|
|
3844
|
-
if("held"===c.outcome||"failed"===c.outcome)return
|
|
3921
|
+
const c=await(0,X.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree,catchUp:{files:a.files}})
|
|
3922
|
+
if("held"===c.outcome||"failed"===c.outcome)return te.set(i.requestId,{at:Date.now(),files:a.files.join("\n")}),"failed"===c.outcome&&o.logger.warn("main could not come in at the worker's tool call",{requestId:i.requestId,text:c.text}),void t.json({success:!0,data:{outcome:c.outcome}})
|
|
3845
3923
|
if("merged"===c.outcome||"conflict"===c.outcome){const e=c.from?.length?` (from ${c.from.join(", ")})`:"",t="merged"===c.outcome?`Brought in main's changes to ${a.files.join(", ")}${e}.`:`Brought in main's changes to ${a.files.join(", ")}${e}. They conflict with this work in ${(c.conflictedFiles??[]).join(", ")||"those files"}; its worker is resolving that now.`
|
|
3846
3924
|
await o.apiClient.post(`/api/projects/${n}/session-messages/reports`,{fromSessionId:r,status:"success",summary:t,reportKind:"progress",requestId:i.requestId}).catch(e=>o.logger.warn("main came in, but the line on the request was not posted",{requestId:i.requestId,error:(0,P.getErrorMessage)(e)}))}t.json({success:!0,data:{outcome:c.outcome,text:c.text}})}catch(e){o.logger.warn("catch-up failed (the tool runs as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{outcome:"failed"}})}}),e.post("/api/commit-worktree-leftovers",async(e,t)=>{try{const r="string"==typeof e.body?.requestId?e.body.requestId:void 0
|
|
3847
3925
|
if(!r||!/^req[0-9]{4,10}$/.test(r))return void t.status(400).json({success:!1,error:"a request local id (reqNNNNNN) is required"})
|
|
3848
3926
|
const s=(0,w.requestWorktreesDir)(o.projectPath),n=M.default.join(s,r)
|
|
3849
3927
|
if(n!==M.default.join(s,M.default.basename(n))||!n.startsWith(s+M.default.sep))return void t.status(400).json({success:!1,error:"requestId must name one of the project's request worktrees"})
|
|
3850
|
-
if(!await
|
|
3851
|
-
const i=(0,
|
|
3928
|
+
if(!await N.promises.stat(n).then(e=>e.isDirectory()).catch(()=>!1))return void t.json({success:!0,data:{committed:!1,reason:"no worktree"}})
|
|
3929
|
+
const i=(0,U.simpleGit)(n)
|
|
3852
3930
|
await(0,x.assertGitNotUserFolder)(i,"commit of worktree leftovers")
|
|
3853
3931
|
const a=await i.status()
|
|
3854
3932
|
if(a.isClean())return void t.json({success:!0,data:{committed:!1,reason:"clean"}})
|
|
3855
3933
|
const c=[...a.modified,...a.not_added,...a.created,...a.deleted]
|
|
3856
3934
|
await i.add(["-A"])
|
|
3857
|
-
const{note:l}=await(0,
|
|
3935
|
+
const{note:l}=await(0,Z.holdBackStagedCredentials)(n)
|
|
3858
3936
|
l&&o.logger.warn("commit-worktree-leftovers held key material back",{requestId:r,note:l}),await i.commit(`chore: commit uncommitted worktree changes at completion (${r})${l?`\n\n${l}`:""}`)
|
|
3859
3937
|
const d=(await i.revparse(["HEAD"])).trim()
|
|
3860
3938
|
o.logger.info("commit-worktree-leftovers",{requestId:r,files:c.length,sha:d.slice(0,8)}),t.json({success:!0,data:{committed:!0,sha:d,files:c}})}catch(e){o.logger.warn("commit-worktree-leftovers failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{committed:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.get("/api/branch-head",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0,s=("string"==typeof e.query.branch?e.query.branch:void 0)||(r?(0,R.generateRequestBranchName)(r,""):void 0)
|
|
3861
3939
|
if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
|
|
3862
|
-
const n=(await(0,
|
|
3940
|
+
const n=(await(0,U.simpleGit)(o.projectPath).revparse([s])).trim(),i=(await(0,U.simpleGit)(o.projectPath).revparse([`${s}^{tree}`]).catch(()=>"")).trim()
|
|
3863
3941
|
t.json({success:!0,data:{branch:s,sha:n,.../^[0-9a-f]{40}$/.test(i)?{tree:i}:{}}})}catch(e){o.logger.debug("branch-head lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{branch:null,sha:null}})}}),e.post("/api/release-pool-slot",async(e,t)=>{try{const{worktreePath:r}=e.body||{}
|
|
3864
3942
|
if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"})
|
|
3865
3943
|
let s=null
|
|
3866
3944
|
try{const e=o.getProjectId(),t=e?await(0,v.readAllPages)(o.apiClient,t=>`/api/projects/${e}/test-runs?limit=100&offset=${t}`,500):void 0
|
|
3867
3945
|
if(t){const e=e=>"string"==typeof e?(0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),e):null
|
|
3868
|
-
s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,
|
|
3869
|
-
const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(
|
|
3870
|
-
let
|
|
3871
|
-
const
|
|
3946
|
+
s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,F.releaseSlot)((0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),r)),o.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0,released:!0})}catch(e){o.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,P.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}})}
|
|
3947
|
+
const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(39175),N=t(79896),F=t(60489),L=t(92456),U=t(83807),B=t(45738),H=t(65856),W=t(82167),G=t(99970),K=t(24106),z=t(73775),V=t(27584),Y=t(3031),J=t(52232),X=t(62040),Q=t(32559),Z=t(96511),ee=new Map,te=new Map,re=new T.ParallelNotes
|
|
3948
|
+
let se=null
|
|
3949
|
+
const ne=e=>({id:String(e?.localId??e?.id??""),title:String(e?.title??""),status:String(e?.status??""),description:"string"==typeof e?.description?e.description:null})
|
|
3872
3950
|
exports.COMPLETION_REPLAY_BUDGET_MS=15e4
|
|
3873
|
-
const
|
|
3951
|
+
const oe=12e4},60885(e,exports,t){"use strict"
|
|
3874
3952
|
exports.makeRunOwn=function(e,t){return async o=>{n||(n=new r.Agent({headersTimeout:0,bodyTimeout:0}))
|
|
3875
3953
|
const i="number"==typeof o.timeoutMs?o.timeoutMs:12e4,a=`http://127.0.0.1:${e}/api/run`,c={"Content-Type":"application/json"},l=(0,s.localSecretForUrl)(a,t)
|
|
3876
3954
|
return l&&(c[s.LOCAL_SECRET_HEADER]=l),(await(0,r.fetch)(a,{method:"POST",headers:c,body:JSON.stringify(o),signal:AbortSignal.timeout(Math.max(18e4,i+6e4)),dispatcher:n})).json()}}
|
|
@@ -4775,37 +4853,37 @@ let h=null
|
|
|
4775
4853
|
const p=new Map},62366(e,exports,t){"use strict"
|
|
4776
4854
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
4777
4855
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
4778
|
-
const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(
|
|
4779
|
-
class
|
|
4780
|
-
this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0
|
|
4856
|
+
const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(39175),T=t(52640),x=t(65232),M=t(95671),R=t(44192),P=t(14139),O=t(41152),D=r(t(16928)),q=r(t(79896)),N=r(t(70857)),F=t(5607),L=t(76634),U=t(31),B=t(90524),H=t(18984),W=t(58824),G=t(29869),K=t(67078),z=t(60070),V=t(37210),Y=t(87227),J=t(82928),X=t(76616),Q=t(15047),Z=t(90629),ee=t(42298),te=t(67563),re=t(41950),se=t(88087),ne=t(1285),oe=t(20564),ie=Object.keys(F.actionHandlers).join(","),ae=t(6850),ce=t(58836),le=t(25146),de=t(23411),ue=t(43218),he=t(81570),pe=t(18815),fe=t(73613),me=t(49779),ge=t(76982),we=t(53340),ye=t(89063),ve=t(61292),be=t(11017),ke=t(28148),Se=t(18649),Ie=t(64306),$e=t(96685),je=t(49816),Ee=t(35419),Ae=t(33521),Ce=t(56844),_e=t(84352),Te=t(40713),xe=t(75361),Me=t(75167),Re=t(72188),Pe=t(6688),Oe=t(39068),De=t(3435),qe=t(48463),Ne=t(60489),Fe=t(24106),Le=t(81618),Ue=t(80211),Be=t(2566),He=t(83807),We=t(92456),Ge=t(95040),Ke=t(60355),ze=t(31026),Ve=t(73782),Ye=t(53608),Je=t(63484),Xe=t(31851),Qe=t(84102),Ze=t(75979),et=t(79329),tt=t(60833),rt=t(42593),st=t(69706),nt=t(11773),ot=t(20990),it=t(45956),at=t(36799),ct=t(86116),lt=t(13751),dt=t(13791),ut=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ht=65536
|
|
4857
|
+
class pt{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment":"on your machine",i=(0,Ae.isStandingRulePattern)(s),a=`You chose "Always allow" on the approval card for \`${t.askedCommand.slice(0,300)}\`${t.approvalId?` (card ${t.approvalId})`:""}${t.sessionId?`, asked by agent session ${t.sessionId}`:""}, on ${t.at.replace("T"," ").slice(0,16)} UTC.`;(async()=>{(await(0,je.getRegisteredAllows)(this.apiClient,e,r??null).catch(()=>[])).some(e=>("string"==typeof e?e:e.exact).trim()===s.trim())||(await this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"ok:card",command:(0,n.commandAsStored)(s),description:`${a} ${i?`It allows exactly this shape, where <file> is any one file inside the project, ${o}`:`This exact command runs ${o}`} without asking. Delete it or switch it off to be asked again.`,timeout:120,isActive:!0,exactMatch:!0,...r?{environmentId:r}:{}}),(0,je.invalidateRegisteredAllows)(e))})().catch(e=>this.logger.warn("Failed to remember approved command",{error:String(e)}))}rememberJudgeApprovedCommand(e,t,r){const s=new Date(Date.now()+pt.JUDGE_APPROVAL_TTL_MS).toISOString()
|
|
4858
|
+
this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,je.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
|
|
4781
4859
|
if(!this.parkController?.noteServeDeath(`exit code=${e.code} signal=${e.signal}`))return void this.logger.info("OpenCode serve exit observed during managed lifecycle -- no action",{code:e.code,signal:e.signal,pid:e.pid})
|
|
4782
4860
|
const t=Date.now()
|
|
4783
4861
|
this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.defaultModelUnchosen=!1,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
|
|
4784
|
-
return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,
|
|
4785
|
-
const r=(0,
|
|
4786
|
-
if((0,
|
|
4862
|
+
return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,re.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.followingDefaultTo=null,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,st.installServeAuthFetch)(),(0,re.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
|
|
4863
|
+
const r=(0,O.createMetricsCollector)(!1)
|
|
4864
|
+
if((0,nt.setFleetServerKey)(t.projectPath?t.projectPath.split("/").pop():void 0),this.apiClient=new R.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3,serverKey:t.projectPath?t.projectPath.split("/").pop():void 0}),t.tokenManager){const e=t.tokenManager
|
|
4787
4865
|
this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
|
|
4788
|
-
return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new
|
|
4866
|
+
return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new H.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new V.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,w.default)(),r=e=>(0,ee.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
|
|
4789
4867
|
e.use((e,t,r)=>{const n=e.headers.host
|
|
4790
|
-
if((0,
|
|
4791
|
-
s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:
|
|
4792
|
-
if(!r(n))return this.logger.warn("Refused a browser request from an origin this project does not know",{origin:n,path:e.path,method:e.method}),void t.status(403).json({success:!1,error:
|
|
4793
|
-
n&&t.setHeader("Access-Control-Allow-Private-Network","true"),s()}),e.use((0,v.default)({origin:(e,t)=>t(null,r(e)),credentials:!0,allowedHeaders:["Content-Type","Authorization","Accept","X-Alchemy-Project"],maxAge:600})),e.use((0,
|
|
4794
|
-
const n=(0,
|
|
4795
|
-
e.use((0,
|
|
4868
|
+
if((0,ee.isLoopbackHost)(n))return r()
|
|
4869
|
+
s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:ee.HOST_REFUSED_MESSAGE})}),e.use((0,ee.inertActiveContent)()),e.use((e,t,s)=>{const n=e.headers.origin
|
|
4870
|
+
if(!r(n))return this.logger.warn("Refused a browser request from an origin this project does not know",{origin:n,path:e.path,method:e.method}),void t.status(403).json({success:!1,error:ee.ORIGIN_REFUSED_MESSAGE})
|
|
4871
|
+
n&&t.setHeader("Access-Control-Allow-Private-Network","true"),s()}),e.use((0,v.default)({origin:(e,t)=>t(null,r(e)),credentials:!0,allowedHeaders:["Content-Type","Authorization","Accept","X-Alchemy-Project"],maxAge:600})),e.use((0,se.localSecretGate)({secret:this.localSecret,logger:this.logger}))
|
|
4872
|
+
const n=(0,te.makeTicketVerifier)({apiClient:this.apiClient,projectId:()=>this.projectId,logger:this.logger})
|
|
4873
|
+
e.use((0,oe.browserTicketGate)({verifier:n,secret:this.localSecret,logger:this.logger})),e.use((0,y.default)({filter:(e,t)=>!e.headers.accept?.includes("text/event-stream")&&y.default.filter(e,t)})),e.use(w.default.json({limit:"10mb"}))
|
|
4796
4874
|
const o=["/api/opencode","/api/spawn-","/api/setup-","/api/run-tests-now","/api/flesh-out-request","/sync-agents","/api/stuck-sessions/"],i=["/api/opencode/session","/api/opencode/global/health"]
|
|
4797
4875
|
e.use((e,t,r)=>{const s="string"==typeof e.query.projectId?e.query.projectId:void 0
|
|
4798
4876
|
if(!s)return r()
|
|
4799
4877
|
const n=this.projectId
|
|
4800
|
-
n&&s!==n?t.status(409).json({success:!1,error:`project mismatch: this action server serves ${n} (${
|
|
4878
|
+
n&&s!==n?t.status(409).json({success:!1,error:`project mismatch: this action server serves ${n} (${D.default.basename(this.options.projectPath)}), not ${s}. Re-discover the action server for your project -- ports shuffle on restart.`,code:"PROJECT_MISMATCH",data:{servingProjectId:n,servingProject:D.default.basename(this.options.projectPath)}}):r()}),e.use((e,t,r)=>{const s=!0===e.headers.accept?.includes("text/event-stream")
|
|
4801
4879
|
if("GET"===e.method&&i.some(t=>e.path===t||"/api/opencode/session"===t&&e.path===`${t}/`)||s&&e.path.startsWith("/api/opencode"))return"active"!==this.parkController?.getState()&&this.parkController?void t.status(503).json({success:!1,parked:!0,error:"Project is parked (idle); serve is stopped"}):void r()
|
|
4802
|
-
o.some(t=>e.path.startsWith(t))?this.ensureServeAlive(`route:${e.path}`).then(()=>r()):r()}),e.get("/health",this.handleHealth.bind(this)),e.get("/api/read-token",(e,t)=>{t.setHeader("Cache-Control","no-store"),t.json({success:!0,data:(0,
|
|
4880
|
+
o.some(t=>e.path.startsWith(t))?this.ensureServeAlive(`route:${e.path}`).then(()=>r()):r()}),e.get("/health",this.handleHealth.bind(this)),e.get("/api/read-token",(e,t)=>{t.setHeader("Cache-Control","no-store"),t.json({success:!0,data:(0,ne.mintReadToken)(this.localSecret)})}),this.options.projectPath&&(0,b.registerAssetRoutes)(e,{projectPath:this.options.projectPath,getProjectId:()=>this.projectId??null,logger:this.logger}),e.post("/admin/shutdown",(e,t)=>{this.logger.warn("Retiring: a newer action server for this project requested takeover",{reason:e.body?.reason}),t.json({ok:!0,pid:process.pid}),setTimeout(()=>{try{process.kill(process.pid,"SIGTERM")}catch{process.exit(0)}},150)}),(0,xe.initGateLogPersistence)(this.options.projectPath),(0,Me.initGateModes)(this.options.projectPath),(0,Re.setActivityRequestResolver)(async e=>{const t=this.projectId
|
|
4803
4881
|
if(!t)throw new Error("no project bound")
|
|
4804
4882
|
const r=`/api/projects/${t}/session-request-associations`
|
|
4805
4883
|
let s=!1
|
|
4806
4884
|
const n=e=>e&&"object"==typeof e&&"data"in e?e.data:e,o={getCurrentForSession:async e=>{try{const t=n(await this.apiClient.get(`${r}/current/${encodeURIComponent(e)}`))
|
|
4807
4885
|
return null===t?null:t&&"string"==typeof t.requestLocalId?{requestLocalId:t.requestLocalId,ended:!0===t.ended,withdrawn:!0===t.withdrawn}:void 0}catch{return}},getActiveRequestsForSession:async e=>{try{const t=n(await this.apiClient.get(`${r}/by-session/${encodeURIComponent(e)}?activeOnly=true`))
|
|
4808
|
-
return Array.isArray(t)?t:[]}catch(e){throw s=!0,e}}},i=await
|
|
4886
|
+
return Array.isArray(t)?t:[]}catch(e){throw s=!0,e}}},i=await Pe.sessionIdentity.getSessionRequestId({getCLISessionClient:()=>null,getSessionRequestAssociationClient:()=>o},this.logger,e)
|
|
4809
4887
|
if(s)throw new Error("session request lookup failed")
|
|
4810
4888
|
return i})
|
|
4811
4889
|
const a=async e=>Promise.all((0,$.listEntries)().map(async t=>{let r,s="",n=0,o=t.finishedAt??t.startedAt
|
|
@@ -4814,24 +4892,24 @@ try{const e=await k.promises.stat(t.logPath)
|
|
|
4814
4892
|
n=e.size,o=e.mtime.toISOString(),r=await k.promises.open(t.logPath,"r")
|
|
4815
4893
|
const i=Math.max(0,n-16384),a=Buffer.alloc(n-i)
|
|
4816
4894
|
await r.read(a,0,a.length,i),s=a.toString("utf-8").split("\n").slice(-50).join("\n")}catch{}finally{await(r?.close().catch(()=>{}))}return{...t,tail:s,sizeBytes:n,modifiedAt:o}}))
|
|
4817
|
-
return e.get("/api/commands/running",async(e,t)=>{try{t.json({success:!0,data:await a("0"!==e.query.tail)})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4818
|
-
t.json({success:!0,data:await(0,
|
|
4819
|
-
if(!(r&&s&&n&&o&&a&&c))return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.addStart)({id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:"number"==typeof l&&l>0?l:void 0}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4895
|
+
return e.get("/api/commands/running",async(e,t)=>{try{t.json({success:!0,data:await a("0"!==e.query.tail)})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/model-switch",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{pending:this.pendingConfigReload,pendingSince:this.pendingConfigReload&&this.pendingConfigReloadSince?this.pendingConfigReloadSince:null,lastReloadAt:this.lastConfigReloadAt||null,capMs:pt.RELOAD_DEFER_CAP_MS,busySessionIds:await this.busySessionIds()}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/envs/running",async(e,t)=>{try{if(t.setHeader("Cache-Control","no-cache"),!this.projectId)return void t.json({success:!0,data:{at:Date.now(),dockerAvailable:!1,hostCores:null,hostMemBytes:null,computerCores:N.default.cpus().length,computerMemBytes:N.default.totalmem(),envs:[]}})
|
|
4896
|
+
t.json({success:!0,data:await(0,Ue.runningEnvs)(this.projectId)})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/commands/activity",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{runs:await a("0"!==e.query.tail),gate:(0,xe.listGateLog)(200),experiments:this.projectId?(0,$e.listExperiments)(this.projectId):[],execs:(0,G.listRunningExecs)(),previews:(0,_.previewHandlesWithVersions)(this.options.projectPath)}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/commands/start",async(e,t)=>{try{const{id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:l}=e.body??{}
|
|
4897
|
+
if(!(r&&s&&n&&o&&a&&c))return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.addStart)({id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:"number"==typeof l&&l>0?l:void 0}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/commands/end",async(e,t)=>{try{const{id:r,exitCode:s,timedOut:n,finishedAt:o}=e.body??{}
|
|
4820
4898
|
if(!r||"number"!=typeof s||!o)return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.markEnd)({id:r,exitCode:s,timedOut:!!n,finishedAt:o})
|
|
4821
4899
|
try{const e=(0,$.listEntries)().find(e=>e.id===r)
|
|
4822
|
-
if(e){const t=Date.parse(o)-Date.parse(e.startedAt);(0,
|
|
4900
|
+
if(e){const t=Date.parse(o)-Date.parse(e.startedAt);(0,xe.recordGate)({source:"command",sessionId:e.sessionId,...void 0!==e.requestId?{requestId:e.requestId}:{},environmentName:"Workspace",command:e.command,action:"auto",ruleId:"registered",exit:s,ms:Number.isFinite(t)?t:void 0})}}catch{}t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.post("/api/auth-renew",async(e,t)=>{const r=this.options.tokenManager
|
|
4823
4901
|
if(r)try{const e=await r.renewIfDue(Number.POSITIVE_INFINITY).catch(()=>null)
|
|
4824
4902
|
if(e?.token)return this.updateAuthToken(e.token),void t.json({success:!0,renewed:!0})
|
|
4825
4903
|
const s=r.getAuthStatus()
|
|
4826
|
-
if(s.isPendingAuth&&s.authUrl)return void t.json({success:!0,opened:(0,
|
|
4904
|
+
if(s.isPendingAuth&&s.authUrl)return void t.json({success:!0,opened:(0,Z.openBrowser)(s.authUrl),alreadyPending:!0})
|
|
4827
4905
|
const{authUrl:n}=await r.startAuthenticationFlow()
|
|
4828
|
-
t.json({success:!0,opened:(0,
|
|
4906
|
+
t.json({success:!0,opened:(0,Z.openBrowser)(n)})}catch(e){this.logger.error("auth renew failed to start",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}else t.status(400).json({success:!1,error:"No token manager on this server"})}),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/opencode/restart",async(e,t)=>{try{if(!this.relaunchOpenCode)return void t.status(409).json({success:!1,error:"no serve child to restart (serve-only mode)"})
|
|
4829
4907
|
for(const e of this.executePark())this.managedExitPids.add(e)
|
|
4830
|
-
await this.executeRevive(),t.json({success:!0,data:{port:this.openCodePort??null}})}catch(e){this.logger.error("Manual serve restart failed",{error:(0,
|
|
4831
|
-
await this.parkController.ensureAlive(),t.json({success:!0,parkState:this.parkController.getState()})}catch(e){this.logger.error("Manual serve revive failed",{error:(0,
|
|
4908
|
+
await this.executeRevive(),t.json({success:!0,data:{port:this.openCodePort??null}})}catch(e){this.logger.error("Manual serve restart failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/opencode/revive",async(e,t)=>{try{if(!this.parkController||"active"===this.parkController.getState())return void t.json({success:!0,parkState:"active"})
|
|
4909
|
+
await this.parkController.ensureAlive(),t.json({success:!0,parkState:this.parkController.getState()})}catch(e){this.logger.error("Manual serve revive failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.get("/api/commits/:sha/diff",async(e,t)=>{const r=String(e.params.sha||"")
|
|
4832
4910
|
if(!/^[0-9a-f]{7,64}$/i.test(r))return void t.status(400).json({success:!1,error:"sha must be a git commit hash"})
|
|
4833
4911
|
const s="string"!=typeof e.query.path||e.query.path.includes("..")?void 0:e.query.path
|
|
4834
|
-
try{const e=(0,
|
|
4912
|
+
try{const e=(0,He.simpleGit)(this.options.projectPath),n=524288,o=(await e.raw(["rev-list","--parents","-n","1",r])).trim().split(/\s+/).length>2,i=[...(0,g.documentDiffGitArgs)(),"show","--format=%H%n%an%n%ad%n%s","--patch",...o?["--first-parent","-m"]:[],r]
|
|
4835
4913
|
s&&i.push("--",s)
|
|
4836
4914
|
let a=await(0,g.documentDiffGit)(this.options.projectPath).raw(i)
|
|
4837
4915
|
const c=a.length>n
|
|
@@ -4842,79 +4920,79 @@ try{const s=this.projectId
|
|
|
4842
4920
|
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
4843
4921
|
let n=null
|
|
4844
4922
|
try{const e=await this.apiClient.get(`/api/projects/${s}/requests/${r}`)
|
|
4845
|
-
n=e?.data??e??null}catch{}const o=(0,
|
|
4846
|
-
return(Array.isArray(t)?t:[]).filter(e=>n(e?.metadata?.commitHash)).map(e=>({commitSha:e.metadata.commitHash,previousSha:n(e?.metadata?.previousCommitHash)?e.metadata.previousCommitHash:void 0}))}catch{return}},{baseSha:a,headSha:c}=await(0,
|
|
4923
|
+
n=e?.data??e??null}catch{}const o=(0,He.simpleGit)(this.options.projectPath),i=async()=>{try{const e=await this.apiClient.get(`/api/projects/${s}/tool-records?requestId=${encodeURIComponent(r)}&sortBy=created_at&sortDirection=asc&limit=100`),t=e?.data??e??[],n=e=>"string"==typeof e&&/^[0-9a-f]{7,64}$/i.test(e)
|
|
4924
|
+
return(Array.isArray(t)?t:[]).filter(e=>n(e?.metadata?.commitHash)).map(e=>({commitSha:e.metadata.commitHash,previousSha:n(e?.metadata?.previousCommitHash)?e.metadata.previousCommitHash:void 0}))}catch{return}},{baseSha:a,headSha:c}=await(0,We.resolveRequestDiffRange)(o,r,{mergeBaseSha:n?.mergeBaseSha,mergeCommitSha:n?.mergeCommitSha,status:n?.status},i),l=await(0,Ge.requestDocumentEdits)(this.apiClient,s,r).catch(()=>[])
|
|
4847
4925
|
if(!(a&&c||0!==l.length))return void t.status(404).json({success:!1,error:`No diff range found for ${r}.`})
|
|
4848
|
-
if("1"===e.query.stats){const e=a&&c?await(0,
|
|
4926
|
+
if("1"===e.query.stats){const e=a&&c?await(0,We.computeDiffStats)(o,a,c):void 0
|
|
4849
4927
|
if(!e&&0===l.length)return void t.status(404).json({success:!1,error:`No diff stats for ${r}.`})
|
|
4850
|
-
const s=(0,
|
|
4851
|
-
return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,
|
|
4852
|
-
t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4853
|
-
t.json({success:!0,data:e})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4928
|
+
const s=(0,Ge.withDocumentStats)(e,l)
|
|
4929
|
+
return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,Ge.withDocumentEdits)(u,l),p=h.length>d
|
|
4930
|
+
t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),(0,ze.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,Ve.registerLocalModelRoutes)(e),(0,Ze.registerExhibitPreviewRoutes)(e),e.get("/api/publish-capabilities",async(e,t)=>{try{const e=await(0,L.getPublishCapabilities)(this.options.projectPath,(0,L.defaultRunner)())
|
|
4931
|
+
t.json({success:!0,data:e})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/requests/:id/publish-pr",async(e,t)=>{const r=e.params.id
|
|
4854
4932
|
try{const s=this.projectId
|
|
4855
4933
|
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
4856
4934
|
const{base:n,draft:o}=e.body??{},i=()=>{const e={"X-Client-Type":"alchemy-local-service"},t=this.apiClient.authToken||this.options.authToken
|
|
4857
|
-
return t&&(e.Authorization=`Bearer ${t}`),e},a=(e.body??{}).mode??"pr",c={run:(0,
|
|
4935
|
+
return t&&(e.Authorization=`Bearer ${t}`),e},a=(e.body??{}).mode??"pr",c={run:(0,L.defaultRunner)(),fetchRequest:async e=>{const t=await this.apiClient.get(`/api/projects/${s}/requests/${e}`)
|
|
4858
4936
|
return t?.data??t??null},fetchReceiptMarkdown:async e=>{const t=await fetch(`${this.options.remoteServerUrl}/api/projects/${s}/requests/${e}/receipt?format=md`,{headers:i()})
|
|
4859
4937
|
if(!t.ok)throw new Error(`Receipt fetch failed: HTTP ${t.status}`)
|
|
4860
4938
|
return await t.text()}},l={},d=[]
|
|
4861
|
-
if("pr"===a||"both"===a)try{l.pr=await(0,
|
|
4862
|
-
d.length&&(l.warnings=d),t.json({success:!0,data:l})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4939
|
+
if("pr"===a||"both"===a)try{l.pr=await(0,L.publishRequestPr)({projectPath:this.options.projectPath,requestLocalId:r,base:n,draft:o},c)}catch(e){d.push(`pr: ${(0,W.getErrorMessage)(e)}`)}if("notes"===a||"both"===a)try{l.note=await(0,L.publishReceiptNote)({projectPath:this.options.projectPath,requestLocalId:r},c)}catch(e){d.push(`notes: ${(0,W.getErrorMessage)(e)}`)}if(!l.pr&&!l.note)return void t.status(500).json({success:!1,error:d.join("; ")||"nothing published"})
|
|
4940
|
+
d.length&&(l.warnings=d),t.json({success:!0,data:l})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),(0,Ke.registerHooksRoutes)(e,{apiClient:this.apiClient,logger:this.logger,getProjectId:()=>this.projectId}),e.get("/api/project-settings",async(e,t)=>{Date.now()-this.lastSettingsSyncAt>2e3&&(this.lastSettingsSyncAt=Date.now(),await this.syncProjectSettings().catch(()=>{})),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null,receipts:this.cachedReceipts,integrations:this.cachedIntegrations}})}),e.get("/api/agents/markdown",(e,t)=>{t.json({success:!0,data:this.configurer.getAgentMarkdowns()})}),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/after-compaction",async(e,t)=>{try{const r=this.projectId
|
|
4863
4941
|
if(!r)return void t.json({success:!0,data:{text:""}})
|
|
4864
4942
|
const s=await(0,p.afterCompactionBlock)({apiClient:this.apiClient,projectId:r,projectPath:this.options.projectPath},String(e.params.sessionId||""))
|
|
4865
4943
|
t.json({success:!0,data:{text:s}})}catch(e){t.json({success:!0,data:{text:""}})}}),e.post("/api/sessions/:sessionId/deliver",async(e,r)=>{try{const s=String(e.params.sessionId||""),{message:n,kind:o,partPayload:i,noVerify:a,verifySeconds:c}=e.body||{}
|
|
4866
4944
|
if(!s||"string"!=typeof n||!n.trim())return void r.status(400).json({success:!1,error:"sessionId and a non-empty message are required"});(0,u.wakeSession)(s)
|
|
4867
4945
|
const{deliverPrompt:l}=await Promise.resolve().then(()=>t(17039)),d="worker-report"===o&&(0,h.isQuietReporter)(i?.fromSessionId)||"report-acknowledged"===o&&!0===i?.quiet,p=await l({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},s,n,{kind:o,partPayload:i,noVerify:!!a,...d?{noReply:d}:{},..."number"==typeof c?{verifySeconds:c}:{}})
|
|
4868
|
-
p.ok||this.logger.warn("deliver door: delivery failed",{sessionId:s,kind:o,error:p.error,detail:p.detail}),r.json({success:!0,data:p})}catch(e){(0,
|
|
4946
|
+
p.ok||this.logger.warn("deliver door: delivery failed",{sessionId:s,kind:o,error:p.error,detail:p.detail}),r.json({success:!0,data:p})}catch(e){(0,De.respondWithError)(r,this.logger,"Deliver to session",e)}}),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",async(e,t)=>{try{const{sessionId:r}=e.params,s=this.projectId
|
|
4869
4947
|
if(!s)return void t.status(400).json({success:!1,error:"No project"})
|
|
4870
4948
|
const n=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),o=(n?.data||n||[]).map(e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))
|
|
4871
|
-
t.json({success:!0,ancestors:o})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4872
|
-
r.json({success:!0,data:e()})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4949
|
+
t.json({success:!0,ancestors:o})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/registry/projects",async(e,r)=>{try{const{listLive:e}=await Promise.resolve().then(()=>t(52640))
|
|
4950
|
+
r.json({success:!0,data:e()})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/tool-call-event",(e,t)=>{const{action:r,callId:s,sessionId:n,requestId:o,tool:i}=e.body||{}
|
|
4873
4951
|
if("note-nudge"===r)return this.logger.info("note nudge appended",{tool:i??null,sessionId:n??null,requestId:o??null}),void t.json({success:!0})
|
|
4874
4952
|
!s||"start"!==r&&"end"!==r?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(r,String(s)),t.json({success:!0}))}),e.post("/api/results/spill",async(e,r)=>{try{const{text:s,toolName:n,callId:o}=e.body??{}
|
|
4875
4953
|
if("string"!=typeof s||!s)return void r.status(400).json({success:!1,error:"text is required"})
|
|
4876
4954
|
const i=await Promise.resolve().then(()=>t(13080)),a=await i.putBuffer(this.options.projectPath,s)
|
|
4877
4955
|
if(this.projectId){const{recordIdForCall:e}=await t.e(111).then(()=>t(56111)),r=e("string"==typeof o?o:void 0)
|
|
4878
|
-
this.apiClient.post(`/api/projects/${this.projectId}/results`,{address:a.address,locator:a.locator,bytes:a.bytes,name:`${String(n??"tool")}-result`,mediaType:"text/plain",...r?{producedBy:r}:{},summary:{characters:s.length,head:s.slice(0,400),heldBackFromContext:!0},producerNote:`Result of ${String(n??"a tool")}, too large for context`}).catch(e=>this.logger.warn("Spilled artifact could not be recorded",{error:(0,
|
|
4956
|
+
this.apiClient.post(`/api/projects/${this.projectId}/results`,{address:a.address,locator:a.locator,bytes:a.bytes,name:`${String(n??"tool")}-result`,mediaType:"text/plain",...r?{producedBy:r}:{},summary:{characters:s.length,head:s.slice(0,400),heldBackFromContext:!0},producerNote:`Result of ${String(n??"a tool")}, too large for context`}).catch(e=>this.logger.warn("Spilled artifact could not be recorded",{error:(0,W.getErrorMessage)(e)}))}this.logger.info("Oversized tool result spilled to the result store",{tool:n,bytes:a.bytes,deduped:a.deduped}),r.json({success:!0,data:a})}catch(e){this.logger.warn("Could not spill an oversized tool result",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/workflow-policy/reload",async(e,t)=>{try{if(!this.reconfigureOpenCode)return void t.status(503).json({success:!1,error:"This action server cannot reconfigure itself (serve-only mode). The change applies on the next start."})
|
|
4879
4957
|
const e=await this.reconfigureOpenCode()
|
|
4880
4958
|
if(!e.success)return void t.status(500).json({success:!1,error:e.error??"reconfigure failed"})
|
|
4881
|
-
this.requestConfigReload("workflow-policy"),this.logger.info("Workflow tool policy pushed to the running project"),t.json({success:!0,data:{reconfigured:!0}})}catch(e){this.logger.error("Workflow policy reload failed",{error:(0,
|
|
4959
|
+
this.requestConfigReload("workflow-policy"),this.logger.info("Workflow tool policy pushed to the running project"),t.json({success:!0,data:{reconfigured:!0}})}catch(e){this.logger.error("Workflow policy reload failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/reload",async(e,r)=>{try{const s=String(e.body?.scope||"all"),{reloadOwnMcp:n,syncMcpConnections:o,syncMcpGrants:i}=await Promise.resolve().then(()=>t.t(73613,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729))
|
|
4882
4960
|
a()
|
|
4883
4961
|
const c={}
|
|
4884
|
-
"all"!==s&&"own"!==s||(c.own=await n({projectPath:this.options.projectPath,logger:this.logger})),"all"!==s&&"connections"!==s||!this.projectId||(c.connections=await o({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")}})),"grants"===s&&this.projectId&&(c.grants=await i({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-grants")}})),r.json({success:!0,data:c})}catch(e){this.logger.error("MCP reload failed",{error:(0,
|
|
4885
|
-
r.json({success:!0,data:{keys:s(this.options.projectPath,String(e.params.name))}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4962
|
+
"all"!==s&&"own"!==s||(c.own=await n({projectPath:this.options.projectPath,logger:this.logger})),"all"!==s&&"connections"!==s||!this.projectId||(c.connections=await o({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")}})),"grants"===s&&this.projectId&&(c.grants=await i({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-grants")}})),r.json({success:!0,data:c})}catch(e){this.logger.error("MCP reload failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/mcp/secrets/:name",async(e,r)=>{try{const{heldSecretKeys:s}=await Promise.resolve().then(()=>t.t(59373,23))
|
|
4963
|
+
r.json({success:!0,data:{keys:s(this.options.projectPath,String(e.params.name))}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/secrets/:name",async(e,r)=>{try{const{key:s,value:n}=e.body||{}
|
|
4886
4964
|
if(!s||"string"!=typeof s)return void r.status(400).json({success:!1,error:"key is required"})
|
|
4887
4965
|
if("string"!=typeof n||!n)return void r.status(400).json({success:!1,error:"value is required"})
|
|
4888
4966
|
const{setSecret:o,heldSecretKeys:i}=await Promise.resolve().then(()=>t.t(59373,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729)),c=String(e.params.name)
|
|
4889
4967
|
if(o(this.options.projectPath,c,s,n),a(),this.logger.info("MCP secret stored on this host",{connection:c,key:s}),this.projectId){const{syncMcpConnections:e}=await Promise.resolve().then(()=>t.t(73613,23))
|
|
4890
|
-
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-secret-set")}})}r.json({success:!0,data:{keys:i(this.options.projectPath,c)}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4891
|
-
s(this.options.projectPath,o,String(e.params.key)),r.json({success:!0,data:{keys:n(this.options.projectPath,o)}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4968
|
+
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-secret-set")}})}r.json({success:!0,data:{keys:i(this.options.projectPath,c)}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.delete("/api/mcp/secrets/:name/:key",async(e,r)=>{try{const{clearSecret:s,heldSecretKeys:n}=await Promise.resolve().then(()=>t.t(59373,23)),o=String(e.params.name)
|
|
4969
|
+
s(this.options.projectPath,o,String(e.params.key)),r.json({success:!0,data:{keys:n(this.options.projectPath,o)}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/mcp/namespaces",async(e,r)=>{try{if(!this.projectId)return void r.json({success:!0,data:{namespaces:[]}})
|
|
4892
4970
|
const{getRegisteredMcpServers:e,gatedNamespaces:s}=await t.e(729).then(()=>t(92729)),n=await e({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger})
|
|
4893
|
-
r.json({success:!0,data:{namespaces:s(n)}})}catch(e){this.logger.error("MCP namespaces lookup failed",{error:(0,
|
|
4971
|
+
r.json({success:!0,data:{namespaces:s(n)}})}catch(e){this.logger.error("MCP namespaces lookup failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/gate",async(e,r)=>{try{const{toolName:s,sessionId:n,args:o}=e.body||{}
|
|
4894
4972
|
if(!s)return void r.status(400).json({success:!1,error:"toolName is required"})
|
|
4895
4973
|
if(!this.projectId)return void r.json({success:!0,data:{allow:!0,verdict:"not-external",reason:"No project"}})
|
|
4896
4974
|
const{gateMcpCall:i}=await t.e(729).then(()=>t(92729)),a=await i({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{toolName:String(s),sessionId:n?String(n):void 0,args:o})
|
|
4897
4975
|
if(a.allow||this.logger.info("MCP call refused at the gate",{toolName:s,verdict:a.verdict}),"not-external"!==a.verdict){const{getRegisteredMcpServers:r,gatedNamespaces:i,splitToolKey:c,summariseArgs:l}=await t.e(729).then(()=>t(92729)),{openMcpReceipt:d}=await t.e(111).then(()=>t(56111)),u=await r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}),h=c(String(s),i(u))
|
|
4898
|
-
h&&await d({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{connection:h.server,tool:h.tool,target:l(o),sessionId:n?String(n):void 0,callId:e.body?.callId?String(e.body.callId):void 0,verdict:a.verdict,allowed:a.allow,reason:a.reason})}r.json({success:!0,data:a})}catch(e){this.logger.error("MCP gate failed",{error:(0,
|
|
4976
|
+
h&&await d({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{connection:h.server,tool:h.tool,target:l(o),sessionId:n?String(n):void 0,callId:e.body?.callId?String(e.body.callId):void 0,verdict:a.verdict,allowed:a.allow,reason:a.reason})}r.json({success:!0,data:a})}catch(e){this.logger.error("MCP gate failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/hook-event",async(e,r)=>{try{const{eventType:s,sessionId:n,variables:o,timing:i}=e.body
|
|
4899
4977
|
if(!s)return void r.status(400).json({success:!1,error:"eventType is required"})
|
|
4900
4978
|
if("session_idle"===s&&n){const{notifyInboxChatSessionIdle:e}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
4901
4979
|
e(String(n))
|
|
4902
4980
|
const{notifyPlannerHandoffSessionIdle:r}=await t.e(991).then(()=>t(18991))
|
|
4903
4981
|
r(String(n))}const a=await this.hookExecutor.processEvent({eventType:s,sessionId:n||"",variables:o||{},timing:i})
|
|
4904
|
-
r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing failed",{error:(0,
|
|
4982
|
+
r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/tool-event",async(e,r)=>{try{const{toolName:s,sessionId:n,input:o,output:i,timing:a}=e.body||{}
|
|
4905
4983
|
if(!s||"before"!==a&&"after"!==a)return void r.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
|
|
4906
4984
|
if(n){const{notifyPlannerHandoffToolEvent:e}=await t.e(991).then(()=>t(18991))
|
|
4907
4985
|
e(String(n),String(s),o)}if("after"===a&&e.body?.callId&&this.projectId){const{closeMcpReceipt:r}=await t.e(111).then(()=>t(56111))
|
|
4908
|
-
r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,
|
|
4986
|
+
r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,Y.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
|
|
4909
4987
|
let l=0
|
|
4910
4988
|
for(const e of c)l+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:n||"",variables:e.variables,timing:e.timing})).triggered
|
|
4911
|
-
this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,
|
|
4989
|
+
this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/web-call",async(e,r)=>{try{const{tool:s,sessionId:n,callId:o,input:i,metadata:a,error:c}=e.body||{}
|
|
4912
4990
|
if("string"!=typeof s||!this.projectId)return void r.status(400).json({success:!1,error:"tool is required"})
|
|
4913
4991
|
const{writeWebReceipt:l}=await t.e(9).then(()=>t(82009)),d=await l({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{tool:s,sessionId:n?String(n):void 0,callId:o?String(o):void 0,input:i,metadata:a,error:"string"==typeof c?c:void 0})
|
|
4914
|
-
r.json({success:!0,data:{recorded:d}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4992
|
+
r.json({success:!0,data:{recorded:d}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/spawn-planner",this.handleSpawnPlanner.bind(this)),e.post("/api/setup-run",this.handleSetupRun.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.post("/api/main-change-at-landing",async(e,r)=>{try{const{requestId:s,landingSha:n}=e.body??{}
|
|
4915
4993
|
if(!s||!n||!this.projectId)return void r.status(400).json({success:!1,error:"requestId and landingSha are required"})
|
|
4916
4994
|
const{mainChangeAtLanding:o}=await Promise.resolve().then(()=>t(69689)),i=await o({projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient},s,n)
|
|
4917
|
-
r.json({success:!0,data:i})}catch(e){r.json({success:!0,data:null,error:(0,
|
|
4995
|
+
r.json({success:!0,data:i})}catch(e){r.json({success:!0,data:null,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/flesh-out-request",async(e,t)=>{try{if(!(0,De.validateRequest)(e,t,this.projectId,["requestId"]))return
|
|
4918
4996
|
const r=this.projectId
|
|
4919
4997
|
if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
|
|
4920
4998
|
const{requestId:s,sourceSessionId:n}=e.body||{}
|
|
@@ -4922,87 +5000,87 @@ let o=""
|
|
|
4922
5000
|
try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${s}`)
|
|
4923
5001
|
o=(e?.data||e)?.title||""}catch{}let i=""
|
|
4924
5002
|
if(n)try{const e=this.getOpenCodeUrl()
|
|
4925
|
-
e&&(i=(await(0,
|
|
5003
|
+
e&&(i=(await(0,rt.collectMessages)(e,n,this.logger)).filter(e=>e.text&&("user"===e.role||"assistant"===e.role)).slice(-8).map(e=>`${e.role}: ${e.text}`).join("\n").slice(0,4e3))}catch{}const a=["[Planner Flavor: request-shaping] You are fleshing out a DRAFT request in the background (no user is watching this session). This is a scoped single-request job: no plan document, no decomposition \u2014 do not use plan_write.",`Draft request id: ${s}${o?` | one-liner: "${o}"`:""}.`,i?`\nContext from the session that requested this:\n${i}\n`:"","Author ONE focused request:","1. Read the draft with get_request_information.","2. Using the one-liner (and any context above) plus a quick look at the project for grounding, write a clear DESCRIPTION and 1-5 specific, testable ACCEPTANCE CRITERIA (phrased so a confined test run could demonstrate them; any content or decision that must come from the user becomes its own criterion carrying the literal marker [user-input]).","3. Apply them to the draft with request_operations (update). Keep the title unless it is unclear. Do NOT register it and do NOT change its status; it stays a draft for the user to review and register.","4. BOUNDARY: if this is bigger than one focused request (several independent pieces, open-ended), do NOT cram it into one. Leave a short note in the description recommending a quest or a split, and stop.","Work autonomously from the information you have; do not wait for user input. When done, end your session."].filter(Boolean).join("\n"),c=await(0,ke.spawnActionAgent)({agent:"planner",prompt:a,cwd:this.options.projectPath,projectPath:this.options.projectPath,projectId:r,title:o?`Flesh out: ${o.slice(0,60)}`:"Flesh out draft request",parentSessionId:n,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0,skipWorktree:!0,shapingRequestLocalId:s})
|
|
4926
5004
|
if(!c.success||!c.sessionId)return void t.status(500).json({success:!1,error:c.error||"Failed to spawn request-shaping planner"})
|
|
4927
|
-
t.json({success:!0,data:{sessionId:c.sessionId,requestId:s}})}catch(e){(0,
|
|
5005
|
+
t.json({success:!0,data:{sessionId:c.sessionId,requestId:s}})}catch(e){(0,De.respondWithError)(t,this.logger,"Flesh out request",e)}}),e.post("/api/environments/verify",async(e,t)=>{const{config:r,smoke:s,branchName:n,requestId:o}=e.body||{}
|
|
4928
5006
|
if(!r||"object"!=typeof r)return void t.status(400).json({success:!1,error:"config (environment config) is required"})
|
|
4929
5007
|
const i="string"==typeof s&&s.trim()?s.trim():"true",a=this.options.projectPath,c=this.projectId
|
|
4930
5008
|
if(!c)return void t.status(400).json({success:!1,error:"project not registered"})
|
|
4931
5009
|
const l={composeFile:r.composeFile,gateImage:r.image,connEnv:r.connEnv,install:r.install,artifacts:r.artifacts,shellInit:r.shellInit,projectId:c}
|
|
4932
5010
|
let d
|
|
4933
|
-
try{await(0,
|
|
4934
|
-
const e=await(0,
|
|
4935
|
-
d=
|
|
4936
|
-
const u=await(0,
|
|
4937
|
-
h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,
|
|
5011
|
+
try{await(0,qe.sweepStaleSetupWorktrees)(a,this.logger)
|
|
5012
|
+
const e=await(0,qe.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,qe.generateSetupId)()
|
|
5013
|
+
d=D.default.join((0,qe.getSetupWorktreesBase)(a),`verify-${s}`),await(0,qe.createSetupWorktree)(a,d,e,this.logger)
|
|
5014
|
+
const u=await(0,Le.withContainerLabels)({"alchemy.purpose":"verify","alchemy.request":"string"==typeof o?o:void 0},()=>(0,Fe.runConfinedGate)(d,[{name:"verify",command:i,required:!0,timeout:600}],l,{projectName:`verify-${s}`})),h=!String(r.shellInit??"").trim()&&(0,Be.carriesOwnShellInit)(i),p=h?["The smoke command sources its own setup, and the environment declares no shellInit. This verify proves the smoke command, not the environment: docker exec runs no ENTRYPOINT and reads no login profile, so every LATER command gets a bare PATH. Move the preamble into config.shellInit and verify again with the command alone."]:[]
|
|
5015
|
+
h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}finally{if(d&&q.default.existsSync(d))try{await(0,He.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,Ye.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Qe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Xe.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
|
|
4938
5016
|
if(!t||!/^[A-Za-z0-9_-]+$/.test(e))return null
|
|
4939
5017
|
const r=await this.apiClient.get(`/api/projects/${t}/documents/${e}`).catch(()=>null),s=r?.data??r
|
|
4940
|
-
return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,
|
|
4941
|
-
try{
|
|
4942
|
-
r.json({success:!0,message:"Update started. The CLI will restart when complete."}),n("npm install -g alchemylab@latest",{timeout:12e4},(e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout(()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)},1e3))})}catch(e){r.json({success:!1,error:(0,
|
|
4943
|
-
return void t.json({status:e?"degraded":"healthy",service:"alchemylab-action-server"})}const r=this.remoteLastOkAt?Date.now()-this.remoteLastOkAt:null,s=Date.now()-this.startTime.getTime(),n=this.remoteFailures>0?"degraded":null!==r?r<
|
|
5018
|
+
return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,Je.registerWorkspaceRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath}),e.get("/api/stuck-sessions",this.handleGetStuckSessions.bind(this)),e.post("/api/stuck-sessions/test-inject",this.handleTestInjectStuckSession.bind(this)),e.post("/api/stuck-sessions/:sessionId/retry",this.handleRetryStuckSession.bind(this)),e.get("/api/version-warnings",(e,t)=>{t.json({success:!0,data:this.versionWarnings})}),e.post("/api/self-update",async(e,r)=>{try{const{execSync:e}=await Promise.resolve().then(()=>t.t(35317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
|
|
5019
|
+
try{q.default.accessSync(s,q.default.constants.W_OK)}catch{return void r.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:n}=await Promise.resolve().then(()=>t.t(35317,23))
|
|
5020
|
+
r.json({success:!0,message:"Update started. The CLI will restart when complete."}),n("npm install -g alchemylab@latest",{timeout:12e4},(e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout(()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)},1e3))})}catch(e){r.json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.use("/api/opencode",(e,t)=>this.handleOpenCodeProxy(e,t)),e}handleHealth(e,t){if(!0!==t.locals.provenCaller){const e=this.remoteFailures>0&&this.isPolling
|
|
5021
|
+
return void t.json({status:e?"degraded":"healthy",service:"alchemylab-action-server"})}const r=this.remoteLastOkAt?Date.now()-this.remoteLastOkAt:null,s=Date.now()-this.startTime.getTime(),n=this.remoteFailures>0?"degraded":null!==r?r<pt.REMOTE_STALE_MS?"ok":"degraded":s<pt.REMOTE_STALE_MS?"unknown":"degraded",o={url:this.options.remoteServerUrl??process.env.REMOTE_SERVER_URL??null,state:n,ok:"ok"===n,consecutiveFailures:this.remoteFailures,lastOkAt:this.remoteLastOkAt?new Date(this.remoteLastOkAt).toISOString():null,failingForMs:"degraded"===n?r??s:null,lastError:this.remoteLastError,polling:this.isPolling}
|
|
4944
5022
|
t.json({status:"degraded"===n&&this.isPolling?"degraded":"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),build:(0,f.readBuildInfo)(),projectId:this.projectId,projectPath:this.options.projectPath,remote:o,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
|
|
4945
5023
|
if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),n=e?await r.getToken():null
|
|
4946
|
-
t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,
|
|
4947
|
-
try{if(!
|
|
4948
|
-
const e=
|
|
4949
|
-
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,
|
|
5024
|
+
t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,W.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5025
|
+
try{if(!q.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
5026
|
+
const e=q.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
|
|
5027
|
+
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,W.getErrorMessage)(e)})}}async followUnchosenDefault(){const e=(0,ue.unchosenDefaultToFollow)(this.cachedDefaultModel,this.defaultModelUnchosen,this.configurer.getConnectedProviders())
|
|
4950
5028
|
if(!e||!this.projectId||this.followingDefaultTo===e)return
|
|
4951
5029
|
this.followingDefaultTo=e
|
|
4952
5030
|
const t=this.cachedDefaultModel
|
|
4953
|
-
try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,
|
|
5031
|
+
try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,W.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,W.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
|
|
4954
5032
|
let t=0
|
|
4955
5033
|
for(const[r,s]of this.activeMcpCalls)s<e&&(this.activeMcpCalls.delete(r),t++)
|
|
4956
|
-
t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<
|
|
5034
|
+
t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<pt.PROMPT_DELIVERY_BUSY_MS)return!0
|
|
4957
5035
|
try{const e=this.getOpenCodeUrl()
|
|
4958
5036
|
if(!e)return!1
|
|
4959
5037
|
const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
|
|
4960
5038
|
if(!t.ok)return!1
|
|
4961
5039
|
const r=await t.json()
|
|
4962
|
-
return Object.values(r).some(e=>!!e&&"idle"!==e.type)}catch(e){return this.logger.debug("Could not fetch OpenCode session status",{error:(0,
|
|
5040
|
+
return Object.values(r).some(e=>!!e&&"idle"!==e.type)}catch(e){return this.logger.debug("Could not fetch OpenCode session status",{error:(0,W.getErrorMessage)(e)}),!1}}async busySessionIds(){try{const e=this.getOpenCodeUrl()
|
|
4963
5041
|
if(!e)return[]
|
|
4964
5042
|
const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
|
|
4965
5043
|
if(!t.ok)return[]
|
|
4966
5044
|
const r=await t.json()
|
|
4967
|
-
return Object.entries(r).filter(([,e])=>!!e&&"idle"!==e.type).map(([e])=>e)}catch{return[]}}notePendingConfigReload(e,t){this.pendingConfigReload||(this.pendingConfigReload=!0,this.pendingConfigReloadSince=Date.now()),this.startPendingReloadRecheck(),this.logger.info("Deferring OpenCode config reload until MCP calls and session turns are idle",{context:e,inFlightMcp:t})}startPendingReloadRecheck(){this.pendingReloadRecheckInterval||(this.pendingReloadRecheckInterval=setInterval(()=>{this.fireDeferredReloadIfIdle("recheck")},
|
|
5045
|
+
return Object.entries(r).filter(([,e])=>!!e&&"idle"!==e.type).map(([e])=>e)}catch{return[]}}notePendingConfigReload(e,t){this.pendingConfigReload||(this.pendingConfigReload=!0,this.pendingConfigReloadSince=Date.now()),this.startPendingReloadRecheck(),this.logger.info("Deferring OpenCode config reload until MCP calls and session turns are idle",{context:e,inFlightMcp:t})}startPendingReloadRecheck(){this.pendingReloadRecheckInterval||(this.pendingReloadRecheckInterval=setInterval(()=>{this.fireDeferredReloadIfIdle("recheck")},pt.RELOAD_RECHECK_MS),this.pendingReloadRecheckInterval.unref?.())}stopPendingReloadRecheck(){this.pendingReloadRecheckInterval&&(clearInterval(this.pendingReloadRecheckInterval),this.pendingReloadRecheckInterval=void 0)}async fireDeferredReloadIfIdle(e){if(this.pendingConfigReload){if(!this.deferredReloadCheckInFlight){this.deferredReloadCheckInFlight=!0
|
|
4968
5046
|
try{const t=Date.now()-this.pendingConfigReloadSince
|
|
4969
|
-
if(t<
|
|
5047
|
+
if(t<pt.RELOAD_DEFER_CAP_MS){if(this.activeMcpCalls.size>0)return
|
|
4970
5048
|
if(await this.anySessionBusy())return}else this.logger.warn("Config reload deferred past cap; firing despite activity (staleness beats permanent deferral)",{trigger:e,waitedMs:t})
|
|
4971
5049
|
this.pendingConfigReload=!1,this.stopPendingReloadRecheck(),this.logger.info("Firing deferred OpenCode config reload",{trigger:e,waitedMs:t}),await this.reloadOpenCodeConfigNow(`deferred-${e}`)}finally{this.deferredReloadCheckInFlight=!1}}}else this.stopPendingReloadRecheck()}async applyClaudeThinking(e){const t=this.claudeThinkingSeen
|
|
4972
|
-
this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,
|
|
4973
|
-
try{const r=JSON.parse(await
|
|
4974
|
-
if(!r||"object"!=typeof r||!(0,
|
|
4975
|
-
await
|
|
5050
|
+
this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,W.getErrorMessage)(e)})))}async applyAnthropicCompaction(e){const t=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
|
|
5051
|
+
try{const r=JSON.parse(await q.default.promises.readFile(t,"utf8"))
|
|
5052
|
+
if(!r||"object"!=typeof r||!(0,H.withAnthropicCompaction)(r,e))return
|
|
5053
|
+
await q.default.promises.writeFile(t,JSON.stringify(r,null,2),"utf8"),this.logger.info("Claude's own compaction setting changed; reloading the serve's config",{on:e}),await this.requestConfigReload("anthropic-compaction")}catch(e){this.logger.debug("Claude's own compaction setting not applied yet",{error:(0,W.getErrorMessage)(e)})}}async applyPause(e){const t=this.getOpenCodeUrl()?.replace(/\/$/,"")
|
|
4976
5054
|
if(t&&(this.pauseApplied?.url!==t||this.pauseApplied.paused!==e))try{const r=await fetch(`${t}/config/pause`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paused:e}),signal:AbortSignal.timeout(15e3)})
|
|
4977
5055
|
if(!r.ok)return void this.logger.warn("The serve did not take the pause",{paused:e,status:r.status})
|
|
4978
5056
|
const s=await r.json().catch(()=>({}))
|
|
4979
5057
|
this.pauseApplied={url:t,paused:e}
|
|
4980
|
-
const n=(0,
|
|
5058
|
+
const n=(0,at.setProjectPaused)(e)
|
|
4981
5059
|
if(e){const e=Array.isArray(s.stopped)?s.stopped:[]
|
|
4982
5060
|
let t=0
|
|
4983
|
-
for(const r of e)t+=await(0,
|
|
5061
|
+
for(const r of e)t+=await(0,G.cancelRunningExecs)({sessionId:r}).catch(()=>0)
|
|
4984
5062
|
const r=this.projectId?await(0,j.experimentRegistry)().releaseForPause(this.projectId).catch(()=>[]):[]
|
|
4985
|
-
return void this.logger.info("Project paused: every turn stopped and held",{stopped:e.length,commandsCancelled:t,boxesReleased:r.length})}(0,
|
|
5063
|
+
return void this.logger.info("Project paused: every turn stopped and held",{stopped:e.length,commandsCancelled:t,boxesReleased:r.length})}(0,ct.extendChatWatches)(n)
|
|
4986
5064
|
const o=Array.isArray(s.waiting)?s.waiting:[],i={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel}
|
|
4987
5065
|
let a=0
|
|
4988
|
-
for(const e of o)(await(0,d.deliverPrompt)(i,e,
|
|
4989
|
-
this.logger.info("Project resumed",{waiting:o.length,woken:a,pausedForMs:n})}catch(t){this.logger.warn("Could not apply the pause to the serve",{paused:e,error:(0,
|
|
5066
|
+
for(const e of o)(await(0,d.deliverPrompt)(i,e,at.RESUME_TEXT,{kind:"inserted-prompt"}).catch(()=>({ok:!1}))).ok?a++:this.logger.warn("A session held by the pause could not be woken",{sessionId:e})
|
|
5067
|
+
this.logger.info("Project resumed",{waiting:o.length,woken:a,pausedForMs:n})}catch(t){this.logger.warn("Could not apply the pause to the serve",{paused:e,error:(0,W.getErrorMessage)(t)})}}async reloadOpenCodeConfigNow(e){try{const t=this.getOpenCodeUrl()
|
|
4990
5068
|
if(!t)return!1
|
|
4991
5069
|
const r=await fetch(`${t}/config/reload`,{method:"POST",signal:AbortSignal.timeout(5e3)})
|
|
4992
5070
|
if(r.ok){this.logger.info("Triggered OpenCode config reload",{context:e})
|
|
4993
5071
|
const t=await r.json().catch(()=>null)
|
|
4994
|
-
return!0!==t?.deferred&&(0,a.noteServeLoadedLogins)(),this.stuckSessionWatchdog?.capacityChanged(`config-reloaded:${e}`),(0,
|
|
4995
|
-
return t>0||await this.anySessionBusy()?(this.notePendingConfigReload(e,t),"deferred"):await this.reloadOpenCodeConfigNow(e)?"reloaded":"failed"}async syncOpencodeConfigModel(e){const t=(0,
|
|
4996
|
-
try{const r=await
|
|
5072
|
+
return!0!==t?.deferred&&(0,a.noteServeLoadedLogins)(),this.stuckSessionWatchdog?.capacityChanged(`config-reloaded:${e}`),(0,he.noteConfigReloaded)(this.options.projectPath),this.lastConfigReloadAt=Date.now(),!0}return this.logger.warn("OpenCode config reload returned non-OK status",{context:e,status:r.status}),!1}catch(t){return this.logger.warn("Could not reload OpenCode config",{context:e,error:(0,W.getErrorMessage)(t)}),!1}}async requestConfigReload(e){const t=this.activeMcpCalls.size
|
|
5073
|
+
return t>0||await this.anySessionBusy()?(this.notePendingConfigReload(e,t),"deferred"):await this.reloadOpenCodeConfigNow(e)?"reloaded":"failed"}async syncOpencodeConfigModel(e){const t=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
|
|
5074
|
+
try{const r=await q.default.promises.readFile(t,"utf8"),s=JSON.parse(r)
|
|
4997
5075
|
if(!s||"object"!=typeof s||s.model===e)return
|
|
4998
|
-
s.model=e,await
|
|
5076
|
+
s.model=e,await q.default.promises.writeFile(t,JSON.stringify(s,null,2),"utf8"),this.logger.info("opencode.json model field brought in step",{model:e})}catch(e){this.logger.debug("opencode.json model sync skipped",{error:(0,W.getErrorMessage)(e)})}}async resyncAgentMarkdowns(){if(await this.configurer.refreshConnectedProviders(this.options.projectPath).catch(()=>!1),!this.projectId)return
|
|
4999
5077
|
const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
|
|
5000
5078
|
if(!e?.length)return
|
|
5001
5079
|
const t=await this.configurer.fetchProjectFromRemote(this.projectId)
|
|
5002
5080
|
this.ownerGuidanceStale=!1
|
|
5003
|
-
const r=await this.configurer.agentPreambleFor(this.projectId,t?.metadata?.globalSystemPrompt),s=
|
|
5081
|
+
const r=await this.configurer.agentPreambleFor(this.projectId,t?.metadata?.globalSystemPrompt),s=D.default.join(this.options.projectPath,".opencode","agent"),n=this.cachedDefaultModel||M.FLEET_DEFAULT_MODEL,o=await this.configurer.resolveWorkflowEffort(this.projectId),i=await this.mcpGrantInputs(this.projectId),a=this.agentConfigFingerprint()
|
|
5004
5082
|
this.configurer.clearAgentMarkdowns()
|
|
5005
|
-
for(const s of e)s.enabled&&this.configurer.setAgentMarkdown(s.name,this.configurer.generateAgentMarkdown(s,"alchemy",n,r,{effort:o,extraTools:(0,
|
|
5083
|
+
for(const s of e)s.enabled&&this.configurer.setAgentMarkdown(s.name,this.configurer.generateAgentMarkdown(s,"alchemy",n,r,{effort:o,extraTools:(0,me.agentToolGrants)(i.servers,s.name,i.permissions),limits:(0,M.modelLimitsOf)(t?.metadata),claudeThinking:(0,H.claudeThinkingOf)(t?.metadata)}))
|
|
5006
5084
|
this.configurer.setAgentMarkdown("explore",this.configurer.buildExploreMarkdown(r)),await this.configurer.removeOnDiskAgentFiles(s),this.lastAgentResyncModel=n,await this.syncOpencodeConfigModel(n),this.logger.info("Re-synced agent markdowns after model change",{model:n,count:e.length}),this.agentConfigFingerprint()!==a?(this.stuckSessionWatchdog?.capacityChanged("agents-resynced"),await this.requestConfigReload("agent-resync")):this.logger.info("Agent definitions unchanged; no config reload",{context:"agent-resync"})}async checkOwnerGuidanceApply(){if(!this.ownerGuidanceStale||!this.projectId)return
|
|
5007
5085
|
const e=this.getOpenCodeUrl()
|
|
5008
5086
|
let t=!1,r=null
|
|
@@ -5011,10 +5089,10 @@ if(!s.ok)return
|
|
|
5011
5089
|
const o=await s.json(),i=n.ok?await n.json():{}
|
|
5012
5090
|
t=Object.values(i??{}).some(e=>"busy"===e?.type)||this.activeMcpCalls.size>0
|
|
5013
5091
|
for(const e of o){const t=Number(e.time?.updated??e.time?.created??0)
|
|
5014
|
-
t&&(null===r||t>r)&&(r=t)}}catch{return}"apply"===(0,
|
|
5015
|
-
return{servers:t,permissions:r?.connectionPermissions??null}}agentConfigFingerprint(){const e=(0,
|
|
5092
|
+
t&&(null===r||t>r)&&(r=t)}}catch{return}"apply"===(0,it.ownerGuidanceApplyDecision)({stale:this.ownerGuidanceStale,serveUp:!!e,anyBusy:t,lastTurnMs:r,now:Date.now(),coldMs:(0,it.ownerGuidanceColdMs)()})&&(this.logger.info("Applying your changed guidance now: nothing here has had a turn for longer than a cache lives, so no cache is lost",{quietMs:(0,it.ownerGuidanceColdMs)()}),await this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance failed; it stays pending",{error:(0,W.getErrorMessage)(e)})))}async mcpGrantInputs(e){const t=await this.configurer.fetchMcpServersFromRemote(e).catch(()=>[]),r=await this.configurer.fetchDefaultWorkflowFromRemote(e).catch(()=>null)
|
|
5093
|
+
return{servers:t,permissions:r?.connectionPermissions??null}}agentConfigFingerprint(){const e=(0,ge.createHash)("sha256")
|
|
5016
5094
|
for(const t of[...this.configurer.getAgentMarkdowns()].sort((e,t)=>e.name.localeCompare(t.name)))e.update(t.name).update("\0").update(t.markdown).update("\x01")
|
|
5017
|
-
try{e.update(
|
|
5095
|
+
try{e.update(q.default.readFileSync((0,Ee.generatedOpencodeConfigPath)(this.options.projectPath),"utf-8"))}catch{}return e.digest("hex")}async handleSyncAgents(e,t){try{const e=this.projectId
|
|
5018
5096
|
if(!e)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
5019
5097
|
this.logger.info("Syncing agents from remote",{projectId:e})
|
|
5020
5098
|
const r=await this.configurer.fetchAgentsFromRemote(e)
|
|
@@ -5023,19 +5101,19 @@ const s=await this.configurer.fetchProjectFromRemote(e)
|
|
|
5023
5101
|
this.logger.debug("Fetched project for global prompt",{projectId:e,hasProject:!!s,hasGlobalSystemPrompt:!!s?.metadata?.globalSystemPrompt})
|
|
5024
5102
|
const n=await this.configurer.agentPreambleFor(e,s?.metadata?.globalSystemPrompt)
|
|
5025
5103
|
n?this.logger.info("Using global system prompt from project metadata",{projectId:e,promptLength:n.length}):this.logger.debug("No global system prompt found in project metadata")
|
|
5026
|
-
const o=
|
|
5027
|
-
await
|
|
5028
|
-
const i="alchemy",a=this.cachedDefaultModel||
|
|
5104
|
+
const o=D.default.join(this.options.projectPath,".opencode","agent")
|
|
5105
|
+
await q.default.promises.mkdir(o,{recursive:!0})
|
|
5106
|
+
const i="alchemy",a=this.cachedDefaultModel||M.FLEET_DEFAULT_MODEL,c=r.filter(e=>e.enabled),l=await this.configurer.resolveWorkflowEffort(e),d=await this.mcpGrantInputs(e),u=this.agentConfigFingerprint()
|
|
5029
5107
|
this.configurer.clearAgentMarkdowns()
|
|
5030
5108
|
let h=0
|
|
5031
|
-
for(const e of c)try{this.configurer.setAgentMarkdown(e.name,this.configurer.generateAgentMarkdown(e,i,a,n,{effort:l,extraTools:(0,
|
|
5032
|
-
try{const e=(0,
|
|
5033
|
-
if(
|
|
5109
|
+
for(const e of c)try{this.configurer.setAgentMarkdown(e.name,this.configurer.generateAgentMarkdown(e,i,a,n,{effort:l,extraTools:(0,me.agentToolGrants)(d.servers,e.name,d.permissions),limits:(0,M.modelLimitsOf)(s?.metadata),claudeThinking:(0,H.claudeThinkingOf)(s?.metadata)})),h++}catch(t){this.logger.warn("Agent markdown not generated",{name:e.name,error:(0,W.getErrorMessage)(t)})}this.configurer.setAgentMarkdown("explore",this.configurer.buildExploreMarkdown(n)),await this.configurer.removeOnDiskAgentFiles(o),this.lastAgentResyncModel=a
|
|
5110
|
+
try{const e=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
|
|
5111
|
+
if(q.default.existsSync(e)){const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
5034
5112
|
if(r.agent){for(const e of Object.values(r.agent))e&&"object"==typeof e&&"prompt"in e&&delete e.prompt
|
|
5035
|
-
|
|
5113
|
+
q.default.writeFileSync(e,JSON.stringify(r,null,2),"utf-8")}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,W.getErrorMessage)(e)})}const p=this.agentConfigFingerprint()===u
|
|
5036
5114
|
p&&this.logger.info("Agent definitions unchanged; no config reload",{context:"sync-agents"}),this.ownerGuidanceStale=!1
|
|
5037
5115
|
const f=p?"unchanged":await this.requestConfigReload("sync-agents"),m="reloaded"===f
|
|
5038
|
-
t.json({success:!0,message:`Synced ${h} agent(s) into the running serve${m?" - config reloaded":"deferred"===f?" - config reload deferred until MCP is idle":""}`,count:h,reloaded:m,reloadDeferred:"deferred"===f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,
|
|
5116
|
+
t.json({success:!0,message:`Synced ${h} agent(s) into the running serve${m?" - config reloaded":"deferred"===f?" - config reload deferred until MCP is idle":""}`,count:h,reloaded:m,reloadDeferred:"deferred"===f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
|
|
5039
5117
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
5040
5118
|
const{sessionId:s,requestId:n,summary:o,status:i}=e.body
|
|
5041
5119
|
if(!s)return void t.status(400).json({success:!1,error:"sessionId is required"})
|
|
@@ -5043,26 +5121,26 @@ this.logger.info("Direct report creation",{sessionId:s,requestId:n,status:i})
|
|
|
5043
5121
|
const a=[]
|
|
5044
5122
|
try{const e=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),t=e?.data||e||[]
|
|
5045
5123
|
let n=s
|
|
5046
|
-
for(const e of t)a.push({ancestorId:e.externalSessionId,childId:n}),n=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,
|
|
5124
|
+
for(const e of t)a.push({ancestorId:e.externalSessionId,childId:n}),n=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,W.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
|
|
5047
5125
|
let c,l=o
|
|
5048
5126
|
if(!l&&n)try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${n}`),t=e?.data||e
|
|
5049
5127
|
c=t?.title
|
|
5050
5128
|
const s=t?.completionSummary
|
|
5051
|
-
s?l=s:c&&(l=`Done: ${c}`)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:n,error:(0,
|
|
5129
|
+
s?l=s:c&&(l=`Done: ${c}`)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:n,error:(0,W.getErrorMessage)(e)})}l||(l=`Completed request ${n||"unknown"}`)
|
|
5052
5130
|
const d=n||s
|
|
5053
5131
|
if(this.directReportDedup.size>=1e3){const e=this.directReportDedup.keys().next().value
|
|
5054
5132
|
void 0!==e&&this.directReportDedup.delete(e)}this.directReportDedup.has(d)||this.directReportDedup.set(d,new Set)
|
|
5055
5133
|
const u=this.directReportDedup.get(d),h=a.filter(({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1)),p=await Promise.allSettled(h.map(async({ancestorId:e,childId:t})=>{const s=await this.apiClient.post(`/api/projects/${r}/session-messages/reports`,{fromSessionId:t,toSessionId:e,status:i||"success",summary:l}),n=s?.data||s
|
|
5056
|
-
return u.add(e),n?.messageCode&&this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(n.messageCode)}/acknowledge`,{}).catch(e=>this.logger.debug("Direct report pre-ack failed (non-fatal)",{messageCode:n.messageCode,error:(0,
|
|
5134
|
+
return u.add(e),n?.messageCode&&this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(n.messageCode)}/acknowledge`,{}).catch(e=>this.logger.debug("Direct report pre-ack failed (non-fatal)",{messageCode:n.messageCode,error:(0,W.getErrorMessage)(e)})),this.logger.info("Direct report created for ancestor",{messageCode:n?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:n?.messageCode,fromSessionId:t,content:n?.content}})),f=[]
|
|
5057
5135
|
for(const e of p)"fulfilled"===e.status&&f.push(e.value)
|
|
5058
|
-
for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,
|
|
5136
|
+
for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,W.getErrorMessage)(e.reason)})
|
|
5059
5137
|
if(setTimeout(()=>{this.directReportDedup.delete(d)},3e5),n)try{const e=await this.apiClient.get(`/api/projects/${r}/session-request-associations/by-session/${encodeURIComponent(s)}?activeOnly=true`),t=e?.data||e||[],n=(await Promise.allSettled(t.map(e=>this.apiClient.post(`/api/projects/${r}/session-request-associations/end`,{sessionId:s,requestLocalId:e.requestLocalId})))).filter(e=>"rejected"===e.status).length
|
|
5060
|
-
this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:n})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,
|
|
5138
|
+
this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:n})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,W.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:f,report:f[0]?{messageCode:f[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:r}=e.params,s=this.projectId
|
|
5061
5139
|
if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),n=e?.data||e
|
|
5062
5140
|
if(this.logger.info("Agent type DB lookup",{sessionId:r,dbAgentType:n?.agentType||null,dbId:n?.id||n?.localId||null}),n?.agentType){const e=this.configurer.agentModelOf(n.agentType)
|
|
5063
|
-
return void t.json({success:!0,agentType:n.agentType,source:"db",...e?{model:e}:{}})}}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,
|
|
5141
|
+
return void t.json({success:!0,agentType:n.agentType,source:"db",...e?{model:e}:{}})}}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,W.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,s=this.projectId
|
|
5064
5142
|
if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/resolve/${encodeURIComponent(r)}`),n=e?.data?.parent?.externalSessionId||""
|
|
5065
|
-
return void t.json({success:!0,parentSessionId:n})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,
|
|
5143
|
+
return void t.json({success:!0,parentSessionId:n})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,W.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
|
|
5066
5144
|
if(!r)return void t.status(502).json({error:"OpenCode not running"})
|
|
5067
5145
|
const s=e.path.replace(/^\//,""),n=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",o=`${r}/${s}${n}`,i=e.headers.accept?.includes("text/event-stream")
|
|
5068
5146
|
if(i){const r=new AbortController,s=()=>{this.proxyAbortControllers.delete(r)},n=(async()=>{try{const e=await fetch(o,{headers:{Accept:"text/event-stream"},signal:r.signal})
|
|
@@ -5083,18 +5161,18 @@ if(!t||t===r)throw e
|
|
|
5083
5161
|
l=`${t}/${s}${n}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:r,newUrl:t}),c=await fetch(l,a)}if(204===c.status||"0"===c.headers.get("content-length"))return void t.status(c.status).end()
|
|
5084
5162
|
const d=c.headers.get("content-type")||""
|
|
5085
5163
|
let u
|
|
5086
|
-
try{u=d.includes("application/json")?await c.json():await c.text()}catch{return void t.status(c.status).end()}t.status(c.status),d&&t.setHeader("Content-Type",d),"string"==typeof u?t.send(u):t.json(u)}finally{this.inFlightServeProxyRequests--}}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,
|
|
5164
|
+
try{u=d.includes("application/json")?await c.json():await c.text()}catch{return void t.status(c.status).end()}t.status(c.status),d&&t.setHeader("Content-Type",d),"string"==typeof u?t.send(u):t.json(u)}finally{this.inFlightServeProxyRequests--}}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,W.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,W.getErrorMessage)(r)})}}async handleSpawnPlanner(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId,["planId","prompt"]))return
|
|
5087
5165
|
const{agent:r,planId:s,prompt:n,title:o,parentSessionId:i,flavor:a}=e.body||{},c="auto-planner"===r?"planner":r,l="auto-planner"===r||"autonomous"===a?"autonomous":void 0,d=new Set(["planner"])
|
|
5088
5166
|
if(c&&!d.has(c))return void t.status(400).json({success:!1,error:`agent must be one of: ${[...d].join(", ")}`})
|
|
5089
|
-
const u=await(0,
|
|
5167
|
+
const u=await(0,Se.spawnPlannerWithDoc)({agent:c||"planner",flavor:l,planId:s,prompt:n,title:o||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
|
|
5090
5168
|
if(!u.success||!u.sessionId)return void t.status(500).json({success:!1,error:u.error||"spawn failed"})
|
|
5091
|
-
t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,
|
|
5092
|
-
if(t&&t.externalSessionId){const e=(0,
|
|
5169
|
+
t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,De.respondWithError)(t,this.logger,"Spawn planner",e)}}scheduleHubAlchemist(e){this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.hubRetryTimer=setTimeout(async()=>{this.hubRetryTimer=void 0,await this.ensureHubAlchemist()||this.scheduleHubAlchemist(Math.min(2*e,6e4))},e),this.hubRetryTimer.unref?.()}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
|
|
5170
|
+
if(t&&t.externalSessionId){const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath),r=e?await this.serveHasSession(e,String(t.externalSessionId)):null
|
|
5093
5171
|
if(!1!==r)return this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r}),!0
|
|
5094
|
-
this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,
|
|
5095
|
-
const r=new
|
|
5096
|
-
return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,
|
|
5097
|
-
const e=(0,
|
|
5172
|
+
this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,W.getErrorMessage)(e)}))}if(!this.projectId)return!0
|
|
5173
|
+
const r=new ve.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}),s="Hub",n=await r.prepareSession({agent:"alchemist",prompt:s,title:s,projectPath:this.options.projectPath})
|
|
5174
|
+
return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,W.getErrorMessage)(e)}),!1}}async reapVanishedSessions(){if(!this.projectId)return
|
|
5175
|
+
const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
5098
5176
|
if(!e)return
|
|
5099
5177
|
let t
|
|
5100
5178
|
try{const r=await fetch(`${e.replace(/\/$/,"")}/session?limit=5000`,{signal:AbortSignal.timeout(15e3)})
|
|
@@ -5107,15 +5185,15 @@ try{for(let e=0;e<1e3;e+=100){const n=await this.apiClient.get(`/api/projects/${
|
|
|
5107
5185
|
if(!Array.isArray(o)||0===o.length)break
|
|
5108
5186
|
for(const e of o){s++
|
|
5109
5187
|
const n=e?.externalSessionId??e?.external_session_id
|
|
5110
|
-
n&&!t.has(String(n))&&r.push({id:String(e.id),ext:String(n)})}if(o.length<100)break}}catch(e){return void this.logger.warn("vanished-session sweep could not read the session list; ending nothing",{error:(0,
|
|
5111
|
-
for(const e of r)try{await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${e.id}`,{status:"completed"}),n++}catch(t){o++,o<=3&&this.logger.warn("could not end a vanished session row",{sessionId:e.id,externalSessionId:e.ext,error:(0,
|
|
5112
|
-
return!!r.ok||404!==r.status&&null}catch{return null}}async handleSpawnSession(e,t){try{if(!(0,
|
|
5113
|
-
const{agent:r,prompt:s,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,shapingRequestLocalId:l,userOpened:d}=e.body,u=this.projectId,h=new
|
|
5114
|
-
p?.sessionId&&o&&!0!==d&&this.watchSubagentForParent(String(p.sessionId),String(o),String(r)),t.json({success:!0,data:p})}catch(e){(0,
|
|
5115
|
-
return r.ok?(0,
|
|
5188
|
+
n&&!t.has(String(n))&&r.push({id:String(e.id),ext:String(n)})}if(o.length<100)break}}catch(e){return void this.logger.warn("vanished-session sweep could not read the session list; ending nothing",{error:(0,W.getErrorMessage)(e)})}let n=0,o=0
|
|
5189
|
+
for(const e of r)try{await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${e.id}`,{status:"completed"}),n++}catch(t){o++,o<=3&&this.logger.warn("could not end a vanished session row",{sessionId:e.id,externalSessionId:e.ext,error:(0,W.getErrorMessage)(t)})}o>0&&this.logger.warn("vanished-session sweep could not end every row it meant to",{ended:n,failed:o,scanned:s}),n>0&&this.logger.info("ended rows for sessions this serve no longer has",{ended:n,scanned:s,live:t.size})}async serveHasSession(e,t){try{const r=await fetch(`${e.replace(/\/$/,"")}/session/${encodeURIComponent(t)}`,{signal:AbortSignal.timeout(5e3)})
|
|
5190
|
+
return!!r.ok||404!==r.status&&null}catch{return null}}async handleSpawnSession(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId,["agent","prompt"]))return
|
|
5191
|
+
const{agent:r,prompt:s,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,shapingRequestLocalId:l,userOpened:d}=e.body,u=this.projectId,h=new ve.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),p=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,..."string"==typeof l&&/^req\d+$/.test(l)?{shapingRequestLocalId:l,skipWorktree:!0}:{}})
|
|
5192
|
+
p?.sessionId&&o&&!0!==d&&this.watchSubagentForParent(String(p.sessionId),String(o),String(r)),t.json({success:!0,data:p})}catch(e){(0,De.respondWithError)(t,this.logger,"Spawn session",e)}}async childTurnDeath(e,t){try{const r=await fetch(`${e}/session/${encodeURIComponent(t)}/message?limit=6`,{signal:AbortSignal.timeout(5e3)})
|
|
5193
|
+
return r.ok?(0,X.lastTurnDeath)(await r.json()):null}catch{return null}}async endFinishedChildSession(e,t){try{const r=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=r?.data??r
|
|
5116
5194
|
if(!s?.id)return
|
|
5117
5195
|
if(s.status&&"active"!==s.status)return
|
|
5118
|
-
await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${s.id}`,{status:"completed"}),this.logger.info("subagent idle rail: child session ended",{childSessionId:e,agent:t})}catch(t){this.logger.warn("subagent idle rail: could not end the child session row",{childSessionId:e,error:(0,
|
|
5196
|
+
await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${s.id}`,{status:"completed"}),this.logger.info("subagent idle rail: child session ended",{childSessionId:e,agent:t})}catch(t){this.logger.warn("subagent idle rail: could not end the child session row",{childSessionId:e,error:(0,W.getErrorMessage)(t)})}}watchSubagentForParent(e,r,s){try{const{onNextSessionIdle:n}=t(67078),o=n(e,()=>{(async()=>{try{const n=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?sessionId=${encodeURIComponent(e)}&direction=outgoing&limit=20`).catch(()=>null)
|
|
5119
5197
|
if((Array.isArray(n?.data)?n.data:[]).some(t=>String(t?.fromSessionId??t?.from_session_id??"")===e&&["report","question"].includes(String(t?.messageType??t?.message_type??""))))return this.logger.info("subagent idle rail: child already reported; parent not re-woken",{childSessionId:e,parentSessionId:r,agent:s}),void await this.endFinishedChildSession(e,s)
|
|
5120
5198
|
const{getOpenCodeServerUrl:o}=await Promise.resolve().then(()=>t(28148)),i=o(this.options.projectPath,this.logger)
|
|
5121
5199
|
if(!i)return
|
|
@@ -5128,11 +5206,11 @@ if(!u||!u.trim())return void await this.endFinishedChildSession(e,s)
|
|
|
5128
5206
|
const{deliverPrompt:h}=await Promise.resolve().then(()=>t(17039)),p={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}
|
|
5129
5207
|
if(d.test(u)){const t=(this.leakedCallNudges.get(e)??0)+1
|
|
5130
5208
|
if(this.leakedCallNudges.set(e,t),t<=2)return await h(p,e,'[system] Your last reply wrote tool calls as plain text ("to=functions.\u2026"), so none of them ran: nothing you described was created. Make each of those calls now as real tool calls, then finish as you meant to.',{kind:"agent-context"}),this.logger.warn("subagent idle rail: tool calls came out as text; told the session and watching again",{childSessionId:e,agent:s,tries:t}),void this.watchSubagentForParent(e,r,s)
|
|
5131
|
-
u=`It wrote its tool calls as plain text instead of making them, and did again after being told twice, so nothing it describes below was actually created. Its last message:\n\n${u}`}await h(p,r,`[${s} finished] It did not file a report, so here is what it produced:\n\n${u.slice(0,8e3)}`,{kind:"subagent-idle"}),this.logger.info("subagent idle rail: woke the parent with the child's final message",{childSessionId:e,parentSessionId:r,agent:s}),await this.endFinishedChildSession(e,s)}catch(t){this.logger.warn("subagent idle rail failed (the parent stays asleep)",{childSessionId:e,parentSessionId:r,error:(0,
|
|
5132
|
-
i.unref?.()}catch(t){this.logger.warn("subagent idle rail could not be armed",{childSessionId:e,parentSessionId:r,error:(0,
|
|
5209
|
+
u=`It wrote its tool calls as plain text instead of making them, and did again after being told twice, so nothing it describes below was actually created. Its last message:\n\n${u}`}await h(p,r,`[${s} finished] It did not file a report, so here is what it produced:\n\n${u.slice(0,8e3)}`,{kind:"subagent-idle"}),this.logger.info("subagent idle rail: woke the parent with the child's final message",{childSessionId:e,parentSessionId:r,agent:s}),await this.endFinishedChildSession(e,s)}catch(t){this.logger.warn("subagent idle rail failed (the parent stays asleep)",{childSessionId:e,parentSessionId:r,error:(0,W.getErrorMessage)(t)})}})()}),i=setTimeout(()=>{try{o()}catch{}},36e5)
|
|
5210
|
+
i.unref?.()}catch(t){this.logger.warn("subagent idle rail could not be armed",{childSessionId:e,parentSessionId:r,error:(0,W.getErrorMessage)(t)})}}visualBaselineDir(){return D.default.join(this.options.projectPath,".alchemy_lab",P.VISUAL_BASELINES_DIR_NAME)}async handleRunTestsNow(e,r){try{if(!(0,De.validateRequest)(e,r,this.projectId,["requestId"]))return
|
|
5133
5211
|
const{requestId:s,parentSessionId:n,testProcessIds:o,commandNames:i,catchUp:a}=e.body||{},{handleRunTestsAction:c}=await Promise.resolve().then(()=>t(76555)),l={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},d=await c({requestId:s,parentSessionId:n,testProcessIds:o,commandNames:i,catchUp:a,mode:"auto"},l)
|
|
5134
5212
|
if(!d.success)return void r.status(500).json({success:!1,error:d.error||d.message})
|
|
5135
|
-
r.json({success:!0,data:d.data})}catch(e){(0,
|
|
5213
|
+
r.json({success:!0,data:d.data})}catch(e){(0,De.respondWithError)(r,this.logger,"Run tests now",e)}}async handleSetupRun(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId))return
|
|
5136
5214
|
const r=this.projectId
|
|
5137
5215
|
if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
|
|
5138
5216
|
const s=String(e.body?.runId??"")
|
|
@@ -5141,21 +5219,21 @@ const n=await this.apiClient.get(`/api/projects/${r}/setup-runs/${s}`),o=n?.data
|
|
|
5141
5219
|
if(!o?.id)return void t.status(404).json({success:!1,error:`no setup run ${s}`})
|
|
5142
5220
|
if("running"!==o.state)return void t.json({success:!0,data:{skipped:`run is ${o.state}`}})
|
|
5143
5221
|
if(o.architectSessionId)return void t.json({success:!0,data:{sessionId:o.architectSessionId,already:!0}})
|
|
5144
|
-
const i=Array.isArray(o.parts)?o.parts:[],a=Array.isArray(o.asks)?o.asks:[],c=[o.askedBy,...a.map(e=>e?.from)].map(e=>e&&("request"===e.by||"worker"===e.by)&&/^req\d{6}$/.test(String(e.ref??""))?String(e.ref):void 0).find(Boolean),l=await(0,
|
|
5222
|
+
const i=Array.isArray(o.parts)?o.parts:[],a=Array.isArray(o.asks)?o.asks:[],c=[o.askedBy,...a.map(e=>e?.from)].map(e=>e&&("request"===e.by||"worker"===e.by)&&/^req\d{6}$/.test(String(e.ref??""))?String(e.ref):void 0).find(Boolean),l=await(0,qe.resolveSetupBranch)(this.apiClient,r,void 0,c,this.logger,this.options.projectPath),d=a.map(e=>String(e?.need??"")).filter(Boolean),u=a.find(e=>"checks"===e?.part&&("alchemist"===e?.from?.by||"user"===e?.from?.by))?.need,h=i.includes("checks")?"checks":i.includes("environment")?"environment":"commands"
|
|
5145
5223
|
let p,f
|
|
5146
|
-
if("checks"===h){const e=await(0,
|
|
5224
|
+
if("checks"===h){const e=await(0,qe.gateAsksOnRecord)(this.apiClient,r,this.logger)
|
|
5147
5225
|
let t=!1
|
|
5148
5226
|
try{const e=await this.apiClient.get(`/api/projects/${r}/test-processes`),s=Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[]
|
|
5149
5227
|
t=s.length>0}catch{}let s=!1
|
|
5150
|
-
try{s=await(0,
|
|
5151
|
-
try{n=await(0,
|
|
5228
|
+
try{s=await(0,qe.testGateOffForSetup)(this.apiClient,r,c)}catch{}let n=!1
|
|
5229
|
+
try{n=await(0,qe.selfDrivingForSetup)(this.apiClient,r)}catch{}p=async(r,o,i,a)=>(0,qe.buildSetupPrompt)(r,c,void 0,e,"string"==typeof u?u:void 0,s,a?await(0,Ce.declaredFloorsIn)(a).catch(()=>[]):[],{existingProcesses:t,selfDriving:n}),f="Test check setup"}else if("environment"===h){let e="interactive"
|
|
5152
5230
|
try{const t=await this.apiClient.get(`/api/projects/${r}`),s=(t?.data||t)?.metadata?.environmentSetupMode
|
|
5153
|
-
"auto"!==s&&"ask"!==s&&"interactive"!==s||(e=s)}catch{}p=async t=>(0,
|
|
5154
|
-
const m=await(0,
|
|
5155
|
-
if(!m.success||!m.sessionId)return await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/finish`,{system:!0,actor:"spawn",outcome:"stopped",summary:`The setup architect could not be started: ${String(m.error??"unknown error").slice(0,300)}`}).catch(e=>this.logger.warn("setup run spawn failed and the run could not be ended (the reconciler ends it)",{runId:o.id,error:(0,
|
|
5156
|
-
await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/started`,{architectSessionId:m.sessionId}),this.logger.info("setup run architect spawned",{runId:o.id,parts:i,sessionId:m.sessionId,branch:m.branch}),t.json({success:!0,data:{sessionId:m.sessionId,worktreePath:m.worktreePath,branch:m.branch}})}catch(e){(0,
|
|
5157
|
-
const e=this.projectId,t=(0,
|
|
5158
|
-
try{const r=await(0,
|
|
5231
|
+
"auto"!==s&&"ask"!==s&&"interactive"!==s||(e=s)}catch{}p=async t=>(0,qe.buildEnvironmentSetupPrompt)(t,c,d.join("\n"),e),f="Environment setup"}else p=async e=>(0,qe.buildCommandSetupPrompt)(e,c,d.join("\n")),f="Commands setup"
|
|
5232
|
+
const m=await(0,Ie.setupAgentInWorktree)({agent:"setup-architect",promptBuilder:async(e,t,r,s)=>`${await p(e,t,r,s)}\n${(0,qe.setupRunClosing)({id:o.id,parts:i,asks:a})}`,titleBuilder:()=>`[${o.id}] ${f}`,worktreeErrorLabel:`Failed to create setup worktree (${f})`,runId:o.id,parentSessionId:o.parentSessionId??void 0,branchName:l,apiClient:this.apiClient,logger:this.logger,projectId:r,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
|
|
5233
|
+
if(!m.success||!m.sessionId)return await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/finish`,{system:!0,actor:"spawn",outcome:"stopped",summary:`The setup architect could not be started: ${String(m.error??"unknown error").slice(0,300)}`}).catch(e=>this.logger.warn("setup run spawn failed and the run could not be ended (the reconciler ends it)",{runId:o.id,error:(0,W.getErrorMessage)(e)})),void t.status(m.statusCode||500).json({success:!1,error:m.error})
|
|
5234
|
+
await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/started`,{architectSessionId:m.sessionId}),this.logger.info("setup run architect spawned",{runId:o.id,parts:i,sessionId:m.sessionId,branch:m.branch}),t.json({success:!0,data:{sessionId:m.sessionId,worktreePath:m.worktreePath,branch:m.branch}})}catch(e){(0,De.respondWithError)(t,this.logger,"Setup run",e)}}async reconcileSetupRunPass(){if(!this.projectId)return
|
|
5235
|
+
const e=this.projectId,t=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
5236
|
+
try{const r=await(0,Q.reconcileSetupRun)({runningRun:async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
|
|
5159
5237
|
return r?.id?{id:r.id,architectSessionId:r.architectSessionId??null,startedAt:r.startedAt??null}:null},architectState:async e=>{if(!t)return"unknown"
|
|
5160
5238
|
const r=await this.serveHasSession(t,e)
|
|
5161
5239
|
if(!1===r)return"gone"
|
|
@@ -5163,37 +5241,37 @@ if(null===r)return"unknown"
|
|
|
5163
5241
|
try{const r=await fetch(`${t.replace(/\/$/,"")}/session/status`,{signal:AbortSignal.timeout(5e3)})
|
|
5164
5242
|
if(r.ok){const t=(await r.json())?.[e]
|
|
5165
5243
|
if(t&&"idle"!==t.type)return"busy"}}catch{return"unknown"}try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}/message?limit=6`,{signal:AbortSignal.timeout(5e3)})
|
|
5166
|
-
return r.ok?(0,
|
|
5244
|
+
return r.ok?(0,Q.architectTurnState)(await r.json()):"unknown"}catch{return"unknown"}},lastActivityAt:async e=>{if(!t)return null
|
|
5167
5245
|
try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(5e3)})
|
|
5168
5246
|
if(!r.ok)return null
|
|
5169
5247
|
const s=Number((await r.json())?.time?.updated)
|
|
5170
5248
|
return Number.isFinite(s)&&s>0?s:null}catch{return null}},hasPendingQuestion:async t=>{const r=await this.apiClient.get(`/api/projects/${e}/session-messages?sessionId=${encodeURIComponent(t)}&direction=outgoing&messageType=question&status=pending&limit=5`).catch(()=>null)
|
|
5171
5249
|
return(Array.isArray(r?.data)?r.data:[]).some(e=>"pending"===String(e?.status??""))},remind:async(t,r)=>{await(0,d.deliverPrompt)({projectPath:this.options.projectPath,projectId:e,apiClient:this.apiClient,logger:this.logger},t,`[${r} is still running] Your setup run has not ended: call \`setup_finish\` with its outcome and a summary that answers every ask the run carries. If you still need the user's answer, ask it as one question.`,{kind:"agent-context"})},stop:async(t,r)=>{await this.apiClient.post(`/api/projects/${e}/setup-runs/${t}/finish`,{system:!0,actor:"reconciler",outcome:"stopped",summary:r})}})
|
|
5172
|
-
"none"!==r.action&&"running"!==r.action&&this.logger.info("setup run reconciled",r)}catch(e){this.logger.warn("setup run reconcile pass failed (the next pass looks again)",{error:(0,
|
|
5250
|
+
"none"!==r.action&&"running"!==r.action&&this.logger.info("setup run reconciled",r)}catch(e){this.logger.warn("setup run reconcile pass failed (the next pass looks again)",{error:(0,W.getErrorMessage)(e)})}await(0,qe.removeEndedSetupRunWorktrees)(this.options.projectPath,async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
|
|
5173
5251
|
return"string"==typeof r?.id?r.id:null},this.logger)}async handleGetAgentByName(e,t){try{const r=this.projectId
|
|
5174
5252
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
5175
|
-
const{agentName:s}=e.params,n=(await(0,
|
|
5253
|
+
const{agentName:s}=e.params,n=(await(0,be.getProjectAgents)(this.apiClient,r)).find(e=>e.name===s)
|
|
5176
5254
|
if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
|
|
5177
|
-
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,
|
|
5178
|
-
const e=(0,
|
|
5179
|
-
try{if(!
|
|
5180
|
-
const t=
|
|
5181
|
-
return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,
|
|
5255
|
+
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<pt.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
|
|
5256
|
+
const e=(0,ot.identityPath)(this.options.projectPath)
|
|
5257
|
+
try{if(!q.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
|
|
5258
|
+
const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
5259
|
+
return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,W.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e,(0,st.noteServe)(e)
|
|
5182
5260
|
const t=`http://127.0.0.1:${e}`
|
|
5183
5261
|
this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.serveStartedAt=Date.now(),this.stuckSessionWatchdog?.serveStarted(this.serveStartedAt),(async()=>{for(let e=0;e<10;e++){try{const t=await this.configurer.refreshConnectedProviders(this.options.projectPath)
|
|
5184
|
-
if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,
|
|
5262
|
+
if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,W.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,W.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
5185
5263
|
if(!t.ok)return
|
|
5186
|
-
const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,
|
|
5187
|
-
c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,
|
|
5264
|
+
const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,M.primaryProviderOf)(s),o=r?.default||{},[i,a]=Object.entries(o)[0]||[],c=n?(0,M.defaultModelFor)(n):i&&a?`${i}/${a}`:null
|
|
5265
|
+
c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,W.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.parkController?.isServeDown())return null
|
|
5188
5266
|
if(this.openCodePort)return`http://127.0.0.1:${this.openCodePort}`
|
|
5189
5267
|
if(this.openCodeUrlCache&&Date.now()-this.openCodeUrlCache.timestamp<3e4)return this.openCodeUrlCache.url
|
|
5190
|
-
const e=(0,
|
|
5268
|
+
const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
5191
5269
|
return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async register(){const e=this.readProjectConfig()
|
|
5192
5270
|
if(!e?.id)throw new Error("No project configuration found")
|
|
5193
|
-
this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.cachedDefaultModel&&this.lastAgentResyncModel!==this.cachedDefaultModel&&this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Post-registration agent markdown re-sync failed",{error:(0,
|
|
5194
|
-
try{const e=(0,
|
|
5195
|
-
if(e){const t=JSON.parse(
|
|
5196
|
-
this.opencodeVersion=t.version}}catch{}const t={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:
|
|
5271
|
+
this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.cachedDefaultModel&&this.lastAgentResyncModel!==this.cachedDefaultModel&&this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Post-registration agent markdown re-sync failed",{error:(0,W.getErrorMessage)(e)})),this.startParkController(),this.startProjectSubsystems("register").catch(e=>{this.logger.error("project subsystems failed to start from register()",{error:(0,W.getErrorMessage)(e)})}),this.clientVersion=(0,i.ownVersion)()
|
|
5272
|
+
try{const e=(0,Oe.findOpencodeDir)()
|
|
5273
|
+
if(e){const t=JSON.parse(q.default.readFileSync(D.default.join(e,"package.json"),"utf8"))
|
|
5274
|
+
this.opencodeVersion=t.version}}catch{}const t={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:N.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion,...this.registeredOnce?{}:{boot:!0}}
|
|
5197
5275
|
this.logger.info("Registering with remote",{projectId:this.projectId})
|
|
5198
5276
|
const r=await this.apiClient.post("/api/servers/register",t)
|
|
5199
5277
|
if(this.registeredOnce=!0,r.success&&r.data?.serverId){this.serverId=r.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId}),this.startHeartbeat()
|
|
@@ -5203,18 +5281,18 @@ for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] $
|
|
|
5203
5281
|
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}async applyVersionWarnings(e){if(e.map(e=>`${e.component}:${e.severity}`).join(",")!==this.versionWarnings.map(e=>`${e.component}:${e.severity}`).join(",")){for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] ${t.message}`),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${t.message}\x1b[0m\n`)):(this.logger.warn(`[UPDATE RECOMMENDED] ${t.message}`),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
|
|
5204
5282
|
this.versionWarnings=e,!e.some(e=>"required"===e.severity)&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){if(!this.serverId)return
|
|
5205
5283
|
if(this.heartbeatInterval)return
|
|
5206
|
-
this.registryHeartbeatInterval=setInterval(()=>{if(this.projectId)try{(0,
|
|
5207
|
-
const e=async()=>{if(this.projectId)try{const e=await(0,
|
|
5284
|
+
this.registryHeartbeatInterval=setInterval(()=>{if(this.projectId)try{(0,T.touchHeartbeat)(this.projectId,{projectId:this.projectId,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:this.startTime.toISOString()})}catch{}},3e4),"function"==typeof this.registryHeartbeatInterval.unref&&this.registryHeartbeatInterval.unref(),this.vanishedSessionSweepInterval=setInterval(()=>{this.reapVanishedSessions()},6e5),"function"==typeof this.vanishedSessionSweepInterval.unref&&this.vanishedSessionSweepInterval.unref()
|
|
5285
|
+
const e=async()=>{if(this.projectId)try{const e=await(0,ue.fetchProviderUsage)(this.options.projectPath)
|
|
5208
5286
|
e.length&&await this.apiClient.post(`/api/projects/${this.projectId}/plan-usage-readings`,{providers:e})}catch{}}
|
|
5209
5287
|
setTimeout(()=>{e()},2e4).unref?.(),this.planUsageInterval=setInterval(()=>{e()},6e5),"function"==typeof this.planUsageInterval.unref&&this.planUsageInterval.unref(),this.runningReportInterval=setInterval(()=>{this.reportRunningSessions(!0)},15e3),"function"==typeof this.runningReportInterval.unref&&this.runningReportInterval.unref(),this.heartbeatInterval=setInterval(async()=>{try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings??[]
|
|
5210
|
-
await this.applyVersionWarnings(t)}catch(e){const t=(0,
|
|
5288
|
+
await this.applyVersionWarnings(t)}catch(e){const t=(0,W.getErrorMessage)(e)
|
|
5211
5289
|
if(t.includes("Server not found or inactive")){this.logger.warn("Heartbeat hit a stale registration; re-registering",{staleServerId:this.serverId})
|
|
5212
|
-
try{await this.register(),this.logger.info("Re-registered after stale heartbeat",{serverId:this.serverId})}catch(e){this.logger.warn("Re-registration failed; will retry on next heartbeat",{error:(0,
|
|
5290
|
+
try{await this.register(),this.logger.info("Re-registered after stale heartbeat",{serverId:this.serverId})}catch(e){this.logger.warn("Re-registration failed; will retry on next heartbeat",{error:(0,W.getErrorMessage)(e)})}return}this.logger.warn("Heartbeat failed",{error:t})}},3e5)}noteSessionRunning(e,t){t!==this.runningSessions.has(e)&&(t?this.runningSessions.add(e):this.runningSessions.delete(e),this.runningReportDebounce&&clearTimeout(this.runningReportDebounce),this.runningReportDebounce=setTimeout(()=>{this.reportRunningSessions()},300),"function"==typeof this.runningReportDebounce.unref&&this.runningReportDebounce.unref())}async reportRunningSessions(e=!1){if(!this.projectId)return
|
|
5213
5291
|
const t=Array.from(this.runningSessions).sort(),r=t.join(",")
|
|
5214
5292
|
if(e||r!==this.lastRunningReport){this.lastRunningReport=r
|
|
5215
|
-
try{await this.apiClient.post(`/api/projects/${this.projectId}/session-activity`,{running:t,at:(new Date).toISOString()})}catch(e){this.lastRunningReport="",this.logger.debug("Running-sessions report failed",{error:(0,
|
|
5293
|
+
try{await this.apiClient.post(`/api/projects/${this.projectId}/session-activity`,{running:t,at:(new Date).toISOString()})}catch(e){this.lastRunningReport="",this.logger.debug("Running-sessions report failed",{error:(0,W.getErrorMessage)(e)})}}}startSSEEventForwarder(){this.sseForwarderInterval||(this.logger.info("Starting SSE event forwarder"),this.sseForwarderInterval=setInterval(async()=>{await this.flushSSEEvents()},2e3),this.connectToOpenCodeSSE())}stopSSEEventForwarder(){this.sseConnectGeneration++
|
|
5216
5294
|
for(const e of this.sseReconnectTimers)clearTimeout(e)
|
|
5217
|
-
this.sseReconnectTimers.clear(),this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch(e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,
|
|
5295
|
+
this.sseReconnectTimers.clear(),this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch(e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,W.getErrorMessage)(e)}))}scheduleSSEReconnect(e){const t=setTimeout(()=>{this.sseReconnectTimers.delete(t),this.connectToOpenCodeSSE()},e)
|
|
5218
5296
|
this.sseReconnectTimers.add(t)}async connectToOpenCodeSSE(){const e=this.sseConnectGeneration,r=()=>e!==this.sseConnectGeneration,s=this.getOpenCodeUrl()
|
|
5219
5297
|
if(!s)return void(this.sseForwarderInterval&&!r()&&(this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),this.scheduleSSEReconnect(5e3)))
|
|
5220
5298
|
const n=`${s}/event`
|
|
@@ -5229,41 +5307,41 @@ if(r()){try{await s.cancel()}catch{}return}if(e)break
|
|
|
5229
5307
|
const o=i.decode(n,{stream:!0})
|
|
5230
5308
|
if(l){const e=o.indexOf("\n")
|
|
5231
5309
|
if(-1===e)continue
|
|
5232
|
-
l=!1,a=o.slice(e+1),u||this.logger.warn("Oversized SSE event discarded as it arrived",{type:d})}else if(a+=o,a.length>
|
|
5310
|
+
l=!1,a=o.slice(e+1),u||this.logger.warn("Oversized SSE event discarded as it arrived",{type:d})}else if(a+=o,a.length>ht&&-1===a.indexOf("\n")&&!(a.length<=c.SESSION_DIFF_PARSE_CEILING&&(0,c.trimmableWhenWhole)(a.replace(/^data: /,"")))){d=(0,c.sseEventTypeOf)(a)
|
|
5233
5311
|
const e=(0,c.trimOversizedSseEvent)(a.replace(/^data: /,""),Number.POSITIVE_INFINITY)
|
|
5234
5312
|
u=!!e,e?(this.sseEventBuffer.push({type:e.type,data:e,at:Date.now()}),this.logger.debug("Oversized SSE event forwarded as a summary",{type:d})):(0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++),l=!0,a=""
|
|
5235
5313
|
continue}const h=a.split("\n")
|
|
5236
5314
|
a=h.pop()||""
|
|
5237
5315
|
for(const e of h)if(e.startsWith("data: ")){let r=null
|
|
5238
|
-
if(e.length>
|
|
5239
|
-
else if(e.length>
|
|
5240
|
-
0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++,this.logger.warn("Oversized SSE event dropped before parsing",{type:t,chars:e.length,limit:
|
|
5316
|
+
if(e.length>ht&&(r=(0,c.trimOversizedSseEvent)(e.slice(6)),r&&"session.diff"!==r.type&&JSON.stringify(r).length>ht&&(r=null)),r)this.logger.debug("Oversized SSE event forwarded as a summary",{type:r.type,chars:e.length})
|
|
5317
|
+
else if(e.length>ht){const t=(0,c.sseEventTypeOf)(e)
|
|
5318
|
+
0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++,this.logger.warn("Oversized SSE event dropped before parsing",{type:t,chars:e.length,limit:ht})
|
|
5241
5319
|
continue}try{const s=r??JSON.parse(e.slice(6)),n=s.type||"unknown"
|
|
5242
5320
|
this.sseEventBuffer.push({type:n,data:s,at:Date.now()})
|
|
5243
5321
|
{const e=s?.properties?.sessionID||s?.sessionID
|
|
5244
5322
|
if(e)if("session.busy"===n?this.noteSessionRunning(e,!0):"session.idle"!==n&&"session.error"!==n&&"session.retry"!==n||this.noteSessionRunning(e,!1),"session.error"===n)Promise.resolve().then(()=>t.t(86116,23)).then(t=>t.notifyInboxChatSessionError(e,s?.properties?.error)).catch(()=>{}),this.stuckSessionWatchdog?.turnFailed(e,s?.properties?.error)
|
|
5245
5323
|
else if("session.status"===n){const t=s?.properties?.status?.type
|
|
5246
|
-
"running"===t||"busy"===t?this.noteSessionRunning(e,!0):"idle"!==t&&"retry"!==t&&"error"!==t||this.noteSessionRunning(e,!1)}}if("server.heartbeat"===n||"server.connected"===n||(0,
|
|
5324
|
+
"running"===t||"busy"===t?this.noteSessionRunning(e,!0):"idle"!==t&&"retry"!==t&&"error"!==t||this.noteSessionRunning(e,!1)}}if("server.heartbeat"===n||"server.connected"===n||(0,de.isKeepWarmTick)(s?.properties?.sessionID??s?.properties?.info?.sessionID??s?.properties?.part?.sessionID??s?.sessionID)||this.parkController?.noteActivity(),this.handleSSEHookEvent(s).catch(e=>this.logger.warn("SSE hook event processing failed",{type:s?.type,error:(0,W.getErrorMessage)(e)})),"message.part.updated"===n&&"step-finish"===s?.properties?.part?.type){const e=String(s?.properties?.part?.sessionID??"")
|
|
5247
5325
|
if(e&&!this.usageStepTimers.has(e)){const t=setTimeout(()=>{this.usageStepTimers.delete(e),this.recordUsageForSession(e).catch(()=>{})},2e3)
|
|
5248
|
-
"function"==typeof t.unref&&t.unref(),this.usageStepTimers.set(e,t)}}if("session.idle"===n){this.flushSSEEvents().catch(e=>this.logger.debug("Immediate SSE flush failed",{error:(0,
|
|
5326
|
+
"function"==typeof t.unref&&t.unref(),this.usageStepTimers.set(e,t)}}if("session.idle"===n){this.flushSSEEvents().catch(e=>this.logger.debug("Immediate SSE flush failed",{error:(0,W.getErrorMessage)(e)})),this.recordUsageForSession(s?.properties?.sessionID||s?.sessionID||"").catch(()=>{})
|
|
5249
5327
|
const e=s?.properties?.sessionID||s?.sessionID
|
|
5250
|
-
e&&(0,
|
|
5251
|
-
e&&(0,
|
|
5328
|
+
e&&(0,K.notifySessionIdle)(e)}if("session.status"===n&&"idle"===s?.properties?.status?.type){const e=s?.properties?.sessionID||s?.sessionID
|
|
5329
|
+
e&&(0,K.notifySessionIdle)(e)}"permission.asked"===n&&this.rejectPermissionAsk(s).catch(e=>this.logger.warn("Permission auto-reject failed",{error:(0,W.getErrorMessage)(e)}))}catch{}}}}catch(e){"AbortError"===e?.name||String(e?.message).includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,W.getErrorMessage)(e)})}this.sseForwarderInterval&&!r()&&(this.logger.info("SSE forwarder reconnecting in 3s"),this.scheduleSSEReconnect(3e3))})()}catch(e){"AbortError"===e?.name||String(e?.message).includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,W.getErrorMessage)(e)}),this.sseForwarderInterval&&!r()&&this.scheduleSSEReconnect(5e3))}}async handleSSEHookEvent(e){const t=e?.type
|
|
5252
5330
|
if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
|
|
5253
5331
|
if(!t)return
|
|
5254
5332
|
this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,r=t?.id||""
|
|
5255
5333
|
if(!r)return
|
|
5256
5334
|
const s=t?.metadata
|
|
5257
5335
|
if(s?.agentType&&this.projectId){const e=t?.parentID||null
|
|
5258
|
-
this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active"}).catch(e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,
|
|
5336
|
+
this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active"}).catch(e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,W.getErrorMessage)(e)})})}setTimeout(async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:r}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:r,variables:{sessionId:r}})},500)}}async recordUsageForSession(e){if(!this.projectId||!e)return
|
|
5259
5337
|
const t=this.getOpenCodeUrl()
|
|
5260
5338
|
if(t)try{const r=await fetch(`${t}/session/${e}/message?limit=200`,{signal:AbortSignal.timeout(5e3)})
|
|
5261
5339
|
if(!r.ok)return
|
|
5262
|
-
const s=await r.json(),n=this.recordedUsageMsgs.get(e),o=(0,
|
|
5340
|
+
const s=await r.json(),n=this.recordedUsageMsgs.get(e),o=(0,dt.turnSubjects)(s),i=[]
|
|
5263
5341
|
for(const t of s){const r=t?.info
|
|
5264
5342
|
if(!r||"assistant"!==r.role||!r.id)continue
|
|
5265
5343
|
if(n&&n.has(String(r.id)))continue
|
|
5266
|
-
const{input:s,output:a,reasoning:c,cacheRead:l,cacheWrite:d,cost:u,hasUsage:h}=(0,
|
|
5344
|
+
const{input:s,output:a,reasoning:c,cacheRead:l,cacheWrite:d,cost:u,hasUsage:h}=(0,ye.tokensOfMessage)(t)
|
|
5267
5345
|
if(!h)continue
|
|
5268
5346
|
const p=!!r.time?.completed
|
|
5269
5347
|
i.push({finished:p,sessionId:e,messageId:String(r.id),agentType:r.agent??r.mode??null,provider:r.providerID??null,model:r.modelID??null,inputTokens:s,outputTokens:a,reasoningTokens:c,cacheReadTokens:l,cacheWriteTokens:d,cost:u,...r.parentID?{promptId:String(r.parentID)}:{},...r.parentID&&o.get(String(r.parentID))?{threadRef:o.get(String(r.parentID))}:{}})}if(0===i.length)return
|
|
@@ -5271,9 +5349,9 @@ await this.apiClient.post(`/api/projects/${this.projectId}/usage-events`,{events
|
|
|
5271
5349
|
let a=n
|
|
5272
5350
|
if(!a){if(a=new Set,this.recordedUsageMsgs.size>=300)for(const e of Array.from(this.recordedUsageMsgs.keys()).slice(0,50))this.recordedUsageMsgs.delete(e)
|
|
5273
5351
|
this.recordedUsageMsgs.set(e,a)}for(const e of i)e.finished&&a.add(e.messageId)
|
|
5274
|
-
this.logger.debug("Recorded session usage to remote",{sessionId:e,count:i.length})}catch(t){this.logger.debug("Usage recording failed",{sessionId:e,error:(0,
|
|
5352
|
+
this.logger.debug("Recorded session usage to remote",{sessionId:e,count:i.length})}catch(t){this.logger.debug("Usage recording failed",{sessionId:e,error:(0,W.getErrorMessage)(t)})}}async sweepUsage(e,t=8e3){const r=this.getOpenCodeUrl()
|
|
5275
5353
|
if(!this.projectId||!r)return
|
|
5276
|
-
const s=await(0,
|
|
5354
|
+
const s=await(0,lt.sweepSessionUsage)(r,e,e=>this.recordUsageForSession(e),t)
|
|
5277
5355
|
s.error?this.logger.warn("usage sweep failed",{error:s.error}):s.outOfTime&&this.logger.warn("usage sweep ran out of time; some sessions not read",{read:s.read})}async rejectPermissionAsk(e){const t=e?.properties||{},r=t?.id
|
|
5278
5356
|
if(!r)return
|
|
5279
5357
|
const s=this.getOpenCodeUrl()
|
|
@@ -5286,7 +5364,7 @@ if(!r.ok)return
|
|
|
5286
5364
|
const s=await r.json()
|
|
5287
5365
|
if(!Array.isArray(s)||0===s.length)return
|
|
5288
5366
|
this.logger.info("Sweeping pending permission asks",{count:s.length})
|
|
5289
|
-
for(const e of s)await this.rejectPermissionAsk({properties:e}).catch(t=>this.logger.warn("Pending-permission reject failed",{permissionId:e?.id,sessionId:e?.sessionID,permission:e?.permission,patterns:Array.isArray(e?.patterns)?e.patterns.join(", "):void 0,error:(0,
|
|
5367
|
+
for(const e of s)await this.rejectPermissionAsk({properties:e}).catch(t=>this.logger.warn("Pending-permission reject failed",{permissionId:e?.id,sessionId:e?.sessionID,permission:e?.permission,patterns:Array.isArray(e?.patterns)?e.patterns.join(", "):void 0,error:(0,W.getErrorMessage)(t)}))}catch(e){this.logger.debug("Pending-permission sweep failed",{error:(0,W.getErrorMessage)(e)})}}async flushSSEEvents(){if(!this.projectId)return
|
|
5290
5368
|
if(0===this.sseEventBuffer.length&&0===this.droppedEventCount)return
|
|
5291
5369
|
const e=Date.now()-3e5
|
|
5292
5370
|
let t=0
|
|
@@ -5294,36 +5372,36 @@ for(;t<this.sseEventBuffer.length&&this.sseEventBuffer[t].at<e;)t++
|
|
|
5294
5372
|
if(t>0&&(this.sseEventBuffer.splice(0,t),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=t),!this.hasRemoteEventConsumers){if(this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
|
|
5295
5373
|
this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}return}const r=this.sseEventBuffer.splice(0),s=this.droppedEventCount>0?{at:Date.now(),type:"alchemy.events.dropped",data:{count:this.droppedEventCount,firstDropAt:this.firstDropAt,reportedAt:Date.now()}}:null
|
|
5296
5374
|
s&&r.unshift(s)
|
|
5297
|
-
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:r}),s&&(this.logger.warn("Reported dropped SSE events to remote",{count:s.data.count,firstDropAt:s.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(e){this.logger.debug("SSE event flush failed",{error:(0,
|
|
5375
|
+
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:r}),s&&(this.logger.warn("Reported dropped SSE events to remote",{count:s.data.count,firstDropAt:s.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(e){this.logger.debug("SSE event flush failed",{error:(0,W.getErrorMessage)(e),count:r.length})
|
|
5298
5376
|
const t=s?r.slice(1):r
|
|
5299
5377
|
if(this.sseEventBuffer.unshift(...t),this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
|
|
5300
5378
|
this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce(),this.remoteLastOkAt=Date.now(),this.remoteFailures=0,this.remoteLastError=null}catch(e){if(!this.isPolling)return
|
|
5301
|
-
this.remoteFailures+=1,this.remoteLastError=(0,
|
|
5302
|
-
const e=(await(0,
|
|
5379
|
+
this.remoteFailures+=1,this.remoteLastError=(0,W.getErrorMessage)(e),this.logger.debug("Long-poll error, retrying in 5s",{error:(0,W.getErrorMessage)(e)}),await new Promise(e=>setTimeout(e,5e3))}}async readMainSha(){try{this.mainBranchName||(this.mainBranchName=await(0,m.resolveDefaultBranch)(this.options.projectPath))
|
|
5380
|
+
const e=(await(0,He.simpleGit)(this.options.projectPath).revparse([this.mainBranchName])).trim()
|
|
5303
5381
|
return/^[0-9a-f]{40}$/.test(e)?e:null}catch{return this.mainBranchName=null,null}}async pollOnce(){if(this.isPolling&&this.projectId&&!this.updateBlocked)try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=30000`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
5304
|
-
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,
|
|
5382
|
+
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,nt.machineCallerHeaders)(this.options.remoteServerUrl)),this.clientVersion&&(t["X-Client-Version"]=this.clientVersion),this.opencodeVersion&&(t["X-OpenCode-Version"]=this.opencodeVersion),this.port&&(t["X-Action-Server-Port"]=String(this.port)),t["X-Client-Action-Types"]=ie
|
|
5305
5383
|
const r=await this.readMainSha()
|
|
5306
5384
|
r&&(t["X-Main-Sha"]=r)
|
|
5307
5385
|
const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
|
|
5308
5386
|
if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
|
|
5309
5387
|
const n=await s.json(),o={success:!0,data:n.data||n}
|
|
5310
5388
|
if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,anthropicCompaction:s,defaultModelUnchosen:n}=o.data.settings
|
|
5311
|
-
"boolean"==typeof n&&(this.defaultModelUnchosen=n),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r),"boolean"==typeof s&&await this.applyAnthropicCompaction(s)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new
|
|
5312
|
-
this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,
|
|
5389
|
+
"boolean"==typeof n&&(this.defaultModelUnchosen=n),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r),"boolean"==typeof s&&await this.applyAnthropicCompaction(s)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new x.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,nt.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,T.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
|
|
5390
|
+
this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,W.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
|
|
5313
5391
|
if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
|
|
5314
5392
|
this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
|
|
5315
5393
|
for(const t of e)this.scheduleRunner?.nudge(t).catch(e=>{this.logger.warn("Schedule nudge dispatch failed",{docId:t,error:String(e)})})}o.success&&Array.isArray(o.data?.versionWarnings)&&this.applyVersionWarnings(o.data.versionWarnings),o.success&&o.data?.actions?.length>0&&this.dispatchActions(o.data.actions)}catch(e){throw e}}dispatchActions(e){const t=[],r=[]
|
|
5316
|
-
for(const s of e)
|
|
5317
|
-
for(const e of t)this.inFlightProxyActions++,this.processAction(e).catch(t=>this.logger.warn("Proxy action failed",{actionId:e.id,error:(0,
|
|
5394
|
+
for(const s of e)ut.has(s.type)?t.push(s):r.push(s)
|
|
5395
|
+
for(const e of t)this.inFlightProxyActions++,this.processAction(e).catch(t=>this.logger.warn("Proxy action failed",{actionId:e.id,error:(0,W.getErrorMessage)(t)})).finally(()=>{this.inFlightProxyActions--})
|
|
5318
5396
|
let s=0
|
|
5319
5397
|
for(const e of r)this.queuedRegularActionIds.has(e.id)?this.logger.debug("Skipping re-delivered action already held locally",{actionId:e.id,type:e.type}):(this.queuedRegularActionIds.add(e.id),this.pendingRegularActions.push(e),s++)
|
|
5320
5398
|
s>0&&this.drainRegularActions()}async drainRegularActions(){if(!this.isProcessingAction){this.isProcessingAction=!0
|
|
5321
5399
|
try{for(;this.isPolling;){const e=this.pendingRegularActions.shift()
|
|
5322
5400
|
if(!e)break
|
|
5323
|
-
try{await this.processAction(e)}catch(t){this.logger.warn("Action processing failed",{actionId:e.id,type:e.type,error:(0,
|
|
5401
|
+
try{await this.processAction(e)}catch(t){this.logger.warn("Action processing failed",{actionId:e.id,type:e.type,error:(0,W.getErrorMessage)(t)})}finally{this.queuedRegularActionIds.delete(e.id)}}}finally{for(const e of this.pendingRegularActions)this.queuedRegularActionIds.delete(e.id)
|
|
5324
5402
|
this.pendingRegularActions.length=0,this.isProcessingAction=!1}}}async syncProjectSettings(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}`)
|
|
5325
5403
|
if(e.success&&e.data?.metadata){const{defaultModel:t}=e.data.metadata
|
|
5326
|
-
this.cachedReceipts=e.data.metadata.receipts??null,this.cachedIntegrations=e.data.metadata.integrations??null,void 0!==t&&this.updateDefaultModel(t)}}catch(e){this.logger.debug("Settings sync failed",{error:(0,
|
|
5404
|
+
this.cachedReceipts=e.data.metadata.receipts??null,this.cachedIntegrations=e.data.metadata.integrations??null,void 0!==t&&this.updateDefaultModel(t)}}catch(e){this.logger.debug("Settings sync failed",{error:(0,W.getErrorMessage)(e)})}}async sweepOrphanTestRuns(e){try{const r=(await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/test-runs?limit=100&offset=${e}`,500)).filter(t=>{if(!t||"pending"!==t.status&&"running"!==t.status)return!1
|
|
5327
5405
|
const r=Date.parse(t.createdAt??t.created_at??"")
|
|
5328
5406
|
return Number.isFinite(r)&&r<e})
|
|
5329
5407
|
if(0===r.length)return
|
|
@@ -5331,22 +5409,22 @@ const s=new Map
|
|
|
5331
5409
|
for(const e of r){const t=e.requestId??e.request_local_id
|
|
5332
5410
|
t&&e.id&&s.set(t,[...s.get(t)??[],e.id])}const n={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},{handleRunTestsAction:o}=await Promise.resolve().then(()=>t(76555))
|
|
5333
5411
|
for(const[e,t]of s){this.logger.warn("Re-dispatching test run(s) orphaned by restart",{requestId:e,testRunIds:t})
|
|
5334
|
-
try{await o({requestId:e,testRunIds:t,mode:"auto"},n)}catch(t){this.logger.warn("Orphan test-run re-dispatch failed (stuck-run watchdog will backstop)",{requestId:e,error:(0,
|
|
5412
|
+
try{await o({requestId:e,testRunIds:t,mode:"auto"},n)}catch(t){this.logger.warn("Orphan test-run re-dispatch failed (stuck-run watchdog will backstop)",{requestId:e,error:(0,W.getErrorMessage)(t)})}}}catch(e){this.logger.warn("Orphan test-run sweep failed (non-fatal)",{error:(0,W.getErrorMessage)(e)})}}async processAction(e){this.parkController?.noteActivity(),this.logger.info("Processing action",{actionId:e.id,type:e.type})
|
|
5335
5413
|
const t=Date.now()
|
|
5336
5414
|
let r
|
|
5337
|
-
try{const s={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},n=await(0,
|
|
5338
|
-
r={success:n.success,result:n.refused?{...n.data??{},refused:!0}:n.data,error:n.message||n.error,duration:Date.now()-t}}catch(e){r={success:!1,error:(0,
|
|
5339
|
-
try{await this.apiClient.post(`/api/actions/${e.id}/result`,n),r.success?(this.logger.info("Action processed",{actionId:e.id,type:e.type,success:!0,duration:r.duration}),"prune-worktree"===e.type&&this.sweepUsage(Date.now()-216e5).catch(()=>{})):r.result?.refused?this.logger.info("Action refused (by design)",{actionId:e.id,type:e.type,why:r.error}):this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r.error})}catch(t){this.logger.error("Failed to POST action result after retries; posting it again later",{actionId:e.id,type:e.type,workSucceeded:r.success,postError:(0,
|
|
5340
|
-
if(!await(0,
|
|
5415
|
+
try{const s={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},n=await(0,F.executeAction)(e.type,e.payload||{},s)
|
|
5416
|
+
r={success:n.success,result:n.refused?{...n.data??{},refused:!0}:n.data,error:n.message||n.error,duration:Date.now()-t}}catch(e){r={success:!1,error:(0,W.getErrorMessage)(e),duration:Date.now()-t}}const n={...r,claimToken:e.claimToken}
|
|
5417
|
+
try{await this.apiClient.post(`/api/actions/${e.id}/result`,n),r.success?(this.logger.info("Action processed",{actionId:e.id,type:e.type,success:!0,duration:r.duration}),"prune-worktree"===e.type&&this.sweepUsage(Date.now()-216e5).catch(()=>{})):r.result?.refused?this.logger.info("Action refused (by design)",{actionId:e.id,type:e.type,why:r.error}):this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r.error})}catch(t){this.logger.error("Failed to POST action result after retries; posting it again later",{actionId:e.id,type:e.type,workSucceeded:r.success,postError:(0,W.getErrorMessage)(t)}),(0,s.redeliverResult)({post:()=>this.apiClient.post(`/api/actions/${e.id}/result`,n)}).then(t=>this.logger[t.delivered?"info":"error"](t.delivered?"Action result posted on a later try":"Action result never reached the remote",{actionId:e.id,type:e.type,tries:t.tries,...t.delivered?{}:{postError:(0,W.getErrorMessage)(t.lastError)}}))}}startLinearAutoImportPoller(){const e=async()=>{try{if(!this.projectId)return
|
|
5418
|
+
if(!await(0,U.readLinearKey)())return
|
|
5341
5419
|
await this.syncProjectSettings().catch(()=>{})
|
|
5342
5420
|
const e=this.cachedIntegrations?.linear?.autoImport
|
|
5343
5421
|
if(!0!==e?.enabled)return
|
|
5344
|
-
const t=e?.label?.trim()||"alchemylab",r=e?.team?.trim()||void 0,s=await(0,
|
|
5422
|
+
const t=e?.label?.trim()||"alchemylab",r=e?.team?.trim()||void 0,s=await(0,U.listLinearIssuesByLabel)(t,20,r)
|
|
5345
5423
|
if(0===s.length)return
|
|
5346
|
-
const n={projectId:this.projectId,apiClient:this.apiClient},o=await(0,
|
|
5347
|
-
for(const e of a)try{const t=await(0,
|
|
5348
|
-
this.logger.info("linear auto-import: filed a draft",{issue:e.identifier,requestId:t})}catch(t){if(t instanceof
|
|
5349
|
-
this.logger.warn("linear auto-import: issue not imported",{issue:e.identifier,error:(0,
|
|
5424
|
+
const n={projectId:this.projectId,apiClient:this.apiClient},o=await(0,B.knownLinearIssues)(n,s.map(e=>e.id)),i=new Set([...Object.keys(o.imported),...o.failed]),a=s.filter(e=>!i.has(e.id)).slice(0,3)
|
|
5425
|
+
for(const e of a)try{const t=await(0,B.importLinearIssue)(n,e,{auto:!0})
|
|
5426
|
+
this.logger.info("linear auto-import: filed a draft",{issue:e.identifier,requestId:t})}catch(t){if(t instanceof B.AlreadyImportedError)continue
|
|
5427
|
+
this.logger.warn("linear auto-import: issue not imported",{issue:e.identifier,error:(0,W.getErrorMessage)(t)}),await this.apiClient.post(`/api/projects/${this.projectId}/integrations/linear/import-failures`,{issueId:e.id,issueKey:e.identifier,title:e.title,error:(0,W.getErrorMessage)(t)}).catch(t=>this.logger.warn("linear auto-import: the failure could not be recorded; it is tried again next tick",{issue:e.identifier,error:(0,W.getErrorMessage)(t)}))}}catch(e){this.logger.warn("linear auto-import tick failed",{error:(0,U.linearErrorInWords)(e)})}},t=setInterval(()=>{e()},6e4)
|
|
5350
5428
|
"function"==typeof t.unref&&t.unref()}startExperimentReaper(){const e=async()=>{try{const e=this.getOpenCodeUrl()
|
|
5351
5429
|
if(!e)return
|
|
5352
5430
|
const r=await fetch(`${e}/session?limit=1000`,{signal:AbortSignal.timeout(5e3)})
|
|
@@ -5365,40 +5443,40 @@ const t=await e.json(),r=t?.data?.frontendUrl
|
|
|
5365
5443
|
"string"==typeof r&&/^https?:\/\//.test(r)&&(this.frontendOrigin=new URL(r).origin,this.logger.info("Browser requests allowed from the app origin",{origin:this.frontendOrigin}))}catch{}})()
|
|
5366
5444
|
const r=await(0,S.ensureSoleInstance)({projectPath:this.options.projectPath,logger:this.logger,retire:this.options.retireTwins})
|
|
5367
5445
|
if(!r.ok)throw this.logger.error("Refusing to start: "+r.reason),Object.assign(new Error(r.reason),{runningElsewhere:!0===r.runningElsewhere})
|
|
5368
|
-
try{await(0,I.migrateWorkCopies)(this.options.projectPath,this.logger)}catch(e){this.logger.warn("Working copies could not be moved out of the project folder (they stay where they are)",{error:(0,
|
|
5446
|
+
try{await(0,I.migrateWorkCopies)(this.options.projectPath,this.logger)}catch(e){this.logger.warn("Working copies could not be moved out of the project folder (they stay where they are)",{error:(0,W.getErrorMessage)(e)})}this.workCopiesGate.resolve()
|
|
5369
5447
|
for(let e=0;e<50;e++)try{await new Promise((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",()=>{this.logger.info("HTTP server started",{port:this.port}),(0,S.writeInstanceLock)(this.options.projectPath,this.port),e()}),this.server.on("error",e=>{e.code,t(e)})}catch(e){t(e)}})
|
|
5370
5448
|
break}catch(t){if("EADDRINUSE"===t?.code&&e<49){if(0===e&&!1!==this.options.retireTwins&&await(0,S.askStaleTwinToRetire)({port:this.port,projectPath:this.options.projectPath,logger:this.logger})){this.logger.info("Stale twin retired; retrying our preferred port",{port:this.port})
|
|
5371
5449
|
continue}this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
|
|
5372
|
-
continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)}try{const e=
|
|
5450
|
+
continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)}try{const e=D.default.join(this.options.projectPath,".alchemy_lab","action-server.json");(0,re.writeActionServerFile)(this.options.projectPath,this.port,this.localSecret),setTimeout(()=>{this.requestConfigReload("agents ready").catch(()=>{})},4e3),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,W.getErrorMessage)(e)})}(0,C.setPreviewSelfBaseUrl)(`http://localhost:${this.port}`)
|
|
5373
5451
|
try{const e=this.readProjectConfig()
|
|
5374
5452
|
if(e?.id){const{upsertEntry:r}=await Promise.resolve().then(()=>t(52640))
|
|
5375
|
-
r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,
|
|
5376
|
-
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0
|
|
5453
|
+
r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,W.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,W.getErrorMessage)(e)
|
|
5454
|
+
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,$e.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
|
|
5377
5455
|
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}this.startExperimentReaper(),this.startLinearAutoImportPoller()
|
|
5378
5456
|
try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=0&settingsOnly=true`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
5379
|
-
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,
|
|
5457
|
+
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,nt.machineCallerHeaders)(this.options.remoteServerUrl))
|
|
5380
5458
|
const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
|
|
5381
5459
|
if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
|
|
5382
|
-
"boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,
|
|
5460
|
+
"boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,W.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Le.setContainerProject)(this.projectId),this.startSSEEventForwarder()
|
|
5383
5461
|
try{const{resumeInboxChatWatches:e}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
5384
|
-
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,
|
|
5385
|
-
t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,
|
|
5462
|
+
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,W.getErrorMessage)(e)})}this.apiClient.post(`/api/projects/${this.projectId}/session-messages/expire-orphaned-approvals`,{}).then(e=>{const t=e?.data?.expired??0
|
|
5463
|
+
t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,W.getErrorMessage)(e)})}),this.startStuckSessionWatchdog(),this.startScheduleRunner(),(0,we.ensureOpsDiagnostics)(this.apiClient,this.projectId,this.options.projectPath,this.logger),(0,ce.startSessionCompactor)({getOpenCodeUrl:()=>this.getOpenCodeUrl()??void 0,apiClient:this.apiClient,projectId:this.projectId||void 0,projectName:this.options.projectPath?D.default.basename(this.options.projectPath):void 0,logger:this.logger}),this.keepWarmRunner=(0,de.startKeepWarmRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,getDefaultModel:()=>this.cachedDefaultModel}),this.ownerGuidanceTimer=setInterval(()=>{this.checkOwnerGuidanceApply()},6e4),this.ownerGuidanceTimer.unref?.(),this.workMetricsRunner||(this.workMetricsRunner=(0,pe.startWorkMetricsRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})),this.mcpConnectionRunner||(this.mcpConnectionRunner=(0,fe.startMcpConnectionRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")},isBusy:async()=>this.activeMcpCalls.size>0||await this.anySessionBusy()})),(0,le.startMonitorRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,serveReady:async()=>(await this.ensureServeAlive("monitor-wake"),null!=this.openCodePort&&!this.parkController?.isServeDown()),reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},runOwn:e=>this.runOwn(e),verifyDeliveryEnv:async e=>{let r
|
|
5386
5464
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/environments/${e}`)
|
|
5387
5465
|
r=t?.data||t}catch{throw new Error("environment fetch failed")}const s=r?.config?.delivery
|
|
5388
5466
|
if(!r||!s?.targetPath)return{gone:!0,reason:"environment or its delivery config no longer exists"}
|
|
5389
5467
|
const{verifyDelivery:n}=await Promise.resolve().then(()=>t.t(32504,23)),o=await n(s)
|
|
5390
|
-
return{healthy:o.healthy,verified:!0===o.verified,failing:(o.checks||[]).filter(e=>!e.pass).map(e=>e.name).concat(!1===o.healthy?["health"]:[])}},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}}}).catch(e=>{this.logger.error("Monitor fleet-health report failed to post",{error:(0,
|
|
5468
|
+
return{healthy:o.healthy,verified:!0===o.verified,failing:(o.checks||[]).filter(e=>!e.pass).map(e=>e.name).concat(!1===o.healthy?["health"]:[])}},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}}}).catch(e=>{this.logger.error("Monitor fleet-health report failed to post",{error:(0,W.getErrorMessage)(e)})})}})}}startParkController(){if(this.parkController)return
|
|
5391
5469
|
if(!this.relaunchOpenCode)return
|
|
5392
5470
|
const e=this.options.onIdlePark
|
|
5393
|
-
this.parkController=new
|
|
5394
|
-
return{idle:e,warm:(0,
|
|
5471
|
+
this.parkController=new z.ParkController({logger:this.logger,isIdle:()=>this.isProjectIdle(),park:()=>e?(e(),[]):this.executePark(),revive:()=>this.executeRevive()}),this.parkController.start()}parkVeto(e){return this.lastParkVetoes=[e],!1}async idleReport(){const e=await this.isProjectIdle({ignoreWarm:!0})
|
|
5472
|
+
return{idle:e,warm:(0,de.keepWarmHoldsUntil)()>Date.now(),lastActivityAt:Date.now()-(this.parkController?.idleForMs()??0),...e?{}:{veto:this.lastParkVetoes[0]}}}async isProjectIdle(e={}){if(this.updateBlocked)return this.parkVeto("update-mode")
|
|
5395
5473
|
if(this.isProcessingAction||this.inFlightProxyActions>0)return this.parkVeto("processing-action")
|
|
5396
5474
|
if(this.inFlightServeProxyRequests>0)return this.parkVeto("serve-proxy-in-flight")
|
|
5397
5475
|
if(this.hasRemoteEventConsumers)return this.parkVeto("ui-event-consumers")
|
|
5398
5476
|
if(this.activeMcpCalls.size>0||this.pendingConfigReload)return this.parkVeto("mcp-or-config-activity")
|
|
5399
|
-
if((0,
|
|
5400
|
-
if((0,
|
|
5401
|
-
if((0,
|
|
5477
|
+
if((0,Ne.anyPoolActivity)())return this.parkVeto("pool-activity")
|
|
5478
|
+
if((0,_e.listPendingApprovals)().length>0||(0,Te.listPendingPaintRequests)().length>0)return this.parkVeto("pending-approvals")
|
|
5479
|
+
if((0,G.listRunningExecs)().length>0)return this.parkVeto("running-execs")
|
|
5402
5480
|
if((0,C.listPreviewHandles)().length>0)return this.parkVeto("preview-handles")
|
|
5403
5481
|
if(this.projectId&&(0,j.experimentRegistry)().countForProject(this.projectId)>0)return this.parkVeto("experiments")
|
|
5404
5482
|
if(this.scheduleRunner?.hasInFlightFirings())return this.parkVeto("schedule-firing")
|
|
@@ -5407,55 +5485,55 @@ for(const e of[...this.trackedChildPids])if(e!==r)try{return process.kill(e,0),t
|
|
|
5407
5485
|
this.trackedChildPids.delete(e)}const{hasActiveInboxChatTurns:s}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
5408
5486
|
if(s())return this.parkVeto("inbox-chat-turn")
|
|
5409
5487
|
const{hasParkVetoingPlannerHandoffWatches:n}=await t.e(991).then(()=>t(18991))
|
|
5410
|
-
return n()?this.parkVeto("planner-handoff"):await this.anySessionBusy()?this.parkVeto("session-busy"):!e.ignoreWarm&&(0,
|
|
5488
|
+
return n()?this.parkVeto("planner-handoff"):await this.anySessionBusy()?this.parkVeto("session-busy"):!e.ignoreWarm&&(0,de.keepWarmHoldsUntil)()>Date.now()?this.parkVeto("keep-warm-window"):(this.lastParkVetoes=[],!0)}executePark(){const e=new Set(this.trackedChildPids),t=this.readServePid()
|
|
5411
5489
|
t&&e.add(t),this.killTrackedProcesses()
|
|
5412
|
-
try{const e=
|
|
5413
|
-
if(
|
|
5414
|
-
delete t.pid,delete t.port,t.parkedAt=(new Date).toISOString(),
|
|
5490
|
+
try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5491
|
+
if(q.default.existsSync(e)){const t=JSON.parse(q.default.readFileSync(e,"utf-8"))
|
|
5492
|
+
delete t.pid,delete t.port,t.parkedAt=(new Date).toISOString(),q.default.writeFileSync(e,JSON.stringify(t,null,2))}}catch{}return this.stopSSEEventForwarder(),[...e]}async executeRevive(){if(!this.relaunchOpenCode)throw new Error("no serve relaunch path (serve-only mode)")
|
|
5415
5493
|
const e=await this.relaunchOpenCode()
|
|
5416
5494
|
if(this.parkController?.isStopped()||this.updateBlocked)throw e.pid&&this.killProcessGroup(e.pid),new Error("revival aborted: server stopping or in update mode")
|
|
5417
5495
|
if(e.pid&&this.trackChildPid(e.pid),!e.success||!e.port)throw e.pid&&(this.killProcessGroup(e.pid),this.trackedChildPids.delete(e.pid)),new Error(e.success?"OpenCode serve relaunch did not announce a port":e.error||"OpenCode serve relaunch failed")
|
|
5418
5496
|
this.setOpenCodePort(e.port)
|
|
5419
|
-
try{const t=
|
|
5420
|
-
|
|
5421
|
-
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}}async ensureServeAlive(e){if(this.parkController&&!this.updateBlocked)try{await this.parkController.ensureAlive()}catch(t){this.logger.warn("OpenCode serve revival failed",{context:e,error:(0,
|
|
5422
|
-
t&&await Promise.race([t.catch(()=>{}),new Promise(t=>{setTimeout(t,e).unref?.()})])}killProcessGroup(e){try{process.kill(-e,"SIGTERM")}catch(t){if("ESRCH"!==t?.code)try{process.kill(e,"SIGTERM")}catch{}}}readServePid(){try{const e=
|
|
5423
|
-
if(!
|
|
5424
|
-
const t=JSON.parse(
|
|
5425
|
-
return"number"==typeof t.pid?t.pid:void 0}catch{return}}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,
|
|
5426
|
-
try{const e=
|
|
5427
|
-
if(
|
|
5428
|
-
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,
|
|
5497
|
+
try{const t=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5498
|
+
q.default.writeFileSync(t,JSON.stringify({port:e.port,pid:e.pid,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("Could not write opencode.json after revival",{error:(0,W.getErrorMessage)(e)})}if(this.startSSEEventForwarder(),this.ownerGuidanceStale&&this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance after waking failed; it stays pending",{error:(0,W.getErrorMessage)(e)})),this.projectId){const e=Date.now()
|
|
5499
|
+
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}}async ensureServeAlive(e){if(this.parkController&&!this.updateBlocked)try{await this.parkController.ensureAlive()}catch(t){this.logger.warn("OpenCode serve revival failed",{context:e,error:(0,W.getErrorMessage)(t)})}}async awaitInFlightRevival(e=15e3){const t=this.parkController?.getInFlightRevival()
|
|
5500
|
+
t&&await Promise.race([t.catch(()=>{}),new Promise(t=>{setTimeout(t,e).unref?.()})])}killProcessGroup(e){try{process.kill(-e,"SIGTERM")}catch(t){if("ESRCH"!==t?.code)try{process.kill(e,"SIGTERM")}catch{}}}readServePid(){try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5501
|
+
if(!q.default.existsSync(e))return
|
|
5502
|
+
const t=JSON.parse(q.default.readFileSync(e,"utf-8"))
|
|
5503
|
+
return"number"==typeof t.pid?t.pid:void 0}catch{return}}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,W.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
5504
|
+
try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5505
|
+
if(q.default.existsSync(e)){const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
5506
|
+
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,W.getErrorMessage)(e)})}}}}catch{}}async runOwn(e){return(0,E.makeRunOwn)(this.port,this.options.projectPath)(e)}startScheduleRunner(){this.scheduleRunner=new ae.ScheduleRunner({apiClient:this.apiClient,logger:this.logger,getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath,noteActivity:()=>this.parkController?.noteActivity(),ensureServe:()=>this.ensureServeAlive("schedule-firing"),runOwn:e=>this.runOwn(e)}),this.scheduleRunner.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new J.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,reloadServe:e=>{this.requestConfigReload(e)},logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),deliver:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),i=await o({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},e,r,{kind:"inserted-prompt",...s?{partPayload:{requestId:s,nudge:!0,...n?.restart?{restart:!0}:{}}}:{}})
|
|
5429
5507
|
return i.ok||this.logger.warn("watchdog nudge not delivered",{sessionId:e,requestId:s,error:i.error,detail:i.detail}),i.ok},getAgentModel:e=>this.configurer.agentModelOf(e),isAgentModelPinned:e=>this.configurer.agentPinnedOf(e),resumeHubTurn:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),{trackInboxChatTurn:i,resumeStalledWatchedTurn:a}=await Promise.resolve().then(()=>t.t(86116,23)),c={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel}
|
|
5430
5508
|
if(n?.endTurn){const t=this.getOpenCodeUrl()?.replace(/\/$/,""),s=async()=>{if(!t)return
|
|
5431
5509
|
const r=await(0,d.endOpenTurn)(t,e)
|
|
5432
5510
|
"ended"!==r&&this.logger.warn("Stalled turn not ended before its resume",{sessionId:e,kind:r}),await new Promise(e=>setTimeout(e,1e3))},n=await a(e,r,s)
|
|
5433
5511
|
if(null!==n)return n
|
|
5434
5512
|
await s()}const l=await o(c,e,r,{kind:"inserted-prompt",partPayload:{nudge:!0}})
|
|
5435
|
-
return l.ok&&l.messageId?(i(c,e,Date.now(),l.messageId,void 0,void 0,void 0,s),!0):(this.logger.warn("Alchemist resume not delivered",{sessionId:e,error:l.error,detail:l.detail}),!1)},recordProviderBlock:async(e,t,r,s,n)=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block`,{providerID:e,resetsAt:t,planType:r,reason:s,...n?{stoppedSessionId:n}:{}})}catch(t){this.logger.warn("provider cap not recorded (the report still went out)",{providerID:e,error:(0,
|
|
5513
|
+
return l.ok&&l.messageId?(i(c,e,Date.now(),l.messageId,void 0,void 0,void 0,s),!0):(this.logger.warn("Alchemist resume not delivered",{sessionId:e,error:l.error,detail:l.detail}),!1)},recordProviderBlock:async(e,t,r,s,n)=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block`,{providerID:e,resetsAt:t,planType:r,reason:s,...n?{stoppedSessionId:n}:{}})}catch(t){this.logger.warn("provider cap not recorded (the report still went out)",{providerID:e,error:(0,W.getErrorMessage)(t)})}},clearCapStopped:async e=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block/resumed`,{sessionIds:e})}catch(t){this.logger.warn("could not clear cap-stopped sessions on the record",{sessionIds:e,error:(0,W.getErrorMessage)(t)})}},onStuckResolved:e=>this.handleStuckSessionResolved(e),getSessionAgentType:async e=>{if(!this.projectId)return null
|
|
5436
5514
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t
|
|
5437
|
-
return"string"==typeof r?.agentType&&r.agentType?r.agentType:null}catch{return null}},hasLiveCommand:e=>(0,
|
|
5515
|
+
return"string"==typeof r?.agentType&&r.agentType?r.agentType:null}catch{return null}},hasLiveCommand:e=>(0,G.listRunningExecs)().some(t=>t.sessionId===e)||(0,$.listEntries)().some(t=>t.sessionId===e&&"running"===t.status),getSessionSettled:async e=>{if(!this.projectId)return null
|
|
5438
5516
|
const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}/settled`),r=t?.data??t
|
|
5439
5517
|
return"boolean"==typeof r?.settled?{settled:r.settled,..."string"==typeof r.why?{why:r.why}:{}}:null},getPendingQuestionAskerIds:async()=>{const e=new Set
|
|
5440
5518
|
if(!this.projectId)return e
|
|
5441
5519
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?messageType=question&status=pending&limit=100`)
|
|
5442
|
-
for(const r of t?.data||t||[]){if(!(0,
|
|
5520
|
+
for(const r of t?.data||t||[]){if(!(0,J.isWaitDeclaringQuestion)(r))continue
|
|
5443
5521
|
const t=r?.fromSessionId||r?.from_external_session_id
|
|
5444
5522
|
"string"==typeof t&&t&&e.add(t)}const r=await this.apiClient.get(`/api/projects/${this.projectId}/monitors?status=armed`)
|
|
5445
5523
|
for(const t of r?.data||[]){const r=t?.owner_external_session_id
|
|
5446
|
-
"string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,
|
|
5524
|
+
"string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,W.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,W.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
|
|
5447
5525
|
try{const o=await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask`,{sessionId:t,detail:r,...s?{refusedModel:s}:{},...n?{refusedPinned:!0}:{}}),i=o?.data??o
|
|
5448
|
-
return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,
|
|
5526
|
+
return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,W.getErrorMessage)(t)}),!1}},getStrandedCandidates:async()=>{if(!this.projectId)return[]
|
|
5449
5527
|
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/stranded-candidates`),t=e?.data||e
|
|
5450
5528
|
return Array.isArray(t?.candidates)?t.candidates:[]}catch{return[]}},getRequestWaitState:async e=>{if(!this.projectId)return null
|
|
5451
5529
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests/${encodeURIComponent(e)}/wait-state`)
|
|
5452
|
-
return(0,
|
|
5530
|
+
return(0,J.waitFactsFromRemote)(t?.data||t)}catch{return null}},fetchRecentReportDedupKeys:async()=>{if(!this.projectId)return[]
|
|
5453
5531
|
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages/report-dedup-keys`),t=e?.data??e
|
|
5454
5532
|
return Array.isArray(t)?t.filter(e=>"string"==typeof e&&!!e):[]}catch{return[]}},getOwnedRequestForSession:async e=>{if(!this.projectId)return null
|
|
5455
5533
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-request-associations/by-session/${encodeURIComponent(e)}?activeOnly=true`),r=(Array.isArray(t?.data)?t.data:Array.isArray(t)?t:[]).map(e=>e?.requestLocalId??e?.request_local_id).filter(e=>"string"==typeof e&&/^req\d{6}$/.test(e))
|
|
5456
5534
|
return r.length>0?r[r.length-1]:null}catch{return null}},getLiveRequestLocalIds:async()=>{if(!this.projectId)return new Set
|
|
5457
5535
|
const e=await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/requests?inFlight=true&limit=100&offset=${e}`),t=await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/requests?heldAtIntake=true&limit=100&offset=${e}`)
|
|
5458
|
-
return(0,
|
|
5536
|
+
return(0,J.requestsNotFinished)(e,t,A.TERMINAL_REQUEST_STATUSES)}}),this.stuckSessionWatchdog.start(),this.serveStartedAt&&this.stuckSessionWatchdog.serveStarted(this.serveStartedAt)}handleStuckSessionDetected(e){this.sseEventBuffer.push({at:Date.now(),type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({at:Date.now(),type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
|
|
5459
5537
|
t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
|
|
5460
5538
|
const{sessionId:r,title:s}=e.body||{},n=this.stuckSessionWatchdog.injectTestStuck(r,s)
|
|
5461
5539
|
this.logger.info("Test-injected stuck session",{sessionId:n.sessionId}),t.json({success:!0,data:n})}async handleRetryStuckSession(e,t){const{sessionId:r}=e.params
|
|
@@ -5464,13 +5542,13 @@ const s=await this.stuckSessionWatchdog.retrySession(r)
|
|
|
5464
5542
|
s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
|
|
5465
5543
|
for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
|
|
5466
5544
|
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
5467
|
-
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,
|
|
5545
|
+
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,et.closeExhibitPreviewBrowser)()
|
|
5468
5546
|
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
|
|
5469
|
-
try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,
|
|
5470
|
-
try{const e=
|
|
5471
|
-
|
|
5547
|
+
try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,W.getErrorMessage)(e)})}this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0),this.stopPendingReloadRecheck()
|
|
5548
|
+
try{const e=D.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
5549
|
+
q.default.existsSync(e)&&q.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(()=>t(52640))
|
|
5472
5550
|
e(this.projectId)}catch{}if(this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.runningReportInterval&&(clearInterval(this.runningReportInterval),this.runningReportInterval=void 0),this.runningReportDebounce&&(clearTimeout(this.runningReportDebounce),this.runningReportDebounce=void 0),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.registryHeartbeatInterval&&(clearInterval(this.registryHeartbeatInterval),this.registryHeartbeatInterval=void 0),this.vanishedSessionSweepInterval&&(clearInterval(this.vanishedSessionSweepInterval),this.vanishedSessionSweepInterval=void 0),this.planUsageInterval&&(clearInterval(this.planUsageInterval),this.planUsageInterval=void 0),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}await new Promise(e=>{if(this.server){const t=setTimeout(()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()},1e4)
|
|
5473
|
-
this.server.close(()=>{clearTimeout(t),e()})}else e()}),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=
|
|
5551
|
+
this.server.close(()=>{clearTimeout(t),e()})}else e()}),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=pt,pt.JUDGE_APPROVAL_TTL_MS=864e5,pt.PROJECT_CONFIG_TTL=3e4,pt.REMOTE_STALE_MS=9e4,pt.PROMPT_DELIVERY_BUSY_MS=2e4,pt.RELOAD_RECHECK_MS=12e3,pt.RELOAD_DEFER_CAP_MS=3e5},61292(e,exports,t){"use strict"
|
|
5474
5552
|
function r(e,t){const r=String(e||"").replace(/\s+/g," ").trim().split(/(?<=[.!?])\s+/),s=/^(research only|read[- ]only|no edits?|do not|don't|never|only )/i,n=(r.find(e=>e.length>8&&!s.test(e.trim()))??r[0]??"").replace(/[.!?]+$/,"").trim()
|
|
5475
5553
|
return n.length>t?`${n.slice(0,t)}...`:n}async function s(e,t){const r=v.get(t)
|
|
5476
5554
|
if(r&&Date.now()-r.at<w)return{model:r.model,reason:r.model?"ok":"unconfigured"}
|
|
@@ -5794,7 +5872,12 @@ a&&(s.atMs=a)
|
|
|
5794
5872
|
const c=i.providerID??i.model?.providerID,l=i.modelID??i.model?.modelID
|
|
5795
5873
|
c&&(s.providerID=String(c)),l&&(s.modelID=String(l))
|
|
5796
5874
|
const d=i.tokens
|
|
5797
|
-
d&&(s.contextTokens=d.total||(d.input??0)+(d.output??0)+(d.cache?.read??0)+(d.cache?.write??0))}catch{}return s},exports.
|
|
5875
|
+
d&&(s.contextTokens=d.total||(d.input??0)+(d.output??0)+(d.cache?.read??0)+(d.cache?.write??0))}catch{}return s},exports.firstCallAfter=async function(e,t,r,s=fetch){try{const n=await s(`${e.replace(/\/$/,"")}/session/${t}/message?limit=30`,{signal:AbortSignal.timeout(6e3)}),o=n.ok?await n.json():[]
|
|
5876
|
+
for(const e of Array.isArray(o)?o:[]){const t=e?.info
|
|
5877
|
+
if("assistant"!==t?.role||!t.time?.completed)continue
|
|
5878
|
+
if(Number(t.time?.created??0)<r)continue
|
|
5879
|
+
const s=t.tokens??{},n=Number(s.input??0),o=Number(s.cache?.read??0),i=Number(s.cache?.write??0),a=n+o+i
|
|
5880
|
+
return{atMs:Number(t.time.created),input:n,cacheRead:o,cacheWrite:i,hitShare:a>0?Math.round(o/a*100)/100:0}}}catch{}return null},exports.readModelLimits=async function(e,t=fetch){const s=r.get(e)
|
|
5798
5881
|
if(s&&Date.now()-s.at<6e5)return s.value
|
|
5799
5882
|
const n=new Map
|
|
5800
5883
|
try{const s=await t(`${e.replace(/\/$/,"")}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
@@ -5903,23 +5986,26 @@ return{root:(0,i.resolve)((0,a.requestWorktreePath)(e,s),o),main:!1}}const c=awa
|
|
|
5903
5986
|
return{root:(0,i.resolve)(c,o),main:!0}},exports.serveSite=async function(e,t={}){const a=(0,i.resolve)(e),d=l.get(a)
|
|
5904
5987
|
if(d)return{port:d.port,url:`http://127.0.0.1:${d.port}`}
|
|
5905
5988
|
let u=0
|
|
5906
|
-
const h=(0,s.createServer)(async(e,
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
if(
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
if(
|
|
5914
|
-
|
|
5915
|
-
|
|
5989
|
+
const h={server:void 0,port:0,root:a,...t.refresh?{refresh:t.refresh}:{}},p=(0,s.createServer)(async(e,t)=>{try{const s=h.root
|
|
5990
|
+
h.refresh&&Date.now()-u>1e3&&(u=Date.now(),await h.refresh().catch(()=>{}))
|
|
5991
|
+
const a=decodeURIComponent((e.url||"/").split("?")[0])
|
|
5992
|
+
let l=(0,i.resolve)((0,i.join)(s,a))
|
|
5993
|
+
if(l!==s&&!l.startsWith(s+i.sep))return void t.writeHead(403).end("forbidden")
|
|
5994
|
+
if(r((0,i.relative)(s,l)))return void t.writeHead(404).end("not found")
|
|
5995
|
+
let d=await(0,o.stat)(l).catch(()=>null)
|
|
5996
|
+
if(d?.isDirectory()&&(l=(0,i.join)(l,"index.html"),d=await(0,o.stat)(l).catch(()=>null)),d?.isFile()){const[e,n]=await Promise.all([(0,o.realpath)(s).catch(()=>s),(0,o.realpath)(l).catch(()=>"")])
|
|
5997
|
+
if(!n||n!==e&&!n.startsWith(e+i.sep)||r((0,i.relative)(e,n)))return void t.writeHead(404).end("not found")}if(!d?.isFile()){if("/favicon.ico"===a)return void t.writeHead(204).end()
|
|
5998
|
+
const e=await(0,o.stat)((0,i.join)(s,"index.html")).catch(()=>null)
|
|
5999
|
+
return e?.isFile()?void t.writeHead(404).end("not found"):void t.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}).end(function(e){const t=(0,i.resolve)(e).split(i.sep),r=t.lastIndexOf("worktrees")
|
|
5916
6000
|
return`<!doctype html><meta charset="utf-8"><title>Nothing to show</title><body style="font:15px/1.5 system-ui,sans-serif;max-width:36em;margin:3em auto;padding:0 1em;color:#333"><p><b>Nothing to show.</b> This preview serves ${s=r>=0&&/^req\d{6}$/.test(t[r+1]??"")?`${t.slice(r+2).join("/")||"the top folder"} in ${t[r+1]}'s working copy`:t.slice(-1)[0]||e,s.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""}[e]))}, and that folder has no index.html any more: it was removed or rebuilt after the preview started.</p><p>Build output kept for a preview belongs in a folder git ignores (the project's build folder), so it stays on this machine and out of the request's branch.</p></body>`
|
|
5917
|
-
var s}(
|
|
5918
|
-
return l.set(a,
|
|
6001
|
+
var s}(s))}t.writeHead(200,{"Content-Type":c[(0,i.extname)(l).toLowerCase()]||"application/octet-stream","Content-Length":String(d.size),"Cache-Control":"no-store"}),(0,n.createReadStream)(l).pipe(t)}catch{t.writeHead(500).end("error")}}),f=await new Promise((e,t)=>{p.once("error",t),p.listen(0,"127.0.0.1",()=>e(p.address().port))})
|
|
6002
|
+
return h.server=p,h.port=f,l.set(a,h),{port:f,url:`http://127.0.0.1:${f}`}},exports.stopSite=function(e){if(e){const t=(0,i.resolve)(e)
|
|
5919
6003
|
return l.get(t)?.server.close(),void l.delete(t)}for(const[,e]of l)e.server.close()
|
|
5920
6004
|
l.clear()},exports.stopSitesUnder=function(e){const t=(0,i.resolve)(e),r=[]
|
|
5921
6005
|
for(const[e,s]of l)(e===t||e.startsWith(t+i.sep))&&(s.server.close(),l.delete(e),r.push(`http://127.0.0.1:${s.port}`))
|
|
5922
|
-
return r},exports.
|
|
6006
|
+
return r},exports.sitesUnder=function(e){const t=(0,i.resolve)(e)
|
|
6007
|
+
return Array.from(l.values()).filter(e=>e.root===t||e.root.startsWith(t+i.sep)).map(e=>({root:e.root,port:e.port}))},exports.rehomeSite=function(e,t,r){const s=(0,i.resolve)(e),n=(0,i.resolve)(t),o=l.get(s)
|
|
6008
|
+
return!(!o||n!==s&&l.has(n)||(l.delete(s),o.root=n,o.refresh=r,l.set(n,o),0))},exports.stopSiteAt=function(e){let t=0
|
|
5923
6009
|
try{t=Number(new URL(e).port)}catch{return!1}for(const[e,r]of l)if(r.port===t)return r.server.close(),l.delete(e),!0
|
|
5924
6010
|
return!1},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
|
|
5925
6011
|
const s=t(58611),n=t(79896),o=t(91943),i=t(16928),a=t(7202),c={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".wasm":"application/wasm",".webmanifest":"application/manifest+json",".xml":"application/xml; charset=utf-8",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".mp4":"video/mp4",".webm":"video/webm",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".avif":"image/avif",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".otf":"font/otf",".txt":"text/plain; charset=utf-8",".pdf":"application/pdf"},l=new Map},3031(e,exports,t){"use strict"
|
|
@@ -10093,10 +10179,10 @@ for(const r of Array.isArray(e)?e:[]){const e=o(r)
|
|
|
10093
10179
|
if(e)for(const n of s(r))t.set(n.trim(),e)}return t}
|
|
10094
10180
|
const i=t(67071),a=t(65454),c={node:"node",npm:"node",npx:"node",yarn:"node",pnpm:"node",tsx:"node","ts-node":"node",corepack:"node",deno:"deno",bun:"bun",python:"python",python3:"python",pip:"python",pip3:"python",pytest:"python",uv:"python",poetry:"python",pipenv:"python",tox:"python",go:"go",gofmt:"go",cargo:"rust",rustc:"rust",ruby:"ruby",gem:"ruby",bundle:"ruby",rake:"ruby",rspec:"ruby",java:"java",javac:"java",mvn:"java",gradle:"java","./gradlew":"java",kotlin:"java",kotlinc:"java",php:"php",composer:"php",phpunit:"php",dotnet:"dotnet",swift:"swift",zig:"zig",elixir:"elixir",mix:"elixir",dart:"dart",flutter:"dart",perl:"perl",lua:"lua",julia:"julia",Rscript:"r",pdfinfo:"pdf",pdftotext:"pdf",pdftoppm:"pdf",pdfimages:"pdf",pdffonts:"pdf",pdfseparate:"pdf",pdfunite:"pdf",qpdf:"pdf",soffice:"office",libreoffice:"office",pandoc:"office","render-pdf":"office",zbarimg:"office",segno:"office"},l=new Set(["if","then","else","elif","do","while","until","!","time","exec","env","command","sudo","{"])},64469(e,exports,t){"use strict"
|
|
10095
10181
|
function r(e,t,r=0){return!!t.lastActiveMs&&!(r&&t.lastActiveMs<r)&&e-t.lastActiveMs<=(0,i.cacheWarmForMs)(t.providerID)}function s(e,t){return!(!e||!t)&&(e===t||(0,o.isBranchWorkerAgentType)(e)&&(0,o.isBranchWorkerAgentType)(t))}exports.b6=exports.nZ=exports.WARM_CODER_DEFAULTS=void 0,exports.readWarmCoderPolicy=function(e){const t=e?.warmCoder??{},r=Number(t.maxContextTokens),s=Number(t.coldMaxContextTokens),n=Number(t.coldMaxChangedLines)
|
|
10096
|
-
return{enabled:void 0===t.enabled?exports.WARM_CODER_DEFAULTS.enabled:!1!==t.enabled,maxContextTokens:Number.isFinite(r)&&r>0?r:exports.WARM_CODER_DEFAULTS.maxContextTokens,coldResume:void 0===t.coldResume?exports.WARM_CODER_DEFAULTS.coldResume:!1!==t.coldResume,coldMaxContextTokens:Number.isFinite(s)&&s>0?s:exports.WARM_CODER_DEFAULTS.coldMaxContextTokens,coldMaxChangedLines:Number.isFinite(n)&&n>=0?n:exports.WARM_CODER_DEFAULTS.coldMaxChangedLines,areaReuse:void 0===t.areaReuse?exports.WARM_CODER_DEFAULTS.areaReuse:!1!==t.areaReuse}},exports.isCacheWarm=r,exports.contextCeilingFor=function(e){const
|
|
10097
|
-
const
|
|
10098
|
-
return
|
|
10099
|
-
return void 0===
|
|
10182
|
+
return{enabled:void 0===t.enabled?exports.WARM_CODER_DEFAULTS.enabled:!1!==t.enabled,maxContextTokens:Number.isFinite(r)&&r>0?r:exports.WARM_CODER_DEFAULTS.maxContextTokens,coldResume:void 0===t.coldResume?exports.WARM_CODER_DEFAULTS.coldResume:!1!==t.coldResume,coldMaxContextTokens:Number.isFinite(s)&&s>0?s:exports.WARM_CODER_DEFAULTS.coldMaxContextTokens,coldMaxChangedLines:Number.isFinite(n)&&n>=0?n:exports.WARM_CODER_DEFAULTS.coldMaxChangedLines,areaReuse:void 0===t.areaReuse?exports.WARM_CODER_DEFAULTS.areaReuse:!1!==t.areaReuse}},exports.isCacheWarm=r,exports.contextCeilingFor=function(e,t){const r=function(e,t){if(!(e&&e.context>0))return
|
|
10183
|
+
const r=Math.min(e.output??0,a)||a
|
|
10184
|
+
return"openai"===String(t??"")&&e.input?e.input-Math.round(.05*e.input):e.input?e.input-Math.min(2e4,r):e.context-r}(e,t)
|
|
10185
|
+
return void 0===r?e&&0===e.context?Number.POSITIVE_INFINITY:exports.b6:Math.max(0,r-exports.nZ)},exports.pickWarmCoder=function(e,t,n,o,i=0){if(!e.enabled)return{reuse:!1,why:"off"}
|
|
10100
10186
|
if(!o.length)return{reuse:!1,why:"no-candidate"}
|
|
10101
10187
|
const a=["role","unrelated","model","tools","busy","context","archived","cold","cold-context","stale"]
|
|
10102
10188
|
let c=0
|