alchemylab 3.0.5 → 3.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +153 -134
  2. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -3801,10 +3801,11 @@ if(!r||r.startsWith("..")||M.default.isAbsolute(r)||".alchemy_lab"===r.split(M.d
3801
3801
  const s=await te.note({sessionId:n,requestId:c.requestId,worktree:c.worktree,relPath:r.split(M.default.sep).join("/"),tool:i,..."read"===i&&e.lines?{readRange:e.lines}:{}},l)
3802
3802
  s&&d.push(a.length>1?`${r}:\n${s}`:s)}t.json({success:!0,data:{note:d.length>0?d.join("\n\n"):null}})}catch(e){o.logger.debug("parallel-note failed (the tool result stays as it is)",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:{note:null}})}}),e.get("/api/sibling-overlap",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
3803
3803
  if(!r||!/^req\d+$/.test(r))return void t.status(400).json({success:!1,error:"requestId required"})
3804
- const s=o.getProjectId(),n=(0,L.simpleGit)(o.projectPath),i=(await n.raw(["for-each-ref","--format=%(refname:short)","refs/heads/requests/"])).split("\n").map(e=>e.trim().replace(/^requests\//,"")).filter(e=>/^req\d+$/.test(e)&&e!==r),a=new Set(["open","in_progress","ready_for_review","completing","resolving_conflicts"]),c=[]
3805
- for(const e of i)try{const t=s?await o.apiClient.get(`/api/projects/${s}/requests/${e}`):null,r=t?.data??t
3806
- r&&a.has(String(r.status))&&c.push({id:e,title:String(r.title??""),status:String(r.status)})}catch{}const l=await(0,Y.resolveDefaultBranch)(o.projectPath),d=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:c.map(e=>e.id),defaultBranch:l,git:n}),u=new Map(c.map(e=>[e.id,e]))
3807
- t.json({success:!0,data:d.map(e=>({...e,siblingTitle:u.get(e.sibling)?.title??"",siblingStatus:u.get(e.sibling)?.status??""}))})}catch(e){o.logger.debug("sibling-overlap lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:[]})}}),e.get("/api/request-diff-stats",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
3804
+ const s=o.getProjectId(),n=(0,L.simpleGit)(o.projectPath),i=(await n.raw(["for-each-ref","--format=%(refname:short)","refs/heads/requests/"])).split("\n").map(e=>e.trim().replace(/^requests\//,"")).filter(e=>/^req\d+$/.test(e)&&e!==r),a=new Set(["open","in_progress","ready_for_review","completing","resolving_conflicts"]),c=new Set(i),l=[]
3805
+ if(s&&c.size>0){const e=await o.apiClient.get(`/api/projects/${s}/requests?inFlight=true&limit=100`),t=Array.isArray(e?.data)?e.data:[]
3806
+ for(const e of t){const t=String(e?.localId??"")
3807
+ c.has(t)&&a.has(String(e.status))&&l.push({id:t,title:String(e.title??""),status:String(e.status)})}}const d=await(0,Y.resolveDefaultBranch)(o.projectPath),u=await(0,_.siblingOverlap)({projectPath:o.projectPath,requestId:r,siblingIds:l.map(e=>e.id),defaultBranch:d,git:n}),h=new Map(l.map(e=>[e.id,e]))
3808
+ t.json({success:!0,data:u.map(e=>({...e,siblingTitle:h.get(e.sibling)?.title??"",siblingStatus:h.get(e.sibling)?.status??""}))})}catch(e){o.logger.debug("sibling-overlap lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:[]})}}),e.get("/api/request-diff-stats",async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0
3808
3809
  if(!r)return void t.status(400).json({success:!1,error:"requestId required"})
3809
3810
  const s=(0,L.simpleGit)(o.projectPath),n=await(0,F.resolveRequestDiffRange)(s,r,{status:"in_progress"}),i=n.headSha,a="string"==typeof e.query.since&&/^[0-9a-f]{7,40}$/.test(e.query.since)?e.query.since:"",c=a&&i&&await s.raw(["merge-base","--is-ancestor",a,i]).then(()=>!0,()=>!1)?a:n.baseSha,l=c&&i?await(0,F.computeDiffStats)(s,c,i):void 0,d=await(0,Y.resolveDefaultBranch)(o.projectPath).catch(()=>{})
3810
3811
  t.json({success:!0,data:l?{...l,baseSha:c,headSha:i,...d?{branch:d}:{}}:null})}catch(e){o.logger.debug("request-diff-stats lookup failed",{error:(0,P.getErrorMessage)(e)}),t.json({success:!0,data:null})}}),e.post("/api/verify-preflight",async(e,t)=>{try{const{requestId:r,commands:s}=e.body??{},n=Array.isArray(s)?s.filter(e=>"string"==typeof e&&!!e.trim()):[]
@@ -4968,7 +4969,10 @@ try{const t=Date.now()-this.pendingConfigReloadSince
4968
4969
  if(t<ht.RELOAD_DEFER_CAP_MS){if(this.activeMcpCalls.size>0)return
4969
4970
  if(await this.anySessionBusy())return}else this.logger.warn("Config reload deferred past cap; firing despite activity (staleness beats permanent deferral)",{trigger:e,waitedMs:t})
4970
4971
  this.pendingConfigReload=!1,this.stopPendingReloadRecheck(),this.logger.info("Firing deferred OpenCode config reload",{trigger:e,waitedMs:t}),await this.reloadOpenCodeConfigNow(`deferred-${e}`)}finally{this.deferredReloadCheckInFlight=!1}}}else this.stopPendingReloadRecheck()}async applyClaudeThinking(e){const t=this.claudeThinkingSeen
4971
- this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,H.getErrorMessage)(e)})))}async applyPause(e){const t=this.getOpenCodeUrl()?.replace(/\/$/,"")
4972
+ this.claudeThinkingSeen=e,void 0!==t&&t!==e&&this.projectId&&(this.logger.info("Claude thinking setting changed; re-syncing agents",{from:t,to:e}),await this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Agent re-sync after the Claude thinking change failed",{error:(0,H.getErrorMessage)(e)})))}async applyAnthropicCompaction(e){const t=(0,je.generatedOpencodeConfigPath)(this.options.projectPath)
4973
+ try{const r=JSON.parse(await D.default.promises.readFile(t,"utf8"))
4974
+ if(!r||"object"!=typeof r||!(0,B.withAnthropicCompaction)(r,e))return
4975
+ await D.default.promises.writeFile(t,JSON.stringify(r,null,2),"utf8"),this.logger.info("Claude's own compaction setting changed; reloading the serve's config",{on:e}),await this.requestConfigReload("anthropic-compaction")}catch(e){this.logger.debug("Claude's own compaction setting not applied yet",{error:(0,H.getErrorMessage)(e)})}}async applyPause(e){const t=this.getOpenCodeUrl()?.replace(/\/$/,"")
4972
4976
  if(t&&(this.pauseApplied?.url!==t||this.pauseApplied.paused!==e))try{const r=await fetch(`${t}/config/pause`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paused:e}),signal:AbortSignal.timeout(15e3)})
4973
4977
  if(!r.ok)return void this.logger.warn("The serve did not take the pause",{paused:e,status:r.status})
4974
4978
  const s=await r.json().catch(()=>({}))
@@ -5303,8 +5307,8 @@ r&&(t["X-Main-Sha"]=r)
5303
5307
  const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
5304
5308
  if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
5305
5309
  const n=await s.json(),o={success:!0,data:n.data||n}
5306
- if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,defaultModelUnchosen:s}=o.data.settings
5307
- "boolean"==typeof s&&(this.defaultModelUnchosen=s),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
5310
+ if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,anthropicCompaction:s,defaultModelUnchosen:n}=o.data.settings
5311
+ "boolean"==typeof n&&(this.defaultModelUnchosen=n),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r),"boolean"==typeof s&&await this.applyAnthropicCompaction(s)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
5308
5312
  this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,H.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
5309
5313
  if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
5310
5314
  this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
@@ -5802,30 +5806,30 @@ if("number"!=typeof t?.context)continue
5802
5806
  const o={context:t.context,..."number"==typeof t.input?{input:t.input}:{},..."number"==typeof t.output?{output:t.output}:{}}
5803
5807
  n.set(`${e.id}/${r}`,o),s?.id&&s.id!==r&&n.set(`${e.id}/${s.id}`,o)}}r.set(e,{at:Date.now(),value:n})}catch{}return n}
5804
5808
  const t=new Map,r=new Map},58836(e,exports,t){"use strict"
5805
- function r(e){return"alchemist"===e?1e5:6e4}exports.startSessionCompactor=function(e){let t,i,a=0
5806
- const c=r=>e.apiClient.post("/api/liveness/report",{name:"session-compactor",project:e.projectName,description:`Compacts the hub session idle past its provider's cache window (~60-90 min, per-provider heuristic) with >${Math.round(60)}k context -> summarize so reuse is cheap`,intervalMs:6e5,lastRunAt:(new Date).toISOString(),runs:a,lastAction:t,lastActionAt:i,...r}).catch(()=>{}),l=async()=>{if((0,o.isProjectPaused)())return
5807
- const l=Date.now()
5808
- a++
5809
+ function r(e,t){const r=e?.info??e??{}
5810
+ if((0,n.isCompactionSummary)(r))return{compact:!1,why:"already compacted"}
5811
+ const s=(0,n.contextTokensOf)(r)
5812
+ return s<("alchemist"===t.agentType?1e5:6e4)?{compact:!1,why:"small"}:r.time&&!r.time.completed?{compact:!1,why:"mid-turn"}:r.providerID&&r.modelID?t.idleMs<(0,n.cacheRetentionMsFor)(r.providerID)?{compact:!1,why:"cache plausibly still warm"}:{compact:!0,ctx:s}:{compact:!1,why:"no model"}}exports.startSessionCompactor=function(e){let t,n,i=0
5813
+ const a=r=>e.apiClient.post("/api/liveness/report",{name:"session-compactor",project:e.projectName,description:`Compacts the hub session idle past its provider's cache window (~60-90 min, per-provider heuristic) with >${Math.round(60)}k context -> summarize so reuse is cheap`,intervalMs:6e5,lastRunAt:(new Date).toISOString(),runs:i,lastAction:t,lastActionAt:n,...r}).catch(()=>{}),c=async()=>{if((0,o.isProjectPaused)())return
5814
+ const c=Date.now()
5815
+ i++
5809
5816
  try{const o=e.getOpenCodeUrl()
5810
- if(!o||!e.projectId)return void await c({lastDurationMs:Date.now()-l})
5811
- const a=await(0,s.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/cli-sessions?status=active&limit=100&offset=${t}`),d=new Map
5812
- for(const e of a){const t=e.externalSessionId||e.external_session_id,r=e.agentType||e.agent_type
5813
- t&&"alchemist"===r&&"active"===(e.status??"active")&&d.set(t,{title:e.title||t,agentType:r})}if(0===d.size)return void await c({lastDurationMs:Date.now()-l})
5814
- const u=await fetch(`${o}/session?limit=1000`,{signal:AbortSignal.timeout(1e4)}),h=await u.json().catch(()=>[]),p=Date.now()
5815
- for(const s of Array.isArray(h)?h:[]){const a=d.get(s.id)
5816
- if(!a)continue
5817
- const c=a.title,l=s.time?.updated??s.time?.created??0
5818
- if(p-l<27e5)continue
5819
- let u
5817
+ if(!o||!e.projectId)return void await a({lastDurationMs:Date.now()-c})
5818
+ const i=await(0,s.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/cli-sessions?status=active&limit=100&offset=${t}`),l=new Map
5819
+ for(const e of i){const t=e.externalSessionId||e.external_session_id,r=e.agentType||e.agent_type
5820
+ t&&"alchemist"===r&&"active"===(e.status??"active")&&l.set(t,{title:e.title||t,agentType:r})}if(0===l.size)return void await a({lastDurationMs:Date.now()-c})
5821
+ const d=await fetch(`${o}/session?limit=1000`,{signal:AbortSignal.timeout(1e4)}),u=await d.json().catch(()=>[]),h=Date.now()
5822
+ for(const s of Array.isArray(u)?u:[]){const i=l.get(s.id)
5823
+ if(!i)continue
5824
+ const a=i.title,c=s.time?.updated??s.time?.created??0
5825
+ if(h-c<27e5)continue
5826
+ let d
5820
5827
  try{const e=await fetch(`${o}/session/${s.id}/message?limit=1`,{signal:AbortSignal.timeout(1e4)}),t=await e.json()
5821
- u=Array.isArray(t)?t[t.length-1]:void 0}catch{continue}const h=u?.info??u??{},f=(0,n.contextTokensOf)(h)
5822
- if(f<r(a.agentType))continue
5823
- if(h.time&&!h.time.completed)continue
5824
- if(!h.providerID||!h.modelID)continue
5825
- if(p-l<(0,n.cacheRetentionMsFor)(h.providerID))continue
5826
- const m=await fetch(`${o}/session/${s.id}/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerID:h.providerID,modelID:h.modelID}),signal:AbortSignal.timeout(12e4)})
5827
- m.ok?(t=`compacted ${a.agentType} ${c} (${Math.round(f/1e3)}k ctx, idle ${Math.round((p-l)/6e4)}m)`,i=(new Date).toISOString(),e.logger.info("Session compactor: compacted an idle session",{sessionId:s.id,title:c,ctx:f})):e.logger.warn("Session compactor: summarize failed",{sessionId:s.id,status:m.status})}await c({lastDurationMs:Date.now()-l})}catch(e){await c({lastError:String(e).slice(0,200),lastDurationMs:Date.now()-l})}}
5828
- setInterval(l,6e5),l()}
5828
+ d=Array.isArray(t)?t[t.length-1]:void 0}catch{continue}const u=d?.info??d??{},p=r(d,{agentType:i.agentType,idleMs:h-c})
5829
+ if(!p.compact)continue
5830
+ const f=p.ctx,m=await fetch(`${o}/session/${s.id}/summarize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({providerID:u.providerID,modelID:u.modelID}),signal:AbortSignal.timeout(12e4)})
5831
+ m.ok?(t=`compacted ${i.agentType} ${a} (${Math.round(f/1e3)}k ctx, idle ${Math.round((h-c)/6e4)}m)`,n=(new Date).toISOString(),e.logger.info("Session compactor: compacted an idle session",{sessionId:s.id,title:a,ctx:f})):e.logger.warn("Session compactor: summarize failed",{sessionId:s.id,status:m.status})}await a({lastDurationMs:Date.now()-c})}catch(e){await a({lastError:String(e).slice(0,200),lastDurationMs:Date.now()-c})}}
5832
+ setInterval(c,6e5),c()}
5829
5833
  const s=t(5641),n=t(89063),o=t(36799)},67078(e,exports){"use strict"
5830
5834
  function t(e,t){let s=r.get(e)
5831
5835
  return s||(s=new Set,r.set(e,s)),s.add(t),()=>{const s=r.get(e)
@@ -6745,23 +6749,35 @@ else{const r=await(0,i.resolveDefaultBranch)(e)
6745
6749
  await u.raw(["worktree","add","-b",c,d,r]),a.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:c,mainBranch:r,worktreePath:d})}}catch(e){const r=e instanceof Error?e.message:String(e)
6746
6750
  a.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
6747
6751
  const r=t(79896),s=t(16928),n=t(83807),o=t(7202),i=t(52232)},35419(e,exports,t){"use strict"
6748
- function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){return{ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
6752
+ function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){const n=process.env.ALCHEMYLAB_ANTHROPIC_COMPACTION
6753
+ return{...void 0===n?{}:{ALCHEMYLAB_ANTHROPIC_COMPACTION:n},ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
6749
6754
  const n=t(70857),o=t(16928),i=t(14139)
6750
6755
  exports.t="2.1.280"},39068(e,exports,t){"use strict"
6751
6756
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
6752
6757
  if(e)return e
6753
6758
  try{const e=("undefined"!=typeof require?require:t(54245)).resolve("alchemylab-opencode/package.json")
6754
- return(0,n.dirname)(e)}catch{}}function s(e,t){return{command:function(){if(c)return c
6755
- try{const e=(0,i.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
6756
- if((0,a.isSupportedNode)(e))return c="node",c}catch{}const e=process.env.NVM_DIR||(0,n.join)(process.env.HOME||"",".nvm"),t=(0,n.join)(e,"versions","node")
6757
- if((0,o.existsSync)(t))try{const e=(0,o.readdirSync)(t).filter(e=>(0,a.isSupportedNode)(e)).sort((e,t)=>{const r=e.replace("v","").split(".").map(Number),s=t.replace("v","").split(".").map(Number)
6759
+ return(0,a.dirname)(e)}catch{}}function s(){if(u)return u
6760
+ try{const e=(0,l.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
6761
+ if((0,d.isSupportedNode)(e))return u="node",u}catch{}const e=process.env.NVM_DIR||(0,a.join)(process.env.HOME||"",".nvm"),t=(0,a.join)(e,"versions","node")
6762
+ if((0,c.existsSync)(t))try{const e=(0,c.readdirSync)(t).filter(e=>(0,d.isSupportedNode)(e)).sort((e,t)=>{const r=e.replace("v","").split(".").map(Number),s=t.replace("v","").split(".").map(Number)
6758
6763
  return s[0]-r[0]||s[1]-r[1]||s[2]-r[2]})
6759
- if(e.length>0){const r=(0,n.join)(t,e[0],"bin","node")
6760
- if((0,o.existsSync)(r))return c=r,c}}catch{}throw new Error((0,a.unsupportedNodeMessage)(process.version))}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}exports.findOpencodeDir=r,exports.resolveServeCommand=function(){const e=r()
6761
- return e?s((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
6762
- return e?s((0,n.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
6763
- const n=t(16928),o=t(79896),i=t(35317),a=t(84973)
6764
- let c},69706(e,exports,t){"use strict"
6764
+ if(e.length>0){const r=(0,a.join)(t,e[0],"bin","node")
6765
+ if((0,c.existsSync)(r))return u=r,u}}catch{}throw new Error((0,d.unsupportedNodeMessage)(process.version))}function n(e,t){return{command:s(),prependArgs:["--no-warnings",(0,a.join)(e,t)]}}function o(e,t,r){const s=(0,l.spawnSync)(e,[t],{cwd:r,encoding:"utf8",stdio:["ignore","pipe","pipe"],timeout:12e4}),n=`${s.stdout??""}${s.stderr??""}`
6766
+ if(s.error||0!==s.status)throw Object.assign(new Error(s.error?.message??`exit ${s.status}`),{stderr:n})
6767
+ return n}function i(e,t=o){if(!e||process.env.ALCHEMY_OPENCODE_DIR)return"not-needed"
6768
+ if(h?.dir===e)return h.state
6769
+ const r=(0,a.join)(e,"postinstall.cjs"),n=(t,r="")=>(h={dir:e,state:t,detail:r},t)
6770
+ if(!(0,c.existsSync)(r)||!(0,c.existsSync)((0,a.join)(e,"patches")))return n("not-needed")
6771
+ let i=""
6772
+ try{i=String(JSON.parse((0,c.readFileSync)((0,a.join)(e,"package.json"),"utf8")).version??"")}catch{}const l=(0,a.join)(e,exports.BY)
6773
+ try{if(i&&(0,c.readFileSync)(l,"utf8")===i)return n("done")}catch{}try{const o=t(s(),r,e)
6774
+ return i&&(0,c.writeFileSync)(l,i),n("applied",String(o??"").trim())}catch(t){const r=String(t?.stderr||t?.message||t).trim().split("\n").slice(-3).join(" ")
6775
+ return process.stderr.write(`AlchemyLab could not apply its engine's dependency patches in ${e} (${r}); the engine starts without them.\n`),n("failed",r)}}exports.BY=void 0,exports.findOpencodeDir=r,exports.lastEnginePatchState=function(){return h},exports.resolveServeCommand=function(){const e=r()
6776
+ return e?(i(e),n((0,a.join)(e,"dist"),"serve.js")):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
6777
+ return e?(i(e),n((0,a.join)(e,"dist"),"run.js")):{command:"opencode",prependArgs:[]}}
6778
+ const a=t(16928),c=t(79896),l=t(35317),d=t(84973)
6779
+ let u,h
6780
+ exports.BY=".alchemylab-patches-applied"},69706(e,exports,t){"use strict"
6765
6781
  function r(e){try{const t=JSON.parse(a.default.readFileSync(c.default.join(e,l),"utf8"))
6766
6782
  return"string"==typeof t.password&&t.password?t.password:null}catch{return null}}function s(e){const t=process.env.OPENCODE_SERVER_USERNAME||exports.SERVE_USERNAME
6767
6783
  return`Basic ${Buffer.from(`${t}:${e}`).toString("base64")}`}function n(e){if(!d.port)return!1
@@ -7781,17 +7797,20 @@ const d=Number(s.prepare("SELECT v FROM meta WHERE k = 'generation'").get()?.v??
7781
7797
  i.prepare("UPDATE meta SET v = max(v, ?) WHERE k = 'generation'").run(d),i.exec("COMMIT")}finally{s.close()}}}finally{i.close()}if(t===p.items+e)return{projectId:s,restored:!1,unchanged:!0,items:t,keptFromLive:e}
7782
7798
  if(n.default.chmodSync(u,384),n.default.existsSync(d)){n.default.renameSync(d,`${d}.before-restore-${a}`)
7783
7799
  for(const e of["-wal","-shm"])n.default.existsSync(d+e)&&n.default.renameSync(d+e,`${d}.before-restore-${a}${e}`)}return n.default.renameSync(u,d),{projectId:s,restored:!0,items:p.items+e,keptFromLive:e}}finally{o.unhold(s),n.default.rmSync(u,{force:!0})}}}},18984(e,exports,t){"use strict"
7784
- function r(e){return"summary"===e?.claudeThinking?"summary":"omitted"}exports.MCPClientConfigurer=exports.Vr=exports.Es=exports.R1=exports.NK=void 0,exports.claudeThinkingOf=r
7785
- const s=t(75632),n=t(58824),o=t(89796),i=t(95671),a=t(43218),c=t(49779),l=t(11773),d=t(79896),u=t(16928),h=t(35317),p=t(91943),f=t(39023),m=t(35419)
7800
+ function r(e){return"summary"===e?.claudeThinking?"summary":"omitted"}function s(e,t){const r=void 0!==e.compaction?.anthropic
7801
+ if(t?!0===e.compaction?.anthropic:!r)return!1
7802
+ const{anthropic:s,...n}=e.compaction??{}
7803
+ return t?e.compaction={...n,anthropic:!0}:Object.keys(n).length>0?e.compaction=n:delete e.compaction,!0}exports.MCPClientConfigurer=exports.Vr=exports.Es=exports.R1=exports.NK=void 0,exports.claudeThinkingOf=r,exports.withAnthropicCompaction=s
7804
+ const n=t(75632),o=t(58824),i=t(89796),a=t(95671),c=t(43218),l=t(49779),d=t(11773),u=t(79896),h=t(16928),p=t(35317),f=t(91943),m=t(39023),g=t(35419)
7786
7805
  exports.NK="medium"
7787
- const g=new Set(["low","medium","high","xhigh","max"])
7788
- let w=exports.NK,y=[]
7806
+ const w=new Set(["low","medium","high","xhigh","max"])
7807
+ let y=exports.NK,v=[]
7789
7808
  exports.R1=new Set(["$schema","model","mcp","agent","mode","permission","plugin","tools","small_model","share","autoshare"]),exports.Es=new Set(["theme","keybinds","tui","layout","username","watcher","instructions"]),exports.Vr=new Set(["reviewer","acceptance-judge","command-judge","guidance-judge","intake-reviewer"])
7790
- class v{constructor(e,t,r){this.logger=e,this.agentMarkdowns=new Map,this.pinnedAgents=new Set,this.connectedProviders=null,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
7809
+ class b{constructor(e,t,r){this.logger=e,this.agentMarkdowns=new Map,this.pinnedAgents=new Set,this.connectedProviders=null,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
7791
7810
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot initialize agents"),!1
7792
7811
  try{const r=`${t}/api/agents/seed`
7793
7812
  this.logger.info("Ensuring built-in agents exist",{url:r})
7794
- const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,l.machineCallerHeaders)(t)}
7813
+ const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,d.machineCallerHeaders)(t)}
7795
7814
  this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
7796
7815
  const n=await fetch(r,{method:"POST",headers:s})
7797
7816
  n.ok||this.logger.warn("Failed to seed global agents",{status:n.status,statusText:n.statusText})
@@ -7800,16 +7819,16 @@ this.logger.info("Initializing project agent activations",{url:o})
7800
7819
  const i=await fetch(o,{method:"POST",headers:s})
7801
7820
  if(!i.ok)return this.logger.warn("Failed to initialize project agents",{status:i.status,statusText:i.statusText}),!1
7802
7821
  const a=await i.json()
7803
- return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,n.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
7822
+ return this.logger.info("Initialized project agent activations",{initialized:a.data?.initialized||0}),!0}catch(e){return this.logger.warn("Error initializing agents from remote",{error:(0,o.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
7804
7823
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, using baseline plugin manifest"),{...e}
7805
7824
  const r=`${t}/api/distribution/plugin-versions`
7806
- try{const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,l.machineCallerHeaders)(t)}
7825
+ try{const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service",...(0,d.machineCallerHeaders)(t)}
7807
7826
  this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
7808
7827
  const n=await fetch(r,{method:"GET",headers:s})
7809
7828
  if(!n.ok)return this.logger.warn("Plugin manifest fetch returned non-OK; using baseline",{status:n.status}),{...e}
7810
7829
  const o=await n.json(),i=o?.data?.plugins??o?.plugins??{},a={...e}
7811
7830
  for(const[e,t]of Object.entries(i)){const r=t?.version
7812
- "string"==typeof r&&r.trim()&&(a[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:a}),a}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,n.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(()=>t.t(91943,23)),s=await Promise.resolve().then(()=>t.t(16928,23)),n=(await Promise.resolve().then(()=>t.t(70857,23))).homedir(),o=new Set
7831
+ "string"==typeof r&&r.trim()&&(a[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:a}),a}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,o.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(()=>t.t(91943,23)),s=await Promise.resolve().then(()=>t.t(16928,23)),n=(await Promise.resolve().then(()=>t.t(70857,23))).homedir(),o=new Set
7813
7832
  let i=e
7814
7833
  for(let e=0;e<6;e++){o.add(s.join(i,".opencode","plugin","anthropic-auth.js"))
7815
7834
  const e=s.dirname(i)
@@ -7832,17 +7851,17 @@ this.authToken&&(s.Authorization=`Bearer ${this.authToken}`)
7832
7851
  const n=await fetch(r,{method:"GET",headers:s})
7833
7852
  if(!n.ok)return this.logger.warn("Failed to fetch agents from remote",{status:n.status,statusText:n.statusText}),null
7834
7853
  const o=await n.json(),i=o.data||o
7835
- return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,n.getErrorMessage)(e)}),null}}async fetchDefaultWorkflowFromRemote(e){const t=this.remoteServerUrl
7854
+ return this.logger.info("Fetched agents from remote",{count:i.length}),i}catch(e){return this.logger.warn("Error fetching agents from remote",{error:(0,o.getErrorMessage)(e)}),null}}async fetchDefaultWorkflowFromRemote(e){const t=this.remoteServerUrl
7836
7855
  if(!t)return null
7837
7856
  try{const r=await fetch(`${t}/api/projects/${e}/default-workflow`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7838
7857
  if(!r.ok)return this.logger.warn("Failed to fetch default workflow from remote",{projectId:e,status:r.status}),null
7839
7858
  const s=await r.json()
7840
7859
  return s.data||s||null}catch(t){return this.logger.warn("Error fetching default workflow from remote",{projectId:e,error:t instanceof Error?t.message:String(t)}),null}}async fetchMcpServersFromRemote(e){const t=this.remoteServerUrl
7841
- if(!t)return y
7860
+ if(!t)return v
7842
7861
  try{const r=await fetch(`${t}/api/projects/${e}/mcp-servers`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7843
- if(!r.ok)return this.logger.warn("Failed to fetch MCP connections; keeping the last known denies",{projectId:e,status:r.status,known:y.length}),y
7862
+ if(!r.ok)return this.logger.warn("Failed to fetch MCP connections; keeping the last known denies",{projectId:e,status:r.status,known:v.length}),v
7844
7863
  const s=await r.json(),n=s.data||s||[]
7845
- return Array.isArray(n)?(y=n,n):y}catch(t){return this.logger.warn("Error fetching MCP connections; keeping the last known denies",{projectId:e,error:t instanceof Error?t.message:String(t),known:y.length}),y}}async fetchOwnerGuidanceFromRemote(e){const t=this.remoteServerUrl
7864
+ return Array.isArray(n)?(v=n,n):v}catch(t){return this.logger.warn("Error fetching MCP connections; keeping the last known denies",{projectId:e,error:t instanceof Error?t.message:String(t),known:v.length}),v}}async fetchOwnerGuidanceFromRemote(e){const t=this.remoteServerUrl
7846
7865
  if(!t)return[]
7847
7866
  try{const r=await fetch(`${t}/api/projects/${e}/owner-guidance`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7848
7867
  if(!r.ok)return[]
@@ -7854,97 +7873,97 @@ if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch project f
7854
7873
  try{const r=await fetch(`${t}/api/projects/${e}`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:`Bearer ${this.authToken}`}:{}}})
7855
7874
  if(!r.ok)return this.logger.warn("Failed to fetch project from remote",{projectId:e,status:r.status,statusText:r.statusText}),null
7856
7875
  const s=await r.json()
7857
- return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,n.getErrorMessage)(t)}),null}}getAgentMarkdowns(){return[...this.agentMarkdowns].map(([e,t])=>({name:e,markdown:t}))}setAgentMarkdown(e,t){this.agentMarkdowns.set(e,t)}agentModelOf(e){const t=this.agentMarkdowns.get(e),r=t?/^model:\s*"([^"\n]+)"\s*$/m.exec(t):null
7876
+ return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,o.getErrorMessage)(t)}),null}}getAgentMarkdowns(){return[...this.agentMarkdowns].map(([e,t])=>({name:e,markdown:t}))}setAgentMarkdown(e,t){this.agentMarkdowns.set(e,t)}agentModelOf(e){const t=this.agentMarkdowns.get(e),r=t?/^model:\s*"([^"\n]+)"\s*$/m.exec(t):null
7858
7877
  return r&&r[1].includes("/")?r[1]:null}agentPinnedOf(e){return this.pinnedAgents.has(e)}clearAgentMarkdowns(){this.agentMarkdowns.clear()}async removeOnDiskAgentFiles(e,r=[]){const s=await Promise.resolve().then(()=>t.t(91943,23)),n=await Promise.resolve().then(()=>t.t(16928,23)),o=new Set(r.map(e=>`${e}.md`)),i=(new Date).toISOString().slice(0,10)
7859
7878
  try{for(const t of await s.readdir(e)){if(!t.endsWith(".md"))continue
7860
7879
  const r=n.join(e,t)
7861
7880
  let a=""
7862
7881
  try{a=(await s.readFile(r,"utf8")).slice(0,400)}catch{}if(o.has(t)&&/^---[\s\S]*\bmode:\s*(primary|subagent|all)\b/m.test(a)){await s.unlink(r)
7863
7882
  continue}const c=`${r}.removed-by-alchemy-${i}`
7864
- try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&g.has(t.effort)?t.effort:exports.NK
7865
- r!==w&&this.logger.info("Effort dial read from the workflow",{effort:r,was:w}),w=r}catch{}return w}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,a.fetchConnectedProviders)(e)
7883
+ try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&w.has(t.effort)?t.effort:exports.NK
7884
+ r!==y&&this.logger.info("Effort dial read from the workflow",{effort:r,was:y}),y=r}catch{}return y}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,c.fetchConnectedProviders)(e)
7866
7885
  if(!t)return!1
7867
7886
  const r=JSON.stringify(t)!==JSON.stringify(this.connectedProviders)
7868
- return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,n,o={}){let c=r
7869
- if(e.model?.providerId&&e.model?.modelId?this.pinnedAgents.add(e.name):this.pinnedAgents.delete(e.name),e.model?.providerId&&e.model?.modelId)c=`${e.model.providerId}/${e.model.modelId}`
7870
- else if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,i.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0},o.limits)
7871
- t.resolved&&(c=t.fullModel)}const l=(0,a.runnableModel)(c,this.connectedProviders)
7872
- if(l&&l!==c){const t=`${e.name}:${c}->${l}`
7873
- v.rerouteLogged.has(t)||(v.rerouteLogged.add(t),this.logger.warn("agent model provider not connected; the agent runs on a connected provider's default until it is",{agent:e.name,configured:c,running:l})),c=l}const d=[...Object.entries(e.tools||{}).map(([e,r])=>{const s=e.replace(/-/g,"_")
7874
- return` ${v.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`}),...Object.entries(o.extraTools||{}).map(([e,t])=>` ${e}: ${t}`)].join("\n")
7875
- let u=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${c}"\n`
7876
- u+=`variant: "${e.variant||o.effort||w}"\n`
7887
+ return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,s,o={}){let i=r
7888
+ if(e.model?.providerId&&e.model?.modelId?this.pinnedAgents.add(e.name):this.pinnedAgents.delete(e.name),e.model?.providerId&&e.model?.modelId)i=`${e.model.providerId}/${e.model.modelId}`
7889
+ else if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,a.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0},o.limits)
7890
+ t.resolved&&(i=t.fullModel)}const l=(0,c.runnableModel)(i,this.connectedProviders)
7891
+ if(l&&l!==i){const t=`${e.name}:${i}->${l}`
7892
+ b.rerouteLogged.has(t)||(b.rerouteLogged.add(t),this.logger.warn("agent model provider not connected; the agent runs on a connected provider's default until it is",{agent:e.name,configured:i,running:l})),i=l}const d=[...Object.entries(e.tools||{}).map(([e,r])=>{const s=e.replace(/-/g,"_")
7893
+ return` ${b.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`}),...Object.entries(o.extraTools||{}).map(([e,t])=>` ${e}: ${t}`)].join("\n")
7894
+ let u=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${i}"\n`
7895
+ u+=`variant: "${e.variant||o.effort||y}"\n`
7877
7896
  const h=[]
7878
- "summary"===o.claudeThinking&&c.startsWith("anthropic/")&&h.push(' thinking:\n display: "summarized"'),exports.Vr.has(e.name)&&c.startsWith("anthropic/")&&h.push(' cacheTtl: "5m"')
7879
- const p=(0,s.toolScopesFor)(e.name,t,e.tools||{})
7897
+ "summary"===o.claudeThinking&&i.startsWith("anthropic/")&&h.push(' thinking:\n display: "summarized"'),exports.Vr.has(e.name)&&i.startsWith("anthropic/")&&h.push(' cacheTtl: "5m"')
7898
+ const p=(0,n.toolScopesFor)(e.name,t,e.tools||{})
7880
7899
  if(Object.keys(p).length>0&&h.push(` toolScopes: ${JSON.stringify(p)}`),h.length>0&&(u+=`options:\n${h.join("\n")}\n`),e.color&&(u+=`color: "${e.color}"\n`),u+=`tools:\n${d}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){u+="permission:\n"
7881
7900
  for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){u+=` ${t}:\n`
7882
- for(const[e,t]of Object.entries(r))u+=` "${e}": ${t}\n`}else u+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(u+=`temperature: ${e.temperature}\n`),"number"==typeof e.maxSteps&&Number.isInteger(e.maxSteps)&&e.maxSteps>0&&(u+=`steps: ${e.maxSteps}\n`),u+="---\n\n",n&&(u+=n.trim()+"\n\n"),e.prompt&&(u+=e.prompt),e.promptAddition&&(u+="\n\n"+e.promptAddition),u}async configureOpenCode(e,s,a="node",l,g){try{const w=await Promise.resolve().then(()=>t.t(91943,23)),y=await Promise.resolve().then(()=>t.t(16928,23)),b=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),k="alchemy",S=y.join(e,"mcp-direct.js")
7883
- this.logger.info("Configuring OpenCode",{isDevMode:b,serverName:k,scriptPath:S,projectPath:s}),await(0,o.ensureAlchemyIgnoreEntries)(s,this.logger),await this.sweepLegacyAuthPluginFiles(s)
7884
- const I=(0,m.generatedOpencodeConfigPath)(s),$=y.join(s,"opencode.json")
7885
- let j=null,E=null,A=null
7886
- const C={}
7887
- let _=null,T=!1
7888
- const x=y.join(s,".alchemy_lab","config.json")
7889
- if(!l)try{const e=await w.readFile(x,"utf-8"),t=JSON.parse(e)
7890
- t?.openCodeTools?(j=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:j})):T=!0,t?.openCodeModel&&(E=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),T=!0}{let e=null
7891
- try{e=JSON.parse(await w.readFile(I,"utf-8"))}catch{}const t=await async function(e,t){const r=(0,u.join)(e,"opencode.json")
7901
+ for(const[e,t]of Object.entries(r))u+=` "${e}": ${t}\n`}else u+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(u+=`temperature: ${e.temperature}\n`),"number"==typeof e.maxSteps&&Number.isInteger(e.maxSteps)&&e.maxSteps>0&&(u+=`steps: ${e.maxSteps}\n`),u+="---\n\n",s&&(u+=s.trim()+"\n\n"),e.prompt&&(u+=e.prompt),e.promptAddition&&(u+="\n\n"+e.promptAddition),u}async configureOpenCode(e,n,c="node",d,w){try{const v=await Promise.resolve().then(()=>t.t(91943,23)),k=await Promise.resolve().then(()=>t.t(16928,23)),S=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),I="alchemy",$=k.join(e,"mcp-direct.js")
7902
+ this.logger.info("Configuring OpenCode",{isDevMode:S,serverName:I,scriptPath:$,projectPath:n}),await(0,i.ensureAlchemyIgnoreEntries)(n,this.logger),await this.sweepLegacyAuthPluginFiles(n)
7903
+ const j=(0,g.generatedOpencodeConfigPath)(n),E=k.join(n,"opencode.json")
7904
+ let A=null,C=null,_=null
7905
+ const T={}
7906
+ let x=null,M=!1
7907
+ const R=k.join(n,".alchemy_lab","config.json")
7908
+ if(!d)try{const e=await v.readFile(R,"utf-8"),t=JSON.parse(e)
7909
+ t?.openCodeTools?(A=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:A})):M=!0,t?.openCodeModel&&(C=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),M=!0}{let e=null
7910
+ try{e=JSON.parse(await v.readFile(j,"utf-8"))}catch{}const t=await async function(e,t){const r=(0,h.join)(e,"opencode.json")
7892
7911
  let s
7893
- try{s=JSON.parse(await(0,p.readFile)(r,"utf8"))}catch{return null}if(!s||"object"!=typeof s||Array.isArray(s)||!s.mcp||"object"!=typeof s.mcp||!("alchemy"in s.mcp))return null
7894
- try{return await(0,f.promisify)(h.execFile)("git",["ls-files","--error-unmatch","--","opencode.json"],{cwd:e}),null}catch{}const o=Object.fromEntries(Object.entries(s).filter(([e])=>!exports.R1.has(e)&&"instructions"!==e&&"skills"!==e))
7895
- try{0===Object.keys(o).length?await(0,p.unlink)(r):await(0,p.writeFile)(r,JSON.stringify(o,null,2),"utf8"),t?.info("Moved the generated OpenCode config out of the project folder",{removed:0===Object.keys(o).length,keptUserKeys:Object.keys(o)})}catch(e){t?.warn("Could not clear the old generated opencode.json from the project folder",{error:(0,n.getErrorMessage)(e)})}return s}(s,this.logger)
7896
- e=e??t,!l&&e?.mode?.alchemy?.tools&&(A=e.mode.alchemy.tools,this.logger.info("Found existing tool settings in the generated OpenCode config",{existingTools:A})),!l&&e?.model&&(_=e.model)
7897
- try{const e=JSON.parse(await w.readFile($,"utf-8")),{kept:t,setAside:r}=function(e){const t={},r={}
7912
+ try{s=JSON.parse(await(0,f.readFile)(r,"utf8"))}catch{return null}if(!s||"object"!=typeof s||Array.isArray(s)||!s.mcp||"object"!=typeof s.mcp||!("alchemy"in s.mcp))return null
7913
+ try{return await(0,m.promisify)(p.execFile)("git",["ls-files","--error-unmatch","--","opencode.json"],{cwd:e}),null}catch{}const n=Object.fromEntries(Object.entries(s).filter(([e])=>!exports.R1.has(e)&&"instructions"!==e&&"skills"!==e))
7914
+ try{0===Object.keys(n).length?await(0,f.unlink)(r):await(0,f.writeFile)(r,JSON.stringify(n,null,2),"utf8"),t?.info("Moved the generated OpenCode config out of the project folder",{removed:0===Object.keys(n).length,keptUserKeys:Object.keys(n)})}catch(e){t?.warn("Could not clear the old generated opencode.json from the project folder",{error:(0,o.getErrorMessage)(e)})}return s}(n,this.logger)
7915
+ e=e??t,!d&&e?.mode?.alchemy?.tools&&(_=e.mode.alchemy.tools,this.logger.info("Found existing tool settings in the generated OpenCode config",{existingTools:_})),!d&&e?.model&&(x=e.model)
7916
+ try{const e=JSON.parse(await v.readFile(E,"utf-8")),{kept:t,setAside:r}=function(e){const t={},r={}
7898
7917
  if(!e||"object"!=typeof e||Array.isArray(e))return{kept:t,setAside:r}
7899
7918
  for(const[s,n]of Object.entries(e))if(!exports.R1.has(s))if(exports.Es.has(s)){if("instructions"===s&&Array.isArray(n)){const e=n.filter(e=>"string"==typeof e&&!/^[a-z][a-z0-9+.-]*:\/\//i.test(e)),o=n.filter(t=>!e.includes(t))
7900
7919
  e.length>0&&(t[s]=e),o.length>0&&(r[s]=o)
7901
7920
  continue}t[s]=n}else r[s]=n
7902
7921
  return{kept:t,setAside:r}}(e)
7903
- Object.assign(C,t),Object.keys(t).length>0&&this.logger.info("Keeping opencode.json keys AlchemyLab does not manage",{keys:Object.keys(t)}),Object.keys(r).length>0&&this.logger.warn("Not loading opencode.json keys: they run commands, change the runtime or send data elsewhere (providers are connected in Settings); the file is left as it is",{keys:Object.keys(r)})}catch{this.logger.info("No opencode.json of the user's in the project folder")}}const M=l?.openCodeTools||j||A||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===l?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==g,todoread:!1,todowrite:!1},R={tools:M}
7904
- let P=null,O=null
7905
- if(l?.projectId&&!l?.openCodeModel)try{O=await this.fetchProjectFromRemote(l.projectId)
7906
- const e=O?.metadata?.defaultModel
7907
- "string"==typeof e&&e.includes("/")&&(P=e)}catch(e){this.logger.warn("Could not fetch project default model; falling back",{error:e instanceof Error?e.message:String(e)})}const D=l?.openCodeModel||P||E||_||i.FLEET_DEFAULT_MODEL
7908
- D!==i.FLEET_DEFAULT_MODEL||l?.openCodeModel||P||E||_||this.logger.warn("No default model configured anywhere for this project; agent files fall back to the fleet default model",{projectId:l?.projectId??null}),l?.openCodeModel?R.model=l.openCodeModel:P?R.model=P:E?R.model=E:_&&(R.model=_)
7909
- const q=y.join(s,".opencode","agent")
7910
- await w.mkdir(q,{recursive:!0})
7911
- const N=D||i.FLEET_DEFAULT_MODEL
7912
- let F,L=null,U=null,B=[],H=null
7913
- if(l?.projectId)if(await this.seedAgentsFromRemote(l.projectId),U=O??await this.fetchProjectFromRemote(l.projectId),F=U?.metadata?.globalSystemPrompt,F&&this.logger.info("Using global system prompt from project metadata",{projectId:l.projectId,promptLength:F.length}),F=await this.agentPreambleFor(l.projectId,F),B=await this.fetchMcpServersFromRemote(l.projectId),H=(await this.fetchDefaultWorkflowFromRemote(l.projectId))?.connectionPermissions??null,L=await this.fetchAgentsFromRemote(l.projectId),L&&L.length>0){this.logger.info("Using agents from remote database",{count:L.length})
7914
- const e=await this.resolveWorkflowEffort(l.projectId)
7922
+ Object.assign(T,t),Object.keys(t).length>0&&this.logger.info("Keeping opencode.json keys AlchemyLab does not manage",{keys:Object.keys(t)}),Object.keys(r).length>0&&this.logger.warn("Not loading opencode.json keys: they run commands, change the runtime or send data elsewhere (providers are connected in Settings); the file is left as it is",{keys:Object.keys(r)})}catch{this.logger.info("No opencode.json of the user's in the project folder")}}const P=d?.openCodeTools||A||_||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===d?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==w,todoread:!1,todowrite:!1},O={tools:P}
7923
+ let D=null,q=null
7924
+ if(d?.projectId&&!d?.openCodeModel)try{q=await this.fetchProjectFromRemote(d.projectId)
7925
+ const e=q?.metadata?.defaultModel
7926
+ "string"==typeof e&&e.includes("/")&&(D=e)}catch(e){this.logger.warn("Could not fetch project default model; falling back",{error:e instanceof Error?e.message:String(e)})}const N=d?.openCodeModel||D||C||x||a.FLEET_DEFAULT_MODEL
7927
+ N!==a.FLEET_DEFAULT_MODEL||d?.openCodeModel||D||C||x||this.logger.warn("No default model configured anywhere for this project; agent files fall back to the fleet default model",{projectId:d?.projectId??null}),d?.openCodeModel?O.model=d.openCodeModel:D?O.model=D:C?O.model=C:x&&(O.model=x)
7928
+ const F=k.join(n,".opencode","agent")
7929
+ await v.mkdir(F,{recursive:!0})
7930
+ const L=N||a.FLEET_DEFAULT_MODEL
7931
+ let U,B=null,H=null,W=[],G=null
7932
+ if(d?.projectId)if(await this.seedAgentsFromRemote(d.projectId),H=q??await this.fetchProjectFromRemote(d.projectId),U=H?.metadata?.globalSystemPrompt,U&&this.logger.info("Using global system prompt from project metadata",{projectId:d.projectId,promptLength:U.length}),U=await this.agentPreambleFor(d.projectId,U),W=await this.fetchMcpServersFromRemote(d.projectId),G=(await this.fetchDefaultWorkflowFromRemote(d.projectId))?.connectionPermissions??null,B=await this.fetchAgentsFromRemote(d.projectId),B&&B.length>0){this.logger.info("Using agents from remote database",{count:B.length})
7933
+ const e=await this.resolveWorkflowEffort(d.projectId)
7915
7934
  this.clearAgentMarkdowns()
7916
- for(const t of L)t.enabled&&this.setAgentMarkdown(t.name,this.generateAgentMarkdown(t,k,N,F,{effort:e,extraTools:(0,c.agentToolGrants)(B,t.name,H),limits:(0,i.modelLimitsOf)(U?.metadata),claudeThinking:r(U?.metadata)}))
7917
- await this.removeOnDiskAgentFiles(q,[...L.filter(e=>e.enabled).map(e=>e.name),"explore"]),this.logger.info("Agent prompts held in memory (served to OpenCode over the action server)",{count:this.agentMarkdowns.size})
7918
- try{const e=new Set([...L.filter(e=>e.enabled).map(e=>`${e.name}.md`),"explore.md"])
7919
- for(const t of await w.readdir(q))if(t.endsWith(".md")&&!e.has(t)){const e=y.join(q,t),r=`${e}.removed-by-alchemy-${(new Date).toISOString().slice(0,10)}`
7920
- try{await w.rename(e,r),this.logger.info(`Moved agent file ${t} aside (no agent by that name)`,{movedTo:r})}catch(e){this.logger.warn(`Could not move agent file ${t} aside; leaving it`,{error:e instanceof Error?e.message:String(e)})}}}catch(e){this.logger.warn("Orphaned agent-file cleanup failed",{error:e instanceof Error?e.message:String(e)})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
7935
+ for(const t of B)t.enabled&&this.setAgentMarkdown(t.name,this.generateAgentMarkdown(t,I,L,U,{effort:e,extraTools:(0,l.agentToolGrants)(W,t.name,G),limits:(0,a.modelLimitsOf)(H?.metadata),claudeThinking:r(H?.metadata)}))
7936
+ await this.removeOnDiskAgentFiles(F,[...B.filter(e=>e.enabled).map(e=>e.name),"explore"]),this.logger.info("Agent prompts held in memory (served to OpenCode over the action server)",{count:this.agentMarkdowns.size})
7937
+ try{const e=new Set([...B.filter(e=>e.enabled).map(e=>`${e.name}.md`),"explore.md"])
7938
+ for(const t of await v.readdir(F))if(t.endsWith(".md")&&!e.has(t)){const e=k.join(F,t),r=`${e}.removed-by-alchemy-${(new Date).toISOString().slice(0,10)}`
7939
+ try{await v.rename(e,r),this.logger.info(`Moved agent file ${t} aside (no agent by that name)`,{movedTo:r})}catch(e){this.logger.warn(`Could not move agent file ${t} aside; leaving it`,{error:e instanceof Error?e.message:String(e)})}}}catch(e){this.logger.warn("Orphaned agent-file cleanup failed",{error:e instanceof Error?e.message:String(e)})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
7921
7940
  else this.logger.warn("No projectId provided - agents must be configured via database")
7922
- const W={$schema:"https://opencode.ai/config.json",share:"disabled",model:D,mcp:{[k]:{type:"local",command:[a,S,s],enabled:!0,timeout:6e5}}}
7923
- b&&process.env.REMOTE_SERVER_URL&&(W.mcp[k].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL})
7924
- const G=l?.projectId?await this.fetchDefaultWorkflowFromRemote(l.projectId):null,K=!1!==G?.webAccess
7925
- if(W.permission={...W.permission??{},bash:"deny"},Object.assign(W.permission,(0,c.globalDenyRules)(B)),L&&L.length>0){W.agent={}
7926
- for(const e of L){if(!e.enabled)continue
7941
+ const K={$schema:"https://opencode.ai/config.json",share:"disabled",model:N,mcp:{[I]:{type:"local",command:[c,$,n],enabled:!0,timeout:6e5}}}
7942
+ S&&process.env.REMOTE_SERVER_URL&&(K.mcp[I].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL})
7943
+ const z=d?.projectId?await this.fetchDefaultWorkflowFromRemote(d.projectId):null,V=!1!==z?.webAccess
7944
+ if(K.permission={...K.permission??{},bash:"deny"},Object.assign(K.permission,(0,l.globalDenyRules)(W)),B&&B.length>0){K.agent={}
7945
+ for(const e of B){if(!e.enabled)continue
7927
7946
  const t={}
7928
7947
  for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
7929
- t[v.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${k}_${n}`]=e}if(!K)for(const e of v.WEB_TOOLS)t[v.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${k}_${e}`]=!1
7930
- Object.assign(t,(0,c.agentToolGrants)(B,e.name,H)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),W.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}W.agent.general={...W.agent.general,disable:!0},W.agent.explore={...W.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${k}_search`]:!0,[`${k}_read_files`]:!0,[`${k}_view_asset`]:!0,[`${k}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
7931
- const e=this.buildExploreMarkdown(F)
7932
- this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(W.agent).length})}const z=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),V=(await this.fetchPluginManifest())["opencode-claude-auth"],Y=W.plugin,J=Array.isArray(Y)?Y.filter(e=>!z(e)):[]
7933
- V&&this.applyAuthPluginPin(J,V),J.length>0?W.plugin=J:delete W.plugin
7934
- try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,d.existsSync)(y.join(s,e)))
7935
- e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}W.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,u.join)(e,t)).find(e=>(0,d.existsSync)(e))??(0,u.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,u.isAbsolute)(t)?t:(0,u.join)(e,t))
7936
- return[...new Set([r,...s])]}(s,C.instructions)
7937
- const X=[".opencode/skill",".opencode/skills",".claude/skills",".agents/skills"].map(e=>y.join(s,e)).filter(e=>(0,d.existsSync)(e))
7938
- X.length>0&&(W.skills={paths:X})
7939
- const Q={...C,...W}
7940
- await w.mkdir(y.dirname(I),{recursive:!0}),await w.writeFile(I,JSON.stringify(Q,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:I})
7941
- try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=y.join(e.homedir(),".alchemy_lab","opencode","config"),s=y.join(r,"opencode.json")
7948
+ t[b.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${I}_${n}`]=e}if(!V)for(const e of b.WEB_TOOLS)t[b.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${I}_${e}`]=!1
7949
+ Object.assign(t,(0,l.agentToolGrants)(W,e.name,G)),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name||"reviewer"===e.name||"criteria-reviewer"===e.name)&&void 0===t.task&&(t.task=!1),K.agent[e.name]={tools:t,...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}K.agent.general={...K.agent.general,disable:!0},K.agent.explore={...K.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[`${I}_search`]:!0,[`${I}_read_files`]:!0,[`${I}_view_asset`]:!0,[`${I}_query_history`]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
7950
+ const e=this.buildExploreMarkdown(U)
7951
+ this.setAgentMarkdown("explore",e),this.logger.info("Explore agent override held in memory"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(K.agent).length})}const Y=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),J=(await this.fetchPluginManifest())["opencode-claude-auth"],X=K.plugin,Q=Array.isArray(X)?X.filter(e=>!Y(e)):[]
7952
+ J&&this.applyAuthPluginPin(Q,J),Q.length>0?K.plugin=Q:delete K.plugin
7953
+ try{const e=["opencode.jsonc",".opencode/opencode.json",".opencode/opencode.jsonc",".opencode/plugin",".opencode/tool",".opencode/command"].filter(e=>(0,u.existsSync)(k.join(n,e)))
7954
+ e.length>0&&this.logger.warn("This repository carries OpenCode config the AlchemyLab runtime does not load (only the generated config is read)",{ignored:e})}catch{}K.instructions=function(e,t){const r=["AGENTS.md","CLAUDE.md","CONTEXT.md"].map(t=>(0,h.join)(e,t)).find(e=>(0,u.existsSync)(e))??(0,h.join)(e,"AGENTS.md"),s=(Array.isArray(t)?t:[]).filter(e=>"string"==typeof e&&e.trim().length>0).map(t=>t.startsWith("~/")||(0,h.isAbsolute)(t)?t:(0,h.join)(e,t))
7955
+ return[...new Set([r,...s])]}(n,T.instructions)
7956
+ const Z=[".opencode/skill",".opencode/skills",".claude/skills",".agents/skills"].map(e=>k.join(n,e)).filter(e=>(0,u.existsSync)(e))
7957
+ Z.length>0&&(K.skills={paths:Z})
7958
+ const ee={...T,...K}
7959
+ s(ee,(y=H?.metadata,!0===y?.anthropicCompaction)),await v.mkdir(k.dirname(j),{recursive:!0}),await v.writeFile(j,JSON.stringify(ee,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:j})
7960
+ try{const e=await Promise.resolve().then(()=>t.t(70857,23)),r=k.join(e.homedir(),".alchemy_lab","opencode","config"),s=k.join(r,"opencode.json")
7942
7961
  let n=null
7943
- try{n=JSON.parse(await w.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!z(e))
7944
- V&&this.applyAuthPluginPin(a,V),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await w.mkdir(r,{recursive:!0}),await w.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(T&&!l)try{let e={}
7945
- try{const t=await w.readFile(x,"utf-8")
7946
- e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=M,await w.writeFile(x,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:x}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:n}=await t.e(939).then(()=>t.t(49939,23)),o=await r(b??!1,this.logger,e)
7947
- o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await n(s,this.logger)}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:I}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,n.getErrorMessage)(e)}),{success:!1,error:(0,n.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=v,v.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],v.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),v.rerouteLogged=new Set},6688(e,exports,t){"use strict"
7962
+ try{n=JSON.parse(await v.readFile(s,"utf-8"))}catch{}const o=n??{},i=Array.isArray(o.plugin)?o.plugin.slice():[],a=i.filter(e=>!Y(e))
7963
+ J&&this.applyAuthPluginPin(a,J),i.length===a.length&&!i.some((e,t)=>e!==a[t])&&n||(o.plugin=a,await v.mkdir(r,{recursive:!0}),await v.writeFile(s,JSON.stringify(o,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:s,plugins:a}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(M&&!d)try{let e={}
7964
+ try{const t=await v.readFile(R,"utf-8")
7965
+ e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=P,await v.writeFile(R,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:R}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:s}=await t.e(939).then(()=>t.t(49939,23)),o=await r(S??!1,this.logger,e)
7966
+ o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors}),await s(n,this.logger)}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:j}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,o.getErrorMessage)(e)}),{success:!1,error:(0,o.getErrorMessage)(e)}}var y}}exports.MCPClientConfigurer=b,b.WEB_TOOLS=["webfetch","websearch","codesearch","capture_site","read_site","fetch_asset"],b.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","webfetch","websearch","codesearch","agent","task","question","todoread","todowrite"]),b.rerouteLogged=new Set},6688(e,exports,t){"use strict"
7948
7967
  exports.sessionIdentity=void 0
7949
7968
  const r=t(16698),s=t(79896),n=t(16928),o=t(43545),i=t(79414),a=t(87280)
7950
7969
  class c{constructor(){this.parentCache=new Map,this.sessionRequestCache=new Map,this.endedUnderSession=new Map,this.callScope=new r.AsyncLocalStorage,this.agentTypeCache=new Map}setIdentity(e){const t=this.identity
@@ -8019,10 +8038,10 @@ t.info("UnifiedLauncher: reaping stale serve from a previous action-server life"
8019
8038
  try{process.kill(-n,"SIGTERM")}catch{try{process.kill(n,"SIGTERM")}catch{}}await new Promise(e=>setTimeout(e,1500))
8020
8039
  try{process.kill(n,0)
8021
8040
  try{process.kill(-n,"SIGKILL")}catch{try{process.kill(n,"SIGKILL")}catch{}}t.warn("UnifiedLauncher: stale serve needed SIGKILL",{pid:n})}catch{}}catch(e){t.warn("UnifiedLauncher: stale-serve reap failed (continuing with launch)",{error:(0,m.getErrorMessage)(e)})}}(w,this.logger)
8022
- const{resolveServeCommand:v,findOpencodeDir:k}=await Promise.resolve().then(()=>t(39068))
8041
+ const{resolveServeCommand:v,findOpencodeDir:k,lastEnginePatchState:S}=await Promise.resolve().then(()=>t(39068))
8023
8042
  if(!k())try{(0,f.execFileSync)("win32"===process.platform?"where":"which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"alchemylab-opencode package not installed and no system `opencode` on PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:w})
8024
- const S=v()
8025
- return new Promise(t=>{const v=l.default.join(w,".alchemy_lab","opencode"),k=process.env.XDG_DATA_HOME||l.default.join(d.default.homedir(),".local","share")
8043
+ const I=v(),$=S()
8044
+ return"applied"===$?.state?this.logger.info("UnifiedLauncher: applied the engine's dependency patches (its install script had not run)",{dir:$.dir,output:$.detail}):"failed"===$?.state&&this.logger.warn("UnifiedLauncher: the engine's dependency patches could not be applied; it starts without them",{dir:$.dir,error:$.detail}),new Promise(t=>{const v=l.default.join(w,".alchemy_lab","opencode"),k=process.env.XDG_DATA_HOME||l.default.join(d.default.homedir(),".local","share")
8026
8045
  try{u.default.mkdirSync(l.default.join(k,"opencode"),{recursive:!0})}catch(e){this.logger.warn("UnifiedLauncher: Could not pre-create opencode globalData dir",{error:(0,m.getErrorMessage)(e)})}try{!function(e){const t=function(){const e=l.default.join(d.default.homedir(),".alchemy_lab","opencode","config","opencode.json")
8027
8046
  try{const t=JSON.parse(u.default.readFileSync(e,"utf-8"))
8028
8047
  if(Array.isArray(t.plugin)){const e=t.plugin.find(e=>"string"==typeof e&&/^opencode-claude-auth@/.test(e))
@@ -8031,15 +8050,15 @@ if(-1!==t)return e.slice(t+1)}}}catch{}return"1.5.3"}(),i=l.default.join(d.defau
8031
8050
  if(!u.default.existsSync(c))try{u.default.mkdirSync(a,{recursive:!0}),u.default.writeFileSync(l.default.join(a,"package.json"),JSON.stringify({name:"alchemylab-claude-auth-wrapper",private:!0,version:"0.0.0"},null,2)),e.info("UnifiedLauncher: Pre-installing opencode-claude-auth plugin",{version:t,targetDir:a}),(0,f.execFileSync)("npm",["install",`opencode-claude-auth@${t}`,"--no-audit","--no-fund","--silent","--ignore-scripts"],{cwd:a,stdio:"pipe",timeout:12e4}),e.info("UnifiedLauncher: Pre-installed opencode-claude-auth plugin",{version:t})}catch(t){e.warn("UnifiedLauncher: claude-auth pre-install failed; opencode will install lazily and first OAuth click may crash",{error:(0,m.getErrorMessage)(t)})}if(u.default.existsSync(c)&&(r(c,e,`opencode-claude-auth@${t}`),s(l.default.join(l.default.dirname(c),"credentials.js"),e,`opencode-claude-auth@${t}`),n(c,e,`opencode-claude-auth@${t}`),o(c,e,`opencode-claude-auth@${t}`)),u.default.existsSync(i)){let t
8032
8051
  try{t=u.default.readdirSync(i)}catch{return}for(const a of t){if(!a.startsWith("opencode-claude-auth@"))continue
8033
8052
  const t=l.default.join(i,a,"node_modules","opencode-claude-auth","dist","index.js")
8034
- t!==c&&(r(t,e,a),s(l.default.join(l.default.dirname(t),"credentials.js"),e,a),n(t,e,a),o(t,e,a))}}}(this.logger)}catch(e){this.logger.warn("UnifiedLauncher: claude-auth ensure-patched failed",{error:(0,m.getErrorMessage)(e)})}const I=l.default.join(v,"log"),$=l.default.join(I,"opencode-serve.log")
8053
+ t!==c&&(r(t,e,a),s(l.default.join(l.default.dirname(t),"credentials.js"),e,a),n(t,e,a),o(t,e,a))}}}(this.logger)}catch(e){this.logger.warn("UnifiedLauncher: claude-auth ensure-patched failed",{error:(0,m.getErrorMessage)(e)})}const S=l.default.join(v,"log"),$=l.default.join(S,"opencode-serve.log")
8035
8054
  let j=null,E=0
8036
8055
  const A=e=>{if(!j)return
8037
8056
  const t=j
8038
8057
  j=null,t.end(e)},C=e=>{u.default.writeFileSync($,e),j=u.default.createWriteStream($,{flags:"a"}),E=e.length},_=e=>{if(j&&(E+=e.length,j.write(e),E>=20971520))try{const e=j
8039
8058
  j=null,e.end(),u.default.renameSync($,`${$}.1`),C(`# opencode-serve.log \u2014 rotated ${(new Date).toISOString()} (previous in opencode-serve.log.1)\n`)}catch(e){this.logger.warn("UnifiedLauncher: opencode log rotation failed; logging stops for this launch",{error:(0,m.getErrorMessage)(e)})}}
8040
- try{u.default.mkdirSync(I,{recursive:!0}),C(`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`)}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,m.getErrorMessage)(e)})}const T=process.env.ALCHEMY_OPENCODE_LOG_LEVEL||"WARN",x=(0,g.newServePassword)()
8059
+ try{u.default.mkdirSync(S,{recursive:!0}),C(`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`)}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,m.getErrorMessage)(e)})}const T=process.env.ALCHEMY_OPENCODE_LOG_LEVEL||"WARN",x=(0,g.newServePassword)()
8041
8060
  try{(0,g.writeServeAuth)(w,x)}catch(e){this.logger.warn("UnifiedLauncher: could not record the serve password on disk; only this process can reach the serve",{error:(0,m.getErrorMessage)(e)})}(0,g.noteServe)(null,x),(0,c.noteServeLoadedLogins)()
8042
- const M=(0,f.spawn)(S.command,[...S.prependArgs,"--port","0","--data-dir",v,"--log-level",T],{stdio:["ignore","pipe","pipe"],cwd:w,detached:!0,env:{...process.env,...(0,p.getOpencodeSpawnEnv)(w,(0,h.isDevMode)()),...y,OPENCODE_SERVER_PASSWORD:x}})
8061
+ const M=(0,f.spawn)(I.command,[...I.prependArgs,"--port","0","--data-dir",v,"--log-level",T],{stdio:["ignore","pipe","pipe"],cwd:w,detached:!0,env:{...process.env,...(0,p.getOpencodeSpawnEnv)(w,(0,h.isDevMode)()),...y,OPENCODE_SERVER_PASSWORD:x}})
8043
8062
  let R="",P=!1
8044
8063
  if(M.pid)try{const e=l.default.join(w,b,"opencode.json")
8045
8064
  u.default.mkdirSync(l.default.dirname(e),{recursive:!0}),u.default.writeFileSync(e,JSON.stringify({pid:M.pid,port:null,startedAt:(new Date).toISOString()},null,2))}catch(e){this.logger.warn("UnifiedLauncher: could not record the serve pid (an orphan would survive a crash)",{error:(0,m.getErrorMessage)(e)})}const O=e=>{if(P)return
@@ -8165,16 +8184,16 @@ return e.ok?e.version??"":a(3e4)}():await async function(){if(console.log(" In
8165
8184
  const e=await o()
8166
8185
  return e.ok?e.version??"":e.permissionDenied?(c(),null):a(15e3)}()}return null!==h?(console.log("\u2705 Docker ready"+(h?` (server v${h})`:"")),{status:"ready",version:h}):{status:"unavailable",reason:"setup did not complete"}}
8167
8186
  const d=t(35317),u=t(79896),h=t(23785)},89063(e,exports){"use strict"
8168
- function t(e){return exports.lS[String(e??"").toLowerCase()]??exports.Ey}function r(e){const r=t(e).atLeast
8169
- return r-Math.min(5*s,r/10)}exports.Ey=exports.lS=void 0,exports.tokensOfMessage=function(e){const t={input:0,output:0,reasoning:0,cacheRead:0,cacheWrite:0,cost:0,hasUsage:!1}
8187
+ function t(e){return"assistant"===e?.role&&!0===e?.summary}function r(e){return exports.lS[String(e??"").toLowerCase()]??exports.Ey}function s(e){const t=r(e).atLeast
8188
+ return t-Math.min(5*n,t/10)}exports.Ey=exports.lS=void 0,exports.tokensOfMessage=function(e){const t={input:0,output:0,reasoning:0,cacheRead:0,cacheWrite:0,cost:0,hasUsage:!1}
8170
8189
  for(const r of e?.parts||[])if("step-finish"===r?.type&&r?.tokens){t.hasUsage=!0
8171
8190
  const e=r.tokens
8172
8191
  t.input+=e.input||0,t.output+=e.output||0,t.reasoning+=e.reasoning||0,t.cacheRead+=e.cache?.read||0,t.cacheWrite+=e.cache?.write||0,t.cost+=r.cost||0}const r=e?.info
8173
8192
  if(!t.hasUsage&&r?.tokens){const e=r.tokens
8174
- t.input=e.input||0,t.output=e.output||0,t.reasoning=e.reasoning||0,t.cacheRead=e.cache?.read||0,t.cacheWrite=e.cache?.write||0,t.cost=r.cost||0,t.hasUsage=t.input>0||t.output>0||t.cacheRead>0||t.cacheWrite>0||t.cost>0}return t},exports.contextTokensOf=function(e){const t=e?.tokens||{}
8175
- return(t.input??0)+(t.cache?.read??0)},exports.cacheRetentionMsFor=function(e){return t(e).atMost},exports.cacheWarmForMs=r,exports.longestCacheWarmMs=function(){return Math.max(r(null),...Object.keys(exports.lS).map(r))}
8176
- const s=6e4
8177
- exports.lS={anthropic:{atLeast:60*s,atMost:65*s},openai:{atLeast:60*s,atMost:90*s}},exports.Ey={atLeast:5*s,atMost:60*s}},75632(e,exports){"use strict"
8193
+ t.input=e.input||0,t.output=e.output||0,t.reasoning=e.reasoning||0,t.cacheRead=e.cache?.read||0,t.cacheWrite=e.cache?.write||0,t.cost=r.cost||0,t.hasUsage=t.input>0||t.output>0||t.cacheRead>0||t.cacheWrite>0||t.cost>0}return t},exports.contextTokensOf=function(e){const r=e?.tokens||{}
8194
+ return t(e)?r.output??0:(r.input??0)+(r.cache?.read??0)},exports.isCompactionSummary=t,exports.cacheRetentionMsFor=function(e){return r(e).atMost},exports.cacheWarmForMs=s,exports.longestCacheWarmMs=function(){return Math.max(s(null),...Object.keys(exports.lS).map(s))}
8195
+ const n=6e4
8196
+ exports.lS={anthropic:{atLeast:60*n,atMost:65*n},openai:{atLeast:60*n,atMost:90*n}},exports.Ey={atLeast:5*n,atMost:60*n}},75632(e,exports){"use strict"
8178
8197
  exports.REGISTER_BY_SETUP_RUN=exports.b=void 0,exports.toolScopesFor=function(e,t,r){const s=exports.b[e]
8179
8198
  if(!s)return{}
8180
8199
  const n={}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemylab",
3
- "version": "3.0.5",
3
+ "version": "3.0.6",
4
4
  "private": false,
5
5
  "description": "AlchemyLab: the agentic workbench. Work is a request, with criteria, gates and a receipt.",
6
6
  "author": "Testfire Software",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@modelcontextprotocol/sdk": "^1.32.1",
50
- "alchemylab-opencode": "^1.0.26",
50
+ "alchemylab-opencode": "^2.0.0",
51
51
  "chalk": "^4.1.2",
52
52
  "compression": "^1.8.2",
53
53
  "cors": "^2.8.6",