alchemylab 3.0.0 → 3.0.2

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 CHANGED
@@ -641,12 +641,12 @@ if(!t||!r)continue
641
641
  const n=`alchemy-pv-${c.id}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,o=e.service&&String(e.service).trim()
642
642
  await s(n,!o),o?W.push({volume:n,target:r,service:o}):H.push({volume:n,target:r})}O.hostExec||(0,$.sweepBoxGit)((0,k.workDir)(e.projectPath,"box-git"))
643
643
  const G=()=>O.hostExec?new x.HostExperiment({worktreePath:q,sandbox:O.hostExec,seed:P.seed,env:w.connEnv&&"object"==typeof w.connEnv?w.connEnv:void 0}):new I.ConfinedExperiment(q,P,{projectName:`alchemy-exp-${D}`,projectId:e.projectId,readOnlySource:!N,boxGitBaseDir:(0,k.workDir)(e.projectPath,"box-git"),egress:O.egress,egressAllowlist:Array.isArray(w.egressAllowlist)?w.egressAllowlist:void 0,boxMounts:H.length?H:void 0,serviceMounts:W.length?W:void 0,session:{memory:B.memory,cpus:B.cpus,size:(0,p.boxSize)(t.size)??(0,p.boxSize)(B.size),pidsLimit:B.pidsLimit,scratchSizeMb:B.scratchMb,shmSizeMb:B.shmMb,addCaps:O.allowCaps&&Array.isArray(w.addCaps)?w.addCaps:void 0,sysctls:O.allowCaps&&w.sysctls&&"object"==typeof w.sysctls?w.sysctls:void 0,runAsRoot:O.allowRoot&&!0===w.runAsRoot||O.allowCaps&&Array.isArray(w.addCaps)&&w.addCaps.length>0||O.allowDockerSocket&&!0===w.dockerSocket,dockerSocket:O.allowDockerSocket&&!0===w.dockerSocket}}),K={"alchemy.purpose":"experiment","alchemy.session":t.sessionId,"alchemy.request":t.requestId,"alchemy.environment":c?.name}
644
- let V=G(),z=await(0,C.withContainerLabels)(K,()=>V.start())
645
- if(!z.ok&&d&&(P.install||P.artifacts?.length)&&(e.logger?.warn?.("experiment: the default install failed; opening the box without it",{error:z.error}),P.install=void 0,P.artifacts=void 0,V=G(),z=await(0,C.withContainerLabels)(K,()=>V.start())),!z.ok)throw N&&await r(e.projectPath,q),new Error(z.error||"experiment failed to start")
646
- const Y=c?.id?String(c.id):d?(0,u.defaultBoxIdentity)({image:w.image,install:P.install,artifacts:P.artifacts}):void 0,J=Array.from(new Set((0,_.resolveArtifacts)(P).flatMap(e=>e.manifests??[]))),X=Y&&!N?{box:Y,manifests:J,depsFrom:await(0,h.computeFingerprint)(q,J)}:void 0,Q=["policy","agent","human"],Z=Array.isArray(w.commandGate)?w.commandGate.filter(e=>Q.includes(e)):O.commandGate,ee=o.register({projectId:e.projectId,sessionId:t.sessionId,environmentId:c?.id,environmentName:c?.name,commandGate:Z,confinement:w.confinement||"confined",...!N&&t.requestId?{liveWorktree:{path:q,requestId:String(t.requestId),...X?{proves:X}:{}}}:{},...N&&L?{snapshot:{path:q,branch:L}}:{},exp:V,onDispose:async()=>{try{const r=V.getBlockedEgress,s="function"==typeof r?r.call(V):[],n=V.getPrepareEgress,o="function"==typeof n?n.call(V):[],i=[...s.map(e=>({host:e.host,method:e.method,ts:e.t,phase:"run",verdict:"blocked"})),...o.map(e=>({host:e.host,method:e.method,ts:e.t,phase:"prepare",verdict:e.allowed?"observed":"observed-would-block"}))]
644
+ let z=G(),V=await(0,C.withContainerLabels)(K,()=>z.start())
645
+ if(!V.ok&&d&&(P.install||P.artifacts?.length)&&(e.logger?.warn?.("experiment: the default install failed; opening the box without it",{error:V.error}),P.install=void 0,P.artifacts=void 0,z=G(),V=await(0,C.withContainerLabels)(K,()=>z.start())),!V.ok)throw N&&await r(e.projectPath,q),new Error(V.error||"experiment failed to start")
646
+ const Y=c?.id?String(c.id):d?(0,u.defaultBoxIdentity)({image:w.image,install:P.install,artifacts:P.artifacts}):void 0,J=Array.from(new Set((0,_.resolveArtifacts)(P).flatMap(e=>e.manifests??[]))),X=Y&&!N?{box:Y,manifests:J,depsFrom:await(0,h.computeFingerprint)(q,J)}:void 0,Q=["policy","agent","human"],Z=Array.isArray(w.commandGate)?w.commandGate.filter(e=>Q.includes(e)):O.commandGate,ee=o.register({projectId:e.projectId,sessionId:t.sessionId,environmentId:c?.id,environmentName:c?.name,commandGate:Z,confinement:w.confinement||"confined",...!N&&t.requestId?{liveWorktree:{path:q,requestId:String(t.requestId),...X?{proves:X}:{}}}:{},...N&&L?{snapshot:{path:q,branch:L}}:{},exp:z,onDispose:async()=>{try{const r=z.getBlockedEgress,s="function"==typeof r?r.call(z):[],n=z.getPrepareEgress,o="function"==typeof n?n.call(z):[],i=[...s.map(e=>({host:e.host,method:e.method,ts:e.t,phase:"run",verdict:"blocked"})),...o.map(e=>({host:e.host,method:e.method,ts:e.t,phase:"prepare",verdict:e.allowed?"observed":"observed-would-block"}))]
647
647
  i.length&&(await e.apiClient.post(`/api/projects/${e.projectId}/egress-attempts`,{sessionId:t.sessionId,environmentId:c?.id,confinement:w.confinement||"confined",attempts:i}).catch(t=>e.logger?.warn?.("egress-attempts persist failed",{error:t?.message})),o.length&&e.logger?.info?.("prepare-phase egress observed",{environmentName:c?.name,hosts:o.map(e=>e.host)}))}catch{}N&&await r(e.projectPath,q)}})
648
648
  if(!O.hostExec){const e=(0,A.previewSelfBaseUrl)()
649
- e&&(0,A.registerPreviewHandle)(ee.handle,`${e}/api/experiments/${ee.handle}/live-frame`,c?.name||void 0,{awaitingFrame:!0})}return b({handle:ee.handle,environmentId:c?.id,environmentName:c?.name,experimentName:i?.name,image:w.image||"node:20-alpine",hasServices:!!w.composeFile,workdir:O.hostExec?q:"/work",hostTier:!!O.hostExec,hasGit:!O.hostExec&&!0===V.hasGit,...O.hostExec||N||!t.requestId?{}:{sourceWritable:!0},...O.hostExec||"boolean"!=typeof V.hasGitBinary?{}:{gitBinary:V.hasGitBinary}})}(e,t)
649
+ e&&(0,A.registerPreviewHandle)(ee.handle,`${e}/api/experiments/${ee.handle}/live-frame`,c?.name||void 0,{awaitingFrame:!0})}return b({handle:ee.handle,environmentId:c?.id,environmentName:c?.name,experimentName:i?.name,image:w.image||"node:20-alpine",hasServices:!!w.composeFile,workdir:O.hostExec?q:"/work",hostTier:!!O.hostExec,hasGit:!O.hostExec&&!0===z.hasGit,...O.hostExec||N||!t.requestId?{}:{sourceWritable:!0},...O.hostExec||"boolean"!=typeof z.hasGitBinary?{}:{gitBinary:z.hasGitBinary}})}(e,t)
650
650
  L.set(a,l)
651
651
  try{return await l}finally{L.get(a)===l&&L.delete(a)}},exports.runGatedExec=o,exports.boxDestination=i,exports.insertResultIntoExperiment=async function(e){const r=(0,E.experimentRegistry)().use(e.handle)
652
652
  if(!r){const t=(0,E.experimentRegistry)().whyGone(e.handle)
@@ -743,7 +743,8 @@ if(t){const r=await n(M,P,l,x)
743
743
  if("settle"===(0,d.kindProfile)(D?.type).finalize){x.info("Empty branch on a drive request -- completing without merge",{requestId:l})
744
744
  try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:"string"==typeof D?.completionSummary&&D.completionSummary.trim()||`The drive of ${l} finished.`,acceptanceEvidence:[{evidence:"Drive request: verification receipt lives in the completion summary; no code changes by design."}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"drive-completion",diffStats:{additions:0,deletions:0}}})}catch(e){return{success:!1,message:`Drive completion failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return{success:!0,message:`Drive ${l} completed (empty branch, nothing to merge)`}}if("artifact-only"===r.shape){x.info("Empty branch with artifact-only work -- completing without merge",{requestId:l,detail:r.detail})
745
745
  try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:`Finished ${l} with records only -- ${r.detail}. It changed no files, so it was completed with nothing new in your project.`,acceptanceEvidence:[{evidence:`Artifact-only request: ${r.detail}. Persisted outside git; the branch had no code commits to merge.`}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"artifact-only-completion",diffStats:{additions:0,deletions:0}}})}catch(e){return{success:!1,message:`Artifact-only completion failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return(0,h.maybePublishReceiptOnMerge)(l),{success:!0,message:`Request ${l} completed without a merge: artifact-only work (${r.detail}), already persisted; the branch had no code commits.`,data:{artifactOnly:!0}}}if(e>0){const t=(0,m.alreadyInMainSummary)(l,B),r="string"==typeof D?.completionSummary?D.completionSummary.trim():""
746
- try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:r?`${r}\n\n${t}`:t,acceptanceEvidence:[{evidence:`The branch requests/${l} has the same content as ${B}: its change is already there.`}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"already-in-main",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return{success:!1,message:`Finishing ${l} as already in ${B} failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return x.info("Branch content-identical to the default branch -- finished as already in main",{requestId:l,aheadCount:e}),{success:!0,message:`${l} is finished: ${t}`,data:{alreadyInMain:!0}}}return{success:!1,message:t,error:"Empty branch (nothing to merge)"}}}catch(e){x.warn("Could not compute ahead count; proceeding with merge",{requestId:l,error:(0,f.getErrorMessage)(e)})}let V,z,Y
746
+ try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:r?`${r}\n\n${t}`:t,acceptanceEvidence:[{evidence:`The branch requests/${l} has the same content as ${B}: its change is already there.`}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"already-in-main",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return{success:!1,message:`Finishing ${l} as already in ${B} failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return x.info("Branch content-identical to the default branch -- finished as already in main",{requestId:l,aheadCount:e}),{success:!0,message:`${l} is finished: ${t}`,data:{alreadyInMain:!0}}}if(0===e&&"string"==typeof D?.roundOf&&D.roundOf){const e="string"==typeof D?.completionSummary?D.completionSummary.trim():""
747
+ try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,{summary:e||"Nothing was changed.",acceptanceEvidence:[{evidence:"This round changed nothing: its branch has no commit of its own and no record writes into the project."}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"answer-only",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return{success:!1,message:`Finishing ${l} as an answer failed: ${(0,f.getErrorMessage)(e)}`,error:(0,f.getErrorMessage)(e)}}return x.info("A round that changed nothing -- finished as an answer",{requestId:l}),{success:!0,message:`${l} is finished: it changed nothing.`,data:{answerOnly:!0}}}return{success:!1,message:t,error:"Empty branch (nothing to merge)"}}}catch(e){x.warn("Could not compute ahead count; proceeding with merge",{requestId:l,error:(0,f.getErrorMessage)(e)})}let z,V,Y
747
748
  {const e=await(0,S.runMergeQueueGate)({projectPath:R,requestId:l,branchName:F,defaultBranch:B,requestTitle:D?.title,criteria:D?.acceptanceCriteria??D?.acceptance_criteria,acceptEnvironmentGap:u,acceptFailingTests:T,testGateOff:(0,c.checksOff)(D),source:"finalize-request",logger:x,fetchRequestRuns:async()=>{const e=await M.get(`/api/projects/${P}/requests/${l}/test-runs`),t=e?.data??e??[]
748
749
  return Array.isArray(t)?t:[]},fetchActiveProcesses:async()=>{const e=await M.get(`/api/projects/${P}/test-processes/active?forRequest=${encodeURIComponent(l)}`),t=e?.data??e??[]
749
750
  return(Array.isArray(t)?t:[t]).filter(Boolean)},evaluateMainChange:async({landingSha:e})=>{const{mainChangeAtLanding:r}=await Promise.resolve().then(()=>t(69689))
@@ -756,11 +757,11 @@ return{success:!1,message:`${o(l)}, and its tests passed: one check of a criteri
756
757
  if("error"===e.action)return{success:!1,message:`Merge queue check failed: ${e.message}`,error:e.message}
757
758
  if("sent-back"===e.action)return await(0,I.bringMainIn)({projectPath:R,requestId:l,defaultBranch:B,logger:x}),{success:!1,refused:!0,message:`${o(l)}: ${e.summary}`,error:"Held: main's newer work broke a check written for its criteria",data:{sentBack:!0}}
758
759
  if("review-held"===e.action){const t=await(0,I.holdForMainChangeReview)({projectPath:R,projectId:P,requestId:l,defaultBranch:B,touched:e.touched,logger:x,post:(e,t)=>M.post(e,t)})
759
- return{success:!1,refused:!0,message:`${o(l,!0)}: ${e.summary} ${t.reviewing?"A reviewer is checking its criteria against that change; it is completed after that if nothing needs fixing.":"A review of that change could not be started; send it back to rework or sign off again."}`,error:"Held: a reviewer checks main's change against the criteria first",data:{reviewHeld:!0,reviewing:t.reviewing}}}"land"===e.action&&(V=e.certifiedCommitSha)}try{z=(await L.raw(["merge-base",B,F])).trim()||void 0}catch{}const J=!1,X=V??(await L.revparse([F])).trim(),Q=await(0,v.findCommittedMarkers)(L,X)
760
+ return{success:!1,refused:!0,message:`${o(l,!0)}: ${e.summary} ${t.reviewing?"A reviewer is checking its criteria against that change; it is completed after that if nothing needs fixing.":"A review of that change could not be started; send it back to rework or sign off again."}`,error:"Held: a reviewer checks main's change against the criteria first",data:{reviewHeld:!0,reviewing:t.reviewing}}}"land"===e.action&&(z=e.certifiedCommitSha)}try{V=(await L.raw(["merge-base",B,F])).trim()||void 0}catch{}const J=!1,X=z??(await L.revparse([F])).trim(),Q=await(0,v.findCommittedMarkers)(L,X)
760
761
  if(!Q.determined||Q.files.length>0){const e=Q.files??[]
761
- return{success:!1,message:Q.determined?`Cannot merge ${l}: committed conflict markers remain in ${e.join(", ")} (the branch was freshened while this merge was queued). Its worker must resolve them and finish again first.`:`Cannot merge ${l}: the conflict-marker check could not run on the landing commit. Nothing was merged.`,error:"Unresolved conflict markers",data:{hasConflicts:!0,markerFiles:e}}}if(V){const e=await(0,$.landCertifiedMerge)(R,{defaultBranch:B,mergeCommitSha:V})
762
+ return{success:!1,message:Q.determined?`Cannot merge ${l}: committed conflict markers remain in ${e.join(", ")} (the branch was freshened while this merge was queued). Its worker must resolve them and finish again first.`:`Cannot merge ${l}: the conflict-marker check could not run on the landing commit. Nothing was merged.`,error:"Unresolved conflict markers",data:{hasConflicts:!0,markerFiles:e}}}if(z){const e=await(0,$.landCertifiedMerge)(R,{defaultBranch:B,mergeCommitSha:z})
762
763
  if(!e.landed)return{success:!1,message:"overlap"===e.refusal||"busy"===e.refusal?`${o(l)}: ${e.reason}`:`${o(l)}: ${e.reason}. Try again, and its checks run again first.`,error:"Certified landing refused",data:{landingRefused:!0,reason:e.refusal,files:e.files??[]}}
763
- Y=V,z=await(0,g.firstParentSha)(L,V)??z,x.info("merge-queue: landed certified catch-up commit (tested tree == merged tree)",{requestId:l,mergeCommit:Y.substring(0,8)})}else{const t=D?.title?`Merge ${F}-${D.title}`:`Merge ${F}`,n=async e=>{const r=await(0,j.buildMergeCommit)(R,{defaultBranch:B,branchSha:e,message:t})
764
+ Y=z,V=await(0,g.firstParentSha)(L,z)??V,x.info("merge-queue: landed certified catch-up commit (tested tree == merged tree)",{requestId:l,mergeCommit:Y.substring(0,8)})}else{const t=D?.title?`Merge ${F}-${D.title}`:`Merge ${F}`,n=async e=>{const r=await(0,j.buildMergeCommit)(R,{defaultBranch:B,branchSha:e,message:t})
764
765
  return r.ok?{built:r,landed:await(0,j.landOnDefaultBranch)(R,{defaultBranch:B,commitSha:r.commitSha,expectedHead:r.baseHead})}:{built:r,landed:null}}
765
766
  let i=await n(X)
766
767
  if(!i.built.ok&&"conflicts"===i.built.reason&&null===(0,d.kindProfile)(D?.type).worker){const e=(0,d.kindProfile)(D?.type).label
@@ -774,7 +775,7 @@ if(i=await n(e),!i.built.ok){const e="conflicts"===i.built.reason?`it still conf
774
775
  return{success:!1,message:`Merge of ${l} failed after a clean reconcile: ${e}. Nothing was merged.`,error:e}}}}if(!i.built.ok){if("nothing-to-merge"===i.built.reason)return{success:!1,message:`${o(l)}: its work holds nothing the project does not already have.`,error:"Nothing to merge"}
775
776
  const t="error"===i.built.reason?i.built.error:"the merge commit could not be built"
776
777
  return r?.__rediscovered||(await L.branch()).all.includes(F)?{success:!1,message:`Merge failed: ${t}`,error:t}:(x.warn("finalize: the request branch disappeared mid-merge; re-deciding as branchless",{requestId:l,errorMsg:t}),await e({...r,__rediscovered:!0},s))}if(!i.landed||!i.landed.landed){const e=i.landed&&!i.landed.landed?i.landed:null
777
- return{success:!1,message:`${o(l)}: ${e?.message??"its changes could not be put into the project"}`,error:"Landing refused",data:{landingRefused:!0,reason:e?.reason,files:e?.files??[]}}}Y=i.built.commitSha,x.info("Merge landed through the door",{requestId:l,mergeCommit:Y.substring(0,8),where:i.landed.where})}const Z=(Y?await(0,g.firstParentSha)(L,Y):void 0)??z,ee=await(0,g.computeDiffStats)(L,Z,Y),te=Y?await(0,v.describeAdditiveAutoMerges)(L,Z,Y):null,re=Y&&(0,c.checksOff)(D)?await(0,A.landedOnTopOf)(R,Y,l).catch(()=>[]):[],se={summary:`Finalized request ${l}`,acceptanceEvidence:[{evidence:"Finalized via GUI after review"},...T?[{evidence:'The user answered the test-cap card with "merge anyway": this merged with the tests failing, waived for this merge.'}]:[],...u?[{evidence:"The user accepted an environment gap: the box could not run the checks, and this merged without them."}]:[],...te?[{evidence:`Parts of this merge were combined automatically and not reviewed together: ${te}`}]:[]],mergeResult:{mergeCommitHash:Y||"unknown",mergeBaseSha:z,cleanedUp:J,method:"action-server-merge",diffStats:ee,...re.length?{notCheckedWith:re}:{}}}
778
+ return{success:!1,message:`${o(l)}: ${e?.message??"its changes could not be put into the project"}`,error:"Landing refused",data:{landingRefused:!0,reason:e?.reason,files:e?.files??[]}}}Y=i.built.commitSha,x.info("Merge landed through the door",{requestId:l,mergeCommit:Y.substring(0,8),where:i.landed.where})}const Z=(Y?await(0,g.firstParentSha)(L,Y):void 0)??V,ee=await(0,g.computeDiffStats)(L,Z,Y),te=Y?await(0,v.describeAdditiveAutoMerges)(L,Z,Y):null,re=Y&&(0,c.checksOff)(D)?await(0,A.landedOnTopOf)(R,Y,l).catch(()=>[]):[],se={summary:`Finalized request ${l}`,acceptanceEvidence:[{evidence:"Finalized via GUI after review"},...T?[{evidence:'The user answered the test-cap card with "merge anyway": this merged with the tests failing, waived for this merge.'}]:[],...u?[{evidence:"The user accepted an environment gap: the box could not run the checks, and this merged without them."}]:[],...te?[{evidence:`Parts of this merge were combined automatically and not reviewed together: ${te}`}]:[]],mergeResult:{mergeCommitHash:Y||"unknown",mergeBaseSha:V,cleanedUp:J,method:"action-server-merge",diffStats:ee,...re.length?{notCheckedWith:re}:{}}}
778
779
  try{await M.post(`/api/projects/${P}/requests/${l}/finalize-completion`,se)}catch(e){const t=(0,f.getErrorMessage)(e)
779
780
  return x.error("Merge landed but the server could not record it yet; the record rides this action's result",{requestId:l,mergeCommitHash:Y,error:t}),(0,$.clearMergeCandidate)(R,l).catch(()=>{}),{success:!1,message:`Code was merged (commit: ${Y?.substring(0,8)}) but server finalization failed: ${t}`,error:t,data:{mergeCommitHash:Y,partialSuccess:!0,finalizeCompletion:se}}}return(0,h.maybePublishReceiptOnMerge)(l),(0,$.clearMergeCandidate)(R,l).catch(()=>{}),{success:!0,message:`Successfully finalized request ${l}`,data:{mergeCommitHash:Y,branchDeleted:J,...te?{autoMergeNote:te}:{},...re.length?{notCheckedWith:re}:{}}}}catch(e){const t=(0,f.getErrorMessage)(e)
780
781
  return x.error("finalize-request action failed",{requestId:l,error:t}),{success:!1,message:`Failed to finalize request: ${t}`,error:t}}finally{if(q&&!N)try{(0,k.releaseMergeLease)(R,l)}catch{}}}
@@ -1315,9 +1316,13 @@ if(!u.ok)return{advanced:!1,clean:!0,why:u.why}
1315
1316
  try{const r=await e.apiClient.patch(`/api/projects/${e.projectId}/requests/${t.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:t.coderSessionId},headSha:i,reviewRound:{...s.reviewed?{reviewed:s.reviewed}:{},clean:!0}})
1316
1317
  if(r&&!1===r.success)return{advanced:!1,clean:!0,why:`the finish signal was refused: ${r.error??r.message??"no reason given"}`}}catch(e){return{advanced:!1,clean:!0,why:`the finish signal failed: ${(0,f.getErrorMessage)(e)}`}}return{advanced:!0,clean:!0}}function d(e){const t="DELIVERY: ESCALATE",r=e.map(e=>`${e.content?.summary??""}\n${e.content?.details??""}`).join("\n"),s=/^\(?\s*(none|n\/a|nothing|no items?|nothing to fix)\s*\)?\.?$/i,n=[],o=r.split("\n").map(e=>e.trim())
1317
1318
  for(let e=0;e<o.length;e++)if(/^\**must_fix\**:?$/i.test(o[e]))for(let t=e+1;t<o.length&&!/^\**(nice_to_have|out_of_scope)\**:?$/i.test(o[t]);t++){const e=o[t].match(/^[-*]\s+(.+)$/)
1318
- e&&!s.test(e[1])&&n.push(e[1])}if(r.includes(t)&&0===n.length)return{escalated:!0,rework:!1,reason:r.slice(r.indexOf(t)+18).replace(/^[\s:\u2013-]+/,"").split("\n").map(e=>e.trim()).find(Boolean)??"The delivery diverges from what was asked; the reviewer could not settle it."}
1319
- const i=/^\W*Match\W*:\s*no\b[\s,.:;-]*(?:because\s*)?(.*)$/im.exec(r)
1320
- return e.some(e=>"failure"===String(e.content?.reportStatus??""))||n.length>0||i?{escalated:!1,rework:!0,reason:n[0]??(i?.[1]?.trim()||(e[0]?.content?.summary??"").slice(0,300))}:{escalated:!1,rework:!1,reason:(e[0]?.content?.summary??"").slice(0,800)}}async function u(e,t,r,s,n,o){const i={skipped:r,...s?{reviewed:s}:{}}
1319
+ e&&!s.test(e[1])&&n.push(e[1])}const i=/^[\s:\u2013-]*(?:(?:is|was|would be|are)\s+)?(?:not|never)\b|^[\s:\u2013-]*(?:isn'?t|wasn'?t|unnecessary)\b/i
1320
+ let a=-1
1321
+ for(let e=r.indexOf(t);e>=0;e=r.indexOf(t,e+1)){const t=r.slice(e+18).split("\n")[0]??""
1322
+ if(!i.test(t)){a=e
1323
+ break}}if(a>=0&&0===n.length)return{escalated:!0,rework:!1,reason:r.slice(a+18).replace(/^[\s:\u2013-]+/,"").split("\n").map(e=>e.trim()).find(Boolean)??"The delivery diverges from what was asked; the reviewer could not settle it."}
1324
+ const c=/^\W*Match\W*:\s*no\b[\s,.:;-]*(?:because\s*)?(.*)$/im.exec(r)
1325
+ return e.some(e=>"failure"===String(e.content?.reportStatus??""))||n.length>0||c?{escalated:!1,rework:!0,reason:n[0]??(c?.[1]?.trim()||(e[0]?.content?.summary??"").slice(0,300))}:{escalated:!1,rework:!1,reason:(e[0]?.content?.summary??"").slice(0,800)}}async function u(e,t,r,s,n,o){const i={skipped:r,...s?{reviewed:s}:{}}
1321
1326
  let a=!1
1322
1327
  if("no-rule"===r)try{const r=await e.apiClient.get(`/api/projects/${e.projectId}/requests/${t.requestId}`)
1323
1328
  a=r.success&&"ready_for_review"===r.data?.status}catch{}if("no-rule"!==r||a){try{await e.apiClient.patch(`/api/projects/${e.projectId}/requests/${t.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:t.coderSessionId},reviewRound:i})}catch(r){e.logger.error("review-fanout: finish signal failed (gate stays pending -- reconciler surfaces it)",{requestId:t.requestId,error:(0,f.getErrorMessage)(r)})}"nothing"===r&&o&&await(0,A.injectPromptToSession)(e,t.coderSessionId,o)}else await(0,A.injectPromptToSession)(e,t.coderSessionId,o)
@@ -1353,15 +1358,15 @@ return r&&U.push(e),c.info(r?"review-fanout: a reviewer ended its turn without a
1353
1358
  q()
1354
1359
  const W=Date.now(),G=P.filter(e=>B.missingSessionIds.includes(e.sessionId)).map(e=>e.label),K=o(P,U,B.missingSessionIds)
1355
1360
  c.info("review-fanout: barrier fired",{requestId:a.requestId,reason:B.reason,reported:D.length-G.length,missing:G.length,...K.length?{silentEnds:K}:{}})
1356
- const V=v?(H??[]).filter(e=>P.some(t=>t.sessionId===e.fromSessionId)):[]
1357
- if(v){try{const{escalated:e,rework:r,reason:s}=d(V),n=P[0]?.sessionId
1358
- 0===V.length?(await t.apiClient.post(`/api/projects/${t.projectId}/requests/${a.requestId}/delivery-verdict`,{escalated:!0,reviewerSessionId:n,reason:"The delivery review did not report, so nothing looked at this request before merge."}),c.warn("review-fanout: DELIVERY did not report; escalated rather than merged",{requestId:a.requestId})):(await t.apiClient.post(`/api/projects/${t.projectId}/requests/${a.requestId}/delivery-verdict`,{escalated:e,rework:r,reviewerSessionId:n,reason:s.slice(0,800)}),c.info("review-fanout: DELIVERY verdict applied",{requestId:a.requestId,escalated:e,rework:r}))}catch(e){c.warn("review-fanout: DELIVERY verdict failed to apply",{requestId:a.requestId,error:(0,f.getErrorMessage)(e)})}return{success:!0,message:`review-fanout: delivery verdict applied for ${a.requestId}`,...K.length?{data:{silentEnds:K}}:{}}}let z=!1,Y=null
1361
+ const z=v?(H??[]).filter(e=>P.some(t=>t.sessionId===e.fromSessionId)):[]
1362
+ if(v){try{const{escalated:e,rework:r,reason:s}=d(z),n=P[0]?.sessionId
1363
+ 0===z.length?(await t.apiClient.post(`/api/projects/${t.projectId}/requests/${a.requestId}/delivery-verdict`,{escalated:!0,reviewerSessionId:n,reason:"The delivery review did not report, so nothing looked at this request before merge."}),c.warn("review-fanout: DELIVERY did not report; escalated rather than merged",{requestId:a.requestId})):(await t.apiClient.post(`/api/projects/${t.projectId}/requests/${a.requestId}/delivery-verdict`,{escalated:e,rework:r,reviewerSessionId:n,reason:s.slice(0,800)}),c.info("review-fanout: DELIVERY verdict applied",{requestId:a.requestId,escalated:e,rework:r}))}catch(e){c.warn("review-fanout: DELIVERY verdict failed to apply",{requestId:a.requestId,error:(0,f.getErrorMessage)(e)})}return{success:!0,message:`review-fanout: delivery verdict applied for ${a.requestId}`,...K.length?{data:{silentEnds:K}}:{}}}let V=!1,Y=null
1359
1364
  try{const e=await t.apiClient.get(`/api/projects/${t.projectId}/requests/${a.requestId}`)
1360
- Y=e.success?e.data:null,z="ready_for_review"===Y?.status}catch{}if(z){try{await t.apiClient.patch(`/api/projects/${t.projectId}/requests/${a.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:a.coderSessionId},reviewRound:b?{reviewed:b}:{}}),t.logger.info("review-fanout: parked request -- barrier signaled the review finish",{requestId:a.requestId})}catch(e){t.logger.error("review-fanout: parked finish signal failed (gate stays pending -- reconciler surfaces it)",{requestId:a.requestId,error:(0,f.getErrorMessage)(e)})}let e=!0
1365
+ Y=e.success?e.data:null,V="ready_for_review"===Y?.status}catch{}if(V){try{await t.apiClient.patch(`/api/projects/${t.projectId}/requests/${a.requestId}`,{reviewStatus:"completed",cliOrigin:{sessionId:a.coderSessionId},reviewRound:b?{reviewed:b}:{}}),t.logger.info("review-fanout: parked request -- barrier signaled the review finish",{requestId:a.requestId})}catch(e){t.logger.error("review-fanout: parked finish signal failed (gate stays pending -- reconciler surfaces it)",{requestId:a.requestId,error:(0,f.getErrorMessage)(e)})}let e=!0
1361
1366
  try{const r=await t.apiClient.get(`/api/projects/${t.projectId}/requests/${a.requestId}`)
1362
1367
  e=!r.success||"ready_for_review"===r.data?.status}catch{}e&&await(0,A.injectPromptToSession)(t,a.coderSessionId,(G.length>0?`The review round on ${a.requestId} closed; ${G.join(", ")} did not report`:`All reviewers have reported on ${a.requestId}`)+". The request is held at ready_for_review, so do NOT re-complete it -- the server evaluates the review gate and the remaining gates advance on their own. Read the findings for context; a human decides at the next gate.")}let J=!1,X=!1
1363
- if(!z){const e=await l(t,a,{expectedSessionIds:N,missing:G.length,unspawned:O,reports:H??[],reviewed:b,request:Y})
1364
- J=e.clean,e.advanced?(X=!0,c.info("review-fanout: clean review, the request went on without a worker step",{requestId:a.requestId,reviewers:D.length,reviewed:b,msAfterBarrier:Date.now()-W})):(J&&c.info("review-fanout: clean review, but the completion does not stand as it was; the worker completes again",{requestId:a.requestId,why:e.why}),await(0,A.injectPromptToSession)(t,a.coderSessionId,i(D,G)))}return{success:!0,message:`Review fan-out ${B.reason}: ${D.length-G.length}/${D.length} reviewers reported${O>0?` -- WARNING: ${O} requested reviewer${1===O?"":"s"} failed to spawn and that coverage never ran`:""}${z?" (held finish signaled)":""}${X?" (clean: went on without a worker step)":""}`,data:{reason:B.reason,reviewers:D,missing:G,parked:z,unspawned:O,...b?{reviewed:b}:{},...z?{}:{clean:J,cleanAdvance:X},...K.length?{silentEnds:K}:{}}}}
1368
+ if(!V){const e=await l(t,a,{expectedSessionIds:N,missing:G.length,unspawned:O,reports:H??[],reviewed:b,request:Y})
1369
+ J=e.clean,e.advanced?(X=!0,c.info("review-fanout: clean review, the request went on without a worker step",{requestId:a.requestId,reviewers:D.length,reviewed:b,msAfterBarrier:Date.now()-W})):(J&&c.info("review-fanout: clean review, but the completion does not stand as it was; the worker completes again",{requestId:a.requestId,why:e.why}),await(0,A.injectPromptToSession)(t,a.coderSessionId,i(D,G)))}return{success:!0,message:`Review fan-out ${B.reason}: ${D.length-G.length}/${D.length} reviewers reported${O>0?` -- WARNING: ${O} requested reviewer${1===O?"":"s"} failed to spawn and that coverage never ran`:""}${V?" (held finish signaled)":""}${X?" (clean: went on without a worker step)":""}`,data:{reason:B.reason,reviewers:D,missing:G,parked:V,unspawned:O,...b?{reviewed:b}:{},...V?{}:{clean:J,cleanAdvance:X},...K.length?{silentEnds:K}:{}}}}
1365
1370
  const f=t(58824),m=t(7202),g=t(87775),w=t(20979),y=t(35317),v=t(39023),b=t(79896),k=(0,v.promisify)(y.execFile),S=p(t(83807)),I=t(27478),$=t(95040),j=t(76982),E=t(27838),A=t(17039),C=t(53691),_=t(52232),T=t(82928),x=e=>Math.min(3e3,200*2**e)
1366
1371
  exports.FILE_REPORT_ASK='You ended your turn without filing your report, so your review reached nobody: what you write as a reply is not read. File it now with the report tool: session_message_operations send with type "report", and your findings as the summary. Then stop.'},76555(e,exports,t){"use strict"
1367
1372
  async function r(e){const t=(Array.isArray(e.results?.commands)?e.results.commands:[]).filter(e=>"failed"===e?.status&&!(0,R.isKeptCommand)(e))
@@ -1412,7 +1417,7 @@ s=e?.data??e}catch{e=!0
1412
1417
  continue}const n=s?.status
1413
1418
  if("passed"!==n&&("skipped"!==n||!0!==s?.results?.scopeSkipped)&&!(0,K.failsOnlyOnMain)(s))if((0,K.waitsOnMain)(s))e=!0
1414
1419
  else{if("failed"===n||"error"===n||"skipped"===n)return"failed"
1415
- e=!0}}return e?"pending":"green"},p=async e=>{if("failed"===e){try{(0,G.releaseMergeLease)(t,s)}catch{}try{await(0,K.clearMergeCandidate)(t,s)}catch{}try{const e=await(0,Q.resolveDefaultBranch)(t),r=await(0,V.reconcileBranchWithMain)({projectPath:t,requestId:s,branchName:n,defaultBranch:e})
1420
+ e=!0}}return e?"pending":"green"},p=async e=>{if("failed"===e){try{(0,G.releaseMergeLease)(t,s)}catch{}try{await(0,K.clearMergeCandidate)(t,s)}catch{}try{const e=await(0,Q.resolveDefaultBranch)(t),r=await(0,z.reconcileBranchWithMain)({projectPath:t,requestId:s,branchName:n,defaultBranch:e})
1416
1421
  a.info("merge-queue: catch-up failed -- ticket released, branch reconciled for rework",{requestId:s,reconcile:r.outcome})}catch(e){a.warn("merge-queue: post-failure branch reconcile failed (rework proceeds without it)",{requestId:s,error:(0,j.getErrorMessage)(e)})}}else if((0,G.renewMergeLease)(t,s,{ttlMs:G.MERGE_LEASE_LAND_GRACE_TTL_MS,phase:"catchup"}),a.info("merge-queue: catch-up green -- lease held for landing",{requestId:s}),c&&l?.finalize)try{await i.post(`/api/projects/${r}/actions`,{type:"finalize-request",payload:{requestId:s,source:"merge-queue-catchup",...l.signoff?{signoff:l.signoff}:{}}}),a.info("merge-queue: catch-up green -- follow-up finalize queued automatically",{requestId:s})}catch(e){a.warn("merge-queue: failed to queue follow-up finalize; lease held with landing grace, a manual finalize will land the certified commit",{requestId:s,error:(0,j.getErrorMessage)(e)})}}
1417
1422
  if(!c){const e=await h()
1418
1423
  return"pending"!==e&&await p(e),e}for(;Date.now()<u;){await new Promise(e=>setTimeout(e,d))
@@ -1450,7 +1455,7 @@ try{const se=await X.get(`/api/projects/${ee}/requests/${b}`)
1450
1455
  if(!se.success||!se.data)return{success:!1,message:`Request ${b} not found`,error:"Request not found"}
1451
1456
  const oe=se.data
1452
1457
  if("main-came-in"===Y){if("ready_for_review"!==String(oe.status))return{success:!0,message:`${b} is ${oe.status}, not waiting for sign-off; the checks after main came in are not run`}
1453
- const e=`requests/${b}`,t=await(0,V.conflictPending)(te,oe,e)
1458
+ const e=`requests/${b}`,t=await(0,z.conflictPending)(te,oe,e)
1454
1459
  if(t)return{success:!0,message:`not run on ${b}: ${t}`}
1455
1460
  const r=await(0,Q.resolveDefaultBranch)(te),s=await(0,Z.mainBroughtInSinceWork)(te,e,r).catch(()=>null)
1456
1461
  if(!s)return{success:!0,message:`nothing came into ${b} from main after its work; the checks are not run again`}
@@ -1517,7 +1522,7 @@ h=e.data,Ae.push(h.id)}else{const e=await X.post(`/api/projects/${ee}/test-runs`
1517
1522
  h=e.data,Ae.push(h.id)}}catch(e){J.error("Failed to get/create test run record",{requestId:b,processId:process.id,preCreatedId:p?.id,error:(0,j.getErrorMessage)(e)})
1518
1523
  continue}let y,v=process.config?.environment,k=process.config?.environment??null
1519
1524
  if(process.environmentId)try{const e=await X.get(`/api/projects/${ee}/environments/${process.environmentId}`),t=e?.data||e
1520
- t?.config&&(y=t.name,k=t.config,v=(0,z.gateEnvironmentFromRow)(t.config,ee,e=>J.warn("tests gate: clamped elevated environment config",{testProcessId:process.id,environmentId:t.id,environmentName:t.name,clamped:e})))}catch(e){J.warn("Failed to resolve environment_id; using embedded config",{testProcessId:process.id,error:(0,j.getErrorMessage)(e)})}if("experiment"===process.kind){try{let e="(no environment configured for this experiment; the judge has no run evidence)",r=[]
1525
+ t?.config&&(y=t.name,k=t.config,v=(0,V.gateEnvironmentFromRow)(t.config,ee,e=>J.warn("tests gate: clamped elevated environment config",{testProcessId:process.id,environmentId:t.id,environmentName:t.name,clamped:e})))}catch(e){J.warn("Failed to resolve environment_id; using embedded config",{testProcessId:process.id,error:(0,j.getErrorMessage)(e)})}if("experiment"===process.kind){try{let e="(no environment configured for this experiment; the judge has no run evidence)",r=[]
1521
1526
  const s=f(oe,J,b),n=s.commands,o=s.withVerify
1522
1527
  if(v){const s=[...(0,_.buildCapabilityProbes)((0,_.probeableRuntimes)(n)),...process.config?.commands||[],...n],o=await(0,U.withContainerLabels)({"alchemy.purpose":"check","alchemy.request":b,"alchemy.environment":y,"alchemy.check":process.name},()=>(0,O.runTestsGate)(we,{commands:s,environment:v,setup:(0,N.setupOfEnvironment)(de,process.environmentId?String(process.environmentId):null)},{})),i=o.results?.commands||[]
1523
1528
  try{const e=s.filter(e=>!0===e?.required||/^verify\[/.test(String(e?.name??""))).map(e=>String(e?.command??"").trim()).filter(e=>e.length>0).sort(),t=(0,A.createHash)("sha256").update(e.join("\n")).digest("hex")
@@ -1557,34 +1562,34 @@ let t=null
1557
1562
  try{t=await(0,L.decideCatchUpReuse)({runs:await E(),excludeRunId:h.id,process,landingTree:Ee,fingerprint:{kind:"all",value:e},changedBetween:(0,L.gitChangedBetween)(te)})}catch(e){J.debug("catch-up reuse lookup failed; running the gate",{error:(0,j.getErrorMessage)(e)})}if(t?.reuse){const r=(0,L.catchUpReusedRun)(t,process.pathGlobs??[],Ee)
1558
1563
  await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,{status:"passed",results:r.results,executedFingerprint:e}),_e.push(h.id),J.info("Catch-up: main's newer work did not touch this check's paths; its earlier pass is reused",{testRunId:h.id,priorRunId:t.prior.id,processName:process.name,changedSincePass:t.changedPaths.length})
1559
1564
  continue}t&&J.info("Catch-up: the check runs again",{testRunId:h.id,processName:process.name,why:t.why})}const T=Date.now(),x={"alchemy.purpose":"check","alchemy.request":b,"alchemy.environment":y,"alchemy.check":process.name},P=(0,D.splitGateCommands)(c),F=ae.some(e=>!i(e)&&Array.isArray(e?.config?.setup)&&e.config.setup.length>0),H=Array.isArray(process.config?.setup)?process.config.setup:[],G=w&&P.verify.length>0&&!!je&&!F&&!e.commandNames?.length&&!e.skipCommandNames?.length
1560
- let V,Y=!0,Z=null
1565
+ let z,Y=!0,Z=null
1561
1566
  if(G){let e=null
1562
1567
  if(P.process.length>0){const t=(0,D.groupFingerprint)(P.process)
1563
1568
  let r=null
1564
1569
  try{r=(0,D.findProcessEvidence)(await E(),{excludeRunId:h.id,processId:process.id,testedTree:Ee,processFingerprint:t})}catch(e){J.debug("process-evidence lookup failed; running the test commands",{error:(0,j.getErrorMessage)(e)})}let s=null
1565
- if(!r&&K&&Ee)try{s=await(0,L.decideCatchUpReuse)({runs:await E(),excludeRunId:h.id,process,landingTree:Ee,fingerprint:{kind:"process",value:t},changedBetween:(0,L.gitChangedBetween)(te)})}catch(e){J.debug("catch-up reuse lookup failed; running the test commands",{error:(0,j.getErrorMessage)(e)})}r?(e=(0,D.reusedProcessResult)(r,je),Y=!1,J.info("Tests gate: the test commands already passed at this commit; only the criteria checks run",{testRunId:h.id,priorRunId:r.id,processName:process.name,commitSha:je})):s?.reuse?(e=(0,L.catchUpReusedProcess)(s,process.pathGlobs??[]),Y=!1,J.info("Catch-up: main's newer work did not touch this check's paths; its test commands are reused, the criteria checks run",{testRunId:h.id,priorRunId:s.prior.id,processName:process.name})):(s&&J.info("Catch-up: the test commands run again",{testRunId:h.id,processName:process.name,why:s.why}),e=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:P.process,environment:v,setup:H})))}if(e?.error)V=e
1570
+ if(!r&&K&&Ee)try{s=await(0,L.decideCatchUpReuse)({runs:await E(),excludeRunId:h.id,process,landingTree:Ee,fingerprint:{kind:"process",value:t},changedBetween:(0,L.gitChangedBetween)(te)})}catch(e){J.debug("catch-up reuse lookup failed; running the test commands",{error:(0,j.getErrorMessage)(e)})}r?(e=(0,D.reusedProcessResult)(r,je),Y=!1,J.info("Tests gate: the test commands already passed at this commit; only the criteria checks run",{testRunId:h.id,priorRunId:r.id,processName:process.name,commitSha:je})):s?.reuse?(e=(0,L.catchUpReusedProcess)(s,process.pathGlobs??[]),Y=!1,J.info("Catch-up: main's newer work did not touch this check's paths; its test commands are reused, the criteria checks run",{testRunId:h.id,priorRunId:s.prior.id,processName:process.name})):(s&&J.info("Catch-up: the test commands run again",{testRunId:h.id,processName:process.name,why:s.why}),e=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:P.process,environment:v,setup:H})))}if(e?.error)z=e
1566
1571
  else{let t=we
1567
1572
  try{t=await(0,D.prepareVerifyTree)(te,we,(await(0,I.simpleGit)(we).revparse(["HEAD"])).trim())}catch(e){J.warn("Tests gate: no clean checkout for the criteria checks; running them in the pool slot",{testRunId:h.id,error:(0,j.getErrorMessage)(e)})}Z=t
1568
1573
  const r=je?await(0,q.takeProofResultsAt)(b,je):null,o=r&&(await(0,I.simpleGit)(t).revparse(["HEAD^{tree}"]).catch(()=>"")).trim()||null,{settled:i,left:a}=(0,D.replayedVerify)(P.verify,r,o)
1569
1574
  i&&J.info("Tests gate: criteria checks the completion's replay settled at this commit are taken, not run again",{testRunId:h.id,taken:(i.results.commands??[]).length,left:a.length,commitSha:je})
1570
1575
  const c=(0,M.verifyByEnvironment)(a,p)
1571
1576
  let l=c.checkBox.length>0?await(0,U.withContainerLabels)({...x,"alchemy.check":`${process.name} (criteria)`},()=>(0,O.runTestsGate)(t,{commands:c.checkBox,environment:v,setup:H})):null
1572
- for(const[e,r]of c.pinned){const o=await s(X,ee,e),i=o?o.id===String(process.environmentId??"")?H:(0,N.setupOfEnvironment)(de,o.id):[],a=o?await(0,U.withContainerLabels)({...x,"alchemy.check":`${process.name} (criteria, ${o.name})`},()=>(0,O.runTestsGate)(t,{commands:r,environment:(0,z.gateEnvironmentFromRow)(o.config,ee),setup:i})):n(e,r)
1573
- l=(0,D.joinVerifyResults)(l,a)}l=(0,D.joinVerifyResults)(l,i),V=(0,D.mergeGroupResults)(e,l)}}else V=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:c,environment:v,setup:H},{commandNames:e.commandNames,skipCommandNames:e.skipCommandNames}))
1577
+ for(const[e,r]of c.pinned){const o=await s(X,ee,e),i=o?o.id===String(process.environmentId??"")?H:(0,N.setupOfEnvironment)(de,o.id):[],a=o?await(0,U.withContainerLabels)({...x,"alchemy.check":`${process.name} (criteria, ${o.name})`},()=>(0,O.runTestsGate)(t,{commands:r,environment:(0,V.gateEnvironmentFromRow)(o.config,ee),setup:i})):n(e,r)
1578
+ l=(0,D.joinVerifyResults)(l,a)}l=(0,D.joinVerifyResults)(l,i),z=(0,D.mergeGroupResults)(e,l)}}else z=await(0,U.withContainerLabels)(x,()=>(0,O.runTestsGate)(we,{commands:c,environment:v,setup:H},{commandNames:e.commandNames,skipCommandNames:e.skipCommandNames}))
1574
1579
  if(!G&&w&&je&&P.verify.length>0){const e=await(0,q.takeProofResultsAt)(b,je),t=(e?.settled??[]).filter(e=>!e.ok).map(e=>({command:e.command,exit:e.exit,output:e.output}))
1575
- t.length>0&&(V=(0,D.withProofFailures)(V,P.verify,t),J.info("Tests gate: proofs the completion replay found failing fail this run too",{testRunId:h.id,failed:t.length,commitSha:je}))}const re=Y?await a(we,te,J):{screenshots:[],found:0}
1580
+ t.length>0&&(z=(0,D.withProofFailures)(z,P.verify,t),J.info("Tests gate: proofs the completion replay found failing fail this run too",{testRunId:h.id,failed:t.length,commitSha:je}))}const re=Y?await a(we,te,J):{screenshots:[],found:0}
1576
1581
  if(Z&&Z!==we){const e=await a(Z,te,J)
1577
1582
  re.screenshots=[...re.screenshots,...e.screenshots],re.found+=e.found}Y&&d(te,we,J)
1578
1583
  const se=(0,B.mainChecksOf)([process])[0]
1579
- let ie={...V.results||{},...S.length?{kept:S.map(e=>({requestId:e.requestId,criterionId:e.criterionId,text:String(e.text??"").slice(0,300),commands:e.commands.map(e=>String(e).slice(0,600))}))}:{},processFingerprint:(0,D.provenProcessFingerprint)(P.process,V.results?.commands),...Ee?{testedTree:Ee}:{},...se?{checkVersion:(0,B.checkVersionOf)(se),environmentVersion:(0,B.environmentVersionOf)(k)}:{}}
1580
- if(t&&(ie={...ie||{},scopeVerifyOnly:!0,summary:["Process commands out of scope for this change; ran criterion verify commands only.",ie?.summary].filter(Boolean).join(" \xb7 ")}),w){const e=m(V.results?.commands||[],{checkName:String(process.name??""),checkToolchains:(0,M.toolchainsOf)((process.config?.commands??[]).map(e=>e?.command)),uncovered:new Map(g.filter(e=>!e.covered).map(e=>[(0,C.criterionShortKey)(String(p[e.index]?.id??""),e.index),e.needs]))})
1584
+ let ie={...z.results||{},...S.length?{kept:S.map(e=>({requestId:e.requestId,criterionId:e.criterionId,text:String(e.text??"").slice(0,300),commands:e.commands.map(e=>String(e).slice(0,600))}))}:{},processFingerprint:(0,D.provenProcessFingerprint)(P.process,z.results?.commands),...Ee?{testedTree:Ee}:{},...se?{checkVersion:(0,B.checkVersionOf)(se),environmentVersion:(0,B.environmentVersionOf)(k)}:{}}
1585
+ if(t&&(ie={...ie||{},scopeVerifyOnly:!0,summary:["Process commands out of scope for this change; ran criterion verify commands only.",ie?.summary].filter(Boolean).join(" \xb7 ")}),w){const e=m(z.results?.commands||[],{checkName:String(process.name??""),checkToolchains:(0,M.toolchainsOf)((process.config?.commands??[]).map(e=>e?.command)),uncovered:new Map(g.filter(e=>!e.covered).map(e=>[(0,C.criterionShortKey)(String(p[e.index]?.id??""),e.index),e.needs]))})
1581
1586
  e.length&&(ie={...ie||{},acceptanceCriteria:e})}if(re.screenshots.length)ie={...ie||{},screenshots:re.screenshots}
1582
1587
  else if(re.found>0){const e=`${re.found} screenshot(s) found but failed to upload`
1583
- ie={...ie||{},summary:[ie?.summary,e].filter(Boolean).join(" \xb7 ")}}const ce=o(V.error)??("failed"===V.status?await r({apiClient:X,projectId:ee,requestId:b,processId:String(process.id),gateCommands:c||[],results:V.results,repairing:(0,W.environmentRepairInFlight)(b)}).catch(()=>null):null),ue=(V.results?.commands??[]).some(e=>("failed"===e?.status||"error"===e?.status)&&!(0,R.isKeptCommand)(e))
1584
- if("failed"===V.status&&ue&&!V.error&&!ce&&!V.results?.faultClass&&Y&&!t){const e=(0,B.mainChecksOf)([process])[0]
1588
+ ie={...ie||{},summary:[ie?.summary,e].filter(Boolean).join(" \xb7 ")}}const ce=o(z.error)??("failed"===z.status?await r({apiClient:X,projectId:ee,requestId:b,processId:String(process.id),gateCommands:c||[],results:z.results,repairing:(0,W.environmentRepairInFlight)(b)}).catch(()=>null):null),ue=(z.results?.commands??[]).some(e=>("failed"===e?.status||"error"===e?.status)&&!(0,R.isKeptCommand)(e))
1589
+ if("failed"===z.status&&ue&&!z.error&&!ce&&!z.results?.faultClass&&Y&&!t){const e=(0,B.mainChecksOf)([process])[0]
1585
1590
  if(e){const t=(0,I.simpleGit)(te),r=K?.baseSha??le?.mainSha??(je?(await t.raw(["merge-base",await(0,Q.resolveDefaultBranch)(te),je]).catch(()=>"")).trim():"");/^[0-9a-f]{40}$/.test(r)&&(ie={...ie,mainBase:{mainSha:r,checkVersion:(0,B.checkVersionOf)(e)}})}}ce&&(ie={...ie,faultClass:void 0,...ce.results,commands:ie?.commands??[]}),Array.isArray(ie?.commands)&&(ie={...ie,commands:(0,_.withVerifyCommandLines)(ie.commands,c||[])})
1586
1591
  const fe=new Set((ie?.commands||[]).map(e=>String(e?.name??""))),me=(c||[]).filter(e=>fe.has(String(e?.name??""))).filter(e=>!(0,R.isKeptCommand)(e)).filter(e=>!0===e?.required||/^verify\[/.test(String(e?.name??""))).map(e=>String(e?.command??"").trim()).filter(e=>e.length>0).sort(),ge=(0,A.createHash)("sha256").update(me.join("\n")).digest("hex")
1587
- await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,{status:ce?"skipped":V.status,results:ie,executedFingerprint:ge}),_e.push(h.id),J.info("Deterministic Tests gate completed",{testRunId:h.id,processName:process.name,status:V.status,phaseMs:V.phaseMs,beforeGateMs:T-ne,totalMs:Date.now()-ne})}catch(e){J.error("Deterministic Tests gate errored",{testRunId:h.id,error:(0,j.getErrorMessage)(e)}),await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,o((0,j.getErrorMessage)(e))??{status:"error",results:{commands:[],summary:`Tests gate error: ${(0,j.getErrorMessage)(e)}`,faultClass:"fail_environment"}}).catch(()=>{}),_e.push(h.id)}continue}const S=w(oe,process,h,ee,e.commandNames,e.skipCommandNames),$="auto-tester"
1592
+ await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,{status:ce?"skipped":z.status,results:ie,executedFingerprint:ge}),_e.push(h.id),J.info("Deterministic Tests gate completed",{testRunId:h.id,processName:process.name,status:z.status,phaseMs:z.phaseMs,beforeGateMs:T-ne,totalMs:Date.now()-ne})}catch(e){J.error("Deterministic Tests gate errored",{testRunId:h.id,error:(0,j.getErrorMessage)(e)}),await X.patch(`/api/projects/${ee}/test-runs/${h.id}`,o((0,j.getErrorMessage)(e))??{status:"error",results:{commands:[],summary:`Tests gate error: ${(0,j.getErrorMessage)(e)}`,faultClass:"fail_environment"}}).catch(()=>{}),_e.push(h.id)}continue}const S=w(oe,process,h,ee,e.commandNames,e.skipCommandNames),$="auto-tester"
1588
1593
  try{let e
1589
1594
  const t=await(0,T.spawnActionAgent)({getDefaultModel:()=>c.cachedDefaultModel||void 0,agent:$,prompt:S,cwd:we,projectPath:te,projectId:ee,title:`[${b}] Test: ${process.name}`,requestId:b,parentSessionId:G,allowParallel:!0,apiClient:X,logger:J,trackChildPid:re,onSessionPrepared:t=>{e=t,Me.addSession(t)}})
1590
1595
  if(!t.success)throw e&&Me.markSessionDone(e),new Error(t.error||"Spawn failed")
@@ -1595,7 +1600,7 @@ let Pe=null
1595
1600
  if(K){const e={projectPath:te,projectId:ee,requestId:b,branchName:ie,testRunIds:Ae,apiClient:X,logger:J,resume:K.resume}
1596
1601
  0===Ce.length?(Pe=await h({...e,watch:!1}),"pending"===Pe&&h({...e,watch:!0}).catch(e=>J.warn("merge-queue: catch-up watcher crashed; lease TTL will unblock the queue",{requestId:b,error:(0,j.getErrorMessage)(e)}))):(Pe="pending",h({...e,watch:!0}).catch(e=>J.warn("merge-queue: catch-up watcher crashed; lease TTL will unblock the queue",{requestId:b,error:(0,j.getErrorMessage)(e)})))}return{success:!0,message:`Started ${Ce.length+_e.length} test run(s) for request ${b}`,data:{testRunIds:Ae,agentsSpawned:Ce.length,processCount:ae.length,worktreePath:we,spawnedSessionIds:Te,...K?{catchUp:{mergeCommitSha:K.mergeCommitSha,settled:Pe}}:{}}}}catch(e){const t=(0,j.getErrorMessage)(e)
1597
1602
  return J.error("run-tests action failed",{requestId:b,error:t}),{success:!1,message:`Failed to run tests: ${t}`,error:t}}}
1598
- const y=t(64721),v=t(75453),b=t(44600),k=t(79896),S=t(16928),I=t(83807),$=t(63905),j=t(58824),E=t(7202),A=t(76982),C=t(65454),_=t(36160),T=t(28148),x=t(67842),M=t(29418),R=t(54380),P=t(60489),O=t(41666),D=t(79075),q=t(65856),N=t(73775),F=t(87780),L=t(49987),U=t(81618),B=t(80872),H=t(22604),W=t(77238),G=t(56509),K=t(87192),V=t(35510),z=t(96302),Y=t(14139),J=t(40673),X=t(27584),Q=t(52232),Z=t(82865),ee=t(63484),te="alchemy:visual-check"
1603
+ const y=t(64721),v=t(75453),b=t(44600),k=t(79896),S=t(16928),I=t(83807),$=t(63905),j=t(58824),E=t(7202),A=t(76982),C=t(65454),_=t(36160),T=t(28148),x=t(67842),M=t(29418),R=t(54380),P=t(60489),O=t(41666),D=t(79075),q=t(65856),N=t(73775),F=t(87780),L=t(49987),U=t(81618),B=t(80872),H=t(22604),W=t(77238),G=t(56509),K=t(87192),z=t(35510),V=t(96302),Y=t(14139),J=t(40673),X=t(27584),Q=t(52232),Z=t(82865),ee=t(63484),te="alchemy:visual-check"
1599
1604
  exports.rP='results: { summary, commands: [ { name: "each drive and verify[<key>] step from the evidence", status: "passed"|"failed"|"skipped"|"error", output? } ], acceptanceCriteria: [ { key: "the [key] shown next to the criterion", criterion, status: "verified"|"unverified"|"unclear", evidence } ], faultClass?: "fail_code"|"fail_evidence"|"fail_environment", missingCapabilities?: string[] }'},64306(e,exports,t){"use strict"
1600
1605
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1601
1606
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.setupAgentInWorktree=async function(e){const{projectPath:t,projectId:r,apiClient:a,logger:c,trackChildPid:l}=e
@@ -1890,7 +1895,7 @@ if(!n)return{success:!1,message:"requestId is required",error:"Missing payload"}
1890
1895
  i.info("Starting start-drive action",{requestId:n,creatorSessionId:o})
1891
1896
  try{const e=await a.get(`/api/projects/${c}/requests/${n}`)
1892
1897
  if(!e.success||!e.data)return{success:!1,message:`Request ${n} not found`,error:"Request not found"}
1893
- const r=e.data,u=Array.isArray(r.acceptanceCriteria)&&r.acceptanceCriteria.length>0?r.acceptanceCriteria.map(e=>`- ${e?.description??e}`).join("\n"):"(none authored -- treat the description as the claim list, and say so in the receipt)",h=`Drive ${n}.\n\nYou own this drive request. Its acceptance criteria are CLAIMS to verify against the running system through user doors, per your instructions. Verify each claim with exact evidence, file reports for divergences beyond the criteria, and end by completing the request (request_operations complete, requestId: "${n}") with the claim -> verdict -> one-line-evidence receipt. Do not call merge; the server settles the record after your completion.\n\n## The request\n\nTitle: ${r.title??"(untitled)"}\n\nDescription:\n${r.description||"(no description)"}\n\nAcceptance criteria (the claims):\n${u}`,p=await(0,s.spawnActionAgent)({agent:"driver",prompt:h,cwd:l,projectPath:l,projectId:c,title:/^(drive|deploy check)\b/i.test(String(r.title??""))?`[${n}] ${r.title}`.trim():`[${n}] Drive: ${r.title??""}`.trim(),requestId:n,parentSessionId:o,allowParallel:!0,skipWorktree:!0,apiClient:a,logger:i,trackChildPid:d,getDefaultModel:()=>t.cachedDefaultModel??void 0})
1898
+ const r=e.data,u=Array.isArray(r.acceptanceCriteria)&&r.acceptanceCriteria.length>0?r.acceptanceCriteria.map(e=>`- ${e?.description??e}`).join("\n"):"(none authored -- treat the description as the claim list, and say so in the receipt)",h=`Drive ${n}.\n\n${function(e,t){return t?`You own this. It is a message from the user on the page of ${t}, a drive that is completed; its record is there to read (get_request_information). If it asks something that record answers, the answer is the summary you complete with, and nothing is driven. If it asks for another pass, or the record does not answer it, drive what it asks through user doors, per your instructions, and complete with the receipt. Either way end by completing (request_operations complete, requestId: "${e}"). Do not call merge; the server settles the request on your completion.`:`You own this drive request. Its acceptance criteria are CLAIMS to verify against the running system through user doors, per your instructions. Verify each claim with exact evidence, file reports for divergences beyond the criteria, and end by completing the request (request_operations complete, requestId: "${e}") with the claim -> verdict -> one-line-evidence receipt. Do not call merge; the server settles the record after your completion.`}(n,r.roundOf)}\n\n## The request\n\nTitle: ${r.title??"(untitled)"}\n\nDescription:\n${r.description||"(no description)"}\n\nAcceptance criteria (the claims):\n${u}`,p=await(0,s.spawnActionAgent)({agent:"driver",prompt:h,cwd:l,projectPath:l,projectId:c,title:/^(drive|deploy check)\b/i.test(String(r.title??""))?`[${n}] ${r.title}`.trim():`[${n}] Drive: ${r.title??""}`.trim(),requestId:n,parentSessionId:o,allowParallel:!0,skipWorktree:!0,apiClient:a,logger:i,trackChildPid:d,getDefaultModel:()=>t.cachedDefaultModel??void 0})
1894
1899
  return p.success?(i.info("Driver spawned",{requestId:n,sessionId:p.sessionId}),{success:!0,message:`Driver spawned for ${n} (session ${p.sessionId})`}):{success:!1,message:`Failed to spawn driver for ${n}`,error:p.error}}catch(e){const t=(0,r.getErrorMessage)(e)
1895
1900
  return i.error("start-drive action failed",{requestId:n,error:t}),{success:!1,message:`Drive dispatch failed for ${n}`,error:t}}}
1896
1901
  const r=t(58824),s=t(28148)},93503(e,exports,t){"use strict"
@@ -1982,7 +1987,7 @@ const v=T.get(s.requestId)
1982
1987
  if(v)return{success:!0,message:`${s.requestId} starts on ${v} when its current turn ends`}
1983
1988
  try{const e=async e=>{const t=await o.get(`/api/projects/${i}/requests/${e}`)
1984
1989
  return(t?.data??t)?.type}
1985
- t.projectPath&&await(0,_.defaultBranchHasCode)(t.projectPath,e)&&(await(0,_.reportProjectHasCode)(i,!0,(e,t)=>o.patch(e,t)),await o.post(`/api/projects/${i}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){d.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,S.getErrorMessage)(e)})}const b=await(u.openDuplicates??r)(t,w,s.requestId),k=await(u.requestRecord??n)(t,s.requestId,w),I=function(e,t,r,s){const n=[`Implement ${e.requestId} ("${t?.title??""}") and own its full lifecycle. It was registered via ${e.source??"a registration door"}.`,s?"Its record, as get_request_information shows it now, is at the end of this message: start from it, and read the request again only when you need what changed since. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.":"Read it with get_request_information first. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.",`If its acceptance criteria ALREADY hold as the code stands, do no work: complete ${e.requestId} yourself as a verification-only completion, naming exactly which criteria hold and where. Nobody else closes it: a report saying so left req000210 in progress with nobody working it.`,'Otherwise build what it asks (its own choices go in an opening brief when the pickup briefing asks for one), run the relevant validation, and complete the request with evidence. A value the request states is the value: a version number, a label, a default. Do not bump or "improve" it (two coders raised a footer version the user had just set, 2026-09-07).']
1990
+ t.projectPath&&await(0,_.defaultBranchHasCode)(t.projectPath,e)&&(await(0,_.reportProjectHasCode)(i,!0,(e,t)=>o.patch(e,t)),await o.post(`/api/projects/${i}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){d.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,S.getErrorMessage)(e)})}const b=await(u.openDuplicates??r)(t,w,s.requestId),k=await(u.requestRecord??n)(t,s.requestId,w),I=function(e,t,r,s){const n=[`Implement ${e.requestId} ("${t?.title??""}") and own its full lifecycle. It was registered via ${e.source??"a registration door"}.`,s?"Its record, as get_request_information shows it now, is at the end of this message: start from it, and read the request again only when you need what changed since. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.":"Read it with get_request_information first. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.",...t?.roundOf?[]:[`If its acceptance criteria ALREADY hold as the code stands, do no work: complete ${e.requestId} yourself as a verification-only completion, naming exactly which criteria hold and where. Nobody else closes it: a report saying so left req000210 in progress with nobody working it.`,'Otherwise build what it asks (its own choices go in an opening brief when the pickup briefing asks for one), run the relevant validation, and complete the request with evidence. A value the request states is the value: a version number, a label, a default. Do not bump or "improve" it (two coders raised a footer version the user had just set, 2026-09-07).']]
1986
1991
  return r.length&&n.push(`Possible duplicates already open with the same title: ${r.join(", ")}. Check before doing the work twice; if one already covers it, cancel ${e.requestId} as superseded.`),e.userNote&&n.push(`From the user: ${e.userNote}`),e.rules?.length&&n.push(function(e){const t=e.filter(e=>"told"===e.enforcement),r=e.filter(e=>"told"!==e.enforcement),s=[...t.map(e=>`- [guidance:${e.id}] ${e.text}`),...r.map(e=>`- [guidance:${e.id}] (reviewed, ${e.severity}) ${e.text}`)]
1987
1992
  return`The workflow's guidance for this work (the user's; where an item conflicts with the brief above, the guidance wins).${r.length?" A reviewer checks the reviewed ones by id at the review gate: 'block' ones must hold, 'advise' ones are expected.":""} When the user's own words make an exception to an item for this request, record it with guidance exception (its id as listed, their words, the scope they said); when the request contradicts an item and they have not said, guidance conflict, and follow the guidance until they answer.\n${s.join("\n")}`}(e.rules)),s&&n.push(`\n# ${e.requestId}: ${t?.title??""}\n${s}`),n.join("\n")}(s,w,b,k),j="string"==typeof s.worker&&s.worker.trim()||(0,p.kindProfile)(w?.type).worker||"worker",E=(0,m.spawnToolMask)({kind:w?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,m.hasMask)(E)&&d.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:j,off:Object.keys(E)})
1988
1993
  const A={p:s,req:w,worker:j,startPrompt:I,toolMask:E,deps:u}
@@ -2020,7 +2025,7 @@ for(const e of t)e?.localId&&"draft"===e?.status&&"pending"===e?.intake?.status&
2020
2025
  return s},exports.sessionsHeldByRefusal=function(e){return!!E&&e.length>0&&E.heldByRefusal(e)}
2021
2026
  const l=t(87780),d=t(64331),u=t(43218),h=t(4234),p=t(4234),f=t(60721),m=t(86116),g=t(58824),w=t(36799),y=t(4334),v=new Set(["waiting","blocksEverySession","userQuestionCodes","openCardCodes"]),b=["waitingOnSessions","pendingQuestionSessions","monitorArmedSessions","handedOverSessions"],k=new Set(["opening-brief","stalled-agent"]),S=18e5,I=new Set(["busy","running"]),$=["auto-tester","acceptance-judge"],j=["reviewer","criteria-reviewer","guidance-judge","command-judge","promise-judge","research"]
2022
2027
  let E=null
2023
- class A{heldByRefusal(e){return e.some(e=>this.refused.has(e))}constructor(e){this.settledAt=new Map,this.lastStrandedSweepAt=0,this.stallChecked=new Map,this.settledSkip=new Map,this.unconfused=new Map,this.attestedAt=new Map,this.attestReported=new Set,this.reportedKeys=new Set,this.frozenNudges=0,this.lastFrozenAction=null,this.orphanRetries=new Map,this.resumedRecently=new Set,this.capStopped=new Map,this.refused=new Map,this.judgeNow=new Map,this.resumedOn=new Map,this.liftTold=new Set,this.liftedWhy=new Map,this.checking=!1,this.recheckRequested=!1,this.stuckSessions=new Map,this.notifiedSessions=new Set,this.testInjectedIds=new Set,this.serveStartedAt=0,this.processServeStartedAt=0,this.agentListCache=null,this.lastResumeLift=new Map,E=this,this.logger=e.logger,this.getOpenCodeUrl=e.getOpenCodeUrl,this.onStuckDetected=e.onStuckDetected,this.onStuckResolved=e.onStuckResolved,this.checkIntervalMs=e.checkIntervalMs??3e4,this.stuckThresholdMs=e.stuckThresholdMs??3e5,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel,this.reloadServe=e.reloadServe,this.getLiveRequestLocalIds=e.getLiveRequestLocalIds,this.getOwnedRequestForSession=e.getOwnedRequestForSession,this.orphanThresholdMs=e.orphanThresholdMs??12e4,this.maxOrphanRetries=e.maxOrphanRetries??2,this.getSessionAgentType=e.getSessionAgentType,this.getPendingQuestionAskerIds=e.getPendingQuestionAskerIds,this.getSessionSettled=e.getSessionSettled,this.reportLiveness=e.reportLiveness,this.reportFleetHealth=e.reportFleetHealth,this.recordProviderBlock=e.recordProviderBlock,this.clearCapStopped=e.clearCapStopped,this.deliver=e.deliver,this.raiseStalledAsk=e.raiseStalledAsk,this.closeStalledAsk=e.closeStalledAsk,this.frozenTurnThresholdMs=e.frozenTurnThresholdMs??6e5,this.abortSettleMs=e.abortSettleMs??1e3,this.hasLiveCommand=e.hasLiveCommand,this.stalledWorkThresholdMs=e.stalledWorkThresholdMs??24e4,this.getRequestWaitState=e.getRequestWaitState,this.getStrandedCandidates=e.getStrandedCandidates,this.fetchRecentReportDedupKeys=e.fetchRecentReportDedupKeys,this.getAgentModel=e.getAgentModel,this.isAgentModelPinned=e.isAgentModelPinned,this.resumeHubTurn=e.resumeHubTurn}turnFailed(e,t){e&&(0,h.isFatalTurnError)(t)&&(this.judgeNow.set(e,0),setTimeout(()=>{this.check().catch(()=>{})},1500).unref?.())}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),this.seedReportDedup(),setTimeout(()=>this.check().catch(()=>{}),5e3),this.intervalTimer=setInterval(()=>{this.check().catch(()=>{})},this.checkIntervalMs))}stop(){this.intervalTimer&&(clearInterval(this.intervalTimer),this.intervalTimer=void 0),this.stuckSessions.clear(),this.notifiedSessions.clear(),this.orphanRetries.clear(),this.resumedRecently.clear(),this.stallChecked.clear(),this.settledSkip.clear(),this.unconfused.clear(),this.logger.info("Stuck session watchdog stopped")}getStuckSessions(){return Array.from(this.stuckSessions.values())}isStuck(e){return this.stuckSessions.has(e)}injectTestStuck(e,t){const r=e||`test-stuck-${Date.now()}`,s={sessionId:r,messageId:`test-msg-${Date.now()}`,detectedAt:Date.now(),retryCount:0,lastMessageCreated:Date.now()-this.stuckThresholdMs-1e3,sessionTitle:t||"Test Stuck Session"}
2028
+ class A{heldByRefusal(e){return e.some(e=>this.refused.has(e))}constructor(e){this.settledAt=new Map,this.lastStrandedSweepAt=0,this.stallChecked=new Map,this.settledSkip=new Map,this.unconfused=new Map,this.attestedAt=new Map,this.attestReported=new Set,this.reportedKeys=new Set,this.frozenNudges=0,this.lastFrozenAction=null,this.orphanRetries=new Map,this.resumedRecently=new Set,this.capStopped=new Map,this.refused=new Map,this.judgeNow=new Map,this.resumedOn=new Map,this.liftTold=new Set,this.liftedWhy=new Map,this.checking=!1,this.recheckRequested=!1,this.stuckSessions=new Map,this.notifiedSessions=new Set,this.testInjectedIds=new Set,this.serveStartedAt=0,this.processServeStartedAt=0,this.agentListCache=null,this.lastResumeLift=new Map,E=this,this.logger=e.logger,this.getOpenCodeUrl=e.getOpenCodeUrl,this.onStuckDetected=e.onStuckDetected,this.onStuckResolved=e.onStuckResolved,this.checkIntervalMs=e.checkIntervalMs??3e4,this.stuckThresholdMs=e.stuckThresholdMs??3e5,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel,this.reloadServe=e.reloadServe,this.getLiveRequestLocalIds=e.getLiveRequestLocalIds,this.getOwnedRequestForSession=e.getOwnedRequestForSession,this.orphanThresholdMs=e.orphanThresholdMs??12e4,this.maxOrphanRetries=e.maxOrphanRetries??2,this.getSessionAgentType=e.getSessionAgentType,this.getPendingQuestionAskerIds=e.getPendingQuestionAskerIds,this.getSessionSettled=e.getSessionSettled,this.reportLiveness=e.reportLiveness,this.reportFleetHealth=e.reportFleetHealth,this.recordProviderBlock=e.recordProviderBlock,this.clearCapStopped=e.clearCapStopped,this.deliver=e.deliver,this.raiseStalledAsk=e.raiseStalledAsk,this.closeStalledAsk=e.closeStalledAsk,this.frozenTurnThresholdMs=e.frozenTurnThresholdMs??6e5,this.abortSettleMs=e.abortSettleMs??1e3,this.hasLiveCommand=e.hasLiveCommand,this.stalledWorkThresholdMs=e.stalledWorkThresholdMs??24e4,this.awaitedReplyThresholdMs=e.awaitedReplyThresholdMs??12e4,this.isAwaitedReply=e.isAwaitedReply??m.isWatchingInboxChatTurn,this.getRequestWaitState=e.getRequestWaitState,this.getStrandedCandidates=e.getStrandedCandidates,this.fetchRecentReportDedupKeys=e.fetchRecentReportDedupKeys,this.getAgentModel=e.getAgentModel,this.isAgentModelPinned=e.isAgentModelPinned,this.resumeHubTurn=e.resumeHubTurn}turnFailed(e,t){e&&(0,h.isFatalTurnError)(t)&&(this.judgeNow.set(e,0),setTimeout(()=>{this.check().catch(()=>{})},1500).unref?.())}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),this.seedReportDedup(),setTimeout(()=>this.check().catch(()=>{}),5e3),this.intervalTimer=setInterval(()=>{this.check().catch(()=>{})},this.checkIntervalMs))}stop(){this.intervalTimer&&(clearInterval(this.intervalTimer),this.intervalTimer=void 0),this.stuckSessions.clear(),this.notifiedSessions.clear(),this.orphanRetries.clear(),this.resumedRecently.clear(),this.stallChecked.clear(),this.settledSkip.clear(),this.unconfused.clear(),this.logger.info("Stuck session watchdog stopped")}getStuckSessions(){return Array.from(this.stuckSessions.values())}isStuck(e){return this.stuckSessions.has(e)}injectTestStuck(e,t){const r=e||`test-stuck-${Date.now()}`,s={sessionId:r,messageId:`test-msg-${Date.now()}`,detectedAt:Date.now(),retryCount:0,lastMessageCreated:Date.now()-this.stuckThresholdMs-1e3,sessionTitle:t||"Test Stuck Session"}
2024
2029
  return this.stuckSessions.set(r,s),this.testInjectedIds.add(r),this.notifiedSessions.add(r),this.onStuckDetected?.(s),s}clearTestStuck(e){return!!this.testInjectedIds.has(e)&&(this.testInjectedIds.delete(e),this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.onStuckResolved?.(e),!0)}async check(){if(this.checking)this.recheckRequested=!0
2025
2030
  else{this.checking=!0
2026
2031
  try{await this.checkOnce()}finally{this.checking=!1,this.recheckRequested&&(this.recheckRequested=!1,this.check().catch(()=>{}))}}}capacityChanged(e){this.agentListCache=null,0===this.capStopped.size&&0===this.refused.size||(this.logger.info("Capacity may have changed; judging the sessions a provider cap or refusal stopped",{reason:e,stopped:this.capStopped.size,refused:this.refused.size}),this.check().catch(()=>{}))}serveStarted(e=Date.now()){this.processServeStartedAt||(this.processServeStartedAt=e),this.serveStartedAt=e,this.logger.info("The serve started; turns cut by the stop are judged now",{at:e}),this.check().catch(()=>{})}getCapStopped(){return Array.from(this.capStopped.keys())}async checkOnce(){if((0,w.isProjectPaused)())return
@@ -2052,20 +2057,21 @@ if(this.resumedRecently.has(r.id)&&!e)continue
2052
2057
  const s=this.capStopped.has(r.id)||this.refused.has(r.id)||e,a=r.time?.updated??0,c=(0,y.awakeSince)(a,p),l=I.has(t[r.id]?.type)
2053
2058
  if(l&&this.hasLiveCommand?.(r.id))continue
2054
2059
  let u=!1
2055
- if(l&&c<this.frozenTurnThresholdMs){if(c>=this.stalledWorkThresholdMs)try{const e=await fetch(`${n}/session/${r.id}/message?limit=6`,{signal:AbortSignal.timeout(3e3)}),t=e.ok?await e.json():[]
2060
+ const f=this.isAwaitedReply(r.id)?Math.min(this.stalledWorkThresholdMs,this.awaitedReplyThresholdMs):this.stalledWorkThresholdMs
2061
+ if(l&&c<this.frozenTurnThresholdMs){if(c>=f)try{const e=await fetch(`${n}/session/${r.id}/message?limit=6`,{signal:AbortSignal.timeout(3e3)}),t=e.ok?await e.json():[]
2056
2062
  let s=t.length
2057
2063
  for(;s>0&&(0,h.isQuietDelivery)(t[s-1]);)s--
2058
2064
  if(t.length>0&&0===s)throw new Error("only quiet deliveries in view")
2059
2065
  const o=t[s-1]??null,i=o?.info?.role,a=(Array.isArray(o?.parts)?o.parts:[]).filter(e=>e&&("tool"===e.type||"text"===e.type||"reasoning"===e.type)),c=a[a.length-1],l=a.some(e=>"tool"===e.type&&"running"===e.state?.status)||!!c&&("tool"===c.type&&"pending"!==c.state?.status||"tool"!==c.type&&String(c.text??"").trim().length>0)
2060
2066
  u=!o||"user"===i||"assistant"===i&&!l}catch{u=!1}if(!u){this.stuckSessions.has(r.id)&&!this.testInjectedIds.has(r.id)&&(this.stuckSessions.delete(r.id),this.notifiedSessions.delete(r.id),this.onStuckResolved?.(r.id))
2061
- continue}this.logger.warn("Running turn with no streamed parts past the stalled-work threshold; treating as frozen",{sessionId:r.id,idleForMs:c})}const f=(r.title||"").match(o)?.[1],g=f?void 0:(r.title||"").match(d)?.[1]
2062
- if(!(f||g||m.has(r.id)||s))continue
2063
- const v=f?void 0:(r.title||"").match(i)?.[1]
2064
- if((v||g)&&!s&&this.stallChecked.get(r.id)===a)continue
2065
- const b=u?this.stalledWorkThresholdMs:(f||v||g)&&!l?this.orphanThresholdMs:this.frozenTurnThresholdMs,k=!l&&a>0&&a<this.serveStartedAt
2066
- if(!s&&(!k||this.stallChecked.get(r.id)===a)&&c<b)continue
2067
- const S=this.settledSkip.get(r.id)
2068
- !s&&S&&S.updated===a&&p-S.at<18e5||w.push({s:r,tag:f,updated:a,cut:k,running:l,gate:b})}const v=new Set(e.map(e=>e.id))
2067
+ continue}this.logger.warn("Running turn with no streamed parts past the stalled-work threshold; treating as frozen",{sessionId:r.id,idleForMs:c})}const g=(r.title||"").match(o)?.[1],v=g?void 0:(r.title||"").match(d)?.[1]
2068
+ if(!(g||v||m.has(r.id)||s))continue
2069
+ const b=g?void 0:(r.title||"").match(i)?.[1]
2070
+ if((b||v)&&!s&&this.stallChecked.get(r.id)===a)continue
2071
+ const k=u?f:(g||b||v)&&!l?this.orphanThresholdMs:this.frozenTurnThresholdMs,S=!l&&a>0&&a<this.serveStartedAt
2072
+ if(!s&&(!S||this.stallChecked.get(r.id)===a)&&c<k)continue
2073
+ const $=this.settledSkip.get(r.id)
2074
+ !s&&$&&$.updated===a&&p-$.at<18e5||w.push({s:r,tag:g,updated:a,cut:S,running:l,gate:k})}const v=new Set(e.map(e=>e.id))
2069
2075
  for(const e of Array.from(this.stallChecked.keys()))v.has(e)||this.stallChecked.delete(e)
2070
2076
  for(const e of Array.from(this.settledSkip.keys()))v.has(e)||this.settledSkip.delete(e)
2071
2077
  for(const e of Array.from(this.unconfused.keys()))v.has(e)||this.unconfused.delete(e)
@@ -2094,7 +2100,7 @@ if(!t.ok)continue
2094
2100
  M=await t.json()}catch(t){this.logger.debug("Stall check failed for session",{sessionId:e.id,error:(0,g.getErrorMessage)(t)})
2095
2101
  continue}const R="resume"===_&&!!E
2096
2102
  if(m&&!R&&(0,y.awakeSince)(d,p)<j&&!this.capStopped.has(e.id)&&!this.refused.has(e.id)&&!this.judgeNow.has(e.id))continue
2097
- let P=(0,h.assembleTurnEvidence)(M,{now:p,minInFlightMs:this.stalledWorkThresholdMs,...R?{cutBefore:this.serveStartedAt}:{}})
2103
+ let P=(0,h.assembleTurnEvidence)(M,{now:p,minInFlightMs:this.isAwaitedReply(e.id)?Math.min(this.stalledWorkThresholdMs,this.awaitedReplyThresholdMs):this.stalledWorkThresholdMs,...R?{cutBefore:this.serveStartedAt}:{}})
2098
2104
  const O=new Set(["clean-text","empty","no-messages","dead-end","generating"])
2099
2105
  if(x&&O.has(P.kind))continue
2100
2106
  if("quota"===P.kind){const t=await this.modelForSession(e.id)??this.getDefaultModel?.(),r=(0,h.quotaBlockLifted)(P.quota,t,p)
@@ -3657,9 +3663,9 @@ e.post("/api/site-shots",async(e,t)=>{const{url:r,fullPage:s,width:n}=e.body??{}
3657
3663
  if(!r||"string"!=typeof r||!/^https?:\/\//i.test(r))return void t.status(400).json({success:!1,error:"http(s) url required"})
3658
3664
  const i=await u(r)
3659
3665
  if(i)t.status(403).json({success:!1,error:i})
3660
- else if(o.getProjectId())try{const{buf:e,mime:i}=await(0,z.shootUrl)(r,{fullPage:!!s,width:"number"==typeof n?n:void 0}),a=8388608
3666
+ else if(o.getProjectId())try{const{buf:e,mime:i}=await(0,V.shootUrl)(r,{fullPage:!!s,width:"number"==typeof n?n:void 0}),a=8388608
3661
3667
  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`})
3662
- const c=`data:${i};base64,${e.toString("base64")}`,l=await(0,V.storeLocalAsset)(o.projectPath,c).catch(()=>{})
3668
+ const c=`data:${i};base64,${e.toString("base64")}`,l=await(0,z.storeLocalAsset)(o.projectPath,c).catch(()=>{})
3663
3669
  if(!l)return void t.status(502).json({success:!1,error:"asset upload failed"})
3664
3670
  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??{}
3665
3671
  if(!s||"string"!=typeof s||!/^https?:\/\//i.test(s))return void r.status(400).json({success:!1,error:"http(s) url required"})
@@ -3694,7 +3700,7 @@ if(!f){const r=Number(e.body?.width),s=Number.isFinite(r)&&r>0?Math.min(1920,Mat
3694
3700
  u=await a.newPage({viewport:{width:s,height:n},deviceScaleFactor:1,ignoreHTTPSErrors:!0}),u.on("console",e=>{"error"===e.type()&&t.push(String(e.text()).slice(0,300))}),u.on("pageerror",e=>t.push(String(e?.message??e).slice(0,300)))}f&&u.url()===s||(await u.goto(s,{waitUntil:"domcontentloaded",timeout:3e4}),await u.waitForLoadState("load",{timeout:1e4}).catch(()=>{}),await u.waitForTimeout(300))
3695
3701
  let i=null
3696
3702
  const p=async e=>{try{const t=`data:image/png;base64,${(await u.screenshot({type:"png"})).toString("base64")}`
3697
- return"final"===e&&(i=t),`asset:${await(0,V.storeLocalAsset)(o.projectPath,t)}`}catch{return null}}
3703
+ return"final"===e&&(i=t),`asset:${await(0,z.storeLocalAsset)(o.projectPath,t)}`}catch{return null}}
3698
3704
  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"})
3699
3705
  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}:{}}
3700
3706
  try{if("click"===r){if(!s)throw new Error("click needs a selector")
@@ -3718,7 +3724,7 @@ try{const e=M.default.normalize(r).split(M.default.sep).join("/").replace(/\/+$/
3718
3724
  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)`})
3719
3725
  const i=await(0,D.previewRootFor)(o.projectPath,e,()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath),a),c=M.default.join(i.root,"index.html")
3720
3726
  try{await q.promises.access(c)}catch{return void t.status(400).json({success:!1,error:i.main?`no index.html in ${r} on main (a preview of main serves what main has committed, plus build output from commands run for main; files only in the project folder are not served)`:`no index.html in ${r} (build it first, or point at the build output)`})}const{url:l}=await(0,D.serveSite)(i.root,i.main?{refresh:()=>(0,C.ensureDefaultBranchSnapshot)(o.projectPath)}:{}),d=a?`${a}/${"."===e?"":e}`.replace(/\/$/,""):r,u="string"==typeof s&&s.trim()?s.trim():d.replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,""),h=(0,O.registerPreviewHandle)(u,l,"string"==typeof n&&n.trim()?n.trim():void 0,{requestId:a,fallbackLabel:d})
3721
- 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)=>{(0,O.unregisterPreviewHandle)(e.params.name),t.json({success:!0,data:{name:e.params.name,deleted:!0}})}),e.get("/api/file",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)&&!e.query.ref.startsWith("-")?e.query.ref:void 0,n=(0,X.refusedRepoPath)(r)
3727
+ t.json({success:!0,data:{...h,dir:r,ref:`live:${h.name}`}})}catch(e){t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}}),e.delete("/api/previews/:name",(e,t)=>{const r=(0,O.getPreviewHandle)(e.params.name),s=!!r&&(0,D.stopSiteAt)(r.url);(0,O.unregisterPreviewHandle)(e.params.name),t.json({success:!0,data:{name:e.params.name,deleted:!0,stopped:s}})}),e.get("/api/file",async(e,t)=>{try{const r="string"==typeof e.query.path?e.query.path:"",s="string"==typeof e.query.ref&&/^[\w\/.-]+$/.test(e.query.ref)&&!e.query.ref.startsWith("-")?e.query.ref:void 0,n=(0,X.refusedRepoPath)(r)
3722
3728
  if(n)return void t.status(400).json({success:!1,error:n})
3723
3729
  const i=524288,a=async e=>{const t=(0,w.requestWorktreesDir)(o.projectPath)
3724
3730
  let r=null
@@ -3849,7 +3855,7 @@ let s=null
3849
3855
  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
3850
3856
  if(t){const e=e=>"string"==typeof e?(0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),e):null
3851
3857
  s=t.some(t=>e(t?.worktreePath)===e(r)&&("pending"===t.status||"running"===t.status))}}catch(e){o.logger.debug("release-pool-slot sibling lookup failed",{error:(0,P.getErrorMessage)(e)})}if(!1!==s)return o.logger.info("Pool slot kept for the dispatch idle watch",{worktreePath:r,reason:null===s?"sibling check unavailable":"sibling run still active"}),void t.json({success:!0,released:!1});(0,N.releaseSlot)((0,g.worktreeOnThisComputer)(M.default.join(o.projectPath,y.ALCHEMY_DIR_NAME),r)),o.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0,released:!0})}catch(e){o.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,P.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,P.getErrorMessage)(e)})}})}
3852
- const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(79896),N=t(60489),F=t(92456),L=t(83807),U=t(45738),B=t(65856),H=t(82167),W=t(99970),G=t(24106),K=t(73775),V=t(27584),z=t(3031),Y=t(52232),J=t(62040),X=t(32559),Q=t(96511),Z=new Map,ee=new Map,te=new T.ParallelNotes
3858
+ const m=t(76982),g=t(71856),w=t(7202),y=t(14139),v=t(5641),b=t(44600),k=t(96302),S=t(44876),I=t(29418),$=t(96396),j=t(36160),E=t(41666),A=f(t(79896)),C=t(51857),_=t(46898),T=t(6156),x=t(75453),M=f(t(16928)),R=t(27478),P=t(58824),O=t(28295),D=t(3811),q=t(79896),N=t(60489),F=t(92456),L=t(83807),U=t(45738),B=t(65856),H=t(82167),W=t(99970),G=t(24106),K=t(73775),z=t(27584),V=t(3031),Y=t(52232),J=t(62040),X=t(32559),Q=t(96511),Z=new Map,ee=new Map,te=new T.ParallelNotes
3853
3859
  let re=null
3854
3860
  const se=e=>({id:String(e?.localId??e?.id??""),title:String(e?.title??""),status:String(e?.status??""),description:"string"==typeof e?.description?e.description:null})
3855
3861
  exports.COMPLETION_REPLAY_BUDGET_MS=15e4
@@ -4758,7 +4764,7 @@ let h=null
4758
4764
  const p=new Map},62366(e,exports,t){"use strict"
4759
4765
  var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
4760
4766
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
4761
- const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(52640),T=t(65232),x=t(95671),M=t(44192),R=t(14139),P=t(41152),O=r(t(16928)),D=r(t(79896)),q=r(t(70857)),N=t(5607),F=t(76634),L=t(31),U=t(90524),B=t(18984),H=t(58824),W=t(29869),G=t(67078),K=t(60070),V=t(37210),z=t(87227),Y=t(82928),J=t(76616),X=t(15047),Q=t(90629),Z=t(42298),ee=t(67563),te=t(41950),re=t(88087),se=t(1285),ne=t(20564),oe=Object.keys(N.actionHandlers).join(","),ie=t(6850),ae=t(58836),ce=t(25146),le=t(23411),de=t(43218),ue=t(81570),he=t(18815),pe=t(73613),fe=t(49779),me=t(76982),ge=t(53340),we=t(89063),ye=t(61292),ve=t(11017),be=t(28148),ke=t(18649),Se=t(64306),Ie=t(96685),$e=t(49816),je=t(35419),Ee=t(33521),Ae=t(56844),Ce=t(84352),_e=t(40713),Te=t(75361),xe=t(75167),Me=t(72188),Re=t(6688),Pe=t(39068),Oe=t(3435),De=t(48463),qe=t(60489),Ne=t(24106),Fe=t(81618),Le=t(80211),Ue=t(2566),Be=t(83807),He=t(92456),We=t(95040),Ge=t(60355),Ke=t(31026),Ve=t(73782),ze=t(53608),Ye=t(63484),Je=t(31851),Xe=t(84102),Qe=t(75979),Ze=t(79329),et=t(60833),tt=t(42593),rt=t(69706),st=t(11773),nt=t(20990),ot=t(45956),it=t(36799),at=t(86116),ct=t(13751),lt=t(13791),dt=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ut=65536
4767
+ const s=t(84920),n=t(71856),o=t(65856),i=t(42403),a=t(60721),c=t(16130),l=t(5641),d=t(17039),u=t(53526),h=t(53691),p=t(8469),f=t(97387),m=t(52232),g=t(61161),w=r(t(7252)),y=r(t(77174)),v=r(t(98577)),b=t(27584),k=t(79896),S=t(90473),I=t(27207),$=t(85959),j=t(5452),E=t(60885),A=t(21178),C=t(28295),_=t(52640),T=t(65232),x=t(95671),M=t(44192),R=t(14139),P=t(41152),O=r(t(16928)),D=r(t(79896)),q=r(t(70857)),N=t(5607),F=t(76634),L=t(31),U=t(90524),B=t(18984),H=t(58824),W=t(29869),G=t(67078),K=t(60070),z=t(37210),V=t(87227),Y=t(82928),J=t(76616),X=t(15047),Q=t(90629),Z=t(42298),ee=t(67563),te=t(41950),re=t(88087),se=t(1285),ne=t(20564),oe=Object.keys(N.actionHandlers).join(","),ie=t(6850),ae=t(58836),ce=t(25146),le=t(23411),de=t(43218),ue=t(81570),he=t(18815),pe=t(73613),fe=t(49779),me=t(76982),ge=t(53340),we=t(89063),ye=t(61292),ve=t(11017),be=t(28148),ke=t(18649),Se=t(64306),Ie=t(96685),$e=t(49816),je=t(35419),Ee=t(33521),Ae=t(56844),Ce=t(84352),_e=t(40713),Te=t(75361),xe=t(75167),Me=t(72188),Re=t(6688),Pe=t(39068),Oe=t(3435),De=t(48463),qe=t(60489),Ne=t(24106),Fe=t(81618),Le=t(80211),Ue=t(2566),Be=t(83807),He=t(92456),We=t(95040),Ge=t(60355),Ke=t(31026),ze=t(73782),Ve=t(53608),Ye=t(63484),Je=t(31851),Xe=t(84102),Qe=t(75979),Ze=t(79329),et=t(60833),tt=t(42593),rt=t(69706),st=t(11773),nt=t(20990),ot=t(45956),it=t(36799),at=t(86116),ct=t(13751),lt=t(13791),dt=new Set(["opencode-proxy","machine-proxy","run-tests","review-fanout","main-checks","check-target","judge-proposal"]),ut=65536
4762
4768
  class ht{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment":"on your machine",i=(0,Ee.isStandingRulePattern)(s),a=`You chose "Always allow" on the approval card for \`${t.askedCommand.slice(0,300)}\`${t.approvalId?` (card ${t.approvalId})`:""}${t.sessionId?`, asked by agent session ${t.sessionId}`:""}, on ${t.at.replace("T"," ").slice(0,16)} UTC.`;(async()=>{(await(0,$e.getRegisteredAllows)(this.apiClient,e,r??null).catch(()=>[])).some(e=>("string"==typeof e?e:e.exact).trim()===s.trim())||(await this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"ok:card",command:(0,n.commandAsStored)(s),description:`${a} ${i?`It allows exactly this shape, where <file> is any one file inside the project, ${o}`:`This exact command runs ${o}`} without asking. Delete it or switch it off to be asked again.`,timeout:120,isActive:!0,exactMatch:!0,...r?{environmentId:r}:{}}),(0,$e.invalidateRegisteredAllows)(e))})().catch(e=>this.logger.warn("Failed to remember approved command",{error:String(e)}))}rememberJudgeApprovedCommand(e,t,r){const s=new Date(Date.now()+ht.JUDGE_APPROVAL_TTL_MS).toISOString()
4763
4769
  this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,$e.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
4764
4770
  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})
@@ -4768,7 +4774,7 @@ return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,
4768
4774
  const r=(0,P.createMetricsCollector)(!1)
4769
4775
  if((0,st.setFleetServerKey)(t.projectPath?t.projectPath.split("/").pop():void 0),this.apiClient=new M.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3,serverKey:t.projectPath?t.projectPath.split("/").pop():void 0}),t.tokenManager){const e=t.tokenManager
4770
4776
  this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
4771
- return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new B.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,Z.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
4777
+ return t&&t!==this.options.authToken&&this.updateAuthToken(t),t})}this.configurer=new B.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.hookExecutor=new z.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,w.default)(),r=e=>(0,Z.isOriginAllowed)(e,{frontendOrigin:this.frontendOrigin,extra:process.env.ALCHEMY_EXTRA_UI_ORIGINS}),s=new Set
4772
4778
  e.use((e,t,r)=>{const n=e.headers.host
4773
4779
  if((0,Z.isLoopbackHost)(n))return r()
4774
4780
  s.has(String(n))||(s.add(String(n)),this.logger.warn("Refused a request addressed to a name other than localhost (DNS rebinding?)",{host:n,path:e.path})),t.status(403).json({success:!1,error:Z.HOST_REFUSED_MESSAGE})}),e.use((0,Z.inertActiveContent)()),e.use((e,t,s)=>{const n=e.headers.origin
@@ -4832,7 +4838,7 @@ if("1"===e.query.stats){const e=a&&c?await(0,He.computeDiffStats)(o,a,c):void 0
4832
4838
  if(!e&&0===l.length)return void t.status(404).json({success:!1,error:`No diff stats for ${r}.`})
4833
4839
  const s=(0,We.withDocumentStats)(e,l)
4834
4840
  return void t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,stats:s}})}const d=5e5,u=a&&c?await(0,g.documentDiffGit)(this.options.projectPath).raw([...(0,g.documentDiffGitArgs)(),"diff","-M50%",`${a}..${c}`]):"",h=(0,We.withDocumentEdits)(u,l),p=h.length>d
4835
- t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),(0,Ke.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,Ve.registerLocalModelRoutes)(e),(0,Qe.registerExhibitPreviewRoutes)(e),e.get("/api/publish-capabilities",async(e,t)=>{try{const e=await(0,F.getPublishCapabilities)(this.options.projectPath,(0,F.defaultRunner)())
4841
+ t.json({success:!0,data:{requestId:r,baseSha:a,headSha:c,diff:p?h.slice(0,d):h,truncated:p}})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),(0,Ke.registerLinearRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient}),(0,ze.registerLocalModelRoutes)(e),(0,Qe.registerExhibitPreviewRoutes)(e),e.get("/api/publish-capabilities",async(e,t)=>{try{const e=await(0,F.getPublishCapabilities)(this.options.projectPath,(0,F.defaultRunner)())
4836
4842
  t.json({success:!0,data:e})}catch(e){t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/requests/:id/publish-pr",async(e,t)=>{const r=e.params.id
4837
4843
  try{const s=this.projectId
4838
4844
  if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
@@ -4888,7 +4894,7 @@ r.json({success:!0,data:a})}catch(e){this.logger.error("Hook event processing fa
4888
4894
  if(!s||"before"!==a&&"after"!==a)return void r.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
4889
4895
  if(n){const{notifyPlannerHandoffToolEvent:e}=await t.e(991).then(()=>t(18991))
4890
4896
  e(String(n),String(s),o)}if("after"===a&&e.body?.callId&&this.projectId){const{closeMcpReceipt:r}=await t.e(111).then(()=>t(56111))
4891
- 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,z.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
4897
+ r({projectId:this.projectId,apiClient:this.apiClient,logger:this.logger},{callId:String(e.body.callId),ok:!0,outputPreview:"string"==typeof i?i:void 0})}const c=(0,V.parseToolEvents)({toolName:s,sessionId:n||"",input:o,output:i,timing:a})
4892
4898
  let l=0
4893
4899
  for(const e of c)l+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:n||"",variables:e.variables,timing:e.timing})).triggered
4894
4900
  this.logger.info("tool-event received",{toolName:s,timing:a,sessionId:n||"",events:c.length,triggered:l}),r.json({success:!0,data:{events:c.length,triggered:l}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,H.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}}),e.post("/api/web-call",async(e,r)=>{try{const{tool:s,sessionId:n,callId:o,input:i,metadata:a,error:c}=e.body||{}
@@ -4917,7 +4923,7 @@ try{await(0,De.sweepStaleSetupWorktrees)(a,this.logger)
4917
4923
  const e=await(0,De.resolveSetupBranch)(this.apiClient,c,n,"string"==typeof o?o:void 0,this.logger,a),s=(0,De.generateSetupId)()
4918
4924
  d=O.default.join((0,De.getSetupWorktreesBase)(a),`verify-${s}`),await(0,De.createSetupWorktree)(a,d,e,this.logger)
4919
4925
  const u=await(0,Fe.withContainerLabels)({"alchemy.purpose":"verify","alchemy.request":"string"==typeof o?o:void 0},()=>(0,Ne.runConfinedGate)(d,[{name:"verify",command:i,required:!0,timeout:600}],l,{projectName:`verify-${s}`})),h=!String(r.shellInit??"").trim()&&(0,Ue.carriesOwnShellInit)(i),p=h?["The smoke command sources its own setup, and the environment declares no shellInit. This verify proves the smoke command, not the environment: docker exec runs no ENTRYPOINT and reads no login profile, so every LATER command gets a bare PATH. Move the preamble into config.shellInit and verify again with the command alone."]:[]
4920
- h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}finally{if(d&&D.default.existsSync(d))try{await(0,Be.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,ze.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Xe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Je.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
4926
+ h&&this.logger.warn("environment verify passed on a self-sourcing smoke command",{smoke:i}),t.json({success:!0,data:{ok:!u.error&&"passed"===u.status,status:u.status,error:u.error,commands:u.commands,warnings:p}})}catch(e){this.logger.warn("environment verify failed",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,H.getErrorMessage)(e)})}finally{if(d&&D.default.existsSync(d))try{await(0,Be.simpleGit)(a).raw(["worktree","remove","--force",d])}catch{}}}),(0,Ve.registerRunRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath,getDefaultModel:()=>this.cachedDefaultModel||void 0,envSessionStarts:this.envSessionStarts,paintCleaned:this.paintCleaned,rememberApprovedCommand:(e,t,r)=>this.rememberApprovedCommand(e,t,r),rememberJudgeApprovedCommand:(e,t,r)=>this.rememberJudgeApprovedCommand(e,t,r),visualBaselineDir:()=>this.visualBaselineDir()}),(0,Xe.registerRelaySettingsRoutes)(e,{projectPath:this.options.projectPath}),(0,Je.registerProducedFilesRoutes)(e,{projectPath:this.options.projectPath,logger:this.logger,getDocument:async e=>{const t=this.projectId
4921
4927
  if(!t||!/^[A-Za-z0-9_-]+$/.test(e))return null
4922
4928
  const r=await this.apiClient.get(`/api/projects/${t}/documents/${e}`).catch(()=>null),s=r?.data??r
4923
4929
  return s&&"string"==typeof s.content?{filename:String(s.filename||`${e}.md`),content:s.content}:null}}),(0,Ye.registerWorkspaceRoutes)(e,{getProjectId:()=>this.projectId,apiClient:this.apiClient,logger:this.logger,projectPath:this.options.projectPath}),e.get("/api/stuck-sessions",this.handleGetStuckSessions.bind(this)),e.post("/api/stuck-sessions/test-inject",this.handleTestInjectStuckSession.bind(this)),e.post("/api/stuck-sessions/:sessionId/retry",this.handleRetryStuckSession.bind(this)),e.get("/api/version-warnings",(e,t)=>{t.json({success:!0,data:this.versionWarnings})}),e.post("/api/self-update",async(e,r)=>{try{const{execSync:e}=await Promise.resolve().then(()=>t.t(35317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
@@ -5699,12 +5705,12 @@ const s=e.slice(r+1).find(e=>"assistant"===e.role&&e.created>0)
5699
5705
  return s?new Date(s.created).toISOString():void 0}function W(e,t){const r=e.findIndex(e=>e.id===t)
5700
5706
  return e.slice(r+1).filter(e=>"assistant"===e.role&&e.text.trim()).map(e=>e.id)}var G,K=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
5701
5707
  var n=Object.getOwnPropertyDescriptor(t,r)
5702
- 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]}),V=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),z=this&&this.__importStar||(G=function(e){return G=Object.getOwnPropertyNames||function(e){var t=[]
5708
+ 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]}),z=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),V=this&&this.__importStar||(G=function(e){return G=Object.getOwnPropertyNames||function(e){var t=[]
5703
5709
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
5704
5710
  return t},G(e)},function(e){if(e&&e.__esModule)return e
5705
5711
  var t={}
5706
5712
  if(null!=e)for(var r=G(e),s=0;s<r.length;s++)"default"!==r[s]&&K(t,e,r[s])
5707
- return V(t,e),t})
5713
+ return z(t,e),t})
5708
5714
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.extendChatWatches=function(e){if(e>0)for(const t of ce.values())t.hardCapAtMs+=e},exports.persistedExchanges=o,exports.resumeInboxChatWatches=function(e){try{const t=s(e.projectPath)
5709
5715
  if(!re.existsSync(t))return
5710
5716
  const r=JSON.parse(re.readFileSync(t,"utf-8"))
@@ -5727,7 +5733,7 @@ s.checking=!0
5727
5733
  try{await r()
5728
5734
  const n=await T(e,s,t)
5729
5735
  return n&&s.logger.warn("inbox-chat loop: turn stopped answering; ended it and resumed it once",{sessionId:e,markerMessageId:s.exchanges[s.exchanges.length-1].markerMessageId}),n}finally{s.checking=!1}},exports.extractTurnSilence=x,exports.absorbedByReply=q
5730
- const Y=t(60833),J=t(58824),X=t(42593),Q=t(24068),Z=t(4234),ee=t(17039),te=t(81602),re=z(t(79896)),se=z(t(16928)),ne=t(36799),oe=3e5,ie=9e5,ae=8e3,ce=new Map
5736
+ const Y=t(60833),J=t(58824),X=t(42593),Q=t(24068),Z=t(4234),ee=t(17039),te=t(81602),re=V(t(79896)),se=V(t(16928)),ne=t(36799),oe=3e5,ie=9e5,ae=8e3,ce=new Map
5731
5737
  let le=null
5732
5738
  const de=new Map,ue=6e4,he=/^(let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|one (?:moment|sec)|checking|looking|on it|sure[,.!]?\s|okay[,.!]?\s|ok[,.!]?\s|first[, ]|hang on|give me)/i,pe=/^(ok(?:ay)?|done|sure|noted|got it|all set|that's it|right)[.!]?$/i,fe=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]/u,me=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]+/gu,ge=/^(?:(?:now|next|then|first|so)[,:]?\s+)?(?:let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|i['\u2019]m (?:now )?(?:checking|looking|nudging|filing|starting|opening|creating|asking|waking|reading|pulling|fetching|comparing|running|updating|writing|going|registering|drafting))\b/i,we=/get-info|:list|:search|list-areas|read_files|read_file|search|grep|query_history|request_diff|environment_list|environment_get|testing_process_list|check-status|list-sessions|read-messages|results:fetch|commands:list|read_site|:brief|:get\b/i,ye=new Set},81602(e,exports){"use strict"
5733
5739
  exports.Cr=exports.UT=void 0,exports.wakeThatTellsUser=function(e,t){const r="string"==typeof e?exports.Cr.get(e):void 0
@@ -5892,8 +5898,10 @@ return l.set(a,{server:h,port:p,root:a}),{port:p,url:`http://127.0.0.1:${p}`}},e
5892
5898
  return l.get(t)?.server.close(),void l.delete(t)}for(const[,e]of l)e.server.close()
5893
5899
  l.clear()},exports.stopSitesUnder=function(e){const t=(0,i.resolve)(e),r=[]
5894
5900
  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}`))
5895
- return r},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
5896
- 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",".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"
5901
+ return r},exports.stopSiteAt=function(e){let t=0
5902
+ 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
5903
+ return!1},exports.listServedSites=function(){return Array.from(l.values()).map(e=>({root:e.root,port:e.port}))}
5904
+ 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"
5897
5905
  exports.shootUrl=async function(e,r={}){const s=Math.min(1920,Math.max(320,"number"==typeof r.width?Math.round(r.width):1280)),{chromium:n}=t(71257)
5898
5906
  let o
5899
5907
  try{o=await n.launch({channel:"chrome",args:["--no-sandbox"]})}catch{o=await n.launch({args:["--no-sandbox"]})}try{const t=await o.newPage({viewport:{width:s,height:900},deviceScaleFactor:1,ignoreHTTPSErrors:!0})
@@ -6812,56 +6820,60 @@ if(!n.ok){try{o.default.rmdirSync(s)}catch{}return{ok:!1,reason:n.reason}}return
6812
6820
  exports.printBanner=function(){const e=(0,r.ownVersion)()??"unknown"
6813
6821
  console.log([""," .oo 8 8 o 8 "," .P 8 8 8 8 8 "," .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. "," oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 "," .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 ",".P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' ","..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+e,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::",""].join("\n"))}
6814
6822
  const r=t(42403)},38778(e,exports,t){"use strict"
6815
- async function r(e,t,r,s,n="cli-init"){try{const o=(0,i.basename)(r)||"Local Project",c=process.env.ALCHEMY_MACHINE_ID,l=(0,a.folderKeyFromEnv)(),d=await fetch(`${e}/api/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,w.machineCallerHeaders)(e)},body:JSON.stringify({name:o,description:`Project for ${o}`,status:"active",metadata:{basePath:r,createdVia:n},...c?{machineId:c}:{},...l?{folderKey:l}:{}})})
6816
- if(b=void 0,d.ok){const e=await d.json()
6817
- return e.data?.id||e.id}if(s.info(`Project registration failed (HTTP ${d.status})`),d.status>=400&&d.status<500){const e=await d.json().catch(()=>null),t=e?.error?.message??e?.error??e?.message
6818
- "string"==typeof t&&t.trim()&&(b=t.trim())}}catch(e){s.info("Project registration failed",{error:(0,h.getErrorMessage)(e)})}}async function s(e){if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
6823
+ function r(){return S}function s(e){return e?`\u26a0\ufe0f This project is not registered with AlchemyLab yet: ${e}.`.replace(/\.\.$/,"."):"\u26a0\ufe0f This project is not registered with AlchemyLab yet: the hosted service did not answer. Run `alchemy` again to try once more."}async function n(e,t,r,s,n="cli-init"){try{const o=(0,c.basename)(r)||"Local Project",i=process.env.ALCHEMY_MACHINE_ID,a=(0,l.folderKeyFromEnv)()
6824
+ let d=await u()
6825
+ for(let h=0;h<6&&503===d.status;h++)await new Promise(e=>setTimeout(e,2e3)),d=await u()
6826
+ async function u(){return fetch(`${e}/api/projects`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,v.machineCallerHeaders)(e)},body:JSON.stringify({name:o,description:`Project for ${o}`,status:"active",metadata:{basePath:r,createdVia:n},...i?{machineId:i}:{},...a?{folderKey:a}:{}})})}if(S=void 0,d.ok){const p=await d.json()
6827
+ return p.data?.id||p.id}if(s.info(`Project registration failed (HTTP ${d.status})`),401===d.status)S="the sign-in on this computer is no longer accepted. Run `alchemy logout`, then `alchemy`"
6828
+ else if(503===d.status)S="the hosted service has not heard from this computer yet. Run `alchemy` again in a moment"
6829
+ else if(d.status>=400&&d.status<500){const m=await d.json().catch(()=>null),g=m?.error?.message??m?.error??m?.message
6830
+ "string"==typeof g&&g.trim()&&(S=g.trim())}}catch(w){s.info("Project registration failed",{error:(0,f.getErrorMessage)(w)})}}async function o(e){if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
6819
6831
  if(e.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
6820
6832
  try{const t=await fetch(e+"/health")
6821
6833
  if(t.ok){const e=await t.json()
6822
- if(e?.data?.frontendUrl)return e.data.frontendUrl}}catch{}return e.replace(/:\d+/,":3000")}exports.ProjectRefused=void 0,exports.checkProjectIdentity=function(e){const t=(0,y.identityPath)(e)
6823
- if(!(0,n.existsSync)(t))return
6834
+ if(e?.data?.frontendUrl)return e.data.frontendUrl}}catch{}return e.replace(/:\d+/,":3000")}exports.ProjectRefused=void 0,exports.checkProjectIdentity=function(e){const t=(0,b.identityPath)(e)
6835
+ if(!(0,i.existsSync)(t))return
6824
6836
  let r
6825
- try{r=JSON.parse((0,n.readFileSync)(t,"utf8"))?.metadata?.basePath}catch{return}if(!r)return
6826
- const s=e=>{try{return(0,n.realpathSync)(e)}catch{return(0,i.resolve)(e)}}
6827
- if(s(r)!==s(e))throw new v(["\u274c This folder is a copy that has not been re-identified."," .alchemy_lab/project.json belongs to a project whose directory is:",` ${r}`," but you are starting in:",` ${e}`,""," Starting here would register as the ORIGINAL project and silently do no"," work for this one. If a fork is still being created, wait for it to finish."," If you copied this folder by hand, delete .alchemy_lab/project.json and"," start again to get a project of its own."])},exports.prepareProject=async function(e){const{projectPath:t,remoteServerUrl:s,authToken:c,logger:l}=e,d=e.say??(e=>console.log(e)),u=await async function(e,t,s,a,c){if(function(e){return(0,n.existsSync)((0,i.join)(e,f.ALCHEMY_DIR_NAME,"project.json"))}(e))return!1
6828
- c("\u{1f4c1} Initializing AlchemyLab project..."),c("")
6829
- const l=await(0,m.ensureProjectRepository)(e)
6830
- if("created"===l.status)c(`\u{1f331} ${l.message}`)
6831
- else if("failed"===l.status||!l.hasHistory)throw function(e){const t=(0,i.join)(e,f.ALCHEMY_DIR_NAME)
6832
- try{(0,n.readdirSync)(t).every(e=>"server.log"===e)&&(0,n.rmSync)(t,{recursive:!0,force:!0})}catch{}}(e),new v([`\u26a0\ufe0f ${l.message??"This folder cannot be used as a project yet."}`," Nothing was registered and no servers were started."])
6833
- const d=(0,i.join)(e,f.ALCHEMY_DIR_NAME)
6834
- let u
6835
- await(0,o.mkdir)(d,{recursive:!0}),s?(c(`\u{1f4cb} Registering project "${(0,i.basename)(e)||"Local Project"}"...`),u=await r(t,s,e,a),c(u?`\u2705 Project registered: ${u}`:b?`\u26a0\ufe0f Not registered: ${b}`:"\u26a0\ufe0f Project registration failed (will retry next run)")):c("\u26a0\ufe0f No auth token, skipping project registration")
6836
- const h=(0,i.join)(d,"project.json")
6837
- return(0,n.writeFileSync)(h,JSON.stringify({id:u||null,name:(0,i.basename)(e)||"Local Project",status:"active",registeredRemotely:!!u,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:d}},null,2),"utf-8"),c(""),!0}(t,s,c,l,d),p=(0,y.identityPath)(t)
6838
- let k
6839
- try{const e=JSON.parse((0,n.readFileSync)(p,"utf-8"))
6840
- if(k=e.id||void 0,!1===e.registeredRemotely&&c){const o=await r(s,c,t,l,"cli-init-retry")
6841
- o&&(e.id=o,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,n.writeFileSync)(p,JSON.stringify(e,null,2),"utf-8"),k=o,d(`\u2705 Project registered: ${o}`))}}catch{l.warn("Could not read project.json")}const S=async(e,t,r)=>{const n=await fetch(`${s}${t}`,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${c}`,"X-Client-Type":"alchemy-local-service",...(0,w.machineCallerHeaders)(s)},...void 0===r?{}:{body:JSON.stringify(r)}})
6842
- if(!n.ok)throw new Error(`HTTP ${n.status}`)
6843
- return"GET"===e?n.json():void 0},I=c&&k?async e=>{const t=await S("GET",`/api/projects/${k}/requests/${e}`)
6844
- return(t?.data??t)?.type}:void 0,$=await(0,g.defaultBranchHasCode)(t,I).catch(()=>!1)
6845
- let j=Promise.resolve()
6846
- if(c){j=(0,g.reportProjectHasCode)(k,$,(e,t)=>S("PATCH",e,t))
6847
- const e=(0,a.folderKeyFromEnv)()
6848
- k&&e&&S("PATCH",`/api/projects/${k}`,{folderKey:e}).catch(e=>l.info("Folder key not recorded yet",{error:(0,h.getErrorMessage)(e)}))}return{firstRun:u,projectId:k,hasCode:$,codeReported:j}},exports.startProject=async function(e){const{projectPath:t,remoteServerUrl:r,tokenManager:o,authToken:a,projectId:m,logger:g}=e,w=e.port||await(0,p.findAvailablePort)(3456),y=new l.AlchemyActionServer(g,{projectPath:t,remoteServerUrl:r,port:w,authToken:a,tokenManager:o,retireTwins:e.retireTwins,onIdlePark:e.onIdlePark}),v=(0,d.getInstallContext)(),b=new c.MCPClientConfigurer(g,r,a),k=new u.UnifiedLauncher(g,v,b)
6849
- y.setReconfigureOpenCode(async()=>{const e=(await o.getToken())?.token??a,s=new c.MCPClientConfigurer(g,r,e),{installPath:n,nodePath:i}=v,l=await s.configureOpenCode(n,t,i,m?{projectId:m}:void 0,!0)
6850
- return{success:l.success,error:l.error}}),y.setRelaunchOpenCode(async()=>{const e=(await o.getToken())?.token??a,s=new c.MCPClientConfigurer(g,r,e)
6851
- return new u.UnifiedLauncher(g,v,s).launch({projectPath:t,settings:m?{projectId:m}:void 0,onServeExit:e=>y.noteServeExit(e)})})
6852
- try{const e=(0,i.join)(t,f.ALCHEMY_DIR_NAME,"opencode.json")
6853
- if((0,n.existsSync)(e)){const t=JSON.parse((0,n.readFileSync)(e,"utf-8"))
6854
- t&&null!=t.port&&(0,n.writeFileSync)(e,JSON.stringify({...t,port:null},null,2))}}catch{}const S=y.start(),[,I,$]=await Promise.all([S,s(r),Promise.race([y.workCopiesReady,S]).then(()=>k.launch({projectPath:t,settings:m?{projectId:m}:void 0,onServeExit:e=>y.noteServeExit(e)})).catch(e=>(g.warn("Failed to launch OpenCode serve",{error:(0,h.getErrorMessage)(e)}),{success:!1,error:(0,h.getErrorMessage)(e)}))])
6837
+ try{r=JSON.parse((0,i.readFileSync)(t,"utf8"))?.metadata?.basePath}catch{return}if(!r)return
6838
+ const s=e=>{try{return(0,i.realpathSync)(e)}catch{return(0,c.resolve)(e)}}
6839
+ if(s(r)!==s(e))throw new k(["\u274c This folder is a copy that has not been re-identified."," .alchemy_lab/project.json belongs to a project whose directory is:",` ${r}`," but you are starting in:",` ${e}`,""," Starting here would register as the ORIGINAL project and silently do no"," work for this one. If a fork is still being created, wait for it to finish."," If you copied this folder by hand, delete .alchemy_lab/project.json and"," start again to get a project of its own."])},exports.prepareProject=async function(e){const{projectPath:t,remoteServerUrl:o,authToken:d,logger:u}=e,h=e.say??(e=>console.log(e)),p=await async function(e,t,o,l,d){if(function(e){return(0,i.existsSync)((0,c.join)(e,g.ALCHEMY_DIR_NAME,"project.json"))}(e))return!1
6840
+ d("\u{1f4c1} Initializing AlchemyLab project..."),d("")
6841
+ const u=await(0,w.ensureProjectRepository)(e)
6842
+ if("created"===u.status)d(`\u{1f331} ${u.message}`)
6843
+ else if("failed"===u.status||!u.hasHistory)throw function(e){const t=(0,c.join)(e,g.ALCHEMY_DIR_NAME)
6844
+ try{(0,i.readdirSync)(t).every(e=>"server.log"===e)&&(0,i.rmSync)(t,{recursive:!0,force:!0})}catch{}}(e),new k([`\u26a0\ufe0f ${u.message??"This folder cannot be used as a project yet."}`," Nothing was registered and no servers were started."])
6845
+ const h=(0,c.join)(e,g.ALCHEMY_DIR_NAME)
6846
+ let p
6847
+ await(0,a.mkdir)(h,{recursive:!0}),o?(d(`\u{1f4cb} Registering project "${(0,c.basename)(e)||"Local Project"}"...`),p=await n(t,o,e,l),d(p?`\u2705 Project registered: ${p}`:s(r()))):d("\u26a0\ufe0f No auth token, skipping project registration")
6848
+ const f=(0,c.join)(h,"project.json")
6849
+ return(0,i.writeFileSync)(f,JSON.stringify({id:p||null,name:(0,c.basename)(e)||"Local Project",status:"active",registeredRemotely:!!p,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:h}},null,2),"utf-8"),d(""),!0}(t,o,d,u,h),m=(0,b.identityPath)(t)
6850
+ let S
6851
+ try{const e=JSON.parse((0,i.readFileSync)(m,"utf-8"))
6852
+ if(S=e.id||void 0,!1===e.registeredRemotely&&d){const a=await n(o,d,t,u,"cli-init-retry")
6853
+ a?(e.id=a,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,i.writeFileSync)(m,JSON.stringify(e,null,2),"utf-8"),S=a,h(`\u2705 Project registered: ${a}`)):h(s(r()))}}catch{u.warn("Could not read project.json")}const I=async(e,t,r)=>{const s=await fetch(`${o}${t}`,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${d}`,"X-Client-Type":"alchemy-local-service",...(0,v.machineCallerHeaders)(o)},...void 0===r?{}:{body:JSON.stringify(r)}})
6854
+ if(!s.ok)throw new Error(`HTTP ${s.status}`)
6855
+ return"GET"===e?s.json():void 0},$=d&&S?async e=>{const t=await I("GET",`/api/projects/${S}/requests/${e}`)
6856
+ return(t?.data??t)?.type}:void 0,j=await(0,y.defaultBranchHasCode)(t,$).catch(()=>!1)
6857
+ let E=Promise.resolve()
6858
+ if(d){E=(0,y.reportProjectHasCode)(S,j,(e,t)=>I("PATCH",e,t))
6859
+ const e=(0,l.folderKeyFromEnv)()
6860
+ S&&e&&I("PATCH",`/api/projects/${S}`,{folderKey:e}).catch(e=>u.info("Folder key not recorded yet",{error:(0,f.getErrorMessage)(e)}))}return{firstRun:p,projectId:S,hasCode:j,codeReported:E}},exports.startProject=async function(e){const{projectPath:t,remoteServerUrl:r,tokenManager:s,authToken:n,projectId:a,logger:l}=e,w=e.port||await(0,m.findAvailablePort)(3456),y=new u.AlchemyActionServer(l,{projectPath:t,remoteServerUrl:r,port:w,authToken:n,tokenManager:s,retireTwins:e.retireTwins,onIdlePark:e.onIdlePark}),v=(0,h.getInstallContext)(),b=new d.MCPClientConfigurer(l,r,n),k=new p.UnifiedLauncher(l,v,b)
6861
+ y.setReconfigureOpenCode(async()=>{const e=(await s.getToken())?.token??n,o=new d.MCPClientConfigurer(l,r,e),{installPath:i,nodePath:c}=v,u=await o.configureOpenCode(i,t,c,a?{projectId:a}:void 0,!0)
6862
+ return{success:u.success,error:u.error}}),y.setRelaunchOpenCode(async()=>{const e=(await s.getToken())?.token??n,o=new d.MCPClientConfigurer(l,r,e)
6863
+ return new p.UnifiedLauncher(l,v,o).launch({projectPath:t,settings:a?{projectId:a}:void 0,onServeExit:e=>y.noteServeExit(e)})})
6864
+ try{const e=(0,c.join)(t,g.ALCHEMY_DIR_NAME,"opencode.json")
6865
+ if((0,i.existsSync)(e)){const t=JSON.parse((0,i.readFileSync)(e,"utf-8"))
6866
+ t&&null!=t.port&&(0,i.writeFileSync)(e,JSON.stringify({...t,port:null},null,2))}}catch{}const S=y.start(),[,I,$]=await Promise.all([S,o(r),Promise.race([y.workCopiesReady,S]).then(()=>k.launch({projectPath:t,settings:a?{projectId:a}:void 0,onServeExit:e=>y.noteServeExit(e)})).catch(e=>(l.warn("Failed to launch OpenCode serve",{error:(0,f.getErrorMessage)(e)}),{success:!1,error:(0,f.getErrorMessage)(e)}))])
6855
6867
  let j
6856
6868
  if($.success){const e="port"in $?$.port:void 0,r="pid"in $?$.pid:void 0
6857
6869
  if(j={success:!0,port:e,pid:r},e){r&&y.trackChildPid(r),y.setOpenCodePort(e)
6858
- const s=(0,i.join)(t,f.ALCHEMY_DIR_NAME,"opencode.json");(0,n.writeFileSync)(s,JSON.stringify({port:e,pid:r,startedAt:(new Date).toISOString()},null,2)),g.info("Wrote OpenCode config",{path:s,port:e})}}else j={success:!1,error:"error"in $&&$.error?String($.error):"no reason reported"}
6859
- return{actionServer:y,frontendUrl:I,opencode:j}},exports.resolveFrontendUrl=s,exports.getRemoteServerUrl=async function(e){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
6870
+ const s=(0,c.join)(t,g.ALCHEMY_DIR_NAME,"opencode.json");(0,i.writeFileSync)(s,JSON.stringify({port:e,pid:r,startedAt:(new Date).toISOString()},null,2)),l.info("Wrote OpenCode config",{path:s,port:e})}}else j={success:!1,error:"error"in $&&$.error?String($.error):"no reason reported"}
6871
+ return{actionServer:y,frontendUrl:I,opencode:j}},exports.resolveFrontendUrl=o,exports.getRemoteServerUrl=async function(e){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
6860
6872
  try{const{createConfigManager:r}=await t.e(60).then(()=>t.t(49060,23)),s=r(e)
6861
6873
  return(await s.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}
6862
- const n=t(79896),o=t(91943),i=t(16928),a=t(40390),c=t(18984),l=t(62366),d=t(73962),u=t(81187),h=t(58824),p=t(66800),f=t(14139),m=t(37776),g=t(71879),w=t(11773),y=t(20990)
6863
- class v extends Error{constructor(e){super(e.join("\n")),this.lines=e}}let b
6864
- exports.ProjectRefused=v},89122(e,exports,t){"use strict"
6874
+ const i=t(79896),a=t(91943),c=t(16928),l=t(40390),d=t(18984),u=t(62366),h=t(73962),p=t(81187),f=t(58824),m=t(66800),g=t(14139),w=t(37776),y=t(71879),v=t(11773),b=t(20990)
6875
+ class k extends Error{constructor(e){super(e.join("\n")),this.lines=e}}let S
6876
+ exports.ProjectRefused=k},89122(e,exports,t){"use strict"
6865
6877
  function r(e,t=process.env){const r=Number(t.ALCHEMY_DEV_WORDS_DELAY_MS??0)
6866
6878
  if(!Number.isFinite(r)||r<=0)return 0
6867
6879
  let s=""
@@ -7195,21 +7207,7 @@ const o=Array.isArray(n?.data?.wakes)?n.data.wakes:[]
7195
7207
  for(const e of o)"string"==typeof e.projectId&&("work"!==e.reason&&"start"!==e.reason&&"stop"!==e.reason||this.opts.wake(e.projectId,e.reason))
7196
7208
  const i=Array.isArray(n?.data?.creates)?n.data.creates:[]
7197
7209
  for(const e of i)"string"==typeof e.create&&this.opts.create?.(e.create)}catch(t){if(this.stopped)return
7198
- e=Math.min(Math.max(2*e,5e3),3e4),e>=3e4&&this.opts.logger.warn("The hosted service is not answering the wake poll",{error:t instanceof Error?t.message:String(t)})}}else e=6e4}}}},48893(e,exports,t){"use strict"
7199
- var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
7200
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.migrationCandidates=function(e,t={}){let r
7201
- try{r=JSON.parse(s.default.readFileSync(e,"utf8"))}catch{return[]}if(!Array.isArray(r))return[]
7202
- const i=new Set,a=[]
7203
- for(const e of r){if("string"!=typeof e?.projectId||"string"!=typeof e?.projectPath)continue
7204
- const r=(0,o.checkApprovable)(e.projectPath,t)
7205
- if(!r.ok||i.has(r.path))continue
7206
- let c
7207
- try{c=JSON.parse(s.default.readFileSync(n.default.join(r.path,".alchemy_lab","project.json"),"utf8"))}catch{continue}if(c?.id!==e.projectId)continue
7208
- const l=c?.metadata?.basePath
7209
- if("string"!=typeof l)continue
7210
- let d
7211
- try{d=s.default.realpathSync(l)}catch{continue}d===r.path&&(i.add(r.path),a.push({path:r.path,dev:r.dev,ino:r.ino,projectId:e.projectId,name:"string"==typeof c?.name&&c.name?c.name:n.default.basename(r.path)}))}return a.sort((e,t)=>e.path.localeCompare(t.path))}
7212
- const s=r(t(79896)),n=r(t(16928)),o=t(79616)},67518(e,exports,t){"use strict"
7210
+ e=Math.min(Math.max(2*e,5e3),3e4),e>=3e4&&this.opts.logger.warn("The hosted service is not answering the wake poll",{error:t instanceof Error?t.message:String(t)})}}else e=6e4}}}},67518(e,exports,t){"use strict"
7213
7211
  function r(e){return String(e).replace(/[\u0000-\u0009\u000b-\u001f\u007f]/g,"").split("\n").map(e=>`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(" & return & ")}function s(e,t){for(const r of String(t.PATH??"").split(":")){if(!r)continue
7214
7212
  const t=a.default.join(r,e)
7215
7213
  try{return i.default.accessSync(t,i.default.constants.X_OK),t}catch{}}return null}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
@@ -7395,16 +7393,16 @@ r.close()
7395
7393
  const n=s.trim().toLowerCase()
7396
7394
  return""===n?t:"y"===n||"yes"===n}async function o(e,t){const s={}
7397
7395
  process.env.ALCHEMY_LAB_HOME&&(s.ALCHEMY_LAB_HOME=process.env.ALCHEMY_LAB_HOME)
7398
- try{const n=await(0,B.installLoginItem)({nodePath:process.execPath,cliPath:r(),remoteServerUrl:t.remoteServerUrl,envPath:process.env.PATH??"/usr/bin:/bin",env:s,logPath:e.log})
7399
- console.log(n.loaded?`AlchemyLab will start when you log in (${n.file}).`:`Written to ${n.file}, but it could not be loaded now: ${n.detail??"no reason given"}. It loads at your next login.`),"darwin"===process.platform&&console.log('Started at login, macOS may ask once whether "node" may use a project in Documents, Desktop or Downloads. Answer it at the computer.')}catch(e){console.error(`Could not set AlchemyLab to start at login: ${e instanceof Error?e.message:String(e)}`)}}async function i(e,t){await(0,M.isServiceRunning)(e)||(console.log("Starting the AlchemyLab service..."),await(0,M.startServiceInBackground)(e,t,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${e.log}`),process.exit(1)))}function a(e){return e.state}async function c(e){let t,r
7400
- try{r=await(0,M.request)(e,"ping"),t=await(0,M.request)(e,"status")}catch{return void console.log("AlchemyLab service: not running. `alchemy service` starts it.")}console.log(`AlchemyLab service: running (pid ${r.pid}) for ${r.remote}`)
7396
+ try{const n=await(0,U.installLoginItem)({nodePath:process.execPath,cliPath:r(),remoteServerUrl:t.remoteServerUrl,envPath:process.env.PATH??"/usr/bin:/bin",env:s,logPath:e.log})
7397
+ console.log(n.loaded?`AlchemyLab will start every time this computer starts (${n.file}).`:`Written to ${n.file}, but it could not be loaded now: ${n.detail??"no reason given"}. It loads the next time this computer starts.`),"darwin"===process.platform&&console.log('Started with the computer, macOS may ask once whether "node" may use a project in Documents, Desktop or Downloads. Answer it at the computer.')}catch(e){console.error(`Could not set AlchemyLab to start at login: ${e instanceof Error?e.message:String(e)}`)}}async function i(e,t){await(0,x.isServiceRunning)(e)||(console.log("Starting the AlchemyLab service..."),await(0,x.startServiceInBackground)(e,t,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${e.log}`),process.exit(1)))}function a(e){return e.state}async function c(e){let t,r
7398
+ try{r=await(0,x.request)(e,"ping"),t=await(0,x.request)(e,"status")}catch{return void console.log("AlchemyLab service: not running. `alchemy service` starts it.")}console.log(`AlchemyLab service: running (pid ${r.pid}) for ${r.remote}`)
7401
7399
  const s=t.projects??[]
7402
7400
  if(0===s.length)return void console.log(" No projects on this computer yet. Run `alchemy` in a folder to add it.")
7403
7401
  const n=Math.max(8,...s.map(e=>e.name.length))+2
7404
7402
  console.log(` ${"PROJECT".padEnd(n)}${"STATE".padEnd(10)}${"PORT".padEnd(7)}FOLDER`)
7405
7403
  for(const e of s)console.log(` ${e.name.padEnd(n)}${a(e).padEnd(10)}${String(e.port??"").padEnd(7)}${e.path}`),e.reason&&console.log(` ${"".padEnd(n)}${e.reason}`)
7406
7404
  s.some(e=>"parked"===e.state||"stopped"===e.state)&&console.log(" A parked or stopped project connects when you open it in the app.")}function l(e){console.error(`\u274c ${e.reason??"It did not start."}`)
7407
- for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function d(e){const t=async(t,r={},s=12e4)=>{const n=await(0,M.request)(e,"new-computer",{op:t,...r},s)
7405
+ for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function d(e){const t=async(t,r={},s=12e4)=>{const n=await(0,x.request)(e,"new-computer",{op:t,...r},s)
7408
7406
  if(!n.ok)throw new Error(n.reason??"The AlchemyLab service did not do it.")
7409
7407
  return n}
7410
7408
  return{candidates:async()=>{const e=await t("candidates",{},4e4)
@@ -7414,7 +7412,8 @@ return{say:(e="")=>console.log(e),ask:t=>new Promise(r=>e.question(`${t} `,e=>r(
7414
7412
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.serviceSetUp=s,exports.runService=async function(e,t){if(t.includes("--foreground"))return async function(e){const t=(0,A.servicePaths)(e);(0,A.ensurePrivateDir)(t.dir)
7415
7413
  const s=(0,C.readServiceConfig)(t)
7416
7414
  s||(console.error("The AlchemyLab service is not set up for this server yet. Run `alchemy service` in a terminal first."),process.exit(1))
7417
- const n=(0,S.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new x.Supervisor({paths:t,config:s,logger:n,childModule:r(),childArgs:e=>["project-run","--project",e.path,"--expect",`${e.dev}:${e.ino}`],childEnv:()=>({...(0,C.childEnv)((0,C.readServiceConfig)(t)??s),ALCHEMY_MACHINE_ID:(0,p.machineIdentity)(t).id}),approvalEnv:{installDir:W},version:(0,j.ownVersion)()??void 0,onChange:()=>u?.noteChanged(),cap:()=>(0,C.readServiceConfig)(t)?.cap??(0,x.defaultCap)(),machineId:(0,p.machineIdentity)(t).id,confirmIdentity:async()=>(await c).register(),signedOut:()=>!0===u?.signedOut,startAsNew:(0,N.startAsNewProject)({remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token}),connected:()=>i()}),i=async()=>{const e=(e,t)=>Promise.race([e,new Promise(e=>{const r=setTimeout(()=>e(!1),t)
7415
+ const n=(0,S.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new T.Supervisor({paths:t,config:s,logger:n,childModule:r(),childArgs:e=>["project-run","--project",e.path,"--expect",`${e.dev}:${e.ino}`],childEnv:()=>({...(0,C.childEnv)((0,C.readServiceConfig)(t)??s),ALCHEMY_MACHINE_ID:(0,p.machineIdentity)(t).id}),approvalEnv:{installDir:H},version:(0,j.ownVersion)()??void 0,onChange:()=>u?.noteChanged(),cap:()=>(0,C.readServiceConfig)(t)?.cap??(0,T.defaultCap)(),machineId:(0,p.machineIdentity)(t).id,confirmIdentity:async()=>(await c).register(),signedOut:()=>!0===u?.signedOut,startAsNew:(0,q.startAsNewProject)({remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token}),connected:()=>i()}),i=async()=>{k.forgetCachedSignIn()
7416
+ const e=(e,t)=>Promise.race([e,new Promise(e=>{const r=setTimeout(()=>e(!1),t)
7418
7417
  r.unref?.()})]),t=await e(u?u.register(1e4):Promise.resolve(!1),12e3),r=await e(h?h.connectNow():Promise.resolve(!1),12e3)
7419
7418
  return!0===t&&!0===r}
7420
7419
  let a=()=>{}
@@ -7422,43 +7421,43 @@ const c=new Promise(e=>{a=e})
7422
7421
  let l,d,u,h,g,w
7423
7422
  const v=async(e=0)=>{l?.(),await(u?.stop().catch(()=>{})),h?.stop(),w?.stop(),g?.stop(),d?.close(),await o.stop(),process.exit(e)}
7424
7423
  process.on("SIGTERM",()=>{v(0)}),process.on("SIGINT",()=>{v(0)})
7425
- try{await o.start(()=>{v(0)})}catch(e){throw e instanceof x.AlreadyRunningError&&(console.error(e.message),process.exit(0)),e}const k=new I.TokenManager(n,t.dir,{remoteServerUrl:s.remoteServerUrl,allowInteractiveAuth:!1})
7426
- await k.initialize(),l=(0,R.startSignInWatchdog)(k,e=>n.info(e)),u=new q.MachineLink({paths:t,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,projects:()=>o.status(),wake:(e,t)=>o.wake(e,t),archived:e=>o.setArchived(e),answered:(0,N.onListAnswered)({supervisor:o,remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token,logger:n}),version:(0,j.ownVersion)()??void 0,create:e=>{M.create(e,!1).then(t=>n.info("A folder asked for from another device",{name:e,ok:t.ok,reason:t.reason}))},logger:n}),a(u),u.start(),h=new f.ContentLink({paths:t,remoteServerUrl:s.remoteServerUrl,privateData:!0===(0,C.readServiceConfig)(t)?.privateData,getToken:async()=>(await k.getToken())?.token,projectIds:()=>o.status().map(e=>e.projectId).filter(e=>"string"==typeof e),logger:n}),h.start(),g=new y.WordsBackup({store:h.store,stateFile:b.default.join(t.dir,"words-backup.json"),settings:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup),privateData:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,logger:n}),g.start()
7424
+ try{await o.start(()=>{v(0)})}catch(e){throw e instanceof T.AlreadyRunningError&&(console.error(e.message),process.exit(0)),e}const k=new I.TokenManager(n,t.dir,{remoteServerUrl:s.remoteServerUrl,allowInteractiveAuth:!1})
7425
+ await k.initialize(),l=(0,M.startSignInWatchdog)(k,e=>n.info(e)),u=new D.MachineLink({paths:t,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,projects:()=>o.status(),wake:(e,t)=>o.wake(e,t),archived:e=>o.setArchived(e),answered:(0,q.onListAnswered)({supervisor:o,remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token,logger:n}),version:(0,j.ownVersion)()??void 0,create:e=>{R.create(e,!1).then(t=>n.info("A folder asked for from another device",{name:e,ok:t.ok,reason:t.reason}))},logger:n}),a(u),u.start(),h=new f.ContentLink({paths:t,remoteServerUrl:s.remoteServerUrl,privateData:!0===(0,C.readServiceConfig)(t)?.privateData,getToken:async()=>(await k.getToken())?.token,projectIds:()=>o.status().map(e=>e.projectId).filter(e=>"string"==typeof e),logger:n}),h.start(),g=new y.WordsBackup({store:h.store,stateFile:b.default.join(t.dir,"words-backup.json"),settings:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup),privateData:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,logger:n}),g.start()
7427
7426
  const $=async e=>{const r=h?await h.setPrivateData(e):{privateData:e,switching:!1},s=(0,C.readServiceConfig)(t)
7428
7427
  return s&&s.privateData!==r.privateData&&(0,C.writeServiceConfig)(t,{...s,privateData:r.privateData}),n.info(r.privateData?"This install keeps its content on this computer":"This install keeps its content on the hosted service"),r}
7429
7428
  o.handle("connect-now",async()=>({ok:await i()})),o.handle("move-words-here",async()=>{const e=await $(!0)
7430
7429
  return{ok:!e.refused,reason:e.refused,privateData:e.privateData,switching:e.switching}})
7431
7430
  const E=new m.NewComputer({machineId:(0,p.machineIdentity)(t).id,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,channelKey:()=>(0,f.channelKey)(t),store:h.store,backup:g,defaultFolder:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup).folder})
7432
7431
  let _
7433
- o.handle("new-computer",e=>(0,m.newComputerCommand)(E,e)),o.handle("computer-gone",async e=>h?await h.sayGone(String(e.machineId??"")):{ok:!1,reason:"The words channel is not running."}),o.handle("pair-channel",async e=>h?await h.pairAgain(String(e.code??"")):{ok:!1,reason:"The words channel is not running."}),w=new F.ScheduleWaker({remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,parked:()=>o.status().filter(e=>"parked"===e.state&&e.projectId).map(e=>e.projectId),wake:e=>o.wake(e,"work"),logger:n}),w.start(),(0,O.resolveFrontendUrl)(s.remoteServerUrl).then(e=>{try{/^https?:\/\//.test(e)&&(_=new URL(e).origin)}catch{}})
7434
- const T=(0,U.nativeDialogs)(),M=new L.Adding({supervisor:o,dialogs:T,logger:n,approvalEnv:{installDir:W}}),P=e=>{const r=(0,C.readServiceConfig)(t)
7432
+ o.handle("new-computer",e=>(0,m.newComputerCommand)(E,e)),o.handle("computer-gone",async e=>h?await h.sayGone(String(e.machineId??"")):{ok:!1,reason:"The words channel is not running."}),o.handle("pair-channel",async e=>h?await h.pairAgain(String(e.code??"")):{ok:!1,reason:"The words channel is not running."}),w=new N.ScheduleWaker({remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,parked:()=>o.status().filter(e=>"parked"===e.state&&e.projectId).map(e=>e.projectId),wake:e=>o.wake(e,"work"),logger:n}),w.start(),(0,P.resolveFrontendUrl)(s.remoteServerUrl).then(e=>{try{/^https?:\/\//.test(e)&&(_=new URL(e).origin)}catch{}})
7433
+ const x=(0,L.nativeDialogs)(),R=new F.Adding({supervisor:o,dialogs:x,logger:n,approvalEnv:{installDir:H}}),U=e=>{const r=(0,C.readServiceConfig)(t)
7435
7434
  if(!r)throw new Error("the service has no configuration");(0,C.writeServiceConfig)(t,{...r,wordsBackup:{...r.wordsBackup??{},...e}})}
7436
- d=await(0,D.startDoor)({supervisor:o,adding:M,wordsBackup:{view:()=>{const e=(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup)
7437
- return{settings:e,state:g.state(),runs:g.ownRuns(),folderAvailable:g.folderAvailable(e.folder)}},set:e=>P(e),pick:async()=>{const e=await T.pickFolder("Choose a folder for backups of your words (one your backup tool or iCloud Drive covers)")
7438
- return e&&P({folder:e}),e},run:()=>g.run(),restore:e=>g.restore(e)},privateData:{get:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,set:$},newComputer:{api:E,pickBackupFolder:()=>T.pickFolder('Choose the "AlchemyLab Backups" folder of your old computer (restored or copied onto this one)')},verifyMachineTicket:async e=>{const t=(await k.getToken())?.token
7435
+ d=await(0,O.startDoor)({supervisor:o,adding:R,wordsBackup:{view:()=>{const e=(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup)
7436
+ return{settings:e,state:g.state(),runs:g.ownRuns(),folderAvailable:g.folderAvailable(e.folder)}},set:e=>U(e),pick:async()=>{const e=await x.pickFolder("Choose a folder for backups of your words (one your backup tool or iCloud Drive covers)")
7437
+ return e&&U({folder:e}),e},run:()=>g.run(),restore:e=>g.restore(e)},privateData:{get:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,set:$},newComputer:{api:E,pickBackupFolder:()=>x.pickFolder('Choose the "AlchemyLab Backups" folder of your old computer (restored or copied onto this one)')},verifyMachineTicket:async e=>{const t=(await k.getToken())?.token
7439
7438
  if(!t)throw new Error("this computer is not signed in")
7440
- const r=await fetch(`${s.remoteServerUrl}/api/verify-machine-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,H.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:e}),signal:AbortSignal.timeout(1e4)})
7439
+ const r=await fetch(`${s.remoteServerUrl}/api/verify-machine-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,B.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:e}),signal:AbortSignal.timeout(1e4)})
7441
7440
  if(r.status>=400&&r.status<500)return{ok:!1}
7442
7441
  if(!r.ok)throw new Error(`HTTP ${r.status}`)
7443
7442
  const n=await r.json(),o=n?.data??n
7444
7443
  return{ok:!0===o?.ok,expiresAt:o?.expiresAt??null}},logger:n,remoteServerUrl:s.remoteServerUrl,version:(0,j.ownVersion)()??void 0,machineId:u.id,frontendOrigin:()=>_,verifyTicket:async(e,t)=>{const r=(await k.getToken())?.token
7445
7444
  if(!r)throw new Error("this computer is not signed in")
7446
- const n=await fetch(`${s.remoteServerUrl}/api/projects/${encodeURIComponent(e)}/verify-local-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,"X-Client-Type":"alchemy-local-service",...(0,H.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:t}),signal:AbortSignal.timeout(1e4)})
7445
+ const n=await fetch(`${s.remoteServerUrl}/api/projects/${encodeURIComponent(e)}/verify-local-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,"X-Client-Type":"alchemy-local-service",...(0,B.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:t}),signal:AbortSignal.timeout(1e4)})
7447
7446
  if(n.status>=400&&n.status<500)return{ok:!1}
7448
7447
  if(!n.ok)throw new Error(`HTTP ${n.status}`)
7449
7448
  const o=await n.json(),i=o?.data??o
7450
- return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,D.defaultDoorPort)(s.remoteServerUrl)),await new Promise(()=>{})}(e)
7449
+ return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,O.defaultDoorPort)(s.remoteServerUrl)),await new Promise(()=>{})}(e)
7451
7450
  const s=(0,A.servicePaths)(e)
7452
- if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,B.uninstallLoginItem)(e)
7453
- return void console.log(t.removed?`AlchemyLab no longer starts when you log in (${t.file} removed).`:"AlchemyLab was not set to start at login.")}const r=(0,C.readServiceConfig)(s)
7454
- return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(s,r)}if("stop"===t[0]){if(!await(0,M.isServiceRunning)(s))return void console.log("The AlchemyLab service is not running.")
7455
- await(0,M.request)(s,"shutdown")
7456
- for(let e=0;e<240&&await(0,M.isServiceRunning)(s);e++)await new Promise(e=>setTimeout(e,250))
7451
+ if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,U.uninstallLoginItem)(e)
7452
+ return void console.log(t.removed?`AlchemyLab no longer starts when this computer starts (${t.file} removed).`:"AlchemyLab was not set to start at login.")}const r=(0,C.readServiceConfig)(s)
7453
+ return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(s,r)}if("stop"===t[0]){if(!await(0,x.isServiceRunning)(s))return void console.log("The AlchemyLab service is not running.")
7454
+ await(0,x.request)(s,"shutdown")
7455
+ for(let e=0;e<240&&await(0,x.isServiceRunning)(s);e++)await new Promise(e=>setTimeout(e,250))
7457
7456
  return void console.log("The AlchemyLab service stopped, and with it every project it ran. They come back when it starts again.")}if("computer-gone"===t[0]){const e=t.slice(1).join(" ").trim()
7458
- e||(console.error("Usage: alchemy service computer-gone <name or id> (the computer that is gone, by its name in Settings, General, Computers; Copy id there gives its id)"),process.exit(1)),await(0,M.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7459
- const r=await(0,M.request)(s,"computer-gone",{machineId:e})
7460
- return r.ok||(console.error(`Not recorded: ${r.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("Recorded: that computer is gone. Its projects can now be moved to this computer without a backup: `alchemy service new-computer`.")}if("pair"===t[0]){t[1]||(console.error("Usage: alchemy service pair <code> (Settings, Data in the app gives the code)"),process.exit(1)),await(0,M.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7461
- const e=await(0,M.request)(s,"pair-channel",{code:t[1]})
7457
+ e||(console.error("Usage: alchemy service computer-gone <name or id> (the computer that is gone, by its name in Settings, General, Computers; Copy id there gives its id)"),process.exit(1)),await(0,x.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7458
+ const r=await(0,x.request)(s,"computer-gone",{machineId:e})
7459
+ return r.ok||(console.error(`Not recorded: ${r.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("Recorded: that computer is gone. Its projects can now be moved to this computer without a backup: `alchemy service new-computer`.")}if("pair"===t[0]){t[1]||(console.error("Usage: alchemy service pair <code> (Settings, Data in the app gives the code)"),process.exit(1)),await(0,x.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7460
+ const e=await(0,x.request)(s,"pair-channel",{code:t[1]})
7462
7461
  return e.ok||(console.error(`Not paired: ${e.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer is paired again: the hosted service accepts its new channel key.")}if("new-computer"===t[0]){const e=(0,C.readServiceConfig)(s)
7463
7462
  return e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(s,e),void await async function(e){process.stdin.isTTY||(console.error("`alchemy service new-computer` asks questions: run it in a terminal."),process.exit(1))
7464
7463
  const t=d(e),r=u()
@@ -7467,54 +7466,51 @@ if("moved"===s||"no-candidates"===s){const s=v.default.realpathSync(process.cwd(
7467
7466
  if(i){const t=(0,m.attachFolder)(s,i,(0,p.machineIdentity)(e).id)
7468
7467
  r.say(t.ok?`This folder is ${i.name||"that project"} now. Run \`alchemy\` here to start it.`:`Not done: ${t.reason}`)}}}catch(e){r.say(`Stopped: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1}finally{r.close()}}(s)}if("move-words-here"===t[0]){const e=(0,C.readServiceConfig)(s)
7469
7468
  e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(s,e)
7470
- const t=await(0,M.request)(s,"move-words-here",{},9e4)
7469
+ const t=await(0,x.request)(s,"move-words-here",{},9e4)
7471
7470
  return t.ok&&!0===t.privateData||(console.error(`Not started: ${t.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer keeps the words of its projects from now on. The words the hosted service holds for them are moving here; Settings, Data shows the count still held, per project.")}(0,A.ensurePrivateDir)(s.dir)
7472
7471
  let a=(0,C.readServiceConfig)(s)
7473
7472
  const l=!a
7474
- a?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==a.opencodeDir&&(a={...a,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,C.writeServiceConfig)(s,a),console.log(`Projects will run with the OpenCode build in ${a.opencodeDir} from their next start.`)):(a={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,C.writeServiceConfig)(s,a),console.log(`AlchemyLab service set up for ${e}.`),console.log('Your projects\' words (requests, messages, documents, results) are kept on this computer, and backed up daily to the folder "AlchemyLab Backups" in your home folder. Keep that folder in your own backup: a lost disk without it loses the words.')),l&&"ready"!==(await(0,E.runDockerPreflight)({interactive:!0===process.stdin.isTTY&&!0===process.stdout.isTTY})).status&&console.log("Running without Docker: agents fall back to the host sandbox. AlchemyLab picks Docker up when it is there."),!l||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,B.loginItemInstalled)(e)||(await n("Start AlchemyLab when you log in?",!0)?await o(s,a):console.log("Not set. `alchemy service install` sets it later."))
7475
- const h=await(0,M.isServiceRunning)(s),k=new _.ProjectsStore(s.projects)
7476
- k.exists()||h||await async function(e){const t=(0,T.migrationCandidates)(b.default.join((0,A.alchemyLabHome)(),"running-projects.json"),{installDir:W})
7477
- if(0!==t.length)if(process.stdin.isTTY){console.log(""),console.log("These projects already have AlchemyLab on this computer:")
7478
- for(const e of t)console.log(` ${e.name.padEnd(24)} ${e.path}`)
7479
- if(await n(`Keep these ${t.length} projects in AlchemyLab?`,!0)){for(const r of t)e.add({path:r.path,dev:r.dev,ino:r.ino,projectId:r.projectId,name:r.name,wanted:"stopped",addedAt:(new Date).toISOString(),addedVia:"migration"})
7480
- console.log("Added. Each starts when you open it in the app, or with `alchemy start <name>`.")}else e.touch(),console.log("None added. Run `alchemy` in a folder to add it.")}else console.log(`${t.length} existing projects can be added; run \`alchemy service\` in a terminal to choose.`)
7481
- else e.touch()}(k),h||await(0,M.startServiceInBackground)(s,a,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${s.log}`),process.exit(1)),await c(s)},exports.printStatus=c,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
7473
+ a?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==a.opencodeDir&&(a={...a,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,C.writeServiceConfig)(s,a),console.log(`Projects will run with the OpenCode build in ${a.opencodeDir} from their next start.`)):(a={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,C.writeServiceConfig)(s,a),console.log(`AlchemyLab service set up for ${e}.`),console.log('Your projects\' words (requests, messages, documents, results) are kept on this computer, and backed up daily to the folder "AlchemyLab Backups" in your home folder. Keep that folder in your own backup: a lost disk without it loses the words.')),l&&"ready"!==(await(0,E.runDockerPreflight)({interactive:!0===process.stdin.isTTY&&!0===process.stdout.isTTY})).status&&console.log("Running without Docker: agents fall back to the host sandbox. AlchemyLab picks Docker up when it is there."),!l||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,U.loginItemInstalled)(e)||(await n("Start AlchemyLab every time this computer starts?",!0)?await o(s,a):console.log("Not set. `alchemy service install` sets it later."))
7474
+ const h=await(0,x.isServiceRunning)(s),k=new _.ProjectsStore(s.projects)
7475
+ k.exists()||h||k.touch(),h||await(0,x.startServiceInBackground)(s,a,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${s.log}`),process.exit(1)),await c(s)},exports.printStatus=c,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
7482
7476
  return t||(console.error("The AlchemyLab service is not set up yet. Run `alchemy service` once to set it up."),process.exit(1)),t.paths}(e),a=(0,C.readServiceConfig)(o)
7483
7477
  if("status"===t)return c(o)
7484
- if("approve"===t){const e=(await(0,M.request)(o,"approvals")).approvals??[]
7478
+ if("approve"===t){const e=(await(0,x.request)(o,"approvals")).approvals??[]
7485
7479
  if(0===e.length)return void console.log("Nothing is waiting for your approval.")
7486
7480
  for(const t of e){console.log(""),console.log(t.message)
7487
7481
  const e=await n("Approve?",!1)
7488
- await(0,M.request)(o,"approve",{approvalId:t.id,yes:e}),console.log(e?"Approved.":"Declined.")}return}await i(o,a)
7482
+ await(0,x.request)(o,"approve",{approvalId:t.id,yes:e}),console.log(e?"Approved.":"Declined.")}return}await i(o,a)
7489
7483
  const d=function(e){if(!e)return v.default.realpathSync(process.cwd())
7490
7484
  if(e.startsWith("/")||e.startsWith(".")||e.startsWith("~")){const t=e.startsWith("~")?b.default.join(process.env.HOME??"",e.slice(1)):b.default.resolve(e)
7491
7485
  try{return v.default.realpathSync(t)}catch{return t}}return e}(r)
7492
- if("add"===t){const e=await(0,M.request)(o,"add",{path:d})
7486
+ if("add"===t){const e=await(0,x.request)(o,"add",{path:d})
7493
7487
  return e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),void console.log(e.added?`Added ${e.project.path}.`:`${e.project.path} is already on this computer.`)}if("start"===t||"restart"===t){console.log(`${"start"===t?"Starting":"Restarting"} ${b.default.basename(d)}...`)
7494
- const e=await(0,M.request)(o,t,{ref:d},18e4)
7488
+ const e=await(0,x.request)(o,t,{ref:d},18e4)
7495
7489
  for(const t of e.lines??[])e.ok&&console.log(t)
7496
- return e.ok||(l(e),process.exit(e.pending?0:1)),void console.log(`Running, action server on port ${e.port}.`)}if("stop"===t||"remove"===t){const e=await(0,M.request)(o,t,{ref:d},12e4)
7490
+ return e.ok||(l(e),process.exit(e.pending?0:1)),void console.log(`Running, action server on port ${e.port}.`)}if("stop"===t||"remove"===t){const e=await(0,x.request)(o,t,{ref:d},12e4)
7497
7491
  if(e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),console.log("stop"===t?"Stopped.":"Removed from this computer. The folder and its history are untouched."),"remove"===t&&Array.isArray(e.keptWork)&&e.keptWork.length){console.log(`Kept, because ${1===e.keptWork.length?"it holds":"they hold"} work that was never committed:`)
7498
7492
  for(const t of e.keptWork)console.log(` ${t}`)}return}console.error(`Unknown command ${t}`),process.exit(1)},exports.runSupervisedDefault=async function(e,r){const{paths:s,cfg:n}=r,o=v.default.realpathSync(process.cwd()),a=(0,S.createLogger)({serviceName:"alchemy-cli",console:!1,logToFile:!0,logFilePath:s.log,baseDir:s.dir}),c=new I.TokenManager(a,o,{remoteServerUrl:e,allowInteractiveAuth:!1})
7499
7493
  await c.initialize()
7500
- const h=await(0,R.ensureSignedIn)(c)
7501
- await i(s,n),await(0,M.request)(s,"connect-now",{},3e4).catch(()=>{}),await async function(e){const t=b.default.join(e.dir,"new-computer-asked.json")
7494
+ const h=await(0,M.ensureSignedIn)(c)
7495
+ await i(s,n)
7496
+ const p=await(0,x.request)(s,"connect-now",{},3e4).catch(()=>{})
7497
+ p?.ok||await(0,x.request)(s,"connect-now",{},3e4).catch(()=>{}),await async function(e){const t=b.default.join(e.dir,"new-computer-asked.json")
7502
7498
  if(!process.stdin.isTTY||!process.stdout.isTTY||v.default.existsSync(t))return
7503
- if(((await(0,M.request)(e,"status")).projects??[]).length>0)return
7499
+ if(((await(0,x.request)(e,"status")).projects??[]).length>0)return
7504
7500
  const r=d(e),{candidates:s}=await r.candidates()
7505
7501
  if(0===s.length)return
7506
7502
  const n=u()
7507
7503
  try{await(0,g.runNewComputerFlow)(r,n,{explicit:!1}),v.default.writeFileSync(t,JSON.stringify({askedAt:(new Date).toISOString()})+"\n",{mode:384}),n.say("")}finally{n.close()}}(s).catch(()=>{})
7508
- const p=await(0,M.request)(s,"add",{path:o})
7509
- p.ok||(console.error(`\u274c ${p.reason}`),process.exit(1))
7510
- const f=await(0,M.request)(s,"start",{ref:o},18e4)
7511
- for(const e of f.lines??[])console.log(e)
7512
- f.ok||(l(f),process.exit(1)),(0,P.printBanner)(),console.log(`\u{1f4c1} Project: ${o}`),console.log(""),console.log(" Action server .... port "+f.port)
7513
- const m=await(0,O.resolveFrontendUrl)(e)
7514
- if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${m}\x07${m}\x1b]8;;\x07`),console.log(""),f.firstRun){const e=`${m.replace(/\/$/,"")}${f.projectId?`/projects/${f.projectId}`:""}/timeline?welcome=1`
7515
- console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),(0,$.openBrowser)(e,"Open the Alchemy UI link above in a browser on any device to continue there.")}const{InteractiveShell:w}=await t.e(994).then(()=>t.t(75994,23)),y=new w({projectPath:o,logger:a,remoteServerUrl:e,authToken:h,projectId:f.projectId,hasCode:!0===f.hasCode})
7516
- await y.start(async()=>{console.log(""),console.log(`AlchemyLab keeps running ${b.default.basename(o)}. \`alchemy stop\` in this folder stops it.`),process.exit(0)})}
7517
- const p=t(60134),f=t(89122),m=t(98074),g=t(62671),w=t(14139),y=t(79564),v=h(t(79896)),b=h(t(16928)),k=h(t(23785)),S=t(12782),I=t(11253),$=t(90629),j=t(42403),E=t(88918),A=t(18944),C=t(30030),_=t(52800),T=t(48893),x=t(81480),M=t(56257),R=t(19071),P=t(64504),O=t(38778),D=t(35382),q=t(60134),N=t(71918),F=t(36968),L=t(76231),U=t(67518),B=t(99269),H=t(11773),W=(()=>{let e=__dirname
7504
+ const f=await(0,x.request)(s,"add",{path:o})
7505
+ f.ok||(console.error(`\u274c ${f.reason}`),process.exit(1))
7506
+ const m=await(0,x.request)(s,"start",{ref:o},18e4)
7507
+ for(const e of m.lines??[])console.log(e)
7508
+ m.ok||(l(m),process.exit(1)),(0,R.printBanner)(),console.log(`\u{1f4c1} Project: ${o}`),console.log(""),console.log(" Action server .... port "+m.port)
7509
+ const w=await(0,P.resolveFrontendUrl)(e)
7510
+ if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${w}\x07${w}\x1b]8;;\x07`),console.log(""),m.firstRun){const e=`${w.replace(/\/$/,"")}${m.projectId?`/projects/${m.projectId}`:""}/timeline?welcome=1`
7511
+ console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),(0,$.openBrowser)(e,"Open the Alchemy UI link above in a browser on any device to continue there.")}const{InteractiveShell:y}=await t.e(994).then(()=>t.t(75994,23)),k=new y({projectPath:o,logger:a,remoteServerUrl:e,authToken:h,projectId:m.projectId,hasCode:!0===m.hasCode})
7512
+ await k.start(async()=>{console.log(""),console.log(`AlchemyLab keeps running ${b.default.basename(o)}. \`alchemy stop\` in this folder stops it.`),process.exit(0)})}
7513
+ const p=t(60134),f=t(89122),m=t(98074),g=t(62671),w=t(14139),y=t(79564),v=h(t(79896)),b=h(t(16928)),k=h(t(23785)),S=t(12782),I=t(11253),$=t(90629),j=t(42403),E=t(88918),A=t(18944),C=t(30030),_=t(52800),T=t(81480),x=t(56257),M=t(19071),R=t(64504),P=t(38778),O=t(35382),D=t(60134),q=t(71918),N=t(36968),F=t(76231),L=t(67518),U=t(99269),B=t(11773),H=(()=>{let e=__dirname
7518
7514
  for(let t=0;t<6;t++){try{if("alchemylab"===JSON.parse(v.default.readFileSync(b.default.join(e,"package.json"),"utf8"))?.name)return e}catch{}const t=b.default.dirname(e)
7519
7515
  if(t===e)break
7520
7516
  e=t}})()},30030(e,exports,t){"use strict"
@@ -7523,13 +7519,13 @@ if(t&&"string"==typeof t.remoteServerUrl&&/^https?:\/\//.test(t.remoteServerUrl)
7523
7519
  return e.opencodeDir?r.ALCHEMY_OPENCODE_DIR=e.opencodeDir:delete r.ALCHEMY_OPENCODE_DIR,r}
7524
7520
  const r=t(18944)},19071(e,exports,t){"use strict"
7525
7521
  async function r(e,t=3e5){return new Promise(r=>{const s=setInterval(async()=>{await e.hasValidToken()&&(clearInterval(s),r(!0))},2e3)
7526
- setTimeout(()=>{clearInterval(s),r(!1)},t)})}exports.waitForAuth=r,exports.ensureSignedIn=async function(e){let t=await e.getToken()
7527
- if(t?.token)return t.token
7528
- console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
7529
- const n=await e.startAuthenticationFlow()
7530
- console.log(`\u{1f310} ${n.authUrl}`)
7531
- const o=(0,s.openBrowser)(n.authUrl)
7532
- return console.log(""),console.log(o?"\u{1f446} Browser window opened. Complete login there.":"\u{1f446} Complete the login in a browser, then return here."),console.log("\u23f1\ufe0f Waiting for authentication..."),await r(e)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),t=await e.getToken(),console.log(""),t?.token},exports.startSignInWatchdog=function(e,t,n=3e5){let o=!1
7522
+ setTimeout(()=>{clearInterval(s),r(!1)},t)})}exports.waitForAuth=r,exports.ensureSignedIn=async function(e){let t=await e.getToken(),n=!1
7523
+ if(t?.token&&await e.signInRefused()&&(n=!0,await e.removeToken(),t=null),t?.token)return t.token
7524
+ console.log(""),console.log(n?"\u{1f511} The sign-in on this computer is no longer accepted. Opening browser to sign in again...":"\u{1f511} Not logged in. Opening browser to authenticate...")
7525
+ const o=await e.startAuthenticationFlow()
7526
+ console.log(`\u{1f310} ${o.authUrl}`)
7527
+ const i=(0,s.openBrowser)(o.authUrl)
7528
+ return console.log(""),console.log(i?"\u{1f446} Browser window opened. Complete login there.":"\u{1f446} Complete the login in a browser, then return here."),console.log("\u23f1\ufe0f Waiting for authentication..."),await r(e)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),t=await e.getToken(),console.log(""),t?.token},exports.startSignInWatchdog=function(e,t,n=3e5){let o=!1
7533
7529
  const i=setInterval(async()=>{if(!o)try{if(await e.hasValidToken())return
7534
7530
  o=!0,t("Sign-in expired; opening a browser to sign in again")
7535
7531
  const n=await e.startAuthenticationFlow();(0,s.openBrowser)(n.authUrl,`Sign in again: ${n.authUrl}`),t(await r(e)?"Signed in again":"Sign-in not completed; asking again in five minutes")}catch(e){t(`Sign-in check failed: ${e instanceof Error?e.message:String(e)}`)}finally{o=!1}},n)
@@ -7908,8 +7904,8 @@ for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
7908
7904
  t[v.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${k}_${n}`]=e}if(!K)for(const e of v.WEB_TOOLS)t[v.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${k}_${e}`]=!1
7909
7905
  Object.assign(t,(0,c.agentToolGrants)(B,e.name,H)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),W.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}W.agent.general={...W.agent.general,disable:!0},W.agent.explore={...W.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${k}_search`]:!0,[`${k}_read_files`]:!0,[`${k}_view_asset`]:!0,[`${k}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
7910
7906
  const e=this.buildExploreMarkdown(F)
7911
- this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(W.agent).length})}const V=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),z=(await this.fetchPluginManifest())["opencode-claude-auth"],Y=W.plugin,J=Array.isArray(Y)?Y.filter(e=>!V(e)):[]
7912
- z&&this.applyAuthPluginPin(J,z),J.length>0?W.plugin=J:delete W.plugin
7907
+ this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(W.agent).length})}const z=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),V=(await this.fetchPluginManifest())["opencode-claude-auth"],Y=W.plugin,J=Array.isArray(Y)?Y.filter(e=>!z(e)):[]
7908
+ V&&this.applyAuthPluginPin(J,V),J.length>0?W.plugin=J:delete W.plugin
7913
7909
  try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,d.existsSync)(y.join(s,e)))
7914
7910
  e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}W.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,u.join)(e,t)).find(e=>(0,d.existsSync)(e))??(0,u.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,u.isAbsolute)(t)?t:(0,u.join)(e,t))
7915
7911
  return[...new Set([r,...s])]}(s,C.instructions)
@@ -7919,8 +7915,8 @@ const Q={...C,...W}
7919
7915
  await w.mkdir(y.dirname(I),{recursive:!0}),await w.writeFile(I,JSON.stringify(Q,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:I})
7920
7916
  try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=y.join(e.homedir(),".alchemy_lab","opencode","config"),s=y.join(r,"opencode.json")
7921
7917
  let n=null
7922
- try{n=JSON.parse(await w.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!V(e))
7923
- z&&this.applyAuthPluginPin(a,z),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await w.mkdir(r,{recursive:!0}),await w.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(T&&!l)try{let e={}
7918
+ try{n=JSON.parse(await w.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!z(e))
7919
+ V&&this.applyAuthPluginPin(a,V),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await w.mkdir(r,{recursive:!0}),await w.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(T&&!l)try{let e={}
7924
7920
  try{const t=await w.readFile(x,"utf-8")
7925
7921
  e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=M,await w.writeFile(x,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:x}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:n}=await t.e(939).then(()=>t.t(49939,23)),o=await r(b??!1,this.logger,e)
7926
7922
  o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await n(s,this.logger)}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:I}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,n.getErrorMessage)(e)}),{success:!1,error:(0,n.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=v,v.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],v.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),v.rerouteLogged=new Set},6688(e,exports,t){"use strict"
@@ -10008,7 +10004,8 @@ if(t.status===a.PollingStatus.POLLING)return null
10008
10004
  const r=this.authFlow.getPollingUrl(e),s=await fetch(r)
10009
10005
  if(s.ok){const e=await s.json()
10010
10006
  if(e.authenticated&&e.token){const t=this.authFlow.parseTokenResponse(e)
10011
- if(t)return await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),t}}return null}catch(e){return this.logger.error("Error checking authentication status",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,null}}async validateTokenWithRemoteServer(){this.logger.debug("Validating stored token with remote server")
10007
+ if(t)return await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),t}}return null}catch(e){return this.logger.error("Error checking authentication status",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,null}}async signInRefused(){try{const e=await this.getToken()
10008
+ return!!e&&401===(await fetch(`${this.remoteServerUrl}/api/auth/validate`,{method:"GET",headers:{Authorization:`Bearer ${e.token}`},signal:AbortSignal.timeout(1e4)})).status}catch{return!1}}forgetCachedSignIn(){this.invalidateCache()}async validateTokenWithRemoteServer(){this.logger.debug("Validating stored token with remote server")
10012
10009
  try{const e=await this.getToken()
10013
10010
  if(!e)return this.logger.debug("No token available for validation"),!1
10014
10011
  const t=`${this.remoteServerUrl}/auth/validate`
@@ -10252,7 +10249,7 @@ o&&o(e)
10252
10249
  for(var a=0;a<n.length;a++)t[n[a]]=1})(s)}}})()
10253
10250
  let s={};(()=>{"use strict"
10254
10251
  function t(){return(0,i.ownVersion)()??"unknown"}function r(){const e=t()
10255
- console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start this folder's project and open its shell. The\n AlchemyLab service runs the project (the first run sets\n the service up), and leaving the shell leaves it running.\n service Set up the AlchemyLab service for this computer and start it\n ("alchemy service stop" stops it and its projects;\n "alchemy service install" starts it when you log in;\n "alchemy service uninstall" stops starting it at login;\n "alchemy service pair <code>" pairs this computer again,\n with the code Settings, Data in the app gives;\n "alchemy service computer-gone <name>" says another of your\n computers is gone, by its name in Settings, General, Computers\n (or its id), so its projects can move without a backup;\n "alchemy service new-computer" is for a computer that\n replaces another one: it brings that computer's words back\n from a backup, then moves its projects here)\n status Projects on this computer and their state\n start [folder] Start a project (default: this folder)\n stop [folder] Stop a project\n restart [folder]\n add [folder] Add a folder as a project without starting it\n remove [folder] Remove a project from this computer (files are untouched)\n approve Answer requests the app made that need you at this computer\n logout Log out and remove stored auth token\n help Show this help message\n\nOPTIONS:\n --port <n> Action server port (default: auto)\n --help, -h Show this help message\n --version, -v Show version information\n\nEXAMPLES:\n alchemy # Start server + OpenCode (default)\n alchemy logout # Log out\n\nNOTES:\n On startup, alchemy checks for Docker and offers to install or start it.\n Docker powers confined execution (sealed environments, experiments, test\n gates); without it, agents fall back to the host sandbox.\n\nFor more information, visit: https://alchemylabcode.com/\n`)}function n(e){try{(0,I.checkProjectIdentity)(e)}catch(e){if(e instanceof I.ProjectRefused){console.error("")
10252
+ console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start this folder's project and open its shell. The\n AlchemyLab service runs the project (the first run sets\n the service up), and leaving the shell leaves it running.\n service Set up the AlchemyLab service for this computer and start it\n ("alchemy service stop" stops it and its projects;\n "alchemy service install" starts it every time this computer starts;\n "alchemy service uninstall" stops that;\n "alchemy service pair <code>" pairs this computer again,\n with the code Settings, Data in the app gives;\n "alchemy service computer-gone <name>" says another of your\n computers is gone, by its name in Settings, General, Computers\n (or its id), so its projects can move without a backup;\n "alchemy service new-computer" is for a computer that\n replaces another one: it brings that computer's words back\n from a backup, then moves its projects here)\n status Projects on this computer and their state\n start [folder] Start a project (default: this folder)\n stop [folder] Stop a project\n restart [folder]\n add [folder] Add a folder as a project without starting it\n remove [folder] Remove a project from this computer (files are untouched)\n approve Answer requests the app made that need you at this computer\n logout Log out and remove stored auth token\n help Show this help message\n\nOPTIONS:\n --port <n> Action server port (default: auto)\n --help, -h Show this help message\n --version, -v Show version information\n\nEXAMPLES:\n alchemy # Start server + OpenCode (default)\n alchemy logout # Log out\n\nNOTES:\n On startup, alchemy checks for Docker and offers to install or start it.\n Docker powers confined execution (sealed environments, experiments, test\n gates); without it, agents fall back to the host sandbox.\n\nFor more information, visit: https://alchemylabcode.com/\n`)}function n(e){try{(0,I.checkProjectIdentity)(e)}catch(e){if(e instanceof I.ProjectRefused){console.error("")
10256
10253
  for(const t of e.lines)console.error(t)
10257
10254
  console.error(""),process.exit(1)}throw e}}function o(){return(0,a.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:process.cwd()})}let exports=s
10258
10255
  Object.defineProperty(exports,"__esModule",{value:!0})