alchemylab 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1679,7 +1679,7 @@ if(k=s.sessionId,y=s.model,v=s.serverUrl,b=s.initialBranch,S=s.pickupGuidance??n
1679
1679
  if(/refused to spawn a second coder|already has a live coder/.test(t))return m.info("Spawn refused by the record; not falling back",{requestId:u,error:t}),{success:!1,sessionId:null,error:t,refused:!0}
1680
1680
  m.warn("Session preparation failed \u2014 spawning without pre-created session",{error:t}),v=r(l)||void 0}let I=i
1681
1681
  if(S&&(I=`${S}\n\n---\n\n${i}`,m.info("Pickup guidance composed into spawn prompt",{requestId:u,sessionId:k})),k&&v){const e=await(0,n.deliverInitialPrompt)({serverUrl:v,sessionId:k,agent:t,prompt:I,model:y,directory:l,...w?{tools:w}:{}})
1682
- return e.success?(m.info("Spawned action agent",{agent:t,sessionId:k,requestId:u}),{success:!0,sessionId:k,...y?{model:y}:{}}):{success:!1,sessionId:k,error:e.error||"Prompt delivery failed"}}const $=(0,s.spawnDetachedSubagent)({agent:t,prompt:I,cwd:c,sessionId:k||void 0,serverUrl:v,model:y,initialBranch:b,allowParallel:p??!1,onSpawn:e=>{e&&g&&g(e)}})
1682
+ return e.success?(m.info("Spawned action agent",{agent:t,sessionId:k,requestId:u}),{success:!0,sessionId:k,...y?{model:y}:{},...e.messageId?{promptMessageId:e.messageId}:{}}):{success:!1,sessionId:k,error:e.error||"Prompt delivery failed"}}const $=(0,s.spawnDetachedSubagent)({agent:t,prompt:I,cwd:c,sessionId:k||void 0,serverUrl:v,model:y,initialBranch:b,allowParallel:p??!1,onSpawn:e=>{e&&g&&g(e)}})
1683
1683
  return $.success?(m.info("Spawned action agent (legacy CLI fallback)",{agent:t,sessionId:k,pid:$.pid,requestId:u}),{success:!0,sessionId:k,pid:$.pid,...y?{model:y}:{}}):{success:!1,sessionId:k,error:$.error||"Spawn failed"}}
1684
1684
  const s=t(48035),n=t(15899),o=t(58824),i=t(60833),a=t(61292)
1685
1685
  exports.fR=9e4},47078(e,exports,t){"use strict"
@@ -1899,8 +1899,8 @@ const r=e.data,u=Array.isArray(r.acceptanceCriteria)&&r.acceptanceCriteria.lengt
1899
1899
  return p.success?(i.info("Driver spawned",{requestId:n,sessionId:p.sessionId}),{success:!0,message:`Driver spawned for ${n} (session ${p.sessionId})`}):{success:!1,message:`Failed to spawn driver for ${n}`,error:p.error}}catch(e){const t=(0,r.getErrorMessage)(e)
1900
1900
  return i.error("start-drive action failed",{requestId:n,error:t}),{success:!1,message:`Drive dispatch failed for ${n}`,error:t}}}
1901
1901
  const r=t(58824),s=t(28148)},93503(e,exports,t){"use strict"
1902
- async function r(e,t,r){try{const s=await(0,f.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/requests?inFlight=true&limit=100&offset=${t}`),n=e=>String(e??"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),o=n(t?.title)
1903
- return o?s.filter(e=>e&&(e.localId??e.id)!==r&&["open","in_progress","ready_for_review"].includes(e.status)&&n(e.title)===o).map(e=>e.localId??e.id):[]}catch{return[]}}async function s(e,t,r,s=5e3){const n=(0,v.getOpenCodeUrlFromConfig)(e.projectPath)
1902
+ async function r(e,t,r){try{const s=await(0,m.readAllPages)(e.apiClient,t=>`/api/projects/${e.projectId}/requests?inFlight=true&limit=100&offset=${t}`),n=e=>String(e??"").toLowerCase().replace(/[^a-z0-9]+/g," ").trim(),o=n(t?.title)
1903
+ return o?s.filter(e=>e&&(e.localId??e.id)!==r&&["open","in_progress","ready_for_review"].includes(e.status)&&n(e.title)===o).map(e=>e.localId??e.id):[]}catch{return[]}}async function s(e,t,r,s=5e3){const n=(0,k.getOpenCodeUrlFromConfig)(e.projectPath)
1904
1904
  if(!n)return!0
1905
1905
  const o=Date.now()+r
1906
1906
  let i=!1
@@ -1910,101 +1910,105 @@ const r=await e.json()
1910
1910
  if(!Array.isArray(r)||r.some(e=>e?.name===t))return!0}catch{return!0}if(Date.now()>=o)return!1
1911
1911
  i||(i=!0,e.logger.info("start-worker: waiting for the agent to load in the runtime",{agent:t})),await new Promise(e=>setTimeout(e,s))}}async function n(e,t,r){let s=""
1912
1912
  try{const r=await e.apiClient.get(`/api/projects/${e.projectId}/requests/${t}/record-for-agents`)
1913
- s=String((r?.data??r)?.text??"")}catch{}try{return function(e,t){const r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[]
1914
- return[`## Description\n${String(e?.description??"").trim()||"_No description provided._"}`,`## Acceptance Criteria\n${(0,I.formatCriteriaWithHashes)(r)}`,...t&&t.trim()?[t.trim()]:[]].join("\n\n")}(r,s)}catch{return""}}async function o(e,t){if(!t)return null
1915
- try{const r=(0,A.simpleGit)(e),s=await r.diffSummary([`${t}..HEAD`])
1916
- return{files:s.files.map(e=>({file:e.file,insertions:e.insertions??0,deletions:e.deletions??0})),insertions:s.insertions,deletions:s.deletions}}catch{return null}}async function i(e,t){if(!t)return[]
1917
- try{const r=(0,A.simpleGit)(e)
1918
- return(await r.raw(["diff","--name-only",`${t}^1`,t]).catch(async()=>r.raw(["diff","--name-only",`${t}^`,t]))).split("\n").map(e=>e.trim()).filter(Boolean)}catch{return[]}}async function a(e,t,r){const{apiClient:s,projectId:n,projectPath:a,logger:c}=e,{p:l,req:d,worker:u,startPrompt:h,toolMask:f,deps:g}=t,y=r.sessionId
1919
- if(r.reopenLocalId)try{await s.patch(`/api/projects/${n}/cli-sessions/${encodeURIComponent(r.reopenLocalId)}`,{status:"active"})}catch(e){c.warn("session routing: could not reopen the cold worker",{requestId:l.requestId,sessionId:y,error:(0,S.getErrorMessage)(e)})}let I=null
1920
- try{const e=await s.post(`/api/projects/${n}/requests/${l.requestId}/pickup-compose`,{sessionId:y,agentType:u,warm:!0})
1921
- I=(e?.data??e)?.text??null,I=await(0,C.withInFlightFiles)(a,I,e?.data??e)}catch(e){c.warn("session routing: warm pickup guidance failed; sending the request alone",{requestId:l.requestId,error:(0,S.getErrorMessage)(e)})}let $=null
1922
- try{const e=r.previousRequestId?await s.get(`/api/projects/${n}/requests/${r.previousRequestId}`):null,t=(e?.data??e)?.mergeCommitSha??(e?.data??e)?.merge_commit_sha??null,[c,l]=await Promise.all([o(a,t),i(a,t)])
1923
- $=(0,E.sinceYouLastLookedBlock)(c,r.previousRequestId,l)}catch{}const j=[(_=l.requestId,T=r.previousRequestId,x=r.area,[`${x?"Your next request":"Next request in your thread"}: ${_}. This is a NEW record: ${T?`${T} is closed and must not be reopened, re-verified or amended`:"the request you finished before is closed"}. Work on ${_}'s branch, not the old one.`,...x?["It is separate work in the same area, not a follow-up: take nothing over from what you did before except what you know of the code."]:[],"You already have this project in context, so do not re-read the map or the files you know; open only what this request changes."].join("\n")),h,$,I].filter(Boolean).join("\n\n"),A=await(g.deliver??((t,r)=>(0,w.deliverPrompt)(e,t,r,{kind:"inserted-prompt-generic",partPayload:{requestId:l.requestId,source:l.source},...(0,m.hasMask)(f)?{toolDenies:f}:{}})))(y,j)
1924
- var _,T,x
1925
- if(!1===A?.ok)return c.warn("session routing: delivery to the warm worker failed; spawning a fresh one",{requestId:l.requestId,sessionId:y,error:A?.error}),!1
1926
- try{await async function(e,t,r){const{apiClient:s,projectId:n,logger:o}=e
1913
+ s=String((r?.data??r)?.text??"")}catch{}try{return function(e,t){const r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[],s=e?.roundOf?`## ${e.roundOf}'s criteria (met when it completed)\nAn answer needs nothing more. If you change the project, your completion accounts for these again, on your change.\n${(0,j.formatCriteriaWithHashes)(r).replace(/\[UNPROVEN\] /g,"")}`:`## Acceptance Criteria\n${(0,j.formatCriteriaWithHashes)(r)}`
1914
+ return[`## Description\n${String(e?.description??"").trim()||"_No description provided._"}`,s,...t&&t.trim()?[t.trim()]:[]].join("\n\n")}(r,s)}catch{return""}}function o(e,t,r,s){const n=[t?.roundOf?`The user wrote on the page of ${t.roundOf}, which is completed. Their message is ${e.requestId}; its record is at the end of this message. If you only answer it, what you write last in this turn is your reply to them, and nothing else is needed. If you change the project for it, the change is ${e.requestId}'s: make it and complete ${e.requestId} as you complete any request.`:`Implement ${e.requestId} ("${t?.title??""}") and own its full lifecycle. It was registered via ${e.source??"a registration door"}.`,s?"Its record, as get_request_information shows it now, is at the end of this message: start from it, and read the request again only when you need what changed since. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.":"Read it with get_request_information first. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.",...t?.roundOf?[]:[`If its acceptance criteria ALREADY hold as the code stands, do no work: complete ${e.requestId} yourself as a verification-only completion, naming exactly which criteria hold and where. Nobody else closes it: a report saying so left req000210 in progress with nobody working it.`,'Otherwise build what it asks (its own choices go in an opening brief when the pickup briefing asks for one), run the relevant validation, and complete the request with evidence. A value the request states is the value: a version number, a label, a default. Do not bump or "improve" it (two coders raised a footer version the user had just set, 2026-09-07).']]
1915
+ return r.length&&n.push(`Possible duplicates already open with the same title: ${r.join(", ")}. Check before doing the work twice; if one already covers it, cancel ${e.requestId} as superseded.`),e.userNote&&n.push(`From the user: ${e.userNote}`),e.rules?.length&&n.push(function(e){const t=e.filter(e=>"told"===e.enforcement),r=e.filter(e=>"told"!==e.enforcement),s=[...t.map(e=>`- [guidance:${e.id}] ${e.text}`),...r.map(e=>`- [guidance:${e.id}] (reviewed, ${e.severity}) ${e.text}`)]
1916
+ return`The workflow's guidance for this work (the user's; where an item conflicts with the brief above, the guidance wins).${r.length?" A reviewer checks the reviewed ones by id at the review gate: 'block' ones must hold, 'advise' ones are expected.":""} When the user's own words make an exception to an item for this request, record it with guidance exception (its id as listed, their words, the scope they said); when the request contradicts an item and they have not said, guidance conflict, and follow the guidance until they answer.\n${s.join("\n")}`}(e.rules)),s&&n.push(`\n# ${e.requestId}: ${t?.title??""}\n${s}`),n.join("\n")}async function i(e,t){if(!t)return null
1917
+ try{const r=(0,_.simpleGit)(e),s=await r.diffSummary([`${t}..HEAD`])
1918
+ return{files:s.files.map(e=>({file:e.file,insertions:e.insertions??0,deletions:e.deletions??0})),insertions:s.insertions,deletions:s.deletions}}catch{return null}}async function a(e,t){if(!t)return[]
1919
+ try{const r=(0,_.simpleGit)(e)
1920
+ return(await r.raw(["diff","--name-only",`${t}^1`,t]).catch(async()=>r.raw(["diff","--name-only",`${t}^`,t]))).split("\n").map(e=>e.trim()).filter(Boolean)}catch{return[]}}async function c(e,t,r){const{apiClient:s,projectId:n,projectPath:o,logger:c}=e,{p:l,req:d,worker:u,startPrompt:h,toolMask:p,deps:m}=t,w=r.sessionId
1921
+ if(r.reopenLocalId)try{await s.patch(`/api/projects/${n}/cli-sessions/${encodeURIComponent(r.reopenLocalId)}`,{status:"active"})}catch(e){c.warn("session routing: could not reopen the cold worker",{requestId:l.requestId,sessionId:w,error:(0,$.getErrorMessage)(e)})}const v=!!t.req?.roundOf&&!!l.unitWorker&&w===l.unitWorker,b=v?s.post(`/api/projects/${n}/requests/${l.requestId}/pickup-compose`,{sessionId:w,agentType:u,warm:!0}).catch(()=>null):null
1922
+ let j=null
1923
+ if(!v)try{const e=await s.post(`/api/projects/${n}/requests/${l.requestId}/pickup-compose`,{sessionId:w,agentType:u,warm:!0})
1924
+ j=(e?.data??e)?.text??null,j=await(0,T.withInFlightFiles)(o,j,e?.data??e)}catch(e){c.warn("session routing: warm pickup guidance failed; sending the request alone",{requestId:l.requestId,error:(0,$.getErrorMessage)(e)})}let E=null
1925
+ if(!v)try{const e=r.previousRequestId?await s.get(`/api/projects/${n}/requests/${r.previousRequestId}`):null,t=(e?.data??e)?.mergeCommitSha??(e?.data??e)?.merge_commit_sha??null,[c,l]=await Promise.all([i(o,t),a(o,t)])
1926
+ E=(0,C.sinceYouLastLookedBlock)(c,r.previousRequestId,l)}catch{}const A=!!t.req?.roundOf,_=[A?null:(M=l.requestId,R=r.previousRequestId,P=r.area,[`${P?"Your next request":"Next request in your thread"}: ${M}. This is a NEW record: ${R?`${R} is closed and must not be reopened, re-verified or amended`:"the request you finished before is closed"}. Work on ${M}'s branch, not the old one.`,...P?["It is separate work in the same area, not a follow-up: take nothing over from what you did before except what you know of the code."]:[],"You already have this project in context, so do not re-read the map or the files you know; open only what this request changes."].join("\n")),h,E,j].filter(Boolean).join("\n\n"),x=await(m.deliver??((t,r)=>(0,y.deliverPrompt)(e,t,r,{kind:A?"unit-start":"inserted-prompt-generic",partPayload:{requestId:l.requestId,source:l.source},...(0,g.hasMask)(p)?{toolDenies:p}:{}})))(w,_)
1927
+ var M,R,P
1928
+ if(!1===x?.ok)return c.warn("session routing: delivery to the warm worker failed; spawning a fresh one",{requestId:l.requestId,sessionId:w,error:x?.error}),!1
1929
+ try{b&&await b,await async function(e,t,r){const{apiClient:s,projectId:n,logger:o}=e
1927
1930
  try{const e=await s.get(`/api/projects/${n}/session-request-associations?activeOnly=true&limit=500`),i=(e?.data??e??[]).filter(e=>(e?.requestLocalId??e?.request_local_id)===t).map(e=>String(e?.opencodeSessionId??e?.opencode_session_id??"")).filter(e=>e&&e!==r)
1928
1931
  if(!i.length)return
1929
- await s.post(`/api/projects/${n}/session-request-associations/end-all`,{requestLocalId:t}),o.info("session routing: released prior workers before hand-off",{requestId:t,released:i.length})}catch(e){o.warn("session routing: could not release prior workers",{requestId:t,error:(0,S.getErrorMessage)(e)})}}(e,l.requestId,y),await s.post(`/api/projects/${n}/session-request-associations`,{opencodeSessionId:y,requestLocalId:l.requestId,agentType:u})
1932
+ await s.post(`/api/projects/${n}/session-request-associations/end-all`,{requestLocalId:t}),o.info("session routing: released prior workers before hand-off",{requestId:t,released:i.length})}catch(e){o.warn("session routing: could not release prior workers",{requestId:t,error:(0,$.getErrorMessage)(e)})}}(e,l.requestId,w),await s.post(`/api/projects/${n}/session-request-associations`,{opencodeSessionId:w,requestLocalId:l.requestId,agentType:u})
1930
1933
  const t=`[${l.requestId}] ${l.verb??"Implement"} ${String(d?.title??"").trim()}`.trim().slice(0,160)
1931
- try{const e=(0,v.getOpenCodeUrlFromConfig)(a)
1932
- e&&await fetch(`${e.replace(/\/$/,"")}/session/${y}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:t}),signal:AbortSignal.timeout(5e3)}),await s.post(`/api/projects/${n}/cli-sessions`,{externalSessionId:y,providerId:"opencode",title:t})}catch(e){c.warn("session routing: warm worker not retitled",{requestId:l.requestId,sessionId:y,error:(0,S.getErrorMessage)(e)})}}catch(e){c.warn("session routing: could not associate the warm worker with the request",{requestId:l.requestId,sessionId:y,error:(0,S.getErrorMessage)(e)})}try{await(0,b.ensureRequestWorktree)(a,l.requestId,c)}catch(e){c.warn("start-worker: worktree for the warm worker failed",{requestId:l.requestId,error:(0,S.getErrorMessage)(e)})}return c.info(r.area?"session routing: request handed to a warm worker of its area (no thread link)":"session routing: request handed to the warm worker already on its thread",{requestId:l.requestId,sessionId:y,previous:r.previousRequestId}),"request"===(0,p.kindProfile)(d?.type).branch&&(g.prewarm??k.prewarmWorkerBox)(e,y,l.requestId),!0}async function c(e,t){const{apiClient:r,projectId:n,projectPath:o,logger:i}=e,{p:a,req:c,worker:l,startPrompt:d,toolMask:u,deps:h}=t,f="string"==typeof a.verb&&a.verb.trim()||(0,p.kindProfile)(c?.type).verb,w=(e,t)=>{const r=String(t??"").slice(0,50)
1934
+ try{const e=(0,k.getOpenCodeUrlFromConfig)(o)
1935
+ e&&await fetch(`${e.replace(/\/$/,"")}/session/${w}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({title:t}),signal:AbortSignal.timeout(5e3)}),await s.post(`/api/projects/${n}/cli-sessions`,{externalSessionId:w,providerId:"opencode",title:t})}catch(e){c.warn("session routing: warm worker not retitled",{requestId:l.requestId,sessionId:w,error:(0,$.getErrorMessage)(e)})}}catch(e){c.warn("session routing: could not associate the warm worker with the request",{requestId:l.requestId,sessionId:w,error:(0,$.getErrorMessage)(e)})}try{await(0,S.ensureRequestWorktree)(o,l.requestId,c)}catch(e){c.warn("start-worker: worktree for the warm worker failed",{requestId:l.requestId,error:(0,$.getErrorMessage)(e)})}return c.info(r.area?"session routing: request handed to a warm worker of its area (no thread link)":"session routing: request handed to the warm worker already on its thread",{requestId:l.requestId,sessionId:w,previous:r.previousRequestId}),"request"===(0,f.kindProfile)(d?.type).branch&&(m.prewarm??I.prewarmWorkerBox)(e,w,l.requestId),!0}async function l(e,t){const{apiClient:r,projectId:n,projectPath:o,logger:i}=e,{p:a,req:c,worker:l,startPrompt:d,toolMask:u,deps:h}=t,p="string"==typeof a.verb&&a.verb.trim()||(0,f.kindProfile)(c?.type).verb,m=(e,t)=>{const r=String(t??"").slice(0,50)
1933
1936
  return r.toLowerCase().startsWith(`${e.toLowerCase()} `)?r:`${e} ${r}`},y=function(e){if(e&&(e.tier||e.provider))return{...e.tier?{modelTier:e.tier}:{},...e.provider?{provider:e.provider}:{}}}(a.model)
1934
1937
  if(!await(h.agentLoaded??s)(e,l,39e4))return{success:!1,message:`start-worker: the agent "${l}" has not loaded in this project's runtime, so ${a.requestId} has nobody to start on. A new or changed agent loads when running work pauses; start the request again then, or restart AlchemyLab in the project folder.`,error:"agent_not_loaded"}
1935
- const v=h.spawnCoder??(async()=>(0,g.spawnActionAgent)({agent:l,prompt:d,cwd:o,projectPath:o,projectId:n,title:`[${a.requestId}] ${w(f,c?.title)}`,requestId:a.requestId,parentSessionId:a.hubSessionId,apiClient:r,logger:i,trackChildPid:e.trackChildPid,...(0,m.hasMask)(u)?{toolMask:u}:{},...y?{modelOverride:y}:{}})),b=await v(d,{modelOverride:y})
1936
- return b?.refused?(i.info("start-worker: the request already has its worker; nothing spawned",{requestId:a.requestId,reason:b?.error}),{success:!0,message:`${a.requestId} already has its worker; no second one started`}):b?.success&&b?.sessionId?(i.info("session routing: worker spawned onto the request",{requestId:a.requestId,worker:l,sessionId:b.sessionId}),"request"===(0,p.kindProfile)(c?.type).branch&&(h.prewarm??k.prewarmWorkerBox)(e,b.sessionId,a.requestId),{success:!0,message:`${a.requestId} handed to ${l} ${b.sessionId}`}):{success:!1,message:`start-worker: could not start a ${l} session for ${a.requestId}`,error:b?.error??"spawn_failed"}}async function l(e,t,r){const{apiClient:s,projectId:n,projectPath:o}=e,i=(0,v.getOpenCodeUrlFromConfig)(o)
1938
+ const b=h.spawnCoder??(async()=>(0,w.spawnActionAgent)({agent:l,prompt:d,cwd:o,projectPath:o,projectId:n,title:`[${a.requestId}] ${m(p,c?.title)}`,requestId:a.requestId,parentSessionId:a.hubSessionId,apiClient:r,logger:i,trackChildPid:e.trackChildPid,...(0,g.hasMask)(u)?{toolMask:u}:{},...y?{modelOverride:y}:{}})),k=await b(d,{modelOverride:y})
1939
+ return k?.refused?(i.info("start-worker: the request already has its worker; nothing spawned",{requestId:a.requestId,reason:k?.error}),{success:!0,message:`${a.requestId} already has its worker; no second one started`}):k?.success&&k?.sessionId?(i.info("session routing: worker spawned onto the request",{requestId:a.requestId,worker:l,sessionId:k.sessionId}),c?.roundOf&&"string"==typeof k.promptMessageId&&k.promptMessageId&&(0,v.trackInboxChatTurn)(e,k.sessionId,Date.now(),k.promptMessageId,void 0,void 0,void 0,void 0,{kind:"unit-start",requestId:a.requestId}),"request"===(0,f.kindProfile)(c?.type).branch&&(h.prewarm??I.prewarmWorkerBox)(e,k.sessionId,a.requestId),{success:!0,message:`${a.requestId} handed to ${l} ${k.sessionId}`}):{success:!1,message:`start-worker: could not start a ${l} session for ${a.requestId}`,error:k?.error??"spawn_failed"}}async function d(e,t,r){const{apiClient:s,projectId:n,projectPath:o}=e,i=(0,k.getOpenCodeUrlFromConfig)(o)
1937
1940
  if(!i)return{busy:!1,liveElsewhere:null}
1938
- const a=await(0,w.sessionBusy)(i.replace(/\/$/,""),t)
1939
- return a?{busy:a,liveElsewhere:await d(e,t,r)}:{busy:!1,liveElsewhere:null}}async function d(e,t,r){const{apiClient:s,projectId:n}=e
1941
+ const a=await(0,y.sessionBusy)(i.replace(/\/$/,""),t)
1942
+ return a?{busy:a,liveElsewhere:await u(e,t,r)}:{busy:!1,liveElsewhere:null}}async function u(e,t,r){const{apiClient:s,projectId:n}=e
1940
1943
  try{const e=await s.get(`/api/projects/${n}/session-request-associations/by-session/${encodeURIComponent(t)}`),o=Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[],i=Array.from(new Set(o.map(e=>String(e?.requestLocalId??e?.request_local_id??"")).filter(e=>/^req\d+$/.test(e)&&e!==r)))
1941
1944
  for(const e of i){const t=await s.get(`/api/projects/${n}/requests/${e}`).catch(()=>null),r=String((t?.data??t)?.status??"")
1942
- if(!x.has(r))return e}return null}catch{return null}}async function u(e,t){if(t)try{const r=await e.apiClient.get(`/api/projects/${e.projectId}/requests/${t}`),s=(r?.data??r)?.mergeCommitSha??(r?.data??r)?.merge_commit_sha??null
1945
+ if(!R.has(r))return e}return null}catch{return null}}async function h(e,t){if(t)try{const r=await e.apiClient.get(`/api/projects/${e.projectId}/requests/${t}`),s=(r?.data??r)?.mergeCommitSha??(r?.data??r)?.merge_commit_sha??null
1943
1946
  if(!s)return
1944
- const[n,a]=await Promise.all([o(e.projectPath,s),i(e.projectPath,s)])
1947
+ const[n,o]=await Promise.all([i(e.projectPath,s),a(e.projectPath,s)])
1945
1948
  if(!n)return
1946
- const c=new Set(a)
1947
- return n.files.filter(e=>c.has(e.file)).reduce((e,t)=>e+t.insertions+t.deletions,0)}catch{return}}async function h(e,t){const{apiClient:r,projectId:s,projectPath:n,logger:o}=e,i=e.startWorkerDeps??{}
1948
- try{const a=await r.get(`/api/projects/${s}`),c=(0,$.readWarmCoderPolicy)((a?.data??a)?.metadata)
1949
+ const c=new Set(o)
1950
+ return n.files.filter(e=>c.has(e.file)).reduce((e,t)=>e+t.insertions+t.deletions,0)}catch{return}}async function p(e,t){const{apiClient:r,projectId:s,projectPath:n,logger:o}=e,i=e.startWorkerDeps??{}
1951
+ try{const a=await r.get(`/api/projects/${s}`),c=(0,E.readWarmCoderPolicy)((a?.data??a)?.metadata)
1949
1952
  if(!c.enabled)return{why:"off"}
1950
- const l=(0,v.getOpenCodeUrlFromConfig)(n),h=i.reloadedAt??(0,j.configReloadedAt)(n),p=i.lastCall??(e=>l?(0,j.readLastCall)(l,e):Promise.resolve({})),m=i.modelLimits??(l?await(0,j.readModelLimits)(l):new Map),g=new Set,w=new Map
1953
+ const l=(0,k.getOpenCodeUrlFromConfig)(n),d=i.reloadedAt??(0,A.configReloadedAt)(n),p=i.lastCall??(e=>l?(0,A.readLastCall)(l,e):Promise.resolve({})),f=i.modelLimits??(l?await(0,A.readModelLimits)(l):new Map),g=new Set,w=new Map
1951
1954
  if(l){const[e,t]=await Promise.all([fetch(`${l}/session`,{signal:AbortSignal.timeout(6e3)}).catch(()=>null),fetch(`${l}/session/status`,{signal:AbortSignal.timeout(6e3)}).catch(()=>null)])
1952
1955
  if(e?.ok){const t=await e.json().catch(()=>[])
1953
1956
  for(const e of t)w.set(e.id,Number(e.time?.updated??e.time?.created??0))}if(t?.ok){const e=await t.json().catch(()=>({}))
1954
- for(const[t,r]of Object.entries(e??{}))"busy"===r?.type&&g.add(t)}}const y=await(0,f.readAllPages)(r,e=>`/api/projects/${s}/cli-sessions?status=active&limit=100&offset=${e}`),b=new Set
1957
+ for(const[t,r]of Object.entries(e??{}))"busy"===r?.type&&g.add(t)}}const y=await(0,m.readAllPages)(r,e=>`/api/projects/${s}/cli-sessions?status=active&limit=100&offset=${e}`),v=new Set
1955
1958
  try{const e=await r.get(`/api/projects/${s}/requests/${t.requestId}/arc`)
1956
- for(const t of(e?.data??e)?.requests??[])t?.localId&&b.add(String(t.localId))}catch{}if(t.planDocumentId)try{const e=await(0,f.readAllPages)(r,e=>`/api/projects/${s}/requests?planDocumentId=${encodeURIComponent(t.planDocumentId)}&limit=100&offset=${e}`)
1957
- for(const t of e)(t?.localId??t?.id)&&b.add(String(t.localId??t.id))}catch{}for(const e of t.waitedOn??[])/^req\d{6}$/.test(e)&&b.add(e)
1958
- b.delete(t.requestId)
1959
- const k=new Map,S=new Map,I=new Map
1959
+ for(const t of(e?.data??e)?.requests??[])t?.localId&&v.add(String(t.localId))}catch{}if(t.planDocumentId)try{const e=await(0,m.readAllPages)(r,e=>`/api/projects/${s}/requests?planDocumentId=${encodeURIComponent(t.planDocumentId)}&limit=100&offset=${e}`)
1960
+ for(const t of e)(t?.localId??t?.id)&&v.add(String(t.localId??t.id))}catch{}for(const e of t.waitedOn??[])/^req\d{6}$/.test(e)&&v.add(e)
1961
+ v.delete(t.requestId)
1962
+ const b=new Map,S=new Map,I=new Map
1960
1963
  try{const e=await r.get(`/api/projects/${s}/session-request-associations?limit=500`)
1961
1964
  for(const r of e?.data??e??[]){const e=String(r?.opencodeSessionId??r?.opencode_session_id??""),s=String(r?.requestLocalId??r?.request_local_id??"")
1962
1965
  if(!e||!s||s===t.requestId)continue
1963
- b.has(s)&&k.set(e,(k.get(e)??0)+1)
1966
+ v.has(s)&&b.set(e,(b.get(e)??0)+1)
1964
1967
  const n=Date.parse(r?.startedAt??r?.started_at??"")||0
1965
- n>=(I.get(e)??-1)&&(I.set(e,n),S.set(e,s))}}catch{}const E=new Set(y.map(e=>e?.externalSessionId).filter(Boolean))
1966
- if(c.coldResume)for(const e of Array.from(k.keys()))if(!E.has(e))try{const t=await r.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),n=t?.data??t
1967
- n?.externalSessionId&&"archived"===n.status&&y.push(n)}catch{}const A=new Map,C=[]
1968
+ n>=(I.get(e)??-1)&&(I.set(e,n),S.set(e,s))}}catch{}const $=new Set(y.map(e=>e?.externalSessionId).filter(Boolean))
1969
+ if(c.coldResume)for(const e of Array.from(b.keys()))if(!$.has(e))try{const t=await r.get(`/api/projects/${s}/cli-sessions/external/opencode/${encodeURIComponent(e)}`),n=t?.data??t
1970
+ n?.externalSessionId&&"archived"===n.status&&y.push(n)}catch{}const j=new Map,C=[]
1968
1971
  for(const n of y){const o=n?.externalSessionId
1969
- if(!o||!(0,$.sameWorkerRole)(n.agentType,t.worker))continue
1972
+ if(!o||!(0,E.sameWorkerRole)(n.agentType,t.worker))continue
1970
1973
  const a=/\[(req\d{6})\]/.exec(String(n.title??""))
1971
1974
  !S.has(o)&&a&&S.set(o,a[1])
1972
- let l="",f=[]
1975
+ let l="",m=[]
1973
1976
  const y=S.get(o)
1974
1977
  if(y)try{const e=await r.get(`/api/projects/${s}/requests/${y}`)
1975
1978
  l=String((e?.data??e)?.type??"")
1976
1979
  const t=(e?.data??e)?.areas
1977
- Array.isArray(t)&&(f=t.map(String))}catch{}const v=(0,$.sharedAreas)(t.areas,f),b=g.has(o);(n.localId??n.id)&&A.set(o,String(n.localId??n.id))
1978
- const I=(k.get(o)??0)>0,j=c.areaReuse&&v>0,E=I||j?await p(o):{},_=Math.max(w.get(o)??0,E.atMs??0)||Date.parse(n.updatedAt??n.createdAt??"")||0,T=E.providerID&&E.modelID?m.get(`${E.providerID}/${E.modelID}`):void 0,x="archived"!==n.status&&(0,$.isCacheWarm)(Date.now(),{lastActiveMs:_,providerID:E.providerID},h),M=c.coldResume&&I&&!b&&!x?await(i.changedSince??(t=>u(e,t)))(y):void 0
1979
- C.push({...void 0!==E.contextTokens?{contextTokens:E.contextTokens}:{},...void 0!==M?{changedSinceLines:M}:{},...I||j?{contextCeiling:(0,$.contextCeilingFor)(T)}:{},providerID:E.providerID??null,externalSessionId:o,agentType:n.agentType,threadRequests:k.get(o)??0,sharedAreas:v,modelKey:(0,$.modelKeyOf)(t.kindModels,l),toolsKey:(0,$.toolsKeyOf)(l),lastActiveMs:_,busy:b,busyElsewhere:b&&(k.get(o)??0)>0?!!await d(e,o,t.requestId):void 0,archived:"archived"===n.status})}const _=(0,$.pickWarmCoder)(c,Date.now(),{worker:t.worker,modelKey:(0,$.modelKeyOf)(t.kindModels,t.kind),toolsKey:(0,$.toolsKeyOf)(t.kind)},C,h)
1980
- if(!_.reuse){const e=C.filter(e=>(e.threadRequests??0)>0&&(e.archived||!(0,$.isCacheWarm)(Date.now(),e,h))&&(void 0!==e.contextTokens||void 0!==e.changedSinceLines))
1980
+ Array.isArray(t)&&(m=t.map(String))}catch{}const v=(0,E.sharedAreas)(t.areas,m),k=g.has(o);(n.localId??n.id)&&j.set(o,String(n.localId??n.id))
1981
+ const I=(b.get(o)??0)>0,$=c.areaReuse&&v>0,A=I||$?await p(o):{},_=Math.max(w.get(o)??0,A.atMs??0)||Date.parse(n.updatedAt??n.createdAt??"")||0,T=A.providerID&&A.modelID?f.get(`${A.providerID}/${A.modelID}`):void 0,x="archived"!==n.status&&(0,E.isCacheWarm)(Date.now(),{lastActiveMs:_,providerID:A.providerID},d),M=c.coldResume&&I&&!k&&!x?await(i.changedSince??(t=>h(e,t)))(y):void 0
1982
+ C.push({...void 0!==A.contextTokens?{contextTokens:A.contextTokens}:{},...void 0!==M?{changedSinceLines:M}:{},...I||$?{contextCeiling:(0,E.contextCeilingFor)(T)}:{},providerID:A.providerID??null,externalSessionId:o,agentType:n.agentType,threadRequests:b.get(o)??0,sharedAreas:v,modelKey:(0,E.modelKeyOf)(t.kindModels,l),toolsKey:(0,E.toolsKeyOf)(l),lastActiveMs:_,busy:k,busyElsewhere:k&&(b.get(o)??0)>0?!!await u(e,o,t.requestId):void 0,archived:"archived"===n.status})}const _=(0,E.pickWarmCoder)(c,Date.now(),{worker:t.worker,modelKey:(0,E.modelKeyOf)(t.kindModels,t.kind),toolsKey:(0,E.toolsKeyOf)(t.kind)},C,d)
1983
+ if(!_.reuse){const e=C.filter(e=>(e.threadRequests??0)>0&&(e.archived||!(0,E.isCacheWarm)(Date.now(),e,d))&&(void 0!==e.contextTokens||void 0!==e.changedSinceLines))
1981
1984
  return e.length&&o.info("session routing: cold thread worker weighed and not resumed",{requestId:t.requestId,why:_.why,limits:{contextTokens:c.coldMaxContextTokens,changedLines:c.coldMaxChangedLines},weighed:e.map(e=>({sessionId:e.externalSessionId,contextTokens:e.contextTokens,changedSinceLines:e.changedSinceLines}))}),{why:_.why}}const T=C.find(e=>e.externalSessionId===_.sessionId)
1982
- return o.info(_.cold?"session routing: cold worker chosen (it built this thread and its code is still current)":_.area?"session routing: warm worker of the same area chosen (no thread link)":"session routing: warm worker chosen",{requestId:t.requestId,sessionId:_.sessionId,threadRequestsWorked:k.get(_.sessionId)??0,..._.area?{sharedAreas:T?.sharedAreas}:{},contextTokens:T?.contextTokens,contextCeiling:T?.contextCeiling,provider:T?.providerID??void 0,afterTurn:!!_.afterTurn,candidates:C.length,..._.cold?{changedSinceLines:T?.changedSinceLines,archived:!!T?.archived}:{}}),{sessionId:_.sessionId,previousRequestId:S.get(_.sessionId),..._.afterTurn?{afterTurn:!0}:{},..._.area?{area:!0}:{},..._.cold&&T?.archived?{reopenLocalId:A.get(_.sessionId)}:{}}}catch(e){return o.warn("session routing: warm-worker survey failed; spawning",{error:(0,S.getErrorMessage)(e)}),{why:"no-candidate"}}}exports.handleStartWorkerAction=async function(e,t){const s=e,{apiClient:o,projectId:i,logger:d}=t
1985
+ return o.info(_.cold?"session routing: cold worker chosen (it built this thread and its code is still current)":_.area?"session routing: warm worker of the same area chosen (no thread link)":"session routing: warm worker chosen",{requestId:t.requestId,sessionId:_.sessionId,threadRequestsWorked:b.get(_.sessionId)??0,..._.area?{sharedAreas:T?.sharedAreas}:{},contextTokens:T?.contextTokens,contextCeiling:T?.contextCeiling,provider:T?.providerID??void 0,afterTurn:!!_.afterTurn,candidates:C.length,..._.cold?{changedSinceLines:T?.changedSinceLines,archived:!!T?.archived}:{}}),{sessionId:_.sessionId,previousRequestId:S.get(_.sessionId),..._.afterTurn?{afterTurn:!0}:{},..._.area?{area:!0}:{},..._.cold&&T?.archived?{reopenLocalId:j.get(_.sessionId)}:{}}}catch(e){return o.warn("session routing: warm-worker survey failed; spawning",{error:(0,$.getErrorMessage)(e)}),{why:"no-candidate"}}}exports.handleStartWorkerAction=async function(e,t){const s=e,{apiClient:i,projectId:a,logger:h}=t
1983
1986
  if(!s.requestId)return{success:!1,message:"requestId is required",error:"missing_params"}
1984
- const u=t.startWorkerDeps??{},g=await o.get(`/api/projects/${i}/requests/${s.requestId}`),w=g?.data??g
1985
- if(!w||!["open"].includes(w.status))return{success:!0,message:`${s.requestId} is ${w?.status??"unknown"}: nothing to start`}
1986
- const v=T.get(s.requestId)
1987
- if(v)return{success:!0,message:`${s.requestId} starts on ${v} when its current turn ends`}
1988
- try{const e=async e=>{const t=await o.get(`/api/projects/${i}/requests/${e}`)
1987
+ const w=t.startWorkerDeps??{},y=await i.get(`/api/projects/${a}/requests/${s.requestId}`),v=y?.data??y
1988
+ if(!v||!["open"].includes(v.status))return{success:!0,message:`${s.requestId} is ${v?.status??"unknown"}: nothing to start`}
1989
+ const k=M.get(s.requestId)
1990
+ if(k)return{success:!0,message:`${s.requestId} starts on ${k} when its current turn ends`}
1991
+ if(v.roundOf&&"string"==typeof s.unitWorker&&s.unitWorker&&null!==(0,f.kindProfile)(v.type).worker)try{const[e,r]=await Promise.all([(w.turnState??d)(t,s.unitWorker,s.requestId),(w.requestRecord??n)(t,s.requestId,v)]),i=e.busy?e.liveElsewhere:await u(t,s.unitWorker,s.requestId).then(e=>e&&e!==v.roundOf?e:null)
1992
+ if(!e.busy&&!i){const e="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",n=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0}),i={p:s,req:v,worker:e,startPrompt:o(s,v,[],r),toolMask:n,deps:w}
1993
+ if(await c(t,i,{sessionId:s.unitWorker,previousRequestId:v.roundOf}))return{success:!0,message:`${s.requestId} handed to ${v.roundOf}'s own ${e} ${s.unitWorker}`}}}catch(e){h.info("session routing: the unit did not go straight to its request's worker; the ordinary start takes it",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}try{const e=async e=>{const t=await i.get(`/api/projects/${a}/requests/${e}`)
1989
1994
  return(t?.data??t)?.type}
1990
- t.projectPath&&await(0,_.defaultBranchHasCode)(t.projectPath,e)&&(await(0,_.reportProjectHasCode)(i,!0,(e,t)=>o.patch(e,t)),await o.post(`/api/projects/${i}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){d.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,S.getErrorMessage)(e)})}const b=await(u.openDuplicates??r)(t,w,s.requestId),k=await(u.requestRecord??n)(t,s.requestId,w),I=function(e,t,r,s){const n=[`Implement ${e.requestId} ("${t?.title??""}") and own its full lifecycle. It was registered via ${e.source??"a registration door"}.`,s?"Its record, as get_request_information shows it now, is at the end of this message: start from it, and read the request again only when you need what changed since. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.":"Read it with get_request_information first. If something it BUILDS ON does not exist in this project, call request_operations premise-block with the reason instead of starting. New content the request introduces is not a missing premise.",...t?.roundOf?[]:[`If its acceptance criteria ALREADY hold as the code stands, do no work: complete ${e.requestId} yourself as a verification-only completion, naming exactly which criteria hold and where. Nobody else closes it: a report saying so left req000210 in progress with nobody working it.`,'Otherwise build what it asks (its own choices go in an opening brief when the pickup briefing asks for one), run the relevant validation, and complete the request with evidence. A value the request states is the value: a version number, a label, a default. Do not bump or "improve" it (two coders raised a footer version the user had just set, 2026-09-07).']]
1991
- return r.length&&n.push(`Possible duplicates already open with the same title: ${r.join(", ")}. Check before doing the work twice; if one already covers it, cancel ${e.requestId} as superseded.`),e.userNote&&n.push(`From the user: ${e.userNote}`),e.rules?.length&&n.push(function(e){const t=e.filter(e=>"told"===e.enforcement),r=e.filter(e=>"told"!==e.enforcement),s=[...t.map(e=>`- [guidance:${e.id}] ${e.text}`),...r.map(e=>`- [guidance:${e.id}] (reviewed, ${e.severity}) ${e.text}`)]
1992
- return`The workflow's guidance for this work (the user's; where an item conflicts with the brief above, the guidance wins).${r.length?" A reviewer checks the reviewed ones by id at the review gate: 'block' ones must hold, 'advise' ones are expected.":""} When the user's own words make an exception to an item for this request, record it with guidance exception (its id as listed, their words, the scope they said); when the request contradicts an item and they have not said, guidance conflict, and follow the guidance until they answer.\n${s.join("\n")}`}(e.rules)),s&&n.push(`\n# ${e.requestId}: ${t?.title??""}\n${s}`),n.join("\n")}(s,w,b,k),j="string"==typeof s.worker&&s.worker.trim()||(0,p.kindProfile)(w?.type).worker||"worker",E=(0,m.spawnToolMask)({kind:w?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,m.hasMask)(E)&&d.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:j,off:Object.keys(E)})
1993
- const A={p:s,req:w,worker:j,startPrompt:I,toolMask:E,deps:u}
1994
- let C=null
1995
- const x=w?.filedBySession??w?.filed_by_session,M=w?.filedByAgent??w?.filed_by_agent
1996
- if("string"==typeof x&&x&&(0,$.sameWorkerRole)(M,j))try{if((await(0,f.readAllPages)(o,e=>`/api/projects/${i}/cli-sessions?status=active&limit=100&offset=${e}`)).some(e=>e?.externalSessionId===x)){const e=await(u.turnState??l)(t,x,s.requestId)
1997
- e.liveElsewhere?d.info("session routing: the filer is mid-turn on another live request; not handing it this one",{requestId:s.requestId,sessionId:x,on:e.liveElsewhere}):(C={sessionId:x},d.info("session routing: the session that filed this request keeps it",{requestId:s.requestId,sessionId:x}))}}catch{}if(C=C??await(u.pickWarm??h)(t,{worker:j,requestId:s.requestId,kind:w?.type,kindModels:s.kindModels,planDocumentId:w?.planDocumentId??w?.plan_document_id??null,areas:Array.isArray(w?.areas)?w.areas.map(String):[],waitedOn:Array.isArray(w?.waitedOn)?w.waitedOn.map(String):[]}),C?.sessionId){const e=C.sessionId,r=await(u.turnState??l)(t,e,s.requestId)
1998
- if(r.liveElsewhere)d.info("session routing: the chosen worker is mid-turn on another live request; spawning instead",{requestId:s.requestId,sessionId:e,on:r.liveElsewhere})
1999
- else{if(r.busy)return T.set(s.requestId,e),(u.whenIdle??((e,r)=>(0,y.whenSessionIdle)(e,async()=>(await l(t,e,s.requestId)).busy,r)))(e,e=>{(async function(e,t,r,s){const{apiClient:n,projectId:o,logger:i}=e,{p:d}=t
2000
- try{const u=await n.get(`/api/projects/${o}/requests/${d.requestId}`),h=u?.data??u
2001
- if(!h||"open"!==h.status)return void i.info("session routing: the request moved on while its worker finished a turn; nothing to hand over",{requestId:d.requestId,status:h?.status??"unknown"})
2002
- const p=await(t.deps.turnState??l)(e,r.sessionId,d.requestId)
2003
- if("idle"===s&&!p.busy&&!p.liveElsewhere&&await a(e,{...t,req:h},r))return
2004
- i.info("session routing: the worker did not come free for this start; spawning",{requestId:d.requestId,sessionId:r.sessionId,why:s,busy:p.busy,on:p.liveElsewhere}),await c(e,{...t,req:h})}catch(e){i.warn("session routing: the start after a turn failed",{requestId:d.requestId,sessionId:r.sessionId,error:(0,S.getErrorMessage)(e)})}})(t,A,C,e).finally(()=>T.delete(s.requestId))}),d.info("session routing: the thread's worker is finishing a turn; the start is handed over when it ends",{requestId:s.requestId,sessionId:e,previous:C.previousRequestId}),{success:!0,message:`${s.requestId} starts on warm ${j} ${e} when its current turn ends (the thread shares the session)`}
2005
- if(await a(t,A,C))return{success:!0,message:`${s.requestId} handed to warm ${j} ${e} (${C.area?"same area, separate work":"the thread shares the session"})`}}}else C?.why&&d.info(`session routing: spawning, because ${(0,$.whyNotReused)(C.why)}`,{requestId:s.requestId,why:C.why})
2006
- return c(t,A)}
2007
- const p=t(87780),f=t(5641),m=t(45687),g=t(28148),w=t(17039),y=t(67078),v=t(60833),b=t(2424),k=t(23687),S=t(58824),I=t(65454),$=t(64469),j=t(81570),E=t(76379),A=t(83807),C=t(47912),_=t(71879),T=new Map,x=new Set(["completed","canceled","reverted"])},82928(e,exports,t){"use strict"
1995
+ t.projectPath&&await(0,x.defaultBranchHasCode)(t.projectPath,e)&&(await(0,x.reportProjectHasCode)(a,!0,(e,t)=>i.patch(e,t)),await i.post(`/api/projects/${a}/requests/${s.requestId}/test-setup-offer`,{...s.creatorSessionId?{creatorSessionId:s.creatorSessionId}:{}}))}catch(e){h.warn("test-check card could not be asked for (the start goes on)",{requestId:s.requestId,error:(0,$.getErrorMessage)(e)})}const S=await(w.openDuplicates??r)(t,v,s.requestId),I=await(w.requestRecord??n)(t,s.requestId,v),j=o(s,v,S,I),A="string"==typeof s.worker&&s.worker.trim()||(0,f.kindProfile)(v?.type).worker||"worker",C=(0,g.spawnToolMask)({kind:v?.type,hasTestProcess:"boolean"==typeof s.hasTestProcess?s.hasTestProcess:void 0});(0,g.hasMask)(C)&&h.info("session routing: narrowing the worker tool surface for this request",{requestId:s.requestId,worker:A,off:Object.keys(C)})
1996
+ const _={p:s,req:v,worker:A,startPrompt:j,toolMask:C,deps:w}
1997
+ let T=null
1998
+ const R=v?.filedBySession??v?.filed_by_session,P=v?.filedByAgent??v?.filed_by_agent
1999
+ if("string"==typeof R&&R&&(0,E.sameWorkerRole)(P,A))try{if((await(0,m.readAllPages)(i,e=>`/api/projects/${a}/cli-sessions?status=active&limit=100&offset=${e}`)).some(e=>e?.externalSessionId===R)){const e=await(w.turnState??d)(t,R,s.requestId)
2000
+ e.liveElsewhere?h.info("session routing: the filer is mid-turn on another live request; not handing it this one",{requestId:s.requestId,sessionId:R,on:e.liveElsewhere}):(T={sessionId:R},h.info("session routing: the session that filed this request keeps it",{requestId:s.requestId,sessionId:R}))}}catch{}if(T=T??await(w.pickWarm??p)(t,{worker:A,requestId:s.requestId,kind:v?.type,kindModels:s.kindModels,planDocumentId:v?.planDocumentId??v?.plan_document_id??null,areas:Array.isArray(v?.areas)?v.areas.map(String):[],waitedOn:Array.isArray(v?.waitedOn)?v.waitedOn.map(String):[]}),T?.sessionId){const e=T.sessionId,r=await(w.turnState??d)(t,e,s.requestId)
2001
+ if(!r.busy&&!r.liveElsewhere&&v.roundOf){const n=await(w.liveElsewhere??u)(t,e,s.requestId)
2002
+ n&&n!==v.roundOf&&(r.liveElsewhere=n)}if(r.liveElsewhere)h.info("session routing: the chosen worker is mid-turn on another live request; spawning instead",{requestId:s.requestId,sessionId:e,on:r.liveElsewhere})
2003
+ else{if(r.busy)return M.set(s.requestId,e),(w.whenIdle??((e,r)=>(0,b.whenSessionIdle)(e,async()=>(await d(t,e,s.requestId)).busy,r)))(e,e=>{(async function(e,t,r,s){const{apiClient:n,projectId:o,logger:i}=e,{p:a}=t
2004
+ try{const u=await n.get(`/api/projects/${o}/requests/${a.requestId}`),h=u?.data??u
2005
+ if(!h||"open"!==h.status)return void i.info("session routing: the request moved on while its worker finished a turn; nothing to hand over",{requestId:a.requestId,status:h?.status??"unknown"})
2006
+ const p=await(t.deps.turnState??d)(e,r.sessionId,a.requestId)
2007
+ if("idle"===s&&!p.busy&&!p.liveElsewhere&&await c(e,{...t,req:h},r))return
2008
+ i.info("session routing: the worker did not come free for this start; spawning",{requestId:a.requestId,sessionId:r.sessionId,why:s,busy:p.busy,on:p.liveElsewhere}),await l(e,{...t,req:h})}catch(e){i.warn("session routing: the start after a turn failed",{requestId:a.requestId,sessionId:r.sessionId,error:(0,$.getErrorMessage)(e)})}})(t,_,T,e).finally(()=>M.delete(s.requestId))}),h.info("session routing: the thread's worker is finishing a turn; the start is handed over when it ends",{requestId:s.requestId,sessionId:e,previous:T.previousRequestId}),{success:!0,message:`${s.requestId} starts on warm ${A} ${e} when its current turn ends (the thread shares the session)`}
2009
+ if(await c(t,_,T))return{success:!0,message:`${s.requestId} handed to warm ${A} ${e} (${T.area?"same area, separate work":"the thread shares the session"})`}}}else T?.why&&h.info(`session routing: spawning, because ${(0,E.whyNotReused)(T.why)}`,{requestId:s.requestId,why:T.why})
2010
+ return l(t,_)}
2011
+ const f=t(87780),m=t(5641),g=t(45687),w=t(28148),y=t(17039),v=t(86116),b=t(67078),k=t(60833),S=t(2424),I=t(23687),$=t(58824),j=t(65454),E=t(64469),A=t(81570),C=t(76379),_=t(83807),T=t(47912),x=t(71879),M=new Map,R=new Set(["completed","canceled","reverted"])},82928(e,exports,t){"use strict"
2008
2012
  function r(e){let t=0
2009
2013
  for(const r of[...e??[]].reverse())if("user"===r?.info?.role&&!(r.parts??[]).some(e=>!0===e?.metadata?.alchemy?.payload?.nudge&&!0===e?.metadata?.alchemy?.payload?.restart)){if(!(r.parts??[]).some(e=>!0===e?.metadata?.alchemy?.payload?.nudge))break
2010
2014
  t++}return t}function s(e,t){return(e??[]).some(e=>"assistant"===e?.info?.role&&(e.info.time?.created??0)>t&&(e.parts??[]).some(e=>"tool"===e?.type))}function n(e,t){const r=String(e??"").match(/^ProviderQuotaError(?:\(([\w-]+)\))?/)
@@ -2159,7 +2163,7 @@ const i=e.refusal
2159
2163
  this.logger.error("REFUSED TURN: session stopped on an error another try on the same model cannot get past",{sessionId:t.id,title:t.title,errorName:e.errorName,model:i?.model,message:i?.message})
2160
2164
  const a=i?.model??"its model",l=this.resumedOn.get(t.id)
2161
2165
  this.resumedOn.delete(t.id)
2162
- const d=!!s&&"hub"!==n&&!0===this.isAgentModelPinned?.(s),u=(w={scope:i?.scope,providerID:i?.providerID,pinned:d}).pinned?"Sending it again on the same model fails the same way. This agent is pinned to that model, so a pick at the top of Steer does not move it: open Workflow agents and clear its pinned model, or pin another one, and it resumes on the new model by itself.":"provider"===w.scope?`Reconnect ${w.providerID??"the provider"} in Settings > Providers, or pick a model from another provider by clicking the model name at the top of Steer: it resumes on it by itself.`:"Sending it again on the same model fails the same way. Pick another model by clicking the model name at the top of Steer: it resumes on it by itself.",h=`${a} refused its last turn: "${i?.message??e.errorName??"unknown error"}". ${u}`,p=c(n,s,r,t.title),f=l?`Resuming it on ${l} did not work. `:"",g=()=>this.reportFleetHealth?.(`${f}${p} stopped: ${h}`,{sessionId:t.id,attention:"act",dedupKey:o,...r?{requestId:r}:{}})
2166
+ const d=!!s&&"hub"!==n&&!0===this.isAgentModelPinned?.(s),u=(w={scope:i?.scope,providerID:i?.providerID,pinned:d}).pinned?"Sending it again on the same model fails the same way. This agent is pinned to that model, so a pick at the top of Steer does not move it: open Workflow agents and clear its pinned model, or pin another one, and it resumes on the new model by itself.":"provider"===w.scope?`Reconnect ${w.providerID??"the provider"} in Settings > Providers, or pick a model from another provider by clicking the model name at the top of Steer: it resumes on it by itself.`:"Sending it again on the same model fails the same way. Pick another model by clicking the model name at the top of Steer: it resumes on it by itself.",h=i?.looped?`${a} could not use AlchemyLab's tools. ${i.message} ${u}`:`${a} refused its last turn: "${i?.message??e.errorName??"unknown error"}". ${u}`,p=c(n,s,r,t.title),f=l?`Resuming it on ${l} did not work. `:"",g=()=>this.reportFleetHealth?.(`${f}${p} stopped: ${h}`,{sessionId:t.id,attention:"act",dedupKey:o,...r?{requestId:r}:{}})
2163
2167
  var w
2164
2168
  "hub"!==n&&r&&this.raiseStalledAsk?this.raiseStalledAsk({requestId:r,sessionId:t.id,detail:("owned"===n?`${f}It stopped because ${h}`:`${f}${p} stopped: ${h}`).replace(/\.$/,""),refusedModel:i?.model??"its model",...d?{refusedPinned:!0}:{}}).then(e=>{e||g()}).catch(()=>g()):g()}noticeSilence(e,t,r,s,o,a){const c=`silent|${e.id}|${r.messageId??""}`
2165
2169
  if(this.reportedKeys.has(c))return!0
@@ -2207,8 +2211,10 @@ if(s.ok){const e=await s.json(),r=e?.[e.length-1]?.info?.providerID
2207
2211
  r&&(t=r)}}catch{}this.reportQuotaExhaustion({providerID:t,planType:void 0,message:String(s.message??"the provider is refusing requests")},r,`retry attempt ${s.attempt}`)}}reportQuotaExhaustion(e,t,r){const s=`quota|${e.providerID}|${e.resetsAt??"unknown"}`
2208
2212
  if(this.reportedKeys.has(s))return
2209
2213
  this.reportedKeys.add(s)
2210
- const n=e.resetsAt?` The limit resets ${new Date(1e3*e.resetsAt).toLocaleString("sv-SE",{timeZoneName:"short"})}.`:"",o=e.planType?` (plan: ${e.planType})`:""
2211
- this.logger.error("PROVIDER QUOTA EXHAUSTED: turns on this provider fail until the limit resets",{providerID:e.providerID,resetsAt:e.resetsAt,planType:e.planType,sessionId:t,title:r}),this.recordProviderBlock?.(e.providerID,e.resetsAt,e.planType,e.message),this.reportFleetHealth?.(`${e.providerID} usage limit reached${o}: "${e.message}". Work on ${e.providerID} models cannot take a turn until the limit resets.${n} To go on now, pick a model from another provider by clicking the model name at the top of Steer: everything this stopped resumes on it by itself. Otherwise it resumes when the limit resets.`,{sessionId:t,attention:"act",dedupKey:s})}async checkModelAttestation(e){const t=this.getOpenCodeUrl()
2214
+ const n=e.planType?` (plan: ${e.planType})`:""
2215
+ this.logger.error("PROVIDER QUOTA EXHAUSTED: turns on this provider fail until the limit resets",{providerID:e.providerID,resetsAt:e.resetsAt,planType:e.planType,sessionId:t,title:r}),this.recordProviderBlock?.(e.providerID,e.resetsAt,e.planType,e.message),this.reportFleetHealth?.(function(e,t,r="",s=new Date){let n="until the limit lifts"
2216
+ if(t){const e=new Date(1e3*t),r=e.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"})
2217
+ n=e.toDateString()===s.toDateString()?`until ${r}`:`until ${e.toLocaleDateString("en-GB",{day:"numeric",month:"short"})} ${r}`}return`${e} usage limit reached${r}. Work on ${e} models waits ${n}, then resumes by itself. To go on now, pick a model from another provider by clicking the model name at the top of Steer: what stopped resumes on it by itself.`}(e.providerID,e.resetsAt,n),{sessionId:t,attention:"act",dedupKey:s,details:`What ${e.providerID} said: ${e.message}`})}async checkModelAttestation(e){const t=this.getOpenCodeUrl()
2212
2218
  if(!t)return
2213
2219
  const r=new Set(e.map(e=>e.id))
2214
2220
  for(const e of Array.from(this.attestedAt.keys()))r.has(e)||this.attestedAt.delete(e)
@@ -2677,7 +2683,9 @@ const s=t(e),n=t(r)
2677
2683
  if(!s||!n)return!1
2678
2684
  if(s===n)return!1
2679
2685
  const o=(e,t)=>t.startsWith(e)&&/^[-_.]\d[\d._-]{5,}$/.test(t.slice(e.length))
2680
- return!o(s,n)&&!o(n,s)},exports.substitutionMessage=function(e,t,r){return`Model substitution detected${r?` (session: ${r})`:""}: AlchemyLab requested \`${e}\` but the provider reported serving \`${t}\`. Work produced in this session did NOT run on the model you selected. Check the provider account's model access and any routing in front of it.`}},25146(e,exports,t){"use strict"
2686
+ if(o(s,n)||o(n,s))return!1
2687
+ const i=e=>e.replace(/:[a-z0-9._-]+$/,"")
2688
+ return s.includes(":")===n.includes(":")||i(s)!==i(n)},exports.substitutionMessage=function(e,t,r){return`Model substitution detected${r?` (session: ${r})`:""}: AlchemyLab requested \`${e}\` but the provider reported serving \`${t}\`. Work produced in this session did NOT run on the model you selected. Check the provider account's model access and any routing in front of it.`}},25146(e,exports,t){"use strict"
2681
2689
  async function r(e,t){const r=e.condition_params||{}
2682
2690
  switch(e.condition_type){case"deadline":{const s=Number(r.deadlineSeconds)||0,n=new Date(e.created_at).getTime()+1e3*s
2683
2691
  return t.now()>=n?{fire:!0,payload:{deadlineSeconds:s}}:{fire:!1}}case"command-check":{const s=String(r.fireOn||"fail"),n=await t.runCheck(String(r.commandName||""),{monitorId:e.id,minIntervalSeconds:Math.max(30,Number(r.minIntervalSeconds)||300),trackChange:"change"===s,inlineCommand:r.command?String(r.command):void 0,ownerSessionId:e.owner_external_session_id||void 0})
@@ -2972,35 +2980,37 @@ const d=r.command.replace(/\s+/g," ").trim()
2972
2980
  if((i[1].endsWith("\u2026")?d.startsWith(a):d===a)&&(r.command===s||t(r.command).includes(c)))return r}}
2973
2981
  const r=new Map},43218(e,exports,t){"use strict"
2974
2982
  function r(e){if(e&&e.includes("/"))return e.split("/")[0].trim().toLowerCase()||void 0}async function s(e,t=fetch){if(!e)return null
2975
- const r=a.get(e)
2983
+ const r=c.get(e)
2976
2984
  if(r&&Date.now()-r.at<3e4)return r.value
2977
2985
  try{const r=await t(`${e.replace(/\/$/,"")}/config/providers`,{signal:AbortSignal.timeout(5e3)})
2978
2986
  if(!r.ok)return null
2979
2987
  const s=await r.json(),n={connected:(s.providers||[]).map(e=>String(e.id||"")).filter(Boolean),defaults:s.default||{}}
2980
- return a.set(e,{at:Date.now(),value:n}),n}catch{return null}}exports.b=void 0,exports.fetchProviderUsage=async function(e,t=fetch){const r=(0,n.getOpenCodeUrlFromConfig)(e)
2988
+ return c.set(e,{at:Date.now(),value:n}),n}catch{return null}}function n(e,t){if(!e||!t||0===t.connected.length)return e
2989
+ const s=r(e)
2990
+ if(!s||t.connected.includes(s))return e
2991
+ const n=(0,i.primaryProviderOf)(t.connected,e)
2992
+ if(n)return(0,i.carryModelToProvider)(e,n)??(0,i.defaultModelFor)(n)
2993
+ for(const e of t.connected)if(t.defaults[e])return`${e}/${t.defaults[e]}`
2994
+ return e}exports.b=void 0,exports.fetchProviderUsage=async function(e,t=fetch){const r=(0,o.getOpenCodeUrlFromConfig)(e)
2981
2995
  if(!r)return[]
2982
- const s=r.replace(/\/$/,""),o=i.get(s)
2983
- if(o&&Date.now()-o.at<6e4)return o.data
2996
+ const s=r.replace(/\/$/,""),n=a.get(s)
2997
+ if(n&&Date.now()-n.at<6e4)return n.data
2984
2998
  try{const e=await t(`${s}/config/usage`,{signal:AbortSignal.timeout(15e3)})
2985
- if(!e.ok)return o?.data??[]
2986
- const r=await e.json(),n=Array.isArray(r?.providers)?r.providers:[]
2987
- return i.set(s,{at:Date.now(),data:n}),n}catch{return o?.data??[]}},exports.providerOf=r,exports.spentWindow=function(e,t,r=exports.b){if(!t)return null
2999
+ if(!e.ok)return n?.data??[]
3000
+ const r=await e.json(),o=Array.isArray(r?.providers)?r.providers:[]
3001
+ return a.set(s,{at:Date.now(),data:o}),o}catch{return n?.data??[]}},exports.providerOf=r,exports.spentWindow=function(e,t,r=exports.b){if(!t)return null
2988
3002
  const s=e.find(e=>e.providerID===t)
2989
3003
  if(!s)return null
2990
3004
  let n=null
2991
3005
  for(const e of s.windows)e.usedPercent>=r&&(!n||e.usedPercent>n.usedPercent)&&(n=e)
2992
3006
  return n},exports.describeWindow=function(e,t){const r=t.resetsAt?new Date(1e3*t.resetsAt).toLocaleString([],{weekday:"short",hour:"2-digit",minute:"2-digit"}):"an unknown time"
2993
- return`${e} is ${t.usedPercent}% into its ${t.label} (resets ${r})`},exports.fetchConnectedProviders=async function(e,t=fetch){return s((0,n.getOpenCodeUrlFromConfig)(e),t)},exports.fetchConnectedProvidersAt=s,exports.runnableModel=function(e,t){if(!e||!t||0===t.connected.length)return e
2994
- const s=r(e)
2995
- if(!s||t.connected.includes(s))return e
2996
- const n=(0,o.primaryProviderOf)(t.connected,e)
2997
- if(n)return(0,o.carryModelToProvider)(e,n)??(0,o.defaultModelFor)(n)
2998
- for(const e of t.connected)if(t.defaults[e])return`${e}/${t.defaults[e]}`
2999
- return e}
3000
- const n=t(60833),o=t(95671)
3007
+ return`${e} is ${t.usedPercent}% into its ${t.label} (resets ${r})`},exports.fetchConnectedProviders=async function(e,t=fetch){return s((0,o.getOpenCodeUrlFromConfig)(e),t)},exports.fetchConnectedProvidersAt=s,exports.unchosenDefaultToFollow=function(e,t,r){if(!t||!e||!r||0===r.connected.length)return null
3008
+ const s=n(e,r)
3009
+ return s&&s!==e?s:null},exports.runnableModel=n
3010
+ const o=t(60833),i=t(95671)
3001
3011
  exports.b=(()=>{const e=Number(process.env.ALCHEMY_PROVIDER_SPENT_PERCENT)
3002
3012
  return Number.isFinite(e)&&e>0&&e<=100?e:90})()
3003
- const i=new Map,a=new Map},1285(e,exports,t){"use strict"
3013
+ const a=new Map,c=new Map},1285(e,exports,t){"use strict"
3004
3014
  function r(e,t){return(0,s.createHmac)("sha256",e).update(`read|${t}`).digest("base64url")}exports.J=void 0,exports.mintReadToken=function(e,t=Date.now()){const s=t+exports.J
3005
3015
  return{token:`r1.${s}.${r(e,s)}`,expiresAt:s}},exports.readTokenValid=function(e,t,s=Date.now()){if("string"!=typeof e||!e.startsWith("r1."))return!1
3006
3016
  const[,o,i]=e.split("."),a=Number(o)
@@ -4769,8 +4779,8 @@ class ht{rememberApprovedCommand(e,t,r){const s=t.rule,o=r?"in this environment"
4769
4779
  this.apiClient.post(`/api/projects/${e}/check-commands`,{name:"agent:judge",command:(0,n.commandAsStored)(t),description:"Approved by the command judge. Expires on its own; delete it to withdraw the approval sooner.",timeout:120,isActive:!0,expiresAt:s,exactMatch:!0,...r?{environmentId:r}:{}}).then(()=>(0,$e.invalidateRegisteredAllows)(e)).catch(e=>this.logger.warn("Failed to remember judge-approved command",{error:String(e)}))}get workCopiesReady(){return this.workCopiesGate.promise}updateAuthToken(e){this.options.authToken=e,this.apiClient.setAuthToken(e),this.configurer&&(this.configurer.authToken=e)}setReconfigureOpenCode(e){this.reconfigureOpenCode=e}setRelaunchOpenCode(e){this.relaunchOpenCode=e}noteServeExit(e){if(e.pid&&this.trackedChildPids.delete(e.pid),e.pid&&this.managedExitPids.delete(e.pid))return void this.logger.info("OpenCode serve exit from a managed restart -- no action",{code:e.code,signal:e.signal,pid:e.pid})
4770
4780
  if(!this.parkController?.noteServeDeath(`exit code=${e.code} signal=${e.signal}`))return void this.logger.info("OpenCode serve exit observed during managed lifecycle -- no action",{code:e.code,signal:e.signal,pid:e.pid})
4771
4781
  const t=Date.now()
4772
- this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
4773
- return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,te.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,rt.installServeAuthFetch)(),(0,te.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
4782
+ this.serveDeathTimestamps=this.serveDeathTimestamps.filter(e=>t-e<6e5),this.serveDeathTimestamps.push(t),this.logger.warn("OpenCode serve died unexpectedly -- parked for self-heal; next needing signal revives it",{code:e.code,signal:e.signal,pid:e.pid,unexpectedDeathsLast10m:this.serveDeathTimestamps.length}),this.serveDeathTimestamps.length>=3&&this.logger.error("OpenCode serve is FLAPPING (3+ unexpected deaths in 10 minutes). Revival keeps retrying, but check the opencode-serve log for the underlying crash.",{})}constructor(e,t){this.envSessionStarts=new Map,this.leakedCallNudges=new Map,this.paintCleaned=new Set,this.runningSessions=new Set,this.lastRunningReport="",this.directReportDedup=new Map,this.cachedReceipts=null,this.cachedIntegrations=null,this.defaultModelUnchosen=!1,this.lastSettingsSyncAt=0,this.activeMcpCalls=new Map,this.ownerGuidanceStale=!1,this.workCopiesGate=(()=>{let e
4783
+ return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.pendingConfigReload=!1,this.pendingConfigReloadSince=0,this.lastConfigReloadAt=0,this.deferredReloadCheckInFlight=!1,this.settingsSyncCounter=0,this.versionWarnings=[],this.updateBlocked=!1,this._pendingUpdateBlock=!1,this.clientVersion=(0,i.ownVersion)(),this.remoteLastOkAt=null,this.remoteFailures=0,this.remoteLastError=null,this.isPolling=!1,this.isProcessingAction=!1,this.localSecret=(0,te.newLocalSecret)(),this.openCodeUrlCache=null,this.openCodePort=null,this.proxyAbortControllers=new Map,this.previewTunnel=null,this.trackedChildPids=new Set,this.sseEventBuffer=[],this.droppedEventCount=0,this.firstDropAt=0,this.sseReconnectTimers=new Set,this.sseConnectGeneration=0,this.hasRemoteEventConsumers=!1,this.cachedProjectConfig=null,this.serveStartedAt=0,this.registeredOnce=!1,this.recordedUsageMsgs=new Map,this.usageStepTimers=new Map,this.inFlightProxyActions=0,this.pendingRegularActions=[],this.queuedRegularActionIds=new Set,this.inFlightServeProxyRequests=0,this.serveDeathTimestamps=[],this.managedExitPids=new Set,this.followingDefaultTo=null,this.pauseApplied=null,this.childDeathTold=new Set,this.mainBranchName=null,this.projectSubsystemsStarted=!1,this.lastParkVetoes=[],this.logger=e,this.options=t,(0,rt.installServeAuthFetch)(),(0,te.installLocalSecretFetch)(()=>this.options.projectPath),t.projectPath&&(process.env[n.PROJECT_ROOT_VAR]=t.projectPath),this.port=t.port||3456,this.startTime=new Date
4774
4784
  const r=(0,P.createMetricsCollector)(!1)
4775
4785
  if((0,st.setFleetServerKey)(t.projectPath?t.projectPath.split("/").pop():void 0),this.apiClient=new M.BaseApiClient(t.remoteServerUrl,e,r,t.authToken,{timeout:35e3,serverKey:t.projectPath?t.projectPath.split("/").pop():void 0}),t.tokenManager){const e=t.tokenManager
4776
4786
  this.apiClient.setTokenRefresher(async()=>{const t=(await e.getToken())?.token
@@ -4935,7 +4945,11 @@ if(r)try{const e=await r.hasValidToken(),s=r.getAuthStatus(),n=e?await r.getToke
4935
4945
  t.json({authenticated:e,isPendingAuth:s.isPendingAuth,expiresAt:n?.expiresAt??null})}catch(e){this.logger.warn("auth-status check failed",{error:(0,H.getErrorMessage)(e)}),t.json({authenticated:!1,isPendingAuth:!1})}else t.json({authenticated:!0,isPendingAuth:!1})}handleStatus(e,t){t.json({status:"running",uptime:Math.floor((Date.now()-this.startTime.getTime())/1e3),startTime:this.startTime.toISOString(),projectId:this.projectId,serverId:this.serverId,isPolling:this.isPolling,parkState:this.parkController?.getState(),parkVetoes:this.parkController?this.lastParkVetoes:void 0,parkIdleForMs:this.parkController?.idleForMs(),port:this.port,timestamp:(new Date).toISOString()})}handleOpenCodeConfig(e,t){const r=O.default.join(this.options.projectPath,".alchemy_lab","opencode.json")
4936
4946
  try{if(!D.default.existsSync(r))return void t.status(404).json({error:"OpenCode config not found",message:"OpenCode serve may not be running"})
4937
4947
  const e=D.default.readFileSync(r,"utf-8"),s=JSON.parse(e)
4938
- t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,H.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,H.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
4948
+ t.json({port:s.port,pid:s.pid,startedAt:s.startedAt,baseUrl:`http://localhost:${s.port}`,directory:this.options.projectPath,proxyEnabled:!0,parkState:this.parkController?.getState()??"active"})}catch(e){this.logger.error("Failed to read OpenCode config",{error:(0,H.getErrorMessage)(e)}),t.status(500).json({error:"Failed to read OpenCode config",message:(0,H.getErrorMessage)(e)})}}async followUnchosenDefault(){const e=(0,de.unchosenDefaultToFollow)(this.cachedDefaultModel,this.defaultModelUnchosen,this.configurer.getConnectedProviders())
4949
+ if(!e||!this.projectId||this.followingDefaultTo===e)return
4950
+ this.followingDefaultTo=e
4951
+ const t=this.cachedDefaultModel
4952
+ try{await this.apiClient.patch(`/api/projects/${this.projectId}`,{metadata:{defaultModel:e,defaultModelUnchosen:!0}}),this.logger.info("unchosen default model moved to a connected provider",{from:t,to:e}),await this.updateDefaultModel(e)}catch(e){this.followingDefaultTo=null,this.logger.debug("could not move the unchosen default model",{error:(0,H.getErrorMessage)(e)})}}updateDefaultModel(e){return void 0===e?Promise.resolve():(e!==this.cachedDefaultModel&&(this.cachedDefaultModel=e,this.hookExecutor.setDefaultModel(e||""),this.stuckSessionWatchdog?.capacityChanged("model-changed")),e&&this.projectId&&this.lastAgentResyncModel!==e?this.resyncAgentMarkdowns().catch(e=>this.logger.debug("Background agent markdown re-sync failed",{error:(0,H.getErrorMessage)(e)})):Promise.resolve())}handleMcpToolCallEvent(e,t){this.parkController?.noteActivity(),"start"!==e?(this.activeMcpCalls.delete(t),0===this.activeMcpCalls.size&&this.pendingConfigReload&&this.fireDeferredReloadIfIdle("mcp-idle")):this.activeMcpCalls.set(t,Date.now())}startMcpStaleCleanup(){this.mcpStaleCleanupInterval||(this.mcpStaleCleanupInterval=setInterval(()=>{const e=Date.now()-12e4
4939
4953
  let t=0
4940
4954
  for(const[r,s]of this.activeMcpCalls)s<e&&(this.activeMcpCalls.delete(r),t++)
4941
4955
  t>0&&this.logger.warn("Cleaned up stale MCP tool-call tracking entries",{removed:t}),this.pendingConfigReload&&this.fireDeferredReloadIfIdle("stale-cleanup")},6e4))}async anySessionBusy(){if(Date.now()-(0,d.lastPromptDeliveredAt)()<ht.PROMPT_DELIVERY_BUSY_MS)return!0
@@ -5070,12 +5084,12 @@ const{agent:r,planId:s,prompt:n,title:o,parentSessionId:i,flavor:a}=e.body||{},c
5070
5084
  if(c&&!d.has(c))return void t.status(400).json({success:!1,error:`agent must be one of: ${[...d].join(", ")}`})
5071
5085
  const u=await(0,ke.spawnPlannerWithDoc)({agent:c||"planner",flavor:l,planId:s,prompt:n,title:o||"Planner",parentSessionId:i,apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,projectPath:this.options.projectPath,trackChildPid:e=>this.trackedChildPids.add(e)})
5072
5086
  if(!u.success||!u.sessionId)return void t.status(500).json({success:!1,error:u.error||"spawn failed"})
5073
- t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Spawn planner",e)}}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
5087
+ t.json({success:!0,data:{sessionId:u.sessionId,planId:u.planId,pid:u.pid}})}catch(e){(0,Oe.respondWithError)(t,this.logger,"Spawn planner",e)}}scheduleHubAlchemist(e){this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.hubRetryTimer=setTimeout(async()=>{this.hubRetryTimer=void 0,await this.ensureHubAlchemist()||this.scheduleHubAlchemist(Math.min(2*e,6e4))},e),this.hubRetryTimer.unref?.()}async ensureHubAlchemist(){try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/cli-sessions/persistent/alchemist`),t=e?.data??e
5074
5088
  if(t&&t.externalSessionId){const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath),r=e?await this.serveHasSession(e,String(t.externalSessionId)):null
5075
- if(!1!==r)return void this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r})
5076
- this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,H.getErrorMessage)(e)}))}if(!this.projectId)return
5089
+ if(!1!==r)return this.logger.debug("Hub alchemist already present",{sessionId:t.id,verified:!0===r}),!0
5090
+ this.logger.warn("Hub alchemist row points at a session this serve does not have; ending it and creating a new hub",{sessionId:t.id,externalSessionId:t.externalSessionId}),await this.apiClient.patch(`/api/projects/${this.projectId}/cli-sessions/${t.id}`,{status:"completed"}).catch(e=>this.logger.warn("could not end the orphan hub row",{sessionId:t.id,error:(0,H.getErrorMessage)(e)}))}if(!this.projectId)return!0
5077
5091
  const r=new ye.SessionSpawnService({apiClient:this.apiClient,logger:this.logger,projectId:this.projectId,getDefaultModel:()=>this.cachedDefaultModel||void 0}),s="Hub",n=await r.prepareSession({agent:"alchemist",prompt:s,title:s,projectPath:this.options.projectPath})
5078
- this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null})}catch(e){this.logger.warn("Hub alchemist bootstrap failed; project cannot be steered until one exists",{error:(0,H.getErrorMessage)(e)})}}async reapVanishedSessions(){if(!this.projectId)return
5092
+ return this.logger.info("Hub alchemist created at boot (no browser required)",{sessionId:n?.sessionId??null}),!0}catch(e){return this.logger.warn("Hub alchemist bootstrap failed; trying again shortly",{error:(0,H.getErrorMessage)(e)}),!1}}async reapVanishedSessions(){if(!this.projectId)return
5079
5093
  const e=(0,et.getOpenCodeUrlFromConfig)(this.options.projectPath)
5080
5094
  if(!e)return
5081
5095
  let t
@@ -5163,7 +5177,7 @@ const t=D.default.readFileSync(e,"utf-8"),r=JSON.parse(t)
5163
5177
  return this.cachedProjectConfig={config:r,timestamp:Date.now()},r}catch(e){return this.logger.error("Failed to read project config",{error:(0,H.getErrorMessage)(e)}),this.cachedProjectConfig={config:null,timestamp:Date.now()},null}}setOpenCodePort(e){this.openCodePort=e,(0,rt.noteServe)(e)
5164
5178
  const t=`http://127.0.0.1:${e}`
5165
5179
  this.openCodeUrlCache={url:t,timestamp:Date.now()},this.logger.info("OpenCode port set directly",{port:e}),this.serveStartedAt=Date.now(),this.stuckSessionWatchdog?.serveStarted(this.serveStartedAt),(async()=>{for(let e=0;e<10;e++){try{const t=await this.configurer.refreshConnectedProviders(this.options.projectPath)
5166
- if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),void(t&&await this.resyncAgentMarkdowns())}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,H.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,H.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
5180
+ if(this.configurer.hasConnectedProviders())return this.logger.info("connected providers known",{changed:t,attempt:e}),t&&await this.resyncAgentMarkdowns(),void await this.followUnchosenDefault()}catch(t){this.logger.debug("connected-provider refresh failed",{attempt:e,error:(0,H.getErrorMessage)(t)})}await new Promise(e=>setTimeout(e,3e3))}this.logger.warn("the serve never reported its connected providers; agent models were not checked against them")})(),this.cachedDefaultModel||this.seedDefaultModel(e).catch(e=>this.logger.debug("Failed to seed default model",{error:(0,H.getErrorMessage)(e)}))}async seedDefaultModel(e){const t=await fetch(`http://localhost:${e}/config/providers`,{signal:AbortSignal.timeout(5e3)})
5167
5181
  if(!t.ok)return
5168
5182
  const r=await t.json(),s=(r?.providers||[]).map(e=>String(e.id||"")).filter(Boolean),n=(0,x.primaryProviderOf)(s),o=r?.default||{},[i,a]=Object.entries(o)[0]||[],c=n?(0,x.defaultModelFor)(n):i&&a?`${i}/${a}`:null
5169
5183
  c&&(this.updateDefaultModel(c),this.logger.info("Seeded defaultModel",{defaultModel:c,primaryProvider:n}),this.apiClient.post("/api/user/preferences",{defaultModel:c}).catch(e=>this.logger.debug("Failed to seed user preference",{error:(0,H.getErrorMessage)(e)})))}getOpenCodeUrl(){if(this.parkController?.isServeDown())return null
@@ -5289,8 +5303,8 @@ r&&(t["X-Main-Sha"]=r)
5289
5303
  const s=await fetch(e,{headers:t,signal:AbortSignal.timeout(35e3)})
5290
5304
  if(!s.ok)throw new Error(`Poll failed with status ${s.status}`)
5291
5305
  const n=await s.json(),o={success:!0,data:n.data||n}
5292
- if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r}=o.data.settings
5293
- void 0!==e&&await this.updateDefaultModel(e),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
5306
+ if(o.success&&o.data&&void 0!==o.data.hasRemoteEventConsumers&&(this.hasRemoteEventConsumers=o.data.hasRemoteEventConsumers,this.hasRemoteEventConsumers&&this.parkController?.noteActivity()),o.success&&o.data?.settings){const{defaultModel:e,paused:t,claudeThinking:r,defaultModelUnchosen:s}=o.data.settings
5307
+ "boolean"==typeof s&&(this.defaultModelUnchosen=s),void 0!==e&&await this.updateDefaultModel(e),await this.followUnchosenDefault(),"boolean"==typeof t&&await this.applyPause(t),"string"==typeof r&&await this.applyClaudeThinking(r)}if(o.success&&!0===o.data?.previewTunnel&&this.projectId&&(this.previewTunnel||(this.previewTunnel=new T.PreviewTunnelClient({remoteServerUrl:this.options.remoteServerUrl,projectId:this.projectId,projectPath:this.options.projectPath,headers:()=>({...this.apiClient.authToken?{Authorization:`Bearer ${this.apiClient.authToken}`}:{},...(0,st.machineCallerHeaders)(this.options.remoteServerUrl)}),ports:()=>({ownPort:this.port,blocked:[...this.openCodePort?[this.openCodePort]:[],...(0,_.listLive)().map(e=>e.port).filter(e=>e!==this.port)]}),logger:this.logger})),this.previewTunnel.want()),o.success&&Array.isArray(o.data?.invalidations)&&o.data.invalidations.length>0){const e=o.data.invalidations
5294
5308
  this.logger.info("Received cache invalidations from remote",{invalidations:e}),(e.includes("quests")||e.includes("documents")||e.includes("plans"))&&this.scheduleRunner?.invalidateCache(),e.includes("alchemist")&&this.scheduleRunner?.invalidateAlchemistCache(),e.includes("owner-guidance")&&(this.ownerGuidanceStale||this.logger.info("Your guidance changed; it reaches this project's agents at the next moment that costs nothing"),this.ownerGuidanceStale=!0),e.includes("agents")&&(this.logger.info("Agent definitions changed on the remote; re-syncing markdowns"),this.resyncAgentMarkdowns().catch(e=>this.logger.warn("Agent markdown re-sync after invalidation failed",{error:(0,H.getErrorMessage)(e)})))}const i=o.success?o.data?.scheduleNudges??o.data?.questNudges:void 0,a=Array.isArray(i)?i:[],c=o.success&&Array.isArray(o.data?.actions)?o.data.actions.length:0,l=a.length
5295
5309
  if((c>0||l>0)&&await this.ensureServeAlive("poll-work"),a.length>0){const e=a
5296
5310
  this.logger.info("Received goal nudges from remote",{scheduleNudges:e})
@@ -5355,13 +5369,13 @@ continue}throw new Error(`Port ${this.port} is already in use (tried 50 ports)`)
5355
5369
  try{const e=this.readProjectConfig()
5356
5370
  if(e?.id){const{upsertEntry:r}=await Promise.resolve().then(()=>t(52640))
5357
5371
  r({projectId:e.id,projectPath:this.options.projectPath,port:this.port,pid:process.pid,startedAt:(new Date).toISOString(),lastHeartbeat:(new Date).toISOString()}),this.logger.info("Wrote running-projects registry entry",{projectId:e.id,port:this.port})}}catch(e){this.logger.warn("Failed to write project registry entry",{error:(0,H.getErrorMessage)(e)})}try{await this.register(),this.startHeartbeat()}catch(e){const t=(0,H.getErrorMessage)(e)
5358
- t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(setTimeout(()=>{this.ensureHubAlchemist()},1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,Ie.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
5372
+ t.includes("Required update"),this.logger.warn("Remote registration failed, continuing in local-only mode",{error:t})}if(this.projectId&&(this.scheduleHubAlchemist(1e4),setTimeout(()=>{this.reconcileSetupRunPass()},12e3),this.setupRunReconcileTimer=setInterval(()=>{this.reconcileSetupRunPass()},3e5),this.setupRunReconcileTimer.unref?.()),this.projectId&&(0,Ie.sweepOrphanExperiments)(this.projectId,this.logger,this.options.projectPath),this.projectId){const e=Date.now()
5359
5373
  setTimeout(()=>{this.sweepOrphanTestRuns(e)},15e3)}this.startExperimentReaper(),this.startLinearAutoImportPoller()
5360
5374
  try{const e=`${this.options.remoteServerUrl}/api/projects/${this.projectId}/actions/pending?timeout=0&settingsOnly=true`,t={"Content-Type":"application/json",Accept:"application/json"}
5361
5375
  this.apiClient.authToken&&(t.Authorization=`Bearer ${this.apiClient.authToken}`),Object.assign(t,(0,st.machineCallerHeaders)(this.options.remoteServerUrl))
5362
5376
  const r=await fetch(e,{headers:t,signal:AbortSignal.timeout(8e3)})
5363
5377
  if(r.ok){const e=await r.json(),t=e?.data?.settings||e?.settings
5364
- void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,H.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Fe.setContainerProject)(this.projectId),this.startSSEEventForwarder()
5378
+ "boolean"==typeof t?.defaultModelUnchosen&&(this.defaultModelUnchosen=t.defaultModelUnchosen),void 0!==t?.defaultModel&&(this.updateDefaultModel(t.defaultModel),this.logger.info("Fetched defaultModel on startup via quick poll",{defaultModel:this.cachedDefaultModel})),"boolean"==typeof t?.paused&&await this.applyPause(t.paused),"string"==typeof t?.claudeThinking&&(this.claudeThinkingSeen=t.claudeThinking)}}catch(e){this.logger.debug("Quick poll for startup settings failed",{error:(0,H.getErrorMessage)(e)})}this.startPolling(),await this.startProjectSubsystems("start"),this.startMcpStaleCleanup(),this.projectId&&this.startParkController()}async startProjectSubsystems(e){if(this.projectId&&!this.projectSubsystemsStarted){this.projectSubsystemsStarted=!0,this.logger.info("Starting project subsystems",{source:e,projectId:this.projectId}),(0,Fe.setContainerProject)(this.projectId),this.startSSEEventForwarder()
5365
5379
  try{const{resumeInboxChatWatches:e}=await Promise.resolve().then(()=>t.t(86116,23))
5366
5380
  e({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})}catch(e){this.logger.warn("inbox watch resume failed at start",{error:(0,H.getErrorMessage)(e)})}this.apiClient.post(`/api/projects/${this.projectId}/session-messages/expire-orphaned-approvals`,{}).then(e=>{const t=e?.data?.expired??0
5367
5381
  t>0&&this.logger.info("Ended approval cards left behind by the previous engine",{expired:t})}).catch(e=>{this.logger.warn("Could not end orphaned approval cards",{error:(0,H.getErrorMessage)(e)})}),this.startStuckSessionWatchdog(),this.startScheduleRunner(),(0,ge.ensureOpsDiagnostics)(this.apiClient,this.projectId,this.options.projectPath,this.logger),(0,ae.startSessionCompactor)({getOpenCodeUrl:()=>this.getOpenCodeUrl()??void 0,apiClient:this.apiClient,projectId:this.projectId||void 0,projectName:this.options.projectPath?O.default.basename(this.options.projectPath):void 0,logger:this.logger}),this.keepWarmRunner=(0,le.startKeepWarmRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,getDefaultModel:()=>this.cachedDefaultModel}),this.ownerGuidanceTimer=setInterval(()=>{this.checkOwnerGuidanceApply()},6e4),this.ownerGuidanceTimer.unref?.(),this.workMetricsRunner||(this.workMetricsRunner=(0,he.startWorkMetricsRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient})),this.mcpConnectionRunner||(this.mcpConnectionRunner=(0,pe.startMcpConnectionRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,onConfigChanged:()=>{this.requestConfigReload("mcp-connections")},isBusy:async()=>this.activeMcpCalls.size>0||await this.anySessionBusy()})),(0,ce.startMonitorRunner)({projectPath:this.options.projectPath,projectId:this.projectId,logger:this.logger,apiClient:this.apiClient,cachedDefaultModel:this.cachedDefaultModel,serveReady:async()=>(await this.ensureServeAlive("monitor-wake"),null!=this.openCodePort&&!this.parkController?.isServeDown()),reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},runOwn:e=>this.runOwn(e),verifyDeliveryEnv:async e=>{let r
@@ -5425,7 +5439,7 @@ for(const r of t?.data||t||[]){if(!(0,Y.isWaitDeclaringQuestion)(r))continue
5425
5439
  const t=r?.fromSessionId||r?.from_external_session_id
5426
5440
  "string"==typeof t&&t&&e.add(t)}const r=await this.apiClient.get(`/api/projects/${this.projectId}/monitors?status=armed`)
5427
5441
  for(const t of r?.data||[]){const r=t?.owner_external_session_id
5428
- "string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,H.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,H.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
5442
+ "string"==typeof r&&r&&e.add(r)}}catch{}return e},reportLiveness:e=>{this.apiClient.post("/api/liveness/report",{...e,project:this.options.projectPath?this.options.projectPath.split("/").pop():void 0}).catch(()=>{})},reportFleetHealth:(e,t)=>{this.projectId&&this.apiClient.post(`/api/projects/${this.projectId}/session-messages/reports`,{fromSessionId:t.sessionId,status:"act"===t.attention?"blocked":"success",summary:e,...t.details?{details:t.details}:t.dedupKey?{details:`[dedup:${t.dedupKey}]`}:{},extraContent:{byMonitor:!0,...t.dedupKey?{dedupKey:t.dedupKey}:{}},...t.requestId?{requestId:t.requestId}:{},attention:t.attention}).catch(t=>{this.logger.warn("Fleet-health report did not land on the steer stream",{error:(0,H.getErrorMessage)(t),summary:e.slice(0,120)})})},closeStalledAsk:async({requestId:e,sessionId:t,why:r})=>{if(this.projectId)try{await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask/resumed`,{sessionId:t,why:r})}catch(r){this.logger.warn("stalled-ask close after resume failed",{requestId:e,sessionId:t,error:(0,H.getErrorMessage)(r)})}},raiseStalledAsk:async({requestId:e,sessionId:t,detail:r,refusedModel:s,refusedPinned:n})=>{if(!this.projectId)return!1
5429
5443
  try{const o=await this.apiClient.post(`/api/projects/${this.projectId}/requests/${e}/stalled-ask`,{sessionId:t,detail:r,...s?{refusedModel:s}:{},...n?{refusedPinned:!0}:{}}),i=o?.data??o
5430
5444
  return!0===i?.created||"string"==typeof i?.reason||null===i?.messageCode}catch(t){return this.logger.warn("stalled-ask mint failed; falling back to the plain report",{requestId:e,error:(0,H.getErrorMessage)(t)}),!1}},getStrandedCandidates:async()=>{if(!this.projectId)return[]
5431
5445
  try{const e=await this.apiClient.get(`/api/projects/${this.projectId}/requests/stranded-candidates`),t=e?.data||e
@@ -5445,9 +5459,9 @@ if(!this.stuckSessionWatchdog)return void t.status(503).json({success:!1,error:"
5445
5459
  const s=await this.stuckSessionWatchdog.retrySession(r)
5446
5460
  s.success?t.json({success:!0}):t.status(400).json({success:!1,error:s.error})}async enterUpdateMode(){if(!this.updateBlocked){this.updateBlocked=!0,this.logger.error("Entering update-required mode \u2014 shutting down all services"),console.error("\n\x1b[31m\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),console.error("\u2551 UPDATE REQUIRED \u2551"),console.error("\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\x1b[0m")
5447
5461
  for(const e of this.versionWarnings)console.error(`\x1b[31m ${e.message}\x1b[0m`)
5448
- console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
5462
+ console.error("\x1b[31m\n Functionality disabled. Update via UI or run:\n npm update -g alchemylab\x1b[0m\n"),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival(),this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.isPolling=!1,this.pollAbortController&&this.pollAbortController.abort(),this.pollingInterval&&clearInterval(this.pollingInterval)
5449
5463
  for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.clear(),this.logger.info("Update-required mode active. HTTP server still running on port "+this.port)}}async stop(){this.logger.info("Stopping AlchemyActionServer..."),(0,o.markServerStopping)(),(0,S.clearInstanceLock)(this.options.projectPath),(0,Ze.closeExhibitPreviewBrowser)()
5450
- for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
5464
+ for(const[e]of this.proxyAbortControllers)try{e.abort()}catch{}this.proxyAbortControllers.size>0&&await Promise.allSettled([...this.proxyAbortControllers.values()]),this.proxyAbortControllers.clear(),this.previewTunnel?.close("shutting down"),await this.sweepUsage(this.startTime.getTime(),8e3),this.parkController?.stop(),this.ownerGuidanceTimer&&clearInterval(this.ownerGuidanceTimer),this.setupRunReconcileTimer&&clearInterval(this.setupRunReconcileTimer),this.hubRetryTimer&&clearTimeout(this.hubRetryTimer),this.signInTimer&&clearInterval(this.signInTimer),await this.awaitInFlightRevival()
5451
5465
  try{await Promise.race([(0,j.experimentRegistry)().stopAll(),new Promise(e=>setTimeout(e,15e3))])}catch(e){this.logger.warn("shutdown: boxes not all stopped",{error:(0,H.getErrorMessage)(e)})}this.killTrackedProcesses(),this.stuckSessionWatchdog?.stop(),this.scheduleRunner?.stop(),this.stopSSEEventForwarder(),this.mcpStaleCleanupInterval&&(clearInterval(this.mcpStaleCleanupInterval),this.mcpStaleCleanupInterval=void 0),this.stopPendingReloadRecheck()
5452
5466
  try{const e=O.default.join(this.options.projectPath,".alchemy_lab","action-server.json")
5453
5467
  D.default.existsSync(e)&&D.default.unlinkSync(e)}catch{}if(this.projectId)try{const{removeEntry:e}=await Promise.resolve().then(()=>t(52640))
@@ -5533,18 +5547,18 @@ const i=(async()=>{try{const r=await e.get(`/api/projects/${s}/agents`),n=r?.dat
5533
5547
  return t.set(s,{agents:n,timestamp:Date.now()}),n}finally{r.delete(s)}})()
5534
5548
  return r.set(s,i),i}
5535
5549
  const t=new Map,r=new Map},86116(e,exports,t){"use strict"
5536
- function r(e){return!!e.systemNoteRequestId||!!e.systemWake}function s(e){return se.join(e,".alchemy_lab","inbox-watches.json")}function n(e){try{const t=[...ce.entries()].filter(([,t])=>t.projectPath===e).map(([e,t])=>({sessionId:e,exchanges:t.exchanges.map(e=>({markerMessageId:e.markerMessageId,injectedAtMs:e.injectedAtMs,...e.exchangeKey?{exchangeKey:e.exchangeKey}:{},...e.askAnnotationId?{askAnnotationId:e.askAnnotationId}:{},...e.systemNoteRequestId?{systemNoteRequestId:e.systemNoteRequestId}:{},...e.systemWake?{systemWake:e.systemWake}:{},...e.resumes?{resumes:e.resumes}:{}})),...t.resumed?{resumed:!0}:{},...t.asideAnswered?.length?{asideAnswered:t.asideAnswered}:{},...t.relayedIds?.length?{relayedIds:t.relayedIds}:{},...t.held?.length?{held:t.held}:{}})),r=s(e)
5537
- if(0===t.length)return void(re.existsSync(r)&&re.unlinkSync(r))
5550
+ function r(e){return!!e.systemNoteRequestId||!!e.systemWake}function s(e){return ne.join(e,".alchemy_lab","inbox-watches.json")}function n(e){try{const t=[...le.entries()].filter(([,t])=>t.projectPath===e).map(([e,t])=>({sessionId:e,exchanges:t.exchanges.map(e=>({markerMessageId:e.markerMessageId,injectedAtMs:e.injectedAtMs,...e.exchangeKey?{exchangeKey:e.exchangeKey}:{},...e.askAnnotationId?{askAnnotationId:e.askAnnotationId}:{},...e.systemNoteRequestId?{systemNoteRequestId:e.systemNoteRequestId}:{},...e.systemWake?{systemWake:e.systemWake}:{},...e.resumes?{resumes:e.resumes}:{}})),...t.resumed?{resumed:!0}:{},...t.asideAnswered?.length?{asideAnswered:t.asideAnswered}:{},...t.relayedIds?.length?{relayedIds:t.relayedIds}:{},...t.held?.length?{held:t.held}:{}})),r=s(e)
5551
+ if(0===t.length)return void(se.existsSync(r)&&se.unlinkSync(r))
5538
5552
  const n=`${r}.tmp-${process.pid}`
5539
- re.writeFileSync(n,JSON.stringify(t)),re.renameSync(n,r)}catch{}}function o(e){if(Array.isArray(e.exchanges))return e.exchanges.filter(e=>"string"==typeof e?.markerMessageId).map(e=>({markerMessageId:e.markerMessageId,injectedAtMs:e.injectedAtMs||Date.now(),...e.exchangeKey?{exchangeKey:e.exchangeKey}:{},...e.askAnnotationId?{askAnnotationId:e.askAnnotationId}:{},...e.systemNoteRequestId?{systemNoteRequestId:e.systemNoteRequestId}:{},..."string"==typeof e.systemWake?.kind&&e.systemWake.kind?{systemWake:{kind:e.systemWake.kind,..."string"==typeof e.systemWake.requestId?{requestId:e.systemWake.requestId}:{}}}:{},..."string"==typeof e.resumes&&e.resumes?{resumes:e.resumes}:{}}))
5553
+ se.writeFileSync(n,JSON.stringify(t)),se.renameSync(n,r)}catch{}}function o(e){if(Array.isArray(e.exchanges))return e.exchanges.filter(e=>"string"==typeof e?.markerMessageId).map(e=>({markerMessageId:e.markerMessageId,injectedAtMs:e.injectedAtMs||Date.now(),...e.exchangeKey?{exchangeKey:e.exchangeKey}:{},...e.askAnnotationId?{askAnnotationId:e.askAnnotationId}:{},...e.systemNoteRequestId?{systemNoteRequestId:e.systemNoteRequestId}:{},..."string"==typeof e.systemWake?.kind&&e.systemWake.kind?{systemWake:{kind:e.systemWake.kind,..."string"==typeof e.systemWake.requestId?{requestId:e.systemWake.requestId}:{}}}:{},..."string"==typeof e.resumes&&e.resumes?{resumes:e.resumes}:{}}))
5540
5554
  if("string"!=typeof e.markerMessageId)return[]
5541
5555
  const t={markerMessageId:e.markerMessageId,injectedAtMs:e.injectedAtMs||Date.now(),...e.exchangeKey?{exchangeKey:e.exchangeKey}:{}}
5542
- return e.originMarkerId&&e.originMarkerId!==e.markerMessageId?[{markerMessageId:e.originMarkerId,injectedAtMs:e.originInjectedAtMs||t.injectedAtMs,...e.originExchangeKey?{exchangeKey:e.originExchangeKey}:{}},t]:[t]}function i(e,t,r,s,o,i,a,c,l){const d={markerMessageId:s,injectedAtMs:r,...o?{exchangeKey:o}:{},...i?{askAnnotationId:i}:{},...a?{systemNoteRequestId:a}:{},...l?{systemWake:l}:{},...c?{resumes:c}:{}},u=ce.get(t)
5556
+ return e.originMarkerId&&e.originMarkerId!==e.markerMessageId?[{markerMessageId:e.originMarkerId,injectedAtMs:e.originInjectedAtMs||t.injectedAtMs,...e.originExchangeKey?{exchangeKey:e.originExchangeKey}:{}},t]:[t]}function i(e,t,r,s,o,i,a,c,l){const d={markerMessageId:s,injectedAtMs:r,...o?{exchangeKey:o}:{},...i?{askAnnotationId:i}:{},...a?{systemNoteRequestId:a}:{},...l?{systemWake:l}:{},...c?{resumes:c}:{}},u=le.get(t)
5543
5557
  if(u){if(u.exchanges.some(e=>e.markerMessageId===s))return
5544
- return clearTimeout(u.deadline),u.exchanges.push(d),u.hardCapAtMs=r+ie,u.deadline=setTimeout(()=>{R(t)},oe),n(u.projectPath),void u.logger.info("inbox-chat loop: another message joins the watched turn",{sessionId:t,injectedAtMs:r,markerMessageId:s,open:u.exchanges.length})}const h={projectId:e.projectId,projectPath:e.projectPath,apiClient:e.apiClient,logger:e.logger,exchanges:[d],hardCapAtMs:r+ie,deadline:setTimeout(()=>{R(t)},oe),poll:setInterval(()=>{M(t,!1)},4e3),checking:!1,context:e}
5545
- ce.set(t,h)
5546
- const p=function(e){const t=de.get(e)
5547
- return t?(de.delete(e),Date.now()-t.at<=ue?t.detail:void 0):void 0}(t)
5558
+ return clearTimeout(u.deadline),u.exchanges.push(d),u.hardCapAtMs=r+ae,u.deadline=setTimeout(()=>{R(t)},ie),n(u.projectPath),void u.logger.info("inbox-chat loop: another message joins the watched turn",{sessionId:t,injectedAtMs:r,markerMessageId:s,open:u.exchanges.length})}const h={projectId:e.projectId,projectPath:e.projectPath,apiClient:e.apiClient,logger:e.logger,exchanges:[d],hardCapAtMs:r+ae,deadline:setTimeout(()=>{R(t)},ie),poll:setInterval(()=>{M(t,!1)},4e3),checking:!1,context:e}
5559
+ le.set(t,h)
5560
+ const p=function(e){const t=ue.get(e)
5561
+ return t?(ue.delete(e),Date.now()-t.at<=he?t.detail:void 0):void 0}(t)
5548
5562
  p&&(h.serveError=p,setTimeout(()=>{M(t,!1)},0)),n(e.projectPath),e.logger.info("inbox-chat loop: watching turn",{sessionId:t,injectedAtMs:r,markerMessageId:s})}function a(e){return!e.askAnnotationId&&!r(e)}function c(e){const t=e??{},r="string"==typeof t.name&&t.name?t.name:"UnknownError",s=t.data?.providerID&&t.data?.modelID?`${t.data.providerID}/${t.data.modelID}`:""
5549
5563
  if("UnknownError"!==r)return s?`${r} ${s}`:r
5550
5564
  const n="string"==typeof t.data?.message?t.data.message.trim():"",o=/model not found:?\s*([^\s/]+\/[^\s.]+)/i.exec(n)
@@ -5552,13 +5566,13 @@ return o?`ProviderModelNotFoundError ${o[1]}`:/^[A-Z][A-Za-z]*Error$/.test(n)?n:
5552
5566
  const s=e.findIndex(e=>e.id===t)
5553
5567
  return-1===s||e.slice(s+1).some(e=>"assistant"===e.role)?null:r}function u(e,t=60){const r=e.trim()
5554
5568
  if(!r)return!1
5555
- if(pe.test(r))return!1
5556
- const s=fe.test(r)
5557
- return r.length>=(s?Math.ceil(t/3):t)?!(he.test(r)&&r.length<(s?40:120)):/[.!?\u3002\uff01\uff1f]/.test(r)&&!he.test(r)&&r.length>=(s?8:24)}function h(e){const t=new Set
5569
+ if(fe.test(r))return!1
5570
+ const s=me.test(r)
5571
+ return r.length>=(s?Math.ceil(t/3):t)?!(pe.test(r)&&r.length<(s?40:120)):/[.!?\u3002\uff01\uff1f]/.test(r)&&!pe.test(r)&&r.length>=(s?8:24)}function h(e){const t=new Set
5558
5572
  for(const r of e.toLowerCase().replace(/[*_`#>]/g," ").split(/[^\p{L}\p{N}.,']+/u)){const e=r.replace(/^[.,']+|[.,']+$/g,"")
5559
- if(e){for(const r of e.match(me)??[])if(1!==r.length)for(let e=0;e+1<r.length;e++)t.add(r.slice(e,e+2))
5573
+ if(e){for(const r of e.match(ge)??[])if(1!==r.length)for(let e=0;e+1<r.length;e++)t.add(r.slice(e,e+2))
5560
5574
  else t.add(r)
5561
- for(const r of e.replace(me," ").split(/\s+/))r&&(/^req\d+$/.test(r)||/\p{N}/u.test(r)||r.length>=4)&&t.add(r)}}return t}function p(e,t){const r=h(e),s=h(t)
5575
+ for(const r of e.replace(ge," ").split(/\s+/))r&&(/^req\d+$/.test(r)||/\p{N}/u.test(r)||r.length>=4)&&t.add(r)}}return t}function p(e,t){const r=h(e),s=h(t)
5562
5576
  if(0===s.size)return!1
5563
5577
  let n=0
5564
5578
  for(const e of s)r.has(e)&&n++
@@ -5571,7 +5585,7 @@ if("user"===o.role&&w(e,r,t))break
5571
5585
  "assistant"===o.role&&o.completed&&o.text.trim()&&(s.push(o.text.trim()),n=I(o))}if(0===s.length)return null
5572
5586
  const o=s[s.length-1],i=s.find(e=>u(e))
5573
5587
  if(!i||i===o)return{reply:o}
5574
- const a=b(e,t,i),c=(ge.test(y(i.split(/\n{2,}/)[0]??"")[0]??"")||n)&&u(o,40)||a&&u(o,40)&&!p(i,o)?o:void 0
5588
+ const a=b(e,t,i),c=(we.test(y(i.split(/\n{2,}/)[0]??"")[0]??"")||n)&&u(o,40)||a&&u(o,40)&&!p(i,o)?o:void 0
5575
5589
  return c?{reply:v(i,c)}:{reply:i}}function m(e,t){const r=e.findIndex(e=>e.id===t)
5576
5590
  if(-1===r)return null
5577
5591
  let s=null
@@ -5588,13 +5602,13 @@ let s=0
5588
5602
  for(const e of t)r.has(e)&&s++
5589
5603
  return s/t.size>=.6},n=[]
5590
5604
  for(const t of e.split(/\n{2,}/)){if(/^\s*(?:[-*\u2022]|\d+[.)])\s/m.test(t)){s(t)||n.push(t.trim())
5591
- continue}const e=y(t).filter(e=>!ge.test(e)&&!s(e))
5605
+ continue}const e=y(t).filter(e=>!we.test(e)&&!s(e))
5592
5606
  e.length&&n.push(e.join(" "))}return n.length?`${n.join("\n\n")}\n\n${t.trim()}`:t.trim()}function b(e,t,r){const s=e.findIndex(e=>e.id===t)
5593
5607
  if(-1===s)return!1
5594
5608
  let n=!1
5595
5609
  for(let t=s+1;t<e.length;t++){const s=e[t]
5596
5610
  if("assistant"===s.role){if(!n){if(!s.completed||s.text.trim()!==r)continue
5597
- n=!0}for(const e of s.tools??[])if(!we.test(e))return!0}}return!1}function k(e,t,r=[],s=[]){const n=e.findIndex(e=>e.id===t)
5611
+ n=!0}for(const e of s.tools??[])if(!ye.test(e))return!0}}return!1}function k(e,t,r=[],s=[]){const n=e.findIndex(e=>e.id===t)
5598
5612
  if(-1===n)return e
5599
5613
  let o=!1,i=!0
5600
5614
  return e.filter((e,a)=>a<=n||"assistant"!==e.role||!e.parentId||(e.parentId===t?(o=!0,i=!0,!0):!e.aside&&(s.includes(e.parentId)||(i=!o&&!r.includes(e.parentId)),i)))}function S(e,t){const r=e.findIndex(e=>e.id===t)
@@ -5623,19 +5637,19 @@ if(-1===r)return!1
5623
5637
  let s=null
5624
5638
  for(let t=r+1;t<e.length;t++){const n=e[t]
5625
5639
  if("user"===n.role&&w(e,r,t))break
5626
- "assistant"===n.role&&(s=n)}return"MessageAbortedError"===s?.errorName}function _(e){ye.add(e),ye.size>500&&ye.delete(ye.values().next().value)}async function T(e,t,r){const s={projectId:t.projectId,projectPath:t.projectPath,apiClient:t.apiClient,logger:t.logger},o=await(0,ee.deliverPrompt)(s,e,r,{kind:"inserted-prompt",partPayload:{nudge:!0}})
5640
+ "assistant"===n.role&&(s=n)}return"MessageAbortedError"===s?.errorName}function _(e){ve.add(e),ve.size>500&&ve.delete(ve.values().next().value)}async function T(e,t,r){const s={projectId:t.projectId,projectPath:t.projectPath,apiClient:t.apiClient,logger:t.logger},o=await(0,te.deliverPrompt)(s,e,r,{kind:"inserted-prompt",partPayload:{nudge:!0}})
5627
5641
  if(!o.ok||!o.messageId)return t.logger.warn("inbox-chat loop: could not resume a turn",{sessionId:e,error:o.error}),!1
5628
5642
  const i=Date.now(),a=t.exchanges[t.exchanges.length-1]
5629
- return t.resumed=!0,t.serveError=void 0,a.markerMessageId=o.messageId,a.injectedAtMs=i,t.hardCapAtMs=i+ie,clearTimeout(t.deadline),t.deadline=setTimeout(()=>{R(e)},oe),n(t.projectPath),!0}function x(e,t){const r=e.findIndex(e=>e.id===t)
5643
+ return t.resumed=!0,t.serveError=void 0,a.markerMessageId=o.messageId,a.injectedAtMs=i,t.hardCapAtMs=i+ae,clearTimeout(t.deadline),t.deadline=setTimeout(()=>{R(e)},ie),n(t.projectPath),!0}function x(e,t){const r=e.findIndex(e=>e.id===t)
5630
5644
  if(-1===r)return!1
5631
5645
  let s=!1
5632
5646
  for(let t=r+1;t<e.length;t++){const r=e[t]
5633
5647
  if("assistant"===r.role){if(r.text.trim())return!1
5634
5648
  if(r.errorName)return!1
5635
- r.completed&&(s=!0)}}return s}async function M(e,t){const s=ce.get(e)
5636
- if(s&&!s.checking)if((0,ne.isProjectPaused)())t&&(s.deadline=setTimeout(()=>{R(e)},oe))
5649
+ r.completed&&(s=!0)}}return s}async function M(e,t){const s=le.get(e)
5650
+ if(s&&!s.checking)if((0,oe.isProjectPaused)())t&&(s.deadline=setTimeout(()=>{R(e)},ie))
5637
5651
  else{s.checking=!0
5638
- try{const n=(0,Y.getOpenCodeUrlFromConfig)(s.projectPath)?.replace(/\/$/,"")
5652
+ try{const n=(0,J.getOpenCodeUrlFromConfig)(s.projectPath)?.replace(/\/$/,"")
5639
5653
  if(!n)return void(t&&await D(e,s,{turnDied:!0}))
5640
5654
  const o=await async function(e,t){try{const r=await fetch(`${e}/session/status`,{signal:AbortSignal.timeout(5e3)})
5641
5655
  if(!r.ok)return null
@@ -5643,39 +5657,39 @@ const s=await r.json(),n=s?.[t]?.type
5643
5657
  return!!n&&"idle"!==n}catch{return null}}(n,e)
5644
5658
  if(null===o&&!t)return
5645
5659
  const i=o??!0,l=await async function(e,t){try{const r=await fetch(`${e}/session/${t}/message?limit=100`,{signal:AbortSignal.timeout(1e4)})
5646
- return r.ok?(await r.json()).map(e=>({id:e.info?.id||"",role:e.info?.role||"unknown",text:(0,X.replyText)(e.parts),created:e.info?.time?.created??0,tools:(e.parts||[]).filter(e=>"tool"===e.type&&e.tool).map(e=>{const t=e?.state?.input?.operation
5647
- return String(e.tool)+("string"==typeof t&&t?`:${t}`:"")}),web:(0,Q.webLooksOf)(e.parts),...e.info?.parentID?{parentId:String(e.info.parentID)}:{},completed:!!e.info?.time?.completed,...e.info?.time?.completed?{completedAt:e.info.time.completed}:{},...e.info?.finish?{finish:String(e.info.finish)}:{},...(e.parts||[]).some(e=>"text"===e?.type&&"final_answer"===e?.metadata?.openai?.phase)?{finalAnswer:!0}:{},...(e.parts||[]).some(e=>"text"===e?.type&&"commentary"===e?.metadata?.openai?.phase)?{commentary:!0}:{},...!0===e.info?.aside?{aside:!0}:{},...(()=>{if("user"!==e.info?.role)return{}
5660
+ return r.ok?(await r.json()).map(e=>({id:e.info?.id||"",role:e.info?.role||"unknown",text:(0,Q.replyText)(e.parts),created:e.info?.time?.created??0,tools:(e.parts||[]).filter(e=>"tool"===e.type&&e.tool).map(e=>{const t=e?.state?.input?.operation
5661
+ return String(e.tool)+("string"==typeof t&&t?`:${t}`:"")}),web:(0,Z.webLooksOf)(e.parts),...e.info?.parentID?{parentId:String(e.info.parentID)}:{},completed:!!e.info?.time?.completed,...e.info?.time?.completed?{completedAt:e.info.time.completed}:{},...e.info?.finish?{finish:String(e.info.finish)}:{},...(e.parts||[]).some(e=>"text"===e?.type&&"final_answer"===e?.metadata?.openai?.phase)?{finalAnswer:!0}:{},...(e.parts||[]).some(e=>"text"===e?.type&&"commentary"===e?.metadata?.openai?.phase)?{commentary:!0}:{},...!0===e.info?.aside?{aside:!0}:{},...(()=>{if("user"!==e.info?.role)return{}
5648
5662
  const t=(e.parts||[]).map(e=>e?.metadata?.alchemy).find(e=>e&&"string"==typeof e.kind)
5649
- return t?{alchemyKind:String(t.kind),...(0,te.holdsDuringUserTurn)(t.kind,t.payload)?{heldKind:!0}:{}}:{}})(),...e.info?.error?.name?{errorName:String(e.info.error.name),errorDetail:c(e.info.error),errorFatal:(0,Z.isFatalTurnError)(e.info.error),...(0,Z.isFatalTurnError)(e.info.error)?{errorRefusal:(0,Z.turnRefusal)(e.info.error,e.info)}:{}}:{}})).sort((e,t)=>e.created-t.created):null}catch{return null}}(n,e),u=s.exchanges.map(e=>e.markerMessageId),h=[...s.exchanges.filter(r).map(e=>e.markerMessageId),...(l??[]).filter(e=>"user"===e.role&&e.heldKind).map(e=>e.id)],p=e=>k(l??[],e.markerMessageId,u.filter(t=>t!==e.markerMessageId),s.asideAnswered??[])
5663
+ return t?{alchemyKind:String(t.kind),...(0,re.holdsDuringUserTurn)(t.kind,t.payload)?{heldKind:!0}:{}}:{}})(),...e.info?.error?.name?{errorName:String(e.info.error.name),errorDetail:c(e.info.error),errorFatal:(0,ee.isFatalTurnError)(e.info.error),...(0,ee.isFatalTurnError)(e.info.error)?{errorRefusal:(0,ee.turnRefusal)(e.info.error,e.info)}:{}}:{}})).sort((e,t)=>e.created-t.created):null}catch{return null}}(n,e),u=s.exchanges.map(e=>e.markerMessageId),h=[...s.exchanges.filter(r).map(e=>e.markerMessageId),...(l??[]).filter(e=>"user"===e.role&&e.heldKind).map(e=>e.id)],p=e=>k(l??[],e.markerMessageId,u.filter(t=>t!==e.markerMessageId),s.asideAnswered??[])
5650
5664
  if(l){for(const t of[...s.exchanges]){if(!s.exchanges.includes(t))continue
5651
5665
  if(r(t))continue
5652
5666
  const n=(l??[]).find(e=>e.aside&&e.parentId===t.markerMessageId&&e.completed&&!e.errorName&&e.text.trim())
5653
- if(n){s.asideAnswered=[...s.asideAnswered??[],t.markerMessageId],L(s,[n.id]),await N(e,s,t,{turnText:n.text.trim().slice(0,ae),aside:!0},!1)
5667
+ if(n){s.asideAnswered=[...s.asideAnswered??[],t.markerMessageId],L(s,[n.id]),await N(e,s,t,{turnText:n.text.trim().slice(0,ce),aside:!0},!1)
5654
5668
  continue}const o=U(p(t),s)
5655
5669
  if(!S(o,t.markerMessageId))continue
5656
5670
  const a=f(o,t.markerMessageId)
5657
- a&&(i&&s.logger.info("inbox-chat loop: answer complete mid-turn, relayed while the session works on",{sessionId:e,markerMessageId:t.markerMessageId}),await N(e,s,t,{turnText:(0,Q.withSources)(a.reply.slice(0,ae),j(o,t.markerMessageId)),turnTrace:$(o,t.markerMessageId)??void 0,askedBefore:H(o,t.markerMessageId)})&&!t.askAnnotationId&&L(s,W(o,t.markerMessageId)))}if(0===s.exchanges.length)return void P(e,s)}if(i&&!t)return
5658
- if(i&&t&&Date.now()<s.hardCapAtMs)return s.deadline=setTimeout(()=>{R(e)},oe),void s.logger.info("inbox-chat loop: session busy at deadline, extending watch",{sessionId:e})
5671
+ a&&(i&&s.logger.info("inbox-chat loop: answer complete mid-turn, relayed while the session works on",{sessionId:e,markerMessageId:t.markerMessageId}),await N(e,s,t,{turnText:(0,Z.withSources)(a.reply.slice(0,ce),j(o,t.markerMessageId)),turnTrace:$(o,t.markerMessageId)??void 0,askedBefore:H(o,t.markerMessageId)})&&!t.askAnnotationId&&L(s,W(o,t.markerMessageId)))}if(0===s.exchanges.length)return void P(e,s)}if(i&&!t)return
5672
+ if(i&&t&&Date.now()<s.hardCapAtMs)return s.deadline=setTimeout(()=>{R(e)},ie),void s.logger.info("inbox-chat loop: session busy at deadline, extending watch",{sessionId:e})
5659
5673
  if(l&&C(l,(s.resumed?s.exchanges[s.exchanges.length-1]:s.exchanges[0]).markerMessageId))return void await D(e,s,{turnDied:!0,turnError:"MessageAbortedError"})
5660
5674
  const g=l?A(l,s.exchanges[0].markerMessageId):null
5661
5675
  if(g){if(g.transient&&!s.resumed&&await async function(e,t,r){const s=await T(e,t,"Your previous turn was cut off by a transient provider error (the connection to the model dropped), not by anything you did. A tool call that seemed to vanish may still have run: check before repeating one that changes something. Carry on answering the user's last message from where you stopped.")
5662
5676
  return s&&t.logger.warn("inbox-chat loop: turn cut off by a provider error; resumed it once",{sessionId:e,error:r,markerMessageId:t.exchanges[t.exchanges.length-1].markerMessageId}),s}(e,s,g.detail))return
5663
- return _(g.messageId),void await D(e,s,{turnDied:!0,turnError:s.resumed?`${g.detail}, twice`:g.detail,...g.refusal?{turnRefusal:g.refusal}:{}})}if(l){for(const n of s.exchanges.filter(e=>e.askAnnotationId||r(e))){const r=U(p(n),s),o=n.systemWake?m(r,n.markerMessageId):null,i=n.systemWake?o?{reply:o}:null:f(r,n.markerMessageId);(!n.systemWake||i||t||x(r,n.markerMessageId))&&await N(e,s,n,i?{turnText:i.reply.slice(0,ae),changed:B(r,n.markerMessageId)}:{turnSilent:!0},!1)}if(0===s.exchanges.length)return void P(e,s)
5677
+ return _(g.messageId),void await D(e,s,{turnDied:!0,turnError:s.resumed?`${g.detail}, twice`:g.detail,...g.refusal?{turnRefusal:g.refusal}:{}})}if(l){for(const n of s.exchanges.filter(e=>e.askAnnotationId||r(e))){const r=U(p(n),s),o=n.systemWake?m(r,n.markerMessageId):null,i=n.systemWake?o?{reply:o}:null:f(r,n.markerMessageId);(!n.systemWake||i||t||x(r,n.markerMessageId))&&await N(e,s,n,i?{turnText:i.reply.slice(0,ce),changed:B(r,n.markerMessageId)}:{turnSilent:!0},!1)}if(0===s.exchanges.length)return void P(e,s)
5664
5678
  if(!s.exchanges.some(a)){const t=s.exchanges[s.exchanges.length-1],r=E(l,t.markerMessageId)??d(l,t.markerMessageId,s.serveError)
5665
5679
  return void(r&&await D(e,s,{turnDied:!0,turnError:r}))}}const w=s.exchanges[s.exchanges.length-1],y=s.exchanges[0],v=U((l??[]).filter(e=>!(e.aside&&e.parentId!==y.markerMessageId||"assistant"===e.role&&e.parentId&&h.includes(e.parentId))),s),b=l?f(v,y.markerMessageId):null
5666
- if(b)return void await O(e,s,{turnText:(0,Q.withSources)(b.reply.slice(0,ae),j(v,y.markerMessageId)),turnTrace:$(v,y.markerMessageId)??void 0,askedBefore:H(v,w.markerMessageId)})
5680
+ if(b)return void await O(e,s,{turnText:(0,Z.withSources)(b.reply.slice(0,ce),j(v,y.markerMessageId)),turnTrace:$(v,y.markerMessageId)??void 0,askedBefore:H(v,w.markerMessageId)})
5667
5681
  const I=l?E(l,w.markerMessageId):null
5668
5682
  if(I)return void await D(e,s,{turnDied:!0,turnError:I})
5669
5683
  const M=l?d(l,w.markerMessageId,s.serveError):null
5670
5684
  if(M)return s.logger.warn("inbox-chat loop: turn died before an assistant message existed",{sessionId:e,error:M}),void await D(e,s,{turnDied:!0,turnError:M})
5671
5685
  if(l&&x(v,y.markerMessageId))return s.logger.info("inbox-chat loop: turn concluded silently (no text, no error)",{sessionId:e}),void await O(e,s,{turnSilent:!0})
5672
- t&&await D(e,s,{turnDied:!0})}finally{const t=ce.get(e)
5673
- t&&(t.checking=!1)}}}async function R(e){await M(e,!0)}function P(e,t){clearTimeout(t.deadline),clearInterval(t.poll),ce.get(e)===t&&ce.delete(e),n(t.projectPath),function(e,t){const r=t.held??[]
5686
+ t&&await D(e,s,{turnDied:!0})}finally{const t=le.get(e)
5687
+ t&&(t.checking=!1)}}}async function R(e){await M(e,!0)}function P(e,t){clearTimeout(t.deadline),clearInterval(t.poll),le.get(e)===t&&le.delete(e),n(t.projectPath),function(e,t){const r=t.held??[]
5674
5688
  if(t.held=[],0===r.length)return
5675
- const s=t.context,n=le
5676
- !s||!n&&r.some(e=>"deliver"!==e.via)?t.logger.error("inbox-chat loop: held system notes could not be delivered (no deliverer)",{sessionId:e,held:r.length}):setImmediate(()=>{(async()=>{for(const o of r)try{if("deliver"===o.via){const e=await(0,ee.deliverPrompt)(s,o.sessionId,o.message,{...o.opts??{},...o.kind?{kind:o.kind}:{},...o.partPayload?{partPayload:o.partPayload}:{}})
5689
+ const s=t.context,n=de
5690
+ !s||!n&&r.some(e=>"deliver"!==e.via)?t.logger.error("inbox-chat loop: held system notes could not be delivered (no deliverer)",{sessionId:e,held:r.length}):setImmediate(()=>{(async()=>{for(const o of r)try{if("deliver"===o.via){const e=await(0,te.deliverPrompt)(s,o.sessionId,o.message,{...o.opts??{},...o.kind?{kind:o.kind}:{},...o.partPayload?{partPayload:o.partPayload}:{}})
5677
5691
  if(!e.ok)throw new Error(e.detail??String(e.error))}else await n(o,s)
5678
- t.logger.info("inbox-chat loop: held system note delivered",{sessionId:e,kind:o.kind??null})}catch(r){t.logger.error("inbox-chat loop: held system note delivery failed",{sessionId:e,error:(0,J.getErrorMessage)(r)})}})()})}(e,t)}async function O(e,t,r){const s=t.exchanges[t.exchanges.length-1],n=t.exchanges.filter(e=>e!==s&&!e.askAnnotationId).map(e=>e.exchangeKey).filter(e=>!!e)
5692
+ t.logger.info("inbox-chat loop: held system note delivered",{sessionId:e,kind:o.kind??null})}catch(r){t.logger.error("inbox-chat loop: held system note delivery failed",{sessionId:e,error:(0,X.getErrorMessage)(r)})}})()})}(e,t)}async function O(e,t,r){const s=t.exchanges[t.exchanges.length-1],n=t.exchanges.filter(e=>e!==s&&!e.askAnnotationId).map(e=>e.exchangeKey).filter(e=>!!e)
5679
5693
  P(e,t),await F(e,t,s,{...r,absorbs:n})}async function D(e,t,r){const s=[...t.exchanges]
5680
5694
  P(e,t)
5681
5695
  for(const n of s)await F(e,t,n,r)}function q(e,t,s=!0){if(!s||t.askAnnotationId||r(t))return[]
@@ -5684,22 +5698,25 @@ return e.slice(0,Math.max(0,n)).filter(e=>!e.askAnnotationId&&!r(e))}async funct
5684
5698
  return t.exchanges=t.exchanges.filter(e=>e!==r&&!i.includes(e)),n(t.projectPath),i.length&&t.logger.info("inbox-chat loop: earlier messages answered by this reply",{sessionId:e,markers:i.map(e=>e.markerMessageId)}),F(e,t,r,{...s,absorbs:i.map(e=>e.exchangeKey).filter(e=>!!e)})}async function F(e,t,r,s){if(r.askAnnotationId)return async function(e,t,r,s){const n={sessionId:e,...s.turnDied?{turnDied:!0}:s.turnSilent?{turnSilent:!0}:{turnText:s.turnText??""}}
5685
5699
  for(const s of[0,2e3,8e3]){s>0&&await new Promise(e=>setTimeout(e,s))
5686
5700
  try{const s=await t.apiClient.post(`/api/projects/${t.projectId}/annotations/${encodeURIComponent(r)}/ask-outcome`,n)
5687
- if(!1!==s?.success)return t.logger.info("inbox-chat loop: document ask outcome reported",{sessionId:e,annotationId:r,posted:s?.data?.posted,reason:s?.data?.reason}),!0===s?.data?.posted}catch(s){t.logger.warn("inbox-chat loop: document ask outcome failed",{sessionId:e,annotationId:r,error:(0,J.getErrorMessage)(s)})}}return t.logger.error("inbox-chat loop: document ask outcome undeliverable after retries",{sessionId:e,annotationId:r}),!1}(e,t,r.askAnnotationId,s)
5688
- if(r.systemWake)return async function(e,t,r,s,n){const o=n.turnDied||n.turnSilent||!(0,te.wakeLineIsForUser)(s.kind,!0===n.changed)?"":(n.turnText??"").trim(),i={sessionId:e,wake:s.kind,...s.requestId?{requestId:s.requestId}:{},injectedAt:new Date(r.injectedAtMs).toISOString(),...o?{turnText:o.slice(0,ae)}:{}}
5701
+ if(!1!==s?.success)return t.logger.info("inbox-chat loop: document ask outcome reported",{sessionId:e,annotationId:r,posted:s?.data?.posted,reason:s?.data?.reason}),!0===s?.data?.posted}catch(s){t.logger.warn("inbox-chat loop: document ask outcome failed",{sessionId:e,annotationId:r,error:(0,X.getErrorMessage)(s)})}}return t.logger.error("inbox-chat loop: document ask outcome undeliverable after retries",{sessionId:e,annotationId:r}),!1}(e,t,r.askAnnotationId,s)
5702
+ if(r.systemWake)return async function(e,t,r,s,n){const o=n.turnDied||n.turnSilent||!(0,re.wakeLineIsForUser)(s.kind,!0===n.changed)?"":(n.turnText??"").trim()
5703
+ let i=!1
5704
+ if("unit-start"===s.kind&&s.requestId&&o)try{const e=await fetch(`${(0,Y.getActionServerBaseUrl)()}/api/request-diff-stats?requestId=${encodeURIComponent(s.requestId)}`,{signal:AbortSignal.timeout(5e3)}),t=e.ok?(await e.json())?.data:null
5705
+ i=!!t&&0===t.filesChanged&&0===t.additions&&0===t.deletions}catch{}const a={sessionId:e,wake:s.kind,...s.requestId?{requestId:s.requestId}:{},injectedAt:new Date(r.injectedAtMs).toISOString(),...o?{turnText:o.slice(0,ce)}:{},...i?{diffNone:!0}:{}}
5689
5706
  for(const r of[0,2e3,8e3]){r>0&&await new Promise(e=>setTimeout(e,r))
5690
- try{const r=await t.apiClient.post(`/api/projects/${t.projectId}/system-turn-note`,i)
5691
- if(!1!==r?.success)return t.logger.info("inbox-chat loop: system wake turn reported",{sessionId:e,wake:s.kind,requestId:s.requestId??null,posted:r?.data?.posted,reason:r?.data?.reason}),!0===r?.data?.posted}catch(r){t.logger.warn("inbox-chat loop: system wake turn report failed",{sessionId:e,wake:s.kind,error:(0,J.getErrorMessage)(r)})}}return t.logger.error("inbox-chat loop: system wake turn undeliverable after retries; its closing line is lost",{sessionId:e,wake:s.kind}),!1}(e,t,r,r.systemWake,s)
5707
+ try{const r=await t.apiClient.post(`/api/projects/${t.projectId}/system-turn-note`,a)
5708
+ if(!1!==r?.success)return t.logger.info("inbox-chat loop: system wake turn reported",{sessionId:e,wake:s.kind,requestId:s.requestId??null,posted:r?.data?.posted,reason:r?.data?.reason}),!0===r?.data?.posted}catch(r){t.logger.warn("inbox-chat loop: system wake turn report failed",{sessionId:e,wake:s.kind,error:(0,X.getErrorMessage)(r)})}}return t.logger.error("inbox-chat loop: system wake turn undeliverable after retries; its closing line is lost",{sessionId:e,wake:s.kind}),!1}(e,t,r,r.systemWake,s)
5692
5709
  if(r.systemNoteRequestId)return async function(e,t,r,s){const n=(s.turnText??"").trim()
5693
5710
  if(!n||s.turnDied||s.turnSilent||!s.changed)return!1
5694
- try{const s=await t.apiClient.post(`/api/projects/${t.projectId}/system-turn-note`,{sessionId:e,requestId:r.systemNoteRequestId,injectedAt:new Date(r.injectedAtMs).toISOString(),turnText:n.slice(0,ae)})
5695
- return t.logger.info("inbox-chat loop: system turn note reported",{sessionId:e,requestId:r.systemNoteRequestId,posted:s?.data?.posted,reason:s?.data?.reason}),!0===s?.data?.posted}catch(r){return t.logger.warn("inbox-chat loop: system turn note failed",{sessionId:e,error:(0,J.getErrorMessage)(r)}),!1}}(e,t,r,s)
5711
+ try{const s=await t.apiClient.post(`/api/projects/${t.projectId}/system-turn-note`,{sessionId:e,requestId:r.systemNoteRequestId,injectedAt:new Date(r.injectedAtMs).toISOString(),turnText:n.slice(0,ce)})
5712
+ return t.logger.info("inbox-chat loop: system turn note reported",{sessionId:e,requestId:r.systemNoteRequestId,posted:s?.data?.posted,reason:s?.data?.reason}),!0===s?.data?.posted}catch(r){return t.logger.warn("inbox-chat loop: system turn note failed",{sessionId:e,error:(0,X.getErrorMessage)(r)}),!1}}(e,t,r,s)
5696
5713
  const n={sessionId:e,markerMessageId:r.markerMessageId,injectedAt:new Date(r.injectedAtMs).toISOString(),...r.exchangeKey?{exchangeKey:r.exchangeKey}:{},...r.resumes?{resumesMarkerId:r.resumes}:{},...s.turnDied?{turnDied:!0,...s.turnError?{turnError:s.turnError}:{},...s.turnRefusal?{turnErrorMessage:s.turnRefusal.message,turnErrorScope:s.turnRefusal.scope,...s.turnRefusal.model?{turnErrorModel:s.turnRefusal.model}:{}}:{}}:s.turnSilent?{turnSilent:!0}:{turnText:s.turnText??"",...s.followUpText?{followUpText:s.followUpText}:{},...s.turnTrace?{turnTrace:s.turnTrace}:{}},...s.askedBefore?{askedBefore:s.askedBefore}:{},...s.absorbs?{absorbs:s.absorbs}:{},...s.aside?{aside:!0}:{}},o=[0,2e3,8e3]
5697
5714
  for(let r=0;r<o.length;r++){o[r]>0&&await new Promise(e=>setTimeout(e,o[r]))
5698
5715
  try{const o=await t.apiClient.post(`/api/projects/${t.projectId}/inbox-chat/loop-check`,n)
5699
5716
  if(!1!==o?.success)return t.logger.info("inbox-chat loop: turn outcome reported",{sessionId:e,died:!!s.turnDied,relayed:o?.data?.relayed,reason:o?.data?.reason}),!0
5700
- t.logger.warn("inbox-chat loop: loop-check rejected",{sessionId:e,attempt:r,error:o?.error})}catch(s){t.logger.warn("inbox-chat loop: loop-check failed",{sessionId:e,attempt:r,error:(0,J.getErrorMessage)(s)})}}return t.logger.error("inbox-chat loop: loop-check undeliverable after retries \u2014 reply relay lost",{sessionId:e}),!1}function L(e,t){0!==t.length&&(e.relayedIds=[...new Set([...e.relayedIds??[],...t])].slice(-200),n(e.projectPath))}function U(e,t){const r=new Set(t.relayedIds??[])
5717
+ t.logger.warn("inbox-chat loop: loop-check rejected",{sessionId:e,attempt:r,error:o?.error})}catch(s){t.logger.warn("inbox-chat loop: loop-check failed",{sessionId:e,attempt:r,error:(0,X.getErrorMessage)(s)})}}return t.logger.error("inbox-chat loop: loop-check undeliverable after retries \u2014 reply relay lost",{sessionId:e}),!1}function L(e,t){0!==t.length&&(e.relayedIds=[...new Set([...e.relayedIds??[],...t])].slice(-200),n(e.projectPath))}function U(e,t){const r=new Set(t.relayedIds??[])
5701
5718
  return r.size?e.filter(e=>!("assistant"===e.role&&r.has(e.id))):e}function B(e,t){const r=e.findIndex(e=>e.id===t)
5702
- return-1!==r&&e.slice(r+1).some(e=>"assistant"===e.role&&(e.tools??[]).some(e=>!we.test(e)))}function H(e,t){const r=e.findIndex(e=>e.id===t)
5719
+ return-1!==r&&e.slice(r+1).some(e=>"assistant"===e.role&&(e.tools??[]).some(e=>!ye.test(e)))}function H(e,t){const r=e.findIndex(e=>e.id===t)
5703
5720
  if(-1===r)return
5704
5721
  const s=e.slice(r+1).find(e=>"assistant"===e.role&&e.created>0)
5705
5722
  return s?new Date(s.created).toISOString():void 0}function W(e,t){const r=e.findIndex(e=>e.id===t)
@@ -5711,34 +5728,34 @@ return t},G(e)},function(e){if(e&&e.__esModule)return e
5711
5728
  var t={}
5712
5729
  if(null!=e)for(var r=G(e),s=0;s<r.length;s++)"default"!==r[s]&&K(t,e,r[s])
5713
5730
  return z(t,e),t})
5714
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.extendChatWatches=function(e){if(e>0)for(const t of ce.values())t.hardCapAtMs+=e},exports.persistedExchanges=o,exports.resumeInboxChatWatches=function(e){try{const t=s(e.projectPath)
5715
- if(!re.existsSync(t))return
5716
- const r=JSON.parse(re.readFileSync(t,"utf-8"))
5717
- for(const t of r){if(!t?.sessionId||ce.has(t.sessionId))continue
5731
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.extendChatWatches=function(e){if(e>0)for(const t of le.values())t.hardCapAtMs+=e},exports.persistedExchanges=o,exports.resumeInboxChatWatches=function(e){try{const t=s(e.projectPath)
5732
+ if(!se.existsSync(t))return
5733
+ const r=JSON.parse(se.readFileSync(t,"utf-8"))
5734
+ for(const t of r){if(!t?.sessionId||le.has(t.sessionId))continue
5718
5735
  const r=o(t)
5719
5736
  if(0===r.length)continue
5720
5737
  for(const s of r)i(e,t.sessionId,s.injectedAtMs,s.markerMessageId,s.exchangeKey,s.askAnnotationId,s.systemNoteRequestId,s.resumes,s.systemWake)
5721
- const s=ce.get(t.sessionId)
5722
- s&&t.resumed&&(s.resumed=!0),s&&Array.isArray(t.asideAnswered)&&(s.asideAnswered=t.asideAnswered.filter(e=>"string"==typeof e)),s&&Array.isArray(t.relayedIds)&&(s.relayedIds=t.relayedIds.filter(e=>"string"==typeof e)),s&&Array.isArray(t.held)&&(s.held=t.held.filter(e=>"string"==typeof e?.sessionId&&"string"==typeof e?.message)),e.logger.info("inbox-chat loop: resumed watch from a previous life",{sessionId:t.sessionId,exchanges:r.length}),M(t.sessionId,!1)}}catch(t){e.logger.warn("inbox-chat loop: watch resume failed",{error:(0,J.getErrorMessage)(t)})}},exports.trackInboxChatTurn=i,exports.registerHeldInjectDeliverer=function(e){le=e},exports.holdForChatTurn=function(e){const t=ce.get(e.sessionId)
5723
- return!(!t||!t.exchanges.some(a)||(t.held=[...t.held??[],e],n(t.projectPath),t.logger.info("inbox-chat loop: system note held until the user's turn ends",{sessionId:e.sessionId,kind:e.kind??null,open:t.exchanges.length,held:t.held.length}),0))},exports.notifyInboxChatSessionIdle=function(e){ce.has(e)&&M(e,!1)},exports.notifyInboxChatSessionError=function(e,t){const r=c(t),s=ce.get(e)
5724
- if(!s){const t=de.get(e)
5725
- if(t&&Date.now()-t.at<=ue&&l(t.detail)>=l(r))return
5726
- de.set(e,{detail:r,at:Date.now()})
5727
- for(const[e,t]of de)Date.now()-t.at>ue&&de.delete(e)
5728
- return}s.serveError&&l(s.serveError)>=l(r)||(s.serveError=r,M(e,!1))},exports.describeServeError=c,exports.preMessageDeath=d,exports.hasActiveInboxChatTurns=function(){return ce.size>0},exports.isWatchingInboxChatTurn=function(e){return ce.has(e)},exports.isSubstantiveReply=u,exports.isRestatement=p,exports.pickTurnReply=f,exports.closingText=m,exports.startsNewTurn=g,exports.endsExchange=w,exports.mergeReplyWithFollowUp=v,exports.turnChangedSomethingAfter=b,exports.ownView=k,exports.hasDeclaredAnswer=S,exports.summarizeTurnTrace=$,exports.turnWebLooks=j,exports.extractTurnDeath=E,exports.extractTurnCutOff=A,exports.extractTurnStopped=C,exports.noteCutOffRelayed=_,exports.cutOffAlreadyRelayed=function(e){return!!e&&ye.has(e)},exports.resumeStalledWatchedTurn=async function(e,t,r){const s=ce.get(e)
5738
+ const s=le.get(t.sessionId)
5739
+ s&&t.resumed&&(s.resumed=!0),s&&Array.isArray(t.asideAnswered)&&(s.asideAnswered=t.asideAnswered.filter(e=>"string"==typeof e)),s&&Array.isArray(t.relayedIds)&&(s.relayedIds=t.relayedIds.filter(e=>"string"==typeof e)),s&&Array.isArray(t.held)&&(s.held=t.held.filter(e=>"string"==typeof e?.sessionId&&"string"==typeof e?.message)),e.logger.info("inbox-chat loop: resumed watch from a previous life",{sessionId:t.sessionId,exchanges:r.length}),M(t.sessionId,!1)}}catch(t){e.logger.warn("inbox-chat loop: watch resume failed",{error:(0,X.getErrorMessage)(t)})}},exports.trackInboxChatTurn=i,exports.registerHeldInjectDeliverer=function(e){de=e},exports.holdForChatTurn=function(e){const t=le.get(e.sessionId)
5740
+ return!(!t||!t.exchanges.some(a)||(t.held=[...t.held??[],e],n(t.projectPath),t.logger.info("inbox-chat loop: system note held until the user's turn ends",{sessionId:e.sessionId,kind:e.kind??null,open:t.exchanges.length,held:t.held.length}),0))},exports.notifyInboxChatSessionIdle=function(e){le.has(e)&&M(e,!1)},exports.notifyInboxChatSessionError=function(e,t){const r=c(t),s=le.get(e)
5741
+ if(!s){const t=ue.get(e)
5742
+ if(t&&Date.now()-t.at<=he&&l(t.detail)>=l(r))return
5743
+ ue.set(e,{detail:r,at:Date.now()})
5744
+ for(const[e,t]of ue)Date.now()-t.at>he&&ue.delete(e)
5745
+ return}s.serveError&&l(s.serveError)>=l(r)||(s.serveError=r,M(e,!1))},exports.describeServeError=c,exports.preMessageDeath=d,exports.hasActiveInboxChatTurns=function(){return le.size>0},exports.isWatchingInboxChatTurn=function(e){return le.has(e)},exports.isSubstantiveReply=u,exports.isRestatement=p,exports.pickTurnReply=f,exports.closingText=m,exports.startsNewTurn=g,exports.endsExchange=w,exports.mergeReplyWithFollowUp=v,exports.turnChangedSomethingAfter=b,exports.ownView=k,exports.hasDeclaredAnswer=S,exports.summarizeTurnTrace=$,exports.turnWebLooks=j,exports.extractTurnDeath=E,exports.extractTurnCutOff=A,exports.extractTurnStopped=C,exports.noteCutOffRelayed=_,exports.cutOffAlreadyRelayed=function(e){return!!e&&ve.has(e)},exports.resumeStalledWatchedTurn=async function(e,t,r){const s=le.get(e)
5729
5746
  if(!s)return null
5730
5747
  for(let e=0;e<50&&s.checking;e++)await new Promise(e=>setTimeout(e,100))
5731
- if(s.checking||ce.get(e)!==s)return!ce.has(e)&&null
5748
+ if(s.checking||le.get(e)!==s)return!le.has(e)&&null
5732
5749
  s.checking=!0
5733
5750
  try{await r()
5734
5751
  const n=await T(e,s,t)
5735
5752
  return n&&s.logger.warn("inbox-chat loop: turn stopped answering; ended it and resumed it once",{sessionId:e,markerMessageId:s.exchanges[s.exchanges.length-1].markerMessageId}),n}finally{s.checking=!1}},exports.extractTurnSilence=x,exports.absorbedByReply=q
5736
- const Y=t(60833),J=t(58824),X=t(42593),Q=t(24068),Z=t(4234),ee=t(17039),te=t(81602),re=V(t(79896)),se=V(t(16928)),ne=t(36799),oe=3e5,ie=9e5,ae=8e3,ce=new Map
5737
- let le=null
5738
- const de=new Map,ue=6e4,he=/^(let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|one (?:moment|sec)|checking|looking|on it|sure[,.!]?\s|okay[,.!]?\s|ok[,.!]?\s|first[, ]|hang on|give me)/i,pe=/^(ok(?:ay)?|done|sure|noted|got it|all set|that's it|right)[.!]?$/i,fe=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]/u,me=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]+/gu,ge=/^(?:(?:now|next|then|first|so)[,:]?\s+)?(?:let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|i['\u2019]m (?:now )?(?:checking|looking|nudging|filing|starting|opening|creating|asking|waking|reading|pulling|fetching|comparing|running|updating|writing|going|registering|drafting))\b/i,we=/get-info|:list|:search|list-areas|read_files|read_file|search|grep|query_history|request_diff|environment_list|environment_get|testing_process_list|check-status|list-sessions|read-messages|results:fetch|commands:list|read_site|:brief|:get\b/i,ye=new Set},81602(e,exports){"use strict"
5753
+ const Y=t(79414),J=t(60833),X=t(58824),Q=t(42593),Z=t(24068),ee=t(4234),te=t(17039),re=t(81602),se=V(t(79896)),ne=V(t(16928)),oe=t(36799),ie=3e5,ae=9e5,ce=8e3,le=new Map
5754
+ let de=null
5755
+ const ue=new Map,he=6e4,pe=/^(let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|one (?:moment|sec)|checking|looking|on it|sure[,.!]?\s|okay[,.!]?\s|ok[,.!]?\s|first[, ]|hang on|give me)/i,fe=/^(ok(?:ay)?|done|sure|noted|got it|all set|that's it|right)[.!]?$/i,me=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]/u,ge=/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Thai}]+/gu,we=/^(?:(?:now|next|then|first|so)[,:]?\s+)?(?:let me|i(?:['\u2019]ll| will| am going to|['\u2019]m going to)|i['\u2019]m (?:now )?(?:checking|looking|nudging|filing|starting|opening|creating|asking|waking|reading|pulling|fetching|comparing|running|updating|writing|going|registering|drafting))\b/i,ye=/get-info|:list|:search|list-areas|read_files|read_file|search|grep|query_history|request_diff|environment_list|environment_get|testing_process_list|check-status|list-sessions|read-messages|results:fetch|commands:list|read_site|:brief|:get\b/i,ve=new Set},81602(e,exports){"use strict"
5739
5756
  exports.Cr=exports.UT=void 0,exports.wakeThatTellsUser=function(e,t){const r="string"==typeof e?exports.Cr.get(e):void 0
5740
5757
  return r?r.only&&r.only!==t?null:r:null},exports.wakeLineIsForUser=function(e,t){return"when-changed"!==exports.Cr.get(e)?.says||t},exports.holdsDuringUserTurn=function(e,t){const r=t?.systemNoteRequestId
5741
- return!("string"!=typeof r||!r)||"string"==typeof e&&exports.UT.has(e)},exports.UT=new Map([["request-registered","the [info] notice that a request was dispatched; the Alchemist is told it is informed, never waited on"],["request-info","an [info] notice about a request; informational"],["start-note","a request started; informational"],["request-canceled-notice","a request was canceled; informational"],["setup-settled","a setup settled minutes after it was started; it wakes the session to carry on, which a turn of its own does"],["wait-settled","a declared wait on another request settled; declared waits outlive turns"],["effect-failure-notice","a system effect failed; a report, not an answer to anything asked in the turn"],["worker-report","a check run's report to the session that owns it; runs outlive turns"],["monitor-wake","a monitor the session set fired; monitors wake idle sessions, not running turns"],["schedule-tick","a schedule fired; standing goals are never part of a user's turn"],["plan-complete","a plan finished building its requests; a hand-off, not an answer"],["main-moved","the main branch moved into lines a worker edits; advisory, nothing is blocked"],["merge-conflict","a sign-off merge conflicted and the branch comes back to its worker; work for a turn of its own"],["sitting-close","a sitting ended (the user went quiet, left the page or pressed Complete); the worker completes the round as a turn of its own, after the change it is on"],["keep-warm","a keep-warm tick; the runner skips busy sessions, and one arriving mid-turn has nothing to keep warm"]]),exports.Cr=new Map([["setup-settled",{says:"always",why:"a setup the session started or was asked about has ended; it carries on and says what it did, or how it ended"}],["monitor-wake",{says:"always",only:"alchemist",carries:"What you write last in this turn is shown to the user as your message in Steer. When there is nothing they need to hear, write nothing.",why:"a reminder or a watch the Alchemist set has fired; what it found is what the user asked to be told"}],["schedule-tick",{says:"when-changed",carries:"When you change something in this turn (a request filed, a document edited), what you write last is shown to the user as your message in Steer. When you change nothing, nothing you write is shown.",why:"a schedule fired; a firing that files a request says which and why, and a firing that finds nothing to do says nothing"}]])},76616(e,exports,t){"use strict"
5758
+ return!("string"!=typeof r||!r)||"string"==typeof e&&exports.UT.has(e)},exports.UT=new Map([["request-registered","the [info] notice that a request was dispatched; the Alchemist is told it is informed, never waited on"],["request-info","an [info] notice about a request; informational"],["start-note","a request started; informational"],["request-canceled-notice","a request was canceled; informational"],["setup-settled","a setup settled minutes after it was started; it wakes the session to carry on, which a turn of its own does"],["wait-settled","a declared wait on another request settled; declared waits outlive turns"],["effect-failure-notice","a system effect failed; a report, not an answer to anything asked in the turn"],["worker-report","a check run's report to the session that owns it; runs outlive turns"],["monitor-wake","a monitor the session set fired; monitors wake idle sessions, not running turns"],["schedule-tick","a schedule fired; standing goals are never part of a user's turn"],["unit-start","a message on a completed request's page, handed to its worker; where that worker still owes the user a reply, this starts when that turn has ended"],["plan-complete","a plan finished building its requests; a hand-off, not an answer"],["main-moved","the main branch moved into lines a worker edits; advisory, nothing is blocked"],["merge-conflict","a sign-off merge conflicted and the branch comes back to its worker; work for a turn of its own"],["sitting-close","a sitting ended (the user went quiet, left the page or pressed Complete); the worker completes the round as a turn of its own, after the change it is on"],["keep-warm","a keep-warm tick; the runner skips busy sessions, and one arriving mid-turn has nothing to keep warm"]]),exports.Cr=new Map([["setup-settled",{says:"always",why:"a setup the session started or was asked about has ended; it carries on and says what it did, or how it ended"}],["monitor-wake",{says:"always",only:"alchemist",carries:"What you write last in this turn is shown to the user as your message in Steer. When there is nothing they need to hear, write nothing.",why:"a reminder or a watch the Alchemist set has fired; what it found is what the user asked to be told"}],["schedule-tick",{says:"when-changed",carries:"When you change something in this turn (a request filed, a document edited), what you write last is shown to the user as your message in Steer. When you change nothing, nothing you write is shown.",why:"a schedule fired; a firing that files a request says which and why, and a firing that finds nothing to do says nothing"}],["unit-start",{says:"always",why:"the user asked on a completed request's page; an answer is the turn's closing text, and a change is completed as any request is"}]])},76616(e,exports,t){"use strict"
5742
5759
  function r(e){const t=(0,s.assembleTurnEvidence)(e)
5743
5760
  if("fatal-error"===t.kind){const e=t.refusal
5744
5761
  return{kind:t.kind,messageId:t.messageId,refusal:`${e?.model??"its model"} refused its turn ("${e?.message??t.errorName??"unknown error"}")`}}return"quota"===t.kind?{kind:t.kind,messageId:t.messageId,refusal:`the ${t.quota?.providerID??"provider"} usage limit was reached`}:"retryable-error"===t.kind&&"MessageAbortedError"!==t.errorName?{kind:t.kind,messageId:t.messageId}:null}exports.lastTurnDeath=r,exports.endSupersededPlanners=async function(e,t,s){const{apiClient:i,projectId:a,serverUrl:c,logger:l}=e,d=e.fetchImpl??fetch,u=[]
@@ -5962,14 +5979,15 @@ function t(e,t){if((t.parts??[]).some(e=>"tool"===e.type&&"error"===e.state?.sta
5962
5979
  if("MessageAbortedError"===e?.name)return{by:"stopped"}
5963
5980
  if("APIError"===e?.name){const t=e.data?.statusCode
5964
5981
  return{by:"provider",..."number"==typeof t?{status:t}:{}}}return{by:"unknown"}}function r(e,t){const r=e?.data??{},s="number"==typeof r.statusCode?r.statusCode:void 0,n=("string"==typeof r.message&&r.message.trim()?r.message.trim():String(e?.name??"an unknown error")).replace(/\s+/g," ").slice(0,300),o=t?.providerID||("string"==typeof r.providerID?r.providerID:void 0),i=t?.modelID||("string"==typeof r.modelID?r.modelID:void 0)
5965
- return{message:n,...o&&i?{model:`${o}/${i}`}:{},...o?{providerID:o}:{},scope:"ProviderAuthError"===e?.name||401===s||403===s?"provider":"model"}}function s(e){return(e.parts??[]).filter(e=>"text"===e.type&&"string"==typeof e.text).map(e=>String(e.text)).join(" ").trim()}function n(e){return(e.parts??[]).some(e=>"tool"===e.type||"text"===e.type&&String(e.text??"").trim().length>0)}function o(e){if(!e||!e.name)return!1
5982
+ return{message:n,...o&&i?{model:`${o}/${i}`}:{},...o?{providerID:o}:{},scope:"ProviderAuthError"===e?.name||401===s||403===s?"provider":"model",..."repeated-tool-failure"===r.metadata?.alchemyStop?{looped:!0}:{}}}function s(e){return(e.parts??[]).filter(e=>"text"===e.type&&"string"==typeof e.text).map(e=>String(e.text)).join(" ").trim()}function n(e){return(e.parts??[]).some(e=>"tool"===e.type||"text"===e.type&&String(e.text??"").trim().length>0)}function o(e){if(!e||!e.name)return!1
5966
5983
  if("ProviderQuotaError"===e.name)return!0
5967
5984
  const t=e.data??{},r="number"==typeof t.statusCode?t.statusCode:void 0,s=`${"string"==typeof t.message?t.message:""} ${"string"==typeof t.responseBody?t.responseBody:""}`
5968
- return"ProviderAuthError"===e.name||"ProviderModelNotFoundError"===e.name||401===r||403===r||!1===t.isRetryable||/revoked|authentication_error|invalid[_ ]api[_ ]key|unauthorized|model ?not ?found/i.test(s)}function i(e){return"user"===e.info.role&&!!e.parts?.length&&e.parts.every(e=>!0===e.metadata?.alchemy?.noReply)}exports.yX=void 0,exports.turnRefusal=r,exports.refusalLifted=function(e,t){if(!e||!t||!t.includes("/"))return null
5985
+ return"ProviderAuthError"===e.name||"ProviderModelNotFoundError"===e.name||401===r||403===r||!1===t.isRetryable||/revoked|authentication_error|invalid[_ ]api[_ ]key|unauthorized|model ?not ?found/i.test(s)}function i(e){return"user"===e.info.role&&!!e.parts?.length&&e.parts.every(e=>!0===e.metadata?.alchemy?.noReply)}exports.yX=exports.i0=void 0,exports.turnRefusal=r,exports.refusalLifted=function(e,t){if(!e||!t||!t.includes("/"))return null
5969
5986
  const r=t.trim().toLowerCase()
5970
5987
  if("provider"===e.scope){const t=(e.providerID??e.model?.split("/")[0]??"").trim().toLowerCase()
5971
5988
  return t&&r.split("/")[0]!==t?"model-switched":null}return e.model&&r!==e.model.trim().toLowerCase()?"model-switched":null},exports.quotaBlockLifted=function(e,t,r){if(!e)return null
5972
5989
  if(e.resetsAt&&1e3*e.resetsAt<r)return"window-passed"
5990
+ if(!e.resetsAt&&e.observedAt&&r-e.observedAt>exports.i0)return"window-passed"
5973
5991
  const s=(t??"").includes("/")?t.split("/")[0].trim().toLowerCase():"",n=(e.providerID??"").trim().toLowerCase()
5974
5992
  return s&&n&&s!==n?"provider-switched":null},exports.isFatalTurnError=o,exports.isQuietDelivery=i,exports.assembleTurnEvidence=function(e,a){let d=e.length
5975
5993
  for(;d>0&&i(e[d-1]);)d--
@@ -5981,7 +5999,7 @@ if("assistant"!==h.info.role)return{kind:"no-messages"}
5981
5999
  const f=h.info,m=f.error
5982
6000
  if(!f.time.completed)return p(h)?{kind:"generating",messageId:f.id}:{kind:"unfinished",messageId:f.id,finish:f.finish}
5983
6001
  if(m&&"object"==typeof m&&m.name){if("ProviderQuotaError"===m.name){const e=m.data??{}
5984
- return{kind:"quota",messageId:f.id,errorName:m.name,quota:{providerID:"string"==typeof e.providerID&&e.providerID?e.providerID:"the provider",resetsAt:"number"==typeof e.resetsAt?e.resetsAt:void 0,planType:"string"==typeof e.planType?e.planType:void 0,message:"string"==typeof e.message&&e.message?e.message:"usage limit reached"}}}return o(m)?{kind:"fatal-error",messageId:f.id,errorName:m.name,finish:f.finish,refusal:r(m,f)}:{kind:"retryable-error",messageId:f.id,errorName:m.name,finish:f.finish,cause:t(m,h)}}const g=f.finish,w=n(h)
6002
+ return{kind:"quota",messageId:f.id,errorName:m.name,quota:{providerID:"string"==typeof e.providerID&&e.providerID?e.providerID:"the provider",resetsAt:"number"==typeof e.resetsAt?e.resetsAt:void 0,observedAt:f.time.completed,planType:"string"==typeof e.planType?e.planType:void 0,message:"string"==typeof e.message&&e.message?e.message:"usage limit reached"}}}return o(m)?{kind:"fatal-error",messageId:f.id,errorName:m.name,finish:f.finish,refusal:r(m,f)}:{kind:"retryable-error",messageId:f.id,errorName:m.name,finish:f.finish,cause:t(m,h)}}const g=f.finish,w=n(h)
5985
6003
  if(g&&l.has(g)&&!w)return{kind:"dead-end",messageId:f.id,finish:g,providerID:f.providerID,modelID:f.modelID}
5986
6004
  if("tool-calls"===g)return{kind:"interrupted-toolchain",messageId:f.id,finish:g}
5987
6005
  const y=s(h)
@@ -6001,7 +6019,7 @@ case"unfinished":case"interrupted-toolchain":case"lost-prompt":return"hub"===r?{
6001
6019
  case"clean-text":return"owned"===r?{act:"unconfuse-then-report"}:{act:"none"}
6002
6020
  default:return{act:"none"}}},exports.retryLooksLikeRefusal=function(e){return!(!e||"retry"!==e.type)&&!((e.attempt??0)<exports.yX)&&d.test(String(e.message??""))}
6003
6021
  const a=/MCP error -32000|\bConnection closed\b|\bNot connected\b/i,c=300,l=new Set(["content-filter","length","error","other","unknown"])
6004
- exports.yX=3
6022
+ exports.i0=6e5,exports.yX=3
6005
6023
  const d=/\b(exceeded|exhausted|quota|out of (credit|credits)|insufficient|subscribe|upgrade your plan|billing)\b/i},90473(e,exports,t){"use strict"
6006
6024
  function r(e,t){const r=(0,a.readActionServerFile)(e)
6007
6025
  return r.port===t&&r.secret?{[a.LOCAL_SECRET_HEADER]:r.secret}:{}}async function s(e){const{port:t,projectPath:s,logger:n,pollMs:o=500,pollAttempts:i=16}=e,a=`http://127.0.0.1:${t}`
@@ -6716,7 +6734,7 @@ let r=!1
6716
6734
  for(;Date.now()<t;){try{const e=await fetch(`${f}/session/${o}/message?limit=5`,{signal:AbortSignal.timeout(4e3)})
6717
6735
  if(e.ok){const t=await e.json().catch(()=>[])
6718
6736
  if(Array.isArray(t)&&t.some(e=>(e?.info?.id??e?.id)===p)){r=!0
6719
- break}}}catch{}await new Promise(e=>setTimeout(e,1500))}return r?{success:!0}:{success:!1,error:`delivery_unverified: prompt_async accepted but message ${p} never appeared in session ${o}`}}catch(e){return{success:!1,error:(0,r.getErrorMessage)(e)}}}
6737
+ break}}}catch{}await new Promise(e=>setTimeout(e,1500))}return r?{success:!0,messageId:p}:{success:!1,error:`delivery_unverified: prompt_async accepted but message ${p} never appeared in session ${o}`}}catch(e){return{success:!1,error:(0,r.getErrorMessage)(e)}}}
6720
6738
  const r=t(58824),s=t(72485),n=t(45687)},2424(e,exports,t){"use strict"
6721
6739
  exports.ensureRequestWorktree=async function(e,t,a){const c=`requests/${t}`,l=(0,o.requestWorktreesDir)(e),d=(0,s.join)(l,t)
6722
6740
  if((0,r.existsSync)(d))return void a.info("Worktree already exists on disk, skipping pre-spawn creation",{requestId:t,worktreePath:d})
@@ -6727,7 +6745,7 @@ else{const r=await(0,i.resolveDefaultBranch)(e)
6727
6745
  await u.raw(["worktree","add","-b",c,d,r]),a.info("Pre-spawn: created worktree with new branch from latest main",{requestId:t,branchName:c,mainBranch:r,worktreePath:d})}}catch(e){const r=e instanceof Error?e.message:String(e)
6728
6746
  a.warn("Pre-spawn: failed to create worktree (MCP process will retry)",{requestId:t,error:r})}}
6729
6747
  const r=t(79896),s=t(16928),n=t(83807),o=t(7202),i=t(52232)},35419(e,exports,t){"use strict"
6730
- function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){return{ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"alchemy",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
6748
+ function r(e){return(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode-config.json")}function s(e){return(0,o.join)((0,n.homedir)(),i.ALCHEMY_DIR_NAME,e?"opencode-dev":"opencode")}exports.t=void 0,exports.generatedOpencodeConfigPath=r,exports.getGlobalConfigDir=s,exports.getOpencodeSpawnEnv=function(e,t){return{ANTHROPIC_CLI_VERSION:process.env.ANTHROPIC_CLI_VERSION??exports.t,OPENCODE_CLIENT:"cli",ALCHEMYLAB:"1",OPENCODE_ENABLE_EXA:"1",OPENCODE_DATA_DIR:(0,o.join)(e,i.ALCHEMY_DIR_NAME,"opencode"),OPENCODE_CONFIG_DIR:s(t),ALCHEMY_PROTECTED_ROOT:e,OPENCODE_CONFIG:r(e),OPENCODE_DISABLE_PROJECT_CONFIG:"1",OPENCODE_DISABLE_CLAUDE_CODE:"1",OPENCODE_DISABLE_EXTERNAL_SKILLS:"1"}}
6731
6749
  const n=t(70857),o=t(16928),i=t(14139)
6732
6750
  exports.t="2.1.280"},39068(e,exports,t){"use strict"
6733
6751
  function r(){const e=process.env.ALCHEMY_OPENCODE_DIR
@@ -6844,13 +6862,13 @@ else if("failed"===u.status||!u.hasHistory)throw function(e){const t=(0,c.join)(
6844
6862
  try{(0,i.readdirSync)(t).every(e=>"server.log"===e)&&(0,i.rmSync)(t,{recursive:!0,force:!0})}catch{}}(e),new k([`\u26a0\ufe0f ${u.message??"This folder cannot be used as a project yet."}`," Nothing was registered and no servers were started."])
6845
6863
  const h=(0,c.join)(e,g.ALCHEMY_DIR_NAME)
6846
6864
  let p
6847
- await(0,a.mkdir)(h,{recursive:!0}),o?(d(`\u{1f4cb} Registering project "${(0,c.basename)(e)||"Local Project"}"...`),p=await n(t,o,e,l),d(p?`\u2705 Project registered: ${p}`:s(r()))):d("\u26a0\ufe0f No auth token, skipping project registration")
6865
+ await(0,a.mkdir)(h,{recursive:!0}),o?(d(`\u{1f4cb} Adding "${(0,c.basename)(e)||"this folder"}" to your AlchemyLab account...`),p=await n(t,o,e,l),d(p?"\u2705 Project added.":s(r()))):d("\u26a0\ufe0f No auth token, skipping project registration")
6848
6866
  const f=(0,c.join)(h,"project.json")
6849
6867
  return(0,i.writeFileSync)(f,JSON.stringify({id:p||null,name:(0,c.basename)(e)||"Local Project",status:"active",registeredRemotely:!!p,createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:e,alchemyPath:h}},null,2),"utf-8"),d(""),!0}(t,o,d,u,h),m=(0,b.identityPath)(t)
6850
6868
  let S
6851
6869
  try{const e=JSON.parse((0,i.readFileSync)(m,"utf-8"))
6852
6870
  if(S=e.id||void 0,!1===e.registeredRemotely&&d){const a=await n(o,d,t,u,"cli-init-retry")
6853
- a?(e.id=a,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,i.writeFileSync)(m,JSON.stringify(e,null,2),"utf-8"),S=a,h(`\u2705 Project registered: ${a}`)):h(s(r()))}}catch{u.warn("Could not read project.json")}const I=async(e,t,r)=>{const s=await fetch(`${o}${t}`,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${d}`,"X-Client-Type":"alchemy-local-service",...(0,v.machineCallerHeaders)(o)},...void 0===r?{}:{body:JSON.stringify(r)}})
6871
+ a?(e.id=a,e.registeredRemotely=!0,e.updatedAt=(new Date).toISOString(),(0,i.writeFileSync)(m,JSON.stringify(e,null,2),"utf-8"),S=a,h("\u2705 Project added.")):h(s(r()))}}catch{u.warn("Could not read project.json")}const I=async(e,t,r)=>{const s=await fetch(`${o}${t}`,{method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${d}`,"X-Client-Type":"alchemy-local-service",...(0,v.machineCallerHeaders)(o)},...void 0===r?{}:{body:JSON.stringify(r)}})
6854
6872
  if(!s.ok)throw new Error(`HTTP ${s.status}`)
6855
6873
  return"GET"===e?s.json():void 0},$=d&&S?async e=>{const t=await I("GET",`/api/projects/${S}/requests/${e}`)
6856
6874
  return(t?.data??t)?.type}:void 0,j=await(0,y.defaultBranchHasCode)(t,$).catch(()=>!1)
@@ -6879,7 +6897,8 @@ if(!Number.isFinite(r)||r<=0)return 0
6879
6897
  let s=""
6880
6898
  try{s=new URL(e).hostname}catch{return 0}return"localhost"===s||"127.0.0.1"===s||"::1"===s?Math.min(r,2e3):0}function s(e,t,r){if(r&&"stores"===r.op)try{return{ok:!0,result:{projects:e.projects()}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"drop"===r.op&&"string"==typeof r.projectId)try{return{ok:!0,result:{removed:e.drop(r.projectId)}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"put"===r.op&&"string"==typeof r.projectId)try{const t=r.args??{}
6881
6899
  return{ok:!0,result:{ids:e.put(r.projectId,Array.isArray(t.items)?t.items:[])}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"has"===r.op&&"string"==typeof r.projectId)try{const t=r.args??{}
6882
- return{ok:!0,result:{ids:e.hasMany(r.projectId,Array.isArray(t.ids)?t.ids.map(String):[])}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(!r||"string"!=typeof r.projectId||!t.has(r.projectId)&&!e.has(r.projectId))return{ok:!1,error:"this computer keeps no words for that project"}
6900
+ return{ok:!0,result:{ids:e.hasMany(r.projectId,Array.isArray(t.ids)?t.ids.map(String):[])}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(r&&"getAcross"===r.op)try{const s=r.args?.projects&&"object"==typeof r.args.projects?r.args.projects:{},n={}
6901
+ for(const[r,o]of Object.entries(s))if(t.has(r)||e.has(r))try{n[r]=e.getMany(r,Array.isArray(o)?o.map(String):[])}catch{}return{ok:!0,result:{items:n}}}catch(e){return{ok:!1,error:e instanceof Error?e.message.slice(0,200):"the content store failed"}}if(!r||"string"!=typeof r.projectId||!t.has(r.projectId)&&!e.has(r.projectId))return{ok:!1,error:"this computer keeps no words for that project"}
6883
6902
  try{const t=r.args??{}
6884
6903
  switch(r.op){case"put":return{ok:!0,result:{ids:e.put(r.projectId,Array.isArray(t.items)?t.items:[])}}
6885
6904
  case"getMany":return{ok:!0,result:{items:e.getMany(r.projectId,Array.isArray(t.ids)?t.ids.map(String):[])}}
@@ -7386,132 +7405,138 @@ if(!o.ok)throw new Error(`HTTP ${o.status}`)
7386
7405
  const i=await o.json(),a=Array.isArray(i?.data)?i.data:Array.isArray(i)?i:[]
7387
7406
  for(const e of a){const t=e?.metadata?.schedule
7388
7407
  t&&"object"==typeof t&&!0===t.enabled&&r.push(t)}if(a.length<100)break}return r}}},86090(e,exports,t){"use strict"
7389
- function r(){return b.default.resolve(process.argv[1])}function s(e){const t=(0,A.servicePaths)(e),r=(0,C.readServiceConfig)(t)
7408
+ function r(){return S.default.resolve(process.argv[1])}function s(e){const t=(0,_.servicePaths)(e),r=(0,T.readServiceConfig)(t)
7390
7409
  return r?{paths:t,cfg:r}:void 0}async function n(e,t){if(!process.stdin.isTTY)return t
7391
- const r=k.default.createInterface({input:process.stdin,output:process.stdout}),s=await new Promise(s=>r.question(`${e} ${t?"[Y/n]":"[y/N]"} `,s))
7410
+ const r=I.default.createInterface({input:process.stdin,output:process.stdout}),s=await new Promise(s=>r.question(`${e} ${t?"[Y/n]":"[y/N]"} `,s))
7392
7411
  r.close()
7393
7412
  const n=s.trim().toLowerCase()
7394
7413
  return""===n?t:"y"===n||"yes"===n}async function o(e,t){const s={}
7395
7414
  process.env.ALCHEMY_LAB_HOME&&(s.ALCHEMY_LAB_HOME=process.env.ALCHEMY_LAB_HOME)
7396
- try{const n=await(0,U.installLoginItem)({nodePath:process.execPath,cliPath:r(),remoteServerUrl:t.remoteServerUrl,envPath:process.env.PATH??"/usr/bin:/bin",env:s,logPath:e.log})
7397
- console.log(n.loaded?`AlchemyLab will start every time this computer starts (${n.file}).`:`Written to ${n.file}, but it could not be loaded now: ${n.detail??"no reason given"}. It loads the next time this computer starts.`),"darwin"===process.platform&&console.log('Started with the computer, macOS may ask once whether "node" may use a project in Documents, Desktop or Downloads. Answer it at the computer.')}catch(e){console.error(`Could not set AlchemyLab to start at login: ${e instanceof Error?e.message:String(e)}`)}}async function i(e,t){await(0,x.isServiceRunning)(e)||(console.log("Starting the AlchemyLab service..."),await(0,x.startServiceInBackground)(e,t,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${e.log}`),process.exit(1)))}function a(e){return e.state}async function c(e){let t,r
7398
- try{r=await(0,x.request)(e,"ping"),t=await(0,x.request)(e,"status")}catch{return void console.log("AlchemyLab service: not running. `alchemy service` starts it.")}console.log(`AlchemyLab service: running (pid ${r.pid}) for ${r.remote}`)
7415
+ try{const n=await(0,H.installLoginItem)({nodePath:process.execPath,cliPath:r(),remoteServerUrl:t.remoteServerUrl,envPath:process.env.PATH??"/usr/bin:/bin",env:s,logPath:e.log})
7416
+ console.log(n.loaded?`AlchemyLab will start every time this computer starts (${n.file}).`:`Written to ${n.file}, but it could not be loaded now: ${n.detail??"no reason given"}. It loads the next time this computer starts.`),"darwin"===process.platform&&console.log('Started with the computer, macOS may ask once whether "node" may use a project in Documents, Desktop or Downloads. Answer it at the computer.')}catch(e){console.error(`Could not set AlchemyLab to start at login: ${e instanceof Error?e.message:String(e)}`)}}async function i(e,t){await(0,R.isServiceRunning)(e)||(console.log("Starting the AlchemyLab service..."),await(0,R.startServiceInBackground)(e,t,r())||(console.error(a(e.log)),process.exit(1)))}function a(e){const t=c(e)
7417
+ return`\u274c The AlchemyLab service did not start${t?`: ${t}`:"."}\n Its log: ${e}`}function c(e){let t=""
7418
+ try{const r=k.default.openSync(e,"r")
7419
+ try{const e=k.default.fstatSync(r).size,s=Math.min(e,16384),n=Buffer.alloc(s)
7420
+ k.default.readSync(r,n,0,s,e-s),t=n.toString("utf8")}finally{k.default.closeSync(r)}}catch{return""}const r=t.split("\n").map(e=>e.trim()).filter(e=>e&&!e.startsWith("at "))
7421
+ for(let e=r.length-1;e>=0;e--){const t=/Error:\s*(.+)$/.exec(r[e])
7422
+ if(t)return t[1].replace(/^Error:\s*/,"").slice(0,300)}return""}function l(e){return e.state}async function d(e){let t,r
7423
+ try{r=await(0,R.request)(e,"ping"),t=await(0,R.request)(e,"status")}catch{return void console.log("AlchemyLab service: not running. `alchemy service` starts it.")}console.log(`AlchemyLab service: running (pid ${r.pid}) for ${r.remote}`)
7399
7424
  const s=t.projects??[]
7400
7425
  if(0===s.length)return void console.log(" No projects on this computer yet. Run `alchemy` in a folder to add it.")
7401
7426
  const n=Math.max(8,...s.map(e=>e.name.length))+2
7402
7427
  console.log(` ${"PROJECT".padEnd(n)}${"STATE".padEnd(10)}${"PORT".padEnd(7)}FOLDER`)
7403
- for(const e of s)console.log(` ${e.name.padEnd(n)}${a(e).padEnd(10)}${String(e.port??"").padEnd(7)}${e.path}`),e.reason&&console.log(` ${"".padEnd(n)}${e.reason}`)
7404
- s.some(e=>"parked"===e.state||"stopped"===e.state)&&console.log(" A parked or stopped project connects when you open it in the app.")}function l(e){console.error(`\u274c ${e.reason??"It did not start."}`)
7405
- for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function d(e){const t=async(t,r={},s=12e4)=>{const n=await(0,x.request)(e,"new-computer",{op:t,...r},s)
7428
+ for(const e of s)console.log(` ${e.name.padEnd(n)}${l(e).padEnd(10)}${String(e.port??"").padEnd(7)}${e.path}`),e.reason&&console.log(` ${"".padEnd(n)}${e.reason}`)
7429
+ s.some(e=>"parked"===e.state||"stopped"===e.state)&&console.log(" A parked or stopped project connects when you open it in the app.")}function u(e){console.error(`\u274c ${e.reason??"It did not start."}`)
7430
+ for(const t of(e.lines??[]).slice(-8))console.error(` ${t}`)}function h(e){const t=async(t,r={},s=12e4)=>{const n=await(0,R.request)(e,"new-computer",{op:t,...r},s)
7406
7431
  if(!n.ok)throw new Error(n.reason??"The AlchemyLab service did not do it.")
7407
7432
  return n}
7408
7433
  return{candidates:async()=>{const e=await t("candidates",{},4e4)
7409
7434
  return{candidates:e.candidates??[],here:e.here??[]}},backups:async(e,r)=>{const s=await t("backups",{folder:e,from:r})
7410
- return{folder:s.folder,runs:s.runs??[],others:Number(s.others)||0}},restore:async(e,r)=>(await t("restore",{folder:e,run:r},18e5)).results??[],preview:async e=>(await t("preview",{from:e},3e5)).preview,move:async(e,r)=>(await t("move",{from:e,withoutWords:r},9e5)).result,forget:async e=>{await t("forget",{from:e},4e4)}}}function u(){const e=k.default.createInterface({input:process.stdin,output:process.stdout})
7411
- return{say:(e="")=>console.log(e),ask:t=>new Promise(r=>e.question(`${t} `,e=>r(e.trim()))),close:()=>e.close()}}var h=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
7412
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.serviceSetUp=s,exports.runService=async function(e,t){if(t.includes("--foreground"))return async function(e){const t=(0,A.servicePaths)(e);(0,A.ensurePrivateDir)(t.dir)
7413
- const s=(0,C.readServiceConfig)(t)
7435
+ return{folder:s.folder,runs:s.runs??[],others:Number(s.others)||0}},restore:async(e,r)=>(await t("restore",{folder:e,run:r},18e5)).results??[],preview:async e=>(await t("preview",{from:e},3e5)).preview,move:async(e,r)=>(await t("move",{from:e,withoutWords:r},9e5)).result,forget:async e=>{await t("forget",{from:e},4e4)}}}function p(){const e=I.default.createInterface({input:process.stdin,output:process.stdout})
7436
+ return{say:(e="")=>console.log(e),ask:t=>new Promise(r=>e.question(`${t} `,e=>r(e.trim()))),close:()=>e.close()}}var f=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
7437
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.serviceSetUp=s,exports.runService=async function(e,t,s={}){if(t.includes("--foreground"))return async function(e){const t=(0,_.servicePaths)(e);(0,_.ensurePrivateDir)(t.dir)
7438
+ const s=(0,T.readServiceConfig)(t)
7414
7439
  s||(console.error("The AlchemyLab service is not set up for this server yet. Run `alchemy service` in a terminal first."),process.exit(1))
7415
- const n=(0,S.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new T.Supervisor({paths:t,config:s,logger:n,childModule:r(),childArgs:e=>["project-run","--project",e.path,"--expect",`${e.dev}:${e.ino}`],childEnv:()=>({...(0,C.childEnv)((0,C.readServiceConfig)(t)??s),ALCHEMY_MACHINE_ID:(0,p.machineIdentity)(t).id}),approvalEnv:{installDir:H},version:(0,j.ownVersion)()??void 0,onChange:()=>u?.noteChanged(),cap:()=>(0,C.readServiceConfig)(t)?.cap??(0,T.defaultCap)(),machineId:(0,p.machineIdentity)(t).id,confirmIdentity:async()=>(await c).register(),signedOut:()=>!0===u?.signedOut,startAsNew:(0,q.startAsNewProject)({remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token}),connected:()=>i()}),i=async()=>{k.forgetCachedSignIn()
7440
+ const n=(0,$.createLogger)({serviceName:"alchemy-service",console:!1,logToFile:!0,logFilePath:t.log,baseDir:t.dir}),o=new M.Supervisor({paths:t,config:s,logger:n,childModule:r(),childArgs:e=>["project-run","--project",e.path,"--expect",`${e.dev}:${e.ino}`],childEnv:()=>({...(0,T.childEnv)((0,T.readServiceConfig)(t)??s),ALCHEMY_MACHINE_ID:(0,m.machineIdentity)(t).id}),approvalEnv:{installDir:G},version:(0,A.ownVersion)()??void 0,onChange:()=>u?.noteChanged(),cap:()=>(0,T.readServiceConfig)(t)?.cap??(0,M.defaultCap)(),machineId:(0,m.machineIdentity)(t).id,confirmIdentity:async()=>(await c).register(),signedOut:()=>!0===u?.signedOut,startAsNew:(0,F.startAsNewProject)({remoteServerUrl:s.remoteServerUrl,machineId:(0,m.machineIdentity)(t).id,getToken:async()=>(await v.getToken())?.token}),connected:()=>i()}),i=async()=>{v.forgetCachedSignIn()
7416
7441
  const e=(e,t)=>Promise.race([e,new Promise(e=>{const r=setTimeout(()=>e(!1),t)
7417
7442
  r.unref?.()})]),t=await e(u?u.register(1e4):Promise.resolve(!1),12e3),r=await e(h?h.connectNow():Promise.resolve(!1),12e3)
7418
7443
  return!0===t&&!0===r}
7419
7444
  let a=()=>{}
7420
7445
  const c=new Promise(e=>{a=e})
7421
- let l,d,u,h,g,w
7422
- const v=async(e=0)=>{l?.(),await(u?.stop().catch(()=>{})),h?.stop(),w?.stop(),g?.stop(),d?.close(),await o.stop(),process.exit(e)}
7423
- process.on("SIGTERM",()=>{v(0)}),process.on("SIGINT",()=>{v(0)})
7424
- try{await o.start(()=>{v(0)})}catch(e){throw e instanceof T.AlreadyRunningError&&(console.error(e.message),process.exit(0)),e}const k=new I.TokenManager(n,t.dir,{remoteServerUrl:s.remoteServerUrl,allowInteractiveAuth:!1})
7425
- await k.initialize(),l=(0,M.startSignInWatchdog)(k,e=>n.info(e)),u=new D.MachineLink({paths:t,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,projects:()=>o.status(),wake:(e,t)=>o.wake(e,t),archived:e=>o.setArchived(e),answered:(0,q.onListAnswered)({supervisor:o,remoteServerUrl:s.remoteServerUrl,machineId:(0,p.machineIdentity)(t).id,getToken:async()=>(await k.getToken())?.token,logger:n}),version:(0,j.ownVersion)()??void 0,create:e=>{R.create(e,!1).then(t=>n.info("A folder asked for from another device",{name:e,ok:t.ok,reason:t.reason}))},logger:n}),a(u),u.start(),h=new f.ContentLink({paths:t,remoteServerUrl:s.remoteServerUrl,privateData:!0===(0,C.readServiceConfig)(t)?.privateData,getToken:async()=>(await k.getToken())?.token,projectIds:()=>o.status().map(e=>e.projectId).filter(e=>"string"==typeof e),logger:n}),h.start(),g=new y.WordsBackup({store:h.store,stateFile:b.default.join(t.dir,"words-backup.json"),settings:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup),privateData:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,logger:n}),g.start()
7426
- const $=async e=>{const r=h?await h.setPrivateData(e):{privateData:e,switching:!1},s=(0,C.readServiceConfig)(t)
7427
- return s&&s.privateData!==r.privateData&&(0,C.writeServiceConfig)(t,{...s,privateData:r.privateData}),n.info(r.privateData?"This install keeps its content on this computer":"This install keeps its content on the hosted service"),r}
7428
- o.handle("connect-now",async()=>({ok:await i()})),o.handle("move-words-here",async()=>{const e=await $(!0)
7446
+ let l,d,u,h,p,f
7447
+ const y=async(e=0)=>{l?.(),await(u?.stop().catch(()=>{})),h?.stop(),f?.stop(),p?.stop(),d?.close(),await o.stop(),process.exit(e)}
7448
+ process.on("SIGTERM",()=>{y(0)}),process.on("SIGINT",()=>{y(0)})
7449
+ try{await o.start(()=>{y(0)})}catch(e){throw e instanceof M.AlreadyRunningError&&(console.error(e.message),process.exit(0)),e}const v=new j.TokenManager(n,t.dir,{remoteServerUrl:s.remoteServerUrl,allowInteractiveAuth:!1})
7450
+ await v.initialize(),l=(0,P.startSignInWatchdog)(v,e=>n.info(e)),u=new N.MachineLink({paths:t,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await v.getToken())?.token,projects:()=>o.status(),wake:(e,t)=>o.wake(e,t),archived:e=>o.setArchived(e),answered:(0,F.onListAnswered)({supervisor:o,remoteServerUrl:s.remoteServerUrl,machineId:(0,m.machineIdentity)(t).id,getToken:async()=>(await v.getToken())?.token,logger:n}),version:(0,A.ownVersion)()??void 0,create:e=>{x.create(e,!1).then(t=>n.info("A folder asked for from another device",{name:e,ok:t.ok,reason:t.reason}))},logger:n}),a(u),u.start(),h=new g.ContentLink({paths:t,remoteServerUrl:s.remoteServerUrl,privateData:!0===(0,T.readServiceConfig)(t)?.privateData,getToken:async()=>(await v.getToken())?.token,projectIds:()=>o.status().map(e=>e.projectId).filter(e=>"string"==typeof e),logger:n}),h.start(),p=new b.WordsBackup({store:h.store,stateFile:S.default.join(t.dir,"words-backup.json"),settings:()=>(0,b.backupSettingsOf)((0,T.readServiceConfig)(t)?.wordsBackup),privateData:()=>h?.privateData??!0===(0,T.readServiceConfig)(t)?.privateData,logger:n}),p.start()
7451
+ const k=async e=>{const r=h?await h.setPrivateData(e):{privateData:e,switching:!1},s=(0,T.readServiceConfig)(t)
7452
+ return s&&s.privateData!==r.privateData&&(0,T.writeServiceConfig)(t,{...s,privateData:r.privateData}),n.info(r.privateData?"This install keeps its content on this computer":"This install keeps its content on the hosted service"),r}
7453
+ o.handle("connect-now",async()=>({ok:await i()})),o.handle("move-words-here",async()=>{const e=await k(!0)
7429
7454
  return{ok:!e.refused,reason:e.refused,privateData:e.privateData,switching:e.switching}})
7430
- const E=new m.NewComputer({machineId:(0,p.machineIdentity)(t).id,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,channelKey:()=>(0,f.channelKey)(t),store:h.store,backup:g,defaultFolder:()=>(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup).folder})
7431
- let _
7432
- o.handle("new-computer",e=>(0,m.newComputerCommand)(E,e)),o.handle("computer-gone",async e=>h?await h.sayGone(String(e.machineId??"")):{ok:!1,reason:"The words channel is not running."}),o.handle("pair-channel",async e=>h?await h.pairAgain(String(e.code??"")):{ok:!1,reason:"The words channel is not running."}),w=new N.ScheduleWaker({remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await k.getToken())?.token,parked:()=>o.status().filter(e=>"parked"===e.state&&e.projectId).map(e=>e.projectId),wake:e=>o.wake(e,"work"),logger:n}),w.start(),(0,P.resolveFrontendUrl)(s.remoteServerUrl).then(e=>{try{/^https?:\/\//.test(e)&&(_=new URL(e).origin)}catch{}})
7433
- const x=(0,L.nativeDialogs)(),R=new F.Adding({supervisor:o,dialogs:x,logger:n,approvalEnv:{installDir:H}}),U=e=>{const r=(0,C.readServiceConfig)(t)
7434
- if(!r)throw new Error("the service has no configuration");(0,C.writeServiceConfig)(t,{...r,wordsBackup:{...r.wordsBackup??{},...e}})}
7435
- d=await(0,O.startDoor)({supervisor:o,adding:R,wordsBackup:{view:()=>{const e=(0,y.backupSettingsOf)((0,C.readServiceConfig)(t)?.wordsBackup)
7436
- return{settings:e,state:g.state(),runs:g.ownRuns(),folderAvailable:g.folderAvailable(e.folder)}},set:e=>U(e),pick:async()=>{const e=await x.pickFolder("Choose a folder for backups of your words (one your backup tool or iCloud Drive covers)")
7437
- return e&&U({folder:e}),e},run:()=>g.run(),restore:e=>g.restore(e)},privateData:{get:()=>h?.privateData??!0===(0,C.readServiceConfig)(t)?.privateData,set:$},newComputer:{api:E,pickBackupFolder:()=>x.pickFolder('Choose the "AlchemyLab Backups" folder of your old computer (restored or copied onto this one)')},verifyMachineTicket:async e=>{const t=(await k.getToken())?.token
7455
+ const I=new w.NewComputer({machineId:(0,m.machineIdentity)(t).id,remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await v.getToken())?.token,channelKey:()=>(0,g.channelKey)(t),store:h.store,backup:p,defaultFolder:()=>(0,b.backupSettingsOf)((0,T.readServiceConfig)(t)?.wordsBackup).folder})
7456
+ let E
7457
+ o.handle("new-computer",e=>(0,w.newComputerCommand)(I,e)),o.handle("computer-gone",async e=>h?await h.sayGone(String(e.machineId??"")):{ok:!1,reason:"The words channel is not running."}),o.handle("pair-channel",async e=>h?await h.pairAgain(String(e.code??"")):{ok:!1,reason:"The words channel is not running."}),f=new L.ScheduleWaker({remoteServerUrl:s.remoteServerUrl,getToken:async()=>(await v.getToken())?.token,parked:()=>o.status().filter(e=>"parked"===e.state&&e.projectId).map(e=>e.projectId),wake:e=>o.wake(e,"work"),logger:n}),f.start(),(0,D.resolveFrontendUrl)(s.remoteServerUrl).then(e=>{try{/^https?:\/\//.test(e)&&(E=new URL(e).origin)}catch{}})
7458
+ const C=(0,B.nativeDialogs)(),x=new U.Adding({supervisor:o,dialogs:C,logger:n,approvalEnv:{installDir:G}}),R=e=>{const r=(0,T.readServiceConfig)(t)
7459
+ if(!r)throw new Error("the service has no configuration");(0,T.writeServiceConfig)(t,{...r,wordsBackup:{...r.wordsBackup??{},...e}})}
7460
+ d=await(0,q.startDoor)({supervisor:o,adding:x,wordsBackup:{view:()=>{const e=(0,b.backupSettingsOf)((0,T.readServiceConfig)(t)?.wordsBackup)
7461
+ return{settings:e,state:p.state(),runs:p.ownRuns(),folderAvailable:p.folderAvailable(e.folder)}},set:e=>R(e),pick:async()=>{const e=await C.pickFolder("Choose a folder for backups of your words (one your backup tool or iCloud Drive covers)")
7462
+ return e&&R({folder:e}),e},run:()=>p.run(),restore:e=>p.restore(e)},privateData:{get:()=>h?.privateData??!0===(0,T.readServiceConfig)(t)?.privateData,set:k},newComputer:{api:I,pickBackupFolder:()=>C.pickFolder('Choose the "AlchemyLab Backups" folder of your old computer (restored or copied onto this one)')},verifyMachineTicket:async e=>{const t=(await v.getToken())?.token
7438
7463
  if(!t)throw new Error("this computer is not signed in")
7439
- const r=await fetch(`${s.remoteServerUrl}/api/verify-machine-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,B.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:e}),signal:AbortSignal.timeout(1e4)})
7464
+ const r=await fetch(`${s.remoteServerUrl}/api/verify-machine-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,"X-Client-Type":"alchemy-local-service",...(0,W.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:e}),signal:AbortSignal.timeout(1e4)})
7440
7465
  if(r.status>=400&&r.status<500)return{ok:!1}
7441
7466
  if(!r.ok)throw new Error(`HTTP ${r.status}`)
7442
7467
  const n=await r.json(),o=n?.data??n
7443
- return{ok:!0===o?.ok,expiresAt:o?.expiresAt??null}},logger:n,remoteServerUrl:s.remoteServerUrl,version:(0,j.ownVersion)()??void 0,machineId:u.id,frontendOrigin:()=>_,verifyTicket:async(e,t)=>{const r=(await k.getToken())?.token
7468
+ return{ok:!0===o?.ok,expiresAt:o?.expiresAt??null}},logger:n,remoteServerUrl:s.remoteServerUrl,version:(0,A.ownVersion)()??void 0,machineId:u.id,frontendOrigin:()=>E,verifyTicket:async(e,t)=>{const r=(await v.getToken())?.token
7444
7469
  if(!r)throw new Error("this computer is not signed in")
7445
- const n=await fetch(`${s.remoteServerUrl}/api/projects/${encodeURIComponent(e)}/verify-local-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,"X-Client-Type":"alchemy-local-service",...(0,B.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:t}),signal:AbortSignal.timeout(1e4)})
7470
+ const n=await fetch(`${s.remoteServerUrl}/api/projects/${encodeURIComponent(e)}/verify-local-ticket`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${r}`,"X-Client-Type":"alchemy-local-service",...(0,W.machineCallerHeaders)(s.remoteServerUrl)},body:JSON.stringify({ticket:t}),signal:AbortSignal.timeout(1e4)})
7446
7471
  if(n.status>=400&&n.status<500)return{ok:!1}
7447
7472
  if(!n.ok)throw new Error(`HTTP ${n.status}`)
7448
7473
  const o=await n.json(),i=o?.data??o
7449
- return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,O.defaultDoorPort)(s.remoteServerUrl)),await new Promise(()=>{})}(e)
7450
- const s=(0,A.servicePaths)(e)
7451
- if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,U.uninstallLoginItem)(e)
7452
- return void console.log(t.removed?`AlchemyLab no longer starts when this computer starts (${t.file} removed).`:"AlchemyLab was not set to start at login.")}const r=(0,C.readServiceConfig)(s)
7453
- return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(s,r)}if("stop"===t[0]){if(!await(0,x.isServiceRunning)(s))return void console.log("The AlchemyLab service is not running.")
7454
- await(0,x.request)(s,"shutdown")
7455
- for(let e=0;e<240&&await(0,x.isServiceRunning)(s);e++)await new Promise(e=>setTimeout(e,250))
7474
+ return{ok:!0===i?.ok,expiresAt:i?.expiresAt??null}}},s.doorPort??(0,q.defaultDoorPort)(s.remoteServerUrl)),await new Promise(()=>{})}(e)
7475
+ const c=(0,_.servicePaths)(e)
7476
+ if("install"===t[0]||"uninstall"===t[0]){if("uninstall"===t[0]){const t=await(0,H.uninstallLoginItem)(e)
7477
+ return void console.log(t.removed?`AlchemyLab no longer starts when this computer starts (${t.file} removed).`:"AlchemyLab was not set to start at login.")}const r=(0,T.readServiceConfig)(c)
7478
+ return r||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),void await o(c,r)}if("stop"===t[0]){if(!await(0,R.isServiceRunning)(c))return void console.log("The AlchemyLab service is not running.")
7479
+ await(0,R.request)(c,"shutdown")
7480
+ for(let e=0;e<240&&await(0,R.isServiceRunning)(c);e++)await new Promise(e=>setTimeout(e,250))
7456
7481
  return void console.log("The AlchemyLab service stopped, and with it every project it ran. They come back when it starts again.")}if("computer-gone"===t[0]){const e=t.slice(1).join(" ").trim()
7457
- e||(console.error("Usage: alchemy service computer-gone <name or id> (the computer that is gone, by its name in Settings, General, Computers; Copy id there gives its id)"),process.exit(1)),await(0,x.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7458
- const r=await(0,x.request)(s,"computer-gone",{machineId:e})
7459
- return r.ok||(console.error(`Not recorded: ${r.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("Recorded: that computer is gone. Its projects can now be moved to this computer without a backup: `alchemy service new-computer`.")}if("pair"===t[0]){t[1]||(console.error("Usage: alchemy service pair <code> (Settings, Data in the app gives the code)"),process.exit(1)),await(0,x.isServiceRunning)(s)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7460
- const e=await(0,x.request)(s,"pair-channel",{code:t[1]})
7461
- return e.ok||(console.error(`Not paired: ${e.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer is paired again: the hosted service accepts its new channel key.")}if("new-computer"===t[0]){const e=(0,C.readServiceConfig)(s)
7462
- return e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(s,e),void await async function(e){process.stdin.isTTY||(console.error("`alchemy service new-computer` asks questions: run it in a terminal."),process.exit(1))
7463
- const t=d(e),r=u()
7464
- try{const s=await(0,g.runNewComputerFlow)(t,r,{explicit:!0})
7465
- if("moved"===s||"no-candidates"===s){const s=v.default.realpathSync(process.cwd()),n=v.default.existsSync(b.default.join(s,w.ALCHEMY_DIR_NAME,"project.json")),{here:o}=await t.candidates(),i=n?null:await(0,g.pickMovedProject)(o,s,r)
7466
- if(i){const t=(0,m.attachFolder)(s,i,(0,p.machineIdentity)(e).id)
7467
- r.say(t.ok?`This folder is ${i.name||"that project"} now. Run \`alchemy\` here to start it.`:`Not done: ${t.reason}`)}}}catch(e){r.say(`Stopped: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1}finally{r.close()}}(s)}if("move-words-here"===t[0]){const e=(0,C.readServiceConfig)(s)
7468
- e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(s,e)
7469
- const t=await(0,x.request)(s,"move-words-here",{},9e4)
7470
- return t.ok&&!0===t.privateData||(console.error(`Not started: ${t.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer keeps the words of its projects from now on. The words the hosted service holds for them are moving here; Settings, Data shows the count still held, per project.")}(0,A.ensurePrivateDir)(s.dir)
7471
- let a=(0,C.readServiceConfig)(s)
7472
- const l=!a
7473
- a?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==a.opencodeDir&&(a={...a,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,C.writeServiceConfig)(s,a),console.log(`Projects will run with the OpenCode build in ${a.opencodeDir} from their next start.`)):(a={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,C.writeServiceConfig)(s,a),console.log(`AlchemyLab service set up for ${e}.`),console.log('Your projects\' words (requests, messages, documents, results) are kept on this computer, and backed up daily to the folder "AlchemyLab Backups" in your home folder. Keep that folder in your own backup: a lost disk without it loses the words.')),l&&"ready"!==(await(0,E.runDockerPreflight)({interactive:!0===process.stdin.isTTY&&!0===process.stdout.isTTY})).status&&console.log("Running without Docker: agents fall back to the host sandbox. AlchemyLab picks Docker up when it is there."),!l||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,U.loginItemInstalled)(e)||(await n("Start AlchemyLab every time this computer starts?",!0)?await o(s,a):console.log("Not set. `alchemy service install` sets it later."))
7474
- const h=await(0,x.isServiceRunning)(s),k=new _.ProjectsStore(s.projects)
7475
- k.exists()||h||k.touch(),h||await(0,x.startServiceInBackground)(s,a,r())||(console.error(`\u274c The AlchemyLab service did not start. Its log: ${s.log}`),process.exit(1)),await c(s)},exports.printStatus=c,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
7476
- return t||(console.error("The AlchemyLab service is not set up yet. Run `alchemy service` once to set it up."),process.exit(1)),t.paths}(e),a=(0,C.readServiceConfig)(o)
7477
- if("status"===t)return c(o)
7478
- if("approve"===t){const e=(await(0,x.request)(o,"approvals")).approvals??[]
7482
+ e||(console.error("Usage: alchemy service computer-gone <name or id> (the computer that is gone, by its name in Settings, General, Computers; Copy id there gives its id)"),process.exit(1)),await(0,R.isServiceRunning)(c)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7483
+ const r=await(0,R.request)(c,"computer-gone",{machineId:e})
7484
+ return r.ok||(console.error(`Not recorded: ${r.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("Recorded: that computer is gone. Its projects can now be moved to this computer without a backup: `alchemy service new-computer`.")}if("pair"===t[0]){t[1]||(console.error("Usage: alchemy service pair <code> (Settings, Data in the app gives the code)"),process.exit(1)),await(0,R.isServiceRunning)(c)||(console.error("Start the AlchemyLab service first: `alchemy service`."),process.exit(1))
7485
+ const e=await(0,R.request)(c,"pair-channel",{code:t[1]})
7486
+ return e.ok||(console.error(`Not paired: ${e.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer is paired again: the hosted service accepts its new channel key.")}if("new-computer"===t[0]){const e=(0,T.readServiceConfig)(c)
7487
+ return e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(c,e),void await async function(e){process.stdin.isTTY||(console.error("`alchemy service new-computer` asks questions: run it in a terminal."),process.exit(1))
7488
+ const t=h(e),r=p()
7489
+ try{const s=await(0,y.runNewComputerFlow)(t,r,{explicit:!0})
7490
+ if("moved"===s||"no-candidates"===s){const s=k.default.realpathSync(process.cwd()),n=k.default.existsSync(S.default.join(s,v.ALCHEMY_DIR_NAME,"project.json")),{here:o}=await t.candidates(),i=n?null:await(0,y.pickMovedProject)(o,s,r)
7491
+ if(i){const t=(0,w.attachFolder)(s,i,(0,m.machineIdentity)(e).id)
7492
+ r.say(t.ok?`This folder is ${i.name||"that project"} now. Run \`alchemy\` here to start it.`:`Not done: ${t.reason}`)}}}catch(e){r.say(`Stopped: ${e instanceof Error?e.message:String(e)}`),process.exitCode=1}finally{r.close()}}(c)}if("move-words-here"===t[0]){const e=(0,T.readServiceConfig)(c)
7493
+ e||(console.error("Set the service up first: `alchemy service`."),process.exit(1)),await i(c,e)
7494
+ const t=await(0,R.request)(c,"move-words-here",{},9e4)
7495
+ return t.ok&&!0===t.privateData||(console.error(`Not started: ${t.reason??"the hosted service refused it."}`),process.exit(1)),void console.log("This computer keeps the words of its projects from now on. The words the hosted service holds for them are moving here; Settings, Data shows the count still held, per project.")}(0,_.ensurePrivateDir)(c.dir)
7496
+ let l=(0,T.readServiceConfig)(c)
7497
+ const u=!l
7498
+ l?process.env.ALCHEMY_OPENCODE_DIR&&process.env.ALCHEMY_OPENCODE_DIR!==l.opencodeDir&&(l={...l,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR},(0,T.writeServiceConfig)(c,l),console.log(`Projects will run with the OpenCode build in ${l.opencodeDir} from their next start.`)):(l={remoteServerUrl:e,opencodeDir:process.env.ALCHEMY_OPENCODE_DIR||void 0,privateData:!0,createdAt:(new Date).toISOString()},(0,T.writeServiceConfig)(c,l),console.log(`AlchemyLab service set up for ${e}.`),console.log('Your projects\' words (requests, messages, documents, results) are kept on this computer, and backed up daily to the folder "AlchemyLab Backups" in your home folder. Keep that folder in your own backup: a lost disk without it loses the words.')),u&&"ready"!==(await(0,C.runDockerPreflight)({interactive:!0===process.stdin.isTTY&&!0===process.stdout.isTTY})).status&&console.log("Running without Docker: agents fall back to the host sandbox. AlchemyLab picks Docker up when it is there."),!u||!process.stdin.isTTY||"darwin"!==process.platform&&"linux"!==process.platform||(0,H.loginItemInstalled)(e)||(await n("Start AlchemyLab every time this computer starts?",!0)?await o(c,l):console.log("Not set. `alchemy service install` sets it later."))
7499
+ const f=await(0,R.isServiceRunning)(c),I=new x.ProjectsStore(c.projects)
7500
+ I.exists()||f||I.touch(),f||await(0,R.startServiceInBackground)(c,l,r())||(console.error(a(c.log)),process.exit(1)),s.forFolder?console.log("AlchemyLab service: running."):await d(c)},exports.serviceDidNotStart=a,exports.lastLogReason=c,exports.printStatus=d,exports.runControl=async function(e,t,r){const o=function(e){const t=s(e)
7501
+ return t||(console.error("The AlchemyLab service is not set up yet. Run `alchemy service` once to set it up."),process.exit(1)),t.paths}(e),a=(0,T.readServiceConfig)(o)
7502
+ if("status"===t)return d(o)
7503
+ if("approve"===t){const e=(await(0,R.request)(o,"approvals")).approvals??[]
7479
7504
  if(0===e.length)return void console.log("Nothing is waiting for your approval.")
7480
7505
  for(const t of e){console.log(""),console.log(t.message)
7481
7506
  const e=await n("Approve?",!1)
7482
- await(0,x.request)(o,"approve",{approvalId:t.id,yes:e}),console.log(e?"Approved.":"Declined.")}return}await i(o,a)
7483
- const d=function(e){if(!e)return v.default.realpathSync(process.cwd())
7484
- if(e.startsWith("/")||e.startsWith(".")||e.startsWith("~")){const t=e.startsWith("~")?b.default.join(process.env.HOME??"",e.slice(1)):b.default.resolve(e)
7485
- try{return v.default.realpathSync(t)}catch{return t}}return e}(r)
7486
- if("add"===t){const e=await(0,x.request)(o,"add",{path:d})
7487
- return e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),void console.log(e.added?`Added ${e.project.path}.`:`${e.project.path} is already on this computer.`)}if("start"===t||"restart"===t){console.log(`${"start"===t?"Starting":"Restarting"} ${b.default.basename(d)}...`)
7488
- const e=await(0,x.request)(o,t,{ref:d},18e4)
7507
+ await(0,R.request)(o,"approve",{approvalId:t.id,yes:e}),console.log(e?"Approved.":"Declined.")}return}await i(o,a)
7508
+ const c=function(e){if(!e)return k.default.realpathSync(process.cwd())
7509
+ if(e.startsWith("/")||e.startsWith(".")||e.startsWith("~")){const t=e.startsWith("~")?S.default.join(process.env.HOME??"",e.slice(1)):S.default.resolve(e)
7510
+ try{return k.default.realpathSync(t)}catch{return t}}return e}(r)
7511
+ if("add"===t){const e=await(0,R.request)(o,"add",{path:c})
7512
+ return e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),void console.log(e.added?`Added ${e.project.path}.`:`${e.project.path} is already on this computer.`)}if("start"===t||"restart"===t){console.log(`${"start"===t?"Starting":"Restarting"} ${S.default.basename(c)}...`)
7513
+ const e=await(0,R.request)(o,t,{ref:c},18e4)
7489
7514
  for(const t of e.lines??[])e.ok&&console.log(t)
7490
- return e.ok||(l(e),process.exit(e.pending?0:1)),void console.log(`Running, action server on port ${e.port}.`)}if("stop"===t||"remove"===t){const e=await(0,x.request)(o,t,{ref:d},12e4)
7515
+ return e.ok||(u(e),process.exit(e.pending?0:1)),void console.log(`Running, action server on port ${e.port}.`)}if("stop"===t||"remove"===t){const e=await(0,R.request)(o,t,{ref:c},12e4)
7491
7516
  if(e.ok||(console.error(`\u274c ${e.reason}`),process.exit(1)),console.log("stop"===t?"Stopped.":"Removed from this computer. The folder and its history are untouched."),"remove"===t&&Array.isArray(e.keptWork)&&e.keptWork.length){console.log(`Kept, because ${1===e.keptWork.length?"it holds":"they hold"} work that was never committed:`)
7492
- for(const t of e.keptWork)console.log(` ${t}`)}return}console.error(`Unknown command ${t}`),process.exit(1)},exports.runSupervisedDefault=async function(e,r){const{paths:s,cfg:n}=r,o=v.default.realpathSync(process.cwd()),a=(0,S.createLogger)({serviceName:"alchemy-cli",console:!1,logToFile:!0,logFilePath:s.log,baseDir:s.dir}),c=new I.TokenManager(a,o,{remoteServerUrl:e,allowInteractiveAuth:!1})
7517
+ for(const t of e.keptWork)console.log(` ${t}`)}return}console.error(`Unknown command ${t}`),process.exit(1)},exports.runSupervisedDefault=async function(e,r){const{paths:s,cfg:n}=r,o=k.default.realpathSync(process.cwd()),a=(0,$.createLogger)({serviceName:"alchemy-cli",console:!1,logToFile:!0,logFilePath:s.log,baseDir:s.dir}),c=new j.TokenManager(a,o,{remoteServerUrl:e,allowInteractiveAuth:!1})
7493
7518
  await c.initialize()
7494
- const h=await(0,M.ensureSignedIn)(c)
7519
+ const l=await(0,P.ensureSignedIn)(c)
7495
7520
  await i(s,n)
7496
- const p=await(0,x.request)(s,"connect-now",{},3e4).catch(()=>{})
7497
- p?.ok||await(0,x.request)(s,"connect-now",{},3e4).catch(()=>{}),await async function(e){const t=b.default.join(e.dir,"new-computer-asked.json")
7498
- if(!process.stdin.isTTY||!process.stdout.isTTY||v.default.existsSync(t))return
7499
- if(((await(0,x.request)(e,"status")).projects??[]).length>0)return
7500
- const r=d(e),{candidates:s}=await r.candidates()
7521
+ const d=await(0,R.request)(s,"connect-now",{},3e4).catch(()=>{})
7522
+ d?.ok||await(0,R.request)(s,"connect-now",{},3e4).catch(()=>{}),await async function(e){const t=S.default.join(e.dir,"new-computer-asked.json")
7523
+ if(!process.stdin.isTTY||!process.stdout.isTTY||k.default.existsSync(t))return
7524
+ if(((await(0,R.request)(e,"status")).projects??[]).length>0)return
7525
+ const r=h(e),{candidates:s}=await r.candidates()
7501
7526
  if(0===s.length)return
7502
- const n=u()
7503
- try{await(0,g.runNewComputerFlow)(r,n,{explicit:!1}),v.default.writeFileSync(t,JSON.stringify({askedAt:(new Date).toISOString()})+"\n",{mode:384}),n.say("")}finally{n.close()}}(s).catch(()=>{})
7504
- const f=await(0,x.request)(s,"add",{path:o})
7527
+ const n=p()
7528
+ try{await(0,y.runNewComputerFlow)(r,n,{explicit:!1}),k.default.writeFileSync(t,JSON.stringify({askedAt:(new Date).toISOString()})+"\n",{mode:384}),n.say("")}finally{n.close()}}(s).catch(()=>{})
7529
+ const f=await(0,R.request)(s,"add",{path:o})
7505
7530
  f.ok||(console.error(`\u274c ${f.reason}`),process.exit(1))
7506
- const m=await(0,x.request)(s,"start",{ref:o},18e4)
7531
+ const m=await(0,R.request)(s,"start",{ref:o},18e4)
7507
7532
  for(const e of m.lines??[])console.log(e)
7508
- m.ok||(l(m),process.exit(1)),(0,R.printBanner)(),console.log(`\u{1f4c1} Project: ${o}`),console.log(""),console.log(" Action server .... port "+m.port)
7509
- const w=await(0,P.resolveFrontendUrl)(e)
7510
- if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${w}\x07${w}\x1b]8;;\x07`),console.log(""),m.firstRun){const e=`${w.replace(/\/$/,"")}${m.projectId?`/projects/${m.projectId}`:""}/timeline?welcome=1`
7511
- console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),(0,$.openBrowser)(e,"Open the Alchemy UI link above in a browser on any device to continue there.")}const{InteractiveShell:y}=await t.e(994).then(()=>t.t(75994,23)),k=new y({projectPath:o,logger:a,remoteServerUrl:e,authToken:h,projectId:m.projectId,hasCode:!0===m.hasCode})
7512
- await k.start(async()=>{console.log(""),console.log(`AlchemyLab keeps running ${b.default.basename(o)}. \`alchemy stop\` in this folder stops it.`),process.exit(0)})}
7513
- const p=t(60134),f=t(89122),m=t(98074),g=t(62671),w=t(14139),y=t(79564),v=h(t(79896)),b=h(t(16928)),k=h(t(23785)),S=t(12782),I=t(11253),$=t(90629),j=t(42403),E=t(88918),A=t(18944),C=t(30030),_=t(52800),T=t(81480),x=t(56257),M=t(19071),R=t(64504),P=t(38778),O=t(35382),D=t(60134),q=t(71918),N=t(36968),F=t(76231),L=t(67518),U=t(99269),B=t(11773),H=(()=>{let e=__dirname
7514
- for(let t=0;t<6;t++){try{if("alchemylab"===JSON.parse(v.default.readFileSync(b.default.join(e,"package.json"),"utf8"))?.name)return e}catch{}const t=b.default.dirname(e)
7533
+ m.ok||(u(m),process.exit(1)),(0,O.printBanner)(),console.log(`\u{1f4c1} Project: ${o}`),console.log(""),console.log(" Action server .... port "+m.port)
7534
+ const g=await(0,D.resolveFrontendUrl)(e)
7535
+ if(console.log(""),console.log(` Alchemy UI: \x1b]8;;${g}\x07${g}\x1b]8;;\x07`),console.log(""),m.firstRun){const e=`${g.replace(/\/$/,"")}${m.projectId?`/projects/${m.projectId}`:""}/timeline?welcome=1`
7536
+ console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),(0,E.openBrowser)(e,`Open this link in a browser on any device to continue there, with the tour: ${e}`)}const{InteractiveShell:w}=await t.e(994).then(()=>t.t(75994,23)),v=new w({projectPath:o,logger:a,remoteServerUrl:e,authToken:l,projectId:m.projectId,hasCode:!0===m.hasCode})
7537
+ await v.start(async()=>{console.log(""),console.log(`AlchemyLab keeps running ${S.default.basename(o)}. \`alchemy stop\` in this folder stops it.`),process.exit(0)})}
7538
+ const m=t(60134),g=t(89122),w=t(98074),y=t(62671),v=t(14139),b=t(79564),k=f(t(79896)),S=f(t(16928)),I=f(t(23785)),$=t(12782),j=t(11253),E=t(90629),A=t(42403),C=t(88918),_=t(18944),T=t(30030),x=t(52800),M=t(81480),R=t(56257),P=t(19071),O=t(64504),D=t(38778),q=t(35382),N=t(60134),F=t(71918),L=t(36968),U=t(76231),B=t(67518),H=t(99269),W=t(11773),G=(()=>{let e=__dirname
7539
+ for(let t=0;t<6;t++){try{if("alchemylab"===JSON.parse(k.default.readFileSync(S.default.join(e,"package.json"),"utf8"))?.name)return e}catch{}const t=S.default.dirname(e)
7515
7540
  if(t===e)break
7516
7541
  e=t}})()},30030(e,exports,t){"use strict"
7517
7542
  exports.readServiceConfig=function(e){const t=(0,r.readJson)(e.config)
@@ -7837,7 +7862,7 @@ let a=""
7837
7862
  try{a=(await s.readFile(r,"utf8")).slice(0,400)}catch{}if(o.has(t)&&/^---[\s\S]*\bmode:\s*(primary|subagent|all)\b/m.test(a)){await s.unlink(r)
7838
7863
  continue}const c=`${r}.removed-by-alchemy-${i}`
7839
7864
  try{await s.rename(r,c),this.logger.warn("Moved an agent file AlchemyLab did not write out of the way (agent prompts are served from memory now)",{from:r,movedTo:c})}catch(e){this.logger.warn("Could not move an unrecognised agent file aside; leaving it",{path:r,error:e instanceof Error?e.message:String(e)})}}}catch{}}buildExploreMarkdown(e){return`---\ndescription: Fast codebase exploration and research subagent\nmode: subagent\n---\n\n${e?e.trim()+"\n\n":""}You are a codebase exploration agent. Your job is to find specific information and return concise, targeted answers.\n\n## Response Guidelines\n\n- **Be concise.** Return only what was asked for. A typical answer is 100-500 words.\n- **Lead with the answer.** State findings first, then supporting details.\n- **Reference specific files and lines.** Use \`file_path:line_number\` format.\n- **Don't exhaustively document everything you find.** Focus on what's relevant to the question.\n- **Use structured output** when listing multiple items (bullet points, not prose).\n\n## Strategy\n\n1. Start with \`search\` to find relevant files by name or content patterns\n2. Use \`read_files\` to read specific sections (use line ranges, don't read entire large files)\n3. Use \`query_history\` for understanding why code changed or tracing decisions\n\n## Anti-patterns to Avoid\n\n- Don't produce multi-thousand-word reports for simple questions\n- Don't read every file in a directory when a targeted search would suffice\n- Don't explain how tools work \u2014 just use them and report findings\n`}async resolveWorkflowEffort(e){try{const t=await this.fetchDefaultWorkflowFromRemote(e),r="string"==typeof t?.effort&&g.has(t.effort)?t.effort:exports.NK
7840
- r!==w&&this.logger.info("Effort dial read from the workflow",{effort:r,was:w}),w=r}catch{}return w}hasConnectedProviders(){return null!==this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,a.fetchConnectedProviders)(e)
7865
+ r!==w&&this.logger.info("Effort dial read from the workflow",{effort:r,was:w}),w=r}catch{}return w}hasConnectedProviders(){return null!==this.connectedProviders}getConnectedProviders(){return this.connectedProviders}async refreshConnectedProviders(e){const t=await(0,a.fetchConnectedProviders)(e)
7841
7866
  if(!t)return!1
7842
7867
  const r=JSON.stringify(t)!==JSON.stringify(this.connectedProviders)
7843
7868
  return this.connectedProviders=t,r}generateAgentMarkdown(e,t,r,n,o={}){let c=r
@@ -8695,14 +8720,16 @@ if(!t)return`NOT-MEASURED-YET:it is measured after the work is completed, on the
8695
8720
  const s=String(t.note??"").replace(/\s+/g," ").trim().slice(0,300)
8696
8721
  return`${"met"===t.verdict?"MEASURED-MET":"unmet"===t.verdict?"MEASURED-WORSE":"MEASURED"}:${s||"measured"}; ${r}`}const s=n(e)
8697
8722
  return s?`CHECK-AT-COMPLETION:${s}`:!0===e?.proven?"reviewer"===t.by?"PROVEN:review":"you"===t.by?"PROVEN:user-hand-check":"driver"===t.by?"PROVEN:drive":"PROVEN:test":"not-checked"===r.driver?.verdict?"NOT-CHECKED:drive":"answer"===t.reword?.from?`REWORD:${String(t.reword.note??"the answer it was written for changed").slice(0,300)}; reword it with request_operations update, its key and userSaid "${t.reword.ref}"`:t.notARealTest?"NOT-A-REAL-TEST":t.checkFails?"met"===r.reviewer?.verdict?"FAILED:test (review met, check fails)":"FAILED:test":t.disputed?`DISPUTED:its check passes, but the reviewer says it does not hold${t.disputed.note?` ("${String(t.disputed.note).slice(0,200)}")`:""}; strengthen the check so it tests what the criterion says, or fix the code`:"could-not-run"===r.instrument?.verdict?"COULD-NOT-RUN:test (its check could not run here; it says nothing either way)":"unmet"===r.reviewer?.verdict?"UNMET:review":!0===r.reviewer?.reword?"REWORD:a reviewer found its words no longer fit what was asked; reword it with the user's words, or ask them":"withdrawn"===r.reviewer?.verdict?"RE-REVIEW:its review was withdrawn (the work changed after it, or a new pass is due)":e?.coder?"ATTESTED":"UNPROVEN"}function n(e){const t=e?.proof?.checkSetup
8698
- return"string"==typeof t&&t?`its check starts when the request is completed: set up at completion (setup run ${t}). The check runs what this request adds, so it is registered once the request is completed, and its first run on main is the proof, written on the criterion then. Decided by that run only, never by a review: registering the check is not a part of this request left undone. What the check will run is in the diff and is yours to read`:null}function o(e){const r=new Map
8723
+ return"string"==typeof t&&t?`its check starts when the request is completed: set up at completion (setup run ${t}). The check runs what this request adds, so it is registered once the request is completed, and its first run on main is the proof, written on the criterion then. Decided by that run only, never by a review: registering the check is not a part of this request left undone. What the check will run is in the diff and is yours to read`:null}function o(e){const t=String(e??"").trim(),r=/^\[?(\d+[a-z])\](?:\s|$)/i.exec(t)??/^(\d+[a-z])$/i.exec(t)
8724
+ return r?r[1].toLowerCase():t}function i(e){const r=new c
8699
8725
  for(let s=0;s<e.length;s++){const n=t(e[s].id,s)
8700
- r.set(n,e[s])}return r}function i(e){if(0===e.length)return"No acceptance criteria defined."
8701
- const t=o(e),r=[]
8726
+ r.set(n,e[s])}return r}function a(e){if(0===e.length)return"No acceptance criteria defined."
8727
+ const t=i(e),r=[]
8702
8728
  for(const[e,n]of t){const t=s(n),o="objective"===n.kind||"subjective"===n.kind||"gate"===n.kind?` [${n.kind.toUpperCase()}]`:"",i=n.standing?n.waived?" [WAIVED for this request by the user; it no longer binds, do not work on or judge it]":` [FROM THE ${n.standing.processName?`PROCESS "${String(n.standing.processName).slice(0,60)}"`:"WORKFLOW"}: every request that follows it carries it; it cannot be dropped or reworded; if it does not apply, the guidance tool with guidanceId "[guidance:${n.standing.ruleKey?String(n.standing.ruleKey):String(n.standing.id??"")}]": exception when the user said so, else conflict]`:"",a="string"==typeof n.environment&&n.environment.trim()?` [its checks run in the "${n.environment.trim().slice(0,80)}" environment]`:""
8703
8729
  r.push(`[${e}] [${t}]${o}${i}${a} ${n.description}`)}return r.join("\n")}exports.formatCriteriaWithHashes=void 0,exports.criterionShortKey=t,exports.latestRow=r,exports.reviewedSha=function(e){const t=r(e,["reviewer"])
8704
8730
  return t&&("met"===t.verdict||"unmet"===t.verdict)&&"string"==typeof t.commit&&t.commit?t.commit:null},exports.criterionPlainLine=function(e){const t=String(e?.description??"").trim(),r=n(e)
8705
- return t&&r?`${t} (${r})`:t},exports.buildCriteriaKeyMap=o,exports.formatCriteriaWithKeys=i,exports.formatCriteriaWithHashes=i},75453(e,exports,t){"use strict"
8731
+ return t&&r?`${t} (${r})`:t},exports.buildCriteriaKeyMap=i,exports.formatCriteriaWithKeys=a
8732
+ class c extends Map{get(e){return super.get(e)??super.get(o(e))}has(e){return super.has(e)||super.has(o(e))}}exports.formatCriteriaWithHashes=a},75453(e,exports,t){"use strict"
8706
8733
  function r(e,t,r={}){return new Promise(s=>{const n=(0,y.execFile)(r.gitBinary||"git",t,{cwd:e,env:{...process.env,...r.env||{}},maxBuffer:33554432},(e,t,r)=>{s({code:e?"number"==typeof e.code?e.code:1:0,stdout:String(t??""),stderr:String(r??"")})})
8707
8734
  void 0!==r.stdin&&n.stdin?.end(r.stdin)})}async function s(e,t,s){const n=await r(e,["rev-parse","--verify","--quiet",`${t}^{commit}`],{gitBinary:s})
8708
8735
  return 0===n.code?n.stdout.trim():null}function n(e){try{return v.realpathSync(e)}catch{return b.resolve(e)}}async function o(e,t){const s=await r(e,["worktree","list","--porcelain"],{gitBinary:t})
@@ -9279,19 +9306,21 @@ s&&(t={...t,toSha:s})
9279
9306
  try{const r=await e.post(`/api/projects/${e.projectId}/requests/${e.requestId}/main-change-review`,{touched:t,landing:!0}),s=r?.data??r
9280
9307
  return{reviewing:!0===s?.reviewing}}catch(t){return e.logger.warn("held for a review of main's change: the review could not be asked for",{requestId:e.requestId,error:t instanceof Error?t.message:String(t)}),{reviewing:!1}}}
9281
9308
  const s=t(35317),n=t(35510)},49779(e,exports){"use strict"
9282
- function t(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function r(e,r){return`${t(e)}_${t(r)}`}function s(e){return`${t(e)}_*`}exports.x4=exports.Tt=void 0,exports.validateRepoConfig=function(e){if(void 0!==e.command)return"A repository connection derives its own command from the image; give it a dockerfile instead"
9283
- const t=e=>{const t=e.replace(/\\/g,"/")
9284
- return!t.startsWith("/")&&!/^[a-zA-Z]:/.test(t)&&!t.split("/").includes("..")},r=e.dockerfile
9285
- if("string"!=typeof r||!r.trim())return"A repository connection needs the dockerfile to build, as a path inside the repository, for example mcp/server.Dockerfile"
9286
- if(!t(r.trim()))return`dockerfile must be a path inside the repository, not "${r}"`
9309
+ function t(e){return/[\r\n]/.test(e)||e.trim().length>240}function r(e){return e.replace(/[^a-zA-Z0-9_-]/g,"_")}function s(e,t){return`${r(e)}_${r(t)}`}function n(e){return`${r(e)}_*`}exports.x4=exports.Tt=void 0,exports.validateRepoConfig=function(e){if(void 0!==e.command)return"A repository connection derives its own command from the image; give it a dockerfile instead"
9310
+ const r=e=>{const t=e.replace(/\\/g,"/")
9311
+ return!t.startsWith("/")&&!/^[a-zA-Z]:/.test(t)&&!t.split("/").includes("..")},s=e.dockerfile
9312
+ if("string"!=typeof s||!s.trim())return"A repository connection needs the dockerfile to build, as a path inside the repository, for example mcp/server.Dockerfile"
9313
+ if(t(s))return"dockerfile is the path of a Dockerfile in this repository, for example mcp/server.Dockerfile, not its contents. To run a published image, the repository needs a Dockerfile that says FROM <the image>: have it written in the repository first, then give its path here."
9314
+ if(!r(s.trim()))return`dockerfile must be a path inside the repository, not "${s}"`
9287
9315
  if(void 0!==e.context){if("string"!=typeof e.context||!e.context.trim())return"context, when given, is a directory inside the repository"
9288
- if(!t(e.context.trim()))return`context must be a directory inside the repository, not "${e.context}"`}return null},exports.namespacePattern=s,exports.globalDenyRules=function(e){const t={}
9289
- for(const r of e)t[s(r.name)]="deny"
9290
- return t},exports.agentToolGrants=function(e,t,n){const o=new Set(n?.[t]??[])
9316
+ if(t(e.context))return"context is the path of a directory in this repository (the docker build context), for example mcp/, not a description. Leave it out to build from the repository root."
9317
+ if(!r(e.context.trim()))return`context must be a directory inside the repository, not "${e.context}"`}return null},exports.namespacePattern=n,exports.globalDenyRules=function(e){const t={}
9318
+ for(const r of e)t[n(r.name)]="deny"
9319
+ return t},exports.agentToolGrants=function(e,t,r){const o=new Set(r?.[t]??[])
9291
9320
  if(0===o.size)return{}
9292
9321
  const i={}
9293
- for(const t of e)if(t.enabled&&o.has(t.name))if(null!==t.allowedTools)for(const e of t.allowedTools)i[r(t.name,e)]=!0
9294
- else i[s(t.name)]=!0
9322
+ for(const t of e)if(t.enabled&&o.has(t.name))if(null!==t.allowedTools)for(const e of t.allowedTools)i[s(t.name,e)]=!0
9323
+ else i[n(t.name)]=!0
9295
9324
  return i},exports.diffManifests=function(e,t){if(!e||!t)return null
9296
9325
  const r=new Map(e.map(e=>[e.name,e])),s=new Map(t.map(e=>[e.name,e])),n=[...s.keys()].filter(e=>!r.has(e)).sort(),o=[...r.keys()].filter(e=>!s.has(e)).sort(),i=[...s.entries()].filter(([e,t])=>!0===r.get(e)?.readOnly&&!0!==t.readOnly).map(([e])=>e).sort()
9297
9326
  return 0===n.length&&0===o.length&&0===i.length?null:{added:n,removed:o,nowWrites:i}},exports.describeDrift=function(e,t){const r=[]
@@ -10261,7 +10290,7 @@ const j=s.filter((e,t)=>"--port"!==e&&(-1===$||t!==$+1))
10261
10290
  if(0===j.length){try{for(const e of(0,m.otherInstallsNotice)(process.argv[1],(0,m.otherInstalls)(process.argv[1])))console.log(e)}catch{}if(void 0===i){const e=await(0,I.getRemoteServerUrl)(o())
10262
10291
  let t=(0,k.serviceSetUp)(e)
10263
10292
  if(!t){const r=o(),s=new d.TokenManager(r,process.cwd(),{remoteServerUrl:e,allowInteractiveAuth:!1})
10264
- await s.initialize(),await(0,v.ensureSignedIn)(s),await(0,k.runService)(e,[]),t=(0,k.serviceSetUp)(e)}if(t)return void await(0,k.runSupervisedDefault)(e,t)}return void await async function(t={}){const r=process.cwd()
10293
+ await s.initialize(),await(0,v.ensureSignedIn)(s),await(0,k.runService)(e,[],{forFolder:!0}),t=(0,k.serviceSetUp)(e)}if(t)return void await(0,k.runSupervisedDefault)(e,t)}return void await async function(t={}){const r=process.cwd()
10265
10294
  n(r)
10266
10295
  const s=(0,l.join)(r,p.ALCHEMY_DIR_NAME)
10267
10296
  await(0,c.mkdir)(s,{recursive:!0})
@@ -10281,7 +10310,7 @@ for(const t of e.error.split("\n").slice(0,4))console.log(` ${t}`)
10281
10310
  console.log(` Full log: ${(0,l.join)(t,p.ALCHEMY_DIR_NAME,"server.log")}`)}}(o,r),console.log("")
10282
10311
  const i=`\x1b]8;;${n}\x07${n}\x1b]8;;\x07`
10283
10312
  if(console.log(" Alchemy UI: "+i),console.log(""),k){const e=`${n.replace(/\/$/,"")}${S?`/projects/${S}`:""}/timeline?welcome=1`
10284
- console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),await j,(0,f.openBrowser)(e,"Open the Alchemy UI link above in a browser on any device to continue there.")}let c=!1,d=null
10313
+ console.log(" \u{1f310} First run: opening AlchemyLab in your browser (60-second welcome tour)..."),console.log(" No AI provider connected yet? That is the next step there (Settings > Providers)."),console.log(""),await j,(0,f.openBrowser)(e,`Open this link in a browser on any device to continue there, with the tour: ${e}`)}let c=!1,d=null
10285
10314
  const h=async()=>{c||(c=!0,d&&clearInterval(d),console.log(""),console.log("Shutting down..."),await s.stop(),process.exit(0))}
10286
10315
  process.on("SIGTERM",h),process.on("SIGINT",h)
10287
10316
  const{InteractiveShell:w}=await e.e(994).then(()=>e.t(75994,23)),b=new w({projectPath:r,logger:a,remoteServerUrl:m,authToken:y,projectId:S,hasCode:$})