alchemylab 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/19.js CHANGED
@@ -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(j)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,v.appendFileSync)(o,s)}catch{}}async function s(e,n,s,o,i,r){if(!(e&&n&&s&&o))return null
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
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
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,k.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:n,toSessionId:s,body:o,requestId:i,logger:r}=e
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
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,k.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,k.getErrorMessage)(e)})}}async function r(e,t,r,a,l){const{agent:c="work",prompt:d,requestId:u,allowParallel:p=!1,useExistingSession:g}=e
12
- if(n(t,"handleSpawn called",{agent:c,promptLength:d.length,requestId:u,useExistingSession:g,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:${E}/api/agents/by-name/${encodeURIComponent(e)}`,n=await fetch(t,{signal:AbortSignal.timeout(8e3)})
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
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)})
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)),v=(0,$.getServerUrl)(t,r),x=(async()=>{if(!a||!u)return null
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
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,k.getErrorMessage)(e)}),null))),[T,q,P,R]=await Promise.all([h,m,v,x])
19
- try{if(n(t,"Spawn agent type check",{callerSessionId:f||null,callerAgentType:T||null,pid:process.pid}),T){const e=(0,A.getAllowedSubagents)(T)
20
- if(e&&!e.includes(c))return{content:[{type:"text",text:`\u274c Agent type '${T}' cannot spawn '${c}'.\n\n**Allowed subagents for ${T}:** ${e.join(", ")}\n\nUse one of the allowed agent types instead.`}],isError:!0}}}catch(e){n(t,"allowedSubagents check failed",{error:(0,k.getErrorMessage)(e)})}let U=q?.requestContextMode
21
- if(!U){const e=(0,A.getBuiltInAgentConfig)(c)
22
- U=e?.requestContextMode}if(U&&"none"!==U&&!u)return{content:[{type:"text",text:`\u274c Agent '${c}' requires a requestId to be provided.\n\nThis agent has requestContextMode: '${U}' 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"===U&&a)try{const e=await a.getRequestClient().getRequest(u)
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])
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)
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,k.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")
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
26
  const M=l?.sessionId
27
27
  let O=""
28
- if(R){const e=(M?R.filter((e=>e.opencodeSessionId!==M)):R).filter((e=>!1!==e.working))
28
+ if(U){const e=(M?U.filter((e=>e.opencodeSessionId!==M)):U).filter((e=>!1!==e.working))
29
29
  if(e.length>0){const t=e.map((e=>e.opencodeSessionId)).join(", ")
30
- if(!p)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}
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
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:U||"undefined"}),_="readonly"===U?`Working on request ${u} (READONLY mode - you cannot modify files, only explore and report).\n\n${d}`:`Working on request ${u}.\n\n${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
33
  let F,N=!1
34
- if(g){if(L=g,r.info("Using existing session for spawn",{sessionId:L,requestId:u}),!P)throw new w.ValidationError("Could not determine OpenCode server URL")
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")
35
35
  try{let e
36
- try{const t=await fetch(`http://localhost:${E}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
36
+ try{const t=await fetch(`http://localhost:${j}/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
39
  e={providerID:t,modelID:s.join("/")}}}}catch{}F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:L,body:_,requestId:u,logger:r})
40
40
  let n=_
41
41
  const d=await s(a,t.projectId,u,L,r,t.mainBasePath||t.basePath)
42
42
  d&&(n=`${d}\n\n---\n\n${n}`)
43
- const p={body:_,fromSessionId:l?.sessionId,fromAgent:T,...u?{requestId:u}:{},...F?{instructionCode:F}:{}},g=await(0,S.deliverToSession)(L,n,{kind:"agent-delegation",partPayload:p})
44
- if(await i(a,t.projectId,F,g,r),!g.ok)throw new Error((0,S.deliveryFailureText)(L,g))
45
- r.info("Prompt delivered to existing session",{sessionId:L,agent:c,messageId:g.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,k.getErrorMessage)(e)}`)}}else{N=!0,n(t,"Creating session via spawn-session endpoint")
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
46
  const i=M
47
- j&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:i})
48
- try{const g=`http://localhost:${E}/api/spawn-session`,f=await fetch(g,{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,specialization:e.specialization,specializationHeadline:e.specializationHeadline??(e.specialization?e.specialization.split(/[.\n]/)[0].trim().slice(0,80):void 0),allowParallel:p,callerAgentType:T})})
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})})
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:$,initialBranch:S}=h.data
52
+ const{sessionId:m,model:w,serverUrl:y,initialBranch:S}=h.data
53
53
  D=S,F=await o({apiClient:a,fromSessionId:l?.sessionId,toSessionId:m,body:_,requestId:u,logger:r})
54
54
  let I=_
55
55
  const b="pickupGuidance"in(h?.data??{})?h.data.pickupGuidance??null:await s(a,t.projectId,u,m,r,t.mainBasePath||t.basePath)
56
56
  b&&(I=`${b}\n\n---\n\n${I}`)
57
- const k=await(0,y.deliverInitialPrompt)({serverUrl:$,sessionId:m,agent:c,prompt:I,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:_,fromSessionId:l?.sessionId,fromAgent:T,...u?{requestId:u}:{},...F?{instructionCode:F}:{}}}}})
58
- if(!k.success)throw new Error(`Failed to spawn subagent: ${k.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,k.getErrorMessage)(e)}`)}}let B=O
60
- B+=g?`Prompt sent to existing session ${L}.`:`Background ${c} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),F&&(B+=`\nInstruction code: ${F} (the specialist will cite this on their reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
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
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,$.getServerUrl)(n,s))throw new w.ValidationError("Could not determine OpenCode server URL")
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")
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,k.getErrorMessage)(e)})}let c=a
68
- if(!c)try{const t=`http://localhost:${E}/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,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)})
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:${E}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
72
+ try{const e=await fetch(`http://localhost:${j}/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,p={body:t,fromSessionId:l?.sessionId,fromAgent:await I.sessionIdentity.getAgentTypeFor(l?.sessionId)||void 0,...d?{instructionCode:d}:{}},g=await(0,S.deliverToSession)(e,u,{kind:"agent-followup",partPayload:p})
76
- return await i(r,n.projectId,d,g,s),g.ok?(s.info("Message delivered to subagent session",{sessionId:e,agent:c,instructionCode:d,messageId:g.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,g)+(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,k.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 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)
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=[]
@@ -85,23 +85,23 @@ const n=t.find((e=>"pending"===e.state||"running"===e.state)),s=t.filter((e=>"co
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
86
  if(e&&s<q)return{status:"working",detail:`last action ${d(s)}`,isReady:!1}
87
87
  const o=d(s).replace(/ ago$/,"")
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 p(e){let t=0,n=0,s=0
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
90
90
  const[e,i]=o.split("\t")
91
- "-"!==e&&(t+=parseInt(e,10)||0),"-"!==i&&(n+=parseInt(i,10)||0),s++}return{additions:t,deletions:n,files:s}}function g(e,t,n,s){const o=e.get(t)
92
- return e.set(t,{at:s,fingerprint:n}),o&&o.fingerprint===n?`Nothing changed since your last check at ${new Date(o.at).toISOString().slice(11,19)} UTC (${d(s-o.at)}): same activity, same messages, same tokens.`:null}async function f(e,n,s,o){const i=await(0,$.getServerUrl)(n,s)
91
+ "-"!==e&&(t+=parseInt(e,10)||0),"-"!==i&&(n+=parseInt(i,10)||0),s++}return{additions:t,deletions:n,files:s}}function p(e,t,n,s){const o=e.get(t)
92
+ return e.set(t,{at:s,fingerprint:n}),o&&o.fingerprint===n?`Nothing changed since your last check at ${new Date(o.at).toISOString().slice(11,19)} UTC (${d(s-o.at)}): same activity, same messages, same tokens.`:null}async function f(e,n,s,o){const i=await(0,y.getServerUrl)(n,s)
93
93
  if(!i)throw new w.ValidationError("Could not determine OpenCode server URL")
94
94
  s.info("Checking status of subagent session",{sessionId:e})
95
95
  try{const r=`${i.replace(/\/$/,"")}/session/${e}`,a=await fetch(r,{signal:AbortSignal.timeout(8e3)})
96
96
  if(!a.ok)return{content:[{type:"text",text:`Session ${e} not found or has exited.`}],isError:!0}
97
97
  const d=await a.json(),f=`${i.replace(/\/$/,"")}/session/${e}/message`,h=await fetch(f,{signal:AbortSignal.timeout(8e3)})
98
- let m={input:0,output:0,reasoning:0},w="",y=u(null,d.time.updated),$=-1
98
+ let m={input:0,output:0,reasoning:0},w="",$=u(null,d.time.updated),y=-1
99
99
  if(h.ok){const e=await h.json()
100
- $=e.length
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
- y=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=y.status,b=y.isReady,k=m.input+m.output+m.reasoning
104
- let v="",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 S=Date.now()-d.time.created,I=$.status,b=$.isReady,v=m.input+m.output+m.reasoning
104
+ let k="",x="",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]
@@ -111,86 +111,69 @@ t.length>0&&a.push(`Active: ${t.map((e=>e.requestLocalId)).join(", ")}`),o.lengt
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"})
114
- if(e.trim())return p(e)}try{const t=s("git",["log","main","--merges","--oneline","-n","50",`--grep=${e}`],{cwd:n,encoding:"utf8"}).trim()
114
+ if(e.trim())return g(e)}try{const t=s("git",["log","main","--merges","--oneline","-n","50",`--grep=${e}`],{cwd:n,encoding:"utf8"}).trim()
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
- if(o.trim())return{...p(o),merged:!0}}}catch{}return null}catch{return null}}(A,n.basePath)
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
- v=`\nDiff vs main (${A}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${n}`}}const C=g(R,`${n.currentSessionId??""}>${e}`,`${d.time.updated}:${$}:${k}`,Date.now())
119
- return{content:[{type:"text",text:`${C?`${C}\n\n`:""}Session ${e}\nTitle: ${d.title||"Untitled"}\nStatus: ${I} (${y.detail})\nReady for input: ${b?"Yes":"No"}\nAge: ${l(S)}\nTokens: ${k.toLocaleString()} (in: ${m.input.toLocaleString()}, out: ${m.output.toLocaleString()}${m.reasoning>0?`, reasoning: ${m.reasoning.toLocaleString()}`:""})${w}${x}${v}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,k.getErrorMessage)(e)}`)}}async function h(e,t,n){const s=await(0,$.getServerUrl)(t,n)
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)
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,k.getErrorMessage)(e)}`)}}async function m(e,t,n,s={}){const{activeOnly:o=!1,requestId:i,limit:r,minTokens:a,maxTokens:d,maxAgeHours:p}=s,g=await(0,$.getServerUrl)(e,t)
125
- if(!g)throw new w.ValidationError("Could not determine OpenCode server URL")
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)
125
+ if(!p)throw new w.ValidationError("Could not determine OpenCode server URL")
126
126
  t.info("Listing sessions",{filters:s})
127
- try{const t=`${g.replace(/\/$/,"")}/session?limit=1000`,f=await fetch(t,{signal:AbortSignal.timeout(8e3)})
128
- if(!f.ok)throw new Error(`Failed to fetch sessions: ${f.status}`)
129
- let h=await f.json()
130
- if(0===h.length)return{content:[{type:"text",text:"No sessions found."}]}
131
- const m=h
132
- s.parentSessionId&&(h=h.filter((e=>(e.parentId||e.parentID)===s.parentSessionId))),h.sort(((e,t)=>t.time.created-e.time.created))
133
- const w=`${g.replace(/\/$/,"")}/session/status`,y=await fetch(w,{signal:AbortSignal.timeout(8e3)}),$=y.ok?await y.json():{}
134
- let S=h
135
- if(o&&(S=S.filter((e=>"idle"!==($[e.id]?.type||"idle")))),void 0!==p){const e=60*p*60*1e3,t=Date.now()-e
136
- S=S.filter((e=>e.time.created>=t))}if(0===S.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
137
- let I={}
138
- const b=S.map((e=>e.id))
139
- let k={},v=[]
140
- if("specialist"===s.agentType){const e=new Set(b)
141
- for(const t of m){const n=t.parentId||t.parentID
142
- n&&e.has(n)&&(k[n]||(k[n]=[]),k[n].push(t.id),v.push(t.id))}}if(n)try{const e=n.getCLISessionClient()
143
- if(e){const t=[...b,...v]
144
- I=await e.batchEnrich(t)}}catch{}const x=g.replace(/\/$/,""),A=await Promise.allSettled(S.map((e=>fetch(`${x}/session/${e.id}/message`,{signal:AbortSignal.timeout(8e3)}).then((e=>e.ok?e.json():[])).catch((()=>[]))))),C=[]
145
- for(let e=0;e<S.length;e++){const t=S[e],n=Date.now()-t.time.created
146
- let o=0,r=null,l=[],p=u(null,t.time.updated)
147
- const g=A[e],f="fulfilled"===g.status?g.value:[]
127
+ try{const e=`${p.replace(/\/$/,"")}/session?limit=1000`,t=await fetch(e,{signal:AbortSignal.timeout(8e3)})
128
+ if(!t.ok)throw new Error(`Failed to fetch sessions: ${t.status}`)
129
+ let f=await t.json()
130
+ if(0===f.length)return{content:[{type:"text",text:"No sessions found."}]}
131
+ s.parentSessionId&&(f=f.filter((e=>(e.parentId||e.parentID)===s.parentSessionId))),f.sort(((e,t)=>t.time.created-e.time.created))
132
+ const h=`${p.replace(/\/$/,"")}/session/status`,m=await fetch(h,{signal:AbortSignal.timeout(8e3)}),w=m.ok?await m.json():{}
133
+ let $=f
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
+ $=$.filter((e=>e.time.created>=t))}if(0===$.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
136
+ let y={}
137
+ const S=$.map((e=>e.id))
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=[]
140
+ for(let e=0;e<$.length;e++){const t=$[e],n=Date.now()-t.time.created
141
+ let o=0,r=null,l=[],g=u(null,t.time.updated)
142
+ const p=b[e],f="fulfilled"===p.status?p.value:[]
148
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))
149
144
  if(f.length>0){const e=c(f)
150
- p=u(e.lastActivityTime,t.time.updated),r=e.current||null,l=e.recent}const h=p.status
145
+ g=u(e.lastActivityTime,t.time.updated),r=e.current||null,l=e.recent}const h=g.status
151
146
  if(void 0!==a&&o<a)continue
152
147
  if(void 0!==d&&o>d)continue
153
- const m=I[t.id]
148
+ const m=y[t.id]
154
149
  if(s.agentType&&(!m||m.agentType!==s.agentType))continue
155
150
  if("archived"===m?.status)continue
156
151
  let w=""
157
- const y=[],b=[]
158
- m?.associations&&b.push(...m.associations)
159
- const v=k[t.id]
160
- if(v)for(const e of v){const t=I[e]
161
- if(t?.associations){const n=$[e]?.type||"idle",s="idle"===n||"stalled"===n
162
- for(const e of t.associations)s&&!e.endedAt?b.push({...e,endedAt:"idle"}):b.push(e)}}if(b.length>0){const e=new Map
163
- for(const t of b){const n=e.get(t.requestLocalId)
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)
164
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))
165
- y.push(...t,...n)
156
+ S.push(...t,...n)
166
157
  const s=[]
167
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
168
- s.push(`prev: ${t.join(", ")}${o>0?` +${o} more`:""}`)}s.length>0&&(w=` [${s.join(" | ")}]`)}if(i&&!y.includes(i))continue
169
- const x=t.title||"Untitled",T=x.length>50?x.slice(0,47)+"...":x
170
- C.push({id:t.id,title:T,status:h,age:n,totalTokens:o,requestsInfo:w,allRequestIds:y,statusDetail:p.detail,specialization:m?.specialization??null,currentAction:r,recentActions:l})}if(0===C.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
171
- const T=r?C.slice(0,r):C
172
- let j={}
173
- if("specialist"===s.agentType&&n&&e.projectId){const t={},s=[]
174
- for(const e of T)for(const n of e.allRequestIds)t[n]||(t[n]=!0,s.push(n))
175
- if(s.length>0)try{const t=await n.proxyPost(`/api/projects/${e.projectId}/areas/by-requests`,{requestLocalIds:s}),o=t?.data?.areasByRequest??t?.areasByRequest
176
- o&&"object"==typeof o&&(j=o)}catch{}}const E=T.map((e=>{const t=[]
177
- for(const n of e.allRequestIds){const e=j[n]
178
- if(Array.isArray(e)){for(const n of e){if(t.length>=5)break
179
- "string"!=typeof n||t.includes(n)||t.push(n)}if(t.length>=5)break}}const n=(e.specialization||"").split(",").map((e=>e.trim())).filter(Boolean),o=n.length>0?n.slice(0,3):t.slice(0,3),i=o.length>0?`\n Areas: ${o.join(", ")}`:"",r=e.currentAction?`\n Current: ${e.currentAction}`:"",a="specialist"!==s.agentType&&e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
180
- return`\u2022 ${e.id}\n ${e.title}${i}\n Status: ${e.status} (${e.statusDetail}) | Age: ${l(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${r}${a}`})),q=r&&C.length>r?` (showing ${r} of ${C.length})`:""
181
- return{content:[{type:"text",text:`Found ${T.length} session${1!==T.length?"s":""}${q}:\n\n${E.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,k.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=g,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&&!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")
182
165
  try{const n=e,{sessionId:d,operation:u}=n
183
- let p
184
- switch(l.info("Processing subagent_operations",{operation:u,sessionId:d}),u){case"spawn":p=await r({agent:n.agent,prompt:n.prompt,requestId:n.requestId,allowParallel:n.allowParallel,useExistingSession:n.useExistingSession},t,l,o,i)
166
+ let g
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)
185
168
  break
186
- case"continue":p=await a(d,n.prompt,t,l,o,void 0,i)
169
+ case"continue":g=await a(d,n.prompt,t,l,o,void 0,i)
187
170
  break
188
- case"check-status":p=await f(d,t,l,o)
171
+ case"check-status":g=await f(d,t,l,o)
189
172
  break
190
- case"abort":p=await h(d,t,l)
173
+ case"abort":g=await h(d,t,l)
191
174
  break
192
175
  case"read-messages":{const e=!(!n.compact&&null==n.select),s=!e&&null==n.limit,o=n.limit??(e?50:8)
193
- p=await async function(e,t,n,s,o){const i=await(0,$.getServerUrl)(n,s)
176
+ g=await async function(e,t,n,s,o){const i=await(0,y.getServerUrl)(n,s)
194
177
  if(!i)throw new w.ValidationError("Could not determine OpenCode server URL")
195
178
  s.info("Reading messages from subagent session",{sessionId:e,limit:t,compact:o?.compact,select:o?.select})
196
179
  try{const n=`${i.replace(/\/$/,"")}/session/${e}/message?limit=${t}`,s=await fetch(n,{signal:AbortSignal.timeout(8e3)})
@@ -207,7 +190,7 @@ n.push(`\u{1f527} ${e.tool||"tool"} [${t.status??"unknown"}]`),n.push(` input:
207
190
  const s=u(t.output)
208
191
  s&&n.push(` output: ${a(s,1500)}`)}return{content:[{type:"text",text:n.join("\n")}]}}if(o?.compact){const t=r.map(((e,t)=>{const n=e.parts.filter((e=>"tool"===e.type)).map(d),s=e.parts.filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join(" ").replace(/\s+/g," ").trim().slice(0,140),o=[...n,s?`"${s}${s.length>=140?"\u2026":""}"`:""].filter(Boolean).join(" \xb7 ")
209
192
  return`[${t+1}] ${"USER"===l(e)?"USER":"ASST"} (${c(e)}): ${o||"(empty)"}`}))
210
- return{content:[{type:"text",text:`Session ${e}: ${r.length} messages (compact; use select:N for one in full):\n\n${t.join("\n")}`}]}}const p=(e,t)=>{const n=[`[${t+1}] ${l(e)} (${c(e)}):`]
193
+ return{content:[{type:"text",text:`Session ${e}: ${r.length} messages (compact; use select:N for one in full):\n\n${t.join("\n")}`}]}}const g=(e,t)=>{const n=[`[${t+1}] ${l(e)} (${c(e)}):`]
211
194
  for(const t of e.parts)if("text"===t.type&&t.text)n.push(a(t.text,5e3))
212
195
  else if("tool"===t.type){const e=t.state||{},s=u(e.output).replace(/\s+/g," ").trim()
213
196
  n.push(`\u{1f527} ${t.tool||"tool"} [${e.status??"unknown"}]${s?` \u2192 ${a(s,200)}`:""}`)}return n.length>1?n.join("\n"):`${n[0]}\n(no content)`}
@@ -215,15 +198,15 @@ if(o?.latest){const t=e=>e.parts.some((e=>"text"===e.type&&e.text||"tool"===e.ty
215
198
  let n=r.length-1
216
199
  for(;n>0&&!t(r[n]);)n--
217
200
  const s=r.length-1-n
218
- 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${p(r[n],n)}`}]}}const g=r.map(((e,t)=>p(e,t))).join("\n\n---\n\n")
219
- return{content:[{type:"text",text:`Session ${e}: last ${r.length} messages:\n\n${g}`}]}}catch(e){throw new Error(`Failed to read messages: ${(0,k.getErrorMessage)(e)}`)}}(d,o,t,l,{compact:n.compact,select:n.select,latest:s})
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})
220
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}
221
- p=await m(t,l,o,e)
204
+ g=await m(t,l,o,e)
222
205
  break
223
206
  default:throw new w.ValidationError(`Unknown operation: ${u}`)}c({success:!0}),s.incrementCounter(`subagent_operations_${u}_success`,1)
224
- const g=T[u]
225
- return g?(0,C.withAlchemyKind)(p,g):p}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,k.getErrorMessage)(e)}`}],isError:!0}}}
226
- const w=t(98547),y=t(3928),$=t(16714),S=t(49725),I=t(7451),b=t(87907),k=t(21319),v=t(79896),x=t(16928),A=t(97734),C=t(62263),T={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},j="true"===process.env.ALCHEMY_DEBUG_LOGS,E=(0,t(53603).getActionServerPort)(),q=6e4,P=3e5,R=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
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
227
210
  if(s&&s.basePath===e.basePath&&s.envUrl===i)return s.result
228
211
  let r=i
229
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)
@@ -240,11 +223,11 @@ const i=await e.json(),l=await t.json()
240
223
  let c=null
241
224
  if(s&&s.ok)try{const e=await s.json(),t=e?.[r.sessionId]
242
225
  c=void 0!==t&&"busy"===("string"==typeof t?t:t?.type)}catch{}const d=(0,n.extractRecentActivity)(l),u=(0,n.deriveActivityStatus)(d.lastActivityTime,i.time?.updated??Date.now())
243
- let p=null,g=null
226
+ let g=null,p=null
244
227
  for(let e=l.length-1;e>=0;e--){const t=l[e]
245
228
  if("assistant"!==t.info.role)continue
246
229
  const s=(t.parts??[]).filter((e=>"text"===e.type&&e.text)).map((e=>e.text)).join(" ").replace(/\s+/g," ").trim()
247
- if(s){p=s.length>220?s.slice(0,217)+"...":s,g=(0,n.formatTimeSince)(Date.now()-(t.info.time.completed??t.info.time.created))
230
+ if(s){g=s.length>220?s.slice(0,217)+"...":s,p=(0,n.formatTimeSince)(Date.now()-(t.info.time.completed??t.info.time.created))
248
231
  break}}const f=function(e){for(let t=e.length-1;t>=0;t--){const n=e[t]
249
232
  if("user"===n.info.role)return null
250
233
  const s=n.parts??[]
@@ -252,8 +235,9 @@ for(let e=s.length-1;e>=0;e--){const t=s[e]
252
235
  if("tool"!==t.type)continue
253
236
  const n="error"===t.state?.status?String(t.state?.error??""):"string"==typeof t.state?.output?t.state.output:""
254
237
  if("error"===t.state?.status||o.test(n)){const e=String(t.tool??"tool").replace(/^alchemy_/,""),s=n.replace(/\s+/g," ").trim()
255
- return`${e}: ${s.length>240?s.slice(0,237)+"...":s}`}}}return null}(l)
256
- return{sessionId:r.sessionId,agentType:r.agentType,busy:c,status:u.status,detail:u.detail,current:d.current,recent:d.recent,lastSaid:p,lastSaidAgo:g,lastRefusal:f}}catch{return null}}
238
+ 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
+ if("string"==typeof s&&s)return s}return null}(l)
240
+ 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}}
257
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
258
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))
259
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.`}
package/dist/603.js ADDED
@@ -0,0 +1,18 @@
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
3
+ if(n&&null!==y&&e-v<g)return y
4
+ const o=(0,l.join)(t||r(),h,d)
5
+ if((0,a.existsSync)(o))try{const t=(0,a.readFileSync)(o,"utf-8"),r=JSON.parse(t)
6
+ if("number"==typeof r.port&&r.port>0&&r.port<65536)return n&&(y=r.port,v=e),r.port}catch{}return n&&(y=f,v=e),f}function o(t){return t||r()}async function c(t){try{const e=await(0,p.fetch)(`http://localhost:${t}/health`,{signal:AbortSignal.timeout(1500)})
7
+ if(!e.ok)return null
8
+ const r=await e.json()
9
+ return"string"==typeof r?.projectPath?r.projectPath:null}catch{return null}}async function s(t){const e=o(t),r=Date.now(),s=w.get(e)
10
+ if(s&&r-s.at<S)return s.port
11
+ for(const o of[0,1]){1===o&&(y=null,v=0)
12
+ const s=n(t),u=await c(s)
13
+ if(u&&i(u)===i(e))return w.set(e,{port:s,at:r}),s}return w.delete(e),null}function i(t){try{return(0,a.realpathSync)(t)}catch{return t}}function u(){const t=Date.now()
14
+ return t-m>S&&(m=t,s().then((t=>{null===t&&(y=null,v=0,console.error(`[alchemy] the recorded action-server port does not serve ${o()}: re-reading it. If this repeats, the server for this project is not running.`))})).catch((()=>{}))),`http://localhost:${n()}`}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getActionServerPort=n,exports.verifiedActionServerPort=s,exports.getActionServerBaseUrl=u,exports.actionServerFetch=function(t,e){return j||(j=new p.Agent({headersTimeout:0,bodyTimeout:0})),(0,p.fetch)(`${u()}${t}`,{...e,dispatcher:j})}
15
+ const a=e(79896),l=e(16928),p=e(9956),f=3456,h=".alchemy_lab",d="action-server.json"
16
+ let y=null,v=0
17
+ const g=5e3,w=new Map,S=3e4
18
+ let j,m=0}}