alchemylab 1.3.5 → 1.4.0

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
@@ -1,8 +1,71 @@
1
1
  #!/usr/bin/env node
2
- (()=>{function e(t){var s=n[t]
3
- if(void 0!==s)return s.exports
2
+ (()=>{function e(t){var r=n[t]
3
+ if(void 0!==r)return r.exports
4
4
  var module=n[t]={exports:{}}
5
- return o[t].call(module.exports,module,module.exports,e),module.exports}var t,s,r,o={848(e,exports,t){"use strict"
5
+ return o[t].call(module.exports,module,module.exports,e),module.exports}var t,r,s,o={45(module){"use strict"
6
+ module.exports=require("alchemylab-opencode/package.json")},270(e,exports){"use strict"
7
+ Object.defineProperty(exports,"__esModule",{value:!0})},355(e,exports,t){"use strict"
8
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.registerHooksRoutes=function(e,t){e.get("/api/event-hooks/by-event/:eventType",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
9
+ try{const{eventType:r}=e.params,{sessionId:s}=e.query,a=i()
10
+ if(!a)return void t.status(400).json({success:!1,error:"Project not registered"})
11
+ n.info("Proxying event hooks request",{projectId:a,eventType:r,sessionId:s})
12
+ const c=s?"?sessionId="+encodeURIComponent(s):"",l=await o.get(`/api/projects/${a}/event-hooks/by-event/${r}${c}`)
13
+ t.json(l)}catch(e){n.error("Failed to fetch event hooks",{error:(0,r.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,r.getErrorMessage)(e)})}}(e,s,t))),e.post("/api/hook-triggers",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
14
+ try{const r=i()
15
+ if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
16
+ const{hookId:s,hookName:a,eventType:c,executionMode:l,agent:d}=e.body
17
+ n.debug("Proxying hook trigger notification",{projectId:r,hookName:a,eventType:c})
18
+ const u=await o.post(`/api/projects/${r}/hook-triggers`,{hookId:s,hookName:a,eventType:c,executionMode:l,agent:d})
19
+ t.json(u)}catch(e){n.debug("Failed to proxy hook trigger",{error:(0,r.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,r.getErrorMessage)(e)})}}(e,s,t))),e.get("/api/hook-iterations/:sessionId/:hookId",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
20
+ try{const r=i()
21
+ if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
22
+ const{sessionId:s,hookId:n}=e.params,a=await o.get(`/api/projects/${r}/hook-iterations/${s}/${n}`)
23
+ t.json(a)}catch(e){n.debug("Failed to get hook iteration",{error:(0,r.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}(e,s,t))),e.post("/api/hook-iterations/:sessionId/:hookId/increment",((e,s)=>async function(e,t,s){const{apiClient:o,logger:n,getProjectId:i}=s
24
+ try{const r=i()
25
+ if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
26
+ const{sessionId:s,hookId:n}=e.params,a=await o.post(`/api/projects/${r}/hook-iterations/${s}/${n}/increment`,{})
27
+ t.json(a)}catch(e){n.debug("Failed to increment hook iteration",{error:(0,r.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}(e,s,t)))}
28
+ const r=t(8824)},441(e,exports){"use strict"
29
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.BAD_REQUEST=exports.INTERNAL_ERROR=exports.NOT_FOUND=exports.FORBIDDEN=exports.UNAUTHORIZED=exports.VALIDATION_ERROR=exports.MCP_PROTOCOL_ERROR=exports.REQUEST_VALIDATION_FAILED=exports.REQUEST_ID_INVALID_FORMAT=exports.REQUEST_NOT_FOUND=exports.CONNECTION_ERROR=exports.SERVICE_UNAVAILABLE=exports.CONNECTION_TIMEOUT=exports.SERVER_ERROR=exports.NETWORK_ERROR=exports.TASK_VALIDATION_FAILED=exports.TASK_ID_INVALID_FORMAT=exports.TASK_NOT_FOUND=void 0,exports.TASK_NOT_FOUND="TASK_NOT_FOUND",exports.TASK_ID_INVALID_FORMAT="TASK_ID_INVALID_FORMAT",exports.TASK_VALIDATION_FAILED="TASK_VALIDATION_FAILED",exports.NETWORK_ERROR="NETWORK_ERROR",exports.SERVER_ERROR="SERVER_ERROR",exports.CONNECTION_TIMEOUT="CONNECTION_TIMEOUT",exports.SERVICE_UNAVAILABLE="SERVICE_UNAVAILABLE",exports.CONNECTION_ERROR="CONNECTION_ERROR",exports.REQUEST_NOT_FOUND="REQUEST_NOT_FOUND",exports.REQUEST_ID_INVALID_FORMAT="REQUEST_ID_INVALID_FORMAT",exports.REQUEST_VALIDATION_FAILED="REQUEST_VALIDATION_FAILED",exports.MCP_PROTOCOL_ERROR="MCP_PROTOCOL_ERROR",exports.VALIDATION_ERROR="VALIDATION_ERROR",exports.UNAUTHORIZED="UNAUTHORIZED",exports.FORBIDDEN="FORBIDDEN",exports.NOT_FOUND="NOT_FOUND",exports.INTERNAL_ERROR="INTERNAL_ERROR",exports.BAD_REQUEST="BAD_REQUEST"},489(e,exports,t){"use strict"
30
+ function r(e,t){return(0,h.join)(e,p,m,"pool-"+t)}function s(){const e=C.shift()
31
+ e&&setImmediate(e)}async function o(e,t){const o=Date.now()
32
+ !function(e){let t=!1
33
+ for(const r of E.values())r.busy&&r.busySince&&e-r.busySince>b&&(r.busy=!1,r.busySince=void 0,t=!0)
34
+ t&&s()}(o)
35
+ for(let s=0;t>s;s++){const t=r(e,s)
36
+ if(!(E.get(t)||{busy:!1}).busy)return E.set(t,{busy:!0,busySince:o}),{path:t,index:s,storedFingerprint:await a(t)}}return null}function n(e){const t=E.get(e)
37
+ t&&t.busy&&(t.busy=!1,t.busySince=void 0,s())}function i(e,t){setTimeout((()=>n(e)),t).unref()}async function a(e){try{return(await d.promises.readFile((0,h.join)(e,f),"utf-8")).trim()||null}catch{return null}}async function c(e){const t=[]
38
+ return await async function r(s){const o=s?(0,h.join)(e,s):e
39
+ let n
40
+ try{n=await d.promises.readdir(o,{withFileTypes:!0})}catch{return}for(const e of n){const o=s?`${s}/${e.name}`:e.name
41
+ !s&&w.has(e.name)||!s&&v.has(e.name)||(e.isDirectory()?await r(o):(e.isFile()||e.isSymbolicLink())&&t.push(o))}}(""),t.sort(),t}async function l(e){try{const t=(await d.promises.readFile((0,h.join)(e,y),"utf-8")).split("\n").map((e=>e.trim())).filter(Boolean)
42
+ return new Set(t)}catch{return null}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getSlotPath=r,exports.acquireSlot=async function(e,t,r=I){return await o(e,t)||new Promise(((i,a)=>{let c=!1
43
+ const l=Date.now(),d=setTimeout((()=>{if(c)return
44
+ c=!0
45
+ const e=C.indexOf(u)
46
+ 0>e||C.splice(e,1),a(Error(`All ${t} pool slot(s) busy after waiting ${Math.round((Date.now()-l)/1e3)}s`))}),r)
47
+ d.unref()
48
+ const u=async()=>{if(c)return
49
+ const r=await o(e,t)
50
+ c?r&&n(r.path):r?(c=!0,clearTimeout(d),i(r)):(C.unshift(u),s())}
51
+ C.push(u)}))},exports.releaseSlot=n,exports.scheduleSlotRelease=i,exports.watchPidAndRelease=function(e,t,r,s=5e3){if(i(t,r),!e||0>=e)return
52
+ const o=setInterval((()=>{try{process.kill(e,0)}catch{n(t),clearInterval(o)}}),s)
53
+ o.unref()},exports.readSlotFingerprint=a,exports.writeSlotFingerprint=async function(e,t){try{await d.promises.writeFile((0,h.join)(e,f),t,"utf-8")}catch(e){console.warn("worktree-pool: writeSlotFingerprint failed (non-fatal):",(0,g.getErrorMessage)(e))}},exports.computeFingerprint=async function(e,t){if(!t||0===t.length)return""
54
+ const r=(0,u.createHash)("sha256")
55
+ for(const s of t)try{const t=await d.promises.readFile((0,h.join)(e,s),"utf-8")
56
+ r.update(s).update("\0").update(t).update("\n")}catch{r.update(s).update("\0:MISSING\n")}return r.digest("hex")},exports.listSlotFiles=c,exports.writeWarmSnapshot=async function(e){try{const t=await c(e)
57
+ await d.promises.writeFile((0,h.join)(e,y),t.join("\n")+"\n","utf-8")}catch(e){console.warn("worktree-pool: writeWarmSnapshot failed (non-fatal):",(0,g.getErrorMessage)(e))}},exports.readWarmSnapshot=l,exports.restoreToWarmSnapshot=async function(e){const t=await l(e)
58
+ if(!t)return{removed:0}
59
+ const r=await c(e)
60
+ let s=0
61
+ for(const o of r)if(!t.has(o))try{await d.promises.rm((0,h.join)(e,o),{force:!0}),s++}catch{}return{removed:s}},exports._resetForTests=function(){E.clear(),C.length=0}
62
+ const d=t(9896),u=t(6982),h=t(6928),g=t(8824),p=".alchemy_lab",m="test-worktrees",f=".fingerprint.sha256",y=".alchemy-warm-snapshot.txt",v=new Set([f,y]),w=new Set([".git"]),b=12e5,I=6e5,E=new Map,C=[]},833(e,exports,t){"use strict"
63
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
64
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeUrlFromConfig=function(e){const t=o.default.join(e,".alchemy_lab","opencode.json")
65
+ try{if(!s.default.existsSync(t))return null
66
+ const e=JSON.parse(s.default.readFileSync(t,"utf-8"))
67
+ if(e.port)return"http://127.0.0.1:"+e.port}catch{}return null}
68
+ const s=r(t(9896)),o=r(t(6928))},848(e,exports,t){"use strict"
6
69
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleFinalizeRequestAction=async function(e,t){const{requestId:o}=e,{logger:n,apiClient:i,projectPath:a,projectId:c}=t
7
70
  if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
8
71
  n.info("Starting finalize-request action",{requestId:o})
@@ -10,607 +73,702 @@ try{const e=await i.post(`/api/projects/${c}/requests/${o}/validate-completion`,
10
73
  if(!e.success||!e.data)return{success:!1,message:"Server validation failed: "+(e.data?.reason||"Unknown error"),error:"Validation failed"}
11
74
  const t=e.data
12
75
  if(!t.canComplete){let e="Cannot finalize request: "+t.reason
13
- return t.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",t.blockedByTasks.forEach(((t,s)=>{e+=`${s+1}. ${t.taskId}: ${t.title} (${t.status})\n`}))),{success:!1,message:e,error:t.reason}}const l=await i.get(`/api/projects/${c}/requests/${o}`),d=l?.data,u="requests/"+o,h=(0,s.simpleGit)(a),g=await h.status(),p=await h.branch(),f=p.all.includes("main")?"main":"master"
14
- if(g.current!==f&&await h.checkout(f),!p.all.includes(u))return{success:!1,message:`Branch '${u}' not found. The request may not have any file changes to merge.`,error:"Branch not found"}
15
- let m,y=!1
76
+ return t.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",t.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. ${t.taskId}: ${t.title} (${t.status})\n`}))),{success:!1,message:e,error:t.reason}}const l=await i.get(`/api/projects/${c}/requests/${o}`),d=l?.data,u="requests/"+o,h=(0,r.simpleGit)(a),g=await h.status(),p=await h.branch(),m=p.all.includes("main")?"main":"master"
77
+ if(g.current!==m&&await h.checkout(m),!p.all.includes(u))return{success:!1,message:`Branch '${u}' not found. The request may not have any file changes to merge.`,error:"Branch not found"}
78
+ let f,y=!1
16
79
  try{const e=d?.title?`Merge ${u}-${d.title}`:"Merge "+u
17
80
  await h.merge([u,"--no-ff","-m",e])
18
81
  const t=await h.log({maxCount:1})
19
- m=t.latest?.hash,n.info("Git merge completed",{requestId:o,mergeCommit:m?.substring(0,8)})}catch(e){const t=(0,r.getErrorMessage)(e)
20
- if(t.toLowerCase().includes("conflict")){try{await h.merge(["--abort"])}catch(e){n.error("CRITICAL: git merge --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:`Merge conflicts detected for request ${o}. Please resolve conflicts before finalizing.`,error:"Merge conflicts",data:{hasConflicts:!0}}}return{success:!1,message:"Merge failed: "+t,error:t}}try{await h.deleteLocalBranch(u,!0),y=!0}catch{}try{await i.post(`/api/projects/${c}/requests/${o}/finalize-completion`,{summary:"Finalized request "+o,acceptanceEvidence:[{evidence:"Finalized via GUI after review"}],mergeResult:{mergeCommitHash:m||"unknown",cleanedUp:y,method:"action-server-merge"}})}catch(e){const t=(0,r.getErrorMessage)(e)
21
- return n.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:o,mergeCommitHash:m,error:t}),{success:!1,message:`Code was merged (commit: ${m?.substring(0,8)}) but server finalization failed: ${t}`,error:t,data:{mergeCommitHash:m,partialSuccess:!0}}}return{success:!0,message:`Successfully finalized request ${o}: ${d?.title||o}`,data:{mergeCommitHash:m,branchDeleted:y}}}catch(e){const t=(0,r.getErrorMessage)(e)
82
+ f=t.latest?.hash,n.info("Git merge completed",{requestId:o,mergeCommit:f?.substring(0,8)})}catch(e){const t=(0,s.getErrorMessage)(e)
83
+ if(t.toLowerCase().includes("conflict")){try{await h.merge(["--abort"])}catch(e){n.error("CRITICAL: git merge --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:`Merge conflicts detected for request ${o}. Please resolve conflicts before finalizing.`,error:"Merge conflicts",data:{hasConflicts:!0}}}return{success:!1,message:"Merge failed: "+t,error:t}}try{await h.deleteLocalBranch(u,!0),y=!0}catch{}try{await i.post(`/api/projects/${c}/requests/${o}/finalize-completion`,{summary:"Finalized request "+o,acceptanceEvidence:[{evidence:"Finalized via GUI after review"}],mergeResult:{mergeCommitHash:f||"unknown",cleanedUp:y,method:"action-server-merge"}})}catch(e){const t=(0,s.getErrorMessage)(e)
84
+ return n.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:o,mergeCommitHash:f,error:t}),{success:!1,message:`Code was merged (commit: ${f?.substring(0,8)}) but server finalization failed: ${t}`,error:t,data:{mergeCommitHash:f,partialSuccess:!0}}}return{success:!0,message:`Successfully finalized request ${o}: ${d?.title||o}`,data:{mergeCommitHash:f,branchDeleted:y}}}catch(e){const t=(0,s.getErrorMessage)(e)
22
85
  return n.error("finalize-request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to finalize request: "+t,error:t}}}
23
- const s=t(3807),r=t(8824)},857(module){"use strict"
86
+ const r=t(3807),s=t(8824)},857(module){"use strict"
24
87
  module.exports=require("os")},1152(e,exports){"use strict"
25
- function t(e=!0){return new s(e)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.createMetricsCollector=t,exports.createNullMetricsCollector=function(){return t(!1)}
26
- class s{constructor(e=!0){this.enabled=e,this.counters=new Map,this.gauges=new Map,this.histograms=new Map}getKey(e,t){const s=Object.entries(t).map((([e,t])=>`${e}="${t+""}"`)).join(",")
27
- return s?`${e}{${s}}`:e}incrementCounter(e,t=1,s={}){if(!this.enabled)return
28
- const r=this.getKey(e,s),o=this.counters.get(r)||0
29
- this.counters.set(r,o+t)}setGauge(e,t,s={}){if(!this.enabled)return
30
- const r=this.getKey(e,s)
31
- this.gauges.set(r,t)}observeHistogram(e,t,s={}){if(!this.enabled)return
32
- const r=this.getKey(e,s),o=this.histograms.get(r)||[]
33
- o.push(t),this.histograms.set(r,o)}recordValue(e,t,s={}){this.observeHistogram(e,t,s)}startTimer(e){if(!this.enabled)return()=>{}
88
+ function t(e=!0){return new r(e)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.createMetricsCollector=t,exports.createNullMetricsCollector=function(){return t(!1)}
89
+ class r{constructor(e=!0){this.enabled=e,this.counters=new Map,this.gauges=new Map,this.histograms=new Map}getKey(e,t){const r=Object.entries(t).map((([e,t])=>`${e}="${t+""}"`)).join(",")
90
+ return r?`${e}{${r}}`:e}incrementCounter(e,t=1,r={}){if(!this.enabled)return
91
+ const s=this.getKey(e,r),o=this.counters.get(s)||0
92
+ this.counters.set(s,o+t)}setGauge(e,t,r={}){if(!this.enabled)return
93
+ const s=this.getKey(e,r)
94
+ this.gauges.set(s,t)}observeHistogram(e,t,r={}){if(!this.enabled)return
95
+ const s=this.getKey(e,r),o=this.histograms.get(s)||[]
96
+ o.push(t),this.histograms.set(s,o)}recordValue(e,t,r={}){this.observeHistogram(e,t,r)}startTimer(e){if(!this.enabled)return()=>{}
34
97
  const t=Date.now()
35
- return(s={})=>{const r=Date.now()-t
36
- this.observeHistogram(e,r,s)}}getMetrics(){return{counters:Object.fromEntries(this.counters),gauges:Object.fromEntries(this.gauges),histograms:Object.fromEntries(this.histograms)}}reset(){this.counters.clear(),this.gauges.clear(),this.histograms.clear()}}},1187(e,exports,t){"use strict"
37
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
98
+ return(r={})=>{const s=Date.now()-t
99
+ this.observeHistogram(e,s,r)}}getMetrics(){return{counters:Object.fromEntries(this.counters),gauges:Object.fromEntries(this.gauges),histograms:Object.fromEntries(this.histograms)}}reset(){this.counters.clear(),this.gauges.clear(),this.histograms.clear()}}},1187(e,exports,t){"use strict"
100
+ function r(){const e=n.default.homedir(),t=o.default.join(e,".alchemy_lab","opencode","config","opencode.json")
101
+ let r=null
102
+ try{const e=JSON.parse(i.default.readFileSync(t,"utf-8"))
103
+ Array.isArray(e.plugin)&&(r=e.plugin.find((e=>"string"==typeof e&&/^opencode-claude-auth(@|$)/.test(e)))||null)}catch{r=null}const s=o.default.join(e,".cache","opencode","packages"),a=r?o.default.join(s,r):null,c=!!a&&i.default.existsSync(a)
104
+ let l=null,d=!1
105
+ if(a){const e=o.default.join(a,"node_modules","opencode-claude-auth","package.json")
106
+ if(d=i.default.existsSync(e),d)try{const t=JSON.parse(i.default.readFileSync(e,"utf-8"))
107
+ l="string"==typeof t.version?t.version:null}catch{l=null}}return{at:(new Date).toISOString(),pinnedSpec:r,expectedDir:a,dirExists:c,installedPkgJsonExists:d,installedVersion:l}}var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
38
108
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.UnifiedLauncher=void 0
39
- const r=s(t(6928)),o=t(5317),n=t(8824)
40
- exports.UnifiedLauncher=class{constructor(e,t,s){this.logger=e,this.installContext=t,this.configurer=s}async launch(e){const{projectPath:t,settings:s,multiAgent:r}=e,{installPath:o,nodePath:i}=this.installContext
41
- this.logger.info("UnifiedLauncher: Starting launch",{projectPath:t,installPath:o})
42
- try{const n=await this.configurer.configureOpenCode(o,t,i,s,r)
43
- return n.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:n.configPath}),this.launchBackground(e,n)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:n.error}),{success:!1,error:n.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,n.getErrorMessage)(e)}),{success:!1,error:(0,n.getErrorMessage)(e)}}}async launchBackground(e,s){const{projectPath:n,envVars:i}=e
44
- try{(0,o.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:n})
45
- const{resolveServeCommand:a}=await Promise.resolve().then(t.bind(t,9068)),c=a()
46
- return new Promise((e=>{const t=r.default.join(n,".alchemy_lab","opencode"),a=(0,o.spawn)(c.command,[...c.prependArgs,"--port","0","--data-dir",t],{stdio:["ignore","pipe","pipe"],cwd:n,detached:!0,env:{...process.env,...i}})
47
- let l="",d=!1
48
- const u=t=>{l+=t
49
- const r=l.match(/listening on http:\/\/[^:]+:(\d+)/)
50
- if(r&&!d){const t=parseInt(r[1],10)
51
- d=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:a.pid,port:t}),a.unref(),e({success:!0,pid:a.pid,port:t,configPath:s.configPath})}}
52
- a.stdout?.on("data",(e=>u(e.toString()))),a.stderr?.on("data",(e=>u(e.toString()))),setTimeout((()=>{d||(d=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:l.slice(0,500)}),a.unref(),e({success:!0,pid:a.pid,configPath:s.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),a.on("error",(t=>{d||(d=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),a.on("exit",(t=>{d||(d=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:l.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){"use strict"
53
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
109
+ const o=s(t(6928)),n=s(t(857)),i=s(t(9896)),a=t(3962),c=t(5419),l=t(5317),d=t(8824)
110
+ exports.UnifiedLauncher=class{constructor(e,t,r){this.logger=e,this.installContext=t,this.configurer=r}async launch(e){const{projectPath:t,settings:s,multiAgent:o}=e,{installPath:n,nodePath:i}=this.installContext
111
+ this.logger.info("UnifiedLauncher: Starting launch",{projectPath:t,installPath:n})
112
+ try{const a=await this.configurer.configureOpenCode(n,t,i,s,o)
113
+ return a.success?(this.logger.info("UnifiedLauncher: Configuration complete",{configPath:a.configPath}),this.logger.info("[plugin-diag] pre-spawn",r()),this.launchBackground(e,a)):(this.logger.error("UnifiedLauncher: Configuration failed",{error:a.error}),{success:!1,error:a.error})}catch(e){return this.logger.error("UnifiedLauncher: Launch failed",{error:(0,d.getErrorMessage)(e)}),{success:!1,error:(0,d.getErrorMessage)(e)}}}async launchBackground(e,s){const{projectPath:n,envVars:u}=e
114
+ try{(0,l.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{return{success:!1,error:"OpenCode not found in PATH."}}this.logger.info("UnifiedLauncher: Launching opencode serve in background",{command:"opencode serve",cwd:n})
115
+ const{resolveServeCommand:h}=await Promise.resolve().then(t.bind(t,9068)),g=h()
116
+ return new Promise((e=>{const t=o.default.join(n,".alchemy_lab","opencode"),h=o.default.join(t,"log")
117
+ let p=null
118
+ const m=e=>{if(!p)return
119
+ const t=p
120
+ p=null,t.end(e)}
121
+ try{i.default.mkdirSync(h,{recursive:!0})
122
+ const e=o.default.join(h,"opencode-serve.log")
123
+ i.default.writeFileSync(e,`# opencode-serve.log \u2014 launched ${(new Date).toISOString()}\n`),p=i.default.createWriteStream(e,{flags:"a"})}catch(e){this.logger.warn("UnifiedLauncher: Could not open opencode log file",{error:(0,d.getErrorMessage)(e)})}const f=(0,l.spawn)(g.command,[...g.prependArgs,"--port","0","--data-dir",t],{stdio:["ignore","pipe","pipe"],cwd:n,detached:!0,env:{...process.env,...(0,c.getOpencodeSpawnEnv)(n,(0,a.isDevMode)()),...u}})
124
+ let y="",v=!1
125
+ const w=t=>{y+=t
126
+ const o=y.match(/listening on http:\/\/[^:]+:(\d+)/)
127
+ if(o&&!v){const t=parseInt(o[1],10)
128
+ v=!0,this.logger.info("UnifiedLauncher: Discovered opencode serve port",{pid:f.pid,port:t}),this.logger.info("[plugin-diag] port-ready",{pid:f.pid,port:t,...r()}),setTimeout((()=>{this.logger.info("[plugin-diag] port-ready+5s",{pid:f.pid,...r()})}),5e3).unref(),setTimeout((()=>{this.logger.info("[plugin-diag] port-ready+15s",{pid:f.pid,...r()})}),15e3).unref(),f.unref(),e({success:!0,pid:f.pid,port:t,configPath:s.configPath})}}
129
+ f.stdout?.on("data",(e=>{p&&p.write(e),w(e.toString())})),f.stderr?.on("data",(e=>{if(p){const t=e.toString().split("\n")
130
+ for(let e=0;e<t.length;e++){const r=t[e]
131
+ 0===r.length&&e===t.length-1||p.write(`[stderr] ${r}\n`)}}w(e.toString())})),f.on("exit",((e,t)=>{m(`\n# opencode exited code=${e} signal=${t} at=${(new Date).toISOString()}\n`)})),setTimeout((()=>{v||(v=!0,this.logger.warn("UnifiedLauncher: Timeout waiting for opencode serve port",{outputBuffer:y.slice(0,500)}),f.unref(),e({success:!0,pid:f.pid,configPath:s.configPath,error:"Could not determine port from opencode serve output"}))}),1e4),f.on("error",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve failed to start",{error:t.message}),m(`\n# child error: ${t.message}\n`),e({success:!1,error:"Failed to start opencode serve: "+t.message}))})),f.on("exit",(t=>{v||(v=!0,this.logger.error("UnifiedLauncher: opencode serve exited early",{code:t,output:y.slice(0,500)}),e({success:!1,error:"opencode serve exited with code "+t}))}))}))}}},1253(e,exports,t){"use strict"
132
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
54
133
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.TokenManager=void 0
55
- const r=s(t(6928)),o=s(t(1943)),n=s(t(6982)),i=s(t(857)),a=t(9067),c=t(8824),l="alchemy-lab"
56
- exports.TokenManager=class{constructor(e,t,s={}){this.projectId=null,this.encryptionKey=null,this.pendingAuthCheck=!1,this.tempAuthCode=null,this.CACHE_TTL_MS=6e4,this.TOKEN_CACHE={manualToken:null,encryptedToken:null,hasValidToken:null},this.initPromise=null,this.initialized=!1,this.logger=e.child({component:"token-manager"}),this.remoteServerUrl=s.remoteServerUrl||"https://api.alchemylabcode.com",this.allowInteractiveAuth=!1!==s.allowInteractiveAuth,void 0!==s.cacheTtlMs&&s.cacheTtlMs>0&&(this.CACHE_TTL_MS=s.cacheTtlMs),this.serverInstallDir=this.getModuleDir(),this.tokenDirPath=this.serverInstallDir,this.authFlow=new a.ExternalAuthFlow(e,this.remoteServerUrl),this.logger.info("TokenManager initialized",{workingDir:t,serverInstallDir:this.serverInstallDir,tokenDirPath:this.tokenDirPath,allowInteractiveAuth:this.allowInteractiveAuth,cacheTtlMs:this.CACHE_TTL_MS}),this.generateEncryptionKey()}getModuleDir(){const e=__dirname||process.cwd(),t=i.default.homedir()
57
- return e.includes("node_modules")?r.default.join(t,".alchemy_lab","tokens"):r.default.join(t,".alchemy_lab","tokens-dev")}async initialize(){return this.initPromise||(this.initPromise=this.ensureTokenDirectoryExists().then((()=>{this.initialized=!0,this.logger.info("TokenManager initialization completed")})).catch((e=>{throw this.logger.error("TokenManager initialization failed",{error:e instanceof Error?e.stack:e+""}),Error("TokenManager initialization failed")}))),this.initPromise}getStableMachineId(){try{const e=t(857),s=r.default.join(this.serverInstallDir,".machine-id")
58
- try{const e=t(9896).readFileSync(s,"utf8").trim()
134
+ const s=r(t(6928)),o=r(t(1943)),n=r(t(6982)),i=r(t(857)),a=t(9067),c=t(8824),l="alchemy-lab"
135
+ exports.TokenManager=class{constructor(e,t,r={}){this.projectId=null,this.encryptionKey=null,this.pendingAuthCheck=!1,this.tempAuthCode=null,this.CACHE_TTL_MS=6e4,this.TOKEN_CACHE={manualToken:null,encryptedToken:null,hasValidToken:null},this.initPromise=null,this.initialized=!1,this.logger=e.child({component:"token-manager"}),this.remoteServerUrl=r.remoteServerUrl||"https://api.alchemylabcode.com",this.allowInteractiveAuth=!1!==r.allowInteractiveAuth,void 0!==r.cacheTtlMs&&r.cacheTtlMs>0&&(this.CACHE_TTL_MS=r.cacheTtlMs),this.serverInstallDir=this.getModuleDir(),this.tokenDirPath=this.serverInstallDir,this.authFlow=new a.ExternalAuthFlow(e,this.remoteServerUrl),this.logger.info("TokenManager initialized",{workingDir:t,serverInstallDir:this.serverInstallDir,tokenDirPath:this.tokenDirPath,allowInteractiveAuth:this.allowInteractiveAuth,cacheTtlMs:this.CACHE_TTL_MS}),this.generateEncryptionKey()}getModuleDir(){const e=__dirname||process.cwd(),t=i.default.homedir()
136
+ return e.includes("node_modules")?s.default.join(t,".alchemy_lab","tokens"):s.default.join(t,".alchemy_lab","tokens-dev")}async initialize(){return this.initPromise||(this.initPromise=this.ensureTokenDirectoryExists().then((()=>{this.initialized=!0,this.logger.info("TokenManager initialization completed")})).catch((e=>{throw this.logger.error("TokenManager initialization failed",{error:e instanceof Error?e.stack:e+""}),Error("TokenManager initialization failed")}))),this.initPromise}getStableMachineId(){try{const e=t(857),r=s.default.join(this.serverInstallDir,".machine-id")
137
+ try{const e=t(9896).readFileSync(r,"utf8").trim()
59
138
  if(e&&e.length>0)return e}catch{}const o=e.platform(),i=e.arch(),a=e.homedir(),c=e.cpus(),l=`${o}-${i}-${c&&c.length>0?c[0].model:"unknown"}-${a}`,d=n.default.createHash("sha256").update(l).digest("hex")
60
- try{t(9896).writeFileSync(s,d,"utf8"),this.logger.debug("Created new machine ID file",{path:s})}catch(e){this.logger.warn("Could not persist machine ID, will regenerate on restart",{error:e instanceof Error?e.message:e+""})}return d}catch(e){return this.logger.error("Error getting stable machine ID",{error:(0,c.getErrorMessage)(e)}),n.default.randomBytes(32).toString("hex")}}generateEncryptionKey(){try{const e=t(857).platform(),s=this.getStableMachineId(),r=`${l}-${e}-${s}`
61
- this.encryptionKey=n.default.createHash("sha256").update(r).digest(),this.logger.debug("Generated encryption key using stable machine ID")}catch(e){this.logger.error("Error generating encryption key",{error:(0,c.getErrorMessage)(e)}),this.encryptionKey=Buffer.from(l.repeat(4),"utf8")}}generateLegacyEncryptionKey(){try{const e=t(857).platform(),s=t(857).hostname(),r=`${l}-${e}-${s}`
62
- return n.default.createHash("sha256").update(r).digest()}catch(e){return this.logger.error("Error generating legacy encryption key",{error:(0,c.getErrorMessage)(e)}),Buffer.from(l.repeat(4),"utf8")}}async ensureTokenDirectoryExists(){try{await o.default.mkdir(this.tokenDirPath,{recursive:!0}),this.logger.debug("Token directory ensured exists",{path:this.tokenDirPath})}catch(e){throw this.logger.error("Error creating token directory",{path:this.tokenDirPath,error:(0,c.getErrorMessage)(e)}),Error("Error creating token directory: "+this.tokenDirPath)}}setProjectId(e){this.projectId=e,this.logger.info("Set project ID for token manager",{projectId:e})}getAccountKey(){return"default-user-"+n.default.createHash("sha256").update(this.remoteServerUrl).digest("hex").substring(0,8)}getTokenFilePath(e){const t=e.replace(/[^a-zA-Z0-9]/g,"_")
63
- return r.default.join(this.tokenDirPath,t+".token")}invalidateCache(){this.logger.debug("Invalidating all token caches"),this.TOKEN_CACHE.manualToken=null,this.TOKEN_CACHE.encryptedToken=null,this.TOKEN_CACHE.hasValidToken=null}async checkForManualToken(){const e=Date.now(),t=r.default.join(this.tokenDirPath,"manual-token.json")
139
+ try{t(9896).writeFileSync(r,d,"utf8"),this.logger.debug("Created new machine ID file",{path:r})}catch(e){this.logger.warn("Could not persist machine ID, will regenerate on restart",{error:e instanceof Error?e.message:e+""})}return d}catch(e){return this.logger.error("Error getting stable machine ID",{error:(0,c.getErrorMessage)(e)}),n.default.randomBytes(32).toString("hex")}}generateEncryptionKey(){try{const e=t(857).platform(),r=this.getStableMachineId(),s=`${l}-${e}-${r}`
140
+ this.encryptionKey=n.default.createHash("sha256").update(s).digest(),this.logger.debug("Generated encryption key using stable machine ID")}catch(e){this.logger.error("Error generating encryption key",{error:(0,c.getErrorMessage)(e)}),this.encryptionKey=Buffer.from(l.repeat(4),"utf8")}}generateLegacyEncryptionKey(){try{const e=t(857).platform(),r=t(857).hostname(),s=`${l}-${e}-${r}`
141
+ return n.default.createHash("sha256").update(s).digest()}catch(e){return this.logger.error("Error generating legacy encryption key",{error:(0,c.getErrorMessage)(e)}),Buffer.from(l.repeat(4),"utf8")}}async ensureTokenDirectoryExists(){try{await o.default.mkdir(this.tokenDirPath,{recursive:!0}),this.logger.debug("Token directory ensured exists",{path:this.tokenDirPath})}catch(e){throw this.logger.error("Error creating token directory",{path:this.tokenDirPath,error:(0,c.getErrorMessage)(e)}),Error("Error creating token directory: "+this.tokenDirPath)}}setProjectId(e){this.projectId=e,this.logger.info("Set project ID for token manager",{projectId:e})}getAccountKey(){return"default-user-"+n.default.createHash("sha256").update(this.remoteServerUrl).digest("hex").substring(0,8)}getTokenFilePath(e){const t=e.replace(/[^a-zA-Z0-9]/g,"_")
142
+ return s.default.join(this.tokenDirPath,t+".token")}invalidateCache(){this.logger.debug("Invalidating all token caches"),this.TOKEN_CACHE.manualToken=null,this.TOKEN_CACHE.encryptedToken=null,this.TOKEN_CACHE.hasValidToken=null}async checkForManualToken(){const e=Date.now(),t=s.default.join(this.tokenDirPath,"manual-token.json")
64
143
  if(this.TOKEN_CACHE.manualToken&&e<this.TOKEN_CACHE.manualToken.expiresAt){const t=this.TOKEN_CACHE.manualToken
65
144
  return this.logger.debug("Using cached manual token check result",{ageSecs:Math.round((e-t.timestamp)/1e3),exists:t.exists,hasToken:null!==t.value}),t.exists&&t.value?t.value:null}this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Checking for manual token file",{tokenFilePath:t})
66
- try{let s=!1,r=null
67
- try{await o.default.stat(t),s=!0,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file exists")}catch{this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file does not exist")}if(s)try{const s=await o.default.readFile(t,"utf8"),n=JSON.parse(s)
68
- new Date(n.expiresAt)>new Date?(r=n,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Valid manual token found",{username:r.username,expiresAt:r.expiresAt})):this.logger.debug("Manual token has expired",{expiresAt:n.expiresAt})}catch(e){this.logger.error("Error parsing manual token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.manualToken={value:r,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:s},r}catch(e){return this.logger.error("Error checking for manual token",{error:(0,c.getErrorMessage)(e)}),null}}encrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
69
- const t=n.default.randomBytes(16),s=n.default.createCipheriv("aes-256-cbc",this.encryptionKey,t)
70
- let r=s.update(e,"utf8","hex")
71
- return r+=s.final("hex"),`${t.toString("hex")}:${r}`}decrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
145
+ try{let r=!1,s=null
146
+ try{await o.default.stat(t),r=!0,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file exists")}catch{this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Manual token file does not exist")}if(r)try{const r=await o.default.readFile(t,"utf8"),n=JSON.parse(r)
147
+ new Date(n.expiresAt)>new Date?(s=n,this.TOKEN_CACHE.manualToken&&e-(this.TOKEN_CACHE.manualToken.lastCheck||0)<=2*this.CACHE_TTL_MS||this.logger.debug("Valid manual token found",{username:s.username,expiresAt:s.expiresAt})):this.logger.debug("Manual token has expired",{expiresAt:n.expiresAt})}catch(e){this.logger.error("Error parsing manual token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.manualToken={value:s,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:r},s}catch(e){return this.logger.error("Error checking for manual token",{error:(0,c.getErrorMessage)(e)}),null}}encrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
148
+ const t=n.default.randomBytes(16),r=n.default.createCipheriv("aes-256-cbc",this.encryptionKey,t)
149
+ let s=r.update(e,"utf8","hex")
150
+ return s+=r.final("hex"),`${t.toString("hex")}:${s}`}decrypt(e){if(!this.encryptionKey)throw Error("Encryption key not available")
72
151
  try{return this.decryptWithKey(e,this.encryptionKey)}catch(t){this.logger.warn("Failed to decrypt with current key, trying legacy hostname-based key",{error:t instanceof Error?t.message:t+""})
73
- try{const t=this.generateLegacyEncryptionKey(),s=this.decryptWithKey(e,t)
74
- return this.logger.info("Successfully decrypted with legacy key, will re-encrypt with new key on next store"),s}catch(e){this.logger.error("Failed to decrypt with both current and legacy keys",{primaryError:t instanceof Error?t.message:t+"",legacyError:e instanceof Error?e.message:e+""})
75
- const s=this.getAccountKey(),r=this.getTokenFilePath(s)
76
- try{o.default.rename(r,r+".corrupt").catch((()=>this.logger.warn("Could not backup corrupted token file")))}catch(e){this.logger.error("Error backing up corrupted file: "+e)}throw Error("Could not decrypt token file with current or legacy key - it may be corrupted")}}}decryptWithKey(e,t){try{const[s,r]=e.split(":")
77
- if(!s||!r)throw Error("Invalid encrypted data format")
78
- const o=Buffer.from(s,"hex"),i=n.default.createDecipheriv("aes-256-cbc",t,o)
79
- let a=i.update(r,"hex","utf8")
152
+ try{const t=this.generateLegacyEncryptionKey(),r=this.decryptWithKey(e,t)
153
+ return this.logger.info("Successfully decrypted with legacy key, will re-encrypt with new key on next store"),r}catch(e){this.logger.error("Failed to decrypt with both current and legacy keys",{primaryError:t instanceof Error?t.message:t+"",legacyError:e instanceof Error?e.message:e+""})
154
+ const r=this.getAccountKey(),s=this.getTokenFilePath(r)
155
+ try{o.default.rename(s,s+".corrupt").catch((()=>this.logger.warn("Could not backup corrupted token file")))}catch(e){this.logger.error("Error backing up corrupted file: "+e)}throw Error("Could not decrypt token file with current or legacy key - it may be corrupted")}}}decryptWithKey(e,t){try{const[r,s]=e.split(":")
156
+ if(!r||!s)throw Error("Invalid encrypted data format")
157
+ const o=Buffer.from(r,"hex"),i=n.default.createDecipheriv("aes-256-cbc",t,o)
158
+ let a=i.update(s,"hex","utf8")
80
159
  return a+=i.final("utf8"),a}catch(e){throw e}}async storeToken(e){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
81
- try{const t=this.getAccountKey(),s=this.getTokenFilePath(t),r=JSON.stringify(e),n=this.encrypt(r)
82
- await o.default.writeFile(s,n,"utf8"),this.logger.info("Token stored securely",{username:e.username,expiresAt:e.expiresAt}),this.invalidateCache()}catch(e){throw this.logger.error("Error storing token",{error:(0,c.getErrorMessage)(e)}),Error("Failed to store token securely")}}async getToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
160
+ try{const t=this.getAccountKey(),r=this.getTokenFilePath(t),s=JSON.stringify(e),n=this.encrypt(s)
161
+ await o.default.writeFile(r,n,"utf8"),this.logger.info("Token stored securely",{username:e.username,expiresAt:e.expiresAt}),this.invalidateCache()}catch(e){throw this.logger.error("Error storing token",{error:(0,c.getErrorMessage)(e)}),Error("Failed to store token securely")}}async getToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
83
162
  try{if(this.hasPendingAuth()&&this.tempAuthCode){const e=await this.checkAuthStatus(this.tempAuthCode)
84
163
  if(e)return e}const e=await this.checkForManualToken()
85
164
  if(e)return e
86
165
  const t=Date.now()
87
166
  if(this.TOKEN_CACHE.encryptedToken&&t<this.TOKEN_CACHE.encryptedToken.expiresAt){const e=this.TOKEN_CACHE.encryptedToken
88
- return this.logger.debug("Using cached encrypted token",{ageSecs:Math.round((t-e.timestamp)/1e3),hasToken:null!==e.value}),e.value}const s=this.getAccountKey(),r=this.getTokenFilePath(s)
89
- try{await o.default.stat(r)}catch{return this.logger.debug("No encrypted token file found"),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:t,expiresAt:t+this.CACHE_TTL_MS,lastCheck:t,exists:!1},this.allowInteractiveAuth&&!this.hasPendingAuth()?(this.logger.debug("No stored token found, starting authentication flow"),this.startAuthenticationFlow().catch((e=>{this.logger.error("Failed to start authentication flow",{error:e instanceof Error?e.message:e+""})}))):this.logger.debug("Interactive authentication disabled or already pending"),null}try{const e=await o.default.readFile(r,"utf8"),t=this.decrypt(e),s=JSON.parse(t),n=new Date(s.expiresAt),i=new Date,a=n.getTime()-i.getTime(),c=a/36e5
90
- return this.logger.info("Token expiration check details",{tokenExpiresAt:s.expiresAt,currentTime:i.toISOString(),isExpired:i>=n,timeUntilExpiryMs:a,timeUntilExpiryHours:Math.round(100*c)/100,username:s.username,systemTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),n>i?(this.logger.info("Retrieved valid token from storage",{username:s.username,expiresAt:s.expiresAt}),this.TOKEN_CACHE.encryptedToken={value:s,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!0},s):(this.logger.info("Stored token has expired",{expiresAt:s.expiresAt,expiredByMs:i.getTime()-n.getTime(),expiredByHours:Math.round((i.getTime()-n.getTime())/36e5*100)/100}),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null)}catch(e){this.logger.error("Error reading/decrypting token file",{error:(0,c.getErrorMessage)(e),path:r})
91
- try{const e=r+".backup"
92
- await o.default.rename(r,e),this.logger.info("Moved corrupted token file to backup",{from:r,to:e})}catch(e){this.logger.error("Error creating backup of corrupted token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null}}catch(e){return this.logger.error("Error retrieving token",{error:(0,c.getErrorMessage)(e)}),null}}async removeToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
167
+ return this.logger.debug("Using cached encrypted token",{ageSecs:Math.round((t-e.timestamp)/1e3),hasToken:null!==e.value}),e.value}const r=this.getAccountKey(),s=this.getTokenFilePath(r)
168
+ try{await o.default.stat(s)}catch{return this.logger.debug("No encrypted token file found"),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:t,expiresAt:t+this.CACHE_TTL_MS,lastCheck:t,exists:!1},this.allowInteractiveAuth&&!this.hasPendingAuth()?(this.logger.debug("No stored token found, starting authentication flow"),this.startAuthenticationFlow().catch((e=>{this.logger.error("Failed to start authentication flow",{error:e instanceof Error?e.message:e+""})}))):this.logger.debug("Interactive authentication disabled or already pending"),null}try{const e=await o.default.readFile(s,"utf8"),t=this.decrypt(e),r=JSON.parse(t),n=new Date(r.expiresAt),i=new Date,a=n.getTime()-i.getTime(),c=a/36e5
169
+ return this.logger.info("Token expiration check details",{tokenExpiresAt:r.expiresAt,currentTime:i.toISOString(),isExpired:i>=n,timeUntilExpiryMs:a,timeUntilExpiryHours:Math.round(100*c)/100,username:r.username,systemTimezone:Intl.DateTimeFormat().resolvedOptions().timeZone}),n>i?(this.logger.info("Retrieved valid token from storage",{username:r.username,expiresAt:r.expiresAt}),this.TOKEN_CACHE.encryptedToken={value:r,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!0},r):(this.logger.info("Stored token has expired",{expiresAt:r.expiresAt,expiredByMs:i.getTime()-n.getTime(),expiredByHours:Math.round((i.getTime()-n.getTime())/36e5*100)/100}),this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null)}catch(e){this.logger.error("Error reading/decrypting token file",{error:(0,c.getErrorMessage)(e),path:s})
170
+ try{const e=s+".backup"
171
+ await o.default.rename(s,e),this.logger.info("Moved corrupted token file to backup",{from:s,to:e})}catch(e){this.logger.error("Error creating backup of corrupted token file",{error:e instanceof Error?e.message:e+""})}return this.TOKEN_CACHE.encryptedToken={value:null,timestamp:Date.now(),expiresAt:Date.now()+this.CACHE_TTL_MS,lastCheck:Date.now(),exists:!1},null}}catch(e){return this.logger.error("Error retrieving token",{error:(0,c.getErrorMessage)(e)}),null}}async removeToken(){!this.initPromise&&this.initialized?await this.initPromise:this.initialized||await this.initialize()
93
172
  try{const e=this.getAccountKey(),t=this.getTokenFilePath(e)
94
173
  try{await o.default.stat(t)}catch{return this.logger.info("No token file to remove"),!1}return await o.default.unlink(t),this.logger.info("Token removed from secure storage"),this.invalidateCache(),!0}catch(e){return this.logger.error("Error removing token",{error:(0,c.getErrorMessage)(e)}),!1}}async hasValidToken(){const e=Date.now()
95
174
  if(this.TOKEN_CACHE.hasValidToken&&e<this.TOKEN_CACHE.hasValidToken.expiresAt){const t=this.TOKEN_CACHE.hasValidToken
96
175
  return this.logger.debug("Using cached hasValidToken result",{ageSecs:Math.round((e-t.timestamp)/1e3),hasToken:t.value}),t.value}this.logger.debug("Checking if valid token exists (cache miss or expired)")
97
176
  const t=null!==await this.getToken()
98
- return this.TOKEN_CACHE.hasValidToken={value:t,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:t},t}getTokenDirectoryPath(){return this.tokenDirPath}getManualTokenFilePath(){return r.default.join(this.tokenDirPath,"manual-token.json")}logTokenLocationInstructions(){const e=this.getManualTokenFilePath()
177
+ return this.TOKEN_CACHE.hasValidToken={value:t,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:t},t}getTokenDirectoryPath(){return this.tokenDirPath}getManualTokenFilePath(){return s.default.join(this.tokenDirPath,"manual-token.json")}logTokenLocationInstructions(){const e=this.getManualTokenFilePath()
99
178
  console.log("========================================"),console.log("To use the alchemy CLI, you need to authenticate."),console.log("Token file location: "+e),console.log("You can either:"),console.log('\n1. Use the authenticate tool (recommended)\n2. Manually place a token file at the above location\n3. Set up automatic authentication flow\n\nFor manual token placement, create a JSON file with:\n{\n "token": "your_token_here",\n "userId": "your_user_id",\n "username": "your_username", \n "role": "user",\n "expiresAt": "2025-12-31T23:59:59.000Z"\n}\n'),console.log("For more information, see the authentication documentation."),console.log("========================================")}async startAuthenticationFlow(){this.pendingAuthCheck=!0
100
179
  try{const{tempCode:e,authUrl:t}=await this.authFlow.startAuthFlow()
101
180
  return this.tempAuthCode=e,this.logger.info("Started external authentication flow",{tempCode:e,authUrl:t}),this.authFlow.startPolling(e,(async t=>{this.logger.info("Authentication flow completed",{tempCode:e.substring(0,4)+"***",username:t.username}),await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),console.log("Authentication successful!"),console.log(`Welcome, ${t.username}!`),console.log("You can now use the alchemy CLI."),console.log("========================================")}),(()=>{this.logger.info("Authentication flow timed out",{tempCode:e.substring(0,4)+"***"}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow timed out."),console.log("Please try again or manually place a token file."),console.log("Token file location:"),console.log(""+this.getManualTokenFilePath()),console.log("========================================")}),(t=>{this.logger.error("Authentication flow error",{tempCode:e.substring(0,4)+"***",error:t}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow error."),console.log("Error: "+t),console.log("Please try again or manually place a token file."),console.log(""+this.getManualTokenFilePath()),console.log("========================================")})),{tempCode:e,authUrl:t}}catch(e){throw this.logger.error("Error starting authentication flow",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,e}}hasPendingAuth(){return this.pendingAuthCheck||!!this.tempAuthCode}getAuthStatus(){return this.hasPendingAuth()?{hasToken:!1,isPendingAuth:!0,tempCode:this.tempAuthCode,authUrl:this.authFlow.getAuthUrl(this.tempAuthCode)}:{hasToken:!1,isPendingAuth:!1}}async checkAuthStatus(e){if(!e)return null
102
181
  try{const t=this.authFlow.getPollingStatus()
103
182
  if(t.status===a.PollingStatus.AUTHENTICATED&&t.tokenInfo)return this.logger.info("Authentication polling has token available",{tempCode:e.substring(0,4)+"***",username:t.tokenInfo.username}),t.tokenInfo
104
183
  if(t.status===a.PollingStatus.ERROR||t.status===a.PollingStatus.TIMEOUT)return this.pendingAuthCheck=!1,this.tempAuthCode=null,null
105
- const s=this.authFlow.getPollingUrl(e),r=await fetch(s)
106
- if(r.ok){const e=await r.json()
184
+ const r=this.authFlow.getPollingUrl(e),s=await fetch(r)
185
+ if(s.ok){const e=await s.json()
107
186
  if(e.authenticated&&e.token){const t=this.authFlow.parseTokenResponse(e)
108
187
  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")
109
188
  try{const e=await this.getToken()
110
189
  if(!e)return this.logger.debug("No token available for validation"),!1
111
190
  const t=this.remoteServerUrl+"/auth/validate"
112
191
  this.logger.debug("Making token validation request",{validationUrl:t})
113
- const s=await fetch(t,{method:"GET",headers:{Authorization:"Bearer "+e.token,"Content-Type":"application/json"},signal:AbortSignal.timeout(1e4)})
114
- if(s.ok){const e=await s.json()
115
- return this.logger.info("Token validation successful",{status:e.status||"unknown",username:e.username||"unknown"}),!0}return this.logger.info("Token validation failed - token may be invalid or expired",{status:s.status,statusText:s.statusText}),this.invalidateCache(),!1}catch(e){return e instanceof Error&&"AbortError"===e.name?(this.logger.info("Token validation request timed out - assuming offline or server unavailable"),!0):(this.logger.info("Token validation request failed",{error:(0,c.getErrorMessage)(e)}),!0)}}async hasValidAndWorkingToken(){return!!await this.hasValidToken()&&await this.validateTokenWithRemoteServer()}async isGoogleOAuthAvailable(){return await this.authFlow.isGoogleOAuthAvailable()}async startGoogleOAuthFlow(){return await this.authFlow.startGoogleOAuthFlow()}async startGoogleOAuthPolling(e,t,s,r,o){return await this.authFlow.startGoogleOAuthPolling(e,t,s,r,o)}}},1292(e,exports,t){"use strict"
192
+ const r=await fetch(t,{method:"GET",headers:{Authorization:"Bearer "+e.token,"Content-Type":"application/json"},signal:AbortSignal.timeout(1e4)})
193
+ if(r.ok){const e=await r.json()
194
+ return this.logger.info("Token validation successful",{status:e.status||"unknown",username:e.username||"unknown"}),!0}return this.logger.info("Token validation failed - token may be invalid or expired",{status:r.status,statusText:r.statusText}),this.invalidateCache(),!1}catch(e){return e instanceof Error&&"AbortError"===e.name?(this.logger.info("Token validation request timed out - assuming offline or server unavailable"),!0):(this.logger.info("Token validation request failed",{error:(0,c.getErrorMessage)(e)}),!0)}}async hasValidAndWorkingToken(){return!!await this.hasValidToken()&&await this.validateTokenWithRemoteServer()}async isGoogleOAuthAvailable(){return await this.authFlow.isGoogleOAuthAvailable()}async startGoogleOAuthFlow(){return await this.authFlow.startGoogleOAuthFlow()}async startGoogleOAuthPolling(e,t,r,s,o){return await this.authFlow.startGoogleOAuthPolling(e,t,r,s,o)}}},1292(e,exports,t){"use strict"
116
195
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.SessionSpawnService=void 0
117
- const s=t(9896),r=t(6928),o=t(2424),n=t(8824),i=t(5671),a=t(8955)
118
- exports.SessionSpawnService=class{constructor(e){this.deps=e}getOpenCodeServerUrl(e){try{const t=(0,r.join)(e,".alchemy_lab","opencode.json"),o=(0,s.readFileSync)(t,"utf8"),n=JSON.parse(o)
119
- if(n.port)return"http://127.0.0.1:"+n.port}catch(e){this.deps.logger.debug("Could not read opencode.json for server URL",{error:(0,n.getErrorMessage)(e)})}return null}async prepareSession(e){const{agent:t,prompt:s,projectPath:r,requestId:i,parentSessionId:a,specialization:c,callerAgentType:l}=e,{logger:d}=this.deps,u=this.deps.projectId,h=this.getOpenCodeServerUrl(r)
120
- if(!h)throw Error("Cannot prepare session: OpenCode server not running (no .alchemy_lab/opencode.json)")
121
- const g=e.title?e.title:i?`[${i}] ${s.slice(0,40)}${s.length>40?"...":""}`:`${s.slice(0,50)}${s.length>50?"...":""}`,p={agentType:t}
122
- i&&(p.requestId=i),c&&(p.specialization=c),l&&(p.callerAgentType=l)
123
- const f=await fetch(h+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:g,...a?{parentID:a}:{},metadata:p})})
196
+ const r=t(2424),s=t(833),o=t(8824),n=t(5671),i=t(8955)
197
+ exports.SessionSpawnService=class{constructor(e){this.deps=e}getOpenCodeServerUrl(e){return(0,s.getOpenCodeUrlFromConfig)(e)}async prepareSession(e){const{agent:t,prompt:s,projectPath:n,requestId:i,parentSessionId:a,specialization:c,specializationHeadline:l,callerAgentType:d}=e,{logger:u}=this.deps,h=this.deps.projectId,g=this.getOpenCodeServerUrl(n)
198
+ if(!g)throw Error("Cannot prepare session: OpenCode server not running (no .alchemy_lab/opencode.json)")
199
+ const p=e.title?e.title:i?`[${i}] ${s.slice(0,40)}${s.length>40?"...":""}`:`${s.slice(0,50)}${s.length>50?"...":""}`,m={agentType:t}
200
+ i&&(m.requestId=i),c&&(m.specialization=c),l&&(m.specializationHeadline=l),d&&(m.callerAgentType=d)
201
+ const f=await fetch(g+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:p,...a?{parentID:a}:{},metadata:m})})
124
202
  if(!f.ok)throw Error("Failed to create session in OpenCode: "+f.status)
125
- const m=await f.json()
126
- if(!m.id)throw Error("Session created in OpenCode but no ID returned")
127
- const y=m.id
128
- d.info("Pre-created OpenCode session",{sessionId:y,title:g,agent:t})
129
- const v=this.registerSession({sessionId:y,parentSessionId:a,projectId:u,agent:t,title:g,specialization:c,callerAgentType:l,requestId:i}),w=this.resolveModel(t,r),I=i?(0,o.ensureRequestWorktree)(r,i,d).catch((e=>{d.warn("Pre-spawn worktree creation failed (non-fatal)",{requestId:i,error:(0,n.getErrorMessage)(e)})})):Promise.resolve(),[,k]=await Promise.all([v,w,I])
130
- return{sessionId:y,model:k,serverUrl:h,initialBranch:i?"requests/"+i:void 0}}async registerSession(e){const{sessionId:t,parentSessionId:s,projectId:r,agent:o,title:i,specialization:a,callerAgentType:c,requestId:l}=e,{apiClient:d,logger:u}=this.deps
131
- try{let e,h
132
- if(s)try{const t=await d.post(`/api/projects/${r}/cli-sessions`,{externalSessionId:s,providerId:"opencode",status:"active",agentType:c||void 0}),o=t?.data||t
133
- e=o?.id,h=o?.workflowId,u.debug("Registered parent session",{parentSessionId:s,parentDbId:e,parentWorkflowId:h})}catch(e){u.warn("Failed to register parent session",{error:(0,n.getErrorMessage)(e)})}if(await d.post(`/api/projects/${r}/cli-sessions`,{externalSessionId:t,providerId:"opencode",parentSessionId:e||null,title:i,status:"active",agentType:o,specialization:a,workflowId:h||void 0}),u.info("Registered session in alchemy DB",{sessionId:t,agent:o,parentSessionId:s}),l){try{await d.post(`/api/projects/${r}/session-request-associations`,{opencodeSessionId:t,requestLocalId:l}),u.info("Created session-request association",{sessionId:t,requestId:l})}catch(e){u.warn("Failed to create session-request association",{error:(0,n.getErrorMessage)(e)})}if("coder"===o)try{await d.patch(`/api/projects/${r}/requests/${l}`,{cliOrigin:{providerId:"opencode",sessionId:t,createdAt:(new Date).toISOString()}}),u.debug("Updated request cliOrigin",{sessionId:t,requestId:l})}catch(e){u.warn("Failed to update request cliOrigin",{error:(0,n.getErrorMessage)(e)})}}}catch(e){u.warn("Failed to register session in alchemy DB",{error:(0,n.getErrorMessage)(e)})}}async resolveModel(e,t){const{logger:s}=this.deps,r=this.fetchProjectDefaultModel()||void 0
134
- let o
135
- try{const s=await this.fetchAgentConfig(e)
136
- if(s){const e=s.preferredProvider||"inherit",n=s.preferredModelTier||"same",a=(0,i.resolveAgentModel)(t,e,n,r)
137
- a.resolved&&(o=a.fullModel)}else{const s=(0,a.getBuiltInAgentConfig)(e)
138
- if(s){const e=(0,i.resolveAgentModel)(t,s.preferredProvider||"inherit",s.preferredModelTier||"same",r)
139
- e.resolved&&(o=e.fullModel)}}}catch(t){s.debug("Error resolving agent model",{agent:e,error:(0,n.getErrorMessage)(t)})}return!o&&r&&(o=r),o}async fetchAgentConfig(e){try{const t=await this.deps.apiClient.get(`/api/projects/${this.deps.projectId}/agents`)
140
- return(t?.data||t||[]).find((t=>t.name===e))||null}catch{return null}}fetchProjectDefaultModel(){return this.deps.getDefaultModel()||null}}},1405(e,exports,t){"use strict"
141
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleStartBuildingAction=async function(e,t){const{plannerSessionId:o,parentSessionId:n,requestIds:i,summary:a}=e,{logger:c,apiClient:l,projectId:d,projectPath:u}=t
142
- if(!o)return{success:!1,message:"plannerSessionId is required",error:"missing_params"}
143
- c.info("Starting start-building action",{plannerSessionId:o,requestIds:i,payloadParent:n})
144
- let h=null
145
- try{const e=await l.get(`/api/projects/${d}/cli-sessions/resolve/${o}`)
146
- e.success&&(h=e.data)}catch(e){c.warn("Failed to resolve planner session",{error:(0,s.getErrorMessage)(e)})}const g=n||h?.parent?.externalSessionId||null
147
- if(!g)return{success:!1,message:"Could not find parent session for the planner. The planner session may not have been properly linked at creation time.",error:"parent_not_found"}
148
- c.info("Resolved parent session",{plannerSessionId:o,parentSessionId:g})
149
- const p=async(e,t)=>{try{return await t()}catch(r){return c.warn(e+": first attempt failed, retrying",{error:(0,s.getErrorMessage)(r)}),await new Promise((e=>setTimeout(e,500))),await t()}}
203
+ const y=await f.json()
204
+ if(!y.id)throw Error("Session created in OpenCode but no ID returned")
205
+ const v=y.id
206
+ u.info("Pre-created OpenCode session",{sessionId:v,title:p,agent:t})
207
+ const w=this.registerSession({sessionId:v,parentSessionId:a,projectId:h,agent:t,title:p,specialization:c,specializationHeadline:l,callerAgentType:d,requestId:i}),b=this.resolveModel(t,n),I=i?(0,r.ensureRequestWorktree)(n,i,u).catch((e=>{u.warn("Pre-spawn worktree creation failed (non-fatal)",{requestId:i,error:(0,o.getErrorMessage)(e)})})):Promise.resolve(),[,E]=await Promise.all([w,b,I])
208
+ return{sessionId:v,model:E,serverUrl:g,initialBranch:i?"requests/"+i:void 0}}async registerSession(e){const{sessionId:t,parentSessionId:r,projectId:s,agent:n,title:i,specialization:a,specializationHeadline:c,callerAgentType:l,requestId:d}=e,{apiClient:u,logger:h}=this.deps
209
+ try{let e,g
210
+ if(r)try{const t=await u.post(`/api/projects/${s}/cli-sessions`,{externalSessionId:r,providerId:"opencode",status:"active",agentType:l||void 0}),o=t?.data||t
211
+ e=o?.id,g=o?.workflowId,h.info("Parent session upsert result",{parentExternalId:r,parentDbId:e??null,parentWorkflowId:g??null,responseHasSuccess:void 0!==t?.success,responseDataKeys:o?Object.keys(o):[]})}catch(e){h.warn("Failed to register parent session",{parentSessionId:r,error:(0,o.getErrorMessage)(e)})}const p={externalSessionId:t,providerId:"opencode",parentSessionId:e||null,title:i,status:"active",agentType:n,specialization:a,specializationHeadline:c,workflowId:g||void 0}
212
+ if(h.info("Child session upsert payload",{sessionId:t,agent:n,parentExternalId:r??null,parentDbIdSent:p.parentSessionId,workflowIdSent:p.workflowId??null}),await u.post(`/api/projects/${s}/cli-sessions`,p),h.info("Registered session in alchemy DB",{sessionId:t,agent:n,parentSessionId:r}),d){try{await u.post(`/api/projects/${s}/session-request-associations`,{opencodeSessionId:t,requestLocalId:d}),h.info("Created session-request association",{sessionId:t,requestId:d})}catch(e){h.warn("Failed to create session-request association",{error:(0,o.getErrorMessage)(e)})}if("coder"===n)try{await u.patch(`/api/projects/${s}/requests/${d}`,{cliOrigin:{providerId:"opencode",sessionId:t,createdAt:(new Date).toISOString()}}),h.debug("Updated request cliOrigin",{sessionId:t,requestId:d})}catch(e){h.warn("Failed to update request cliOrigin",{error:(0,o.getErrorMessage)(e)})}}}catch(e){h.warn("Failed to register session in alchemy DB",{error:(0,o.getErrorMessage)(e)})}}async resolveModel(e,t){const{logger:r}=this.deps,s=this.fetchProjectDefaultModel()||void 0
213
+ let a
214
+ try{const r=await this.fetchAgentConfig(e)
215
+ if(r){const e=r.preferredProvider||"inherit",o=r.preferredModelTier||"same",i=(0,n.resolveAgentModel)(t,e,o,s)
216
+ i.resolved&&(a=i.fullModel)}else{const r=(0,i.getBuiltInAgentConfig)(e)
217
+ if(r){const e=(0,n.resolveAgentModel)(t,r.preferredProvider||"inherit",r.preferredModelTier||"same",s)
218
+ e.resolved&&(a=e.fullModel)}}}catch(t){r.debug("Error resolving agent model",{agent:e,error:(0,o.getErrorMessage)(t)})}return!a&&s&&(a=s),a}async fetchAgentConfig(e){try{const t=await this.deps.apiClient.get(`/api/projects/${this.deps.projectId}/agents`)
219
+ return(t?.data||t||[]).find((t=>t.name===e))||null}catch{return null}}fetchProjectDefaultModel(){return this.deps.getDefaultModel()||null}}},1401(e,exports,t){"use strict"
220
+ function r(e,t){let r=0,s=0
221
+ for(const o of e)exports.IN_FLIGHT_REQUEST_STATUSES.has(o.status)?r++:t.has(o.id)&&s++
222
+ return{openCount:r+s,statusCount:r,taskOnlyCount:s}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.QuestTickScheduler=exports.IN_FLIGHT_REQUEST_STATUSES=void 0,exports.computeOpenCount=r
223
+ const s=t(4769),o=t(3416)
224
+ exports.IN_FLIGHT_REQUEST_STATUSES=new Set(["open","in_progress","resolving_conflicts","ready_for_review","ready_for_merge"]),exports.QuestTickScheduler=class{constructor(e){this.inFlight=new Set,this.cachedQuests=[],this.lastQuestsFetch=0,this.cachedAlchemistSessionId=null,this.cachedAlchemistAt=0,this.apiClient=e.apiClient,this.logger=e.logger,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath}start(){this.tickTimer||(this.logger.info("Quest tick scheduler starting",{tickInterval:1e4}),setTimeout((()=>this.tick().catch((()=>{}))),5e3),this.tickTimer=setInterval((()=>{this.tick().catch((()=>{}))}),1e4))}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.inFlight.clear(),this.logger.info("Quest tick scheduler stopped")}async tick(){if(!this.projectId)return
225
+ let e
226
+ try{e=await this.fetchOpenQuests()}catch(e){return void this.logger.warn("Quest tick: fetch quests failed",{error:e+""})}if(0!==e.length){this.logger.info("Quest tick scan",{openQuests:e.length,projectId:this.projectId})
227
+ for(const t of e){if(this.inFlight.has(t.id))continue
228
+ const e=t.metadata||{}
229
+ if((e.status||o.QUEST_STATUS_OPEN)!==o.QUEST_STATUS_OPEN)continue
230
+ const r=e.tickIntervalSeconds||o.QUEST_DEFAULTS.tickIntervalSeconds,s=e.lastTickAt?Date.parse(e.lastTickAt):NaN,n=Number.isFinite(s)?s:0,i=Date.now()
231
+ n>0&&r>(i-n)/1e3||(this.inFlight.add(t.id),this.fireTick(t).finally((()=>this.inFlight.delete(t.id))))}}}async fireTick(e){const t=e.metadata||{},n=t.maxInFlight??o.QUEST_DEFAULTS.maxInFlight,i=t.maxPerDay??o.QUEST_DEFAULTS.maxPerDay,a=e.localId||e.id
232
+ let c
233
+ try{c=await this.fetchQuestRequests(e.id)}catch(e){return void this.logger.warn("Quest tick: fetch requests failed; skipping",{questId:a,error:e+""})}const l=new Set
234
+ try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/tasks?status=in_progress,pending&limit=1000`),t=e?.data||e||[],r=new Set(c.map((e=>e.id)))
235
+ for(const e of t)e.requestId&&r.has(e.requestId)&&l.add(e.requestId)}catch(e){this.logger.warn("Quest tick: task fetch failed; counting by status only",{questId:a,error:e+""})}const{openCount:d,statusCount:u,taskOnlyCount:h}=r(c,l)
236
+ if(d>=n)return void this.logger.info("Quest tick: maxInFlight reached, skipping",{questId:a,openCount:d,maxInFlight:n,statusCount:u,taskOnlyCount:h})
237
+ const g=(new Date).toISOString().slice(0,10),p=t.ticksTodayDate===g?t.ticksTodayCount??0:0
238
+ if(p>=i)return void this.logger.info("Quest tick: maxPerDay reached, skipping",{questId:a,ticksToday:p,maxPerDay:i})
239
+ const m=await this.findAlchemistSessionId()
240
+ if(!m)return void this.logger.info("Quest tick: no persistent alchemist session in project, skipping",{questId:a,projectId:this.projectId})
241
+ const f=(t.currentIteration||0)+1
242
+ let y=!1
243
+ try{y=await this.bumpQuestMetadata(e,f)}catch(e){return void this.logger.warn("Quest tick: metadata PATCH failed; skipping prompt",{questId:a,error:e+""})}if(!y)return void this.logger.info("Quest tick: status changed during tick; skipping prompt",{questId:a})
244
+ const v=this.buildTickPrompt(e,f,d,h,p+1),w={logger:this.logger,projectId:this.projectId,projectPath:this.projectPath,apiClient:this.apiClient,cachedDefaultModel:this.getDefaultModel()??null},b={alchemy:{kind:"quest-tick",payload:{questId:a,iteration:f,criteria:t.criteria||"",openCount:d,taskOnlyCount:h,maxInFlight:n,ticksToday:p+1,maxPerDay:i,minIterations:t.minIterations??o.QUEST_DEFAULTS.minIterations}}}
245
+ try{await(0,s.executeSameSession)(v,m,w,b),this.logger.info("Quest tick fired",{questId:a,iteration:f,alchemistSessionId:m})}catch(e){this.logger.warn("Quest tick: prompt send failed",{questId:a,error:e+""})}}invalidateCache(){this.lastQuestsFetch=0}invalidateAlchemistCache(){this.cachedAlchemistSessionId=null,this.cachedAlchemistAt=0}async fetchOpenQuests(){const e=Date.now()
246
+ if(18e5>e-this.lastQuestsFetch)return this.cachedQuests
247
+ try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/plans?type=Quest&limit=100`),r=t?.data||t||[]
248
+ return this.cachedQuests=r.filter((e=>e&&(e.metadata?.status||o.QUEST_STATUS_OPEN)===o.QUEST_STATUS_OPEN)),this.lastQuestsFetch=e,this.cachedQuests}catch(t){throw this.lastQuestsFetch=e,t}}async fetchQuestRequests(e){const t=await this.apiClient.get(`/api/projects/${this.projectId}/requests?questDocumentId=${encodeURIComponent(e)}&limit=1000`)
249
+ return(t?.data||t||[]).map((e=>({id:e.id,status:e.status,questDocumentId:e.questDocumentId,createdAt:e.createdAt})))}async findAlchemistSessionId(){if(this.cachedAlchemistAt>0&&18e5>Date.now()-this.cachedAlchemistAt)return this.cachedAlchemistSessionId
250
+ const e=e=>(this.cachedAlchemistSessionId=e,this.cachedAlchemistAt=Date.now(),e)
251
+ try{const t=await this.apiClient.get("/api/projects/"+this.projectId),r=t?.data||t,s=r?.activeAlchemistSessionId
252
+ if(s){const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/${s}`),r=t?.data||t
253
+ if(r?.externalSessionId&&"active"===r?.status)return e(r.externalSessionId)
254
+ this.logger.info("Quest tick: project active alchemist not active, ignoring",{projectId:this.projectId,sessionUlid:s,status:r?.status})}}catch(e){this.logger.warn("Quest tick: project active-alchemist lookup failed",{error:e+""})}try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/agent-types`),r=(t?.data||t||[]).filter((e=>"alchemist"===e.agentType))
255
+ return 0===r.length?(this.logger.info("Quest tick: no alchemist session found in project",{projectId:this.projectId}),e(null)):(this.logger.info("Quest tick: active-alchemist not set, using first alchemist as fallback",{projectId:this.projectId,alchemistCount:r.length}),e(r[0].externalSessionId))}catch(e){return this.logger.warn("Quest tick: alchemist fallback lookup failed",{error:e+""}),null}}async bumpQuestMetadata(e,t){const r=await this.apiClient.get(`/api/projects/${this.projectId}/documents/${encodeURIComponent(e.id)}`),s=r?.data||r,n=s?.metadata||{}
256
+ if((n.status||o.QUEST_STATUS_OPEN)!==o.QUEST_STATUS_OPEN)return!1
257
+ const i=(new Date).toISOString().slice(0,10),a=(n.ticksTodayDate===i?n.ticksTodayCount??0:0)+1,c={...n,lastTickAt:(new Date).toISOString(),currentIteration:t,ticksTodayDate:i,ticksTodayCount:a}
258
+ return await this.apiClient.patch(`/api/projects/${this.projectId}/documents/${encodeURIComponent(e.id)}`,{metadata:c}),!0}buildTickPrompt(e,t,r,s,n){const i=e.metadata||{},a=e.localId||e.id,c=i.minIterations??o.QUEST_DEFAULTS.minIterations
259
+ return[`[Quest tick: ${a}, iteration ${t}]`,"","Filename: "+(e.filename||"(unknown)"),"Criteria: "+(i.criteria||"(none specified)"),"",`Caps: maxInFlight=${i.maxInFlight??o.QUEST_DEFAULTS.maxInFlight}, maxPerDay=${i.maxPerDay??o.QUEST_DEFAULTS.maxPerDay}, minIterations=${c}.`,`Currently in flight under this quest: ${r}${s>0?` (incl. ${s} via in-progress/pending tasks under non-active requests)`:""}. Ticks today (incl. this): ${n}/${i.maxPerDay??o.QUEST_DEFAULTS.maxPerDay}.`,'\nDecide what to take action on this tick. If progress requires a request, call specialist_ops with operation="delegate". Inside the prompt you write for the specialist, you MUST include this exact line so the spawned request is tagged to the quest:',` "When you call create_request, pass questDocumentId="${a}"."`,"Without that instruction in the specialist's prompt, the spawned request will not be tagged and the runner can't track it.\nDo NOT mark this quest complete \u2014 quest end is a user action, not yours. Just do this tick's work and stop."].join("\n")}}},1405(e,exports,t){"use strict"
260
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleStartBuildingAction=async function(e,t){const{plannerSessionId:n,parentSessionId:i,requestIds:a,summary:c}=e,{logger:l,apiClient:d,projectId:u,projectPath:h}=t
261
+ if(!n)return{success:!1,message:"plannerSessionId is required",error:"missing_params"}
262
+ l.info("Starting start-building action",{plannerSessionId:n,requestIds:a,payloadParent:i})
263
+ let g=null
264
+ try{const e=await d.get(`/api/projects/${u}/cli-sessions/resolve/${n}`)
265
+ e.success&&(g=e.data)}catch(e){l.warn("Failed to resolve planner session",{error:(0,r.getErrorMessage)(e)})}const p=i||g?.parent?.externalSessionId||null
266
+ if(!p)return{success:!1,message:"Could not find parent session for the planner. The planner session may not have been properly linked at creation time.",error:"parent_not_found"}
267
+ l.info("Resolved parent session",{plannerSessionId:n,parentSessionId:p})
268
+ const m=async(e,t)=>{try{return await t()}catch(s){return l.warn(e+": first attempt failed, retrying",{error:(0,r.getErrorMessage)(s)}),await new Promise((e=>setTimeout(e,500))),await t()}}
150
269
  let f=!1
151
- try{await p("create report",(()=>l.post(`/api/projects/${d}/session-messages/reports`,{fromSessionId:o,toSessionId:g,status:"success",summary:"Plan complete. Created requests: "+(i||[]).join(", "),details:a||"Plan complete"}))),f=!0}catch(e){c.error("Failed to create report after retry",{error:(0,s.getErrorMessage)(e)})}let m=!1
152
- const y=(0,r.getOpenCodeServerUrl)(u,c)
153
- if(y){const e=`\x3c!-- inserted-prompt --\x3eThe planner has finished creating the plan.\n\nCreated requests: ${(i||[]).join(", ")}\n\nPlease begin working on these requests.`
154
- try{const s=await p("notify parent",(()=>fetch(`${y}/session/${g}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e}],...t.cachedDefaultModel?(()=>{const[e,...s]=t.cachedDefaultModel.split("/")
155
- return{model:{providerID:e,modelID:s.join("/")}}})():{}})})))
156
- s.ok?(m=!0,c.info("Notified parent session to start building",{parentSessionId:g})):c.error("Parent session prompt returned non-OK",{status:s.status,parentSessionId:g})}catch(e){c.error("Could not notify parent session after retry",{error:(0,s.getErrorMessage)(e)})}}else c.error("Could not resolve OpenCode server URL \u2014 parent prompt skipped")
157
- if(f&&m){const e=h?.id
158
- if(e)try{await l.patch(`/api/projects/${d}/cli-sessions/${e}`,{status:"archived"}),c.info("Archived planner session",{plannerSessionId:o,sessionLocalId:e})}catch(e){c.warn("Failed to archive planner session (non-fatal)",{error:(0,s.getErrorMessage)(e)})}}return f&&m?{success:!0,message:"Build report sent to parent session",data:{parentSessionId:g}}:{success:!1,message:`Build handoff failed (report: ${f}, prompt: ${m}). The parent session was not fully notified \u2014 please retry.`,error:"handoff_partial_failure",data:{parentSessionId:g,reportOk:f,promptOk:m}}}
159
- const s=t(8824),r=t(8148)},1943(module){"use strict"
270
+ try{await m("create report",(()=>d.post(`/api/projects/${u}/session-messages/reports`,{fromSessionId:n,toSessionId:p,status:"success",summary:"Plan complete. Created requests: "+(a||[]).join(", "),details:c||"Plan complete"}))),f=!0}catch(e){l.error("Failed to create report after retry",{error:(0,r.getErrorMessage)(e)})}let y=!1
271
+ const v=(0,o.getOpenCodeServerUrl)(h,l)
272
+ if(v){const e=`The planner has finished creating the plan.\n\nCreated requests: ${(a||[]).join(", ")}\n\nPlease begin working on these requests.`
273
+ try{const r=await m("notify parent",(()=>fetch(`${v}/session/${p}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e,metadata:(0,s.partMeta)("inserted-prompt")}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
274
+ return{model:{providerID:e,modelID:r.join("/")}}})():{}})})))
275
+ r.ok?(y=!0,l.info("Notified parent session to start building",{parentSessionId:p})):l.error("Parent session prompt returned non-OK",{status:r.status,parentSessionId:p})}catch(e){l.error("Could not notify parent session after retry",{error:(0,r.getErrorMessage)(e)})}}else l.error("Could not resolve OpenCode server URL \u2014 parent prompt skipped")
276
+ if(f&&y){const e=g?.id
277
+ if(e)try{await d.patch(`/api/projects/${u}/cli-sessions/${e}`,{status:"archived"}),l.info("Archived planner session",{plannerSessionId:n,sessionLocalId:e})}catch(e){l.warn("Failed to archive planner session (non-fatal)",{error:(0,r.getErrorMessage)(e)})}}return f&&y?{success:!0,message:"Build report sent to parent session",data:{parentSessionId:p}}:{success:!1,message:`Build handoff failed (report: ${f}, prompt: ${y}). The parent session was not fully notified \u2014 please retry.`,error:"handoff_partial_failure",data:{parentSessionId:p,reportOk:f,promptOk:y}}}
278
+ const r=t(8824),s=t(2272),o=t(8148)},1943(module){"use strict"
160
279
  module.exports=require("fs/promises")},2096(e,exports,t){"use strict"
161
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
280
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
162
281
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectConfigService=void 0
163
- const r=s(t(1943)),o=s(t(6928)),n=s(t(857)),i=t(4139),a=t(8824)
164
- exports.ProjectConfigService=class{constructor(e){this.logger=e.child({component:"project-config"})}async readProjectId(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"project.json"),s=await r.default.readFile(t,"utf-8"),n=JSON.parse(s)
165
- return n.id?n.id+"":null}catch(e){return this.logger.debug("Could not read project ID",{error:(0,a.getErrorMessage)(e)}),null}}async readProjectConfig(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"config.json"),s=await r.default.readFile(t,"utf-8")
166
- return JSON.parse(s)}catch(e){return this.logger.debug("Could not read project config",{error:(0,a.getErrorMessage)(e)}),{}}}async saveProjectConfig(e,t){try{const s=o.default.join(e,i.ALCHEMY_DIR_NAME),n=o.default.join(s,"config.json")
167
- await r.default.mkdir(s,{recursive:!0})
282
+ const s=r(t(1943)),o=r(t(6928)),n=r(t(857)),i=t(4139),a=t(8824)
283
+ exports.ProjectConfigService=class{constructor(e){this.logger=e.child({component:"project-config"})}async readProjectId(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"project.json"),r=await s.default.readFile(t,"utf-8"),n=JSON.parse(r)
284
+ return n.id?n.id+"":null}catch(e){return this.logger.debug("Could not read project ID",{error:(0,a.getErrorMessage)(e)}),null}}async readProjectConfig(e){try{const t=o.default.join(e,i.ALCHEMY_DIR_NAME,"config.json"),r=await s.default.readFile(t,"utf-8")
285
+ return JSON.parse(r)}catch(e){return this.logger.debug("Could not read project config",{error:(0,a.getErrorMessage)(e)}),{}}}async saveProjectConfig(e,t){try{const r=o.default.join(e,i.ALCHEMY_DIR_NAME),n=o.default.join(r,"config.json")
286
+ await s.default.mkdir(r,{recursive:!0})
168
287
  let a={}
169
- try{const e=await r.default.readFile(n,"utf-8")
170
- a=JSON.parse(e)}catch(e){this.logger.debug("No existing config.json, creating new one")}void 0!==t&&(a.autoJournal=t),a.lockTimeout||(a.lockTimeout=3600),a.worktreeCleanupDelay||(a.worktreeCleanupDelay=900),a.maxWorktreeAge||(a.maxWorktreeAge=24),a.cleanupInterval||(a.cleanupInterval=15),void 0===a.preserveBranchesOnCleanup&&(a.preserveBranchesOnCleanup=!0),a.maxConcurrentSessions||(a.maxConcurrentSessions=10),a.sessionTimeoutWarning||(a.sessionTimeoutWarning=45),await r.default.writeFile(n,JSON.stringify(a,null,2),"utf-8"),this.logger.info("Saved project config",{configPath:n})}catch(e){this.logger.error("Failed to save project config",{error:(0,a.getErrorMessage)(e)})}}getToolPreferencesPath(){const e=n.default.homedir()
288
+ try{const e=await s.default.readFile(n,"utf-8")
289
+ a=JSON.parse(e)}catch(e){this.logger.debug("No existing config.json, creating new one")}void 0!==t&&(a.autoJournal=t),a.lockTimeout||(a.lockTimeout=3600),a.worktreeCleanupDelay||(a.worktreeCleanupDelay=900),a.maxWorktreeAge||(a.maxWorktreeAge=24),a.cleanupInterval||(a.cleanupInterval=15),void 0===a.preserveBranchesOnCleanup&&(a.preserveBranchesOnCleanup=!0),a.maxConcurrentSessions||(a.maxConcurrentSessions=10),a.sessionTimeoutWarning||(a.sessionTimeoutWarning=45),await s.default.writeFile(n,JSON.stringify(a,null,2),"utf-8"),this.logger.info("Saved project config",{configPath:n})}catch(e){this.logger.error("Failed to save project config",{error:(0,a.getErrorMessage)(e)})}}getToolPreferencesPath(){const e=n.default.homedir()
171
290
  return o.default.join(e,".alchemy_lab","tool-preferences.json")}async loadToolPreferences(e){const t=this.getToolPreferencesPath()
172
- try{const s=await r.default.readFile(t,"utf-8"),o=JSON.parse(s)
173
- return e&&o[e]?o[e]:e?null:o}catch(t){return this.logger.debug("No user preferences found",{error:(0,a.getErrorMessage)(t)}),e?null:{}}}async saveToolPreferences(e,t,s,n){const i=this.getToolPreferencesPath(),a=o.default.dirname(i)
174
- this.logger.info("Saving user tool preferences",{clientType:e,preset:t,disabledToolsCount:s?.length||0,allowedToolsCount:n?.length||0}),await r.default.mkdir(a,{recursive:!0})
291
+ try{const r=await s.default.readFile(t,"utf-8"),o=JSON.parse(r)
292
+ return e&&o[e]?o[e]:e?null:o}catch(t){return this.logger.debug("No user preferences found",{error:(0,a.getErrorMessage)(t)}),e?null:{}}}async saveToolPreferences(e,t,r,n){const i=this.getToolPreferencesPath(),a=o.default.dirname(i)
293
+ this.logger.info("Saving user tool preferences",{clientType:e,preset:t,disabledToolsCount:r?.length||0,allowedToolsCount:n?.length||0}),await s.default.mkdir(a,{recursive:!0})
175
294
  let c={}
176
- try{const e=await r.default.readFile(i,"utf-8")
177
- c=JSON.parse(e)}catch(e){}c[e]={preset:t,disabledTools:s||[],allowedAlchemyTools:n||[],updatedAt:(new Date).toISOString()},await r.default.writeFile(i,JSON.stringify(c,null,2),"utf-8"),this.logger.info("User tool preferences saved",{clientType:e,prefsFile:i})}}},2366(e,exports,t){"use strict"
178
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
295
+ try{const e=await s.default.readFile(i,"utf-8")
296
+ c=JSON.parse(e)}catch(e){}c[e]={preset:t,disabledTools:r||[],allowedAlchemyTools:n||[],updatedAt:(new Date).toISOString()},await s.default.writeFile(i,JSON.stringify(c,null,2),"utf-8"),this.logger.info("User tool preferences saved",{clientType:e,prefsFile:i})}}},2272(e,exports){"use strict"
297
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.partMeta=function(e,t){return t?{alchemy:{kind:e,payload:t}}:{alchemy:{kind:e}}},exports.alchemyMeta=function(e){return{_meta:{alchemy:e}}},exports.withAlchemyKind=function(e,t,r){const s=e._meta,o=s?.alchemy??{}
298
+ return{...e,_meta:{...s,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},2366(e,exports,t){"use strict"
299
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
179
300
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.AlchemyActionServer=void 0
180
- const r=s(t(7252)),o=s(t(8577)),n=t(4192),i=t(1152),a=s(t(6928)),c=s(t(9896)),l=s(t(857)),d=t(5607),u=t(8984),h=t(8524),g=t(8824),p=t(2096),f=t(7210),m=t(2928),y=t(6563),v=t(1292),w=t(5317)
181
- class I{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.activeMcpCalls=new Map,this.pendingAgentResync=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
182
- const s=(0,i.createMetricsCollector)(!1)
183
- this.apiClient=new n.BaseApiClient(t.remoteServerUrl,e,s,t.authToken,{timeout:35e3}),this.configurer=new u.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new h.CLIBridgeWebSocket(e),(0,h.setCLIBridge)(this.cliBridge),this.hookExecutor=new f.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,r.default)()
184
- return e.use(((e,t,s)=>{t.setHeader("Access-Control-Allow-Private-Network","true"),s()})),e.use((0,o.default)()),e.use(r.default.json({limit:"10mb"})),e.get("/health",this.handleHealth.bind(this)),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.post("/api/generate-journal",this.handleGenerateJournal.bind(this)),e.get("/api/event-hooks/by-event/:eventType",this.handleEventHooksProxy.bind(this)),e.post("/api/hook-triggers",this.handleHookTriggersProxy.bind(this)),e.get("/api/hook-iterations/:sessionId/:hookId",this.handleGetHookIteration.bind(this)),e.post("/api/hook-iterations/:sessionId/:hookId/increment",this.handleIncrementHookIteration.bind(this)),e.get("/api/project-settings",((e,t)=>{t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.post("/api/sync-settings",(async(e,t)=>{await this.syncProjectSettings(),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",(async(e,t)=>{try{const{sessionId:s}=e.params,r=this.projectId
185
- if(!r)return void t.status(400).json({success:!1,error:"No project"})
186
- const o=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),n=(o?.data||o||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title})))
187
- t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/registry/projects",(async(e,s)=>{try{const{listLive:e}=await t.e(640).then(t.bind(t,2640))
188
- s.json({success:!0,data:e()})}catch(e){s.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/mcp/tool-call-event",((e,t)=>{const{action:s,callId:r}=e.body||{}
189
- !r||"start"!==s&&"end"!==s?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(s,r+""),t.json({success:!0}))})),e.post("/api/hook-event",(async(e,t)=>{try{const{eventType:s,sessionId:r,variables:o,timing:n}=e.body
190
- if(!s)return void t.status(400).json({success:!1,error:"eventType is required"})
191
- const i=await this.hookExecutor.processEvent({eventType:s,sessionId:r||"",variables:o||{},timing:n})
192
- t.json({success:!0,data:i})}catch(e){this.logger.error("Hook event processing failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.get("/api/timer-hooks/status",((e,t)=>{const s=this.timerHookScheduler?.getStatus()??{running:!1,trackedHooks:0,inFlight:[],hooks:[]}
193
- t.json({success:!0,data:s})})),e.post("/api/timer-hooks/:hookId/pause",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
194
- const s=this.timerHookScheduler.pauseHook(e.params.hookId)
195
- t.json({success:s})})),e.post("/api/timer-hooks/:hookId/resume",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
196
- const s=this.timerHookScheduler.resumeHook(e.params.hookId)
197
- t.json({success:s})})),e.post("/api/timer-hooks/:hookId/stop",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
198
- const s=this.timerHookScheduler.stopHook(e.params.hookId)
199
- t.json({success:s})})),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,s)=>{try{const{execSync:e}=await Promise.resolve().then(t.t.bind(t,5317,23)),r=e("npm root -g",{encoding:"utf8"}).trim()
200
- try{c.default.accessSync(r,c.default.constants.W_OK)}catch{return void s.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:o}=await Promise.resolve().then(t.t.bind(t,5317,23))
201
- s.json({success:!0,message:"Update started. The CLI will restart when complete."}),o("npm install -g alchemylab@latest",{timeout:12e4},((e,t,s)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:s}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout((()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)}),1e3))}))}catch(e){s.json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),timestamp:(new Date).toISOString()})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const s=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
202
- try{if(!c.default.existsSync(s))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
203
- const e=c.default.readFileSync(s,"utf-8"),r=JSON.parse(e)
204
- t.json({port:r.port,pid:r.pid,startedAt:r.startedAt,baseUrl:"http://localhost:"+r.port,directory:this.options.projectPath,proxyEnabled:!0})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,g.getErrorMessage)(e)})}}updateDefaultModel(e){void 0!==e&&e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),e&&this.projectId&&this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,g.getErrorMessage)(e)}))))}handleMcpToolCallEvent(e,t){"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.logger.info("Firing deferred agent resync now that MCP is idle"),this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync failed",{error:(0,g.getErrorMessage)(e)}))))):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval((()=>{const e=Date.now()-12e4
301
+ const s=r(t(7252)),o=r(t(8577)),n=t(4192),i=t(1152),a=r(t(6928)),c=r(t(9896)),l=r(t(857)),d=t(5607),u=t(8984),h=t(8524),g=t(8824),p=t(2096),m=t(7210),f=t(7227),y=t(5419),v=t(3962),w=t(2928),b=t(6563),I=t(1401),E=t(1292),C=t(8148),S=t(8463),k=t(489),T=t(3807),_=t(7478),j=t(5317),A=t(355),P=t(833)
302
+ class M{updateAuthToken(e){this.options.authToken=e,this.apiClient.authToken=e,this.configurer.authToken=e}constructor(e,t){this.directReportDedup=new Map,this.activeMcpCalls=new Map,this.pendingAgentResync=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.isPolling=!1,this.isProcessingAction=!1,this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.logger=e,this.options=t,this.port=t.port||3456,this.startTime=new Date
303
+ const r=(0,i.createMetricsCollector)(!1)
304
+ this.apiClient=new n.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3}),this.configurer=new u.MCPClientConfigurer(e,t.remoteServerUrl,t.authToken),this.cliBridge=new h.CLIBridgeWebSocket(e),(0,h.setCLIBridge)(this.cliBridge),this.hookExecutor=new m.HookExecutor(e,"",t.projectPath,this.apiClient,{cachedDefaultModel:this.cachedDefaultModel,trackChildPid:e=>this.trackedChildPids.add(e)}),this.app=this.createApp()}createApp(){const e=(0,s.default)()
305
+ return e.use(((e,t,r)=>{t.setHeader("Access-Control-Allow-Private-Network","true"),r()})),e.use((0,o.default)()),e.use(s.default.json({limit:"10mb"})),e.get("/health",this.handleHealth.bind(this)),e.get("/api/auth-status",this.handleAuthStatus.bind(this)),e.get("/status",this.handleStatus.bind(this)),e.get("/opencode",this.handleOpenCodeConfig.bind(this)),e.post("/sync-agents",this.handleSyncAgents.bind(this)),e.post("/api/generate-journal",this.handleGenerateJournal.bind(this)),(0,A.registerHooksRoutes)(e,{apiClient:this.apiClient,logger:this.logger,getProjectId:()=>this.projectId}),e.get("/api/project-settings",((e,t)=>{t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.post("/api/sync-settings",(async(e,t)=>{await this.syncProjectSettings(),t.json({success:!0,data:{defaultModel:this.cachedDefaultModel||null}})})),e.get("/api/agents/by-name/:agentName",this.handleGetAgentByName.bind(this)),e.get("/api/sessions/:sessionId/agent-type",this.handleGetSessionAgentType.bind(this)),e.get("/api/sessions/:sessionId/parent",this.handleGetSessionParent.bind(this)),e.get("/api/sessions/:sessionId/ancestors",(async(e,t)=>{try{const{sessionId:r}=e.params,s=this.projectId
306
+ if(!s)return void t.status(400).json({success:!1,error:"No project"})
307
+ const o=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),n=(o?.data||o||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title})))
308
+ t.json({success:!0,ancestors:n})}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/registry/projects",(async(e,r)=>{try{const{listLive:e}=await t.e(640).then(t.bind(t,2640))
309
+ r.json({success:!0,data:e()})}catch(e){r.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/mcp/tool-call-event",((e,t)=>{const{action:r,callId:s}=e.body||{}
310
+ !s||"start"!==r&&"end"!==r?t.status(400).json({success:!1,error:"action must be start|end, callId required"}):(this.handleMcpToolCallEvent(r,s+""),t.json({success:!0}))})),e.post("/api/hook-event",(async(e,t)=>{try{const{eventType:r,sessionId:s,variables:o,timing:n}=e.body
311
+ if(!r)return void t.status(400).json({success:!1,error:"eventType is required"})
312
+ const i=await this.hookExecutor.processEvent({eventType:r,sessionId:s||"",variables:o||{},timing:n})
313
+ t.json({success:!0,data:i})}catch(e){this.logger.error("Hook event processing failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/tool-event",(async(e,t)=>{try{const{toolName:r,sessionId:s,input:o,output:n,timing:i}=e.body||{}
314
+ if(!r||"before"!==i&&"after"!==i)return void t.status(400).json({success:!1,error:"toolName and timing ('before'|'after') are required"})
315
+ const a=(0,f.parseToolEvents)({toolName:r,sessionId:s||"",input:o,output:n,timing:i})
316
+ let c=0
317
+ for(const e of a)c+=(await this.hookExecutor.processEvent({eventType:e.eventType,sessionId:s||"",variables:e.variables,timing:e.timing})).triggered
318
+ this.logger.info("tool-event received",{toolName:r,timing:i,sessionId:s||"",events:a.length,triggered:c}),t.json({success:!0,data:{events:a.length,triggered:c}})}catch(e){this.logger.error("tool-event processing failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.post("/api/direct-report",this.handleDirectReport.bind(this)),e.post("/api/cli-bridge/command",this.handleCLIBridgeCommand.bind(this)),e.get("/api/cli-bridge/status",this.handleCLIBridgeStatus.bind(this)),e.post("/api/spawn-session",this.handleSpawnSession.bind(this)),e.post("/api/setup-tests",this.handleSetupTests.bind(this)),e.post("/api/setup-commands",this.handleSetupCommands.bind(this)),e.post("/api/run-tests-now",this.handleRunTestsNow.bind(this)),e.get("/api/branch-head",(async(e,t)=>{try{const r="string"==typeof e.query.requestId?e.query.requestId:void 0,s=("string"==typeof e.query.branch?e.query.branch:void 0)||(r?(0,_.generateRequestBranchName)(r,""):void 0)
319
+ if(!s)return void t.status(400).json({success:!1,error:"requestId or branch required"})
320
+ const o=(await(0,T.simpleGit)(this.options.projectPath).revparse([s])).trim()
321
+ t.json({success:!0,data:{branch:s,sha:o}})}catch(e){this.logger.debug("branch-head lookup failed",{error:(0,g.getErrorMessage)(e)}),t.json({success:!0,data:{branch:null,sha:null}})}})),e.post("/api/release-pool-slot",(async(e,t)=>{try{const{worktreePath:r}=e.body||{}
322
+ if(!r||"string"!=typeof r)return void t.status(400).json({success:!1,error:"worktreePath required"});(0,k.releaseSlot)(r),this.logger.info("Released pool slot via report-back",{worktreePath:r}),t.json({success:!0})}catch(e){this.logger.warn("release-pool-slot failed (non-fatal)",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.get("/api/timer-hooks/status",((e,t)=>{const r=this.timerHookScheduler?.getStatus()??{running:!1,trackedHooks:0,inFlight:[],hooks:[]}
323
+ t.json({success:!0,data:r})})),e.post("/api/timer-hooks/:hookId/pause",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
324
+ const r=this.timerHookScheduler.pauseHook(e.params.hookId)
325
+ t.json({success:r})})),e.post("/api/timer-hooks/:hookId/resume",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
326
+ const r=this.timerHookScheduler.resumeHook(e.params.hookId)
327
+ t.json({success:r})})),e.post("/api/timer-hooks/:hookId/stop",((e,t)=>{if(!this.timerHookScheduler)return void t.status(503).json({success:!1,error:"Scheduler not running"})
328
+ const r=this.timerHookScheduler.stopHook(e.params.hookId)
329
+ t.json({success:r})})),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.bind(t,5317,23)),s=e("npm root -g",{encoding:"utf8"}).trim()
330
+ try{c.default.accessSync(s,c.default.constants.W_OK)}catch{return void r.json({success:!1,error:"Permission denied. Run manually: sudo npm install -g alchemylab@latest"})}const{exec:o}=await Promise.resolve().then(t.t.bind(t,5317,23))
331
+ r.json({success:!0,message:"Update started. The CLI will restart when complete."}),o("npm install -g alchemylab@latest",{timeout:12e4},((e,t,r)=>{e?this.logger.error("Self-update failed",{error:e.message,stderr:r}):(this.logger.info("Self-update complete, shutting down for restart",{stdout:t.trim()}),setTimeout((()=>{console.log("\n\x1b[32mUpdate complete. Please restart alchemylab.\x1b[0m\n"),process.exit(0)}),1e3))}))}catch(e){r.json({success:!1,error:(0,g.getErrorMessage)(e)})}})),e.use("/api/opencode",((e,t)=>this.handleOpenCodeProxy(e,t))),e}handleHealth(e,t){t.json({status:"healthy",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),timestamp:(new Date).toISOString()})}async handleAuthStatus(e,t){const r=this.options.tokenManager
332
+ if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),o=e?await r.getToken():null
333
+ t.json({authenticated:e,isPendingAuth:s.isPendingAuth,authUrl:e?void 0:s.authUrl,expiresAt:o?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,g.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
334
+ try{if(!c.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
335
+ const e=c.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
336
+ t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:"http://localhost:"+s.port,directory:this.options.projectPath,proxyEnabled:!0})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,g.getErrorMessage)(e)})}}updateDefaultModel(e){void 0!==e&&e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),e&&this.projectId&&this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,g.getErrorMessage)(e)}))))}handleMcpToolCallEvent(e,t){"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.logger.info("Firing deferred agent resync now that MCP is idle"),this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync failed",{error:(0,g.getErrorMessage)(e)}))))):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval((()=>{const e=Date.now()-12e4
205
337
  let t=0
206
- for(const[s,r]of this.activeMcpCalls)e>r&&(this.activeMcpCalls.delete(s),t++)
338
+ for(const[r,s]of this.activeMcpCalls)e>s&&(this.activeMcpCalls.delete(r),t++)
207
339
  t>0&&(this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),0===this.activeMcpCalls.size&&this.pendingAgentResync&&(this.pendingAgentResync=!1,this.resyncAgentMarkdowns().catch((e=>this.logger.debug("Deferred resync after cleanup failed",{error:(0,g.getErrorMessage)(e)})))))}),6e4))}async resyncAgentMarkdowns(){if(!this.projectId)return
208
340
  if(this.activeMcpCalls.size>0)return this.pendingAgentResync=!0,void this.logger.info("Deferring agent resync until MCP tool calls complete",{inFlight:this.activeMcpCalls.size})
209
341
  const e=await this.configurer.fetchAgentsFromRemote(this.projectId)
210
342
  if(!e?.length)return
211
- const t=await this.configurer.fetchProjectFromRemote(this.projectId),s=t?.metadata?.globalSystemPrompt,r=a.default.join(this.options.projectPath,".opencode","agent")
212
- await c.default.promises.mkdir(r,{recursive:!0})
343
+ const t=await this.configurer.fetchProjectFromRemote(this.projectId),r=t?.metadata?.globalSystemPrompt,s=a.default.join(this.options.projectPath,".opencode","agent")
344
+ await c.default.promises.mkdir(s,{recursive:!0})
213
345
  const o=this.cachedDefaultModel||"anthropic/claude-opus-4-5"
214
346
  for(const t of e){if(!t.enabled)continue
215
- const e=this.configurer.generateAgentMarkdown(t,"alchemy",o,s)
216
- await c.default.promises.writeFile(a.default.join(r,t.name+".md"),e,"utf8")}this.logger.info("Re-synced agent markdowns after model change",{model:o,count:e.length})
217
- try{const e=new v.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}).getOpenCodeServerUrl(this.options.projectPath)
218
- e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&this.logger.info("Triggered OpenCode config reload")}catch(e){this.logger.debug("Failed to trigger OpenCode config reload",{error:(0,g.getErrorMessage)(e)})}}async handleSyncAgents(e,t){try{const e=this.readProjectConfig()
219
- if(!e?.id)return void t.status(400).json({success:!1,error:"No project configuration found"})
220
- this.logger.info("Syncing agents from remote",{projectId:e.id})
221
- const s=await this.configurer.fetchAgentsFromRemote(e.id)
222
- if(!s||0===s.length)return void t.json({success:!0,message:"No agents to sync",count:0})
223
- const r=await this.configurer.fetchProjectFromRemote(e.id)
224
- this.logger.debug("Fetched project for global prompt",{projectId:e.id,hasProject:!!r,hasGlobalSystemPrompt:!!r?.metadata?.globalSystemPrompt})
225
- const o=r?.metadata?.globalSystemPrompt
226
- o?this.logger.info("Using global system prompt from project metadata",{projectId:e.id,promptLength:o.length}):this.logger.debug("No global system prompt found in project metadata")
347
+ const e=this.configurer.generateAgentMarkdown(t,"alchemy",o,r)
348
+ await c.default.promises.writeFile(a.default.join(s,t.name+".md"),e,"utf8")}this.logger.info("Re-synced agent markdowns after model change",{model:o,count:e.length})
349
+ try{const e=this.getOpenCodeUrl()
350
+ e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&this.logger.info("Triggered OpenCode config reload")}catch(e){this.logger.debug("Failed to trigger OpenCode config reload",{error:(0,g.getErrorMessage)(e)})}}async handleSyncAgents(e,t){try{const e=this.projectId
351
+ if(!e)return void t.status(400).json({success:!1,error:"Project not registered"})
352
+ this.logger.info("Syncing agents from remote",{projectId:e})
353
+ const r=await this.configurer.fetchAgentsFromRemote(e)
354
+ if(!r||0===r.length)return void t.json({success:!0,message:"No agents to sync",count:0})
355
+ const s=await this.configurer.fetchProjectFromRemote(e)
356
+ this.logger.debug("Fetched project for global prompt",{projectId:e,hasProject:!!s,hasGlobalSystemPrompt:!!s?.metadata?.globalSystemPrompt})
357
+ const o=s?.metadata?.globalSystemPrompt
358
+ o?this.logger.info("Using global system prompt from project metadata",{projectId:e,promptLength:o.length}):this.logger.debug("No global system prompt found in project metadata")
227
359
  const n=a.default.join(this.options.projectPath,".opencode","agent")
228
360
  await c.default.promises.mkdir(n,{recursive:!0})
229
- const i="alchemy",l=this.cachedDefaultModel||"anthropic/claude-opus-4-5",d=s.filter((e=>e.enabled)),u=await Promise.allSettled(d.map((e=>{const t=this.configurer.generateAgentMarkdown(e,i,l,o),s=a.default.join(n,e.name+".md")
230
- return c.default.promises.writeFile(s,t,"utf8").then((()=>{this.logger.info("Synced agent file",{name:e.name,path:s,mode:e.mode})}))}))),h=u.filter((e=>"fulfilled"===e.status)).length,p=u.filter((e=>"rejected"===e.status)).length
361
+ const i="alchemy",l=this.cachedDefaultModel||"anthropic/claude-opus-4-5",d=r.filter((e=>e.enabled)),u=await Promise.allSettled(d.map((e=>{const t=this.configurer.generateAgentMarkdown(e,i,l,o),r=a.default.join(n,e.name+".md")
362
+ return c.default.promises.writeFile(r,t,"utf8").then((()=>{this.logger.info("Synced agent file",{name:e.name,path:r,mode:e.mode})}))}))),h=u.filter((e=>"fulfilled"===e.status)).length,p=u.filter((e=>"rejected"===e.status)).length
231
363
  p>0&&this.logger.warn("Some agent file writes failed",{writeFails:p})
232
364
  try{const e=a.default.join(this.options.projectPath,"opencode.json")
233
- if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),o=JSON.parse(t),n=r?.metadata?.bashToolAgents||{}
234
- if(o.agent){for(const e of s)e.enabled&&o.agent[e.name]&&e.name in n&&(o.agent[e.name].tools.bash=!!n[e.name])
235
- c.default.writeFileSync(e,JSON.stringify(o,null,2),"utf-8"),this.logger.info("Updated opencode.json agent tools",{bashOverrides:Object.keys(n).length})}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,g.getErrorMessage)(e)})}let f=!1
236
- try{const e=new v.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}).getOpenCodeServerUrl(this.options.projectPath)
237
- e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&(this.logger.info("Triggered OpenCode config reload after agent sync"),f=!0)}catch(e){this.logger.warn("Could not reload OpenCode config",{error:(0,g.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${h} agent(s) to local files${f?" - config reloaded":""}`,count:h,reloaded:f})}catch(e){this.logger.error("Failed to sync agents",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGenerateJournal(e,s){try{const{requestId:r}=e.body
238
- if(!r)return void s.status(400).json({success:!1,error:"requestId is required"})
239
- if(!/^req\d+$/.test(r))return void s.status(400).json({success:!1,error:"Invalid requestId format"})
240
- this.logger.info("Generating journal for request",{requestId:r})
241
- const o=`Generate a journal entry for request ${r}.\n\nSteps:\n1. Use the generate_request_journal tool with requestId: ${r}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${r}.md using document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,{resolveRunCommand:n}=await Promise.resolve().then(t.bind(t,9068)),i=n(),a=(0,w.spawn)(i.command,[...i.prependArgs,"run","--agent","journal",o],{detached:!0,stdio:"ignore",cwd:this.options.projectPath})
242
- a.pid&&this.trackChildPid(a.pid),a.unref(),s.json({success:!0,message:"Journal generation started for "+r})}catch(e){this.logger.error("Failed to generate journal",{error:(0,g.getErrorMessage)(e)}),s.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleEventHooksProxy(e,t){try{const{eventType:s}=e.params,{sessionId:r}=e.query,o=this.projectId
243
- if(!o)return void t.status(400).json({success:!1,error:"Project not registered"})
244
- this.logger.info("Proxying event hooks request",{projectId:o,eventType:s,sessionId:r})
245
- const n=r?"?sessionId="+encodeURIComponent(r):"",i=await this.apiClient.get(`/api/projects/${o}/event-hooks/by-event/${s}${n}`)
246
- t.json(i)}catch(e){this.logger.error("Failed to fetch event hooks",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleHookTriggersProxy(e,t){try{const s=this.projectId
247
- if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
248
- const{hookId:r,hookName:o,eventType:n,executionMode:i,agent:a}=e.body
249
- this.logger.debug("Proxying hook trigger notification",{projectId:s,hookName:o,eventType:n})
250
- const c=await this.apiClient.post(`/api/projects/${s}/hook-triggers`,{hookId:r,hookName:o,eventType:n,executionMode:i,agent:a})
251
- t.json(c)}catch(e){this.logger.debug("Failed to proxy hook trigger",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetHookIteration(e,t){try{const s=this.readProjectConfig()
252
- if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
253
- const{sessionId:r,hookId:o}=e.params,n=await this.apiClient.get(`/api/projects/${s.id}/hook-iterations/${r}/${o}`)
254
- t.json(n)}catch(e){this.logger.debug("Failed to get hook iteration",{error:(0,g.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}async handleIncrementHookIteration(e,t){try{const s=this.readProjectConfig()
255
- if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
256
- const{sessionId:r,hookId:o}=e.params,n=await this.apiClient.post(`/api/projects/${s.id}/hook-iterations/${r}/${o}/increment`,{})
257
- t.json(n)}catch(e){this.logger.debug("Failed to increment hook iteration",{error:(0,g.getErrorMessage)(e)}),t.json({success:!0,data:{iterationCount:0}})}}async handleDirectReport(e,t){try{const s=this.projectId
258
- if(!s)return void t.status(400).json({success:!1,error:"Project not registered"})
259
- const{sessionId:r,requestId:o,summary:n,status:i}=e.body
260
- if(!r)return void t.status(400).json({success:!1,error:"sessionId is required"})
261
- this.logger.info("Direct report creation",{sessionId:r,requestId:o,status:i})
365
+ if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),o=JSON.parse(t),n=s?.metadata?.bashToolAgents||{}
366
+ if(o.agent){for(const e of r)e.enabled&&o.agent[e.name]&&e.name in n&&(o.agent[e.name].tools.bash=!!n[e.name])
367
+ c.default.writeFileSync(e,JSON.stringify(o,null,2),"utf-8"),this.logger.info("Updated opencode.json agent tools",{bashOverrides:Object.keys(n).length})}}}catch(e){this.logger.warn("Failed to update opencode.json agent tools",{error:(0,g.getErrorMessage)(e)})}let m=!1
368
+ try{const e=this.getOpenCodeUrl()
369
+ e&&(await fetch(e+"/config/reload",{method:"POST",signal:AbortSignal.timeout(5e3)})).ok&&(this.logger.info("Triggered OpenCode config reload after agent sync"),m=!0)}catch(e){this.logger.warn("Could not reload OpenCode config",{error:(0,g.getErrorMessage)(e)})}t.json({success:!0,message:`Synced ${h} agent(s) to local files${m?" - config reloaded":""}`,count:h,reloaded:m})}catch(e){this.logger.error("Failed to sync agents",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGenerateJournal(e,r){try{const{requestId:s}=e.body
370
+ if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
371
+ if(!/^req\d+$/.test(s))return void r.status(400).json({success:!1,error:"Invalid requestId format"})
372
+ this.logger.info("Generating journal for request",{requestId:s})
373
+ const o=`Generate a journal entry for request ${s}.\n\nSteps:\n1. Use the generate_request_journal tool with requestId: ${s}\n2. Write a concise narrative journal entry based on the returned data covering:\n - What was implemented and why\n - Key decisions and trade-offs made\n - Challenges encountered and solutions\n - Files modified and their purposes\n - How acceptance criteria were met\n3. Save as JOURNAL-${s}.md using document_operations (operation: create, type: Report)\n\nKeep it story-focused and informative.`,{resolveRunCommand:n}=await Promise.resolve().then(t.bind(t,9068)),i=n(),a=(0,j.spawn)(i.command,[...i.prependArgs,"run","--agent","journal",o],{detached:!0,stdio:"ignore",cwd:this.options.projectPath,env:{...process.env,...(0,y.getOpencodeSpawnEnv)(this.options.projectPath,(0,v.isDevMode)())}})
374
+ a.pid&&this.trackChildPid(a.pid),a.unref(),r.json({success:!0,message:"Journal generation started for "+s})}catch(e){this.logger.error("Failed to generate journal",{error:(0,g.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleDirectReport(e,t){try{const r=this.projectId
375
+ if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
376
+ const{sessionId:s,requestId:o,summary:n,status:i}=e.body
377
+ if(!s)return void t.status(400).json({success:!1,error:"sessionId is required"})
378
+ this.logger.info("Direct report creation",{sessionId:s,requestId:o,status:i})
262
379
  const a=[]
263
- try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}/ancestors`),t=e?.data||e||[]
264
- let o=r
265
- for(const e of t)a.push({ancestorId:e.externalSessionId,childId:o}),o=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:r,error:(0,g.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
380
+ try{const e=await this.apiClient.get(`/api/projects/${r}/cli-sessions/external/opencode/${encodeURIComponent(s)}/ancestors`),t=e?.data||e||[]
381
+ let o=s
382
+ for(const e of t)a.push({ancestorId:e.externalSessionId,childId:o}),o=e.externalSessionId}catch(e){this.logger.warn("Failed to fetch ancestor chain",{sessionId:s,error:(0,g.getErrorMessage)(e)})}if(0===a.length)return void t.status(404).json({success:!1,error:"No parent session found"})
266
383
  let c,l=n
267
- if(!l&&o)try{const e=await this.apiClient.get(`/api/projects/${s}/requests/${o}`),t=e?.data||e
384
+ if(!l&&o)try{const e=await this.apiClient.get(`/api/projects/${r}/requests/${o}`),t=e?.data||e
268
385
  c=t?.title
269
- const r=t?.completionSummary
270
- r?l=r:c&&(l="Done: "+c)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:o,error:(0,g.getErrorMessage)(e)})}l||(l="Completed request "+(o||"unknown"))
271
- const d=o||r
386
+ const s=t?.completionSummary
387
+ s?l=s:c&&(l="Done: "+c)}catch(e){this.logger.debug("Could not fetch request details for summary",{requestId:o,error:(0,g.getErrorMessage)(e)})}l||(l="Completed request "+(o||"unknown"))
388
+ const d=o||s
272
389
  if(this.directReportDedup.size>=1e3){const e=this.directReportDedup.keys().next().value
273
390
  void 0!==e&&this.directReportDedup.delete(e)}this.directReportDedup.has(d)||this.directReportDedup.set(d,new Set)
274
- const u=this.directReportDedup.get(d),h=a.filter((({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1))),p=await Promise.allSettled(h.map((async({ancestorId:e,childId:t})=>{const r=await this.apiClient.post(`/api/projects/${s}/session-messages/reports`,{fromSessionId:t,toSessionId:e,status:i||"success",summary:l}),o=r?.data||r
275
- return u.add(e),this.logger.info("Direct report created for ancestor",{messageCode:o?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:o?.messageCode,fromSessionId:t}}))),f=[]
276
- for(const e of p)"fulfilled"===e.status&&f.push(e.value)
391
+ const u=this.directReportDedup.get(d),h=a.filter((({ancestorId:e})=>!u.has(e)||(this.logger.info("Direct report dedup: already notified",{ancestorId:e,dedupKey:d}),!1))),p=await Promise.allSettled(h.map((async({ancestorId:e,childId:t})=>{const s=await this.apiClient.post(`/api/projects/${r}/session-messages/reports`,{fromSessionId:t,toSessionId:e,status:i||"success",summary:l}),o=s?.data||s
392
+ return u.add(e),this.logger.info("Direct report created for ancestor",{messageCode:o?.messageCode,ancestorId:e,fromChild:t}),{externalSessionId:e,messageCode:o?.messageCode,fromSessionId:t,content:o?.content}}))),m=[]
393
+ for(const e of p)"fulfilled"===e.status&&m.push(e.value)
277
394
  for(const e of p)"rejected"===e.status&&this.logger.warn("Failed to create report for ancestor",{error:(0,g.getErrorMessage)(e.reason)})
278
- if(setTimeout((()=>{this.directReportDedup.delete(d)}),3e5),o)try{const e=await this.apiClient.get(`/api/projects/${s}/session-request-associations/by-session/${encodeURIComponent(r)}?activeOnly=true`),t=e?.data||e||[],o=(await Promise.allSettled(t.map((e=>this.apiClient.post(`/api/projects/${s}/session-request-associations/end`,{sessionId:r,requestLocalId:e.requestLocalId}))))).filter((e=>"rejected"===e.status)).length
279
- this.logger.info("Released request associations",{sessionId:r,count:t.length,failCount:o})}catch(e){this.logger.warn("Failed to release associations",{sessionId:r,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:f,report:f[0]?{messageCode:f[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:s}=e.params,r=this.readProjectConfig()
280
- if(r?.id)try{const e=await this.apiClient.get(`/api/projects/${r.id}/cli-sessions/external/opencode/${encodeURIComponent(s)}`),o=e?.data||e
281
- if(this.logger.info("Agent type DB lookup",{sessionId:s,dbAgentType:o?.agentType||null,dbId:o?.id||o?.localId||null}),o?.agentType)return void t.json({success:!0,agentType:o.agentType,source:"db"})}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:s,error:(0,g.getErrorMessage)(e)})}if(this.cliBridge.isUIConnected())try{const e=await this.cliBridge.sendCommand("get-session-status",{sessionId:s},5e3)
282
- if(this.logger.info("Agent type bridge lookup",{sessionId:s,bridgeAgentType:e?.data?.agentType||null,bridgeSuccess:e?.success}),e.success&&e.data?.agentType)return void t.json({success:!0,agentType:e.data.agentType,source:"bridge"})}catch(e){this.logger.info("Agent type bridge lookup failed",{sessionId:s,error:(0,g.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:s}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:s}=e.params,r=this.readProjectConfig()
283
- if(r?.id)try{const e=await this.apiClient.get(`/api/projects/${r.id}/cli-sessions/resolve/${encodeURIComponent(s)}`),o=e?.data?.parent?.externalSessionId||""
284
- return void t.json({success:!0,parentSessionId:o})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:s,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const s=this.getOpenCodeUrl()
285
- if(!s)return void t.status(502).json({error:"OpenCode not running"})
286
- const r=e.path.replace(/^\//,""),o=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",n=`${s}/${r}${o}`,i=e.headers.accept?.includes("text/event-stream")
287
- if(i){const s=new AbortController,r=()=>{this.proxyAbortControllers.delete(s)},o=(async()=>{try{const e=await fetch(n,{headers:{Accept:"text/event-stream"},signal:s.signal})
288
- if(!e.ok||!e.body)return r(),void t.status(e.status).json({error:"SSE upstream error"})
395
+ if(setTimeout((()=>{this.directReportDedup.delete(d)}),3e5),o)try{const e=await this.apiClient.get(`/api/projects/${r}/session-request-associations/by-session/${encodeURIComponent(s)}?activeOnly=true`),t=e?.data||e||[],o=(await Promise.allSettled(t.map((e=>this.apiClient.post(`/api/projects/${r}/session-request-associations/end`,{sessionId:s,requestLocalId:e.requestLocalId}))))).filter((e=>"rejected"===e.status)).length
396
+ this.logger.info("Released request associations",{sessionId:s,count:t.length,failCount:o})}catch(e){this.logger.warn("Failed to release associations",{sessionId:s,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,data:{notifiedSessions:m,report:m[0]?{messageCode:m[0].messageCode}:void 0,parentExternalSessionId:a[0]?.ancestorId||null}})}catch(e){this.logger.error("Direct report creation failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetSessionAgentType(e,t){try{const{sessionId:r}=e.params,s=this.projectId
397
+ if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),o=e?.data||e
398
+ if(this.logger.info("Agent type DB lookup",{sessionId:r,dbAgentType:o?.agentType||null,dbId:o?.id||o?.localId||null}),o?.agentType)return void t.json({success:!0,agentType:o.agentType,source:"db"})}catch(e){this.logger.info("Agent type DB lookup failed",{sessionId:r,error:(0,g.getErrorMessage)(e)})}if(this.cliBridge.isUIConnected())try{const e=await this.cliBridge.sendCommand("get-session-status",{sessionId:r},5e3)
399
+ if(this.logger.info("Agent type bridge lookup",{sessionId:r,bridgeAgentType:e?.data?.agentType||null,bridgeSuccess:e?.success}),e.success&&e.data?.agentType)return void t.json({success:!0,agentType:e.data.agentType,source:"bridge"})}catch(e){this.logger.info("Agent type bridge lookup failed",{sessionId:r,error:(0,g.getErrorMessage)(e)})}this.logger.info("Agent type not found",{sessionId:r}),t.json({success:!0,agentType:null,source:"unknown"})}catch(e){this.logger.debug("Failed to get session agent type",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetSessionParent(e,t){try{const{sessionId:r}=e.params,s=this.projectId
400
+ if(s)try{const e=await this.apiClient.get(`/api/projects/${s}/cli-sessions/resolve/${encodeURIComponent(r)}`),o=e?.data?.parent?.externalSessionId||""
401
+ return void t.json({success:!0,parentSessionId:o})}catch(e){this.logger.info("Parent session DB lookup failed",{sessionId:r,error:(0,g.getErrorMessage)(e)})}t.json({success:!0,parentSessionId:""})}catch(e){this.logger.debug("Failed to get parent session",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleOpenCodeProxy(e,t){try{const r=this.getOpenCodeUrl()
402
+ if(!r)return void t.status(502).json({error:"OpenCode not running"})
403
+ const s=e.path.replace(/^\//,""),o=e.originalUrl.includes("?")?e.originalUrl.substring(e.originalUrl.indexOf("?")):"",n=`${r}/${s}${o}`,i=e.headers.accept?.includes("text/event-stream")
404
+ if(i){const r=new AbortController,s=()=>{this.proxyAbortControllers.delete(r)},o=(async()=>{try{const e=await fetch(n,{headers:{Accept:"text/event-stream"},signal:r.signal})
405
+ if(!e.ok||!e.body)return s(),void t.status(e.status).json({error:"SSE upstream error"})
289
406
  t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"}),t.flushHeaders()
290
407
  const o=e.body.getReader()
291
- try{for(;;){const{done:e,value:s}=await o.read()
408
+ try{for(;;){const{done:e,value:r}=await o.read()
292
409
  if(e)break
293
- t.write(s),"function"==typeof t.flush&&t.flush()}}catch{}t.end()}catch{t.headersSent||t.status(502).json({error:"SSE upstream error"})}finally{r()}})()
294
- return this.proxyAbortControllers.set(s,o),void e.on("close",(()=>{try{s.abort()}catch{}}))}const a={"Content-Type":e.headers["content-type"]||"application/json"},c={method:e.method,headers:a}
410
+ t.write(r),"function"==typeof t.flush&&t.flush()}}catch{}t.end()}catch{t.headersSent||t.status(502).json({error:"SSE upstream error"})}finally{s()}})()
411
+ return this.proxyAbortControllers.set(r,o),void e.on("close",(()=>{try{r.abort()}catch{}}))}const a={"Content-Type":e.headers["content-type"]||"application/json"},c={method:e.method,headers:a}
295
412
  let l
296
413
  "GET"!==e.method&&"HEAD"!==e.method&&e.body&&(c.body=JSON.stringify(e.body))
297
414
  let d=n
298
415
  try{l=await fetch(n,c)}catch(e){this.openCodePort=null,this.openCodeUrlCache=null
299
416
  const t=this.getOpenCodeUrl()
300
- if(!t||t===s)throw e
301
- d=`${t}/${r}${o}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:s,newUrl:t}),l=await fetch(d,c)}if(204===l.status||"0"===l.headers.get("content-length"))return void t.status(l.status).end()
417
+ if(!t||t===r)throw e
418
+ d=`${t}/${s}${o}`,this.logger.info("OpenCode proxy: retrying with fresh port",{oldUrl:r,newUrl:t}),l=await fetch(d,c)}if(204===l.status||"0"===l.headers.get("content-length"))return void t.status(l.status).end()
302
419
  const u=l.headers.get("content-type")||""
303
420
  let h
304
- try{h=u.includes("application/json")?await l.json():await l.text()}catch{return void t.status(l.status).end()}t.status(l.status),u&&t.setHeader("Content-Type",u),"string"==typeof h?t.send(h):t.json(h)}catch(s){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,g.getErrorMessage)(s),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,g.getErrorMessage)(s)})}}async handleSpawnSession(e,t){try{const{agent:s,prompt:r,requestId:o,parentSessionId:n,title:i,specialization:a,allowParallel:c,callerAgentType:l}=e.body
305
- if(!s||!r)return void t.status(400).json({success:!1,error:"agent and prompt are required"})
421
+ try{h=u.includes("application/json")?await l.json():await l.text()}catch{return void t.status(l.status).end()}t.status(l.status),u&&t.setHeader("Content-Type",u),"string"==typeof h?t.send(h):t.json(h)}catch(r){this.openCodePort=null,this.openCodeUrlCache=null,this.logger.error("OpenCode proxy error",{error:(0,g.getErrorMessage)(r),path:e.params[0]}),t.status(502).json({error:"OpenCode proxy error",message:(0,g.getErrorMessage)(r)})}}async handleSpawnSession(e,t){try{const{agent:r,prompt:s,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d}=e.body
422
+ if(!r||!s)return void t.status(400).json({success:!1,error:"agent and prompt are required"})
423
+ if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
424
+ const u=this.projectId,h=new E.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:u,getDefaultModel:()=>this.cachedDefaultModel||void 0}),g=await h.prepareSession({agent:r,prompt:s,projectPath:this.options.projectPath,requestId:o,parentSessionId:n,title:i,specialization:a,specializationHeadline:c,allowParallel:l,callerAgentType:d})
425
+ t.json({success:!0,data:g})}catch(e){this.logger.error("Spawn session failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleRunTestsNow(e,r){try{const{requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i}=e.body||{}
426
+ if(!this.projectId)return void r.status(400).json({success:!1,error:"No project configured"})
427
+ if(!s)return void r.status(400).json({success:!1,error:"requestId is required"})
428
+ const{handleRunTestsAction:a}=await Promise.resolve().then(t.bind(t,6555)),c={projectId:this.projectId,projectPath:this.options.projectPath,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackedChildPids.add(e),cachedDefaultModel:this.cachedDefaultModel??null},l=await a({requestId:s,parentSessionId:o,testProcessIds:n,commandNames:i,mode:"auto"},c)
429
+ if(!l.success)return void r.status(500).json({success:!1,error:l.error||l.message})
430
+ r.json({success:!0,data:l.data})}catch(e){this.logger.error("Run tests now failed",{error:(0,g.getErrorMessage)(e)}),r.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleSetupTests(e,t){try{const{requestId:r,parentSessionId:s,branchName:o}=e.body||{}
431
+ if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
432
+ const n=this.projectId,i=this.options.projectPath
433
+ await(0,S.sweepStaleSetupWorktrees)(i,this.logger)
434
+ const c=await(0,S.resolveSetupBranch)(this.apiClient,n,o,r,this.logger,i),l=(0,S.generateSetupId)(),d=a.default.join((0,S.getSetupWorktreesBase)(i),l)
435
+ try{await(0,S.createSetupWorktree)(i,d,c,this.logger)}catch(e){const r=(0,g.getErrorMessage)(e)
436
+ return this.logger.error("Failed to create setup worktree",{branch:c,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const u=(0,S.buildSetupPrompt)(i,r),h=await(0,C.spawnActionAgent)({agent:"test-architect",prompt:u,cwd:d,projectPath:i,projectId:n,title:r?`[${r}] Test setup`:"Test setup",requestId:r,parentSessionId:s,allowParallel:!0,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
437
+ if(!h.success)return void t.status(500).json({success:!1,error:h.error||"Spawn failed"})
438
+ t.json({success:!0,data:{sessionId:h.sessionId,worktreePath:d,branch:c}})}catch(e){this.logger.error("Setup tests failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleSetupCommands(e,t){try{const{requestId:r,parentSessionId:s,branchName:o,context:n}=e.body||{}
306
439
  if(!this.projectId)return void t.status(400).json({success:!1,error:"No project configured"})
307
- const d=this.projectId,u=new v.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:d,getDefaultModel:()=>this.cachedDefaultModel||void 0}),h=await u.prepareSession({agent:s,prompt:r,projectPath:this.options.projectPath,requestId:o,parentSessionId:n,title:i,specialization:a,allowParallel:c,callerAgentType:l})
308
- t.json({success:!0,data:h})}catch(e){this.logger.error("Spawn session failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetAgentByName(e,t){try{const s=this.readProjectConfig()
309
- if(!s?.id)return void t.status(400).json({success:!1,error:"No project configured"})
310
- const{agentName:r}=e.params,o=await this.apiClient.get(`/api/projects/${s.id}/agents`),n=(o?.data||o||[]).find((e=>e.name===r))
311
- if(!n)return void t.status(404).json({success:!1,error:`Agent '${r}' not found`})
312
- t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<I.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
440
+ const i=this.projectId,c=this.options.projectPath
441
+ await(0,S.sweepStaleSetupWorktrees)(c,this.logger)
442
+ const l=await(0,S.resolveSetupBranch)(this.apiClient,i,o,r,this.logger,c),d=(0,S.generateSetupId)(),u=a.default.join((0,S.getSetupWorktreesBase)(c),d)
443
+ try{await(0,S.createSetupWorktree)(c,u,l,this.logger)}catch(e){const r=(0,g.getErrorMessage)(e)
444
+ return this.logger.error("Failed to create setup worktree (commands)",{branch:l,error:r}),void t.status(500).json({success:!1,error:"Failed to create setup worktree: "+r})}const h=(0,S.buildCommandSetupPrompt)(c,r,n),p=await(0,C.spawnActionAgent)({agent:"command-architect",prompt:h,cwd:u,projectPath:c,projectId:i,title:r?`[${r}] Commands setup`:"Commands setup",requestId:r,parentSessionId:s,allowParallel:!0,apiClient:this.apiClient,logger:this.logger,trackChildPid:e=>this.trackedChildPids.add(e),getDefaultModel:()=>this.cachedDefaultModel||void 0})
445
+ if(!p.success)return void t.status(500).json({success:!1,error:p.error||"Spawn failed"})
446
+ t.json({success:!0,data:{sessionId:p.sessionId,worktreePath:u,branch:l}})}catch(e){this.logger.error("Setup commands failed",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleGetAgentByName(e,t){try{const r=this.projectId
447
+ if(!r)return void t.status(400).json({success:!1,error:"Project not registered"})
448
+ const{agentName:s}=e.params,o=await this.apiClient.get(`/api/projects/${r}/agents`),n=(o?.data||o||[]).find((e=>e.name===s))
449
+ if(!n)return void t.status(404).json({success:!1,error:`Agent '${s}' not found`})
450
+ t.json({success:!0,data:n})}catch(e){this.logger.debug("Failed to get agent by name",{error:(0,g.getErrorMessage)(e)}),t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}readProjectConfig(){if(this.cachedProjectConfig&&Date.now()-this.cachedProjectConfig.timestamp<M.PROJECT_CONFIG_TTL)return this.cachedProjectConfig.config
313
451
  const e=a.default.join(this.options.projectPath,".alchemy_lab","project.json")
314
452
  try{if(!c.default.existsSync(e))return this.cachedProjectConfig={config:null,timestamp:Date.now()},null
315
- const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t)
316
- return this.cachedProjectConfig={config:s,timestamp:Date.now()},s}catch(e){return this.logger.error("Failed to read project config",{error:(0,g.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e
453
+ const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
454
+ return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,g.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e
317
455
  const t="http://127.0.0.1:"+e
318
456
  this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.cachedDefaultModel||this.seedDefaultModelFromZen(e).catch((e=>this.logger.debug("Failed to seed zen default model",{error:(0,g.getErrorMessage)(e)})))}async seedDefaultModelFromZen(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
319
457
  if(!t.ok)return
320
- const s=await t.json(),r=s?.default||{},[o,n]=Object.entries(r)[0]||[]
458
+ const r=await t.json(),s=r?.default||{},[o,n]=Object.entries(s)[0]||[]
321
459
  if(!o||!n)return
322
460
  const i=`${o}/${n}`
323
461
  this.updateDefaultModel(i),this.logger.info("Seeded defaultModel from OpenCode zen default",{defaultModel:i}),this.apiClient.post("/api/user/preferences",{defaultModel:i}).catch((e=>this.logger.debug("Failed to seed user preference",{error:(0,g.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.openCodePort)return"http://127.0.0.1:"+this.openCodePort
324
462
  if(this.openCodeUrlCache&&3e4>Date.now()-this.openCodeUrlCache.timestamp)return this.openCodeUrlCache.url
325
- const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
326
- try{if(!c.default.existsSync(e))return null
327
- const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t)
328
- if(s.port){const e="http://127.0.0.1:"+s.port
329
- return this.openCodeUrlCache={url:e,timestamp:Date.now()},e}}catch{}return null}async handleSessionCommand(e,t){const s=this.getOpenCodeUrl()
330
- if(!s)return{success:!1,error:"OpenCode server not available. Is 'opencode serve' running?"}
331
- try{switch(e){case"list-sessions":{const[e,r]=await Promise.all([fetch(s+"/session"),fetch(s+"/session/status")])
463
+ const e=(0,P.getOpenCodeUrlFromConfig)(this.options.projectPath)
464
+ return e&&(this.openCodeUrlCache={url:e,timestamp:Date.now()}),e}async handleSessionCommand(e,t){const r=this.getOpenCodeUrl()
465
+ if(!r)return{success:!1,error:"OpenCode server not available. Is 'opencode serve' running?"}
466
+ try{switch(e){case"list-sessions":{const[e,s]=await Promise.all([fetch(r+"/session"),fetch(r+"/session/status")])
332
467
  if(!e.ok)return{success:!1,error:"Failed to list sessions: "+e.status}
333
- const o=await e.json(),n=r.ok?await r.json():{}
468
+ const o=await e.json(),n=s.ok?await s.json():{}
334
469
  let i={}
335
470
  if(this.projectId)try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions`,{limit:200}),t=e?.data||e||[]
336
471
  for(const e of t)e.externalSessionId&&e.agentType&&(i[e.externalSessionId]=e.agentType)}catch{}let a=o.map((e=>({id:e.id,title:e.title||"(untitled)",parentId:e.parentID||void 0,status:n[e.id]?.type||"idle",agentType:i[e.id]||void 0,time:e.time})))
337
472
  if(t.status&&(a=a.filter((e=>e.status===t.status))),t.agentType&&(a=a.filter((e=>e.agentType===t.agentType))),t.search){const e=t.search.toLowerCase()
338
473
  a=a.filter((t=>t.title?.toLowerCase().includes(e)))}return t.limit&&(a=a.slice(0,t.limit)),{success:!0,data:a}}case"get-messages":{const e=t.sessionId
339
474
  if(!e)return{success:!1,error:"sessionId is required"}
340
- const r=t.limit||50,o=await fetch(`${s}/session/${e}/message?limit=${r}`)
475
+ const s=t.limit||50,o=await fetch(`${r}/session/${e}/message?limit=${s}`)
341
476
  return o.ok?{success:!0,data:(await o.json()).map((e=>({role:e.info.role,time:e.info.time?.created,parts:e.parts||[]})))}:404===o.status?{success:!1,error:"Session not found"}:{success:!1,error:"Failed to get messages: "+o.status}}case"send-message":{const e=t.sessionId
342
477
  if(!e)return{success:!1,error:"sessionId is required"}
343
478
  if(!t.message)return{success:!1,error:"message is required"}
344
- const r={parts:[{type:"text",text:t.message}]}
345
- if(t.agent&&(r.agent=t.agent),t.variant&&(r.variant=t.variant),t.system&&(r.system=t.system),this.cachedDefaultModel){const[e,...t]=this.cachedDefaultModel.split("/")
346
- r.model={providerID:e,modelID:t.join("/")}}const o=await fetch(`${s}/session/${e}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)})
479
+ const s={parts:[{type:"text",text:t.message}]}
480
+ if(t.agent&&(s.agent=t.agent),t.variant&&(s.variant=t.variant),t.system&&(s.system=t.system),this.cachedDefaultModel){const[e,...t]=this.cachedDefaultModel.split("/")
481
+ s.model={providerID:e,modelID:t.join("/")}}const o=await fetch(`${r}/session/${e}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)})
347
482
  if(!o.ok){const e=await o.text().catch((()=>"Unknown error"))
348
483
  return{success:!1,error:`Failed to send message: ${o.status} ${e}`}}return{success:!0,data:{messageId:null}}}case"create-session":{const e={}
349
484
  t.parentId&&(e.parentID=t.parentId),t.title&&(e.title=t.title)
350
- const r=await fetch(s+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})
351
- if(!r.ok)return{success:!1,error:"Failed to create session: "+r.status}
352
- const o=await r.json()
485
+ const s=await fetch(r+"/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})
486
+ if(!s.ok)return{success:!1,error:"Failed to create session: "+s.status}
487
+ const o=await s.json()
353
488
  return{success:!0,data:{id:o.id,title:o.title||t.title||"(untitled)",status:"idle",agentType:t.agent||void 0}}}case"get-session-status":{const e=t.sessionId
354
489
  if(!e)return{success:!1,error:"sessionId is required"}
355
- const[r,o]=await Promise.all([fetch(s+"/session/status"),fetch(s+"/session")]),n=r.ok?await r.json():{}
490
+ const[s,o]=await Promise.all([fetch(r+"/session/status"),fetch(r+"/session")]),n=s.ok?await s.json():{}
356
491
  let i,a
357
492
  if(o.ok){const t=(await o.json()).find((t=>t.id===e))
358
- i=t?.title}if(this.projectId)try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=t?.data||t
359
- s?.agentType&&(a=s.agentType)}catch{}return{success:!0,data:{id:e,title:i||"(untitled)",status:n[e]?.type||"idle",agentType:a}}}default:return{success:!1,error:"Unknown session action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}async register(){const e=this.readProjectConfig()
493
+ i=t?.title}if(this.projectId)try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t
494
+ r?.agentType&&(a=r.agentType)}catch{}return{success:!0,data:{id:e,title:i||"(untitled)",status:n[e]?.type||"idle",agentType:a}}}default:return{success:!1,error:"Unknown session action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}async register(){const e=this.readProjectConfig()
360
495
  if(!e?.id)throw Error("No project configuration found")
361
496
  this.projectId=e.id,this.hookExecutor.setProjectId(this.projectId),this.timerHookScheduler?.setProjectId(this.projectId)
362
497
  try{const e=a.default.resolve(__dirname,"../../package.json"),t=JSON.parse(c.default.readFileSync(e,"utf8"))
363
- this.clientVersion=t.version}catch{}try{const e=t(Object(function(){var e=Error("Cannot find module 'alchemylab-opencode/package.json'")
364
- throw e.code="MODULE_NOT_FOUND",e}()))
498
+ this.clientVersion=t.version}catch{}try{const e=t(45)
365
499
  this.opencodeVersion=e.version}catch{try{const e=process.env.ALCHEMY_OPENCODE_DIR
366
500
  if(e){const t=JSON.parse(c.default.readFileSync(a.default.join(e,"package.json"),"utf8"))
367
- this.opencodeVersion=t.version}}catch{}}const s={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:l.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}
501
+ this.opencodeVersion=t.version}}catch{}}const r={projectId:this.projectId,projectPath:this.options.projectPath,serverPort:this.port,hostname:l.default.hostname(),platform:process.platform,clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}
368
502
  this.logger.info("Registering with remote",{projectId:this.projectId})
369
- const r=await this.apiClient.post("/api/servers/register",s)
370
- if(r.success&&r.data?.serverId){this.serverId=r.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId})
371
- const e=r.data.versionWarnings
503
+ const s=await this.apiClient.post("/api/servers/register",r)
504
+ if(s.success&&s.data?.serverId){this.serverId=s.data.serverId,this.logger.info("Registered successfully",{serverId:this.serverId})
505
+ const e=s.data.versionWarnings
372
506
  if(e&&e.length>0){const t=e.some((e=>"required"===e.severity))
373
507
  for(const t of e)"required"===t.severity?(this.logger.error("[UPDATE REQUIRED] "+t.message),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\x1b[31m\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m"),console.error(`\x1b[31m${t.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+t.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
374
- this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:r.error?.message||"Unknown error"})}startHeartbeat(){this.serverId&&(this.heartbeatInterval=setInterval((async()=>{if(this.projectId)try{const{touchHeartbeat:e}=await t.e(640).then(t.bind(t,2640))
375
- e(this.projectId)}catch{}try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings
376
- if(t&&t.length>0){if(t.map((e=>`${e.component}:${e.severity}`)).join(",")!==this.versionWarnings.map((e=>`${e.component}:${e.severity}`)).join(",")){for(const e of t)"required"===e.severity?(this.logger.error("[UPDATE REQUIRED] "+e.message),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${e.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+e.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${e.message}\x1b[0m\n`))
377
- this.versionWarnings=t}!t.some((e=>"required"===e.severity))&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}else this.versionWarnings.length>0&&(this.versionWarnings=[])}catch(e){this.logger.warn("Heartbeat failed",{error:(0,g.getErrorMessage)(e)})}}),3e4))}startSSEEventForwarder(){this.logger.info("Starting SSE event forwarder"),this.connectToOpenCodeSSE(),this.sseForwarderInterval=setInterval((async()=>{await this.flushSSEEvents()}),2e3)}stopSSEEventForwarder(){this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch((e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,g.getErrorMessage)(e)})))}async connectToOpenCodeSSE(){const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
378
- if(!c.default.existsSync(e))return this.logger.debug("SSE forwarder: opencode.json not found, will retry"),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
379
- let t
380
- try{t=JSON.parse(c.default.readFileSync(e,"utf-8")).port}catch{return void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)}const s=`http://localhost:${t}/event`
381
- this.logger.info("SSE forwarder connecting",{url:s}),this.sseAbortController=new AbortController
382
- try{const e=await fetch(s,{headers:{Accept:"text/event-stream"},signal:this.sseAbortController.signal})
508
+ this.versionWarnings=e,t&&(this._pendingUpdateBlock=!0)}}else this.logger.warn("Registration failed, continuing without remote connection",{error:s.error?.message||"Unknown error"})}async applyVersionWarnings(e){if(e.map((e=>`${e.component}:${e.severity}`)).join(",")!==this.versionWarnings.map((e=>`${e.component}:${e.severity}`)).join(",")){for(const t of e)"required"===t.severity?(this.logger.error("[UPDATE REQUIRED] "+t.message),console.error(`\n\x1b[31m[UPDATE REQUIRED] ${t.message}\x1b[0m\n`)):(this.logger.warn("[UPDATE RECOMMENDED] "+t.message),console.warn(`\n\x1b[33m[UPDATE RECOMMENDED] ${t.message}\x1b[0m\n`))
509
+ this.versionWarnings=e,!e.some((e=>"required"===e.severity))&&!this._pendingUpdateBlock||this.updateBlocked||await this.enterUpdateMode()}}startHeartbeat(){this.serverId&&(this.heartbeatInterval=setInterval((async()=>{if(this.projectId)try{const{touchHeartbeat:e}=await t.e(640).then(t.bind(t,2640))
510
+ e(this.projectId)}catch{}try{const e=await this.apiClient.post(`/api/servers/${this.serverId}/heartbeat`,{timestamp:(new Date).toISOString(),clientVersion:this.clientVersion,opencodeVersion:this.opencodeVersion}),t=e?.data?.versionWarnings??[]
511
+ await this.applyVersionWarnings(t)}catch(e){this.logger.warn("Heartbeat failed",{error:(0,g.getErrorMessage)(e)})}}),3e5))}startSSEEventForwarder(){this.logger.info("Starting SSE event forwarder"),this.connectToOpenCodeSSE(),this.sseForwarderInterval=setInterval((async()=>{await this.flushSSEEvents()}),2e3)}stopSSEEventForwarder(){this.sseForwarderInterval&&(clearInterval(this.sseForwarderInterval),this.sseForwarderInterval=void 0),this.sseAbortController&&(this.sseAbortController.abort(),this.sseAbortController=void 0),this.flushSSEEvents().catch((e=>this.logger.warn("Failed to flush remaining SSE events",{error:(0,g.getErrorMessage)(e)})))}async connectToOpenCodeSSE(){const e=this.getOpenCodeUrl()
512
+ if(!e)return this.logger.debug("SSE forwarder: OpenCode URL unavailable, will retry"),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
513
+ const t=e+"/event"
514
+ this.logger.info("SSE forwarder connecting",{url:t}),this.sseAbortController=new AbortController
515
+ try{const e=await fetch(t,{headers:{Accept:"text/event-stream"},signal:this.sseAbortController.signal})
383
516
  if(!e.ok||!e.body)return this.logger.warn("SSE forwarder: upstream error",{status:e.status}),void setTimeout((()=>this.connectToOpenCodeSSE()),5e3)
384
- const t=e.body.getReader(),r=new TextDecoder
385
- let o="";(async()=>{try{for(;;){const{done:e,value:s}=await t.read()
517
+ const r=e.body.getReader(),s=new TextDecoder
518
+ let o="";(async()=>{try{for(;;){const{done:e,value:t}=await r.read()
386
519
  if(e)break
387
- o+=r.decode(s,{stream:!0})
520
+ o+=s.decode(t,{stream:!0})
388
521
  const n=o.split("\n")
389
522
  o=n.pop()||""
390
- for(const e of n)if(e.startsWith("data: "))try{const t=JSON.parse(e.slice(6)),s=t.type||"unknown"
391
- this.sseEventBuffer.push({type:s,data:t}),this.handleSSEHookEvent(t).catch((e=>this.logger.warn("SSE hook event processing failed",{type:t?.type,error:(0,g.getErrorMessage)(e)}))),"session.idle"===s&&this.flushSSEEvents().catch((e=>this.logger.debug("Immediate SSE flush failed",{error:(0,g.getErrorMessage)(e)})))}catch{}}}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,g.getErrorMessage)(e)})}this.sseForwarderInterval&&(this.logger.info("SSE forwarder reconnecting in 3s"),setTimeout((()=>this.connectToOpenCodeSSE()),3e3))})()}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,g.getErrorMessage)(e)}),this.sseForwarderInterval&&setTimeout((()=>this.connectToOpenCodeSSE()),5e3))}}async handleSSEHookEvent(e){const t=e?.type
523
+ for(const e of n)if(e.startsWith("data: "))try{const t=JSON.parse(e.slice(6)),r=t.type||"unknown"
524
+ this.sseEventBuffer.push({type:r,data:t}),this.handleSSEHookEvent(t).catch((e=>this.logger.warn("SSE hook event processing failed",{type:t?.type,error:(0,g.getErrorMessage)(e)}))),"session.idle"===r&&this.flushSSEEvents().catch((e=>this.logger.debug("Immediate SSE flush failed",{error:(0,g.getErrorMessage)(e)})))}catch{}}}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||this.logger.debug("SSE forwarder stream ended",{error:(0,g.getErrorMessage)(e)})}this.sseForwarderInterval&&(this.logger.info("SSE forwarder reconnecting in 3s"),setTimeout((()=>this.connectToOpenCodeSSE()),3e3))})()}catch(e){"AbortError"===e?.name||(e?.message+"").includes("terminated")||(this.logger.debug("SSE forwarder connection failed",{error:(0,g.getErrorMessage)(e)}),this.sseForwarderInterval&&setTimeout((()=>this.connectToOpenCodeSSE()),5e3))}}async handleSSEHookEvent(e){const t=e?.type
392
525
  if(t&&this.projectId)if("session.idle"===t){const t=e?.properties?.sessionID||e?.sessionID||""
393
526
  if(!t)return
394
- this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,s=t?.id||""
395
- if(!s)return
396
- const r=t?.metadata
397
- r?.agentType&&this.projectId&&(this.logger.debug("SSE: session.created with alchemy metadata, ensuring registration",{sessionId:s,agentType:r.agentType}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:s,providerId:"opencode",agentType:r.agentType,status:"active",specialization:r.specialization||void 0}).catch((e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:s,error:(0,g.getErrorMessage)(e)})}))),setTimeout((async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:s}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:s,variables:{sessionId:s}})}),500)}}async flushSSEEvents(){if(0===this.sseEventBuffer.length||!this.projectId)return
398
- if(!this.hasRemoteEventConsumers)return void(this.sseEventBuffer.length>500&&this.sseEventBuffer.splice(0,this.sseEventBuffer.length-500))
399
- const e=this.sseEventBuffer.splice(0)
400
- try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:e})}catch(t){this.logger.debug("SSE event flush failed",{error:(0,g.getErrorMessage)(t),count:e.length}),this.sseEventBuffer.unshift(...e),this.sseEventBuffer.length>500&&this.sseEventBuffer.splice(0,this.sseEventBuffer.length-500)}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce()}catch(e){if(!this.isPolling)return
527
+ this.logger.debug("SSE hook: session_idle",{sessionId:t}),await this.hookExecutor.processEvent({eventType:"session_idle",sessionId:t,variables:{sessionId:t}})}else if("session.created"===t){const t=e?.properties?.info||e?.info,r=t?.id||""
528
+ if(!r)return
529
+ const s=t?.metadata
530
+ if(s?.agentType&&this.projectId){const e=t?.parentID||null
531
+ this.logger.info("SSE-driven cli-sessions upsert",{sessionId:r,agentType:s.agentType,parentExternalIdFromOpenCode:e,note:"no parent fields included in body; preserves existing parent if record exists"}),this.apiClient.post(`/api/projects/${this.projectId}/cli-sessions`,{externalSessionId:r,providerId:"opencode",agentType:s.agentType,status:"active",specialization:s.specialization||void 0}).catch((e=>{this.logger.debug("SSE session registration (idempotent)",{sessionId:r,error:(0,g.getErrorMessage)(e)})}))}setTimeout((async()=>{this.logger.debug("SSE hook: session_created (delayed)",{sessionId:r}),await this.hookExecutor.processEvent({eventType:"session_created",sessionId:r,variables:{sessionId:r}})}),500)}}async flushSSEEvents(){if(!this.projectId)return
532
+ if(0===this.sseEventBuffer.length&&0===this.droppedEventCount)return
533
+ if(!this.hasRemoteEventConsumers){if(this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
534
+ this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}return}const e=this.sseEventBuffer.splice(0),t=this.droppedEventCount>0?{type:"alchemy.events.dropped",data:{count:this.droppedEventCount,firstDropAt:this.firstDropAt,reportedAt:Date.now()}}:null
535
+ t&&e.unshift(t)
536
+ try{await this.apiClient.post(`/api/projects/${this.projectId}/opencode-events`,{events:e}),t&&(this.logger.warn("Reported dropped SSE events to remote",{count:t.data.count,firstDropAt:t.data.firstDropAt}),this.droppedEventCount=0,this.firstDropAt=0)}catch(r){this.logger.debug("SSE event flush failed",{error:(0,g.getErrorMessage)(r),count:e.length})
537
+ const s=t?e.slice(1):e
538
+ if(this.sseEventBuffer.unshift(...s),this.sseEventBuffer.length>500){const e=this.sseEventBuffer.length-500
539
+ this.sseEventBuffer.splice(0,e),0===this.droppedEventCount&&(this.firstDropAt=Date.now()),this.droppedEventCount+=e}}}startPolling(){this.projectId&&(this.isPolling=!0,this.pollAbortController=new AbortController,this.logger.info("Starting action long-polling",{projectId:this.projectId}),this.longPollLoop())}async longPollLoop(){for(;this.isPolling;)try{await this.pollOnce()}catch(e){if(!this.isPolling)return
401
540
  this.logger.debug("Long-poll error, retrying in 5s",{error:(0,g.getErrorMessage)(e)}),await new Promise((e=>setTimeout(e,5e3)))}}async pollOnce(){if(this.isPolling&&this.projectId&&!this.updateBlocked)try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=30000`,t={"Content-Type":"application/json",Accept:"application/json"}
402
541
  this.apiClient.authToken&&(t.Authorization="Bearer "+this.apiClient.authToken)
403
- const s=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
404
- t["X-Service-Secret"]=s
405
- const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
406
- if(!r.ok)throw Error("Poll failed with status "+r.status)
407
- const o=await r.json(),n={success:!0,data:o.data||o}
542
+ const r=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
543
+ t["X-Service-Secret"]=r,this.clientVersion&&(t["X-Client-Version"]=this.clientVersion),this.opencodeVersion&&(t["X-OpenCode-Version"]=this.opencodeVersion)
544
+ const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
545
+ if(!s.ok)throw Error("Poll failed with status "+s.status)
546
+ const o=await s.json(),n={success:!0,data:o.data||o}
408
547
  if(n.success&&n.data&&void 0!==n.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=n.data.hasRemoteEventConsumers),n.success&&n.data?.settings){const{defaultModel:e,autoJournal:t}=n.data.settings
409
548
  if(void 0!==e&&this.updateDefaultModel(e),void 0!==t&&t!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed",{oldValue:this.cachedAutoJournal,newValue:t})
410
549
  const e=new p.ProjectConfigService(this.logger)
411
- await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}if(n.success&&n.data?.actions?.length>0){const e=[],t=[]
412
- for(const s of n.data.actions)"opencode-proxy"===s.type||"run-tests"===s.type?e.push(s):t.push(s)
550
+ await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}if(n.success&&Array.isArray(n.data?.invalidations)&&n.data.invalidations.length>0){const e=n.data.invalidations
551
+ this.logger.info("Received cache invalidations from remote",{invalidations:e}),e.includes("hooks")&&this.timerHookScheduler?.invalidateCache(),e.includes("quests")&&this.questTickScheduler?.invalidateCache(),e.includes("alchemist")&&this.questTickScheduler?.invalidateAlchemistCache()}if(n.success&&Array.isArray(n.data?.versionWarnings)&&this.applyVersionWarnings(n.data.versionWarnings),n.success&&n.data?.actions?.length>0){const e=[],t=[]
552
+ for(const r of n.data.actions)"opencode-proxy"===r.type||"run-tests"===r.type?e.push(r):t.push(r)
413
553
  for(const t of e)this.processAction(t).catch((e=>this.logger.warn("Proxy action failed",{actionId:t.id,error:(0,g.getErrorMessage)(e)})))
414
554
  if(t.length>0&&!this.isProcessingAction){this.isProcessingAction=!0
415
555
  try{for(const e of t)await this.processAction(e)}finally{this.isProcessingAction=!1}}}}catch(e){throw e}}async syncProjectSettings(){try{const e=await this.apiClient.get("/api/projects/"+this.projectId)
416
- if(e.success&&e.data?.metadata){const{autoJournal:t,defaultModel:s}=e.data.metadata
417
- if(void 0!==s&&this.updateDefaultModel(s),void 0!==t&&t!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed, syncing to local config",{oldValue:this.cachedAutoJournal,newValue:t})
556
+ if(e.success&&e.data?.metadata){const{autoJournal:t,defaultModel:r}=e.data.metadata
557
+ if(void 0!==r&&this.updateDefaultModel(r),void 0!==t&&t!==this.cachedAutoJournal){this.logger.info("autoJournal setting changed, syncing to local config",{oldValue:this.cachedAutoJournal,newValue:t})
418
558
  const e=new p.ProjectConfigService(this.logger)
419
559
  await e.saveProjectConfig(this.options.projectPath,t),this.cachedAutoJournal=t}}}catch(e){this.logger.debug("Settings sync failed",{error:(0,g.getErrorMessage)(e)})}}async processAction(e){this.logger.info("Processing action",{actionId:e.id,type:e.type})
420
560
  const t=Date.now()
421
- try{const s={projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},r=await(0,d.executeAction)(e.type,e.payload||{},s)
422
- await this.apiClient.post(`/api/actions/${e.id}/result`,{success:r.success,result:r.data,error:r.message||r.error,duration:Date.now()-t}),this.logger.info("Action processed",{actionId:e.id,type:e.type,success:r.success,duration:Date.now()-t})}catch(s){const r=(0,g.getErrorMessage)(s)
423
- await this.apiClient.post(`/api/actions/${e.id}/result`,{success:!1,error:r,duration:Date.now()-t}),this.logger.error("Action failed",{actionId:e.id,type:e.type,error:r})}}async handleCLIBridgeCommand(e,t){try{const{action:s,params:r,timeout:o}=e.body
424
- if(!s)return void t.status(400).json({success:!1,error:"action is required"})
425
- if(["list-notifications","get-notification","respond-to-notification"].includes(s)){const e=await this.handleNotificationCommand(s,r||{})
426
- return void t.json(e)}if(["list-sessions","get-messages","send-message","create-session","get-session-status"].includes(s)){const e=await this.handleSessionCommand(s,r||{})
427
- return void t.json(e)}if(["list-requests","get-request"].includes(s)){const e=await this.handleRequestCommand(s,r||{})
428
- return void t.json(e)}const n=await this.cliBridge.sendCommand(s,r||{},o)
561
+ try{const r={projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,trackChildPid:e=>this.trackChildPid(e),cachedDefaultModel:this.cachedDefaultModel},s=await(0,d.executeAction)(e.type,e.payload||{},r)
562
+ await this.apiClient.post(`/api/actions/${e.id}/result`,{success:s.success,result:s.data,error:s.message||s.error,duration:Date.now()-t}),this.logger.info("Action processed",{actionId:e.id,type:e.type,success:s.success,duration:Date.now()-t})}catch(r){const s=(0,g.getErrorMessage)(r)
563
+ await this.apiClient.post(`/api/actions/${e.id}/result`,{success:!1,error:s,duration:Date.now()-t}),this.logger.error("Action failed",{actionId:e.id,type:e.type,error:s})}}async handleCLIBridgeCommand(e,t){try{const{action:r,params:s,timeout:o}=e.body
564
+ if(!r)return void t.status(400).json({success:!1,error:"action is required"})
565
+ if(["list-notifications","get-notification","respond-to-notification"].includes(r)){const e=await this.handleNotificationCommand(r,s||{})
566
+ return void t.json(e)}if(["list-sessions","get-messages","send-message","create-session","get-session-status"].includes(r)){const e=await this.handleSessionCommand(r,s||{})
567
+ return void t.json(e)}if(["list-requests","get-request"].includes(r)){const e=await this.handleRequestCommand(r,s||{})
568
+ return void t.json(e)}const n=await this.cliBridge.sendCommand(r,s||{},o)
429
569
  t.json(n)}catch(e){t.status(500).json({success:!1,error:(0,g.getErrorMessage)(e)})}}async handleNotificationCommand(e,t){if(!this.projectId)return{success:!1,error:"Project not registered yet"}
430
- const s=this.projectId
570
+ const r=this.projectId
431
571
  try{switch(e){case"list-notifications":{const e={limit:t.limit||30}
432
- return t.sessionId&&(e.sessionId=t.sessionId),t.messageType&&(e.messageType=t.messageType),t.status&&(e.status=t.status),t.readStatus&&(e.readStatus=t.readStatus),{success:!0,data:(await this.apiClient.get(`/api/projects/${s}/session-messages`,e)).data}}case"get-notification":return t.messageCode?{success:!0,data:(await this.apiClient.get(`/api/projects/${s}/session-messages/by-code/${encodeURIComponent(t.messageCode)}`)).data}:{success:!1,error:"messageCode is required"}
572
+ return t.sessionId&&(e.sessionId=t.sessionId),t.messageType&&(e.messageType=t.messageType),t.status&&(e.status=t.status),t.readStatus&&(e.readStatus=t.readStatus),{success:!0,data:(await this.apiClient.get(`/api/projects/${r}/session-messages`,e)).data}}case"get-notification":return t.messageCode?{success:!0,data:(await this.apiClient.get(`/api/projects/${r}/session-messages/by-code/${encodeURIComponent(t.messageCode)}`)).data}:{success:!1,error:"messageCode is required"}
433
573
  case"respond-to-notification":{if(!t.messageCode)return{success:!1,error:"messageCode is required"}
434
574
  const e=t.messageCode
435
- return{success:!0,data:(await this.apiClient.post(`/api/projects/${s}/session-messages/${encodeURIComponent(e)}/answer`,{respondedBySessionId:t.respondedBySessionId||"cli-bridge",text:t.responseText,selectedOptions:t.selectedOptions,notes:t.notes})).data}}default:return{success:!1,error:"Unknown notification action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}async handleRequestCommand(e,t){if(!this.projectId)return{success:!1,error:"Project not registered yet"}
436
- const s=this.projectId
575
+ return{success:!0,data:(await this.apiClient.post(`/api/projects/${r}/session-messages/${encodeURIComponent(e)}/answer`,{respondedBySessionId:t.respondedBySessionId||"cli-bridge",text:t.responseText,selectedOptions:t.selectedOptions,notes:t.notes})).data}}default:return{success:!1,error:"Unknown notification action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}async handleRequestCommand(e,t){if(!this.projectId)return{success:!1,error:"Project not registered yet"}
576
+ const r=this.projectId
437
577
  try{switch(e){case"list-requests":{const e={}
438
- return t.status&&(e.status=t.status),t.type&&(e.type=t.type),t.limit&&(e.limit=t.limit),t.offset&&(e.offset=t.offset),t.sortBy&&(e.sortBy=t.sortBy),t.sortDirection&&(e.sortDirection=t.sortDirection),{success:!0,data:(await this.apiClient.get(`/api/projects/${s}/requests`,e)).data}}case"get-request":{if(!t.requestId)return{success:!1,error:"requestId is required"}
439
- const e=encodeURIComponent(t.requestId),[r,o]=await Promise.all([this.apiClient.get(`/api/projects/${s}/requests/${e}`),this.apiClient.get(`/api/projects/${s}/tasks`,{requestId:t.requestId})])
440
- return{success:!0,data:{...r.data,tasks:o.data||[]}}}default:return{success:!1,error:"Unknown request action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}handleCLIBridgeStatus(e,t){t.json({uiConnected:this.cliBridge.isUIConnected()})}async start(){this.logger.info("Starting AlchemyActionServer",{projectPath:this.options.projectPath,port:this.port})
578
+ return t.status&&(e.status=t.status),t.type&&(e.type=t.type),t.limit&&(e.limit=t.limit),t.offset&&(e.offset=t.offset),t.sortBy&&(e.sortBy=t.sortBy),t.sortDirection&&(e.sortDirection=t.sortDirection),{success:!0,data:(await this.apiClient.get(`/api/projects/${r}/requests`,e)).data}}case"get-request":{if(!t.requestId)return{success:!1,error:"requestId is required"}
579
+ const e=encodeURIComponent(t.requestId),[s,o]=await Promise.all([this.apiClient.get(`/api/projects/${r}/requests/${e}`),this.apiClient.get(`/api/projects/${r}/tasks`,{requestId:t.requestId})])
580
+ return{success:!0,data:{...s.data,tasks:o.data||[]}}}default:return{success:!1,error:"Unknown request action: "+e}}}catch(e){return{success:!1,error:(0,g.getErrorMessage)(e)}}}handleCLIBridgeStatus(e,t){t.json({uiConnected:this.cliBridge.isUIConnected()})}async start(){this.logger.info("Starting AlchemyActionServer",{projectPath:this.options.projectPath,port:this.port})
441
581
  for(let e=0;10>e;e++)try{await new Promise(((e,t)=>{try{this.server=this.app.listen(this.port,"127.0.0.1",(()=>{this.logger.info("HTTP server started",{port:this.port}),this.cliBridge.attach(this.server),e()})),this.server.on("error",(e=>{e.code,t(e)}))}catch(e){t(e)}}))
442
582
  break}catch(t){if("EADDRINUSE"===t?.code&&9>e){this.logger.info(`Port ${this.port} in use, trying ${this.port+1}`),this.port++
443
583
  continue}throw Error(`Port ${this.port} is already in use (tried 10 ports)`)}try{const e=a.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
444
584
  c.default.mkdirSync(a.default.dirname(e),{recursive:!0}),c.default.writeFileSync(e,JSON.stringify({port:this.port},null,2),"utf-8"),this.logger.info("Wrote action-server port file",{port:this.port,path:e})}catch(e){this.logger.warn("Failed to write action-server port file",{error:(0,g.getErrorMessage)(e)})}try{const e=this.readProjectConfig()
445
- if(e?.id){const{upsertEntry:s}=await t.e(640).then(t.bind(t,2640))
446
- s({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,g.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,g.getErrorMessage)(e)
585
+ if(e?.id){const{upsertEntry:r}=await t.e(640).then(t.bind(t,2640))
586
+ r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,g.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,g.getErrorMessage)(e)
447
587
  t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=5000`,t={"Content-Type":"application/json",Accept:"application/json"}
448
588
  this.apiClient.authToken&&(t.Authorization="Bearer "+this.apiClient.authToken)
449
- const s=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
450
- t["X-Service-Secret"]=s
451
- const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
452
- if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
453
- void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,g.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.startMcpStaleCleanup()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,g.getErrorMessage)(t)})}}this.trackedChildPids.clear()
589
+ const r=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
590
+ t["X-Service-Secret"]=r
591
+ const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
592
+ if(s.ok){const e=await s.json(),t=e?.data?.settings||e?.settings
593
+ void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel}))}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,g.getErrorMessage)(e)})}this.startPolling(),this.projectId&&this.startSSEEventForwarder(),this.projectId&&this.startStuckSessionWatchdog(),this.projectId&&this.startTimerHookScheduler(),this.projectId&&this.startQuestTickScheduler(),this.startMcpStaleCleanup()}trackChildPid(e){this.trackedChildPids.add(e)}killTrackedProcesses(){for(const e of this.trackedChildPids)try{process.kill(-e,"SIGTERM"),this.logger.info("Killed tracked process group",{pid:e})}catch(t){if("ESRCH"!==t?.code){try{process.kill(e,"SIGTERM")}catch{}this.logger.debug("Could not kill process group, fell back to single kill",{pid:e,error:(0,g.getErrorMessage)(t)})}}this.trackedChildPids.clear()
454
594
  try{const e=a.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
455
- if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),s=JSON.parse(t)
456
- if(s.pid)try{process.kill(-s.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:s.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(s.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:s.pid,error:(0,g.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new y.TimerHookScheduler({hookExecutor:this.hookExecutor,apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.timerHookScheduler.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new m.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),onStuckResolved:e=>this.handleStuckSessionResolved(e)}),this.stuckSessionWatchdog.start()}handleStuckSessionDetected(e){this.sseEventBuffer.push({type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const s=this.stuckSessionWatchdog?.getStuckSessions()??[]
457
- t.json({success:!0,data:s})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
458
- const{sessionId:s,title:r}=e.body||{},o=this.stuckSessionWatchdog.injectTestStuck(s,r)
459
- this.logger.info("Test-injected stuck session",{sessionId:o.sessionId}),t.json({success:!0,data:o})}async handleRetryStuckSession(e,t){const{sessionId:s}=e.params
595
+ if(c.default.existsSync(e)){const t=c.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
596
+ if(r.pid)try{process.kill(-r.pid,"SIGTERM"),this.logger.info("Killed OpenCode process group from config",{pid:r.pid})}catch(e){if("ESRCH"!==e?.code){try{process.kill(r.pid,"SIGTERM")}catch{}this.logger.debug("Could not kill OpenCode group, fell back to single kill",{pid:r.pid,error:(0,g.getErrorMessage)(e)})}}}}catch{}}startTimerHookScheduler(){this.timerHookScheduler=new b.TimerHookScheduler({hookExecutor:this.hookExecutor,apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.timerHookScheduler.start()}startQuestTickScheduler(){this.questTickScheduler=new I.QuestTickScheduler({apiClient:this.apiClient,logger:this.logger,getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,projectId:this.projectId||"",projectPath:this.options.projectPath}),this.questTickScheduler.start()}startStuckSessionWatchdog(){this.stuckSessionWatchdog=new w.StuckSessionWatchdog({getOpenCodeUrl:()=>this.getOpenCodeUrl(),getDefaultModel:()=>this.cachedDefaultModel||void 0,logger:this.logger,onStuckDetected:e=>this.handleStuckSessionDetected(e),onStuckResolved:e=>this.handleStuckSessionResolved(e)}),this.stuckSessionWatchdog.start()}handleStuckSessionDetected(e){this.sseEventBuffer.push({type:"session.stuck",data:{type:"session.stuck",properties:{sessionId:e.sessionId,messageId:e.messageId,detectedAt:e.detectedAt,sessionTitle:e.sessionTitle,retryCount:e.retryCount}}}),this.logger.warn("Stuck session detected",{sessionId:e.sessionId,title:e.sessionTitle})}handleStuckSessionResolved(e){this.sseEventBuffer.push({type:"session.unstuck",data:{type:"session.unstuck",properties:{sessionId:e}}})}handleGetStuckSessions(e,t){const r=this.stuckSessionWatchdog?.getStuckSessions()??[]
597
+ t.json({success:!0,data:r})}handleTestInjectStuckSession(e,t){if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
598
+ const{sessionId:r,title:s}=e.body||{},o=this.stuckSessionWatchdog.injectTestStuck(r,s)
599
+ this.logger.info("Test-injected stuck session",{sessionId:o.sessionId}),t.json({success:!0,data:o})}async handleRetryStuckSession(e,t){const{sessionId:r}=e.params
460
600
  if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"Watchdog not running"})
461
- const r=await this.stuckSessionWatchdog.retrySession(s)
462
- r.success?t.json({success:!0}):t.status(400).json({success:!1,error:r.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
601
+ const s=await this.stuckSessionWatchdog.retrySession(r)
602
+ s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
463
603
  for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
464
- console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
604
+ console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.questTickScheduler?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
465
605
  for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.cliBridge.close(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer...")
466
- for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0)
606
+ for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.timerHookScheduler?.stop(),this.questTickScheduler?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0)
467
607
  try{const e=a.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
468
608
  c.default.existsSync(e)&&c.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await t.e(640).then(t.bind(t,2640))
469
609
  e(this.projectId)}catch{}if(this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval),this.heartbeatInterval&&clearInterval(this.heartbeatInterval),this.serverId)try{await this.apiClient.post(`/api/servers/${this.serverId}/disconnect`)}catch{}this.cliBridge.close(),await new Promise((e=>{if(this.server){const t=setTimeout((()=>{this.logger.warn("Graceful shutdown timed out after 10s, forcing close"),e()}),1e4)
470
- this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=I,I.PROJECT_CONFIG_TTL=3e4},2424(e,exports,t){"use strict"
471
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.ensureRequestWorktree=async function(e,t,i){const a="requests/"+t,c=(0,r.join)(e,n.ALCHEMY_DIR_NAME,"worktrees"),l=(0,r.join)(c,t)
472
- if((0,s.existsSync)(l))return void i.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:l})
473
- const d=(0,o.simpleGit)(e);(0,s.mkdirSync)(c,{recursive:!0})
610
+ this.server.close((()=>{clearTimeout(t),e()}))}else e()})),this.logger.info("AlchemyActionServer stopped")}getPort(){return this.port}isRunning(){return!!this.server}}exports.AlchemyActionServer=M,M.PROJECT_CONFIG_TTL=3e4},2424(e,exports,t){"use strict"
611
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.ensureRequestWorktree=async function(e,t,i){const a="requests/"+t,c=(0,s.join)(e,n.ALCHEMY_DIR_NAME,"worktrees"),l=(0,s.join)(c,t)
612
+ if((0,r.existsSync)(l))return void i.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:l})
613
+ const d=(0,o.simpleGit)(e);(0,r.mkdirSync)(c,{recursive:!0})
474
614
  try{await d.raw(["worktree","prune"])}catch{}const u=await d.branch(),h=u.all.includes(a)
475
615
  try{if(h)await d.raw(["worktree","add",l,a]),i.info("Pre-spawn: created worktree for existing branch",{requestId:t,branchName:a,worktreePath:l})
476
616
  else{const e=u.all.includes("main")?"main":"master"
477
- await d.raw(["worktree","add","-b",a,l,e]),i.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:a,mainBranch:e,worktreePath:l})}}catch(e){const s=e instanceof Error?e.message:e+""
478
- i.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:s})}}
479
- const s=t(9896),r=t(6928),o=t(3807),n=t(4139)},2782(e,exports,t){"use strict"
480
- function s(){return"true"===process.env.ENABLE_DEBUG_TOOLS?.toLowerCase()}function r(e={}){const t={...function(e){const t={}
481
- if(!s())return t.level=(0,l.isDevBuild)()?c.LogLevel.INFO:c.LogLevel.ERROR,t.logToFile=!1,t
617
+ await d.raw(["worktree","add","-b",a,l,e]),i.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:a,mainBranch:e,worktreePath:l})}}catch(e){const r=e instanceof Error?e.message:e+""
618
+ i.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
619
+ const r=t(9896),s=t(6928),o=t(3807),n=t(4139)},2782(e,exports,t){"use strict"
620
+ function r(){return"true"===process.env.ENABLE_DEBUG_TOOLS?.toLowerCase()}function s(e={}){const t={...function(e){const t={}
621
+ if(!r())return t.level=(0,l.isDevBuild)()?c.LogLevel.INFO:c.LogLevel.ERROR,t.logToFile=!1,t
482
622
  if("true"===process.env.DEBUG_LOGGING?.toLowerCase()){t.level=c.LogLevel.DEBUG,t.logToFile=!0
483
- const s=e||process.cwd()
484
- t.logFilePath=i.default.join(s,"logs","debug.log")}const r=process.env.DEBUG_LEVEL?.toLowerCase()
485
- return r&&Object.values(c.LogLevel).includes(r)&&(t.level=r),t}(e.baseDir),...e},{level:o=c.LogLevel.INFO,serviceName:g="mcp-service",logToFile:p=!1,logFilePath:f="./logs/app.log",console=!0}=t,m=[]
486
- if(console&&m.push(new n.default.transports.Console({format:d,stderrLevels:["error","warn","info","debug"]})),p){!function(e){const t=i.default.dirname(e)
487
- a.default.existsSync(t)||a.default.mkdirSync(t,{recursive:!0})}(f)
623
+ const r=e||process.cwd()
624
+ t.logFilePath=i.default.join(r,"logs","debug.log")}const s=process.env.DEBUG_LEVEL?.toLowerCase()
625
+ return s&&Object.values(c.LogLevel).includes(s)&&(t.level=s),t}(e.baseDir),...e},{level:o=c.LogLevel.INFO,serviceName:g="mcp-service",logToFile:p=!1,logFilePath:m="./logs/app.log",console=!0}=t,f=[]
626
+ if(console&&f.push(new n.default.transports.Console({format:d,stderrLevels:["error","warn","info","debug"]})),p){!function(e){const t=i.default.dirname(e)
627
+ a.default.existsSync(t)||a.default.mkdirSync(t,{recursive:!0})}(m)
488
628
  const e=o===c.LogLevel.DEBUG&&"true"===process.env.DEBUG_LOGGING?.toLowerCase()
489
- m.push(new n.default.transports.File({filename:f,format:e?h:u}))}const y=n.default.createLogger({level:o,defaultMeta:{service:g},transports:m})
490
- return o===c.LogLevel.DEBUG&&p&&s()&&y.info("Enhanced logging initialized",{level:o,serviceName:g,logToFile:p,logFilePath:f,debugLogging:process.env.DEBUG_LOGGING}),{error(e,t){y.error(e,t)},warn(e,t){y.warn(e,t)},info(e,t){y.info(e,t)},debug(e,t){y.debug(e,t)},child(e){const s=y.child(e)
491
- return{error(e,t){s.error(e,t)},warn(e,t){s.warn(e,t)},info(e,t){s.info(e,t)},debug(e,t){s.debug(e,t)},child(e){return r({...t,serviceName:`${g}:${e.component||"nested"}`})}}}}}var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
492
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.createLogger=r,exports.createEnvLogger=function(e={}){return r(e)},exports.isDebugToolsEnabled=s,exports.isDebugLoggingEnabled=function(){return!!s()&&"true"===process.env.DEBUG_LOGGING?.toLowerCase()},exports.getDebugLevelFromEnv=function(){if(!s())return c.LogLevel.INFO
629
+ f.push(new n.default.transports.File({filename:m,format:e?h:u}))}const y=n.default.createLogger({level:o,defaultMeta:{service:g},transports:f})
630
+ return o===c.LogLevel.DEBUG&&p&&r()&&y.info("Enhanced logging initialized",{level:o,serviceName:g,logToFile:p,logFilePath:m,debugLogging:process.env.DEBUG_LOGGING}),{error(e,t){y.error(e,t)},warn(e,t){y.warn(e,t)},info(e,t){y.info(e,t)},debug(e,t){y.debug(e,t)},child(e){const r=y.child(e)
631
+ return{error(e,t){r.error(e,t)},warn(e,t){r.warn(e,t)},info(e,t){r.info(e,t)},debug(e,t){r.debug(e,t)},child(e){return s({...t,serviceName:`${g}:${e.component||"nested"}`})}}}}}var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
632
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.createLogger=s,exports.createEnvLogger=function(e={}){return s(e)},exports.isDebugToolsEnabled=r,exports.isDebugLoggingEnabled=function(){return!!r()&&"true"===process.env.DEBUG_LOGGING?.toLowerCase()},exports.getDebugLevelFromEnv=function(){if(!r())return c.LogLevel.INFO
493
633
  const e=process.env.DEBUG_LEVEL?.toLowerCase()
494
634
  return e&&Object.values(c.LogLevel).includes(e)?e:c.LogLevel.INFO}
495
- const n=o(t(5124)),i=o(t(6928)),a=o(t(9896)),c=t(6540),l=t(7960),d=n.default.format.combine(n.default.format.timestamp(),n.default.format.colorize(),n.default.format.printf((({level:e,message:t,timestamp:s,...r})=>`${s} [${e}]: ${t}${Object.keys(r).length?" "+JSON.stringify(r,null,0):""}`))),u=n.default.format.combine(n.default.format.timestamp(),n.default.format.json()),h=n.default.format.combine(n.default.format.timestamp(),n.default.format.printf((({level:e,message:t,timestamp:s,service:r,component:o,...n})=>{const i=r?`[${r}]`:"",a=o?`[${o}]`:"",c=Object.keys(n).length?" "+JSON.stringify(n,null,2):""
496
- return`${s} ${i}${a} [${e.toUpperCase()}]: ${t}${c}`}))),g=r({logToFile:!1,console:!0,level:c.LogLevel.INFO})
635
+ const n=o(t(5124)),i=o(t(6928)),a=o(t(9896)),c=t(6540),l=t(7960),d=n.default.format.combine(n.default.format.timestamp(),n.default.format.colorize(),n.default.format.printf((({level:e,message:t,timestamp:r,...s})=>`${r} [${e}]: ${t}${Object.keys(s).length?" "+JSON.stringify(s,null,0):""}`))),u=n.default.format.combine(n.default.format.timestamp(),n.default.format.json()),h=n.default.format.combine(n.default.format.timestamp(),n.default.format.printf((({level:e,message:t,timestamp:r,service:s,component:o,...n})=>{const i=s?`[${s}]`:"",a=o?`[${o}]`:"",c=Object.keys(n).length?" "+JSON.stringify(n,null,2):""
636
+ return`${r} ${i}${a} [${e.toUpperCase()}]: ${t}${c}`}))),g=s({logToFile:!1,console:!0,level:c.LogLevel.INFO})
497
637
  exports.default=g},2928(e,exports,t){"use strict"
498
638
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.StuckSessionWatchdog=void 0
499
- const s=t(8824)
500
- exports.StuckSessionWatchdog=class{constructor(e){this.stuckSessions=new Map,this.resolvedRecently=new Set,this.notifiedSessions=new Set,this.testInjectedIds=new Set,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??9e4,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),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.resolvedRecently.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 s=e||"test-stuck-"+Date.now(),r={sessionId:s,messageId:"test-msg-"+Date.now(),detectedAt:Date.now(),retryCount:0,lastMessageCreated:Date.now()-this.stuckThresholdMs-1e3,sessionTitle:t||"Test Stuck Session"}
501
- return this.stuckSessions.set(s,r),this.testInjectedIds.add(s),this.notifiedSessions.add(s),this.onStuckDetected?.(r),r}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(){const e=this.getOpenCodeUrl()
502
- if(e)try{const[t,r]=await Promise.all([fetch(e+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})])
503
- if(!t.ok||!r.ok)return
504
- const o=await t.json(),n=await r.json(),i=o.filter((e=>"running"===n[e.id]?.type)),a=new Set
505
- await Promise.all(i.map((async t=>{try{const s=await fetch(`${e}/session/${t.id}/message?limit=5`,{signal:AbortSignal.timeout(5e3)})
506
- if(!s.ok)return
507
- const r=[...await s.json()].reverse().find((e=>"assistant"===e.info.role))
508
- if(!r)return
509
- const o=r.info
639
+ const r=t(8824)
640
+ exports.StuckSessionWatchdog=class{constructor(e){this.stuckSessions=new Map,this.resolvedRecently=new Set,this.notifiedSessions=new Set,this.testInjectedIds=new Set,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??9e4,this.maxRetries=e.maxRetries??3,this.getDefaultModel=e.getDefaultModel}start(){this.intervalTimer||(this.logger.info("Stuck session watchdog starting",{checkInterval:this.checkIntervalMs,stuckThreshold:this.stuckThresholdMs,maxRetries:this.maxRetries}),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.resolvedRecently.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"}
641
+ 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(){const e=this.getOpenCodeUrl()
642
+ if(e)try{const[t,s]=await Promise.all([fetch(e+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})])
643
+ if(!t.ok||!s.ok)return
644
+ const o=await t.json(),n=await s.json(),i=o.filter((e=>"running"===n[e.id]?.type)),a=new Set
645
+ await Promise.all(i.map((async t=>{try{const r=await fetch(`${e}/session/${t.id}/message?limit=5`,{signal:AbortSignal.timeout(5e3)})
646
+ if(!r.ok)return
647
+ const s=[...await r.json()].reverse().find((e=>"assistant"===e.info.role))
648
+ if(!s)return
649
+ const o=s.info
510
650
  if(o.time.completed)return
511
651
  const n=o.tokens
512
652
  if(n&&(n.output>0||n.reasoning>0))return
513
- const i=r.parts||[]
653
+ const i=s.parts||[]
514
654
  if(i.some((e=>"tool"===e.type&&e.state&&("pending"===e.state.status||"running"===e.state.status))))return
515
655
  if(i.some((e=>"text"===e.type&&e.text&&e.text.length>0)))return
516
656
  const c=Date.now()-o.time.created
517
657
  if(c<this.stuckThresholdMs)return
518
658
  if(a.add(t.id),!this.stuckSessions.has(t.id)){const e={sessionId:t.id,messageId:o.id,detectedAt:Date.now(),retryCount:0,lastMessageCreated:o.time.created,sessionTitle:t.title}
519
- this.stuckSessions.set(t.id,e),this.logger.warn("Session detected as stuck",{sessionId:t.id,title:t.title,messageAge:Math.round(c/1e3)}),e.retryCount<this.maxRetries?(this.logger.info("Auto-retrying stuck session",{sessionId:t.id,retryCount:e.retryCount}),this.retrySession(t.id).then((s=>{s.success||(this.logger.warn("Auto-retry failed",{sessionId:t.id,error:s.error}),this.notifiedSessions.has(t.id)||(this.notifiedSessions.add(t.id),this.onStuckDetected?.(e)))})).catch((()=>{}))):this.notifiedSessions.has(t.id)||(this.notifiedSessions.add(t.id),this.onStuckDetected?.(e))}}catch(e){this.logger.debug("Failed to check session for stuck state",{sessionId:t.id,error:(0,s.getErrorMessage)(e)})}})))
520
- for(const[e]of this.stuckSessions)a.has(e)||this.testInjectedIds.has(e)||(this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.resolvedRecently.add(e),this.logger.info("Session no longer stuck",{sessionId:e}),this.onStuckResolved?.(e),setTimeout((()=>this.resolvedRecently.delete(e)),6e4))}catch(e){this.logger.debug("Stuck session check failed",{error:(0,s.getErrorMessage)(e)})}}async retrySession(e){const t=this.getOpenCodeUrl()
659
+ this.stuckSessions.set(t.id,e),this.logger.warn("Session detected as stuck",{sessionId:t.id,title:t.title,messageAge:Math.round(c/1e3)}),e.retryCount<this.maxRetries?(this.logger.info("Auto-retrying stuck session",{sessionId:t.id,retryCount:e.retryCount}),this.retrySession(t.id).then((r=>{r.success||(this.logger.warn("Auto-retry failed",{sessionId:t.id,error:r.error}),this.notifiedSessions.has(t.id)||(this.notifiedSessions.add(t.id),this.onStuckDetected?.(e)))})).catch((()=>{}))):this.notifiedSessions.has(t.id)||(this.notifiedSessions.add(t.id),this.onStuckDetected?.(e))}}catch(e){this.logger.debug("Failed to check session for stuck state",{sessionId:t.id,error:(0,r.getErrorMessage)(e)})}})))
660
+ for(const[e]of this.stuckSessions)a.has(e)||this.testInjectedIds.has(e)||(this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.resolvedRecently.add(e),this.logger.info("Session no longer stuck",{sessionId:e}),this.onStuckResolved?.(e),setTimeout((()=>this.resolvedRecently.delete(e)),6e4))}catch(e){this.logger.debug("Stuck session check failed",{error:(0,r.getErrorMessage)(e)})}}async retrySession(e){const t=this.getOpenCodeUrl()
521
661
  if(!t)return{success:!1,error:"OpenCode server not available"}
522
- const r=this.stuckSessions.get(e)
523
- if(!r)return{success:!1,error:"Session is not currently flagged as stuck"}
524
- if(r.retryCount>=this.maxRetries)return{success:!1,error:`Max retries (${this.maxRetries}) exceeded for this session`}
662
+ const s=this.stuckSessions.get(e)
663
+ if(!s)return{success:!1,error:"Session is not currently flagged as stuck"}
664
+ if(s.retryCount>=this.maxRetries)return{success:!1,error:`Max retries (${this.maxRetries}) exceeded for this session`}
525
665
  try{this.logger.info("Retrying stuck session: aborting",{sessionId:e})
526
- const s=await fetch(`${t}/session/${e}/abort`,{method:"POST",signal:AbortSignal.timeout(5e3)})
527
- s.ok||this.logger.warn("Abort returned non-OK (may already be idle)",{sessionId:e,status:s.status}),await new Promise((e=>setTimeout(e,1e3))),this.logger.info("Retrying stuck session: sending continue",{sessionId:e})
666
+ const r=await fetch(`${t}/session/${e}/abort`,{method:"POST",signal:AbortSignal.timeout(5e3)})
667
+ r.ok||this.logger.warn("Abort returned non-OK (may already be idle)",{sessionId:e,status:r.status}),await new Promise((e=>setTimeout(e,1e3))),this.logger.info("Retrying stuck session: sending continue",{sessionId:e})
528
668
  const o=await fetch(`${t}/session/${e}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:"Continue"}],...(()=>{const e=this.getDefaultModel?.()
529
669
  if(!e)return{}
530
- const[t,...s]=e.split("/")
531
- return{model:{providerID:t,modelID:s.join("/")}}})()}),signal:AbortSignal.timeout(1e4)})
670
+ const[t,...r]=e.split("/")
671
+ return{model:{providerID:t,modelID:r.join("/")}}})()}),signal:AbortSignal.timeout(1e4)})
532
672
  if(!o.ok){const e=await o.text().catch((()=>""))
533
- return{success:!1,error:`Failed to re-send prompt: ${o.status} ${e}`}}return r.retryCount++,this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.logger.info("Stuck session retry sent",{sessionId:e,retryCount:r.retryCount}),{success:!0}}catch(e){return{success:!1,error:(0,s.getErrorMessage)(e)}}}}},3077(e,exports,t){"use strict"
673
+ return{success:!1,error:`Failed to re-send prompt: ${o.status} ${e}`}}return s.retryCount++,this.stuckSessions.delete(e),this.notifiedSessions.delete(e),this.logger.info("Stuck session retry sent",{sessionId:e,retryCount:s.retryCount}),{success:!0}}catch(e){return{success:!1,error:(0,r.getErrorMessage)(e)}}}}},3077(e,exports,t){"use strict"
534
674
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.HookExecutor=void 0
535
- const s=t(8740),r=t(4627),o=t(4769)
536
- exports.HookExecutor=class{constructor(e,t,r,o,n){this.logger=e,this.cache=new s.HookCache(e,t,o),this.ctx={logger:e,projectId:t,projectPath:r,apiClient:o,cachedDefaultModel:n?.cachedDefaultModel,trackChildPid:n?.trackChildPid}}setDefaultModel(e){this.ctx.cachedDefaultModel=e}setProjectId(e){this.ctx.projectId=e,this.cache.setProjectId(e)}async processEvent(e){const{eventType:t,sessionId:s,variables:o,timing:n}=e,i={triggered:0,hooks:[]}
675
+ const r=t(8740),s=t(4627),o=t(4769)
676
+ exports.HookExecutor=class{constructor(e,t,s,o,n){this.logger=e,this.cache=new r.HookCache(e,t,o),this.ctx={logger:e,projectId:t,projectPath:s,apiClient:o,cachedDefaultModel:n?.cachedDefaultModel,trackChildPid:n?.trackChildPid}}setDefaultModel(e){this.ctx.cachedDefaultModel=e}setProjectId(e){this.ctx.projectId=e,this.cache.setProjectId(e)}async processEvent(e){const{eventType:t,sessionId:r,variables:o,timing:n}=e,i={triggered:0,hooks:[]}
537
677
  if(!this.ctx.projectId)return this.logger.debug("No project ID, skipping hook event",{eventType:t}),i
538
- try{const e=await this.cache.getHooksForEvent(t,s)
678
+ try{const e=await this.cache.getHooksForEvent(t,r)
539
679
  if(0===e.length)return i
540
680
  const a=new Set,c=e.filter((e=>!a.has(e.id)&&(a.add(e.id),!0))),l=n||"after",d=c.filter((e=>(e.timing||"after")===l))
541
681
  if(0===d.length)return i
542
682
  const u={...o}
543
- if(s&&(!u.parentSessionId||!u.agentType))try{const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/external/opencode/${encodeURIComponent(s)}`),t=e?.data||e
544
- if(t?.agentType&&!u.agentType&&(u.agentType=t.agentType),!u.parentSessionId&&t?.parentSessionId){const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/${t.parentSessionId}`),s=e?.data||e
545
- s?.externalSessionId&&(u.parentSessionId=s.externalSessionId)}}catch(e){this.logger.warn("Failed to enrich hook variables with session context",{sessionId:s,error:e+""})}const h=d.filter((e=>(0,r.evaluateCondition)(e.condition,u)))
683
+ if(r&&(!u.parentSessionId||!u.agentType))try{const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/external/opencode/${encodeURIComponent(r)}`),t=e?.data||e
684
+ if(t?.agentType&&!u.agentType&&(u.agentType=t.agentType),!u.parentSessionId&&t?.parentSessionId){const e=await this.ctx.apiClient.get(`/api/projects/${this.ctx.projectId}/cli-sessions/${t.parentSessionId}`),r=e?.data||e
685
+ r?.externalSessionId&&(u.parentSessionId=r.externalSessionId)}}catch(e){this.logger.warn("Failed to enrich hook variables with session context",{sessionId:r,error:e+""})}const h=d.filter((e=>(0,s.evaluateCondition)(e.condition,u)))
546
686
  if(0===h.length)return i
547
- for(const e of h){await this.executeHook(e,u,s),i.triggered++,i.hooks.push(e.name)
548
- const t=(0,r.determineEventToEmit)(e,u)
549
- t&&await this.processCustomEvent(t,{...u,sessionId:s||""},0)}"session_idle"!==t&&"session_created"!==t&&s&&this.cache.preWarm("session_idle",s)}catch(e){this.logger.error("Error processing hook event",{eventType:t,error:e+""})}return i}async executeHook(e,t,s){const n=(0,r.interpolatePrompt)(e.promptTemplate,t),i=t.requestId
550
- if(this.logger.info("Executing hook",{name:e.name,action:e.action,target:e.target,eventType:e.eventType,sessionId:s}),this.notifyHookTrigger(e).catch((()=>{})),"inject_prompt"===e.action)switch(e.target){case"same_session":default:await(0,o.executeSameSession)(n,s,this.ctx)
687
+ for(const e of h){await this.executeHook(e,u,r),i.triggered++,i.hooks.push(e.name)
688
+ const t=(0,s.determineEventToEmit)(e,u)
689
+ t&&await this.processCustomEvent(t,{...u,sessionId:r||""},0)}"session_idle"!==t&&"session_created"!==t&&r&&this.cache.preWarm("session_idle",r)}catch(e){this.logger.error("Error processing hook event",{eventType:t,error:e+""})}return i}async executeHook(e,t,r){const n=(0,s.interpolatePrompt)(e.promptTemplate,t),i=t.requestId
690
+ if(this.logger.info("Executing hook",{name:e.name,action:e.action,target:e.target,eventType:e.eventType,sessionId:r}),this.notifyHookTrigger(e).catch((()=>{})),"inject_prompt"===e.action)switch(e.target){case"same_session":default:await(0,o.executeSameSession)(n,r,this.ctx)
551
691
  break
552
- case"parent_session":case"ancestor_chain":await(0,o.executeInjectPrompt)(e,e.promptTemplate,s,t,this.ctx)}else await(0,o.executeBackground)(e,n,s,i,this.ctx)}async processCustomEvent(e,t,s){if(s>=10)return void this.logger.warn("Custom event chain depth exceeded",{eventName:e,chainDepth:s})
692
+ case"parent_session":case"ancestor_chain":await(0,o.executeInjectPrompt)(e,e.promptTemplate,r,t,this.ctx)}else await(0,o.executeBackground)(e,n,r,i,this.ctx)}async processCustomEvent(e,t,r){if(r>=10)return void this.logger.warn("Custom event chain depth exceeded",{eventName:e,chainDepth:r})
553
693
  const n=e.startsWith("custom:")?e:"custom:"+e,i=await this.cache.getHooksForEvent(n,t.sessionId)
554
694
  if(0===i.length)return
555
- const a={...t,chainDepth:s+1+"",triggerEvent:n},c=i.filter((e=>(0,r.evaluateCondition)(e.condition,a)))
556
- for(const e of c){const t=(0,r.interpolatePrompt)(e.promptTemplate,a),i=a.sessionId||""
557
- if(this.logger.info("Executing custom event hook",{name:e.name,event:n,chainDepth:s}),"inject_prompt"===e.action)switch(e.target){case"same_session":default:await(0,o.executeSameSession)(t,i,this.ctx)
695
+ const a={...t,chainDepth:r+1+"",triggerEvent:n},c=i.filter((e=>(0,s.evaluateCondition)(e.condition,a)))
696
+ for(const e of c){const t=(0,s.interpolatePrompt)(e.promptTemplate,a),i=a.sessionId||""
697
+ if(this.logger.info("Executing custom event hook",{name:e.name,event:n,chainDepth:r}),"inject_prompt"===e.action)switch(e.target){case"same_session":default:await(0,o.executeSameSession)(t,i,this.ctx)
558
698
  break
559
699
  case"parent_session":case"ancestor_chain":await(0,o.executeInjectPrompt)(e,e.promptTemplate,i,a,this.ctx)}else await(0,o.executeBackground)(e,t,i,a.requestId,this.ctx)
560
- const c=(0,r.determineEventToEmit)(e,a)
561
- c&&await this.processCustomEvent(c,a,s+1)}}async notifyHookTrigger(e){try{await this.ctx.apiClient.post(`/api/projects/${this.ctx.projectId}/hook-triggers`,{hookId:e.id,hookName:e.name,eventType:e.eventType,action:e.action,target:e.target,agent:e.agent})}catch{}}}},3444(e,exports,t){"use strict"
562
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
700
+ const c=(0,s.determineEventToEmit)(e,a)
701
+ c&&await this.processCustomEvent(c,a,r+1)}}async notifyHookTrigger(e){try{await this.ctx.apiClient.post(`/api/projects/${this.ctx.projectId}/hook-triggers`,{hookId:e.id,hookName:e.name,eventType:e.eventType,action:e.action,target:e.target,agent:e.agent})}catch{}}}},3416(e,exports){"use strict"
702
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.KIND_META=exports.QUEST_STATUS_CLOSED=exports.QUEST_STATUS_OPEN=exports.QUEST_DEFAULTS=exports.PLAN_KINDS=void 0,exports.PLAN_KINDS=["plan","quest"],exports.QUEST_DEFAULTS={tickIntervalSeconds:1800,maxInFlight:1,maxPerDay:24,minIterations:3},exports.QUEST_STATUS_OPEN="open",exports.QUEST_STATUS_CLOSED="closed",exports.KIND_META={plan:{docType:"Plan",filenamePrefix:"PLAN-",label:"plan",idLabel:"Plan ID",headingCreated:"Plan Created",errorVerb:"create plan",alchemyKind:"plan-create",canBeActivePlan:!0},quest:{docType:"Quest",filenamePrefix:"QUEST-",label:"quest",idLabel:"Quest ID",headingCreated:"Quest Created",errorVerb:"create quest",alchemyKind:"quest-create",canBeActivePlan:!1}}},3444(e,exports,t){"use strict"
703
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
563
704
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=exports.defaultLogger=exports.isDebugToolsEnabled=exports.getDebugLevelFromEnv=exports.isDebugLoggingEnabled=exports.createEnvLogger=exports.createLogger=void 0
564
- const r=t(2782)
565
- Object.defineProperty(exports,"createLogger",{enumerable:!0,get(){return r.createLogger}}),Object.defineProperty(exports,"createEnvLogger",{enumerable:!0,get(){return r.createEnvLogger}}),Object.defineProperty(exports,"isDebugLoggingEnabled",{enumerable:!0,get(){return r.isDebugLoggingEnabled}}),Object.defineProperty(exports,"getDebugLevelFromEnv",{enumerable:!0,get(){return r.getDebugLevelFromEnv}}),Object.defineProperty(exports,"isDebugToolsEnabled",{enumerable:!0,get(){return r.isDebugToolsEnabled}})
566
- const o=s(t(2782))
705
+ const s=t(2782)
706
+ Object.defineProperty(exports,"createLogger",{enumerable:!0,get(){return s.createLogger}}),Object.defineProperty(exports,"createEnvLogger",{enumerable:!0,get(){return s.createEnvLogger}}),Object.defineProperty(exports,"isDebugLoggingEnabled",{enumerable:!0,get(){return s.isDebugLoggingEnabled}}),Object.defineProperty(exports,"getDebugLevelFromEnv",{enumerable:!0,get(){return s.getDebugLevelFromEnv}}),Object.defineProperty(exports,"isDebugToolsEnabled",{enumerable:!0,get(){return s.isDebugToolsEnabled}})
707
+ const o=r(t(2782))
567
708
  exports.defaultLogger=o.default
568
709
  const n=t(6540)
569
710
  Object.defineProperty(exports,"LogLevel",{enumerable:!0,get(){return n.LogLevel}}),exports.default=o.default},3614(e,exports,t){"use strict"
570
- async function s(e,t,s){const r=a.default.join(e,".alchemy_lab","project.json")
571
- try{const o=await i.default.readFile(r,"utf-8"),n=JSON.parse(o)
572
- n.id=t,n.registeredRemotely=!0,n.metadata&&(n.metadata.basePath=e,n.metadata.alchemyPath=a.default.join(e,".alchemy_lab")),await i.default.writeFile(r,JSON.stringify(n,null,2),"utf-8"),s.info("Updated project.json",{projectJsonPath:r,newProjectId:t})}catch(e){s.warn("Could not update project.json (will be created on launch)",{error:e instanceof Error?e.message:e+""})}}async function r(e,t,s){const r=a.default.join(t,".alchemy_lab","opencode","opencode-local.db")
573
- try{await i.default.access(r)}catch{return void s.info("No OpenCode database found, skipping session directory update")}try{await l("sqlite3",[r,`UPDATE session SET directory = replace(directory, '${e.replace(/'/g,"''")}', '${t.replace(/'/g,"''")}') WHERE directory LIKE '${e.replace(/'/g,"''")}%';`],{timeout:1e4}),s.info("Updated OpenCode session directories",{sourcePath:e,targetPath:t})}catch(e){s.warn("Could not update OpenCode session directories",{error:e instanceof Error?e.message:e+""})}}async function o(e){const t=[]
574
- for(const s of d){const r=a.default.join(e,s)
575
- t.push(i.default.rm(r,{recursive:!0,force:!0}).catch((()=>{})))}t.push((async()=>{try{const t=a.default.join(e,".alchemy_lab"),s=await i.default.readdir(t)
576
- await Promise.all(s.filter((e=>e.endsWith(".lock"))).map((e=>i.default.unlink(a.default.join(t,e)).catch((()=>{})))))}catch{}})()),await Promise.all(t)}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
711
+ async function r(e,t,r){const s=a.default.join(e,".alchemy_lab","project.json")
712
+ try{const o=await i.default.readFile(s,"utf-8"),n=JSON.parse(o)
713
+ n.id=t,n.registeredRemotely=!0,n.metadata&&(n.metadata.basePath=e,n.metadata.alchemyPath=a.default.join(e,".alchemy_lab")),await i.default.writeFile(s,JSON.stringify(n,null,2),"utf-8"),r.info("Updated project.json",{projectJsonPath:s,newProjectId:t})}catch(e){r.warn("Could not update project.json (will be created on launch)",{error:e instanceof Error?e.message:e+""})}}async function s(e,t,r){const s=a.default.join(t,".alchemy_lab","opencode","opencode-local.db")
714
+ try{await i.default.access(s)}catch{return void r.info("No OpenCode database found, skipping session directory update")}try{await l("sqlite3",[s,`UPDATE session SET directory = replace(directory, '${e.replace(/'/g,"''")}', '${t.replace(/'/g,"''")}') WHERE directory LIKE '${e.replace(/'/g,"''")}%';`],{timeout:1e4}),r.info("Updated OpenCode session directories",{sourcePath:e,targetPath:t})}catch(e){r.warn("Could not update OpenCode session directories",{error:e instanceof Error?e.message:e+""})}}async function o(e){const t=[]
715
+ for(const r of d){const s=a.default.join(e,r)
716
+ t.push(i.default.rm(s,{recursive:!0,force:!0}).catch((()=>{})))}t.push((async()=>{try{const t=a.default.join(e,".alchemy_lab"),r=await i.default.readdir(t)
717
+ await Promise.all(r.filter((e=>e.endsWith(".lock"))).map((e=>i.default.unlink(a.default.join(t,e)).catch((()=>{})))))}catch{}})()),await Promise.all(t)}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
577
718
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleForkFilesystemAction=async function(e,t){const{logger:n}=t,{sourcePath:a,targetPath:c,newProjectId:h}=e
578
719
  if(!a||!c||!h)return{success:!1,message:"sourcePath, targetPath, and newProjectId are required",error:"missing_params"}
579
720
  n.info("Starting filesystem fork",{sourcePath:a,targetPath:c,newProjectId:h})
580
- try{const e=await async function(e,t,s){const r=e.endsWith("/")?e:e+"/"
721
+ try{const e=await async function(e,t,r){const s=e.endsWith("/")?e:e+"/"
581
722
  try{const o=[...d,...u].flatMap((e=>["--exclude",e]))
582
- return await l("rsync",["-a",...o,r,t],{timeout:3e5}),s.info("Directory copied via rsync",{source:e,target:t}),!0}catch(e){s.warn("rsync failed, falling back to cp -R",{error:e instanceof Error?e.message:e+""})}try{await i.default.mkdir(t,{recursive:!0})
583
- const r=e.endsWith("/")?e+".":e+"/."
584
- return await l("cp",["-R",r,t+"/"],{timeout:3e5}),s.info("Directory copied via cp -R",{source:e,target:t}),!0}catch(e){return s.error("cp -R failed",{error:e instanceof Error?e.message:e+""}),!1}}(a,c,n)
585
- return e?(await Promise.all([s(c,h,n),r(a,c,n),o(c)]),n.info("Filesystem fork complete",{targetPath:c,newProjectId:h}),{success:!0,message:"Project forked to "+c,data:{targetPath:c,newProjectId:h}}):{success:!1,message:"Failed to copy project directory",error:"copy_failed"}}catch(e){const t=e instanceof Error?e.message:e+""
723
+ return await l("rsync",["-a",...o,s,t],{timeout:3e5}),r.info("Directory copied via rsync",{source:e,target:t}),!0}catch(e){r.warn("rsync failed, falling back to cp -R",{error:e instanceof Error?e.message:e+""})}try{await i.default.mkdir(t,{recursive:!0})
724
+ const s=e.endsWith("/")?e+".":e+"/."
725
+ return await l("cp",["-R",s,t+"/"],{timeout:3e5}),r.info("Directory copied via cp -R",{source:e,target:t}),!0}catch(e){return r.error("cp -R failed",{error:e instanceof Error?e.message:e+""}),!1}}(a,c,n)
726
+ return e?(await Promise.all([r(c,h,n),s(a,c,n),o(c)]),n.info("Filesystem fork complete",{targetPath:c,newProjectId:h}),{success:!0,message:"Project forked to "+c,data:{targetPath:c,newProjectId:h}}):{success:!1,message:"Failed to copy project directory",error:"copy_failed"}}catch(e){const t=e instanceof Error?e.message:e+""
586
727
  n.error("Filesystem fork failed",{error:t})
587
728
  try{await i.default.rm(c,{recursive:!0,force:!0}),n.info("Cleaned up partial fork directory",{targetPath:c})}catch(e){n.warn("Failed to clean up partial fork",{error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Filesystem fork failed: "+t,error:t}}}
588
729
  const i=n(t(1943)),a=n(t(6928)),c=t(5317),l=(0,t(9023).promisify)(c.execFile),d=[".alchemy_lab/opencode.json",".alchemy_lab/server.log",".alchemy_lab/action-server-server.log",".alchemy_lab/mcp-identity-debug.log",".alchemy_lab/worktrees",".opencode/node_modules"],u=[".alchemy_lab/*.lock"]},3785(module){"use strict"
589
730
  module.exports=require("readline")},3807(module){"use strict"
590
- module.exports=require("simple-git")},3962(e,exports,t){"use strict"
591
- function s(){if(n)return n
731
+ module.exports=require("simple-git")},3922(e,exports,t){"use strict"
732
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.captureCommitHashRobustly=async function(e,t,s,o=3,n=100){t.debug("Starting robust commit hash capture",{context:s,maxRetries:o,delayMs:n})
733
+ for(let i=1;o>=i;i++)try{i>1&&(await new Promise((e=>setTimeout(e,n))),t.debug("Retry attempt after delay",{attempt:i,delayMs:n}))
734
+ const r=await e.revparse(["HEAD"])
735
+ await e.catFile(["-e",r])
736
+ const a=await e.revparse(["HEAD"])
737
+ if(r!==a){if(t.warn("Hash inconsistency detected, retrying",{context:s,attempt:i,firstHash:r.substring(0,8),verificationHash:a.substring(0,8)}),i===o)throw Error(`Git state unstable after ${o} attempts: ${r} vs ${a}`)
738
+ continue}const c=await e.show(["--format=%H %s","--no-patch",r])
739
+ return t.debug("Successfully captured stable commit hash",{context:s,attempt:i,hash:r.substring(0,8),fullHash:r,commitMessage:c.split(" ").slice(1).join(" ")}),r}catch(e){if(t.warn("Failed to capture commit hash",{context:s,attempt:i,error:(0,r.getErrorMessage)(e)}),i===o)throw Error(`Failed to capture stable commit hash after ${o} attempts: ${(0,r.getErrorMessage)(e)}`)}throw Error("Unexpected end of retry loop")},exports.validateCommitHash=async function(e,t,s,o){try{s.debug("Validating commit hash",{context:o,commitHash:t.substring(0,8),fullCommitHash:t}),await e.catFile(["-e",t])
740
+ const r=await e.show(["--format=%H %ct %s","--no-patch",t])
741
+ return s.debug("Commit hash validation successful",{context:o,commitHash:t.substring(0,8),commitDetails:r.trim()}),{valid:!0}}catch(e){const n=(0,r.getErrorMessage)(e)
742
+ return s.warn("Commit hash validation failed",{context:o,commitHash:t.substring(0,8),error:n}),{valid:!1,error:n}}},exports.verifyGitStateConsistency=async function(e,t,s,o){const n=[]
743
+ try{const r=await e.revparse(["HEAD"])
744
+ s.debug("Verifying git state consistency",{context:o,expectedHead:t.substring(0,8),currentHead:r.substring(0,8),match:t===r}),t!==r&&n.push(`HEAD mismatch: expected ${t.substring(0,8)}, got ${r.substring(0,8)}`)
745
+ const i=await e.status()
746
+ return(i.ahead>0||i.behind>0)&&n.push(`Branch not in sync: ahead=${i.ahead}, behind=${i.behind}`),(i.staged.length>0||i.modified.length>0)&&n.push(`Working tree not clean: staged=${i.staged.length}, modified=${i.modified.length}`),{consistent:0===n.length,currentHead:r,issues:n}}catch(e){const t=(0,r.getErrorMessage)(e)
747
+ return n.push("Git state check failed: "+t),{consistent:!1,issues:n}}}
748
+ const r=t(8824)},3962(e,exports,t){"use strict"
749
+ function r(){if(n)return n
592
750
  const e=process.argv[1]||""
593
- let s=e
594
- try{s=t(9896).realpathSync(e)}catch{}let r=""
595
- if(s.includes("node_modules")){const e=s.lastIndexOf("/dist/")
596
- e>0&&(r=s.substring(0,e+5))}else{const e=s.lastIndexOf("/dist-dev/")
597
- if(e>0)r=s.substring(0,e+9)
598
- else{const e=s.lastIndexOf("/dist/")
599
- e>0&&(r=s.substring(0,e+5))}}if(!r){const e=process.cwd()
600
- r=e.includes("alchemy-local")||e.includes("alchemy")?o.default.join(e,"dist-dev"):o.default.join(e,"dist")}const i=r.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1,a=o.default.join(r,"mcp-direct.js"),c=process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com"
601
- return n={installPath:r,scriptPath:a,nodePath:process.execPath,isDevMode:i,serverName:"alchemy",remoteServerUrl:c},n}var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
602
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.getInstallContext=s,exports.clearInstallContextCache=function(){n=null},exports.isDevMode=function(e){return e?e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1:s().isDevMode},exports.getServerName=function(e){return e??s().isDevMode,"alchemy"}
603
- const o=r(t(6928))
751
+ let r=e
752
+ try{r=t(9896).realpathSync(e)}catch{}let s=""
753
+ if(r.includes("node_modules")){const e=r.lastIndexOf("/dist/")
754
+ e>0&&(s=r.substring(0,e+5))}else{const e=r.lastIndexOf("/dist-dev/")
755
+ if(e>0)s=r.substring(0,e+9)
756
+ else{const e=r.lastIndexOf("/dist/")
757
+ e>0&&(s=r.substring(0,e+5))}}if(!s){const e=process.cwd()
758
+ s=e.includes("alchemy-local")||e.includes("alchemy")?o.default.join(e,"dist-dev"):o.default.join(e,"dist")}const i=s.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1,a=o.default.join(s,"mcp-direct.js"),c=process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com"
759
+ return n={installPath:s,scriptPath:a,nodePath:process.execPath,isDevMode:i,serverName:"alchemy",remoteServerUrl:c},n}var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
760
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.getInstallContext=r,exports.clearInstallContextCache=function(){n=null},exports.isDevMode=function(e){return e?e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost")||!1:r().isDevMode},exports.getServerName=function(e){return e??r().isDevMode,"alchemy"}
761
+ const o=s(t(6928))
604
762
  let n=null},4139(e,exports){"use strict"
605
763
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.ALCHEMY_DIR_NAME=exports.TOKEN_DIR_NAME=void 0,exports.TOKEN_DIR_NAME="alchemy-key",exports.ALCHEMY_DIR_NAME=".alchemy_lab"},4192(e,exports,t){"use strict"
606
764
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.BaseApiClient=void 0
607
- const s=t(7163)
608
- exports.BaseApiClient=class{constructor(e,t,r,o,n){this.failureCount=0,this.circuitState="closed",this.circuitResetTimeout=null,this.baseUrl=e,this.logger=t,this.metrics=r,this.authToken=o,this.options={retryAttempts:3,retryDelay:1e3,timeout:1e4,circuitBreakerThreshold:10,circuitBreakerResetTime:1e4,...n},this.logger.debug("BaseApiClient constructor options:",{providedOptions:n?JSON.stringify(n):"undefined",mergedOptions:JSON.stringify(this.options)})
609
- try{(0,s.debugTokenAccess)(this,"BaseApiClient constructor")}catch(e){}}setAuthToken(e){this.authToken=e||void 0,this.logger.debug("Auth token updated",{hasToken:!!e,tokenLength:e?e.length:0})}getAuthToken(){return this.authToken}async requestWithRetry(e,t,s,r,o){if("open"===this.circuitState)throw this.logger.warn(`Circuit breaker open - rejecting request to ${e} ${t}`),Error("Service unavailable - circuit breaker open")
765
+ const r=t(7163)
766
+ exports.BaseApiClient=class{constructor(e,t,s,o,n){this.failureCount=0,this.circuitState="closed",this.circuitResetTimeout=null,this.baseUrl=e,this.logger=t,this.metrics=s,this.authToken=o,this.options={retryAttempts:3,retryDelay:1e3,timeout:1e4,circuitBreakerThreshold:10,circuitBreakerResetTime:1e4,...n},this.logger.debug("BaseApiClient constructor options:",{providedOptions:n?JSON.stringify(n):"undefined",mergedOptions:JSON.stringify(this.options)})
767
+ try{(0,r.debugTokenAccess)(this,"BaseApiClient constructor")}catch(e){}}setAuthToken(e){this.authToken=e||void 0,this.logger.debug("Auth token updated",{hasToken:!!e,tokenLength:e?e.length:0})}getAuthToken(){return this.authToken}async requestWithRetry(e,t,r,s,o){if("open"===this.circuitState)throw this.logger.warn(`Circuit breaker open - rejecting request to ${e} ${t}`),Error("Service unavailable - circuit breaker open")
610
768
  const n=t.startsWith("/")?t:"/"+t
611
769
  let i=`${this.baseUrl}${n}`
612
770
  if(o&&Object.keys(o).length>0){const e=Object.entries(o).filter((([e,t])=>null!=t)).map((([e,t])=>Array.isArray(t)?t.map((t=>`${encodeURIComponent(e)}=${encodeURIComponent(t+"")}`)).join("&"):`${encodeURIComponent(e)}=${encodeURIComponent(t+"")}`)).join("&")
613
- i=`${i}${i.includes("?")?"&":"?"}${e}`}const a={"Content-Type":"application/json",Accept:"application/json",...r}
771
+ i=`${i}${i.includes("?")?"&":"?"}${e}`}const a={"Content-Type":"application/json",Accept:"application/json",...s}
614
772
  this.authToken?(a.Authorization="Bearer "+this.authToken,this.logger.debug("Using auth token for request",{method:e,path:n,tokenLength:this.authToken.length})):this.logger.warn(`No auth token available for ${e} ${t}`,{operation:"api-request",method:e,path:t,authStatus:"missing"})
615
773
  const c=process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"
616
774
  a["X-Service-Secret"]=c
@@ -619,292 +777,307 @@ const d=this.options.retryAttempts,u={method:e,path:n},h=this.metrics.startTimer
619
777
  for(;d>l;){l++
620
778
  try{this.logger.debug(`Making ${e} request to ${i}`,{attempt:l,maxAttempts:d,hasAuth:!!a.Authorization,hasServiceIdentifier:!!a["X-Client-Type"]})
621
779
  const t={method:e,headers:{...a},signal:AbortSignal.timeout(this.options.timeout||1e4)}
622
- if("GET"!==e&&void 0!==s&&(t.body=JSON.stringify(s)),this.logger.debug("Request details",{method:e,url:i}),t.headers){const e={}
623
- Object.entries(t.headers).forEach((([t,s])=>{"authorization"===t.toLowerCase()?e[t]="Bearer [redacted]":e[t]=s})),this.logger.debug("Request headers",{headers:e})}if("GET"!==e&&t.body){const e="string"==typeof t.body?t.body:JSON.stringify(t.body)
624
- this.logger.debug("Request body",{bodyPreview:e.substring(0,200)+(e.length>200?"...":""),bodyLength:e.length})}const r=await fetch(i,t)
625
- if(!r.ok){if(500>r.status||this.incrementFailureCount(),this.logger.error("API request failed with status "+r.status,{method:e,url:i,status:r.status,statusText:r.statusText,attempt:l}),401===r.status)throw g({success:!1,status:r.status,retries:l-1}),Error("Authentication failed - invalid or expired token")
626
- if(429===r.status){const e=r.headers.get("Retry-After"),t=e?1e3*parseInt(e):2*(this.options.retryDelay||1e3)
780
+ if("GET"!==e&&void 0!==r&&(t.body=JSON.stringify(r)),this.logger.debug("Request details",{method:e,url:i}),t.headers){const e={}
781
+ Object.entries(t.headers).forEach((([t,r])=>{"authorization"===t.toLowerCase()?e[t]="Bearer [redacted]":e[t]=r})),this.logger.debug("Request headers",{headers:e})}if("GET"!==e&&t.body){const e="string"==typeof t.body?t.body:JSON.stringify(t.body)
782
+ this.logger.debug("Request body",{bodyPreview:e.substring(0,200)+(e.length>200?"...":""),bodyLength:e.length})}const s=await fetch(i,t)
783
+ if(!s.ok){if(this.logger.error("API request failed with status "+s.status,{method:e,url:i,status:s.status,statusText:s.statusText,attempt:l}),401===s.status)throw g({success:!1,status:s.status,retries:l-1}),Error("Authentication failed - invalid or expired token")
784
+ if(429===s.status){const e=s.headers.get("Retry-After"),t=e?1e3*parseInt(e):2*(this.options.retryDelay||1e3)
627
785
  if(this.logger.warn(`Rate limited (429), retrying after ${t}ms`,{attempt:l,maxAttempts:d,retryAfter:e}),d>l){await new Promise((e=>setTimeout(e,t)))
628
- continue}}if(r.status>=500&&d>l){const e=(this.options.retryDelay||1e3)*Math.pow(2,l-1)
629
- this.logger.warn(`Server error ${r.status}, retry in ${e}ms`,{attempt:l,maxAttempts:d}),await new Promise((t=>setTimeout(t,e)))
630
- continue}let t=`API request failed with status ${r.status}: ${r.statusText}`,s={}
631
- try{const e=await r.json()
632
- if(e&&"object"==typeof e&&!1===e.success&&e.error){const r=e.error
633
- r.message&&(t=r.message),s={code:r.code,statusCode:r.statusCode,retryable:r.retryable,details:r.details}}else e&&"object"==typeof e&&e.message&&(t=e.message)}catch(e){try{const e=await r.text()
634
- e&&e.trim()&&(t=`${t}. Response: ${e.substring(0,200)}`)}catch(t){this.logger.debug("Failed to parse error response",{jsonError:e instanceof Error?e.message:e+"",textError:t instanceof Error?t.message:t+""})}}g({success:!1,status:r.status,retries:l-1})
786
+ continue}}if(s.status>=500&&d>l){const e=(this.options.retryDelay||1e3)*Math.pow(2,l-1)
787
+ this.logger.warn(`Server error ${s.status}, retry in ${e}ms`,{attempt:l,maxAttempts:d}),await new Promise((t=>setTimeout(t,e)))
788
+ continue}let t=`API request failed with status ${s.status}: ${s.statusText}`,r={}
789
+ try{const e=await s.json()
790
+ if(e&&"object"==typeof e&&!1===e.success&&e.error){const s=e.error
791
+ s.message&&(t=s.message),r={code:s.code,statusCode:s.statusCode,retryable:s.retryable,details:s.details}}else e&&"object"==typeof e&&e.message&&(t=e.message)}catch(e){try{const e=await s.text()
792
+ e&&e.trim()&&(t=`${t}. Response: ${e.substring(0,200)}`)}catch(t){this.logger.debug("Failed to parse error response",{jsonError:e instanceof Error?e.message:e+"",textError:t instanceof Error?t.message:t+""})}}g({success:!1,status:s.status,retries:l-1})
635
793
  const o=Error(t)
636
- throw o.status=r.status,o.statusText=r.statusText,o.method=e,o.url=i,o.errorDetails=s,o}this.resetFailureCount()
637
- const o=await r.json()
794
+ throw o.status=s.status,o.statusText=s.statusText,o.method=e,o.url=i,o.errorDetails=r,o}this.resetFailureCount()
795
+ const o=await s.json()
638
796
  if(null===o||"object"!=typeof o)throw Error("Invalid API response: expected object, got "+typeof o)
639
797
  if(o&&"object"==typeof o&&!("success"in o)){this.logger.warn("Non-standard API response detected - wrapping in standard format",{responseKeys:Object.keys(o)})
640
798
  const e={success:!0,data:o}
641
- return g({success:!0,status:r.status,retries:l-1,standardFormat:!1}),e}if(o&&"object"==typeof o&&"success"in o&&!1===o.success){const e=o
642
- throw this.logger.error("API returned error response",{errorCode:e.error?.code,errorMessage:e.error?.message,statusCode:e.error?.statusCode}),g({success:!1,status:r.status,retries:l-1,errorCode:e.error?.code}),Error(`API error: ${e.error?.code}: ${e.error?.message}`)}if(o&&"object"==typeof o&&"success"in o&&!0===o.success&&"data"in o){const t=o
643
- return g({success:!0,status:r.status,retries:l-1,standardFormat:!0}),this.logger.debug("API request succeeded with status "+r.status,{method:e,url:i,status:r.status,retries:l-1,hasMetadata:"metadata"in t}),t}return this.logger.warn("Invalid API response format: Unexpected structure",{responseType:typeof o,responseKeys:"object"==typeof o?Object.keys(o):[]}),{success:!0,data:o}}catch(t){if(l>=d)throw this.incrementFailureCount(),this.logger.error(`API request failed after ${l} attempts`,{method:e,url:i,error:t.message||t+"",status:t.status}),g({success:!1,error:!0,retries:l-1}),t
644
- const s=Math.min((this.options.retryDelay||1e3)*Math.pow(2,l-1),5e3)
645
- this.logger.warn(`API request failed, retrying in ${s}ms`,{method:e,url:i,error:t.message||t+"",attempt:l,maxAttempts:d}),await new Promise((e=>setTimeout(e,s)))}}throw Error("Unexpected end of requestWithRetry")}incrementFailureCount(){this.failureCount++,this.metrics.setGauge("api_failure_count",this.failureCount),"half-open"!==this.circuitState?this.failureCount<this.options.circuitBreakerThreshold||"closed"!==this.circuitState||this.openCircuit():this.logger.warn("Half-open probe failed, will retry on next request")}resetFailureCount(){const e="closed"!==this.circuitState
799
+ return g({success:!0,status:s.status,retries:l-1,standardFormat:!1}),e}if(o&&"object"==typeof o&&"success"in o&&!1===o.success){const e=o
800
+ throw this.logger.error("API returned error response",{errorCode:e.error?.code,errorMessage:e.error?.message,statusCode:e.error?.statusCode}),g({success:!1,status:s.status,retries:l-1,errorCode:e.error?.code}),Error(`API error: ${e.error?.code}: ${e.error?.message}`)}if(o&&"object"==typeof o&&"success"in o&&!0===o.success&&"data"in o){const t=o
801
+ return g({success:!0,status:s.status,retries:l-1,standardFormat:!0}),this.logger.debug("API request succeeded with status "+s.status,{method:e,url:i,status:s.status,retries:l-1,hasMetadata:"metadata"in t}),t}return this.logger.warn("Invalid API response format: Unexpected structure",{responseType:typeof o,responseKeys:"object"==typeof o?Object.keys(o):[]}),{success:!0,data:o}}catch(t){if(l>=d){const r=t?.status
802
+ throw void 0!==r&&500>r||this.incrementFailureCount(),this.logger.error(`API request failed after ${l} attempts`,{method:e,url:i,error:t.message||t+"",status:t.status}),g({success:!1,error:!0,retries:l-1}),t}const r=Math.min((this.options.retryDelay||1e3)*Math.pow(2,l-1),5e3)
803
+ this.logger.warn(`API request failed, retrying in ${r}ms`,{method:e,url:i,error:t.message||t+"",attempt:l,maxAttempts:d}),await new Promise((e=>setTimeout(e,r)))}}throw Error("Unexpected end of requestWithRetry")}incrementFailureCount(){this.failureCount++,this.metrics.setGauge("api_failure_count",this.failureCount),"half-open"!==this.circuitState?this.failureCount<this.options.circuitBreakerThreshold||"closed"!==this.circuitState||this.openCircuit():this.logger.warn("Half-open probe failed, will retry on next request")}resetFailureCount(){const e="closed"!==this.circuitState
646
804
  this.failureCount=0,this.circuitState="closed",this.metrics.setGauge("api_failure_count",0),this.metrics.setGauge("circuit_breaker_open",0),this.circuitResetTimeout&&(clearTimeout(this.circuitResetTimeout),this.circuitResetTimeout=null),e&&this.logger.info("Circuit breaker closed after successful request")}openCircuit(){this.circuitState="open",this.metrics.setGauge("circuit_breaker_open",1),this.logger.warn(`Circuit breaker opened after ${this.failureCount} failures`),this.circuitResetTimeout&&clearTimeout(this.circuitResetTimeout)
647
805
  const e=this.options.circuitBreakerResetTime||5e3
648
- this.circuitResetTimeout=setTimeout((()=>{this.circuitState="half-open",this.logger.info("Circuit breaker half-open, next request will probe")}),e)}async get(e,t,s){return this.requestWithRetry("GET",e,void 0,s,t)}async post(e,t,s){return this.requestWithRetry("POST",e,t,s)}async put(e,t,s){return this.requestWithRetry("PUT",e,t,s)}async delete(e,t){return this.requestWithRetry("DELETE",e,void 0,t)}async patch(e,t,s){return this.requestWithRetry("PATCH",e,t,s)}}},4245(module){function e(e){var t=Error("Cannot find module '"+e+"'")
806
+ this.circuitResetTimeout=setTimeout((()=>{this.circuitState="half-open",this.logger.info("Circuit breaker half-open, next request will probe")}),e)}async get(e,t,r){return this.requestWithRetry("GET",e,void 0,r,t)}async post(e,t,r){return this.requestWithRetry("POST",e,t,r)}async put(e,t,r){return this.requestWithRetry("PUT",e,t,r)}async delete(e,t){return this.requestWithRetry("DELETE",e,void 0,t)}async patch(e,t,r){return this.requestWithRetry("PATCH",e,t,r)}}},4245(module){function e(e){var t=Error("Cannot find module '"+e+"'")
649
807
  throw t.code="MODULE_NOT_FOUND",t}e.keys=()=>[],e.resolve=e,e.id=4245,module.exports=e},4627(e,exports){"use strict"
650
- function t(e,t){try{return function(e){const t=s.get(e)
808
+ function t(e,t){try{return function(e){const t=r.get(e)
651
809
  if(t)return t
652
- let r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"{{GLOBSTAR}}").replace(/\*/g,"[^/]*").replace(/\?/g,".").replace(/\{\{GLOBSTAR\}\}/g,".*")
653
- const o=RegExp(`^${r}$`)
654
- return s.size>500&&s.clear(),s.set(e,o),o}(t).test(e)}catch{return!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.evaluateCondition=function(e,s){if(!e||!e.conditions||0===e.conditions.length)return!0
655
- const r=e.conditions.map((e=>{const r=s[e.field]||""
656
- return"paths"===e.field&&r.includes(", ")?function(e,s,r){switch(s){case"matches":return e.some((e=>t(e,r)))
657
- case"not_matches":return e.every((e=>!t(e,r)))
658
- case"contains":return e.some((e=>e.includes(r)))
659
- case"not_contains":return e.every((e=>!e.includes(r)))
660
- case"equals":return e.some((e=>e===r))
661
- case"not_equals":return e.every((e=>e!==r))
662
- default:return!0}}(r.split(", "),e.operator,e.value):function(e,s,r){switch(s){case"matches":return t(e,r)
663
- case"not_matches":return!t(e,r)
664
- case"contains":return e.includes(r)
665
- case"not_contains":return!e.includes(r)
666
- case"equals":return e===r
667
- case"not_equals":return e!==r
668
- default:return!0}}(r,e.operator,e.value)}))
669
- return"OR"===e.logic?r.some((e=>e)):r.every((e=>e))},exports.interpolatePrompt=function(e,t){let s=e
670
- for(const[e,r]of Object.entries(t))s=s.replace(RegExp(`\\{\\{${e}\\}\\}`,"g"),r)
671
- return s},exports.determineEventToEmit=function(e,t){if(e.emitEventOnCondition){const{condition:s,event:o}=e.emitEventOnCondition,n=t[s.field]||""
810
+ let s=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"{{GLOBSTAR}}").replace(/\*/g,"[^/]*").replace(/\?/g,".").replace(/\{\{GLOBSTAR\}\}/g,".*")
811
+ const o=RegExp(`^${s}$`)
812
+ return r.size>500&&r.clear(),r.set(e,o),o}(t).test(e)}catch{return!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.evaluateCondition=function(e,r){if(!e||!e.conditions||0===e.conditions.length)return!0
813
+ const s=e.conditions.map((e=>{const s=r[e.field]||""
814
+ return"paths"===e.field&&s.includes(", ")?function(e,r,s){switch(r){case"matches":return e.some((e=>t(e,s)))
815
+ case"not_matches":return e.every((e=>!t(e,s)))
816
+ case"contains":return e.some((e=>e.includes(s)))
817
+ case"not_contains":return e.every((e=>!e.includes(s)))
818
+ case"equals":return e.some((e=>e===s))
819
+ case"not_equals":return e.every((e=>e!==s))
820
+ default:return!0}}(s.split(", "),e.operator,e.value):function(e,r,s){switch(r){case"matches":return t(e,s)
821
+ case"not_matches":return!t(e,s)
822
+ case"contains":return e.includes(s)
823
+ case"not_contains":return!e.includes(s)
824
+ case"equals":return e===s
825
+ case"not_equals":return e!==s
826
+ default:return!0}}(s,e.operator,e.value)}))
827
+ return"OR"===e.logic?s.some((e=>e)):s.every((e=>e))},exports.interpolatePrompt=function(e,t){let r=e
828
+ for(const[e,s]of Object.entries(t))r=r.replace(RegExp(`\\{\\{${e}\\}\\}`,"g"),s)
829
+ return r},exports.determineEventToEmit=function(e,t){if(e.emitEventOnCondition){const{condition:r,event:o}=e.emitEventOnCondition,n=t[r.field]||""
672
830
  let i=!1
673
- switch(s.operator){case"contains":i=n.includes(s.value)
831
+ switch(r.operator){case"contains":i=n.includes(r.value)
674
832
  break
675
- case"not_contains":i=!n.includes(s.value)
833
+ case"not_contains":i=!n.includes(r.value)
676
834
  break
677
- case"equals":i=n===s.value
835
+ case"equals":i=n===r.value
678
836
  break
679
- case"not_equals":i=n!==s.value
837
+ case"not_equals":i=n!==r.value
680
838
  break
681
- case"matches":try{r.has(s.value)||(r.set(s.value,RegExp(s.value)),r.size>500&&r.clear()),i=r.get(s.value).test(n)}catch{i=!1}break
682
- case"not_matches":try{r.has(s.value)||(r.set(s.value,RegExp(s.value)),r.size>500&&r.clear()),i=!r.get(s.value).test(n)}catch{i=!0}}if(i)return o}return e.emitEvent||null},exports.createToolCallHash=function(e,t){return`${e}:${t?.substring(0,100)||""}`},exports.isDuplicateToolCall=function(e,t=Date.now()){const s=o.get(e)
683
- if(s&&n>t-s)return!0
684
- for(const[e,s]of o.entries())t-s>2*n&&o.delete(e)
839
+ case"matches":try{s.has(r.value)||(s.set(r.value,RegExp(r.value)),s.size>500&&s.clear()),i=s.get(r.value).test(n)}catch{i=!1}break
840
+ case"not_matches":try{s.has(r.value)||(s.set(r.value,RegExp(r.value)),s.size>500&&s.clear()),i=!s.get(r.value).test(n)}catch{i=!0}}if(i)return o}return e.emitEvent||null},exports.createToolCallHash=function(e,t){return`${e}:${t?.substring(0,100)||""}`},exports.isDuplicateToolCall=function(e,t=Date.now()){const r=o.get(e)
841
+ if(r&&n>t-r)return!0
842
+ for(const[e,r]of o.entries())t-r>2*n&&o.delete(e)
685
843
  return o.set(e,t),!1},exports.tryMarkMessageProcessed=function(e,t){if(!t)return!0
686
844
  if(i.get(e)===t)return!1
687
845
  if(i.set(e,t),i.size>100){const e=i.keys().next().value
688
846
  e&&i.delete(e)}return!0}
689
- const s=new Map,r=new Map,o=new Map,n=2e3,i=new Map},4654(e,exports,t){"use strict"
690
- async function s(e,t){try{const s=await fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})
691
- if(!s.ok)return"error"
692
- const r=await s.json()
693
- return r[t]?.type||"unknown"}catch{return"error"}}function r(e){return new Promise((t=>setTimeout(t,e)))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handlePromptAndWaitAction=async function(e,t){const{logger:n,projectPath:i,projectId:a,apiClient:c,trackChildPid:l}=t,{sessionId:d,message:u,prompt:h,timeoutSeconds:g=300,agent:p="coder",title:f}=e,m=u||h
694
- if(!m)return{success:!1,message:"message is required",error:"missing_params"}
847
+ const r=new Map,s=new Map,o=new Map,n=2e3,i=new Map},4654(e,exports,t){"use strict"
848
+ async function r(e,t){try{const r=await fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})
849
+ if(!r.ok)return"error"
850
+ const s=await r.json()
851
+ return s[t]?.type||"unknown"}catch{return"error"}}function s(e){return new Promise((t=>setTimeout(t,e)))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handlePromptAndWaitAction=async function(e,t){const{logger:n,projectPath:i,projectId:a,apiClient:c,trackChildPid:l}=t,{sessionId:d,message:u,prompt:h,timeoutSeconds:g=300,agent:p="coder",title:m}=e,f=u||h
852
+ if(!f)return{success:!1,message:"message is required",error:"missing_params"}
695
853
  const y=(0,o.getOpenCodeServerUrl)(i,n)
696
854
  if(!y)return{success:!1,message:"OpenCode not running",error:"no_opencode"}
697
855
  const v=Date.now()
698
856
  let w=d
699
- if(w){try{const e=await fetch(`${y}/session/${w}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:m}],...t.cachedDefaultModel?(()=>{const[e,...s]=t.cachedDefaultModel.split("/")
700
- return{model:{providerID:e,modelID:s.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
701
- if(!e.ok)return{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:"Failed to inject prompt: "+(e instanceof Error?e.message:e+""),error:"prompt_failed"}}n.info("Injected prompt into existing session",{sessionId:w})}else{const e=await(0,o.spawnActionAgent)({agent:p,prompt:m,cwd:i,projectPath:i,projectId:a,title:f||"prompt-and-wait",apiClient:c,logger:n,trackChildPid:l,getDefaultModel(){return t.cachedDefaultModel||void 0}})
857
+ if(w){try{const e=await fetch(`${y}/session/${w}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:f}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
858
+ return{model:{providerID:e,modelID:r.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
859
+ if(!e.ok)return{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:"Failed to inject prompt: "+(e instanceof Error?e.message:e+""),error:"prompt_failed"}}n.info("Injected prompt into existing session",{sessionId:w})}else{const e=await(0,o.spawnActionAgent)({agent:p,prompt:f,cwd:i,projectPath:i,projectId:a,title:m||"prompt-and-wait",apiClient:c,logger:n,trackChildPid:l,getDefaultModel(){return t.cachedDefaultModel||void 0}})
702
860
  if(!e.success||!e.sessionId)return{success:!1,message:"Failed to spawn session: "+e.error,error:"spawn_failed"}
703
- w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const I=await async function(e,t,o){const n=Date.now()+1e3*o
861
+ w=e.sessionId,n.info("Spawned session for prompt-and-wait",{sessionId:w})}const b=await async function(e,t,o){const n=Date.now()+1e3*o
704
862
  let i=!1
705
863
  const a=Date.now()+15e3
706
- for(;Date.now()<a&&Date.now()<n;){const o=await s(e,t)
864
+ for(;Date.now()<a&&Date.now()<n;){const o=await r(e,t)
707
865
  if("busy"===o||"running"===o){i=!0
708
- break}await r(500)}for(;Date.now()<n;){const o=await s(e,t)
866
+ break}await s(500)}for(;Date.now()<n;){const o=await r(e,t)
709
867
  if("error"===o)return{status:"error"}
710
868
  if(("idle"===o||"completed"===o)&&i)return{status:"completed"}
711
- "busy"!==o&&"running"!==o||(i=!0),await r(2e3)}return{status:"timeout"}}(y,w,g),k=Date.now()-v,S=await async function(e,t){try{const s=await fetch(`${e}/session/${t}/message?limit=200`,{signal:AbortSignal.timeout(1e4)})
712
- return s.ok?(await s.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),C=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(S),j=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
713
- return""}(S)
714
- return{success:!0,message:"Prompt completed",data:{sessionId:w,status:I.status,messages:S,toolCallCount:C,elapsedMs:k,finalResponse:j}}}
869
+ "busy"!==o&&"running"!==o||(i=!0),await s(2e3)}return{status:"timeout"}}(y,w,g),I=Date.now()-v,E=await async function(e,t){try{const r=await fetch(`${e}/session/${t}/message?limit=200`,{signal:AbortSignal.timeout(1e4)})
870
+ return r.ok?(await r.json()).map((e=>({role:e.info?.role||"unknown",text:(e.parts||[]).filter((e=>"text"===e.type)).map((e=>e.text)).join("\n"),toolCalls:(e.parts||[]).filter((e=>"tool_use"===e.type)).map((e=>({name:e.name,id:e.id}))),timestamp:e.info?.time?.created}))):[]}catch{return[]}}(y,w),C=function(e){return e.reduce(((e,t)=>e+(t.toolCalls?.length||0)),0)}(E),S=function(e){for(let t=e.length-1;t>=0;t--)if("assistant"===e[t].role&&e[t].text)return e[t].text
871
+ return""}(E)
872
+ return{success:!0,message:"Prompt completed",data:{sessionId:w,status:b.status,messages:E,toolCallCount:C,elapsedMs:I,finalResponse:S}}}
715
873
  const o=t(8148)},4769(e,exports,t){"use strict"
716
- function s(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function r(e,t,r){const o=s(r)
717
- if(!o||!t)return!1
718
- try{const s=await async function(e,t){try{const s=await t.apiClient.get(`/api/projects/${t.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=s?.data||s
719
- return r?.agentType||""}catch{return""}}(t,r)||void 0
720
- let n
721
- if(r.cachedDefaultModel){const[e,...t]=r.cachedDefaultModel.split("/")
722
- n={providerID:e,modelID:t.join("/")}}const i=`${o}/session/${t}/prompt_async`,a=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:e}],...s?{agent:s}:{},...n?{model:n}:{}})})
723
- return a.ok?(r.logger.debug("Message sent to session",{sessionId:t}),!0):(r.logger.warn("Failed to send message to session",{sessionId:t,status:a.status}),!1)}catch(e){return r.logger.warn("Error sending message to session",{sessionId:t,error:e+""}),!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeBackground=async function(e,t,s,r,n){const i=await(0,o.spawnActionAgent)({agent:e.agent,prompt:t,cwd:n.projectPath,projectPath:n.projectPath,projectId:n.projectId,title:"Hook: "+e.name,requestId:r,parentSessionId:"independent_session"===e.target?void 0:s||void 0,apiClient:n.apiClient,logger:n.logger,trackChildPid:n.trackChildPid})
724
- i.success||n.logger.warn("Background hook spawn failed",{hook:e.name,error:i.error})},exports.executeSameSession=async function(e,t,s){await r(e,t,s)},exports.executeInjectPrompt=async function(e,s,o,n,i){let a=[]
725
- if("ancestor_chain"===e.target)try{const e=await i.apiClient.get(`/api/projects/${i.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/ancestors`),t=/^ses_[0-9A-Z]{26}$/
726
- a=(e?.data||e||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))).filter((e=>!(!e.externalSessionId||t.test(e.externalSessionId))||(i.logger.warn("inject_prompt: skipping corrupt ancestor (internal-ULID-format externalSessionId)",{sessionId:o,corruptExternalSessionId:e.externalSessionId}),!1))),i.logger.debug("inject_prompt: ancestors fetched",{sessionId:o,count:a.length,ancestors:a.map((e=>e.externalSessionId))})}catch(e){i.logger.warn("inject_prompt: ancestors fetch failed, falling back to parent",{sessionId:o,error:e+""})}if(0===a.length)try{const e=await i.apiClient.get(`/api/projects/${i.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/parent`),t=e?.data?.parentExternalSessionId||e?.parentExternalSessionId
727
- t&&(a=[{externalSessionId:t}])}catch{}if(0===a.length)return void i.logger.debug("inject_prompt: no ancestors found",{sessionId:o})
728
- const c=a.map((e=>`${e.agentType||"agent"}${e.title?" "+e.title:""} (${e.externalSessionId})`)).join(" \u2192 "),{interpolatePrompt:l}=await Promise.resolve().then(t.bind(t,4627)),d={...n,chainInfo:c}
729
- i.logger.debug("inject_prompt: interpolating",{variables:Object.keys(d),chainInfo:c,sessionId:n.sessionId,agentType:n.agentType})
730
- const u=l(s,d)
731
- for(const e of a)i.logger.debug("inject_prompt: sending to ancestor",{targetSessionId:e.externalSessionId,agentType:e.agentType}),await r("\x3c!-- inserted-prompt --\x3e"+u,e.externalSessionId,i)},exports.executeNotification=async function(e,t){const r=s(t)
732
- if(r)try{const t=r+"/tui/toast"
733
- await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:"Background Work Complete",message:e,variant:"success",duration:8e3})})}catch(e){t.logger.warn("Failed to send toast notification",{error:e+""})}},exports.executeDirectReport=async function(e,t,s){try{const o="http://localhost:3456/api/direct-report",n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e,requestId:t||void 0,status:"success"})})
734
- if(!n.ok)return void s.logger.warn("Direct report failed",{sessionId:e,status:n.status})
735
- const i=await n.json()
736
- if(!i.success||!i.data)return void s.logger.warn("Direct report returned failure",{sessionId:e})
737
- const{notifiedSessions:a,report:c,parentExternalSessionId:l}=i.data,d=a||(l?[{externalSessionId:l}]:[])
738
- for(const e of d){const t=c?.content?.reportStatus||"success",o=[`\x3c!-- inserted-prompt --\x3e${{success:"\u2705",partial:"\u26a0\ufe0f",failure:"\u274c",blocked:"\u{1f6ab}"}[t]||"\u{1f4cb}"} **Worker Report \u2014 ${t}**`,"",c?.content?.summary||"Task completed",...c?.content?.details?["",c.content.details]:[]].join("\n")
739
- await r(o,e.externalSessionId,s)}}catch(t){s.logger.warn("Error in direct report",{sessionId:e,error:t+""})}}
740
- const o=t(8148)},5086(module){"use strict"
874
+ function r(e){return(0,o.getOpenCodeServerUrl)(e.projectPath,e.logger)}async function s(e,t,s,o){const n=r(s)
875
+ if(!n||!t)return!1
876
+ try{const r=await async function(e,t){try{const r=await t.apiClient.get(`/api/projects/${t.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=r?.data||r
877
+ return s?.agentType||""}catch{return""}}(t,s)||void 0
878
+ let i
879
+ if(s.cachedDefaultModel){const[e,...t]=s.cachedDefaultModel.split("/")
880
+ i={providerID:e,modelID:t.join("/")}}const a=`${n}/session/${t}/prompt_async`,c={type:"text",text:e}
881
+ o&&(c.metadata=o)
882
+ const l=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[c],...r?{agent:r}:{},...i?{model:i}:{}})})
883
+ return l.ok?(s.logger.debug("Message sent to session",{sessionId:t}),!0):(s.logger.warn("Failed to send message to session",{sessionId:t,status:l.status}),!1)}catch(e){return s.logger.warn("Error sending message to session",{sessionId:t,error:e+""}),!1}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeBackground=async function(e,t,r,s,n){const i=await(0,o.spawnActionAgent)({agent:e.agent,prompt:t,cwd:n.projectPath,projectPath:n.projectPath,projectId:n.projectId,title:"Hook: "+e.name,requestId:s,parentSessionId:"independent_session"===e.target?void 0:r||void 0,apiClient:n.apiClient,logger:n.logger,trackChildPid:n.trackChildPid})
884
+ i.success||n.logger.warn("Background hook spawn failed",{hook:e.name,error:i.error})},exports.executeSameSession=async function(e,t,r,o){await s(e,t,r,o)},exports.executeInjectPrompt=async function(e,r,o,i,a){let c=[]
885
+ if("ancestor_chain"===e.target)try{const e=await a.apiClient.get(`/api/projects/${a.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/ancestors`),t=/^ses_[0-9A-Z]{26}$/
886
+ c=(e?.data||e||[]).map((e=>({externalSessionId:e.externalSessionId,agentType:e.agentType,title:e.title}))).filter((e=>!(!e.externalSessionId||t.test(e.externalSessionId))||(a.logger.warn("inject_prompt: skipping corrupt ancestor (internal-ULID-format externalSessionId)",{sessionId:o,corruptExternalSessionId:e.externalSessionId}),!1))),a.logger.debug("inject_prompt: ancestors fetched",{sessionId:o,count:c.length,ancestors:c.map((e=>e.externalSessionId))})}catch(e){a.logger.warn("inject_prompt: ancestors fetch failed, falling back to parent",{sessionId:o,error:e+""})}if(0===c.length)try{const e=await a.apiClient.get(`/api/projects/${a.projectId}/cli-sessions/external/opencode/${encodeURIComponent(o)}/parent`),t=e?.data?.parentExternalSessionId||e?.parentExternalSessionId
887
+ t&&(c=[{externalSessionId:t}])}catch{}if(0===c.length)return void a.logger.debug("inject_prompt: no ancestors found",{sessionId:o})
888
+ const l=c.map((e=>`${e.agentType||"agent"}${e.title?" "+e.title:""} (${e.externalSessionId})`)).join(" \u2192 "),{interpolatePrompt:d}=await Promise.resolve().then(t.bind(t,4627)),u={...i,chainInfo:l}
889
+ a.logger.debug("inject_prompt: interpolating",{variables:Object.keys(u),chainInfo:l,sessionId:i.sessionId,agentType:i.agentType})
890
+ const h=d(r,u)
891
+ await Promise.allSettled(c.map((e=>(a.logger.debug("inject_prompt: sending to ancestor",{targetSessionId:e.externalSessionId,agentType:e.agentType}),s(h,e.externalSessionId,a,(0,n.partMeta)("inserted-prompt"))))))},exports.executeNotification=async function(e,t){const s=r(t)
892
+ if(s)try{const t=s+"/tui/toast"
893
+ await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:"Background Work Complete",message:e,variant:"success",duration:8e3})})}catch(e){t.logger.warn("Failed to send toast notification",{error:e+""})}},exports.executeDirectReport=async function(e,t,r){try{const o="http://localhost:3456/api/direct-report",n=await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionId:e,requestId:t||void 0,status:"success"})})
894
+ if(!n.ok)return void r.logger.warn("Direct report failed",{sessionId:e,status:n.status})
895
+ const a=await n.json()
896
+ if(!a.success||!a.data)return void r.logger.warn("Direct report returned failure",{sessionId:e})
897
+ const{notifiedSessions:c,parentExternalSessionId:l}=a.data,d=c||(l?[{externalSessionId:l}]:[])
898
+ await Promise.allSettled(d.map((t=>{const o=t.content?.reportStatus||"success",n=t.content?.summary||"Task completed",a=t.content?.details,c=t.messageCode,l=(0,i.formatReportNotification)({messageCode:c,status:o,summary:n,details:a,fromSessionId:e}),d={alchemy:{kind:"worker-report",payload:{messageCode:c,status:o,summary:n,details:a,fromSessionId:e}}}
899
+ return s(l,t.externalSessionId,r,d)})))}catch(t){r.logger.warn("Error in direct report",{sessionId:e,error:t+""})}}
900
+ const o=t(8148),n=t(2272),i=t(7904)},5086(module){"use strict"
741
901
  module.exports=require("ws")},5124(module){"use strict"
742
902
  module.exports=require("winston")},5317(module){"use strict"
743
- module.exports=require("child_process")},5607(e,exports,t){"use strict"
744
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeAction=async function(e,t,s){const r=f[e]
745
- if(!r)return{success:!1,message:"Unknown action type: "+e,error:"No handler registered for action type: "+e}
746
- try{return await r(t,s)}catch(t){const r=(0,p.getErrorMessage)(t)
747
- return s.logger.error("Action handler failed",{actionType:e,error:r}),{success:!1,message:"Action failed: "+r,error:r}}}
748
- const s=t(6636),r=t(7453),o=t(5700),n=t(848),i=t(9735),a=t(7767),c=t(9266),l=t(6555),d=t(3614),u=t(4654),h=t(7078),g=t(1405),p=t(8824),f={revert_request:s.handleRevertRequestAction,"revert-request":s.handleRevertRequestAction,complete_request:r.handleCompleteRequestAction,"complete-request":r.handleCompleteRequestAction,"finalize-request":n.handleFinalizeRequestAction,"approve-request":i.handleApproveRequestAction,"approve-and-work-request":a.handleApproveAndWorkRequestAction,"restart-mcp":o.handleRestartMcpAction,"opencode-proxy":c.handleOpenCodeProxyAction,"run-tests":l.handleRunTestsAction,async"inject-prompt"(e,s){const{sessionId:r,message:o}=e
749
- if(!r||!o)return{success:!1,message:"sessionId and message are required",error:"missing_params"}
750
- const n=await Promise.resolve().then(t.t.bind(t,9896,23)),i=(await Promise.resolve().then(t.t.bind(t,6928,23))).join(s.projectPath,".alchemy_lab","opencode.json")
751
- let a
752
- try{a="http://127.0.0.1:"+JSON.parse(n.readFileSync(i,"utf-8")).port}catch{return{success:!1,message:"Cannot read OpenCode config",error:"no_opencode"}}try{const e=await fetch(`${a}/session/${r}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:o}],...s.cachedDefaultModel?(()=>{const[e,...t]=s.cachedDefaultModel.split("/")
903
+ module.exports=require("child_process")},5419(e,exports,t){"use strict"
904
+ function r(e){return(0,o.join)((0,s.homedir)(),n.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getGlobalConfigDir=r,exports.getOpencodeSpawnEnv=function(e,t){return{OPENCODE_CLIENT:"alchemy",OPENCODE_DATA_DIR:(0,o.join)(e,n.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:r(t)}}
905
+ const s=t(857),o=t(6928),n=t(4139)},5607(e,exports,t){"use strict"
906
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.executeAction=async function(e,t,r){const s=f[e]
907
+ if(!s)return{success:!1,message:"Unknown action type: "+e,error:"No handler registered for action type: "+e}
908
+ try{return await s(t,r)}catch(t){const s=(0,p.getErrorMessage)(t)
909
+ return r.logger.error("Action handler failed",{actionType:e,error:s}),{success:!1,message:"Action failed: "+s,error:s}}}
910
+ const r=t(6636),s=t(7453),o=t(5700),n=t(848),i=t(9735),a=t(7767),c=t(9266),l=t(6555),d=t(3614),u=t(4654),h=t(7078),g=t(1405),p=t(8824),m=t(2272),f={revert_request:r.handleRevertRequestAction,"revert-request":r.handleRevertRequestAction,complete_request:s.handleCompleteRequestAction,"complete-request":s.handleCompleteRequestAction,"finalize-request":n.handleFinalizeRequestAction,"approve-request":i.handleApproveRequestAction,"approve-and-work-request":a.handleApproveAndWorkRequestAction,"restart-mcp":o.handleRestartMcpAction,"opencode-proxy":c.handleOpenCodeProxyAction,"run-tests":l.handleRunTestsAction,async"inject-prompt"(e,r){const{sessionId:s,message:o,kind:n,partPayload:i}=e
911
+ if(!s||!o)return{success:!1,message:"sessionId and message are required",error:"missing_params"}
912
+ const{getOpenCodeUrlFromConfig:a}=await Promise.resolve().then(t.t.bind(t,833,23)),c=a(r.projectPath)
913
+ if(!c)return{success:!1,message:"Cannot read OpenCode config",error:"no_opencode"}
914
+ try{const e=await fetch(`${c}/session/${s}/prompt_async`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:o,metadata:(0,m.partMeta)(n??"inserted-prompt",i)}],...r.cachedDefaultModel?(()=>{const[e,...t]=r.cachedDefaultModel.split("/")
753
915
  return{model:{providerID:e,modelID:t.join("/")}}})():{}}),signal:AbortSignal.timeout(1e4)})
754
- return e.ok?{success:!0,message:"Prompt injected into session "+r}:{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:(0,p.getErrorMessage)(e),error:"prompt_error"}}},"fork-filesystem":d.handleForkFilesystemAction,"prompt-and-wait":u.handlePromptAndWaitAction,"run-ab-prompt":u.handlePromptAndWaitAction,"spawn-auto-planner":h.handleSpawnAutoPlannerAction,"start-building":g.handleStartBuildingAction}},5671(e,exports,t){"use strict"
755
- function s(e){const[t,...s]=e.split("/")
756
- return{provider:t||"anthropic",modelId:s.join("/")||e}}function r(e){const t={anthropic:!1,openai:!1}
757
- try{const r=(0,a.join)(e,"opencode.json")
758
- if((0,i.existsSync)(r)){const e=JSON.parse((0,i.readFileSync)(r,"utf8"))
759
- if(e.provider&&((e.provider.anthropic?.options?.apiKey||process.env.ANTHROPIC_API_KEY)&&(t.anthropic=!0),(e.provider.openai?.options?.apiKey||process.env.OPENAI_API_KEY)&&(t.openai=!0)),e.model){const{provider:r}=s(e.model)
760
- t[r]=!0}}process.env.ANTHROPIC_API_KEY&&(t.anthropic=!0),process.env.OPENAI_API_KEY&&(t.openai=!0)}catch(e){t.anthropic=!0}return t}function o(e){try{const t=(0,a.join)(e,"opencode.json")
761
- if((0,i.existsSync)(t))return JSON.parse((0,i.readFileSync)(t,"utf8")).model||null}catch(e){}return null}function n(e,t,r,o){const n=s(e)
916
+ return e.ok?{success:!0,message:"Prompt injected into session "+s}:{success:!1,message:"prompt_async failed: "+e.status,error:"prompt_failed"}}catch(e){return{success:!1,message:(0,p.getErrorMessage)(e),error:"prompt_error"}}},"fork-filesystem":d.handleForkFilesystemAction,"prompt-and-wait":u.handlePromptAndWaitAction,"run-ab-prompt":u.handlePromptAndWaitAction,"spawn-auto-planner":h.handleSpawnAutoPlannerAction,"start-building":g.handleStartBuildingAction}},5671(e,exports,t){"use strict"
917
+ function r(e){const[t,...r]=e.split("/")
918
+ return{provider:t||"anthropic",modelId:r.join("/")||e}}function s(e){const t={anthropic:!1,openai:!1}
919
+ try{const s=(0,a.join)(e,"opencode.json")
920
+ if((0,i.existsSync)(s)){const e=JSON.parse((0,i.readFileSync)(s,"utf8"))
921
+ if(e.provider&&((e.provider.anthropic?.options?.apiKey||process.env.ANTHROPIC_API_KEY)&&(t.anthropic=!0),(e.provider.openai?.options?.apiKey||process.env.OPENAI_API_KEY)&&(t.openai=!0)),e.model){const{provider:s}=r(e.model)
922
+ t[s]=!0}}process.env.ANTHROPIC_API_KEY&&(t.anthropic=!0),process.env.OPENAI_API_KEY&&(t.openai=!0)}catch(e){t.anthropic=!0}return t}function o(e){try{const t=(0,a.join)(e,"opencode.json")
923
+ if((0,i.existsSync)(t))return JSON.parse((0,i.readFileSync)(t,"utf8")).model||null}catch(e){}return null}function n(e,t,s,o){const n=r(e)
762
924
  let i=n.provider
763
925
  if("inherit"!==t){if(!o[t])return{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!1,reason:`Preferred provider '${t}' not available, using main session model`}
764
- i=t}const a=function(e,t){const s=c[e]
765
- if(!s)return null
766
- const r=[...s].sort(((e,t)=>t.length-e.length))
767
- for(const e of r)if(t.includes(e))return e
926
+ i=t}const a=function(e,t){const r=c[e]
927
+ if(!r)return null
928
+ const s=[...r].sort(((e,t)=>t.length-e.length))
929
+ for(const e of s)if(t.includes(e))return e
768
930
  return null}(n.provider,n.modelId),d=c[n.provider],u=c[i]
769
931
  if(!a||!d||!u)return{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!1,reason:`Could not determine model family for '${n.modelId}', using main session model`}
770
932
  const h=d.indexOf(a)
771
933
  let g,p=0
772
- if("one_down"===r&&(p=1),"two_down"===r&&(p=2),i===n.provider)g=h+p
934
+ if("one_down"===s&&(p=1),"two_down"===s&&(p=2),i===n.provider)g=h+p
773
935
  else{const e=d.length>1?h/(d.length-1):0
774
936
  g=Math.round(e*(u.length-1))+p}g=Math.min(g,u.length-1),g=Math.max(g,0)
775
- const f=u[g],m=l[i]?.[f]
776
- return m?{provider:i,modelId:m,fullModel:`${i}/${m}`,resolved:!0}:{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!0,reason:`No known model for ${i}/${f}, using main session model`}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseModelString=s,exports.getAvailableProviders=r,exports.getMainSessionModel=o,exports.resolveModelTier=n,exports.resolveAgentModel=function(e,t="inherit",i="same",a){const c=a||o(e)
937
+ const m=u[g],f=l[i]?.[m]
938
+ return f?{provider:i,modelId:f,fullModel:`${i}/${f}`,resolved:!0}:{provider:n.provider,modelId:n.modelId,fullModel:e,resolved:!0,reason:`No known model for ${i}/${m}, using main session model`}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseModelString=r,exports.getAvailableProviders=s,exports.getMainSessionModel=o,exports.resolveModelTier=n,exports.resolveAgentModel=function(e,t="inherit",i="same",a){const c=a||o(e)
777
939
  if(!c)return{provider:"anthropic",modelId:"claude-sonnet-4-5",fullModel:"anthropic/claude-sonnet-4-5",resolved:!1,reason:"Could not determine main session model, using default"}
778
- if("inherit"===t&&"same"===i){const e=s(c)
779
- return{provider:e.provider,modelId:e.modelId,fullModel:c,resolved:!0}}return n(c,t,i,r(e))}
940
+ if("inherit"===t&&"same"===i){const e=r(c)
941
+ return{provider:e.provider,modelId:e.modelId,fullModel:c,resolved:!0}}return n(c,t,i,s(e))}
780
942
  const i=t(9896),a=t(6928),c={anthropic:["opus","sonnet","haiku"]},l={anthropic:{opus:"claude-opus-4-6",sonnet:"claude-sonnet-4-6",haiku:"claude-haiku-4-5"}}},5700(e,exports,t){"use strict"
781
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
782
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRestartMcpAction=async function(e,t){const{logger:s,projectPath:n}=t,i=e.reason||"Manual restart requested"
783
- s.info("Executing restart-mcp action",{reason:i})
943
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
944
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRestartMcpAction=async function(e,t){const{logger:r,projectPath:n}=t,i=e.reason||"Manual restart requested"
945
+ r.info("Executing restart-mcp action",{reason:i})
784
946
  try{const e=function(e){return o.default.join(e,".alchemy_lab","mcp-restart-trigger")}(n),t=o.default.dirname(e)
785
- await r.default.mkdir(t,{recursive:!0})
947
+ await s.default.mkdir(t,{recursive:!0})
786
948
  const a=JSON.stringify({timestamp:(new Date).toISOString(),reason:i},null,2)
787
- return await r.default.writeFile(e,a,"utf-8"),s.info("MCP restart triggered",{triggerPath:e,reason:i}),{success:!0,message:"MCP restart triggered: "+i,data:{triggerPath:e}}}catch(e){const t=e instanceof Error?e.message:e+""
788
- return s.error("Failed to trigger MCP restart",{error:t}),{success:!1,message:"Failed to trigger MCP restart: "+t,error:t}}}
789
- const r=s(t(1943)),o=s(t(6928))},6175(e,exports){"use strict"
949
+ return await s.default.writeFile(e,a,"utf-8"),r.info("MCP restart triggered",{triggerPath:e,reason:i}),{success:!0,message:"MCP restart triggered: "+i,data:{triggerPath:e}}}catch(e){const t=e instanceof Error?e.message:e+""
950
+ return r.error("Failed to trigger MCP restart",{error:t}),{success:!1,message:"Failed to trigger MCP restart: "+t,error:t}}}
951
+ const s=r(t(1943)),o=r(t(6928))},6175(e,exports){"use strict"
790
952
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerConditionEvaluator=void 0
791
953
  const t=new Set(["coder","reviewer","tester","journal","planner","researcher"])
792
- exports.TimerConditionEvaluator=class{constructor(e){this.logger=e}async evaluate(e,t,s){if(!e||0===e.length)return{passed:!0,variables:{}}
793
- const r=await this.fetchSessionState(t)
794
- if(!r)return{passed:!1,variables:{},reason:"Failed to fetch session state"}
795
- const o={totalSessions:r.sessions.length+"",runningSessions:r.running.length+"",idleSessions:r.idle.length+""}
954
+ exports.TimerConditionEvaluator=class{constructor(e){this.logger=e}async evaluate(e,t,r){if(!e||0===e.length)return{passed:!0,variables:{}}
955
+ const s=await this.fetchSessionState(t)
956
+ if(!s)return{passed:!1,variables:{},reason:"Failed to fetch session state"}
957
+ const o={totalSessions:s.sessions.length+"",runningSessions:s.running.length+"",idleSessions:s.idle.length+""}
796
958
  let n
797
- for(const t of e){const e=await this.evaluateSingle(t,r,s)
959
+ for(const t of e){const e=await this.evaluateSingle(t,s,r)
798
960
  if(!e.passed)return{passed:!1,variables:o,reason:e.reason}
799
- Object.assign(o,e.variables),e.targetSessionId&&!n&&(n=e.targetSessionId)}return{passed:!0,targetSessionId:n,variables:o}}async evaluateSingle(e,t,s){switch(e.type){case"no_busy_specialists":return this.evalNoBusySpecialists(t,s)
800
- case"no_busy_agents":return this.evalNoBusyAgents(t,e.params?.agentType,s)
801
- case"session_idle_for":return this.evalSessionIdleFor(t,e.params?.seconds??60,e.params?.agentType,s)
961
+ Object.assign(o,e.variables),e.targetSessionId&&!n&&(n=e.targetSessionId)}return{passed:!0,targetSessionId:n,variables:o}}async evaluateSingle(e,t,r){switch(e.type){case"no_busy_specialists":return this.evalNoBusySpecialists(t,r)
962
+ case"no_busy_agents":return this.evalNoBusyAgents(t,e.params?.agentType,r)
963
+ case"session_idle_for":return this.evalSessionIdleFor(t,e.params?.seconds??60,e.params?.agentType,r)
802
964
  case"any_session_running":return this.evalAnySessionRunning(t)
803
965
  case"all_sessions_idle":return this.evalAllSessionsIdle(t)
804
- default:return this.logger.warn("Unknown timer condition type",{type:e.type}),{passed:!0,variables:{}}}}async evalNoBusySpecialists(e,s){if(0===e.running.length)return{passed:!0,variables:{busySpecialists:"0"}}
805
- if(!s)return{passed:0===e.running.length,variables:{busySpecialists:e.running.length+""},reason:"No agent type resolver"}
806
- let r=0
807
- for(const o of e.running){const e=await s(o.id)
808
- t.has(e)&&r++}return{passed:0===r,variables:{busySpecialists:r+""},reason:r>0?r+" specialist(s) still running":void 0}}async evalNoBusyAgents(e,t,s){if(!t)return{passed:0===e.running.length,variables:{busyAgents:e.running.length+""},reason:e.running.length>0?e.running.length+" session(s) still running":void 0}
809
- if(!s)return{passed:!1,variables:{},reason:"No agent type resolver for type filtering"}
810
- let r=0
811
- for(const o of e.running)await s(o.id)===t&&r++
812
- return{passed:0===r,variables:{busyAgents:r+"",checkedAgentType:t},reason:r>0?`${r} ${t} session(s) still running`:void 0}}async evalSessionIdleFor(e,t,s,r){const o=1e3*t,n=Date.now()
966
+ default:return this.logger.warn("Unknown timer condition type",{type:e.type}),{passed:!0,variables:{}}}}async evalNoBusySpecialists(e,r){if(0===e.running.length)return{passed:!0,variables:{busySpecialists:"0"}}
967
+ if(!r)return{passed:0===e.running.length,variables:{busySpecialists:e.running.length+""},reason:"No agent type resolver"}
968
+ let s=0
969
+ for(const o of e.running){const e=await r(o.id)
970
+ t.has(e)&&s++}return{passed:0===s,variables:{busySpecialists:s+""},reason:s>0?s+" specialist(s) still running":void 0}}async evalNoBusyAgents(e,t,r){if(!t)return{passed:0===e.running.length,variables:{busyAgents:e.running.length+""},reason:e.running.length>0?e.running.length+" session(s) still running":void 0}
971
+ if(!r)return{passed:!1,variables:{},reason:"No agent type resolver for type filtering"}
972
+ let s=0
973
+ for(const o of e.running)await r(o.id)===t&&s++
974
+ return{passed:0===s,variables:{busyAgents:s+"",checkedAgentType:t},reason:s>0?`${s} ${t} session(s) still running`:void 0}}async evalSessionIdleFor(e,t,r,s){const o=1e3*t,n=Date.now()
813
975
  for(const t of e.idle){const e=n-t.time.updated
814
- if(e>=o){if(s&&r&&await r(t.id)!==s)continue
815
- return{passed:!0,targetSessionId:t.id,variables:{idleSessionId:t.id,idleDurationSeconds:Math.round(e/1e3)+"",idleSessionTitle:t.title||""}}}}return{passed:!1,variables:{},reason:s?`No ${s} session idle for ${t}s`:`No session idle for ${t}s`}}evalAnySessionRunning(e){const t=e.running.length>0
976
+ if(e>=o){if(r&&s&&await s(t.id)!==r)continue
977
+ return{passed:!0,targetSessionId:t.id,variables:{idleSessionId:t.id,idleDurationSeconds:Math.round(e/1e3)+"",idleSessionTitle:t.title||""}}}}return{passed:!1,variables:{},reason:r?`No ${r} session idle for ${t}s`:`No session idle for ${t}s`}}evalAnySessionRunning(e){const t=e.running.length>0
816
978
  return{passed:t,variables:{runningSessions:e.running.length+""},reason:t?void 0:"No sessions running"}}evalAllSessionsIdle(e){if(0===e.sessions.length)return{passed:!1,variables:{},reason:"No sessions exist"}
817
979
  const t=0===e.running.length
818
- return{passed:t,variables:{idleSessions:e.idle.length+"",runningSessions:e.running.length+""},reason:t?void 0:e.running.length+" session(s) still running"}}async fetchSessionState(e){try{const[t,s]=await Promise.all([fetch(e+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})])
819
- if(!t.ok||!s.ok)return null
820
- const r=await t.json(),o=await s.json(),n=r.filter((e=>"running"===o[e.id]?.type)),i=r.filter((e=>{const t=o[e.id]?.type
980
+ return{passed:t,variables:{idleSessions:e.idle.length+"",runningSessions:e.running.length+""},reason:t?void 0:e.running.length+" session(s) still running"}}async fetchSessionState(e){try{const[t,r]=await Promise.all([fetch(e+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(e+"/session/status",{signal:AbortSignal.timeout(5e3)})])
981
+ if(!t.ok||!r.ok)return null
982
+ const s=await t.json(),o=await r.json(),n=s.filter((e=>"running"===o[e.id]?.type)),i=s.filter((e=>{const t=o[e.id]?.type
821
983
  return"idle"===t||"completed"===t}))
822
- return{sessions:r,statusMap:o,running:n,idle:i}}catch(e){return this.logger.debug("Failed to fetch OpenCode session state",{error:e+""}),null}}}},6540(e,exports){"use strict"
984
+ return{sessions:s,statusMap:o,running:n,idle:i}}catch(e){return this.logger.debug("Failed to fetch OpenCode session state",{error:e+""}),null}}}},6540(e,exports){"use strict"
823
985
  var t
824
986
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=void 0,function(e){e.ERROR="error",e.WARN="warn",e.INFO="info",e.DEBUG="debug"}(t||(exports.LogLevel=t={}))},6555(e,exports,t){"use strict"
825
- function s(e,process,t,s,r=!1){const o=process.config||{},n=(o.setup,o.commands||[])
826
- let i=`You are a test runner agent. Your job is to verify that request ${e.localId||e.id} ("${e.title}") passes all required tests.\n\n## Test Process: ${process.name}\n${process.description?`\n${process.description}\n`:""}\n${process.scope?`**Scope:** Files under \`${process.scope}\`\n`:""}\n## Project ID: ${s}\n\n## Test Commands\nExecute these in order:\n`
827
- for(let e=0;e<n.length;e++){const t=n[e]
828
- i+=`\n### ${e+1}. ${t.name}${!1!==t.required?" (REQUIRED)":" (optional)"}\n- **Command:** \`${t.command}\`\n- **Timeout:** ${t.timeout||300}s\n`}if(i+=r?`\n## Instructions\n\nYou are in interactive mode. Present the test plan to the user and **wait for their direction** before running anything.\n\n1. Greet the user and list the test commands available for this process.\n2. Wait for the user to tell you to proceed (they may want to run specific commands, skip some, or run all).\n3. Use \`execute_test_command\` with processId="${process.id}" and the command name to run each command.\n4. After each command, explain the result and wait for the user before continuing.\n5. When the user is satisfied, call \`report_test_results\` with the overall status.\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`:`\n## Instructions\n\nSetup commands have already been executed. Use the \`execute_test_command\` tool to run each test command:\n\n1. For each command above, call \`execute_test_command\` with processId="${process.id}" and the command name.\n2. Check if it passed or failed.\n3. If it fails: try to determine why \u2014 is it a real bug, a flaky test, or an environment issue? Retry once if it looks flaky.\n4. When all commands are done, call \`report_test_results\` with the overall status and per-command results.\n - If all REQUIRED commands passed \u2192 status "passed"\n - If any REQUIRED command failed \u2192 status "failed" with diagnosis of each failure\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`,o.verifyAcceptanceCriteria&&e.acceptanceCriteria?.length>0){i+="\n\n## Acceptance Criteria Verification\n\nAfter running all commands, verify each acceptance criterion by reading code changes and running ad-hoc commands as needed. This is **advisory only** \u2014 it does not affect the overall pass/fail status.\n\nCriteria to verify:\n"
829
- for(const t of e.acceptanceCriteria)i+=`- ${t}\n`
830
- i+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **criterion**: The criterion text\n- **status**: "verified" (confirmed in code), "unverified" (not found/not implemented), or "unclear" (ambiguous)\n- **evidence**: How you verified it, or why you couldn\'t\n'}return i}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,d){const{requestId:u,testProcessIds:h,parentSessionId:g,mode:p}=e,f="interactive"===p,{logger:m,apiClient:y,projectId:v,projectPath:w,trackChildPid:I}=d
831
- if(!u)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
832
- m.info("Starting run-tests action",{requestId:u,testProcessIds:h})
833
- try{const d=await y.get(`/api/projects/${v}/requests/${u}`)
834
- if(!d.success||!d.data)return{success:!1,message:`Request ${u} not found`,error:"Request not found"}
835
- const p=d.data,k=p.branchName||"requests/"+u
987
+ function r(e,process,t,r,s=!1,o){const n=process.config||{},i=(n.setup,n.commands||[]),a=o&&o.length>0?o.map((e=>i.find((t=>t.name===e)))).filter((e=>e)):i,c=o&&o.length>0&&a.length<i.length
988
+ let l=`You are a test runner agent. Your job is to verify that request ${e.localId||e.id} ("${e.title}") passes all required tests.\n\n## Test Process: ${process.name}\n${process.description?`\n${process.description}\n`:""}\n${process.scope?`**Scope:** Files under \`${process.scope}\`\n`:""}\n## Project ID: ${r}\n\n## Test Commands${c?" (partial run \u2014 only the commands listed below)":""}\nExecute these in order:\n`
989
+ for(let e=0;e<a.length;e++){const t=a[e]
990
+ l+=`\n### ${e+1}. ${t.name}${!1!==t.required?" (REQUIRED)":" (optional)"}\n- **Command:** \`${t.command}\`\n- **Timeout:** ${t.timeout||300}s\n`}if(l+=s?`\n## Instructions\n\nYou are in interactive mode. Present the test plan to the user and **wait for their direction** before running anything.\n\n1. Greet the user and list the test commands available for this process.\n2. Wait for the user to tell you to proceed (they may want to run specific commands, skip some, or run all).\n3. Use \`execute_test_command\` with processId="${process.id}" and the command name to run each command.\n4. After each command, explain the result and wait for the user before continuing.\n5. When the user is satisfied, call \`report_test_results\` with the overall status.\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`:`\n## Instructions\n\nSetup commands have already been executed. Use the \`execute_test_command\` tool to run each test command:\n\n1. For each command above, call \`execute_test_command\` with processId="${process.id}" and the command name.\n2. Check if it passed or failed.\n3. If it fails: try to determine why \u2014 is it a real bug, a flaky test, or an environment issue? Retry once if it looks flaky.\n4. When all commands are done, call \`report_test_results\` with the overall status and per-command results.\n - If all REQUIRED commands passed \u2192 status "passed"\n - If any REQUIRED command failed \u2192 status "failed" with diagnosis of each failure\n\nYou are read-only \u2014 do NOT modify any source code. Your job is to verify, not fix.`,n.verifyAcceptanceCriteria&&e.acceptanceCriteria?.length>0){l+="\n\n## Acceptance Criteria Verification\n\nAfter running all commands, verify each acceptance criterion by reading code changes and running ad-hoc commands as needed. This is **advisory only** \u2014 it does not affect the overall pass/fail status.\n\nCriteria to verify:\n"
991
+ for(const t of e.acceptanceCriteria)l+=`- ${t}\n`
992
+ l+='\nFor each criterion, report in the `acceptanceCriteria` array of your results:\n- **criterion**: The criterion text\n- **status**: "verified" (confirmed in code), "unverified" (not found/not implemented), or "unclear" (ambiguous)\n- **evidence**: How you verified it, or why you couldn\'t\n'}return l}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRunTestsAction=async function(e,l){const{requestId:d,testProcessIds:u,parentSessionId:h,mode:g}=e,p="interactive"===g,{logger:m,apiClient:f,projectId:y,projectPath:v,trackChildPid:w}=l
993
+ if(!d)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
994
+ m.info("Starting run-tests action",{requestId:d,testProcessIds:u})
995
+ try{const l=await f.get(`/api/projects/${y}/requests/${d}`)
996
+ if(!l.success||!l.data)return{success:!1,message:`Request ${d} not found`,error:"Request not found"}
997
+ const g=l.data,b=g.branchName||"requests/"+d
998
+ let I
999
+ if(u?.length)I=(await Promise.all(u.map((e=>f.get(`/api/projects/${y}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
1000
+ else{const e=await f.get(`/api/projects/${y}/test-processes/active`)
1001
+ I=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===I.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
1002
+ const E=Math.max(1,Math.min(8,I[0]?.config?.poolSize??2)),C=I[0]?.config?.fingerprint||[]
836
1003
  let S
837
- if(h?.length)S=(await Promise.all(h.map((e=>y.get(`/api/projects/${v}/test-processes/${e}`))))).filter((e=>e.success&&e.data)).map((e=>e.data))
838
- else{const e=await y.get(`/api/projects/${v}/test-processes/active`)
839
- S=e.success&&e.data?Array.isArray(e.data)?e.data:[e.data]:[]}if(0===S.length)return{success:!1,message:"No test processes configured for this project. Set up testing first.",error:"No test processes"}
840
- const C=(0,o.join)(w,c,l),j=(0,o.join)(C,u),b=(0,n.simpleGit)(w)
841
- if((0,r.existsSync)(j))try{const e=(0,n.simpleGit)(j)
842
- await e.raw(["reset","--hard",k]),m.info("Reset test worktree to latest branch HEAD",{worktreePath:j,branchName:k})}catch(e){m.warn("Could not reset test worktree, recreating",{error:(0,i.getErrorMessage)(e)})
843
- try{await b.raw(["worktree","remove","--force",j])}catch{}}if(!(0,r.existsSync)(j)){(0,r.mkdirSync)(C,{recursive:!0})
844
- try{if(!(await b.branch()).all.includes(k))return{success:!1,message:`Branch ${k} not found`,error:"Branch not found"}
845
- await b.raw(["worktree","add","--detach",j,k]),m.info("Created test worktree (detached)",{worktreePath:j,branchName:k})}catch(e){if(!(e+"").includes("already exists"))return{success:!1,message:"Failed to create test worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
846
- m.info("Test worktree already exists, using as-is",{worktreePath:j})}}const{execSync:E}=await Promise.resolve().then(t.t.bind(t,5317,23))
847
- for(const e of S){const t=e.config?.setup||[]
848
- if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length})
849
- for(const s of t)try{m.info("Setup command",{command:s,worktreePath:j}),E(s,{cwd:j,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:s,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}const T=[],A=[]
850
- for(const process of S){let t
851
- const r=e.testRunIds?.[T.length]
852
- try{r?(await y.patch(`/api/projects/${v}/test-runs/${r}`,{worktreePath:j}),t=(await y.get(`/api/projects/${v}/test-runs/${r}`)).data,T.push(t.id)):(t=(await y.post(`/api/projects/${v}/test-runs`,{requestId:u,testProcessId:process.id,worktreePath:j,parentSessionId:g})).data,T.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:u,processId:process.id,preCreatedId:r,error:(0,i.getErrorMessage)(e)})
853
- continue}const o=s(p,process,0,v,f),n=f?"tester":"auto-tester"
854
- try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:j,projectPath:w,projectId:v,title:`[${u}] Test: ${process.name}`,requestId:u,parentSessionId:g,allowParallel:!0,apiClient:y,logger:m,trackChildPid:I})
1004
+ try{S=await(0,c.acquireSlot)(v,E)}catch(e){return{success:!1,message:"Could not acquire test worktree slot: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}}const k=S.path,T=(0,n.simpleGit)(v)
1005
+ try{if((0,s.existsSync)(k))try{const e=(0,n.simpleGit)(k)
1006
+ await e.raw(["reset","--hard",b]),m.info("Reset pool slot to latest branch HEAD",{worktreePath:k,branchName:b,slotIndex:S.index})}catch(e){m.warn("Could not reset slot, recreating",{error:(0,i.getErrorMessage)(e)})
1007
+ try{await T.raw(["worktree","remove","--force",k])}catch{}}if(!(0,s.existsSync)(k)){(0,s.mkdirSync)((0,o.join)(v,".alchemy_lab","test-worktrees"),{recursive:!0})
1008
+ try{if(!(await T.branch()).all.includes(b))return(0,c.releaseSlot)(k),{success:!1,message:`Branch ${b} not found`,error:"Branch not found"}
1009
+ await T.raw(["worktree","add","--detach",k,b]),m.info("Created pool slot worktree (detached)",{worktreePath:k,branchName:b,slotIndex:S.index})}catch(e){if(!(e+"").includes("already exists"))return(0,c.releaseSlot)(k),{success:!1,message:"Failed to create pool slot worktree: "+(0,i.getErrorMessage)(e),error:(0,i.getErrorMessage)(e)}
1010
+ m.info("Pool slot worktree already exists, using as-is",{worktreePath:k})}}}catch(e){throw(0,c.releaseSlot)(k),e}const _=await(0,c.computeFingerprint)(k,C),j=0===C.length||_!==S.storedFingerprint,A=0===C.length?null:_===S.storedFingerprint
1011
+ j||m.info("Pool slot fingerprint matches; skipping setup",{worktreePath:k,slotIndex:S.index,fingerprintFiles:C})
1012
+ const{execSync:P}=await Promise.resolve().then(t.t.bind(t,5317,23))
1013
+ if(j){for(const e of I){const t=e.config?.setup||[]
1014
+ if(0!==t.length){m.info("Running setup commands for test process",{processName:e.name,commandCount:t.length,slotIndex:S.index})
1015
+ for(const r of t)try{m.info("Setup command",{command:r,worktreePath:k}),P(r,{cwd:k,timeout:12e4,encoding:"utf-8",stdio:["pipe","pipe","pipe"],shell:"/bin/sh",env:{...process.env,FORCE_COLOR:"0"}})}catch(t){m.warn("Setup command failed",{command:r,processName:e.name,exitCode:t.status,stderr:(t.stderr||"").slice(-500)})}}}C.length>0&&await(0,c.writeSlotFingerprint)(k,_),await(0,c.writeWarmSnapshot)(k)}else{const{removed:e}=await(0,c.restoreToWarmSnapshot)(k)
1016
+ e>0&&m.info("Pruned drift from warm slot",{worktreePath:k,slotIndex:S.index,removed:e})}let M
1017
+ try{const e=(0,n.simpleGit)(k)
1018
+ M=(await e.revparse(["HEAD"])).trim()}catch(e){m.warn("Failed to capture worktree HEAD; commit_sha will be NULL on this run",{worktreePath:k,error:(0,i.getErrorMessage)(e)})}const O=[],R=[],D=[]
1019
+ let x
1020
+ const $={worktreePath:k,commitSha:M,fingerprintHit:A}
1021
+ for(const process of I){let t
1022
+ const s=e.testRunIds?.[O.length]
1023
+ try{s?(await f.patch(`/api/projects/${y}/test-runs/${s}`,$),t=(await f.get(`/api/projects/${y}/test-runs/${s}`)).data,O.push(t.id)):(t=(await f.post(`/api/projects/${y}/test-runs`,{requestId:d,testProcessId:process.id,parentSessionId:h,...$})).data,O.push(t.id))}catch(e){m.error("Failed to get/create test run record",{requestId:d,processId:process.id,preCreatedId:s,error:(0,i.getErrorMessage)(e)})
1024
+ continue}const o=r(g,process,0,y,p,e.commandNames),n=p?"tester":"auto-tester"
1025
+ try{const e=await(0,a.spawnActionAgent)({agent:n,prompt:o,cwd:k,projectPath:v,projectId:y,title:`[${d}] Test: ${process.name}`,requestId:d,parentSessionId:h,allowParallel:!0,apiClient:f,logger:m,trackChildPid:w})
855
1026
  if(!e.success)throw Error(e.error||"Spawn failed")
856
- A.push(t.id),e.sessionId&&y.patch(`/api/projects/${v}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:j,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
857
- try{await y.patch(`/api/projects/${v}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}return 0===A.length?{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}:{success:!0,message:`Started ${A.length} test run(s) for request ${u}`,data:{testRunIds:T,agentsSpawned:A.length,processCount:S.length,worktreePath:j}}}catch(e){const t=(0,i.getErrorMessage)(e)
858
- return m.error("run-tests action failed",{requestId:u,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
859
- const r=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(8148),c=".alchemy_lab",l="test-worktrees"},6563(e,exports,t){"use strict"
1027
+ R.push(t.id),e.sessionId&&D.push(e.sessionId),e.pid&&(x=e.pid),e.sessionId&&(f.patch(`/api/projects/${y}/test-runs/${t.id}`,{testerSessionId:e.sessionId}).catch((e=>m.warn("Failed to set testerSessionId on test run",{error:(0,i.getErrorMessage)(e)}))),f.post(`/api/projects/${y}/session-test-process-associations`,{opencodeSessionId:e.sessionId,testProcessId:process.id}).catch((t=>m.warn("Failed to create session\u2194test-process association",{sessionId:e.sessionId,testProcessId:process.id,error:(0,i.getErrorMessage)(t)})))),m.info("Spawned test-runner agent",{testRunId:t.id,processName:process.name,worktreePath:k,pid:e.pid,sessionId:e.sessionId})}catch(e){m.error("Failed to spawn test-runner agent",{testRunId:t.id,error:(0,i.getErrorMessage)(e)})
1028
+ try{await f.patch(`/api/projects/${y}/test-runs/${t.id}`,{status:"error",results:{steps:[],summary:"Failed to spawn test agent: "+(0,i.getErrorMessage)(e)}})}catch{}}}if(0===R.length)return(0,c.releaseSlot)(k),{success:!1,message:"Failed to spawn any test agents",error:"All agent spawns failed"}
1029
+ for(const e of D);return(0,c.watchPidAndRelease)(x,k,12e5),{success:!0,message:`Started ${R.length} test run(s) for request ${d}`,data:{testRunIds:O,agentsSpawned:R.length,processCount:I.length,worktreePath:k,spawnedSessionIds:D}}}catch(e){const t=(0,i.getErrorMessage)(e)
1030
+ return m.error("run-tests action failed",{requestId:d,error:t}),{success:!1,message:"Failed to run tests: "+t,error:t}}}
1031
+ const s=t(9896),o=t(6928),n=t(3807),i=t(8824),a=t(8148),c=t(489)},6563(e,exports,t){"use strict"
860
1032
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.TimerHookScheduler=void 0
861
- const s=t(6175),r=t(4627),o=t(4769)
862
- class n{constructor(e){this.tracking=new Map,this.inFlight=new Set,this.paused=new Set,this.stopped=new Set,this.cachedHooks=[],this.lastHookFetch=0,this.lastActivityTime=0,this.agentTypeCache=new Map,this.logger=e.logger,this.hookExecutor=e.hookExecutor,this.apiClient=e.apiClient,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath,this.conditionEvaluator=new s.TimerConditionEvaluator(e.logger)}start(){this.tickTimer||(this.logger.info("Timer hook scheduler starting",{tickInterval:1e4}),setTimeout((()=>this.tick().catch((()=>{}))),3e3),this.tickTimer=setInterval((()=>{this.tick().catch((()=>{}))}),1e4))}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.tracking.clear(),this.inFlight.clear(),this.paused.clear(),this.stopped.clear(),this.cachedHooks=[],this.logger.info("Timer hook scheduler stopped")}pauseHook(e){return!this.stopped.has(e)&&(this.paused.add(e),this.logger.info("Timer hook paused",{hookId:e}),!0)}resumeHook(e){const t=this.paused.delete(e)
863
- return t&&this.logger.info("Timer hook resumed",{hookId:e}),t}stopHook(e){return this.stopped.add(e),this.paused.delete(e),this.inFlight.delete(e),this.tracking.delete(e),this.logger.info("Timer hook stopped",{hookId:e}),!0}setProjectId(e){this.projectId=e}getStatus(){const e=Date.now(),t=this.cachedHooks.map((t=>{const s=this.tracking.get(t.id),r=t.stopAfterInactiveMinutes??10,o=this.paused.has(t.id),n=this.stopped.has(t.id),i=!o&&!n&&this.lastActivityTime>0&&r>0&&e-this.lastActivityTime>6e4*r,a=!(!t.maxIterations||!s||s.fireCount<t.maxIterations),c=!(!(t.timerExpiresAfter&&s&&s.firstFired>0)||(e-s.firstFired)/1e3<t.timerExpiresAfter)
864
- return{id:t.id,name:t.name,agent:t.agent,action:t.action||"spawn_agent",intervalSeconds:t.intervalSeconds,maxIterations:t.maxIterations,timerExpiresAfter:t.timerExpiresAfter,stopAfterInactiveMinutes:t.stopAfterInactiveMinutes,fireCount:s?.fireCount||0,lastFired:s?.lastFired||0,firstFired:s?.firstFired||0,status:n?"stopped":o?"paused_manual":a?"limit_reached":c?"expired":i?"paused":this.inFlight.has(t.id)?"running":"waiting"}}))
1033
+ const r=t(6175),s=t(4627),o=t(4769)
1034
+ class n{constructor(e){this.tracking=new Map,this.inFlight=new Set,this.paused=new Set,this.stopped=new Set,this.cachedHooks=[],this.lastHookFetch=0,this.lastActivityTime=0,this.agentTypeCache=new Map,this.logger=e.logger,this.hookExecutor=e.hookExecutor,this.apiClient=e.apiClient,this.getOpenCodeUrl=e.getOpenCodeUrl,this.getDefaultModel=e.getDefaultModel,this.projectId=e.projectId,this.projectPath=e.projectPath,this.conditionEvaluator=new r.TimerConditionEvaluator(e.logger)}start(){this.tickTimer||(this.logger.info("Timer hook scheduler starting",{tickInterval:1e4}),setTimeout((()=>this.tick().catch((()=>{}))),3e3),this.tickTimer=setInterval((()=>{this.tick().catch((()=>{}))}),1e4))}stop(){this.tickTimer&&(clearInterval(this.tickTimer),this.tickTimer=void 0),this.tracking.clear(),this.inFlight.clear(),this.paused.clear(),this.stopped.clear(),this.cachedHooks=[],this.logger.info("Timer hook scheduler stopped")}pauseHook(e){return!this.stopped.has(e)&&(this.paused.add(e),this.logger.info("Timer hook paused",{hookId:e}),!0)}resumeHook(e){const t=this.paused.delete(e)
1035
+ return t&&this.logger.info("Timer hook resumed",{hookId:e}),t}stopHook(e){return this.stopped.add(e),this.paused.delete(e),this.inFlight.delete(e),this.tracking.delete(e),this.logger.info("Timer hook stopped",{hookId:e}),!0}setProjectId(e){this.projectId=e}getStatus(){const e=Date.now(),t=this.cachedHooks.map((t=>{const r=this.tracking.get(t.id),s=t.stopAfterInactiveMinutes??10,o=this.paused.has(t.id),n=this.stopped.has(t.id),i=!o&&!n&&this.lastActivityTime>0&&s>0&&e-this.lastActivityTime>6e4*s,a=!(!t.maxIterations||!r||r.fireCount<t.maxIterations),c=!(!(t.timerExpiresAfter&&r&&r.firstFired>0)||(e-r.firstFired)/1e3<t.timerExpiresAfter)
1036
+ return{id:t.id,name:t.name,agent:t.agent,action:t.action||"spawn_agent",intervalSeconds:t.intervalSeconds,maxIterations:t.maxIterations,timerExpiresAfter:t.timerExpiresAfter,stopAfterInactiveMinutes:t.stopAfterInactiveMinutes,fireCount:r?.fireCount||0,lastFired:r?.lastFired||0,firstFired:r?.firstFired||0,status:n?"stopped":o?"paused_manual":a?"limit_reached":c?"expired":i?"paused":this.inFlight.has(t.id)?"running":"waiting"}}))
865
1037
  return{running:!!this.tickTimer,trackedHooks:this.cachedHooks.length,inFlight:Array.from(this.inFlight),lastActivityTime:this.lastActivityTime,hooks:t}}async tick(){if(!this.projectId)return
866
1038
  const e=this.getOpenCodeUrl()
867
1039
  if(e)try{const t=await this.fetchTimerHooks()
868
1040
  if(0===t.length)return
869
- const s=Date.now()
1041
+ const r=Date.now()
870
1042
  await this.updateLastActivityTime(e)
871
- for(const r of t)try{await this.evaluateAndDispatch(r,s,e)}catch(e){this.logger.warn("Error evaluating timer hook",{hookId:r.id,name:r.name,error:e+""})}const r=new Set(t.map((e=>e.id)))
872
- for(const e of this.tracking.keys())r.has(e)||this.tracking.delete(e)}catch(e){this.logger.debug("Timer hook tick failed",{error:e+""})}}async evaluateAndDispatch(e,t,s){const r=1e3*(e.intervalSeconds||60),o=this.tracking.get(e.id)
873
- if(o&&t-o.lastFired<r)return
1043
+ for(const s of t)try{await this.evaluateAndDispatch(s,r,e)}catch(e){this.logger.warn("Error evaluating timer hook",{hookId:s.id,name:s.name,error:e+""})}const s=new Set(t.map((e=>e.id)))
1044
+ for(const e of this.tracking.keys())s.has(e)||this.tracking.delete(e)}catch(e){this.logger.debug("Timer hook tick failed",{error:e+""})}}async evaluateAndDispatch(e,t,r){const s=1e3*(e.intervalSeconds||60),o=this.tracking.get(e.id)
1045
+ if(o&&t-o.lastFired<s)return
874
1046
  if(e.maxIterations&&o&&o.fireCount>=e.maxIterations)return
875
1047
  if(e.timerExpiresAfter&&o&&o.firstFired>0&&(t-o.firstFired)/1e3>=e.timerExpiresAfter)return
876
1048
  if(this.paused.has(e.id)||this.stopped.has(e.id))return
877
1049
  const n=e.stopAfterInactiveMinutes??10
878
1050
  if(this.lastActivityTime>0&&n>0&&t-this.lastActivityTime>6e4*n)return
879
1051
  if(this.inFlight.has(e.id))return
880
- const i=this.parseConditions(e.timerCondition),a=await this.conditionEvaluator.evaluate(i,s,(e=>this.resolveAgentType(e)))
1052
+ const i=this.parseConditions(e.timerCondition),a=await this.conditionEvaluator.evaluate(i,r,(e=>this.resolveAgentType(e)))
881
1053
  if(!a.passed)return void this.logger.debug("Timer condition not met",{hook:e.name,reason:a.reason})
882
1054
  let c=a.targetSessionId||""
883
- if("inject_prompt"===e.action&&!c&&(c=await this.findSessionByAgentType(e.agent,s)||"",!c))return void this.logger.debug("Timer hook skipped: no target session found",{hook:e.name,agent:e.agent})
1055
+ if("inject_prompt"===e.action&&!c&&(c=await this.findSessionByAgentType(e.agent,r)||"",!c))return void this.logger.debug("Timer hook skipped: no target session found",{hook:e.name,agent:e.agent})
884
1056
  this.logger.info("Firing timer hook",{hook:e.name,hookId:e.id,interval:e.intervalSeconds,fireCount:(o?.fireCount||0)+1,targetSessionId:c||"(spawn)"})
885
1057
  const l={lastFired:t,firstFired:o?.firstFired||t,fireCount:(o?.fireCount||0)+1}
886
- this.tracking.set(e.id,l),this.inFlight.add(e.id),this.dispatchHook(e,c,a.variables).finally((()=>{this.inFlight.delete(e.id)}))}async dispatchHook(e,t,s){try{const n=this.tracking.get(e.id),i={...s,hookName:e.name,hookId:e.id,intervalSeconds:(e.intervalSeconds||0)+"",fireCount:(n?.fireCount||0)+"",sessionId:t||""},a=(0,r.interpolatePrompt)(e.promptTemplate,i),c={logger:this.logger,projectId:this.projectId,projectPath:this.projectPath,apiClient:this.apiClient,cachedDefaultModel:this.getDefaultModel()}
1058
+ this.tracking.set(e.id,l),this.inFlight.add(e.id),this.dispatchHook(e,c,a.variables).finally((()=>{this.inFlight.delete(e.id)}))}async dispatchHook(e,t,r){try{const n=this.tracking.get(e.id),i={...r,hookName:e.name,hookId:e.id,intervalSeconds:(e.intervalSeconds||0)+"",fireCount:(n?.fireCount||0)+"",sessionId:t||""},a=(0,s.interpolatePrompt)(e.promptTemplate,i),c={logger:this.logger,projectId:this.projectId,projectPath:this.projectPath,apiClient:this.apiClient,cachedDefaultModel:this.getDefaultModel()}
887
1059
  "inject_prompt"===e.action&&t?await(0,o.executeSameSession)(a,t,c):await(0,o.executeBackground)(e,a,t,void 0,c),this.apiClient.post(`/api/projects/${this.projectId}/hook-triggers`,{hookId:e.id,hookName:e.name,eventType:"timer",action:e.action,target:e.target,agent:e.agent}).catch((()=>{}))}catch(t){this.logger.warn("Timer hook dispatch failed",{hook:e.name,error:t+""})}}async updateLastActivityTime(e){try{const t=await fetch(e+"/session",{signal:AbortSignal.timeout(5e3)})
888
1060
  if(!t.ok)return
889
- const s=await t.json()
890
- let r=0
891
- for(const e of s)e.time.updated>r&&(r=e.time.updated)
892
- r>0&&(this.lastActivityTime=r)}catch{}}async fetchTimerHooks(){const e=Date.now()
893
- if(3e4>e-this.lastHookFetch&&this.cachedHooks.length>0)return this.cachedHooks
894
- try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/timer`),s=t?.data||t||[]
895
- this.cachedHooks=s.map((e=>({id:e.id||e.hookId,name:e.name||e.hookName,eventType:"timer",agent:e.agent||e.hookAgent,promptTemplate:e.promptTemplate||e.hookPromptTemplate,condition:e.condition||e.hookCondition||{},enabled:!1!==e.enabled,action:e.action||e.hookAction||"spawn_agent",target:e.target||e.hookTarget||"independent_session",timing:"after",maxIterations:e.maxIterations||e.hookMaxIterations||void 0,intervalSeconds:e.intervalSeconds||e.hookIntervalSeconds||void 0,timerCondition:e.timerCondition||e.hookTimerCondition||void 0,timerExpiresAfter:e.timerExpiresAfter||e.hookTimerExpiresAfter||void 0,stopAfterInactiveMinutes:e.stopAfterInactiveMinutes??e.hookStopAfterInactiveMinutes??10}))),this.lastHookFetch=e,this.cachedHooks.length>0&&this.logger.debug("Fetched timer hooks",{count:this.cachedHooks.length,hooks:this.cachedHooks.map((e=>e.name))})}catch(e){this.logger.debug("Failed to fetch timer hooks",{error:e+""})}return this.cachedHooks}parseConditions(e){return e&&Array.isArray(e)?e.filter((e=>e&&e.type)):[]}async resolveAgentType(e){const t=this.agentTypeCache.get(e)
1061
+ const r=await t.json()
1062
+ let s=0
1063
+ for(const e of r)e.time.updated>s&&(s=e.time.updated)
1064
+ s>0&&(this.lastActivityTime=s)}catch{}}invalidateCache(){this.lastHookFetch=0}async fetchTimerHooks(){const e=Date.now()
1065
+ if(18e5>e-this.lastHookFetch)return this.cachedHooks
1066
+ try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/timer`),r=t?.data||t||[]
1067
+ this.cachedHooks=r.map((e=>({id:e.id||e.hookId,name:e.name||e.hookName,eventType:"timer",agent:e.agent||e.hookAgent,promptTemplate:e.promptTemplate||e.hookPromptTemplate,condition:e.condition||e.hookCondition||{},enabled:!1!==e.enabled,action:e.action||e.hookAction||"spawn_agent",target:e.target||e.hookTarget||"independent_session",timing:"after",maxIterations:e.maxIterations||e.hookMaxIterations||void 0,intervalSeconds:e.intervalSeconds||e.hookIntervalSeconds||void 0,timerCondition:e.timerCondition||e.hookTimerCondition||void 0,timerExpiresAfter:e.timerExpiresAfter||e.hookTimerExpiresAfter||void 0,stopAfterInactiveMinutes:e.stopAfterInactiveMinutes??e.hookStopAfterInactiveMinutes??10}))),this.lastHookFetch=e,this.cachedHooks.length>0&&this.logger.debug("Fetched timer hooks",{count:this.cachedHooks.length,hooks:this.cachedHooks.map((e=>e.name))})}catch(t){this.logger.debug("Failed to fetch timer hooks",{error:t+""}),this.lastHookFetch=e}return this.cachedHooks}parseConditions(e){return e&&Array.isArray(e)?e.filter((e=>e&&e.type)):[]}async resolveAgentType(e){const t=this.agentTypeCache.get(e)
896
1068
  if(t&&Date.now()-t.time<n.AGENT_TYPE_CACHE_TTL)return t.type
897
- try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),s=t?.data||t,r=s?.agentType||""
898
- return this.agentTypeCache.set(e,{type:r,time:Date.now()}),r}catch{return""}}async findSessionByAgentType(e,t){try{const[s,r]=await Promise.all([fetch(t+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(t+"/session/status",{signal:AbortSignal.timeout(5e3)})])
899
- if(!s.ok)return this.logger.warn("findSessionByAgentType: failed to fetch sessions",{status:s.status}),null
900
- const o=await s.json(),n=r.ok?await r.json():{}
1069
+ try{const t=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),r=t?.data||t,s=r?.agentType||""
1070
+ if(this.agentTypeCache.size>=n.AGENT_TYPE_CACHE_MAX){const e=this.agentTypeCache.keys().next().value
1071
+ void 0!==e&&this.agentTypeCache.delete(e)}return this.agentTypeCache.set(e,{type:s,time:Date.now()}),s}catch{return""}}async findSessionByAgentType(e,t){try{const[r,s]=await Promise.all([fetch(t+"/session",{signal:AbortSignal.timeout(5e3)}),fetch(t+"/session/status",{signal:AbortSignal.timeout(5e3)})])
1072
+ if(!r.ok)return this.logger.warn("findSessionByAgentType: failed to fetch sessions",{status:r.status}),null
1073
+ const o=await r.json(),n=s.ok?await s.json():{}
901
1074
  if(0===o.length)return this.logger.debug("findSessionByAgentType: no sessions found"),null
902
- const i=o.sort(((e,t)=>{const s="running"===n[e.id]?.type||"idle"===n[e.id]?.type?1:0,r="running"===n[t.id]?.type||"idle"===n[t.id]?.type?1:0
903
- return s!==r?r-s:t.time.updated-e.time.updated})),a=e=>!e||"default"===e||"alchemist"===e||e.includes("alchemist"),c="default"===e||"alchemist"===e,l=Math.min(i.length,15)
904
- for(let t=0;l>t;t++){const s=i[t],r=await this.resolveAgentType(s.id)
905
- if(r===e)return this.logger.debug("findSessionByAgentType: exact match",{sessionId:s.id,agentType:r}),s.id
906
- if(c&&a(r))return this.logger.debug("findSessionByAgentType: orchestrator match",{sessionId:s.id,agentType:r,wanted:e}),s.id}const d=e=>!(!e.parentID&&!e.parentId),u=i.filter((e=>!d(e)))
907
- return u.length>0?(this.logger.debug("findSessionByAgentType: fallback to root session",{sessionId:u[0].id,wanted:e}),u[0].id):(this.logger.debug("findSessionByAgentType: fallback to most recent session",{sessionId:i[0].id,wanted:e}),i[0].id)}catch(t){return this.logger.warn("findSessionByAgentType failed",{agentType:e,error:t+""}),null}}}exports.TimerHookScheduler=n,n.AGENT_TYPE_CACHE_TTL=6e4},6636(e,exports,t){"use strict"
1075
+ const i=o.sort(((e,t)=>{const r="running"===n[e.id]?.type||"idle"===n[e.id]?.type?1:0,s="running"===n[t.id]?.type||"idle"===n[t.id]?.type?1:0
1076
+ return r!==s?s-r:t.time.updated-e.time.updated})),a=e=>!e||"default"===e||"alchemist"===e||e.includes("alchemist"),c="default"===e||"alchemist"===e,l=Math.min(i.length,15)
1077
+ for(let t=0;l>t;t++){const r=i[t],s=await this.resolveAgentType(r.id)
1078
+ if(s===e)return this.logger.debug("findSessionByAgentType: exact match",{sessionId:r.id,agentType:s}),r.id
1079
+ if(c&&a(s))return this.logger.debug("findSessionByAgentType: orchestrator match",{sessionId:r.id,agentType:s,wanted:e}),r.id}const d=e=>!(!e.parentID&&!e.parentId),u=i.filter((e=>!d(e)))
1080
+ return u.length>0?(this.logger.debug("findSessionByAgentType: fallback to root session",{sessionId:u[0].id,wanted:e}),u[0].id):(this.logger.debug("findSessionByAgentType: fallback to most recent session",{sessionId:i[0].id,wanted:e}),i[0].id)}catch(t){return this.logger.warn("findSessionByAgentType failed",{agentType:e,error:t+""}),null}}}exports.TimerHookScheduler=n,n.AGENT_TYPE_CACHE_TTL=6e4,n.AGENT_TYPE_CACHE_MAX=500},6636(e,exports,t){"use strict"
908
1081
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRevertRequestAction=async function(e,t){const{requestId:o,reason:n}=e,{logger:i,apiClient:a,projectPath:c,projectId:l}=t
909
1082
  if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
910
1083
  if(!n)return{success:!1,message:"reason is required",error:"Missing reason in payload"}
@@ -913,29 +1086,29 @@ try{const e=await a.post(`/api/projects/${l}/requests/${o}/validate-revert`,{})
913
1086
  if(!e.success||!e.data)return{success:!1,message:"Server validation failed: "+(e.data?.reason||"Unknown error"),error:"Validation failed"}
914
1087
  const t=e.data
915
1088
  if(!t.canRevert){let e="Cannot revert request: "+t.reason
916
- return t.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be reverted first:\n",t.lifoViolation.blockingRequests.forEach(((t,s)=>{e+=`${s+1}. ${t.requestId}: ${t.title}\n`}))),{success:!1,message:e,error:t.reason}}if("cancel"===t.operationType){const e=await a.post(`/api/projects/${l}/requests/${o}/finalize-revert`,{reason:n,operationType:"cancel"})
1089
+ return t.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be reverted first:\n",t.lifoViolation.blockingRequests.forEach(((t,r)=>{e+=`${r+1}. ${t.requestId}: ${t.title}\n`}))),{success:!1,message:e,error:t.reason}}if("cancel"===t.operationType){const e=await a.post(`/api/projects/${l}/requests/${o}/finalize-revert`,{reason:n,operationType:"cancel"})
917
1090
  return e.success?{success:!0,message:"Successfully canceled request "+o,data:{operationType:"cancel",reason:n}}:{success:!1,message:"Failed to finalize cancel operation",error:e.error?.message||"Finalize failed"}}let d=t.mergeCommitHash
918
1091
  if(!d){i.info("No merge commit hash from server, searching git log",{requestId:o})
919
- const e=await async function(e,t){try{const r=(0,s.simpleGit)(t),o=(await r.raw(["log","--merges","--format=%H %s","HEAD"])).trim().split("\n").filter((e=>e.trim())),n=[`Merge requests/${e}-`,`Merge requests/${e} `,"Merge branch 'requests/"+e,"requests/"+e]
920
- for(const e of o){const[t,...s]=e.split(" "),r=s.join(" ")
921
- for(const e of n)if(r.includes(e))return{found:!0,commitHash:t}}return{found:!1,message:"No merge commit found for request "+e}}catch(e){return{found:!1,message:"Error searching for merge commit: "+(0,r.getErrorMessage)(e)}}}(o,c)
1092
+ const e=await async function(e,t){try{const s=(0,r.simpleGit)(t),o=(await s.raw(["log","--merges","--format=%H %s","HEAD"])).trim().split("\n").filter((e=>e.trim())),n=[`Merge requests/${e}-`,`Merge requests/${e} `,"Merge branch 'requests/"+e,"requests/"+e]
1093
+ for(const e of o){const[t,...r]=e.split(" "),s=r.join(" ")
1094
+ for(const e of n)if(s.includes(e))return{found:!0,commitHash:t}}return{found:!1,message:"No merge commit found for request "+e}}catch(e){return{found:!1,message:"Error searching for merge commit: "+(0,s.getErrorMessage)(e)}}}(o,c)
922
1095
  if(!e.found||!e.commitHash)return{success:!1,message:"Could not find merge commit for request "+o,error:"Merge commit not found"}
923
- d=e.commitHash}const u=(0,s.simpleGit)(c),h=await u.status(),g=(await u.branch()).all.includes("main")?"main":"master"
1096
+ d=e.commitHash}const u=(0,r.simpleGit)(c),h=await u.status(),g=(await u.branch()).all.includes("main")?"main":"master"
924
1097
  let p
925
1098
  h.current!==g&&(i.info("Switching to default branch",{from:h.current,to:g}),await u.checkout(g))
926
1099
  try{i.info("Executing git revert",{mergeCommitHash:d.substring(0,8)}),await u.raw(["revert","-m","1","--no-edit",d])
927
1100
  const e=await u.log({maxCount:1})
928
- p=e.latest?.hash||"",i.info("Git revert completed",{originalCommit:d.substring(0,8),revertCommit:p.substring(0,8)})}catch(e){const t=(0,r.getErrorMessage)(e)
1101
+ p=e.latest?.hash||"",i.info("Git revert completed",{originalCommit:d.substring(0,8),revertCommit:p.substring(0,8)})}catch(e){const t=(0,s.getErrorMessage)(e)
929
1102
  i.error("Git revert failed",{requestId:o,error:t})
930
- try{await u.raw(["revert","--abort"])}catch(e){i.error("CRITICAL: git revert --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Git revert failed: "+t,error:t}}const f=await a.post(`/api/projects/${l}/requests/${o}/finalize-revert`,{reason:n,operationType:"revert",revertCommitHash:p,originalMergeCommitHash:d})
931
- return f.success?{success:!0,message:"Successfully reverted request "+o,data:{operationType:"revert",originalMergeCommit:d,revertCommitHash:p,reason:n}}:(i.error("CRITICAL: Git revert succeeded but finalization failed",{requestId:o,revertCommitHash:p,error:f.error}),{success:!1,message:`CRITICAL: Git revert succeeded (commit: ${p.substring(0,8)}) but server finalization failed`,error:"Finalization failed"})}catch(e){const t=(0,r.getErrorMessage)(e)
1103
+ try{await u.raw(["revert","--abort"])}catch(e){i.error("CRITICAL: git revert --abort failed \u2014 repo may be in inconsistent state",{requestId:o,error:e instanceof Error?e.message:e+""})}return{success:!1,message:"Git revert failed: "+t,error:t}}const m=await a.post(`/api/projects/${l}/requests/${o}/finalize-revert`,{reason:n,operationType:"revert",revertCommitHash:p,originalMergeCommitHash:d})
1104
+ return m.success?{success:!0,message:"Successfully reverted request "+o,data:{operationType:"revert",originalMergeCommit:d,revertCommitHash:p,reason:n}}:(i.error("CRITICAL: Git revert succeeded but finalization failed",{requestId:o,revertCommitHash:p,error:m.error}),{success:!1,message:`CRITICAL: Git revert succeeded (commit: ${p.substring(0,8)}) but server finalization failed`,error:"Finalization failed"})}catch(e){const t=(0,s.getErrorMessage)(e)
932
1105
  return i.error("Revert_request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to revert request: "+t,error:t}}}
933
- const s=t(3807),r=t(8824)},6800(e,exports,t){"use strict"
934
- function s(e){return new Promise((t=>{const s=o.default.createServer()
935
- s.once("error",(e=>{e.code,t(!1)})),s.once("listening",(()=>{s.close((()=>{t(!0)}))})),s.listen(e)}))}var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
936
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.isPortAvailable=s,exports.findAvailablePort=async function(e,t=10){for(let r=0;t>r;r++){const t=e+r
937
- if(await s(t))return t}throw Error(`No available port found after ${t} attempts starting from port ${e}`)}
938
- const o=r(t(9278))},6895(module){"use strict"
1106
+ const r=t(3807),s=t(8824)},6800(e,exports,t){"use strict"
1107
+ function r(e){return new Promise((t=>{const r=o.default.createServer()
1108
+ r.once("error",(e=>{e.code,t(!1)})),r.once("listening",(()=>{r.close((()=>{t(!0)}))})),r.listen(e)}))}var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1109
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.isPortAvailable=r,exports.findAvailablePort=async function(e,t=10){for(let s=0;t>s;s++){const t=e+s
1110
+ if(await r(t))return t}throw Error(`No available port found after ${t} attempts starting from port ${e}`)}
1111
+ const o=s(t(9278))},6895(module){"use strict"
939
1112
  module.exports=require("chalk")},6928(module){"use strict"
940
1113
  module.exports=require("path")},6982(module){"use strict"
941
1114
  module.exports=require("crypto")},7078(e,exports,t){"use strict"
@@ -944,23 +1117,44 @@ if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId
944
1117
  i.info("Starting spawn-auto-planner action",{requestId:o})
945
1118
  try{const e=await a.get(`/api/projects/${c}/requests/${o}`)
946
1119
  if(!e.success||!e.data)return{success:!1,message:`Request ${o} not found`,error:"Request not found"}
947
- const t=e.data,s=Array.isArray(t.acceptanceCriteria)?t.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",u=`Create a plan for request ${o}: ${t.title}\n\nDescription: ${t.description||"No description provided."}\n\n${s?"Acceptance Criteria:\n- "+s:""}\n\nResearch the codebase and create a comprehensive implementation plan.`,h=await(0,r.spawnActionAgent)({agent:"auto-planner",prompt:u,cwd:l,projectPath:l,projectId:c,title:`[${o}] Plan: ${t.title}`,requestId:o,parentSessionId:n,apiClient:a,logger:i,trackChildPid:d})
948
- return h.success?(i.info("Spawned auto-planner for request",{requestId:o,sessionId:h.sessionId,pid:h.pid}),{success:!0,message:"Auto-planner spawned for request "+o,data:{sessionId:h.sessionId,pid:h.pid}}):{success:!1,message:"Failed to spawn auto-planner: "+h.error,error:h.error||"Spawn failed"}}catch(e){const t=(0,s.getErrorMessage)(e)
1120
+ const t=e.data,r=Array.isArray(t.acceptanceCriteria)?t.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",u=`Create a plan for request ${o}: ${t.title}\n\nDescription: ${t.description||"No description provided."}\n\n${r?"Acceptance Criteria:\n- "+r:""}\n\nResearch the codebase and create a comprehensive implementation plan.`,h=await(0,s.spawnActionAgent)({agent:"auto-planner",prompt:u,cwd:l,projectPath:l,projectId:c,title:`[${o}] Plan: ${t.title}`,requestId:o,parentSessionId:n,apiClient:a,logger:i,trackChildPid:d})
1121
+ return h.success?(i.info("Spawned auto-planner for request",{requestId:o,sessionId:h.sessionId,pid:h.pid}),{success:!0,message:"Auto-planner spawned for request "+o,data:{sessionId:h.sessionId,pid:h.pid}}):{success:!1,message:"Failed to spawn auto-planner: "+h.error,error:h.error||"Spawn failed"}}catch(e){const t=(0,r.getErrorMessage)(e)
949
1122
  return i.error("spawn-auto-planner action failed",{requestId:o,error:t}),{success:!1,message:"Failed to spawn auto-planner: "+t,error:t}}}
950
- const s=t(8824),r=t(8148)},7163(e,exports){"use strict"
1123
+ const r=t(8824),s=t(8148)},7163(e,exports){"use strict"
951
1124
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.debugTokenAccess=function(){},exports.compareTokens=function(){},exports.debugTokenStorage=function(){},exports.disableTokenDebugging=function(){},exports.enableTokenDebugging=function(){},new WeakMap},7210(e,exports,t){"use strict"
952
1125
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.tryMarkMessageProcessed=exports.createToolCallHash=exports.isDuplicateToolCall=exports.determineEventToEmit=exports.interpolatePrompt=exports.evaluateCondition=exports.TimerConditionEvaluator=exports.TimerHookScheduler=exports.HookCache=exports.HookExecutor=void 0
953
- var s=t(3077)
954
- Object.defineProperty(exports,"HookExecutor",{enumerable:!0,get(){return s.HookExecutor}})
955
- var r=t(8740)
956
- Object.defineProperty(exports,"HookCache",{enumerable:!0,get(){return r.HookCache}})
1126
+ var r=t(3077)
1127
+ Object.defineProperty(exports,"HookExecutor",{enumerable:!0,get(){return r.HookExecutor}})
1128
+ var s=t(8740)
1129
+ Object.defineProperty(exports,"HookCache",{enumerable:!0,get(){return s.HookCache}})
957
1130
  var o=t(6563)
958
1131
  Object.defineProperty(exports,"TimerHookScheduler",{enumerable:!0,get(){return o.TimerHookScheduler}})
959
1132
  var n=t(6175)
960
1133
  Object.defineProperty(exports,"TimerConditionEvaluator",{enumerable:!0,get(){return n.TimerConditionEvaluator}})
961
1134
  var i=t(4627)
962
- Object.defineProperty(exports,"evaluateCondition",{enumerable:!0,get(){return i.evaluateCondition}}),Object.defineProperty(exports,"interpolatePrompt",{enumerable:!0,get(){return i.interpolatePrompt}}),Object.defineProperty(exports,"determineEventToEmit",{enumerable:!0,get(){return i.determineEventToEmit}}),Object.defineProperty(exports,"isDuplicateToolCall",{enumerable:!0,get(){return i.isDuplicateToolCall}}),Object.defineProperty(exports,"createToolCallHash",{enumerable:!0,get(){return i.createToolCallHash}}),Object.defineProperty(exports,"tryMarkMessageProcessed",{enumerable:!0,get(){return i.tryMarkMessageProcessed}})},7252(module){"use strict"
963
- module.exports=require("express")},7453(e,exports,t){"use strict"
1135
+ Object.defineProperty(exports,"evaluateCondition",{enumerable:!0,get(){return i.evaluateCondition}}),Object.defineProperty(exports,"interpolatePrompt",{enumerable:!0,get(){return i.interpolatePrompt}}),Object.defineProperty(exports,"determineEventToEmit",{enumerable:!0,get(){return i.determineEventToEmit}}),Object.defineProperty(exports,"isDuplicateToolCall",{enumerable:!0,get(){return i.isDuplicateToolCall}}),Object.defineProperty(exports,"createToolCallHash",{enumerable:!0,get(){return i.createToolCallHash}}),Object.defineProperty(exports,"tryMarkMessageProcessed",{enumerable:!0,get(){return i.tryMarkMessageProcessed}})},7227(e,exports){"use strict"
1136
+ function t(e){if("string"!=typeof e)return e
1137
+ try{return JSON.parse(e)}catch{return e}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.parseToolEvents=function(e){const{toolName:o,sessionId:n,input:i,output:a,timing:c}=e
1138
+ if("before"===c)return[{eventType:"tool_executed",timing:"before",variables:{tool:o,toolName:o,input:JSON.stringify(i??{}).substring(0,1e3),sessionId:n||""}}]
1139
+ const l=[],d=a||"",u={output:d,toolName:o,sessionId:n||""},h=o.includes("request_operations")||o.includes("complete_request"),g=o.includes("task_operations")||o.includes("complete_task"),p=o.includes("write_file"),m=o.includes("replace_in_files"),f=o.includes("quick_write")||o.includes("quick_replace")||o.includes("quick_manage"),y=o.includes("read_files"),v=o.includes("search"),w=o.includes("manage_files"),b=o.includes("document_operations"),I=o.includes("query_history")
1140
+ if(h&&d){const e=i?.operation,t=function(e){for(const t of r){const r=e.match(t)
1141
+ if(r)return r[1]}return null}(d)
1142
+ t&&("merge"===e||"finalize"===e?l.push({eventType:"request_completed",timing:"after",variables:{...u,requestId:t}}):"complete"===e&&l.push({eventType:"request_ready_for_merge",timing:"after",variables:{...u,requestId:t}}))}else if(g&&d){const e=function(e){const t=e.match(s)
1143
+ return t?t[1]:null}(d)
1144
+ e&&l.push({eventType:"task_completed",timing:"after",variables:{...u,taskId:e}})}else if(p){const e=d.match(/(?:Created|Updated|Wrote)\s+(?:file\s+)?([^\s\n]+)/i)
1145
+ l.push({eventType:"write_file",timing:"after",variables:{...u,path:e?.[1]||""}})}else if(m){const e=t(i?.operations)||[],r=(Array.isArray(e)?e:[]).map((e=>e.path||e.documentId)).filter(Boolean)
1146
+ l.push({eventType:"replace_in_files",timing:"after",variables:{...u,paths:r.join(", "),fileCount:r.length+""}})}else if(f){const e={...u,path:i?.operationArgs?.path||"",title:i?.title||""},t=d.match(/\*\*Request:\*\*\s*(req\d+)/i)
1147
+ t&&(e.requestId=t[1]),l.push({eventType:"quick_edit",timing:"after",variables:e}),e.requestId&&l.push({eventType:"request_completed",timing:"after",variables:{...u,requestId:e.requestId}})}else if(y){const e=t(i?.paths)||[],r=(Array.isArray(e)?e:[]).map((e=>e.path||e.documentId)).filter(Boolean)
1148
+ l.push({eventType:"read_files",timing:"after",variables:{...u,paths:r.join(", "),fileCount:r.length+""}})}else if(v)l.push({eventType:"search",timing:"after",variables:{...u,query:i?.needle||"",directory:i?.directory||"",fileExtension:i?.options?.fileExtension||"*"}})
1149
+ else if(w){const e=t(i?.operations)||[],r=(Array.isArray(e)?e:[]).map((e=>e.operationType)).filter(Boolean)
1150
+ l.push({eventType:"manage_files",timing:"after",variables:{...u,operationTypes:[...new Set(r)].join(", ")}})}else b?l.push({eventType:"document_operations",timing:"after",variables:u}):I&&l.push({eventType:"query_history",timing:"after",variables:u})
1151
+ return l.push({eventType:"tool_executed",timing:"after",variables:{tool:o,toolName:o,output:d,sessionId:n||""}}),l}
1152
+ const r=[/Request completed:\s*(req\d+)/i,/Request (req\d+) completed/i,/completed.*?(req\d{6})/i,/Request finalized:\s*(req\d+)/i,/Request (req\d+) finalized/i,/finalized.*?(req\d{6})/i,/Request (req\d+) merged/i,/merged.*?(req\d{6})/i],s=/Task\s+(tas\d+).*(?:completed|marked as completed)/i},7252(module){"use strict"
1153
+ module.exports=require("express")},7388(e,exports,t){"use strict"
1154
+ var r=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
1155
+ var o=Object.getOwnPropertyDescriptor(t,r)
1156
+ o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),s=this&&this.__exportStar||function(e,exports){for(var t in e)"default"===t||{}.hasOwnProperty.call(exports,t)||r(exports,e,t)}
1157
+ Object.defineProperty(exports,"__esModule",{value:!0}),s(t(441),exports),s(t(270),exports)},7453(e,exports,t){"use strict"
964
1158
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleCompleteRequestAction=async function(e,t){const{requestId:o,reason:n}=e,{logger:i,apiClient:a,projectPath:c,projectId:l}=t
965
1159
  if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
966
1160
  i.info("Starting complete_request action",{requestId:o,reason:n})
@@ -968,23 +1162,160 @@ try{const e=await a.post(`/api/projects/${l}/requests/${o}/validate-completion`,
968
1162
  if(!e.success||!e.data)return{success:!1,message:"Server validation failed: "+(e.data?.reason||"Unknown error"),error:"Validation failed"}
969
1163
  const c=e.data
970
1164
  if(!c.canComplete){let e="Cannot complete request: "+c.reason
971
- return c.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be re-completed first:\n",c.lifoViolation.blockingRequests.forEach(((t,s)=>{e+=`${s+1}. ${t.requestId}: ${t.title}\n`}))),c.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",c.blockedByTasks.forEach(((t,s)=>{e+=`${s+1}. ${t.taskId}: ${t.title} (${t.status})\n`}))),{success:!1,message:e,error:c.reason}}if("revert-removal"!==c.mergeStrategy)return{success:!1,message:"Action server only handles reverted request re-completion. Got merge strategy: "+c.mergeStrategy,error:"Unsupported merge strategy: "+c.mergeStrategy}
1165
+ return c.lifoViolation?.blockingRequests?.length>0&&(e+="\n\nRequests that must be re-completed first:\n",c.lifoViolation.blockingRequests.forEach(((t,r)=>{e+=`${r+1}. ${t.requestId}: ${t.title}\n`}))),c.blockedByTasks?.length>0&&(e+="\n\nTasks that must be completed first:\n",c.blockedByTasks.forEach(((t,r)=>{e+=`${r+1}. ${t.taskId}: ${t.title} (${t.status})\n`}))),{success:!1,message:e,error:c.reason}}if("revert-removal"!==c.mergeStrategy)return{success:!1,message:"Action server only handles reverted request re-completion. Got merge strategy: "+c.mergeStrategy,error:"Unsupported merge strategy: "+c.mergeStrategy}
972
1166
  if(!c.revertInfo?.revertCommitHash)return{success:!1,message:"Server validated but did not provide revert commit hash",error:"Missing revert commit hash"}
973
1167
  const d=await async function(e,t,o){const{logger:n,projectPath:i}=o
974
1168
  n.info("Performing revert removal",{requestId:e,revertCommitHash:t.substring(0,8)})
975
- const a=(0,s.simpleGit)(i),c=(await a.branch()).all.includes("main")?"main":"master",l=await a.status()
1169
+ const a=(0,r.simpleGit)(i),c=(await a.branch()).all.includes("main")?"main":"master",l=await a.status()
976
1170
  if(l.current!==c&&(n.info("Switching to default branch",{from:l.current,to:c}),await a.checkout(c)),(await a.status()).files.length>0)return{success:!1,message:"Cannot re-complete request: working directory has uncommitted changes",error:"Uncommitted changes present"}
977
- if((await a.revparse(["HEAD"])).trim()!==t){const e=await a.raw(["rev-list","--count",t+"..HEAD"]),s=parseInt(e.trim(),10)
978
- if(s>0)return{success:!1,message:`Cannot re-complete: main branch has ${s} commit(s) since the revert. Create a new request instead.`,error:"Main branch has changed since revert"}}try{const s=(await a.raw(["rev-parse",t+"^"])).trim()
979
- await a.reset(["--hard",s])
980
- const r=await a.revparse(["HEAD"])
981
- if(r.trim()!==s)throw Error(`Git reset verification failed: expected ${s.substring(0,8)}, got ${r.substring(0,8)}`)
982
- return n.info("Revert removal succeeded",{requestId:e,removedCommit:t.substring(0,8),restoredTo:s.substring(0,8)}),{success:!0,message:"Successfully re-completed request "+e,data:{operationType:"revert-removal",removedRevertCommit:t,restoredToCommit:s}}}catch(t){const s=(0,r.getErrorMessage)(t)
983
- return n.error("Revert removal failed",{requestId:e,error:s}),{success:!1,message:"Failed to remove revert commit: "+s,error:s}}}(o,c.revertInfo.revertCommitHash,t)
1171
+ if((await a.revparse(["HEAD"])).trim()!==t){const e=await a.raw(["rev-list","--count",t+"..HEAD"]),r=parseInt(e.trim(),10)
1172
+ if(r>0)return{success:!1,message:`Cannot re-complete: main branch has ${r} commit(s) since the revert. Create a new request instead.`,error:"Main branch has changed since revert"}}try{const r=(await a.raw(["rev-parse",t+"^"])).trim()
1173
+ await a.reset(["--hard",r])
1174
+ const s=await a.revparse(["HEAD"])
1175
+ if(s.trim()!==r)throw Error(`Git reset verification failed: expected ${r.substring(0,8)}, got ${s.substring(0,8)}`)
1176
+ return n.info("Revert removal succeeded",{requestId:e,removedCommit:t.substring(0,8),restoredTo:r.substring(0,8)}),{success:!0,message:"Successfully re-completed request "+e,data:{operationType:"revert-removal",removedRevertCommit:t,restoredToCommit:r}}}catch(t){const r=(0,s.getErrorMessage)(t)
1177
+ return n.error("Revert removal failed",{requestId:e,error:r}),{success:!1,message:"Failed to remove revert commit: "+r,error:r}}}(o,c.revertInfo.revertCommitHash,t)
984
1178
  if(!d.success)return d
985
- try{await a.post(`/api/projects/${l}/requests/${o}/finalize-completion`,{summary:n||"Re-completed from UI",acceptanceEvidence:[{evidence:"Re-completed reverted request via GUI action"}],mergeResult:{mergeCommitHash:d.data.restoredToCommit,method:"revert-removal"}})}catch(e){return i.error("Merge succeeded but finalization failed",{requestId:o,error:(0,r.getErrorMessage)(e)}),{success:!1,message:"Git revert removal succeeded but server finalization failed: "+(0,r.getErrorMessage)(e),error:"Finalization failed"}}return d}catch(e){const t=(0,r.getErrorMessage)(e)
1179
+ try{await a.post(`/api/projects/${l}/requests/${o}/finalize-completion`,{summary:n||"Re-completed from UI",acceptanceEvidence:[{evidence:"Re-completed reverted request via GUI action"}],mergeResult:{mergeCommitHash:d.data.restoredToCommit,method:"revert-removal"}})}catch(e){return i.error("Merge succeeded but finalization failed",{requestId:o,error:(0,s.getErrorMessage)(e)}),{success:!1,message:"Git revert removal succeeded but server finalization failed: "+(0,s.getErrorMessage)(e),error:"Finalization failed"}}return d}catch(e){const t=(0,s.getErrorMessage)(e)
986
1180
  return i.error("Complete_request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to complete request: "+t,error:t}}}
987
- const s=t(3807),r=t(8824)},7767(e,exports,t){"use strict"
1181
+ const r=t(3807),s=t(8824)},7478(e,exports,t){"use strict"
1182
+ async function r(e,t,r){const s=r.startTimer("git_get_current_branch_duration")
1183
+ try{const o=(await e.status()).current
1184
+ if(!o)throw new g.ValidationError("Repository is in detached HEAD state or branch detection failed")
1185
+ return t.debug("Retrieved current branch",{branch:o}),r.incrementCounter("git_get_current_branch_success",1),s({success:!0}),o}catch(e){const o=(0,m.getErrorMessage)(e)
1186
+ throw t.error("Failed to get current branch",{error:o}),r.incrementCounter("git_get_current_branch_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to get current branch: "+o)}}async function s(e,t,r,s){const o=s.startTimer("git_branch_exists_duration")
1187
+ try{const n=(await e.branch(["-l"])).all.includes(t)
1188
+ return r.debug("Checked branch existence",{branchName:t,exists:n}),s.incrementCounter("git_branch_exists_check",1,{exists:n.toString()}),o({success:!0}),n}catch(e){const n=(0,m.getErrorMessage)(e)
1189
+ return r.error("Failed to check branch existence",{branchName:t,error:n}),s.incrementCounter("git_branch_exists_failure",1),o({success:!1,error:e instanceof Error?e.name:"unknown"}),!1}}async function o(e,t,o,n){const i=n.startTimer("git_switch_branch_duration")
1190
+ try{const a=await r(e,o,n)
1191
+ if(a===t)return o.debug("Already on target branch",{branchName:t}),n.incrementCounter("git_switch_branch_noop",1),i({success:!0,reason:"already_on_branch"}),{success:!0,branchName:t,previousBranch:a,message:`Already on branch '${t}'`}
1192
+ if(!await s(e,t,o,n)){const e=`Branch '${t}' does not exist`
1193
+ throw o.error(e),n.incrementCounter("git_switch_branch_not_found",1),i({success:!1,reason:"branch_not_found"}),new g.ValidationError(e)}await e.checkout(t)
1194
+ const c=await e.revparse(["HEAD"])
1195
+ return o.info("Switched to branch",{branchName:t,previousBranch:a,commitHash:c.substring(0,8)}),n.incrementCounter("git_switch_branch_success",1),i({success:!0}),{success:!0,branchName:t,previousBranch:a,commitHash:c,message:`Switched from '${a}' to '${t}'`}}catch(e){const r=(0,m.getErrorMessage)(e)
1196
+ throw o.error("Failed to switch branch",{branchName:t,error:r}),n.incrementCounter("git_switch_branch_failure",1),i({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to switch to branch '${t}': ${r}`)}}async function n(e,t,r){const s=r.startTimer("git_repository_status_duration")
1197
+ try{let o,n=!0
1198
+ try{o=await e.status()}catch(e){return n=!1,t.warn("Not a git repository or git not available"),r.incrementCounter("git_repository_status_not_git",1),s({success:!0,reason:"not_git_repo"}),{isGitRepository:!1,currentBranch:"",isClean:!1,hasMainBranch:!1,hasMasterBranch:!1,defaultBranch:"",uncommittedChanges:[],branches:[]}}const i=o.current||"",a=0===o.files.length,c=o.files.map((e=>e.path)),l=await e.branch(["-l"]),d=l.all.map((e=>({name:e,isCurrentBranch:e===i}))),u=l.all.includes("main"),h=l.all.includes("master")
1199
+ let g=""
1200
+ return u?g="main":h?g="master":d.length>0&&(g=d[0].name),t.debug("Retrieved repository status",{currentBranch:i,isClean:a,hasMainBranch:u,hasMasterBranch:h,defaultBranch:g,branchCount:d.length,uncommittedCount:c.length}),r.incrementCounter("git_repository_status_success",1),s({success:!0}),{isGitRepository:n,currentBranch:i,isClean:a,hasMainBranch:u,hasMasterBranch:h,defaultBranch:g,uncommittedChanges:c,branches:d}}catch(e){const o=(0,m.getErrorMessage)(e)
1201
+ throw t.error("Failed to get repository status",{error:o}),r.incrementCounter("git_repository_status_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to get repository status: "+o)}}function i(e){return"requests/"+e}async function a(e,t,r){const s=r.startTimer("git_uncommitted_changes_check_duration")
1202
+ try{t.debug("Starting uncommitted changes check"),t.debug("Checking for uncommitted changes before merge"),Date.now()
1203
+ const o=await e.status()
1204
+ Date.now(),t.debug("Git status result",{files:o.files.map((e=>({path:e.path,index:e.index,working_dir:e.working_dir}))),totalFiles:o.files.length})
1205
+ const n=[],i=[],a=[]
1206
+ o.files.forEach((e=>{t.debug("Processing file",{path:e.path,index:e.index,workingDir:e.working_dir})," "!==e.index&&"?"!==e.index&&(n.push(e.path),t.debug("Added to staged",{path:e.path}))," "!==e.working_dir&&"?"!==e.working_dir&&(i.push(e.path),t.debug("Added to unstaged",{path:e.path})),"?"!==e.working_dir&&"?"!==e.index||(a.push(e.path),t.debug("Added to untracked",{path:e.path}))}))
1207
+ const c=n.length+i.length+a.length,l=c>0
1208
+ t.debug("Uncommitted changes summary",{hasChanges:l,totalFiles:c,staged:n.length,unstaged:i.length,untracked:a.length,stagedFiles:n,unstagedFiles:i,untrackedFiles:a})
1209
+ let d=""
1210
+ if(l){const e=[]
1211
+ n.length>0&&e.push(n.length+" staged file(s)"),i.length>0&&e.push(i.length+" unstaged file(s)"),a.length>0&&e.push(a.length+" untracked file(s)"),d=`Found ${c} uncommitted change(s): ${e.join(", ")}`}else d="No uncommitted changes detected"
1212
+ return(0,h.isDebugToolsEnabled)()&&t.debug("Summary",{summary:d}),t.debug("Uncommitted changes check completed",{hasChanges:l,staged:n.length,unstaged:i.length,untracked:a.length,total:c,summary:d}),r.incrementCounter("git_uncommitted_changes_check_success",1),r.recordValue("git_uncommitted_changes_total_files",c),s({success:!0}),{hasChanges:l,stagedFiles:n,unstagedFiles:i,untrackedFiles:a,totalFiles:c,summary:d}}catch(e){const o=(0,m.getErrorMessage)(e)
1213
+ throw t.error("Failed to check uncommitted changes",{error:o}),r.incrementCounter("git_uncommitted_changes_check_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to check uncommitted changes: "+o)}}var c,l=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
1214
+ var o=Object.getOwnPropertyDescriptor(t,r)
1215
+ o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),d=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),u=this&&this.__importStar||(c=function(e){return c=Object.getOwnPropertyNames||function(e){var t=[]
1216
+ for(var r in e)({}).hasOwnProperty.call(e,r)&&(t[t.length]=r)
1217
+ return t},c(e)},function(e){if(e&&e.__esModule)return e
1218
+ var t={}
1219
+ if(null!=e)for(var r=c(e),s=0;s<r.length;s++)"default"!==r[s]&&l(t,e,r[s])
1220
+ return d(t,e),t})
1221
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.getCurrentBranch=r,exports.branchExists=s,exports.createBranch=async function(e,t,o="HEAD",n,i,a){const c=i.startTimer("git_create_branch_duration")
1222
+ try{if(await s(e,t,n,i)){const e=`Branch '${t}' already exists`
1223
+ return n.warn(e),i.incrementCounter("git_create_branch_already_exists",1),c({success:!1,reason:"already_exists"}),{success:!1,branchName:t,message:e}}const l=await r(e,n,i)
1224
+ await e.checkoutBranch(t,o)
1225
+ const d=await e.revparse(["HEAD"]),u=a||l
1226
+ return await e.checkout(u),n.info("Created new branch",{branchName:t,fromBranch:o,currentBranch:l,returnedToBranch:u,commitHash:d.substring(0,8)}),i.incrementCounter("git_create_branch_success",1),c({success:!0}),{success:!0,branchName:t,previousBranch:l,commitHash:d,message:`Created branch '${t}' from '${o}'`}}catch(e){const r=(0,m.getErrorMessage)(e)
1227
+ throw n.error("Failed to create branch",{branchName:t,fromBranch:o,error:r}),i.incrementCounter("git_create_branch_failure",1),c({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to create branch '${t}': ${r}`)}},exports.switchToBranch=o,exports.mergeBranch=async function(e,t,n,i,a,c){const l=a.startTimer("git_merge_branch_duration")
1228
+ try{const d=await r(e,i,a)
1229
+ if(d!==n&&await o(e,n,i,a),!await s(e,t,i,a)){const e=`Source branch '${t}' does not exist`
1230
+ throw i.error(e),a.incrementCounter("git_merge_branch_source_not_found",1),l({success:!1,reason:"source_not_found"}),new g.ValidationError(e)}const u=c?["--no-ff","-m",c,t]:["--no-ff",t],h=(await e.merge(u),await e.revparse(["HEAD"]))
1231
+ return i.info("Successfully merged branch",{sourceBranch:t,targetBranch:n,commitHash:h.substring(0,8),mergeMessage:c||"default merge message"}),a.incrementCounter("git_merge_branch_success",1),l({success:!0}),{success:!0,branchName:n,previousBranch:d,commitHash:h,message:`Merged '${t}' into '${n}'`}}catch(e){const r=(0,m.getErrorMessage)(e)
1232
+ throw i.error("Failed to merge branch",{sourceBranch:t,targetBranch:n,error:r}),a.incrementCounter("git_merge_branch_failure",1),l({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to merge '${t}' into '${n}': ${r}`)}},exports.deleteBranch=async function(e,t,o=!1,n,i){const a=i.startTimer("git_delete_branch_duration")
1233
+ try{const c=await r(e,n,i)
1234
+ if(c===t){const e=`Cannot delete current branch '${t}'`
1235
+ throw n.error(e),i.incrementCounter("git_delete_branch_current_branch",1),a({success:!1,reason:"current_branch"}),new g.ValidationError(e)}if("main"===t||"master"===t){const e=`Cannot delete protected branch '${t}'`
1236
+ throw n.error(e),i.incrementCounter("git_delete_branch_protected",1),a({success:!1,reason:"protected_branch"}),new g.ValidationError(e)}if(!await s(e,t,n,i)){const e=`Branch '${t}' does not exist`
1237
+ return n.warn(e),i.incrementCounter("git_delete_branch_not_found",1),a({success:!1,reason:"not_found"}),{success:!1,branchName:t,message:e}}const l=o?["-D",t]:["-d",t]
1238
+ return await e.branch(l),n.info("Deleted branch",{branchName:t,force:o,currentBranch:c}),i.incrementCounter("git_delete_branch_success",1),a({success:!0}),{success:!0,branchName:t,previousBranch:c,message:`Deleted branch '${t}'${o?" (forced)":""}`}}catch(e){const r=(0,m.getErrorMessage)(e)
1239
+ throw n.error("Failed to delete branch",{branchName:t,force:o,error:r}),i.incrementCounter("git_delete_branch_failure",1),a({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError(`Failed to delete branch '${t}': ${r}`)}},exports.getRepositoryStatus=n,exports.validateGitRepository=async function(e,t,r){const s=r.startTimer("git_validate_repository_duration")
1240
+ try{const o=await n(e,t,r)
1241
+ return o.isGitRepository?(o.currentBranch||(t.warn("Git repository in detached HEAD state"),r.incrementCounter("git_validate_repository_detached_head",1)),o.isClean||(t.warn("Git repository has uncommitted changes",{count:o.uncommittedChanges.length,files:o.uncommittedChanges.slice(0,5)}),r.incrementCounter("git_validate_repository_dirty",1)),o.defaultBranch||(t.warn("No default branch found in git repository"),r.incrementCounter("git_validate_repository_no_default_branch",1)),t.info("Git repository validation completed",{currentBranch:o.currentBranch,isClean:o.isClean,defaultBranch:o.defaultBranch,branchCount:o.branches.length}),r.incrementCounter("git_validate_repository_success",1),s({success:!0}),o):(t.warn("Git repository validation failed: not a git repository"),r.incrementCounter("git_validate_repository_not_git",1),s({success:!1,reason:"not_git_repo"}),o)}catch(e){const o=(0,m.getErrorMessage)(e)
1242
+ throw t.error("Git repository validation failed",{error:o}),r.incrementCounter("git_validate_repository_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Git repository validation failed: "+o)}},exports.sanitizeBranchName=function(e){return e.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"").substring(0,50)},exports.generateRequestBranchName=i,exports.generateMergeMessagePatterns=function(e){const t=i(e)
1243
+ return[`Merge ${t}-`,"Merge "+t,`Merge ${t}-.*\\(resolved conflicts\\)`,`Merge ${t} \\(resolved conflicts\\)`]},exports.parseRequestIdFromBranchName=function(e){const t=e.match(/^feature\/(req\d{6})-/)
1244
+ return t?t[1]:null},exports.switchToDefaultBranch=async function(e,t,r){const s=r.startTimer("git_switch_to_default_duration")
1245
+ try{const i=await n(e,t,r)
1246
+ if(!i.defaultBranch){const e="No default branch found"
1247
+ return t.warn(e),r.incrementCounter("git_switch_to_default_no_branch",1),s({success:!1,reason:"no_default_branch"}),{success:!1,branchName:"",message:e}}const a=await o(e,i.defaultBranch,t,r)
1248
+ return a.success?(t.info("Switched to default branch",{defaultBranch:i.defaultBranch,previousBranch:a.previousBranch}),r.incrementCounter("git_switch_to_default_success",1),s({success:!0})):(r.incrementCounter("git_switch_to_default_failed",1),s({success:!1,reason:"switch_failed"})),a}catch(e){const o=(0,m.getErrorMessage)(e)
1249
+ throw t.error("Failed to switch to default branch",{error:o}),r.incrementCounter("git_switch_to_default_error",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to switch to default branch: "+o)}},exports.ensureRepositoryReady=async function(e,t,r){const s=r.startTimer("git_ensure_repository_ready_duration")
1250
+ try{const o=await n(e,t,r),i=[]
1251
+ o.isGitRepository||i.push("Not a git repository"),o.currentBranch||i.push("Repository is in detached HEAD state"),o.defaultBranch||i.push("No default branch (main/master) found")
1252
+ const a=0===i.length
1253
+ return t.debug("Repository readiness check completed",{ready:a,issueCount:i.length,issues:i}),r.incrementCounter("git_ensure_repository_ready_check",1,{ready:a.toString()}),s({success:!0,ready:a.toString()}),{ready:a,status:o,issues:i}}catch(e){const o=(0,m.getErrorMessage)(e)
1254
+ return t.error("Failed to check repository readiness",{error:o}),r.incrementCounter("git_ensure_repository_ready_failure",1),s({success:!1,error:e instanceof Error?e.name:"unknown"}),{ready:!1,status:{isGitRepository:!1,currentBranch:"",isClean:!1,hasMainBranch:!1,hasMasterBranch:!1,defaultBranch:"",uncommittedChanges:[],branches:[]},issues:["Repository check failed: "+o]}}},exports.hasMainChangedSinceRevert=async function(e,t,r,s){const o=s.startTimer("git_check_main_changed_since_revert")
1255
+ try{r.debug("Starting main branch change detection",{providedRevertCommitHash:t.substring(0,8),fullRevertCommitHash:t})
1256
+ const s=await e.revparse(["HEAD"])
1257
+ r.debug("Git state captured",{currentMainHash:s.substring(0,8),fullCurrentMainHash:s,revertCommitHash:t.substring(0,8),fullRevertCommitHash:t,hashesMatch:s===t})
1258
+ try{const t=await e.revparse(["--abbrev-ref","HEAD"]),s=await e.status(),o=await e.log(["--oneline","-5"])
1259
+ r.debug("Additional git context",{currentBranch:t,gitStatus:{ahead:s.ahead,behind:s.behind,current:s.current,tracking:s.tracking,staged:s.staged.length,modified:s.modified.length},recentCommits:o.all.map((e=>({hash:e.hash.substring(0,8),message:e.message})))})}catch(e){r.warn("Failed to gather additional git context",{error:e instanceof Error?e.message:e+""})}const n=await(0,p.validateCommitHash)(e,t,r,"hasMainChangedSinceRevert-revert-validation")
1260
+ if(!n.valid)throw r.error("Revert commit validation failed",{revertCommitHash:t.substring(0,8),error:n.error}),Error(`Revert commit ${t.substring(0,8)} not found or not reachable: ${n.error}`)
1261
+ const i=await(0,p.validateCommitHash)(e,s,r,"hasMainChangedSinceRevert-head-validation")
1262
+ if(!i.valid)throw r.error("Current HEAD validation failed",{currentMainHash:s.substring(0,8),error:i.error}),Error(`Current HEAD ${s.substring(0,8)} validation failed: ${i.error}`)
1263
+ if(s===t)return r.info("Main branch unchanged since revert - exact hash match",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),fullHashMatch:!0}),o({success:!0,changed:"false"}),{hasChanged:!1,currentMainHash:s,revertCommitHash:t,commitsSinceRevert:0,message:"Main branch has not changed since revert"}
1264
+ r.debug("Hash comparison shows difference, checking ancestor relationship",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),hashesEqual:!1})
1265
+ let a=!1,c=0,l=null
1266
+ for(let s=1;3>=s;s++)try{r.debug("Checking ancestor relationship with merge-base",{revertCommitHash:t.substring(0,8),currentHead:"HEAD",attempt:s}),s>1&&(await new Promise((e=>setTimeout(e,100))),r.debug("Retry attempt after delay",{attempt:s,delay:100})),await e.raw(["merge-base","--is-ancestor",t,"HEAD"]),r.debug("Revert commit is ancestor of HEAD, counting commits between them",{attempt:s})
1267
+ const o=await e.raw(["rev-list","--count",t+"..HEAD"])
1268
+ c=parseInt(o.trim(),10),a=!0
1269
+ break}catch(e){l=e,r.debug("Ancestor check attempt failed",{attempt:s,error:(0,m.getErrorMessage)(e)}),3===s&&r.debug("All ancestor check attempts failed, treating as non-ancestor")}if(a){try{const s=await e.raw(["rev-list","--oneline",t+"..HEAD"])
1270
+ r.debug("Commits found between revert and HEAD",{count:c,commits:s.trim().split("\n").filter((e=>e.length>0))})}catch(e){r.warn("Could not list commits between revert and HEAD",{error:e instanceof Error?e.message:e+""})}return r.info("Main branch has changed since revert - ancestor relationship confirmed",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),commitsSinceRevert:c,isAncestor:!0}),o({success:!0,changed:"true"}),{hasChanged:!0,currentMainHash:s,revertCommitHash:t,commitsSinceRevert:c,message:`Main branch has ${c} new commit${c>1?"s":""} since revert`}}r.warn("Revert commit is not an ancestor of current HEAD after retries - investigating relationship",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),lastAncestorError:l instanceof Error?l.message:l+"",attempts:3})
1271
+ try{const o=await e.raw(["merge-base",t,"HEAD"])
1272
+ r.debug("Merge base found between revert commit and HEAD",{mergeBase:o.trim().substring(0,8),revertCommitHash:t.substring(0,8),currentMainHash:s.substring(0,8)})}catch(e){r.warn("Could not find merge base",{error:e instanceof Error?e.message:e+""})}try{await e.raw(["merge-base","--is-ancestor","HEAD",t]),r.error("CRITICAL: Current HEAD is ancestor of revert commit - possible git state corruption or incorrect revert hash",{currentMainHash:s.substring(0,8),revertCommitHash:t.substring(0,8),implication:"This suggests the stored revert commit hash may be incorrect or from a different branch state"})}catch(e){r.debug("HEAD is not ancestor of revert commit either - commits may be on divergent paths",{error:e instanceof Error?e.message:e+""})}return o({success:!0,changed:"unknown"}),{hasChanged:!0,currentMainHash:s,revertCommitHash:t,commitsSinceRevert:-1,message:"Cannot determine relationship between revert commit and current HEAD - assuming changed for safety. This may indicate a stored revert commit hash issue."}}catch(e){const s=(0,m.getErrorMessage)(e)
1273
+ throw r.error("Failed to check if main changed since revert",{revertCommitHash:t.substring(0,8),error:s}),o({success:!1,error:e instanceof Error?e.name:"unknown"}),Error("Failed to check main branch changes: "+s)}},exports.findRevertCommitHash=async function(e,t,r){try{r.debug("Searching for revert commit hash in tool records",{requestId:e})
1274
+ const s=await t.getToolRecordsByRequestId(e,"revert-request")
1275
+ if(s.sort(((e,t)=>new Date(t.createdAt).getTime()-new Date(e.createdAt).getTime())),!s||0===s.length)return r.debug("No revert tool records found for request",{requestId:e}),{found:!1,message:"No revert operations found for this request"}
1276
+ r.debug("Searching through tool records for revert commit hash",{requestId:e,totalRecords:s.length,recordTypes:s.map((e=>e.type)),recordStatuses:s.map((e=>e.metadata?.status))})
1277
+ for(const t of s)if(r.debug("Examining tool record",{requestId:e,recordId:t.id,recordType:t.type,status:t.metadata?.status,hasRevertCommitHash:!!t.metadata?.revertCommitHash,revertCommitHash:t.metadata?.revertCommitHash?.substring(0,8),fullRevertCommitHash:t.metadata?.revertCommitHash,createdAt:t.createdAt}),"completed"===t.metadata?.status&&t.metadata?.revertCommitHash){const s="simple",o=t.metadata.revertCommitHash
1278
+ r.debug("Found potential revert commit hash, validating...",{requestId:e,revertCommitHash:o.substring(0,8),fullRevertCommitHash:o,revertType:s,recordId:t.id,revertedAt:t.createdAt})
1279
+ try{if(!/^[a-f0-9]{40}$/i.test(o)){r.warn("Invalid commit hash format found in tool record",{requestId:e,invalidHash:o,recordId:t.id,format:"Expected 40-character hex string"})
1280
+ continue}return r.info("Found valid revert commit hash from tool records",{requestId:e,revertCommitHash:o.substring(0,8),fullRevertCommitHash:o,revertType:s,recordId:t.id,revertedAt:t.createdAt,hashFormat:"valid"}),{found:!0,revertCommitHash:o,revertType:s,revertedAt:t.createdAt,message:`Found ${s} revert commit from ${t.createdAt}`}}catch(s){r.warn("Revert commit hash validation failed, trying next record",{requestId:e,revertCommitHash:o.substring(0,8),recordId:t.id,error:s instanceof Error?s.message:s+""})
1281
+ continue}}return r.warn("Found revert tool records but none have completed status with commit hash",{requestId:e,recordCount:s.length,records:s.map((e=>({type:e.type,status:e.metadata?.status,hasCommitHash:!!e.metadata?.revertCommitHash})))}),{found:!1,message:"Found revert records but none were completed successfully"}}catch(t){const s=(0,m.getErrorMessage)(t)
1282
+ return r.error("Failed to find revert commit hash from tool records",{requestId:e,error:s}),{found:!1,message:"Failed to search tool records: "+s}}},exports.checkUncommittedChanges=a,exports.commitUserChanges=async function(e,t,r,s,o){const n=o.startTimer("git_commit_user_changes_duration")
1283
+ try{if(!r.hasChanges)throw new g.ValidationError("No uncommitted changes to commit")
1284
+ s.debug("Starting user changes commit process",{staged:r.stagedFiles.length,unstaged:r.unstagedFiles.length,untracked:r.untrackedFiles.length,total:r.totalFiles}),s.info("Committing user changes before merge",{requestId:t,staged:r.stagedFiles.length,unstaged:r.unstagedFiles.length,untracked:r.untrackedFiles.length,total:r.totalFiles})
1285
+ const i=[...r.unstagedFiles,...r.untrackedFiles]
1286
+ i.length>0?(s.debug("Staging files",{files:i.slice(0,10),total:i.length}),await e.add(i),s.debug("Staged uncommitted files",{files:i.slice(0,10),total:i.length}),s.debug("Successfully staged files")):s.debug("No additional files to stage (already staged)")
1287
+ const a=`chore: commit user changes before merging request ${t}\n\n${r.summary}\n\nThis commit preserves user modifications made during the request lifecycle\nbefore proceeding with the branch merge operation.`
1288
+ s.debug("Commit message",{message:a.split("\n")[0]}),s.debug("Creating commit"),await e.commit(a)
1289
+ const c=await e.revparse(["HEAD"])
1290
+ return s.debug("Commit created successfully",{commitHash:c.substring(0,8),filesCommitted:r.totalFiles}),s.info("Successfully committed user changes",{requestId:t,commitHash:c.substring(0,8),filesCommitted:r.totalFiles,message:a.split("\n")[0]}),o.incrementCounter("git_commit_user_changes_success",1),o.recordValue("git_commit_user_changes_files",r.totalFiles),n({success:!0}),{commitHash:c,message:a,filesCommitted:r.totalFiles}}catch(e){const i=(0,m.getErrorMessage)(e)
1291
+ throw s.error("Failed to commit user changes",{requestId:t,error:i,uncommittedFiles:r.totalFiles}),o.incrementCounter("git_commit_user_changes_failure",1),n({success:!1,error:e instanceof Error?e.name:"unknown"}),new g.ValidationError("Failed to commit user changes: "+i)}},exports.commitUserChangesBeforeToolOperation=async function(e,r,s,o,n){const i=n.startTimer("git_pre_tool_user_changes_duration")
1292
+ try{let c=Date.now()
1293
+ o.debug("Checking for user changes before tool operation",{toolName:r})
1294
+ let l=e
1295
+ if(s.workspaceManager){const e=s.workspaceManager.getCurrentContext(s.currentSessionId)
1296
+ if("multi-agent"===e.mode&&e.requestId){const{simpleGit}=t(3807)
1297
+ l=simpleGit(e.basePath),o.debug("Using worktree git instance for user changes",{requestId:e.requestId,basePath:e.basePath,toolName:r})}}if(Date.now(),s.workspaceManager?.consumeWorkspaceKnownClean(s.currentSessionId))return o.debug("Skipping uncommitted changes check - workspace known to be clean",{toolName:r,requestId:s.requestId}),i({success:!0,reason:"known_clean"}),{userChangesCommitted:!1}
1298
+ c=Date.now()
1299
+ try{(await l.revparse(["--show-toplevel"])).trim()}catch(e){}const d=await a(l,o,n)
1300
+ if(Date.now(),!d.hasChanges)return o.debug("No user changes detected, proceeding with tool",{toolName:r}),o.debug("No user changes detected before tool operation",{toolName:r,requestId:s.requestId,taskId:s.taskId}),i({success:!0,reason:"no_changes"}),{userChangesCommitted:!1}
1301
+ let u
1302
+ o.debug("Found user changes before tool, committing them first",{toolName:r}),o.info("Found user changes before tool operation, committing separately",{toolName:r,requestId:s.requestId,taskId:s.taskId,totalFiles:d.totalFiles,summary:d.summary}),u=s.requestId&&s.taskId?`chore: commit user changes before ${r} (task ${s.taskId})`:s.requestId?`chore: commit user changes before ${r} (request ${s.requestId})`:`chore: commit user changes before ${r} operation`
1303
+ const h=`${u}\n\n${{...d,summary:`User changes detected before ${r}: ${d.summary.replace("Found ","").toLowerCase()}`}.summary}\n\nThis commit preserves user modifications made before the ${r} tool operation\nto maintain clear separation between user changes and tool changes.`
1304
+ o.debug("Committing user changes with message",{firstLine:h.split("\n")[0]})
1305
+ const g=[...d.unstagedFiles,...d.untrackedFiles]
1306
+ c=Date.now(),g.length>0&&(o.debug("Staging files before tool operation",{files:g.slice(0,10),total:g.length}),await l.add(g)),Date.now(),c=Date.now(),await l.commit(h),Date.now()
1307
+ const p=await l.revparse(["HEAD"])
1308
+ return o.debug("User changes committed successfully before tool",{commitHash:p.substring(0,8),filesCommitted:d.totalFiles,toolName:r}),o.info("Successfully committed user changes before tool operation",{toolName:r,requestId:s.requestId,taskId:s.taskId,commitHash:p.substring(0,8),filesCommitted:d.totalFiles}),n.incrementCounter("git_pre_tool_user_changes_committed",1),n.recordValue("git_pre_tool_user_changes_files",d.totalFiles),i({success:!0,reason:"changes_committed"}),{userChangesCommitted:!0,commitHash:p,filesCommitted:d.totalFiles}}catch(e){const t=(0,m.getErrorMessage)(e)
1309
+ return o.debug("Failed to commit user changes before tool",{toolName:r,error:t}),o.warn("Failed to commit user changes before tool operation",{toolName:r,requestId:s.requestId,taskId:s.taskId,error:t}),n.incrementCounter("git_pre_tool_user_changes_failed",1),i({success:!1,error:e instanceof Error?e.name:"unknown"}),{userChangesCommitted:!1}}},exports.findLargeUntrackedFiles=async function(e,r=104857600){const s=[]
1310
+ try{const o=t(3807).simpleGit(e),n=(await o.status()).not_added||[]
1311
+ for(const t of n)try{const o=y.join(e,t),n=f.statSync(o)
1312
+ n.isFile()&&n.size>=r&&s.push({path:t,sizeBytes:n.size,sizeMB:Math.round(n.size/1048576*10)/10})}catch{}}catch{}return s},exports.formatLargeFileWarning=function(e){if(0===e.length)return""
1313
+ const t=e.reduce(((e,t)=>e+t.sizeMB),0)
1314
+ let r=`\n\n\u26a0\ufe0f **Performance Warning**: Large untracked files detected (${Math.round(t)} MB total) that may slow git operations:\n`
1315
+ for(const t of e.slice(0,5))r+=`- \`${t.path}\` (${t.sizeMB} MB)\n`
1316
+ return e.length>5&&(r+=`- ... and ${e.length-5} more large files\n`),r+="\nConsider adding these to `.gitignore` or moving them outside the repository.",r}
1317
+ const h=t(3444),g=t(9860),p=t(3922),m=t(8824),f=u(t(9896)),y=u(t(6928))
1318
+ t(857)},7767(e,exports,t){"use strict"
988
1319
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveAndWorkRequestAction=async function(e,t){const{requestId:o}=e,{logger:n,apiClient:i,projectId:a,projectPath:c,trackChildPid:l}=t
989
1320
  if(!o)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
990
1321
  n.info("Starting approve-and-work-request action",{requestId:o})
@@ -994,327 +1325,387 @@ const d=e.data
994
1325
  if("pending_approval"!==d.status)return{success:!1,message:`Cannot approve request with status '${d.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+d.status}
995
1326
  if(!(await i.patch(`/api/projects/${a}/requests/${o}`,{status:"open"})).success)return{success:!1,message:"Failed to update request status",error:"Update failed"}
996
1327
  const u=Array.isArray(d.acceptanceCriteria)?d.acceptanceCriteria.map((e=>e.description||e)).join("\n- "):"",h=`Work on request ${o}: ${d.title}\n\nDescription: ${d.description}\n\n${u?"Acceptance Criteria:\n- "+u:""}\n\nThis request has just been approved. Please begin implementation.`
997
- let g,p=!1,f=!1,m=d.cliOrigin?.sessionId||null
998
- if(m)try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${m}`)
999
- e.success&&e.data?.parent?.externalSessionId&&(g=e.data.parent.externalSessionId,m=g,n.info("Resolved parent session for notification",{cliOriginSession:d.cliOrigin.sessionId,parentSession:m}))}catch(e){n.warn("Failed to resolve session parent, using cliOrigin directly",{error:(0,s.getErrorMessage)(e)})}if(!m)try{const e=await i.get(`/api/projects/${a}/session-request-associations/by-request/${o}?activeOnly=true`)
1328
+ let g,p=!1,m=!1,f=d.cliOrigin?.sessionId||null
1329
+ if(f)try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${f}`)
1330
+ e.success&&e.data?.parent?.externalSessionId&&(g=e.data.parent.externalSessionId,f=g,n.info("Resolved parent session for notification",{cliOriginSession:d.cliOrigin.sessionId,parentSession:f}))}catch(e){n.warn("Failed to resolve session parent, using cliOrigin directly",{error:(0,r.getErrorMessage)(e)})}if(!f)try{const e=await i.get(`/api/projects/${a}/session-request-associations/by-request/${o}?activeOnly=true`)
1000
1331
  if(e.success&&Array.isArray(e.data)&&e.data.length>0){const t=e.data[0].opencodeSessionId
1001
1332
  try{const e=await i.get(`/api/projects/${a}/cli-sessions/resolve/${t}`)
1002
- e.data?.parent?.externalSessionId?(m=e.data.parent.externalSessionId,g=m):m=t}catch{m=t}}}catch{}if(m)try{await i.post(`/api/projects/${a}/session-messages/reports`,{fromSessionId:"gui-approval",toSessionId:m,status:"success",summary:`Request ${o} "${d.title}" has been approved. Please begin implementation.`,details:h}),p=!0,n.info("Stored session report for target session",{requestId:o,sessionId:m})
1003
- try{const e=(0,r.getOpenCodeServerUrl)(c,n)
1004
- if(e){const s=`\u2705 [Request Approved]\n\nRequest ${o} "${d.title}" has been approved.\nPlease begin implementation.\n\nDetails:\n${h}`,r=`${e}/session/${m}/prompt_async`,i=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:s}],...t.cachedDefaultModel?(()=>{const[e,...s]=t.cachedDefaultModel.split("/")
1005
- return{model:{providerID:e,modelID:s.join("/")}}})():{}})})
1006
- i.ok?n.info("Pushed notification to CLI session",{sessionId:m}):n.warn("Failed to push notification to CLI session",{status:i.status})}}catch(e){n.warn("Could not push notification to CLI session (may be offline)",{error:(0,s.getErrorMessage)(e)})}}catch(e){n.warn("Failed to notify target session, spawning work agent instead",{requestId:o,error:(0,s.getErrorMessage)(e)})}if(!p)try{const e=await(0,r.spawnActionAgent)({agent:"work",prompt:h,cwd:c,projectPath:c,projectId:a,title:`[${o}] ${d.title}`,requestId:o,parentSessionId:g,apiClient:i,logger:n,trackChildPid:l})
1007
- f=e.success,f?n.info("Spawned work agent for request",{requestId:o,sessionId:e.sessionId}):n.warn("Failed to spawn work agent",{requestId:o,error:e.error})}catch(e){n.warn("Failed to spawn work agent",{requestId:o,error:(0,s.getErrorMessage)(e)})}return n.info("Approve-and-work-request action completed",{requestId:o,notifiedSession:p,agentSpawned:f}),{success:!0,message:`Successfully approved request ${o}: ${d.title}${p?" \u2014 creating session notified":f?" \u2014 work agent spawned":""}`,data:{operationType:"approve-and-work",previousStatus:"pending_approval",newStatus:"open",notifiedSession:p,agentSpawned:f}}}catch(e){const t=(0,s.getErrorMessage)(e)
1333
+ e.data?.parent?.externalSessionId?(f=e.data.parent.externalSessionId,g=f):f=t}catch{f=t}}}catch{}if(f)try{await i.post(`/api/projects/${a}/session-messages/reports`,{fromSessionId:"gui-approval",toSessionId:f,status:"success",summary:`Request ${o} "${d.title}" has been approved. Please begin implementation.`,details:h}),p=!0,n.info("Stored session report for target session",{requestId:o,sessionId:f})
1334
+ try{const e=(0,s.getOpenCodeServerUrl)(c,n)
1335
+ if(e){const r=`\u2705 [Request Approved]\n\nRequest ${o} "${d.title}" has been approved.\nPlease begin implementation.\n\nDetails:\n${h}`,s=`${e}/session/${f}/prompt_async`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({parts:[{type:"text",text:r}],...t.cachedDefaultModel?(()=>{const[e,...r]=t.cachedDefaultModel.split("/")
1336
+ return{model:{providerID:e,modelID:r.join("/")}}})():{}})})
1337
+ i.ok?n.info("Pushed notification to CLI session",{sessionId:f}):n.warn("Failed to push notification to CLI session",{status:i.status})}}catch(e){n.warn("Could not push notification to CLI session (may be offline)",{error:(0,r.getErrorMessage)(e)})}}catch(e){n.warn("Failed to notify target session, spawning work agent instead",{requestId:o,error:(0,r.getErrorMessage)(e)})}if(!p)try{const e=await(0,s.spawnActionAgent)({agent:"work",prompt:h,cwd:c,projectPath:c,projectId:a,title:`[${o}] ${d.title}`,requestId:o,parentSessionId:g,apiClient:i,logger:n,trackChildPid:l})
1338
+ m=e.success,m?n.info("Spawned work agent for request",{requestId:o,sessionId:e.sessionId}):n.warn("Failed to spawn work agent",{requestId:o,error:e.error})}catch(e){n.warn("Failed to spawn work agent",{requestId:o,error:(0,r.getErrorMessage)(e)})}return n.info("Approve-and-work-request action completed",{requestId:o,notifiedSession:p,agentSpawned:m}),{success:!0,message:`Successfully approved request ${o}: ${d.title}${p?" \u2014 creating session notified":m?" \u2014 work agent spawned":""}`,data:{operationType:"approve-and-work",previousStatus:"pending_approval",newStatus:"open",notifiedSession:p,agentSpawned:m}}}catch(e){const t=(0,r.getErrorMessage)(e)
1008
1339
  return n.error("approve-and-work-request action failed",{requestId:o,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
1009
- const s=t(8824),r=t(8148)},7960(e,exports){"use strict"
1340
+ const r=t(8824),s=t(8148)},7904(e,exports){"use strict"
1341
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.REPORT_STATUS_EMOJI=void 0,exports.formatReportNotification=function(e){const{messageCode:t,status:r,summary:s,details:o,fromSessionId:n,senderAgent:i}=e,a=[`${exports.REPORT_STATUS_EMOJI[r]||"\u{1f4cb}"} **Report \u2014 ${r}**`]
1342
+ return t&&a.push("**Report Code:** "+t),i&&a.push(n?`from: ${i} (${n})`:"from: "+i),a.push("",s||"Task completed"),o&&a.push("",o),t&&a.push("",`Use \`session_message_operations\` with \`reply\` operation and message code \`${t}\` to acknowledge.`),a.join("\n")},exports.REPORT_STATUS_EMOJI={success:"\u2705",partial:"\u26a0\ufe0f",failure:"\u274c",blocked:"\u{1f6ab}"}},7960(e,exports){"use strict"
1010
1343
  function t(){const e=process.argv[1]||""
1011
1344
  if(e.includes("/dist-dev/")||e.includes("\\dist-dev\\"))return!0
1012
1345
  if(e.includes("/dist/")||e.includes("\\dist\\"))return!1
1013
1346
  const t=process.cwd()
1014
1347
  return!(!t.includes("/dist-dev")&&!t.includes("\\dist-dev"))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.isDevBuild=t,exports.getBuildMode=function(){return t()?"development":"production"},exports.enableDebugMode=function(){return"true"===process.env.ENABLE_DEBUG_TOOLS&&(process.env.DEBUG_LOGGING="true",!0)}},8035(e,exports,t){"use strict"
1015
- function s(e,t,s){try{const r=`[${(new Date).toISOString()}] [detached-subagent] ${t}${s?" "+JSON.stringify(s):""}\n`,o=(0,i.join)(e||process.cwd(),".alchemy_lab","spawn-debug.log");(0,n.appendFileSync)(o,r)}catch{}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnDetachedSubagent=function(e){const{agent:t,prompt:c,cwd:l,sessionId:d,model:u,initialBranch:h,serverUrl:g,onSpawn:p}=e
1016
- try{const o={...process.env},f=l||process.cwd()
1017
- o.OPENCODE_DATA_DIR=(0,i.join)(f,".alchemy_lab","opencode"),h&&(o.ALCHEMY_INITIAL_BRANCH=h),e.allowParallel&&(o.ALCHEMY_ALLOW_PARALLEL="1")
1018
- const m=new Date,y=`${(m.getHours()+"").padStart(2,"0")}${(m.getMinutes()+"").padStart(2,"0")}${(m.getSeconds()+"").padStart(2,"0")}`,v=Math.random().toString(36).substring(2,7)
1019
- let w
1020
- if(o.ALCHEMY_SESSION_TAG=`mcp-${y}-${v}`,s(l,"Spawn env setup",{agent:t,initialBranch:h||"none",sessionId:d||"new",attachMode:!!g}),w=g&&d&&c?["run","--attach",g,"--session",d,"--agent",t,c]:d&&c?["run","--session",d,"--agent",t,c]:c?["run","--agent",t,c]:["run","--agent",t],u){const e=w.findIndex((e=>e===c))
1021
- e>0?w.splice(e,0,"--model",u):w.push("--model",u)}s(l,"Spawning opencode",{args:w.map((e=>e===c?`[prompt:${e.length}chars]`:e)),model:u||"not specified",promptLength:c?.length,promptPreview:c?.slice(0,100),cwd:l,hasSessionId:!!d})
1022
- const I=(0,i.join)(l||process.cwd(),".alchemy_lab"),k=(0,i.join)(I,"spawn-stderr.log")
1023
- let S
1024
- try{S=(0,n.openSync)(k,"a")}catch{}const C=(0,a.resolveRunCommand)(),j=(0,r.spawn)(C.command,[...C.prependArgs,...w],{detached:!0,stdio:["ignore","ignore",S??"ignore"],cwd:l||process.cwd(),env:o})
1025
- return j.unref(),p&&p(j.pid),{success:!0,pid:j.pid}}catch(e){return{success:!1,error:(0,o.getErrorMessage)(e)}}}
1026
- const r=t(5317),o=t(8824),n=t(9896),i=t(6928),a=t(9068)},8148(e,exports,t){"use strict"
1027
- function s(e,t){try{const t=(0,o.join)(e,c,"opencode.json"),s=(0,r.readFileSync)(t,"utf8"),n=JSON.parse(s)
1028
- if(n.port)return"http://127.0.0.1:"+n.port}catch(e){t.debug("Could not read opencode.json for server URL",{error:(0,i.getErrorMessage)(e)})}return null}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeServerUrl=s,exports.spawnActionAgent=async function(e){const{agent:t,prompt:r,cwd:o,projectPath:c,title:l,requestId:d,parentSessionId:u,allowParallel:h,apiClient:g,logger:p,trackChildPid:f}=e
1029
- let m,y,v,w=null
1030
- try{const s=new a.SessionSpawnService({apiClient:g,logger:p,projectId:e.projectId,getDefaultModel:e.getDefaultModel||(()=>{})}),o=await s.prepareSession({agent:t,prompt:r,projectPath:c,requestId:d,parentSessionId:u,title:l,allowParallel:h})
1031
- w=o.sessionId,m=o.model,y=o.serverUrl,v=o.initialBranch}catch(e){p.warn("Session preparation failed \u2014 spawning without pre-created session",{error:(0,i.getErrorMessage)(e)}),y=s(c,p)||void 0}const I=(0,n.spawnDetachedSubagent)({agent:t,prompt:r,cwd:o,sessionId:w||void 0,serverUrl:y,model:m,initialBranch:v,allowParallel:h??!1,onSpawn(e){e&&f&&f(e)}})
1032
- return I.success?(p.info("Spawned action agent",{agent:t,sessionId:w,pid:I.pid,requestId:d}),{success:!0,sessionId:w,pid:I.pid}):{success:!1,sessionId:w,error:I.error||"Spawn failed"}}
1033
- const r=t(9896),o=t(6928),n=t(8035),i=t(8824),a=t(1292),c=".alchemy_lab"},8524(e,exports,t){"use strict"
1034
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1348
+ function r(e,t,r){try{const s=`[${(new Date).toISOString()}] [detached-subagent] ${t}${r?" "+JSON.stringify(r):""}\n`,o=(0,i.join)(e||process.cwd(),".alchemy_lab","spawn-debug.log");(0,n.appendFileSync)(o,s)}catch{}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.spawnDetachedSubagent=function(e){const{agent:t,prompt:d,cwd:u,sessionId:h,model:g,initialBranch:p,serverUrl:m,onSpawn:f}=e
1349
+ try{const e=u||process.cwd(),o={...process.env,...(0,c.getOpencodeSpawnEnv)(e,(0,l.isDevMode)())}
1350
+ let y
1351
+ if(p&&(o.ALCHEMY_INITIAL_BRANCH=p),r(u,"Spawn env setup",{agent:t,initialBranch:p||"none",sessionId:h||"new",attachMode:!!m}),y=m&&h&&d?["run","--attach",m,"--session",h,"--agent",t,d]:h&&d?["run","--session",h,"--agent",t,d]:d?["run","--agent",t,d]:["run","--agent",t],g){const e=y.findIndex((e=>e===d))
1352
+ e>0?y.splice(e,0,"--model",g):y.push("--model",g)}r(u,"Spawning opencode",{args:y.map((e=>e===d?`[prompt:${e.length}chars]`:e)),model:g||"not specified",promptLength:d?.length,promptPreview:d?.slice(0,100),cwd:u,hasSessionId:!!h})
1353
+ const v=(0,i.join)(u||process.cwd(),".alchemy_lab"),w=(0,i.join)(v,"spawn-stderr.log")
1354
+ let b
1355
+ try{b=(0,n.openSync)(w,"a")}catch{}const I=(0,a.resolveRunCommand)(),E=(0,s.spawn)(I.command,[...I.prependArgs,...y],{detached:!0,stdio:["ignore","ignore",b??"ignore"],cwd:u||process.cwd(),env:o})
1356
+ return E.unref(),f&&f(E.pid),{success:!0,pid:E.pid}}catch(e){return{success:!1,error:(0,o.getErrorMessage)(e)}}}
1357
+ const s=t(5317),o=t(8824),n=t(9896),i=t(6928),a=t(9068),c=t(5419),l=t(3962)},8148(e,exports,t){"use strict"
1358
+ function r(e){return(0,n.getOpenCodeUrlFromConfig)(e)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getOpenCodeServerUrl=r,exports.spawnActionAgent=async function(e){const{agent:t,prompt:n,cwd:a,projectPath:c,title:l,requestId:d,parentSessionId:u,allowParallel:h,apiClient:g,logger:p,trackChildPid:m}=e
1359
+ let f,y,v,w=null
1360
+ try{const r=new i.SessionSpawnService({apiClient:g,logger:p,projectId:e.projectId,getDefaultModel:e.getDefaultModel||(()=>{})}),s=await r.prepareSession({agent:t,prompt:n,projectPath:c,requestId:d,parentSessionId:u,title:l,allowParallel:h})
1361
+ w=s.sessionId,f=s.model,y=s.serverUrl,v=s.initialBranch}catch(e){p.warn("Session preparation failed \u2014 spawning without pre-created session",{error:(0,o.getErrorMessage)(e)}),y=r(c)||void 0}const b=(0,s.spawnDetachedSubagent)({agent:t,prompt:n,cwd:a,sessionId:w||void 0,serverUrl:y,model:f,initialBranch:v,allowParallel:h??!1,onSpawn(e){e&&m&&m(e)}})
1362
+ return b.success?(p.info("Spawned action agent",{agent:t,sessionId:w,pid:b.pid,requestId:d}),{success:!0,sessionId:w,pid:b.pid}):{success:!1,sessionId:w,error:b.error||"Spawn failed"}}
1363
+ const s=t(8035),o=t(8824),n=t(833),i=t(1292)},8463(e,exports,t){"use strict"
1364
+ function r(e){return(0,o.join)(e,c,l)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getSetupWorktreesBase=r,exports.sweepStaleSetupWorktrees=async function(e,t){const i=r(e)
1365
+ if(!(0,s.existsSync)(i))return
1366
+ const c=(0,n.simpleGit)(e)
1367
+ let l=[]
1368
+ try{l=(0,s.readdirSync)(i)}catch(e){return void t.warn("Setup-worktrees sweep: cannot read base dir",{error:(0,a.getErrorMessage)(e)})}for(const e of l){const r=(0,o.join)(i,e)
1369
+ try{await c.raw(["worktree","remove","--force",r])}catch{}try{(0,s.rmSync)(r,{recursive:!0,force:!0})}catch(e){t.debug("Setup-worktrees sweep: rm failed (best-effort)",{worktreePath:r,error:(0,a.getErrorMessage)(e)})}}try{await c.raw(["worktree","prune"])}catch(e){t.debug("Setup-worktrees sweep: prune failed (best-effort)",{error:(0,a.getErrorMessage)(e)})}},exports.generateSetupId=function(){return(0,i.randomBytes)(6).toString("hex")},exports.resolveSetupBranch=async function(e,t,r,s,o,i){if(r)return r
1370
+ if(s)try{const r=await e.get(`/api/projects/${t}/requests/${s}`)
1371
+ if(r?.success&&r?.data?.branchName)return r.data.branchName
1372
+ if(r?.success&&r?.data)return"requests/"+s}catch(e){o.debug("resolveSetupBranch: request lookup failed, falling back to repo HEAD",{requestId:s,error:(0,a.getErrorMessage)(e)})}if(i)try{const e=(0,n.simpleGit)(i),t=(await e.revparse(["--abbrev-ref","HEAD"])).trim()
1373
+ if(t&&"HEAD"!==t)return t}catch(e){o.debug("resolveSetupBranch: revparse HEAD failed, falling back to main",{error:(0,a.getErrorMessage)(e)})}return"main"},exports.createSetupWorktree=async function(e,t,o,i){(0,s.mkdirSync)(r(e),{recursive:!0})
1374
+ const a=(0,n.simpleGit)(e)
1375
+ await a.raw(["worktree","add","--detach",t,o]),i.info("Created setup worktree",{worktreePath:t,branch:o})},exports.buildSetupPrompt=function(e,t){const r=["You are configuring the first test process for this project.","","Project path: "+e]
1376
+ return t&&r.push("Triggered from request: "+t),r.push(""),r.push("1. Inspect manifests (package.json, pyproject.toml, Cargo.toml, go.mod, Makefile, CI configs) to identify build/test/lint commands."),r.push("2. Use `testing_reference` for per-language patterns."),r.push("3. Propose a draft to the user (name, setup commands, test commands with timeouts) as a single message."),r.push("4. Iterate until the user confirms."),r.push("5. Call `testing_process_create` with the agreed configuration."),r.push("6. Send a brief report back to your parent via `session_message_operations` (type: report) summarizing what you registered, then end your session."),r.push(""),r.push("You are read-only with respect to project files. Do not modify source code or run tests."),r.join("\n")},exports.buildCommandSetupPrompt=function(e,t,r){const s=["You are configuring fast project commands (typecheck, lint, build, format, dev server) for this project.","","Project path: "+e]
1377
+ return t&&s.push("Triggered from request: "+t),r&&r.trim()&&(s.push(""),s.push("Caller hint: "+r.trim()),s.push("Treat the caller hint as guidance about what the user likely wants \u2014 still do your full discovery and confirm with the user before registering. Do not blindly register only what was hinted.")),s.push(""),s.push('1. Call `commands({ operation: "list" })` to see what is already registered.'),s.push("2. Inspect manifests at any depth (package.json, pyproject.toml, Cargo.toml, go.mod, Makefile, deno.json, CI configs)."),s.push("3. For monorepos: ask the user which contexts they want commands for. Propose one command per (context x kind) with the right cwd."),s.push("4. Propose a draft to the user (name, command, description, cwd, timeout) in a single message."),s.push("5. Iterate until the user confirms."),s.push('6. Call `commands({ operation: "register", commands: [...] })` to submit the agreed batch \u2014 each item creates a per-command approval notification the user must click through.'),s.push("7. Send a brief report back to your parent via `session_message_operations` (type: report) summarizing what you registered, then end your session."),s.push(""),s.push("You are read-only with respect to project files. Do not modify source code or run the commands."),s.join("\n")}
1378
+ const s=t(9896),o=t(6928),n=t(3807),i=t(6982),a=t(8824),c=".alchemy_lab",l="setup-worktrees"},8524(e,exports,t){"use strict"
1379
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1035
1380
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.CLIBridgeWebSocket=void 0,exports.setCLIBridge=function(e){n=e},exports.getCLIBridge=function(){return n}
1036
- const r=t(5086),o=s(t(6982))
1037
- exports.CLIBridgeWebSocket=class{constructor(e,t=3e4){this.wss=null,this.uiClient=null,this.pendingCommands=new Map,this.heartbeatInterval=null,this.logger=e,this.defaultTimeout=t}attach(e){this.wss=new r.WebSocketServer({server:e,path:"/ws/cli-bridge"}),this.wss.on("connection",(e=>{this.logger.info("[CLIBridge] UI client connected"),this.uiClient&&(this.logger.warn("[CLIBridge] Replacing existing UI client connection"),this.uiClient.close(1e3,"Replaced by new connection")),this.uiClient=e,this.startHeartbeat(),e.on("message",(e=>{try{const t=JSON.parse(e.toString())
1381
+ const s=t(5086),o=r(t(6982))
1382
+ exports.CLIBridgeWebSocket=class{constructor(e,t=3e4){this.wss=null,this.uiClient=null,this.pendingCommands=new Map,this.heartbeatInterval=null,this.logger=e,this.defaultTimeout=t}attach(e){this.wss=new s.WebSocketServer({server:e,path:"/ws/cli-bridge"}),this.wss.on("connection",(e=>{this.logger.info("[CLIBridge] UI client connected"),this.uiClient&&(this.logger.warn("[CLIBridge] Replacing existing UI client connection"),this.uiClient.close(1e3,"Replaced by new connection")),this.uiClient=e,this.startHeartbeat(),e.on("message",(e=>{try{const t=JSON.parse(e.toString())
1038
1383
  this.handleResponse(t)}catch(e){this.logger.error("[CLIBridge] Failed to parse message from UI",{error:e instanceof Error?e.message:e+""})}})),e.on("close",(()=>{this.logger.info("[CLIBridge] UI client disconnected"),this.uiClient===e&&(this.uiClient=null),this.stopHeartbeat()
1039
- for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.resolve({id:e,success:!1,error:"UI client disconnected during command. The command may have been partially executed."}),this.pendingCommands.delete(e)})),e.on("error",(e=>{this.logger.error("[CLIBridge] WebSocket error",{error:e.message})}))})),this.logger.info("[CLIBridge] WebSocket server attached at /ws/cli-bridge")}isUIConnected(){return null!==this.uiClient&&this.uiClient.readyState===r.WebSocket.OPEN}async sendCommand(e,t={},s){if(!this.isUIConnected())return{id:"",success:!1,error:"alchemy-ui is not connected. Please open alchemy-ui in your browser first."}
1040
- const r=o.default.randomUUID(),n={id:r,action:e,params:t}
1041
- return new Promise(((t,o)=>{const i=setTimeout((()=>{this.pendingCommands.delete(r),t({id:r,success:!1,error:`Command '${e}' timed out after ${(s||this.defaultTimeout)/1e3}s`})}),s||this.defaultTimeout)
1042
- this.pendingCommands.set(r,{resolve:t,reject:o,timer:i})
1043
- try{this.uiClient.send(JSON.stringify(n))}catch(e){clearTimeout(i),this.pendingCommands.delete(r),t({id:r,success:!1,error:"Failed to send command: "+(e instanceof Error?e.message:e+"")})}}))}handleResponse(e){const t=this.pendingCommands.get(e.id)
1044
- t?(clearTimeout(t.timer),this.pendingCommands.delete(e.id),t.resolve(e)):this.logger.warn("[CLIBridge] Received response for unknown command",{id:e.id})}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval((()=>{this.uiClient&&this.uiClient.readyState===r.WebSocket.OPEN&&this.uiClient.ping()}),3e4)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null)}close(){this.stopHeartbeat()
1384
+ for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.resolve({id:e,success:!1,error:"UI client disconnected during command. The command may have been partially executed."}),this.pendingCommands.delete(e)})),e.on("error",(e=>{this.logger.error("[CLIBridge] WebSocket error",{error:e.message})}))})),this.logger.info("[CLIBridge] WebSocket server attached at /ws/cli-bridge")}isUIConnected(){return null!==this.uiClient&&this.uiClient.readyState===s.WebSocket.OPEN}async sendCommand(e,t={},r){if(!this.isUIConnected())return{id:"",success:!1,error:"alchemy-ui is not connected. Please open alchemy-ui in your browser first."}
1385
+ const s=o.default.randomUUID(),n={id:s,action:e,params:t}
1386
+ return new Promise(((t,o)=>{const i=setTimeout((()=>{this.pendingCommands.delete(s),t({id:s,success:!1,error:`Command '${e}' timed out after ${(r||this.defaultTimeout)/1e3}s`})}),r||this.defaultTimeout)
1387
+ this.pendingCommands.set(s,{resolve:t,reject:o,timer:i})
1388
+ try{this.uiClient.send(JSON.stringify(n))}catch(e){clearTimeout(i),this.pendingCommands.delete(s),t({id:s,success:!1,error:"Failed to send command: "+(e instanceof Error?e.message:e+"")})}}))}handleResponse(e){const t=this.pendingCommands.get(e.id)
1389
+ t?(clearTimeout(t.timer),this.pendingCommands.delete(e.id),t.resolve(e)):this.logger.warn("[CLIBridge] Received response for unknown command",{id:e.id})}startHeartbeat(){this.stopHeartbeat(),this.heartbeatInterval=setInterval((()=>{this.uiClient&&this.uiClient.readyState===s.WebSocket.OPEN&&this.uiClient.ping()}),3e4)}stopHeartbeat(){this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null)}close(){this.stopHeartbeat()
1045
1390
  for(const[e,t]of this.pendingCommands)clearTimeout(t.timer),t.reject(Error("Bridge shutting down")),this.pendingCommands.delete(e)
1046
1391
  this.uiClient&&(this.uiClient.close(1e3,"Server shutting down"),this.uiClient=null),this.wss&&(this.wss.close(),this.wss=null)}}
1047
1392
  let n=null},8577(module){"use strict"
1048
- module.exports=require("cors")},8740(e,exports){"use strict"
1049
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.HookCache=void 0,exports.HookCache=class{constructor(e,t,s){this.cache=new Map,this.logger=e,this.projectId=t,this.apiClient=s}setProjectId(e){this.projectId=e}getCacheKey(e,t){return t?`${e}:session:${t}`:e+":all"}async getHooksForEvent(e,t,s){const r=this.getCacheKey(e,t)
1050
- if(!s){const e=this.cache.get(r)
1051
- if(e&&6e4>Date.now()-e.time)return e.hooks.filter((e=>e.enabled))}try{const s=t?"?sessionId="+encodeURIComponent(t):"",o=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/by-event/${e}${s}`),n=(o?.data||o||[]).map((e=>({id:e.id||e.hookId,name:e.name||e.hookName,eventType:e.eventType||e.hookEventType,agent:e.agent||e.hookAgent,promptTemplate:e.promptTemplate||e.hookPromptTemplate,condition:e.condition||e.hookCondition||{},enabled:!1!==e.enabled,action:e.action||e.hookAction||void 0,target:e.target||e.hookTarget||void 0,timing:e.timing||e.hookTiming||"after",maxIterations:e.maxIterations||e.hookMaxIterations||void 0,emitEvent:e.emitEvent||e.hookEmitEvent||void 0,emitEventOnCondition:e.emitEventOnCondition||e.hookEmitEventOnCondition||void 0,intervalSeconds:e.intervalSeconds||e.hookIntervalSeconds||void 0,timerCondition:e.timerCondition||e.hookTimerCondition||void 0,timerExpiresAfter:e.timerExpiresAfter||e.hookTimerExpiresAfter||void 0,stopAfterInactiveMinutes:e.stopAfterInactiveMinutes??e.hookStopAfterInactiveMinutes??10})))
1052
- return this.cache.set(r,{hooks:n,time:Date.now()}),n.filter((e=>e.enabled))}catch(t){return this.logger.warn("Failed to fetch hooks",{eventType:e,error:t+""}),[]}}preWarm(e,t){this.getHooksForEvent(e,t).catch((()=>{}))}clear(){this.cache.clear()}}},8824(e,exports){"use strict"
1053
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.getErrorMessage=function(e){return e instanceof Error?e.message:"string"==typeof e?e:null===e?"null error":void 0===e?"undefined error":e+""},exports.getErrorStack=function(e){return e instanceof Error?e.stack||e.message:e+""},exports.formatErrorForLog=function(e){return e instanceof Error?{message:e.message,stack:e.stack}:{message:e+""}},exports.isError=function(e){return e instanceof Error}},8955(e,exports){"use strict"
1054
- function t(){return n||r}function s(){return t().map((e=>e.name))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.initializeAgentRegistry=async function(e,t){return a||(a=(async()=>{try{if(!e)return t.debug("No API client available, using default agents"),n=r,void(i=!0)
1393
+ module.exports=require("cors")},8625(e,exports,t){"use strict"
1394
+ function r(e,t,r){const s=exports.ERROR_TYPE_PROPERTIES[e]||exports.ERROR_TYPE_PROPERTIES[i.INTERNAL_ERROR]
1395
+ return{success:!1,error:{code:e,message:t||s.defaultMessage,details:r,retryable:s.retryable,statusCode:s.statusCode}}}function s(e){return"object"==typeof e&&null!==e&&"code"in e&&"statusCode"in e&&"details"in e&&"retryable"in e&&"string"==typeof e.code&&"number"==typeof e.statusCode}function o(e){if(s(e)){const t=r(e.code,e.message,e.details)
1396
+ return{statusCode:e.statusCode,body:t}}if(e instanceof Error&&"ConnectionError"===e.name){const t=e
1397
+ return{statusCode:503,body:r("CONNECTION_ERROR",t.message||"Connection to remote server failed",{operation:t.operation||"unknown",lastConnectionAttempt:t.lastConnectionAttempt||null,suggestions:["Check your network connection","Verify that the remote server is running","Try again in a few moments"],isConnectionIssue:!0})}}const t=e instanceof Error?e:Error(e+""),o=r(i.INTERNAL_ERROR,t.message||"An unexpected error occurred")
1398
+ return{statusCode:o.error.statusCode,body:o}}function n(e){return!s(e)||e.isRetryable()}Object.defineProperty(exports,"__esModule",{value:!0}),exports.UnauthorizedError=exports.McpProtocolError=exports.FileOperationError=exports.ServiceError=exports.DatabaseError=exports.AuthorizationError=exports.AuthenticationError=exports.ValidationError=exports.NotFoundError=exports.AppError=exports.ErrorUtils=exports.ERROR_TYPE_PROPERTIES=void 0,exports.createErrorResponse=r,exports.createAppError=function(e,t="INTERNAL_ERROR",r=500,s){return new a(e,t,r,s)},exports.createNotFoundError=function(e="Resource not found",t){return new c(e,t)},exports.createValidationError=function(e="Invalid input",t){return new l(e,t)},exports.createAuthenticationError=function(e="Authentication required",t){return new d(e,t)},exports.createAuthorizationError=function(e="Not authorized",t){return new u(e,t)},exports.createDatabaseError=function(e="Database error",t){return new h(e,t)},exports.createServiceError=function(e="Service unavailable",t){return new g(e,t)},exports.createFileOperationError=function(e="File operation error",t){return new p(e,t)},exports.createMcpProtocolError=function(e="MCP protocol error",t){return new m(e,t)},exports.isAppError=s,exports.errorToResponse=o,exports.isRetryableError=n
1399
+ const i=t(7388)
1400
+ exports.ERROR_TYPE_PROPERTIES={[i.TASK_NOT_FOUND]:{statusCode:404,retryable:!1,defaultMessage:"Task ID does not exist"},[i.TASK_ID_INVALID_FORMAT]:{statusCode:400,retryable:!1,defaultMessage:"Task ID format is invalid"},[i.TASK_VALIDATION_FAILED]:{statusCode:400,retryable:!1,defaultMessage:"Task validation failed"},[i.NETWORK_ERROR]:{statusCode:503,retryable:!0,defaultMessage:"Network error occurred"},[i.SERVER_ERROR]:{statusCode:500,retryable:!0,defaultMessage:"Server error occurred"},[i.CONNECTION_TIMEOUT]:{statusCode:504,retryable:!0,defaultMessage:"Connection timed out"},[i.SERVICE_UNAVAILABLE]:{statusCode:503,retryable:!0,defaultMessage:"Service is currently unavailable"},[i.CONNECTION_ERROR]:{statusCode:503,retryable:!0,defaultMessage:"Connection to remote server failed"},[i.REQUEST_NOT_FOUND]:{statusCode:404,retryable:!1,defaultMessage:"Request ID does not exist"},[i.REQUEST_ID_INVALID_FORMAT]:{statusCode:400,retryable:!1,defaultMessage:"Request ID format is invalid"},[i.REQUEST_VALIDATION_FAILED]:{statusCode:400,retryable:!1,defaultMessage:"Request validation failed"},[i.VALIDATION_ERROR]:{statusCode:400,retryable:!1,defaultMessage:"Validation error"},[i.UNAUTHORIZED]:{statusCode:401,retryable:!1,defaultMessage:"Authentication required"},[i.FORBIDDEN]:{statusCode:403,retryable:!1,defaultMessage:"Access forbidden"},[i.NOT_FOUND]:{statusCode:404,retryable:!1,defaultMessage:"Resource not found"},[i.INTERNAL_ERROR]:{statusCode:500,retryable:!0,defaultMessage:"Internal server error"},[i.BAD_REQUEST]:{statusCode:400,retryable:!1,defaultMessage:"Bad request"},[i.MCP_PROTOCOL_ERROR]:{statusCode:500,retryable:!0,defaultMessage:"MCP protocol error"}},exports.ErrorUtils={isAppError:s,errorToResponse:o,isRetryableError:n,createErrorResponse:r}
1401
+ class a extends Error{constructor(e,t="INTERNAL_ERROR",r=500,s){super(e),this.name=this.constructor.name,this.code=t,this.statusCode=r,this.details=s||{},s&&"code"in s&&"string"==typeof s.code&&(this.code=s.code)
1402
+ const o=exports.ERROR_TYPE_PROPERTIES[this.code]
1403
+ this.retryable=o?o.retryable:!s||!("retryable"in s)||!!s.retryable,Error.captureStackTrace(this,this.constructor)}toJSON(){return{name:this.name,message:this.message,code:this.code,statusCode:this.statusCode,details:this.details,retryable:this.retryable,stack:this.stack}}get metadata(){return this.details}isRetryable(){return this.retryable}}class c extends a{constructor(e="Resource not found",t){super(e,i.NOT_FOUND,404,t),this.resourceType="unknown",t&&("taskId"in t?(this.code=i.TASK_NOT_FOUND,this.resourceType="task"):"requestId"in t&&(this.code=i.REQUEST_NOT_FOUND,this.resourceType="request"))}}class l extends a{constructor(e="Invalid input",t){super(e,i.VALIDATION_ERROR,400,t)}}class d extends a{constructor(e="Authentication required",t){super(e,i.UNAUTHORIZED,401,t)}}class u extends a{constructor(e="Not authorized",t){super(e,i.FORBIDDEN,403,t)}}class h extends a{constructor(e="Database error",t){super(e,"DATABASE_ERROR",500,t)}}class g extends a{constructor(e="Service unavailable",t){if(super(e,i.SERVICE_UNAVAILABLE,503,t),t&&"structuredError"in t){const e=t.structuredError
1404
+ e&&e.error&&"retryable"in e.error&&(this.details.retryable=!!e.error.retryable)}}}class p extends a{constructor(e="File operation error",t){super(e,"FILE_OPERATION_ERROR",500,t)}}class m extends a{constructor(e="MCP protocol error",t){super(e,i.MCP_PROTOCOL_ERROR,500,t)}}exports.AppError=a,exports.NotFoundError=c,exports.ValidationError=l,exports.AuthenticationError=d,exports.AuthorizationError=u,exports.DatabaseError=h,exports.ServiceError=g,exports.FileOperationError=p,exports.McpProtocolError=m,exports.UnauthorizedError=d},8740(e,exports){"use strict"
1405
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.HookCache=void 0,exports.HookCache=class{constructor(e,t,r){this.cache=new Map,this.logger=e,this.projectId=t,this.apiClient=r}setProjectId(e){this.projectId=e}getCacheKey(e,t){return t?`${e}:session:${t}`:e+":all"}async getHooksForEvent(e,t,r){const s=this.getCacheKey(e,t)
1406
+ if(!r){const e=this.cache.get(s)
1407
+ if(e&&6e4>Date.now()-e.time)return e.hooks.filter((e=>e.enabled))}try{const r=t?"?sessionId="+encodeURIComponent(t):"",o=await this.apiClient.get(`/api/projects/${this.projectId}/event-hooks/by-event/${e}${r}`),n=(o?.data||o||[]).map((e=>({id:e.id||e.hookId,name:e.name||e.hookName,eventType:e.eventType||e.hookEventType,agent:e.agent||e.hookAgent,promptTemplate:e.promptTemplate||e.hookPromptTemplate,condition:e.condition||e.hookCondition||{},enabled:!1!==e.enabled,action:e.action||e.hookAction||void 0,target:e.target||e.hookTarget||void 0,timing:e.timing||e.hookTiming||"after",maxIterations:e.maxIterations||e.hookMaxIterations||void 0,emitEvent:e.emitEvent||e.hookEmitEvent||void 0,emitEventOnCondition:e.emitEventOnCondition||e.hookEmitEventOnCondition||void 0,intervalSeconds:e.intervalSeconds||e.hookIntervalSeconds||void 0,timerCondition:e.timerCondition||e.hookTimerCondition||void 0,timerExpiresAfter:e.timerExpiresAfter||e.hookTimerExpiresAfter||void 0,stopAfterInactiveMinutes:e.stopAfterInactiveMinutes??e.hookStopAfterInactiveMinutes??10})))
1408
+ return this.cache.set(s,{hooks:n,time:Date.now()}),n.filter((e=>e.enabled))}catch(t){return this.logger.warn("Failed to fetch hooks",{eventType:e,error:t+""}),[]}}preWarm(e,t){this.getHooksForEvent(e,t).catch((()=>{}))}clear(){this.cache.clear()}}},8824(e,exports){"use strict"
1409
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.getErrorMessage=function(e){return e instanceof Error?e.message:"string"==typeof e?e:null===e?"null error":void 0===e?"undefined error":e+""},exports.getErrorStack=function(e){return e instanceof Error?e.stack||e.message:e+""},exports.formatErrorForLog=function(e){return e instanceof Error?{message:e.message,stack:e.stack}:{message:e+""}},exports.isError=function(e){return e instanceof Error},exports.getStructuredErrorMessage=function(e){if(e instanceof Error)return e.message
1410
+ if("string"==typeof e)return e
1411
+ if(!e||"object"!=typeof e)return e+""
1412
+ const t=e
1413
+ if(t.message)return t.message
1414
+ if(t.error?.message)return t.error.message
1415
+ if("string"==typeof t.error)return t.error
1416
+ if(t.statusText&&t.status)return`HTTP ${t.status}: ${t.statusText}`
1417
+ if("string"==typeof t.body){try{const e=JSON.parse(t.body)
1418
+ if(e.error?.message)return e.error.message
1419
+ if(e.message)return e.message}catch{}return t.body.substring(0,200)}for(const e of Object.keys(t)){const r=t[e]
1420
+ if("string"==typeof r&&r.length>0)return`${e}: ${r}`}return"Unknown error (no readable properties)"}},8955(e,exports){"use strict"
1421
+ function t(){return n||s}function r(){return t().map((e=>e.name))}Object.defineProperty(exports,"__esModule",{value:!0}),exports.REQUEST_SCOPED_AGENTS=void 0,exports.initializeAgentRegistry=async function(e,t){return a||(a=(async()=>{try{if(!e)return t.debug("No API client available, using default agents"),n=s,void(i=!0)
1055
1422
  t.info("Fetching available agents from API")
1056
- const s=await e.proxyGet("/api/agents/templates")
1057
- s&&Array.isArray(s)&&s.length>0?(n=s.map((e=>({id:e.id||e.name,name:e.name,description:e.description,mode:e.mode,builtIn:e.builtIn}))),t.info("Agent registry initialized",{agentCount:n.length})):(t.warn("No agents returned from API, using defaults"),n=r),i=!0}catch(e){t.warn("Failed to fetch agents from API, using defaults",{error:e instanceof Error?e.message:e+""}),n=r,i=!0}})(),a)},exports.getAvailableAgents=t,exports.getAgentNames=s,exports.isValidAgent=function(e){return s().includes(e)},exports.getDefaultAgent=function(){return"work"},exports.formatAgentsForDescription=function(){return t().map((e=>`'${e.name}'${e.description?" - "+e.description:""}`)).join(", ")},exports.isAgentRegistryInitialized=function(){return i},exports.getBuiltInAgentConfig=function(e){const t=r.find((t=>t.name===e))
1058
- return t?{preferredProvider:t.preferredProvider||"inherit",preferredModelTier:t.preferredModelTier||"same",requestContextMode:t.requestContextMode}:null},exports.getAllowedSubagents=function(e){return o[e]||null},exports.resetAgentRegistry=function(){n=null,i=!1,a=null}
1059
- const r=[{id:"coder",name:"coder",description:"Development and implementation agent",builtIn:!0,preferredModelTier:"same",requestContextMode:"required"},{id:"research",name:"research",description:"Codebase research and exploration agent",builtIn:!0,preferredModelTier:"two_down",requestContextMode:"none"},{id:"journal",name:"journal",description:"Journal generation agent",builtIn:!0,preferredModelTier:"one_down"},{id:"planner",name:"planner",description:"Planning and architecture agent",builtIn:!0,preferredModelTier:"same"},{id:"specialist",name:"specialist",description:"Domain specialist for development tasks",builtIn:!0,preferredModelTier:"same"},{id:"auto-planner",name:"auto-planner",description:"Autonomous background planner",builtIn:!0,preferredModelTier:"same"},{id:"alchemist",name:"alchemist",description:"Persistent orchestrator",builtIn:!0,preferredModelTier:"same"}],o={alchemist:["specialist","research","planner"],specialist:["coder","research","auto-planner","planner"],tester:["research"],"auto-tester":["research"]}
1423
+ const r=await e.proxyGet("/api/agents/templates")
1424
+ r&&Array.isArray(r)&&r.length>0?(n=r.map((e=>({id:e.id||e.name,name:e.name,description:e.description,mode:e.mode,builtIn:e.builtIn}))),t.info("Agent registry initialized",{agentCount:n.length})):(t.warn("No agents returned from API, using defaults"),n=s),i=!0}catch(e){t.warn("Failed to fetch agents from API, using defaults",{error:e instanceof Error?e.message:e+""}),n=s,i=!0}})(),a)},exports.getAvailableAgents=t,exports.getAgentNames=r,exports.isValidAgent=function(e){return r().includes(e)},exports.getDefaultAgent=function(){return"work"},exports.formatAgentsForDescription=function(){return t().map((e=>`'${e.name}'${e.description?" - "+e.description:""}`)).join(", ")},exports.isAgentRegistryInitialized=function(){return i},exports.getBuiltInAgentConfig=function(e){const t=s.find((t=>t.name===e))
1425
+ return t?{preferredProvider:t.preferredProvider||"inherit",preferredModelTier:t.preferredModelTier||"same",requestContextMode:t.requestContextMode}:null},exports.getAllowedSubagents=function(e){return o[e]||null},exports.resetAgentRegistry=function(){n=null,i=!1,a=null},exports.REQUEST_SCOPED_AGENTS=["coder","tester","test-architect"]
1426
+ const s=[{id:"coder",name:"coder",description:"Development and implementation agent",builtIn:!0,preferredModelTier:"same",requestContextMode:"required"},{id:"research",name:"research",description:"Codebase research and exploration agent",builtIn:!0,preferredModelTier:"two_down",requestContextMode:"none"},{id:"journal",name:"journal",description:"Journal generation agent",builtIn:!0,preferredModelTier:"one_down"},{id:"planner",name:"planner",description:"Planning and architecture agent",builtIn:!0,preferredModelTier:"same"},{id:"specialist",name:"specialist",description:"Domain specialist for development tasks",builtIn:!0,preferredModelTier:"same"},{id:"auto-planner",name:"auto-planner",description:"Autonomous background planner",builtIn:!0,preferredModelTier:"same"},{id:"alchemist",name:"alchemist",description:"Persistent orchestrator",builtIn:!0,preferredModelTier:"same"}],o={alchemist:["specialist","research","planner"],specialist:["coder","research","auto-planner","planner"],tester:["research"],"auto-tester":["research"]}
1060
1427
  let n=null,i=!1,a=null},8984(e,exports,t){"use strict"
1061
1428
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.MCPClientConfigurer=void 0
1062
- const s=t(8824),r=t(5671)
1063
- class o{constructor(e,t,s){this.logger=e,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=s}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
1429
+ const r=t(8824),s=t(5671)
1430
+ class o{constructor(e,t,r){this.logger=e,this.remoteServerUrl=t||process.env.REMOTE_SERVER_URL,this.authToken=r}async seedAgentsFromRemote(e){const t=this.remoteServerUrl
1064
1431
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot initialize agents"),!1
1065
- try{const s=t+"/api/agents/seed"
1066
- this.logger.info("Ensuring built-in agents exist",{url:s})
1067
- const r={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"}
1068
- this.authToken&&(r.Authorization="Bearer "+this.authToken)
1069
- const o=await fetch(s,{method:"POST",headers:r})
1432
+ try{const r=t+"/api/agents/seed"
1433
+ this.logger.info("Ensuring built-in agents exist",{url:r})
1434
+ const s={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"}
1435
+ this.authToken&&(s.Authorization="Bearer "+this.authToken)
1436
+ const o=await fetch(r,{method:"POST",headers:s})
1070
1437
  o.ok||this.logger.warn("Failed to seed global agents",{status:o.status,statusText:o.statusText})
1071
1438
  const n=`${t}/api/projects/${e}/agents/initialize`
1072
1439
  this.logger.info("Initializing project agent activations",{url:n})
1073
- const i=await fetch(n,{method:"POST",headers:r})
1440
+ const i=await fetch(n,{method:"POST",headers:s})
1074
1441
  if(!i.ok)return this.logger.warn("Failed to initialize project agents",{status:i.status,statusText:i.statusText}),!1
1075
1442
  const a=await i.json()
1076
- 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,s.getErrorMessage)(e)}),!1}}async fetchAgentsFromRemote(e){const t=this.remoteServerUrl
1443
+ 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,r.getErrorMessage)(e)}),!1}}async fetchPluginManifest(){const e={"opencode-claude-auth":"1.5.3"},t=this.remoteServerUrl
1444
+ if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, using baseline plugin manifest"),{...e}
1445
+ const s=t+"/api/distribution/plugin-versions"
1446
+ try{const t={"Content-Type":"application/json","X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"}
1447
+ this.authToken&&(t.Authorization="Bearer "+this.authToken)
1448
+ const r=await fetch(s,{method:"GET",headers:t})
1449
+ if(!r.ok)return this.logger.warn("Plugin manifest fetch returned non-OK; using baseline",{status:r.status}),{...e}
1450
+ const o=await r.json(),n=o?.data?.plugins??o?.plugins??{},i={...e}
1451
+ for(const[e,t]of Object.entries(n)){const r=t?.version
1452
+ "string"==typeof r&&r.trim()&&(i[e]=r.trim())}return this.logger.info("Fetched plugin manifest",{manifest:i}),i}catch(t){return this.logger.warn("Plugin manifest fetch failed; using baseline",{error:(0,r.getErrorMessage)(t)}),{...e}}}async sweepLegacyAuthPluginFiles(e){const r=await Promise.resolve().then(t.t.bind(t,1943,23)),s=await Promise.resolve().then(t.t.bind(t,6928,23)),o=(await Promise.resolve().then(t.t.bind(t,857,23))).homedir(),n=new Set
1453
+ let i=e
1454
+ for(let e=0;6>e;e++){n.add(s.join(i,".opencode","plugin","anthropic-auth.js"))
1455
+ const e=s.dirname(i)
1456
+ if(e===i)break
1457
+ i=e}n.add(s.join(o,".opencode","plugin","anthropic-auth.js")),n.add(s.join(o,".config","opencode","plugin","anthropic-auth.js")),n.add(s.join(o,".alchemy_lab","opencode-dev","plugin","anthropic-auth.js"))
1458
+ const a=(new Date).toISOString().slice(0,10)
1459
+ await Promise.all(Array.from(n).map((async e=>{try{const t=await r.readFile(e,"utf-8").catch((()=>null))
1460
+ if(null===t)return
1461
+ if(!(t.includes("Alchemy Anthropic Auth Plugin")||t.includes("opencode-anthropic-auth@0.0.5")||t.includes("Transforming tool names to Title Case")))return void this.logger.debug("anthropic-auth.js signature did not match legacy plugin, leaving in place",{path:e})
1462
+ const s=`${e}.removed-by-alchemy-${a}`
1463
+ await r.rename(e,s),this.logger.warn("Removed legacy Alchemy Anthropic Auth Plugin file",{from:e,movedTo:s,reason:"shadows opencode-claude-auth and causes 400 'out of extra usage' responses from Anthropic"})}catch(t){this.logger.debug("sweepLegacyAuthPluginFiles candidate failed",{candidate:e,error:t instanceof Error?t.message:t+""})}})))}applyAuthPluginPin(e,t){const r="opencode-claude-auth@"+t,s=e.slice(),o=e.filter((e=>!(e=>"string"==typeof e&&/^opencode-claude-auth(@|$)/.test(e))(e)))
1464
+ if(e.length=0,e.push(r,...o),s.length!==e.length)return!0
1465
+ for(let t=0;t<s.length;t++)if(s[t]!==e[t])return!0
1466
+ return!1}async fetchAgentsFromRemote(e){const t=this.remoteServerUrl
1077
1467
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch agents from remote"),null
1078
- try{const s=`${t}/api/projects/${e}/agents/enabled`
1079
- this.logger.info("Fetching agents from remote",{url:s})
1080
- const r={"Content-Type":"application/json"}
1081
- this.authToken&&(r.Authorization="Bearer "+this.authToken)
1082
- const o=await fetch(s,{method:"GET",headers:r})
1468
+ try{const r=`${t}/api/projects/${e}/agents/enabled`
1469
+ this.logger.info("Fetching agents from remote",{url:r})
1470
+ const s={"Content-Type":"application/json"}
1471
+ this.authToken&&(s.Authorization="Bearer "+this.authToken)
1472
+ const o=await fetch(r,{method:"GET",headers:s})
1083
1473
  if(!o.ok)return this.logger.warn("Failed to fetch agents from remote",{status:o.status,statusText:o.statusText}),null
1084
1474
  const n=await o.json(),i=n.data||n
1085
- 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,s.getErrorMessage)(e)}),null}}async fetchProjectFromRemote(e){const t=this.remoteServerUrl
1475
+ 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,r.getErrorMessage)(e)}),null}}async fetchProjectFromRemote(e){const t=this.remoteServerUrl
1086
1476
  if(!t)return this.logger.debug("No REMOTE_SERVER_URL set, cannot fetch project from remote"),null
1087
- try{const s=await fetch(`${t}/api/projects/${e}`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:"Bearer "+this.authToken}:{}}})
1088
- if(!s.ok)return this.logger.warn("Failed to fetch project from remote",{projectId:e,status:s.status,statusText:s.statusText}),null
1089
- const r=await s.json()
1090
- return r.data||r}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,s.getErrorMessage)(t)}),null}}generateAgentMarkdown(e,t,s,n){let i=s
1091
- if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,r.resolveModelTier)(s,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0})
1092
- t.resolved&&(i=t.fullModel)}const a=Object.entries(e.tools||{}).map((([e,s])=>{const r=e.replace(/-/g,"_")
1093
- return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${r}`}: ${s}`})).join("\n")
1477
+ try{const r=await fetch(`${t}/api/projects/${e}`,{headers:{"Content-Type":"application/json",...this.authToken?{Authorization:"Bearer "+this.authToken}:{}}})
1478
+ if(!r.ok)return this.logger.warn("Failed to fetch project from remote",{projectId:e,status:r.status,statusText:r.statusText}),null
1479
+ const s=await r.json()
1480
+ return s.data||s}catch(t){return this.logger.error("Error fetching project from remote",{projectId:e,error:(0,r.getErrorMessage)(t)}),null}}generateAgentMarkdown(e,t,r,n){let i=r
1481
+ if(e.preferredModelTier&&"same"!==e.preferredModelTier){const t=(0,s.resolveModelTier)(r,e.preferredProvider||"inherit",e.preferredModelTier,{anthropic:!0,openai:!0})
1482
+ t.resolved&&(i=t.fullModel)}const a=Object.entries(e.tools||{}).map((([e,r])=>{const s=e.replace(/-/g,"_")
1483
+ return` ${o.OPENCODE_BUILTIN_TOOLS.has(e)?e:`${t}_${s}`}: ${r}`})).join("\n")
1094
1484
  let c=`---\ndescription: "${e.description||e.name}"\nmode: "${"utility"===e.mode?"primary":e.mode||"primary"}"\nmodel: "${i}"\n`
1095
1485
  if(e.color&&(c+=`color: "${e.color}"\n`),c+=`tools:\n${a}\n`,e.opencodePermission&&Object.keys(e.opencodePermission).length>0){c+="permission:\n"
1096
- for(const[t,s]of Object.entries(e.opencodePermission))if("object"==typeof s&&null!==s){c+=` ${t}:\n`
1097
- for(const[e,t]of Object.entries(s))c+=` ${e}: ${t}\n`}else c+=` ${t}: ${s}\n`}return void 0!==e.temperature&&(c+=`temperature: ${e.temperature}\n`),(e.name.endsWith("-thinking")||e.thinkingModeDefault)&&(c+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(c+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),c+="---\n\n",n&&(c+=n.trim()+"\n\n"),e.prompt&&(c+=e.prompt),e.promptAddition&&(c+="\n\n"+e.promptAddition),c}async configureOpenCode(e,r,n="node",i,a){try{const s=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=c.join(e,"mcp-direct.js")
1098
- this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:r})
1099
- const h=c.join(r,"opencode.json")
1100
- let g=null,p=null,f=null,m=null,y=!1,v=!1
1101
- const w=c.join(r,".alchemy_lab","config.json")
1102
- if(!i){try{const e=await s.readFile(w,"utf-8"),t=JSON.parse(e)
1103
- t?.openCodeTools?(g=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:g})):v=!0,t?.openCodeModel&&(p=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),v=!0}try{const e=await s.readFile(h,"utf-8"),t=JSON.parse(e)
1104
- t?.mode?.alchemy?.tools&&(f=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:f})),t?.model&&(m=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const I=i?.openCodeTools||g||f||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===i?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==a,todoread:!1,todowrite:!1},k={tools:I},S=i?.openCodeModel||p||m||"anthropic/claude-opus-4-5"
1105
- i?.openCodeModel?k.model=i.openCodeModel:p?k.model=p:m&&(k.model=m)
1106
- const C=c.join(r,".opencode","agent")
1107
- await s.mkdir(C,{recursive:!0})
1108
- const j=S||"anthropic/claude-opus-4-5-20251101"
1109
- let b,E=null,T=null
1110
- if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),T=await this.fetchProjectFromRemote(i.projectId),b=T?.metadata?.globalSystemPrompt,b&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:b.length}),E=await this.fetchAgentsFromRemote(i.projectId),E&&E.length>0){this.logger.info("Using agents from remote database",{count:E.length})
1111
- for(const e of E){if(!e.enabled)continue
1112
- const t=this.generateAgentMarkdown(e,d,j,b),r=c.join(C,e.name+".md")
1113
- await s.writeFile(r,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:r,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
1486
+ for(const[t,r]of Object.entries(e.opencodePermission))if("object"==typeof r&&null!==r){c+=` ${t}:\n`
1487
+ for(const[e,t]of Object.entries(r))c+=` ${e}: ${t}\n`}else c+=` ${t}: ${r}\n`}return void 0!==e.temperature&&(c+=`temperature: ${e.temperature}\n`),(e.name.endsWith("-thinking")||e.thinkingModeDefault)&&(c+="thinking:\n type: enabled\n budgetTokens: 10000\n"),"journal"===e.name&&(c+=`mcp:\n ${t}:\n env:\n ALCHEMY_MODE: "journal"\n`),c+="---\n\n",n&&(c+=n.trim()+"\n\n"),e.prompt&&(c+=e.prompt),e.promptAddition&&(c+="\n\n"+e.promptAddition),c}async configureOpenCode(e,s,n="node",i,a){try{const r=await Promise.resolve().then(t.t.bind(t,1943,23)),c=await Promise.resolve().then(t.t.bind(t,6928,23)),l=e.includes("dist-dev")||process.env.REMOTE_SERVER_URL?.includes("localhost"),d="alchemy",u=c.join(e,"mcp-direct.js")
1488
+ this.logger.info("Configuring OpenCode",{isDevMode:l,serverName:d,scriptPath:u,projectPath:s}),await this.sweepLegacyAuthPluginFiles(s)
1489
+ const h=c.join(s,"opencode.json")
1490
+ let g=null,p=null,m=null,f=null,y=!1,v=!1
1491
+ const w=c.join(s,".alchemy_lab","config.json")
1492
+ if(!i){try{const e=await r.readFile(w,"utf-8"),t=JSON.parse(e)
1493
+ t?.openCodeTools?(g=t.openCodeTools,this.logger.info("Found tool settings in project config",{projectConfigTools:g})):v=!0,t?.openCodeModel&&(p=t.openCodeModel)}catch(e){this.logger.debug("No project config found, will create with defaults"),v=!0}try{const e=await r.readFile(h,"utf-8"),t=JSON.parse(e)
1494
+ t?.mode?.alchemy?.tools&&(m=t.mode.alchemy.tools,this.logger.info("Found existing tool settings in opencode.json",{existingTools:m})),t?.model&&(f=t.model),t?.mode?.journal&&(y=!0)}catch(e){this.logger.info("No existing opencode.json found, will use defaults")}}const b=i?.openCodeTools||g||m||{glob:!1,grep:!1,ls:!1,list:!1,view:!1,read:!1,write:!1,edit:!1,patch:!1,diagnostics:!0,bash:!0===i?.enableBash,fetch:!1,sourcegraph:!0,agent:!1!==a,todoread:!1,todowrite:!1},I={tools:b},E=i?.openCodeModel||p||f||"anthropic/claude-opus-4-5"
1495
+ i?.openCodeModel?I.model=i.openCodeModel:p?I.model=p:f&&(I.model=f)
1496
+ const C=c.join(s,".opencode","agent")
1497
+ await r.mkdir(C,{recursive:!0})
1498
+ const S=E||"anthropic/claude-opus-4-5-20251101"
1499
+ let k,T=null,_=null
1500
+ if(i?.projectId)if(await this.seedAgentsFromRemote(i.projectId),_=await this.fetchProjectFromRemote(i.projectId),k=_?.metadata?.globalSystemPrompt,k&&this.logger.info("Using global system prompt from project metadata",{projectId:i.projectId,promptLength:k.length}),T=await this.fetchAgentsFromRemote(i.projectId),T&&T.length>0){this.logger.info("Using agents from remote database",{count:T.length})
1501
+ for(const e of T){if(!e.enabled)continue
1502
+ const t=this.generateAgentMarkdown(e,d,S,k),s=c.join(C,e.name+".md")
1503
+ await r.writeFile(s,t,"utf8"),this.logger.info(`Created ${e.name} agent file from remote config`,{agentPath:s,mode:e.mode})}}else this.logger.warn("No agents found - check remote server or enable agents via UI")
1114
1504
  else this.logger.warn("No projectId provided - agents must be configured via database")
1115
- const A={$schema:"https://opencode.ai/config.json",model:S,mcp:{[d]:{type:"local",command:[n,u,r],enabled:!0}}}
1116
- if(l&&process.env.REMOTE_SERVER_URL&&(A.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),E&&E.length>0){A.agent={}
1117
- for(const e of E){if(!e.enabled)continue
1505
+ const j={$schema:"https://opencode.ai/config.json",model:E,mcp:{[d]:{type:"local",command:[n,u,s],enabled:!0}}}
1506
+ if(l&&process.env.REMOTE_SERVER_URL&&(j.mcp[d].environment={REMOTE_SERVER_URL:process.env.REMOTE_SERVER_URL}),T&&T.length>0){j.agent={}
1507
+ for(const e of T){if(!e.enabled)continue
1118
1508
  const t={}
1119
- for(const[s,r]of Object.entries(e.tools||{})){const e=r,n=s.replace(/-/g,"_")
1120
- t[o.OPENCODE_BUILTIN_TOOLS.has(s)?s:`${d}_${n}`]=e}const s=T?.metadata?.bashToolAgents||{}
1121
- e.name in s&&(t.bash=!!s[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
1122
- let r=""
1123
- b&&(r+=b.trim()+"\n\n"),e.prompt&&(r+=e.prompt),e.promptAddition&&(r+="\n\n"+e.promptAddition),A.agent[e.name]={tools:t,...r?{prompt:r}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}A.agent.general={...A.agent.general,disable:!0},A.agent.explore={...A.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
1124
- const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${b?b.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`
1125
- await s.writeFile(c.join(C,"explore.md"),e,"utf8"),this.logger.info("Created explore agent override with research prompt"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(A.agent).length})}const P=[]
1126
- P.push("file://"+c.join(r,".opencode","plugin","inject-session.js")),P.push("file://"+c.join(r,".opencode","plugin","event-hooks.js")),P.push("file://"+c.join(r,".opencode","plugin","system-logger.js")),P.length>0&&(A.plugin=P),await s.writeFile(h,JSON.stringify(A,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h,plugins:P})
1127
- try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),r=c.join(e.homedir(),".alchemy_lab","opencode","config")
1128
- await s.mkdir(r,{recursive:!0})
1129
- const o=c.join(r,"opencode.json")
1130
- let n={}
1131
- try{n=JSON.parse(await s.readFile(o,"utf-8"))}catch{}const i=(n.plugin||[]).filter((e=>!e.includes(".opencode/plugin/")))
1132
- n.plugin=[...i,...P],n.$schema=n.$schema||"https://opencode.ai/config.json",await s.writeFile(o,JSON.stringify(n,null,2),"utf8"),this.logger.info("Updated global alchemy opencode config",{globalConfigPath:o,plugins:n.plugin})}catch(e){this.logger.warn("Failed to write global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(v&&!i)try{let e={}
1133
- try{const t=await s.readFile(w,"utf-8")
1134
- e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=I,await s.writeFile(w,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:w}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPlugins:s}=await t.e(939).then(t.t.bind(t,9939,23)),o=await s(r,l??!1,this.logger,e)
1135
- o.synced.length>0&&this.logger.info("Synced plugins during OpenCode config",{synced:o.synced}),o.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:o.errors})}catch(e){this.logger.warn("Plugin sync failed during OpenCode config",{error:e instanceof Error?e.message:"Unknown error"})}return{success:!0,configPath:h}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,s.getErrorMessage)(e)}),{success:!1,error:(0,s.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=o,o.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},9023(module){"use strict"
1509
+ for(const[r,s]of Object.entries(e.tools||{})){const e=s,n=r.replace(/-/g,"_")
1510
+ t[o.OPENCODE_BUILTIN_TOOLS.has(r)?r:`${d}_${n}`]=e}const r=_?.metadata?.bashToolAgents||{}
1511
+ e.name in r&&(t.bash=!!r[e.name]),t.skill=!1,("subagent"===e.mode||"tester"===e.name||"auto-tester"===e.name)&&void 0===t.task&&(t.task=!1)
1512
+ let s=""
1513
+ k&&(s+=k.trim()+"\n\n"),e.prompt&&(s+=e.prompt),e.promptAddition&&(s+="\n\n"+e.promptAddition),j.agent[e.name]={tools:t,...s?{prompt:s}:{},...e.description?{description:e.description}:{},mode:"utility"===e.mode?"primary":e.mode||"primary",...e.color?{color:e.color}:{}}}j.agent.general={...j.agent.general,disable:!0},j.agent.explore={...j.agent.explore,tools:{grep:!1,glob:!1,list:!1,read:!1,bash:!1,[d+"_search"]:!0,[d+"_read_files"]:!0,[d+"_view_asset"]:!0,[d+"_query_history"]:!0,webfetch:!0,websearch:!0,codesearch:!0}}
1514
+ const e=`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${k?k.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`
1515
+ await r.writeFile(c.join(C,"explore.md"),e,"utf8"),this.logger.info("Created explore agent override with research prompt"),this.logger.info("Added agent tools to opencode.json",{agentCount:Object.keys(j.agent).length})}const A=e=>"string"==typeof e&&e.includes(".opencode/plugin/"),P=(await this.fetchPluginManifest())["opencode-claude-auth"],M=j.plugin,O=Array.isArray(M)?M.filter((e=>!A(e))):[]
1516
+ P&&this.applyAuthPluginPin(O,P),O.length>0?j.plugin=O:delete j.plugin,await r.writeFile(h,JSON.stringify(j,null,2),"utf8"),this.logger.info("OpenCode configured successfully",{configPath:h})
1517
+ try{const e=await Promise.resolve().then(t.t.bind(t,857,23)),s=c.join(e.homedir(),".alchemy_lab","opencode","config"),o=c.join(s,"opencode.json")
1518
+ let n=null
1519
+ try{n=JSON.parse(await r.readFile(o,"utf-8"))}catch{}const i=n??{},a=Array.isArray(i.plugin)?i.plugin.slice():[],l=a.filter((e=>!A(e)))
1520
+ P&&this.applyAuthPluginPin(l,P),a.length===l.length&&!a.some(((e,t)=>e!==l[t]))&&n||(i.plugin=l,await r.mkdir(s,{recursive:!0}),await r.writeFile(o,JSON.stringify(i,null,2),"utf8"),this.logger.info("Updated global opencode config plugin list",{globalConfigPath:o,plugins:l}))}catch(e){this.logger.warn("Failed to update global alchemy opencode config",{error:e instanceof Error?e.message:"Unknown error"})}if(v&&!i)try{let e={}
1521
+ try{const t=await r.readFile(w,"utf-8")
1522
+ e=JSON.parse(t)}catch{}e.openCodeTools||(e.openCodeTools=b,await r.writeFile(w,JSON.stringify(e,null,2),"utf8"),this.logger.info("Created project config with default tool settings",{projectConfigPath:w}))}catch(e){this.logger.warn("Failed to create project config",{error:e instanceof Error?e.message:"Unknown error"})}try{const{syncPluginsGlobal:r,cleanupLegacyProjectPlugins:o}=await t.e(939).then(t.t.bind(t,9939,23)),n=await r(l??!1,this.logger,e)
1523
+ n.errors.length>0&&this.logger.warn("Some plugins failed to sync",{errors:n.errors}),await o(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:h}}catch(e){return this.logger.error("Failed to configure OpenCode",{error:(0,r.getErrorMessage)(e)}),{success:!1,error:(0,r.getErrorMessage)(e)}}}}exports.MCPClientConfigurer=o,o.OPENCODE_BUILTIN_TOOLS=new Set(["glob","grep","ls","list","view","read","write","edit","patch","diagnostics","sourcegraph","bash","fetch","agent","task","question","todoread","todowrite"])},9023(module){"use strict"
1136
1524
  module.exports=require("util")},9067(e,exports,t){"use strict"
1137
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1525
+ var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1138
1526
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.ExternalAuthFlow=exports.PollingStatus=void 0
1139
- const r=s(t(6982)),o=t(8824)
1527
+ const s=r(t(6982)),o=t(8824)
1140
1528
  var n
1141
- !function(e){e.NOT_STARTED="not_started",e.POLLING="polling",e.AUTHENTICATED="authenticated",e.TIMEOUT="timeout",e.ERROR="error"}(n||(exports.PollingStatus=n={})),exports.ExternalAuthFlow=class{constructor(e,t,s=5e3,r=9e5){this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.pollingIntervalId=null,this.tokenResult=null,this.pollingError=null,this.logger=e.child({component:"auth-flow"}),this.remoteServerUrl=t,this.pollingIntervalMs=s,this.maxPollingTimeMs=r}getAuthUrl(e){let s=this.remoteServerUrl
1142
- s=s.includes("api.alchemylabcode.com")?s.replace("api.alchemylabcode.com","alchemylabcode.com"):s.match(/localhost:\d+/)?s.replace(/localhost:\d+/,"localhost:3000"):s.replace(/\/\/api\./g,"//")
1143
- const r=t(6928).basename(process.cwd()),o=`${s}/login?mcp_code=${e}&project=${encodeURIComponent(r)}`
1144
- return this.logger.info("Generated auth URL",{url:o,frontendUrl:s,backendUrl:this.remoteServerUrl}),o}getPollingUrl(e){const t=`${this.remoteServerUrl}/auth/check-auth?code=${e}`
1529
+ !function(e){e.NOT_STARTED="not_started",e.POLLING="polling",e.AUTHENTICATED="authenticated",e.TIMEOUT="timeout",e.ERROR="error"}(n||(exports.PollingStatus=n={})),exports.ExternalAuthFlow=class{constructor(e,t,r=5e3,s=9e5){this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.pollingIntervalId=null,this.tokenResult=null,this.pollingError=null,this.logger=e.child({component:"auth-flow"}),this.remoteServerUrl=t,this.pollingIntervalMs=r,this.maxPollingTimeMs=s}getAuthUrl(e){let r=this.remoteServerUrl
1530
+ r=r.includes("api.alchemylabcode.com")?r.replace("api.alchemylabcode.com","alchemylabcode.com"):r.match(/localhost:\d+/)?r.replace(/localhost:\d+/,"localhost:3000"):r.replace(/\/\/api\./g,"//")
1531
+ const s=t(6928).basename(process.cwd()),o=`${r}/login?mcp_code=${e}&project=${encodeURIComponent(s)}`
1532
+ return this.logger.info("Generated auth URL",{url:o,frontendUrl:r,backendUrl:this.remoteServerUrl}),o}getPollingUrl(e){const t=`${this.remoteServerUrl}/auth/check-auth?code=${e}`
1145
1533
  return this.logger.info("Generated polling URL",{url:t}),t}async requestTemporaryCode(){const e=this.remoteServerUrl+"/auth/generate-code"
1146
1534
  this.logger.info("Requesting temporary code from remote server",{url:e})
1147
1535
  try{const t=await fetch(e)
1148
1536
  if(!t.ok){const e=await t.text().catch((()=>"Failed to read error response body"))
1149
- throw Error(`Error requesting temporary code: ${t.status} ${t.statusText}\nResponse: ${e}`)}const s=await t.json()
1150
- if(!s.success||!s.code)throw Error("Invalid response from server: "+JSON.stringify(s))
1151
- const r=s.code
1152
- return this.logger.info("Received temporary code from server",{code:r}),r}catch(e){throw this.logger.error("Failed to request temporary code",{error:(0,o.getErrorMessage)(e)}),e}}async startAuthFlow(){const e=await this.requestTemporaryCode(),t=this.getAuthUrl(e),s=this.getPollingUrl(e)
1153
- return this.logger.info("Starting external authentication flow",{tempCode:e,authUrl:t,pollingUrl:s,remoteServerUrl:this.remoteServerUrl}),{tempCode:e,authUrl:t,pollingUrl:s}}parseTokenResponse(e){try{return e&&e.token&&e.expiresAt?{token:e.token,userId:e.userId||"unknown",username:e.username||"unknown",role:e.role||"user",expiresAt:e.expiresAt}:null}catch(e){return this.logger.error("Error parsing token response",{error:(0,o.getErrorMessage)(e)}),null}}startPolling(e,t,s,r){this.pollingStatus=n.POLLING,this.pollingStartTime=Date.now(),this.tokenResult=null,this.pollingError=null,this.stopPolling(),this.logger.info("Starting polling for authentication",{tempCode:e.substring(0,4)+"***",interval:this.pollingIntervalMs,maxTime:this.maxPollingTimeMs})
1154
- const i=async()=>{try{const r=Date.now()-this.pollingStartTime
1155
- if(r>this.maxPollingTimeMs)return this.logger.warn("Authentication polling timed out",{tempCode:e.substring(0,4)+"***",elapsedTime:r,maxTime:this.maxPollingTimeMs}),this.pollingStatus=n.TIMEOUT,this.stopPolling(),void(s&&s())
1537
+ throw Error(`Error requesting temporary code: ${t.status} ${t.statusText}\nResponse: ${e}`)}const r=await t.json()
1538
+ if(!r.success||!r.code)throw Error("Invalid response from server: "+JSON.stringify(r))
1539
+ const s=r.code
1540
+ return this.logger.info("Received temporary code from server",{code:s}),s}catch(e){throw this.logger.error("Failed to request temporary code",{error:(0,o.getErrorMessage)(e)}),e}}async startAuthFlow(){const e=await this.requestTemporaryCode(),t=this.getAuthUrl(e),r=this.getPollingUrl(e)
1541
+ return this.logger.info("Starting external authentication flow",{tempCode:e,authUrl:t,pollingUrl:r,remoteServerUrl:this.remoteServerUrl}),{tempCode:e,authUrl:t,pollingUrl:r}}parseTokenResponse(e){try{return e&&e.token&&e.expiresAt?{token:e.token,userId:e.userId||"unknown",username:e.username||"unknown",role:e.role||"user",expiresAt:e.expiresAt}:null}catch(e){return this.logger.error("Error parsing token response",{error:(0,o.getErrorMessage)(e)}),null}}startPolling(e,t,r,s){this.pollingStatus=n.POLLING,this.pollingStartTime=Date.now(),this.tokenResult=null,this.pollingError=null,this.stopPolling(),this.logger.info("Starting polling for authentication",{tempCode:e.substring(0,4)+"***",interval:this.pollingIntervalMs,maxTime:this.maxPollingTimeMs})
1542
+ const i=async()=>{try{const s=Date.now()-this.pollingStartTime
1543
+ if(s>this.maxPollingTimeMs)return this.logger.warn("Authentication polling timed out",{tempCode:e.substring(0,4)+"***",elapsedTime:s,maxTime:this.maxPollingTimeMs}),this.pollingStatus=n.TIMEOUT,this.stopPolling(),void(r&&r())
1156
1544
  const i=this.getPollingUrl(e)
1157
1545
  this.logger.debug("Polling for authentication status",{tempCode:e,url:i})
1158
- try{const s=await fetch(i)
1159
- if(!s.ok){const e=await s.text().catch((()=>"Failed to read error response body"))
1160
- throw Error(`Error polling for authentication: ${s.status} ${s.statusText}\nResponse: ${e}`)}const r=await s.json()
1161
- if(this.logger.debug("Polling response",{data:r}),r.authenticated&&r.token){this.logger.info("Authentication successful",{tempCode:e.substring(0,4)+"***",userId:r.userId})
1162
- const s=this.parseTokenResponse(r)
1163
- if(!s)throw Error("Invalid token response from server")
1164
- this.pollingStatus=n.AUTHENTICATED,this.tokenResult=s,this.stopPolling(),t&&t(s)}else this.logger.debug("Not authenticated yet, continuing to poll",{tempCode:e.substring(0,4)+"***"})}catch(e){const t=(0,o.getErrorMessage)(e)
1165
- throw Error(`Error during polling: ${t}${/fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network/i.test(t)?" (remote may be unreachable \u2014 check your connection and try again)":""}`)}}catch(t){const s=(0,o.getErrorMessage)(t)
1166
- this.logger.error("Error polling for authentication",{tempCode:e.substring(0,4)+"***",error:s}),this.pollingStatus=n.ERROR,this.pollingError=s,this.stopPolling(),r&&r(s)}}
1546
+ try{const r=await fetch(i)
1547
+ if(!r.ok){const e=await r.text().catch((()=>"Failed to read error response body"))
1548
+ throw Error(`Error polling for authentication: ${r.status} ${r.statusText}\nResponse: ${e}`)}const s=await r.json()
1549
+ if(this.logger.debug("Polling response",{data:s}),s.authenticated&&s.token){this.logger.info("Authentication successful",{tempCode:e.substring(0,4)+"***",userId:s.userId})
1550
+ const r=this.parseTokenResponse(s)
1551
+ if(!r)throw Error("Invalid token response from server")
1552
+ this.pollingStatus=n.AUTHENTICATED,this.tokenResult=r,this.stopPolling(),t&&t(r)}else this.logger.debug("Not authenticated yet, continuing to poll",{tempCode:e.substring(0,4)+"***"})}catch(e){const t=(0,o.getErrorMessage)(e)
1553
+ throw Error(`Error during polling: ${t}${/fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network/i.test(t)?" (remote may be unreachable \u2014 check your connection and try again)":""}`)}}catch(t){const r=(0,o.getErrorMessage)(t)
1554
+ this.logger.error("Error polling for authentication",{tempCode:e.substring(0,4)+"***",error:r}),this.pollingStatus=n.ERROR,this.pollingError=r,this.stopPolling(),s&&s(r)}}
1167
1555
  return i(),this.pollingIntervalId=setInterval(i,this.pollingIntervalMs),()=>this.stopPolling()}stopPolling(){this.pollingIntervalId&&(clearInterval(this.pollingIntervalId),this.pollingIntervalId=null,this.logger.debug("Stopped polling for authentication",{status:this.pollingStatus}))}getPollingStatus(){const e={status:this.pollingStatus}
1168
1556
  return this.pollingStatus===n.AUTHENTICATED&&this.tokenResult?e.tokenInfo=this.tokenResult:this.pollingStatus===n.ERROR&&this.pollingError&&(e.error=this.pollingError),this.pollingStartTime>0&&(e.elapsedTimeMs=Date.now()-this.pollingStartTime),e}resetPollingState(){this.stopPolling(),this.pollingStatus=n.NOT_STARTED,this.pollingStartTime=0,this.tokenResult=null,this.pollingError=null}async startGoogleOAuthFlow(){this.logger.info("Starting Google OAuth authentication flow")
1169
- try{const e=r.default.randomBytes(16).toString("hex"),t=`${this.remoteServerUrl}/api/auth/google/auth-url?state=${e}`,s=await fetch(t)
1170
- if(!s.ok){const e=await s.text().catch((()=>"Failed to read error response body"))
1171
- throw Error(`Error getting Google OAuth URL: ${s.status} ${s.statusText}\nResponse: ${e}`)}const o=await s.json()
1557
+ try{const e=s.default.randomBytes(16).toString("hex"),t=`${this.remoteServerUrl}/api/auth/google/auth-url?state=${e}`,r=await fetch(t)
1558
+ if(!r.ok){const e=await r.text().catch((()=>"Failed to read error response body"))
1559
+ throw Error(`Error getting Google OAuth URL: ${r.status} ${r.statusText}\nResponse: ${e}`)}const o=await r.json()
1172
1560
  if(!o.success||!o.data||!o.data.authUrl)throw Error("Invalid response from server: "+JSON.stringify(o))
1173
1561
  const n=o.data.authUrl
1174
1562
  return this.logger.info("Received Google OAuth URL from server",{authUrl:n,state:e}),{authUrl:n,state:e}}catch(e){throw this.logger.error("Failed to start Google OAuth flow",{error:(0,o.getErrorMessage)(e)}),e}}async isGoogleOAuthAvailable(){try{const e=this.remoteServerUrl+"/api/auth/google/status",t=await fetch(e)
1175
1563
  if(!t.ok)return!1
1176
- const s=await t.json()
1177
- return s.success&&s.data&&!0===s.data.enabled}catch(e){return this.logger.debug("Error checking Google OAuth availability",{error:(0,o.getErrorMessage)(e)}),!1}}async startGoogleOAuthPolling(e,t,s,r,i){return this.logger.info("Starting Google OAuth polling",{state:t}),this.pollingStatus=n.POLLING,this.pollingStartTime=Date.now(),this.tokenResult=null,this.pollingError=null,this.logger.info("Google OAuth authentication URLs ready for user interaction"),this.pollingIntervalId=setInterval((async()=>{try{if(Date.now()-this.pollingStartTime>this.maxPollingTimeMs)return this.logger.info("Google OAuth polling timed out"),this.pollingStatus=n.TIMEOUT,this.stopPolling(),void(r&&r())
1564
+ const r=await t.json()
1565
+ return r.success&&r.data&&!0===r.data.enabled}catch(e){return this.logger.debug("Error checking Google OAuth availability",{error:(0,o.getErrorMessage)(e)}),!1}}async startGoogleOAuthPolling(e,t,r,s,i){return this.logger.info("Starting Google OAuth polling",{state:t}),this.pollingStatus=n.POLLING,this.pollingStartTime=Date.now(),this.tokenResult=null,this.pollingError=null,this.logger.info("Google OAuth authentication URLs ready for user interaction"),this.pollingIntervalId=setInterval((async()=>{try{if(Date.now()-this.pollingStartTime>this.maxPollingTimeMs)return this.logger.info("Google OAuth polling timed out"),this.pollingStatus=n.TIMEOUT,this.stopPolling(),void(s&&s())
1178
1566
  const e=`${this.remoteServerUrl}/api/auth/google/check-status?state=${t}`,o=await fetch(e)
1179
1567
  if(!o.ok)return void this.logger.debug("Polling response not ok",{status:o.status})
1180
1568
  const i=await o.json()
1181
1569
  if(this.logger.debug("Google OAuth polling response",{data:i}),i.success&&i.data&&i.data.authenticated&&i.data.token){this.logger.info("Google OAuth authentication completed")
1182
1570
  const e=this.parseTokenResponse(i.data)
1183
- e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),s&&s(e))}else this.logger.debug("Not authenticated yet, continuing to poll",{hasSuccess:!!i.success,hasData:!!i.data,authenticated:i.data?.authenticated})}catch(e){this.logger.error("Error during Google OAuth polling",{error:(0,o.getErrorMessage)(e)}),this.pollingStatus=n.ERROR,this.pollingError=(0,o.getErrorMessage)(e),this.stopPolling(),i&&i(this.pollingError)}}),this.pollingIntervalMs),{authUrl:e,state:t}}}},9068(e,exports,t){"use strict"
1184
- function s(){const e=process.env.ALCHEMY_OPENCODE_DIR
1571
+ e&&(this.pollingStatus=n.AUTHENTICATED,this.tokenResult=e,this.stopPolling(),r&&r(e))}else this.logger.debug("Not authenticated yet, continuing to poll",{hasSuccess:!!i.success,hasData:!!i.data,authenticated:i.data?.authenticated})}catch(e){this.logger.error("Error during Google OAuth polling",{error:(0,o.getErrorMessage)(e)}),this.pollingStatus=n.ERROR,this.pollingError=(0,o.getErrorMessage)(e),this.stopPolling(),i&&i(this.pollingError)}}),this.pollingIntervalMs),{authUrl:e,state:t}}}},9068(e,exports,t){"use strict"
1572
+ function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
1185
1573
  if(e)return e
1186
1574
  try{const e=("undefined"!=typeof require?require:t(4245)).resolve("alchemylab-opencode/package.json")
1187
- return(0,n.dirname)(e)}catch{}}function r(e){return 22===e||e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
1188
- if(r(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,n.join)(process.env.HOME||"",".nvm"),t=(0,n.join)(e,"versions","node")
1189
- if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>r(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const s=parseInt(e.replace("v","").split(".")[0],10)
1190
- return parseInt(t.replace("v","").split(".")[0],10)-s}))
1191
- if(e.length>0){const s=(0,n.join)(t,e[0],"bin","node")
1192
- if((0,i.existsSync)(s))return s}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.resolveServeCommand=function(){const e=s()
1193
- return e?o((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=s()
1575
+ return(0,n.dirname)(e)}catch{}}function s(e){return 22===e||e>=24}function o(e,t){return{command:function(){try{const e=(0,a.execFileSync)("node",["--version"],{encoding:"utf8"}).trim()
1576
+ if(s(parseInt(e.replace("v","").split(".")[0],10)))return"node"}catch{}const e=process.env.NVM_DIR||(0,n.join)(process.env.HOME||"",".nvm"),t=(0,n.join)(e,"versions","node")
1577
+ if((0,i.existsSync)(t))try{const e=(0,i.readdirSync)(t).filter((e=>s(parseInt(e.replace("v","").split(".")[0],10)))).sort(((e,t)=>{const r=parseInt(e.replace("v","").split(".")[0],10)
1578
+ return parseInt(t.replace("v","").split(".")[0],10)-r}))
1579
+ if(e.length>0){const r=(0,n.join)(t,e[0],"bin","node")
1580
+ if((0,i.existsSync)(r))return r}}catch{}throw Error(`AlchemyLab requires Node.js 22 or 24+ to run the OpenCode server (Node 23 is not supported).\nYour current version: ${process.version}\nInstall a compatible version: nvm install 22 (or download from https://nodejs.org)`)}(),prependArgs:["--no-warnings",(0,n.join)(e,t)]}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.resolveServeCommand=function(){const e=r()
1581
+ return e?o((0,n.join)(e,"dist"),"serve.js"):{command:"opencode",prependArgs:["serve"]}},exports.resolveRunCommand=function(){const e=r()
1194
1582
  return e?o((0,n.join)(e,"dist"),"run.js"):{command:"opencode",prependArgs:[]}}
1195
1583
  const n=t(6928),i=t(9896),a=t(5317)},9266(e,exports,t){"use strict"
1196
- var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1197
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleOpenCodeProxyAction=async function(e,t){const{method:s,path:i,body:a,headers:c}=e
1198
- if(!s||!i)return{success:!1,message:"method and path are required in proxy payload"}
1199
- const l=function(e){const t=r.default.join(e,".alchemy_lab","opencode.json")
1200
- if(!o.default.existsSync(t))return null
1201
- try{return"http://localhost:"+JSON.parse(o.default.readFileSync(t,"utf-8")).port}catch{return null}}(t.projectPath)
1202
- if(!l)return{success:!1,message:"OpenCode not running (no opencode.json found)"}
1203
- const d=`${l}${i.startsWith("/")?i:"/"+i}`
1204
- try{const e={"Content-Type":"application/json",...c},r={method:s.toUpperCase(),headers:e}
1205
- if("GET"!==s.toUpperCase()&&"HEAD"!==s.toUpperCase()&&void 0!==a){let e=a
1206
- if("object"==typeof a&&null!==a&&"_alchemyAgentType"in a){const{_alchemyAgentType:t,...s}=a
1207
- e=s}r.body="string"==typeof e?e:JSON.stringify(e)}const o=await fetch(d,r),l=o.headers.get("content-type")||""
1584
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleOpenCodeProxyAction=async function(e,t){const{method:o,path:n,body:i,headers:a}=e
1585
+ if(!o||!n)return{success:!1,message:"method and path are required in proxy payload"}
1586
+ const c=(0,s.getOpenCodeUrlFromConfig)(t.projectPath)
1587
+ if(!c)return{success:!1,message:"OpenCode not running (no opencode.json found)"}
1588
+ const l=`${c}${n.startsWith("/")?n:"/"+n}`
1589
+ try{const e={"Content-Type":"application/json",...a},s={method:o.toUpperCase(),headers:e}
1590
+ if("GET"!==o.toUpperCase()&&"HEAD"!==o.toUpperCase()&&void 0!==i){let e=i
1591
+ if("object"==typeof i&&null!==i&&"_alchemyAgentType"in i){const{_alchemyAgentType:t,...r}=i
1592
+ e=r}s.body="string"==typeof e?e:JSON.stringify(e)}const c=await fetch(l,s),d=c.headers.get("content-type")||""
1208
1593
  let u
1209
- if(u=l.includes("application/json")?await o.json():await o.text(),"POST"===s.toUpperCase()&&"/session"===i&&o.status>=200&&300>o.status&&u?.id)try{const e=u.id,s=a?.parentID
1210
- await t.apiClient.post(`/api/projects/${t.projectId}/cli-sessions`,{externalSessionId:e,providerId:"opencode",...s?{parentExternalSessionId:s}:{},agentType:a?._alchemyAgentType||"unknown",status:"active"}),t.logger.info("Registered proxy-created session in DB",{sessionId:e,parentSessionId:s})}catch(e){t.logger.warn("Failed to register proxy-created session",{error:(0,n.getErrorMessage)(e)})}return{success:!0,message:"Proxy request completed",data:{status:o.status,contentType:l,body:u}}}catch(e){return{success:!1,message:"Proxy request failed: "+(0,n.getErrorMessage)(e),error:(0,n.getErrorMessage)(e)}}}
1211
- const r=s(t(6928)),o=s(t(9896)),n=t(8824)},9278(module){"use strict"
1594
+ if(u=d.includes("application/json")?await c.json():await c.text(),"POST"===o.toUpperCase()&&"/session"===n&&c.status>=200&&300>c.status&&u?.id)try{const e=u.id,r=i?.parentID
1595
+ await t.apiClient.post(`/api/projects/${t.projectId}/cli-sessions`,{externalSessionId:e,providerId:"opencode",...r?{parentExternalSessionId:r}:{},agentType:i?._alchemyAgentType||"unknown",status:"active"}),t.logger.info("Registered proxy-created session in DB",{sessionId:e,parentSessionId:r})}catch(e){t.logger.warn("Failed to register proxy-created session",{error:(0,r.getErrorMessage)(e)})}return{success:!0,message:"Proxy request completed",data:{status:c.status,contentType:d,body:u}}}catch(e){return{success:!1,message:"Proxy request failed: "+(0,r.getErrorMessage)(e),error:(0,r.getErrorMessage)(e)}}}
1596
+ const r=t(8824),s=t(833)},9278(module){"use strict"
1212
1597
  module.exports=require("net")},9735(e,exports,t){"use strict"
1213
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveRequestAction=async function(e,t){const{requestId:r}=e,{logger:o,apiClient:n,projectId:i}=t
1214
- if(!r)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
1215
- o.info("Starting approve-request action",{requestId:r})
1216
- try{const e=await n.get(`/api/projects/${i}/requests/${r}`)
1217
- if(!e.success||!e.data)return{success:!1,message:`Request ${r} not found`,error:"Request not found"}
1598
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleApproveRequestAction=async function(e,t){const{requestId:s}=e,{logger:o,apiClient:n,projectId:i}=t
1599
+ if(!s)return{success:!1,message:"requestId is required",error:"Missing requestId in payload"}
1600
+ o.info("Starting approve-request action",{requestId:s})
1601
+ try{const e=await n.get(`/api/projects/${i}/requests/${s}`)
1602
+ if(!e.success||!e.data)return{success:!1,message:`Request ${s} not found`,error:"Request not found"}
1218
1603
  const t=e.data
1219
- return"pending_approval"!==t.status?{success:!1,message:`Cannot approve request with status '${t.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+t.status}:(await n.patch(`/api/projects/${i}/requests/${r}`,{status:"open"})).success?(o.info("Approve-request action completed",{requestId:r}),{success:!0,message:`Successfully approved request ${r}: ${t.title}`,data:{operationType:"approve",previousStatus:"pending_approval",newStatus:"open"}}):{success:!1,message:"Failed to update request status",error:"Update failed"}}catch(e){const t=(0,s.getErrorMessage)(e)
1220
- return o.error("approve-request action failed",{requestId:r,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
1221
- const s=t(8824)},9896(module){"use strict"
1604
+ return"pending_approval"!==t.status?{success:!1,message:`Cannot approve request with status '${t.status}'. Only requests in 'pending_approval' status can be approved.`,error:"Invalid status: "+t.status}:(await n.patch(`/api/projects/${i}/requests/${s}`,{status:"open"})).success?(o.info("Approve-request action completed",{requestId:s}),{success:!0,message:`Successfully approved request ${s}: ${t.title}`,data:{operationType:"approve",previousStatus:"pending_approval",newStatus:"open"}}):{success:!1,message:"Failed to update request status",error:"Update failed"}}catch(e){const t=(0,r.getErrorMessage)(e)
1605
+ return o.error("approve-request action failed",{requestId:s,error:t}),{success:!1,message:"Failed to approve request: "+t,error:t}}}
1606
+ const r=t(8824)},9860(e,exports,t){"use strict"
1607
+ var r=this&&this.__createBinding||(Object.create?function(e,t,r,s){void 0===s&&(s=r)
1608
+ var o=Object.getOwnPropertyDescriptor(t,r)
1609
+ o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get(){return t[r]}}),Object.defineProperty(e,s,o)}:function(e,t,r,s){void 0===s&&(s=r),e[s]=t[r]}),s=this&&this.__exportStar||function(e,exports){for(var t in e)"default"===t||{}.hasOwnProperty.call(exports,t)||r(exports,e,t)}
1610
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.UnauthorizedError=exports.McpProtocolError=exports.FileOperationError=exports.ServiceError=exports.DatabaseError=exports.AuthorizationError=exports.AuthenticationError=exports.ValidationError=exports.NotFoundError=exports.AppError=exports.ErrorUtils=exports.ERROR_TYPE_PROPERTIES=exports.isRetryableError=exports.errorToResponse=exports.isAppError=exports.createMcpProtocolError=exports.createFileOperationError=exports.createServiceError=exports.createDatabaseError=exports.createAuthorizationError=exports.createAuthenticationError=exports.createValidationError=exports.createNotFoundError=exports.createAppError=exports.createErrorResponse=void 0
1611
+ const o=t(8625)
1612
+ Object.defineProperty(exports,"createErrorResponse",{enumerable:!0,get(){return o.createErrorResponse}}),Object.defineProperty(exports,"createAppError",{enumerable:!0,get(){return o.createAppError}}),Object.defineProperty(exports,"createNotFoundError",{enumerable:!0,get(){return o.createNotFoundError}}),Object.defineProperty(exports,"createValidationError",{enumerable:!0,get(){return o.createValidationError}}),Object.defineProperty(exports,"createAuthenticationError",{enumerable:!0,get(){return o.createAuthenticationError}}),Object.defineProperty(exports,"createAuthorizationError",{enumerable:!0,get(){return o.createAuthorizationError}}),Object.defineProperty(exports,"createDatabaseError",{enumerable:!0,get(){return o.createDatabaseError}}),Object.defineProperty(exports,"createServiceError",{enumerable:!0,get(){return o.createServiceError}}),Object.defineProperty(exports,"createFileOperationError",{enumerable:!0,get(){return o.createFileOperationError}}),Object.defineProperty(exports,"createMcpProtocolError",{enumerable:!0,get(){return o.createMcpProtocolError}}),Object.defineProperty(exports,"isAppError",{enumerable:!0,get(){return o.isAppError}}),Object.defineProperty(exports,"errorToResponse",{enumerable:!0,get(){return o.errorToResponse}}),Object.defineProperty(exports,"isRetryableError",{enumerable:!0,get(){return o.isRetryableError}}),Object.defineProperty(exports,"ERROR_TYPE_PROPERTIES",{enumerable:!0,get(){return o.ERROR_TYPE_PROPERTIES}}),Object.defineProperty(exports,"ErrorUtils",{enumerable:!0,get(){return o.ErrorUtils}}),Object.defineProperty(exports,"AppError",{enumerable:!0,get(){}}),Object.defineProperty(exports,"NotFoundError",{enumerable:!0,get(){return o.NotFoundError}}),Object.defineProperty(exports,"ValidationError",{enumerable:!0,get(){return o.ValidationError}}),Object.defineProperty(exports,"AuthenticationError",{enumerable:!0,get(){return o.AuthenticationError}}),Object.defineProperty(exports,"AuthorizationError",{enumerable:!0,get(){return o.AuthorizationError}}),Object.defineProperty(exports,"DatabaseError",{enumerable:!0,get(){return o.DatabaseError}}),Object.defineProperty(exports,"ServiceError",{enumerable:!0,get(){return o.ServiceError}}),Object.defineProperty(exports,"FileOperationError",{enumerable:!0,get(){return o.FileOperationError}}),Object.defineProperty(exports,"McpProtocolError",{enumerable:!0,get(){return o.McpProtocolError}}),Object.defineProperty(exports,"UnauthorizedError",{enumerable:!0,get(){return o.UnauthorizedError}}),s(t(441),exports)},9896(module){"use strict"
1222
1613
  module.exports=require("fs")}},n={}
1223
- e.m=o,s=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,e.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r
1224
- if("object"==typeof r&&r){if(4&o&&r.__esModule)return r
1225
- if(16&o&&"function"==typeof r.then)return r}var n=Object.create(null)
1614
+ e.m=o,r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,e.t=function(s,o){if(1&o&&(s=this(s)),8&o)return s
1615
+ if("object"==typeof s&&s){if(4&o&&s.__esModule)return s
1616
+ if(16&o&&"function"==typeof s.then)return s}var n=Object.create(null)
1226
1617
  e.r(n)
1227
1618
  var i={}
1228
- t=t||[null,s({}),s([]),s(s)]
1229
- for(var a=2&o&&r;"object"==typeof a&&!~t.indexOf(a);a=s(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>r[e]))
1230
- return i.default=()=>r,e.d(n,i),n},e.d=(exports,t)=>{for(var s in t)e.o(t,s)&&!e.o(exports,s)&&Object.defineProperty(exports,s,{enumerable:!0,get:t[s]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((s,r)=>(e.f[r](t,s),s)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},r={27:1},e.f.require=t=>{r[t]||(t=>{var s=t.modules,o=t.ids,n=t.runtime
1231
- for(var i in s)e.o(s,i)&&(e.m[i]=s[i])
1619
+ t=t||[null,r({}),r([]),r(r)]
1620
+ for(var a=2&o&&s;"object"==typeof a&&!~t.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>s[e]))
1621
+ return i.default=()=>s,e.d(n,i),n},e.d=(exports,t)=>{for(var r in t)e.o(t,r)&&!e.o(exports,r)&&Object.defineProperty(exports,r,{enumerable:!0,get:t[r]})},e.f={},e.e=t=>Promise.all(Object.keys(e.f).reduce(((r,s)=>(e.f[s](t,r),r)),[])),e.u=e=>e+".js",e.o=(e,t)=>({}.hasOwnProperty.call(e,t)),e.r=exports=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})},s={27:1},e.f.require=t=>{s[t]||(t=>{var r=t.modules,o=t.ids,n=t.runtime
1622
+ for(var i in r)e.o(r,i)&&(e.m[i]=r[i])
1232
1623
  n&&n(e)
1233
- for(var a=0;a<o.length;a++)r[o[a]]=1})(require("./"+e.u(t)))}
1624
+ for(var a=0;a<o.length;a++)s[o[a]]=1})(require("./"+e.u(t)))}
1234
1625
  var i={};(()=>{"use strict"
1235
1626
  function t(){const e=[(0,h.join)(__dirname,"..","package.json"),(0,h.join)(__dirname,"..","..","package.json")]
1236
1627
  for(const t of e)try{const e=JSON.parse((0,d.readFileSync)(t,"utf-8"))
1237
- if(e.version)return e.version}catch{}return"unknown"}function s(){const e=t()
1238
- console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start action server + launch OpenCode (default)\n serve Start the action server only (for GUI-triggered actions)\n logout Log out and remove stored auth token\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 serve # Start action server only\n alchemy logout # Log out\nFor more information, visit: https://alchemylabcode.com/\n`)}function r(e){const t=(0,h.join)(e,S.ALCHEMY_DIR_NAME,"project.json")
1239
- return(0,d.existsSync)(t)}function o(e){const t=(0,p.platform)(),s="darwin"===t?"open":"win32"===t?"cmd":"xdg-open",r="win32"===t?["/c","start","",e]:[e];(0,g.spawn)(s,r,{detached:!0,stdio:"ignore"}).unref()}async function n(e,t=3e5){return new Promise((s=>{const r=setInterval((async()=>{await e.hasValidToken()&&(clearInterval(r),s(!0))}),2e3)
1240
- setTimeout((()=>{clearInterval(r),s(!1)}),t)}))}async function a(t){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
1241
- try{const{createConfigManager:s}=await e.e(60).then(e.t.bind(e,9060,23)),r=s(t)
1242
- return(await r.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}async function c(e,t,s,r,o="cli-init"){try{const n=(0,h.basename)(s)||"Local Project",i=await fetch(e+"/api/projects",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+t,"X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"},body:JSON.stringify({name:n,description:"Project for "+n,status:"active",metadata:{basePath:s,createdVia:o}})})
1628
+ if(e.version)return e.version}catch{}return"unknown"}function r(){const e=t()
1629
+ console.log(`\nAlchemyLab CLI v${e}\n\nUSAGE:\n alchemy [command] [options]\n\nCOMMANDS:\n (none) Start action server + launch OpenCode (default)\n serve Start the action server only (for GUI-triggered actions)\n logout Log out and remove stored auth token\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 serve # Start action server only\n alchemy logout # Log out\nFor more information, visit: https://alchemylabcode.com/\n`)}function s(e){const t=(0,h.join)(e,E.ALCHEMY_DIR_NAME,"project.json")
1630
+ return(0,d.existsSync)(t)}function o(e){const t=(0,p.platform)(),r="darwin"===t?"open":"win32"===t?"cmd":"xdg-open",s="win32"===t?["/c","start","",e]:[e];(0,g.spawn)(r,s,{detached:!0,stdio:"ignore"}).unref()}async function n(e,t=3e5){return new Promise((r=>{const s=setInterval((async()=>{await e.hasValidToken()&&(clearInterval(s),r(!0))}),2e3)
1631
+ setTimeout((()=>{clearInterval(s),r(!1)}),t)}))}async function a(t){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
1632
+ try{const{createConfigManager:r}=await e.e(60).then(e.t.bind(e,9060,23)),s=r(t)
1633
+ return(await s.getInstallationConfig()).remoteServerUrl||"https://api.alchemylabcode.com"}catch{return"https://api.alchemylabcode.com"}}async function c(e,t,r,s,o="cli-init"){try{const n=(0,h.basename)(r)||"Local Project",i=await fetch(e+"/api/projects",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+t,"X-Client-Type":"alchemy-local-service","X-Service-Secret":process.env.SERVICE_SHARED_SECRET||"dev-service-secret-change-in-production"},body:JSON.stringify({name:n,description:"Project for "+n,status:"active",metadata:{basePath:r,createdVia:o}})})
1243
1634
  if(i.ok){const e=await i.json()
1244
- return e.data?.id||e.id}r.info(`Project registration failed (HTTP ${i.status})`)}catch(e){r.info("Project registration failed",{error:(0,I.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
1245
- const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),f=e(1253),m=e(8984),y=e(2366),v=e(3962),w=e(1187),I=e(8824),k=e(6800),S=e(4139);(async function(){23===parseInt(process.version.replace("v","").split(".")[0],10)&&(console.error(`\n\u274c Node.js ${process.version} is not supported.`),console.error(" AlchemyLab requires Node.js 22 or 24+ (Node 23 has a broken sqlite API)."),console.error("\n Switch with: nvm use 22 or nvm use 24\n"),process.exit(1))
1635
+ return e.data?.id||e.id}s.info(`Project registration failed (HTTP ${i.status})`)}catch(e){s.info("Project registration failed",{error:(0,b.getErrorMessage)(e)})}}Object.defineProperty(i,"__esModule",{value:!0})
1636
+ const l=e(3444),d=e(9896),u=e(1943),h=e(6928),g=e(5317),p=e(857),m=e(1253),f=e(8984),y=e(2366),v=e(3962),w=e(1187),b=e(8824),I=e(6800),E=e(4139);(async function(){23===parseInt(process.version.replace("v","").split(".")[0],10)&&(console.error(`\n\u274c Node.js ${process.version} is not supported.`),console.error(" AlchemyLab requires Node.js 22 or 24+ (Node 23 has a broken sqlite API)."),console.error("\n Switch with: nvm use 22 or nvm use 24\n"),process.exit(1))
1246
1637
  const i=process.argv.slice(2)
1247
1638
  let C
1248
- const j=i.indexOf("--port");-1!==j&&i[j+1]&&(C=parseInt(i[j+1],10),isNaN(C)&&(console.error("\u274c Invalid port number"),process.exit(1)))
1249
- const b=i.filter(((e,t)=>"--port"!==e&&(-1===j||t!==j+1)))
1250
- if(0===b.length)return void await async function(s={}){const i=process.cwd(),l=(0,h.join)(i,S.ALCHEMY_DIR_NAME)
1639
+ const S=i.indexOf("--port");-1!==S&&i[S+1]&&(C=parseInt(i[S+1],10),isNaN(C)&&(console.error("\u274c Invalid port number"),process.exit(1)))
1640
+ const k=i.filter(((e,t)=>"--port"!==e&&(-1===S||t!==S+1)))
1641
+ if(0===k.length)return void await async function(r={}){const i=process.cwd(),l=(0,h.join)(i,E.ALCHEMY_DIR_NAME)
1251
1642
  await(0,u.mkdir)(l,{recursive:!0})
1252
- const C=(0,h.join)(l,"server.log"),{createLogger:j}=await Promise.resolve().then(e.t.bind(e,2782,23)),b=j({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:C,baseDir:i}),E=await a(b),T=new f.TokenManager(b,i,{remoteServerUrl:E,allowInteractiveAuth:!1})
1253
- await T.initialize()
1254
- let A=await T.getToken()
1255
- if(!A?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
1256
- const e=await T.startAuthenticationFlow()
1257
- console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(T)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),A=await T.getToken(),console.log(`\u2705 Welcome, ${A?.username||"user"}!`),console.log("")}let P=A?.token
1258
- await async function(e,t,s,o){if(r(e))return
1643
+ const C=(0,h.join)(l,"server.log"),{createLogger:S}=await Promise.resolve().then(e.t.bind(e,2782,23)),k=S({serviceName:"alchemy-server",console:!1,logToFile:!0,logFilePath:C,baseDir:i}),T=await a(k),_=new m.TokenManager(k,i,{remoteServerUrl:T,allowInteractiveAuth:!1})
1644
+ await _.initialize()
1645
+ let j=await _.getToken()
1646
+ if(!j?.token){console.log(""),console.log("\u{1f511} Not logged in. Opening browser to authenticate...")
1647
+ const e=await _.startAuthenticationFlow()
1648
+ console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log(""),console.log("\u{1f446} Browser window opened. Complete login there."),console.log("\u23f1\ufe0f Waiting for authentication..."),await n(_)||(console.error("\u274c Authentication timed out. Run `alchemy` again to retry."),process.exit(1)),j=await _.getToken(),console.log(`\u2705 Welcome, ${j?.username||"user"}!`),console.log("")}let A=j?.token
1649
+ await async function(e,t,r,o){if(s(e))return
1259
1650
  console.log("\u{1f4c1} Initializing AlchemyLab project..."),console.log("")
1260
- const n=(0,h.join)(e,S.ALCHEMY_DIR_NAME)
1651
+ const n=(0,h.join)(e,E.ALCHEMY_DIR_NAME)
1261
1652
  let i
1262
- await(0,u.mkdir)(n,{recursive:!0}),s?(console.log(`\u{1f4cb} Registering project "${(0,h.basename)(e)||"Local Project"}"...`),i=await c(t,s,e,o),console.log(i?"\u2705 Project registered: "+i:"\u26a0\ufe0f Project registration failed (will retry next run)")):console.log("\u26a0\ufe0f No auth token \u2014 skipping project registration")
1263
- const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,E,P,b)
1264
- const M=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"project.json")
1265
- let _
1266
- try{const e=JSON.parse((0,d.readFileSync)(M,"utf-8"))
1267
- if(_=e.id||void 0,!1===e.registeredRemotely&&P){const t=await c(E,P,i,b,"cli-init-retry")
1268
- t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(M,JSON.stringify(e,null,2),"utf-8"),_=t,console.log("\u2705 Project registered: "+t))}}catch{b.warn("Could not read project.json")}await async function(t){try{return void(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let s="latest"
1269
- try{const e=(0,h.join)(t,S.ALCHEMY_DIR_NAME,"config.json")
1653
+ await(0,u.mkdir)(n,{recursive:!0}),r?(console.log(`\u{1f4cb} Registering project "${(0,h.basename)(e)||"Local Project"}"...`),i=await c(t,r,e,o),console.log(i?"\u2705 Project registered: "+i:"\u26a0\ufe0f Project registration failed (will retry next run)")):console.log("\u26a0\ufe0f No auth token \u2014 skipping project registration")
1654
+ const a=(0,h.join)(n,"project.json");(0,d.writeFileSync)(a,JSON.stringify({id:i||null,name:(0,h.basename)(e)||"Local Project",status:"active",registeredRemotely:!!i,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:n}},null,2),"utf-8"),console.log("")}(i,T,A,k)
1655
+ const P=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"project.json")
1656
+ let M
1657
+ try{const e=JSON.parse((0,d.readFileSync)(P,"utf-8"))
1658
+ if(M=e.id||void 0,!1===e.registeredRemotely&&A){const t=await c(T,A,i,k,"cli-init-retry")
1659
+ t&&(e.id=t,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,d.writeFileSync)(P,JSON.stringify(e,null,2),"utf-8"),M=t,console.log("\u2705 Project registered: "+t))}}catch{k.warn("Could not read project.json")}await async function(t){try{return void(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"})}catch{}let r="latest"
1660
+ try{const e=(0,h.join)(t,E.ALCHEMY_DIR_NAME,"config.json")
1270
1661
  if((0,d.existsSync)(e)){const t=JSON.parse((0,d.readFileSync)(e,"utf-8"))
1271
- t.opencodeVersion&&(s=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
1272
- const r="latest"===s?"latest":"v"+s
1273
- try{(0,g.execFileSync)("which",["go"],{stdio:"pipe"}),console.log(" Running: go install github.com/sst/opencode@"+r),(0,g.execFileSync)("go",["install","github.com/sst/opencode@"+r],{stdio:"inherit",timeout:12e4})
1274
- try{return(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"}),void console.log("\u2705 OpenCode installed successfully via go install")}catch{console.log("\u26a0\ufe0f go install completed but opencode not found in PATH"),console.log(" Ensure $(go env GOPATH)/bin is in your PATH")}}catch{}try{const{platform:t,arch:r}=await Promise.resolve().then(e.t.bind(e,857,23)),o=t(),n=r(),i="darwin"===o?"darwin":"linux"===o?"linux":null,a="arm64"===n?"arm64":"x64"===n?"amd64":null
1275
- if(i&&a){const e=`opencode_${i}_${a}`,t="latest"===s?"https://github.com/sst/opencode/releases/latest/download/"+e:`https://github.com/sst/opencode/releases/download/v${s}/${e}`
1662
+ t.opencodeVersion&&(r=t.opencodeVersion)}}catch{}console.log(""),console.log("\u{1f4e6} OpenCode not found in PATH. Installing...")
1663
+ const s="latest"===r?"latest":"v"+r
1664
+ try{(0,g.execFileSync)("which",["go"],{stdio:"pipe"}),console.log(" Running: go install github.com/sst/opencode@"+s),(0,g.execFileSync)("go",["install","github.com/sst/opencode@"+s],{stdio:"inherit",timeout:12e4})
1665
+ try{return(0,g.execFileSync)("which",["opencode"],{stdio:"pipe"}),void console.log("\u2705 OpenCode installed successfully via go install")}catch{console.log("\u26a0\ufe0f go install completed but opencode not found in PATH"),console.log(" Ensure $(go env GOPATH)/bin is in your PATH")}}catch{}try{const{platform:t,arch:s}=await Promise.resolve().then(e.t.bind(e,857,23)),o=t(),n=s(),i="darwin"===o?"darwin":"linux"===o?"linux":null,a="arm64"===n?"arm64":"x64"===n?"amd64":null
1666
+ if(i&&a){const e=`opencode_${i}_${a}`,t="latest"===r?"https://github.com/sst/opencode/releases/latest/download/"+e:`https://github.com/sst/opencode/releases/download/v${r}/${e}`
1276
1667
  console.log(" Downloading from: "+t)
1277
- const r=(0,h.join)((0,p.homedir)(),".local","bin")
1278
- await(0,u.mkdir)(r,{recursive:!0})
1279
- const o=(0,h.join)(r,"opencode")
1280
- return(0,g.execFileSync)("curl",["-fsSL","-o",o,t],{stdio:"inherit",timeout:6e4}),(0,g.execFileSync)("chmod",["+x",o],{stdio:"pipe"}),(process.env.PATH||"").split(":").includes(r)||(console.log(` \u26a0\ufe0f Add ${r} to your PATH:`),console.log(` export PATH="${r}:$PATH"`),console.log(""),process.env.PATH=`${r}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,I.getErrorMessage)(e))}console.log(""),console.log("\u26a0\ufe0f Could not auto-install OpenCode."),console.log(""),console.log(" Install manually:"),console.log(" go install github.com/sst/opencode@latest"),console.log(" Or download from:"),console.log(" https://github.com/sst/opencode/releases"),console.log("")}(i)
1668
+ const s=(0,h.join)((0,p.homedir)(),".local","bin")
1669
+ await(0,u.mkdir)(s,{recursive:!0})
1670
+ const o=(0,h.join)(s,"opencode")
1671
+ return(0,g.execFileSync)("curl",["-fsSL","-o",o,t],{stdio:"inherit",timeout:6e4}),(0,g.execFileSync)("chmod",["+x",o],{stdio:"pipe"}),(process.env.PATH||"").split(":").includes(s)||(console.log(` \u26a0\ufe0f Add ${s} to your PATH:`),console.log(` export PATH="${s}:$PATH"`),console.log(""),process.env.PATH=`${s}:${process.env.PATH}`),void console.log("\u2705 OpenCode installed successfully")}}catch(e){console.log("\u26a0\ufe0f Binary download failed: "+(0,b.getErrorMessage)(e))}console.log(""),console.log("\u26a0\ufe0f Could not auto-install OpenCode."),console.log(""),console.log(" Install manually:"),console.log(" go install github.com/sst/opencode@latest"),console.log(" Or download from:"),console.log(" https://github.com/sst/opencode/releases"),console.log("")}(i)
1281
1672
  const O=t()
1282
1673
  console.log(["\n .oo 8 8 o 8 \n .P 8 8 8 8 8 \n .P 8 8 .oPYo. 8oPYo. .oPYo. ooYoYo. o o 8 .oPYo. 8oPYo. \n oPooo8 8 8 ' 8 8 8oooo8 8' 8 8 8 8 8 .oooo8 8 8 \n .P 8 8 8 . 8 8 8. 8 8 8 8 8 8 8 8 8 8 \n.P 8 8 `YooP' 8 8 `Yooo' 8 8 8 `YooP8 8oooo `YooP8 `YooP' \n..:::::....:.....:..:::..:.....:..:..:..:....8 ......:.....::.....:","::::::::::::::::::::::::::::::::::::::::::ooP'.::::::::::::::::::: v"+O,"::::::::::::::::::::::::::::::::::::::::::...::::::::::::::::::::::\n"].join("\n")),console.log("\u{1f4c1} Project: "+i)
1283
- const x=s.port||await(0,k.findAvailablePort)(3456),$=new y.AlchemyActionServer(b,{projectPath:i,remoteServerUrl:E,port:x,authToken:P})
1284
- try{const t=(0,v.getInstallContext)(),s=new m.MCPClientConfigurer(b,E,P),r=new w.UnifiedLauncher(b,t,s),[,a,c]=await Promise.all([$.start(),(async()=>{if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
1285
- if(E.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
1286
- try{const e=await fetch(E+"/health")
1674
+ const R=r.port||await(0,I.findAvailablePort)(3456),D=new y.AlchemyActionServer(k,{projectPath:i,remoteServerUrl:T,port:R,authToken:A,tokenManager:_})
1675
+ try{const t=(0,v.getInstallContext)(),r=new f.MCPClientConfigurer(k,T,A),s=new w.UnifiedLauncher(k,t,r),[,a,c]=await Promise.all([D.start(),(async()=>{if(process.env.FRONTEND_URL)return process.env.FRONTEND_URL
1676
+ if(T.includes("api.alchemylabcode.com"))return"https://alchemylabcode.com"
1677
+ try{const e=await fetch(T+"/health")
1287
1678
  if(e.ok){const t=await e.json()
1288
- if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return E.replace(/:\d+/,":3000")})(),r.launch({projectPath:i,settings:_?{projectId:_}:void 0}).catch((e=>(b.warn("Failed to launch OpenCode serve",{error:(0,I.getErrorMessage)(e)}),{success:!1,error:(0,I.getErrorMessage)(e)})))])
1289
- if(console.log(""),console.log(" Action server .... port "+$.getPort()),c.success&&"port"in c&&c.port){"pid"in c&&c.pid&&$.trackChildPid(c.pid),$.setOpenCodePort(c.port),console.log(" OpenCode serve ... port "+c.port)
1290
- const e=(0,h.join)(i,S.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),b.info("Wrote OpenCode config",{path:e,port:c.port})}else c.success?console.log(" OpenCode serve ... started"):console.log(" OpenCode serve ... failed (run manually: opencode serve)")
1679
+ if(t?.data?.frontendUrl)return t.data.frontendUrl}}catch{}return T.replace(/:\d+/,":3000")})(),s.launch({projectPath:i,settings:M?{projectId:M}:void 0}).catch((e=>(k.warn("Failed to launch OpenCode serve",{error:(0,b.getErrorMessage)(e)}),{success:!1,error:(0,b.getErrorMessage)(e)})))])
1680
+ if(console.log(""),console.log(" Action server .... port "+D.getPort()),c.success&&"port"in c&&c.port){"pid"in c&&c.pid&&D.trackChildPid(c.pid),D.setOpenCodePort(c.port),console.log(" OpenCode serve ... port "+c.port)
1681
+ const e=(0,h.join)(i,E.ALCHEMY_DIR_NAME,"opencode.json");(0,d.writeFileSync)(e,JSON.stringify({port:c.port,pid:"pid"in c?c.pid:void 0,startedAt:(new Date).toISOString()},null,2)),k.info("Wrote OpenCode config",{path:e,port:c.port})}else c.success?console.log(" OpenCode serve ... started"):console.log(" OpenCode serve ... failed (run manually: opencode serve)")
1291
1682
  console.log(""),console.log(` \u2192 Alchemy UI: \x1b]8;;${a}\x07${a}\x1b]8;;\x07`),console.log("")
1292
1683
  let l=!1,u=null
1293
- const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await $.stop(),process.exit(0))}
1684
+ const g=async()=>{l||(l=!0,u&&clearInterval(u),console.log(""),console.log("Shutting down..."),await D.stop(),process.exit(0))}
1294
1685
  process.on("SIGTERM",g),process.on("SIGINT",g)
1295
- const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),f=new p({projectPath:i,logger:b,remoteServerUrl:E,authToken:P,projectId:_})
1686
+ const{InteractiveShell:p}=await e.e(994).then(e.t.bind(e,5994,23)),m=new p({projectPath:i,logger:k,remoteServerUrl:T,authToken:A,projectId:M})
1296
1687
  let y=!1
1297
- u=setInterval((async()=>{if(!y)try{if(await T.hasValidToken())return
1688
+ u=setInterval((async()=>{if(!y)try{if(await _.hasValidToken())return
1298
1689
  y=!0,console.log(""),console.log("\u26a0\ufe0f Auth token expired. Opening browser to re-authenticate...")
1299
- const e=await T.startAuthenticationFlow()
1300
- if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(T)){const e=await T.getToken()
1301
- e?.token&&(P=e.token,$.updateAuthToken(P)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){b.warn("Token watchdog check failed",{error:(0,I.getErrorMessage)(e)})}finally{y=!1}}),3e5),await f.start(g)}catch(e){b.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:C})
1302
- const E=b[0].toLowerCase()
1303
- switch("--help"!==E&&"-h"!==E||(s(),process.exit(0)),"--version"!==E&&"-v"!==E||(console.log("alchemy v"+t()),process.exit(0)),E){case"serve":await async function(e={}){const s=process.cwd()
1304
- r(s)||(console.log("\u{1f4c1} Current directory is not an AlchemyLab project."),console.log(""),console.log("\u{1f4a1} Run `alchemy` to initialize this project."),console.log(""),process.exit(1))
1305
- const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:s}),n=t()
1306
- console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+s)
1307
- const i=await a(o),c=new f.TokenManager(o,s,{remoteServerUrl:i,allowInteractiveAuth:!1})
1690
+ const e=await _.startAuthenticationFlow()
1691
+ if(console.log("\u{1f310} "+e.authUrl),o(e.authUrl),console.log("\u23f1\ufe0f Waiting for re-authentication..."),await n(_)){const e=await _.getToken()
1692
+ e?.token&&(A=e.token,D.updateAuthToken(A)),console.log("\u2705 Re-authenticated successfully.")}else console.log("\u274c Re-authentication timed out. Some features may not work until you restart.")}catch(e){k.warn("Token watchdog check failed",{error:(0,b.getErrorMessage)(e)})}finally{y=!1}}),3e5),await m.start(g)}catch(e){k.error("Failed to start server",{error:(0,b.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,b.getErrorMessage)(e)),process.exit(1)}}({port:C})
1693
+ const T=k[0].toLowerCase()
1694
+ switch("--help"!==T&&"-h"!==T||(r(),process.exit(0)),"--version"!==T&&"-v"!==T||(console.log("alchemy v"+t()),process.exit(0)),T){case"serve":await async function(e={}){const r=process.cwd()
1695
+ s(r)||(console.log("\u{1f4c1} Current directory is not an AlchemyLab project."),console.log(""),console.log("\u{1f4a1} Run `alchemy` to initialize this project."),console.log(""),process.exit(1))
1696
+ const o=(0,l.createEnvLogger)({serviceName:"alchemy-server",console:!0,baseDir:r}),n=t()
1697
+ console.log(`\n\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n\u2551 AlchemyLab Server v${n.padEnd(10)} \u2551\n\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n`),console.log("\u{1f4c1} Project: "+r)
1698
+ const i=await a(o),c=new m.TokenManager(o,r,{remoteServerUrl:i,allowInteractiveAuth:!1})
1308
1699
  await c.initialize()
1309
1700
  const d=await c.getToken(),u=d?.token
1310
1701
  u||console.log("\u26a0\ufe0f No auth token. Some features may be limited. Run `alchemy` to authenticate.")
1311
- const h=e.port||await(0,k.findAvailablePort)(3456),g=new y.AlchemyActionServer(o,{projectPath:s,remoteServerUrl:i,port:h,authToken:u})
1702
+ const h=e.port||await(0,I.findAvailablePort)(3456),g=new y.AlchemyActionServer(o,{projectPath:r,remoteServerUrl:i,port:h,authToken:u,tokenManager:c})
1312
1703
  try{await g.start(),console.log(""),console.log("\u2705 Action server running!"),console.log(` Health: http://127.0.0.1:${g.getPort()}/health`),console.log(""),console.log("Press Ctrl+C to stop the server."),console.log("")
1313
1704
  let e=!1
1314
1705
  const t=async()=>{e||(e=!0,console.log(""),console.log("Shutting down action server..."),await g.stop(),process.exit(0))}
1315
- process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,I.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,I.getErrorMessage)(e)),process.exit(1)}}({port:C})
1706
+ process.on("SIGTERM",t),process.on("SIGINT",t),await new Promise((()=>{}))}catch(e){o.error("Failed to start server",{error:(0,b.getErrorMessage)(e)}),console.error("\u274c Failed to start: "+(0,b.getErrorMessage)(e)),process.exit(1)}}({port:C})
1316
1707
  break
1317
- case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),s=await a(t),r=new f.TokenManager(t,e,{remoteServerUrl:s,allowInteractiveAuth:!1})
1318
- await r.initialize(),await r.hasValidToken()?await r.removeToken()?console.log("Logged out successfully."):(console.error("Failed to remove auth token."),process.exit(1)):console.log("Not currently logged in.")}()
1708
+ case"logout":await async function(){const e=process.cwd(),t=(0,l.createEnvLogger)({serviceName:"alchemy-cli",console:!1,baseDir:e}),r=await a(t),s=new m.TokenManager(t,e,{remoteServerUrl:r,allowInteractiveAuth:!1})
1709
+ await s.initialize(),await s.hasValidToken()?await s.removeToken()?console.log("Logged out successfully."):(console.error("Failed to remove auth token."),process.exit(1)):console.log("Not currently logged in.")}()
1319
1710
  break
1320
- default:console.error("\u274c Unknown command: "+E),console.log(""),s(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()
1711
+ default:console.error("\u274c Unknown command: "+T),console.log(""),r(),process.exit(1)}})().catch((e=>{console.error("\u{1f4a5} Unexpected error:",e),process.exit(1)}))})(),module.exports=i})()