alchemylab 3.0.5 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +594 -489
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1133,11 +1133,11 @@ let d,u
|
|
|
1133
1133
|
if(a(s,c,e.health?.service),e.health){const t=await n(o,e.health)
|
|
1134
1134
|
d=t.body||void 0,u=t.healthy}return{projectName:s,target:t,running:l,health:d,healthy:u}}
|
|
1135
1135
|
const p=t(79896),f=h(t(16928)),m=u(t(7089)),g=t(55481),w=t(81618),y=t(28295),v=m.safeLoad||m.load,b=m.safeDump||m.dump},46508(e,exports,t){"use strict"
|
|
1136
|
-
async function r(e,t,r){const s=(0,
|
|
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,83 +3871,84 @@ t.json({success:!0,data:{workdir:i,kind:"default-branch-snapshot"}})}catch(e){t.
|
|
|
3793
3871
|
return Number.isFinite(t)&&t>=1&&r>=t?{path:e.path,lines:[Math.floor(t),Math.min(r,Number.MAX_SAFE_INTEGER)]}:{path:e.path}}),c=i&&a.length>0?await r(o,n):null
|
|
3794
3872
|
if(!c||!i)return void t.json({success:!0,data:{note:null}})
|
|
3795
3873
|
const l=await async function(e){const t=e.getProjectId(),r=Date.now()
|
|
3796
|
-
if(!
|
|
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,
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3882
|
+
const s=o.getProjectId(),n=(0,U.simpleGit)(o.projectPath),i=(await n.raw(["for-each-ref","--format=%(refname:short)","refs/heads/requests/"])).split("\n").map(e=>e.trim().replace(/^requests\//,"")).filter(e=>/^req\d+$/.test(e)&&e!==r),a=new Set(["open","in_progress","ready_for_review","completing","resolving_conflicts"]),c=new Set(i),l=[]
|
|
3883
|
+
if(s&&c.size>0){const e=await o.apiClient.get(`/api/projects/${s}/requests?inFlight=true&limit=100`),t=Array.isArray(e?.data)?e.data:[]
|
|
3884
|
+
for(const e of t){const t=String(e?.localId??"")
|
|
3885
|
+
c.has(t)&&a.has(String(e.status))&&l.push({id:t,title:String(e.title??""),status:String(e.status)})}}const d=await(0,J.resolveDefaultBranch)(o.projectPath),u=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:l.map(e=>e.id),defaultBranch:d,git:n}),h=new Map(l.map(e=>[e.id,e]))
|
|
3886
|
+
t.json({success:!0,data:u.map(e=>({...e,siblingTitle:h.get(e.sibling)?.title??"",siblingStatus:h.get(e.sibling)?.status??""}))})}catch(e){o.logger.debug("sibling-overlap lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:[]})}}),e.get("/api/request-diff-stats",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
|
|
3808
3887
|
if(!r)return void t.status(400).json({success:!1,error:"requestId required"})
|
|
3809
|
-
const s=(0,
|
|
3888
|
+
const s=(0,U.simpleGit)(o.projectPath),n=await(0,L.resolveRequestDiffRange)(s,r,{status:"in_progress"}),i=n.headSha,a="string"==typeof e.query.since&&/^[0-9a-f]{7,40}$/.test(e.query.since)?e.query.since:"",c=a&&i&&await s.raw(["merge-base","--is-ancestor",a,i]).then(()=>!0,()=>!1)?a:n.baseSha,l=c&&i?await(0,L.computeDiffStats)(s,c,i):void 0,d=await(0,J.resolveDefaultBranch)(o.projectPath).catch(()=>{})
|
|
3810
3889
|
t.json({success:!0,data:l?{...l,baseSha:c,headSha:i,...d?{branch:d}:{}}:null})}catch(e){o.logger.debug("request-diff-stats lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:null})}}),e.post("/api/verify-preflight",async(e,t)=>{try{const{requestId:r,commands:s}=e.body??{},n=Array.isArray(s)?s.filter(e=>"string"==typeof e&&!!e.trim()):[]
|
|
3811
3890
|
if(!r||0===n.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty commands array are required"})
|
|
3812
|
-
const i=(await(0,
|
|
3891
|
+
const i=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim(),a=Array.isArray(e.body?.proofs)?e.body.proofs.filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command):[]
|
|
3813
3892
|
if(!0===e.body?.background){const e=JSON.stringify([[...n].sort(),a.map(e=>`${e.criterionId}\0${e.command}`).sort()])
|
|
3814
|
-
return(0,
|
|
3893
|
+
return(0,H.startPendingProof)(r,i,async e=>{const t=await d(o,r,i,n,{proofs:a,onAdvisory:e.advisory,onStep:e.step}),s=o.getProjectId(),l=c(a,t)
|
|
3815
3894
|
if(e.step("recording what it found",6e4),s&&l.length>0&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:t.sha,tree:t.tree??null,proofs:l}).catch(e=>o.logger.warn("proof replay: evidence not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)})),!t.available||!Array.isArray(t.results))return null
|
|
3816
3895
|
const u=t.results.filter(e=>!e.timedOut&&!e.couldNotRun&&!e.cutShort&&!1!==(e.authoritative??t.authoritative)).map(e=>({command:e.command,ok:e.ok,exit:e.exit,output:[e.printed,e.error].filter(Boolean).join("\n")||String(e.output??""),...e.flaky?{flaky:!0}:{}})),h=u.filter(e=>!e.ok).map(e=>({command:e.command,exit:e.exit,output:String(e.output??"")}))
|
|
3817
3896
|
return o.logger.info("proof replay finished beside the sign-off check",{requestId:r,sha:i.slice(0,8),blocking:h.length,settled:u.length,ms:t.ms}),{sha:t.sha,...t.tree?{tree:t.tree}:{},blocking:h,settled:u}},{key:e,onOverdue:async(e,t)=>{const s=o.getProjectId()
|
|
3818
|
-
o.logger.warn("proof replay: overdue, the merge goes on without it",{requestId:r,sha:i.slice(0,8),step:e.what,afterSec:Math.round(t/1e3)}),s&&0!==a.length&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:i,proofs:l(a,e,t)}).catch(e=>o.logger.warn("proof replay: its overdue end was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})||o.logger.info("proof replay: the same proofs at this commit are already replayed; joined",{requestId:r,sha:i.slice(0,8)}),void t.json({success:!0,data:{available:!0,pending:!0,sha:i}})}t.json({success:!0,data:await d(o,r,i,n,{proofs:a})})}catch(e){o.logger.debug("verify-preflight failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{available:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/showing-run",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(0,
|
|
3897
|
+
o.logger.warn("proof replay: overdue, the merge goes on without it",{requestId:r,sha:i.slice(0,8),step:e.what,afterSec:Math.round(t/1e3)}),s&&0!==a.length&&await o.apiClient.post(`/api/projects/${s}/requests/${r}/proof-evidence`,{commitSha:i,proofs:l(a,e,t)}).catch(e=>o.logger.warn("proof replay: its overdue end was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})||o.logger.info("proof replay: the same proofs at this commit are already replayed; joined",{requestId:r,sha:i.slice(0,8)}),void t.json({success:!0,data:{available:!0,pending:!0,sha:i}})}t.json({success:!0,data:await d(o,r,i,n,{proofs:a})})}catch(e){o.logger.debug("verify-preflight failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{available:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/showing-run",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(0,G.showCommands)(e.body?.commands),n=r&&/^req\d+$/.test(r)&&!0===e.body?.page?(0,G.servedPageOf)((0,w.requestWorktreePath)(o.projectPath,r),r,(0,O.listPreviewHandles)(),(0,D.listServedSites)()):null
|
|
3819
3898
|
if(!r||!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"requestId is required"})
|
|
3820
3899
|
if(0===s.length&&!n)return void t.json({success:!0,data:{started:!1,reason:"no commands were named and no page is being served"}})
|
|
3821
|
-
const i=(await(0,
|
|
3900
|
+
const i=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(async()=>{const{runs:e,pages:t}=await(0,G.runShowing)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,i,s,{page:n}),c=o.getProjectId()
|
|
3822
3901
|
if(!c)return
|
|
3823
3902
|
const l=(0,m.randomUUID)()
|
|
3824
|
-
for(const s of[...t.map(e=>(0,
|
|
3903
|
+
for(const s of[...t.map(e=>(0,G.pageRecord)(r,e)),...e.map(e=>(0,G.showingRecord)(r,e))]){const e={...s,metadata:{...s.metadata,showing:l}}
|
|
3825
3904
|
await o.apiClient.post(`/api/projects/${c}/tool-records`,e).catch(e=>o.logger.warn("showing: a run or a picture was not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))}})().catch(e=>o.logger.warn("showing runs failed",{requestId:r,error:(0,P.getErrorMessage)(e)})),t.json({success:!0,data:{started:!0,sha:i,commands:s.length,page:n?n.dir:null}})}catch(e){o.logger.warn("showing runs could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{started:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/baseline-measure",async(e,t)=>{try{const{requestId:r}=e.body??{},s=(Array.isArray(e.body?.baselines)?e.body.baselines:[]).filter(e=>"string"==typeof e?.criterionId&&"string"==typeof e?.command&&e.command.trim()&&e?.baseline&&"object"==typeof e.baseline).slice(0,5)
|
|
3826
3905
|
if(!r||0===s.length)return void t.status(400).json({success:!1,error:"requestId and a non-empty baselines array ({ criterionId, command, baseline }) are required"})
|
|
3827
|
-
const n=(await(0,
|
|
3906
|
+
const n=(await(0,U.simpleGit)(o.projectPath).revparse([(0,R.generateRequestBranchName)(r,"")])).trim();(0,H.startPendingProof)(r,n,async()=>{const{measured:e}=await(0,W.measureBaselines)({projectPath:o.projectPath,getProjectId:o.getProjectId,logger:o.logger,resolveEnv:()=>a(o)},r,n,s),t=o.getProjectId()
|
|
3828
3907
|
t&&e.length>0&&await o.apiClient.post(`/api/projects/${t}/requests/${r}/proof-evidence`,{commitSha:n,proofs:e.map(e=>({criterionId:e.criterionId,command:e.command,ok:e.ok,...e.couldNotRun?{couldNotRun:!0}:{},output:e.words.slice(0,200),words:e.words,baseline:e.metric}))}).catch(e=>o.logger.warn("baseline measurement: evidence not recorded",{requestId:r,error:(0,P.getErrorMessage)(e)}))
|
|
3829
3908
|
const i=e.filter(e=>!e.ok&&!e.couldNotRun).length
|
|
3830
|
-
return o.logger.info("baselines measured beside the sign-off check",{requestId:r,sha:n.slice(0,8),measured:e.length,regressed:i}),{sha:n,blocking:[],baselines:{measured:e.length,regressed:i}}},{kind:"baselines",waitMs:
|
|
3909
|
+
return o.logger.info("baselines measured beside the sign-off check",{requestId:r,sha:n.slice(0,8),measured:e.length,regressed:i}),{sha:n,blocking:[],baselines:{measured:e.length,regressed:i}}},{kind:"baselines",waitMs:W.BASELINE_BUDGET_MS+18e4}),t.json({success:!0,data:{pending:!0,sha:n,baselines:s.length}})}catch(e){o.logger.warn("baseline measurement could not start",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{pending:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.post("/api/bring-main-in",async(e,t)=>{try{const s="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await r(o,s)
|
|
3831
3910
|
if(!i||!n)return void t.json({success:!0,data:{outcome:"failed",text:"This session works on no request with a worktree, so there is no branch to bring main into."}})
|
|
3832
3911
|
const a="string"==typeof e.body?.requestId?e.body.requestId:""
|
|
3833
3912
|
if(a&&a!==i.requestId)return void t.json({success:!0,data:{outcome:"failed",text:`Main can be brought only into your own request's branch (${i.requestId}), not ${a}'s.`}})
|
|
3834
|
-
const c=await(0,
|
|
3913
|
+
const c=await(0,X.bringMainInOnAsk)({projectPath:o.projectPath,projectId:n,apiClient:o.apiClient,logger:o.logger,requestId:i.requestId,worktreePath:i.worktree})
|
|
3835
3914
|
t.json({success:!0,data:{requestId:i.requestId,...c}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.get("/api/catch-up-due",async(e,t)=>{try{const r=await s(o,"string"==typeof e.query.sessionId?e.query.sessionId:"")
|
|
3836
3915
|
if(!r)return void t.json({success:!0,data:{due:!1}})
|
|
3837
|
-
const i=await
|
|
3916
|
+
const i=await re.catchUpDue(o.projectPath,await(0,J.resolveDefaultBranch)(o.projectPath),r.requestId)
|
|
3838
3917
|
t.json({success:!0,data:{due:i.due&&!n(r.requestId,i.files)}})}catch(e){o.logger.debug("catch-up-due failed (the tool runs as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{due:!1}})}}),e.post("/api/catch-up",async(e,t)=>{try{const r="string"==typeof e.body?.sessionId?e.body.sessionId:"",n=o.getProjectId(),i=await s(o,r)
|
|
3839
3918
|
if(!i||!n)return void t.json({success:!0,data:{outcome:"none"}})
|
|
3840
|
-
const a=await
|
|
3919
|
+
const a=await re.catchUpDue(o.projectPath,await(0,J.resolveDefaultBranch)(o.projectPath),i.requestId)
|
|
3841
3920
|
if(!a.due)return void t.json({success:!0,data:{outcome:"none"}})
|
|
3842
|
-
const c=await(0,
|
|
3843
|
-
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}})
|
|
3844
3923
|
if("merged"===c.outcome||"conflict"===c.outcome){const e=c.from?.length?` (from ${c.from.join(", ")})`:"",t="merged"===c.outcome?`Brought in main's changes to ${a.files.join(", ")}${e}.`:`Brought in main's changes to ${a.files.join(", ")}${e}. They conflict with this work in ${(c.conflictedFiles??[]).join(", ")||"those files"}; its worker is resolving that now.`
|
|
3845
3924
|
await o.apiClient.post(`/api/projects/${n}/session-messages/reports`,{fromSessionId:r,status:"success",summary:t,reportKind:"progress",requestId:i.requestId}).catch(e=>o.logger.warn("main came in, but the line on the request was not posted",{requestId:i.requestId,error:(0,P.getErrorMessage)(e)}))}t.json({success:!0,data:{outcome:c.outcome,text:c.text}})}catch(e){o.logger.warn("catch-up failed (the tool runs as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{outcome:"failed"}})}}),e.post("/api/commit-worktree-leftovers",async(e,t)=>{try{const r="string"==typeof e.body?.requestId?e.body.requestId:void 0
|
|
3846
3925
|
if(!r||!/^req[0-9]{4,10}$/.test(r))return void t.status(400).json({success:!1,error:"a request local id (reqNNNNNN) is required"})
|
|
3847
3926
|
const s=(0,w.requestWorktreesDir)(o.projectPath),n=M.default.join(s,r)
|
|
3848
3927
|
if(n!==M.default.join(s,M.default.basename(n))||!n.startsWith(s+M.default.sep))return void t.status(400).json({success:!1,error:"requestId must name one of the project's request worktrees"})
|
|
3849
|
-
if(!await
|
|
3850
|
-
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)
|
|
3851
3930
|
await(0,x.assertGitNotUserFolder)(i,"commit of worktree leftovers")
|
|
3852
3931
|
const a=await i.status()
|
|
3853
3932
|
if(a.isClean())return void t.json({success:!0,data:{committed:!1,reason:"clean"}})
|
|
3854
3933
|
const c=[...a.modified,...a.not_added,...a.created,...a.deleted]
|
|
3855
3934
|
await i.add(["-A"])
|
|
3856
|
-
const{note:l}=await(0,
|
|
3935
|
+
const{note:l}=await(0,Z.holdBackStagedCredentials)(n)
|
|
3857
3936
|
l&&o.logger.warn("commit-worktree-leftovers held key material back",{requestId:r,note:l}),await i.commit(`chore: commit uncommitted worktree changes at completion (${r})${l?`\n\n${l}`:""}`)
|
|
3858
3937
|
const d=(await i.revparse(["HEAD"])).trim()
|
|
3859
3938
|
o.logger.info("commit-worktree-leftovers",{requestId:r,files:c.length,sha:d.slice(0,8)}),t.json({success:!0,data:{committed:!0,sha:d,files:c}})}catch(e){o.logger.warn("commit-worktree-leftovers failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{committed:!1,reason:(0,P.getErrorMessage)(e)}})}}),e.get("/api/branch-head",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0,s=("string"==typeof e.query.branch?e.query.branch:void 0)||(r?(0,R.generateRequestBranchName)(r,""):void 0)
|
|
3860
3939
|
if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
|
|
3861
|
-
const n=(await(0,
|
|
3940
|
+
const n=(await(0,U.simpleGit)(o.projectPath).revparse([s])).trim(),i=(await(0,U.simpleGit)(o.projectPath).revparse([`${s}^{tree}`]).catch(()=>"")).trim()
|
|
3862
3941
|
t.json({success:!0,data:{branch:s,sha:n,.../^[0-9a-f]{40}$/.test(i)?{tree:i}:{}}})}catch(e){o.logger.debug("branch-head lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{branch:null,sha:null}})}}),e.post("/api/release-pool-slot",async(e,t)=>{try{const{worktreePath:r}=e.body||{}
|
|
3863
3942
|
if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"})
|
|
3864
3943
|
let s=null
|
|
3865
3944
|
try{const e=o.getProjectId(),t=e?await(0,v.readAllPages)(o.apiClient,t=>`/api/projects/${e}/test-runs?limit=100&offset=${t}`,500):void 0
|
|
3866
3945
|
if(t){const e=e=>"string"==typeof e?(0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),e):null
|
|
3867
|
-
s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,
|
|
3868
|
-
const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(
|
|
3869
|
-
let
|
|
3870
|
-
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})
|
|
3871
3950
|
exports.COMPLETION_REPLAY_BUDGET_MS=15e4
|
|
3872
|
-
const
|
|
3951
|
+
const oe=12e4},60885(e,exports,t){"use strict"
|
|
3873
3952
|
exports.makeRunOwn=function(e,t){return async o=>{n||(n=new r.Agent({headersTimeout:0,bodyTimeout:0}))
|
|
3874
3953
|
const i="number"==typeof o.timeoutMs?o.timeoutMs:12e4,a=`http://127.0.0.1:${e}/api/run`,c={"Content-Type":"application/json"},l=(0,s.localSecretForUrl)(a,t)
|
|
3875
3954
|
return l&&(c[s.LOCAL_SECRET_HEADER]=l),(await(0,r.fetch)(a,{method:"POST",headers:c,body:JSON.stringify(o),signal:AbortSignal.timeout(Math.max(18e4,i+6e4)),dispatcher:n})).json()}}
|
|
@@ -4774,37 +4853,37 @@ let h=null
|
|
|
4774
4853
|
const p=new Map},62366(e,exports,t){"use strict"
|
|
4775
4854
|
var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
|
|
4776
4855
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
|
|
4777
|
-
const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(
|
|
4778
|
-
class
|
|
4779
|
-
this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0
|
|
4856
|
+
const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(39175),T=t(52640),x=t(65232),M=t(95671),R=t(44192),P=t(14139),O=t(41152),D=r(t(16928)),q=r(t(79896)),N=r(t(70857)),F=t(5607),L=t(76634),U=t(31),B=t(90524),H=t(18984),W=t(58824),G=t(29869),K=t(67078),z=t(60070),V=t(37210),Y=t(87227),J=t(82928),X=t(76616),Q=t(15047),Z=t(90629),ee=t(42298),te=t(67563),re=t(41950),se=t(88087),ne=t(1285),oe=t(20564),ie=Object.keys(F.actionHandlers).join(","),ae=t(6850),ce=t(58836),le=t(25146),de=t(23411),ue=t(43218),he=t(81570),pe=t(18815),fe=t(73613),me=t(49779),ge=t(76982),we=t(53340),ye=t(89063),ve=t(61292),be=t(11017),ke=t(28148),Se=t(18649),Ie=t(64306),$e=t(96685),je=t(49816),Ee=t(35419),Ae=t(33521),Ce=t(56844),_e=t(84352),Te=t(40713),xe=t(75361),Me=t(75167),Re=t(72188),Pe=t(6688),Oe=t(39068),De=t(3435),qe=t(48463),Ne=t(60489),Fe=t(24106),Le=t(81618),Ue=t(80211),Be=t(2566),He=t(83807),We=t(92456),Ge=t(95040),Ke=t(60355),ze=t(31026),Ve=t(73782),Ye=t(53608),Je=t(63484),Xe=t(31851),Qe=t(84102),Ze=t(75979),et=t(79329),tt=t(60833),rt=t(42593),st=t(69706),nt=t(11773),ot=t(20990),it=t(45956),at=t(36799),ct=t(86116),lt=t(13751),dt=t(13791),ut=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ht=65536
|
|
4857
|
+
class pt{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment":"on your machine",i=(0,Ae.isStandingRulePattern)(s),a=`You chose "Always allow" on the approval card for \`${t.askedCommand.slice(0,300)}\`${t.approvalId?` (card ${t.approvalId})`:""}${t.sessionId?`, asked by agent session ${t.sessionId}`:""}, on ${t.at.replace("T"," ").slice(0,16)} UTC.`;(async()=>{(await(0,je.getRegisteredAllows)(this.apiClient,e,r??null).catch(()=>[])).some(e=>("string"==typeof e?e:e.exact).trim()===s.trim())||(await this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"ok:card",command:(0,n.commandAsStored)(s),description:`${a} ${i?`It allows exactly this shape, where <file> is any one file inside the project, ${o}`:`This exact command runs ${o}`} without asking. Delete it or switch it off to be asked again.`,timeout:120,isActive:!0,exactMatch:!0,...r?{environmentId:r}:{}}),(0,je.invalidateRegisteredAllows)(e))})().catch(e=>this.logger.warn("Failed to remember approved command",{error:String(e)}))}rememberJudgeApprovedCommand(e,t,r){const s=new Date(Date.now()+pt.JUDGE_APPROVAL_TTL_MS).toISOString()
|
|
4858
|
+
this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,je.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
|
|
4780
4859
|
if(!this.parkController?.noteServeDeath(`exit code=${e.code} signal=${e.signal}`))return void this.logger.info("OpenCode serve exit observed during managed lifecycle -- no action",{code:e.code,signal:e.signal,pid:e.pid})
|
|
4781
4860
|
const t=Date.now()
|
|
4782
4861
|
this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.defaultModelUnchosen=!1,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
|
|
4783
|
-
return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,
|
|
4784
|
-
const r=(0,
|
|
4785
|
-
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
|
|
4786
4865
|
this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
|
|
4787
|
-
return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new
|
|
4866
|
+
return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new H.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new V.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,w.default)(),r=e=>(0,ee.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
|
|
4788
4867
|
e.use((e,t,r)=>{const n=e.headers.host
|
|
4789
|
-
if((0,
|
|
4790
|
-
s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:
|
|
4791
|
-
if(!r(n))return this.logger.warn("Refused a browser request from an origin this project does not know",{origin:n,path:e.path,method:e.method}),void t.status(403).json({success:!1,error:
|
|
4792
|
-
n&&t.setHeader("Access-Control-Allow-Private-Network","true"),s()}),e.use((0,v.default)({origin:(e,t)=>t(null,r(e)),credentials:!0,allowedHeaders:["Content-Type","Authorization","Accept","X-Alchemy-Project"],maxAge:600})),e.use((0,
|
|
4793
|
-
const n=(0,
|
|
4794
|
-
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"}))
|
|
4795
4874
|
const o=["/api/opencode","/api/spawn-","/api/setup-","/api/run-tests-now","/api/flesh-out-request","/sync-agents","/api/stuck-sessions/"],i=["/api/opencode/session","/api/opencode/global/health"]
|
|
4796
4875
|
e.use((e,t,r)=>{const s="string"==typeof e.query.projectId?e.query.projectId:void 0
|
|
4797
4876
|
if(!s)return r()
|
|
4798
4877
|
const n=this.projectId
|
|
4799
|
-
n&&s!==n?t.status(409).json({success:!1,error:`project mismatch: this action server serves ${n} (${
|
|
4878
|
+
n&&s!==n?t.status(409).json({success:!1,error:`project mismatch: this action server serves ${n} (${D.default.basename(this.options.projectPath)}), not ${s}. Re-discover the action server for your project -- ports shuffle on restart.`,code:"PROJECT_MISMATCH",data:{servingProjectId:n,servingProject:D.default.basename(this.options.projectPath)}}):r()}),e.use((e,t,r)=>{const s=!0===e.headers.accept?.includes("text/event-stream")
|
|
4800
4879
|
if("GET"===e.method&&i.some(t=>e.path===t||"/api/opencode/session"===t&&e.path===`${t}/`)||s&&e.path.startsWith("/api/opencode"))return"active"!==this.parkController?.getState()&&this.parkController?void t.status(503).json({success:!1,parked:!0,error:"Project is parked (idle); serve is stopped"}):void r()
|
|
4801
|
-
o.some(t=>e.path.startsWith(t))?this.ensureServeAlive(`route:${e.path}`).then(()=>r()):r()}),e.get("/health",this.handleHealth.bind(this)),e.get("/api/read-token",(e,t)=>{t.setHeader("Cache-Control","no-store"),t.json({success:!0,data:(0,
|
|
4880
|
+
o.some(t=>e.path.startsWith(t))?this.ensureServeAlive(`route:${e.path}`).then(()=>r()):r()}),e.get("/health",this.handleHealth.bind(this)),e.get("/api/read-token",(e,t)=>{t.setHeader("Cache-Control","no-store"),t.json({success:!0,data:(0,ne.mintReadToken)(this.localSecret)})}),this.options.projectPath&&(0,b.registerAssetRoutes)(e,{projectPath:this.options.projectPath,getProjectId:()=>this.projectId??null,logger:this.logger}),e.post("/admin/shutdown",(e,t)=>{this.logger.warn("Retiring: a newer action server for this project requested takeover",{reason:e.body?.reason}),t.json({ok:!0,pid:process.pid}),setTimeout(()=>{try{process.kill(process.pid,"SIGTERM")}catch{process.exit(0)}},150)}),(0,xe.initGateLogPersistence)(this.options.projectPath),(0,Me.initGateModes)(this.options.projectPath),(0,Re.setActivityRequestResolver)(async e=>{const t=this.projectId
|
|
4802
4881
|
if(!t)throw new Error("no project bound")
|
|
4803
4882
|
const r=`/api/projects/${t}/session-request-associations`
|
|
4804
4883
|
let s=!1
|
|
4805
4884
|
const n=e=>e&&"object"==typeof e&&"data"in e?e.data:e,o={getCurrentForSession:async e=>{try{const t=n(await this.apiClient.get(`${r}/current/${encodeURIComponent(e)}`))
|
|
4806
4885
|
return null===t?null:t&&"string"==typeof t.requestLocalId?{requestLocalId:t.requestLocalId,ended:!0===t.ended,withdrawn:!0===t.withdrawn}:void 0}catch{return}},getActiveRequestsForSession:async e=>{try{const t=n(await this.apiClient.get(`${r}/by-session/${encodeURIComponent(e)}?activeOnly=true`))
|
|
4807
|
-
return Array.isArray(t)?t:[]}catch(e){throw s=!0,e}}},i=await
|
|
4886
|
+
return Array.isArray(t)?t:[]}catch(e){throw s=!0,e}}},i=await Pe.sessionIdentity.getSessionRequestId({getCLISessionClient:()=>null,getSessionRequestAssociationClient:()=>o},this.logger,e)
|
|
4808
4887
|
if(s)throw new Error("session request lookup failed")
|
|
4809
4888
|
return i})
|
|
4810
4889
|
const a=async e=>Promise.all((0,$.listEntries)().map(async t=>{let r,s="",n=0,o=t.finishedAt??t.startedAt
|
|
@@ -4813,24 +4892,24 @@ try{const e=await k.promises.stat(t.logPath)
|
|
|
4813
4892
|
n=e.size,o=e.mtime.toISOString(),r=await k.promises.open(t.logPath,"r")
|
|
4814
4893
|
const i=Math.max(0,n-16384),a=Buffer.alloc(n-i)
|
|
4815
4894
|
await r.read(a,0,a.length,i),s=a.toString("utf-8").split("\n").slice(-50).join("\n")}catch{}finally{await(r?.close().catch(()=>{}))}return{...t,tail:s,sizeBytes:n,modifiedAt:o}}))
|
|
4816
|
-
return e.get("/api/commands/running",async(e,t)=>{try{t.json({success:!0,data:await a("0"!==e.query.tail)})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4817
|
-
t.json({success:!0,data:await(0,
|
|
4818
|
-
if(!(r&&s&&n&&o&&a&&c))return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.addStart)({id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:"number"==typeof l&&l>0?l:void 0}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4895
|
+
return e.get("/api/commands/running",async(e,t)=>{try{t.json({success:!0,data:await a("0"!==e.query.tail)})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/model-switch",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{pending:this.pendingConfigReload,pendingSince:this.pendingConfigReload&&this.pendingConfigReloadSince?this.pendingConfigReloadSince:null,lastReloadAt:this.lastConfigReloadAt||null,capMs:pt.RELOAD_DEFER_CAP_MS,busySessionIds:await this.busySessionIds()}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/envs/running",async(e,t)=>{try{if(t.setHeader("Cache-Control","no-cache"),!this.projectId)return void t.json({success:!0,data:{at:Date.now(),dockerAvailable:!1,hostCores:null,hostMemBytes:null,computerCores:N.default.cpus().length,computerMemBytes:N.default.totalmem(),envs:[]}})
|
|
4896
|
+
t.json({success:!0,data:await(0,Ue.runningEnvs)(this.projectId)})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/commands/activity",async(e,t)=>{try{t.setHeader("Cache-Control","no-cache"),t.json({success:!0,data:{runs:await a("0"!==e.query.tail),gate:(0,xe.listGateLog)(200),experiments:this.projectId?(0,$e.listExperiments)(this.projectId):[],execs:(0,G.listRunningExecs)(),previews:(0,_.previewHandlesWithVersions)(this.options.projectPath)}})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/commands/start",async(e,t)=>{try{const{id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:l}=e.body??{}
|
|
4897
|
+
if(!(r&&s&&n&&o&&a&&c))return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.addStart)({id:r,name:s,command:n,cwd:o,sessionId:i,startedAt:a,logPath:c,timeoutMs:"number"==typeof l&&l>0?l:void 0}),t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/commands/end",async(e,t)=>{try{const{id:r,exitCode:s,timedOut:n,finishedAt:o}=e.body??{}
|
|
4819
4898
|
if(!r||"number"!=typeof s||!o)return void t.status(400).json({success:!1,error:"missing required fields"});(0,$.markEnd)({id:r,exitCode:s,timedOut:!!n,finishedAt:o})
|
|
4820
4899
|
try{const e=(0,$.listEntries)().find(e=>e.id===r)
|
|
4821
|
-
if(e){const t=Date.parse(o)-Date.parse(e.startedAt);(0,
|
|
4900
|
+
if(e){const t=Date.parse(o)-Date.parse(e.startedAt);(0,xe.recordGate)({source:"command",sessionId:e.sessionId,...void 0!==e.requestId?{requestId:e.requestId}:{},environmentName:"Workspace",command:e.command,action:"auto",ruleId:"registered",exit:s,ms:Number.isFinite(t)?t:void 0})}}catch{}t.json({success:!0})}catch(e){t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.post("/api/auth-renew",async(e,t)=>{const r=this.options.tokenManager
|
|
4822
4901
|
if(r)try{const e=await r.renewIfDue(Number.POSITIVE_INFINITY).catch(()=>null)
|
|
4823
4902
|
if(e?.token)return this.updateAuthToken(e.token),void t.json({success:!0,renewed:!0})
|
|
4824
4903
|
const s=r.getAuthStatus()
|
|
4825
|
-
if(s.isPendingAuth&&s.authUrl)return void t.json({success:!0,opened:(0,
|
|
4904
|
+
if(s.isPendingAuth&&s.authUrl)return void t.json({success:!0,opened:(0,Z.openBrowser)(s.authUrl),alreadyPending:!0})
|
|
4826
4905
|
const{authUrl:n}=await r.startAuthenticationFlow()
|
|
4827
|
-
t.json({success:!0,opened:(0,
|
|
4906
|
+
t.json({success:!0,opened:(0,Z.openBrowser)(n)})}catch(e){this.logger.error("auth renew failed to start",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}else t.status(400).json({success:!1,error:"No token manager on this server"})}),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/opencode/restart",async(e,t)=>{try{if(!this.relaunchOpenCode)return void t.status(409).json({success:!1,error:"no serve child to restart (serve-only mode)"})
|
|
4828
4907
|
for(const e of this.executePark())this.managedExitPids.add(e)
|
|
4829
|
-
await this.executeRevive(),t.json({success:!0,data:{port:this.openCodePort??null}})}catch(e){this.logger.error("Manual serve restart failed",{error:(0,
|
|
4830
|
-
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||"")
|
|
4831
4910
|
if(!/^[0-9a-f]{7,64}$/i.test(r))return void t.status(400).json({success:!1,error:"sha must be a git commit hash"})
|
|
4832
4911
|
const s="string"!=typeof e.query.path||e.query.path.includes("..")?void 0:e.query.path
|
|
4833
|
-
try{const e=(0,
|
|
4912
|
+
try{const e=(0,He.simpleGit)(this.options.projectPath),n=524288,o=(await e.raw(["rev-list","--parents","-n","1",r])).trim().split(/\s+/).length>2,i=[...(0,g.documentDiffGitArgs)(),"show","--format=%H%n%an%n%ad%n%s","--patch",...o?["--first-parent","-m"]:[],r]
|
|
4834
4913
|
s&&i.push("--",s)
|
|
4835
4914
|
let a=await(0,g.documentDiffGit)(this.options.projectPath).raw(i)
|
|
4836
4915
|
const c=a.length>n
|
|
@@ -4841,79 +4920,79 @@ try{const s=this.projectId
|
|
|
4841
4920
|
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
4842
4921
|
let n=null
|
|
4843
4922
|
try{const e=await this.apiClient.get(`/api/projects/${s}/requests/${r}`)
|
|
4844
|
-
n=e?.data??e??null}catch{}const o=(0,
|
|
4845
|
-
return(Array.isArray(t)?t:[]).filter(e=>n(e?.metadata?.commitHash)).map(e=>({commitSha:e.metadata.commitHash,previousSha:n(e?.metadata?.previousCommitHash)?e.metadata.previousCommitHash:void 0}))}catch{return}},{baseSha:a,headSha:c}=await(0,
|
|
4923
|
+
n=e?.data??e??null}catch{}const o=(0,He.simpleGit)(this.options.projectPath),i=async()=>{try{const e=await this.apiClient.get(`/api/projects/${s}/tool-records?requestId=${encodeURIComponent(r)}&sortBy=created_at&sortDirection=asc&limit=100`),t=e?.data??e??[],n=e=>"string"==typeof e&&/^[0-9a-f]{7,64}$/i.test(e)
|
|
4924
|
+
return(Array.isArray(t)?t:[]).filter(e=>n(e?.metadata?.commitHash)).map(e=>({commitSha:e.metadata.commitHash,previousSha:n(e?.metadata?.previousCommitHash)?e.metadata.previousCommitHash:void 0}))}catch{return}},{baseSha:a,headSha:c}=await(0,We.resolveRequestDiffRange)(o,r,{mergeBaseSha:n?.mergeBaseSha,mergeCommitSha:n?.mergeCommitSha,status:n?.status},i),l=await(0,Ge.requestDocumentEdits)(this.apiClient,s,r).catch(()=>[])
|
|
4846
4925
|
if(!(a&&c||0!==l.length))return void t.status(404).json({success:!1,error:`No diff range found for ${r}.`})
|
|
4847
|
-
if("1"===e.query.stats){const e=a&&c?await(0,
|
|
4926
|
+
if("1"===e.query.stats){const e=a&&c?await(0,We.computeDiffStats)(o,a,c):void 0
|
|
4848
4927
|
if(!e&&0===l.length)return void t.status(404).json({success:!1,error:`No diff stats for ${r}.`})
|
|
4849
|
-
const s=(0,
|
|
4850
|
-
return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,
|
|
4851
|
-
t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4852
|
-
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
|
|
4853
4932
|
try{const s=this.projectId
|
|
4854
4933
|
if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
4855
4934
|
const{base:n,draft:o}=e.body??{},i=()=>{const e={"X-Client-Type":"alchemy-local-service"},t=this.apiClient.authToken||this.options.authToken
|
|
4856
|
-
return t&&(e.Authorization=`Bearer ${t}`),e},a=(e.body??{}).mode??"pr",c={run:(0,
|
|
4935
|
+
return t&&(e.Authorization=`Bearer ${t}`),e},a=(e.body??{}).mode??"pr",c={run:(0,L.defaultRunner)(),fetchRequest:async e=>{const t=await this.apiClient.get(`/api/projects/${s}/requests/${e}`)
|
|
4857
4936
|
return t?.data??t??null},fetchReceiptMarkdown:async e=>{const t=await fetch(`${this.options.remoteServerUrl}/api/projects/${s}/requests/${e}/receipt?format=md`,{headers:i()})
|
|
4858
4937
|
if(!t.ok)throw new Error(`Receipt fetch failed: HTTP ${t.status}`)
|
|
4859
4938
|
return await t.text()}},l={},d=[]
|
|
4860
|
-
if("pr"===a||"both"===a)try{l.pr=await(0,
|
|
4861
|
-
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
|
|
4862
4941
|
if(!r)return void t.json({success:!0,data:{text:""}})
|
|
4863
4942
|
const s=await(0,p.afterCompactionBlock)({apiClient:this.apiClient,projectId:r,projectPath:this.options.projectPath},String(e.params.sessionId||""))
|
|
4864
4943
|
t.json({success:!0,data:{text:s}})}catch(e){t.json({success:!0,data:{text:""}})}}),e.post("/api/sessions/:sessionId/deliver",async(e,r)=>{try{const s=String(e.params.sessionId||""),{message:n,kind:o,partPayload:i,noVerify:a,verifySeconds:c}=e.body||{}
|
|
4865
4944
|
if(!s||"string"!=typeof n||!n.trim())return void r.status(400).json({success:!1,error:"sessionId and a non-empty message are required"});(0,u.wakeSession)(s)
|
|
4866
4945
|
const{deliverPrompt:l}=await Promise.resolve().then(()=>t(17039)),d="worker-report"===o&&(0,h.isQuietReporter)(i?.fromSessionId)||"report-acknowledged"===o&&!0===i?.quiet,p=await l({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},s,n,{kind:o,partPayload:i,noVerify:!!a,...d?{noReply:d}:{},..."number"==typeof c?{verifySeconds:c}:{}})
|
|
4867
|
-
p.ok||this.logger.warn("deliver door: delivery failed",{sessionId:s,kind:o,error:p.error,detail:p.detail}),r.json({success:!0,data:p})}catch(e){(0,
|
|
4946
|
+
p.ok||this.logger.warn("deliver door: delivery failed",{sessionId:s,kind:o,error:p.error,detail:p.detail}),r.json({success:!0,data:p})}catch(e){(0,De.respondWithError)(r,this.logger,"Deliver to session",e)}}),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",async(e,t)=>{try{const{sessionId:r}=e.params,s=this.projectId
|
|
4868
4947
|
if(!s)return void t.status(400).json({success:!1,error:"No project"})
|
|
4869
4948
|
const n=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),o=(n?.data||n||[]).map(e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))
|
|
4870
|
-
t.json({success:!0,ancestors:o})}catch(e){t.status(500).json({success:!1,error:(0,
|
|
4871
|
-
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||{}
|
|
4872
4951
|
if("note-nudge"===r)return this.logger.info("note nudge appended",{tool:i??null,sessionId:n??null,requestId:o??null}),void t.json({success:!0})
|
|
4873
4952
|
!s||"start"!==r&&"end"!==r?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(r,String(s)),t.json({success:!0}))}),e.post("/api/results/spill",async(e,r)=>{try{const{text:s,toolName:n,callId:o}=e.body??{}
|
|
4874
4953
|
if("string"!=typeof s||!s)return void r.status(400).json({success:!1,error:"text is required"})
|
|
4875
4954
|
const i=await Promise.resolve().then(()=>t(13080)),a=await i.putBuffer(this.options.projectPath,s)
|
|
4876
4955
|
if(this.projectId){const{recordIdForCall:e}=await t.e(111).then(()=>t(56111)),r=e("string"==typeof o?o:void 0)
|
|
4877
|
-
this.apiClient.post(`/api/projects/${this.projectId}/results`,{address:a.address,locator:a.locator,bytes:a.bytes,name:`${String(n??"tool")}-result`,mediaType:"text/plain",...r?{producedBy:r}:{},summary:{characters:s.length,head:s.slice(0,400),heldBackFromContext:!0},producerNote:`Result of ${String(n??"a tool")}, too large for context`}).catch(e=>this.logger.warn("Spilled artifact could not be recorded",{error:(0,
|
|
4956
|
+
this.apiClient.post(`/api/projects/${this.projectId}/results`,{address:a.address,locator:a.locator,bytes:a.bytes,name:`${String(n??"tool")}-result`,mediaType:"text/plain",...r?{producedBy:r}:{},summary:{characters:s.length,head:s.slice(0,400),heldBackFromContext:!0},producerNote:`Result of ${String(n??"a tool")}, too large for context`}).catch(e=>this.logger.warn("Spilled artifact could not be recorded",{error:(0,W.getErrorMessage)(e)}))}this.logger.info("Oversized tool result spilled to the result store",{tool:n,bytes:a.bytes,deduped:a.deduped}),r.json({success:!0,data:a})}catch(e){this.logger.warn("Could not spill an oversized tool result",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/workflow-policy/reload",async(e,t)=>{try{if(!this.reconfigureOpenCode)return void t.status(503).json({success:!1,error:"This action server cannot reconfigure itself (serve-only mode). The change applies on the next start."})
|
|
4878
4957
|
const e=await this.reconfigureOpenCode()
|
|
4879
4958
|
if(!e.success)return void t.status(500).json({success:!1,error:e.error??"reconfigure failed"})
|
|
4880
|
-
this.requestConfigReload("workflow-policy"),this.logger.info("Workflow tool policy pushed to the running project"),t.json({success:!0,data:{reconfigured:!0}})}catch(e){this.logger.error("Workflow policy reload failed",{error:(0,
|
|
4959
|
+
this.requestConfigReload("workflow-policy"),this.logger.info("Workflow tool policy pushed to the running project"),t.json({success:!0,data:{reconfigured:!0}})}catch(e){this.logger.error("Workflow policy reload failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/reload",async(e,r)=>{try{const s=String(e.body?.scope||"all"),{reloadOwnMcp:n,syncMcpConnections:o,syncMcpGrants:i}=await Promise.resolve().then(()=>t.t(73613,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729))
|
|
4881
4960
|
a()
|
|
4882
4961
|
const c={}
|
|
4883
|
-
"all"!==s&&"own"!==s||(c.own=await n({projectPath:this.options.projectPath,logger:this.logger})),"all"!==s&&"connections"!==s||!this.projectId||(c.connections=await o({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")}})),"grants"===s&&this.projectId&&(c.grants=await i({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-grants")}})),r.json({success:!0,data:c})}catch(e){this.logger.error("MCP reload failed",{error:(0,
|
|
4884
|
-
r.json({success:!0,data:{keys:s(this.options.projectPath,String(e.params.name))}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4962
|
+
"all"!==s&&"own"!==s||(c.own=await n({projectPath:this.options.projectPath,logger:this.logger})),"all"!==s&&"connections"!==s||!this.projectId||(c.connections=await o({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")}})),"grants"===s&&this.projectId&&(c.grants=await i({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-grants")}})),r.json({success:!0,data:c})}catch(e){this.logger.error("MCP reload failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/mcp/secrets/:name",async(e,r)=>{try{const{heldSecretKeys:s}=await Promise.resolve().then(()=>t.t(59373,23))
|
|
4963
|
+
r.json({success:!0,data:{keys:s(this.options.projectPath,String(e.params.name))}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/secrets/:name",async(e,r)=>{try{const{key:s,value:n}=e.body||{}
|
|
4885
4964
|
if(!s||"string"!=typeof s)return void r.status(400).json({success:!1,error:"key is required"})
|
|
4886
4965
|
if("string"!=typeof n||!n)return void r.status(400).json({success:!1,error:"value is required"})
|
|
4887
4966
|
const{setSecret:o,heldSecretKeys:i}=await Promise.resolve().then(()=>t.t(59373,23)),{invalidateMcpRegistry:a}=await t.e(729).then(()=>t(92729)),c=String(e.params.name)
|
|
4888
4967
|
if(o(this.options.projectPath,c,s,n),a(),this.logger.info("MCP secret stored on this host",{connection:c,key:s}),this.projectId){const{syncMcpConnections:e}=await Promise.resolve().then(()=>t.t(73613,23))
|
|
4889
|
-
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-secret-set")}})}r.json({success:!0,data:{keys:i(this.options.projectPath,c)}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4890
|
-
s(this.options.projectPath,o,String(e.params.key)),r.json({success:!0,data:{keys:n(this.options.projectPath,o)}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4968
|
+
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-secret-set")}})}r.json({success:!0,data:{keys:i(this.options.projectPath,c)}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.delete("/api/mcp/secrets/:name/:key",async(e,r)=>{try{const{clearSecret:s,heldSecretKeys:n}=await Promise.resolve().then(()=>t.t(59373,23)),o=String(e.params.name)
|
|
4969
|
+
s(this.options.projectPath,o,String(e.params.key)),r.json({success:!0,data:{keys:n(this.options.projectPath,o)}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.get("/api/mcp/namespaces",async(e,r)=>{try{if(!this.projectId)return void r.json({success:!0,data:{namespaces:[]}})
|
|
4891
4970
|
const{getRegisteredMcpServers:e,gatedNamespaces:s}=await t.e(729).then(()=>t(92729)),n=await e({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger})
|
|
4892
|
-
r.json({success:!0,data:{namespaces:s(n)}})}catch(e){this.logger.error("MCP namespaces lookup failed",{error:(0,
|
|
4971
|
+
r.json({success:!0,data:{namespaces:s(n)}})}catch(e){this.logger.error("MCP namespaces lookup failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/mcp/gate",async(e,r)=>{try{const{toolName:s,sessionId:n,args:o}=e.body||{}
|
|
4893
4972
|
if(!s)return void r.status(400).json({success:!1,error:"toolName is required"})
|
|
4894
4973
|
if(!this.projectId)return void r.json({success:!0,data:{allow:!0,verdict:"not-external",reason:"No project"}})
|
|
4895
4974
|
const{gateMcpCall:i}=await t.e(729).then(()=>t(92729)),a=await i({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{toolName:String(s),sessionId:n?String(n):void 0,args:o})
|
|
4896
4975
|
if(a.allow||this.logger.info("MCP call refused at the gate",{toolName:s,verdict:a.verdict}),"not-external"!==a.verdict){const{getRegisteredMcpServers:r,gatedNamespaces:i,splitToolKey:c,summariseArgs:l}=await t.e(729).then(()=>t(92729)),{openMcpReceipt:d}=await t.e(111).then(()=>t(56111)),u=await r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}),h=c(String(s),i(u))
|
|
4897
|
-
h&&await d({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{connection:h.server,tool:h.tool,target:l(o),sessionId:n?String(n):void 0,callId:e.body?.callId?String(e.body.callId):void 0,verdict:a.verdict,allowed:a.allow,reason:a.reason})}r.json({success:!0,data:a})}catch(e){this.logger.error("MCP gate failed",{error:(0,
|
|
4976
|
+
h&&await d({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{connection:h.server,tool:h.tool,target:l(o),sessionId:n?String(n):void 0,callId:e.body?.callId?String(e.body.callId):void 0,verdict:a.verdict,allowed:a.allow,reason:a.reason})}r.json({success:!0,data:a})}catch(e){this.logger.error("MCP gate failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/hook-event",async(e,r)=>{try{const{eventType:s,sessionId:n,variables:o,timing:i}=e.body
|
|
4898
4977
|
if(!s)return void r.status(400).json({success:!1,error:"eventType is required"})
|
|
4899
4978
|
if("session_idle"===s&&n){const{notifyInboxChatSessionIdle:e}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
4900
4979
|
e(String(n))
|
|
4901
4980
|
const{notifyPlannerHandoffSessionIdle:r}=await t.e(991).then(()=>t(18991))
|
|
4902
4981
|
r(String(n))}const a=await this.hookExecutor.processEvent({eventType:s,sessionId:n||"",variables:o||{},timing:i})
|
|
4903
|
-
r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing failed",{error:(0,
|
|
4982
|
+
r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/tool-event",async(e,r)=>{try{const{toolName:s,sessionId:n,input:o,output:i,timing:a}=e.body||{}
|
|
4904
4983
|
if(!s||"before"!==a&&"after"!==a)return void r.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
|
|
4905
4984
|
if(n){const{notifyPlannerHandoffToolEvent:e}=await t.e(991).then(()=>t(18991))
|
|
4906
4985
|
e(String(n),String(s),o)}if("after"===a&&e.body?.callId&&this.projectId){const{closeMcpReceipt:r}=await t.e(111).then(()=>t(56111))
|
|
4907
|
-
r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,
|
|
4986
|
+
r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,Y.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
|
|
4908
4987
|
let l=0
|
|
4909
4988
|
for(const e of c)l+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:n||"",variables:e.variables,timing:e.timing})).triggered
|
|
4910
|
-
this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,
|
|
4989
|
+
this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,W.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/web-call",async(e,r)=>{try{const{tool:s,sessionId:n,callId:o,input:i,metadata:a,error:c}=e.body||{}
|
|
4911
4990
|
if("string"!=typeof s||!this.projectId)return void r.status(400).json({success:!1,error:"tool is required"})
|
|
4912
4991
|
const{writeWebReceipt:l}=await t.e(9).then(()=>t(82009)),d=await l({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{tool:s,sessionId:n?String(n):void 0,callId:o?String(o):void 0,input:i,metadata:a,error:"string"==typeof c?c:void 0})
|
|
4913
|
-
r.json({success:!0,data:{recorded:d}})}catch(e){r.status(500).json({success:!1,error:(0,
|
|
4992
|
+
r.json({success:!0,data:{recorded:d}})}catch(e){r.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/spawn-planner",this.handleSpawnPlanner.bind(this)),e.post("/api/setup-run",this.handleSetupRun.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.post("/api/main-change-at-landing",async(e,r)=>{try{const{requestId:s,landingSha:n}=e.body??{}
|
|
4914
4993
|
if(!s||!n||!this.projectId)return void r.status(400).json({success:!1,error:"requestId and landingSha are required"})
|
|
4915
4994
|
const{mainChangeAtLanding:o}=await Promise.resolve().then(()=>t(69689)),i=await o({projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient},s,n)
|
|
4916
|
-
r.json({success:!0,data:i})}catch(e){r.json({success:!0,data:null,error:(0,
|
|
4995
|
+
r.json({success:!0,data:i})}catch(e){r.json({success:!0,data:null,error:(0,W.getErrorMessage)(e)})}}),e.post("/api/flesh-out-request",async(e,t)=>{try{if(!(0,De.validateRequest)(e,t,this.projectId,["requestId"]))return
|
|
4917
4996
|
const r=this.projectId
|
|
4918
4997
|
if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
|
|
4919
4998
|
const{requestId:s,sourceSessionId:n}=e.body||{}
|
|
@@ -4921,84 +5000,87 @@ let o=""
|
|
|
4921
5000
|
try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${s}`)
|
|
4922
5001
|
o=(e?.data||e)?.title||""}catch{}let i=""
|
|
4923
5002
|
if(n)try{const e=this.getOpenCodeUrl()
|
|
4924
|
-
e&&(i=(await(0,
|
|
5003
|
+
e&&(i=(await(0,rt.collectMessages)(e,n,this.logger)).filter(e=>e.text&&("user"===e.role||"assistant"===e.role)).slice(-8).map(e=>`${e.role}: ${e.text}`).join("\n").slice(0,4e3))}catch{}const a=["[Planner Flavor: request-shaping] You are fleshing out a DRAFT request in the background (no user is watching this session). This is a scoped single-request job: no plan document, no decomposition \u2014 do not use plan_write.",`Draft request id: ${s}${o?` | one-liner: "${o}"`:""}.`,i?`\nContext from the session that requested this:\n${i}\n`:"","Author ONE focused request:","1. Read the draft with get_request_information.","2. Using the one-liner (and any context above) plus a quick look at the project for grounding, write a clear DESCRIPTION and 1-5 specific, testable ACCEPTANCE CRITERIA (phrased so a confined test run could demonstrate them; any content or decision that must come from the user becomes its own criterion carrying the literal marker [user-input]).","3. Apply them to the draft with request_operations (update). Keep the title unless it is unclear. Do NOT register it and do NOT change its status; it stays a draft for the user to review and register.","4. BOUNDARY: if this is bigger than one focused request (several independent pieces, open-ended), do NOT cram it into one. Leave a short note in the description recommending a quest or a split, and stop.","Work autonomously from the information you have; do not wait for user input. When done, end your session."].filter(Boolean).join("\n"),c=await(0,ke.spawnActionAgent)({agent:"planner",prompt:a,cwd:this.options.projectPath,projectPath:this.options.projectPath,projectId:r,title:o?`Flesh out: ${o.slice(0,60)}`:"Flesh out draft request",parentSessionId:n,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0,skipWorktree:!0,shapingRequestLocalId:s})
|
|
4925
5004
|
if(!c.success||!c.sessionId)return void t.status(500).json({success:!1,error:c.error||"Failed to spawn request-shaping planner"})
|
|
4926
|
-
t.json({success:!0,data:{sessionId:c.sessionId,requestId:s}})}catch(e){(0,
|
|
5005
|
+
t.json({success:!0,data:{sessionId:c.sessionId,requestId:s}})}catch(e){(0,De.respondWithError)(t,this.logger,"Flesh out request",e)}}),e.post("/api/environments/verify",async(e,t)=>{const{config:r,smoke:s,branchName:n,requestId:o}=e.body||{}
|
|
4927
5006
|
if(!r||"object"!=typeof r)return void t.status(400).json({success:!1,error:"config (environment config) is required"})
|
|
4928
5007
|
const i="string"==typeof s&&s.trim()?s.trim():"true",a=this.options.projectPath,c=this.projectId
|
|
4929
5008
|
if(!c)return void t.status(400).json({success:!1,error:"project not registered"})
|
|
4930
5009
|
const l={composeFile:r.composeFile,gateImage:r.image,connEnv:r.connEnv,install:r.install,artifacts:r.artifacts,shellInit:r.shellInit,projectId:c}
|
|
4931
5010
|
let d
|
|
4932
|
-
try{await(0,
|
|
4933
|
-
const e=await(0,
|
|
4934
|
-
d=
|
|
4935
|
-
const u=await(0,
|
|
4936
|
-
h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,
|
|
5011
|
+
try{await(0,qe.sweepStaleSetupWorktrees)(a,this.logger)
|
|
5012
|
+
const e=await(0,qe.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,qe.generateSetupId)()
|
|
5013
|
+
d=D.default.join((0,qe.getSetupWorktreesBase)(a),`verify-${s}`),await(0,qe.createSetupWorktree)(a,d,e,this.logger)
|
|
5014
|
+
const u=await(0,Le.withContainerLabels)({"alchemy.purpose":"verify","alchemy.request":"string"==typeof o?o:void 0},()=>(0,Fe.runConfinedGate)(d,[{name:"verify",command:i,required:!0,timeout:600}],l,{projectName:`verify-${s}`})),h=!String(r.shellInit??"").trim()&&(0,Be.carriesOwnShellInit)(i),p=h?["The smoke command sources its own setup, and the environment declares no shellInit. This verify proves the smoke command, not the environment: docker exec runs no ENTRYPOINT and reads no login profile, so every LATER command gets a bare PATH. Move the preamble into config.shellInit and verify again with the command alone."]:[]
|
|
5015
|
+
h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}finally{if(d&&q.default.existsSync(d))try{await(0,He.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,Ye.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Qe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Xe.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
|
|
4937
5016
|
if(!t||!/^[A-Za-z0-9_-]+$/.test(e))return null
|
|
4938
5017
|
const r=await this.apiClient.get(`/api/projects/${t}/documents/${e}`).catch(()=>null),s=r?.data??r
|
|
4939
|
-
return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,
|
|
4940
|
-
try{
|
|
4941
|
-
r.json({success:!0,message:"Update started. The CLI will restart when complete."}),n("npm install -g alchemylab@latest",{timeout:12e4},(e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout(()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)},1e3))})}catch(e){r.json({success:!1,error:(0,
|
|
4942
|
-
return void t.json({status:e?"degraded":"healthy",service:"alchemylab-action-server"})}const r=this.remoteLastOkAt?Date.now()-this.remoteLastOkAt:null,s=Date.now()-this.startTime.getTime(),n=this.remoteFailures>0?"degraded":null!==r?r<
|
|
5018
|
+
return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,Je.registerWorkspaceRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath}),e.get("/api/stuck-sessions",this.handleGetStuckSessions.bind(this)),e.post("/api/stuck-sessions/test-inject",this.handleTestInjectStuckSession.bind(this)),e.post("/api/stuck-sessions/:sessionId/retry",this.handleRetryStuckSession.bind(this)),e.get("/api/version-warnings",(e,t)=>{t.json({success:!0,data:this.versionWarnings})}),e.post("/api/self-update",async(e,r)=>{try{const{execSync:e}=await Promise.resolve().then(()=>t.t(35317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
|
|
5019
|
+
try{q.default.accessSync(s,q.default.constants.W_OK)}catch{return void r.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:n}=await Promise.resolve().then(()=>t.t(35317,23))
|
|
5020
|
+
r.json({success:!0,message:"Update started. The CLI will restart when complete."}),n("npm install -g alchemylab@latest",{timeout:12e4},(e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout(()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)},1e3))})}catch(e){r.json({success:!1,error:(0,W.getErrorMessage)(e)})}}),e.use("/api/opencode",(e,t)=>this.handleOpenCodeProxy(e,t)),e}handleHealth(e,t){if(!0!==t.locals.provenCaller){const e=this.remoteFailures>0&&this.isPolling
|
|
5021
|
+
return void t.json({status:e?"degraded":"healthy",service:"alchemylab-action-server"})}const r=this.remoteLastOkAt?Date.now()-this.remoteLastOkAt:null,s=Date.now()-this.startTime.getTime(),n=this.remoteFailures>0?"degraded":null!==r?r<pt.REMOTE_STALE_MS?"ok":"degraded":s<pt.REMOTE_STALE_MS?"unknown":"degraded",o={url:this.options.remoteServerUrl??process.env.REMOTE_SERVER_URL??null,state:n,ok:"ok"===n,consecutiveFailures:this.remoteFailures,lastOkAt:this.remoteLastOkAt?new Date(this.remoteLastOkAt).toISOString():null,failingForMs:"degraded"===n?r??s:null,lastError:this.remoteLastError,polling:this.isPolling}
|
|
4943
5022
|
t.json({status:"degraded"===n&&this.isPolling?"degraded":"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),build:(0,f.readBuildInfo)(),projectId:this.projectId,projectPath:this.options.projectPath,remote:o,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
|
|
4944
5023
|
if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),n=e?await r.getToken():null
|
|
4945
|
-
t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,
|
|
4946
|
-
try{if(!
|
|
4947
|
-
const e=
|
|
4948
|
-
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,
|
|
5024
|
+
t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,W.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5025
|
+
try{if(!q.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
|
|
5026
|
+
const e=q.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
|
|
5027
|
+
t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,W.getErrorMessage)(e)})}}async followUnchosenDefault(){const e=(0,ue.unchosenDefaultToFollow)(this.cachedDefaultModel,this.defaultModelUnchosen,this.configurer.getConnectedProviders())
|
|
4949
5028
|
if(!e||!this.projectId||this.followingDefaultTo===e)return
|
|
4950
5029
|
this.followingDefaultTo=e
|
|
4951
5030
|
const t=this.cachedDefaultModel
|
|
4952
|
-
try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,
|
|
5031
|
+
try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,W.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,W.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
|
|
4953
5032
|
let t=0
|
|
4954
5033
|
for(const[r,s]of this.activeMcpCalls)s<e&&(this.activeMcpCalls.delete(r),t++)
|
|
4955
|
-
t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<
|
|
5034
|
+
t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<pt.PROMPT_DELIVERY_BUSY_MS)return!0
|
|
4956
5035
|
try{const e=this.getOpenCodeUrl()
|
|
4957
5036
|
if(!e)return!1
|
|
4958
5037
|
const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
|
|
4959
5038
|
if(!t.ok)return!1
|
|
4960
5039
|
const r=await t.json()
|
|
4961
|
-
return Object.values(r).some(e=>!!e&&"idle"!==e.type)}catch(e){return this.logger.debug("Could not fetch OpenCode session status",{error:(0,
|
|
5040
|
+
return Object.values(r).some(e=>!!e&&"idle"!==e.type)}catch(e){return this.logger.debug("Could not fetch OpenCode session status",{error:(0,W.getErrorMessage)(e)}),!1}}async busySessionIds(){try{const e=this.getOpenCodeUrl()
|
|
4962
5041
|
if(!e)return[]
|
|
4963
5042
|
const t=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
|
|
4964
5043
|
if(!t.ok)return[]
|
|
4965
5044
|
const r=await t.json()
|
|
4966
|
-
return Object.entries(r).filter(([,e])=>!!e&&"idle"!==e.type).map(([e])=>e)}catch{return[]}}notePendingConfigReload(e,t){this.pendingConfigReload||(this.pendingConfigReload=!0,this.pendingConfigReloadSince=Date.now()),this.startPendingReloadRecheck(),this.logger.info("Deferring OpenCode config reload until MCP calls and session turns are idle",{context:e,inFlightMcp:t})}startPendingReloadRecheck(){this.pendingReloadRecheckInterval||(this.pendingReloadRecheckInterval=setInterval(()=>{this.fireDeferredReloadIfIdle("recheck")},
|
|
5045
|
+
return Object.entries(r).filter(([,e])=>!!e&&"idle"!==e.type).map(([e])=>e)}catch{return[]}}notePendingConfigReload(e,t){this.pendingConfigReload||(this.pendingConfigReload=!0,this.pendingConfigReloadSince=Date.now()),this.startPendingReloadRecheck(),this.logger.info("Deferring OpenCode config reload until MCP calls and session turns are idle",{context:e,inFlightMcp:t})}startPendingReloadRecheck(){this.pendingReloadRecheckInterval||(this.pendingReloadRecheckInterval=setInterval(()=>{this.fireDeferredReloadIfIdle("recheck")},pt.RELOAD_RECHECK_MS),this.pendingReloadRecheckInterval.unref?.())}stopPendingReloadRecheck(){this.pendingReloadRecheckInterval&&(clearInterval(this.pendingReloadRecheckInterval),this.pendingReloadRecheckInterval=void 0)}async fireDeferredReloadIfIdle(e){if(this.pendingConfigReload){if(!this.deferredReloadCheckInFlight){this.deferredReloadCheckInFlight=!0
|
|
4967
5046
|
try{const t=Date.now()-this.pendingConfigReloadSince
|
|
4968
|
-
if(t<
|
|
5047
|
+
if(t<pt.RELOAD_DEFER_CAP_MS){if(this.activeMcpCalls.size>0)return
|
|
4969
5048
|
if(await this.anySessionBusy())return}else this.logger.warn("Config reload deferred past cap; firing despite activity (staleness beats permanent deferral)",{trigger:e,waitedMs:t})
|
|
4970
5049
|
this.pendingConfigReload=!1,this.stopPendingReloadRecheck(),this.logger.info("Firing deferred OpenCode config reload",{trigger:e,waitedMs:t}),await this.reloadOpenCodeConfigNow(`deferred-${e}`)}finally{this.deferredReloadCheckInFlight=!1}}}else this.stopPendingReloadRecheck()}async applyClaudeThinking(e){const t=this.claudeThinkingSeen
|
|
4971
|
-
this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,
|
|
5050
|
+
this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,W.getErrorMessage)(e)})))}async applyAnthropicCompaction(e){const t=(0,Ee.generatedOpencodeConfigPath)(this.options.projectPath)
|
|
5051
|
+
try{const r=JSON.parse(await q.default.promises.readFile(t,"utf8"))
|
|
5052
|
+
if(!r||"object"!=typeof r||!(0,H.withAnthropicCompaction)(r,e))return
|
|
5053
|
+
await q.default.promises.writeFile(t,JSON.stringify(r,null,2),"utf8"),this.logger.info("Claude's own compaction setting changed; reloading the serve's config",{on:e}),await this.requestConfigReload("anthropic-compaction")}catch(e){this.logger.debug("Claude's own compaction setting not applied yet",{error:(0,W.getErrorMessage)(e)})}}async applyPause(e){const t=this.getOpenCodeUrl()?.replace(/\/$/,"")
|
|
4972
5054
|
if(t&&(this.pauseApplied?.url!==t||this.pauseApplied.paused!==e))try{const r=await fetch(`${t}/config/pause`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paused:e}),signal:AbortSignal.timeout(15e3)})
|
|
4973
5055
|
if(!r.ok)return void this.logger.warn("The serve did not take the pause",{paused:e,status:r.status})
|
|
4974
5056
|
const s=await r.json().catch(()=>({}))
|
|
4975
5057
|
this.pauseApplied={url:t,paused:e}
|
|
4976
|
-
const n=(0,
|
|
5058
|
+
const n=(0,at.setProjectPaused)(e)
|
|
4977
5059
|
if(e){const e=Array.isArray(s.stopped)?s.stopped:[]
|
|
4978
5060
|
let t=0
|
|
4979
|
-
for(const r of e)t+=await(0,
|
|
5061
|
+
for(const r of e)t+=await(0,G.cancelRunningExecs)({sessionId:r}).catch(()=>0)
|
|
4980
5062
|
const r=this.projectId?await(0,j.experimentRegistry)().releaseForPause(this.projectId).catch(()=>[]):[]
|
|
4981
|
-
return void this.logger.info("Project paused: every turn stopped and held",{stopped:e.length,commandsCancelled:t,boxesReleased:r.length})}(0,
|
|
5063
|
+
return void this.logger.info("Project paused: every turn stopped and held",{stopped:e.length,commandsCancelled:t,boxesReleased:r.length})}(0,ct.extendChatWatches)(n)
|
|
4982
5064
|
const o=Array.isArray(s.waiting)?s.waiting:[],i={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel}
|
|
4983
5065
|
let a=0
|
|
4984
|
-
for(const e of o)(await(0,d.deliverPrompt)(i,e,
|
|
4985
|
-
this.logger.info("Project resumed",{waiting:o.length,woken:a,pausedForMs:n})}catch(t){this.logger.warn("Could not apply the pause to the serve",{paused:e,error:(0,
|
|
5066
|
+
for(const e of o)(await(0,d.deliverPrompt)(i,e,at.RESUME_TEXT,{kind:"inserted-prompt"}).catch(()=>({ok:!1}))).ok?a++:this.logger.warn("A session held by the pause could not be woken",{sessionId:e})
|
|
5067
|
+
this.logger.info("Project resumed",{waiting:o.length,woken:a,pausedForMs:n})}catch(t){this.logger.warn("Could not apply the pause to the serve",{paused:e,error:(0,W.getErrorMessage)(t)})}}async reloadOpenCodeConfigNow(e){try{const t=this.getOpenCodeUrl()
|
|
4986
5068
|
if(!t)return!1
|
|
4987
5069
|
const r=await fetch(`${t}/config/reload`,{method:"POST",signal:AbortSignal.timeout(5e3)})
|
|
4988
5070
|
if(r.ok){this.logger.info("Triggered OpenCode config reload",{context:e})
|
|
4989
5071
|
const t=await r.json().catch(()=>null)
|
|
4990
|
-
return!0!==t?.deferred&&(0,a.noteServeLoadedLogins)(),this.stuckSessionWatchdog?.capacityChanged(`config-reloaded:${e}`),(0,
|
|
4991
|
-
return t>0||await this.anySessionBusy()?(this.notePendingConfigReload(e,t),"deferred"):await this.reloadOpenCodeConfigNow(e)?"reloaded":"failed"}async syncOpencodeConfigModel(e){const t=(0,
|
|
4992
|
-
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)
|
|
4993
5075
|
if(!s||"object"!=typeof s||s.model===e)return
|
|
4994
|
-
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
|
|
4995
5077
|
const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
|
|
4996
5078
|
if(!e?.length)return
|
|
4997
5079
|
const t=await this.configurer.fetchProjectFromRemote(this.projectId)
|
|
4998
5080
|
this.ownerGuidanceStale=!1
|
|
4999
|
-
const r=await this.configurer.agentPreambleFor(this.projectId,t?.metadata?.globalSystemPrompt),s=
|
|
5081
|
+
const r=await this.configurer.agentPreambleFor(this.projectId,t?.metadata?.globalSystemPrompt),s=D.default.join(this.options.projectPath,".opencode","agent"),n=this.cachedDefaultModel||M.FLEET_DEFAULT_MODEL,o=await this.configurer.resolveWorkflowEffort(this.projectId),i=await this.mcpGrantInputs(this.projectId),a=this.agentConfigFingerprint()
|
|
5000
5082
|
this.configurer.clearAgentMarkdowns()
|
|
5001
|
-
for(const s of e)s.enabled&&this.configurer.setAgentMarkdown(s.name,this.configurer.generateAgentMarkdown(s,"alchemy",n,r,{effort:o,extraTools:(0,
|
|
5083
|
+
for(const s of e)s.enabled&&this.configurer.setAgentMarkdown(s.name,this.configurer.generateAgentMarkdown(s,"alchemy",n,r,{effort:o,extraTools:(0,me.agentToolGrants)(i.servers,s.name,i.permissions),limits:(0,M.modelLimitsOf)(t?.metadata),claudeThinking:(0,H.claudeThinkingOf)(t?.metadata)}))
|
|
5002
5084
|
this.configurer.setAgentMarkdown("explore",this.configurer.buildExploreMarkdown(r)),await this.configurer.removeOnDiskAgentFiles(s),this.lastAgentResyncModel=n,await this.syncOpencodeConfigModel(n),this.logger.info("Re-synced agent markdowns after model change",{model:n,count:e.length}),this.agentConfigFingerprint()!==a?(this.stuckSessionWatchdog?.capacityChanged("agents-resynced"),await this.requestConfigReload("agent-resync")):this.logger.info("Agent definitions unchanged; no config reload",{context:"agent-resync"})}async checkOwnerGuidanceApply(){if(!this.ownerGuidanceStale||!this.projectId)return
|
|
5003
5085
|
const e=this.getOpenCodeUrl()
|
|
5004
5086
|
let t=!1,r=null
|
|
@@ -5007,10 +5089,10 @@ if(!s.ok)return
|
|
|
5007
5089
|
const o=await s.json(),i=n.ok?await n.json():{}
|
|
5008
5090
|
t=Object.values(i??{}).some(e=>"busy"===e?.type)||this.activeMcpCalls.size>0
|
|
5009
5091
|
for(const e of o){const t=Number(e.time?.updated??e.time?.created??0)
|
|
5010
|
-
t&&(null===r||t>r)&&(r=t)}}catch{return}"apply"===(0,
|
|
5011
|
-
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")
|
|
5012
5094
|
for(const t of[...this.configurer.getAgentMarkdowns()].sort((e,t)=>e.name.localeCompare(t.name)))e.update(t.name).update("\0").update(t.markdown).update("\x01")
|
|
5013
|
-
try{e.update(
|
|
5095
|
+
try{e.update(q.default.readFileSync((0,Ee.generatedOpencodeConfigPath)(this.options.projectPath),"utf-8"))}catch{}return e.digest("hex")}async handleSyncAgents(e,t){try{const e=this.projectId
|
|
5014
5096
|
if(!e)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
5015
5097
|
this.logger.info("Syncing agents from remote",{projectId:e})
|
|
5016
5098
|
const r=await this.configurer.fetchAgentsFromRemote(e)
|
|
@@ -5019,19 +5101,19 @@ const s=await this.configurer.fetchProjectFromRemote(e)
|
|
|
5019
5101
|
this.logger.debug("Fetched project for global prompt",{projectId:e,hasProject:!!s,hasGlobalSystemPrompt:!!s?.metadata?.globalSystemPrompt})
|
|
5020
5102
|
const n=await this.configurer.agentPreambleFor(e,s?.metadata?.globalSystemPrompt)
|
|
5021
5103
|
n?this.logger.info("Using global system prompt from project metadata",{projectId:e,promptLength:n.length}):this.logger.debug("No global system prompt found in project metadata")
|
|
5022
|
-
const o=
|
|
5023
|
-
await
|
|
5024
|
-
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()
|
|
5025
5107
|
this.configurer.clearAgentMarkdowns()
|
|
5026
5108
|
let h=0
|
|
5027
|
-
for(const e of c)try{this.configurer.setAgentMarkdown(e.name,this.configurer.generateAgentMarkdown(e,i,a,n,{effort:l,extraTools:(0,
|
|
5028
|
-
try{const e=(0,
|
|
5029
|
-
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)
|
|
5030
5112
|
if(r.agent){for(const e of Object.values(r.agent))e&&"object"==typeof e&&"prompt"in e&&delete e.prompt
|
|
5031
|
-
|
|
5113
|
+
q.default.writeFileSync(e,JSON.stringify(r,null,2),"utf-8")}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,W.getErrorMessage)(e)})}const p=this.agentConfigFingerprint()===u
|
|
5032
5114
|
p&&this.logger.info("Agent definitions unchanged; no config reload",{context:"sync-agents"}),this.ownerGuidanceStale=!1
|
|
5033
5115
|
const f=p?"unchanged":await this.requestConfigReload("sync-agents"),m="reloaded"===f
|
|
5034
|
-
t.json({success:!0,message:`Synced ${h} agent(s) into the running serve${m?" - config reloaded":"deferred"===f?" - config reload deferred until MCP is idle":""}`,count:h,reloaded:m,reloadDeferred:"deferred"===f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,
|
|
5116
|
+
t.json({success:!0,message:`Synced ${h} agent(s) into the running serve${m?" - config reloaded":"deferred"===f?" - config reload deferred until MCP is idle":""}`,count:h,reloaded:m,reloadDeferred:"deferred"===f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
|
|
5035
5117
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
5036
5118
|
const{sessionId:s,requestId:n,summary:o,status:i}=e.body
|
|
5037
5119
|
if(!s)return void t.status(400).json({success:!1,error:"sessionId is required"})
|
|
@@ -5039,26 +5121,26 @@ this.logger.info("Direct report creation",{sessionId:s,requestId:n,status:i})
|
|
|
5039
5121
|
const a=[]
|
|
5040
5122
|
try{const e=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),t=e?.data||e||[]
|
|
5041
5123
|
let n=s
|
|
5042
|
-
for(const e of t)a.push({ancestorId:e.externalSessionId,childId:n}),n=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,
|
|
5124
|
+
for(const e of t)a.push({ancestorId:e.externalSessionId,childId:n}),n=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,W.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
|
|
5043
5125
|
let c,l=o
|
|
5044
5126
|
if(!l&&n)try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${n}`),t=e?.data||e
|
|
5045
5127
|
c=t?.title
|
|
5046
5128
|
const s=t?.completionSummary
|
|
5047
|
-
s?l=s:c&&(l=`Done: ${c}`)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:n,error:(0,
|
|
5129
|
+
s?l=s:c&&(l=`Done: ${c}`)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:n,error:(0,W.getErrorMessage)(e)})}l||(l=`Completed request ${n||"unknown"}`)
|
|
5048
5130
|
const d=n||s
|
|
5049
5131
|
if(this.directReportDedup.size>=1e3){const e=this.directReportDedup.keys().next().value
|
|
5050
5132
|
void 0!==e&&this.directReportDedup.delete(e)}this.directReportDedup.has(d)||this.directReportDedup.set(d,new Set)
|
|
5051
5133
|
const u=this.directReportDedup.get(d),h=a.filter(({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1)),p=await Promise.allSettled(h.map(async({ancestorId:e,childId:t})=>{const s=await this.apiClient.post(`/api/projects/${r}/session-messages/reports`,{fromSessionId:t,toSessionId:e,status:i||"success",summary:l}),n=s?.data||s
|
|
5052
|
-
return u.add(e),n?.messageCode&&this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(n.messageCode)}/acknowledge`,{}).catch(e=>this.logger.debug("Direct report pre-ack failed (non-fatal)",{messageCode:n.messageCode,error:(0,
|
|
5134
|
+
return u.add(e),n?.messageCode&&this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(n.messageCode)}/acknowledge`,{}).catch(e=>this.logger.debug("Direct report pre-ack failed (non-fatal)",{messageCode:n.messageCode,error:(0,W.getErrorMessage)(e)})),this.logger.info("Direct report created for ancestor",{messageCode:n?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:n?.messageCode,fromSessionId:t,content:n?.content}})),f=[]
|
|
5053
5135
|
for(const e of p)"fulfilled"===e.status&&f.push(e.value)
|
|
5054
|
-
for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,
|
|
5136
|
+
for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,W.getErrorMessage)(e.reason)})
|
|
5055
5137
|
if(setTimeout(()=>{this.directReportDedup.delete(d)},3e5),n)try{const e=await this.apiClient.get(`/api/projects/${r}/session-request-associations/by-session/${encodeURIComponent(s)}?activeOnly=true`),t=e?.data||e||[],n=(await Promise.allSettled(t.map(e=>this.apiClient.post(`/api/projects/${r}/session-request-associations/end`,{sessionId:s,requestLocalId:e.requestLocalId})))).filter(e=>"rejected"===e.status).length
|
|
5056
|
-
this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:n})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,
|
|
5138
|
+
this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:n})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,W.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:f,report:f[0]?{messageCode:f[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:r}=e.params,s=this.projectId
|
|
5057
5139
|
if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),n=e?.data||e
|
|
5058
5140
|
if(this.logger.info("Agent type DB lookup",{sessionId:r,dbAgentType:n?.agentType||null,dbId:n?.id||n?.localId||null}),n?.agentType){const e=this.configurer.agentModelOf(n.agentType)
|
|
5059
|
-
return void t.json({success:!0,agentType:n.agentType,source:"db",...e?{model:e}:{}})}}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,
|
|
5141
|
+
return void t.json({success:!0,agentType:n.agentType,source:"db",...e?{model:e}:{}})}}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,W.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,s=this.projectId
|
|
5060
5142
|
if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/resolve/${encodeURIComponent(r)}`),n=e?.data?.parent?.externalSessionId||""
|
|
5061
|
-
return void t.json({success:!0,parentSessionId:n})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,
|
|
5143
|
+
return void t.json({success:!0,parentSessionId:n})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,W.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,W.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,W.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
|
|
5062
5144
|
if(!r)return void t.status(502).json({error:"OpenCode not running"})
|
|
5063
5145
|
const s=e.path.replace(/^\//,""),n=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",o=`${r}/${s}${n}`,i=e.headers.accept?.includes("text/event-stream")
|
|
5064
5146
|
if(i){const r=new AbortController,s=()=>{this.proxyAbortControllers.delete(r)},n=(async()=>{try{const e=await fetch(o,{headers:{Accept:"text/event-stream"},signal:r.signal})
|
|
@@ -5079,18 +5161,18 @@ if(!t||t===r)throw e
|
|
|
5079
5161
|
l=`${t}/${s}${n}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:r,newUrl:t}),c=await fetch(l,a)}if(204===c.status||"0"===c.headers.get("content-length"))return void t.status(c.status).end()
|
|
5080
5162
|
const d=c.headers.get("content-type")||""
|
|
5081
5163
|
let u
|
|
5082
|
-
try{u=d.includes("application/json")?await c.json():await c.text()}catch{return void t.status(c.status).end()}t.status(c.status),d&&t.setHeader("Content-Type",d),"string"==typeof u?t.send(u):t.json(u)}finally{this.inFlightServeProxyRequests--}}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,
|
|
5164
|
+
try{u=d.includes("application/json")?await c.json():await c.text()}catch{return void t.status(c.status).end()}t.status(c.status),d&&t.setHeader("Content-Type",d),"string"==typeof u?t.send(u):t.json(u)}finally{this.inFlightServeProxyRequests--}}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,W.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,W.getErrorMessage)(r)})}}async handleSpawnPlanner(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId,["planId","prompt"]))return
|
|
5083
5165
|
const{agent:r,planId:s,prompt:n,title:o,parentSessionId:i,flavor:a}=e.body||{},c="auto-planner"===r?"planner":r,l="auto-planner"===r||"autonomous"===a?"autonomous":void 0,d=new Set(["planner"])
|
|
5084
5166
|
if(c&&!d.has(c))return void t.status(400).json({success:!1,error:`agent must be one of: ${[...d].join(", ")}`})
|
|
5085
|
-
const u=await(0,
|
|
5167
|
+
const u=await(0,Se.spawnPlannerWithDoc)({agent:c||"planner",flavor:l,planId:s,prompt:n,title:o||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
|
|
5086
5168
|
if(!u.success||!u.sessionId)return void t.status(500).json({success:!1,error:u.error||"spawn failed"})
|
|
5087
|
-
t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,
|
|
5088
|
-
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
|
|
5089
5171
|
if(!1!==r)return this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r}),!0
|
|
5090
|
-
this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,
|
|
5091
|
-
const r=new
|
|
5092
|
-
return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,
|
|
5093
|
-
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)
|
|
5094
5176
|
if(!e)return
|
|
5095
5177
|
let t
|
|
5096
5178
|
try{const r=await fetch(`${e.replace(/\/$/,"")}/session?limit=5000`,{signal:AbortSignal.timeout(15e3)})
|
|
@@ -5103,15 +5185,15 @@ try{for(let e=0;e<1e3;e+=100){const n=await this.apiClient.get(`/api/projects/${
|
|
|
5103
5185
|
if(!Array.isArray(o)||0===o.length)break
|
|
5104
5186
|
for(const e of o){s++
|
|
5105
5187
|
const n=e?.externalSessionId??e?.external_session_id
|
|
5106
|
-
n&&!t.has(String(n))&&r.push({id:String(e.id),ext:String(n)})}if(o.length<100)break}}catch(e){return void this.logger.warn("vanished-session sweep could not read the session list; ending nothing",{error:(0,
|
|
5107
|
-
for(const e of r)try{await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${e.id}`,{status:"completed"}),n++}catch(t){o++,o<=3&&this.logger.warn("could not end a vanished session row",{sessionId:e.id,externalSessionId:e.ext,error:(0,
|
|
5108
|
-
return!!r.ok||404!==r.status&&null}catch{return null}}async handleSpawnSession(e,t){try{if(!(0,
|
|
5109
|
-
const{agent:r,prompt:s,requestId:n,parentSessionId:o,title:i,allowParallel:a,callerAgentType:c,shapingRequestLocalId:l,userOpened:d}=e.body,u=this.projectId,h=new
|
|
5110
|
-
p?.sessionId&&o&&!0!==d&&this.watchSubagentForParent(String(p.sessionId),String(o),String(r)),t.json({success:!0,data:p})}catch(e){(0,
|
|
5111
|
-
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
|
|
5112
5194
|
if(!s?.id)return
|
|
5113
5195
|
if(s.status&&"active"!==s.status)return
|
|
5114
|
-
await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${s.id}`,{status:"completed"}),this.logger.info("subagent idle rail: child session ended",{childSessionId:e,agent:t})}catch(t){this.logger.warn("subagent idle rail: could not end the child session row",{childSessionId:e,error:(0,
|
|
5196
|
+
await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${s.id}`,{status:"completed"}),this.logger.info("subagent idle rail: child session ended",{childSessionId:e,agent:t})}catch(t){this.logger.warn("subagent idle rail: could not end the child session row",{childSessionId:e,error:(0,W.getErrorMessage)(t)})}}watchSubagentForParent(e,r,s){try{const{onNextSessionIdle:n}=t(67078),o=n(e,()=>{(async()=>{try{const n=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?sessionId=${encodeURIComponent(e)}&direction=outgoing&limit=20`).catch(()=>null)
|
|
5115
5197
|
if((Array.isArray(n?.data)?n.data:[]).some(t=>String(t?.fromSessionId??t?.from_session_id??"")===e&&["report","question"].includes(String(t?.messageType??t?.message_type??""))))return this.logger.info("subagent idle rail: child already reported; parent not re-woken",{childSessionId:e,parentSessionId:r,agent:s}),void await this.endFinishedChildSession(e,s)
|
|
5116
5198
|
const{getOpenCodeServerUrl:o}=await Promise.resolve().then(()=>t(28148)),i=o(this.options.projectPath,this.logger)
|
|
5117
5199
|
if(!i)return
|
|
@@ -5124,11 +5206,11 @@ if(!u||!u.trim())return void await this.endFinishedChildSession(e,s)
|
|
|
5124
5206
|
const{deliverPrompt:h}=await Promise.resolve().then(()=>t(17039)),p={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger}
|
|
5125
5207
|
if(d.test(u)){const t=(this.leakedCallNudges.get(e)??0)+1
|
|
5126
5208
|
if(this.leakedCallNudges.set(e,t),t<=2)return await h(p,e,'[system] Your last reply wrote tool calls as plain text ("to=functions.\u2026"), so none of them ran: nothing you described was created. Make each of those calls now as real tool calls, then finish as you meant to.',{kind:"agent-context"}),this.logger.warn("subagent idle rail: tool calls came out as text; told the session and watching again",{childSessionId:e,agent:s,tries:t}),void this.watchSubagentForParent(e,r,s)
|
|
5127
|
-
u=`It wrote its tool calls as plain text instead of making them, and did again after being told twice, so nothing it describes below was actually created. Its last message:\n\n${u}`}await h(p,r,`[${s} finished] It did not file a report, so here is what it produced:\n\n${u.slice(0,8e3)}`,{kind:"subagent-idle"}),this.logger.info("subagent idle rail: woke the parent with the child's final message",{childSessionId:e,parentSessionId:r,agent:s}),await this.endFinishedChildSession(e,s)}catch(t){this.logger.warn("subagent idle rail failed (the parent stays asleep)",{childSessionId:e,parentSessionId:r,error:(0,
|
|
5128
|
-
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
|
|
5129
5211
|
const{requestId:s,parentSessionId:n,testProcessIds:o,commandNames:i,catchUp:a}=e.body||{},{handleRunTestsAction:c}=await Promise.resolve().then(()=>t(76555)),l={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},d=await c({requestId:s,parentSessionId:n,testProcessIds:o,commandNames:i,catchUp:a,mode:"auto"},l)
|
|
5130
5212
|
if(!d.success)return void r.status(500).json({success:!1,error:d.error||d.message})
|
|
5131
|
-
r.json({success:!0,data:d.data})}catch(e){(0,
|
|
5213
|
+
r.json({success:!0,data:d.data})}catch(e){(0,De.respondWithError)(r,this.logger,"Run tests now",e)}}async handleSetupRun(e,t){try{if(!(0,De.validateRequest)(e,t,this.projectId))return
|
|
5132
5214
|
const r=this.projectId
|
|
5133
5215
|
if(!r)return void t.status(400).json({success:!1,error:"project not registered"})
|
|
5134
5216
|
const s=String(e.body?.runId??"")
|
|
@@ -5137,21 +5219,21 @@ const n=await this.apiClient.get(`/api/projects/${r}/setup-runs/${s}`),o=n?.data
|
|
|
5137
5219
|
if(!o?.id)return void t.status(404).json({success:!1,error:`no setup run ${s}`})
|
|
5138
5220
|
if("running"!==o.state)return void t.json({success:!0,data:{skipped:`run is ${o.state}`}})
|
|
5139
5221
|
if(o.architectSessionId)return void t.json({success:!0,data:{sessionId:o.architectSessionId,already:!0}})
|
|
5140
|
-
const i=Array.isArray(o.parts)?o.parts:[],a=Array.isArray(o.asks)?o.asks:[],c=[o.askedBy,...a.map(e=>e?.from)].map(e=>e&&("request"===e.by||"worker"===e.by)&&/^req\d{6}$/.test(String(e.ref??""))?String(e.ref):void 0).find(Boolean),l=await(0,
|
|
5222
|
+
const i=Array.isArray(o.parts)?o.parts:[],a=Array.isArray(o.asks)?o.asks:[],c=[o.askedBy,...a.map(e=>e?.from)].map(e=>e&&("request"===e.by||"worker"===e.by)&&/^req\d{6}$/.test(String(e.ref??""))?String(e.ref):void 0).find(Boolean),l=await(0,qe.resolveSetupBranch)(this.apiClient,r,void 0,c,this.logger,this.options.projectPath),d=a.map(e=>String(e?.need??"")).filter(Boolean),u=a.find(e=>"checks"===e?.part&&("alchemist"===e?.from?.by||"user"===e?.from?.by))?.need,h=i.includes("checks")?"checks":i.includes("environment")?"environment":"commands"
|
|
5141
5223
|
let p,f
|
|
5142
|
-
if("checks"===h){const e=await(0,
|
|
5224
|
+
if("checks"===h){const e=await(0,qe.gateAsksOnRecord)(this.apiClient,r,this.logger)
|
|
5143
5225
|
let t=!1
|
|
5144
5226
|
try{const e=await this.apiClient.get(`/api/projects/${r}/test-processes`),s=Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[]
|
|
5145
5227
|
t=s.length>0}catch{}let s=!1
|
|
5146
|
-
try{s=await(0,
|
|
5147
|
-
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"
|
|
5148
5230
|
try{const t=await this.apiClient.get(`/api/projects/${r}`),s=(t?.data||t)?.metadata?.environmentSetupMode
|
|
5149
|
-
"auto"!==s&&"ask"!==s&&"interactive"!==s||(e=s)}catch{}p=async t=>(0,
|
|
5150
|
-
const m=await(0,
|
|
5151
|
-
if(!m.success||!m.sessionId)return await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/finish`,{system:!0,actor:"spawn",outcome:"stopped",summary:`The setup architect could not be started: ${String(m.error??"unknown error").slice(0,300)}`}).catch(e=>this.logger.warn("setup run spawn failed and the run could not be ended (the reconciler ends it)",{runId:o.id,error:(0,
|
|
5152
|
-
await this.apiClient.post(`/api/projects/${r}/setup-runs/${o.id}/started`,{architectSessionId:m.sessionId}),this.logger.info("setup run architect spawned",{runId:o.id,parts:i,sessionId:m.sessionId,branch:m.branch}),t.json({success:!0,data:{sessionId:m.sessionId,worktreePath:m.worktreePath,branch:m.branch}})}catch(e){(0,
|
|
5153
|
-
const e=this.projectId,t=(0,
|
|
5154
|
-
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
|
|
5155
5237
|
return r?.id?{id:r.id,architectSessionId:r.architectSessionId??null,startedAt:r.startedAt??null}:null},architectState:async e=>{if(!t)return"unknown"
|
|
5156
5238
|
const r=await this.serveHasSession(t,e)
|
|
5157
5239
|
if(!1===r)return"gone"
|
|
@@ -5159,37 +5241,37 @@ if(null===r)return"unknown"
|
|
|
5159
5241
|
try{const r=await fetch(`${t.replace(/\/$/,"")}/session/status`,{signal:AbortSignal.timeout(5e3)})
|
|
5160
5242
|
if(r.ok){const t=(await r.json())?.[e]
|
|
5161
5243
|
if(t&&"idle"!==t.type)return"busy"}}catch{return"unknown"}try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}/message?limit=6`,{signal:AbortSignal.timeout(5e3)})
|
|
5162
|
-
return r.ok?(0,
|
|
5244
|
+
return r.ok?(0,Q.architectTurnState)(await r.json()):"unknown"}catch{return"unknown"}},lastActivityAt:async e=>{if(!t)return null
|
|
5163
5245
|
try{const r=await fetch(`${t.replace(/\/$/,"")}/session/${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(5e3)})
|
|
5164
5246
|
if(!r.ok)return null
|
|
5165
5247
|
const s=Number((await r.json())?.time?.updated)
|
|
5166
5248
|
return Number.isFinite(s)&&s>0?s:null}catch{return null}},hasPendingQuestion:async t=>{const r=await this.apiClient.get(`/api/projects/${e}/session-messages?sessionId=${encodeURIComponent(t)}&direction=outgoing&messageType=question&status=pending&limit=5`).catch(()=>null)
|
|
5167
5249
|
return(Array.isArray(r?.data)?r.data:[]).some(e=>"pending"===String(e?.status??""))},remind:async(t,r)=>{await(0,d.deliverPrompt)({projectPath:this.options.projectPath,projectId:e,apiClient:this.apiClient,logger:this.logger},t,`[${r} is still running] Your setup run has not ended: call \`setup_finish\` with its outcome and a summary that answers every ask the run carries. If you still need the user's answer, ask it as one question.`,{kind:"agent-context"})},stop:async(t,r)=>{await this.apiClient.post(`/api/projects/${e}/setup-runs/${t}/finish`,{system:!0,actor:"reconciler",outcome:"stopped",summary:r})}})
|
|
5168
|
-
"none"!==r.action&&"running"!==r.action&&this.logger.info("setup run reconciled",r)}catch(e){this.logger.warn("setup run reconcile pass failed (the next pass looks again)",{error:(0,
|
|
5250
|
+
"none"!==r.action&&"running"!==r.action&&this.logger.info("setup run reconciled",r)}catch(e){this.logger.warn("setup run reconcile pass failed (the next pass looks again)",{error:(0,W.getErrorMessage)(e)})}await(0,qe.removeEndedSetupRunWorktrees)(this.options.projectPath,async()=>{const t=await this.apiClient.get(`/api/projects/${e}/setup-status`),r=(t?.data??t)?.running
|
|
5169
5251
|
return"string"==typeof r?.id?r.id:null},this.logger)}async handleGetAgentByName(e,t){try{const r=this.projectId
|
|
5170
5252
|
if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
|
|
5171
|
-
const{agentName:s}=e.params,n=(await(0,
|
|
5253
|
+
const{agentName:s}=e.params,n=(await(0,be.getProjectAgents)(this.apiClient,r)).find(e=>e.name===s)
|
|
5172
5254
|
if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
|
|
5173
|
-
t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,
|
|
5174
|
-
const e=(0,
|
|
5175
|
-
try{if(!
|
|
5176
|
-
const t=
|
|
5177
|
-
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)
|
|
5178
5260
|
const t=`http://127.0.0.1:${e}`
|
|
5179
5261
|
this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.serveStartedAt=Date.now(),this.stuckSessionWatchdog?.serveStarted(this.serveStartedAt),(async()=>{for(let e=0;e<10;e++){try{const t=await this.configurer.refreshConnectedProviders(this.options.projectPath)
|
|
5180
|
-
if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,
|
|
5262
|
+
if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,W.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,W.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
5181
5263
|
if(!t.ok)return
|
|
5182
|
-
const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,
|
|
5183
|
-
c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,
|
|
5264
|
+
const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,M.primaryProviderOf)(s),o=r?.default||{},[i,a]=Object.entries(o)[0]||[],c=n?(0,M.defaultModelFor)(n):i&&a?`${i}/${a}`:null
|
|
5265
|
+
c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,W.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.parkController?.isServeDown())return null
|
|
5184
5266
|
if(this.openCodePort)return`http://127.0.0.1:${this.openCodePort}`
|
|
5185
5267
|
if(this.openCodeUrlCache&&Date.now()-this.openCodeUrlCache.timestamp<3e4)return this.openCodeUrlCache.url
|
|
5186
|
-
const e=(0,
|
|
5268
|
+
const e=(0,tt.getOpenCodeUrlFromConfig)(this.options.projectPath)
|
|
5187
5269
|
return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async register(){const e=this.readProjectConfig()
|
|
5188
5270
|
if(!e?.id)throw new Error("No project configuration found")
|
|
5189
|
-
this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.cachedDefaultModel&&this.lastAgentResyncModel!==this.cachedDefaultModel&&this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Post-registration agent markdown re-sync failed",{error:(0,
|
|
5190
|
-
try{const e=(0,
|
|
5191
|
-
if(e){const t=JSON.parse(
|
|
5192
|
-
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}}
|
|
5193
5275
|
this.logger.info("Registering with remote",{projectId:this.projectId})
|
|
5194
5276
|
const r=await this.apiClient.post("/api/servers/register",t)
|
|
5195
5277
|
if(this.registeredOnce=!0,r.success&&r.data?.serverId){this.serverId=r.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId}),this.startHeartbeat()
|
|
@@ -5199,18 +5281,18 @@ for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] $
|
|
|
5199
5281
|
this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}async applyVersionWarnings(e){if(e.map(e=>`${e.component}:${e.severity}`).join(",")!==this.versionWarnings.map(e=>`${e.component}:${e.severity}`).join(",")){for(const t of e)"required"===t.severity?(this.logger.error(`[UPDATE REQUIRED] ${t.message}`),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${t.message}\x1b[0m\n`)):(this.logger.warn(`[UPDATE RECOMMENDED] ${t.message}`),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
|
|
5200
5282
|
this.versionWarnings=e,!e.some(e=>"required"===e.severity)&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){if(!this.serverId)return
|
|
5201
5283
|
if(this.heartbeatInterval)return
|
|
5202
|
-
this.registryHeartbeatInterval=setInterval(()=>{if(this.projectId)try{(0,
|
|
5203
|
-
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)
|
|
5204
5286
|
e.length&&await this.apiClient.post(`/api/projects/${this.projectId}/plan-usage-readings`,{providers:e})}catch{}}
|
|
5205
5287
|
setTimeout(()=>{e()},2e4).unref?.(),this.planUsageInterval=setInterval(()=>{e()},6e5),"function"==typeof this.planUsageInterval.unref&&this.planUsageInterval.unref(),this.runningReportInterval=setInterval(()=>{this.reportRunningSessions(!0)},15e3),"function"==typeof this.runningReportInterval.unref&&this.runningReportInterval.unref(),this.heartbeatInterval=setInterval(async()=>{try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings??[]
|
|
5206
|
-
await this.applyVersionWarnings(t)}catch(e){const t=(0,
|
|
5288
|
+
await this.applyVersionWarnings(t)}catch(e){const t=(0,W.getErrorMessage)(e)
|
|
5207
5289
|
if(t.includes("Server not found or inactive")){this.logger.warn("Heartbeat hit a stale registration; re-registering",{staleServerId:this.serverId})
|
|
5208
|
-
try{await this.register(),this.logger.info("Re-registered after stale heartbeat",{serverId:this.serverId})}catch(e){this.logger.warn("Re-registration failed; will retry on next heartbeat",{error:(0,
|
|
5290
|
+
try{await this.register(),this.logger.info("Re-registered after stale heartbeat",{serverId:this.serverId})}catch(e){this.logger.warn("Re-registration failed; will retry on next heartbeat",{error:(0,W.getErrorMessage)(e)})}return}this.logger.warn("Heartbeat failed",{error:t})}},3e5)}noteSessionRunning(e,t){t!==this.runningSessions.has(e)&&(t?this.runningSessions.add(e):this.runningSessions.delete(e),this.runningReportDebounce&&clearTimeout(this.runningReportDebounce),this.runningReportDebounce=setTimeout(()=>{this.reportRunningSessions()},300),"function"==typeof this.runningReportDebounce.unref&&this.runningReportDebounce.unref())}async reportRunningSessions(e=!1){if(!this.projectId)return
|
|
5209
5291
|
const t=Array.from(this.runningSessions).sort(),r=t.join(",")
|
|
5210
5292
|
if(e||r!==this.lastRunningReport){this.lastRunningReport=r
|
|
5211
|
-
try{await this.apiClient.post(`/api/projects/${this.projectId}/session-activity`,{running:t,at:(new Date).toISOString()})}catch(e){this.lastRunningReport="",this.logger.debug("Running-sessions report failed",{error:(0,
|
|
5293
|
+
try{await this.apiClient.post(`/api/projects/${this.projectId}/session-activity`,{running:t,at:(new Date).toISOString()})}catch(e){this.lastRunningReport="",this.logger.debug("Running-sessions report failed",{error:(0,W.getErrorMessage)(e)})}}}startSSEEventForwarder(){this.sseForwarderInterval||(this.logger.info("Starting SSE event forwarder"),this.sseForwarderInterval=setInterval(async()=>{await this.flushSSEEvents()},2e3),this.connectToOpenCodeSSE())}stopSSEEventForwarder(){this.sseConnectGeneration++
|
|
5212
5294
|
for(const e of this.sseReconnectTimers)clearTimeout(e)
|
|
5213
|
-
this.sseReconnectTimers.clear(),this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch(e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,
|
|
5295
|
+
this.sseReconnectTimers.clear(),this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch(e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,W.getErrorMessage)(e)}))}scheduleSSEReconnect(e){const t=setTimeout(()=>{this.sseReconnectTimers.delete(t),this.connectToOpenCodeSSE()},e)
|
|
5214
5296
|
this.sseReconnectTimers.add(t)}async connectToOpenCodeSSE(){const e=this.sseConnectGeneration,r=()=>e!==this.sseConnectGeneration,s=this.getOpenCodeUrl()
|
|
5215
5297
|
if(!s)return void(this.sseForwarderInterval&&!r()&&(this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),this.scheduleSSEReconnect(5e3)))
|
|
5216
5298
|
const n=`${s}/event`
|
|
@@ -5225,41 +5307,41 @@ if(r()){try{await s.cancel()}catch{}return}if(e)break
|
|
|
5225
5307
|
const o=i.decode(n,{stream:!0})
|
|
5226
5308
|
if(l){const e=o.indexOf("\n")
|
|
5227
5309
|
if(-1===e)continue
|
|
5228
|
-
l=!1,a=o.slice(e+1),u||this.logger.warn("Oversized SSE event discarded as it arrived",{type:d})}else if(a+=o,a.length>
|
|
5310
|
+
l=!1,a=o.slice(e+1),u||this.logger.warn("Oversized SSE event discarded as it arrived",{type:d})}else if(a+=o,a.length>ht&&-1===a.indexOf("\n")&&!(a.length<=c.SESSION_DIFF_PARSE_CEILING&&(0,c.trimmableWhenWhole)(a.replace(/^data: /,"")))){d=(0,c.sseEventTypeOf)(a)
|
|
5229
5311
|
const e=(0,c.trimOversizedSseEvent)(a.replace(/^data: /,""),Number.POSITIVE_INFINITY)
|
|
5230
5312
|
u=!!e,e?(this.sseEventBuffer.push({type:e.type,data:e,at:Date.now()}),this.logger.debug("Oversized SSE event forwarded as a summary",{type:d})):(0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++),l=!0,a=""
|
|
5231
5313
|
continue}const h=a.split("\n")
|
|
5232
5314
|
a=h.pop()||""
|
|
5233
5315
|
for(const e of h)if(e.startsWith("data: ")){let r=null
|
|
5234
|
-
if(e.length>
|
|
5235
|
-
else if(e.length>
|
|
5236
|
-
0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++,this.logger.warn("Oversized SSE event dropped before parsing",{type:t,chars:e.length,limit:
|
|
5316
|
+
if(e.length>ht&&(r=(0,c.trimOversizedSseEvent)(e.slice(6)),r&&"session.diff"!==r.type&&JSON.stringify(r).length>ht&&(r=null)),r)this.logger.debug("Oversized SSE event forwarded as a summary",{type:r.type,chars:e.length})
|
|
5317
|
+
else if(e.length>ht){const t=(0,c.sseEventTypeOf)(e)
|
|
5318
|
+
0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount++,this.logger.warn("Oversized SSE event dropped before parsing",{type:t,chars:e.length,limit:ht})
|
|
5237
5319
|
continue}try{const s=r??JSON.parse(e.slice(6)),n=s.type||"unknown"
|
|
5238
5320
|
this.sseEventBuffer.push({type:n,data:s,at:Date.now()})
|
|
5239
5321
|
{const e=s?.properties?.sessionID||s?.sessionID
|
|
5240
5322
|
if(e)if("session.busy"===n?this.noteSessionRunning(e,!0):"session.idle"!==n&&"session.error"!==n&&"session.retry"!==n||this.noteSessionRunning(e,!1),"session.error"===n)Promise.resolve().then(()=>t.t(86116,23)).then(t=>t.notifyInboxChatSessionError(e,s?.properties?.error)).catch(()=>{}),this.stuckSessionWatchdog?.turnFailed(e,s?.properties?.error)
|
|
5241
5323
|
else if("session.status"===n){const t=s?.properties?.status?.type
|
|
5242
|
-
"running"===t||"busy"===t?this.noteSessionRunning(e,!0):"idle"!==t&&"retry"!==t&&"error"!==t||this.noteSessionRunning(e,!1)}}if("server.heartbeat"===n||"server.connected"===n||(0,
|
|
5324
|
+
"running"===t||"busy"===t?this.noteSessionRunning(e,!0):"idle"!==t&&"retry"!==t&&"error"!==t||this.noteSessionRunning(e,!1)}}if("server.heartbeat"===n||"server.connected"===n||(0,de.isKeepWarmTick)(s?.properties?.sessionID??s?.properties?.info?.sessionID??s?.properties?.part?.sessionID??s?.sessionID)||this.parkController?.noteActivity(),this.handleSSEHookEvent(s).catch(e=>this.logger.warn("SSE hook event processing failed",{type:s?.type,error:(0,W.getErrorMessage)(e)})),"message.part.updated"===n&&"step-finish"===s?.properties?.part?.type){const e=String(s?.properties?.part?.sessionID??"")
|
|
5243
5325
|
if(e&&!this.usageStepTimers.has(e)){const t=setTimeout(()=>{this.usageStepTimers.delete(e),this.recordUsageForSession(e).catch(()=>{})},2e3)
|
|
5244
|
-
"function"==typeof t.unref&&t.unref(),this.usageStepTimers.set(e,t)}}if("session.idle"===n){this.flushSSEEvents().catch(e=>this.logger.debug("Immediate SSE flush failed",{error:(0,
|
|
5326
|
+
"function"==typeof t.unref&&t.unref(),this.usageStepTimers.set(e,t)}}if("session.idle"===n){this.flushSSEEvents().catch(e=>this.logger.debug("Immediate SSE flush failed",{error:(0,W.getErrorMessage)(e)})),this.recordUsageForSession(s?.properties?.sessionID||s?.sessionID||"").catch(()=>{})
|
|
5245
5327
|
const e=s?.properties?.sessionID||s?.sessionID
|
|
5246
|
-
e&&(0,
|
|
5247
|
-
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
|
|
5248
5330
|
if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
|
|
5249
5331
|
if(!t)return
|
|
5250
5332
|
this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,r=t?.id||""
|
|
5251
5333
|
if(!r)return
|
|
5252
5334
|
const s=t?.metadata
|
|
5253
5335
|
if(s?.agentType&&this.projectId){const e=t?.parentID||null
|
|
5254
|
-
this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active"}).catch(e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,
|
|
5336
|
+
this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active"}).catch(e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,W.getErrorMessage)(e)})})}setTimeout(async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:r}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:r,variables:{sessionId:r}})},500)}}async recordUsageForSession(e){if(!this.projectId||!e)return
|
|
5255
5337
|
const t=this.getOpenCodeUrl()
|
|
5256
5338
|
if(t)try{const r=await fetch(`${t}/session/${e}/message?limit=200`,{signal:AbortSignal.timeout(5e3)})
|
|
5257
5339
|
if(!r.ok)return
|
|
5258
|
-
const s=await r.json(),n=this.recordedUsageMsgs.get(e),o=(0,
|
|
5340
|
+
const s=await r.json(),n=this.recordedUsageMsgs.get(e),o=(0,dt.turnSubjects)(s),i=[]
|
|
5259
5341
|
for(const t of s){const r=t?.info
|
|
5260
5342
|
if(!r||"assistant"!==r.role||!r.id)continue
|
|
5261
5343
|
if(n&&n.has(String(r.id)))continue
|
|
5262
|
-
const{input:s,output:a,reasoning:c,cacheRead:l,cacheWrite:d,cost:u,hasUsage:h}=(0,
|
|
5344
|
+
const{input:s,output:a,reasoning:c,cacheRead:l,cacheWrite:d,cost:u,hasUsage:h}=(0,ye.tokensOfMessage)(t)
|
|
5263
5345
|
if(!h)continue
|
|
5264
5346
|
const p=!!r.time?.completed
|
|
5265
5347
|
i.push({finished:p,sessionId:e,messageId:String(r.id),agentType:r.agent??r.mode??null,provider:r.providerID??null,model:r.modelID??null,inputTokens:s,outputTokens:a,reasoningTokens:c,cacheReadTokens:l,cacheWriteTokens:d,cost:u,...r.parentID?{promptId:String(r.parentID)}:{},...r.parentID&&o.get(String(r.parentID))?{threadRef:o.get(String(r.parentID))}:{}})}if(0===i.length)return
|
|
@@ -5267,9 +5349,9 @@ await this.apiClient.post(`/api/projects/${this.projectId}/usage-events`,{events
|
|
|
5267
5349
|
let a=n
|
|
5268
5350
|
if(!a){if(a=new Set,this.recordedUsageMsgs.size>=300)for(const e of Array.from(this.recordedUsageMsgs.keys()).slice(0,50))this.recordedUsageMsgs.delete(e)
|
|
5269
5351
|
this.recordedUsageMsgs.set(e,a)}for(const e of i)e.finished&&a.add(e.messageId)
|
|
5270
|
-
this.logger.debug("Recorded session usage to remote",{sessionId:e,count:i.length})}catch(t){this.logger.debug("Usage recording failed",{sessionId:e,error:(0,
|
|
5352
|
+
this.logger.debug("Recorded session usage to remote",{sessionId:e,count:i.length})}catch(t){this.logger.debug("Usage recording failed",{sessionId:e,error:(0,W.getErrorMessage)(t)})}}async sweepUsage(e,t=8e3){const r=this.getOpenCodeUrl()
|
|
5271
5353
|
if(!this.projectId||!r)return
|
|
5272
|
-
const s=await(0,
|
|
5354
|
+
const s=await(0,lt.sweepSessionUsage)(r,e,e=>this.recordUsageForSession(e),t)
|
|
5273
5355
|
s.error?this.logger.warn("usage sweep failed",{error:s.error}):s.outOfTime&&this.logger.warn("usage sweep ran out of time; some sessions not read",{read:s.read})}async rejectPermissionAsk(e){const t=e?.properties||{},r=t?.id
|
|
5274
5356
|
if(!r)return
|
|
5275
5357
|
const s=this.getOpenCodeUrl()
|
|
@@ -5282,7 +5364,7 @@ if(!r.ok)return
|
|
|
5282
5364
|
const s=await r.json()
|
|
5283
5365
|
if(!Array.isArray(s)||0===s.length)return
|
|
5284
5366
|
this.logger.info("Sweeping pending permission asks",{count:s.length})
|
|
5285
|
-
for(const e of s)await this.rejectPermissionAsk({properties:e}).catch(t=>this.logger.warn("Pending-permission reject failed",{permissionId:e?.id,sessionId:e?.sessionID,permission:e?.permission,patterns:Array.isArray(e?.patterns)?e.patterns.join(", "):void 0,error:(0,
|
|
5367
|
+
for(const e of s)await this.rejectPermissionAsk({properties:e}).catch(t=>this.logger.warn("Pending-permission reject failed",{permissionId:e?.id,sessionId:e?.sessionID,permission:e?.permission,patterns:Array.isArray(e?.patterns)?e.patterns.join(", "):void 0,error:(0,W.getErrorMessage)(t)}))}catch(e){this.logger.debug("Pending-permission sweep failed",{error:(0,W.getErrorMessage)(e)})}}async flushSSEEvents(){if(!this.projectId)return
|
|
5286
5368
|
if(0===this.sseEventBuffer.length&&0===this.droppedEventCount)return
|
|
5287
5369
|
const e=Date.now()-3e5
|
|
5288
5370
|
let t=0
|
|
@@ -5290,36 +5372,36 @@ for(;t<this.sseEventBuffer.length&&this.sseEventBuffer[t].at<e;)t++
|
|
|
5290
5372
|
if(t>0&&(this.sseEventBuffer.splice(0,t),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=t),!this.hasRemoteEventConsumers){if(this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
|
|
5291
5373
|
this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}return}const r=this.sseEventBuffer.splice(0),s=this.droppedEventCount>0?{at:Date.now(),type:"alchemy.events.dropped",data:{count:this.droppedEventCount,firstDropAt:this.firstDropAt,reportedAt:Date.now()}}:null
|
|
5292
5374
|
s&&r.unshift(s)
|
|
5293
|
-
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:r}),s&&(this.logger.warn("Reported dropped SSE events to remote",{count:s.data.count,firstDropAt:s.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(e){this.logger.debug("SSE event flush failed",{error:(0,
|
|
5375
|
+
try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:r}),s&&(this.logger.warn("Reported dropped SSE events to remote",{count:s.data.count,firstDropAt:s.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(e){this.logger.debug("SSE event flush failed",{error:(0,W.getErrorMessage)(e),count:r.length})
|
|
5294
5376
|
const t=s?r.slice(1):r
|
|
5295
5377
|
if(this.sseEventBuffer.unshift(...t),this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
|
|
5296
5378
|
this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce(),this.remoteLastOkAt=Date.now(),this.remoteFailures=0,this.remoteLastError=null}catch(e){if(!this.isPolling)return
|
|
5297
|
-
this.remoteFailures+=1,this.remoteLastError=(0,
|
|
5298
|
-
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()
|
|
5299
5381
|
return/^[0-9a-f]{40}$/.test(e)?e:null}catch{return this.mainBranchName=null,null}}async pollOnce(){if(this.isPolling&&this.projectId&&!this.updateBlocked)try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=30000`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
5300
|
-
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,
|
|
5382
|
+
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,nt.machineCallerHeaders)(this.options.remoteServerUrl)),this.clientVersion&&(t["X-Client-Version"]=this.clientVersion),this.opencodeVersion&&(t["X-OpenCode-Version"]=this.opencodeVersion),this.port&&(t["X-Action-Server-Port"]=String(this.port)),t["X-Client-Action-Types"]=ie
|
|
5301
5383
|
const r=await this.readMainSha()
|
|
5302
5384
|
r&&(t["X-Main-Sha"]=r)
|
|
5303
5385
|
const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
|
|
5304
5386
|
if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
|
|
5305
5387
|
const n=await s.json(),o={success:!0,data:n.data||n}
|
|
5306
|
-
if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,defaultModelUnchosen:
|
|
5307
|
-
"boolean"==typeof
|
|
5308
|
-
this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,
|
|
5388
|
+
if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,anthropicCompaction:s,defaultModelUnchosen:n}=o.data.settings
|
|
5389
|
+
"boolean"==typeof n&&(this.defaultModelUnchosen=n),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r),"boolean"==typeof s&&await this.applyAnthropicCompaction(s)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new x.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,nt.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,T.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
|
|
5390
|
+
this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,W.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
|
|
5309
5391
|
if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
|
|
5310
5392
|
this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
|
|
5311
5393
|
for(const t of e)this.scheduleRunner?.nudge(t).catch(e=>{this.logger.warn("Schedule nudge dispatch failed",{docId:t,error:String(e)})})}o.success&&Array.isArray(o.data?.versionWarnings)&&this.applyVersionWarnings(o.data.versionWarnings),o.success&&o.data?.actions?.length>0&&this.dispatchActions(o.data.actions)}catch(e){throw e}}dispatchActions(e){const t=[],r=[]
|
|
5312
|
-
for(const s of e)
|
|
5313
|
-
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--})
|
|
5314
5396
|
let s=0
|
|
5315
5397
|
for(const e of r)this.queuedRegularActionIds.has(e.id)?this.logger.debug("Skipping re-delivered action already held locally",{actionId:e.id,type:e.type}):(this.queuedRegularActionIds.add(e.id),this.pendingRegularActions.push(e),s++)
|
|
5316
5398
|
s>0&&this.drainRegularActions()}async drainRegularActions(){if(!this.isProcessingAction){this.isProcessingAction=!0
|
|
5317
5399
|
try{for(;this.isPolling;){const e=this.pendingRegularActions.shift()
|
|
5318
5400
|
if(!e)break
|
|
5319
|
-
try{await this.processAction(e)}catch(t){this.logger.warn("Action processing failed",{actionId:e.id,type:e.type,error:(0,
|
|
5401
|
+
try{await this.processAction(e)}catch(t){this.logger.warn("Action processing failed",{actionId:e.id,type:e.type,error:(0,W.getErrorMessage)(t)})}finally{this.queuedRegularActionIds.delete(e.id)}}}finally{for(const e of this.pendingRegularActions)this.queuedRegularActionIds.delete(e.id)
|
|
5320
5402
|
this.pendingRegularActions.length=0,this.isProcessingAction=!1}}}async syncProjectSettings(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}`)
|
|
5321
5403
|
if(e.success&&e.data?.metadata){const{defaultModel:t}=e.data.metadata
|
|
5322
|
-
this.cachedReceipts=e.data.metadata.receipts??null,this.cachedIntegrations=e.data.metadata.integrations??null,void 0!==t&&this.updateDefaultModel(t)}}catch(e){this.logger.debug("Settings sync failed",{error:(0,
|
|
5404
|
+
this.cachedReceipts=e.data.metadata.receipts??null,this.cachedIntegrations=e.data.metadata.integrations??null,void 0!==t&&this.updateDefaultModel(t)}}catch(e){this.logger.debug("Settings sync failed",{error:(0,W.getErrorMessage)(e)})}}async sweepOrphanTestRuns(e){try{const r=(await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/test-runs?limit=100&offset=${e}`,500)).filter(t=>{if(!t||"pending"!==t.status&&"running"!==t.status)return!1
|
|
5323
5405
|
const r=Date.parse(t.createdAt??t.created_at??"")
|
|
5324
5406
|
return Number.isFinite(r)&&r<e})
|
|
5325
5407
|
if(0===r.length)return
|
|
@@ -5327,22 +5409,22 @@ const s=new Map
|
|
|
5327
5409
|
for(const e of r){const t=e.requestId??e.request_local_id
|
|
5328
5410
|
t&&e.id&&s.set(t,[...s.get(t)??[],e.id])}const n={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},{handleRunTestsAction:o}=await Promise.resolve().then(()=>t(76555))
|
|
5329
5411
|
for(const[e,t]of s){this.logger.warn("Re-dispatching test run(s) orphaned by restart",{requestId:e,testRunIds:t})
|
|
5330
|
-
try{await o({requestId:e,testRunIds:t,mode:"auto"},n)}catch(t){this.logger.warn("Orphan test-run re-dispatch failed (stuck-run watchdog will backstop)",{requestId:e,error:(0,
|
|
5412
|
+
try{await o({requestId:e,testRunIds:t,mode:"auto"},n)}catch(t){this.logger.warn("Orphan test-run re-dispatch failed (stuck-run watchdog will backstop)",{requestId:e,error:(0,W.getErrorMessage)(t)})}}}catch(e){this.logger.warn("Orphan test-run sweep failed (non-fatal)",{error:(0,W.getErrorMessage)(e)})}}async processAction(e){this.parkController?.noteActivity(),this.logger.info("Processing action",{actionId:e.id,type:e.type})
|
|
5331
5413
|
const t=Date.now()
|
|
5332
5414
|
let r
|
|
5333
|
-
try{const s={projectPath:this.options.projectPath,projectId:this.projectId,serverPort:this.port,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},n=await(0,
|
|
5334
|
-
r={success:n.success,result:n.refused?{...n.data??{},refused:!0}:n.data,error:n.message||n.error,duration:Date.now()-t}}catch(e){r={success:!1,error:(0,
|
|
5335
|
-
try{await this.apiClient.post(`/api/actions/${e.id}/result`,n),r.success?(this.logger.info("Action processed",{actionId:e.id,type:e.type,success:!0,duration:r.duration}),"prune-worktree"===e.type&&this.sweepUsage(Date.now()-216e5).catch(()=>{})):r.result?.refused?this.logger.info("Action refused (by design)",{actionId:e.id,type:e.type,why:r.error}):this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r.error})}catch(t){this.logger.error("Failed to POST action result after retries; posting it again later",{actionId:e.id,type:e.type,workSucceeded:r.success,postError:(0,
|
|
5336
|
-
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
|
|
5337
5419
|
await this.syncProjectSettings().catch(()=>{})
|
|
5338
5420
|
const e=this.cachedIntegrations?.linear?.autoImport
|
|
5339
5421
|
if(!0!==e?.enabled)return
|
|
5340
|
-
const t=e?.label?.trim()||"alchemylab",r=e?.team?.trim()||void 0,s=await(0,
|
|
5422
|
+
const t=e?.label?.trim()||"alchemylab",r=e?.team?.trim()||void 0,s=await(0,U.listLinearIssuesByLabel)(t,20,r)
|
|
5341
5423
|
if(0===s.length)return
|
|
5342
|
-
const n={projectId:this.projectId,apiClient:this.apiClient},o=await(0,
|
|
5343
|
-
for(const e of a)try{const t=await(0,
|
|
5344
|
-
this.logger.info("linear auto-import: filed a draft",{issue:e.identifier,requestId:t})}catch(t){if(t instanceof
|
|
5345
|
-
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)
|
|
5346
5428
|
"function"==typeof t.unref&&t.unref()}startExperimentReaper(){const e=async()=>{try{const e=this.getOpenCodeUrl()
|
|
5347
5429
|
if(!e)return
|
|
5348
5430
|
const r=await fetch(`${e}/session?limit=1000`,{signal:AbortSignal.timeout(5e3)})
|
|
@@ -5361,40 +5443,40 @@ const t=await e.json(),r=t?.data?.frontendUrl
|
|
|
5361
5443
|
"string"==typeof r&&/^https?:\/\//.test(r)&&(this.frontendOrigin=new URL(r).origin,this.logger.info("Browser requests allowed from the app origin",{origin:this.frontendOrigin}))}catch{}})()
|
|
5362
5444
|
const r=await(0,S.ensureSoleInstance)({projectPath:this.options.projectPath,logger:this.logger,retire:this.options.retireTwins})
|
|
5363
5445
|
if(!r.ok)throw this.logger.error("Refusing to start: "+r.reason),Object.assign(new Error(r.reason),{runningElsewhere:!0===r.runningElsewhere})
|
|
5364
|
-
try{await(0,I.migrateWorkCopies)(this.options.projectPath,this.logger)}catch(e){this.logger.warn("Working copies could not be moved out of the project folder (they stay where they are)",{error:(0,
|
|
5446
|
+
try{await(0,I.migrateWorkCopies)(this.options.projectPath,this.logger)}catch(e){this.logger.warn("Working copies could not be moved out of the project folder (they stay where they are)",{error:(0,W.getErrorMessage)(e)})}this.workCopiesGate.resolve()
|
|
5365
5447
|
for(let e=0;e<50;e++)try{await new Promise((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",()=>{this.logger.info("HTTP server started",{port:this.port}),(0,S.writeInstanceLock)(this.options.projectPath,this.port),e()}),this.server.on("error",e=>{e.code,t(e)})}catch(e){t(e)}})
|
|
5366
5448
|
break}catch(t){if("EADDRINUSE"===t?.code&&e<49){if(0===e&&!1!==this.options.retireTwins&&await(0,S.askStaleTwinToRetire)({port:this.port,projectPath:this.options.projectPath,logger:this.logger})){this.logger.info("Stale twin retired; retrying our preferred port",{port:this.port})
|
|
5367
5449
|
continue}this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
|
|
5368
|
-
continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)}try{const e=
|
|
5450
|
+
continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)}try{const e=D.default.join(this.options.projectPath,".alchemy_lab","action-server.json");(0,re.writeActionServerFile)(this.options.projectPath,this.port,this.localSecret),setTimeout(()=>{this.requestConfigReload("agents ready").catch(()=>{})},4e3),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,W.getErrorMessage)(e)})}(0,C.setPreviewSelfBaseUrl)(`http://localhost:${this.port}`)
|
|
5369
5451
|
try{const e=this.readProjectConfig()
|
|
5370
5452
|
if(e?.id){const{upsertEntry:r}=await Promise.resolve().then(()=>t(52640))
|
|
5371
|
-
r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,
|
|
5372
|
-
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0
|
|
5453
|
+
r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,W.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,W.getErrorMessage)(e)
|
|
5454
|
+
t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,$e.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
|
|
5373
5455
|
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}this.startExperimentReaper(),this.startLinearAutoImportPoller()
|
|
5374
5456
|
try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=0&settingsOnly=true`,t={"Content-Type":"application/json",Accept:"application/json"}
|
|
5375
|
-
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,
|
|
5457
|
+
this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,nt.machineCallerHeaders)(this.options.remoteServerUrl))
|
|
5376
5458
|
const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
|
|
5377
5459
|
if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
|
|
5378
|
-
"boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,
|
|
5460
|
+
"boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,W.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Le.setContainerProject)(this.projectId),this.startSSEEventForwarder()
|
|
5379
5461
|
try{const{resumeInboxChatWatches:e}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
5380
|
-
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,
|
|
5381
|
-
t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,
|
|
5462
|
+
e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,W.getErrorMessage)(e)})}this.apiClient.post(`/api/projects/${this.projectId}/session-messages/expire-orphaned-approvals`,{}).then(e=>{const t=e?.data?.expired??0
|
|
5463
|
+
t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,W.getErrorMessage)(e)})}),this.startStuckSessionWatchdog(),this.startScheduleRunner(),(0,we.ensureOpsDiagnostics)(this.apiClient,this.projectId,this.options.projectPath,this.logger),(0,ce.startSessionCompactor)({getOpenCodeUrl:()=>this.getOpenCodeUrl()??void 0,apiClient:this.apiClient,projectId:this.projectId||void 0,projectName:this.options.projectPath?D.default.basename(this.options.projectPath):void 0,logger:this.logger}),this.keepWarmRunner=(0,de.startKeepWarmRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,getDefaultModel:()=>this.cachedDefaultModel}),this.ownerGuidanceTimer=setInterval(()=>{this.checkOwnerGuidanceApply()},6e4),this.ownerGuidanceTimer.unref?.(),this.workMetricsRunner||(this.workMetricsRunner=(0,pe.startWorkMetricsRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})),this.mcpConnectionRunner||(this.mcpConnectionRunner=(0,fe.startMcpConnectionRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")},isBusy:async()=>this.activeMcpCalls.size>0||await this.anySessionBusy()})),(0,le.startMonitorRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,serveReady:async()=>(await this.ensureServeAlive("monitor-wake"),null!=this.openCodePort&&!this.parkController?.isServeDown()),reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},runOwn:e=>this.runOwn(e),verifyDeliveryEnv:async e=>{let r
|
|
5382
5464
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/environments/${e}`)
|
|
5383
5465
|
r=t?.data||t}catch{throw new Error("environment fetch failed")}const s=r?.config?.delivery
|
|
5384
5466
|
if(!r||!s?.targetPath)return{gone:!0,reason:"environment or its delivery config no longer exists"}
|
|
5385
5467
|
const{verifyDelivery:n}=await Promise.resolve().then(()=>t.t(32504,23)),o=await n(s)
|
|
5386
|
-
return{healthy:o.healthy,verified:!0===o.verified,failing:(o.checks||[]).filter(e=>!e.pass).map(e=>e.name).concat(!1===o.healthy?["health"]:[])}},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}}}).catch(e=>{this.logger.error("Monitor fleet-health report failed to post",{error:(0,
|
|
5468
|
+
return{healthy:o.healthy,verified:!0===o.verified,failing:(o.checks||[]).filter(e=>!e.pass).map(e=>e.name).concat(!1===o.healthy?["health"]:[])}},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}}}).catch(e=>{this.logger.error("Monitor fleet-health report failed to post",{error:(0,W.getErrorMessage)(e)})})}})}}startParkController(){if(this.parkController)return
|
|
5387
5469
|
if(!this.relaunchOpenCode)return
|
|
5388
5470
|
const e=this.options.onIdlePark
|
|
5389
|
-
this.parkController=new
|
|
5390
|
-
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")
|
|
5391
5473
|
if(this.isProcessingAction||this.inFlightProxyActions>0)return this.parkVeto("processing-action")
|
|
5392
5474
|
if(this.inFlightServeProxyRequests>0)return this.parkVeto("serve-proxy-in-flight")
|
|
5393
5475
|
if(this.hasRemoteEventConsumers)return this.parkVeto("ui-event-consumers")
|
|
5394
5476
|
if(this.activeMcpCalls.size>0||this.pendingConfigReload)return this.parkVeto("mcp-or-config-activity")
|
|
5395
|
-
if((0,
|
|
5396
|
-
if((0,
|
|
5397
|
-
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")
|
|
5398
5480
|
if((0,C.listPreviewHandles)().length>0)return this.parkVeto("preview-handles")
|
|
5399
5481
|
if(this.projectId&&(0,j.experimentRegistry)().countForProject(this.projectId)>0)return this.parkVeto("experiments")
|
|
5400
5482
|
if(this.scheduleRunner?.hasInFlightFirings())return this.parkVeto("schedule-firing")
|
|
@@ -5403,55 +5485,55 @@ for(const e of[...this.trackedChildPids])if(e!==r)try{return process.kill(e,0),t
|
|
|
5403
5485
|
this.trackedChildPids.delete(e)}const{hasActiveInboxChatTurns:s}=await Promise.resolve().then(()=>t.t(86116,23))
|
|
5404
5486
|
if(s())return this.parkVeto("inbox-chat-turn")
|
|
5405
5487
|
const{hasParkVetoingPlannerHandoffWatches:n}=await t.e(991).then(()=>t(18991))
|
|
5406
|
-
return n()?this.parkVeto("planner-handoff"):await this.anySessionBusy()?this.parkVeto("session-busy"):!e.ignoreWarm&&(0,
|
|
5488
|
+
return n()?this.parkVeto("planner-handoff"):await this.anySessionBusy()?this.parkVeto("session-busy"):!e.ignoreWarm&&(0,de.keepWarmHoldsUntil)()>Date.now()?this.parkVeto("keep-warm-window"):(this.lastParkVetoes=[],!0)}executePark(){const e=new Set(this.trackedChildPids),t=this.readServePid()
|
|
5407
5489
|
t&&e.add(t),this.killTrackedProcesses()
|
|
5408
|
-
try{const e=
|
|
5409
|
-
if(
|
|
5410
|
-
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)")
|
|
5411
5493
|
const e=await this.relaunchOpenCode()
|
|
5412
5494
|
if(this.parkController?.isStopped()||this.updateBlocked)throw e.pid&&this.killProcessGroup(e.pid),new Error("revival aborted: server stopping or in update mode")
|
|
5413
5495
|
if(e.pid&&this.trackChildPid(e.pid),!e.success||!e.port)throw e.pid&&(this.killProcessGroup(e.pid),this.trackedChildPids.delete(e.pid)),new Error(e.success?"OpenCode serve relaunch did not announce a port":e.error||"OpenCode serve relaunch failed")
|
|
5414
5496
|
this.setOpenCodePort(e.port)
|
|
5415
|
-
try{const t=
|
|
5416
|
-
|
|
5417
|
-
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}}async ensureServeAlive(e){if(this.parkController&&!this.updateBlocked)try{await this.parkController.ensureAlive()}catch(t){this.logger.warn("OpenCode serve revival failed",{context:e,error:(0,
|
|
5418
|
-
t&&await Promise.race([t.catch(()=>{}),new Promise(t=>{setTimeout(t,e).unref?.()})])}killProcessGroup(e){try{process.kill(-e,"SIGTERM")}catch(t){if("ESRCH"!==t?.code)try{process.kill(e,"SIGTERM")}catch{}}}readServePid(){try{const e=
|
|
5419
|
-
if(!
|
|
5420
|
-
const t=JSON.parse(
|
|
5421
|
-
return"number"==typeof t.pid?t.pid:void 0}catch{return}}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,
|
|
5422
|
-
try{const e=
|
|
5423
|
-
if(
|
|
5424
|
-
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,
|
|
5497
|
+
try{const t=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5498
|
+
q.default.writeFileSync(t,JSON.stringify({port:e.port,pid:e.pid,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("Could not write opencode.json after revival",{error:(0,W.getErrorMessage)(e)})}if(this.startSSEEventForwarder(),this.ownerGuidanceStale&&this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Applying the changed guidance after waking failed; it stays pending",{error:(0,W.getErrorMessage)(e)})),this.projectId){const e=Date.now()
|
|
5499
|
+
setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}}async ensureServeAlive(e){if(this.parkController&&!this.updateBlocked)try{await this.parkController.ensureAlive()}catch(t){this.logger.warn("OpenCode serve revival failed",{context:e,error:(0,W.getErrorMessage)(t)})}}async awaitInFlightRevival(e=15e3){const t=this.parkController?.getInFlightRevival()
|
|
5500
|
+
t&&await Promise.race([t.catch(()=>{}),new Promise(t=>{setTimeout(t,e).unref?.()})])}killProcessGroup(e){try{process.kill(-e,"SIGTERM")}catch(t){if("ESRCH"!==t?.code)try{process.kill(e,"SIGTERM")}catch{}}}readServePid(){try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5501
|
+
if(!q.default.existsSync(e))return
|
|
5502
|
+
const t=JSON.parse(q.default.readFileSync(e,"utf-8"))
|
|
5503
|
+
return"number"==typeof t.pid?t.pid:void 0}catch{return}}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,W.getErrorMessage)(t)})}}this.trackedChildPids.clear()
|
|
5504
|
+
try{const e=D.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
|
|
5505
|
+
if(q.default.existsSync(e)){const t=q.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
|
|
5506
|
+
if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,W.getErrorMessage)(e)})}}}}catch{}}async runOwn(e){return(0,E.makeRunOwn)(this.port,this.options.projectPath)(e)}startScheduleRunner(){this.scheduleRunner=new ae.ScheduleRunner({apiClient:this.apiClient,logger:this.logger,getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath,noteActivity:()=>this.parkController?.noteActivity(),ensureServe:()=>this.ensureServeAlive("schedule-firing"),runOwn:e=>this.runOwn(e)}),this.scheduleRunner.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new J.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,reloadServe:e=>{this.requestConfigReload(e)},logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),deliver:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),i=await o({projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel},e,r,{kind:"inserted-prompt",...s?{partPayload:{requestId:s,nudge:!0,...n?.restart?{restart:!0}:{}}}:{}})
|
|
5425
5507
|
return i.ok||this.logger.warn("watchdog nudge not delivered",{sessionId:e,requestId:s,error:i.error,detail:i.detail}),i.ok},getAgentModel:e=>this.configurer.agentModelOf(e),isAgentModelPinned:e=>this.configurer.agentPinnedOf(e),resumeHubTurn:async(e,r,s,n)=>{const{deliverPrompt:o}=await Promise.resolve().then(()=>t(17039)),{trackInboxChatTurn:i,resumeStalledWatchedTurn:a}=await Promise.resolve().then(()=>t.t(86116,23)),c={projectPath:this.options.projectPath,projectId:this.projectId,apiClient:this.apiClient,logger:this.logger,cachedDefaultModel:this.cachedDefaultModel}
|
|
5426
5508
|
if(n?.endTurn){const t=this.getOpenCodeUrl()?.replace(/\/$/,""),s=async()=>{if(!t)return
|
|
5427
5509
|
const r=await(0,d.endOpenTurn)(t,e)
|
|
5428
5510
|
"ended"!==r&&this.logger.warn("Stalled turn not ended before its resume",{sessionId:e,kind:r}),await new Promise(e=>setTimeout(e,1e3))},n=await a(e,r,s)
|
|
5429
5511
|
if(null!==n)return n
|
|
5430
5512
|
await s()}const l=await o(c,e,r,{kind:"inserted-prompt",partPayload:{nudge:!0}})
|
|
5431
|
-
return l.ok&&l.messageId?(i(c,e,Date.now(),l.messageId,void 0,void 0,void 0,s),!0):(this.logger.warn("Alchemist resume not delivered",{sessionId:e,error:l.error,detail:l.detail}),!1)},recordProviderBlock:async(e,t,r,s,n)=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block`,{providerID:e,resetsAt:t,planType:r,reason:s,...n?{stoppedSessionId:n}:{}})}catch(t){this.logger.warn("provider cap not recorded (the report still went out)",{providerID:e,error:(0,
|
|
5513
|
+
return l.ok&&l.messageId?(i(c,e,Date.now(),l.messageId,void 0,void 0,void 0,s),!0):(this.logger.warn("Alchemist resume not delivered",{sessionId:e,error:l.error,detail:l.detail}),!1)},recordProviderBlock:async(e,t,r,s,n)=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block`,{providerID:e,resetsAt:t,planType:r,reason:s,...n?{stoppedSessionId:n}:{}})}catch(t){this.logger.warn("provider cap not recorded (the report still went out)",{providerID:e,error:(0,W.getErrorMessage)(t)})}},clearCapStopped:async e=>{try{await this.apiClient.post(`/api/projects/${this.projectId}/provider-block/resumed`,{sessionIds:e})}catch(t){this.logger.warn("could not clear cap-stopped sessions on the record",{sessionIds:e,error:(0,W.getErrorMessage)(t)})}},onStuckResolved:e=>this.handleStuckSessionResolved(e),getSessionAgentType:async e=>{if(!this.projectId)return null
|
|
5432
5514
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t
|
|
5433
|
-
return"string"==typeof r?.agentType&&r.agentType?r.agentType:null}catch{return null}},hasLiveCommand:e=>(0,
|
|
5515
|
+
return"string"==typeof r?.agentType&&r.agentType?r.agentType:null}catch{return null}},hasLiveCommand:e=>(0,G.listRunningExecs)().some(t=>t.sessionId===e)||(0,$.listEntries)().some(t=>t.sessionId===e&&"running"===t.status),getSessionSettled:async e=>{if(!this.projectId)return null
|
|
5434
5516
|
const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}/settled`),r=t?.data??t
|
|
5435
5517
|
return"boolean"==typeof r?.settled?{settled:r.settled,..."string"==typeof r.why?{why:r.why}:{}}:null},getPendingQuestionAskerIds:async()=>{const e=new Set
|
|
5436
5518
|
if(!this.projectId)return e
|
|
5437
5519
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages?messageType=question&status=pending&limit=100`)
|
|
5438
|
-
for(const r of t?.data||t||[]){if(!(0,
|
|
5520
|
+
for(const r of t?.data||t||[]){if(!(0,J.isWaitDeclaringQuestion)(r))continue
|
|
5439
5521
|
const t=r?.fromSessionId||r?.from_external_session_id
|
|
5440
5522
|
"string"==typeof t&&t&&e.add(t)}const r=await this.apiClient.get(`/api/projects/${this.projectId}/monitors?status=armed`)
|
|
5441
5523
|
for(const t of r?.data||[]){const r=t?.owner_external_session_id
|
|
5442
|
-
"string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,
|
|
5524
|
+
"string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,W.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,W.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
|
|
5443
5525
|
try{const o=await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask`,{sessionId:t,detail:r,...s?{refusedModel:s}:{},...n?{refusedPinned:!0}:{}}),i=o?.data??o
|
|
5444
|
-
return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,
|
|
5526
|
+
return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,W.getErrorMessage)(t)}),!1}},getStrandedCandidates:async()=>{if(!this.projectId)return[]
|
|
5445
5527
|
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/stranded-candidates`),t=e?.data||e
|
|
5446
5528
|
return Array.isArray(t?.candidates)?t.candidates:[]}catch{return[]}},getRequestWaitState:async e=>{if(!this.projectId)return null
|
|
5447
5529
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests/${encodeURIComponent(e)}/wait-state`)
|
|
5448
|
-
return(0,
|
|
5530
|
+
return(0,J.waitFactsFromRemote)(t?.data||t)}catch{return null}},fetchRecentReportDedupKeys:async()=>{if(!this.projectId)return[]
|
|
5449
5531
|
try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/session-messages/report-dedup-keys`),t=e?.data??e
|
|
5450
5532
|
return Array.isArray(t)?t.filter(e=>"string"==typeof e&&!!e):[]}catch{return[]}},getOwnedRequestForSession:async e=>{if(!this.projectId)return null
|
|
5451
5533
|
try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/session-request-associations/by-session/${encodeURIComponent(e)}?activeOnly=true`),r=(Array.isArray(t?.data)?t.data:Array.isArray(t)?t:[]).map(e=>e?.requestLocalId??e?.request_local_id).filter(e=>"string"==typeof e&&/^req\d{6}$/.test(e))
|
|
5452
5534
|
return r.length>0?r[r.length-1]:null}catch{return null}},getLiveRequestLocalIds:async()=>{if(!this.projectId)return new Set
|
|
5453
5535
|
const e=await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/requests?inFlight=true&limit=100&offset=${e}`),t=await(0,l.readAllPages)(this.apiClient,e=>`/api/projects/${this.projectId}/requests?heldAtIntake=true&limit=100&offset=${e}`)
|
|
5454
|
-
return(0,
|
|
5536
|
+
return(0,J.requestsNotFinished)(e,t,A.TERMINAL_REQUEST_STATUSES)}}),this.stuckSessionWatchdog.start(),this.serveStartedAt&&this.stuckSessionWatchdog.serveStarted(this.serveStartedAt)}handleStuckSessionDetected(e){this.sseEventBuffer.push({at:Date.now(),type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({at:Date.now(),type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
|
|
5455
5537
|
t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
|
|
5456
5538
|
const{sessionId:r,title:s}=e.body||{},n=this.stuckSessionWatchdog.injectTestStuck(r,s)
|
|
5457
5539
|
this.logger.info("Test-injected stuck session",{sessionId:n.sessionId}),t.json({success:!0,data:n})}async handleRetryStuckSession(e,t){const{sessionId:r}=e.params
|
|
@@ -5460,13 +5542,13 @@ const s=await this.stuckSessionWatchdog.retrySession(r)
|
|
|
5460
5542
|
s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
|
|
5461
5543
|
for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
|
|
5462
5544
|
console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
|
|
5463
|
-
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,
|
|
5545
|
+
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,et.closeExhibitPreviewBrowser)()
|
|
5464
5546
|
for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
|
|
5465
|
-
try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,
|
|
5466
|
-
try{const e=
|
|
5467
|
-
|
|
5547
|
+
try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,W.getErrorMessage)(e)})}this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0),this.stopPendingReloadRecheck()
|
|
5548
|
+
try{const e=D.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
|
|
5549
|
+
q.default.existsSync(e)&&q.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(()=>t(52640))
|
|
5468
5550
|
e(this.projectId)}catch{}if(this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.runningReportInterval&&(clearInterval(this.runningReportInterval),this.runningReportInterval=void 0),this.runningReportDebounce&&(clearTimeout(this.runningReportDebounce),this.runningReportDebounce=void 0),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.registryHeartbeatInterval&&(clearInterval(this.registryHeartbeatInterval),this.registryHeartbeatInterval=void 0),this.vanishedSessionSweepInterval&&(clearInterval(this.vanishedSessionSweepInterval),this.vanishedSessionSweepInterval=void 0),this.planUsageInterval&&(clearInterval(this.planUsageInterval),this.planUsageInterval=void 0),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}await new Promise(e=>{if(this.server){const t=setTimeout(()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()},1e4)
|
|
5469
|
-
this.server.close(()=>{clearTimeout(t),e()})}else e()}),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=
|
|
5551
|
+
this.server.close(()=>{clearTimeout(t),e()})}else e()}),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=pt,pt.JUDGE_APPROVAL_TTL_MS=864e5,pt.PROJECT_CONFIG_TTL=3e4,pt.REMOTE_STALE_MS=9e4,pt.PROMPT_DELIVERY_BUSY_MS=2e4,pt.RELOAD_RECHECK_MS=12e3,pt.RELOAD_DEFER_CAP_MS=3e5},61292(e,exports,t){"use strict"
|
|
5470
5552
|
function r(e,t){const r=String(e||"").replace(/\s+/g," ").trim().split(/(?<=[.!?])\s+/),s=/^(research only|read[- ]only|no edits?|do not|don't|never|only )/i,n=(r.find(e=>e.length>8&&!s.test(e.trim()))??r[0]??"").replace(/[.!?]+$/,"").trim()
|
|
5471
5553
|
return n.length>t?`${n.slice(0,t)}...`:n}async function s(e,t){const r=v.get(t)
|
|
5472
5554
|
if(r&&Date.now()-r.at<w)return{model:r.model,reason:r.model?"ok":"unconfigured"}
|
|
@@ -5790,7 +5872,12 @@ a&&(s.atMs=a)
|
|
|
5790
5872
|
const c=i.providerID??i.model?.providerID,l=i.modelID??i.model?.modelID
|
|
5791
5873
|
c&&(s.providerID=String(c)),l&&(s.modelID=String(l))
|
|
5792
5874
|
const d=i.tokens
|
|
5793
|
-
d&&(s.contextTokens=d.total||(d.input??0)+(d.output??0)+(d.cache?.read??0)+(d.cache?.write??0))}catch{}return s},exports.
|
|
5875
|
+
d&&(s.contextTokens=d.total||(d.input??0)+(d.output??0)+(d.cache?.read??0)+(d.cache?.write??0))}catch{}return s},exports.firstCallAfter=async function(e,t,r,s=fetch){try{const n=await s(`${e.replace(/\/$/,"")}/session/${t}/message?limit=30`,{signal:AbortSignal.timeout(6e3)}),o=n.ok?await n.json():[]
|
|
5876
|
+
for(const e of Array.isArray(o)?o:[]){const t=e?.info
|
|
5877
|
+
if("assistant"!==t?.role||!t.time?.completed)continue
|
|
5878
|
+
if(Number(t.time?.created??0)<r)continue
|
|
5879
|
+
const s=t.tokens??{},n=Number(s.input??0),o=Number(s.cache?.read??0),i=Number(s.cache?.write??0),a=n+o+i
|
|
5880
|
+
return{atMs:Number(t.time.created),input:n,cacheRead:o,cacheWrite:i,hitShare:a>0?Math.round(o/a*100)/100:0}}}catch{}return null},exports.readModelLimits=async function(e,t=fetch){const s=r.get(e)
|
|
5794
5881
|
if(s&&Date.now()-s.at<6e5)return s.value
|
|
5795
5882
|
const n=new Map
|
|
5796
5883
|
try{const s=await t(`${e.replace(/\/$/,"")}/config/providers`,{signal:AbortSignal.timeout(5e3)})
|
|
@@ -5802,30 +5889,30 @@ if("number"!=typeof t?.context)continue
|
|
|
5802
5889
|
const o={context:t.context,..."number"==typeof t.input?{input:t.input}:{},..."number"==typeof t.output?{output:t.output}:{}}
|
|
5803
5890
|
n.set(`${e.id}/${r}`,o),s?.id&&s.id!==r&&n.set(`${e.id}/${s.id}`,o)}}r.set(e,{at:Date.now(),value:n})}catch{}return n}
|
|
5804
5891
|
const t=new Map,r=new Map},58836(e,exports,t){"use strict"
|
|
5805
|
-
function r(e){
|
|
5806
|
-
|
|
5807
|
-
const
|
|
5808
|
-
|
|
5892
|
+
function r(e,t){const r=e?.info??e??{}
|
|
5893
|
+
if((0,n.isCompactionSummary)(r))return{compact:!1,why:"already compacted"}
|
|
5894
|
+
const s=(0,n.contextTokensOf)(r)
|
|
5895
|
+
return s<("alchemist"===t.agentType?1e5:6e4)?{compact:!1,why:"small"}:r.time&&!r.time.completed?{compact:!1,why:"mid-turn"}:r.providerID&&r.modelID?t.idleMs<(0,n.cacheRetentionMsFor)(r.providerID)?{compact:!1,why:"cache plausibly still warm"}:{compact:!0,ctx:s}:{compact:!1,why:"no model"}}exports.startSessionCompactor=function(e){let t,n,i=0
|
|
5896
|
+
const a=r=>e.apiClient.post("/api/liveness/report",{name:"session-compactor",project:e.projectName,description:`Compacts the hub session idle past its provider's cache window (~60-90 min, per-provider heuristic) with >${Math.round(60)}k context -> summarize so reuse is cheap`,intervalMs:6e5,lastRunAt:(new Date).toISOString(),runs:i,lastAction:t,lastActionAt:n,...r}).catch(()=>{}),c=async()=>{if((0,o.isProjectPaused)())return
|
|
5897
|
+
const c=Date.now()
|
|
5898
|
+
i++
|
|
5809
5899
|
try{const o=e.getOpenCodeUrl()
|
|
5810
|
-
if(!o||!e.projectId)return void await
|
|
5811
|
-
const
|
|
5812
|
-
for(const e of
|
|
5813
|
-
t&&"alchemist"===r&&"active"===(e.status??"active")&&
|
|
5814
|
-
const
|
|
5815
|
-
for(const s of Array.isArray(
|
|
5816
|
-
if(!
|
|
5817
|
-
const
|
|
5818
|
-
if(
|
|
5819
|
-
let
|
|
5900
|
+
if(!o||!e.projectId)return void await a({lastDurationMs:Date.now()-c})
|
|
5901
|
+
const i=await(0,s.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/cli-sessions?status=active&limit=100&offset=${t}`),l=new Map
|
|
5902
|
+
for(const e of i){const t=e.externalSessionId||e.external_session_id,r=e.agentType||e.agent_type
|
|
5903
|
+
t&&"alchemist"===r&&"active"===(e.status??"active")&&l.set(t,{title:e.title||t,agentType:r})}if(0===l.size)return void await a({lastDurationMs:Date.now()-c})
|
|
5904
|
+
const d=await fetch(`${o}/session?limit=1000`,{signal:AbortSignal.timeout(1e4)}),u=await d.json().catch(()=>[]),h=Date.now()
|
|
5905
|
+
for(const s of Array.isArray(u)?u:[]){const i=l.get(s.id)
|
|
5906
|
+
if(!i)continue
|
|
5907
|
+
const a=i.title,c=s.time?.updated??s.time?.created??0
|
|
5908
|
+
if(h-c<27e5)continue
|
|
5909
|
+
let d
|
|
5820
5910
|
try{const e=await fetch(`${o}/session/${s.id}/message?limit=1`,{signal:AbortSignal.timeout(1e4)}),t=await e.json()
|
|
5821
|
-
|
|
5822
|
-
if(
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
const m=await fetch(`${o}/session/${s.id}/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerID:h.providerID,modelID:h.modelID}),signal:AbortSignal.timeout(12e4)})
|
|
5827
|
-
m.ok?(t=`compacted ${a.agentType} ${c} (${Math.round(f/1e3)}k ctx, idle ${Math.round((p-l)/6e4)}m)`,i=(new Date).toISOString(),e.logger.info("Session compactor: compacted an idle session",{sessionId:s.id,title:c,ctx:f})):e.logger.warn("Session compactor: summarize failed",{sessionId:s.id,status:m.status})}await c({lastDurationMs:Date.now()-l})}catch(e){await c({lastError:String(e).slice(0,200),lastDurationMs:Date.now()-l})}}
|
|
5828
|
-
setInterval(l,6e5),l()}
|
|
5911
|
+
d=Array.isArray(t)?t[t.length-1]:void 0}catch{continue}const u=d?.info??d??{},p=r(d,{agentType:i.agentType,idleMs:h-c})
|
|
5912
|
+
if(!p.compact)continue
|
|
5913
|
+
const f=p.ctx,m=await fetch(`${o}/session/${s.id}/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerID:u.providerID,modelID:u.modelID}),signal:AbortSignal.timeout(12e4)})
|
|
5914
|
+
m.ok?(t=`compacted ${i.agentType} ${a} (${Math.round(f/1e3)}k ctx, idle ${Math.round((h-c)/6e4)}m)`,n=(new Date).toISOString(),e.logger.info("Session compactor: compacted an idle session",{sessionId:s.id,title:a,ctx:f})):e.logger.warn("Session compactor: summarize failed",{sessionId:s.id,status:m.status})}await a({lastDurationMs:Date.now()-c})}catch(e){await a({lastError:String(e).slice(0,200),lastDurationMs:Date.now()-c})}}
|
|
5915
|
+
setInterval(c,6e5),c()}
|
|
5829
5916
|
const s=t(5641),n=t(89063),o=t(36799)},67078(e,exports){"use strict"
|
|
5830
5917
|
function t(e,t){let s=r.get(e)
|
|
5831
5918
|
return s||(s=new Set,r.set(e,s)),s.add(t),()=>{const s=r.get(e)
|
|
@@ -5899,23 +5986,26 @@ return{root:(0,i.resolve)((0,a.requestWorktreePath)(e,s),o),main:!1}}const c=awa
|
|
|
5899
5986
|
return{root:(0,i.resolve)(c,o),main:!0}},exports.serveSite=async function(e,t={}){const a=(0,i.resolve)(e),d=l.get(a)
|
|
5900
5987
|
if(d)return{port:d.port,url:`http://127.0.0.1:${d.port}`}
|
|
5901
5988
|
let u=0
|
|
5902
|
-
const h=(0,s.createServer)(async(e,
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
if(
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
if(
|
|
5910
|
-
|
|
5911
|
-
|
|
5989
|
+
const h={server:void 0,port:0,root:a,...t.refresh?{refresh:t.refresh}:{}},p=(0,s.createServer)(async(e,t)=>{try{const s=h.root
|
|
5990
|
+
h.refresh&&Date.now()-u>1e3&&(u=Date.now(),await h.refresh().catch(()=>{}))
|
|
5991
|
+
const a=decodeURIComponent((e.url||"/").split("?")[0])
|
|
5992
|
+
let l=(0,i.resolve)((0,i.join)(s,a))
|
|
5993
|
+
if(l!==s&&!l.startsWith(s+i.sep))return void t.writeHead(403).end("forbidden")
|
|
5994
|
+
if(r((0,i.relative)(s,l)))return void t.writeHead(404).end("not found")
|
|
5995
|
+
let d=await(0,o.stat)(l).catch(()=>null)
|
|
5996
|
+
if(d?.isDirectory()&&(l=(0,i.join)(l,"index.html"),d=await(0,o.stat)(l).catch(()=>null)),d?.isFile()){const[e,n]=await Promise.all([(0,o.realpath)(s).catch(()=>s),(0,o.realpath)(l).catch(()=>"")])
|
|
5997
|
+
if(!n||n!==e&&!n.startsWith(e+i.sep)||r((0,i.relative)(e,n)))return void t.writeHead(404).end("not found")}if(!d?.isFile()){if("/favicon.ico"===a)return void t.writeHead(204).end()
|
|
5998
|
+
const e=await(0,o.stat)((0,i.join)(s,"index.html")).catch(()=>null)
|
|
5999
|
+
return e?.isFile()?void t.writeHead(404).end("not found"):void t.writeHead(404,{"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store"}).end(function(e){const t=(0,i.resolve)(e).split(i.sep),r=t.lastIndexOf("worktrees")
|
|
5912
6000
|
return`<!doctype html><meta charset="utf-8"><title>Nothing to show</title><body style="font:15px/1.5 system-ui,sans-serif;max-width:36em;margin:3em auto;padding:0 1em;color:#333"><p><b>Nothing to show.</b> This preview serves ${s=r>=0&&/^req\d{6}$/.test(t[r+1]??"")?`${t.slice(r+2).join("/")||"the top folder"} in ${t[r+1]}'s working copy`:t.slice(-1)[0]||e,s.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""}[e]))}, and that folder has no index.html any more: it was removed or rebuilt after the preview started.</p><p>Build output kept for a preview belongs in a folder git ignores (the project's build folder), so it stays on this machine and out of the request's branch.</p></body>`
|
|
5913
|
-
var s}(
|
|
5914
|
-
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)
|
|
5915
6003
|
return l.get(t)?.server.close(),void l.delete(t)}for(const[,e]of l)e.server.close()
|
|
5916
6004
|
l.clear()},exports.stopSitesUnder=function(e){const t=(0,i.resolve)(e),r=[]
|
|
5917
6005
|
for(const[e,s]of l)(e===t||e.startsWith(t+i.sep))&&(s.server.close(),l.delete(e),r.push(`http://127.0.0.1:${s.port}`))
|
|
5918
|
-
return r},exports.
|
|
6006
|
+
return r},exports.sitesUnder=function(e){const t=(0,i.resolve)(e)
|
|
6007
|
+
return Array.from(l.values()).filter(e=>e.root===t||e.root.startsWith(t+i.sep)).map(e=>({root:e.root,port:e.port}))},exports.rehomeSite=function(e,t,r){const s=(0,i.resolve)(e),n=(0,i.resolve)(t),o=l.get(s)
|
|
6008
|
+
return!(!o||n!==s&&l.has(n)||(l.delete(s),o.root=n,o.refresh=r,l.set(n,o),0))},exports.stopSiteAt=function(e){let t=0
|
|
5919
6009
|
try{t=Number(new URL(e).port)}catch{return!1}for(const[e,r]of l)if(r.port===t)return r.server.close(),l.delete(e),!0
|
|
5920
6010
|
return!1},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
|
|
5921
6011
|
const s=t(58611),n=t(79896),o=t(91943),i=t(16928),a=t(7202),c={".html":"text/html; charset=utf-8",".css":"text/css; charset=utf-8",".js":"text/javascript; charset=utf-8",".json":"application/json; charset=utf-8",".mjs":"text/javascript; charset=utf-8",".wasm":"application/wasm",".webmanifest":"application/manifest+json",".xml":"application/xml; charset=utf-8",".mp3":"audio/mpeg",".wav":"audio/wav",".ogg":"audio/ogg",".mp4":"video/mp4",".webm":"video/webm",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".avif":"image/avif",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".otf":"font/otf",".txt":"text/plain; charset=utf-8",".pdf":"application/pdf"},l=new Map},3031(e,exports,t){"use strict"
|
|
@@ -6745,23 +6835,35 @@ else{const r=await(0,i.resolveDefaultBranch)(e)
|
|
|
6745
6835
|
await u.raw(["worktree","add","-b",c,d,r]),a.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:c,mainBranch:r,worktreePath:d})}}catch(e){const r=e instanceof Error?e.message:String(e)
|
|
6746
6836
|
a.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
|
|
6747
6837
|
const r=t(79896),s=t(16928),n=t(83807),o=t(7202),i=t(52232)},35419(e,exports,t){"use strict"
|
|
6748
|
-
function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){
|
|
6838
|
+
function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){const n=process.env.ALCHEMYLAB_ANTHROPIC_COMPACTION
|
|
6839
|
+
return{...void 0===n?{}:{ALCHEMYLAB_ANTHROPIC_COMPACTION:n},ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
|
|
6749
6840
|
const n=t(70857),o=t(16928),i=t(14139)
|
|
6750
6841
|
exports.t="2.1.280"},39068(e,exports,t){"use strict"
|
|
6751
6842
|
function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
|
|
6752
6843
|
if(e)return e
|
|
6753
6844
|
try{const e=("undefined"!=typeof require?require:t(54245)).resolve("alchemylab-opencode/package.json")
|
|
6754
|
-
return(0,
|
|
6755
|
-
try{const e=(0,
|
|
6756
|
-
if((0,
|
|
6757
|
-
if((0,
|
|
6845
|
+
return(0,a.dirname)(e)}catch{}}function s(){if(u)return u
|
|
6846
|
+
try{const e=(0,l.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
|
|
6847
|
+
if((0,d.isSupportedNode)(e))return u="node",u}catch{}const e=process.env.NVM_DIR||(0,a.join)(process.env.HOME||"",".nvm"),t=(0,a.join)(e,"versions","node")
|
|
6848
|
+
if((0,c.existsSync)(t))try{const e=(0,c.readdirSync)(t).filter(e=>(0,d.isSupportedNode)(e)).sort((e,t)=>{const r=e.replace("v","").split(".").map(Number),s=t.replace("v","").split(".").map(Number)
|
|
6758
6849
|
return s[0]-r[0]||s[1]-r[1]||s[2]-r[2]})
|
|
6759
|
-
if(e.length>0){const r=(0,
|
|
6760
|
-
if((0,
|
|
6761
|
-
|
|
6762
|
-
return e
|
|
6763
|
-
|
|
6764
|
-
|
|
6850
|
+
if(e.length>0){const r=(0,a.join)(t,e[0],"bin","node")
|
|
6851
|
+
if((0,c.existsSync)(r))return u=r,u}}catch{}throw new Error((0,d.unsupportedNodeMessage)(process.version))}function n(e,t){return{command:s(),prependArgs:["--no-warnings",(0,a.join)(e,t)]}}function o(e,t,r){const s=(0,l.spawnSync)(e,[t],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:12e4}),n=`${s.stdout??""}${s.stderr??""}`
|
|
6852
|
+
if(s.error||0!==s.status)throw Object.assign(new Error(s.error?.message??`exit ${s.status}`),{stderr:n})
|
|
6853
|
+
return n}function i(e,t=o){if(!e||process.env.ALCHEMY_OPENCODE_DIR)return"not-needed"
|
|
6854
|
+
if(h?.dir===e)return h.state
|
|
6855
|
+
const r=(0,a.join)(e,"postinstall.cjs"),n=(t,r="")=>(h={dir:e,state:t,detail:r},t)
|
|
6856
|
+
if(!(0,c.existsSync)(r)||!(0,c.existsSync)((0,a.join)(e,"patches")))return n("not-needed")
|
|
6857
|
+
let i=""
|
|
6858
|
+
try{i=String(JSON.parse((0,c.readFileSync)((0,a.join)(e,"package.json"),"utf8")).version??"")}catch{}const l=(0,a.join)(e,exports.BY)
|
|
6859
|
+
try{if(i&&(0,c.readFileSync)(l,"utf8")===i)return n("done")}catch{}try{const o=t(s(),r,e)
|
|
6860
|
+
return i&&(0,c.writeFileSync)(l,i),n("applied",String(o??"").trim())}catch(t){const r=String(t?.stderr||t?.message||t).trim().split("\n").slice(-3).join(" ")
|
|
6861
|
+
return process.stderr.write(`AlchemyLab could not apply its engine's dependency patches in ${e} (${r}); the engine starts without them.\n`),n("failed",r)}}exports.BY=void 0,exports.findOpencodeDir=r,exports.lastEnginePatchState=function(){return h},exports.resolveServeCommand=function(){const e=r()
|
|
6862
|
+
return e?(i(e),n((0,a.join)(e,"dist"),"serve.js")):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
|
|
6863
|
+
return e?(i(e),n((0,a.join)(e,"dist"),"run.js")):{command:"opencode",prependArgs:[]}}
|
|
6864
|
+
const a=t(16928),c=t(79896),l=t(35317),d=t(84973)
|
|
6865
|
+
let u,h
|
|
6866
|
+
exports.BY=".alchemylab-patches-applied"},69706(e,exports,t){"use strict"
|
|
6765
6867
|
function r(e){try{const t=JSON.parse(a.default.readFileSync(c.default.join(e,l),"utf8"))
|
|
6766
6868
|
return"string"==typeof t.password&&t.password?t.password:null}catch{return null}}function s(e){const t=process.env.OPENCODE_SERVER_USERNAME||exports.SERVE_USERNAME
|
|
6767
6869
|
return`Basic ${Buffer.from(`${t}:${e}`).toString("base64")}`}function n(e){if(!d.port)return!1
|
|
@@ -7781,17 +7883,20 @@ const d=Number(s.prepare("SELECT v FROM meta WHERE k = 'generation'").get()?.v??
|
|
|
7781
7883
|
i.prepare("UPDATE meta SET v = max(v, ?) WHERE k = 'generation'").run(d),i.exec("COMMIT")}finally{s.close()}}}finally{i.close()}if(t===p.items+e)return{projectId:s,restored:!1,unchanged:!0,items:t,keptFromLive:e}
|
|
7782
7884
|
if(n.default.chmodSync(u,384),n.default.existsSync(d)){n.default.renameSync(d,`${d}.before-restore-${a}`)
|
|
7783
7885
|
for(const e of["-wal","-shm"])n.default.existsSync(d+e)&&n.default.renameSync(d+e,`${d}.before-restore-${a}${e}`)}return n.default.renameSync(u,d),{projectId:s,restored:!0,items:p.items+e,keptFromLive:e}}finally{o.unhold(s),n.default.rmSync(u,{force:!0})}}}},18984(e,exports,t){"use strict"
|
|
7784
|
-
function r(e){return"summary"===e?.claudeThinking?"summary":"omitted"}
|
|
7785
|
-
|
|
7886
|
+
function r(e){return"summary"===e?.claudeThinking?"summary":"omitted"}function s(e,t){const r=void 0!==e.compaction?.anthropic
|
|
7887
|
+
if(t?!0===e.compaction?.anthropic:!r)return!1
|
|
7888
|
+
const{anthropic:s,...n}=e.compaction??{}
|
|
7889
|
+
return t?e.compaction={...n,anthropic:!0}:Object.keys(n).length>0?e.compaction=n:delete e.compaction,!0}exports.MCPClientConfigurer=exports.Vr=exports.Es=exports.R1=exports.NK=void 0,exports.claudeThinkingOf=r,exports.withAnthropicCompaction=s
|
|
7890
|
+
const n=t(75632),o=t(58824),i=t(89796),a=t(95671),c=t(43218),l=t(49779),d=t(11773),u=t(79896),h=t(16928),p=t(35317),f=t(91943),m=t(39023),g=t(35419)
|
|
7786
7891
|
exports.NK="medium"
|
|
7787
|
-
const
|
|
7788
|
-
let
|
|
7892
|
+
const w=new Set(["low","medium","high","xhigh","max"])
|
|
7893
|
+
let y=exports.NK,v=[]
|
|
7789
7894
|
exports.R1=new Set(["$schema","model","mcp","agent","mode","permission","plugin","tools","small_model","share","autoshare"]),exports.Es=new Set(["theme","keybinds","tui","layout","username","watcher","instructions"]),exports.Vr=new Set(["reviewer","acceptance-judge","command-judge","guidance-judge","intake-reviewer"])
|
|
7790
|
-
class
|
|
7895
|
+
class b{constructor(e,t,r){this.logger=e,this.agentMarkdowns=new Map,this.pinnedAgents=new Set,this.connectedProviders=null,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
|
|
7791
7896
|
if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot initialize agents"),!1
|
|
7792
7897
|
try{const r=`${t}/api/agents/seed`
|
|
7793
7898
|
this.logger.info("Ensuring built-in agents exist",{url:r})
|
|
7794
|
-
const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,
|
|
7899
|
+
const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,d.machineCallerHeaders)(t)}
|
|
7795
7900
|
this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
|
|
7796
7901
|
const n=await fetch(r,{method:"POST",headers:s})
|
|
7797
7902
|
n.ok||this.logger.warn("Failed to seed global agents",{status:n.status,statusText:n.statusText})
|
|
@@ -7800,16 +7905,16 @@ this.logger.info("Initializing project agent activations",{url:o})
|
|
|
7800
7905
|
const i=await fetch(o,{method:"POST",headers:s})
|
|
7801
7906
|
if(!i.ok)return this.logger.warn("Failed to initialize project agents",{status:i.status,statusText:i.statusText}),!1
|
|
7802
7907
|
const a=await i.json()
|
|
7803
|
-
return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,
|
|
7908
|
+
return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,o.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
|
|
7804
7909
|
if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, using baseline plugin manifest"),{...e}
|
|
7805
7910
|
const r=`${t}/api/distribution/plugin-versions`
|
|
7806
|
-
try{const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,
|
|
7911
|
+
try{const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,d.machineCallerHeaders)(t)}
|
|
7807
7912
|
this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
|
|
7808
7913
|
const n=await fetch(r,{method:"GET",headers:s})
|
|
7809
7914
|
if(!n.ok)return this.logger.warn("Plugin manifest fetch returned non-OK; using baseline",{status:n.status}),{...e}
|
|
7810
7915
|
const o=await n.json(),i=o?.data?.plugins??o?.plugins??{},a={...e}
|
|
7811
7916
|
for(const[e,t]of Object.entries(i)){const r=t?.version
|
|
7812
|
-
"string"==typeof r&&r.trim()&&(a[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:a}),a}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,
|
|
7917
|
+
"string"==typeof r&&r.trim()&&(a[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:a}),a}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,o.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(()=>t.t(91943,23)),s=await Promise.resolve().then(()=>t.t(16928,23)),n=(await Promise.resolve().then(()=>t.t(70857,23))).homedir(),o=new Set
|
|
7813
7918
|
let i=e
|
|
7814
7919
|
for(let e=0;e<6;e++){o.add(s.join(i,".opencode","plugin","anthropic-auth.js"))
|
|
7815
7920
|
const e=s.dirname(i)
|
|
@@ -7832,17 +7937,17 @@ this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
|
|
|
7832
7937
|
const n=await fetch(r,{method:"GET",headers:s})
|
|
7833
7938
|
if(!n.ok)return this.logger.warn("Failed to fetch agents from remote",{status:n.status,statusText:n.statusText}),null
|
|
7834
7939
|
const o=await n.json(),i=o.data||o
|
|
7835
|
-
return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,
|
|
7940
|
+
return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,o.getErrorMessage)(e)}),null}}async fetchDefaultWorkflowFromRemote(e){const t=this.remoteServerUrl
|
|
7836
7941
|
if(!t)return null
|
|
7837
7942
|
try{const r=await fetch(`${t}/api/projects/${e}/default-workflow`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
|
|
7838
7943
|
if(!r.ok)return this.logger.warn("Failed to fetch default workflow from remote",{projectId:e,status:r.status}),null
|
|
7839
7944
|
const s=await r.json()
|
|
7840
7945
|
return s.data||s||null}catch(t){return this.logger.warn("Error fetching default workflow from remote",{projectId:e,error:t instanceof Error?t.message:String(t)}),null}}async fetchMcpServersFromRemote(e){const t=this.remoteServerUrl
|
|
7841
|
-
if(!t)return
|
|
7946
|
+
if(!t)return v
|
|
7842
7947
|
try{const r=await fetch(`${t}/api/projects/${e}/mcp-servers`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
|
|
7843
|
-
if(!r.ok)return this.logger.warn("Failed to fetch MCP connections; keeping the last known denies",{projectId:e,status:r.status,known:
|
|
7948
|
+
if(!r.ok)return this.logger.warn("Failed to fetch MCP connections; keeping the last known denies",{projectId:e,status:r.status,known:v.length}),v
|
|
7844
7949
|
const s=await r.json(),n=s.data||s||[]
|
|
7845
|
-
return Array.isArray(n)?(
|
|
7950
|
+
return Array.isArray(n)?(v=n,n):v}catch(t){return this.logger.warn("Error fetching MCP connections; keeping the last known denies",{projectId:e,error:t instanceof Error?t.message:String(t),known:v.length}),v}}async fetchOwnerGuidanceFromRemote(e){const t=this.remoteServerUrl
|
|
7846
7951
|
if(!t)return[]
|
|
7847
7952
|
try{const r=await fetch(`${t}/api/projects/${e}/owner-guidance`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
|
|
7848
7953
|
if(!r.ok)return[]
|
|
@@ -7854,97 +7959,97 @@ if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch project f
|
|
|
7854
7959
|
try{const r=await fetch(`${t}/api/projects/${e}`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
|
|
7855
7960
|
if(!r.ok)return this.logger.warn("Failed to fetch project from remote",{projectId:e,status:r.status,statusText:r.statusText}),null
|
|
7856
7961
|
const s=await r.json()
|
|
7857
|
-
return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,
|
|
7962
|
+
return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,o.getErrorMessage)(t)}),null}}getAgentMarkdowns(){return[...this.agentMarkdowns].map(([e,t])=>({name:e,markdown:t}))}setAgentMarkdown(e,t){this.agentMarkdowns.set(e,t)}agentModelOf(e){const t=this.agentMarkdowns.get(e),r=t?/^model:\s*"([^"\n]+)"\s*$/m.exec(t):null
|
|
7858
7963
|
return r&&r[1].includes("/")?r[1]:null}agentPinnedOf(e){return this.pinnedAgents.has(e)}clearAgentMarkdowns(){this.agentMarkdowns.clear()}async removeOnDiskAgentFiles(e,r=[]){const s=await Promise.resolve().then(()=>t.t(91943,23)),n=await Promise.resolve().then(()=>t.t(16928,23)),o=new Set(r.map(e=>`${e}.md`)),i=(new Date).toISOString().slice(0,10)
|
|
7859
7964
|
try{for(const t of await s.readdir(e)){if(!t.endsWith(".md"))continue
|
|
7860
7965
|
const r=n.join(e,t)
|
|
7861
7966
|
let a=""
|
|
7862
7967
|
try{a=(await s.readFile(r,"utf8")).slice(0,400)}catch{}if(o.has(t)&&/^---[\s\S]*\bmode:\s*(primary|subagent|all)\b/m.test(a)){await s.unlink(r)
|
|
7863
7968
|
continue}const c=`${r}.removed-by-alchemy-${i}`
|
|
7864
|
-
try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&
|
|
7865
|
-
r!==
|
|
7969
|
+
try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&w.has(t.effort)?t.effort:exports.NK
|
|
7970
|
+
r!==y&&this.logger.info("Effort dial read from the workflow",{effort:r,was:y}),y=r}catch{}return y}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,c.fetchConnectedProviders)(e)
|
|
7866
7971
|
if(!t)return!1
|
|
7867
7972
|
const r=JSON.stringify(t)!==JSON.stringify(this.connectedProviders)
|
|
7868
|
-
return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,
|
|
7869
|
-
if(e.model?.providerId&&e.model?.modelId?this.pinnedAgents.add(e.name):this.pinnedAgents.delete(e.name),e.model?.providerId&&e.model?.modelId)
|
|
7870
|
-
else if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,
|
|
7871
|
-
t.resolved&&(
|
|
7872
|
-
if(l&&l!==
|
|
7873
|
-
|
|
7874
|
-
return` ${
|
|
7875
|
-
let u=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${
|
|
7876
|
-
u+=`variant: "${e.variant||o.effort||
|
|
7973
|
+
return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,s,o={}){let i=r
|
|
7974
|
+
if(e.model?.providerId&&e.model?.modelId?this.pinnedAgents.add(e.name):this.pinnedAgents.delete(e.name),e.model?.providerId&&e.model?.modelId)i=`${e.model.providerId}/${e.model.modelId}`
|
|
7975
|
+
else if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,a.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0},o.limits)
|
|
7976
|
+
t.resolved&&(i=t.fullModel)}const l=(0,c.runnableModel)(i,this.connectedProviders)
|
|
7977
|
+
if(l&&l!==i){const t=`${e.name}:${i}->${l}`
|
|
7978
|
+
b.rerouteLogged.has(t)||(b.rerouteLogged.add(t),this.logger.warn("agent model provider not connected; the agent runs on a connected provider's default until it is",{agent:e.name,configured:i,running:l})),i=l}const d=[...Object.entries(e.tools||{}).map(([e,r])=>{const s=e.replace(/-/g,"_")
|
|
7979
|
+
return` ${b.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`}),...Object.entries(o.extraTools||{}).map(([e,t])=>` ${e}: ${t}`)].join("\n")
|
|
7980
|
+
let u=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${i}"\n`
|
|
7981
|
+
u+=`variant: "${e.variant||o.effort||y}"\n`
|
|
7877
7982
|
const h=[]
|
|
7878
|
-
"summary"===o.claudeThinking&&
|
|
7879
|
-
const p=(0,
|
|
7983
|
+
"summary"===o.claudeThinking&&i.startsWith("anthropic/")&&h.push(' thinking:\n display: "summarized"'),exports.Vr.has(e.name)&&i.startsWith("anthropic/")&&h.push(' cacheTtl: "5m"')
|
|
7984
|
+
const p=(0,n.toolScopesFor)(e.name,t,e.tools||{})
|
|
7880
7985
|
if(Object.keys(p).length>0&&h.push(` toolScopes: ${JSON.stringify(p)}`),h.length>0&&(u+=`options:\n${h.join("\n")}\n`),e.color&&(u+=`color: "${e.color}"\n`),u+=`tools:\n${d}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){u+="permission:\n"
|
|
7881
7986
|
for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){u+=` ${t}:\n`
|
|
7882
|
-
for(const[e,t]of Object.entries(r))u+=` "${e}": ${t}\n`}else u+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(u+=`temperature: ${e.temperature}\n`),"number"==typeof e.maxSteps&&Number.isInteger(e.maxSteps)&&e.maxSteps>0&&(u+=`steps: ${e.maxSteps}\n`),u+="---\n\n",
|
|
7883
|
-
this.logger.info("Configuring OpenCode",{isDevMode:
|
|
7884
|
-
const
|
|
7885
|
-
let
|
|
7886
|
-
const
|
|
7887
|
-
let
|
|
7888
|
-
const
|
|
7889
|
-
if(!
|
|
7890
|
-
t?.openCodeTools?(
|
|
7891
|
-
try{e=JSON.parse(await
|
|
7987
|
+
for(const[e,t]of Object.entries(r))u+=` "${e}": ${t}\n`}else u+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(u+=`temperature: ${e.temperature}\n`),"number"==typeof e.maxSteps&&Number.isInteger(e.maxSteps)&&e.maxSteps>0&&(u+=`steps: ${e.maxSteps}\n`),u+="---\n\n",s&&(u+=s.trim()+"\n\n"),e.prompt&&(u+=e.prompt),e.promptAddition&&(u+="\n\n"+e.promptAddition),u}async configureOpenCode(e,n,c="node",d,w){try{const v=await Promise.resolve().then(()=>t.t(91943,23)),k=await Promise.resolve().then(()=>t.t(16928,23)),S=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),I="alchemy",$=k.join(e,"mcp-direct.js")
|
|
7988
|
+
this.logger.info("Configuring OpenCode",{isDevMode:S,serverName:I,scriptPath:$,projectPath:n}),await(0,i.ensureAlchemyIgnoreEntries)(n,this.logger),await this.sweepLegacyAuthPluginFiles(n)
|
|
7989
|
+
const j=(0,g.generatedOpencodeConfigPath)(n),E=k.join(n,"opencode.json")
|
|
7990
|
+
let A=null,C=null,_=null
|
|
7991
|
+
const T={}
|
|
7992
|
+
let x=null,M=!1
|
|
7993
|
+
const R=k.join(n,".alchemy_lab","config.json")
|
|
7994
|
+
if(!d)try{const e=await v.readFile(R,"utf-8"),t=JSON.parse(e)
|
|
7995
|
+
t?.openCodeTools?(A=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:A})):M=!0,t?.openCodeModel&&(C=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),M=!0}{let e=null
|
|
7996
|
+
try{e=JSON.parse(await v.readFile(j,"utf-8"))}catch{}const t=await async function(e,t){const r=(0,h.join)(e,"opencode.json")
|
|
7892
7997
|
let s
|
|
7893
|
-
try{s=JSON.parse(await(0,
|
|
7894
|
-
try{return await(0,
|
|
7895
|
-
try{0===Object.keys(
|
|
7896
|
-
e=e??t,!
|
|
7897
|
-
try{const e=JSON.parse(await
|
|
7998
|
+
try{s=JSON.parse(await(0,f.readFile)(r,"utf8"))}catch{return null}if(!s||"object"!=typeof s||Array.isArray(s)||!s.mcp||"object"!=typeof s.mcp||!("alchemy"in s.mcp))return null
|
|
7999
|
+
try{return await(0,m.promisify)(p.execFile)("git",["ls-files","--error-unmatch","--","opencode.json"],{cwd:e}),null}catch{}const n=Object.fromEntries(Object.entries(s).filter(([e])=>!exports.R1.has(e)&&"instructions"!==e&&"skills"!==e))
|
|
8000
|
+
try{0===Object.keys(n).length?await(0,f.unlink)(r):await(0,f.writeFile)(r,JSON.stringify(n,null,2),"utf8"),t?.info("Moved the generated OpenCode config out of the project folder",{removed:0===Object.keys(n).length,keptUserKeys:Object.keys(n)})}catch(e){t?.warn("Could not clear the old generated opencode.json from the project folder",{error:(0,o.getErrorMessage)(e)})}return s}(n,this.logger)
|
|
8001
|
+
e=e??t,!d&&e?.mode?.alchemy?.tools&&(_=e.mode.alchemy.tools,this.logger.info("Found existing tool settings in the generated OpenCode config",{existingTools:_})),!d&&e?.model&&(x=e.model)
|
|
8002
|
+
try{const e=JSON.parse(await v.readFile(E,"utf-8")),{kept:t,setAside:r}=function(e){const t={},r={}
|
|
7898
8003
|
if(!e||"object"!=typeof e||Array.isArray(e))return{kept:t,setAside:r}
|
|
7899
8004
|
for(const[s,n]of Object.entries(e))if(!exports.R1.has(s))if(exports.Es.has(s)){if("instructions"===s&&Array.isArray(n)){const e=n.filter(e=>"string"==typeof e&&!/^[a-z][a-z0-9+.-]*:\/\//i.test(e)),o=n.filter(t=>!e.includes(t))
|
|
7900
8005
|
e.length>0&&(t[s]=e),o.length>0&&(r[s]=o)
|
|
7901
8006
|
continue}t[s]=n}else r[s]=n
|
|
7902
8007
|
return{kept:t,setAside:r}}(e)
|
|
7903
|
-
Object.assign(
|
|
7904
|
-
let
|
|
7905
|
-
if(
|
|
7906
|
-
const e=
|
|
7907
|
-
"string"==typeof e&&e.includes("/")&&(
|
|
7908
|
-
|
|
7909
|
-
const
|
|
7910
|
-
await
|
|
7911
|
-
const N
|
|
7912
|
-
let
|
|
7913
|
-
if(
|
|
7914
|
-
const e=await this.resolveWorkflowEffort(
|
|
8008
|
+
Object.assign(T,t),Object.keys(t).length>0&&this.logger.info("Keeping opencode.json keys AlchemyLab does not manage",{keys:Object.keys(t)}),Object.keys(r).length>0&&this.logger.warn("Not loading opencode.json keys: they run commands, change the runtime or send data elsewhere (providers are connected in Settings); the file is left as it is",{keys:Object.keys(r)})}catch{this.logger.info("No opencode.json of the user's in the project folder")}}const P=d?.openCodeTools||A||_||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===d?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==w,todoread:!1,todowrite:!1},O={tools:P}
|
|
8009
|
+
let D=null,q=null
|
|
8010
|
+
if(d?.projectId&&!d?.openCodeModel)try{q=await this.fetchProjectFromRemote(d.projectId)
|
|
8011
|
+
const e=q?.metadata?.defaultModel
|
|
8012
|
+
"string"==typeof e&&e.includes("/")&&(D=e)}catch(e){this.logger.warn("Could not fetch project default model; falling back",{error:e instanceof Error?e.message:String(e)})}const N=d?.openCodeModel||D||C||x||a.FLEET_DEFAULT_MODEL
|
|
8013
|
+
N!==a.FLEET_DEFAULT_MODEL||d?.openCodeModel||D||C||x||this.logger.warn("No default model configured anywhere for this project; agent files fall back to the fleet default model",{projectId:d?.projectId??null}),d?.openCodeModel?O.model=d.openCodeModel:D?O.model=D:C?O.model=C:x&&(O.model=x)
|
|
8014
|
+
const F=k.join(n,".opencode","agent")
|
|
8015
|
+
await v.mkdir(F,{recursive:!0})
|
|
8016
|
+
const L=N||a.FLEET_DEFAULT_MODEL
|
|
8017
|
+
let U,B=null,H=null,W=[],G=null
|
|
8018
|
+
if(d?.projectId)if(await this.seedAgentsFromRemote(d.projectId),H=q??await this.fetchProjectFromRemote(d.projectId),U=H?.metadata?.globalSystemPrompt,U&&this.logger.info("Using global system prompt from project metadata",{projectId:d.projectId,promptLength:U.length}),U=await this.agentPreambleFor(d.projectId,U),W=await this.fetchMcpServersFromRemote(d.projectId),G=(await this.fetchDefaultWorkflowFromRemote(d.projectId))?.connectionPermissions??null,B=await this.fetchAgentsFromRemote(d.projectId),B&&B.length>0){this.logger.info("Using agents from remote database",{count:B.length})
|
|
8019
|
+
const e=await this.resolveWorkflowEffort(d.projectId)
|
|
7915
8020
|
this.clearAgentMarkdowns()
|
|
7916
|
-
for(const t of
|
|
7917
|
-
await this.removeOnDiskAgentFiles(
|
|
7918
|
-
try{const e=new Set([...
|
|
7919
|
-
for(const t of await
|
|
7920
|
-
try{await
|
|
8021
|
+
for(const t of B)t.enabled&&this.setAgentMarkdown(t.name,this.generateAgentMarkdown(t,I,L,U,{effort:e,extraTools:(0,l.agentToolGrants)(W,t.name,G),limits:(0,a.modelLimitsOf)(H?.metadata),claudeThinking:r(H?.metadata)}))
|
|
8022
|
+
await this.removeOnDiskAgentFiles(F,[...B.filter(e=>e.enabled).map(e=>e.name),"explore"]),this.logger.info("Agent prompts held in memory (served to OpenCode over the action server)",{count:this.agentMarkdowns.size})
|
|
8023
|
+
try{const e=new Set([...B.filter(e=>e.enabled).map(e=>`${e.name}.md`),"explore.md"])
|
|
8024
|
+
for(const t of await v.readdir(F))if(t.endsWith(".md")&&!e.has(t)){const e=k.join(F,t),r=`${e}.removed-by-alchemy-${(new Date).toISOString().slice(0,10)}`
|
|
8025
|
+
try{await v.rename(e,r),this.logger.info(`Moved agent file ${t} aside (no agent by that name)`,{movedTo:r})}catch(e){this.logger.warn(`Could not move agent file ${t} aside; leaving it`,{error:e instanceof Error?e.message:String(e)})}}}catch(e){this.logger.warn("Orphaned agent-file cleanup failed",{error:e instanceof Error?e.message:String(e)})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
|
|
7921
8026
|
else this.logger.warn("No projectId provided - agents must be configured via database")
|
|
7922
|
-
const
|
|
7923
|
-
|
|
7924
|
-
const
|
|
7925
|
-
if(
|
|
7926
|
-
for(const e of
|
|
8027
|
+
const K={$schema:"https://opencode.ai/config.json",share:"disabled",model:N,mcp:{[I]:{type:"local",command:[c,$,n],enabled:!0,timeout:6e5}}}
|
|
8028
|
+
S&&process.env.REMOTE_SERVER_URL&&(K.mcp[I].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL})
|
|
8029
|
+
const z=d?.projectId?await this.fetchDefaultWorkflowFromRemote(d.projectId):null,V=!1!==z?.webAccess
|
|
8030
|
+
if(K.permission={...K.permission??{},bash:"deny"},Object.assign(K.permission,(0,l.globalDenyRules)(W)),B&&B.length>0){K.agent={}
|
|
8031
|
+
for(const e of B){if(!e.enabled)continue
|
|
7927
8032
|
const t={}
|
|
7928
8033
|
for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
|
|
7929
|
-
t[
|
|
7930
|
-
Object.assign(t,(0,
|
|
7931
|
-
const e=this.buildExploreMarkdown(
|
|
7932
|
-
this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(
|
|
7933
|
-
|
|
7934
|
-
try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,
|
|
7935
|
-
e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}
|
|
7936
|
-
return[...new Set([r,...s])]}(
|
|
7937
|
-
const
|
|
7938
|
-
|
|
7939
|
-
const
|
|
7940
|
-
await
|
|
7941
|
-
try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=
|
|
8034
|
+
t[b.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${I}_${n}`]=e}if(!V)for(const e of b.WEB_TOOLS)t[b.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${I}_${e}`]=!1
|
|
8035
|
+
Object.assign(t,(0,l.agentToolGrants)(W,e.name,G)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),K.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}K.agent.general={...K.agent.general,disable:!0},K.agent.explore={...K.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${I}_search`]:!0,[`${I}_read_files`]:!0,[`${I}_view_asset`]:!0,[`${I}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
|
|
8036
|
+
const e=this.buildExploreMarkdown(U)
|
|
8037
|
+
this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(K.agent).length})}const Y=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),J=(await this.fetchPluginManifest())["opencode-claude-auth"],X=K.plugin,Q=Array.isArray(X)?X.filter(e=>!Y(e)):[]
|
|
8038
|
+
J&&this.applyAuthPluginPin(Q,J),Q.length>0?K.plugin=Q:delete K.plugin
|
|
8039
|
+
try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,u.existsSync)(k.join(n,e)))
|
|
8040
|
+
e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}K.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,h.join)(e,t)).find(e=>(0,u.existsSync)(e))??(0,h.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,h.isAbsolute)(t)?t:(0,h.join)(e,t))
|
|
8041
|
+
return[...new Set([r,...s])]}(n,T.instructions)
|
|
8042
|
+
const Z=[".opencode/skill",".opencode/skills",".claude/skills",".agents/skills"].map(e=>k.join(n,e)).filter(e=>(0,u.existsSync)(e))
|
|
8043
|
+
Z.length>0&&(K.skills={paths:Z})
|
|
8044
|
+
const ee={...T,...K}
|
|
8045
|
+
s(ee,(y=H?.metadata,!0===y?.anthropicCompaction)),await v.mkdir(k.dirname(j),{recursive:!0}),await v.writeFile(j,JSON.stringify(ee,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:j})
|
|
8046
|
+
try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=k.join(e.homedir(),".alchemy_lab","opencode","config"),s=k.join(r,"opencode.json")
|
|
7942
8047
|
let n=null
|
|
7943
|
-
try{n=JSON.parse(await
|
|
7944
|
-
|
|
7945
|
-
try{const t=await
|
|
7946
|
-
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=
|
|
7947
|
-
o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await n
|
|
8048
|
+
try{n=JSON.parse(await v.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!Y(e))
|
|
8049
|
+
J&&this.applyAuthPluginPin(a,J),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await v.mkdir(r,{recursive:!0}),await v.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(M&&!d)try{let e={}
|
|
8050
|
+
try{const t=await v.readFile(R,"utf-8")
|
|
8051
|
+
e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=P,await v.writeFile(R,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:R}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:s}=await t.e(939).then(()=>t.t(49939,23)),o=await r(S??!1,this.logger,e)
|
|
8052
|
+
o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await s(n,this.logger)}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:j}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,o.getErrorMessage)(e)}),{success:!1,error:(0,o.getErrorMessage)(e)}}var y}}exports.MCPClientConfigurer=b,b.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],b.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),b.rerouteLogged=new Set},6688(e,exports,t){"use strict"
|
|
7948
8053
|
exports.sessionIdentity=void 0
|
|
7949
8054
|
const r=t(16698),s=t(79896),n=t(16928),o=t(43545),i=t(79414),a=t(87280)
|
|
7950
8055
|
class c{constructor(){this.parentCache=new Map,this.sessionRequestCache=new Map,this.endedUnderSession=new Map,this.callScope=new r.AsyncLocalStorage,this.agentTypeCache=new Map}setIdentity(e){const t=this.identity
|
|
@@ -8019,10 +8124,10 @@ t.info("UnifiedLauncher: reaping stale serve from a previous action-server life"
|
|
|
8019
8124
|
try{process.kill(-n,"SIGTERM")}catch{try{process.kill(n,"SIGTERM")}catch{}}await new Promise(e=>setTimeout(e,1500))
|
|
8020
8125
|
try{process.kill(n,0)
|
|
8021
8126
|
try{process.kill(-n,"SIGKILL")}catch{try{process.kill(n,"SIGKILL")}catch{}}t.warn("UnifiedLauncher: stale serve needed SIGKILL",{pid:n})}catch{}}catch(e){t.warn("UnifiedLauncher: stale-serve reap failed (continuing with launch)",{error:(0,m.getErrorMessage)(e)})}}(w,this.logger)
|
|
8022
|
-
const{resolveServeCommand:v,findOpencodeDir:k}=await Promise.resolve().then(()=>t(39068))
|
|
8127
|
+
const{resolveServeCommand:v,findOpencodeDir:k,lastEnginePatchState:S}=await Promise.resolve().then(()=>t(39068))
|
|
8023
8128
|
if(!k())try{(0,f.execFileSync)("win32"===process.platform?"where":"which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"alchemylab-opencode package not installed and no system `opencode` on PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:w})
|
|
8024
|
-
const
|
|
8025
|
-
return new Promise(t=>{const v=l.default.join(w,".alchemy_lab","opencode"),k=process.env.XDG_DATA_HOME||l.default.join(d.default.homedir(),".local","share")
|
|
8129
|
+
const I=v(),$=S()
|
|
8130
|
+
return"applied"===$?.state?this.logger.info("UnifiedLauncher: applied the engine's dependency patches (its install script had not run)",{dir:$.dir,output:$.detail}):"failed"===$?.state&&this.logger.warn("UnifiedLauncher: the engine's dependency patches could not be applied; it starts without them",{dir:$.dir,error:$.detail}),new Promise(t=>{const v=l.default.join(w,".alchemy_lab","opencode"),k=process.env.XDG_DATA_HOME||l.default.join(d.default.homedir(),".local","share")
|
|
8026
8131
|
try{u.default.mkdirSync(l.default.join(k,"opencode"),{recursive:!0})}catch(e){this.logger.warn("UnifiedLauncher: Could not pre-create opencode globalData dir",{error:(0,m.getErrorMessage)(e)})}try{!function(e){const t=function(){const e=l.default.join(d.default.homedir(),".alchemy_lab","opencode","config","opencode.json")
|
|
8027
8132
|
try{const t=JSON.parse(u.default.readFileSync(e,"utf-8"))
|
|
8028
8133
|
if(Array.isArray(t.plugin)){const e=t.plugin.find(e=>"string"==typeof e&&/^opencode-claude-auth@/.test(e))
|
|
@@ -8031,15 +8136,15 @@ if(-1!==t)return e.slice(t+1)}}}catch{}return"1.5.3"}(),i=l.default.join(d.defau
|
|
|
8031
8136
|
if(!u.default.existsSync(c))try{u.default.mkdirSync(a,{recursive:!0}),u.default.writeFileSync(l.default.join(a,"package.json"),JSON.stringify({name:"alchemylab-claude-auth-wrapper",private:!0,version:"0.0.0"},null,2)),e.info("UnifiedLauncher: Pre-installing opencode-claude-auth plugin",{version:t,targetDir:a}),(0,f.execFileSync)("npm",["install",`opencode-claude-auth@${t}`,"--no-audit","--no-fund","--silent","--ignore-scripts"],{cwd:a,stdio:"pipe",timeout:12e4}),e.info("UnifiedLauncher: Pre-installed opencode-claude-auth plugin",{version:t})}catch(t){e.warn("UnifiedLauncher: claude-auth pre-install failed; opencode will install lazily and first OAuth click may crash",{error:(0,m.getErrorMessage)(t)})}if(u.default.existsSync(c)&&(r(c,e,`opencode-claude-auth@${t}`),s(l.default.join(l.default.dirname(c),"credentials.js"),e,`opencode-claude-auth@${t}`),n(c,e,`opencode-claude-auth@${t}`),o(c,e,`opencode-claude-auth@${t}`)),u.default.existsSync(i)){let t
|
|
8032
8137
|
try{t=u.default.readdirSync(i)}catch{return}for(const a of t){if(!a.startsWith("opencode-claude-auth@"))continue
|
|
8033
8138
|
const t=l.default.join(i,a,"node_modules","opencode-claude-auth","dist","index.js")
|
|
8034
|
-
t!==c&&(r(t,e,a),s(l.default.join(l.default.dirname(t),"credentials.js"),e,a),n(t,e,a),o(t,e,a))}}}(this.logger)}catch(e){this.logger.warn("UnifiedLauncher: claude-auth ensure-patched failed",{error:(0,m.getErrorMessage)(e)})}const
|
|
8139
|
+
t!==c&&(r(t,e,a),s(l.default.join(l.default.dirname(t),"credentials.js"),e,a),n(t,e,a),o(t,e,a))}}}(this.logger)}catch(e){this.logger.warn("UnifiedLauncher: claude-auth ensure-patched failed",{error:(0,m.getErrorMessage)(e)})}const S=l.default.join(v,"log"),$=l.default.join(S,"opencode-serve.log")
|
|
8035
8140
|
let j=null,E=0
|
|
8036
8141
|
const A=e=>{if(!j)return
|
|
8037
8142
|
const t=j
|
|
8038
8143
|
j=null,t.end(e)},C=e=>{u.default.writeFileSync($,e),j=u.default.createWriteStream($,{flags:"a"}),E=e.length},_=e=>{if(j&&(E+=e.length,j.write(e),E>=20971520))try{const e=j
|
|
8039
8144
|
j=null,e.end(),u.default.renameSync($,`${$}.1`),C(`# opencode-serve.log \u2014 rotated ${(new Date).toISOString()} (previous in opencode-serve.log.1)\n`)}catch(e){this.logger.warn("UnifiedLauncher: opencode log rotation failed; logging stops for this launch",{error:(0,m.getErrorMessage)(e)})}}
|
|
8040
|
-
try{u.default.mkdirSync(
|
|
8145
|
+
try{u.default.mkdirSync(S,{recursive:!0}),C(`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`)}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,m.getErrorMessage)(e)})}const T=process.env.ALCHEMY_OPENCODE_LOG_LEVEL||"WARN",x=(0,g.newServePassword)()
|
|
8041
8146
|
try{(0,g.writeServeAuth)(w,x)}catch(e){this.logger.warn("UnifiedLauncher: could not record the serve password on disk; only this process can reach the serve",{error:(0,m.getErrorMessage)(e)})}(0,g.noteServe)(null,x),(0,c.noteServeLoadedLogins)()
|
|
8042
|
-
const M=(0,f.spawn)(
|
|
8147
|
+
const M=(0,f.spawn)(I.command,[...I.prependArgs,"--port","0","--data-dir",v,"--log-level",T],{stdio:["ignore","pipe","pipe"],cwd:w,detached:!0,env:{...process.env,...(0,p.getOpencodeSpawnEnv)(w,(0,h.isDevMode)()),...y,OPENCODE_SERVER_PASSWORD:x}})
|
|
8043
8148
|
let R="",P=!1
|
|
8044
8149
|
if(M.pid)try{const e=l.default.join(w,b,"opencode.json")
|
|
8045
8150
|
u.default.mkdirSync(l.default.dirname(e),{recursive:!0}),u.default.writeFileSync(e,JSON.stringify({pid:M.pid,port:null,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("UnifiedLauncher: could not record the serve pid (an orphan would survive a crash)",{error:(0,m.getErrorMessage)(e)})}const O=e=>{if(P)return
|
|
@@ -8165,16 +8270,16 @@ return e.ok?e.version??"":a(3e4)}():await async function(){if(console.log(" In
|
|
|
8165
8270
|
const e=await o()
|
|
8166
8271
|
return e.ok?e.version??"":e.permissionDenied?(c(),null):a(15e3)}()}return null!==h?(console.log("\u2705 Docker ready"+(h?` (server v${h})`:"")),{status:"ready",version:h}):{status:"unavailable",reason:"setup did not complete"}}
|
|
8167
8272
|
const d=t(35317),u=t(79896),h=t(23785)},89063(e,exports){"use strict"
|
|
8168
|
-
function t(e){return exports.lS[String(e??"").toLowerCase()]??exports.Ey}function
|
|
8169
|
-
return
|
|
8273
|
+
function t(e){return"assistant"===e?.role&&!0===e?.summary}function r(e){return exports.lS[String(e??"").toLowerCase()]??exports.Ey}function s(e){const t=r(e).atLeast
|
|
8274
|
+
return t-Math.min(5*n,t/10)}exports.Ey=exports.lS=void 0,exports.tokensOfMessage=function(e){const t={input:0,output:0,reasoning:0,cacheRead:0,cacheWrite:0,cost:0,hasUsage:!1}
|
|
8170
8275
|
for(const r of e?.parts||[])if("step-finish"===r?.type&&r?.tokens){t.hasUsage=!0
|
|
8171
8276
|
const e=r.tokens
|
|
8172
8277
|
t.input+=e.input||0,t.output+=e.output||0,t.reasoning+=e.reasoning||0,t.cacheRead+=e.cache?.read||0,t.cacheWrite+=e.cache?.write||0,t.cost+=r.cost||0}const r=e?.info
|
|
8173
8278
|
if(!t.hasUsage&&r?.tokens){const e=r.tokens
|
|
8174
|
-
t.input=e.input||0,t.output=e.output||0,t.reasoning=e.reasoning||0,t.cacheRead=e.cache?.read||0,t.cacheWrite=e.cache?.write||0,t.cost=r.cost||0,t.hasUsage=t.input>0||t.output>0||t.cacheRead>0||t.cacheWrite>0||t.cost>0}return t},exports.contextTokensOf=function(e){const
|
|
8175
|
-
return(
|
|
8176
|
-
const
|
|
8177
|
-
exports.lS={anthropic:{atLeast:60*
|
|
8279
|
+
t.input=e.input||0,t.output=e.output||0,t.reasoning=e.reasoning||0,t.cacheRead=e.cache?.read||0,t.cacheWrite=e.cache?.write||0,t.cost=r.cost||0,t.hasUsage=t.input>0||t.output>0||t.cacheRead>0||t.cacheWrite>0||t.cost>0}return t},exports.contextTokensOf=function(e){const r=e?.tokens||{}
|
|
8280
|
+
return t(e)?r.output??0:(r.input??0)+(r.cache?.read??0)},exports.isCompactionSummary=t,exports.cacheRetentionMsFor=function(e){return r(e).atMost},exports.cacheWarmForMs=s,exports.longestCacheWarmMs=function(){return Math.max(s(null),...Object.keys(exports.lS).map(s))}
|
|
8281
|
+
const n=6e4
|
|
8282
|
+
exports.lS={anthropic:{atLeast:60*n,atMost:65*n},openai:{atLeast:60*n,atMost:90*n}},exports.Ey={atLeast:5*n,atMost:60*n}},75632(e,exports){"use strict"
|
|
8178
8283
|
exports.REGISTER_BY_SETUP_RUN=exports.b=void 0,exports.toolScopesFor=function(e,t,r){const s=exports.b[e]
|
|
8179
8284
|
if(!s)return{}
|
|
8180
8285
|
const n={}
|
|
@@ -10074,10 +10179,10 @@ for(const r of Array.isArray(e)?e:[]){const e=o(r)
|
|
|
10074
10179
|
if(e)for(const n of s(r))t.set(n.trim(),e)}return t}
|
|
10075
10180
|
const i=t(67071),a=t(65454),c={node:"node",npm:"node",npx:"node",yarn:"node",pnpm:"node",tsx:"node","ts-node":"node",corepack:"node",deno:"deno",bun:"bun",python:"python",python3:"python",pip:"python",pip3:"python",pytest:"python",uv:"python",poetry:"python",pipenv:"python",tox:"python",go:"go",gofmt:"go",cargo:"rust",rustc:"rust",ruby:"ruby",gem:"ruby",bundle:"ruby",rake:"ruby",rspec:"ruby",java:"java",javac:"java",mvn:"java",gradle:"java","./gradlew":"java",kotlin:"java",kotlinc:"java",php:"php",composer:"php",phpunit:"php",dotnet:"dotnet",swift:"swift",zig:"zig",elixir:"elixir",mix:"elixir",dart:"dart",flutter:"dart",perl:"perl",lua:"lua",julia:"julia",Rscript:"r",pdfinfo:"pdf",pdftotext:"pdf",pdftoppm:"pdf",pdfimages:"pdf",pdffonts:"pdf",pdfseparate:"pdf",pdfunite:"pdf",qpdf:"pdf",soffice:"office",libreoffice:"office",pandoc:"office","render-pdf":"office",zbarimg:"office",segno:"office"},l=new Set(["if","then","else","elif","do","while","until","!","time","exec","env","command","sudo","{"])},64469(e,exports,t){"use strict"
|
|
10076
10181
|
function r(e,t,r=0){return!!t.lastActiveMs&&!(r&&t.lastActiveMs<r)&&e-t.lastActiveMs<=(0,i.cacheWarmForMs)(t.providerID)}function s(e,t){return!(!e||!t)&&(e===t||(0,o.isBranchWorkerAgentType)(e)&&(0,o.isBranchWorkerAgentType)(t))}exports.b6=exports.nZ=exports.WARM_CODER_DEFAULTS=void 0,exports.readWarmCoderPolicy=function(e){const t=e?.warmCoder??{},r=Number(t.maxContextTokens),s=Number(t.coldMaxContextTokens),n=Number(t.coldMaxChangedLines)
|
|
10077
|
-
return{enabled:void 0===t.enabled?exports.WARM_CODER_DEFAULTS.enabled:!1!==t.enabled,maxContextTokens:Number.isFinite(r)&&r>0?r:exports.WARM_CODER_DEFAULTS.maxContextTokens,coldResume:void 0===t.coldResume?exports.WARM_CODER_DEFAULTS.coldResume:!1!==t.coldResume,coldMaxContextTokens:Number.isFinite(s)&&s>0?s:exports.WARM_CODER_DEFAULTS.coldMaxContextTokens,coldMaxChangedLines:Number.isFinite(n)&&n>=0?n:exports.WARM_CODER_DEFAULTS.coldMaxChangedLines,areaReuse:void 0===t.areaReuse?exports.WARM_CODER_DEFAULTS.areaReuse:!1!==t.areaReuse}},exports.isCacheWarm=r,exports.contextCeilingFor=function(e){const
|
|
10078
|
-
const
|
|
10079
|
-
return
|
|
10080
|
-
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"}
|
|
10081
10186
|
if(!o.length)return{reuse:!1,why:"no-candidate"}
|
|
10082
10187
|
const a=["role","unrelated","model","tools","busy","context","archived","cold","cold-context","stale"]
|
|
10083
10188
|
let c=0
|