alchemylab 2.0.2 → 2.1.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.
@@ -1,5 +1,5 @@
1
1
  "use strict"
2
- exports.id=674,exports.ids=[674],exports.modules={75674:(e,exports,t)=>{async function o(e,t){if(t){if(!n||Date.now()-n.at>i)try{const t=await e.apiClient.get(`/api/projects/${e.projectId}/session-request-associations?activeOnly=true&limit=500`),o=t?.data??t??[],r=new Map
2
+ exports.id=111,exports.ids=[111],exports.modules={56111:(e,exports,t)=>{async function o(e,t){if(t){if(!n||Date.now()-n.at>i)try{const t=await e.apiClient.get(`/api/projects/${e.projectId}/session-request-associations?activeOnly=true&limit=500`),o=t?.data??t??[],r=new Map
3
3
  for(const e of o)e?.opencodeSessionId&&e?.requestLocalId&&r.set(e.opencodeSessionId,e.requestLocalId)
4
4
  n={at:Date.now(),bySession:r}}catch(t){e.logger.debug?.("MCP receipt: could not read associations",{error:(0,r.getErrorMessage)(t)}),n=n??{at:Date.now(),bySession:new Map}}return n.bySession.get(t)}}exports.requestForSession=o,exports.openMcpReceipt=async function(e,t){const n=await o(e,t.sessionId??""),i=t.target?` (${t.target})`:"",c=`${t.tool} on the "${t.connection}" MCP server${i}`,d=t.allowed?`Called ${c}`:`Did not run ${c}: ${t.reason}`,l={connection:t.connection,tool:t.tool,target:t.target??null,gate:t.verdict,status:t.allowed?"started":"refused",sessionId:t.sessionId??null,callId:t.callId??null,startedAt:(new Date).toISOString()}
5
5
  try{const o=await e.apiClient.post(`/api/projects/${e.projectId}/tool-records`,{...n?{requestId:n}:{},type:"mcp-call",...t.sessionId?{cliOrigin:{providerId:"opencode",sessionId:t.sessionId,createdAt:(new Date).toISOString()}}:{},metadata:l,note:d}),r=o?.data?.id??o?.id
@@ -10,6 +10,6 @@ s.delete(t)}}())}catch(o){e.logger.error("MCP receipt could not be written",{con
10
10
  let o=s.get(t.callId)
11
11
  if(o||(await new Promise((e=>setTimeout(e,250))),o=s.get(t.callId)),o){s.delete(t.callId)
12
12
  try{await e.apiClient.patch(`/api/projects/${e.projectId}/tool-records/${o.recordId}`,{metadata:{...o.metadata,status:t.ok?"completed":"failed",durationMs:Date.now()-o.startedAt,finishedAt:(new Date).toISOString(),...t.outputPreview?{outputPreview:t.outputPreview.slice(0,500)}:{},...t.error?{error:String(t.error).slice(0,500)}:{}}})}catch(o){e.logger.warn("MCP receipt could not be closed",{callId:t.callId,error:(0,r.getErrorMessage)(o)})}}}
13
- const r=t(21319),s=new Map,a=500
13
+ const r=t(58824),s=new Map,a=500
14
14
  let n=null
15
15
  const i=3e4}}
@@ -1,79 +1,79 @@
1
1
  "use strict"
2
- exports.id=19,exports.ids=[19],exports.modules={16554:(e,exports,t)=>{function n(e,t,n){if(T)try{const s=`[${(new Date).toISOString()}] ${t}${n?" "+JSON.stringify(n):""}\n`,o=(0,x.join)(e.basePath||process.cwd(),".alchemy_lab","spawn-debug.log");(0,k.appendFileSync)(o,s)}catch{}}async function s(e,n,s,o,i,r){if(!(e&&n&&s&&o))return null
2
+ exports.id=24,exports.ids=[24],exports.modules={51725:(e,exports,t)=>{function n(e,t,n){if(q)try{const s=`[${(new Date).toISOString()}] ${t}${n?" "+JSON.stringify(n):""}\n`,o=(0,v.join)(e.basePath||process.cwd(),".alchemy_lab","spawn-debug.log");(0,x.appendFileSync)(o,s)}catch{}}async function s(e,n,s,o,i,r){if(!(e&&n&&s&&o))return null
3
3
  try{const i=await e.proxyPost(`/api/projects/${n}/requests/${s}/pickup-compose`,{sessionId:o}),a=i?.data?.text??i?.text,l="string"==typeof a&&a.trim()?a.trim():null
4
4
  if(l&&/Project map/.test(l))return l
5
5
  let c=null
6
- try{const{generateProjectMap:e}=await t.e(491).then(t.t.bind(t,23491,23))
7
- c=e(r||process.cwd())}catch{c=null}return l||c?[l,c].filter(Boolean).join("\n\n"):null}catch(e){return i.warn("pickup-compose failed; the association hook will deliver guidance as an inject",{requestId:s,error:(0,v.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:n,toSessionId:s,body:o,requestId:i,logger:r}=e
6
+ try{const{generateProjectMap:e}=await t.e(356).then(t.t.bind(t,69356,23))
7
+ c=e(r||process.cwd())}catch{c=null}return l||c?[l,c].filter(Boolean).join("\n\n"):null}catch(e){return i.warn("pickup-compose failed; the association hook will deliver guidance as an inject",{requestId:s,error:(0,b.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:n,toSessionId:s,body:o,requestId:i,logger:r}=e
8
8
  if(!t||!n)return
9
9
  const a=t.getSessionMessageClient()
10
10
  if(a)try{const e=await a.createInstruction({fromSessionId:n,toSessionId:s,body:o,...i?{requestId:i}:{}})
11
- return e?.messageCode}catch(e){return void r.warn("Failed to mint instruction code",{error:(0,v.getErrorMessage)(e),fromSessionId:n,toSessionId:s})}}async function i(e,t,n,s,o){if(e&&t&&n)try{await e.proxyPost(`/api/projects/${t}/session-messages/${encodeURIComponent(n)}/delivered`,{ok:s.ok,error:s.detail||s.error,messageId:s.messageId})}catch(e){o.warn("instruction delivery receipt not written",{instructionCode:n,error:(0,v.getErrorMessage)(e)})}}async function r(e,t,r,a,l){const{agent:c="work",prompt:d,requestId:u,allowParallel:g=!1,useExistingSession:p}=e
11
+ return e?.messageCode}catch(e){return void r.warn("Failed to mint instruction code",{error:(0,b.getErrorMessage)(e),fromSessionId:n,toSessionId:s})}}async function i(e,t,n,s,o){if(e&&t&&n)try{await e.proxyPost(`/api/projects/${t}/session-messages/${encodeURIComponent(n)}/delivered`,{ok:s.ok,error:s.detail||s.error,messageId:s.messageId})}catch(e){o.warn("instruction delivery receipt not written",{instructionCode:n,error:(0,b.getErrorMessage)(e)})}}async function r(e,t,r,a,l){const{agent:c="work",prompt:d,requestId:u,allowParallel:g=!1,useExistingSession:p}=e
12
12
  if(n(t,"handleSpawn called",{agent:c,promptLength:d.length,requestId:u,useExistingSession:p,cliOriginSessionId:l?.sessionId||null}),!(0,A.isValidAgent)(c))return{content:[{type:"text",text:`\u274c Unknown agent '${c}'.\n\n**Available agents:** ${(0,A.getAgentNames)().join(", ")}\n\nChoose one of the available agents listed above to see detailed descriptions of each agent.`}],isError:!0}
13
- const f=l?.sessionId,h=f?I.sessionIdentity.getAgentTypeFor(f).then((e=>e||void 0)):Promise.resolve(void 0),m=async function(e){try{const t=`http://localhost:${j}/api/agents/by-name/${encodeURIComponent(e)}`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
13
+ const f=l?.sessionId,h=f?S.sessionIdentity.getAgentTypeFor(f).then((e=>e||void 0)):Promise.resolve(void 0),m=async function(e){try{const t=`http://localhost:${T}/api/agents/by-name/${encodeURIComponent(e)}`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
14
14
  if(!n.ok)return null
15
15
  const s=await n.json()
16
- return s.success?s.data??null:null}catch{return null}}(c).catch((()=>null)),k=(0,y.getServerUrl)(t,r),x=(async()=>{if(!a||!u)return null
16
+ return s.success?s.data??null:null}catch{return null}}(c).catch((()=>null)),x=(0,y.getServerUrl)(t,r),v=(async()=>{if(!a||!u)return null
17
17
  const e=a.getSessionRequestAssociationClient()
18
- return e?await e.getSessionsForRequest(u,!0):null})().catch((e=>(r.debug("Failed to check for existing workers",{error:(0,v.getErrorMessage)(e)}),null))),[E,q,P,U]=await Promise.all([h,m,k,x])
18
+ return e?await e.getSessionsForRequest(u,!0):null})().catch((e=>(r.debug("Failed to check for existing workers",{error:(0,b.getErrorMessage)(e)}),null))),[E,j,P,R]=await Promise.all([h,m,x,v])
19
19
  try{if(n(t,"Spawn agent type check",{callerSessionId:f||null,callerAgentType:E||null,pid:process.pid}),E){const e=(0,A.getAllowedSubagents)(E)
20
- if(e&&!e.includes(c))return{content:[{type:"text",text:`\u274c Agent type '${E}' cannot spawn '${c}'.\n\n**Allowed subagents for ${E}:** ${e.join(", ")}\n\nUse one of the allowed agent types instead.`}],isError:!0}}}catch(e){n(t,"allowedSubagents check failed",{error:(0,v.getErrorMessage)(e)})}let R=q?.requestContextMode
21
- if(!R){const e=(0,A.getBuiltInAgentConfig)(c)
22
- R=e?.requestContextMode}if(R&&"none"!==R&&!u)return{content:[{type:"text",text:`\u274c Agent '${c}' requires a requestId to be provided.\n\nThis agent has requestContextMode: '${R}' which means it needs to work in the context of a specific request.\n\nPlease provide a requestId when spawning this agent.`}],isError:!0}
23
- if(u&&"required"===R&&a)try{const e=await a.getRequestClient().getRequest(u)
20
+ if(e&&!e.includes(c))return{content:[{type:"text",text:`\u274c Agent type '${E}' cannot spawn '${c}'.\n\n**Allowed subagents for ${E}:** ${e.join(", ")}\n\nUse one of the allowed agent types instead.`}],isError:!0}}}catch(e){n(t,"allowedSubagents check failed",{error:(0,b.getErrorMessage)(e)})}let M=j?.requestContextMode
21
+ if(!M){const e=(0,A.getBuiltInAgentConfig)(c)
22
+ M=e?.requestContextMode}if(M&&"none"!==M&&!u)return{content:[{type:"text",text:`\u274c Agent '${c}' requires a requestId to be provided.\n\nThis agent has requestContextMode: '${M}' which means it needs to work in the context of a specific request.\n\nPlease provide a requestId when spawning this agent.`}],isError:!0}
23
+ if(u&&"required"===M&&a)try{const e=await a.getRequestClient().getRequest(u)
24
24
  if("draft"===e?.status){const t=e?.intake,n="rejected"===t?.status?`the intake reviewer rejected it: ${String(t?.objection||"").slice(0,240)||"(no objection text)"}`:"pending"===t?.status?"it is held at intake awaiting a verdict":"it has not been registered"
25
- return{content:[{type:"text",text:`\u274c ${u} is a DRAFT: ${n}.\n\nWork cannot start on drafts. Wait for the request to be registered (the user resolves the intake question from the timeline), or address the objection with a revised proposal. Do NOT spawn workers for draft requests.`}],isError:!0}}}catch(e){n(t,"draft-spawn guard read failed (continuing)",{error:(0,v.getErrorMessage)(e)})}if(n(t,"serverUrl resolved",{serverUrl:P}),!t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath&&!t.basePath)throw new w.ValidationError("Working directory not available in context")
26
- const M=l?.sessionId
27
- let O=""
28
- if(U){const e=(M?U.filter((e=>e.opencodeSessionId!==M)):U).filter((e=>!1!==e.working))
25
+ return{content:[{type:"text",text:`\u274c ${u} is a DRAFT: ${n}.\n\nWork cannot start on drafts. Wait for the request to be registered (the user resolves the intake question from the timeline), or address the objection with a revised proposal. Do NOT spawn workers for draft requests.`}],isError:!0}}}catch(e){n(t,"draft-spawn guard read failed (continuing)",{error:(0,b.getErrorMessage)(e)})}if(n(t,"serverUrl resolved",{serverUrl:P}),!t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath&&!t.basePath)throw new w.ValidationError("Working directory not available in context")
26
+ const U=l?.sessionId
27
+ let D=""
28
+ if(R){const e=(U?R.filter((e=>e.opencodeSessionId!==U)):R).filter((e=>!1!==e.working))
29
29
  if(e.length>0){const t=e.map((e=>e.opencodeSessionId)).join(", ")
30
30
  if(!g)return r.warn("Blocked spawn: request already has active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId))}),{content:[{type:"text",text:`\u274c Cannot spawn: Request ${u} already has ${e.length} active worker(s): ${t}\n\nOptions:\n\u2022 Use \`useExistingSession: "${e[0].opencodeSessionId}"\` to send work to the existing session\n\u2022 Use \`allowParallel: true\` if you really need multiple workers on the same request`}],isError:!0}
31
- O=`\u26a0\ufe0f Warning: Request ${u} already has ${e.length} active worker(s): ${t}. Proceeding anyway (allowParallel=true).\n\n`,r.warn("Spawning parallel subagent for request with existing active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId)),allowParallel:!0})}}let D,L,_=d
32
- u&&(D=`requests/${u}`,n(t,"Setting initialBranch for agent",{agent:c,requestId:u,initialBranch:D,agentRequestContextMode:R||"undefined"}),_="readonly"===R?`Working on request ${u} (READONLY mode - you cannot modify files, only explore and report).\n\n${d}`:`Working on request ${u}.\n\n${d}`)
33
- let F,N=!1
34
- if(p){if(L=p,r.info("Using existing session for spawn",{sessionId:L,requestId:u}),!P)throw new w.ValidationError("Could not determine OpenCode server URL")
31
+ D=`\u26a0\ufe0f Warning: Request ${u} already has ${e.length} active worker(s): ${t}. Proceeding anyway (allowParallel=true).\n\n`,r.warn("Spawning parallel subagent for request with existing active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map((e=>e.opencodeSessionId)),allowParallel:!0})}}let L,O,F=d
32
+ u&&(L=`requests/${u}`,n(t,"Setting initialBranch for agent",{agent:c,requestId:u,initialBranch:L,agentRequestContextMode:M||"undefined"}),F="readonly"===M?`Working on request ${u} (READONLY mode - you cannot modify files, only explore and report).\n\n${d}`:`Working on request ${u}.\n\n${d}`)
33
+ let N,_=!1
34
+ if(p){if(O=p,r.info("Using existing session for spawn",{sessionId:O,requestId:u}),!P)throw new w.ValidationError("Could not determine OpenCode server URL")
35
35
  try{let e
36
- try{const t=await fetch(`http://localhost:${j}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
36
+ try{const t=await fetch(`http://localhost:${T}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
37
37
  if(t.ok){const n=await t.json()
38
38
  if(n.data?.defaultModel){const[t,...s]=n.data.defaultModel.split("/")
39
- e={providerID:t,modelID:s.join("/")}}}}catch{}F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:L,body:_,requestId:u,logger:r})
40
- let n=_
41
- const d=await s(a,t.projectId,u,L,r,t.mainBasePath||t.basePath)
39
+ e={providerID:t,modelID:s.join("/")}}}}catch{}N=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:O,body:F,requestId:u,logger:r})
40
+ let n=F
41
+ const d=await s(a,t.projectId,u,O,r,t.mainBasePath||t.basePath)
42
42
  d&&(n=`${d}\n\n---\n\n${n}`)
43
- const g={body:_,fromSessionId:l?.sessionId,fromAgent:E,...u?{requestId:u}:{},...F?{instructionCode:F}:{}},p=await(0,S.deliverToSession)(L,n,{kind:"agent-delegation",partPayload:g})
44
- if(await i(a,t.projectId,F,p,r),!p.ok)throw new Error((0,S.deliveryFailureText)(L,p))
45
- r.info("Prompt delivered to existing session",{sessionId:L,agent:c,messageId:p.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,v.getErrorMessage)(e)}`)}}else{N=!0,n(t,"Creating session via spawn-session endpoint")
46
- const i=M
47
- T&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:i})
48
- try{const p=`http://localhost:${j}/api/spawn-session`,f=await fetch(p,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:c,prompt:d,requestId:u,parentSessionId:i,title:e.title,allowParallel:g,callerAgentType:E})})
43
+ const g={body:F,fromSessionId:l?.sessionId,fromAgent:E,...u?{requestId:u}:{},...N?{instructionCode:N}:{}},p=await(0,I.deliverToSession)(O,n,{kind:"agent-delegation",partPayload:g})
44
+ if(await i(a,t.projectId,N,p,r),!p.ok)throw new Error((0,I.deliveryFailureText)(O,p))
45
+ r.info("Prompt delivered to existing session",{sessionId:O,agent:c,messageId:p.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,b.getErrorMessage)(e)}`)}}else{_=!0,n(t,"Creating session via spawn-session endpoint")
46
+ const i=U
47
+ q&&(0,k.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:i})
48
+ try{const p=`http://localhost:${T}/api/spawn-session`,f=await fetch(p,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:c,prompt:d,requestId:u,parentSessionId:i,title:e.title,allowParallel:g,callerAgentType:E})})
49
49
  if(!f.ok){const e=await f.text().catch((()=>"Unknown error"))
50
50
  throw new Error(`Spawn session endpoint failed: ${f.status} ${e}`)}const h=await f.json()
51
51
  if(!h.success||!h.data)throw new Error(h.error||"Spawn session returned unsuccessful")
52
- const{sessionId:m,model:w,serverUrl:y,initialBranch:S}=h.data
53
- D=S,F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:m,body:_,requestId:u,logger:r})
54
- let I=_
55
- const b="pickupGuidance"in(h?.data??{})?h.data.pickupGuidance??null:await s(a,t.projectId,u,m,r,t.mainBasePath||t.basePath)
56
- b&&(I=`${b}\n\n---\n\n${I}`)
57
- const v=await(0,$.deliverInitialPrompt)({serverUrl:y,sessionId:m,agent:c,prompt:I,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:_,fromSessionId:l?.sessionId,fromAgent:E,...u?{requestId:u}:{},...F?{instructionCode:F}:{}}}}})
58
- if(!v.success)throw new Error(`Failed to spawn subagent: ${v.error}`)
59
- L=m,n(t,"Spawn successful via unified endpoint",{sessionId:m,parentSessionId:i,model:w,instructionCode:F}),r.info("Background subagent spawned via unified endpoint",{agent:c,requestId:u,sessionId:m,parentSessionId:i,instructionCode:F})}catch(e){throw new Error(`Failed to spawn subagent: ${(0,v.getErrorMessage)(e)}`)}}let B=O
60
- B+=p?`Prompt sent to existing session ${L}.`:`Background ${c} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),F&&(B+=`\nInstruction code: ${F} (the agent will cite this on its reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
61
- const V=!!L&&"unknown"!==L&&"spawning"!==L
62
- return{content:[{type:"text",text:B}],...(0,C.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:V?L:void 0,agentType:c,requestId:u||void 0,description:e.title||d.slice(0,50)||c,...F?{instructionCode:F}:{}}),...F?{instructionCode:F}:{}}}async function a(e,t,n,s,r,a,l){if(!await(0,y.getServerUrl)(n,s))throw new w.ValidationError("Could not determine OpenCode server URL")
52
+ const{sessionId:m,model:w,serverUrl:y,initialBranch:I}=h.data
53
+ L=I,N=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:m,body:F,requestId:u,logger:r})
54
+ let S=F
55
+ const k="pickupGuidance"in(h?.data??{})?h.data.pickupGuidance??null:await s(a,t.projectId,u,m,r,t.mainBasePath||t.basePath)
56
+ k&&(S=`${k}\n\n---\n\n${S}`)
57
+ const b=await(0,$.deliverInitialPrompt)({serverUrl:y,sessionId:m,agent:c,prompt:S,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:F,fromSessionId:l?.sessionId,fromAgent:E,...u?{requestId:u}:{},...N?{instructionCode:N}:{}}}}})
58
+ if(!b.success)throw new Error(`Failed to spawn subagent: ${b.error}`)
59
+ O=m,n(t,"Spawn successful via unified endpoint",{sessionId:m,parentSessionId:i,model:w,instructionCode:N}),r.info("Background subagent spawned via unified endpoint",{agent:c,requestId:u,sessionId:m,parentSessionId:i,instructionCode:N})}catch(e){throw new Error(`Failed to spawn subagent: ${(0,b.getErrorMessage)(e)}`)}}let B=D
60
+ B+=p?`Prompt sent to existing session ${O}.`:`Background ${c} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),N&&(B+=`\nInstruction code: ${N} (the agent will cite this on its reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
61
+ const W=!!O&&"unknown"!==O&&"spawning"!==O
62
+ return{content:[{type:"text",text:B}],...(0,C.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:W?O:void 0,agentType:c,requestId:u||void 0,description:e.title||d.slice(0,50)||c,...N?{instructionCode:N}:{}}),...N?{instructionCode:N}:{}}}async function a(e,t,n,s,r,a,l){if(!await(0,y.getServerUrl)(n,s))throw new w.ValidationError("Could not determine OpenCode server URL")
63
63
  if(s.info("Sending message to subagent session",{sessionId:e,promptLength:t.length}),r)try{const t=l?.sessionId
64
64
  if(t){const n=r.getCLISessionClient()
65
65
  if(n){let o=await n.getByExternalId(t)
66
66
  if(o||(o=await n.createOrUpdateSession({externalSessionId:t,providerId:"opencode",status:"active"}),s.info("Created caller session in DB for re-parenting",{callerSessionId:t,dbId:o?.id})),o){const i=await n.getByExternalId(e).catch((()=>null)),r=i?.parentSessionId??null
67
- r===o.id?s.debug("Parent unchanged for continued agent; no re-parent write",{sessionId:e}):(await n.createOrUpdateSession({externalSessionId:e,providerId:"opencode",parentSessionId:o.id,status:"active"}),s.info("RE-PARENTED continued agent: reports now flow to the current caller",{sessionId:e,fromParentDbId:r,toParentDbId:o.id,toCallerSessionId:t}))}}}}catch(e){s.warn("Failed to update parent session - reports may go to wrong parent",{error:(0,v.getErrorMessage)(e)})}let c=a
68
- if(!c)try{const t=`http://localhost:${j}/api/sessions/${encodeURIComponent(e)}/agent-type`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
67
+ r===o.id?s.debug("Parent unchanged for continued agent; no re-parent write",{sessionId:e}):(await n.createOrUpdateSession({externalSessionId:e,providerId:"opencode",parentSessionId:o.id,status:"active"}),s.info("RE-PARENTED continued agent: reports now flow to the current caller",{sessionId:e,fromParentDbId:r,toParentDbId:o.id,toCallerSessionId:t}))}}}}catch(e){s.warn("Failed to update parent session - reports may go to wrong parent",{error:(0,b.getErrorMessage)(e)})}let c=a
68
+ if(!c)try{const t=`http://localhost:${T}/api/sessions/${encodeURIComponent(e)}/agent-type`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
69
69
  if(n.ok){const e=await n.json()
70
70
  e.success&&e.agentType&&(c=e.agentType)}}catch{s.warn("Agent type lookup failed for session",{sessionId:e})}if(!c)throw new w.ValidationError(`Cannot continue session ${e}: unable to determine agent type. The session's agent type could not be looked up from the action server.`)
71
71
  try{let a
72
- try{const e=await fetch(`http://localhost:${j}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
72
+ try{const e=await fetch(`http://localhost:${T}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
73
73
  if(e.ok){const t=await e.json()
74
74
  if(t.data?.defaultModel){const[e,...n]=t.data.defaultModel.split("/")
75
- a={providerID:e,modelID:n.join("/")}}}}catch{}const d=await o({apiClient:r,fromSessionId:l?.sessionId,toSessionId:e,body:t,logger:s}),u=t,g={body:t,fromSessionId:l?.sessionId,fromAgent:await I.sessionIdentity.getAgentTypeFor(l?.sessionId)||void 0,...d?{instructionCode:d}:{}},p=await(0,S.deliverToSession)(e,u,{kind:"agent-followup",partPayload:g})
76
- return await i(r,n.projectId,d,p,s),p.ok?(s.info("Message delivered to subagent session",{sessionId:e,agent:c,instructionCode:d,messageId:p.messageId}),{content:[{type:"text",text:[d?`Message delivered to session ${e} (${d}); it is in that session's transcript.`:`Message delivered to session ${e}; it is in that session's transcript.`,c?`That session is a ${c}. It will process this in the background -- it is NOT proof that work started; check for tasks before reporting progress.`:"Role unknown for that session. It will process this in the background -- that is not proof work started; check for tasks before reporting progress."].join("\n")}],...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d}},instructionCode:d}:{}}):{content:[{type:"text",text:(0,S.deliveryFailureText)(e,p)+(c?` That session is a ${c}.`:"")}],isError:!0,...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d,failed:!0}},instructionCode:d}:{}}}catch(e){throw new Error(`Failed to continue session: ${(0,v.getErrorMessage)(e)}`)}}function l(e){const t=Math.floor(e/1e3)
75
+ a={providerID:e,modelID:n.join("/")}}}}catch{}const d=await o({apiClient:r,fromSessionId:l?.sessionId,toSessionId:e,body:t,logger:s}),u=t,g={body:t,fromSessionId:l?.sessionId,fromAgent:await S.sessionIdentity.getAgentTypeFor(l?.sessionId)||void 0,...d?{instructionCode:d}:{}},p=await(0,I.deliverToSession)(e,u,{kind:"agent-followup",partPayload:g})
76
+ return await i(r,n.projectId,d,p,s),p.ok?(s.info("Message delivered to subagent session",{sessionId:e,agent:c,instructionCode:d,messageId:p.messageId}),{content:[{type:"text",text:[d?`Message delivered to session ${e} (${d}); it is in that session's transcript.`:`Message delivered to session ${e}; it is in that session's transcript.`,c?`That session is a ${c}. It will process this in the background -- it is NOT proof that work started; check for tasks before reporting progress.`:"Role unknown for that session. It will process this in the background -- that is not proof work started; check for tasks before reporting progress."].join("\n")}],...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d}},instructionCode:d}:{}}):{content:[{type:"text",text:(0,I.deliveryFailureText)(e,p)+(c?` That session is a ${c}.`:"")}],isError:!0,...d?{_meta:{alchemy:{kind:"continue-subagent",instructionCode:d,failed:!0}},instructionCode:d}:{}}}catch(e){throw new Error(`Failed to continue session: ${(0,b.getErrorMessage)(e)}`)}}function l(e){const t=Math.floor(e/1e3)
77
77
  if(t<60)return`${t}s`
78
78
  const n=Math.floor(t/60)
79
79
  return n<60?`${n}m ${t%60}s`:`${Math.floor(n/60)}h ${n%60}m`}function c(e){const t=[]
@@ -83,7 +83,7 @@ const i=s.path||s.file||s.directory,r=s.needle||s.query||s.pattern
83
83
  "string"==typeof i?o+=` ${i.split("/").pop()||i}`:"string"==typeof r&&(o+=` "${r.length>30?r.slice(0,27)+"...":r}"`),t.push({name:o,state:e.state.status,time:n.info.time.created})}t.sort(((e,t)=>t.time-e.time))
84
84
  const n=t.find((e=>"pending"===e.state||"running"===e.state)),s=t.filter((e=>"completed"===e.state||"error"===e.state)).slice(0,3),o=t.length>0?t[0].time:null
85
85
  return{current:n?.name??null,recent:s.map((e=>e.name)),lastActivityTime:o}}function d(e){return e<1e3?"<1s ago":e<6e4?`${Math.floor(e/1e3)}s ago`:e<36e5?`${Math.floor(e/6e4)}m ago`:`${Math.floor(e/36e5)}h ago`}function u(e,t){const n=e||t,s=Date.now()-n
86
- if(e&&s<q)return{status:"working",detail:`last action ${d(s)}`,isReady:!1}
86
+ if(e&&s<j)return{status:"working",detail:`last action ${d(s)}`,isReady:!1}
87
87
  const o=d(s).replace(/ ago$/,"")
88
88
  return s>P&&e?{status:"stalled",detail:`no activity for ${o}`,isReady:!0}:e?{status:"idle",detail:`no activity for ${o}`,isReady:!0}:{status:"idle",detail:"no tool activity",isReady:!0}}function g(e){let t=0,n=0,s=0
89
89
  for(const o of e.trim().split("\n")){if(!o)continue
@@ -100,14 +100,14 @@ if(h.ok){const e=await h.json()
100
100
  y=e.length
101
101
  for(const t of e)"assistant"===t.info.role&&t.info.tokens&&(m.input+=t.info.tokens.input||0,m.output+=t.info.tokens.output||0,m.reasoning+=t.info.tokens.reasoning||0)
102
102
  const t=c(e)
103
- $=u(t.lastActivityTime,d.time.updated),t.current&&(w+=`\nCurrent: ${t.current}`),t.recent.length>0&&(w+=`\nRecent: ${t.recent.join(", ")}`)}const S=Date.now()-d.time.created,I=$.status,b=$.isReady,v=m.input+m.output+m.reasoning
104
- let k="",x="",A=null
103
+ $=u(t.lastActivityTime,d.time.updated),t.current&&(w+=`\nCurrent: ${t.current}`),t.recent.length>0&&(w+=`\nRecent: ${t.recent.join(", ")}`)}const I=Date.now()-d.time.created,S=$.status,k=$.isReady,b=m.input+m.output+m.reasoning
104
+ let x="",v="",A=null
105
105
  if(o)try{const t=o.getSessionRequestAssociationClient()
106
106
  if(t){const n=await t.getActiveRequestsForSession(e,!1)
107
107
  if(n.length>0){const t=n.filter((e=>!e.endedAt)).sort(((e,t)=>new Date(t.startedAt).getTime()-new Date(e.startedAt).getTime())),o=n.filter((e=>e.endedAt)).sort(((e,t)=>new Date(t.endedAt).getTime()-new Date(e.endedAt).getTime())),i=o[0],r=t[0]
108
108
  i&&r?A=new Date(i.endedAt).getTime()>new Date(r.startedAt).getTime()?i.requestLocalId:r.requestLocalId:i?A=i.requestLocalId:r&&(A=r.requestLocalId)
109
109
  const a=[]
110
- t.length>0&&a.push(`Active: ${t.map((e=>e.requestLocalId)).join(", ")}`),o.length>0&&a.push(`Previous: ${o.map((e=>e.requestLocalId)).join(", ")}`),a.length>0&&(x=`\nRequests: ${a.join(" | ")}`),s.debug("Found request associations",{sessionId:e,activeCount:t.length,historicalCount:o.length})}}}catch(e){s.debug("Failed to get request associations, falling back to title extraction",{error:e instanceof Error?e.message:String(e)})}if(A||(A=function(e){const t=e.match(/\[(req\d{6})\]/)
110
+ t.length>0&&a.push(`Active: ${t.map((e=>e.requestLocalId)).join(", ")}`),o.length>0&&a.push(`Previous: ${o.map((e=>e.requestLocalId)).join(", ")}`),a.length>0&&(v=`\nRequests: ${a.join(" | ")}`),s.debug("Found request associations",{sessionId:e,activeCount:t.length,historicalCount:o.length})}}}catch(e){s.debug("Failed to get request associations, falling back to title extraction",{error:e instanceof Error?e.message:String(e)})}if(A||(A=function(e){const t=e.match(/\[(req\d{6})\]/)
111
111
  return t?t[1]:null}(d.title||"")),A&&n.basePath){const e=await async function(e,n){try{const{execFileSync:s}=await Promise.resolve().then(t.t.bind(t,35317,23)),o=`requests/${e}`
112
112
  let i=!1
113
113
  try{s("git",["rev-parse","--verify",o],{cwd:n,stdio:"pipe"}),i=!0}catch{i=!1}if(i){const e=s("git",["diff",`main...${o}`,"--numstat"],{cwd:n,encoding:"utf8"})
@@ -115,13 +115,13 @@ if(e.trim())return g(e)}try{const t=s("git",["log","main","--merges","--oneline"
115
115
  if(t){const e=t.split("\n")[0].split(" ")[0],o=s("git",["diff",`${e}^1...${e}`,"--numstat"],{cwd:n,encoding:"utf8"})
116
116
  if(o.trim())return{...g(o),merged:!0}}}catch{}return null}catch{return null}}(A,n.basePath)
117
117
  if(e){const t=e.additions+e.deletions,n=e.merged?" (merged)":""
118
- k=`\nDiff vs main (${A}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${n}`}}const C=p(U,`${n.currentSessionId??""}>${e}`,`${d.time.updated}:${y}:${v}`,Date.now())
119
- return{content:[{type:"text",text:`${C?`${C}\n\n`:""}Session ${e}\nTitle: ${d.title||"Untitled"}\nStatus: ${I} (${$.detail})\nReady for input: ${b?"Yes":"No"}\nAge: ${l(S)}\nTokens: ${v.toLocaleString()} (in: ${m.input.toLocaleString()}, out: ${m.output.toLocaleString()}${m.reasoning>0?`, reasoning: ${m.reasoning.toLocaleString()}`:""})${w}${x}${k}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,v.getErrorMessage)(e)}`)}}async function h(e,t,n){const s=await(0,y.getServerUrl)(t,n)
118
+ x=`\nDiff vs main (${A}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${n}`}}const C=p(R,`${n.currentSessionId??""}>${e}`,`${d.time.updated}:${y}:${b}`,Date.now())
119
+ return{content:[{type:"text",text:`${C?`${C}\n\n`:""}Session ${e}\nTitle: ${d.title||"Untitled"}\nStatus: ${S} (${$.detail})\nReady for input: ${k?"Yes":"No"}\nAge: ${l(I)}\nTokens: ${b.toLocaleString()} (in: ${m.input.toLocaleString()}, out: ${m.output.toLocaleString()}${m.reasoning>0?`, reasoning: ${m.reasoning.toLocaleString()}`:""})${w}${v}${x}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,b.getErrorMessage)(e)}`)}}async function h(e,t,n){const s=await(0,y.getServerUrl)(t,n)
120
120
  if(!s)throw new w.ValidationError("Could not determine OpenCode server URL")
121
121
  n.info("Aborting subagent session",{sessionId:e})
122
122
  try{const t=`${s.replace(/\/$/,"")}/session/${e}/abort`,o=await fetch(t,{signal:AbortSignal.timeout(8e3),method:"POST"})
123
123
  if(!o.ok){const e=await o.text().catch((()=>"Unknown error"))
124
- throw new Error(`Failed to abort session: ${o.status} ${e}`)}return n.info("Session aborted",{sessionId:e}),{content:[{type:"text",text:`Session ${e} has been aborted.`}]}}catch(e){throw new Error(`Failed to abort session: ${(0,v.getErrorMessage)(e)}`)}}async function m(e,t,n,s={}){const{activeOnly:o=!1,requestId:i,limit:r,minTokens:a,maxTokens:d,maxAgeHours:g}=s,p=await(0,y.getServerUrl)(e,t)
124
+ throw new Error(`Failed to abort session: ${o.status} ${e}`)}return n.info("Session aborted",{sessionId:e}),{content:[{type:"text",text:`Session ${e} has been aborted.`}]}}catch(e){throw new Error(`Failed to abort session: ${(0,b.getErrorMessage)(e)}`)}}async function m(e,t,n,s={}){const{activeOnly:o=!1,requestId:i,limit:r,minTokens:a,maxTokens:d,maxAgeHours:g}=s,p=await(0,y.getServerUrl)(e,t)
125
125
  if(!p)throw new w.ValidationError("Could not determine OpenCode server URL")
126
126
  t.info("Listing sessions",{filters:s})
127
127
  try{const e=`${p.replace(/\/$/,"")}/session?limit=1000`,t=await fetch(e,{signal:AbortSignal.timeout(8e3)})
@@ -134,12 +134,12 @@ let $=f
134
134
  if(o&&($=$.filter((e=>"idle"!==(w[e.id]?.type||"idle")))),void 0!==g){const e=60*g*60*1e3,t=Date.now()-e
135
135
  $=$.filter((e=>e.time.created>=t))}if(0===$.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
136
136
  let y={}
137
- const S=$.map((e=>e.id))
137
+ const I=$.map((e=>e.id))
138
138
  if(n)try{const e=n.getCLISessionClient()
139
- e&&(y=await e.batchEnrich(S))}catch{}const I=p.replace(/\/$/,""),b=await Promise.allSettled($.map((e=>fetch(`${I}/session/${e.id}/message`,{signal:AbortSignal.timeout(8e3)}).then((e=>e.ok?e.json():[])).catch((()=>[]))))),v=[]
139
+ e&&(y=await e.batchEnrich(I))}catch{}const S=p.replace(/\/$/,""),k=await Promise.allSettled($.map((e=>fetch(`${S}/session/${e.id}/message`,{signal:AbortSignal.timeout(8e3)}).then((e=>e.ok?e.json():[])).catch((()=>[]))))),b=[]
140
140
  for(let e=0;e<$.length;e++){const t=$[e],n=Date.now()-t.time.created
141
141
  let o=0,r=null,l=[],g=u(null,t.time.updated)
142
- const p=b[e],f="fulfilled"===p.status?p.value:[]
142
+ const p=k[e],f="fulfilled"===p.status?p.value:[]
143
143
  for(const e of f)"assistant"===e.info.role&&e.info.tokens&&(o+=(e.info.tokens.input||0)+(e.info.tokens.output||0)+(e.info.tokens.reasoning||0))
144
144
  if(f.length>0){const e=c(f)
145
145
  g=u(e.lastActivityTime,t.time.updated),r=e.current||null,l=e.recent}const h=g.status
@@ -149,19 +149,19 @@ const m=y[t.id]
149
149
  if(s.agentType&&(!m||m.agentType!==s.agentType))continue
150
150
  if("archived"===m?.status)continue
151
151
  let w=""
152
- const S=[],I=[]
153
- if(m?.associations&&I.push(...m.associations),I.length>0){const e=new Map
154
- for(const t of I){const n=e.get(t.requestLocalId)
152
+ const I=[],S=[]
153
+ if(m?.associations&&S.push(...m.associations),S.length>0){const e=new Map
154
+ for(const t of S){const n=e.get(t.requestLocalId)
155
155
  void 0!==n&&(n||t.endedAt)||e.set(t.requestLocalId,!t.endedAt)}const t=[...e.entries()].filter((([,e])=>e)).map((([e])=>e)),n=[...e.entries()].filter((([,e])=>!e)).map((([e])=>e))
156
- S.push(...t,...n)
156
+ I.push(...t,...n)
157
157
  const s=[]
158
158
  if(t.length>0&&s.push(`active: ${t.join(", ")}`),n.length>0){const e=5,t=n.slice(0,e),o=n.length-t.length
159
- s.push(`prev: ${t.join(", ")}${o>0?` +${o} more`:""}`)}s.length>0&&(w=` [${s.join(" | ")}]`)}if(i&&!S.includes(i))continue
160
- const k=t.title||"Untitled",x=k.length>50?k.slice(0,47)+"...":k
161
- v.push({id:t.id,title:x,status:h,age:n,totalTokens:o,requestsInfo:w,allRequestIds:S,statusDetail:g.detail,currentAction:r,recentActions:l})}if(0===v.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
162
- const k=r?v.slice(0,r):v,x=k.map((e=>{const t=e.currentAction?`\n Current: ${e.currentAction}`:"",n=e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
163
- return`\u2022 ${e.id}\n ${e.title}\n Status: ${e.status} (${e.statusDetail}) | Age: ${l(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${t}${n}`})),A=r&&v.length>r?` (showing ${r} of ${v.length})`:""
164
- return{content:[{type:"text",text:`Found ${k.length} session${1!==k.length?"s":""}${A}:\n\n${x.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,v.getErrorMessage)(e)}`)}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawn=r,exports.handleContinue=a,exports.extractRecentActivity=c,exports.formatTimeSince=d,exports.deriveActivityStatus=u,exports.noteStatusCheck=p,exports.handleCheckStatus=f,exports.handleAbort=h,exports.handleListSessions=m,exports.handleSubagentOperations=async function(e,t,n,s,o,i){const l=n.child({tool:"subagent_operations"}),c=s.startTimer("tool_execution_time")
159
+ s.push(`prev: ${t.join(", ")}${o>0?` +${o} more`:""}`)}s.length>0&&(w=` [${s.join(" | ")}]`)}if(i&&!I.includes(i))continue
160
+ const x=t.title||"Untitled",v=x.length>50?x.slice(0,47)+"...":x
161
+ b.push({id:t.id,title:v,status:h,age:n,totalTokens:o,requestsInfo:w,allRequestIds:I,statusDetail:g.detail,currentAction:r,recentActions:l})}if(0===b.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
162
+ const x=r?b.slice(0,r):b,v=x.map((e=>{const t=e.currentAction?`\n Current: ${e.currentAction}`:"",n=e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
163
+ return`\u2022 ${e.id}\n ${e.title}\n Status: ${e.status} (${e.statusDetail}) | Age: ${l(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${t}${n}`})),A=r&&b.length>r?` (showing ${r} of ${b.length})`:""
164
+ return{content:[{type:"text",text:`Found ${x.length} session${1!==x.length?"s":""}${A}:\n\n${v.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,b.getErrorMessage)(e)}`)}}Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSpawn=r,exports.handleContinue=a,exports.extractRecentActivity=c,exports.formatTimeSince=d,exports.deriveActivityStatus=u,exports.noteStatusCheck=p,exports.handleCheckStatus=f,exports.handleAbort=h,exports.handleListSessions=m,exports.handleSubagentOperations=async function(e,t,n,s,o,i){const l=n.child({tool:"subagent_operations"}),c=s.startTimer("tool_execution_time")
165
165
  try{const n=e,{sessionId:d,operation:u}=n
166
166
  let g
167
167
  switch(l.info("Processing subagent_operations",{operation:u,sessionId:d}),u){case"spawn":g=await r({agent:n.agent,prompt:n.prompt,requestId:n.requestId,allowParallel:n.allowParallel,useExistingSession:n.useExistingSession},t,l,o,i)
@@ -199,20 +199,14 @@ let n=r.length-1
199
199
  for(;n>0&&!t(r[n]);)n--
200
200
  const s=r.length-1-n
201
201
  return{content:[{type:"text",text:`Session ${e}: latest message${s>0?` (skipped ${s} trailing empty message${1===s?"":"s"}; use compact:true for the full trajectory)`:""}:\n\n${g(r[n],n)}`}]}}const p=r.map(((e,t)=>g(e,t))).join("\n\n---\n\n")
202
- return{content:[{type:"text",text:`Session ${e}: last ${r.length} messages:\n\n${p}`}]}}catch(e){throw new Error(`Failed to read messages: ${(0,v.getErrorMessage)(e)}`)}}(d,o,t,l,{compact:n.compact,select:n.select,latest:s})
202
+ return{content:[{type:"text",text:`Session ${e}: last ${r.length} messages:\n\n${p}`}]}}catch(e){throw new Error(`Failed to read messages: ${(0,b.getErrorMessage)(e)}`)}}(d,o,t,l,{compact:n.compact,select:n.select,latest:s})
203
203
  break}case"list-sessions":const e={activeOnly:n.activeOnly,requestId:n.requestId,limit:n.limit,minTokens:n.minTokens,maxTokens:n.maxTokens,maxAgeHours:n.maxAgeHours,parentSessionId:i?.sessionId}
204
204
  g=await m(t,l,o,e)
205
205
  break
206
206
  default:throw new w.ValidationError(`Unknown operation: ${u}`)}c({success:!0}),s.incrementCounter(`subagent_operations_${u}_success`,1)
207
207
  const p=E[u]
208
- return p?(0,C.withAlchemyKind)(g,p):g}catch(e){return l.error("Subagent operations failed",{error:e instanceof Error?e.stack:String(e)}),c({success:!1}),s.incrementCounter("subagent_operations_errors",1),{content:[{type:"text",text:`Subagent operation failed: ${(0,v.getErrorMessage)(e)}`}],isError:!0}}}
209
- const w=t(98547),$=t(3928),y=t(16714),S=t(49725),I=t(7451),b=t(87907),v=t(21319),k=t(79896),x=t(16928),A=t(97734),C=t(62263),E={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},T="true"===process.env.ALCHEMY_DEBUG_LOGS,j=(0,t(53603).getActionServerPort)(),q=6e4,P=3e5,U=new Map},16714:(e,exports,t)=>{Object.defineProperty(exports,"__esModule",{value:!0}),exports.invalidateServerUrlCache=function(){s=null},exports.getServerUrl=async function(e,o){const i=process.env.OPENCODE_TUI_URL
210
- if(s&&s.basePath===e.basePath&&s.envUrl===i)return s.result
211
- let r=i
212
- if(!r)try{const{readFileSync:n}=await Promise.resolve().then(t.t.bind(t,79896,23)),{join:s}=await Promise.resolve().then(t.t.bind(t,16928,23)),i=n(s(e.basePath,".alchemy_lab","opencode.json"),"utf8"),a=JSON.parse(i)
213
- a.port&&(r=`http://127.0.0.1:${a.port}`,o.debug("Discovered OpenCode server URL from .alchemy_lab/opencode.json",{serverUrl:r}))}catch(e){o.debug("Could not read .alchemy_lab/opencode.json",{error:e instanceof Error?e.message:String(e)})}if(r)try{(0,n.noteServe)(Number(new URL(r).port),(0,n.currentServePassword)(e.basePath))}catch{}return s={basePath:e.basePath,envUrl:i,result:r},r}
214
- const n=t(7245)
215
- let s=null},24019:(e,exports,t)=>{exports.formatLiveWorker=function(e){const t=e.agentType?e.agentType:"worker",n=[],s=!0===e.busy?"mid-turn":!1===e.busy?"between turns":"unknown"
208
+ return p?(0,C.withAlchemyKind)(g,p):g}catch(e){return l.error("Subagent operations failed",{error:e instanceof Error?e.stack:String(e)}),c({success:!1}),s.incrementCounter("subagent_operations_errors",1),{content:[{type:"text",text:`Subagent operation failed: ${(0,b.getErrorMessage)(e)}`}],isError:!0}}}
209
+ const w=t(59860),$=t(15899),y=t(47617),I=t(39844),S=t(6688),k=t(87280),b=t(58824),x=t(79896),v=t(16928),A=t(8955),C=t(82272),E={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},q="true"===process.env.ALCHEMY_DEBUG_LOGS,T=(0,t(79414).getActionServerPort)(),j=6e4,P=3e5,R=new Map},80024:(e,exports,t)=>{exports.formatLiveWorker=function(e){const t=e.agentType?e.agentType:"worker",n=[],s=!0===e.busy?"mid-turn":!1===e.busy?"between turns":"unknown"
216
210
  return n.push(`- ${t} (${e.sessionId}): ${e.status}, ${e.detail}; ${s}`),e.current&&n.push(` now: ${e.current}`),e.recent.length>0&&n.push(` recently: ${e.recent.join(", ")}`),e.lastSaid&&n.push(` last said${e.lastSaidAgo?` (${e.lastSaidAgo})`:""}: "${e.lastSaid}"`),e.lastRefusal&&n.push(` last refused (its tool said): ${e.lastRefusal}`),n.join("\n")},exports.readLiveWorker=async function(e,t){if(!e)return null
217
211
  const i=t.filter((e=>(0,s.isWorkerAgentType)(e.agentType??""))),r=(i.length>0?i:t).slice(-1)[0]
218
212
  if(!r)return null
@@ -238,7 +232,4 @@ if("error"===t.state?.status||o.test(n)){const e=String(t.tool??"tool").replace(
238
232
  return`${e}: ${s.length>240?s.slice(0,237)+"...":s}`}}}return null}(l),h=function(e){for(let t=e.length-1;t>=0;t--){const n=e[t]?.info,s=n?.providerID??n?.model?.providerID
239
233
  if("string"==typeof s&&s)return s}return null}(l)
240
234
  return{sessionId:r.sessionId,agentType:r.agentType,busy:c,status:u.status,detail:u.detail,current:d.current,recent:d.recent,lastSaid:g,lastSaidAgo:p,lastRefusal:f,provider:h}}catch{return null}}
241
- const n=t(16554),s=t(47221),o=/^\s*(?:Error:|\u26d4|\u274c|Not filed|Not started|Not kept|Cannot |STOP:)/},49725:(e,exports,t)=>{Object.defineProperty(exports,"__esModule",{value:!0}),exports.deliverToSession=async function(e,t,s={}){const o=s.noVerify?0:s.verifySeconds??20
242
- try{const i=await fetch(`${(0,n.getActionServerBaseUrl)()}/api/sessions/${encodeURIComponent(e)}/deliver`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:t,kind:s.kind,partPayload:s.partPayload,noVerify:s.noVerify,verifySeconds:s.verifySeconds}),signal:AbortSignal.timeout(1e3*(o+25))}),r=await i.json().catch((()=>null))
243
- return i.ok?r?.data??{ok:!1,error:"deliver_route",detail:"empty response"}:{ok:!1,error:"deliver_route",detail:r?.error||`HTTP ${i.status}`}}catch(e){return{ok:!1,error:"deliver_route",detail:e instanceof Error?e.message:String(e)}}},exports.deliveryFailureText=function(e,t){return`\u274c NOT delivered to session ${e}: ${t.detail||t.error||"unknown reason"}. The session did not receive this and nothing will act on it.`}
244
- const n=t(53603)}}
235
+ const n=t(51725),s=t(87780),o=/^\s*(?:Error:|\u26d4|\u274c|Not filed|Not started|Not kept|Cannot |STOP:)/}}
@@ -1,9 +1,9 @@
1
1
  "use strict"
2
- exports.id=540,exports.ids=[540],exports.modules={76390:(e,exports,t)=>{function r(...e){const t=(0,o.createHash)("sha256")
2
+ exports.id=293,exports.ids=[293],exports.modules={56149:(e,exports,t)=>{function r(...e){const t=(0,o.createHash)("sha256")
3
3
  for(const r of e){const e=r??""
4
4
  t.update(String(e.length)).update(":").update(e).update("\0")}return t.digest("hex")}function n(e,t=exports.SHORT_ADDRESS_LENGTH){return e.slice(0,t)}Object.defineProperty(exports,"__esModule",{value:!0}),exports.SHORT_ADDRESS_LENGTH=void 0,exports.contentAddress=r,exports.shortAddress=n,exports.addressedName=function(e,...t){return`${e}-${n(r(...t))}`}
5
5
  const o=t(76982)
6
- exports.SHORT_ADDRESS_LENGTH=12},78540:(e,exports,t)=>{function r(e){const t=String(e??"").lastIndexOf("@")
6
+ exports.SHORT_ADDRESS_LENGTH=12},79293:(e,exports,t)=>{function r(e){const t=String(e??"").lastIndexOf("@")
7
7
  return t>0?e.slice(0,t):String(e??"")}function n(e){const t=e.lastIndexOf("@"),n=t>0?e.slice(t+1):""
8
8
  if(!n||!/^\d/.test(n))throw new Error(`"${e}" has no pinned version. A boxed connection installs once and reuses that build, so it needs an exact version, for example ${r(e)}@1.2.3.`)
9
9
  return n}function o(e){return n(e),(0,m.addressedName)("alchemy-mcp-pkg",e)}function i(e,t){return(0,m.addressedName)("alchemy-mcp-state",e,t)}function a(e,t){return(0,m.addressedName)("alchemy-mcp-box",e,t)}function c(e,t,r,n){return`${(0,m.addressedName)("alchemy-mcp-repo",e,t,r,n)}:latest`}function s(e,t,r){return["docker","exec","-i",e,...t??[],...r??[]]}Object.defineProperty(exports,"__esModule",{value:!0}),exports.REGISTRY_ALLOWLIST=void 0,exports.packageNameOf=r,exports.assertPinned=n,exports.artifactVolumeFor=o,exports.stateVolumeFor=i,exports.boxNameFor=a,exports.prepareConnectionArtifact=async function(e,t){const r=String(e.packageSpec??"")
@@ -51,6 +51,6 @@ const c=setTimeout((()=>{try{t.kill("SIGKILL")}catch{}a("")}),r)
51
51
  t.on("error",(e=>{clearTimeout(c),a(String(e.message??e))})),t.on("close",(e=>{clearTimeout(c)
52
52
  const t=o.trim().split("\n").filter(Boolean).slice(-4).join("\n")
53
53
  a(t?`exit ${e}: ${t}`:0===e?"":`exit ${e} with no output`)}))}catch(e){a(String(e?.message??e))}}))}
54
- const l=t(6175),u=t(53157),d=t(79242),m=t(76390),p=3e5,f="node:20-alpine",g="alchemy.mcp=connection"
54
+ const l=t(81618),u=t(26166),d=t(99559),m=t(56149),p=3e5,f="node:20-alpine",g="alchemy.mcp=connection"
55
55
  exports.REGISTRY_ALLOWLIST=["registry.npmjs.org","npmjs.org","npmjs.com"]
56
56
  const k=".alchemy-install-complete",h=12e5}}
@@ -1,5 +1,5 @@
1
1
  "use strict"
2
- exports.id=491,exports.ids=[491],exports.modules={23491:function(e,exports,t){function s(e,t="",n=0,r=[]){if(n>4||r.length>=i)return r
2
+ exports.id=356,exports.ids=[356],exports.modules={69356:function(e,exports,t){function s(e,t="",n=0,r=[]){if(n>4||r.length>=i)return r
3
3
  let c=[]
4
4
  try{c=a.default.readdirSync(l.default.join(e,t),{withFileTypes:!0})}catch{return r}for(const f of c.sort(((e,t)=>e.name.localeCompare(t.name)))){if(f.name.startsWith(".")||o.has(f.name))continue
5
5
  const c=t?`${t}/${f.name}`:f.name
@@ -1,5 +1,5 @@
1
1
  "use strict"
2
- exports.id=603,exports.ids=[603],exports.modules={53603:(t,exports,e)=>{function r(){return process.env.ALCHEMY_PROJECT_PATH||process.cwd()}function n(t){const e=Date.now(),n=!t
2
+ exports.id=414,exports.ids=[414],exports.modules={79414:(t,exports,e)=>{function r(){return process.env.ALCHEMY_PROJECT_PATH||process.cwd()}function n(t){const e=Date.now(),n=!t
3
3
  if(n&&null!==y&&e-v<g)return y
4
4
  const o=(0,l.join)(t||r(),h,d)
5
5
  if((0,a.existsSync)(o))try{const t=(0,a.readFileSync)(o,"utf-8"),r=JSON.parse(t)
package/dist/478.js ADDED
@@ -0,0 +1,89 @@
1
+ "use strict"
2
+ exports.id=478,exports.ids=[478],exports.modules={18029:function(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
3
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectIDBackwardCompat=exports.ProjectIDFactory=exports.ProjectID=void 0
4
+ const i=r(t(76982))
5
+ class o{static create(){return exports.ProjectIDFactory.create()}static fromString(e){return exports.ProjectIDFactory.fromString(e)}static isValid(e){return exports.ProjectIDFactory.isValid(e)}constructor(e){this.id=e}toString(){return this.id}equals(e){return this.id===e.toString()}isStandardFormat(){return this.id.includes("_")&&this.id.startsWith("prj_")}}exports.ProjectID=o,exports.ProjectIDFactory={create(){const e=Date.now().toString().substring(5),t=i.default.randomBytes(4).toString("hex")
6
+ return o.fromString(`prj_${e}_${t}`)},fromString(e){return this.isValid(e)?new o(e):null},isValid(e){if(!e||"string"!=typeof e||e.length<5||!e.startsWith("prj"))return!1
7
+ if(e.includes("_")){const t=e.split("_")
8
+ return t.length>=3&&"prj"===t[0]}return!1}},exports.ProjectIDBackwardCompat={create:exports.ProjectIDFactory.create,fromString:exports.ProjectIDFactory.fromString,isValid:exports.ProjectIDFactory.isValid},Object.assign(o,exports.ProjectIDBackwardCompat)},52568:function(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
9
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectSynchronizer=void 0
10
+ const i=r(t(16928)),o=t(18029),s=t(58824)
11
+ exports.ProjectSynchronizer=class{constructor(e,t,r,i,o={}){this.logger=e.child({component:"project-synchronizer"}),this.registry=t,this.remoteApi=r,this.fileSystem=i,this.options={createRemoteProjects:o.createRemoteProjects??!0,maxRetryAttempts:o.maxRetryAttempts??3,retryDelayMs:o.retryDelayMs??1e3},this.logger.info("ProjectSynchronizer initialized",{options:this.options})}async ensureProjectExists(e){const t=Math.random().toString(36).substring(2,8)
12
+ this.logger.info("\u{1f4de} SYNC-TRACE: ensureProjectExists called",{callId:t,projectId:e,stackTrace:(new Error).stack?.split("\n").slice(1,4).map((e=>e.trim())).join(" -> ")||"unknown"})
13
+ try{if(!this.remoteApi.isOnline())return this.logger.info("\u{1f4de} SYNC-TRACE: System offline, returning original ID",{callId:t,projectId:e}),e
14
+ if(this.logger.info("\u{1f4de} SYNC-TRACE: System online, proceeding",{callId:t,projectId:e}),this.registry.isVerified(e))return this.logger.info("\u{1f4de} SYNC-TRACE: Cache hit - already verified",{callId:t,projectId:e}),e
15
+ this.logger.info("\u{1f4de} SYNC-TRACE: Cache miss - checking remote server",{callId:t,projectId:e})
16
+ const r=await this.remoteApi.fetchProject(e)
17
+ if(this.logger.info("\u{1f4de} SYNC-TRACE: Remote fetch completed",{callId:t,projectId:e,found:!!r,remoteProject:r?{id:r.id,name:r.name}:null}),r)return this.logger.info("Project exists on remote server",{projectId:e}),this.registry.register(r),r.id
18
+ if(this.logger.info("\u{1f4de} SYNC-TRACE: Project not found, will create",{callId:t,projectId:e}),!this.options.createRemoteProjects)throw this.logger.info("Project does not exist on remote and creation is disabled",{projectId:e}),new Error(`Project ${e} does not exist on remote server and creation is disabled`)
19
+ {this.logger.info("\u{1f4de} SYNC-TRACE: Creating project on remote server",{callId:t,projectId:e})
20
+ let r={id:e,name:"Remote Synchronized Project",description:"Automatically created during synchronization",status:"active"}
21
+ const i=this.registry.getProject(e)
22
+ i&&(this.logger.info("\u{1f4de} SYNC-TRACE: Using local project data",{callId:t,projectId:e,projectName:i.name}),r={...i})
23
+ try{this.logger.info("\u{1f4de} SYNC-TRACE: Calling remoteApi.createProject",{callId:t,projectId:e,projectName:r.name})
24
+ const i=await this.remoteApi.createProject(r)
25
+ return this.logger.info("Create project API call succeeded",{returnedId:i?.id,returnedName:i?.name}),this.registry.register(i),this.logger.info("Successfully created project on remote server",{projectId:i.id,projectName:i.name}),i.id}catch(t){if(t&&"object"==typeof t&&"status"in t&&409===t.status)return this.logger.info("Project already exists on remote (409 conflict)",{projectId:e}),this.registry.markVerified(e),e
26
+ throw this.logger.error("Failed to create project on remote server",{projectId:e,error:t instanceof Error?t.stack:String(t),status:t?.status,message:t instanceof Error?t.message:String(t)}),t}}}catch(t){throw this.logger.error("Error verifying project existence",{projectId:e,error:t instanceof Error?t.stack:String(t),message:(0,s.getErrorMessage)(t)}),t}}async createProject(e,t){try{if(e.id){if(!o.ProjectIDFactory.isValid(e.id)){this.logger.warn("Invalid project ID format, generating new ID",{originalId:e.id})
27
+ const t=o.ProjectIDFactory.create().toString()
28
+ e.id=t}}else{const t=o.ProjectIDFactory.create().toString()
29
+ e.id=t,this.logger.info("Generated new project ID",{projectId:t})}const r=(new Date).toISOString(),i={id:e.id,name:e.name||"Unnamed Project",description:e.description||"No description provided",status:e.status||"active",createdAt:e.createdAt||r,updatedAt:e.updatedAt||r,metadata:e.metadata||{}}
30
+ if(this.registry.register(i),t&&await this.fileSystem.writeProjectFile(i,t),this.remoteApi.isOnline())try{this.logger.info("Syncing new project to remote server",{projectId:i.id}),await this.ensureProjectExists(i.id)}catch(e){this.logger.warn("Failed to sync new project to remote, will retry later",{projectId:i.id,error:e instanceof Error?e.message:String(e)})}return i}catch(t){throw this.logger.error("Failed to create project",{projectData:e,error:t instanceof Error?t.stack:String(t)}),t}}async syncAllProjects(e){try{if(!this.remoteApi.isOnline())return void this.logger.info("Skipping project synchronization - system is offline")
31
+ this.logger.info("Starting project synchronization from directory",{basePath:e})
32
+ const t=await this.fileSystem.readProjectFile(e)
33
+ if(!t||!t.id)return void this.logger.info("No valid local project found, skipping synchronization",{basePath:e})
34
+ this.logger.info("Found local project",{projectId:t.id,projectName:t.name}),this.logger.info("Registering project in registry before synchronization",{projectId:t.id,projectName:t.name}),this.registry.register(t),this.logger.info("Ensuring project exists on remote server",{projectId:t.id}),await this.ensureProjectExists(t.id)?(this.logger.info("Project exists on remote, checking metadata sync",{projectId:t.id,projectName:t.name,basePath:e}),t.metadata&&Object.keys(t.metadata).length>0&&(this.logger.info("Syncing project metadata to remote (async)",{projectId:t.id,metadataKeys:Object.keys(t.metadata)}),this.remoteApi.updateProject(t.id,{metadata:t.metadata}).then((()=>{this.logger.info("Project metadata synced successfully",{projectId:t.id})})).catch((e=>{this.logger.warn("Failed to sync project metadata",{projectId:t.id,error:e instanceof Error?e.message:String(e)})}))),this.logger.info("Successfully synchronized project",{projectId:t.id,projectName:t.name,basePath:e})):this.logger.warn("Failed to verify/create project on remote server",{projectId:t.id,basePath:e})}catch(t){this.logger.error("Error during project synchronization",{basePath:e,error:t instanceof Error?t.stack:String(t)})}}async loadLocalProject(e){try{this.logger.info("Loading local project from directory",{basePath:e})
35
+ const t=await this.fileSystem.readProjectFile(e)
36
+ return t&&t.id?(this.registry.register(t),this.logger.info("Successfully loaded local project",{projectId:t.id,basePath:e}),t):(this.logger.info("No valid local project found",{basePath:e}),null)}catch(t){return this.logger.error("Error loading local project",{basePath:e,error:t instanceof Error?t.stack:String(t)}),null}}async getOrCreateProject(e,t={}){this.logger.info("\u{1f680} getOrCreateProject called",{basePath:e,hasProjectData:!!Object.keys(t).length,projectData:Object.keys(t)})
37
+ try{this.logger.info("Attempting to load existing local project",{basePath:e})
38
+ const r=await this.loadLocalProject(e)
39
+ if(r){this.logger.info("Found existing local project",{projectId:r.id,projectName:r.name,basePath:e})
40
+ const t=this.remoteApi.isOnline()
41
+ if(this.logger.info("Checking online status for verification",{isOnline:t,projectId:r.id}),!t)return this.logger.info("Using existing project (offline, skipping server verification)",{projectId:r.id,basePath:e}),r
42
+ {this.logger.info("System is online - starting project verification",{projectId:r.id,basePath:e})
43
+ const t=await this.ensureProjectExists(r.id)
44
+ if(this.logger.info("Project verification completed",{projectId:r.id,exists:t,basePath:e}),t)return this.logger.info("Using existing project (verified on server)",{projectId:r.id,basePath:e}),r
45
+ this.logger.warn("Local project not found on server, creating new project",{invalidProjectId:r.id,basePath:e})}}this.logger.info("No existing project found, creating new one",{basePath:e}),t.name||(t.name=i.default.basename(e))
46
+ const o=await this.createProject(t,e)
47
+ return this.logger.info("Created new project for directory",{projectId:o.id,basePath:e}),o}catch(t){this.logger.error("Error getting or creating project",{basePath:e,error:t instanceof Error?t.stack:String(t)})
48
+ const r=await this.createProject({name:`Fallback Project for ${i.default.basename(e)}`,description:"Created as fallback due to error"},e)
49
+ return this.logger.info("Created fallback project due to error",{projectId:r.id,basePath:e}),r}}async verifyAndFixProjectName(e){try{if(!this.remoteApi.isOnline())return this.logger.info("Skipping project name verification - system is offline"),!0
50
+ if(this.registry.isVerifiedWithName&&this.registry.isVerifiedWithName(e.id,e.name))return this.logger.info("Project name already verified, skipping verification",{projectId:e.id,projectName:e.name}),!0
51
+ const t=await this.remoteApi.fetchProject(e.id)
52
+ if(!t)return this.logger.warn("Cannot verify project name - project not found on remote",{projectId:e.id}),!1
53
+ if(t.name===e.name)return this.logger.info("Project name matches remote, marking as verified",{projectId:e.id,projectName:e.name}),this.registry.markVerifiedWithName&&this.registry.markVerifiedWithName(e.id,e.name),!0
54
+ this.logger.info("Project name mismatch, updating remote name",{projectId:e.id,localName:e.name,remoteName:t.name})
55
+ const r=this.options.maxRetryAttempts,i=this.options.retryDelayMs
56
+ for(let t=1;t<=r;t++)try{return await this.remoteApi.updateProject(e.id,{name:e.name}),this.logger.info("Project name updated successfully",{projectId:e.id,newName:e.name}),this.registry.markVerifiedWithName&&this.registry.markVerifiedWithName(e.id,e.name),!0}catch(o){if(!(t<r))throw o
57
+ this.logger.warn(`Failed to update project name, retrying (${t}/${r})`,{projectId:e.id,error:o instanceof Error?o.message:String(o)}),await new Promise((e=>setTimeout(e,i)))}return!1}catch(t){return this.logger.error("Failed to verify/fix project name",{projectId:e.id,projectName:e.name,error:t instanceof Error?t.stack:String(t)}),!1}}async syncProject(e,t){try{if(!this.remoteApi.isOnline())return this.logger.info("Skipping project synchronization - system is offline",{projectId:e}),!1
58
+ let r=null
59
+ if(t&&(r=await this.loadLocalProject(t),r&&r.id!==e&&(this.logger.warn("Project ID mismatch between request and loaded project",{requestedId:e,loadedId:r.id}),r=null)),!r){const t=this.registry.getProject(e)
60
+ t&&(r=t)}if(!r)return this.logger.info("Project not found locally, attempting to fetch from remote",{projectId:e}),r=await this.remoteApi.fetchProject(e),r?(this.registry.register(r),t&&await this.fileSystem.writeProjectFile(r,t),!0):(this.logger.warn("Project not found locally or remotely",{projectId:e}),!1)
61
+ if(!await this.ensureProjectExists(e))return!1
62
+ if(await this.verifyAndFixProjectName(r)||this.logger.warn("Failed to verify/fix project name",{projectId:r.id,projectName:r.name}),r.metadata&&Object.keys(r.metadata).length>0)try{this.logger.info("Syncing project metadata to remote",{projectId:r.id,metadataKeys:Object.keys(r.metadata)}),await this.remoteApi.updateProject(r.id,{metadata:r.metadata}),this.logger.info("Project metadata synced successfully",{projectId:r.id})}catch(e){this.logger.warn("Failed to sync project metadata",{projectId:r.id,error:e instanceof Error?e.message:String(e)})}return this.logger.info("Project synchronization completed successfully",{projectId:e}),!0}catch(r){return this.logger.error("Error synchronizing project",{projectId:e,basePath:t,error:r instanceof Error?r.stack:String(r)}),!1}}}},53102:function(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
63
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.DefaultProjectFileSystem=void 0
64
+ const i=r(t(79896)),o=r(t(16928)),s=t(14139)
65
+ exports.DefaultProjectFileSystem=class{constructor(e){this.logger=e.child({component:"project-file-system"})}async readProjectFile(e){try{const t=o.default.join(e,"project.json"),r=o.default.join(e,s.ALCHEMY_DIR_NAME,"project.json")
66
+ let a=r
67
+ if(i.default.existsSync(t))a=t,this.logger.info("Found project file in base directory",{path:a})
68
+ else{if(!i.default.existsSync(r))return this.logger.info("Project file does not exist in either location",{directPath:t,nestedPath:r}),null
69
+ this.logger.info("Found project file in alchemy_lab subdirectory",{path:a})}const n=await i.default.promises.readFile(a,"utf8"),c=JSON.parse(n)
70
+ return c.id?(this.logger.info("Read project file successfully",{projectId:c.id,path:a}),c):(this.logger.warn("Project file has no ID",{path:a}),null)}catch(t){return this.logger.error("Failed to read project file",{basePath:e,error:t instanceof Error?t.stack:String(t)}),null}}async writeProjectFile(e,t){try{const r=o.default.join(t,"project.json"),a=o.default.join(t,s.ALCHEMY_DIR_NAME,"project.json")
71
+ let n=r
72
+ !i.default.existsSync(r)&&i.default.existsSync(a)?(n=a,this.logger.info("Using existing nested path for project file",{path:n})):this.logger.info("Using direct path for project file",{path:n}),await this.ensureDirectoryExists(o.default.dirname(n)),await i.default.promises.writeFile(n,JSON.stringify(e,null,2),"utf8"),this.logger.info("Wrote project file successfully",{projectId:e.id,path:n})}catch(r){throw this.logger.error("Failed to write project file",{projectId:e.id,basePath:t,error:r instanceof Error?r.stack:String(r)}),r}}async ensureDirectoryExists(e){try{i.default.existsSync(e)||(await i.default.promises.mkdir(e,{recursive:!0}),this.logger.info("Created directory",{path:e}))}catch(t){throw this.logger.error("Failed to create directory",{path:e,error:t instanceof Error?t.stack:String(t)}),t}}}},75478:(e,exports,t)=>{exports.ProjectSynchronizer=exports.ProjectFileSystem=exports.ProjectRegistry=void 0
73
+ var r=t(89955)
74
+ Object.defineProperty(exports,"ProjectRegistry",{enumerable:!0,get:function(){return r.ProjectRegistry}})
75
+ var i=t(53102)
76
+ Object.defineProperty(exports,"ProjectFileSystem",{enumerable:!0,get:function(){return i.DefaultProjectFileSystem}})
77
+ var o=t(52568)
78
+ Object.defineProperty(exports,"ProjectSynchronizer",{enumerable:!0,get:function(){return o.ProjectSynchronizer}}),t(26975)},89955:function(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
79
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.ProjectRegistry=void 0
80
+ const i=r(t(79896)),o=r(t(16928))
81
+ exports.ProjectRegistry=class{constructor(e,t={}){this.projects=new Map,this.verifiedIds=new Set,this.verifiedNames=new Map,this.logger=e.child({component:"project-registry"}),this.options={persistToDisk:t.persistToDisk??!1,storageDirectory:t.storageDirectory??"./temp-projects"},this.options.persistToDisk?this.logger.warn("Project persistence to disk is enabled - this may be legacy functionality"):this.logger.info("Project persistence to disk is disabled - this is the recommended configuration"),this.logger.info("ProjectRegistry initialized",{options:this.options,persistenceEnabled:this.options.persistToDisk})}register(e,t={}){if(!e.id)return void this.logger.warn("Attempted to register project without ID")
82
+ const r=t.markVerified??!1,i=t.saveToFile??this.options.persistToDisk
83
+ this.projects.set(e.id,e),r&&(this.verifiedIds.add(e.id),e.name&&this.verifiedNames.set(e.id,e.name)),this.logger.info("Registered project",{projectId:e.id,projectName:e.name,markVerified:r,saveToFile:i}),i&&(this.options.persistToDisk?this.saveProjectToFile(e):this.logger.debug("Skipping project file persistence (disabled in configuration)",{projectId:e.id}))}hasProject(e){return this.projects.has(e)}markVerified(e){this.verifiedIds.add(e),this.logger.info("Marked project as verified",{projectId:e})}isVerified(e){return this.verifiedIds.has(e)}markVerifiedWithName(e,t){this.verifiedIds.add(e),this.verifiedNames.set(e,t),this.logger.info("Marked project as verified with name",{projectId:e,projectName:t})}isVerifiedWithName(e,t){return this.verifiedIds.has(e)&&this.verifiedNames.get(e)===t}getVerifiedName(e){return this.verifiedNames.get(e)}getProject(e){return this.projects.get(e)}getAllProjects(){return Array.from(this.projects.values())}clearVerificationCache(e){e?(this.verifiedIds.delete(e),this.verifiedNames.delete(e),this.logger.info("Cleared verification for project",{projectId:e})):(this.verifiedIds.clear(),this.verifiedNames.clear(),this.logger.info("Cleared all project verifications"))}saveProjectToFile(e){try{if(!i.default.existsSync(this.options.storageDirectory))try{this.logger.info("Creating storage directory",{directory:this.options.storageDirectory,absolutePath:o.default.resolve(this.options.storageDirectory)}),i.default.mkdirSync(this.options.storageDirectory,{recursive:!0}),this.logger.info("Storage directory created successfully",{directory:this.options.storageDirectory,absolutePath:o.default.resolve(this.options.storageDirectory)})}catch(e){return void this.logger.error("Failed to create storage directory",{directory:this.options.storageDirectory,absolutePath:o.default.resolve(this.options.storageDirectory),error:e instanceof Error?e.stack:String(e)})}const t=o.default.join(this.options.storageDirectory,`project-${e.id}.json`)
84
+ i.default.writeFileSync(t,JSON.stringify(e,null,2),"utf8"),this.logger.info("Saved project to file",{projectId:e.id,filePath:t})}catch(t){this.logger.error("Failed to save project to file",{projectId:e.id,error:t instanceof Error?t.stack:String(t)})}}async loadFromDisk(){try{if(!i.default.existsSync(this.options.storageDirectory))try{return this.logger.info("Storage directory does not exist, creating it",{directory:this.options.storageDirectory,absolutePath:o.default.resolve(this.options.storageDirectory)}),i.default.mkdirSync(this.options.storageDirectory,{recursive:!0}),void this.logger.info("Storage directory created successfully",{directory:this.options.storageDirectory,absolutePath:o.default.resolve(this.options.storageDirectory)})}catch(e){return void this.logger.error("Failed to create storage directory",{directory:this.options.storageDirectory,absolutePath:o.default.resolve(this.options.storageDirectory),error:e instanceof Error?e.stack:String(e)})}const e=i.default.readdirSync(this.options.storageDirectory).filter((e=>e.startsWith("project-")&&e.endsWith(".json")))
85
+ this.logger.info("Loading projects from disk",{directory:this.options.storageDirectory,fileCount:e.length})
86
+ let t=0
87
+ for(const r of e)try{const e=o.default.join(this.options.storageDirectory,r),s=i.default.readFileSync(e,"utf8"),a=JSON.parse(s)
88
+ if(!a.id){this.logger.warn("Skipping project file with missing ID",{filePath:e})
89
+ continue}this.register(a,{saveToFile:!1}),t++}catch(e){this.logger.error("Error loading project file",{file:r,error:e instanceof Error?e.stack:String(e)})}this.logger.info("Finished loading projects from disk",{totalLoaded:t,totalFiles:e.length})}catch(e){this.logger.error("Failed to load projects from disk",{error:e instanceof Error?e.stack:String(e)})}}setStorageDirectory(e){this.options.storageDirectory=e,this.logger.info("Set storage directory",{directory:e})}setPersistToDisk(e){this.options.persistToDisk=e,this.logger.info("Set persist to disk",{persistToDisk:e})}}}}
@@ -1,6 +1,6 @@
1
1
  "use strict"
2
- exports.id=636,exports.ids=[636],exports.modules={54636:(e,exports,t)=>{function n(e){const t=/(?:^|[\s:])([A-Za-z0-9_.+-]+): (?:command )?not found(?=\s|$)/m.exec(e)
3
- return t?t[1]:null}async function a(e,n,a){const{actionServerFetch:s}=await Promise.resolve().then(t.bind(t,53603))
2
+ exports.id=579,exports.ids=[579],exports.modules={74579:(e,exports,t)=>{function n(e){const t=/(?:^|[\s:])([A-Za-z0-9_.+-]+): (?:command )?not found(?=\s|$)/m.exec(e)
3
+ return t?t[1]:null}async function a(e,n,a){const{actionServerFetch:s}=await Promise.resolve().then(t.bind(t,79414))
4
4
  return(await s(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(a)})).json().catch((()=>({})))}exports.trialFailures=function(e){const t=[]
5
5
  for(const a of e){const e=n(a.output)
6
6
  e?t.push(`${a.name}: the environment has no \`${e}\` (the command ran, but the shell said "${e}: not found")`):0!==a.exit&&t.push(`${a.name}: exited ${a.exit}: ${a.output.replace(/\s+/g," ").trim().slice(0,240)}`)}return t},exports.trialRun=async function(e){const t=await a("/api/experiments/start",{sessionId:e.sessionId,environmentId:e.environmentId,...e.branchName?{branchName:e.branchName}:{}},18e4).catch((()=>null)),n=t?.success?t?.data?.handle:null
@@ -1,7 +1,7 @@
1
1
  "use strict"
2
- exports.id=565,exports.ids=[565],exports.modules={87565:function(t,exports,e){var a=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}}
2
+ exports.id=60,exports.ids=[60],exports.modules={49060:function(t,exports,e){var a=this&&this.__importDefault||function(t){return t&&t.__esModule?t:{default:t}}
3
3
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.ConfigManager=void 0,exports.createConfigManager=function(t){return new g(t)}
4
- const i=a(e(91943)),n=a(e(16928)),o=a(e(70857)),r=e(21992),s=e(21319),c={},l={remoteServerUrl:"https://api.alchemylabcode.com",nodePath:"node"}
4
+ const i=a(e(91943)),n=a(e(16928)),o=a(e(70857)),r=e(14139),s=e(58824),c={},l={remoteServerUrl:"https://api.alchemylabcode.com",nodePath:"node"}
5
5
  class g{constructor(t){this.logger=t.child({component:"config-manager"}),this.installationConfigDir=this.getInstallationConfigDir()}getInstallationConfigDir(){const t=__dirname||process.cwd(),e=o.default.homedir()
6
6
  return t.includes("node_modules")?n.default.join(e,".alchemy_lab","config"):n.default.join(e,".alchemy_lab","config-dev")}async ensureDirectory(t){try{await i.default.access(t)}catch(e){await i.default.mkdir(t,{recursive:!0}),this.logger.debug("Created directory",{dirPath:t})}}async readConfig(t,e){try{const a=await i.default.readFile(t,"utf8"),n=JSON.parse(a)
7
7
  return{...e,...n}}catch(a){return"ENOENT"===a.code||this.logger.warn("Error reading config file, using defaults",{filePath:t,error:(0,s.getErrorMessage)(a)}),e}}async writeConfig(t,e){await this.ensureDirectory(n.default.dirname(t)),await i.default.writeFile(t,JSON.stringify(e,null,2),"utf8"),this.logger.debug("Wrote config file",{filePath:t})}async getProjectConfig(t){const e=n.default.join(t,r.ALCHEMY_DIR_NAME,"config.json")