alchemylab 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -67,7 +67,26 @@ r.startsWith(o+m.sep)&&await a(e,t.address)?c+=await d(e,t.address,r):i.push(t.p
67
67
  const u=t(76982),h=t(79896),p=t(49786),f=t(43106),m=t(16928),g=t(14139)
68
68
  exports.z5="results",exports.COMPRESSED_SUFFIX=".z"
69
69
  const y=.95
70
- exports.TREE_MEDIA_TYPE="application/vnd.alchemy.tree+json",exports.ep=5e4,exports.pO="scratch",exports.POINTER_SUFFIX=".result.json"},86776(e,exports,t){exports.registerAuthRoutes=function(e,t,n,o){const s=o.child({component:"auth-routes"}),i=t.apiClient
70
+ exports.TREE_MEDIA_TYPE="application/vnd.alchemy.tree+json",exports.ep=5e4,exports.pO="scratch",exports.POINTER_SUFFIX=".result.json"},44876(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
71
+ var o=Object.getOwnPropertyDescriptor(t,r)
72
+ o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
73
+ for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
74
+ return t},r(e)},function(e){if(e&&e.__esModule)return e
75
+ var t={}
76
+ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
77
+ return o(t,e),t})
78
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.PYTHON_MANIFESTS=exports.NODE_IMAGE=exports.PYTHON_IMAGE=void 0,exports.defaultBoxFor=function(e){if(!e)return{language:null}
79
+ let t=[]
80
+ try{t=i.readdirSync(e)}catch{return{language:null}}const r=e=>t.includes(e)
81
+ if(exports.PYTHON_MANIFESTS.some(r)||r("Pipfile")||!r("package.json")&&t.some(e=>e.endsWith(".py")))return{language:"python",image:exports.PYTHON_IMAGE,artifacts:[{path:".venv",prepare:c,manifests:exports.PYTHON_MANIFESTS,env:{PATH:"/work/.venv/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin",VIRTUAL_ENV:"/work/.venv"}}]}
82
+ if(r("package.json")){const t=function(e){let t
83
+ try{t=JSON.parse(i.readFileSync(a.join(e,"package.json"),"utf8"))}catch{return}if(!["dependencies","devDependencies","optionalDependencies"].some(e=>t&&"object"==typeof t[e]&&Object.keys(t[e]).length>0))return
84
+ const r=t=>i.existsSync(a.join(e,t))
85
+ return r("pnpm-lock.yaml")?"corepack enable >/dev/null 2>&1; pnpm install --frozen-lockfile":r("yarn.lock")?"corepack enable >/dev/null 2>&1; yarn install --frozen-lockfile":r("package-lock.json")||r("npm-shrinkwrap.json")?"npm ci":"npm install"}(e)
86
+ return{language:"node",image:exports.NODE_IMAGE,...t?{install:t}:{}}}return{language:null}}
87
+ const i=s(t(79896)),a=s(t(16928))
88
+ exports.PYTHON_IMAGE="python:3.12-slim",exports.NODE_IMAGE="node:20-alpine",exports.PYTHON_MANIFESTS=["pyproject.toml","setup.py","setup.cfg","requirements.txt","requirements-dev.txt"]
89
+ const c=["python -m venv /work/.venv","/work/.venv/bin/pip install -q --no-cache-dir pytest","if [ -f requirements.txt ]; then /work/.venv/bin/pip install -q --no-cache-dir -r requirements.txt; fi","if [ -f requirements-dev.txt ]; then /work/.venv/bin/pip install -q --no-cache-dir -r requirements-dev.txt; fi",'{ if [ -f pyproject.toml ] || [ -f setup.py ]; then /work/.venv/bin/pip install -q --no-cache-dir -e . || echo "the project could not be installed; its tests import it from /work"; fi; }'].join(" && ")},86776(e,exports,t){exports.registerAuthRoutes=function(e,t,n,o){const s=o.child({component:"auth-routes"}),i=t.apiClient
71
90
  e.post("/auth/login",async(e,o)=>{try{const{username:r,password:a}=e.body
72
91
  if(!r||!a)return o.status(400).json({success:!1,error:"Username and password are required",timestamp:(new Date).toISOString()})
73
92
  s.info("Attempting login",{username:r})
@@ -105,13 +124,13 @@ const M=new I.FileOperationsClient(q,n.child({component:"file-client"}),o,A,P,R)
105
124
  j.registerClient(M)
106
125
  const D=new $.ProxyClient(q,n.child({component:"proxy-client"}),o,A,P,R)
107
126
  j.registerClient(D)
108
- const F=new E.RequestClient(q,n.child({component:"request-client"}),o,A,P,R)
127
+ const F=new S.RequestClient(q,n.child({component:"request-client"}),o,A,P,R)
109
128
  F.setAuthToken(P),(0,d.isDebugToolsEnabled)()&&n.debug("Explicitly set token on RequestClient",{tokenPresent:!!P}),j.registerClient(F)
110
- const N=new S.DocumentClient(q,n.child({component:"document-client"}),o,A,P,R)
129
+ const N=new E.DocumentClient(q,n.child({component:"document-client"}),o,A,P,R)
111
130
  j.registerClient(N)
112
- const z=new x.ToolRecordClient(q,n.child({component:"tool-record-client"}),o,A,P,R)
131
+ const z=new _.ToolRecordClient(q,n.child({component:"tool-record-client"}),o,A,P,R)
113
132
  j.registerClient(z)
114
- const L=new _.ApiClient(T,A,O,M,F,N,z,D,n.child({component:"api-client"})),B=a.default.Router();(0,h.registerAuthRoutes)(B,{projectClient:A,taskClient:O,fileClient:M,authManager:T,apiClient:L},i,n)
133
+ const L=new x.ApiClient(T,A,O,M,F,N,z,D,n.child({component:"api-client"})),B=a.default.Router();(0,h.registerAuthRoutes)(B,{projectClient:A,taskClient:O,fileClient:M,authManager:T,apiClient:L},i,n)
115
134
  const H=r.getContext();(0,p.registerConfigRoutes)(B,n,H.basePath),e.use("/api",B)
116
135
  const U=e=>{const t=o.startTimer(e)
117
136
  return e=>t(e)},G=(e,t,n)=>{const o=e.query.path||e.body.path
@@ -187,7 +206,7 @@ throw e}}catch(e){r(e)}}),e.patch("/api/proxy/*",W,async(e,t,r)=>{try{const r=e.
187
206
  try{const n=await L.proxyPatch(r,e.body)
188
207
  t.json(n)}catch(e){if(e instanceof Error&&e.message.includes("ECONNREFUSED"))return t.status(503).json({success:!1,error:"Remote server is unreachable",remotePath:r})
189
208
  throw e}}catch(e){r(e)}})}
190
- const a=i(t(7252)),c=i(t(16928)),l=i(t(91943)),d=t(3444),u=t(59860),h=t(86776),p=t(96476),f=t(73306),m=t(14079),g=t(3895),y=s(t(85967)),w=t(64710),b=t(65681),v=t(58824),k=t(12968),I=t(86153),$=t(51887),E=t(61666),S=t(99884),x=t(30922),_=t(63328),C=t(96511)},68625(e,exports,t){function r(e,t,r){const n=exports.ERROR_TYPE_PROPERTIES[e]||exports.ERROR_TYPE_PROPERTIES[i.INTERNAL_ERROR]
209
+ const a=i(t(7252)),c=i(t(16928)),l=i(t(91943)),d=t(3444),u=t(59860),h=t(86776),p=t(96476),f=t(73306),m=t(14079),g=t(3895),y=s(t(85967)),w=t(64710),b=t(65681),v=t(58824),k=t(12968),I=t(86153),$=t(51887),S=t(61666),E=t(99884),_=t(30922),x=t(63328),C=t(96511)},68625(e,exports,t){function r(e,t,r){const n=exports.ERROR_TYPE_PROPERTIES[e]||exports.ERROR_TYPE_PROPERTIES[i.INTERNAL_ERROR]
191
210
  return{success:!1,error:{code:e,message:t||n.defaultMessage,details:r,retryable:n.retryable,statusCode:n.statusCode}}}function n(e){return"object"==typeof e&&null!==e&&"code"in e&&"statusCode"in e&&"details"in e&&"retryable"in e&&"string"==typeof e.code&&"number"==typeof e.statusCode}function o(e){if(n(e)){const t=r(e.code,e.message,e.details)
192
211
  return{statusCode:e.statusCode,body:t}}if(e instanceof Error&&"ConnectionError"===e.name){const t=e
193
212
  return{statusCode:503,body:r("CONNECTION_ERROR",t.message||"Connection to remote server failed",{operation:t.operation||"unknown",lastConnectionAttempt:t.lastConnectionAttempt||null,suggestions:["Check your network connection","Verify that the remote server is running","Try again in a few moments"],isConnectionIssue:!0})}}const t=e instanceof Error?e:new Error(String(e)),o=r(i.INTERNAL_ERROR,t.message||"An unexpected error occurred")
@@ -487,10 +506,10 @@ for(let t=e.row;t<=r.row;t++)for(let s=e.col;s<=r.col;s++)o.push(n[t]?.[s]??"")
487
506
  return`${o.slice(0,8).join(", ")}${o.length>8?`, ... (${o.length})`:""}`}const r=t.map(e=>e.ref.replace(/\$/g,""))
488
507
  return`${t[0].table?`${t[0].table}!`:""}${2===r.length&&r[0]===r[1]?r[0]:r.join(":")}`},l=c(/<chart:categories\b[^>]*table:cell-range-address="([^"]+)"/.exec(e)?.[1]),d=[...e.matchAll(/<chart:series\b([^>]*)>/g)].map(e=>{const t=c(s(e[1],"chart:values-cell-range-address"))
489
508
  return`${c(s(e[1],"chart:label-cell-address"))||"series"}${t?`: ${t}`:""}${l?` by ${l}`:""}`})
490
- return`[chart: ${P[t]??t}${r?` "${r}"`:""}${d.length?`; ${d.join("; ")}`:""}]`}function E(e,t,r){switch(e){case"text:tab":return r.push("\t"),!0
509
+ return`[chart: ${P[t]??t}${r?` "${r}"`:""}${d.length?`; ${d.join("; ")}`:""}]`}function S(e,t,r){switch(e){case"text:tab":return r.push("\t"),!0
491
510
  case"text:line-break":return r.push("\n"),!0
492
511
  case"text:s":return r.push(" ".repeat(Math.min(100,Number(s(t,"text:c")??1)||1))),!0
493
- default:return!1}}function S(e,t=new Map){const r=[],n=[],i=new R
512
+ default:return!1}}function E(e,t=new Map){const r=[],n=[],i=new R
494
513
  let a=0,c=0,l=null,d=null
495
514
  for(const u of e.matchAll(T)){const[,e,h,p,f,m]=u
496
515
  if(void 0!==m){if(c)continue
@@ -518,7 +537,7 @@ case"table:table":{const e=n.pop()
518
537
  if(!e)break
519
538
  const t=e.rows.map(e=>`| ${e.map(e=>e.replace(/\|/g,"\\|")).join(" | ")} |`),o=n[n.length-1]
520
539
  o?.cell?o.cell.push(t.join(" ")):r.push(...t,"")
521
- break}}else{if(E(h,p,i))continue
540
+ break}}else{if(S(h,p,i))continue
522
541
  switch(h){case"text:p":case"text:h":f?!g?.cell&&r.length&&""!==r[r.length-1]&&r.push(""):i.open("text:h"===h?Number(s(p,"text:outline-level")??1)||1:0,a>0)
523
542
  break
524
543
  case"text:list-item":f||a++
@@ -535,9 +554,9 @@ case"draw:image":l&&(l.image=!0)
535
554
  break
536
555
  case"draw:object":case"draw:object-ole":l&&(l.object=!0,l.href=s(p,"xlink:href"))
537
556
  break
538
- case"svg:title":case"svg:desc":!f&&l&&(d="svg:title"===h?"title":"desc")}}}return r.join("\n").replace(/\n{3,}/g,"\n\n").trim()}exports.bi=void 0,exports.documentKindFor=function(e){const t=(0,x.formatFor)(e)
557
+ case"svg:title":case"svg:desc":!f&&l&&(d="svg:title"===h?"title":"desc")}}}return r.join("\n").replace(/\n{3,}/g,"\n\n").trim()}exports.bi=void 0,exports.documentKindFor=function(e){const t=(0,_.formatFor)(e)
539
558
  return t?.text?t.kind:void 0},exports.extractDocumentText=async function e(a,c,l={}){if(a.byteLength>exports.bi)throw new Error(`file is ${(a.byteLength/1048576).toFixed(1)} MB; text extraction stops at ${exports.bi/1048576} MB`)
540
- const u=(0,x.formatOfKind)(c).textVia
559
+ const u=(0,_.formatOfKind)(c).textVia
541
560
  if(u){if(!l.convert)throw new Error(`.${c} files are read by converting them in the office box, which is not reachable from here`)
542
561
  const t=await e(await l.convert(a,c,u),u)
543
562
  return{...t,kind:c,note:`Converted from .${c} to .${u} in the office box first. ${t.note??""}`.trim()}}switch(c){case"pdf":return async function(e){const r=await Promise.resolve().then(()=>t.t(88891,23)),{getDocumentProxy:n,extractText:o}=r?.default??r,s=await n(new Uint8Array(e),{isEvalSupported:!1})
@@ -573,7 +592,7 @@ const p=[...h.values()].find(e=>/notesSlide\d*\.xml$/.test(e)),f=p?await n(t,p):
573
592
  let g=`## Slide ${e+1}\n${s.join("\n")||"(no text)"}`
574
593
  m.length&&(g+=`\n\nNotes:\n${m.join("\n")}`),l.push(g)}return{kind:"pptx",text:l.join("\n\n"),units:c.length,note:"Slide text and speaker notes; pictures show as [image: alt text] and charts as a one-line summary; layout is dropped. To see a slide, view_asset this path."}}(a)
575
594
  case"odt":return async function(e){const{xml:t,objects:r}=await k(e,"application/vnd.oasis.opendocument.text","text document")
576
- return{kind:"odt",text:S(t,r),note:"Text, headings, lists and tables; pictures show as [image: alt text] and charts as a one-line summary; notes, comments and formatting are dropped. To see the pages, view_asset this path."}}(a)
595
+ return{kind:"odt",text:E(t,r),note:"Text, headings, lists and tables; pictures show as [image: alt text] and charts as a one-line summary; notes, comments and formatting are dropped. To see the pages, view_asset this path."}}(a)
577
596
  case"ods":return async function(e){const{xml:t,objects:r}=await k(e,"application/vnd.oasis.opendocument.spreadsheet","spreadsheet"),{sheets:n,truncated:i}=function(e,t=new Map){const r=[]
578
597
  let n=!1,i=null,a=null,c=null,l=null,d=null,u=0,h=!1
579
598
  for(const p of e.matchAll(T)){const[,e,f,m,g,y]=p
@@ -636,7 +655,7 @@ break
636
655
  case"draw:page":{if(!n)break
637
656
  let e=`## Slide ${r.length+1}\n${n.lines.join("\n")||"(no text)"}`
638
657
  n.notes.length&&(e+=`\n\nNotes:\n${n.notes.join("\n")}`),r.push(e),n=null
639
- break}}else{if(E(h,p,i))continue
658
+ break}}else{if(S(h,p,i))continue
640
659
  switch(h){case"draw:page":f||(n={lines:[],notes:[]})
641
660
  break
642
661
  case"presentation:notes":f||a++
@@ -652,9 +671,9 @@ break
652
671
  case"svg:title":case"svg:desc":!f&&l&&(d="svg:title"===h?"title":"desc")}}}else{if(c)continue
653
672
  d&&l?l[d]=(l[d]??"")+o(m):i.push(o(m))}}return r}(t,r)
654
673
  return{kind:"odp",text:n.join("\n\n"),units:n.length,note:"Slide text and speaker notes; pictures show as [image: alt text] and charts as a one-line summary; layout is dropped. To see a slide, view_asset this path."}}(a)
655
- case"rtf":return{kind:"rtf",text:(0,_.rtfToText)(a),note:"Text, paragraphs and table rows; styling, pictures and layout are dropped. To see the pages, view_asset this path."}
674
+ case"rtf":return{kind:"rtf",text:(0,x.rtfToText)(a),note:"Text, paragraphs and table rows; styling, pictures and layout are dropped. To see the pages, view_asset this path."}
656
675
  default:throw new Error(`no text form for .${c}`)}}
657
- const x=t(12661),_=t(43701)
676
+ const _=t(12661),x=t(43701)
658
677
  exports.bi=52428800
659
678
  const C=5e3,q={barChart:"bar",bar3DChart:"bar",lineChart:"line",line3DChart:"line",pieChart:"pie",pie3DChart:"pie",ofPieChart:"pie",doughnutChart:"doughnut",areaChart:"area",area3DChart:"area",scatterChart:"scatter",radarChart:"radar",bubbleChart:"bubble",stockChart:"stock",surfaceChart:"surface",surface3DChart:"surface"},T=/<(\/?)([A-Za-z][\w.-]*:[\w.-]+)\b([^>]*?)(\/?)>|([^<]+)/g,j=new Set(["text:note","office:annotation","text:tracked-changes","office:forms","text:sequence-decls"]),P={circle:"pie",ring:"doughnut"}
660
679
  class R{constructor(){this.stack=[]}open(e,t){this.stack.push({parts:[],heading:e,listed:t})}get current(){return this.stack[this.stack.length-1]}push(e){this.current?.parts.push(e)}pushSpaced(e){const t=this.current?.parts
@@ -665,26 +684,26 @@ if(!e)return null
665
684
  const t=e.parts.join("").replace(/[ \t]+\n/g,"\n").trim()
666
685
  return this.current?(t&&this.pushSpaced(t),null):{plain:t,line:t?e.heading?`${"#".repeat(Math.min(6,e.heading))} ${t}`:e.listed?`- ${t}`:t:""}}}},25992(e,exports,t){var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
667
686
  Object.defineProperty(exports,"__esModule",{value:!0})
668
- const n=r(t(16928)),o=r(t(7252)),s=r(t(98577)),i=t(50184),a=t(31213),c=t(3444),l=t(89955),d=t(52568),u=t(53102),h=t(41152),p=t(73884),f=t(57888),m=t(14139),g=t(66800),y=t(6539),w=t(58824),b=t(26975),v=t(11253),k=process.argv.slice(2),I=k[0]||process.cwd(),$=n.default.resolve(I),E=parseInt(process.env.LOCAL_SERVER_PORT||k[1]||"3002",10),S=(0,c.createEnvLogger)({serviceName:"mcp-http-server"});(0,c.isDebugToolsEnabled)()?S.info("HTTP server initializing",{baseDir:$,preferredPort:E,debugLogging:process.env.DEBUG_LOGGING,debugLevel:process.env.DEBUG_LEVEL}):S.info("HTTP server starting",{port:E})
669
- const x=(0,h.createMetricsCollector)(!0),_=(process.env.REMOTE_SERVER_PORT,process.env.REMOTE_SERVER_HOST,process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com")
670
- S.info("Using remote server URL",{remoteServerUrl:_}),async function(){try{S.info("Finding available port...",{preferredPort:E,baseDir:$,remoteServerUrl:_})
671
- const e=await(0,g.findAvailablePort)(E)
672
- e!==E&&S.info(`Preferred port ${E} is in use, using port ${e} instead`),S.info("Starting HTTP API server...",{port:e,baseDir:$,remoteServerUrl:_})
673
- const t=new l.ProjectRegistry(S),r=new u.DefaultProjectFileSystem(S),c=n.default.join($,m.ALCHEMY_DIR_NAME)
674
- S.info(`Checking ${m.ALCHEMY_DIR_NAME} directory`,{alchemyPath:c}),await r.ensureDirectoryExists(c)
675
- const h=new v.TokenManager(S,n.default.join(c,"auth-token.json")),k=(await y.ClientFactory.createClient(_,S,x,{tokenManager:h,workingDir:$,alchemyPath:c})).projectClient,I=new b.ProjectClientAdapter(k,S),C=new d.ProjectSynchronizer(S,t,I,r),q=await C.getOrCreateProject($,{name:n.default.basename($)||"Local Project",description:`Project for ${n.default.basename($)}`,status:"active",metadata:{basePath:$,alchemyPath:c,bootstrapped:!0}})
676
- S.info("Using project",{projectId:q.id,projectName:q.name})
687
+ const n=r(t(16928)),o=r(t(7252)),s=r(t(98577)),i=t(50184),a=t(31213),c=t(3444),l=t(89955),d=t(52568),u=t(53102),h=t(41152),p=t(73884),f=t(57888),m=t(14139),g=t(66800),y=t(6539),w=t(58824),b=t(26975),v=t(11253),k=process.argv.slice(2),I=k[0]||process.cwd(),$=n.default.resolve(I),S=parseInt(process.env.LOCAL_SERVER_PORT||k[1]||"3002",10),E=(0,c.createEnvLogger)({serviceName:"mcp-http-server"});(0,c.isDebugToolsEnabled)()?E.info("HTTP server initializing",{baseDir:$,preferredPort:S,debugLogging:process.env.DEBUG_LOGGING,debugLevel:process.env.DEBUG_LEVEL}):E.info("HTTP server starting",{port:S})
688
+ const _=(0,h.createMetricsCollector)(!0),x=(process.env.REMOTE_SERVER_PORT,process.env.REMOTE_SERVER_HOST,process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com")
689
+ E.info("Using remote server URL",{remoteServerUrl:x}),async function(){try{E.info("Finding available port...",{preferredPort:S,baseDir:$,remoteServerUrl:x})
690
+ const e=await(0,g.findAvailablePort)(S)
691
+ e!==S&&E.info(`Preferred port ${S} is in use, using port ${e} instead`),E.info("Starting HTTP API server...",{port:e,baseDir:$,remoteServerUrl:x})
692
+ const t=new l.ProjectRegistry(E),r=new u.DefaultProjectFileSystem(E),c=n.default.join($,m.ALCHEMY_DIR_NAME)
693
+ E.info(`Checking ${m.ALCHEMY_DIR_NAME} directory`,{alchemyPath:c}),await r.ensureDirectoryExists(c)
694
+ const h=new v.TokenManager(E,n.default.join(c,"auth-token.json")),k=(await y.ClientFactory.createClient(x,E,_,{tokenManager:h,workingDir:$,alchemyPath:c})).projectClient,I=new b.ProjectClientAdapter(k,E),C=new d.ProjectSynchronizer(E,t,I,r),q=await C.getOrCreateProject($,{name:n.default.basename($)||"Local Project",description:`Project for ${n.default.basename($)}`,status:"active",metadata:{basePath:$,alchemyPath:c,bootstrapped:!0}})
695
+ E.info("Using project",{projectId:q.id,projectName:q.name})
677
696
  const T=(0,o.default)()
678
- T.use((0,s.default)()),T.use(o.default.json({limit:"50mb"})),T.use(o.default.urlencoded({extended:!0})),T.use((0,p.requestLogger)(S))
679
- try{S.info("Creating MCP server instance")
680
- const t=await(0,i.createServer)($,S,x,_,{existingProjectId:q.id})
681
- S.info("MCP server instance created successfully")
697
+ T.use((0,s.default)()),T.use(o.default.json({limit:"50mb"})),T.use(o.default.urlencoded({extended:!0})),T.use((0,p.requestLogger)(E))
698
+ try{E.info("Creating MCP server instance")
699
+ const t=await(0,i.createServer)($,E,_,x,{existingProjectId:q.id})
700
+ E.info("MCP server instance created successfully")
682
701
  const r=t.getContext()
683
- S.info("Got server context",{projectId:r.projectId}),t.getContext()&&t.connectionManager?(S.info("Adding connection status middleware"),T.use((0,f.addConnectionStatusInfo)(t.connectionManager))):S.warn("Unable to add connection status middleware - connection manager not available")
702
+ E.info("Got server context",{projectId:r.projectId}),t.getContext()&&t.connectionManager?(E.info("Adding connection status middleware"),T.use((0,f.addConnectionStatusInfo)(t.connectionManager))):E.warn("Unable to add connection status middleware - connection manager not available")
684
703
  try{const e=q.id
685
- S.info("Setting up project context middleware",{projectId:e}),T.use("/api",(t,r,n)=>{"GET"===t.method&&(t.query.projectId=e),["POST","PATCH","PUT"].includes(t.method)&&t.body&&(t.body.projectId=e),n()})}catch(e){S.warn("Failed to set up project context middleware",{error:(0,w.getErrorMessage)(e)})}(0,a.registerApiRoutes)(T,t,S,x),T.use((0,p.errorHandler)(S,x)),T.listen(e,()=>{S.info(`HTTP API server listening on port ${e}`,{baseDir:$,port:e,nodeVersion:process.version})})}catch(e){S.error("Failed to create or initialize MCP server",{error:e instanceof Error?e.stack:String(e)})
704
+ E.info("Setting up project context middleware",{projectId:e}),T.use("/api",(t,r,n)=>{"GET"===t.method&&(t.query.projectId=e),["POST","PATCH","PUT"].includes(t.method)&&t.body&&(t.body.projectId=e),n()})}catch(e){E.warn("Failed to set up project context middleware",{error:(0,w.getErrorMessage)(e)})}(0,a.registerApiRoutes)(T,t,E,_),T.use((0,p.errorHandler)(E,_)),T.listen(e,()=>{E.info(`HTTP API server listening on port ${e}`,{baseDir:$,port:e,nodeVersion:process.version})})}catch(e){E.error("Failed to create or initialize MCP server",{error:e instanceof Error?e.stack:String(e)})
686
705
  let t=!1
687
- e instanceof Error&&(t=e.message.includes("connection")||e.message.includes("timeout")||e.message.includes("network")||"ConnectionError"===e.name),x.incrementCounter("server_initialization_failure",1,{isConnectionError:t?"true":"false"}),T.use((e,t,r)=>{t.set("X-Connection-Status","offline"),t.set("X-Server-Initialization-Failed","true"),r()}),T.use("/api",(t,r)=>{r.status(503).json({success:!1,error:{code:"CONNECTION_ERROR",message:"Server failed to initialize due to connection issues. Please check connectivity and try again.",details:{serverError:e instanceof Error?e.message:String(e),suggestions:["Check your network connection","Verify that the remote server is running","Try restarting the local server"],isConnectionIssue:!0},retryable:!0,statusCode:503}})}),S.warn("Continuing with limited functionality due to initialization error")}process.on("SIGINT",()=>{S.info("Received SIGINT signal, shutting down..."),process.exit(0)}),process.on("SIGTERM",()=>{S.info("Received SIGTERM signal, shutting down..."),process.exit(0)}),process.on("uncaughtException",e=>{S.error("Uncaught exception",{error:e.stack}),process.exit(1)}),process.on("unhandledRejection",e=>{S.error("Unhandled promise rejection",{reason:e}),process.exit(1)})}catch(e){S.error("Failed to start HTTP API server",{error:e instanceof Error?e.stack:String(e)}),process.exit(1)}}()},28156(e,exports,t){var r=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
706
+ e instanceof Error&&(t=e.message.includes("connection")||e.message.includes("timeout")||e.message.includes("network")||"ConnectionError"===e.name),_.incrementCounter("server_initialization_failure",1,{isConnectionError:t?"true":"false"}),T.use((e,t,r)=>{t.set("X-Connection-Status","offline"),t.set("X-Server-Initialization-Failed","true"),r()}),T.use("/api",(t,r)=>{r.status(503).json({success:!1,error:{code:"CONNECTION_ERROR",message:"Server failed to initialize due to connection issues. Please check connectivity and try again.",details:{serverError:e instanceof Error?e.message:String(e),suggestions:["Check your network connection","Verify that the remote server is running","Try restarting the local server"],isConnectionIssue:!0},retryable:!0,statusCode:503}})}),E.warn("Continuing with limited functionality due to initialization error")}process.on("SIGINT",()=>{E.info("Received SIGINT signal, shutting down..."),process.exit(0)}),process.on("SIGTERM",()=>{E.info("Received SIGTERM signal, shutting down..."),process.exit(0)}),process.on("uncaughtException",e=>{E.error("Uncaught exception",{error:e.stack}),process.exit(1)}),process.on("unhandledRejection",e=>{E.error("Unhandled promise rejection",{reason:e}),process.exit(1)})}catch(e){E.error("Failed to start HTTP API server",{error:e instanceof Error?e.stack:String(e)}),process.exit(1)}}()},28156(e,exports,t){var r=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
688
707
  var o=Object.getOwnPropertyDescriptor(t,r)
689
708
  o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),n=this&&this.__exportStar||function(e,exports){for(var t in e)"default"===t||Object.prototype.hasOwnProperty.call(exports,t)||r(exports,e,t)}
690
709
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.getDebugLevelFromEnv=exports.isDebugLoggingEnabled=exports.createEnvLogger=exports.createLogger=exports.UnauthorizedError=exports.McpProtocolError=exports.FileOperationError=exports.ServiceError=exports.DatabaseError=exports.AuthorizationError=exports.AuthenticationError=exports.ValidationError=exports.NotFoundError=exports.AppError=exports.ErrorUtils=exports.createErrorResponse=exports.isRetryableError=exports.errorToResponse=exports.isAppError=exports.createMcpProtocolError=exports.createFileOperationError=exports.createServiceError=exports.createDatabaseError=exports.createAuthorizationError=exports.createAuthenticationError=exports.createValidationError=exports.createNotFoundError=exports.createAppError=exports.createNullMetricsCollector=exports.createMetricsCollector=exports.ConnectionStatusManager=exports.ConnectionError=exports.LogLevel=exports.BAD_REQUEST=exports.INTERNAL_ERROR=exports.NOT_FOUND=exports.FORBIDDEN=exports.UNAUTHORIZED=exports.VALIDATION_ERROR=exports.MCP_PROTOCOL_ERROR=exports.REQUEST_VALIDATION_FAILED=exports.REQUEST_ID_INVALID_FORMAT=exports.REQUEST_NOT_FOUND=exports.SERVICE_UNAVAILABLE=exports.CONNECTION_TIMEOUT=exports.SERVER_ERROR=exports.NETWORK_ERROR=exports.TASK_VALIDATION_FAILED=exports.TASK_ID_INVALID_FORMAT=exports.TASK_NOT_FOUND=void 0,exports.addConnectionStatusInfo=exports.corsOptions=exports.validateRequest=exports.authenticate=exports.errorHandler=exports.requestLogger=exports.middleware=exports.createServer=exports.isDebugToolsEnabled=void 0
@@ -708,35 +727,35 @@ var h=t(73884)
708
727
  Object.defineProperty(exports,"middleware",{enumerable:!0,get:function(){return h.middleware}}),Object.defineProperty(exports,"requestLogger",{enumerable:!0,get:function(){return h.requestLogger}}),Object.defineProperty(exports,"errorHandler",{enumerable:!0,get:function(){return h.errorHandler}}),Object.defineProperty(exports,"authenticate",{enumerable:!0,get:function(){return h.authenticate}}),Object.defineProperty(exports,"validateRequest",{enumerable:!0,get:function(){return h.validateRequest}}),Object.defineProperty(exports,"corsOptions",{enumerable:!0,get:function(){return h.corsOptions}})
709
728
  var p=t(57888)
710
729
  Object.defineProperty(exports,"addConnectionStatusInfo",{enumerable:!0,get:function(){return p.addConnectionStatusInfo}}),t(25992),t(76552),t(91791),t(41152),t(63328),t(73884),t(66800),t(89955),t(52568),t(53102),t(68625),t(50184),t(91472),t(31213),n(t(3444),exports),n(t(93227),exports),n(t(79914),exports),n(t(14055),exports),n(t(59860),exports),n(t(25992),exports),n(t(76552),exports)},76552(e,exports,t){async function r(){const e=new o.StdioServerTransport,t=(0,l.setupParentProcessDetection)(()=>{v.info("Parent process gone or orphan detected. Shutting down."),process.exit(0)})
711
- if(S)try{await S.connectStdio(),(0,a.isDebugToolsEnabled)()&&v.info("Connected stdio transport to server")}catch(e){const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
730
+ if(E)try{await E.connectStdio(),(0,a.isDebugToolsEnabled)()&&v.info("Connected stdio transport to server")}catch(e){const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
712
731
  throw v.error("Failed to connect stdio transport",{errorDetails:t}),e}else v.error("No global server instance available to connect transport")
713
732
  return{transport:e,shutdownHandler:t}}var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
714
733
  Object.defineProperty(exports,"__esModule",{value:!0})
715
734
  const o=t(25802),s=n(t(16928)),i=t(50184),a=t(3444),c=t(41152),l=t(28851),d=t(98411),u=n(t(79896)),h=t(14139),p=t(58824),f=t(49939),m=t(37960),g=t(69706),y=process.argv.slice(2),w=y[0]||process.cwd(),b=s.default.resolve(w)
716
735
  console.error("\n=== MCP STARTUP DIAGNOSTICS ==="),console.error("Script path (process.argv[1]):",process.argv[1]),console.error("Base directory:",b),console.error("ENABLE_DEBUG_TOOLS env:",process.env.ENABLE_DEBUG_TOOLS),console.error("DEBUG_LOGGING env:",process.env.DEBUG_LOGGING),console.error("isDevBuild():",(0,m.isDevBuild)()),console.error("isDebugToolsEnabled():",(0,a.isDebugToolsEnabled)()),console.error("===============================\n")
717
- const v=(0,a.createEnvLogger)({serviceName:"direct-mcp-server",console:!0,baseDir:b});(0,a.isDebugToolsEnabled)()?v.info("MCP Direct server initializing",{baseDir:b,debugLogging:process.env.DEBUG_LOGGING,debugLevel:process.env.DEBUG_LEVEL,pid:process.pid}):v.info("MCP Direct server starting"),console.warn,console.error,console.log=function(...e){console.error("[LOG]",...e)},console.info=function(...e){console.error("[INFO]",...e)},console.warn=function(...e){console.error("[WARN]",...e)},process.on("uncaughtException",e=>{if(v.error(`UNCAUGHT EXCEPTION: ${e}`,{stack:e.stack||"No stack available"}),S)try{S.cleanupSSEConnection()}catch(e){v.error(`Error during cleanup after uncaught exception: ${e}`)}setTimeout(()=>process.exit(1),500)}),process.on("unhandledRejection",e=>{v.error(`UNHANDLED REJECTION: ${e}`,{stack:e instanceof Error?e.stack:"No stack available"})}),process.on("exit",e=>{v.info(`Process exiting with code: ${e}`)})
736
+ const v=(0,a.createEnvLogger)({serviceName:"direct-mcp-server",console:!0,baseDir:b});(0,a.isDebugToolsEnabled)()?v.info("MCP Direct server initializing",{baseDir:b,debugLogging:process.env.DEBUG_LOGGING,debugLevel:process.env.DEBUG_LEVEL,pid:process.pid}):v.info("MCP Direct server starting"),console.warn,console.error,console.log=function(...e){console.error("[LOG]",...e)},console.info=function(...e){console.error("[INFO]",...e)},console.warn=function(...e){console.error("[WARN]",...e)},process.on("uncaughtException",e=>{if(v.error(`UNCAUGHT EXCEPTION: ${e}`,{stack:e.stack||"No stack available"}),E)try{E.cleanupSSEConnection()}catch(e){v.error(`Error during cleanup after uncaught exception: ${e}`)}setTimeout(()=>process.exit(1),500)}),process.on("unhandledRejection",e=>{v.error(`UNHANDLED REJECTION: ${e}`,{stack:e instanceof Error?e.stack:"No stack available"})}),process.on("exit",e=>{v.info(`Process exiting with code: ${e}`)})
718
737
  const k=process.stdout.write
719
738
  process.stdout.write=function(e,t,r){try{if("string"==typeof e)try{JSON.parse(e)}catch(t){return e.includes('"__message_type"')||(process.stderr.write(`[STDOUT REDIRECTED]: ${e}`),r&&r()),!0}return"string"==typeof t&&"function"==typeof r?k.call(process.stdout,e,t,r):"function"==typeof t?k.call(process.stdout,e,void 0,t):k.call(process.stdout,e)}catch(e){return process.stderr.write(`[STDOUT ERROR]: ${e}`),!0}},(0,a.isDebugToolsEnabled)()&&(v.info("Initialized stdout filtering to ensure only valid JSON is sent"),v.info("!!!MARKER!!! COMMAND-LINE ARGS:",{args:JSON.stringify(y)}),v.info("!!!MARKER!!! BASE DIRECTORY:",{baseDir:w}),v.info("!!!MARKER!!! ABSOLUTE BASE DIR:",{absBaseDir:b}),v.info("Working with base directory:",{absBaseDir:b}))
720
739
  try{if(u.default.existsSync(b)||(v.warn(`Base directory ${b} does not exist. Creating it...`),u.default.mkdirSync(b,{recursive:!0})),(0,a.isDebugToolsEnabled)()){const e=s.default.join(b,".mcp-test-file")
721
740
  u.default.writeFileSync(e,(new Date).toISOString()),v.info(`Successfully wrote test file to ${e}`),u.default.unlinkSync(e),v.info("Successfully deleted test file"),v.info(`Confirmed base directory ${b} exists and is accessible`)}}catch(e){v.error(`Error accessing base directory ${b}:`,{error:e}),v.warn("This may cause issues with file operations - please check permissions")}const I=(0,c.createMetricsCollector)(!1)
722
741
  let $=process.env.REMOTE_SERVER_URL||"https://api.alchemylabcode.com"
723
- const E=global.__serverInitialized
724
- void 0===E?(global.__serverInitialized=!1,(0,a.isDebugToolsEnabled)()&&v.info("FIRST RUN: Setting global.__serverInitialized to false")):(0,a.isDebugToolsEnabled)()&&v.info(`REUSE: global.__serverInitialized is already ${global.__serverInitialized}`)
725
- let S=null
726
- try{(0,d.acquireProcessLock)(b)||(v.error(`Process lock acquisition failed for ${b}. Another instance may be running.`),v.error("This instance will exit to prevent duplicate initialization"),process.exit(1)),(0,a.isDebugToolsEnabled)()&&v.info(`Multi-agent mode enabled - allowing concurrent access to ${b}`)}catch(e){v.error("Lock acquisition threw exception",{error:(0,p.getErrorMessage)(e),stack:e instanceof Error?e.stack:void 0}),process.exit(1)}(0,a.isDebugToolsEnabled)()&&(v.info("============================================================"),v.info(`PROCESS START: PID=${process.pid}, TIME=${(new Date).toISOString()}`),v.info(`BASE DIR: ${b}`),v.info(`ALCHEMY PATH: ${s.default.join(b,h.ALCHEMY_DIR_NAME)}`),v.info(`PROJECT PATH: ${s.default.join(b,`${h.ALCHEMY_DIR_NAME}/project.json`)}`),v.info("MULTI-AGENT MODE: true"),v.info("MCP client information will be captured during protocol initialization")),v.info("Starting MCP server"),(0,a.isDebugToolsEnabled)()&&(v.info("============================================================"),v.info(`Base directory: ${b}`),v.info(`Remote server URL: ${$}`),v.info(`Node version: ${process.version}`),v.info(`Current working directory: ${process.cwd()}`),v.info("ALLOWED_DIRECTORIES env var: not used"),v.info("Server already initialized: "+(E?"YES":"NO"))),E&&((0,a.isDebugToolsEnabled)()&&(v.info("Server already initialized globally, reusing existing server instance"),v.info("Reconnecting stdio to existing server...")),r().catch(e=>{const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
742
+ const S=global.__serverInitialized
743
+ void 0===S?(global.__serverInitialized=!1,(0,a.isDebugToolsEnabled)()&&v.info("FIRST RUN: Setting global.__serverInitialized to false")):(0,a.isDebugToolsEnabled)()&&v.info(`REUSE: global.__serverInitialized is already ${global.__serverInitialized}`)
744
+ let E=null
745
+ try{(0,d.acquireProcessLock)(b)||(v.error(`Process lock acquisition failed for ${b}. Another instance may be running.`),v.error("This instance will exit to prevent duplicate initialization"),process.exit(1)),(0,a.isDebugToolsEnabled)()&&v.info(`Multi-agent mode enabled - allowing concurrent access to ${b}`)}catch(e){v.error("Lock acquisition threw exception",{error:(0,p.getErrorMessage)(e),stack:e instanceof Error?e.stack:void 0}),process.exit(1)}(0,a.isDebugToolsEnabled)()&&(v.info("============================================================"),v.info(`PROCESS START: PID=${process.pid}, TIME=${(new Date).toISOString()}`),v.info(`BASE DIR: ${b}`),v.info(`ALCHEMY PATH: ${s.default.join(b,h.ALCHEMY_DIR_NAME)}`),v.info(`PROJECT PATH: ${s.default.join(b,`${h.ALCHEMY_DIR_NAME}/project.json`)}`),v.info("MULTI-AGENT MODE: true"),v.info("MCP client information will be captured during protocol initialization")),v.info("Starting MCP server"),(0,a.isDebugToolsEnabled)()&&(v.info("============================================================"),v.info(`Base directory: ${b}`),v.info(`Remote server URL: ${$}`),v.info(`Node version: ${process.version}`),v.info(`Current working directory: ${process.cwd()}`),v.info("ALLOWED_DIRECTORIES env var: not used"),v.info("Server already initialized: "+(S?"YES":"NO"))),S&&((0,a.isDebugToolsEnabled)()&&(v.info("Server already initialized globally, reusing existing server instance"),v.info("Reconnecting stdio to existing server...")),r().catch(e=>{const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
727
746
  v.error("Failed to connect stdio transport to existing server",{errorDetails:t}),process.exit(1)}))
728
747
  try{const e=u.default.existsSync(b)
729
748
  if((0,a.isDebugToolsEnabled)()&&v.info(`Base directory exists: ${e}`),e&&(0,a.isDebugToolsEnabled)()){const e=u.default.statSync(b)
730
- v.info(`Directory stats: isDirectory=${e.isDirectory()}, mode=${e.mode.toString(8)}`)}else(0,a.isDebugToolsEnabled)()&&v.info("Directory does not exist, trying to create it"),u.default.mkdirSync(b,{recursive:!0})}catch(e){v.error(`Error checking directory: ${e}`)}if(!u.default.existsSync(b))try{u.default.mkdirSync(b,{recursive:!0}),(0,a.isDebugToolsEnabled)()&&v.info(`Created base directory: ${b}`)}catch(e){v.error(`Failed to create base directory: ${e}`)}const x=s.default.join(b,h.ALCHEMY_DIR_NAME)
731
- if(!u.default.existsSync(x))try{u.default.mkdirSync(x,{recursive:!0}),(0,a.isDebugToolsEnabled)()&&v.info(`Created alchemy lab directory: ${x}`)}catch(e){v.error(`Failed to create alchemy lab directory: ${e}`)}try{(async function(){try{(0,g.installServeAuthFetch)(),$=await async function(){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
749
+ v.info(`Directory stats: isDirectory=${e.isDirectory()}, mode=${e.mode.toString(8)}`)}else(0,a.isDebugToolsEnabled)()&&v.info("Directory does not exist, trying to create it"),u.default.mkdirSync(b,{recursive:!0})}catch(e){v.error(`Error checking directory: ${e}`)}if(!u.default.existsSync(b))try{u.default.mkdirSync(b,{recursive:!0}),(0,a.isDebugToolsEnabled)()&&v.info(`Created base directory: ${b}`)}catch(e){v.error(`Failed to create base directory: ${e}`)}const _=s.default.join(b,h.ALCHEMY_DIR_NAME)
750
+ if(!u.default.existsSync(_))try{u.default.mkdirSync(_,{recursive:!0}),(0,a.isDebugToolsEnabled)()&&v.info(`Created alchemy lab directory: ${_}`)}catch(e){v.error(`Failed to create alchemy lab directory: ${e}`)}try{(async function(){try{(0,g.installServeAuthFetch)(),$=await async function(){if(process.env.REMOTE_SERVER_URL)return process.env.REMOTE_SERVER_URL
732
751
  try{const{createConfigManager:e}=await Promise.resolve().then(()=>t.t(49060,23)),r=e(v),n=await r.getInstallationConfig()
733
752
  if(n.remoteServerUrl)return(0,a.isDebugToolsEnabled)()&&v.info("Loaded REMOTE_SERVER_URL from installation config",{remoteServerUrl:n.remoteServerUrl}),n.remoteServerUrl}catch(e){(0,a.isDebugToolsEnabled)()&&v.debug("Could not read REMOTE_SERVER_URL from installation config",{error:e instanceof Error?e.message:"Unknown error"})}return"https://api.alchemylabcode.com"}()
734
753
  try{const e=await(0,f.syncPluginsGlobal)((0,m.isDevBuild)(),v)
735
754
  e.errors.length>0&&v.warn("Some plugins failed to sync",{errors:e.errors}),await(0,f.cleanupLegacyProjectPlugins)(b,v)}catch(e){v.warn("Plugin sync failed",{error:e instanceof Error?e.message:"Unknown error"})}(0,a.isDebugToolsEnabled)()&&(v.info(`Creating MCP server with base dir: ${b}`),v.info(`Using remote server URL: ${$}`))
736
755
  const e=await(0,i.createServer)(b,v,I,$)
737
- S=e,global.__serverInitialized=!0,(0,a.isDebugToolsEnabled)()&&v.info("MCP server created successfully")
756
+ E=e,global.__serverInitialized=!0,(0,a.isDebugToolsEnabled)()&&v.info("MCP server created successfully")
738
757
  const n=r()
739
- return(0,a.isDebugToolsEnabled)()&&v.info("Stdio transport registered with server"),{server:S,transportInfo:n}}catch(e){throw v.error("Failed to initialize MCP server",{error:e}),e}})().then(()=>{(0,a.isDebugToolsEnabled)()&&v.info("MCP server initialization complete")}).catch(e=>{const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
758
+ return(0,a.isDebugToolsEnabled)()&&v.info("Stdio transport registered with server"),{server:E,transportInfo:n}}catch(e){throw v.error("Failed to initialize MCP server",{error:e}),e}})().then(()=>{(0,a.isDebugToolsEnabled)()&&v.info("MCP server initialization complete")}).catch(e=>{const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
740
759
  v.error("Server initialization failed",{errorDetails:t}),process.exit(1)})}catch(e){const t=e instanceof Error?{message:e.message,name:e.name,stack:e.stack}:String(e)
741
760
  v.error("Exception during server initialization",{errorDetails:t}),process.exit(1)}process.on("SIGTERM",()=>{v.info("SIGTERM received, shutting down"),setTimeout(()=>process.exit(0),500)}),process.on("SIGINT",()=>{v.info("SIGINT received, shutting down"),setTimeout(()=>process.exit(0),500)})},50184(e,exports,t){function r(e,t,r,n,o={}){const s=d.default.resolve(e)
742
761
  if(R.has(s))return t.child({component:"mcp-server-registry"}).debug("Using existing server for path",{path:s}),R.get(s)
@@ -750,9 +769,9 @@ var t={}
750
769
  if(null!=e)for(var r=n(e),i=0;i<r.length;i++)"default"!==r[i]&&o(t,e,r[i])
751
770
  return s(t,e),t}),a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
752
771
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.McpServer=void 0,exports.resetStdoutWrite=function(){process.stdout.write=T},exports.getMcpServer=r,exports.createServer=function(e,t,n,o,s={}){return r(e,t,n,o,s)}
753
- const c=t(83807),l=t(58909),d=a(t(16928)),u=a(t(79896)),h=a(t(91943)),p=t(19567),f=t(3444),m=t(87660),g=t(11253),y=i(t(27478)),w=t(14139),b=t(89796),v=new Set,k=t(59860),I=t(91472),$=t(63328),E=t(8955),S=t(73217),x=t(65278),_=t(49060),C=t(58824),q=(0,f.createLogger)({serviceName:"stdout-capture",console:!1,logToFile:!1}),T=process.stdout.write,j=process.argv.includes("--stdio")||"true"===process.env.MCP_DIRECT_MODE
772
+ const c=t(83807),l=t(58909),d=a(t(16928)),u=a(t(79896)),h=a(t(91943)),p=t(19567),f=t(3444),m=t(87660),g=t(11253),y=i(t(27478)),w=t(14139),b=t(89796),v=new Set,k=t(59860),I=t(91472),$=t(63328),S=t(8955),E=t(73217),_=t(65278),x=t(49060),C=t(58824),q=(0,f.createLogger)({serviceName:"stdout-capture",console:!1,logToFile:!1}),T=process.stdout.write,j=process.argv.includes("--stdio")||"true"===process.env.MCP_DIRECT_MODE
754
773
  !(0,f.isDebugToolsEnabled)()||j&&"true"!==process.env.DEBUG_STDOUT_CAPTURE||(process.stdout.write=function(e){try{return(0,f.isDebugToolsEnabled)()&&"true"===process.env.DEBUG_LOGGING?.toLowerCase()&&"string"==typeof e&&!e.trim().startsWith('{"jsonrpc":"2.0"')&&e.trim().length>0&&q.debug("Stdout output",{content:e.substring(0,100),length:e.length}),T.apply(process.stdout,[e])}catch(e){return q.error("Error in safeStdoutWrite",{error:String(e)}),!0}})
755
- class P{constructor(e,t,r,n,o={}){this.git=null,this.currentSSEConnection=null,this.stdioTransport=null,this.isInitializing=!1,this.isInitialized=!1,this.logger=t.child({component:"mcp-server"}),this.logger.info("Creating McpServer instance",{basePath:e,remoteServerUrl:n,hasExistingProjectId:!!o.existingProjectId}),this.basePath=e,this.alchemyPath=d.default.join(e,w.ALCHEMY_DIR_NAME),this.metrics=r,this.remoteServerUrl=n,this.existingProjectId=o.existingProjectId,this.gitBinary=(0,l.detectGitBinary)(),this.logger.info("Detected git binary",{gitBinary:this.gitBinary,version:(0,l.getGitVersion)()}),this.projectContext=new m.ProjectContext(e,t),this.tokenManager=new g.TokenManager(t,e,{remoteServerUrl:n}),this.configManager=new _.ConfigManager(t),this.logger.debug("Server paths initialized",{basePath:this.basePath,alchemyPath:this.alchemyPath}),this.server=new p.Server({name:"alchemy",version:"1.0.0"},{capabilities:{tools:{listChanged:!0}}}),this.metrics.incrementCounter("mcp_server_init_started",1),this.logger.info("Starting initialization for server instance"),this.initializationPromise=this.initialize()}get branchState(){if(!this.workspaceManager)return{currentBranch:null,activeRequestId:null,isOnMainBranch:!0}
774
+ class P{constructor(e,t,r,n,o={}){this.git=null,this.currentSSEConnection=null,this.stdioTransport=null,this.isInitializing=!1,this.isInitialized=!1,this.logger=t.child({component:"mcp-server"}),this.logger.info("Creating McpServer instance",{basePath:e,remoteServerUrl:n,hasExistingProjectId:!!o.existingProjectId}),this.basePath=e,this.alchemyPath=d.default.join(e,w.ALCHEMY_DIR_NAME),this.metrics=r,this.remoteServerUrl=n,this.existingProjectId=o.existingProjectId,this.gitBinary=(0,l.detectGitBinary)(),this.logger.info("Detected git binary",{gitBinary:this.gitBinary,version:(0,l.getGitVersion)()}),this.projectContext=new m.ProjectContext(e,t),this.tokenManager=new g.TokenManager(t,e,{remoteServerUrl:n}),this.configManager=new x.ConfigManager(t),this.logger.debug("Server paths initialized",{basePath:this.basePath,alchemyPath:this.alchemyPath}),this.server=new p.Server({name:"alchemy",version:"1.0.0"},{capabilities:{tools:{listChanged:!0}}}),this.metrics.incrementCounter("mcp_server_init_started",1),this.logger.info("Starting initialization for server instance"),this.initializationPromise=this.initialize()}get branchState(){if(!this.workspaceManager)return{currentBranch:null,activeRequestId:null,isOnMainBranch:!0}
756
775
  const e=this.workspaceManager.getCurrentContext("__server__")
757
776
  return{currentBranch:e.branchName,activeRequestId:e.requestId,isOnMainBranch:e.isMainBranch}}async updateBranchState(e,t){this.logger.debug("updateBranchState called - managed by WorkspaceManager",{branch:e,requestId:t})}async getCurrentBranchContext(){if(!this.workspaceManager)return{branch:null,requestId:null,isMain:!0}
758
777
  const e=this.workspaceManager.getCurrentContext("__server__")
@@ -786,7 +805,7 @@ e=`prj_${Date.now().toString().substring(5)}_${o.randomBytes(4).toString("hex")}
786
805
  try{const t={id:e,name:d.default.basename(this.basePath)||"Local Project",description:`Project for ${d.default.basename(this.basePath)}`,status:"active",createdAt:(new Date).toISOString(),updatedAt:(new Date).toISOString(),metadata:{basePath:this.basePath,alchemyPath:this.alchemyPath,fallback:!0}},r=d.default.join(this.alchemyPath,"project.json")
787
806
  u.default.writeFileSync(r,JSON.stringify(t,null,2)),this.logger.debug("Successfully wrote fallback project.json file")}catch(e){this.logger.error("Failed to write fallback project.json",{error:e instanceof Error?e.message:String(e)})}}this.logger.info("Server using project ID",{projectId:e})
788
807
  const r=this.remoteServerUrl||"https://api.alchemylabcode.com",n=await $.ClientFactory.createClient(r,this.logger,this.metrics,{workingDir:this.basePath,alchemyPath:this.alchemyPath,tokenManager:this.tokenManager,circuitBreakerThreshold:5,circuitBreakerResetTime:3e4,retryAttempts:3,retryDelay:1e3})
789
- this.apiClient=n,await(0,E.initializeAgentRegistry)(this.apiClient,this.logger),this.logger.debug("Agent registry initialized"),this.documentFileManager=new x.DocumentFileManager(this.apiClient,this.logger),await this.documentFileManager.initialize(),this.logger.debug("Document file manager initialized"),e&&"PENDING_REMOTE_CREATION"!==e&&(this.apiClient.setProjectId(e),this.logger.debug("Set project ID on API client",{projectId:e}))
808
+ this.apiClient=n,await(0,S.initializeAgentRegistry)(this.apiClient,this.logger),this.logger.debug("Agent registry initialized"),this.documentFileManager=new _.DocumentFileManager(this.apiClient,this.logger),await this.documentFileManager.initialize(),this.logger.debug("Document file manager initialized"),e&&"PENDING_REMOTE_CREATION"!==e&&(this.apiClient.setProjectId(e),this.logger.debug("Set project ID on API client",{projectId:e}))
790
809
  try{const r=this.apiClient.getProjectClient()
791
810
  if(this.existingProjectId)this.logger.info("Skipping project synchronization - using existing project from HTTP server",{projectId:e})
792
811
  else if("PENDING_REMOTE_CREATION"===e){const{SimpleProjectCreationLock}=await t.e(800).then(()=>t.t(93800,23)),n=new SimpleProjectCreationLock(this.basePath,this.logger),o=await n.acquireLock()
@@ -807,7 +826,7 @@ const e=this.workspaceManager.getCurrentContext("__server__")
807
826
  return"multi-agent"===e.mode&&e.requestId?this.workspaceManager.getCurrentGit():this.git||void 0}getContext(){if(!this.workspaceManager)throw new Error("WorkspaceManager not initialized")
808
827
  return{mainBasePath:this.basePath,alchemyPath:this.alchemyPath,basePath:this.workspaceManager.getCurrentContext("__server__").basePath,projectId:this.getProjectId(),git:this.getCurrentGitInstance(),gitBinary:this.gitBinary,currentSessionId:"__server__",apiClient:this.apiClient,workspaceManager:this.workspaceManager,documentFileManager:this.documentFileManager,configManager:this.configManager,getNextId:this.getNextId.bind(this),getCurrentBranchContext:this.getCurrentBranchContext.bind(this),syncBranchState:this.syncBranchState.bind(this),switchToMainContext:this.switchToMainContext.bind(this),getCurrentRequestId:this.getCurrentRequestId.bind(this),updateBranchState:this.updateBranchState.bind(this)}}isMainBranch(e){return"main"===e||"master"===e}extractRequestIdFromBranch(e){const t=e.match(/^feature\/req(\d+)-/)
809
828
  return t?`req${t[1]}`:null}async getEnhancedContext(){return{...this.getContext(),branchInfo:await this.getCurrentBranchInfo()}}getTokenManager(){return this.tokenManager}getProjectId(){try{return this.projectContext.getProjectId()}catch(e){return this.logger.debug("ProjectContext not initialized, using fallback project ID",{error:(0,C.getErrorMessage)(e)}),"INITIALIZING"}}generateSessionId(){return`session_${Date.now().toString(36)}_${Math.random().toString(36).substring(2,8)}_${process.pid.toString(36)}`}async getActiveWorktrees(){return await this.workspaceManager.listActiveWorkspaces()}async initializeWorkspaceManager(){try{if(this.logger.debug("initializeWorkspaceManager called",{basePath:this.basePath}),!this.git)throw new Error("Git must be initialized before WorkspaceManager")
810
- this.workspaceManager=(0,S.createWorkspaceManager)(this.basePath,this.logger,this.git,this.metrics,this.apiClient),this.logger.debug("WorkspaceManager created successfully"),this.logger.info("Initialized unified workspace manager",{mode:"multi-agent",basePath:this.basePath})}catch(e){throw this.logger.error("Failed to initialize workspace manager",{error:e}),e}}async switchToRequestContext(e){return this.logger.debug("switchToRequestContext called",{requestId:e,hasWorkspaceManager:!!this.workspaceManager}),this.workspaceManager?this.switchToWorktreeContext(e):{success:!1,message:"Workspace manager not available"}}async switchToWorktreeContext(e){if(this.logger.debug("Using worktree context switching for multi-agent mode",{requestId:e}),!this.workspaceManager)return{success:!1,message:"Workspace manager not available"}
829
+ this.workspaceManager=(0,E.createWorkspaceManager)(this.basePath,this.logger,this.git,this.metrics,this.apiClient),this.logger.debug("WorkspaceManager created successfully"),this.logger.info("Initialized unified workspace manager",{mode:"multi-agent",basePath:this.basePath})}catch(e){throw this.logger.error("Failed to initialize workspace manager",{error:e}),e}}async switchToRequestContext(e){return this.logger.debug("switchToRequestContext called",{requestId:e,hasWorkspaceManager:!!this.workspaceManager}),this.workspaceManager?this.switchToWorktreeContext(e):{success:!1,message:"Workspace manager not available"}}async switchToWorktreeContext(e){if(this.logger.debug("Using worktree context switching for multi-agent mode",{requestId:e}),!this.workspaceManager)return{success:!1,message:"Workspace manager not available"}
811
830
  try{let t=`request-${e}`
812
831
  try{this.logger.debug("Attempting to fetch request title from API",{requestId:e})
813
832
  const r=this.apiClient.getRequestClient(),n=await r.getRequest(e)
@@ -935,14 +954,14 @@ exports.Gw=20
935
954
  const s=/\.(?:[cm]?[jt]sx?|py|pyi|go|rs|java|kt|kts|scala|swift|m|mm|c|cc|cpp|cxx|h|hh|hpp|cs|fs|rb|php|pl|pm|lua|r|jl|dart|ex|exs|erl|hs|clj|cljs|elm|vue|svelte|sh|bash|zsh|ps1|sql)$/i},82272(e,exports){exports.alchemyMeta=function(e){return{_meta:{alchemy:e}}},exports.withAlchemyKind=function(e,t,r){const n=e._meta,o=n?.alchemy??{}
936
955
  return{...e,_meta:{...n,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},58185(e,exports,t){function r(e){if($.has(e))$.delete(e)
937
956
  else if($.size>=2e3){const e=$.keys().next().value
938
- void 0!==e&&$.delete(e)}$.set(e,Date.now())}async function n(e,t,r,n){const o=_.get(e)
939
- if(o&&Date.now()-o.at<x)return o.list
957
+ void 0!==e&&$.delete(e)}$.set(e,Date.now())}async function n(e,t,r,n){const o=x.get(e)
958
+ if(o&&Date.now()-o.at<_)return o.list
940
959
  try{const n=await t.proxyGet(`/api/projects/${e}/guidance/path-anchored`),o=n?.data??(Array.isArray(n)?n:[]),s=Array.isArray(o)?o.filter(e=>e&&"string"==typeof e.id&&Array.isArray(e.paths)&&"string"==typeof e.body):[]
941
- return _.set(e,{at:Date.now(),list:s}),function(e,t,r,n){const o=r?.mainBasePath||r?.basePath
960
+ return x.set(e,{at:Date.now(),list:s}),function(e,t,r,n){const o=r?.mainBasePath||r?.basePath
942
961
  if(o)for(const r of t){let t=[]
943
962
  try{t=(r.paths??[]).filter(e=>(0,g.isLiteralAnchor)(e)&&!(0,f.existsSync)((0,m.join)(o,(0,g.anchorDiskPath)(e))))}catch{continue}if(0===t.length)continue
944
963
  const s=`${e}|${r.id}|${t.join(",")}`
945
- C.has(s)||(C.size>=500&&C.clear(),C.add(s),n.proxyPost(`/api/projects/${e}/guidance/${r.id}/feedback`,{verdict:"anchor-missing",reason:`Pinned paths no longer exist in the repo: ${t.join(", ")} (globs are not checked)`}).catch(()=>{}))}}(e,s.filter(e=>!(0,y.isRuleRow)(e)),r,t),(0,y.reportRuleAnchors)(e,s,r,(e,r)=>t.proxyPost(e,r)),s}catch(t){return n?.debug?.(`path-guidance fetch skipped: ${t instanceof Error?t.message:String(t)}`),_.set(e,{at:Date.now(),list:o?.list??[]}),o?.list??[]}}function o(e,t=new Set,r=0){if(r>4||t.size>=6||!e||"object"!=typeof e)return t
964
+ C.has(s)||(C.size>=500&&C.clear(),C.add(s),n.proxyPost(`/api/projects/${e}/guidance/${r.id}/feedback`,{verdict:"anchor-missing",reason:`Pinned paths no longer exist in the repo: ${t.join(", ")} (globs are not checked)`}).catch(()=>{}))}}(e,s.filter(e=>!(0,y.isRuleRow)(e)),r,t),(0,y.reportRuleAnchors)(e,s,r,(e,r)=>t.proxyPost(e,r)),s}catch(t){return n?.debug?.(`path-guidance fetch skipped: ${t instanceof Error?t.message:String(t)}`),x.set(e,{at:Date.now(),list:o?.list??[]}),o?.list??[]}}function o(e,t=new Set,r=0){if(r>4||t.size>=6||!e||"object"!=typeof e)return t
946
965
  for(const[n,s]of Object.entries(e)){if(t.size>=6)break
947
966
  if("path"!==n&&"filePath"!==n||"string"!=typeof s||!s||s.startsWith("/"))if("paths"===n&&Array.isArray(s))for(const e of s)if("string"==typeof e&&e&&!e.startsWith("/")){if(t.add(e),t.size>=6)break}else e&&"object"==typeof e&&o(e,t,r+1)
948
967
  else if(Array.isArray(s))for(const e of s)o(e,t,r+1)
@@ -962,7 +981,7 @@ for(const e of t)for(const t of r.get(e)??[]){const r=`- ${e}: ${a(t.question)}
962
981
  return 0===s.length?null:`\u2696 Settled decisions behind ${e}: already decided, don't re-litigate; if circumstances changed, raise it to the user:\n${s.join("\n")}`}async function u(e,t,r,n,o){try{const o=t?.mainBasePath||t?.basePath
963
982
  if(!o)return[]
964
983
  const s=t?.gitBinary||"git",i=[],a=[]
965
- for(const t of e){const e=I.get(`${S}${t}`)
984
+ for(const t of e){const e=I.get(`${E}${t}`)
966
985
  if(e&&Date.now()-e.at<k){e.note&&i.push({path:t,note:e.note})
967
986
  continue}const r=c(await l(s,o,t))
968
987
  a.push({path:t,reqIds:r})}if(0===a.length)return i
@@ -973,34 +992,34 @@ if(u.length>0){const e=await n.proxyGet(`/api/projects/${r}/decisions/for-reques
973
992
  if(Array.isArray(t))for(const e of t){if(!e?.requestId||!e?.question||!e?.answer)continue
974
993
  const t=p.get(e.requestId)??[]
975
994
  t.push(e),p.set(e.requestId,t)}}for(const{path:e,reqIds:t}of a){const r=d(e,t,p)
976
- I.set(`${S}${e}`,{at:Date.now(),note:r}),r&&i.push({path:e,note:r})}return i}catch(e){return o?.debug?.(`decisions-at-use enrichment skipped: ${e instanceof Error?e.message:String(e)}`),[]}}exports.enrichWithAnchoredAnnotations=async function(e,t,a,c,l,d,p,f){try{if(!c||!l)return a
995
+ I.set(`${E}${e}`,{at:Date.now(),note:r}),r&&i.push({path:e,note:r})}return i}catch(e){return o?.debug?.(`decisions-at-use enrichment skipped: ${e instanceof Error?e.message:String(e)}`),[]}}exports.enrichWithAnchoredAnnotations=async function(e,t,a,c,l,d,p,f){try{if(!c||!l)return a
977
996
  const m=w.has(e),y=b.has(e)
978
997
  if(!m&&!y)return a
979
998
  if(!a||a.isError||!Array.isArray(a.content))return a
980
999
  const v=m?Array.from(o(t)).map(e=>({ref:`file:${e}`,label:e})):Array.from(s(t)).map(e=>({ref:`document:${e}`,label:e}))
981
1000
  if(0===v.length)return a
982
- const[S,x,_]=await Promise.all([Promise.all(v.map(e=>async function(e,t,r,n){const o=I.get(e)
1001
+ const[E,_,x]=await Promise.all([Promise.all(v.map(e=>async function(e,t,r,n){const o=I.get(e)
983
1002
  if(o&&Date.now()-o.at<k)return{note:o.note,hash:o.hash??""}
984
1003
  let s=null,a=""
985
1004
  try{const o=await n.proxyGet(`/api/projects/${r}/artifact-annotations`,{ref:e,status:"open"}),c=o?.data??o
986
1005
  Array.isArray(c)&&c.length>0&&(a=(0,h.createHash)("sha256").update(c.map(e=>`${e.id}|${e.body}`).join("\0")).digest("hex").slice(0,16),s=`\u{1f4cc} Open comments pinned to ${t}: address while you're here, then resolve (document_operations resolve-annotation with the annotationId; reply-to-annotation to answer):\n${c.slice(0,5).map(e=>{const t=e.artifactAnchor?.line??e.anchor?.lineHint,r=(e.anchor?.selectedText||"").slice(0,60),n=(e.replies?.length??0)>0?` (${e.replies.length} repl${1===e.replies.length?"y":"ies"})`:""
987
1006
  return`- [${e.id}]${t?` line ${t}`:""}${r?` "${r}"`:""}: ${e.body}${n}: ${e.createdByAgent?e.createdByAgent:"user"}, ${i(e.createdAt)}`}).join("\n")}`)}catch{s=null}return I.set(e,{at:Date.now(),note:s,hash:a}),{note:s,hash:a}}(e.ref,e.label,l,c))),m?u(v.map(e=>e.label),p,l,c,d):Promise.resolve([]),m?n(l,c,p,d):Promise.resolve([])]),C=[]
988
- for(let e=0;e<v.length;e++){const{note:t,hash:n}=S[e]
1007
+ for(let e=0;e<v.length;e++){const{note:t,hash:n}=E[e]
989
1008
  if(t){if(f){const t=`a|${f}|${v[e].ref}|${n}`
990
1009
  if($.has(t))continue
991
- r(t)}C.push(t)}}for(const{path:e,note:t}of x){if(f){const t=`d|${f}|file:${e}`,n=$.get(t)
992
- if(void 0!==n&&Date.now()-n<E)continue
993
- r(t)}C.push(t)}let q=m?(0,g.matchGuidanceForPaths)(_,v.map(e=>e.label)):[]
1010
+ r(t)}C.push(t)}}for(const{path:e,note:t}of _){if(f){const t=`d|${f}|file:${e}`,n=$.get(t)
1011
+ if(void 0!==n&&Date.now()-n<S)continue
1012
+ r(t)}C.push(t)}let q=m?(0,g.matchGuidanceForPaths)(x,v.map(e=>e.label)):[]
994
1013
  if(f&&(q=q.filter(e=>{const t=$.get(`g|${f}|${e.id}`)
995
- return void 0===t||Date.now()-t>=E})),q=q.slice(0,3),q.length>0&&(f&&q.forEach(e=>r(`g|${f}|${e.id}`)),C.push((0,g.renderGuidanceBlock)(q)),f&&c.proxyPost(`/api/projects/${l}/guidance/delivered`,{ids:q.map(e=>e.id),sessionExternalId:f}).catch(()=>{})),0===C.length)return a
1014
+ return void 0===t||Date.now()-t>=S})),q=q.slice(0,3),q.length>0&&(f&&q.forEach(e=>r(`g|${f}|${e.id}`)),C.push((0,g.renderGuidanceBlock)(q)),f&&c.proxyPost(`/api/projects/${l}/guidance/delivered`,{ids:q.map(e=>e.id),sessionExternalId:f}).catch(()=>{})),0===C.length)return a
996
1015
  a.content.push({type:"text",text:C.join("\n\n")})}catch(e){d?.debug?.(`anchored-annotations enrichment skipped: ${e instanceof Error?e.message:String(e)}`)}return a}
997
1016
  const h=t(76982),p=t(35317),f=t(79896),m=t(16928),g=t(20979),y=t(40285),w=new Set(["read_files","write_file","modify_file","replace_in_files","replace_strings","replace_lines","insert_lines","quick_replace","quick_write","view_asset"]),b=new Set(["plan_read","plan_write","document_operations"]),v=new Set(["documentId","planId"])
998
1017
  let k=3e4
999
1018
  const I=new Map,$=new Map
1000
- let E=27e5
1001
- const S="decisions|file:"
1002
- let x=6e4
1003
- const _=new Map,C=new Set,q=new Set(["human","decided-in-discussion"])},88849(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
1019
+ let S=27e5
1020
+ const E="decisions|file:"
1021
+ let _=6e4
1022
+ const x=new Map,C=new Set,q=new Set(["human","decided-in-discussion"])},88849(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
1004
1023
  var o=Object.getOwnPropertyDescriptor(t,r)
1005
1024
  o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
1006
1025
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
@@ -1010,8 +1029,8 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
1010
1029
  return o(t,e),t})
1011
1030
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.checkingDefs=void 0
1012
1031
  const i=s(t(76183)),a=t(4425),c=t(12286),l=e=>(t,r)=>e(t,r.context,r.apiClient,r.logger,r.metrics,r.apiClient?.getToolRecordClient?.(),r.cliOrigin,{progressToken:r.progressToken,sendNotification:r.sendNotification})
1013
- exports.checkingDefs=[{name:"run",description:'Run a command -- ALWAYS inside a held experiment box, never on the bare host. First experiment_start (preferring the most confined environment that fits: a sealed box is the safety, so anything runs there without approval; the live host is just env:"workspace", the least-safe choice). Then run({ command, box: handle }), or in one call run({ command, env: "<environment name>" }) which starts the box for you (with no env and no box, run starts the project default: its primary environment, or a sealed box that fits the project, python with pytest or node with its dependencies) -- the box stays warm, so iterate in it (servers, build/test, checks; state persists across runs). A server you start, you stop by its pid: start it with `cmd & echo $! > /tmp/NAME.pid` and stop it with `kill $(cat /tmp/NAME.pid)`, never pkill/killall by name or pattern (on the live host that ends the user\'s own processes). `run({name})` runs a saved command in its own environment.',schema:i.RunSchema,annotations:{title:"Run",readOnlyHint:!1},createsRecords:!0,handler:(e,t)=>{const r=l(a.handleRun)(e,t)
1014
- return(0,c.noteRunStarted)(t.context?.currentSessionId,Promise.resolve(r).then(c.runOutcomeOf,e=>({ok:!1,exit:null,detail:String(e)}))),r}},{name:"commands",description:'The project command CATALOG (not the way to run things): "list" saved commands, "register" to suggest one (lands inactive for a human to activate; a registered command is a per-ENVIRONMENT gate-skip -- it runs without the gate inside that env\'s box, but still needs a held experiment of that env), "remove", "setup" (spawn the setup-architect). To RUN anything, use the `run` verb with a box: run({ name|command, box }).',schema:i.CommandsSchema,annotations:{title:"Commands",readOnlyHint:!1},createsRecords:!0,handler:l(a.handleCommands)}]},4425(e,exports,t){function r(e,t){const r=String(e??"").trim().match(/^cd(?:\s+("[^"]*"|'[^']*'|[^\s;&|]+))?(?=\s|;|&|\||$)/)
1032
+ exports.checkingDefs=[{name:"run",description:"Run a shell command inside a held box, never on the bare host: run({ command, box }) with a handle from experiment_start, or run({ command, env }) to start (or reuse) a box on that environment in one call; with neither, the project's default box starts. The box stays warm between runs (servers, builds, state). run({ name }) runs a saved command in its own environment. To read or search files, use read_files and search, not cat, sed or grep here: they read your branch directly and take fewer steps. Stop a server you started by its pid (`cmd & echo $! > /tmp/NAME.pid`, then `kill $(cat /tmp/NAME.pid)`), never pkill or killall: on the live host that ends the user's own processes.",schema:i.RunSchema,annotations:{title:"Run",readOnlyHint:!1},createsRecords:!0,handler:(e,t)=>{const r=l(a.handleRun)(e,t)
1033
+ return(0,c.noteRunStarted)(t.context?.currentSessionId,Promise.resolve(r).then(c.runOutcomeOf,e=>({ok:!1,exit:null,detail:String(e)}))),r}},{name:"commands",description:"The project's saved-command catalog: list; register (suggests commands; they land inactive until a human activates them, and an active one runs without the approval gate inside its environment's box); remove; setup (spawns the setup-architect). To run anything, use `run`.",schema:i.CommandsSchema,annotations:{title:"Commands",readOnlyHint:!1},createsRecords:!0,handler:l(a.handleCommands)}]},4425(e,exports,t){function r(e,t){const r=String(e??"").trim().match(/^cd(?:\s+("[^"]*"|'[^']*'|[^\s;&|]+))?(?=\s|;|&|\||$)/)
1015
1034
  if(!r)return null
1016
1035
  const n=(r[1]??"").replace(/^["']|["']$/g,"")
1017
1036
  return"worktree"===t&&h.test(n)?null:"worktree"===t&&n.startsWith("/")?`it starts with \`cd ${n}\`, a directory the box does not have: inside it there is /work (your worktree, where commands start), /scratch and /tmp. For a directory of the worktree, put it in \`cwd\` (relative to the worktree root, e.g. cwd: "apps/web") and pass the command without the cd`:`it starts with \`cd\`. Put the directory in \`cwd\` (relative to the ${t} root, e.g. cwd: "apps/web") and pass the command without the cd`}function n(e){return!/^[\w@+.-][\w@+./-]*$/.test(e)||e.split("/").includes("..")?`cwd "${e}" must be a directory inside the worktree, relative to its root (e.g. "apps/web"): no leading /, no .., no spaces or shell characters`:null}async function o(e,o,i,d,h,p,f,m){const g=h.startTimer("tool_execution_time"),y=e.command?.trim(),w=e.name?.trim()
@@ -1029,13 +1048,13 @@ if(!r){const e=t.map(e=>e.name).join(", ")||"(none)"
1029
1048
  return g({success:!1,error:"name_not_found"}),{content:[{type:"text",text:`No saved command "${w}". Saved: ${e}.`}],isError:!0}}I=r.command,p=r.cwd||void 0}catch(e){return g({success:!1,error:"name_lookup"}),{content:[{type:"text",text:`Could not resolve saved command "${w}": ${(0,a.getErrorMessage)(e)}`}],isError:!0}}const $=function(e,t,r){const n=[e||"",t].filter(Boolean).join(" ").trim()
1030
1049
  return n&&r?`cd ${r} && (${n})`:n}(I,e.args,p)
1031
1050
  if(!$)return g({success:!1,error:"no_command"}),{content:[{type:"text",text:"run({box}) needs a `command` or a saved `name` to run in the box."}],isError:!0}
1032
- const E=o.workspaceManager?.getCurrentContext(o.currentSessionId),S="multi-agent"===E?.mode&&E.requestId?E.basePath:void 0,x=S?await(0,l.changedPaths)(S).catch(()=>null):null
1051
+ const S=o.workspaceManager?.getCurrentContext(o.currentSessionId),E="multi-agent"===S?.mode&&S.requestId?S.basePath:void 0,_=E?await(0,l.changedPaths)(E).catch(()=>null):null
1033
1052
  try{const r=await(0,s.withHeartbeat)({progressToken:m?.progressToken,sendNotification:m?.sendNotification,message:()=>`running in box ${b}`},()=>(0,u.actionServerFetch)("/api/experiments/exec",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({handle:b,command:$,timeoutMs:1e3*(e.timeout||120),why:e.why}),signal:AbortSignal.timeout(9e5)})),n=await r.json().catch(()=>({}))
1034
1053
  if(404===r.status||n?.data&&!1===n.data.found)return g({success:!1,error:"no_handle"}),{content:[{type:"text",text:n?.data?.stderr||n?.error||"That experiment is gone (stopped or idle-expired). Start a new one with experiment_start."}],isError:!0}
1035
1054
  if(!r.ok||!n?.success)return g({success:!1,error:"box_run_failed"}),{content:[{type:"text",text:`run in box failed: ${n?.error||"HTTP "+r.status}`}],isError:!0}
1036
1055
  const h=n.data||{},p=[h.stdout,h.stderr].filter(Boolean).join("\n--- stderr ---\n").trim()
1037
1056
  g({success:!0})
1038
- const y=w?`${w}: ${$}`:$,k=x&&(0,l.samePath)(h.sourcePath,S)?await(0,l.commitRunChanges)(S,x,$).catch(e=>({files:[],error:(0,a.getErrorMessage)(e)})):null
1057
+ const y=w?`${w}: ${$}`:$,k=_&&(0,l.samePath)(h.sourcePath,E)?await(0,l.commitRunChanges)(E,_,$).catch(e=>({files:[],error:(0,a.getErrorMessage)(e)})):null
1039
1058
  await async function(e,r,n,o,s){try{let i
1040
1059
  try{const{resolveRequestIdOrError:s}=await Promise.resolve().then(()=>t(36651)),a=o?.sessionId||n?.currentSessionId,c=await s(e,r,a,"no active request")
1041
1060
  c?.ok&&(i=c.requestId)}catch{}await e.getToolRecordClient().createToolRecord({type:"run-command",projectId:n.projectId,...i?{requestId:i}:{},metadata:s})}catch(e){r.warn("could not record the command run",{error:(0,a.getErrorMessage)(e)})}}(i,d,o,f,{command:$,...w?{savedName:w}:{},box:b,environmentName:h.environmentName??null,environmentId:h.environmentId??null,confinement:h.confinement??null,exit:h.exit,ms:h.ms??null,...h.timedOut?{timedOut:!0}:{},...h.cancelled?{cancelled:!0}:{},...h.gate?{gate:h.gate}:{},...e.why?{why:String(e.why).slice(0,300)}:{},...p?{outputPreview:p.slice(0,600)}:{},...k?{changedFiles:k.files.slice(0,200),...k.commit?{commitHash:k.commit}:{},...k.error?{commitError:k.error}:{}}:{}})
@@ -1154,7 +1173,7 @@ try{const s=await fetch(`${(0,r.getActionServerBaseUrl)()}/api/sessions/${encode
1154
1173
  return s.ok?i?.data??{ok:!1,error:"deliver_route",detail:"empty response"}:{ok:!1,error:"deliver_route",detail:i?.error||`HTTP ${s.status}`}}catch(e){return{ok:!1,error:"deliver_route",detail:e instanceof Error?e.message:String(e)}}},exports.deliveryFailureText=function(e,t){return`\u274c NOT delivered to session ${e}: ${t.detail||t.error||"unknown reason"}. The session did not receive this and nothing will act on it.`}
1155
1174
  const r=t(79414)},4961(e,exports,t){exports.def=void 0
1156
1175
  const r=t(76183),n=t(23540)
1157
- exports.def={name:"directive_operations",description:"Record and list standing guidance addressed to an area, request, or the whole project, and search the project's decision + annotation record.\n\nOperations:\n\u2022 set - Propose standing guidance the USER stated: it becomes guidance when they press Adopt on the card. Params: targetType (project | area | request), targetId (the area name or request local id; omit for project), body (the guidance, saying what they said), userSaid (their words stating it, copied exactly; required), optional note, and revises (the key of guidance that stands, when their words change it: adopting then rewords that item instead of adding a second). An instruction about one plan or request right now (hold it, do this first) is not guidance: act on it instead. Guidance the user did not state: guidance_propose.\n\u2022 list - List the project's guidance (optional status filter: active | dormant | archived).\n\u2022 search-decisions - Free-text search over recorded decisions (question + answer) and annotations (ask-the-history). Params: query, optional limit. Use it to answer \"why did we choose X / who decided Y\" from the record: cite the refs the result carries.\n\nGuidance SHAPES future work in scope: it reaches in-flight workers immediately and whoever picks up matching work next. It never STARTS work (create a request for that). Use `set` when the user states a lasting preference, so it can become a durable record instead of staying in chat.",schema:r.DirectiveOperationsSchema,annotations:{title:"Directive Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleDirectiveOperations)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)}},23540(e,exports,t){exports.handleDirectiveOperations=async function(e,t,s,i,a,c){const l=i.child({tool:"directive_operations"}),d=a.startTimer("tool_execution_time"),u=e.operation
1176
+ exports.def={name:"directive_operations",description:'Standing guidance and the decision record.\n\n\u2022 set - Propose standing guidance the USER stated, in their words (userSaid, required): it becomes guidance when they press Adopt on the card. Use it when the user states a lasting preference, so it becomes a record instead of staying in chat. An instruction about one plan or request right now (hold it, do this first) is not guidance: act on it. Guidance the user did not state: guidance_propose.\n\u2022 list - The project\'s guidance.\n\u2022 search-decisions - Free-text search over recorded decisions and annotations, to answer "why did we choose X / who decided Y" from the record; cite the refs the result carries.\n\nGuidance shapes future work in its scope (it reaches in-flight workers at once) and never starts work: create a request for that.',schema:r.DirectiveOperationsSchema,annotations:{title:"Directive Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleDirectiveOperations)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)}},23540(e,exports,t){exports.handleDirectiveOperations=async function(e,t,s,i,a,c){const l=i.child({tool:"directive_operations"}),d=a.startTimer("tool_execution_time"),u=e.operation
1158
1177
  try{const i=t.projectId
1159
1178
  if(!i)throw new r.ValidationError("Project ID is required for directive operations")
1160
1179
  if("set"===u){const n=e.targetType
@@ -1207,7 +1226,7 @@ return{content:[{type:"text",text:`\u2705 Document created successfully!\n\n**Do
1207
1226
  return u.error("Failed to create document",{error:e}),{content:[{type:"text",text:`\u274c ${t}`}],isError:!0}}}catch(e){return u.error("Unexpected error in create document",{error:e}),{content:[{type:"text",text:`\u274c Unexpected error: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}finally{h({tool:"create_document"})}}
1208
1227
  const r=t(95040),n=t(2503),o=t(58824),s=t(52190)},61391(e,exports,t){exports.def=void 0
1209
1228
  const r=t(76183),n=t(5626)
1210
- exports.def={name:"document_operations",description:"Manage tracked documents. A document belongs to the PROJECT; it may also be linked to a request, but many are not -- research written before a request exists is project-level, and stays that way after the request is created.\n\nOperations:\n\u2022 create - Create new document with content. A document whose NAME ENDS IN `.html` is rendered as a PAGE instead of as prose: a layout, tables, charts drawn as inline SVG. It renders with scripts and network off, so everything it needs (styles, data, images) must be inside the file, and annotations are not available on one. That is the shape to reach for when the deliverable is something to LOOK AT rather than read. Plain markdown stays the default; a workflow rule or the user says when a project wants pages. Pass scheduleAtTimes ('09:00', or '09:00,17:00') with optional scheduleOnDays ('weekdays' | 'weekends' | 'mon,thu') to make it a SCHEDULE that runs at a local TIME OF DAY, or scheduleIntervalSeconds for one that runs on an interval (86400 = daily); add schedulePrompt, scheduleMaxPerDay, scheduleMaxInFlight. A schedule is a standing goal the runner fires, creating one linked request per firing through normal intake. This is THE way to set up recurring work -- never claim scheduling is unavailable, and never say a time of day is impossible.\n\u2022 update - Edit an existing document's content in place (documentId + replacements[{old_str,new_str}], mode unique|all). THE way to keep a living deliverable current -- update the document, don't create a parallel one. Take a create-snapshot first when making a major revision.\n\u2022 search - Find documents by content or metadata. Searches the WHOLE project by default; keep it that way. Passing requestId narrows to that one request and HIDES project-level documents -- an empty result there means 'none in this request', never 'no such document'. Search project-wide before concluding a cited document is missing.\n\u2022 create-snapshot - Create a version snapshot with optional label and summary\n\u2022 get-history - View version snapshots and change history\n\u2022 get-version - Retrieve a specific version's content\n\u2022 restore-version - Restore document to a previous version\n\u2022 propose-standard - When the user gives this document as a standard to work to (their coding standard, how they do simulations, a style guide), propose its statements as rules for them to adopt, each with its section: checked for what a reviewer can judge from the delivered work (its files, results and the worker's account), told for practice and for what only the final reply to the user shows. At most 12 per call.\n\u2022 create-artifact-annotation - Pin a line-anchored comment to a repo file (ref: \"file:<path>\" plus file/line/selectedText); use for review findings so they surface on the exact diff line\n\nDocuments support markdown with syntax highlighting, Mermaid diagrams, GFM tables, and entity links ([RECORD:trec######], [DOC:doc######], [REQUEST:req######], [TASK:tas######]).",schema:r.DocumentOperationsSchema,annotations:{title:"Document Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleDocumentOperations)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},5626(e,exports,t){exports.handleDocumentOperations=async function(e,l,d,u,h,p,f){const m=u.child({tool:"document_operations"})
1229
+ exports.def={name:"document_operations",description:"Manage tracked documents. A document belongs to the project; it may also be linked to a request, and research written before a request exists stays project-level.\n\nOperations:\n\u2022 create - Create a document with content. A name ending in `.html` renders as a page (layout, tables, charts) in a sealed frame with no network and no other files, so everything it needs is inside the file; annotations are not available on one. Plain markdown is the default. scheduleAtTimes ('09:00' or '09:00,17:00', optional scheduleOnDays 'weekdays' | 'weekends' | 'mon,thu') or scheduleIntervalSeconds (86400 = daily) makes it a SCHEDULE, the way recurring work is set up: a standing goal that files one linked request per firing through intake (schedulePrompt, scheduleMaxPerDay, scheduleMaxInFlight).\n\u2022 update - Edit a document in place (documentId + replacements[{old_str,new_str}], mode unique|all). Keep a living deliverable current here rather than creating a parallel one; create-snapshot before a major revision.\n\u2022 search - Find documents by content or metadata, project-wide by default. requestId narrows to that request and hides project-level documents, so an empty result there does not mean a document is missing.\n\u2022 create-snapshot / get-history / get-version / restore-version - Versions of a document.\n\u2022 propose-standard - When the user gives this document as a standard to work to (a coding standard, how they run simulations, a style guide), propose its statements as rules for them to adopt, each with its section.\n\u2022 create-artifact-annotation - Pin a line-anchored comment to a repo file (ref \"file:<path>\" plus file, line, selectedText): review findings on the exact diff line.\n\nMarkdown supports syntax highlighting, Mermaid, GFM tables and entity links ([RECORD:trec######], [DOC:doc######], [REQUEST:req######], [TASK:tas######]).",schema:r.DocumentOperationsSchema,annotations:{title:"Document Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleDocumentOperations)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},5626(e,exports,t){exports.handleDocumentOperations=async function(e,l,d,u,h,p,f){const m=u.child({tool:"document_operations"})
1211
1230
  try{const u=e,{operation:g}=u,y=u.documentId
1212
1231
  switch(m.info("Processing document_operations",{operation:g}),g){case"create":{m.info("Routing to create-document handler")
1213
1232
  const e=await(0,n.handleCreateDocument)({requestId:u.requestId,filename:u.filename,content:u.content,type:u.type,comment:u.comment,scheduleIntervalSeconds:u.scheduleIntervalSeconds,scheduleAtTimes:u.scheduleAtTimes,scheduleOnDays:u.scheduleOnDays,schedulePrompt:u.schedulePrompt,scheduleMaxPerDay:u.scheduleMaxPerDay,scheduleMaxInFlight:u.scheduleMaxInFlight},l,d,m,h,p,f)
@@ -1251,7 +1270,7 @@ const e=u.ref,t=u.body,{file:r,selectedText:n}=u,o=void 0!==u.line&&null!==u.lin
1251
1270
  r&&(w.file=r),void 0!==s&&(w.line=s),g&&(w.viaRequest=g)
1252
1271
  const b=await d.proxyPost(`/api/projects/${l.projectId}/artifact-annotations`,{ref:e,selectedText:n,lineHint:s,anchor:Object.keys(w).length>0?w:void 0,body:t,annotationType:"comment",createdByAgent:y,createdBySessionId:f?.sessionId}),v=b?.data||b
1253
1272
  return h.incrementCounter("document_operations_total",1,{operation:"create-artifact-annotation",success:!0}),(0,i.withAlchemyKind)({content:[{type:"text",text:`\u2705 **Artifact Annotation Created**\n\n**ID:** ${v?.id||"unknown"}\n**Ref:** ${e}\n`+(void 0!==s?`**Line:** ${s}\n`:"")+`**Comment:** ${t}`}]},"create-artifact-annotation")}case"propose-standard":{const e=await d.getDocumentClient().getDocument(y),t=String(e?.filename??e?.name??y).replace(/\.(md|html?)$/i,""),r=(u.standardRules??[]).map(e=>({owner:"project",text:e.text.trim(),enforcement:e.checked?"reviewed":"told",...e.checked?{severity:"block"}:{},applies:e.kinds?.length?{kinds:e.kinds}:{},source:{from:"document",ref:y,words:`${t}, "${e.section.trim()}"`}})),n=await d.proxyPost(`/api/projects/${l.projectId}/guidance/propose`,{rules:r,evidence:`From ${t} (${y}), which the user gave as a standard to work to.`,sessionExternalId:f?.sessionId??l.currentSessionId??void 0}),o=n?.data?.messageCode??n?.messageCode
1254
- return h.incrementCounter("document_operations_total",1,{operation:g,success:!0}),(0,i.withAlchemyKind)({content:[{type:"text",text:`Proposed ${r.length} rule${1===r.length?"":"s"} from ${t}${o?` (${o})`:""}: ${r.filter(e=>"reviewed"===e.enforcement).length} checked in review, the rest told to agents. The user adopts them from their inbox; nothing applies until then.`}]},g)}case"archive":case"unarchive":{const e=u.documentId
1273
+ return h.incrementCounter("document_operations_total",1,{operation:g,success:!0}),(0,i.withAlchemyKind)({content:[{type:"text",text:`Proposed ${r.length} rule${1===r.length?"":"s"} from ${t}${o?` (${o})`:""}: ${r.filter(e=>"reviewed"===e.enforcement).length} checked in review, the rest told to agents. They are on one card in Steer; nothing applies until the user adopts them there.`}]},g)}case"archive":case"unarchive":{const e=u.documentId
1255
1274
  if(!e)throw new Error(`${g} requires documentId`)
1256
1275
  return m.info(`${g} document`,{docId:e}),await d.proxyPatch(`/api/projects/${l.projectId}/documents/${e}/${g}`,{}),h.incrementCounter("document_operations_total",1,{operation:g,success:!0}),(0,i.withAlchemyKind)({content:[{type:"text",text:"archive"===g?`\u2705 Document ${e} archived. It leaves the active lists but stays in the record; unarchive restores it.`:`\u2705 Document ${e} unarchived.`}]},g)}case"reply-to-annotation":{m.info("Replying to annotation")
1257
1276
  const e=u.annotationId,t=u.body,r=f?await c.sessionIdentity.getAgentTypeFor(f.sessionId)||"agent":void 0
@@ -1293,12 +1312,12 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
1293
1312
  return o(t,e),t})
1294
1313
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.environmentDefs=void 0
1295
1314
  const i=s(t(76183)),a=t(56819),c=e=>(t,r)=>e(t,r.context,r.apiClient,r.logger,r.metrics,r.cliOrigin)
1296
- exports.environmentDefs=[{name:"environment_list",description:"List the environments (confined runtimes) configured for this project.",schema:i.EnvironmentListSchema,annotations:{title:"List Environments",readOnlyHint:!0},handler:c(a.handleEnvironmentList)},{name:"environment_get",description:"Get the full config of one environment (image, install, services, connection).",schema:i.EnvironmentGetSchema,annotations:{title:"Get Environment",readOnlyHint:!0},handler:c(a.handleEnvironmentGet)},{name:"environment_create",description:"Create an environment: how to stand up a confined runtime for this project (image, deps install, services/compose, connection). The first environment for a project becomes its primary. Saves are verify-gated: the exact config must have passed environment_verify in this session (skipVerify + a reason is the logged escape hatch for genuinely unverifiable configs).",schema:i.EnvironmentCreateSchema,annotations:{title:"Create Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentCreate)},{name:"environment_update",description:"Update an existing environment. Provided fields are replaced; config replaces the existing config wholesale. A new config is verify-gated like environment_create (metadata-only updates are not).",schema:i.EnvironmentUpdateSchema,annotations:{title:"Update Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentUpdate)},{name:"environment_delete",description:"Delete an environment. Test processes referencing it fall back to the LLM path until reassigned.",schema:i.EnvironmentDeleteSchema,annotations:{title:"Delete Environment",readOnlyHint:!1,destructiveHint:!0},handler:c(a.handleEnvironmentDelete)},{name:"environment_verify",description:"Verify a PROPOSED environment by standing it up confined (install + services + a smoke command) in a throwaway worktree, before saving it. Returns whether it stands up + diagnostics. A pass unlocks environment_create / environment_update for that exact config in this session.",schema:i.EnvironmentVerifySchema,annotations:{title:"Verify Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentVerify)},{name:"environment_setup",description:"Launch the setup-architect to configure (and verify) a project environment. For making or rendering Word, Excel, PowerPoint or PDF files, ask for the office preset. Spawns a background session that inspects the project, decides a runtime, verifies it stands up, and saves it; you get a report when it finishes. Use when environment_list shows nothing matching the toolchain you need -- pass context saying what you need and why. Depending on project settings the architect either confirms with the user or provisions autonomously.",schema:i.EnvironmentSetupSchema,annotations:{title:"Set Up Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentSetup)},{name:"environment_provision",description:"Provision an environment's disposable DELIVERY stack -- a running system to verify changes against -- from its config.delivery (compose + target + health). Brings it up contained (host-escapes stripped) and health-gates it. Blast op: waits for approval from the user, and the self-stack guard refuses provisioning the stack you run on.",schema:i.EnvironmentProvisionSchema,annotations:{title:"Provision Delivery Stack",readOnlyHint:!1},handler:c(a.handleEnvironmentProvision)},{name:"environment_teardown",description:"Tear down an environment's delivery stack (named volumes kept, so state survives). Blast op: waits for approval from the user; self-stack guard applies.",schema:i.EnvironmentTeardownSchema,annotations:{title:"Tear Down Delivery Stack",readOnlyHint:!1},handler:c(a.handleEnvironmentTeardown)},{name:"environment_status",description:"Read-only status of an environment's delivery stack: which services are up + the health probe. No approval.",schema:i.EnvironmentStatusSchema,annotations:{title:"Delivery Stack Status",readOnlyHint:!0},handler:c(a.handleEnvironmentStatus)},{name:"environment_verify_stack",description:"Verify a RUNNING delivery stack against the checks declared on its environment (correctness, not just readiness): each check runs inside a service and must pass. Read-only, no approval. Use after provisioning to confirm the deployed change behaves right.",schema:i.EnvironmentVerifyStackSchema,annotations:{title:"Verify Delivery Stack",readOnlyHint:!0},handler:c(a.handleEnvironmentVerifyStack)}]},56819(e,exports,t){function r(e){return null===e||"object"!=typeof e?JSON.stringify(e):Array.isArray(e)?`[${e.map(r).join(",")}]`:`{${Object.keys(e).filter(t=>void 0!==e[t]).sort().map(t=>`${JSON.stringify(t)}:${r(e[t])}`).join(",")}}`}function n(e){return(0,h.createHash)("sha256").update(r(e??{})).digest("hex")}async function o(e){if(e?.sessionId)return e.sessionId
1315
+ exports.environmentDefs=[{name:"environment_list",description:"List the environments (confined runtimes) configured for this project.",schema:i.EnvironmentListSchema,annotations:{title:"List Environments",readOnlyHint:!0},handler:c(a.handleEnvironmentList)},{name:"environment_get",description:"Get the full config of one environment (image, install, services, connection).",schema:i.EnvironmentGetSchema,annotations:{title:"Get Environment",readOnlyHint:!0},handler:c(a.handleEnvironmentGet)},{name:"environment_create",description:"Create an environment: how to stand up a confined runtime for this project (image, deps install, services/compose, connection). The first environment for a project becomes its primary. Saves are verify-gated: the exact config must have passed environment_verify in this session (skipVerify + a reason is the logged escape hatch for genuinely unverifiable configs).",schema:i.EnvironmentCreateSchema,annotations:{title:"Create Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentCreate)},{name:"environment_update",description:"Update an existing environment. Provided fields are replaced; config replaces the existing config wholesale. A new config is verify-gated like environment_create (metadata-only updates are not).",schema:i.EnvironmentUpdateSchema,annotations:{title:"Update Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentUpdate)},{name:"environment_delete",description:"Delete an environment. Test processes referencing it fall back to the LLM path until reassigned.",schema:i.EnvironmentDeleteSchema,annotations:{title:"Delete Environment",readOnlyHint:!1,destructiveHint:!0},handler:c(a.handleEnvironmentDelete)},{name:"environment_verify",description:"Verify a PROPOSED environment by standing it up confined (install + services + a smoke command) in a throwaway worktree, before saving it. Returns whether it stands up + diagnostics. A pass unlocks environment_create / environment_update for that exact config in this session.",schema:i.EnvironmentVerifySchema,annotations:{title:"Verify Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentVerify)},{name:"environment_setup",description:"Launch the setup-architect to configure and verify a project environment, when environment_list has nothing matching the toolchain you need (for making or rendering Word, Excel, PowerPoint or PDF files, ask for the office preset). It runs in the background, confirming with the user or not per the project's settings; you get a report when it finishes.",schema:i.EnvironmentSetupSchema,annotations:{title:"Set Up Environment",readOnlyHint:!1},handler:c(a.handleEnvironmentSetup)},{name:"environment_provision",description:"Provision an environment's disposable delivery stack (a running system to verify changes against) from its config.delivery, contained and health-gated. Waits for the user's approval; refused for the stack you run on.",schema:i.EnvironmentProvisionSchema,annotations:{title:"Provision Delivery Stack",readOnlyHint:!1},handler:c(a.handleEnvironmentProvision)},{name:"environment_teardown",description:"Tear down an environment's delivery stack; named volumes are kept. Waits for the user's approval.",schema:i.EnvironmentTeardownSchema,annotations:{title:"Tear Down Delivery Stack",readOnlyHint:!1},handler:c(a.handleEnvironmentTeardown)},{name:"environment_status",description:"Read-only status of an environment's delivery stack: which services are up + the health probe. No approval.",schema:i.EnvironmentStatusSchema,annotations:{title:"Delivery Stack Status",readOnlyHint:!0},handler:c(a.handleEnvironmentStatus)},{name:"environment_verify_stack",description:"Verify a running delivery stack against the checks declared on its environment (each runs inside a service and must pass). Read-only, no approval.",schema:i.EnvironmentVerifyStackSchema,annotations:{title:"Verify Delivery Stack",readOnlyHint:!0},handler:c(a.handleEnvironmentVerifyStack)}]},56819(e,exports,t){function r(e){return null===e||"object"!=typeof e?JSON.stringify(e):Array.isArray(e)?`[${e.map(r).join(",")}]`:`{${Object.keys(e).filter(t=>void 0!==e[t]).sort().map(t=>`${JSON.stringify(t)}:${r(e[t])}`).join(",")}}`}function n(e){return(0,h.createHash)("sha256").update(r(e??{})).digest("hex")}async function o(e){if(e?.sessionId)return e.sessionId
1297
1316
  try{const{sessionIdentity:e}=await Promise.resolve().then(()=>t(6688)),r=e.getIdentity()?.sessionId
1298
1317
  if(r)return r}catch{}return"(unknown-session)"}async function s(e,t,r,s){const i=await o(s)
1299
1318
  if(t.skipVerify){const n=(t.skipVerifyReason||"").trim()
1300
- return n?(r.warn("environment save: skipVerify used -- saving UNVERIFIED config",{op:e,sessionKey:i,reason:n}),{ok:!0,note:`\n\n\u26a0\ufe0f Saved UNVERIFIED (skipVerify) -- reason logged: ${n}`}):{ok:!1,error:"Not saved: skipVerify requires skipVerifyReason. Say WHY this config genuinely cannot pass environment_verify from here (e.g. it needs credentials, external services, or hardware the confined verifier lacks) -- the reason is logged. If it CAN be verified, run environment_verify with this exact config instead."}}return function(e,t){return g.get(e)?.has(n(t))??!1}(i,t.config)?{ok:!0}:{ok:!1,error:`Not saved: this exact config has not passed environment_verify in this session.\n\nRun environment_verify with this EXACT config first -- it stands the environment up confined (image + install + services + smoke) in a throwaway worktree, so broken configs are caught before they become the project's runtime. When it passes, re-run ${e} with the same config (any edit to the config needs a fresh verify).\n\nGenuinely unverifiable from here (needs secrets, external services, or hardware the confined runner lacks)? Pass skipVerify: true with skipVerifyReason explaining why -- the skip is logged.`}}function i(e,t){let r=y.get(e)
1301
- r||(r=new Set,y.set(e,r))
1319
+ return n?(r.warn("environment save: skipVerify used -- saving UNVERIFIED config",{op:e,sessionKey:i,reason:n}),{ok:!0,note:`\n\n\u26a0\ufe0f Saved UNVERIFIED (skipVerify) -- reason logged: ${n}`}):{ok:!1,error:"Not saved: skipVerify requires skipVerifyReason. Say WHY this config genuinely cannot pass environment_verify from here (e.g. it needs credentials, external services, or hardware the confined verifier lacks) -- the reason is logged. If it CAN be verified, run environment_verify with this exact config instead."}}return function(e,t){return y.get(e)?.has(n(t))??!1}(i,t.config)?{ok:!0}:{ok:!1,error:`Not saved: this exact config has not passed environment_verify in this session.\n\nRun environment_verify with this EXACT config first -- it stands the environment up confined (image + install + services + smoke) in a throwaway worktree, so broken configs are caught before they become the project's runtime. When it passes, re-run ${e} with the same config (any edit to the config needs a fresh verify).\n\nGenuinely unverifiable from here (needs secrets, external services, or hardware the confined runner lacks)? Pass skipVerify: true with skipVerifyReason explaining why -- the skip is logged.`}}function i(e,t){let r=w.get(e)
1320
+ r||(r=new Set,w.set(e,r))
1302
1321
  for(const e of t)e&&r.add(e)}function a(e){const t=e.config||{},r=[t.image?`image: ${t.image}`:null,t.install?`install: \`${t.install}\``:null,t.composeFile?`services: ${t.composeFile}`:null,t.connEnv?`conn: ${Object.keys(t.connEnv).join(", ")}`:null,t.delivery?.targetPath?`delivery: ${String(t.delivery.targetPath).split("/").filter(Boolean).pop()||t.delivery.targetPath}`:null].filter(Boolean).join(" \xb7 ")
1303
1322
  return`**${e.name}** (${e.id})${e.isPrimary?" \u2b50 primary":""}${e.scope?` [scope: ${e.scope}]`:""}\n ${r||"(empty config)"}${e.description?`\n ${e.description}`:""}`}function c(e){const t=(Array.isArray(e?.artifacts)?e.artifacts:[]).filter(e=>e&&"string"==typeof e.prepare&&e.prepare.trim()&&!(Array.isArray(e.manifests)&&e.manifests.length>0))
1304
1323
  if(0===t.length)return""
@@ -1314,23 +1333,26 @@ return o?`Environment: ${o}`:"Environment setup"}exports.handleEnvironmentList=a
1314
1333
  try{const e=await n.proxyGet(`/api/projects/${r.projectId}/environments`),s=e?.data||e||[],c=Array.isArray(s)?s:[]
1315
1334
  d({success:!0}),i(await o(l),c.map(e=>e?.id))
1316
1335
  const u=await async function(){try{const{actionServerFetch:e}=await Promise.resolve().then(()=>t(79414)),r=await e("/api/capabilities",{signal:AbortSignal.timeout(8e3)}),n=(await r.json().catch(()=>({})))?.data??{}
1317
- return`Host capabilities: Docker ${!0===n.docker?"available":"UNAVAILABLE (no daemon reachable: `confined` environments and sealed images cannot run here; use the narrowest host level and expect the Tests gate to need sign-off)"} \xb7 OS sandbox for the \`host\` level: ${n.osSandbox?`${n.osSandbox}`:"none"} \xb7 platform ${n.platform??process.platform}`}catch{return"Host capabilities: unknown (the action server did not answer); assume Docker may be unavailable and verify before proposing a sealed environment."}}(),h=(0,m.presetsLine)(c.map(e=>e?.config?.image).filter(Boolean)),p=h?`\n\n${h}`:""
1318
- return 0===c.length?{content:[{type:"text",text:`${u}\n\nNo environments configured yet. Use \`environment_create\` to define one (the project's confined runtime: image, install, services, connection).`+p}]}:{content:[{type:"text",text:`${u}\n\n## Environments (${c.length})\n\n${c.map(a).join("\n\n")}${p}`}]}}catch(e){return d({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to list environments: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentGet=async function(e,t,r,n,s,c){const l=s.startTimer("tool_execution_time")
1336
+ return`Host capabilities: Docker ${!0===n.docker?"available":"UNAVAILABLE (no daemon reachable: `confined` environments and sealed images cannot run here; use the narrowest host level and expect the Tests gate to need sign-off)"} \xb7 OS sandbox for the \`host\` level: ${n.osSandbox?`${n.osSandbox}`:"none"} \xb7 platform ${n.platform??process.platform}`}catch{return"Host capabilities: unknown (the action server did not answer); assume Docker may be unavailable and verify before proposing a sealed environment."}}(),h=(0,m.presetsLine)(c.map(e=>e?.config?.image).filter(Boolean))
1337
+ let p=""
1338
+ if(!c.some(e=>e?.config?.image)){const e=(0,g.defaultBoxFor)(r.mainBasePath)
1339
+ e.language&&(p=`No sealed environment is set up, and none is needed to run this project's tests: \`run\` with no box starts one that fits it (${"python"===e.language?"Python":"Node"}, ${e.image}, with its dependencies installed). Set up an environment only when that box is not enough.`)}const f=(p?`\n\n${p}`:"")+(h?`\n\n${h}`:"")
1340
+ return 0===c.length?{content:[{type:"text",text:`${u}\n\nNo environments configured yet. Use \`environment_create\` to define one (the project's confined runtime: image, install, services, connection).`+f}]}:{content:[{type:"text",text:`${u}\n\n## Environments (${c.length})\n\n${c.map(a).join("\n\n")}${f}`}]}}catch(e){return d({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to list environments: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentGet=async function(e,t,r,n,s,c){const l=s.startTimer("tool_execution_time")
1319
1341
  try{const n=await r.proxyGet(`/api/projects/${t.projectId}/environments/${e.environmentId}`),s=n?.data||n
1320
- return l({success:!0}),s?(i(await o(c),[s.id]),{content:[{type:"text",text:`${a(s)}\n\nconfig:\n\`\`\`json\n${JSON.stringify(s.config||{},null,2)}\n\`\`\``}]}):{content:[{type:"text",text:`Environment not found: ${e.environmentId}`}],isError:!0}}catch(e){return l({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to get environment: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentCreate=async function(e,r,n,d,u,h){const f=u.startTimer("tool_execution_time"),g={...e,description:e.description??(e.preset?m.ENVIRONMENT_PRESETS[e.preset].description:void 0),config:(0,m.expandEnvironmentPreset)(e.preset,e.config)},w=l(g.config)
1321
- if(w)return f({success:!1,error:"localhost_conn"}),{content:[{type:"text",text:w}],isError:!0}
1342
+ return l({success:!0}),s?(i(await o(c),[s.id]),{content:[{type:"text",text:`${a(s)}\n\nconfig:\n\`\`\`json\n${JSON.stringify(s.config||{},null,2)}\n\`\`\``}]}):{content:[{type:"text",text:`Environment not found: ${e.environmentId}`}],isError:!0}}catch(e){return l({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to get environment: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentCreate=async function(e,r,n,d,u,h){const f=u.startTimer("tool_execution_time"),g={...e,description:e.description??(e.preset?m.ENVIRONMENT_PRESETS[e.preset].description:void 0),config:(0,m.expandEnvironmentPreset)(e.preset,e.config)},y=l(g.config)
1343
+ if(y)return f({success:!1,error:"localhost_conn"}),{content:[{type:"text",text:y}],isError:!0}
1322
1344
  try{const e=await s("environment_create",g,d,h)
1323
1345
  if(!e.ok)return f({success:!1,error:"verify_required"}),{content:[{type:"text",text:e.error}],isError:!0}
1324
1346
  const l=await o(h)
1325
1347
  let u=null
1326
1348
  try{const e=await n.proxyGet(`/api/projects/${r.projectId}/environments`),t=e?.data||e||[]
1327
- u=Array.isArray(t)?t:null}catch{}const p=u?function(e,t){const r=y.get(e)
1349
+ u=Array.isArray(t)?t:null}catch{}const p=u?function(e,t){const r=w.get(e)
1328
1350
  return t.filter(e=>e?.id&&!0!==e?.config?.builtin&&!r?.has(e.id))}(l,u):[]
1329
1351
  if(p.length)return i(l,p.map(e=>e.id)),f({success:!1,error:"unseen_environments"}),{content:[{type:"text",text:`Not saved yet: this project already has ${1===p.length?"an environment":"environments"} you have not looked at (another setup may have saved ${1===p.length?"it":"them"} while you worked):\n\n${p.map(a).join("\n\n")}\n\nOne project, one runtime where it can be: if ${1===p.length?"it runs":"one of them runs"} what you need, use it (pass its id as environmentId, e.g. to testing_process_create) instead of saving another. If none fits, call environment_create again with the same arguments and it is saved.`}],isError:!0}
1330
- const m=await n.proxyPost(`/api/projects/${r.projectId}/environments`,{name:g.name,description:g.description,scope:g.scope,config:g.config||{},isPrimary:g.isPrimary,createdBy:"setup-architect"}),w=m?.data||m
1331
- i(l,[w?.id])
1352
+ const m=await n.proxyPost(`/api/projects/${r.projectId}/environments`,{name:g.name,description:g.description,scope:g.scope,config:g.config||{},isPrimary:g.isPrimary,createdBy:"setup-architect"}),y=m?.data||m
1353
+ i(l,[y?.id])
1332
1354
  try{const{sessionIdentity:e}=await Promise.resolve().then(()=>t(6688)),o=e.getIdentity()
1333
- o?.sessionId&&w?.id&&await n.proxyPost(`/api/projects/${r.projectId}/session-environment-associations`,{opencodeSessionId:o.sessionId,environmentId:w.id})}catch{}return f({success:!0}),{content:[{type:"text",text:`\u2705 Created environment **${w.name}** (${w.id})${w.isPrimary?": now the project's primary environment":""}.\n\n${a(w)}${e.note||""}${c(w?.config??g.config)}`}]}}catch(e){return f({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to create environment: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentUpdate=async function(e,t,r,n,o,i){const d=o.startTimer("tool_execution_time")
1355
+ o?.sessionId&&y?.id&&await n.proxyPost(`/api/projects/${r.projectId}/session-environment-associations`,{opencodeSessionId:o.sessionId,environmentId:y.id})}catch{}return f({success:!0}),{content:[{type:"text",text:`\u2705 Created environment **${y.name}** (${y.id})${y.isPrimary?": now the project's primary environment":""}.\n\n${a(y)}${e.note||""}${c(y?.config??g.config)}`}]}}catch(e){return f({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to create environment: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentUpdate=async function(e,t,r,n,o,i){const d=o.startTimer("tool_execution_time")
1334
1356
  try{let o=""
1335
1357
  if(void 0!==e.config){const t=l(e.config)
1336
1358
  if(t)return d({success:!1,error:"localhost_conn"}),{content:[{type:"text",text:t}],isError:!0}
@@ -1343,8 +1365,8 @@ if(f)return u({success:!1,error:"localhost_conn"}),{content:[{type:"text",text:f
1343
1365
  try{const{actionServerFetch:e}=await Promise.resolve().then(()=>t(79414)),a=await e("/api/environments/verify",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({config:h.config,smoke:h.smoke,...await d(r,s,i)}),signal:AbortSignal.timeout(9e5)}),l=await a.json().catch(()=>({}))
1344
1366
  if(u({success:!0}),!a.ok||!l.success)return{content:[{type:"text",text:`Verify could not run: ${l.error||"HTTP "+a.status}`}],isError:!0}
1345
1367
  const p=l.data||{}
1346
- if(p.ok)return function(e,t){let r=g.get(e)
1347
- r||(r=new Set,g.set(e,r)),r.add(n(t))}(await o(c),h.config),{content:[{type:"text",text:"\u2705 Environment verified: it stands up and the smoke command passed. environment_create / environment_update now accept this exact config (edits to it need a fresh verify)."}]}
1368
+ if(p.ok)return function(e,t){let r=y.get(e)
1369
+ r||(r=new Set,y.set(e,r)),r.add(n(t))}(await o(c),h.config),{content:[{type:"text",text:"\u2705 Environment verified: it stands up and the smoke command passed. environment_create / environment_update now accept this exact config (edits to it need a fresh verify)."}]}
1348
1370
  const f=p.commands&&p.commands[0],m=p.error?p.error:f?`${f.name}: exit ${f.exit}${f.timedOut?" (timed out)":""}\n${(f.stderr||f.stdout||"").slice(-1500)}`:"unknown failure"
1349
1371
  return{content:[{type:"text",text:`\u274c Environment did NOT verify (status: ${p.status||"failed"}). Fix the config and re-verify before saving.\n\n${m}`}]}}catch(e){return u({success:!1,error:"verify_error"}),{content:[{type:"text",text:`Failed to verify environment: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}},exports.handleEnvironmentSetup=async function(e,r,n,o,s,i){const a=s.startTimer("tool_execution_time")
1350
1372
  let c,l=!1
@@ -1390,7 +1412,7 @@ try{const{actionServerFetch:n}=await Promise.resolve().then(()=>t(79414)),o=awai
1390
1412
  if(i({success:!0}),!o.ok||!s.success)return{content:[{type:"text",text:`Verify unavailable: ${s.error||"HTTP "+o.status}`}],isError:!0}
1391
1413
  const a=s.data||{},c=(a.checks||[]).map(e=>`${e.pass?"\u2713":"\u2717"} ${e.name}${e.pass?"":" -- "+(e.output||"").slice(0,140)}`).join("; ")||"(no checks declared)",l=!0===a.healthy?"yes":!1===a.healthy?"no":"n/a (no probe)"
1392
1414
  return{content:[{type:"text",text:`Verify ${a.verified?"PASSED":"FAILED"} | healthy: ${l} | checks: ${c}`}]}}catch(e){return i({success:!1,error:"verify_stack_error"}),{content:[{type:"text",text:`Failed to verify stack: ${(0,p.getErrorMessage)(e)}`}],isError:!0}}}
1393
- const h=t(76982),p=t(58824),f=t(87780),m=t(44853),g=new Map,y=new Map},98095(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
1415
+ const h=t(76982),p=t(58824),f=t(87780),m=t(44853),g=t(44876),y=new Map,w=new Map},98095(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
1394
1416
  var o=Object.getOwnPropertyDescriptor(t,r)
1395
1417
  o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
1396
1418
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
@@ -1400,7 +1422,7 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
1400
1422
  return o(t,e),t})
1401
1423
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.experimentDefs=void 0
1402
1424
  const i=s(t(76183)),a=t(82567),c=e=>(t,r)=>e(t,r.context,r.apiClient,r.logger,r.metrics,r.cliOrigin)
1403
- exports.experimentDefs=[{name:"experiment_start",description:"Hold a BOX you live in -- the place ALL commands run (run({command, box})). Returns a handle; the box STAYS WARM across runs (a server you start keeps running, scratch + deps persist) and shows a live view of your branch at /work. In a request, a run may change that source (a formatter, a --fix, codegen): whatever a run changes is committed at once as that run's commit, and the result lists the files. PREFER THE MOST CONFINED ENV THAT FITS -- safety comes from the environment, not from inspecting commands, so a sealed box runs anything with no approval. Pick `env` by name (from environment_list): a confined env for almost everything (checks, build/test, executing code -- sealed: no network); reach for a looser env ONLY with a reason -- egress-allowlist (needs specific hosts), then privileged/unsafe (needs network/caps -> human sign-off), and env:\"workspace\" LAST (the live host, no sandbox -- only when you must touch the real machine). No suitable env -> environment_setup. It stops by itself when the request's work merges or it sits idle; experiment_stop frees it sooner. Advisory -- never gates a merge.",schema:i.ExperimentStartSchema,annotations:{title:"Start Experiment",readOnlyHint:!1},handler:c(a.handleExperimentStart)},{name:"experiment_stop",description:"Tear down a held experiment and its environment. Call when done (it also auto-expires when idle).",schema:i.ExperimentStopSchema,annotations:{title:"Stop Experiment",readOnlyHint:!1},handler:c(a.handleExperimentStop)},{name:"experiment_list",description:"List this project's saved experiment recipes and the experiments currently running.",schema:i.ExperimentListSchema,annotations:{title:"List Experiments",readOnlyHint:!0},handler:c(a.handleExperimentList)}]},82567(e,exports,t){async function r(e,r,n,o){const{actionServerFetch:s}=await Promise.resolve().then(()=>t(79414)),i=await s(e,{method:r,headers:{"Content-Type":"application/json"},body:"POST"===r?JSON.stringify(n):void 0,signal:AbortSignal.timeout(o)}),a=await i.json().catch(()=>({}))
1425
+ exports.experimentDefs=[{name:"experiment_start",description:"Hold a box, where commands run (run({command, box})). Returns a handle; the box stays warm across runs (servers, scratch and dependencies persist) and shows a live view of your branch at /work. In a request, files a run changes are committed at once as that run's commit. Prefer the most confined environment that fits (by name, from environment_list): a sealed box runs anything without approval. Looser only with a reason: egress-allowlist for specific hosts, privileged/unsafe with the user's sign-off, env \"workspace\" (the live host, no sandbox) last. No suitable environment: environment_setup. The box stops when the request merges or sits idle; experiment_stop frees it sooner.",schema:i.ExperimentStartSchema,annotations:{title:"Start Experiment",readOnlyHint:!1},handler:c(a.handleExperimentStart)},{name:"experiment_stop",description:"Tear down a held experiment and its environment. Call when done (it also auto-expires when idle).",schema:i.ExperimentStopSchema,annotations:{title:"Stop Experiment",readOnlyHint:!1},handler:c(a.handleExperimentStop)},{name:"experiment_list",description:"List this project's saved experiment recipes and the experiments currently running.",schema:i.ExperimentListSchema,annotations:{title:"List Experiments",readOnlyHint:!0},handler:c(a.handleExperimentList)}]},82567(e,exports,t){async function r(e,r,n,o){const{actionServerFetch:s}=await Promise.resolve().then(()=>t(79414)),i=await s(e,{method:r,headers:{"Content-Type":"application/json"},body:"POST"===r?JSON.stringify(n):void 0,signal:AbortSignal.timeout(o)}),a=await i.json().catch(()=>({}))
1404
1426
  return{ok:i.ok,status:i.status,data:a}}exports.handleExperimentStart=async function(e,o,s,i,a,c){const l=a.startTimer("tool_execution_time")
1405
1427
  try{const a=c?.sessionId
1406
1428
  if(!a)return l({success:!1,error:"no_session"}),{content:[{type:"text",text:"Cannot start an experiment without a session context."}],isError:!0}
@@ -1445,24 +1467,24 @@ try{const d=e,{path:u,comment:h}=d,{enforceActiveTask:k}=await Promise.resolve()
1445
1467
  if("errorResult"in I)return I.errorResult
1446
1468
  const{taskId:$}=I
1447
1469
  m.info(`Creating directory: ${u}`,{taskId:$,path:u,comment:h}),l.validateTaskIdFormat($,m,p,v.metricsPrefix),r.getCurrentBranchContext&&await r.getCurrentBranchContext()
1448
- const E=String(u).replace(/\\/g,"/")
1449
- let S=E,x=""
1470
+ const S=String(u).replace(/\\/g,"/")
1471
+ let E=S,_=""
1450
1472
  b=l.FileOperationPhase.TASK_VALIDATION,p.incrementCounter("create_directory_phase",1,{phase:b})
1451
- const _=await l.validateTaskId($,S,n,m,p,v.metricsPrefix),C=_?.requestId,q=await l.validateWorkItemStates($,S,n,s,r,m,p,v.metricsPrefix),T=await l.autoPromoteTaskStatus($,_.status,C,q.request?.status,S,n,s,m,p,v.metricsPrefix)
1452
- T.taskPromoted&&m.info(`Auto-promoted task ${$} (${_.title||"Untitled"}) to in_progress for file operation`,{taskId:$,taskTitle:_.title,previousStatus:_.status,newStatus:T.newTaskStatus,path:S}),T.requestPromoted&&m.info(`Auto-promoted request ${C} to in_progress for file operation`,{requestId:C,previousStatus:q.request?.status,newStatus:T.newRequestStatus,triggeringTaskId:$})
1473
+ const x=await l.validateTaskId($,E,n,m,p,v.metricsPrefix),C=x?.requestId,q=await l.validateWorkItemStates($,E,n,s,r,m,p,v.metricsPrefix),T=await l.autoPromoteTaskStatus($,x.status,C,q.request?.status,E,n,s,m,p,v.metricsPrefix)
1474
+ T.taskPromoted&&m.info(`Auto-promoted task ${$} (${x.title||"Untitled"}) to in_progress for file operation`,{taskId:$,taskTitle:x.title,previousStatus:x.status,newStatus:T.newTaskStatus,path:E}),T.requestPromoted&&m.info(`Auto-promoted request ${C} to in_progress for file operation`,{requestId:C,previousStatus:q.request?.status,newStatus:T.newRequestStatus,triggeringTaskId:$})
1453
1475
  const j=await l.ensureCorrectWorkspaceContext({taskPromoted:T.taskPromoted,requestId:C,context:r,operation:"create-directory",logger:m,metrics:p,metricsPrefix:v.metricsPrefix})
1454
- if(!j.success)throw new Error(j.error);({normalizedPath:S,fullPath:x}=l.validateAndNormalizePath(E,r))
1476
+ if(!j.success)throw new Error(j.error);({normalizedPath:E,fullPath:_}=l.validateAndNormalizePath(S,r))
1455
1477
  let P=!1
1456
- try{P=(await a.default.stat(x)).isDirectory()}catch{P=!1}b=l.FileOperationPhase.RECORD_CREATION,p.incrementCounter("create_directory_phase",1,{phase:b})
1457
- const{toolRecordId:R,localToolRecordId:A}=await l.createFileOperationRecords($,C,r.projectId,v.recordType,S,{directoryExists:P,userComment:h},n,o,m,p,v.metricsPrefix,f)
1478
+ try{P=(await a.default.stat(_)).isDirectory()}catch{P=!1}b=l.FileOperationPhase.RECORD_CREATION,p.incrementCounter("create_directory_phase",1,{phase:b})
1479
+ const{toolRecordId:R,localToolRecordId:A}=await l.createFileOperationRecords($,C,r.projectId,v.recordType,E,{directoryExists:P,userComment:h},n,o,m,p,v.metricsPrefix,f)
1458
1480
  y=R,w=R,b=v.fileOperationPhase,p.incrementCounter("create_directory_phase",1,{phase:b})
1459
1481
  let O=P
1460
- if(!P){await a.default.mkdir(x,{recursive:!0}),m.info(`Successfully created directory ${S}`)
1461
- try{const e=c.default.join(x,".gitkeep")
1482
+ if(!P){await a.default.mkdir(_,{recursive:!0}),m.info(`Successfully created directory ${E}`)
1483
+ try{const e=c.default.join(_,".gitkeep")
1462
1484
  await a.default.writeFile(e,"# This file makes the directory trackable by Git\n"),O=!0,m.info("Created .gitkeep file for Git tracking")}catch(e){m.warn("Failed to create .gitkeep file",{error:e instanceof Error?e.message:String(e)}),O=!1}}const M=Date.now()-g
1463
1485
  p.recordValue("create_directory_time",M),p.incrementCounter("create_directory_success",1),b=l.FileOperationPhase.GIT_COMMIT,p.incrementCounter("create_directory_phase",1,{phase:b})
1464
- const D=`Create directory: ${S} - ${h||"Create directory"}`,F=!P&&O?`${S}/.gitkeep`:S,N=await l.commitToGit($,F,R,A,D,r,m,p,v.metricsPrefix)
1465
- b=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,p.incrementCounter("create_directory_phase",1,{phase:b}),await l.updateFileOperationRecords($,y,w,S,v.recordType,`Successfully ${P?"verified":"created"} directory: ${S}`,{status:"completed",commitHash:N.commitHash,previousCommitHash:N.previousCommitHash,directoryExists:P,isDirectory:!0,userComment:h},`Successfully ${P?"verified":"created"} directory: ${S}`,n,o,m,p,v.metricsPrefix)
1486
+ const D=`Create directory: ${E} - ${h||"Create directory"}`,F=!P&&O?`${E}/.gitkeep`:E,N=await l.commitToGit($,F,R,A,D,r,m,p,v.metricsPrefix)
1487
+ b=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,p.incrementCounter("create_directory_phase",1,{phase:b}),await l.updateFileOperationRecords($,y,w,E,v.recordType,`Successfully ${P?"verified":"created"} directory: ${E}`,{status:"completed",commitHash:N.commitHash,previousCommitHash:N.previousCommitHash,directoryExists:P,isDirectory:!0,userComment:h},`Successfully ${P?"verified":"created"} directory: ${E}`,n,o,m,p,v.metricsPrefix)
1466
1488
  const z=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1467
1489
  return{content:[{type:"text",text:P?"Directory already exists":`Successfully created directory${N.commitHash?`\nCommit: ${N.commitHash.substring(0,8)}`:""}${z.requestId&&!z.permanentBranch?`\nBranch: ${z.requestId}`:""}`}]}}catch(t){if(e.taskId&&o)try{const n=t instanceof d.NotFoundError||t instanceof d.ValidationError||t instanceof u.ZodError
1468
1490
  await o.createToolRecord({...n?{}:{taskId:e.taskId},projectId:r.projectId,type:"create-directory",metadata:{status:"failed",timestamp:(new Date).toISOString(),path:e.path,userComment:e.comment,error:(0,h.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:String(t),phase:b,attemptedTaskId:e.taskId,taskIdValidationFailed:n},note:`Failed to create directory ${e.path} - ${(0,h.getErrorMessage)(t)}`})}catch(t){m.error("Failed to create failure record for create directory operation",{error:t instanceof Error?t.message:String(t),taskId:e.taskId,path:e.path})}const s=Date.now()-g
@@ -1481,17 +1503,17 @@ return o(t,e),t}),i=this&&this.__importDefault||function(e){return e&&e.__esModu
1481
1503
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleDeleteFiles=function(...e){return(0,f.withWorktreeWriteLock)(e[1],()=>async function(e,r,n,o,s,i){const f=o.child({tool:"delete_files"}),m=Date.now()
1482
1504
  let g,y,w=c.FileOperationPhase.VALIDATION
1483
1505
  const b={toolName:"delete-files",operationName:"delete",fileOperationPhase:c.FileOperationPhase.FILE_WRITE,recordType:"delete-files",metricsPrefix:"delete_files",successMessage:"Successfully deleted files",failureMessage:"Failed to delete files"}
1484
- try{const d=n.getTaskClient(),v=n.getToolRecordClient(),k=n.getRequestClient(),I=e,{paths:$,operations:E,comment:S}=I,{enforceActiveTask:x}=await Promise.resolve().then(()=>t(43451)),_=await x(r,n,o,e.taskId)
1485
- if("errorResult"in _)return _.errorResult
1486
- const{taskId:C}=_
1506
+ try{const d=n.getTaskClient(),v=n.getToolRecordClient(),k=n.getRequestClient(),I=e,{paths:$,operations:S,comment:E}=I,{enforceActiveTask:_}=await Promise.resolve().then(()=>t(43451)),x=await _(r,n,o,e.taskId)
1507
+ if("errorResult"in x)return x.errorResult
1508
+ const{taskId:C}=x
1487
1509
  let q=[]
1488
1510
  if($)q=$.map(e=>({type:"file",target:e}))
1489
- else{if(!E)throw new Error("Either paths or operations must be specified")
1490
- q=E.map(e=>{if(e.path)return{type:"file",target:e.path}
1511
+ else{if(!S)throw new Error("Either paths or operations must be specified")
1512
+ q=S.map(e=>{if(e.path)return{type:"file",target:e.path}
1491
1513
  if(e.documentId)return{type:"document",target:e.documentId}
1492
1514
  throw new Error("Invalid operation: neither path nor documentId specified")})}if(0===q.length)throw new Error("At least one file or document must be specified for deletion")
1493
1515
  const T=q.filter(e=>"file"===e.type),j=q.filter(e=>"document"===e.type),P=q.length
1494
- f.info("Starting mixed file and document deletion operation",{taskId:C,totalOperations:q.length,fileOperations:T.length,documentOperations:j.length,comment:S}),c.validateTaskIdFormat(C,f,s,b.metricsPrefix)
1516
+ f.info("Starting mixed file and document deletion operation",{taskId:C,totalOperations:q.length,fileOperations:T.length,documentOperations:j.length,comment:E}),c.validateTaskIdFormat(C,f,s,b.metricsPrefix)
1495
1517
  const R=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1496
1518
  f.info("Initial branch context",{initialBranch:R.branch,initialRequestId:R.requestId,isMain:R.isMain})
1497
1519
  const A=T.map(e=>String(e.target).replace(/\\/g,"/")),O=[]
@@ -1505,7 +1527,7 @@ O.push({normalized:t,full:n})}let B={hadUserMods:!1,userModsCommitHash:void 0}
1505
1527
  if(r.git)try{f.debug("[DELETE-FILES] Checking for user changes before mixed delete operations")
1506
1528
  const e=await(0,u.commitUserChangesBeforeToolOperation)(r.git,"delete-files",{requestId:F,taskId:C,workspaceManager:r.workspaceManager},f,s)
1507
1529
  e.userChangesCommitted&&(f.debug("[DELETE-FILES] User changes committed before mixed delete operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),f.info("User changes committed before mixed delete-files operation",{taskId:C,totalOperations:q.length,fileOperations:T.length,documentOperations:j.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}),B={hadUserMods:!0,userModsCommitHash:e.commitHash})}catch(e){f.debug("[DELETE-FILES] Pre-tool commit failed, continuing with mixed delete operations",{error:e instanceof Error?e.message:String(e)}),f.warn("Pre-tool user changes commit failed, continuing with mixed delete operation",{taskId:C,totalOperations:q.length,fileOperations:T.length,documentOperations:j.length,error:e instanceof Error?e.message:String(e)})}f.debug("[DELETE-FILES] Proceeding with mixed delete operations",{totalOperations:q.length,fileDeletes:T.length,documentDeletes:j.length}),w=c.FileOperationPhase.RECORD_CREATION,s.incrementCounter("delete_files_phase",1,{phase:w})
1508
- const{toolRecordId:H,localToolRecordId:U}=await c.createFileOperationRecords(C,F,r.projectId,b.recordType,M,{totalOperations:q.length,fileOperations:T.length,documentOperations:j.length,filePaths:O.map(e=>e.normalized),documentIds:j.map(e=>e.target),userComment:S,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},d,v,f,s,b.metricsPrefix,i)
1530
+ const{toolRecordId:H,localToolRecordId:U}=await c.createFileOperationRecords(C,F,r.projectId,b.recordType,M,{totalOperations:q.length,fileOperations:T.length,documentOperations:j.length,filePaths:O.map(e=>e.normalized),documentIds:j.map(e=>e.target),userComment:E,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},d,v,f,s,b.metricsPrefix,i)
1509
1531
  g=H,y=H,w=b.fileOperationPhase,s.incrementCounter("delete_files_phase",1,{phase:w})
1510
1532
  const G=[]
1511
1533
  let W=0
@@ -1523,9 +1545,9 @@ let Y=null
1523
1545
  if(W>0){w=c.FileOperationPhase.GIT_COMMIT,s.incrementCounter("delete_files_phase",1,{phase:w})
1524
1546
  const e=G.filter(e=>e.success),t=e.filter(e=>"file"===e.type).length,n=e.filter(e=>"document"===e.type).length
1525
1547
  let o
1526
- o=t>0&&n>0?`Delete ${t} files and ${n} documents - ${S}`:t>0?1===t?`Delete file - ${S}`:`Delete ${t} files - ${S}`:1===n?`Delete document - ${S}`:`Delete ${n} documents - ${S}`,Y=await c.commitToGit(C,e.filter(e=>"file"===e.type&&e.path).map(e=>e.path),H,U,o,r,f,s,b.metricsPrefix)}w=c.FileOperationPhase.RECORD_UPDATE_SUCCESS,s.incrementCounter("delete_files_phase",1,{phase:w})
1548
+ o=t>0&&n>0?`Delete ${t} files and ${n} documents - ${E}`:t>0?1===t?`Delete file - ${E}`:`Delete ${t} files - ${E}`:1===n?`Delete document - ${E}`:`Delete ${n} documents - ${E}`,Y=await c.commitToGit(C,e.filter(e=>"file"===e.type&&e.path).map(e=>e.path),H,U,o,r,f,s,b.metricsPrefix)}w=c.FileOperationPhase.RECORD_UPDATE_SUCCESS,s.incrementCounter("delete_files_phase",1,{phase:w})
1527
1549
  const J=G.filter(e=>e.success),Q=G.filter(e=>!e.success)
1528
- await c.updateFileOperationRecords(C,g,y,M,b.recordType,W===P?`Successfully deleted all ${P} operations`:`Deleted ${W}/${P} operations successfully`,{status:W===P?"completed":"partial_success",commitHash:Y?.commitHash,previousCommitHash:Y?.previousCommitHash,totalOperations:P,fileOperations:T.length,documentOperations:j.length,successCount:W,failureCount:P-W,successfulOperations:J.map(e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename})),failedOperations:Q.map(e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename,error:e.error})),userComment:S,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},W===P?`Successfully deleted all ${P} operations`:`Deleted ${W}/${P} operations successfully`,d,v,f,s,b.metricsPrefix)
1550
+ await c.updateFileOperationRecords(C,g,y,M,b.recordType,W===P?`Successfully deleted all ${P} operations`:`Deleted ${W}/${P} operations successfully`,{status:W===P?"completed":"partial_success",commitHash:Y?.commitHash,previousCommitHash:Y?.previousCommitHash,totalOperations:P,fileOperations:T.length,documentOperations:j.length,successCount:W,failureCount:P-W,successfulOperations:J.map(e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename})),failedOperations:Q.map(e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename,error:e.error})),userComment:E,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},W===P?`Successfully deleted all ${P} operations`:`Deleted ${W}/${P} operations successfully`,d,v,f,s,b.metricsPrefix)
1529
1551
  const Z=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1530
1552
  let X=""
1531
1553
  if(W===P){const e=J.filter(e=>"file"===e.type).length,t=J.filter(e=>"document"===e.type).length
@@ -1585,11 +1607,11 @@ return s(t,e),t}),a=this&&this.__importDefault||function(e){return e&&e.__esModu
1585
1607
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleInsertLines=function(...e){return(0,g.withWorktreeWriteLock)(e[1],()=>async function(e,n,o,s,i,a,m,g){const y=a.child({tool:"insert_lines"}),w=Date.now()
1586
1608
  let b,v,k=l.FileOperationPhase.VALIDATION
1587
1609
  const I={toolName:"insert-lines",operationName:"insert-lines",fileOperationPhase:l.FileOperationPhase.FILE_WRITE,recordType:"insert-lines",metricsPrefix:"insert_lines",successMessage:"Successfully inserted lines into files and documents",failureMessage:"Failed to insert lines into files and documents"}
1588
- try{const $=e,{operations:E,comment:S}=$,{enforceActiveTask:x}=await Promise.resolve().then(()=>t(43451)),_=await x(n,o,a)
1589
- if("errorResult"in _)return _.errorResult
1590
- const{taskId:C}=_
1591
- if(y.info("Inserting lines into files and documents",{taskId:C,operationCount:E.length,comment:S}),!E||0===E.length)throw new d.ValidationError("At least one operation is required")
1592
- const q=E.filter(e=>e.path),T=E.filter(e=>e.documentId)
1610
+ try{const $=e,{operations:S,comment:E}=$,{enforceActiveTask:_}=await Promise.resolve().then(()=>t(43451)),x=await _(n,o,a)
1611
+ if("errorResult"in x)return x.errorResult
1612
+ const{taskId:C}=x
1613
+ if(y.info("Inserting lines into files and documents",{taskId:C,operationCount:S.length,comment:E}),!S||0===S.length)throw new d.ValidationError("At least one operation is required")
1614
+ const q=S.filter(e=>e.path),T=S.filter(e=>e.documentId)
1593
1615
  y.info("Operation breakdown",{fileOperations:q.length,documentOperations:T.length})
1594
1616
  const j=[]
1595
1617
  for(const e of q){const{normalizedPath:t,fullPath:r}=l.validateAndNormalizePath(e.path,n)
@@ -1610,7 +1632,7 @@ if(j.length>0){y.debug("Re-validating file paths with correct workspace context"
1610
1632
  for(let e=0;e<j.length;e++){const t=j[e],r=q[e],{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(r.path,n)
1611
1633
  j[e]={...t,path:o,fullPath:s}}}if(P.length>0&&O)try{await(0,u.validateRequestStatusForDocumentOperation)(O,i,y,m,"insert-lines-document")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
1612
1634
  throw e}k=l.FileOperationPhase.RECORD_CREATION,m.incrementCounter("insert_lines_phase",1,{phase:k})
1613
- const N=[...j.map(e=>({type:"file",path:e.path,insertions:e.insertions})),...P.map(e=>({type:"document",documentId:e.documentId,insertions:e.insertions}))],{toolRecordId:z}=await l.createFileOperationRecords(C,O,n.projectId,I.recordType,R,{operationCount:E.length,fileOperations:j.length,documentOperations:P.length,operations:N,userComment:S},o,s,y,m,I.metricsPrefix,g)
1635
+ const N=[...j.map(e=>({type:"file",path:e.path,insertions:e.insertions})),...P.map(e=>({type:"document",documentId:e.documentId,insertions:e.insertions}))],{toolRecordId:z}=await l.createFileOperationRecords(C,O,n.projectId,I.recordType,R,{operationCount:S.length,fileOperations:j.length,documentOperations:P.length,operations:N,userComment:E},o,s,y,m,I.metricsPrefix,g)
1614
1636
  if(b=z,v=z,k=I.fileOperationPhase,m.incrementCounter("insert_lines_phase",1,{phase:k}),j.length>0&&n.git)try{y.debug("[INSERT-LINES] Checking for user changes before insert operations")
1615
1637
  const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"insert-lines",{requestId:O,taskId:C,workspaceManager:n.workspaceManager},y,m)
1616
1638
  e.userChangesCommitted&&(y.debug("[INSERT-LINES] User changes committed before insert operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),y.info("User changes committed before insert-lines operation",{taskId:C,fileOperationsCount:j.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){y.debug("[INSERT-LINES] Pre-tool commit failed, continuing with insertions",{error:e instanceof Error?e.message:String(e)}),y.warn("Pre-tool user changes commit failed, continuing with insert operation",{taskId:C,fileOperationsCount:j.length,error:e instanceof Error?e.message:String(e)})}y.debug("[INSERT-LINES] Proceeding with file operations",{fileCount:j.length})
@@ -1625,7 +1647,7 @@ t.filename=s?.filename
1625
1647
  const i=await c.default.readFile(o,"utf8"),{newContent:a,results:l}=await r(i,e.insertions)
1626
1648
  t.insertions=l,t.successfulInsertions=l.filter(e=>e.success).length,t.failedInsertions=l.filter(e=>!e.success).length,t.totalLinesAdded=l.filter(e=>e.success).reduce((e,t)=>e+(t.linesAdded||0),0),a!==i&&t.successfulInsertions>0?(await c.default.writeFile(o,a,"utf8"),n.documentFileManager.markForSync(e.documentId),t.success=!0,U++,y.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulInsertions:t.successfulInsertions,failedInsertions:t.failedInsertions,linesAdded:t.totalLinesAdded})):0===t.failedInsertions&&(t.success=!0),B+=t.failedInsertions,H+=t.totalLinesAdded}catch(r){t.error=(0,f.getErrorMessage)(r),t.failedInsertions=e.insertions.length,B+=t.failedInsertions,y.error("Document operation failed",{documentId:e.documentId,error:t.error})}L.push(t)}if(P.length>0)try{for(const e of P){const t=L.find(t=>t.documentId===e.documentId)
1627
1649
  t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const G=await l.handleMultiFileGitCommit(C,j,L,U,{operationType:"Insert lines",toolRecordId:v,successField:"successfulInsertions",logger:y,metrics:m,metricsPrefix:"insert_lines"},n),W=l.processMultiFileOperationResults(L,"successfulInsertions"),{successfulOperations:V,failedOperations:K,totalSuccessfulChanges:Y,overallSuccess:J,successfulFiles:Q,failedFiles:Z}=W
1628
- try{await s.updateToolRecord(v,{metadata:{path:j.length>0?j[0].path:`document:${P[0]?.documentId}`,status:J?"completed":"failed",error:J?void 0:`${K} operations failed`,totalOperations:L.length,successfulFiles:Q,failedFiles:Z,operations:N,totalSuccessfulInsertions:Y,totalFailedInsertions:B,totalLinesAdded:H,operationsWithChanges:U,processingTime:Date.now()-w,userComment:S,...j.length>0&&G.success&&G.commitHash&&{commitHash:G.commitHash,previousCommitHash:G.previousCommitHash||void 0}},note:J?`Line insertion completed: ${V}/${L.length} operations successful${j.length>0?" (committed to git)":""}`:`Line insertion partially failed: ${K}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}let X="## Line Insertion Results\n\n"
1650
+ try{await s.updateToolRecord(v,{metadata:{path:j.length>0?j[0].path:`document:${P[0]?.documentId}`,status:J?"completed":"failed",error:J?void 0:`${K} operations failed`,totalOperations:L.length,successfulFiles:Q,failedFiles:Z,operations:N,totalSuccessfulInsertions:Y,totalFailedInsertions:B,totalLinesAdded:H,operationsWithChanges:U,processingTime:Date.now()-w,userComment:E,...j.length>0&&G.success&&G.commitHash&&{commitHash:G.commitHash,previousCommitHash:G.previousCommitHash||void 0}},note:J?`Line insertion completed: ${V}/${L.length} operations successful${j.length>0?" (committed to git)":""}`:`Line insertion partially failed: ${K}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}let X="## Line Insertion Results\n\n"
1629
1651
  if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
1630
1652
  e.permanentBranch||(X+=`**Branch:** \`${e.branchName}\`\n\n`)}if(X+="**Summary:**\n",X+=`- **Files processed:** ${V+K}\n`,X+=`- **Successful operations:** ${V}\n`,X+=`- **Failed operations:** ${K}\n`,X+=`- **Total insertions:** ${Y} successful, ${B} failed\n`,X+=`- **Total Lines Added:** ${H}\n`,X+="\n",G.success&&G.commitHash&&(X+="**Git Commit:**\n",X+=`- **Commit Hash:** ${G.commitHash.substring(0,8)}\n`,X+=`- **Files Committed:** ${G.filesCommitted.length}\n`,G.previousCommitHash&&(X+=`- **Previous Commit:** ${G.previousCommitHash.substring(0,8)}\n`),X+="\n"),1===L.length){const e=L[0]
1631
1653
  if(X+=`**Operation:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,"file"===e.type?X+=`**File:** ${e.path}\n`:(X+=`**Document:** ${e.documentId}`,e.filename&&(X+=` (${e.filename})`),X+="\n"),e.error?X+=`**Error:** ${e.error}\n`:(X+=`**Insertions:** ${e.successfulInsertions} successful`,e.failedInsertions>0&&(X+=`, ${e.failedInsertions} failed`),X+="\n",X+=`**Lines Added:** ${e.totalLinesAdded}\n`),e.insertions.some(e=>!e.success)){X+="**Failed Insertion Details:**\n"
@@ -1637,7 +1659,7 @@ X+=` - ${t.type} insertion ${e}: ${t.error}\n`}}X+="\n"}}P.length>0&&(X+="\u{1f
1637
1659
  const ee=Date.now()-w
1638
1660
  return y.info("Insert lines operation completed",{duration:ee,success:J,totalOperations:L.length,successfulOperations:V,failedOperations:K}),m.incrementCounter("insert_lines_total",1,{success:J}),{content:[{type:"text",text:X}],isError:!J}}catch(t){const r=Date.now()-w
1639
1661
  if(y.error("Insert lines operation failed",{error:t instanceof Error?t.stack:String(t),duration:r,phase:k}),v)try{await s.updateToolRecord(v,{metadata:{path:e?.operations?.[0]?.path||e?.operations?.[0]?.documentId,status:"failed",error:(0,f.getErrorMessage)(t),phase:k,duration:r,userComment:e?.comment},note:`Line insertion failed: ${(0,f.getErrorMessage)(t)}`})}catch(e){}return m.incrementCounter("insert_lines_errors",1,{phase:k}),{content:[{type:"text",text:`\u274c **Insert Lines Failed:** ${(0,f.getErrorMessage)(t)}`}],isError:!0}}}(...e))}
1640
- const c=a(t(91943)),l=i(t(85967)),d=t(59860),u=t(65003),h=t(27478),p=t(99949),f=t(58824),m=t(43255),g=t(659)},2537(e,exports,t){async function r(e,t,r){if(S.has(r)&&!(t>1048576)){if(0===t)return 0
1662
+ const c=a(t(91943)),l=i(t(85967)),d=t(59860),u=t(65003),h=t(27478),p=t(99949),f=t(58824),m=t(43255),g=t(659)},2537(e,exports,t){async function r(e,t,r){if(E.has(r)&&!(t>1048576)){if(0===t)return 0
1641
1663
  try{const t=await w.default.readFile(e,"utf-8")
1642
1664
  return(0,$.getLineCount)(t)}catch{return}}}function n(e,t,r){const n="depth"in e,o=n?e:r,s=n?e.isDirectory:e.isDirectory(),i=e.name,c=s?"[\u{1f4c1} DIR]":"[\u{1f4c4} FILE]"
1643
1665
  let l=""
@@ -1706,53 +1728,53 @@ let f=v.FileOperationPhase.VALIDATION
1706
1728
  v.FileOperationPhase.FILE_WRITE
1707
1729
  try{const m=e,{path:g,levels:y=1,sortBy:k="name"}=m
1708
1730
  a.info(`Listing directory: ${g}`)
1709
- const{normalizedPath:$,fullPath:S}=v.validateAndNormalizePath(g,t)
1731
+ const{normalizedPath:$,fullPath:E}=v.validateAndNormalizePath(g,t)
1710
1732
  f=v.FileOperationPhase.FILE_WRITE
1711
- try{if(!(await w.default.stat(S)).isDirectory())return{content:[{type:"text",text:`Path is not a directory: ${g}`}],isError:!0}
1712
- await w.default.access(S,w.default.constants.R_OK)}catch(e){return e instanceof Error&&"ENOENT"===e.code?{content:[{type:"text",text:`Directory does not exist: ${g}`}],isError:!0}:e instanceof Error&&"EACCES"===e.code?{content:[{type:"text",text:`Directory not accessible: ${g}`}],isError:!0}:{content:[{type:"text",text:`Cannot access directory: ${g}`}],isError:!0}}let x,_
1713
- const C=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,q=C?await(0,E.gitVisibleFilter)(C):null
1733
+ try{if(!(await w.default.stat(E)).isDirectory())return{content:[{type:"text",text:`Path is not a directory: ${g}`}],isError:!0}
1734
+ await w.default.access(E,w.default.constants.R_OK)}catch(e){return e instanceof Error&&"ENOENT"===e.code?{content:[{type:"text",text:`Directory does not exist: ${g}`}],isError:!0}:e instanceof Error&&"EACCES"===e.code?{content:[{type:"text",text:`Directory not accessible: ${g}`}],isError:!0}:{content:[{type:"text",text:`Cannot access directory: ${g}`}],isError:!0}}let _,x
1735
+ const C=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,q=C?await(0,S.gitVisibleFilter)(C):null
1714
1736
  if(y>1){a.info(`Building tree structure for: ${$} (levels: ${y}, sortBy: ${k})`)
1715
1737
  const e=await s($,t,0,y,k,a,q)
1716
1738
  if(e.length>0){const t=i(e,k)
1717
- x=`Directory tree for ${$} (${y} levels):\n${d(l(e))}\n\n${t}`,_=u(e)}else x=`Directory ${$} is empty or inaccessible.`,_=0}else{const e=await w.default.readdir(S,{withFileTypes:!0}),s=[]
1739
+ _=`Directory tree for ${$} (${y} levels):\n${d(l(e))}\n\n${t}`,x=u(e)}else _=`Directory ${$} is empty or inaccessible.`,x=0}else{const e=await w.default.readdir(E,{withFileTypes:!0}),s=[]
1718
1740
  for(const n of e)try{const e=b.default.join($,n.name).replace(/\\/g,"/")
1719
1741
  if(!o(q,e,n.isDirectory()))continue
1720
1742
  const{fullPath:i}=v.validateAndNormalizePath(e,t),a=await w.default.stat(i),c=n.isFile()?b.default.extname(n.name).toLowerCase():""
1721
1743
  let l
1722
1744
  n.isFile()&&(l=await r(i,a.size,c)),s.push({name:n.name,isDirectory:n.isDirectory(),depth:0,size:a.size,modified:a.mtime,extension:c,lineCount:l})}catch(e){a.debug(`Excluding directory entry from listing: ${n.name}`,{reason:(0,I.getErrorMessage)(e)})}const i=h(s,k),c=i.map(e=>n({name:e.name,isDirectory:()=>e.isDirectory,isFile:()=>!e.isDirectory},k,e))
1723
- x=c.length>0?`Directory listing for ${$}:\n${d(l(i))}\n\n${c.join("\n")}`:`Directory ${$} is empty.`,_=i.length}const T=t.workspaceManager?.getCurrentContext(t.currentSessionId)
1724
- x+=`\n\nBranch: ${T?.branchName||"unknown"}`
1745
+ _=c.length>0?`Directory listing for ${$}:\n${d(l(i))}\n\n${c.join("\n")}`:`Directory ${$} is empty.`,x=i.length}const T=t.workspaceManager?.getCurrentContext(t.currentSessionId)
1746
+ _+=`\n\nBranch: ${T?.branchName||"unknown"}`
1725
1747
  const j=Date.now()-p
1726
- return c.recordValue("list_directory_time",j),c.recordValue("list_directory_entries",_),c.incrementCounter("list_directory_success",1),a.info(`Successfully listed directory: ${$}`,{entryCount:_,levels:y,time:j}),{content:[{type:"text",text:x}]}}catch(e){const t=Date.now()-p
1748
+ return c.recordValue("list_directory_time",j),c.recordValue("list_directory_entries",x),c.incrementCounter("list_directory_success",1),a.info(`Successfully listed directory: ${$}`,{entryCount:x,levels:y,time:j}),{content:[{type:"text",text:_}]}}catch(e){const t=Date.now()-p
1727
1749
  return c.recordValue("list_directory_time",t),c.incrementCounter("list_directory_errors",1),a.error("Error listing directory:",{error:(0,I.getErrorMessage)(e),time:t}),{content:[{type:"text",text:(0,I.getErrorMessage)(e)}],isError:!0}}}
1728
- const w=y(t(91943)),b=y(t(16928)),v=g(t(85967)),k=t(73253),I=t(58824),$=t(43255),E=t(2443),S=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".py",".rb",".go",".rs",".java",".kt",".scala",".c",".cpp",".h",".hpp",".cs",".html",".css",".scss",".sass",".less",".json",".yaml",".yml",".toml",".xml",".md",".txt",".csv",".sql",".sh",".bash",".zsh",".fish",".vim",".lua",".php",".swift",".m",".vue",".svelte",".astro",".env",".gitignore",".dockerignore",".config",".conf",".ini",".cfg"])},89697(e,exports,t){exports.def=void 0
1750
+ const w=y(t(91943)),b=y(t(16928)),v=g(t(85967)),k=t(73253),I=t(58824),$=t(43255),S=t(2443),E=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".py",".rb",".go",".rs",".java",".kt",".scala",".c",".cpp",".h",".hpp",".cs",".html",".css",".scss",".sass",".less",".json",".yaml",".yml",".toml",".xml",".md",".txt",".csv",".sql",".sh",".bash",".zsh",".fish",".vim",".lua",".php",".swift",".m",".vue",".svelte",".astro",".env",".gitignore",".dockerignore",".config",".conf",".ini",".cfg"])},89697(e,exports,t){exports.def=void 0
1729
1751
  const r=t(76183),n=t(17684)
1730
- exports.def={name:"manage_files",description:"Manage file and directory structure.\n\nOperations:\n\u2022 create-directory - Create directories (including nested)\n\u2022 move-files - Move or rename files and directories\n\u2022 delete-files - Delete files or archive documents\n\u2022 get-file-info - Retrieve detailed file/directory metadata\n\nSupports multiple operations in one call.",schema:r.ManageFilesSchema,annotations:{title:"Manage Files",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},handler:(e,t)=>(0,n.handleManageFiles)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.apiClient,t.logger,t.metrics,t.mcpServer)}},17684(e,exports,t){exports.handleManageFiles=async function(e,c,l,d,u,h,p,f,m){const g=p.child({tool:"manage_files"})
1752
+ exports.def={name:"manage_files",description:"Create directories, move or rename files and directories, delete files (documents are archived), or get file info; several operations per call.",schema:r.ManageFilesSchema,annotations:{title:"Manage Files",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},handler:(e,t)=>(0,n.handleManageFiles)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.apiClient,t.logger,t.metrics,t.mcpServer)}},17684(e,exports,t){exports.handleManageFiles=async function(e,c,l,d,u,h,p,f,m){const g=p.child({tool:"manage_files"})
1731
1753
  try{const a=e,{operations:y,comment:w}=a,{enforceActiveTask:b}=await Promise.resolve().then(()=>t(43451)),v=await b(c,h,p,e.taskId)
1732
1754
  if("errorResult"in v)return v.errorResult
1733
1755
  const{taskId:k}=v
1734
1756
  g.info("Processing manage-files operations",{taskId:k,operationCount:y.length,comment:w})
1735
- const I=[],$=[],E=[],S=[]
1757
+ const I=[],$=[],S=[],E=[]
1736
1758
  for(const e of y)switch(e.operationType){case"create-directory":I.push({path:e.createDirectory.path})
1737
1759
  break
1738
1760
  case"move-files":for(const t of e.moveFiles.moves)$.push(t)
1739
1761
  break
1740
- case"delete-files":E.push(...e.deleteFiles.targets)
1762
+ case"delete-files":S.push(...e.deleteFiles.targets)
1741
1763
  break
1742
- case"get-file-info":S.push({path:e.getFileInfo.path})}const x=[]
1764
+ case"get-file-info":E.push({path:e.getFileInfo.path})}const _=[]
1743
1765
  for(const e of I){g.info("Routing to create-directory handler",{path:e.path})
1744
1766
  const t=await(0,r.handleCreateDirectory)({taskId:k,path:e.path,comment:w+" [via manage-files: create-directory]"},c,l,d,u,g,f,m)
1745
- x.push({type:"create-directory",result:t})}if($.length>0){g.info("Routing to move-files handler",{operationCount:$.length})
1767
+ _.push({type:"create-directory",result:t})}if($.length>0){g.info("Routing to move-files handler",{operationCount:$.length})
1746
1768
  const e=await(0,n.handleMoveFiles)({taskId:k,operations:$,comment:w+" [via manage-files: move-files]"},c,l,d,u,g,f,m)
1747
- x.push({type:"move-files",result:e})}if(E.length>0){g.info("Routing to delete-files handler",{operationCount:E.length})
1748
- const e=await(0,i.handleDeleteFiles)({taskId:k,operations:E,comment:w+" [via manage-files: delete-files]"},c,h,g,f,m)
1749
- x.push({type:"delete-files",result:e})}for(const e of S){g.info("Routing to get-file-info handler",{path:e.path})
1769
+ _.push({type:"move-files",result:e})}if(S.length>0){g.info("Routing to delete-files handler",{operationCount:S.length})
1770
+ const e=await(0,i.handleDeleteFiles)({taskId:k,operations:S,comment:w+" [via manage-files: delete-files]"},c,h,g,f,m)
1771
+ _.push({type:"delete-files",result:e})}for(const e of E){g.info("Routing to get-file-info handler",{path:e.path})
1750
1772
  const t=await(0,o.handleGetFileInfo)({path:e.path},c,g,f)
1751
- x.push({type:"get-file-info",result:t})}const _=x.some(e=>e.result.isError),C=x.filter(e=>!e.result.isError).length,q=x.filter(e=>e.result.isError).length
1773
+ _.push({type:"get-file-info",result:t})}const x=_.some(e=>e.result.isError),C=_.filter(e=>!e.result.isError).length,q=_.filter(e=>e.result.isError).length
1752
1774
  let T="## Manage Files Results\n\n"
1753
- T+="**Summary:**\n",T+=`- **Total operation types:** ${x.length}\n`,T+=`- **Successful:** ${C}\n`,T+=`- **Failed:** ${q}\n\n`
1754
- for(const{type:e,result:t}of x)T+=`### ${e} operations\n`,T+=t.content[0].text+"\n\n"
1755
- return g.info("Manage-files operations completed",{totalOperationTypes:x.length,successCount:C,errorCount:q}),f.incrementCounter("manage_files_total",1,{success:!_}),{content:[{type:"text",text:T}],...(0,s.alchemyMeta)({kind:"manage-files",failed:_}),isError:_}}catch(e){return g.error("Manage-files operation failed",{error:e instanceof Error?e.stack:String(e)}),f.incrementCounter("manage_files_errors",1),{content:[{type:"text",text:`\u274c **Manage Files Failed:** ${(0,a.getErrorMessage)(e)}`}],isError:!0}}}
1775
+ T+="**Summary:**\n",T+=`- **Total operation types:** ${_.length}\n`,T+=`- **Successful:** ${C}\n`,T+=`- **Failed:** ${q}\n\n`
1776
+ for(const{type:e,result:t}of _)T+=`### ${e} operations\n`,T+=t.content[0].text+"\n\n"
1777
+ return g.info("Manage-files operations completed",{totalOperationTypes:_.length,successCount:C,errorCount:q}),f.incrementCounter("manage_files_total",1,{success:!x}),{content:[{type:"text",text:T}],...(0,s.alchemyMeta)({kind:"manage-files",failed:x}),isError:x}}catch(e){return g.error("Manage-files operation failed",{error:e instanceof Error?e.stack:String(e)}),f.incrementCounter("manage_files_errors",1),{content:[{type:"text",text:`\u274c **Manage Files Failed:** ${(0,a.getErrorMessage)(e)}`}],isError:!0}}}
1756
1778
  const r=t(28309),n=t(42414),o=t(93628),s=t(82272),i=t(65992),a=t(58824)},8045(e,exports,t){exports.def=void 0
1757
1779
  const r=t(76183),n=t(77456)
1758
1780
  exports.def={name:"modify_file",description:'Make targeted edits to existing files and documents.\n\nOperations:\n\u2022 replace-string - Find and replace text\n - mode: \'unique\' (default, safer - fails if multiple matches)\n - mode: \'all\' (replaces all occurrences)\n\u2022 delete-string - Remove text patterns (same modes as replace-string)\n\u2022 insert-lines - Insert content before/after/between lines\n\u2022 replace-lines - Replace specific line numbers\n\u2022 delete-lines - Remove specific line numbers\n\nLine specification (string format for line-based operations):\n- Single line: "lines": "5"\n- Multiple lines: "lines": "1,3,7,10"\n- Range: "lines": "10-15"\n\nRead file first to know line numbers. Supports multiple files and operations in one call. Works with both filesystem files (path) and tracked documents (documentId).',schema:r.ModifyFileSchema,annotations:{title:"Modify File",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},hidden:!0,handler:(e,t)=>(0,n.handleModifyFile)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},77456(e,exports,t){exports.handleModifyFile=async function(e,i,a,c,l,d,u,h){const p=d.child({tool:"modify_file"})
@@ -1770,22 +1792,22 @@ case"replace-lines":k.push({...t,replacements:e.replaceLines.replacements})
1770
1792
  break
1771
1793
  case"delete-string":I.push({...t,replacements:e.deleteString.patterns.map(e=>({old_str:e,new_str:""})),mode:e.deleteString.mode||"all"})
1772
1794
  break
1773
- case"delete-lines":$.push({...t,lines:e.deleteLines.lines})}}const E=[]
1795
+ case"delete-lines":$.push({...t,lines:e.deleteLines.lines})}}const S=[]
1774
1796
  if(b.length>0){p.info("Routing to replace-strings handler",{operationCount:b.length})
1775
1797
  const e=await(0,r.handleReplaceStrings)({taskId:w,operations:b,comment:m+" [via modify-file: replace-string]"},i,a,c,l,p,u,h)
1776
- E.push({type:"replace-string",result:e})}if(v.length>0){p.info("Routing to insert-lines handler",{operationCount:v.length})
1798
+ S.push({type:"replace-string",result:e})}if(v.length>0){p.info("Routing to insert-lines handler",{operationCount:v.length})
1777
1799
  const e=await(0,n.handleInsertLines)({taskId:w,operations:v,comment:m+" [via modify-file: insert-lines]"},i,a,c,l,p,u,h)
1778
- E.push({type:"insert-lines",result:e})}if(k.length>0){p.info("Routing to replace-lines handler",{operationCount:k.length})
1800
+ S.push({type:"insert-lines",result:e})}if(k.length>0){p.info("Routing to replace-lines handler",{operationCount:k.length})
1779
1801
  const e=await(0,o.handleReplaceLines)({taskId:w,operations:k,comment:m+" [via modify-file: replace-lines]"},i,a,c,l,p,u,h)
1780
- E.push({type:"replace-lines",result:e})}if(I.length>0){p.info("Routing delete-string to replace-strings handler",{operationCount:I.length})
1802
+ S.push({type:"replace-lines",result:e})}if(I.length>0){p.info("Routing delete-string to replace-strings handler",{operationCount:I.length})
1781
1803
  const e=await(0,r.handleReplaceStrings)({taskId:w,operations:I,comment:m+" [via modify-file: delete-string]"},i,a,c,l,p,u,h)
1782
- E.push({type:"delete-string",result:e})}if($.length>0){p.info("Processing delete-lines operations",{operationCount:$.length})
1804
+ S.push({type:"delete-string",result:e})}if($.length>0){p.info("Processing delete-lines operations",{operationCount:$.length})
1783
1805
  const e=$.map(e=>({path:e.path,documentId:e.documentId,replacements:[{lines:e.lines,content:[]}]})),t=await(0,o.handleReplaceLines)({taskId:w,operations:e,comment:m+" [via modify-file: delete-lines]"},i,a,c,l,p,u,h)
1784
- E.push({type:"delete-lines",result:t})}const S=E.some(e=>e.result.isError),x=E.filter(e=>!e.result.isError).length,_=E.filter(e=>e.result.isError).length
1806
+ S.push({type:"delete-lines",result:t})}const E=S.some(e=>e.result.isError),_=S.filter(e=>!e.result.isError).length,x=S.filter(e=>e.result.isError).length
1785
1807
  let C="## Modify File Results\n\n"
1786
- C+="**Summary:**\n",C+=`- **Total operation types:** ${E.length}\n`,C+=`- **Successful:** ${x}\n`,C+=`- **Failed:** ${_}\n\n`
1787
- for(const{type:e,result:t}of E)C+=`### ${e} operations\n`,C+=t.content[0].text+"\n\n"
1788
- return p.info("Modify-file operations completed",{totalOperationTypes:E.length,successCount:x,errorCount:_}),u.incrementCounter("modify_file_total",1,{success:!S}),{content:[{type:"text",text:C}],isError:S}}catch(e){return p.error("Modify-file operation failed",{error:e instanceof Error?e.stack:String(e)}),u.incrementCounter("modify_file_errors",1),{content:[{type:"text",text:`\u274c **Modify File Failed:** ${(0,s.getErrorMessage)(e)}`}],isError:!0}}}
1808
+ C+="**Summary:**\n",C+=`- **Total operation types:** ${S.length}\n`,C+=`- **Successful:** ${_}\n`,C+=`- **Failed:** ${x}\n\n`
1809
+ for(const{type:e,result:t}of S)C+=`### ${e} operations\n`,C+=t.content[0].text+"\n\n"
1810
+ return p.info("Modify-file operations completed",{totalOperationTypes:S.length,successCount:_,errorCount:x}),u.incrementCounter("modify_file_total",1,{success:!E}),{content:[{type:"text",text:C}],isError:E}}catch(e){return p.error("Modify-file operation failed",{error:e instanceof Error?e.stack:String(e)}),u.incrementCounter("modify_file_errors",1),{content:[{type:"text",text:`\u274c **Modify File Failed:** ${(0,s.getErrorMessage)(e)}`}],isError:!0}}}
1789
1811
  const r=t(73306),n=t(96486),o=t(86651),s=t(58824)},5459(e,exports,t){exports.def=void 0
1790
1812
  const r=t(76183),n=t(42414)
1791
1813
  exports.def={name:"move_files",description:"Move or rename multiple files and directories in a single operation. Requires a comment explaining the purpose of the modifications; commits under the active task (resolved automatically from your session). All operations are processed sequentially and committed together. If any operation fails, the tool reports which operations succeeded and which failed.",schema:r.MoveFilesSchema,annotations:{title:"Move or Rename Multiple Files",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},hidden:!0,createsRecords:!0,handler:(e,t)=>(0,n.handleMoveFiles)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},42414(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
@@ -1799,25 +1821,25 @@ return o(t,e),t}),i=this&&this.__importDefault||function(e){return e&&e.__esModu
1799
1821
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleMoveFiles=function(...e){return(0,f.withWorktreeWriteLock)(e[1],()=>async function(e,r,n,o,s,i,f,m){const g=i.child({tool:"move_files"}),y=Date.now()
1800
1822
  let w,b,v=l.FileOperationPhase.VALIDATION
1801
1823
  const k={toolName:"move-files",operationName:"write",fileOperationPhase:l.FileOperationPhase.FILE_WRITE,recordType:"move-files",metricsPrefix:"move_files",successMessage:"Successfully moved files",failureMessage:"Failed to move files"}
1802
- try{const d=e,{operations:u,comment:I}=d,{enforceActiveTask:$}=await Promise.resolve().then(()=>t(43451)),E=await $(r,n,i,e.taskId)
1803
- if("errorResult"in E)return E.errorResult
1804
- const{taskId:S}=E
1805
- if(g.info("Moving multiple files and directories",{taskId:S,operationCount:u.length,comment:I}),!u||0===u.length)throw new Error("At least one move operation must be specified")
1806
- l.validateTaskIdFormat(S,g,f,k.metricsPrefix)
1807
- const x=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1808
- g.info("Initial branch context",{initialBranch:x.branch,initialRequestId:x.requestId,isMain:x.isMain})
1809
- const _=u.map(e=>({source:String(e.source).replace(/\\/g,"/"),destination:String(e.destination).replace(/\\/g,"/")})),C=[]
1824
+ try{const d=e,{operations:u,comment:I}=d,{enforceActiveTask:$}=await Promise.resolve().then(()=>t(43451)),S=await $(r,n,i,e.taskId)
1825
+ if("errorResult"in S)return S.errorResult
1826
+ const{taskId:E}=S
1827
+ if(g.info("Moving multiple files and directories",{taskId:E,operationCount:u.length,comment:I}),!u||0===u.length)throw new Error("At least one move operation must be specified")
1828
+ l.validateTaskIdFormat(E,g,f,k.metricsPrefix)
1829
+ const _=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1830
+ g.info("Initial branch context",{initialBranch:_.branch,initialRequestId:_.requestId,isMain:_.isMain})
1831
+ const x=u.map(e=>({source:String(e.source).replace(/\\/g,"/"),destination:String(e.destination).replace(/\\/g,"/")})),C=[]
1810
1832
  v=l.FileOperationPhase.TASK_VALIDATION,f.incrementCounter("move_files_phase",1,{phase:v})
1811
- const q=await l.validateTaskId(S,_[0].source,n,g,f,k.metricsPrefix),T=q?.requestId,j=await l.validateWorkItemStates(S,_[0].source,n,s,r,g,f,k.metricsPrefix),P=await l.autoPromoteTaskStatus(S,q.status,T,j.request?.status,_[0].source,n,s,g,f,k.metricsPrefix)
1812
- P.taskPromoted&&g.info(`Auto-promoted task ${S} (${q.title||"Untitled"}) to in_progress for file operations`,{taskId:S,taskTitle:q.title,previousStatus:q.status,newStatus:P.newTaskStatus,operationCount:u.length}),P.requestPromoted&&g.info(`Auto-promoted request ${T} to in_progress for file operations`,{requestId:T,previousStatus:j.request?.status,newStatus:P.newRequestStatus,triggeringTaskId:S})
1833
+ const q=await l.validateTaskId(E,x[0].source,n,g,f,k.metricsPrefix),T=q?.requestId,j=await l.validateWorkItemStates(E,x[0].source,n,s,r,g,f,k.metricsPrefix),P=await l.autoPromoteTaskStatus(E,q.status,T,j.request?.status,x[0].source,n,s,g,f,k.metricsPrefix)
1834
+ P.taskPromoted&&g.info(`Auto-promoted task ${E} (${q.title||"Untitled"}) to in_progress for file operations`,{taskId:E,taskTitle:q.title,previousStatus:q.status,newStatus:P.newTaskStatus,operationCount:u.length}),P.requestPromoted&&g.info(`Auto-promoted request ${T} to in_progress for file operations`,{requestId:T,previousStatus:j.request?.status,newStatus:P.newRequestStatus,triggeringTaskId:E})
1813
1835
  const R=await l.ensureCorrectWorkspaceContext({taskPromoted:P.taskPromoted,requestId:T,context:r,operation:"move-files",logger:g,metrics:f,metricsPrefix:k.metricsPrefix})
1814
1836
  if(!R.success)throw new Error(R.error)
1815
- for(const e of _){const{normalizedPath:t,fullPath:n}=l.validateAndNormalizePath(e.source,r),{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(e.destination,r)
1837
+ for(const e of x){const{normalizedPath:t,fullPath:n}=l.validateAndNormalizePath(e.source,r),{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(e.destination,r)
1816
1838
  C.push({source:t,destination:o,fullSource:n,fullDestination:s})}let A={hadUserMods:!1,userModsCommitHash:void 0}
1817
1839
  if(r.git)try{g.debug("[MOVE-FILES] Checking for user changes before move operations")
1818
- const e=await(0,h.commitUserChangesBeforeToolOperation)(r.git,"move-files",{requestId:T,taskId:S,workspaceManager:r.workspaceManager},g,f)
1819
- e.userChangesCommitted&&(g.debug("[MOVE-FILES] User changes committed before move operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),g.info("User changes committed before move-files operation",{taskId:S,moveOperationsCount:C.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}),A={hadUserMods:!0,userModsCommitHash:e.commitHash})}catch(e){g.debug("[MOVE-FILES] Pre-tool commit failed, continuing with move operations",{error:e instanceof Error?e.message:String(e)}),g.warn("Pre-tool user changes commit failed, continuing with move operation",{taskId:S,moveOperationsCount:C.length,error:e instanceof Error?e.message:String(e)})}g.debug("[MOVE-FILES] Proceeding with move operations",{operationCount:C.length}),v=l.FileOperationPhase.RECORD_CREATION,f.incrementCounter("move_files_phase",1,{phase:v})
1820
- const{toolRecordId:O,localToolRecordId:M}=await l.createFileOperationRecords(S,T,r.projectId,k.recordType,C[0].source,{operationCount:u.length,operations:C.map(e=>({source:e.source,destination:e.destination})),userComment:I,hadUserMods:A.hadUserMods,userModsCommitHash:A.userModsCommitHash},n,o,g,f,k.metricsPrefix,m)
1840
+ const e=await(0,h.commitUserChangesBeforeToolOperation)(r.git,"move-files",{requestId:T,taskId:E,workspaceManager:r.workspaceManager},g,f)
1841
+ e.userChangesCommitted&&(g.debug("[MOVE-FILES] User changes committed before move operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),g.info("User changes committed before move-files operation",{taskId:E,moveOperationsCount:C.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}),A={hadUserMods:!0,userModsCommitHash:e.commitHash})}catch(e){g.debug("[MOVE-FILES] Pre-tool commit failed, continuing with move operations",{error:e instanceof Error?e.message:String(e)}),g.warn("Pre-tool user changes commit failed, continuing with move operation",{taskId:E,moveOperationsCount:C.length,error:e instanceof Error?e.message:String(e)})}g.debug("[MOVE-FILES] Proceeding with move operations",{operationCount:C.length}),v=l.FileOperationPhase.RECORD_CREATION,f.incrementCounter("move_files_phase",1,{phase:v})
1842
+ const{toolRecordId:O,localToolRecordId:M}=await l.createFileOperationRecords(E,T,r.projectId,k.recordType,C[0].source,{operationCount:u.length,operations:C.map(e=>({source:e.source,destination:e.destination})),userComment:I,hadUserMods:A.hadUserMods,userModsCommitHash:A.userModsCommitHash},n,o,g,f,k.metricsPrefix,m)
1821
1843
  w=O,b=O,v=k.fileOperationPhase,f.incrementCounter("move_files_phase",1,{phase:v})
1822
1844
  const D=[]
1823
1845
  let F=0
@@ -1830,9 +1852,9 @@ f.recordValue("move_files_time",N),f.incrementCounter("move_files_operations_tot
1830
1852
  let z=null
1831
1853
  if(F>0){v=l.FileOperationPhase.GIT_COMMIT,f.incrementCounter("move_files_phase",1,{phase:v})
1832
1854
  const e=D.filter(e=>e.success),t=1===e.length?`Move ${e[0].isDirectory?"directory":"file"} from ${e[0].operation.source} to ${e[0].operation.destination} - ${I}`:`Move ${e.length} ${1===e.length?"item":"items"} - ${I}`
1833
- z=await l.commitToGit(S,e.flatMap(e=>[e.operation.source,e.operation.destination]),O,M,t,r,g,f,k.metricsPrefix)}v=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,f.incrementCounter("move_files_phase",1,{phase:v})
1855
+ z=await l.commitToGit(E,e.flatMap(e=>[e.operation.source,e.operation.destination]),O,M,t,r,g,f,k.metricsPrefix)}v=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,f.incrementCounter("move_files_phase",1,{phase:v})
1834
1856
  const L=D.filter(e=>e.success),B=D.filter(e=>!e.success)
1835
- await l.updateFileOperationRecords(S,w,b,C[0].source,k.recordType,F===u.length?`Successfully moved all ${u.length} items`:`Moved ${F}/${u.length} items successfully`,{status:F===u.length?"completed":"partial_success",commitHash:z?.commitHash,previousCommitHash:z?.previousCommitHash,operationCount:u.length,successCount:F,failureCount:u.length-F,successfulOperations:L.map(e=>({source:e.operation.source,destination:e.operation.destination,isDirectory:e.isDirectory})),failedOperations:B.map(e=>({source:e.operation.source,destination:e.operation.destination,error:e.error})),userComment:I,hadUserMods:A.hadUserMods,userModsCommitHash:A.userModsCommitHash},F===u.length?`Successfully moved all ${u.length} items`:`Moved ${F}/${u.length} items successfully`,n,o,g,f,k.metricsPrefix)
1857
+ await l.updateFileOperationRecords(E,w,b,C[0].source,k.recordType,F===u.length?`Successfully moved all ${u.length} items`:`Moved ${F}/${u.length} items successfully`,{status:F===u.length?"completed":"partial_success",commitHash:z?.commitHash,previousCommitHash:z?.previousCommitHash,operationCount:u.length,successCount:F,failureCount:u.length-F,successfulOperations:L.map(e=>({source:e.operation.source,destination:e.operation.destination,isDirectory:e.isDirectory})),failedOperations:B.map(e=>({source:e.operation.source,destination:e.operation.destination,error:e.error})),userComment:I,hadUserMods:A.hadUserMods,userModsCommitHash:A.userModsCommitHash},F===u.length?`Successfully moved all ${u.length} items`:`Moved ${F}/${u.length} items successfully`,n,o,g,f,k.metricsPrefix)
1836
1858
  const H=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1837
1859
  let U=""
1838
1860
  if(F===u.length)U=`Successfully moved all ${u.length} ${1===u.length?"item":"items"}`
@@ -1844,7 +1866,7 @@ if(f.recordValue("move_files_time",s),f.incrementCounter("move_files_errors",1),
1844
1866
  try{await l.updateFileOperationRecords(e.taskId,w,b,e.operations?.[0]?.source||"unknown",k.recordType,"Failed to move files",{status:"failed",error:(0,p.getErrorMessage)(t),operationCount:e.operations?.length||0,operations:e.operations||[],userComment:e.comment},"Failed to move files",n,o,g,f,k.metricsPrefix)}catch(t){g.error("Failed to update record with failure status",{taskId:e.taskId,recordId:w,error:t instanceof Error?t.message:String(t)})}}return l.handleFileOperationError(t,v,g,f,e,k)}}(...e))}
1845
1867
  const a=i(t(91943)),c=i(t(16928)),l=s(t(85967)),d=t(59860),u=t(11569),h=t(27478),p=t(58824),f=t(659)},5692(e,exports,t){exports.def=void 0
1846
1868
  const r=t(76183),n=t(2965)
1847
- exports.def={name:"read_files",description:'Read the contents of multiple files and documents with flexible line selection.\n\nPDF, Word (.docx), Excel (.xlsx) and PowerPoint (.pptx) files come back as their TEXT: PDFs page by page with a marker per page, workbooks sheet by sheet as tab-separated rows, decks slide by slide with speaker notes. Line selection applies to that text. To SEE a PDF page (tables, figures, scans, layout) use view_asset with the path and the pages.\n\nONE DEFINITION BY NAME: {"path": "src/db.py", "symbol": "transform_sql"} returns just that function, method, class or constant, with the line numbers it sits on. Use it instead of guessing a line range in a large file. It works on any language by the definition\'s keyword and its braces or indentation; if the name is not defined there, the answer lists the lines that mention it. Under the definition it also lists the other tracked files that mention the name, tests and docs first, so you can see what may pin its behaviour before you change it.\n\nLINE SELECTION (string format):\n- Single line: "lines": "5"\n- Multiple lines: "lines": "1,3,7,10"\n- Range: "lines": "5-15"\n- Entire file: omit "lines" parameter\n\nEXAMPLES:\n- Read first 10 lines: {"path": "file.txt", "lines": "1-10"}\n- Read specific lines: {"path": "file.txt", "lines": "1,5,25"}\n- Read line 42 only: {"path": "file.txt", "lines": "42"}\n- Read entire file: {"path": "file.txt"}\n\nSupports both filesystem files (using \'path\') and tracked documents (using \'documentId\'). More efficient than reading files one by one. Failed reads won\'t stop the entire operation.',schema:r.ReadFilesSchema,readsProjectFiles:!0,annotations:{title:"Read Files/Documents",readOnlyHint:!0},handler:(e,t)=>(0,n.handleReadFiles)(e,t.context,t.logger,t.metrics)}},2965(e,exports,t){async function r(e,t,r,n){try{const{normalizedPath:r,fullPath:o}=u.validateAndNormalizePath(e.path,t)
1869
+ exports.def={name:"read_files",description:'Read several files (path) or tracked documents (documentId) in one call; a failed read does not stop the others. lines selects "5", "1,3,7" or "5-15"; omit it for the whole file. symbol returns just that definition (function, method, class or constant, in any language) with its line numbers, plus the other tracked files that mention the name, tests and docs first: use it instead of guessing a line range in a large file.\n\nExample: {"paths": [{"path": "src/app.ts", "lines": "5-15"}, {"path": "src/db.py", "symbol": "transform_sql"}]}\n\nPDF, Word, Excel and PowerPoint files come back as text (PDFs with a marker per page, sheets as tab-separated rows, slides with speaker notes); lines apply to that text. To SEE a page, use view_asset.',schema:r.ReadFilesSchema,readsProjectFiles:!0,annotations:{title:"Read Files/Documents",readOnlyHint:!0},handler:(e,t)=>(0,n.handleReadFiles)(e,t.context,t.logger,t.metrics)}},2965(e,exports,t){async function r(e,t,r,n){try{const{normalizedPath:r,fullPath:o}=u.validateAndNormalizePath(e.path,t)
1848
1870
  await l.default.access(o)
1849
1871
  const s=await l.default.stat(o)
1850
1872
  if(!s.isFile())return{type:"file",path:r,content:"",success:!1,error:"Path is not a file"}
@@ -1901,7 +1923,7 @@ return i.info("Multiple file/document read completed",{duration:Date.now()-a,tot
1901
1923
  return i.error("Read multiple files operation failed",{error:e instanceof Error?e.stack:String(e),duration:t}),s.incrementCounter("read_files_errors",1),{content:[{type:"text",text:`\u274c **Read Operation Failed:** ${(0,p.getErrorMessage)(e)}`}],isError:!0}}}
1902
1924
  const l=c(t(91943)),d=c(t(16928)),u=a(t(85967)),h=t(19470),p=t(58824),f=t(2503),m=t(82272),g=t(36018),y=t(73317),w=t(43255),b=t(88640),v=t(71198)},12903(e,exports,t){exports.def=void 0
1903
1925
  const r=t(76183),n=t(69442)
1904
- exports.def={name:"replace_in_files",description:'Replace text in multiple files/documents with progressive fuzzy matching in batch operations.\n\nAPI:\n\u2022 operations (required) - Array of file operations\n - path or documentId (required per operation) - Target file or document\n - replacements (required) - Array of {old, new, match_mode?} replacements\n - fuzzy_match (optional) - Override global fuzzy setting for this file\n\u2022 fuzzy_match (optional, default true) - Global fuzzy matching default\n\u2022 comment (required) - Description of changes\n\nSTRUCTURE: Each operation MUST contain a `replacements` array. Do NOT put old/new directly on the operation object.\n\nFUZZY MATCHING (progressive strategies):\n1. exact - Literal match\n2. whitespace_normalized - Collapse/trim whitespace\n3. whitespace_agnostic - Ignore all whitespace\n\nMATCH MODES (per replacement):\n\u2022 \'unique\' (default, safe) - Must find exactly 1 match. Fails with helpful candidates if multiple found.\n\u2022 \'all\' - Replaces all occurrences, returns details for each\n\nRETURNS:\nPer-operation, per-replacement results with line numbers, match types, and success/error details.\n\nEXAMPLES:\n- Single file: {"operations": [{"path": "file.ts", "replacements": [{"old": "oldAPI", "new": "newAPI"}]}], "comment": "Update API"}\n- Multi-file: {"operations": [{"path": "a.ts", "replacements": [...]}, {"path": "b.ts", "replacements": [...]}], "comment": "Refactor"}',schema:r.ReplaceInFilesSchema,annotations:{title:"Replace In Files (Fuzzy Batch)",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},createsRecords:!0,handler:(e,t)=>(0,n.handleReplaceInFiles)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},69442(e,exports,t){function r(e){return{line:e.line,actually_matched:e.actuallyMatched,match_type:e.matchType}}async function n(e,t,n,o,s,i,a){const{path:c,documentId:l,replacements:u,fuzzy_match:f}=e,m=!!l,g=void 0!==f?f:t,y={type:m?"document":"file",path:c,documentId:l,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,externalEditDebugInfo:{detectionRan:!1,reason:m?"Skipped: Document operation":n.git?"Pending":"Skipped: No Git context"}}
1926
+ exports.def={name:"replace_in_files",description:'Replace text in one or more files or documents in one call, with a comment: {"operations": [{"path": "a.ts", "replacements": [{"old": "oldAPI", "new": "newAPI"}]}], "comment": "Rename API"}. old/new go inside replacements, never on the operation; documents use documentId instead of path. Matching tries exact, then whitespace-normalized, then whitespace-agnostic (fuzzy_match false, globally or per operation, turns that off). match_mode "unique" (default) needs exactly one match and lists candidates otherwise; "all" replaces every occurrence. Returns per-replacement results with line numbers.',schema:r.ReplaceInFilesSchema,annotations:{title:"Replace In Files (Fuzzy Batch)",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},createsRecords:!0,handler:(e,t)=>(0,n.handleReplaceInFiles)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},69442(e,exports,t){function r(e){return{line:e.line,actually_matched:e.actuallyMatched,match_type:e.matchType}}async function n(e,t,n,o,s,i,a){const{path:c,documentId:l,replacements:u,fuzzy_match:f}=e,m=!!l,g=void 0!==f?f:t,y={type:m?"document":"file",path:c,documentId:l,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,externalEditDebugInfo:{detectionRan:!1,reason:m?"Skipped: Document operation":n.git?"Pending":"Skipped: No Git context"}}
1905
1927
  try{let t,o
1906
1928
  if(m){t=await n.documentFileManager.getDocumentAsFile(l)
1907
1929
  const e=n.documentFileManager.getCachedDocumentInfo(l)
@@ -1929,14 +1951,14 @@ var t={}
1929
1951
  if(null!=e)for(var r=s(e),n=0;n<r.length;n++)"default"!==r[n]&&i(t,e,r[n])
1930
1952
  return a(t,e),t}),l=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
1931
1953
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleReplaceInFiles=function(...e){return(0,k.withWorktreeWriteLock)(e[1],()=>async function(e,r,s,i,a,c,l,k){const I=c.child({tool:"replace_in_files"}),$=Date.now()
1932
- let E,S=h.FileOperationPhase.VALIDATION
1933
- try{const x=e,{enforceActiveTask:_}=await Promise.resolve().then(()=>t(43451)),C=await _(r,s,c,e.taskId)
1954
+ let S,E=h.FileOperationPhase.VALIDATION
1955
+ try{const _=e,{enforceActiveTask:x}=await Promise.resolve().then(()=>t(43451)),C=await x(r,s,c,e.taskId)
1934
1956
  if("errorResult"in C)return C.errorResult
1935
- const{taskId:q}=C,{operations:T,fuzzy_match:j=!0,comment:P}=x
1957
+ const{taskId:q}=C,{operations:T,fuzzy_match:j=!0,comment:P}=_
1936
1958
  I.info("Replace in files batch operation",{taskId:q,operationCount:T.length,totalReplacements:T.reduce((e,t)=>e+t.replacements.length,0),globalFuzzyMatch:j,comment:P})
1937
1959
  const R=T.filter(e=>e.documentId).map(e=>e.documentId)
1938
1960
  if(R.length>0&&r.apiClient)try{await(0,y.ensureDocumentsNotLocked)(R,r.apiClient,I,l,"replace_in_files-documents")}catch(e){if(e instanceof f.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
1939
- throw e}S=h.FileOperationPhase.TASK_VALIDATION,l.incrementCounter("replace_in_files_phase",1,{phase:S})
1961
+ throw e}E=h.FileOperationPhase.TASK_VALIDATION,l.incrementCounter("replace_in_files_phase",1,{phase:E})
1940
1962
  const A=T[0],O=A.documentId?`document:${A.documentId}`:A.path
1941
1963
  let M=Date.now()
1942
1964
  const D=await h.validateTaskId(q,O,s,I,l,"replace_in_files")
@@ -1960,16 +1982,16 @@ I.info("\u{1f50d} Checking for external edits",{path:t.normalizedPath,affectedLi
1960
1982
  const i=await(0,b.detectExternalEdits)(r.git,o,I)
1961
1983
  I.info("\u{1f50d} External edit detection result",{path:t.normalizedPath,hasExternalEdits:i.hasExternalEdits,conflictingLines:i.conflictingLines,hasMessage:!!i.message}),i.hasExternalEdits&&i.message?(L.set(e.path,{message:i.message,debugInfo:i.debugInfo}),I.warn("\u26a0\ufe0f External edits detected before auto-commit",{path:t.normalizedPath,conflictingLines:i.conflictingLines})):(i.debugInfo&&L.set(e.path,{message:"",debugInfo:i.debugInfo}),I.info("\u2705 No external edit conflicts detected",{path:t.normalizedPath}))}else I.info("\u2139\ufe0f No matches found for external edit detection",{path:t.normalizedPath}),L.set(e.path,{message:"",debugInfo:{detectionRan:!1,reason:"No matches found - patterns not found in file",affectedRegion:"none",uncommittedChanges:"not checked",overlapDetected:!1}})}catch(t){I.warn("Failed to check for external edits before auto-commit",{path:e.path,error:(0,v.getErrorMessage)(t)})}if(Date.now(),M=Date.now(),N&&r.git)try{I.debug("[REPLACE-IN-FILES] Checking for user changes before batch operation")
1962
1984
  const e=await(0,g.commitUserChangesBeforeToolOperation)(r.git,"replace_in_files",{requestId:F,taskId:q,workspaceManager:r.workspaceManager},I,l)
1963
- e.userChangesCommitted&&(I.debug("[REPLACE-IN-FILES] User changes committed",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),I.info("User changes committed before batch operation",{taskId:q,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){I.warn("Pre-tool user changes commit failed",{taskId:q,error:e instanceof Error?e.message:String(e)})}Date.now(),M=Date.now(),S=h.FileOperationPhase.RECORD_CREATION,l.incrementCounter("replace_in_files_phase",1,{phase:S})
1985
+ e.userChangesCommitted&&(I.debug("[REPLACE-IN-FILES] User changes committed",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),I.info("User changes committed before batch operation",{taskId:q,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){I.warn("Pre-tool user changes commit failed",{taskId:q,error:e instanceof Error?e.message:String(e)})}Date.now(),M=Date.now(),E=h.FileOperationPhase.RECORD_CREATION,l.incrementCounter("replace_in_files_phase",1,{phase:E})
1964
1986
  const{toolRecordId:B}=await h.createFileOperationRecords(q,F,r.projectId,"replace-in-files",O,{operationCount:T.length,totalReplacements:T.reduce((e,t)=>e+t.replacements.length,0),globalFuzzyMatch:j,userComment:P},s,i,I,l,"replace_in_files",k)
1965
- E=B,Date.now(),M=Date.now(),S=h.FileOperationPhase.STRING_REPLACEMENT,l.incrementCounter("replace_in_files_phase",1,{phase:S})
1987
+ S=B,Date.now(),M=Date.now(),E=h.FileOperationPhase.STRING_REPLACEMENT,l.incrementCounter("replace_in_files_phase",1,{phase:E})
1966
1988
  const H=[],U=[]
1967
1989
  for(const e of T){const t=e.path?L.get(e.path):void 0,o=await n(e,j,r,0,0,t?.message,t?.debugInfo)
1968
1990
  H.push(o),o.successfulReplacements>0&&"file"===o.type&&o.path&&U.push(o.path)}Date.now(),M=Date.now()
1969
- const G=T.filter(e=>e.path&&!e.documentId).map(e=>({path:e.path})),W=await h.handleMultiFileGitCommit(q,G,H,U.length,{operationType:"Replace in files",toolRecordId:B||E||"unknown",successField:"successfulReplacements",logger:I,metrics:l,metricsPrefix:"replace_in_files"},r),V=W.commitHash
1991
+ const G=T.filter(e=>e.path&&!e.documentId).map(e=>({path:e.path})),W=await h.handleMultiFileGitCommit(q,G,H,U.length,{operationType:"Replace in files",toolRecordId:B||S||"unknown",successField:"successfulReplacements",logger:I,metrics:l,metricsPrefix:"replace_in_files"},r),V=W.commitHash
1970
1992
  Date.now(),Date.now()
1971
1993
  const K=H.filter(e=>e.success).length,Y=H.length-K,J=H.reduce((e,t)=>e+t.successfulReplacements,0),Q=H.reduce((e,t)=>e+t.failedReplacements,0),Z=H.map(e=>({type:e.type,path:e.path,documentId:e.documentId,filename:e.filename,replacements:e.replacements.map(e=>({old_str:e.old,new_str:e.new,success:e.success,error:e.error,matchesFound:e.matchesFound,matchType:e.matchType,lines:e.lines}))})),X=H.filter(e=>e.success).map(e=>({path:e.path,documentId:e.documentId,filename:e.filename,successfulReplacements:e.successfulReplacements,failedReplacements:e.failedReplacements})),ee=H.filter(e=>!e.success).map(e=>({path:e.path,documentId:e.documentId,filename:e.filename,error:e.error,successfulReplacements:e.successfulReplacements,failedReplacements:e.failedReplacements}))
1972
- await i.updateToolRecord(E,{metadata:{path:O,status:0===Y?"completed":"partial",operationCount:T.length,operations:Z,successfulFiles:X,failedFiles:ee,successfulReplacements:J,failedReplacements:Q,processingTime:Date.now()-$,userComment:P,...W.success&&W.commitHash&&{commitHash:W.commitHash,previousCommitHash:W.previousCommitHash||void 0}}})
1994
+ await i.updateToolRecord(S,{metadata:{path:O,status:0===Y?"completed":"partial",operationCount:T.length,operations:Z,successfulFiles:X,failedFiles:ee,successfulReplacements:J,failedReplacements:Q,processingTime:Date.now()-$,userComment:P,...W.success&&W.commitHash&&{commitHash:W.commitHash,previousCommitHash:W.previousCommitHash||void 0}}})
1973
1995
  const te=await async function(e,t,r,n,s){const i=e.filter(e=>e.success).length,a=e.length-i,c=e.filter(e=>!e.success&&!e.error&&e.successfulReplacements>0).length
1974
1996
  let l="## Replace In Files - Batch Results\n\n"
1975
1997
  if(s.workspaceManager)try{const e=s.workspaceManager.getCurrentContext(s.currentSessionId)
@@ -1984,8 +2006,8 @@ for(const e of i)l+=`- \u274c \`${o(e.old,40)}\` \u2192 \`${o(e.new,40)}\`: ${e.
1984
2006
  d.length>0&&(l+=`\n**Document Changes:** ${d.length} modified document(s) synced back to tracking system.\n`)
1985
2007
  const u=s.workspaceManager?.consumeLargeFileWarning(s.currentSessionId)
1986
2008
  return u&&(l+=u),l}(H,V,J,Q,r)
1987
- return{content:[{type:"text",text:te}],isError:0===J,...(0,u.alchemyMeta)({kind:"replace-in-files",toolRecordId:E,failed:Q>0,details:{edits:H.slice(0,20).map(e=>({target:"document"===e.type?e.filename||e.documentId:e.path,applied:e.successfulReplacements,failed:e.failedReplacements,...e.error?{error:String(e.error).slice(0,200)}:{},misses:e.replacements.filter(e=>!e.success).slice(0,8).map(e=>o(e.old,60))}))}})}}catch(e){const t=Date.now()-$
1988
- if(I.error("Replace in files batch operation failed",{error:e instanceof Error?e.stack:String(e),duration:t,phase:S}),E)try{await i.updateToolRecord(E,{metadata:{status:"failed",error:(0,v.getErrorMessage)(e),phase:S,duration:t}})}catch(e){}return l.incrementCounter("replace_in_files_errors",1,{phase:S}),{content:[{type:"text",text:`\u274c **Replace In Files Failed:** ${(0,v.getErrorMessage)(e)}`}],isError:!0}}}(...e))},l(t(70857))
2009
+ return{content:[{type:"text",text:te}],isError:0===J,...(0,u.alchemyMeta)({kind:"replace-in-files",toolRecordId:S,failed:Q>0,details:{edits:H.slice(0,20).map(e=>({target:"document"===e.type?e.filename||e.documentId:e.path,applied:e.successfulReplacements,failed:e.failedReplacements,...e.error?{error:String(e.error).slice(0,200)}:{},misses:e.replacements.filter(e=>!e.success).slice(0,8).map(e=>o(e.old,60))}))}})}}catch(e){const t=Date.now()-$
2010
+ if(I.error("Replace in files batch operation failed",{error:e instanceof Error?e.stack:String(e),duration:t,phase:E}),S)try{await i.updateToolRecord(S,{metadata:{status:"failed",error:(0,v.getErrorMessage)(e),phase:E,duration:t}})}catch(e){}return l.incrementCounter("replace_in_files_errors",1,{phase:E}),{content:[{type:"text",text:`\u274c **Replace In Files Failed:** ${(0,v.getErrorMessage)(e)}`}],isError:!0}}}(...e))},l(t(70857))
1989
2011
  const d=l(t(91943)),u=(c(t(79896)),c(t(16928)),t(82272)),h=c(t(85967)),p=t(73626),f=t(59860),m=t(65003),g=t(27478),y=t(99949),w=t(29947),b=t(91318),v=t(58824),k=t(659)},16770(e,exports,t){exports.def=void 0
1990
2012
  const r=t(76183),n=t(86651)
1991
2013
  exports.def={name:"replace_lines",description:'Replace specific lines or line ranges in files and documents.\n\n\u26a0\ufe0f CRITICAL: Replaces the EXACT line numbers you specify. Always read the file first to verify what\'s on each line before replacing.\n\nLINE TARGETING:\n- Single line: {"line": 5, "content": "new content"}\n- Multiple lines: {"lines": [1, 3, 7], "content": ["line1", "line3", "line7"]}\n- Range: {"startLine": 10, "endLine": 12, "content": ["line 10", "line 11", "line 12"]}\n\nEXAMPLE: {"line": 5, "content": "updated"} replaces whatever is currently on line 5 with "updated".\n\nTargets files (using \'path\') or documents (using \'documentId\'). Multiple operations supported.',schema:r.ReplaceLinesSchema,annotations:{title:"Replace Lines in Files/Documents",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},hidden:!0,createsRecords:!0,handler:(e,t)=>(0,n.handleReplaceLines)(e,t.context,t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},86651(e,exports,t){async function r(e,t){let r=e
@@ -2004,11 +2026,11 @@ return s(t,e),t}),a=this&&this.__importDefault||function(e){return e&&e.__esModu
2004
2026
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleReplaceLines=function(...e){return(0,y.withWorktreeWriteLock)(e[1],()=>async function(e,n,o,s,i,a,m,y){const w=a.child({tool:"replace_lines"}),b=Date.now()
2005
2027
  let v,k,I=l.FileOperationPhase.VALIDATION
2006
2028
  const $={toolName:"replace-lines",operationName:"replace-lines",fileOperationPhase:l.FileOperationPhase.STRING_REPLACEMENT,recordType:"replace-lines",metricsPrefix:"replace_lines",successMessage:"Successfully replaced lines in files and documents",failureMessage:"Failed to replace lines in files and documents"}
2007
- try{const E=e,{operations:S,comment:x}=E,{enforceActiveTask:_}=await Promise.resolve().then(()=>t(43451)),C=await _(n,o,a,e.taskId)
2029
+ try{const S=e,{operations:E,comment:_}=S,{enforceActiveTask:x}=await Promise.resolve().then(()=>t(43451)),C=await x(n,o,a,e.taskId)
2008
2030
  if("errorResult"in C)return C.errorResult
2009
2031
  const{taskId:q}=C
2010
- if(w.info("Replacing lines in files and documents",{taskId:q,operationCount:S.length,comment:x}),!S||0===S.length)throw new d.ValidationError("At least one operation is required")
2011
- const T=S.filter(e=>e.path),j=S.filter(e=>e.documentId)
2032
+ if(w.info("Replacing lines in files and documents",{taskId:q,operationCount:E.length,comment:_}),!E||0===E.length)throw new d.ValidationError("At least one operation is required")
2033
+ const T=E.filter(e=>e.path),j=E.filter(e=>e.documentId)
2012
2034
  w.info("Operation breakdown",{fileOperations:T.length,documentOperations:j.length})
2013
2035
  const P=[]
2014
2036
  for(const e of T){const{normalizedPath:t,fullPath:r}=l.validateAndNormalizePath(e.path,n)
@@ -2029,7 +2051,7 @@ if(P.length>0){w.debug("Re-validating file paths with correct workspace context"
2029
2051
  for(let e=0;e<P.length;e++){const t=P[e],r=T[e],{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(r.path,n)
2030
2052
  P[e]={...t,path:o,fullPath:s}}}if(R.length>0&&M)try{await(0,u.validateRequestStatusForDocumentOperation)(M,i,w,m,"replace-lines-document")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2031
2053
  throw e}I=l.FileOperationPhase.RECORD_CREATION,m.incrementCounter("replace_lines_phase",1,{phase:I})
2032
- const z=[...P.map(e=>({type:"file",path:e.path,replacements:e.replacements})),...R.map(e=>({type:"document",documentId:e.documentId,replacements:e.replacements}))],{toolRecordId:L}=await l.createFileOperationRecords(q,M,n.projectId,$.recordType,A,{operationCount:S.length,fileOperations:P.length,documentOperations:R.length,operations:z,userComment:x},o,s,w,m,$.metricsPrefix,y)
2054
+ const z=[...P.map(e=>({type:"file",path:e.path,replacements:e.replacements})),...R.map(e=>({type:"document",documentId:e.documentId,replacements:e.replacements}))],{toolRecordId:L}=await l.createFileOperationRecords(q,M,n.projectId,$.recordType,A,{operationCount:E.length,fileOperations:P.length,documentOperations:R.length,operations:z,userComment:_},o,s,w,m,$.metricsPrefix,y)
2033
2055
  if(v=L,k=L,I=$.fileOperationPhase,m.incrementCounter("replace_lines_phase",1,{phase:I}),P.length>0&&n.git)try{w.debug("[REPLACE-LINES] Checking for user changes before replace operations")
2034
2056
  const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"replace-lines",{requestId:M,taskId:q,workspaceManager:n.workspaceManager},w,m)
2035
2057
  e.userChangesCommitted&&(w.debug("[REPLACE-LINES] User changes committed before replace operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),w.info("User changes committed before replace-lines operation",{taskId:q,fileOperationsCount:P.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){w.debug("[REPLACE-LINES] Pre-tool commit failed, continuing with replacements",{error:e instanceof Error?e.message:String(e)}),w.warn("Pre-tool user changes commit failed, continuing with replace operation",{taskId:q,fileOperationsCount:P.length,error:e instanceof Error?e.message:String(e)})}w.debug("[REPLACE-LINES] Proceeding with file operations",{fileCount:P.length})
@@ -2044,7 +2066,7 @@ t.filename=s?.filename
2044
2066
  const i=await c.default.readFile(o,"utf8"),{newContent:a,results:l}=await r(i,e.replacements)
2045
2067
  t.replacements=l,t.successfulReplacements=l.filter(e=>e.success).length,t.failedReplacements=l.filter(e=>!e.success).length,t.totalLinesReplaced=l.filter(e=>e.success).reduce((e,t)=>e+(t.linesReplaced||0),0),t.totalLinesAdded=l.filter(e=>e.success).reduce((e,t)=>e+(t.linesAdded||0),0),a!==i&&t.successfulReplacements>0?(await c.default.writeFile(o,a,"utf8"),n.documentFileManager.markForSync(e.documentId),t.success=!0,W++,w.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements,linesReplaced:t.totalLinesReplaced,linesAdded:t.totalLinesAdded})):0===t.failedReplacements&&(t.success=!0),H+=t.failedReplacements,U+=t.totalLinesReplaced,G+=t.totalLinesAdded}catch(r){t.error=(0,f.getErrorMessage)(r),t.failedReplacements=e.replacements.length,H+=t.failedReplacements,w.error("Document operation failed",{documentId:e.documentId,error:t.error})}B.push(t)}if(R.length>0)try{for(const e of R){const t=B.find(t=>t.documentId===e.documentId)
2046
2068
  t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){w.warn("Some documents may not have synced properly",{error:e})}const V=await l.handleMultiFileGitCommit(q,P,B,W,{operationType:"Replace lines",toolRecordId:k,successField:"successfulReplacements",logger:w,metrics:m,metricsPrefix:"replace_lines"},n),K=l.processMultiFileOperationResults(B,"successfulReplacements"),{successfulOperations:Y,failedOperations:J,totalSuccessfulChanges:Q,overallSuccess:Z,successfulFiles:X,failedFiles:ee}=K
2047
- try{await s.updateToolRecord(k,{metadata:{path:P.length>0?P[0].path:`document:${R[0]?.documentId}`,status:Z?"completed":"failed",error:Z?void 0:`${J} operations failed`,totalOperations:B.length,successfulFiles:X,failedFiles:ee,operations:z,totalSuccessfulReplacements:Q,totalFailedReplacements:H,totalLinesReplaced:U,totalLinesAdded:G,operationsWithChanges:W,processingTime:Date.now()-b,userComment:x,...P.length>0&&V.success&&V.commitHash&&{commitHash:V.commitHash,previousCommitHash:V.previousCommitHash||void 0}},note:Z?`Line replacement completed: ${Y}/${B.length} operations successful${P.length>0?" (committed to git)":""}`:`Line replacement partially failed: ${J}/${B.length} operations failed`})}catch(e){w.warn("Failed to update tool record",{recordError:e})}let te="## Line Replacement Results\n\n"
2069
+ try{await s.updateToolRecord(k,{metadata:{path:P.length>0?P[0].path:`document:${R[0]?.documentId}`,status:Z?"completed":"failed",error:Z?void 0:`${J} operations failed`,totalOperations:B.length,successfulFiles:X,failedFiles:ee,operations:z,totalSuccessfulReplacements:Q,totalFailedReplacements:H,totalLinesReplaced:U,totalLinesAdded:G,operationsWithChanges:W,processingTime:Date.now()-b,userComment:_,...P.length>0&&V.success&&V.commitHash&&{commitHash:V.commitHash,previousCommitHash:V.previousCommitHash||void 0}},note:Z?`Line replacement completed: ${Y}/${B.length} operations successful${P.length>0?" (committed to git)":""}`:`Line replacement partially failed: ${J}/${B.length} operations failed`})}catch(e){w.warn("Failed to update tool record",{recordError:e})}let te="## Line Replacement Results\n\n"
2048
2070
  if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
2049
2071
  e.permanentBranch||(te+=`**Branch:** \`${e.branchName}\`\n\n`)}if(te+="**Summary:**\n",te+=`- **Files processed:** ${Y+J}\n`,te+=`- **Successful operations:** ${Y}\n`,te+=`- **Failed operations:** ${J}\n`,te+=`- **Total replacements:** ${Q} successful, ${H} failed\n`,te+=`- **Total Lines Replaced:** ${U}\n`,G>0&&(te+=`- **Total Lines Added:** ${G}\n`),te+="\n",V.success&&V.commitHash&&(te+="**Git Commit:**\n",te+=`- **Commit Hash:** ${V.commitHash.substring(0,8)}\n`,te+=`- **Files Committed:** ${V.filesCommitted.length}\n`,V.previousCommitHash&&(te+=`- **Previous Commit:** ${V.previousCommitHash.substring(0,8)}\n`),te+="\n"),1===B.length){const e=B[0]
2050
2072
  if(te+=`**Operation:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,"file"===e.type?te+=`**File:** ${e.path}\n`:(te+=`**Document:** ${e.documentId}`,e.filename&&(te+=` (${e.filename})`),te+="\n"),e.error?te+=`**Error:** ${e.error}\n`:(te+=`**Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(te+=`, ${e.failedReplacements} failed`),te+="\n",te+=`**Lines Replaced:** ${e.totalLinesReplaced}\n`,e.totalLinesAdded>0&&(te+=`**Lines Added:** ${e.totalLinesAdded}\n`)),e.replacements.some(e=>!e.success)){te+="**Failed Replacement Details:**\n"
@@ -2076,11 +2098,11 @@ return i(t,e),t}),c=this&&this.__importDefault||function(e){return e&&e.__esModu
2076
2098
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleReplaceStrings=function(...e){return(0,g.withWorktreeWriteLock)(e[1],()=>async function(e,r,o,s,i,a,c,g){const y=a.child({tool:"replace_strings"}),w=Date.now()
2077
2099
  let b,v,k=d.FileOperationPhase.VALIDATION
2078
2100
  const I={toolName:"replace-strings",operationName:"replace-string",fileOperationPhase:d.FileOperationPhase.STRING_REPLACEMENT,recordType:"replace-strings",metricsPrefix:"replace_strings",successMessage:"Successfully replaced strings across files and documents",failureMessage:"Failed to replace strings across files and documents"}
2079
- try{const $=e,{operations:E,comment:S}=$,{enforceActiveTask:x}=await Promise.resolve().then(()=>t(43451)),_=await x(r,o,a,e.taskId)
2080
- if("errorResult"in _)return _.errorResult
2081
- const{taskId:C}=_
2082
- if(y.info("Replacing strings across files and documents",{taskId:C,operationCount:E.length,comment:S}),!E||0===E.length)throw new u.ValidationError("At least one operation is required")
2083
- const q=E.filter(e=>e.path),T=E.filter(e=>e.documentId)
2101
+ try{const $=e,{operations:S,comment:E}=$,{enforceActiveTask:_}=await Promise.resolve().then(()=>t(43451)),x=await _(r,o,a,e.taskId)
2102
+ if("errorResult"in x)return x.errorResult
2103
+ const{taskId:C}=x
2104
+ if(y.info("Replacing strings across files and documents",{taskId:C,operationCount:S.length,comment:E}),!S||0===S.length)throw new u.ValidationError("At least one operation is required")
2105
+ const q=S.filter(e=>e.path),T=S.filter(e=>e.documentId)
2084
2106
  y.info("Operation breakdown",{fileOperations:q.length,documentOperations:T.length})
2085
2107
  const j=[]
2086
2108
  for(const e of q){const{normalizedPath:t,fullPath:n}=d.validateAndNormalizePath(e.path,r)
@@ -2101,7 +2123,7 @@ if(j.length>0){y.debug("Re-validating file paths with correct workspace context"
2101
2123
  for(let e=0;e<j.length;e++){const t=j[e],n=q[e],{normalizedPath:o,fullPath:s}=d.validateAndNormalizePath(n.path,r)
2102
2124
  j[e]={...t,path:o,fullPath:s}}}if(P.length>0&&O)try{await(0,h.validateRequestStatusForDocumentOperation)(O,i,y,c,"replace-strings-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2103
2125
  throw e}k=d.FileOperationPhase.RECORD_CREATION,c.incrementCounter("replace_strings_phase",1,{phase:k})
2104
- const N=[...j.map(e=>({type:"file",path:e.path,replacements:e.replacements,mode:e.mode})),...P.map(e=>({type:"document",documentId:e.documentId,replacements:e.replacements,mode:e.mode}))],{toolRecordId:z}=await d.createFileOperationRecords(C,O,r.projectId,I.recordType,R,{operationCount:E.length,fileOperations:j.length,documentOperations:P.length,operations:N,userComment:S},o,s,y,c,I.metricsPrefix,g)
2126
+ const N=[...j.map(e=>({type:"file",path:e.path,replacements:e.replacements,mode:e.mode})),...P.map(e=>({type:"document",documentId:e.documentId,replacements:e.replacements,mode:e.mode}))],{toolRecordId:z}=await d.createFileOperationRecords(C,O,r.projectId,I.recordType,R,{operationCount:S.length,fileOperations:j.length,documentOperations:P.length,operations:N,userComment:E},o,s,y,c,I.metricsPrefix,g)
2105
2127
  if(b=z,v=z,k=I.fileOperationPhase,c.incrementCounter("replace_strings_phase",1,{phase:k}),j.length>0&&r.git)try{y.debug("[REPLACE-STRINGS] Checking for user changes before replace operations")
2106
2128
  const e=await(0,p.commitUserChangesBeforeToolOperation)(r.git,"replace-strings",{requestId:O,taskId:C,workspaceManager:r.workspaceManager},y,c)
2107
2129
  e.userChangesCommitted&&(y.debug("[REPLACE-STRINGS] User changes committed before replace operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),y.info("User changes committed before replace-strings operation",{taskId:C,fileOperationsCount:j.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){y.debug("[REPLACE-STRINGS] Pre-tool commit failed, continuing with replacements",{error:e instanceof Error?e.message:String(e)}),y.warn("Pre-tool user changes commit failed, continuing with replace operation",{taskId:C,fileOperationsCount:j.length,error:e instanceof Error?e.message:String(e)})}y.debug("[REPLACE-STRINGS] Proceeding with file operations",{fileCount:j.length})
@@ -2116,7 +2138,7 @@ t.filename=s?.filename
2116
2138
  const i=await l.default.readFile(o,"utf8"),{newContent:a,results:c}=await n(i,e.replacements,e.mode)
2117
2139
  t.replacements=c,t.successfulReplacements=c.filter(e=>e.success).length,t.failedReplacements=c.filter(e=>!e.success).length,a!==i?(await l.default.writeFile(o,a,"utf8"),r.documentFileManager.markForSync(e.documentId),t.success=!0,H++,y.info("Document updated successfully",{documentId:e.documentId,filename:t.filename,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements})):0===t.failedReplacements&&(t.success=!0),B+=t.failedReplacements}catch(r){t.error=(0,m.getErrorMessage)(r),t.failedReplacements=e.replacements.length,B+=t.failedReplacements,y.error("Document operation failed",{documentId:e.documentId,error:t.error})}L.push(t)}if(P.length>0)try{for(const e of P){const t=L.find(t=>t.documentId===e.documentId)
2118
2140
  t?.success&&await r.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const U=await d.handleMultiFileGitCommit(C,j,L,H,{operationType:"Replace strings",toolRecordId:v,successField:"successfulReplacements",logger:y,metrics:c,metricsPrefix:"replace_strings"},r),G=d.processMultiFileOperationResults(L,"successfulReplacements"),{successfulOperations:W,failedOperations:V,totalSuccessfulChanges:K,overallSuccess:Y,successfulFiles:J,failedFiles:Q}=G
2119
- try{await s.updateToolRecord(v,{metadata:{path:j.length>0?j[0].path:`document:${P[0]?.documentId}`,status:Y?"completed":"failed",error:Y?void 0:`${V} operations failed`,totalOperations:L.length,successfulFiles:J,failedFiles:Q,operations:N,totalSuccessfulReplacements:K,totalFailedReplacements:B,operationsWithChanges:H,processingTime:Date.now()-w,userComment:S,...j.length>0&&U.success&&U.commitHash&&{commitHash:U.commitHash,previousCommitHash:U.previousCommitHash||void 0}},note:Y?`String replacement completed: ${W}/${L.length} operations successful${j.length>0?" (committed to git)":""}`:`String replacement partially failed: ${V}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}d.formatMultiFileToolResponse("String Replacement",{successfulOperations:W,failedOperations:V,totalSuccessfulChanges:K,overallSuccess:Y,successfulFiles:J,failedFiles:Q},U,F)
2141
+ try{await s.updateToolRecord(v,{metadata:{path:j.length>0?j[0].path:`document:${P[0]?.documentId}`,status:Y?"completed":"failed",error:Y?void 0:`${V} operations failed`,totalOperations:L.length,successfulFiles:J,failedFiles:Q,operations:N,totalSuccessfulReplacements:K,totalFailedReplacements:B,operationsWithChanges:H,processingTime:Date.now()-w,userComment:E,...j.length>0&&U.success&&U.commitHash&&{commitHash:U.commitHash,previousCommitHash:U.previousCommitHash||void 0}},note:Y?`String replacement completed: ${W}/${L.length} operations successful${j.length>0?" (committed to git)":""}`:`String replacement partially failed: ${V}/${L.length} operations failed`})}catch(e){y.warn("Failed to update tool record",{recordError:e})}d.formatMultiFileToolResponse("String Replacement",{successfulOperations:W,failedOperations:V,totalSuccessfulChanges:K,overallSuccess:Y,successfulFiles:J,failedFiles:Q},U,F)
2120
2142
  let Z="## String Replacement Results\n\n"
2121
2143
  if(r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2122
2144
  e.permanentBranch||(Z+=`**Branch:** \`${e.branchName}\`\n\n`)}if(Z+="**Summary:**\n",Z+=`- **Files processed:** ${W+V}\n`,Z+=`- **Successful operations:** ${W}\n`,Z+=`- **Failed operations:** ${V}\n`,Z+=`- **Total replacements:** ${K} successful, ${B} failed\n\n`,U.success&&U.commitHash&&(Z+="**Git Commit:**\n",Z+=`- **Commit Hash:** ${U.commitHash.substring(0,8)}\n`,Z+=`- **Files Committed:** ${U.filesCommitted.length}\n`,U.previousCommitHash&&(Z+=`- **Previous Commit:** ${U.previousCommitHash.substring(0,8)}\n`),Z+="\n"),1===L.length){const e=L[0]
@@ -2129,7 +2151,7 @@ return y.info("Replace strings operation completed",{duration:X,success:Y,totalO
2129
2151
  if(y.error("Replace strings operation failed",{error:t instanceof Error?t.stack:String(t),duration:r,phase:k}),v)try{await s.updateToolRecord(v,{metadata:{path:e?.operations?.[0]?.path||e?.operations?.[0]?.documentId,status:"failed",error:(0,m.getErrorMessage)(t),phase:k,duration:r,userComment:e?.comment},note:`String replacement failed: ${(0,m.getErrorMessage)(t)}`})}catch(e){}return c.incrementCounter("replace_strings_errors",1,{phase:k}),{content:[{type:"text",text:`\u274c **Replace Strings Failed:** ${(0,m.getErrorMessage)(t)}`}],isError:!0}}}(...e))}
2130
2152
  const l=c(t(91943)),d=a(t(85967)),u=t(59860),h=t(65003),p=t(27478),f=t(99949),m=t(58824),g=t(659)},260(e,exports,t){exports.def=void 0
2131
2153
  const r=t(76183),n=t(94221)
2132
- exports.def={name:"view_asset",description:'SEE an image, PDF pages or an office document as it looks, as opposed to read_files, which returns text.\n\n\u2022 filename: an image in .alchemy_lab/assets/ (use for asset:filename.png refs in plans and documents)\n\u2022 path: an image, a PDF or an office file (Word, Excel, PowerPoint, OpenDocument, .doc/.xls/.ppt/.rtf) in the working tree; office files are rendered to PDF on this machine\n\u2022 revision (with path): the file as committed at a commit or branch; "base" is the version before this branch changed it, so you can compare how a changed document looked before and after\n\u2022 result: a recorded result (PDF, image or office file), e.g. one made in the office box and recorded with results({ operation: "record", box })\n\u2022 pages: e.g. "12-18"; at most 20 pages per view, required for longer documents\n\nA PDF is attached as a PDF, so you read each page as it looks: tables, figures, charts, scanned pages, layout. For long documents, read_files first (it returns the text with a marker per page) to find the pages worth looking at. Models without PDF input get a notice instead. The first office render on a machine builds the renderer, which takes a few minutes; you are told when that is happening.',schema:r.ViewAssetSchema,readsProjectFiles:!0,annotations:{title:"View Image, PDF or Office Pages",readOnlyHint:!0},handler:(e,t)=>(0,n.handleViewAsset)(e,t.context,t.logger,t.metrics)}},94221(e,exports,t){async function r(e){if("PK\x03\x04"===e.subarray(0,4).toString("latin1"))try{const r=(await Promise.resolve().then(()=>t.t(88194,23))).default,n=await r.loadAsync(e),o=n.file("mimetype")?(await n.file("mimetype").async("string")).trim():""
2154
+ exports.def={name:"view_asset",description:"SEE an image, PDF pages or an office document as it looks (read_files returns the text). Pass one of filename (an image in .alchemy_lab/assets/, from an asset: ref), path (an image, PDF or office file in the working tree; office files are rendered to PDF here, and the first render builds the renderer, which takes minutes) or result (a recorded result). A PDF is attached as a PDF, so tables, figures and scans read as they look; models without PDF input get a notice. For long documents, read_files first to find the pages worth seeing.",schema:r.ViewAssetSchema,readsProjectFiles:!0,annotations:{title:"View Image, PDF or Office Pages",readOnlyHint:!0},handler:(e,t)=>(0,n.handleViewAsset)(e,t.context,t.logger,t.metrics)}},94221(e,exports,t){async function r(e){if("PK\x03\x04"===e.subarray(0,4).toString("latin1"))try{const r=(await Promise.resolve().then(()=>t.t(88194,23))).default,n=await r.loadAsync(e),o=n.file("mimetype")?(await n.file("mimetype").async("string")).trim():""
2133
2155
  if("application/vnd.oasis.opendocument.text"===o)return"odt"
2134
2156
  if("application/vnd.oasis.opendocument.spreadsheet"===o)return"ods"
2135
2157
  if("application/vnd.oasis.opendocument.presentation"===o)return"odp"
@@ -2148,38 +2170,38 @@ try{i.incrementCounter("tool_calls_total",1,{tool:"view_asset"})
2148
2170
  const{filename:s,pages:c}=e,v=e.path
2149
2171
  if(1!==[s,v,e.result].filter(Boolean).length)return o("\u274c Pass exactly one of `filename` (an image in .alchemy_lab/assets/), `path` (an image, PDF or office file in the working tree) or `result` (a recorded result).")
2150
2172
  if(e.revision&&!v)return o("\u274c `revision` reads a file from git: pass it with `path`.")
2151
- let k,I,$,E,S=""
2173
+ let k,I,$,S,E=""
2152
2174
  if(e.result){const s=await async function(e,t){const r=await(0,m.readResultBytes)(e,t,52428800)
2153
2175
  return r.ok?{buffer:r.bytes,address:r.address}:{error:r.tooLarge?`${r.message} Too large to view.`:r.message}}(t.mainBasePath??t.basePath,e.result)
2154
2176
  if("error"in s)return o(`\u274c ${s.error}`)
2155
2177
  if(I=s.buffer,$=n(s.buffer),k=`result ${s.address.slice(0,12)}`,!$){const e=await r(s.buffer)
2156
2178
  if(!e)return o(`\u274c ${k} is not a PDF, an image or an office file, so there is nothing to look at. results({ operation: "read" }) shows text results.`)
2157
- E=`result-${s.address.slice(0,12)}.${e}`}}else if(s){if(!function(e){return/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9]+$/.test(e)&&!e.includes("..")}(s))return o("\u274c Invalid filename. Expected format: `<name>.<ext>` (e.g., `01JMXYZ.png`)")
2179
+ S=`result-${s.address.slice(0,12)}.${e}`}}else if(s){if(!function(e){return/^[a-zA-Z0-9_-]+\.[a-zA-Z0-9]+$/.test(e)&&!e.includes("..")}(s))return o("\u274c Invalid filename. Expected format: `<name>.<ext>` (e.g., `01JMXYZ.png`)")
2158
2180
  const e=d.join(t.basePath,h.ALCHEMY_DIR_NAME,"assets")
2159
- if(S=d.resolve(e,s),!S.startsWith(e+d.sep))return o("\u274c Invalid filename")
2181
+ if(E=d.resolve(e,s),!E.startsWith(e+d.sep))return o("\u274c Invalid filename")
2160
2182
  k=s}else{const{normalizedPath:r,fullPath:n}=p.validateAndNormalizePath(v,t)
2161
- if(S=n,k=r,e.revision){const n=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath
2183
+ if(E=n,k=r,e.revision){const n=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath
2162
2184
  let s=e.revision.trim()
2163
2185
  if("base"===s){const e=await(0,w.mergeBaseWithDefault)(n)
2164
2186
  if(!e)return o('\u274c Could not find where this branch left main, so there is no "base" version to show.')
2165
2187
  s=e}if(!(0,w.isSafeRevision)(s))return o(`\u274c "${e.revision}" is not a commit, branch or tag.`)
2166
2188
  const i=await(0,w.readFileAtRevision)(n,s,r)
2167
2189
  if(!i)return o(`\u274c ${r} is not in ${"base"===e.revision?`the base version (${s.slice(0,7)})`:s}: it may have been added later.`)
2168
- I=i,k=`${r} @ ${"base"===e.revision?`base (${s.slice(0,7)})`:s.slice(0,12)}`,$=b[d.extname(r).toLowerCase().slice(1)]}"office"===(0,g.formatFor)(r)?.render&&(E=d.basename(r))}if(E){const e=I??await l.readFile(S).catch(()=>null)
2190
+ I=i,k=`${r} @ ${"base"===e.revision?`base (${s.slice(0,7)})`:s.slice(0,12)}`,$=b[d.extname(r).toLowerCase().slice(1)]}"office"===(0,g.formatFor)(r)?.render&&(S=d.basename(r))}if(S){const e=I??await l.readFile(E).catch(()=>null)
2169
2191
  if(!e)return o(`\u274c File not found: ${k}`)
2170
- const t=await(0,y.renderViaActionServer)(e,E,"pdf")
2192
+ const t=await(0,y.renderViaActionServer)(e,S,"pdf")
2171
2193
  if(!t.ok){const e=(0,y.renderProblemText)(t,`Cannot show ${k} yet`,"read_files gives its text now.")
2172
- return t.preparing?{content:[{type:"text",text:e}]}:o(`\u274c ${e}`)}I=t.bytes,$="application/pdf",k=`${k} (rendered to PDF)`}if(!I)try{if(!(await l.stat(S)).isFile())return o(`\u274c Not a file: ${k}`)}catch{return o(`\u274c ${s?"Asset":"File"} not found: ${k}`)}const x=I?"image/svg+xml"===$?"svg":"":d.extname(S).toLowerCase().slice(1),_=$??b[x]
2173
- if(!_)return o(`\u274c view_asset shows images (png, jpg, gif, webp, svg), PDFs and office files, not .${x}. For text, use read_files.`)
2174
- if("svg"===x)return{content:[{type:"text",text:`**Asset: ${k}**\n\n\`\`\`svg\n${I?I.toString("utf-8"):await l.readFile(S,"utf-8")}\n\`\`\``}]}
2175
- const C=I??await l.readFile(S)
2176
- if("application/pdf"===_){let t
2194
+ return t.preparing?{content:[{type:"text",text:e}]}:o(`\u274c ${e}`)}I=t.bytes,$="application/pdf",k=`${k} (rendered to PDF)`}if(!I)try{if(!(await l.stat(E)).isFile())return o(`\u274c Not a file: ${k}`)}catch{return o(`\u274c ${s?"Asset":"File"} not found: ${k}`)}const _=I?"image/svg+xml"===$?"svg":"":d.extname(E).toLowerCase().slice(1),x=$??b[_]
2195
+ if(!x)return o(`\u274c view_asset shows images (png, jpg, gif, webp, svg), PDFs and office files, not .${_}. For text, use read_files.`)
2196
+ if("svg"===_)return{content:[{type:"text",text:`**Asset: ${k}**\n\n\`\`\`svg\n${I?I.toString("utf-8"):await l.readFile(E,"utf-8")}\n\`\`\``}]}
2197
+ const C=I??await l.readFile(E)
2198
+ if("application/pdf"===x){let t
2177
2199
  try{t=await(0,f.preparePdfView)(new Uint8Array(C),c)}catch(e){return o(`\u274c Cannot show ${k}: ${(0,u.getErrorMessage)(e)}.`)}if(t.bytes.byteLength>f.MAX_PDF_BYTES_PER_VIEW)return o(`\u274c Those pages of ${k} come to ${(t.bytes.byteLength/1048576).toFixed(1)} MB, over the ${f.MAX_PDF_BYTES_PER_VIEW/1048576} MB per view. Ask for fewer pages.`)
2178
2200
  const r=t.pages.length===t.totalPages?`all ${t.totalPages} pages`:`pages ${(0,f.rangeLabel)(t.pages)} of ${t.totalPages}`
2179
- return a.info("PDF viewed",{path:k,pages:(0,f.rangeLabel)(t.pages),totalPages:t.totalPages,bytes:t.bytes.byteLength}),{content:[{type:"text",text:`**PDF: ${k}** (${r}, ${(t.bytes.byteLength/1024).toFixed(0)} KB). The pages are attached; read them as they appear.`},{type:"resource",resource:{uri:`${e.result?`result:${e.result}`:`file:///${(v??k).replace(/^\/+/,"")}${e.revision?`?revision=${encodeURIComponent(e.revision)}`:""}`}${t.pages.length===t.totalPages?"":`#pages=${(0,f.rangeLabel)(t.pages)}`}`,mimeType:_,blob:Buffer.from(t.bytes).toString("base64")}}]}}return a.info("Asset viewed",{path:k,size:C.length}),{content:[{type:"text",text:`**Asset: ${k}** (${(C.length/1024).toFixed(1)} KB)`},{type:"image",data:C.toString("base64"),mimeType:_}]}}catch(e){return a.error("Failed to view asset",{error:(0,u.getErrorMessage)(e)}),o(`\u274c **Failed to view asset:** ${(0,u.getErrorMessage)(e)}`)}}
2201
+ return a.info("PDF viewed",{path:k,pages:(0,f.rangeLabel)(t.pages),totalPages:t.totalPages,bytes:t.bytes.byteLength}),{content:[{type:"text",text:`**PDF: ${k}** (${r}, ${(t.bytes.byteLength/1024).toFixed(0)} KB). The pages are attached; read them as they appear.`},{type:"resource",resource:{uri:`${e.result?`result:${e.result}`:`file:///${(v??k).replace(/^\/+/,"")}${e.revision?`?revision=${encodeURIComponent(e.revision)}`:""}`}${t.pages.length===t.totalPages?"":`#pages=${(0,f.rangeLabel)(t.pages)}`}`,mimeType:x,blob:Buffer.from(t.bytes).toString("base64")}}]}}return a.info("Asset viewed",{path:k,size:C.length}),{content:[{type:"text",text:`**Asset: ${k}** (${(C.length/1024).toFixed(1)} KB)`},{type:"image",data:C.toString("base64"),mimeType:x}]}}catch(e){return a.error("Failed to view asset",{error:(0,u.getErrorMessage)(e)}),o(`\u274c **Failed to view asset:** ${(0,u.getErrorMessage)(e)}`)}}
2180
2202
  const l=c(t(91943)),d=c(t(16928)),u=t(58824),h=t(14139),p=c(t(85967)),f=t(82926),m=t(50441),g=t(12661),y=t(73317),w=t(86605),b={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",svg:"image/svg+xml",pdf:"application/pdf"}},9030(e,exports,t){exports.def=void 0
2181
2203
  const r=t(76183),n=t(14079)
2182
- exports.def={name:"write_file",description:"Write content to files or documents. Supports both filesystem files (using 'path') and tracked documents (using 'documentId'). For files: requires a comment, creates/overwrites files, and commits to git under the active task (resolved automatically from your session). For documents: writes to cached document and syncs back to tracking system. Set append:true to add content to existing content. Handles both text and JSON content.\n\n`content` accepts a single string OR an array of lines (joined with \\n). PREFER the array form for any file containing brackets, quotes, or code-like syntax (TOML, JSON, Python, YAML, etc.): it sidesteps the most common JSON-escaping foot-gun where multi-line content with `[` or `{` confuses the JSON parser.\n\n`fromResult` instead of `content` writes a recorded result's bytes as the file, unchanged (files only, up to 20 MB). This is how a file made in a sealed box, which cannot write your worktree, lands in the repository: record it from the box, then write_file({ path, fromResult }).\n\nA file whose content starts with `#!` is written executable (git mode 100755). To make an existing script executable, write it again with its same content.",schema:r.WriteFileSchema,annotations:{title:"Write File/Document",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},createsRecords:!0,handler:(e,t)=>(0,n.handleWriteFile)(e,t.context,t.apiClient.getFileClient(),t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},14079(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
2204
+ exports.def={name:"write_file",description:"Write a file (path) or a tracked document (documentId). A file write needs a comment, creates or overwrites the file and commits it under your active task. append:true adds to the end. content is a string or an array of lines (joined with \\n); prefer the array for code, JSON, TOML or YAML, which avoids escaping mistakes. fromResult instead of content writes a recorded result's bytes unchanged: how a file made in a sealed box lands in the repository. Content starting with #! is written executable. To make an existing script executable, write it again with its same content.",schema:r.WriteFileSchema,annotations:{title:"Write File/Document",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},createsRecords:!0,handler:(e,t)=>(0,n.handleWriteFile)(e,t.context,t.apiClient.getFileClient(),t.apiClient.getTaskClient(),t.apiClient.getToolRecordClient(),t.apiClient.getRequestClient(),t.logger,t.metrics,t.mcpServer)}},14079(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
2183
2205
  var o=Object.getOwnPropertyDescriptor(t,r)
2184
2206
  o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
2185
2207
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
@@ -2188,8 +2210,8 @@ var t={}
2188
2210
  if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
2189
2211
  return o(t,e),t}),i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
2190
2212
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleWriteFile=function(...e){return(0,w.withWorktreeWriteLock)(e[1],()=>async function(e,r,n,o,s,i,w,b,v){const k=w.child({tool:"write_file"}),I=b.startTimer("tool_execution_time"),$=Date.now()
2191
- let E,S,x=d.FileOperationPhase.VALIDATION
2192
- const _={toolName:"write_file",operationName:"write",fileOperationPhase:d.FileOperationPhase.FILE_WRITE,recordType:"write-file",metricsPrefix:"write_file",successMessage:"Successfully wrote content",failureMessage:"Failed to write content"}
2213
+ let S,E,_=d.FileOperationPhase.VALIDATION
2214
+ const x={toolName:"write_file",operationName:"write",fileOperationPhase:d.FileOperationPhase.FILE_WRITE,recordType:"write-file",metricsPrefix:"write_file",successMessage:"Successfully wrote content",failureMessage:"Failed to write content"}
2193
2215
  try{const n=e
2194
2216
  let{path:C,documentId:q,content:T,format:j,append:P,comment:R,fromResult:A}=n
2195
2217
  Array.isArray(T)&&(T=T.join("\n"))
@@ -2208,22 +2230,22 @@ const Y=B?C:void 0
2208
2230
  if(L){if(!r.documentFileManager)throw new Error("Document file manager not available")
2209
2231
  W=await r.documentFileManager.getDocumentAsFile(q)
2210
2232
  const e=r.documentFileManager.getCachedDocumentInfo(q)
2211
- G=e?.filename,K=!0,V=!1}x=d.FileOperationPhase.TASK_VALIDATION,b.incrementCounter("write_file_phase",1,{phase:x})
2212
- const J=B?Y:`document:${q}`,Q=await d.validateTaskId(D,J,o,k,b,_.metricsPrefix),Z=Q?.requestId
2233
+ G=e?.filename,K=!0,V=!1}_=d.FileOperationPhase.TASK_VALIDATION,b.incrementCounter("write_file_phase",1,{phase:_})
2234
+ const J=B?Y:`document:${q}`,Q=await d.validateTaskId(D,J,o,k,b,x.metricsPrefix),Z=Q?.requestId
2213
2235
  let X=null
2214
- B&&Z&&(X=await d.validateWorkItemStates(D,J,o,i,r,k,b,_.metricsPrefix))
2236
+ B&&Z&&(X=await d.validateWorkItemStates(D,J,o,i,r,k,b,x.metricsPrefix))
2215
2237
  let ee={taskPromoted:!1,requestPromoted:!1}
2216
- B&&Z&&X&&(ee=await d.autoPromoteTaskStatus(D,Q.status,Z,X.request?.status,J,o,i,k,b,_.metricsPrefix),ee.taskPromoted&&k.info("Auto-promoted task to in_progress for file operation",{taskId:D,requestId:Z,previousStatus:Q.status,newStatus:ee.newTaskStatus,triggeringOperation:"write_file"}))
2217
- const te=await d.ensureCorrectWorkspaceContext({taskPromoted:ee.taskPromoted,requestId:Z,context:r,operation:"write_file",logger:k,metrics:b,metricsPrefix:_.metricsPrefix,requireRequestContext:B})
2238
+ B&&Z&&X&&(ee=await d.autoPromoteTaskStatus(D,Q.status,Z,X.request?.status,J,o,i,k,b,x.metricsPrefix),ee.taskPromoted&&k.info("Auto-promoted task to in_progress for file operation",{taskId:D,requestId:Z,previousStatus:Q.status,newStatus:ee.newTaskStatus,triggeringOperation:"write_file"}))
2239
+ const te=await d.ensureCorrectWorkspaceContext({taskPromoted:ee.taskPromoted,requestId:Z,context:r,operation:"write_file",logger:k,metrics:b,metricsPrefix:x.metricsPrefix,requireRequestContext:B})
2218
2240
  if(!te.success)throw new Error(te.error)
2219
2241
  if(te.switched&&k.info("Workspace context successfully established",{requestId:Z,branchName:te.branchName,operation:"write_file"}),B&&Y){const e=d.validateAndNormalizePath(Y,r)
2220
2242
  H=e.normalizedPath,U=e.fullPath,W=U
2221
2243
  try{await c.default.access(U),K=!0}catch{K=!1,V=!0}const t=(0,g.projectMapCreateProblem)(H,K)
2222
2244
  if(t)throw new u.ValidationError(t)
2223
2245
  k.debug("Path validated with correct workspace context",{rawPath:Y,normalizedPath:H,fullPath:U,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})}if(L&&Z)try{await(0,h.validateRequestStatusForDocumentOperation)(Z,i,k,b,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
2224
- throw e}x=d.FileOperationPhase.RECORD_CREATION,b.incrementCounter("write_file_phase",1,{phase:x})
2225
- const{toolRecordId:re}=await d.createFileOperationRecords(D,Z,r.projectId,_.recordType,J,{operationType:L?"document":"file",documentId:L?q:void 0,documentFilename:G,filePath:B?H:void 0,contentLength:z,...N?{fromResult:N}:{},format:j||"text",append:P||!1,wasFileCreated:!K,userComment:R},o,s,k,b,_.metricsPrefix,v)
2226
- E=re,S=re,x=_.fileOperationPhase,b.incrementCounter("write_file_phase",1,{phase:x})
2246
+ throw e}_=d.FileOperationPhase.RECORD_CREATION,b.incrementCounter("write_file_phase",1,{phase:_})
2247
+ const{toolRecordId:re}=await d.createFileOperationRecords(D,Z,r.projectId,x.recordType,J,{operationType:L?"document":"file",documentId:L?q:void 0,documentFilename:G,filePath:B?H:void 0,contentLength:z,...N?{fromResult:N}:{},format:j||"text",append:P||!1,wasFileCreated:!K,userComment:R},o,s,k,b,x.metricsPrefix,v)
2248
+ S=re,E=re,_=x.fileOperationPhase,b.incrementCounter("write_file_phase",1,{phase:_})
2227
2249
  let ne=""
2228
2250
  if(F);else if("json"===j)if("string"==typeof T)try{JSON.parse(T),ne=T}catch(e){throw new u.ValidationError("Content is not valid JSON")}else ne=JSON.stringify(T,null,2)
2229
2251
  else ne="string"==typeof T?T:JSON.stringify(T,null,2)
@@ -2236,15 +2258,15 @@ await c.default.mkdir(se,{recursive:!0}),F?await c.default.writeFile(W,F):await
2236
2258
  const ie=F?F.length:oe.length,ae=!(!B||F||P)&&await(0,f.markExecutableIfShebang)(W,oe)
2237
2259
  if(k.info("Content written successfully",{path:B?H:`document:${q}`,finalLength:ie,append:P,wasCreated:V}),L){r.documentFileManager.markForSync(q)
2238
2260
  try{await r.documentFileManager.syncDocument(q),k.info("Document synced successfully",{documentId:q})}catch(e){k.warn("Failed to sync document immediately",{documentId:q,error:e})}}let ce,le,de=null,ue=null,he=!1
2239
- if(B){if(x=d.FileOperationPhase.GIT_COMMIT,b.incrementCounter("write_file_phase",1,{phase:x}),r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2240
- le=e.branchName,he=e.permanentBranch,k.debug("Retrieved branch name from workspace manager",{branchName:le,requestId:e.requestId,mode:e.mode,isMainBranch:e.isMainBranch})}try{const e=`${V?"Create":P?"Append to":"Update"} ${H} (${S})`,t=await d.commitToGit(D,H,S,S,e,r,k,b,_.metricsPrefix)
2241
- ce=t.commitHash,ue=t.previousCommitHash,k.info("Changes committed to git via shared utility",{path:H,commitMessage:e,commitHash:ce?.substring(0,8),previousCommitHash:ue?.substring(0,8),branchName:le,toolRecordId:S})}catch(e){k.warn("Git operations failed via shared utility",{error:e}),de=(0,m.getErrorMessage)(e)}}try{await s.updateToolRecord(S,{metadata:{path:B?H:`document:${q}`,status:"completed",finalContentLength:ie,...N?{fromResult:N}:{},wasFileCreated:V,wasAppended:P&&K,operationType:L?"document":"file",processingTime:Date.now()-$,userComment:R,...B&&ce&&{commitHash:ce,previousCommitHash:ue||void 0},...B&&de?{commitFailed:!0,commitFailedReason:de}:null},note:L?"Write operation completed successfully (document synced)":de?"Write landed on disk but the git commit FAILED -- the change is uncommitted":"Write operation completed successfully (committed to git)"})}catch(e){k.warn("Failed to update tool record",{recordError:e})}let pe=`**${V?"Created":P?"Appended to":"Updated"} ${L?`document ${q}${G?` (${G})`:""}`:`file ${H}`}**\n`
2261
+ if(B){if(_=d.FileOperationPhase.GIT_COMMIT,b.incrementCounter("write_file_phase",1,{phase:_}),r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2262
+ le=e.branchName,he=e.permanentBranch,k.debug("Retrieved branch name from workspace manager",{branchName:le,requestId:e.requestId,mode:e.mode,isMainBranch:e.isMainBranch})}try{const e=`${V?"Create":P?"Append to":"Update"} ${H} (${E})`,t=await d.commitToGit(D,H,E,E,e,r,k,b,x.metricsPrefix)
2263
+ ce=t.commitHash,ue=t.previousCommitHash,k.info("Changes committed to git via shared utility",{path:H,commitMessage:e,commitHash:ce?.substring(0,8),previousCommitHash:ue?.substring(0,8),branchName:le,toolRecordId:E})}catch(e){k.warn("Git operations failed via shared utility",{error:e}),de=(0,m.getErrorMessage)(e)}}try{await s.updateToolRecord(E,{metadata:{path:B?H:`document:${q}`,status:"completed",finalContentLength:ie,...N?{fromResult:N}:{},wasFileCreated:V,wasAppended:P&&K,operationType:L?"document":"file",processingTime:Date.now()-$,userComment:R,...B&&ce&&{commitHash:ce,previousCommitHash:ue||void 0},...B&&de?{commitFailed:!0,commitFailedReason:de}:null},note:L?"Write operation completed successfully (document synced)":de?"Write landed on disk but the git commit FAILED -- the change is uncommitted":"Write operation completed successfully (committed to git)"})}catch(e){k.warn("Failed to update tool record",{recordError:e})}let pe=`**${V?"Created":P?"Appended to":"Updated"} ${L?`document ${q}${G?` (${G})`:""}`:`file ${H}`}**\n`
2242
2264
  pe+=N?`**From result:** ${N.slice(0,12)} (${ie.toLocaleString()} bytes, unchanged)\n`:`**Content Length:** ${oe.length} characters\n`,B&&le&&(he||(pe+=`**Branch:** ${le}\n`)),ae&&(pe+="**Mode:** executable (100755), because it starts with #!\n"),B&&de&&(pe+=`\u26a0\ufe0f **NOT COMMITTED:** the file is written on disk but the git commit failed (${de}). Do not complete this work until the change is committed.\n`),B&&ce&&(pe+=`**Commit Hash:** ${ce.substring(0,8)}\n`,ue&&(pe+=`**Previous Commit:** ${ue.substring(0,8)}\n`)),L&&(pe+=`\n**Document Update:** The document content has been updated and synced back to the tracking system. You can continue editing using other file modification tools with documentId \`${q}\`.`)
2243
2265
  const fe=Date.now()
2244
2266
  k.info("Write operation completed successfully",{duration:fe-$,path:B?H:`document:${q}`,contentLength:ie,wasCreated:V}),I({tool:"write_file",success:!0}),b.incrementCounter("write_file_total",1,{success:!0})
2245
2267
  const me=r.workspaceManager?.consumeLargeFileWarning(r.currentSessionId)
2246
- return me&&(pe+=me),B&&H&&/\.(md|markdown|rst|txt|html?)$/i.test(H)&&/https?:\/\/(127\.0\.0\.1|localhost)(:[0-9]+)?/i.test(oe)&&(pe+="\n\nNote: this file carries a localhost / 127.0.0.1 address. A preview address dies with its server; in prose the user keeps, point at the preview by its ref (live:<name>) or at the file itself."),{content:[{type:"text",text:pe}],...(0,l.alchemyMeta)({kind:"write-file",toolRecordId:S,failed:!1})}}catch(t){const r=Date.now()-$
2247
- if(k.error("Write operation failed",{error:t instanceof Error?t.stack:String(t),duration:r,phase:x}),S)try{await s.updateToolRecord(S,{metadata:{path:e?.path,status:"failed",error:(0,m.getErrorMessage)(t),phase:x,duration:r,userComment:e?.comment},note:`Write operation failed: ${(0,m.getErrorMessage)(t)}`})}catch(e){}return I({tool:"write_file",success:!1}),b.incrementCounter("write_file_errors",1,{phase:x}),{content:[{type:"text",text:`\u274c **Write Operation Failed:** ${(0,m.getErrorMessage)(t)}`}],isError:!0}}}(...e))}
2268
+ return me&&(pe+=me),B&&H&&/\.(md|markdown|rst|txt|html?)$/i.test(H)&&/https?:\/\/(127\.0\.0\.1|localhost)(:[0-9]+)?/i.test(oe)&&(pe+="\n\nNote: this file carries a localhost / 127.0.0.1 address. A preview address dies with its server; in prose the user keeps, point at the preview by its ref (live:<name>) or at the file itself."),{content:[{type:"text",text:pe}],...(0,l.alchemyMeta)({kind:"write-file",toolRecordId:E,failed:!1})}}catch(t){const r=Date.now()-$
2269
+ if(k.error("Write operation failed",{error:t instanceof Error?t.stack:String(t),duration:r,phase:_}),E)try{await s.updateToolRecord(E,{metadata:{path:e?.path,status:"failed",error:(0,m.getErrorMessage)(t),phase:_,duration:r,userComment:e?.comment},note:`Write operation failed: ${(0,m.getErrorMessage)(t)}`})}catch(e){}return I({tool:"write_file",success:!1}),b.incrementCounter("write_file_errors",1,{phase:_}),{content:[{type:"text",text:`\u274c **Write Operation Failed:** ${(0,m.getErrorMessage)(t)}`}],isError:!0}}}(...e))}
2248
2270
  const a=i(t(16928)),c=i(t(91943)),l=t(82272),d=s(t(85967)),u=t(59860),h=t(65003),p=t(27478),f=t(97220),m=t(58824),g=t(2503),y=t(50441),w=t(659)},92456(e,exports,t){function r(e){const t=e.match(/^(.*)\{(.*) => (.*)\}(.*)$/)
2249
2271
  if(t)return`${t[1]}${t[3]}${t[4]}`.replace(/\/\/+/g,"/").replace(/^\//,"")
2250
2272
  const r=e.split(" => ")
@@ -2295,13 +2317,13 @@ let r=null
2295
2317
  for(const n of e)r&&r.commitHash===n.commitHash?r.endLine=n.lineNumber:(r&&t.push(r),r={startLine:n.lineNumber,endLine:n.lineNumber,commitHash:n.commitHash,author:n.author,authorEmail:n.authorEmail,date:new Date(1e3*n.timestamp),commitMessage:n.commitMessage||"",firstLineContent:n.content,recordId:null,taskId:null,taskTitle:null,requestId:null,requestTitle:null})
2296
2318
  return r&&t.push(r),t}(I)
2297
2319
  c.info("Parsed blame output",{totalLines:I.length,groups:$.length})
2298
- const E=new Set($.map(e=>e.commitHash)),S=new Map
2299
- for(const e of E){const t=$.find(t=>t.commitHash===e)
2320
+ const S=new Set($.map(e=>e.commitHash)),E=new Map
2321
+ for(const e of S){const t=$.find(t=>t.commitHash===e)
2300
2322
  if(!t)continue
2301
2323
  const r=(0,l.extractRecordId)(t.commitMessage)
2302
2324
  if(r)try{const t=await(0,l.getCachedTraceabilityInfo)(r,s,c)
2303
- S.set(e,t)}catch(t){c.warn("Failed to get traceability info",{recordId:r,error:(0,h.getErrorMessage)(t)}),S.set(e,null)}else S.set(e,null)}for(const e of $){const t=S.get(e.commitHash)
2304
- t&&(e.recordId=t.record?.id||null,e.taskId=t.task?.id||null,e.taskTitle=t.task?.title||null,e.requestId=t.request?.id||null,e.requestTitle=t.request?.title||null)}const x=function(e,t,s){let i=`## Blame: ${e}${s?` (lines ${s.start}-${s.end})`:""}\n\n`
2325
+ E.set(e,t)}catch(t){c.warn("Failed to get traceability info",{recordId:r,error:(0,h.getErrorMessage)(t)}),E.set(e,null)}else E.set(e,null)}for(const e of $){const t=E.get(e.commitHash)
2326
+ t&&(e.recordId=t.record?.id||null,e.taskId=t.task?.id||null,e.taskTitle=t.task?.title||null,e.requestId=t.request?.id||null,e.requestTitle=t.request?.title||null)}const _=function(e,t,s){let i=`## Blame: ${e}${s?` (lines ${s.start}-${s.end})`:""}\n\n`
2305
2327
  if(0===t.length)return i+="_No blame information available._\n",i
2306
2328
  for(const e of t){const t=e.startLine===e.endLine?`Line ${e.startLine}`:`Lines ${e.startLine}-${e.endLine}`
2307
2329
  let s
@@ -2316,8 +2338,8 @@ if(d+=t,e.requestId){const r=a.get(e.requestId)
2316
2338
  r?r.lines+=t:a.set(e.requestId,{title:e.requestTitle||"Untitled",lines:t})}else e.recordId||r(e.commitMessage)?c+=t:l+=t}if(a.size>0||c>0||l>0){i+="## Summary by Request\n\n"
2317
2339
  const e=[...a.entries()].sort((e,t)=>t[1].lines-e[1].lines)
2318
2340
  for(const[t,r]of e){const e=Math.round(r.lines/d*100)
2319
- i+=`- **${t}** "${n(r.title,40)}": ${r.lines} lines (${e}%)\n`}c>0&&(i+=`- **Alchemy (unlinked)**: ${c} lines (${Math.round(c/d*100)}%)\n`),l>0&&(i+=`- **External commits**: ${l} lines (${Math.round(l/d*100)}%)\n`)}return i}(w,$,b),_=Date.now()-p
2320
- return c.info("Blame request completed",{duration:_,groups:$.length,alchemyCommits:$.filter(e=>e.requestId).length,externalCommits:$.filter(e=>!e.requestId).length}),a.incrementCounter("file_blame_total",1,{success:!0}),{content:[{type:"text",text:x}]}}catch(e){return c.error("Blame request failed",{error:e instanceof Error?e.stack:String(e)}),a.incrementCounter("file_blame_errors",1),{content:[{type:"text",text:`\u274c **Blame Failed:** ${(0,h.getErrorMessage)(e)}`}],isError:!0}}}
2341
+ i+=`- **${t}** "${n(r.title,40)}": ${r.lines} lines (${e}%)\n`}c>0&&(i+=`- **Alchemy (unlinked)**: ${c} lines (${Math.round(c/d*100)}%)\n`),l>0&&(i+=`- **External commits**: ${l} lines (${Math.round(l/d*100)}%)\n`)}return i}(w,$,b),x=Date.now()-p
2342
+ return c.info("Blame request completed",{duration:x,groups:$.length,alchemyCommits:$.filter(e=>e.requestId).length,externalCommits:$.filter(e=>!e.requestId).length}),a.incrementCounter("file_blame_total",1,{success:!0}),{content:[{type:"text",text:_}]}}catch(e){return c.error("Blame request failed",{error:e instanceof Error?e.stack:String(e)}),a.incrementCounter("file_blame_errors",1),{content:[{type:"text",text:`\u274c **Blame Failed:** ${(0,h.getErrorMessage)(e)}`}],isError:!0}}}
2321
2343
  const l=t(61181),d=c(t(16928)),u=c(t(91943)),h=t(58824)},72085(e,exports,t){exports.getRequestTimelineForFile=async function(e,t,s,i){let a=`# File History: ${t}\n\n`
2322
2344
  a+="**Context**: Main branch (request-level view)\n\n"
2323
2345
  try{const o=await e.log({file:t,n:100})
@@ -2419,25 +2441,25 @@ try{const{normalizedPath:w,fullPath:b}=c.validateAndNormalizePath(r,s),v=s.works
2419
2441
  if(s.mainBasePath&&t(16928).resolve(v)===t(16928).resolve(s.mainBasePath))return{content:[{type:"text",text:`Refused: recovering ${w} would write into the user's project folder, because this session has no worktree. Recover it from inside the request that owns the change.`}],isError:!0}
2420
2442
  const k=await c.validateTaskId(e,w,u.getTaskClient(),p,f,"file-recover"),{toolRecordId:I}=await c.createFileOperationRecords(e,k?.requestId,s.projectId,"file-recover",w,{operationType:"file",filePath:w,commitHash:n,createBackup:o},u.getTaskClient(),h,p,f,"file-recover",g)
2421
2443
  y=I
2422
- let $,E=!1
2423
- try{await a.access(b),E=!0,o&&($=`${b}.backup-${(new Date).toISOString().replace(/[:.]/g,"-")}`,await a.copyFile(b,$),p.info("Created backup",{originalPath:b,backupPath:$}))}catch{E=!1}const S=s.git
2424
- if(!S)throw new Error("Git is not available")
2425
- if(!(await(0,l.validateCommitHash)(S,n,p,"file-recover")).valid)throw new Error(`Commit hash '${n}' is invalid or not found`)
2426
- let x
2427
- try{x=await S.show([`${n}:${w}`])}catch(e){const t=(0,d.getErrorMessage)(e)
2444
+ let $,S=!1
2445
+ try{await a.access(b),S=!0,o&&($=`${b}.backup-${(new Date).toISOString().replace(/[:.]/g,"-")}`,await a.copyFile(b,$),p.info("Created backup",{originalPath:b,backupPath:$}))}catch{S=!1}const E=s.git
2446
+ if(!E)throw new Error("Git is not available")
2447
+ if(!(await(0,l.validateCommitHash)(E,n,p,"file-recover")).valid)throw new Error(`Commit hash '${n}' is invalid or not found`)
2448
+ let _
2449
+ try{_=await E.show([`${n}:${w}`])}catch(e){const t=(0,d.getErrorMessage)(e)
2428
2450
  if(t.includes("does not exist")||t.includes("not found")||t.includes("Path"))throw new Error(`File '${w}' does not exist in commit '${n}'`)
2429
- throw new Error(`Failed to retrieve file '${w}' from commit '${n}': ${t}`)}await a.writeFile(b,x,"utf8")
2430
- let _={}
2451
+ throw new Error(`Failed to retrieve file '${w}' from commit '${n}': ${t}`)}await a.writeFile(b,_,"utf8")
2452
+ let x={}
2431
2453
  try{const t=`Recover ${w} from ${n.substring(0,8)}`
2432
- _=await c.commitToGit(e,w,y,y,t,s,p,f,"file-recover"),p.info("Recovery committed via shared utility",{path:w,sourceCommitHash:n,newCommitHash:_.commitHash?.substring(0,8),commitMessage:t})}catch(e){p.warn("Failed to commit recovery via shared utility",{error:e})}if(y)try{await h.updateToolRecord(y,{metadata:{path:w,status:"completed",recoveredLength:x.length,hadBackup:o&&E,backupPath:$,wasFileCreated:!E,processingTime:Date.now()-m,..._.commitHash&&{commitHash:_.commitHash,previousCommitHash:_.previousCommitHash||void 0}},note:`File recovered successfully from ${n.substring(0,8)}`})}catch(e){p.warn("Failed to update tool record",{recordError:e})}const C=E?"recovered":"created from recovery"
2454
+ x=await c.commitToGit(e,w,y,y,t,s,p,f,"file-recover"),p.info("Recovery committed via shared utility",{path:w,sourceCommitHash:n,newCommitHash:x.commitHash?.substring(0,8),commitMessage:t})}catch(e){p.warn("Failed to commit recovery via shared utility",{error:e})}if(y)try{await h.updateToolRecord(y,{metadata:{path:w,status:"completed",recoveredLength:_.length,hadBackup:o&&S,backupPath:$,wasFileCreated:!S,processingTime:Date.now()-m,...x.commitHash&&{commitHash:x.commitHash,previousCommitHash:x.previousCommitHash||void 0}},note:`File recovered successfully from ${n.substring(0,8)}`})}catch(e){p.warn("Failed to update tool record",{recordError:e})}const C=S?"recovered":"created from recovery"
2433
2455
  let q=`\u2705 **File ${C.charAt(0).toUpperCase()+C.slice(1)} Successfully**\n\n`
2434
- return q+=`**File:** ${w}\n`,q+=`**Recovered from:** ${n.substring(0,8)}\n`,q+=`**Content length:** ${x.length} characters\n`,o&&E&&$&&(q+=`**Backup created:** ${i.basename($)}\n`),q+="**Git status:** Recovery committed\n",q+="\u{1f4a1} **Tip:** The file has been restored to its state at the specified commit.",p.info("File recovery completed successfully",{path:w,commitHash:n,duration:Date.now()-m}),f.incrementCounter("file_recover_total",1,{success:!0,type:"file"}),{content:[{type:"text",text:q}]}}catch(e){throw e}}(v,g,y,w,r,n,o,p,u,m,h)}catch(e){const t=Date.now()-m
2456
+ return q+=`**File:** ${w}\n`,q+=`**Recovered from:** ${n.substring(0,8)}\n`,q+=`**Content length:** ${_.length} characters\n`,o&&S&&$&&(q+=`**Backup created:** ${i.basename($)}\n`),q+="**Git status:** Recovery committed\n",q+="\u{1f4a1} **Tip:** The file has been restored to its state at the specified commit.",p.info("File recovery completed successfully",{path:w,commitHash:n,duration:Date.now()-m}),f.incrementCounter("file_recover_total",1,{success:!0,type:"file"}),{content:[{type:"text",text:q}]}}catch(e){throw e}}(v,g,y,w,r,n,o,p,u,m,h)}catch(e){const t=Date.now()-m
2435
2457
  return p.error("Recovery operation failed",{error:e instanceof Error?e.stack:String(e),duration:t}),f({tool:"file_recover",success:!1}),u.incrementCounter("file_recover_errors",1),{content:[{type:"text",text:`\u274c **Recovery Failed:** ${(0,d.getErrorMessage)(e)}`}],isError:!0}}}(...e))}
2436
2458
  const i=s(t(16928)),a=s(t(91943)),c=s(t(85967)),l=t(3922),d=t(58824),u=t(659)},79671(e,exports){exports.findPreviousCommit=async function(e,t,r){try{if(!r){const r=await e.raw(["rev-list","--max-count=1","HEAD","--",t])
2437
2459
  return r&&r.trim()?r.trim():null}const n=await e.raw(["rev-list","--max-count=1",`${r}^`,"--",t])
2438
2460
  return n&&n.trim()?n.trim():null}catch(e){return null}}},86317(e,exports,t){exports.def=void 0
2439
2461
  const r=t(76183),n=t(88336)
2440
- exports.def={name:"merge_preview",description:"See your request's work applied ON TOP OF the current default branch, before you finish, without merging anything.\n\nUse it when you suspect the ground moved under you: another request landed in the same file, or you are about to rely on a check you ran a while ago. Every other history tool (request_diff, query_history) reads the PAST: what your branch changed, what a file's versions were. This one constructs the tree your work WOULD land, which does not exist in history and cannot be reached by any git command.\n\nReturns: upToDate (main has not moved -- the common case, answered cheaply), mainAheadBy, conflicts, overlap (files changed by BOTH your branch and main -- the actionable set), addedByMerge (what the merge brings into your branch), and the prospective merge commit.\n\nFORMATS:\n\u2022 summary (default): per-file counts.\n\u2022 unified: the diff the merge would add to your branch. Capped at 500KB; narrow with `path`.\n\nThis merges nothing and changes no branch: the tree is built with git plumbing and no working tree is touched. A CLEAN result means no textual conflict, not that the combined behaviour is correct. If it conflicts, do nothing: the pipeline delivers conflicts to you as markers in the files. You never run git yourself.",schema:r.MergePreviewSchema,annotations:{title:"Merge Preview",readOnlyHint:!0},handler:(e,t)=>(0,n.handleMergePreview)(e,t.context,t.apiClient,t.logger,t.metrics)}},88336(e,exports,t){function r(e,t=!1){return{content:[{type:"text",text:JSON.stringify(e,null,2)}],...(0,i.alchemyMeta)({kind:"merge-preview"}),...t?{isError:!0}:{}}}function n(e){return{content:[{type:"text",text:`\u274c ${e}`}],...(0,i.alchemyMeta)({kind:"merge-preview"}),isError:!0}}async function o(e,t,r){const n=await e.raw(["diff","-M50%","--numstat",t,...r]),o=[]
2462
+ exports.def={name:"merge_preview",description:'See your request\'s work applied on top of the current default branch, without merging or changing anything: for when the ground may have moved (another request landed in your files, or a check you rely on is old). Returns upToDate (the common case), mainAheadBy, conflicts, overlap (files both sides changed), addedByMerge, and the prospective merge commit; format "unified" gives the diff the merge would add (capped at 500KB, narrow with path). Clean means no textual conflict, not that the combined behaviour is correct. On a conflict do nothing: conflicts reach you as markers in the files.',schema:r.MergePreviewSchema,annotations:{title:"Merge Preview",readOnlyHint:!0},handler:(e,t)=>(0,n.handleMergePreview)(e,t.context,t.apiClient,t.logger,t.metrics)}},88336(e,exports,t){function r(e,t=!1){return{content:[{type:"text",text:JSON.stringify(e,null,2)}],...(0,i.alchemyMeta)({kind:"merge-preview"}),...t?{isError:!0}:{}}}function n(e){return{content:[{type:"text",text:`\u274c ${e}`}],...(0,i.alchemyMeta)({kind:"merge-preview"}),isError:!0}}async function o(e,t,r){const n=await e.raw(["diff","-M50%","--numstat",t,...r]),o=[]
2441
2463
  for(const e of n.split("\n").filter(Boolean)){const[t,r,...n]=e.split("\t"),s=n.join("\t")
2442
2464
  s&&o.push({path:s,additions:"-"===t?0:Number(t)||0,deletions:"-"===r?0:Number(r)||0,binary:"-"===t&&"-"===r})}return o.sort((e,t)=>e.path.localeCompare(t.path))}exports.handleMergePreview=async function(e,t,i,d,u){const h=d.child({tool:"merge_preview"}),p=u.startTimer("tool_execution_time")
2443
2465
  try{const{requestId:d,format:f="summary",path:m}=e,g=await i.getRequestClient().getRequest(d)
@@ -2445,17 +2467,17 @@ if(!g)return p({success:!1,reason:"not_found"}),n(`Request not found: ${d}`)
2445
2467
  if("completed"===g.status)return p({success:!1,reason:"completed"}),n(`Request ${d} is already completed and merged: there is nothing prospective about it. Use request_diff for what it changed.`)
2446
2468
  const y=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,w=t.git??(0,s.simpleGit)({baseDir:y,binary:t.gitBinary}),b=`requests/${d}`,v=await async function(e){for(const t of["main","master"])try{return await e.raw(["rev-parse","--verify",t]),t}catch{}return"main"}(w)
2447
2469
  let k,I,$
2448
- try{I=(await w.raw(["rev-parse",v])).trim(),$=(await w.raw(["rev-parse",b])).trim(),k=(await w.raw(["merge-base",v,b])).trim()}catch(e){return p({success:!1,reason:"no_refs"}),n(`Could not resolve ${b} against ${v}: ${(0,a.getErrorMessage)(e)}`)}const E=m?["--",m]:[],S={requestId:d,branch:b,defaultBranch:v,mergeBaseSha:k,mainHeadSha:I,branchTipSha:$}
2449
- if(k===I)return p({success:!0}),u.incrementCounter("merge_preview_total",1,{outcome:"up_to_date"}),r({...S,upToDate:!0,mainAheadBy:0,conflicts:null,overlap:[],summary:`${v} has not moved since this branch forked. What you have built is what would land; nothing to reconcile.`})
2450
- const x=await w.raw(["rev-list","--count",`${k}..${I}`]),_=Number(x.trim())||0,C=await o(w,`${k}..${$}`,E),q=await o(w,`${k}..${I}`,E),T=new Set(C.map(e=>e.path)),j=q.filter(e=>T.has(e.path)).map(e=>e.path),P=await(0,c.buildProspectiveMergeCommit)(y,{defaultBranch:v,branchName:b,requestId:d,requestTitle:g.title,gitBinary:t.gitBinary})
2451
- if(!P.ok&&"conflicts"===P.reason)return p({success:!0}),u.incrementCounter("merge_preview_total",1,{outcome:"conflicts"}),r({...S,upToDate:!1,mainAheadBy:_,conflicts:{summary:P.conflictSummary},overlap:j,yourFiles:C.map(e=>e.path),incomingFiles:q,summary:`${v} moved by ${_} commit(s) and the merge CONFLICTS. Keep working: the pipeline routes conflicts into their own lane and delivers them to you as markers in the files, which you resolve by editing them. You never run git yourself. This is a heads-up, not a task, and NOT a blocker to report: finish your change and complete the request; the conflicted merge comes back to you as markers. Reporting it to your parent registers no wait and moves nothing.`})
2470
+ try{I=(await w.raw(["rev-parse",v])).trim(),$=(await w.raw(["rev-parse",b])).trim(),k=(await w.raw(["merge-base",v,b])).trim()}catch(e){return p({success:!1,reason:"no_refs"}),n(`Could not resolve ${b} against ${v}: ${(0,a.getErrorMessage)(e)}`)}const S=m?["--",m]:[],E={requestId:d,branch:b,defaultBranch:v,mergeBaseSha:k,mainHeadSha:I,branchTipSha:$}
2471
+ if(k===I)return p({success:!0}),u.incrementCounter("merge_preview_total",1,{outcome:"up_to_date"}),r({...E,upToDate:!0,mainAheadBy:0,conflicts:null,overlap:[],summary:`${v} has not moved since this branch forked. What you have built is what would land; nothing to reconcile.`})
2472
+ const _=await w.raw(["rev-list","--count",`${k}..${I}`]),x=Number(_.trim())||0,C=await o(w,`${k}..${$}`,S),q=await o(w,`${k}..${I}`,S),T=new Set(C.map(e=>e.path)),j=q.filter(e=>T.has(e.path)).map(e=>e.path),P=await(0,c.buildProspectiveMergeCommit)(y,{defaultBranch:v,branchName:b,requestId:d,requestTitle:g.title,gitBinary:t.gitBinary})
2473
+ if(!P.ok&&"conflicts"===P.reason)return p({success:!0}),u.incrementCounter("merge_preview_total",1,{outcome:"conflicts"}),r({...E,upToDate:!1,mainAheadBy:x,conflicts:{summary:P.conflictSummary},overlap:j,yourFiles:C.map(e=>e.path),incomingFiles:q,summary:`${v} moved by ${x} commit(s) and the merge CONFLICTS. Keep working: the pipeline routes conflicts into their own lane and delivers them to you as markers in the files, which you resolve by editing them. You never run git yourself. This is a heads-up, not a task, and NOT a blocker to report: finish your change and complete the request; the conflicted merge comes back to you as markers. Reporting it to your parent registers no wait and moves nothing.`})
2452
2474
  if(!P.ok)return p({success:!1,reason:"build_failed"}),n(`Could not build the prospective merge for ${d}: ${P.error}`)
2453
- const R=await o(w,`${$}..${P.mergeCommitSha}`,E),A={...S,upToDate:!1,mainAheadBy:_,conflicts:null,prospectiveMergeCommit:P.mergeCommitSha,treeSha:P.treeSha,overlap:j,yourFiles:C.map(e=>e.path),addedByMerge:R,summary:j.length?`${v} moved by ${_} commit(s) and the merge is CLEAN, but ${j.length} file(s) were changed by both: ${j.slice(0,8).join(", ")}${j.length>8?", ...":""}. Clean means no textual conflict, NOT that the combined behaviour is right: read those files as merged before you rely on your earlier check.`:`${v} moved by ${_} commit(s), the merge is CLEAN, and none of the files you changed were touched. Nothing to reconcile.`}
2454
- if("unified"===f){const e=await w.raw(["diff","-M50%",`${$}..${P.mergeCommitSha}`,...E]),t=e.length>l
2455
- A.diff=t?e.slice(0,l):e,A.truncated=t,t&&(A.hint="Diff truncated at 500KB. Narrow with `path`, or start from `overlap`.")}return h.info("merge preview built",{requestId:d,mainAheadBy:_,overlap:j.length,clean:!0}),p({success:!0}),u.incrementCounter("merge_preview_total",1,{outcome:j.length?"overlap":"clean"}),r(A)}catch(e){return p({success:!1,reason:"error"}),h.error("merge_preview failed",{error:(0,a.getErrorMessage)(e)}),n(`merge_preview failed: ${(0,a.getErrorMessage)(e)}`)}}
2475
+ const R=await o(w,`${$}..${P.mergeCommitSha}`,S),A={...E,upToDate:!1,mainAheadBy:x,conflicts:null,prospectiveMergeCommit:P.mergeCommitSha,treeSha:P.treeSha,overlap:j,yourFiles:C.map(e=>e.path),addedByMerge:R,summary:j.length?`${v} moved by ${x} commit(s) and the merge is CLEAN, but ${j.length} file(s) were changed by both: ${j.slice(0,8).join(", ")}${j.length>8?", ...":""}. Clean means no textual conflict, NOT that the combined behaviour is right: read those files as merged before you rely on your earlier check.`:`${v} moved by ${x} commit(s), the merge is CLEAN, and none of the files you changed were touched. Nothing to reconcile.`}
2476
+ if("unified"===f){const e=await w.raw(["diff","-M50%",`${$}..${P.mergeCommitSha}`,...S]),t=e.length>l
2477
+ A.diff=t?e.slice(0,l):e,A.truncated=t,t&&(A.hint="Diff truncated at 500KB. Narrow with `path`, or start from `overlap`.")}return h.info("merge preview built",{requestId:d,mainAheadBy:x,overlap:j.length,clean:!0}),p({success:!0}),u.incrementCounter("merge_preview_total",1,{outcome:j.length?"overlap":"clean"}),r(A)}catch(e){return p({success:!1,reason:"error"}),h.error("merge_preview failed",{error:(0,a.getErrorMessage)(e)}),n(`merge_preview failed: ${(0,a.getErrorMessage)(e)}`)}}
2456
2478
  const s=t(83807),i=t(82272),a=t(58824),c=t(87192),l=5e5},26473(e,exports,t){exports.def=void 0
2457
2479
  const r=t(76183),n=t(51020)
2458
- exports.def={name:"query_history",description:'Query historical information about files, tasks, or requests.\n\nQuery types:\n\u2022 file-history - View version history of a specific file\n\u2022 task-file-history - See all file changes made during a task\n\u2022 request-file-history - See all file changes across an entire request\n\u2022 recover - Recover a file to a previous version using commit hash\n\u2022 blame - Line-by-line attribution showing which request/task edited each line\n\nBranch defaults to the session\'s active branch. Set `fileHistory.branch` or `blame.branch` to inspect another branch ad-hoc. `request-file-history` derives the branch from the request; `recover` is by commit hash.\n\nCreates backup of current version by default for recovery.\n\nFile history supports `since` parameter for date filtering (ISO date or relative like "2h", "1d", "30m").',schema:r.QueryHistorySchema,annotations:{title:"Query History",readOnlyHint:!0},handler:(e,t)=>(0,n.handleQueryHistory)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer)}},51020(e,exports,t){exports.handleQueryHistory=async function(e,l,d,u,h,p){const f=u.child({tool:"query_history"})
2480
+ exports.def={name:"query_history",description:'History of files, tasks and requests. queryType: file-history (versions of one file; since takes an ISO date or "2h", "1d"), task-file-history, request-file-history, recover (restore a file to a commit, backing up the current version), blame (which request and task wrote each line). Defaults to your branch; fileHistory.branch or blame.branch reads another.',schema:r.QueryHistorySchema,annotations:{title:"Query History",readOnlyHint:!0},handler:(e,t)=>(0,n.handleQueryHistory)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer)}},51020(e,exports,t){exports.handleQueryHistory=async function(e,l,d,u,h,p){const f=u.child({tool:"query_history"})
2459
2481
  try{const c=e,{queryType:m}=c
2460
2482
  let g
2461
2483
  switch(f.info("Processing query_history request",{queryType:m}),m){case"file-history":f.info("Routing to file-history handler",{path:c.fileHistory.path}),g=await(0,r.handleFileHistory)({path:c.fileHistory.path,limit:c.fileHistory.limit,showDiff:c.fileHistory.showDiff,showTraceability:c.fileHistory.showTraceability,branch:c.fileHistory.branch,commit:c.fileHistory.commit,since:c.fileHistory.since},l,d,f,h)
@@ -2477,31 +2499,31 @@ break
2477
2499
  default:throw new Error(`Unknown query type: ${m}`)}return f.info("query_history request completed",{queryType:m,success:!g.isError}),h.incrementCounter("query_history_total",1,{success:!g.isError,queryType:m}),(0,i.withAlchemyKind)(g,"query-history")}catch(e){return f.error("query_history request failed",{error:e instanceof Error?e.stack:String(e)}),h.incrementCounter("query_history_errors",1),{content:[{type:"text",text:`\u274c **Query History Failed:** ${(0,c.getErrorMessage)(e)}`}],isError:!0}}}
2478
2500
  const r=t(58828),n=t(58317),o=t(76891),s=t(44640),i=t(82272),a=t(4811),c=t(58824)},6309(e,exports,t){exports.def=void 0
2479
2501
  const r=t(76183),n=t(88184)
2480
- exports.def={name:"request_diff",description:"Return the content changes introduced by a request: either a structured per-file summary or a full unified diff.\n\nLive request: diff merge-base(default, requests/{id})..requests/{id}.\nCompleted request: diff the merge_base_sha..merge_commit_sha captured at completion (deterministic, FF- and non-FF-safe).\nPre-metadata legacy completed requests cannot be diffed; use query_history with queryType=request-file-history for those.\n\nFORMATS:\n\u2022 summary (default): JSON { baseSha, headSha, branch, files: [{ path, status, additions, deletions, oldPath?, similarity?, binary?, document? }] }, sorted by path. document: true marks a PDF/Word/Excel/PowerPoint file, whose counts and unified diff are of its extracted text.\n\u2022 unified: standard unified diff text. Capped at 500KB; when truncated, payload includes truncated: true and a hint to narrow with `path`.\n\nPATH: optional git pathspec to scope the diff. Standard pathspec semantics: 'src/foo.ts', 'src/', '**/*.ts' all work.\n\nReturns content only: for per-task authorship (which task wrote which change), use query_history with queryType=request-file-history.",schema:r.RequestDiffSchema,annotations:{title:"Request Diff",readOnlyHint:!0},handler:(e,t)=>(0,n.handleRequestDiff)(e,t.context,t.apiClient,t.logger,t.metrics)}},88184(e,exports,t){function r(e,t=!1){return{content:[{type:"text",text:JSON.stringify(e,null,2)}],...(0,i.alchemyMeta)({kind:"request-diff"}),...t?{isError:!0}:{}}}function n(e){return{content:[{type:"text",text:`\u274c ${e}`}],...(0,i.alchemyMeta)({kind:"request-diff"}),isError:!0}}exports.handleRequestDiff=async function(e,t,i,u,h){const p=u.child({tool:"request_diff"}),f=h.startTimer("tool_execution_time")
2502
+ exports.def={name:"request_diff",description:"The changes a request made: a per-file summary (default; document: true marks a PDF or Office file, diffed as its extracted text) or, with format \"unified\", the diff itself (capped at 500KB; narrow with path, a git pathspec such as 'src/' or '**/*.ts'). Live requests diff from the merge base; completed ones use the commits captured at completion. For which task wrote which change, use query_history request-file-history.",schema:r.RequestDiffSchema,annotations:{title:"Request Diff",readOnlyHint:!0},handler:(e,t)=>(0,n.handleRequestDiff)(e,t.context,t.apiClient,t.logger,t.metrics)}},88184(e,exports,t){function r(e,t=!1){return{content:[{type:"text",text:JSON.stringify(e,null,2)}],...(0,i.alchemyMeta)({kind:"request-diff"}),...t?{isError:!0}:{}}}function n(e){return{content:[{type:"text",text:`\u274c ${e}`}],...(0,i.alchemyMeta)({kind:"request-diff"}),isError:!0}}exports.handleRequestDiff=async function(e,t,i,u,h){const p=u.child({tool:"request_diff"}),f=h.startTimer("tool_execution_time")
2481
2503
  try{const a=e,{requestId:u,format:p="summary",path:m}=a,g=i.getRequestClient(),y=await g.getRequest(u)
2482
2504
  if(!y)return f({success:!1,reason:"not_found"}),n(`Request not found: ${u}`)
2483
2505
  const w=t.workspaceManager?.getCurrentContext(t.currentSessionId)?.basePath||t.basePath,b=t.git??(0,o.simpleGit)({baseDir:w,binary:t.gitBinary}),v=(0,c.documentDiffGitArgs)().length?(0,c.documentDiffGit)(w,t.gitBinary):b,k="completed"===y.status,I=`requests/${u}`
2484
- let $,E
2506
+ let $,S
2485
2507
  if(k){const e=y,t=e.mergeBaseSha??e.merge_base_sha??y.metadata?.mergeBaseSha,r=e.mergeCommitSha??e.merge_commit_sha??y.metadata?.mergeCommitSha
2486
2508
  if(!t||!r)return f({success:!1,reason:"no_merge_range"}),n(`Completed request ${u} has no merge range on its record, so request_diff cannot diff it. Use query_history with queryType="request-file-history" for per-task file changes.`)
2487
- $=t,E=r}else{const e=await async function(e){for(const t of["main","master"])try{return await e.raw(["rev-parse","--verify",t]),t}catch{}return"main"}(b)
2488
- try{$=(await b.raw(["merge-base",e,I])).trim()}catch{return f({success:!1,reason:"no_merge_base"}),n(`Could not compute merge-base for ${I} against ${e}. Branch may not exist.`)}try{E=(await b.raw(["rev-parse",I])).trim()}catch{return f({success:!1,reason:"no_branch"}),n(`Branch ${I} not found.`)}}const S=m?["--",m]:[],x=(()=>{try{return i.getProjectId?.()??null}catch{return null}})(),_=k||!x?[]:(await(0,l.requestDocumentEdits)({get:e=>i.proxyGet(e)},x,u).catch(()=>[])).filter(e=>!m||e.path.startsWith(m)||e.filename===m)
2489
- if("unified"===p){const e=(0,l.withDocumentEdits)(await v.raw([...(0,c.documentDiffGitArgs)(),"diff","-M50%",`${$}..${E}`,...S]),_),t=e.length>d,n=t?e.slice(0,d):e
2509
+ $=t,S=r}else{const e=await async function(e){for(const t of["main","master"])try{return await e.raw(["rev-parse","--verify",t]),t}catch{}return"main"}(b)
2510
+ try{$=(await b.raw(["merge-base",e,I])).trim()}catch{return f({success:!1,reason:"no_merge_base"}),n(`Could not compute merge-base for ${I} against ${e}. Branch may not exist.`)}try{S=(await b.raw(["rev-parse",I])).trim()}catch{return f({success:!1,reason:"no_branch"}),n(`Branch ${I} not found.`)}}const E=m?["--",m]:[],_=(()=>{try{return i.getProjectId?.()??null}catch{return null}})(),x=k||!_?[]:(await(0,l.requestDocumentEdits)({get:e=>i.proxyGet(e)},_,u).catch(()=>[])).filter(e=>!m||e.path.startsWith(m)||e.filename===m)
2511
+ if("unified"===p){const e=(0,l.withDocumentEdits)(await v.raw([...(0,c.documentDiffGitArgs)(),"diff","-M50%",`${$}..${S}`,...E]),x),t=e.length>d,n=t?e.slice(0,d):e
2490
2512
  f({success:!0}),h.incrementCounter("request_diff_total",1,{format:"unified"})
2491
2513
  const o=(0,s.longLineChanges)(n)
2492
- return r({baseSha:$,headSha:E,branch:k?null:I,...o.length?{changesInLongLines:o}:{},diff:n,truncated:t,...t?{hint:"Diff truncated at 500KB. Narrow with `path` to focus on a file or directory."}:{}})}const C=await b.raw(["diff","-M50%","--numstat",`${$}..${E}`,...S]),q=await b.raw(["diff","-M50%","--name-status",`${$}..${E}`,...S]),T=new Map
2514
+ return r({baseSha:$,headSha:S,branch:k?null:I,...o.length?{changesInLongLines:o}:{},diff:n,truncated:t,...t?{hint:"Diff truncated at 500KB. Narrow with `path` to focus on a file or directory."}:{}})}const C=await b.raw(["diff","-M50%","--numstat",`${$}..${S}`,...E]),q=await b.raw(["diff","-M50%","--name-status",`${$}..${S}`,...E]),T=new Map
2493
2515
  for(const e of C.split("\n").filter(Boolean)){const t=e.split("\t")
2494
2516
  if(t.length<3)continue
2495
2517
  const[r,n,...o]=t,s=o[o.length-1],i="-"===r&&"-"===n
2496
2518
  T.set(s,{additions:i?0:parseInt(r,10)||0,deletions:i?0:parseInt(n,10)||0,binary:i})}const j=[...T.entries()].filter(([e,t])=>t.binary&&(0,c.isTextDiffedDocument)(e)).map(([e])=>e),P=new Map
2497
- if(j.length&&(0,c.documentDiffGitArgs)().length)try{const e=await v.raw([...(0,c.documentDiffGitArgs)(),"diff","-M50%",`${$}..${E}`,"--",...j])
2519
+ if(j.length&&(0,c.documentDiffGitArgs)().length)try{const e=await v.raw([...(0,c.documentDiffGitArgs)(),"diff","-M50%",`${$}..${S}`,"--",...j])
2498
2520
  for(const[t,r]of(0,c.countPatchLines)(e))P.set(t,r)}catch{}for(const e of j){const t=P.get(e)
2499
2521
  t&&T.set(e,{...t,binary:!1})}const R=[]
2500
2522
  for(const e of q.split("\n").filter(Boolean)){const t=e.split("\t"),r=t[0]
2501
2523
  if(r.startsWith("R")){const e=t[1],n=t[2],o=T.get(n)||{additions:0,deletions:0,binary:!1},s=parseInt(r.slice(1),10),i={path:n,status:"renamed",additions:o.additions,deletions:o.deletions,oldPath:e}
2502
2524
  Number.isFinite(s)&&(i.similarity=s),o.binary&&(i.binary=!0),(0,c.isTextDiffedDocument)(n)&&(i.document=!0),R.push(i)}else{const e=t[1],n=T.get(e)||{additions:0,deletions:0,binary:!1},o={path:e,status:"A"===r?"added":"D"===r?"deleted":"modified",additions:n.additions,deletions:n.deletions}
2503
- n.binary&&(o.binary=!0),(0,c.isTextDiffedDocument)(e)&&(o.document=!0),R.push(o)}}for(const e of _)R.push({path:e.path,status:"modified",additions:e.additions,deletions:e.deletions,document:!0})
2504
- return R.sort((e,t)=>e.path.localeCompare(t.path)),f({success:!0}),h.incrementCounter("request_diff_total",1,{format:"summary"}),r({baseSha:$,headSha:E,branch:k?null:I,files:R})}catch(e){return p.error("request_diff failed",{error:e instanceof Error?e.stack:String(e)}),f({success:!1}),h.incrementCounter("request_diff_errors",1),n(`request_diff failed: ${(0,a.getErrorMessage)(e)}`)}}
2525
+ n.binary&&(o.binary=!0),(0,c.isTextDiffedDocument)(e)&&(o.document=!0),R.push(o)}}for(const e of x)R.push({path:e.path,status:"modified",additions:e.additions,deletions:e.deletions,document:!0})
2526
+ return R.sort((e,t)=>e.path.localeCompare(t.path)),f({success:!0}),h.incrementCounter("request_diff_total",1,{format:"summary"}),r({baseSha:$,headSha:S,branch:k?null:I,files:R})}catch(e){return p.error("request_diff failed",{error:e instanceof Error?e.stack:String(e)}),f({success:!1}),h.incrementCounter("request_diff_errors",1),n(`request_diff failed: ${(0,a.getErrorMessage)(e)}`)}}
2505
2527
  const o=t(83807),s=t(92873),i=t(82272),a=t(58824),c=t(61161),l=t(95040),d=5e5},58317(e,exports,t){function r(e){const t=[]
2506
2528
  return"write-file"===e.type&&e.metadata?.path?t.push(e.metadata.path):"replace-in-files"===e.type&&e.metadata?.operations?e.metadata.operations.forEach(e=>{e.path&&t.push(e.path)}):c.PRECISION_EDIT_TYPES.includes(e.type)&&e.metadata?.operations?e.metadata.operations.forEach(e=>{e?.path&&"string"==typeof e.path&&t.push(e.path)}):"move-files"===e.type&&e.metadata?.successfulOperations?e.metadata.successfulOperations.forEach(e=>{e.destination&&t.push(e.destination)}):"delete-files"===e.type&&e.metadata?.paths?t.push(...e.metadata.paths):"manage-files"===e.type&&e.metadata?.operations&&e.metadata.operations.forEach(e=>{e.createDirectory?.path&&t.push(e.createDirectory.path),e.moveFiles?.moves&&e.moveFiles.moves.forEach(e=>{e.destination&&t.push(e.destination)}),e.deleteFiles?.targets&&e.deleteFiles.targets.forEach(e=>{e.path&&t.push(e.path)})}),[...new Set(t)]}function n(e){switch(e){case"write-file":return"\u{1f4dd} Write File"
2507
2529
  case"replace-in-files":return"\u{1f504} Replace in Files"
@@ -2602,39 +2624,39 @@ if(!Array.isArray(t)||0===t.length)return s.warn(`Skipping ${e.type} record with
2602
2624
  if(!t.some(e=>e&&"string"==typeof e.path&&e.path.trim().length>0))return s.warn(`Skipping ${e.type} record without file operation paths: ${e.id}`),!1}return!0})
2603
2625
  if(s.info(`Found ${$.length} file operation records`),$.length>0){const O={}
2604
2626
  $.forEach(e=>{O[e.type]=(O[e.type]||0)+1}),s.info("Filtered record type distribution:",O)}if(0===$.length)return p({success:!0,fileChanges:0}),{content:[{type:"text",text:`No file changes found for task ${f}.`}]}
2605
- const E=new Map
2627
+ const S=new Map
2606
2628
  $.forEach(e=>{let t=null
2607
2629
  if(e.metadata){if(!e.metadata.commitHash){const t=["gitCommitId","commitId","commit","gitCommit"]
2608
2630
  for(const r of t)if(e.metadata[r]){e.metadata.commitHash=e.metadata[r],s.info(`Using ${r} as commit hash for record ${e.id}`)
2609
2631
  break}}if(exports.PRECISION_EDIT_TYPES.includes(e.type)){if(e.metadata.operations&&Array.isArray(e.metadata.operations)){for(const t of e.metadata.operations)if(t&&t.path&&"string"==typeof t.path){const r=t.path
2610
- E.has(r)||E.set(r,[])
2611
- const n=E.get(r)
2632
+ S.has(r)||S.set(r,[])
2633
+ const n=S.get(r)
2612
2634
  if(n){const o={filePath:r,recordId:e.id,type:e.type,timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e}
2613
2635
  "replace-strings"===e.type?o.replaceOperation=t:"insert-lines"===e.type?o.insertOperation=t:"replace-lines"===e.type&&(o.replaceLineOperation=t),n.push(o)}}t=null}}else if(e.metadata.path)t=e.metadata.path
2614
2636
  else if("move-file"===e.type){t=e.metadata.destination||e.metadata.newPath
2615
2637
  const r=e.metadata.source||e.metadata.oldPath
2616
- if(r){E.has(r)||E.set(r,[])
2617
- const t=E.get(r)
2638
+ if(r){S.has(r)||S.set(r,[])
2639
+ const t=S.get(r)
2618
2640
  t&&t.push({filePath:r,recordId:e.id,type:"move-source",timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e})}}else if("move-files"===e.type){const r=[...e.metadata.successfulOperations||[],...e.metadata.failedOperations||[]]
2619
2641
  if(r.length>0){for(const t of r)if(t&&t.source&&t.destination){const r=t.destination
2620
- E.has(r)||E.set(r,[])
2621
- const n=E.get(r)
2642
+ S.has(r)||S.set(r,[])
2643
+ const n=S.get(r)
2622
2644
  n&&n.push({filePath:r,recordId:e.id,type:"move-files",timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e,moveOperation:t})
2623
2645
  const o=t.source
2624
- E.has(o)||E.set(o,[])
2625
- const s=E.get(o)
2626
- s&&s.push({filePath:o,recordId:e.id,type:"move-files-source",timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e,moveOperation:t})}t=null}}else if("delete-files"===e.type){if(e.metadata.paths&&Array.isArray(e.metadata.paths)){for(const t of e.metadata.paths)if("string"==typeof t&&t.trim().length>0){E.has(t)||E.set(t,[])
2627
- const r=E.get(t)
2646
+ S.has(o)||S.set(o,[])
2647
+ const s=S.get(o)
2648
+ s&&s.push({filePath:o,recordId:e.id,type:"move-files-source",timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e,moveOperation:t})}t=null}}else if("delete-files"===e.type){if(e.metadata.paths&&Array.isArray(e.metadata.paths)){for(const t of e.metadata.paths)if("string"==typeof t&&t.trim().length>0){S.has(t)||S.set(t,[])
2649
+ const r=S.get(t)
2628
2650
  r&&r.push({filePath:t,recordId:e.id,type:"delete-files",timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e})}t=null}}else if("replace-in-files"===e.type&&e.metadata.operations&&Array.isArray(e.metadata.operations)){for(const t of e.metadata.operations)if(t&&t.path&&"string"==typeof t.path){const r=t.path
2629
- E.has(r)||E.set(r,[])
2630
- const n=E.get(r)
2651
+ S.has(r)||S.set(r,[])
2652
+ const n=S.get(r)
2631
2653
  n&&n.push({filePath:r,recordId:e.id,type:"replace-in-files",timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e,replaceOperation:t})}t=null}}if(!t)return void("move-files"===e.type||"delete-files"===e.type||"replace-in-files"===e.type||exports.PRECISION_EDIT_TYPES.includes(e.type)||s.warn(`Record ${e.id} has no valid file path in metadata`))
2632
- E.has(t)||E.set(t,[])
2633
- const r=E.get(t)
2634
- r&&r.push({filePath:t,recordId:e.id,type:e.type,timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e})}),s.info(`Grouped records by file path. Found ${E.size} unique files.`)
2635
- for(const[M,D]of E.entries())D.sort((e,t)=>new Date(e.timestamp).getTime()-new Date(t.timestamp).getTime()),s.info(`File ${M} has ${D.length} records.`),s.info(`Records with commit hashes: ${D.filter(e=>e.commitHash).length}`)
2636
- const S=new Map
2637
- for(const[F,N]of E.entries()){s.info(`Processing file: ${F} (${N.length} records)`)
2654
+ S.has(t)||S.set(t,[])
2655
+ const r=S.get(t)
2656
+ r&&r.push({filePath:t,recordId:e.id,type:e.type,timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e})}),s.info(`Grouped records by file path. Found ${S.size} unique files.`)
2657
+ for(const[M,D]of S.entries())D.sort((e,t)=>new Date(e.timestamp).getTime()-new Date(t.timestamp).getTime()),s.info(`File ${M} has ${D.length} records.`),s.info(`Records with commit hashes: ${D.filter(e=>e.commitHash).length}`)
2658
+ const E=new Map
2659
+ for(const[F,N]of S.entries()){s.info(`Processing file: ${F} (${N.length} records)`)
2638
2660
  const z=new Map
2639
2661
  N.forEach(e=>{e.recordId&&e.record?.metadata?.status&&(z.has(e.recordId)||z.set(e.recordId,[]),z.get(e.recordId)?.push(e.record.metadata.status))}),z.forEach((e,t)=>{s.info(`Record ${t} status updates: ${e.join(" -> ")}`)})
2640
2662
  const L=N.filter(e=>e.commitHash)
@@ -2646,8 +2668,8 @@ if(Q.length>0){const Z=Q.length>1?Q[Q.length-1]:Q[0],X=Q[0],ee=1===Q.length
2646
2668
  s.info(ee?`Using single git commit for ${F}: ${X.commitHash?X.commitHash.substring(0,8):"unknown"}`:`Using git commits for ${F}: ${Z.commitHash?Z.commitHash.substring(0,8):"unknown"} to ${X.commitHash?X.commitHash.substring(0,8):"unknown"}`)
2647
2669
  let te=""
2648
2670
  try{if(ee)try{const re=[`${X.commitHash}:${F}`],ne=await t.git.show(re)
2649
- ne&&ne.trim().length>0?(te=ne.split("\n").map(e=>`+${e}`).join("\n"),S.set(F,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:te})):S.set(F,{firstChange:Z,lastChange:X,hasDiff:!1,diffText:"File exists in git history but has no content"})}catch(oe){s.warn(`Error getting file content for ${F}`,{error:oe instanceof Error?oe.message:String(oe)}),S.set(F,{firstChange:Z,lastChange:X,hasDiff:!1,diffText:"Error getting file content from git"})}else{const se=[`${Z.commitHash}..${X.commitHash}`,"--",F]
2650
- te=await t.git.diff(se),S.set(F,{firstChange:Z,lastChange:X,hasDiff:!!(te&&te.trim().length>0),diffText:te||"No changes detected in git diff"})}}catch(ie){s.warn(`Error generating diff for ${F}`,{error:ie instanceof Error?ie.message:String(ie)}),S.set(F,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:"Error generating diff, but git history is available"})}continue}}else s.info(`No git history found for ${F}`)}}catch(ae){s.warn(`Error getting git history for ${F}`,{error:(0,u.getErrorMessage)(ae)})}S.set(F,{firstChange:N[0],lastChange:N[N.length-1],hasDiff:!1,diffText:"No commit hashes available for this file"})
2671
+ ne&&ne.trim().length>0?(te=ne.split("\n").map(e=>`+${e}`).join("\n"),E.set(F,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:te})):E.set(F,{firstChange:Z,lastChange:X,hasDiff:!1,diffText:"File exists in git history but has no content"})}catch(oe){s.warn(`Error getting file content for ${F}`,{error:oe instanceof Error?oe.message:String(oe)}),E.set(F,{firstChange:Z,lastChange:X,hasDiff:!1,diffText:"Error getting file content from git"})}else{const se=[`${Z.commitHash}..${X.commitHash}`,"--",F]
2672
+ te=await t.git.diff(se),E.set(F,{firstChange:Z,lastChange:X,hasDiff:!!(te&&te.trim().length>0),diffText:te||"No changes detected in git diff"})}}catch(ie){s.warn(`Error generating diff for ${F}`,{error:ie instanceof Error?ie.message:String(ie)}),E.set(F,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:"Error generating diff, but git history is available"})}continue}}else s.info(`No git history found for ${F}`)}}catch(ae){s.warn(`Error getting git history for ${F}`,{error:(0,u.getErrorMessage)(ae)})}E.set(F,{firstChange:N[0],lastChange:N[N.length-1],hasDiff:!1,diffText:"No commit hashes available for this file"})
2651
2673
  continue}const B=L[0],H=L[L.length-1]
2652
2674
  s.info(`First commit: ${B.commitHash?B.commitHash:"unknown"}, Last commit: ${H.commitHash?H.commitHash:"unknown"}`)
2653
2675
  let U=null,G=!1
@@ -2658,36 +2680,36 @@ const de=await t.git.show(le)
2658
2680
  if(de&&de.trim().length>0){let ue=""
2659
2681
  const he=de.split("\n")
2660
2682
  for(const pe of he)ue+=`+${pe}\n`
2661
- S.set(F,{firstChange:W,lastChange:H,hasDiff:!0,diffText:ue}),s.info(`Formatted new file content as diff, ${he.length} lines`)
2662
- continue}}catch(fe){s.warn(`Error getting new file content for ${F}`,{error:(0,u.getErrorMessage)(fe)})}S.set(F,{firstChange:W,lastChange:H,hasDiff:!1,diffText:"Only one commit available for this file"})
2663
- continue}if(B.commitHash===H.commitHash&&G){if(U===B.commitHash){S.set(F,{firstChange:W,lastChange:H,hasDiff:!1,diffText:"No changes detected"})
2683
+ E.set(F,{firstChange:W,lastChange:H,hasDiff:!0,diffText:ue}),s.info(`Formatted new file content as diff, ${he.length} lines`)
2684
+ continue}}catch(fe){s.warn(`Error getting new file content for ${F}`,{error:(0,u.getErrorMessage)(fe)})}E.set(F,{firstChange:W,lastChange:H,hasDiff:!1,diffText:"Only one commit available for this file"})
2685
+ continue}if(B.commitHash===H.commitHash&&G){if(U===B.commitHash){E.set(F,{firstChange:W,lastChange:H,hasDiff:!1,diffText:"No changes detected"})
2664
2686
  continue}s.info("Special case: First and last task commits are the same, but pre-task commit is different")}let V="",K=!1
2665
- if(m||"pre-task"!==W.status){try{if(t.git){try{await t.git.show([`${H.commitHash}:${F}`])}catch(ge){s.warn(`File ${F} does not exist in commit ${H.commitHash}, might be deleted or moved`),V="File was deleted or moved from this location during the task",K=!0,S.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V})
2687
+ if(m||"pre-task"!==W.status){try{if(t.git){try{await t.git.show([`${H.commitHash}:${F}`])}catch(ge){s.warn(`File ${F} does not exist in commit ${H.commitHash}, might be deleted or moved`),V="File was deleted or moved from this location during the task",K=!0,E.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V})
2666
2688
  continue}let me
2667
2689
  if("pre-task"===W.status?(me=[`${W.commitHash}..${H.commitHash}`,"--",F],s.info(`Comparing pre-task commit with final commit: ${W.commitHash?W.commitHash.substring(0,8):"unknown"}..${H.commitHash?H.commitHash.substring(0,8):"unknown"} for ${F}`)):(me=[`4b825dc642cb6eb9a060e54bf8d69288fbee4904..${H.commitHash}`,"--",F],s.info(`File created during task, showing full file diff using empty tree object: ${H.commitHash?H.commitHash.substring(0,8):"unknown"} for ${F}`)),V=await t.git.diff(me),K=!!V&&V.trim().length>0,!K&&W.commitHash!==H.commitHash){s.info("No diff found between commits but commits are different, trying direct file content comparison")
2668
2690
  try{const ye=await t.git.show([`${W.commitHash}:${F}`]),we=await t.git.show([`${H.commitHash}:${F}`])
2669
2691
  if(ye!==we&&(K=!0,V="Changes detected through direct content comparison (not visible in standard diff)",m)){const be=ye.split("\n"),ve=we.split("\n")
2670
2692
  Math.abs(be.length-ve.length)>0&&(V+=`\n\nLine count changed: ${be.length} \u2192 ${ve.length}`),ye.includes("\0")||we.includes("\0")?V+="\n\nBinary content detected - changes not displayed":ye.replace(/\s+/g,"")===we.replace(/\s+/g,"")&&(V+="\n\nWhitespace-only changes detected")}}catch(ke){s.warn(`Error in direct file comparison: ${ke instanceof Error?ke.message:String(ke)}`)}}s.info(`Diff generation result for ${F}: hasDiff=${K}, length=${V.length}`)}}catch(Ie){if(s.warn(`Error getting diff for ${F}`,{error:(0,u.getErrorMessage)(Ie),firstCommit:B.commitHash||"unknown",lastCommit:H.commitHash||"unknown"}),V=`Error getting diff: ${(0,u.getErrorMessage)(Ie)}`,"pre-task"!==B.status&&t.git)try{const $e=[`${H.commitHash}:${F}`]
2671
2693
  s.info(`Trying fallback to show file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${F}`)
2672
- const Ee=await t.git.show($e)
2673
- if(Ee&&Ee.trim().length>0){if(m){let Se=""
2674
- const xe=Ee.split("\n")
2675
- for(const _e of xe)Se+=`+${_e}\n`
2676
- V=Se}else V="File created during this task. Content:\n"+Ee
2677
- K=!0,s.info(`Fallback succeeded, got file content of length ${Ee.length}`)}}catch(Ce){s.warn(`Fallback also failed for ${F}`,{error:Ce instanceof Error?Ce.message:String(Ce)})}}S.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V||"No changes detected between commits"})}else K=W.commitHash!==H.commitHash,V=K?"File has changes":"No changes detected between commits",S.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V})}let x=`# File Changes for Task: ${f}\n\n`
2678
- b&&"object"==typeof b&&(x+="## Task Information\n",x+=`- **Title**: ${b.title||"Unknown"}\n`,x+=`- **Status**: ${b.status||"Unknown"}\n`,x+=`- **Description**: ${b.description||"No description"}\n\n`)
2679
- const _=3e3
2694
+ const Se=await t.git.show($e)
2695
+ if(Se&&Se.trim().length>0){if(m){let Ee=""
2696
+ const _e=Se.split("\n")
2697
+ for(const xe of _e)Ee+=`+${xe}\n`
2698
+ V=Ee}else V="File created during this task. Content:\n"+Se
2699
+ K=!0,s.info(`Fallback succeeded, got file content of length ${Se.length}`)}}catch(Ce){s.warn(`Fallback also failed for ${F}`,{error:Ce instanceof Error?Ce.message:String(Ce)})}}E.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V||"No changes detected between commits"})}else K=W.commitHash!==H.commitHash,V=K?"File has changes":"No changes detected between commits",E.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V})}let _=`# File Changes for Task: ${f}\n\n`
2700
+ b&&"object"==typeof b&&(_+="## Task Information\n",_+=`- **Title**: ${b.title||"Unknown"}\n`,_+=`- **Status**: ${b.status||"Unknown"}\n`,_+=`- **Description**: ${b.description||"No description"}\n\n`)
2701
+ const x=3e3
2680
2702
  let C=0,q=!1
2681
- if(m){for(const qe of S.values())qe.hasDiff&&qe.diffText&&(C+=qe.diffText.split("\n").length)
2682
- q=C>_,s.info(`Total diff size: ${C} lines (limit: ${_} lines)`),s.info(`Diffs ${q?"will be limited":"will be shown"} based on size`)}if(x+="## Summary\n",x+=`- **Files Modified**: ${E.size}\n`,x+=`- **Total File Operations**: ${$.length}\n`,x+="- **What's Shown**: For existing files, comparing state before task started with final state.\n",x+=" For files created during task, showing the entire file as created.\n",q?(x+=`- **Diff Display**: Limited (total diff size of ${C} lines exceeds limit of ${_} lines)\n`,x+=" Use file-history tool with individual files to see detailed changes\n\n"):x+=`- **Diff Display**: ${m?"Enabled":"Disabled"} (use showDiff=${!m} to ${m?"hide":"show"} diffs)\n\n`,I.allMoves.length>0){x+="## Move Operations\n\n",x+=`Detected ${I.allMoves.length} file move operation(s):\n\n`
2683
- for(const Te of I.allMoves)x+=`- **${Te.source}** \u2192 **${Te.destination}**\n`,x+=` - Operation: ${Te.type}\n`,x+=` - Timestamp: ${new Date(Te.timestamp).toLocaleString()}\n`,x+=` - Record ID: ${Te.recordId}\n\n`}x+="## Modified Files\n\n"
2684
- for(const[je,Pe]of S.entries()){const{normalizedPath:Re,fullPath:Ae}=d.validateAndNormalizePath(je,t)
2703
+ if(m){for(const qe of E.values())qe.hasDiff&&qe.diffText&&(C+=qe.diffText.split("\n").length)
2704
+ q=C>x,s.info(`Total diff size: ${C} lines (limit: ${x} lines)`),s.info(`Diffs ${q?"will be limited":"will be shown"} based on size`)}if(_+="## Summary\n",_+=`- **Files Modified**: ${S.size}\n`,_+=`- **Total File Operations**: ${$.length}\n`,_+="- **What's Shown**: For existing files, comparing state before task started with final state.\n",_+=" For files created during task, showing the entire file as created.\n",q?(_+=`- **Diff Display**: Limited (total diff size of ${C} lines exceeds limit of ${x} lines)\n`,_+=" Use file-history tool with individual files to see detailed changes\n\n"):_+=`- **Diff Display**: ${m?"Enabled":"Disabled"} (use showDiff=${!m} to ${m?"hide":"show"} diffs)\n\n`,I.allMoves.length>0){_+="## Move Operations\n\n",_+=`Detected ${I.allMoves.length} file move operation(s):\n\n`
2705
+ for(const Te of I.allMoves)_+=`- **${Te.source}** \u2192 **${Te.destination}**\n`,_+=` - Operation: ${Te.type}\n`,_+=` - Timestamp: ${new Date(Te.timestamp).toLocaleString()}\n`,_+=` - Record ID: ${Te.recordId}\n\n`}_+="## Modified Files\n\n"
2706
+ for(const[je,Pe]of E.entries()){const{normalizedPath:Re,fullPath:Ae}=d.validateAndNormalizePath(je,t)
2685
2707
  let Oe=!1
2686
- try{await a.access(Ae),Oe=!0}catch{}x+=`### ${je} ${Oe?"":"(Deleted)"}\n\n`
2708
+ try{await a.access(Ae),Oe=!0}catch{}_+=`### ${je} ${Oe?"":"(Deleted)"}\n\n`
2687
2709
  const Me=new Date(Pe.firstChange.timestamp).toLocaleString(),De=new Date(Pe.lastChange.timestamp).toLocaleString()
2688
- x+=`**First Change**: ${Me}\n`,x+=`**Last Change**: ${De}\n`,"pre-task"===Pe.firstChange.status&&Pe.firstChange.commitHash?x+=`**Pre-Task Commit**: ${Pe.firstChange.commitHash.substring(0,8)}\n`:Pe.firstChange.commitHash&&(x+=`**File Created During Task**: ${Pe.firstChange.commitHash.substring(0,8)}\n`),Pe.lastChange.commitHash&&(x+=`**Final Commit**: ${Pe.lastChange.commitHash.substring(0,8)}\n`)
2710
+ _+=`**First Change**: ${Me}\n`,_+=`**Last Change**: ${De}\n`,"pre-task"===Pe.firstChange.status&&Pe.firstChange.commitHash?_+=`**Pre-Task Commit**: ${Pe.firstChange.commitHash.substring(0,8)}\n`:Pe.firstChange.commitHash&&(_+=`**File Created During Task**: ${Pe.firstChange.commitHash.substring(0,8)}\n`),Pe.lastChange.commitHash&&(_+=`**Final Commit**: ${Pe.lastChange.commitHash.substring(0,8)}\n`)
2689
2711
  const Fe=I.sourceToMove.get(je),Ne=I.destToMove.get(je)
2690
- Fe?x+=`**\u2139\ufe0f Move Operation**: This file was moved TO **${Fe.destination}**\n`:Ne&&(x+=`**\u2139\ufe0f Move Operation**: This file was moved FROM **${Ne.source}**\n`),m&&!q?Pe.hasDiff?(x+="\n**Changes**:\n",x+="```diff\n",x+=Pe.diffText,x+="\n```\n\n"):x+=`\n**Note**: ${Pe.diffText}\n\n`:q&&Pe.hasDiff?(x+=`\n**Note**: File has changes (${Pe.diffText?Pe.diffText.split("\n").length:0} lines). `,x+=`Compare ${Pe.firstChange.commitHash?Pe.firstChange.commitHash.substring(0,8):"unknown"}..${Pe.lastChange.commitHash?Pe.lastChange.commitHash.substring(0,8):"unknown"} to see changes.\n\n`):Pe.hasDiff?x+="\n**Note**: File has changes. Use showDiff=true to see the diff.\n\n":x+=`\n**Note**: ${Pe.diffText}\n\n`,x+="---\n\n"}return p({success:!0,fileCount:E.size}),{content:[{type:"text",text:x}]}}catch(ze){return s.error("Error in task file history tool",{error:ze instanceof Error?ze.stack:String(ze)}),p({success:!1,error:ze instanceof Error?ze.name:"unknown"}),{content:[{type:"text",text:`Error: ${(0,u.getErrorMessage)(ze)}`}],isError:!0}}}
2712
+ Fe?_+=`**\u2139\ufe0f Move Operation**: This file was moved TO **${Fe.destination}**\n`:Ne&&(_+=`**\u2139\ufe0f Move Operation**: This file was moved FROM **${Ne.source}**\n`),m&&!q?Pe.hasDiff?(_+="\n**Changes**:\n",_+="```diff\n",_+=Pe.diffText,_+="\n```\n\n"):_+=`\n**Note**: ${Pe.diffText}\n\n`:q&&Pe.hasDiff?(_+=`\n**Note**: File has changes (${Pe.diffText?Pe.diffText.split("\n").length:0} lines). `,_+=`Compare ${Pe.firstChange.commitHash?Pe.firstChange.commitHash.substring(0,8):"unknown"}..${Pe.lastChange.commitHash?Pe.lastChange.commitHash.substring(0,8):"unknown"} to see changes.\n\n`):Pe.hasDiff?_+="\n**Note**: File has changes. Use showDiff=true to see the diff.\n\n":_+=`\n**Note**: ${Pe.diffText}\n\n`,_+="---\n\n"}return p({success:!0,fileCount:S.size}),{content:[{type:"text",text:_}]}}catch(ze){return s.error("Error in task file history tool",{error:ze instanceof Error?ze.stack:String(ze)}),p({success:!1,error:ze instanceof Error?ze.name:"unknown"}),{content:[{type:"text",text:`Error: ${(0,u.getErrorMessage)(ze)}`}],isError:!0}}}
2691
2713
  const i=t(59860),a=s(t(91943)),c=t(79671),l=t(82960),d=s(t(85967)),u=t(58824),h=t(94466)
2692
2714
  exports.PRECISION_EDIT_TYPES=["replace-strings","insert-lines","replace-lines"]},20979(e,exports){function t(e,t){const n=e.trim().replace(/^\.\//,"").replace(/\/+$/,""),o=t.trim().replace(/^\.\//,"")
2693
2715
  if(!n||!o)return!1
@@ -2703,8 +2725,8 @@ exports.def={name:"guidance_feedback",description:'Report whether a rule you met
2703
2725
  if(!t.apiClient||!i)return{content:[{type:"text",text:"guidance_feedback unavailable: no project context."}],isError:!0}
2704
2726
  try{const e=await t.apiClient.proxyPost(`/api/projects/${i}/guidance/${encodeURIComponent(r)}/feedback`,{verdict:n,reason:o,sessionExternalId:t.context.currentSessionId??void 0,...s?{requestId:s}:{}}),a="conflict"===n?e?.data?.decided??e?.decided:void 0
2705
2727
  return{content:[{type:"text",text:"obsolete"===n?"Recorded. The guidance is flagged for the user to review; it stays in force until they retire it.":"conflict"!==n||"waive"!==a&&!0!==e?.data?.waived&&!0!==e?.waived?"keep"===a?"The user already decided this for this request: the rule stands. Follow the rule, and say in your completion note which instruction you could not honour because of it.":"conflict"===n?"Recorded. The user gets a card for this rule on your request (keep it, waive it here, or retire it); you will be told their answer. Follow the rule until then.":"Recorded as confirmed.":"The user already waived this rule for this request: follow their instruction. The Rules reviewer will not raise this rule here."}]}}catch(e){return{content:[{type:"text",text:`Could not record feedback: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}},94669(e,exports,t){exports.def=void 0
2706
- const r=t(11569),n=r.z.object({body:r.z.string().min(1).max(800).describe('The guidance, one fact or instruction. The user decides on it, so write it in words they know: what it means for their files, the command only where one is needed, no internal format terms (sectPr, openxml, a library\'s internals). Nothing addressed to the user. e.g. "Build alchemy-local with npm run build:dev; a bare tsc -b exits 0 and silently leaves dist stale."'),scope:r.z.enum(["files","area","project","workflow","you"]).describe('"files" (the usual case: pin it to the files it is about, so agents meet it when they touch them), "area", "project", "workflow" (every project on the workflow), or "you" (how the user wants to work, in every project they own; only for something the user said about themselves)'),enforcement:r.z.enum(["told","reviewed","proven"]).optional().describe('How strong it would be: "told" (told to agents, the default: they get it where it applies), "reviewed" (checked in review: the Rules reviewer checks each change against it), "proven" (proven by a check: every request it applies to must show it)'),pathAnchors:r.z.array(r.z.string()).max(8).optional().describe('scope "files": repo-relative files or folders (a folder covers everything under it; * and ** globs work)'),area:r.z.string().optional().describe('scope "area": the area name'),evidence:r.z.string().min(1).max(1500).describe("What happened that shows it: the command, what it printed, the file. The user judges from this."),requestId:r.z.string().optional().describe("The request you were working when you found it (req...)"),standard:r.z.object({documentId:r.z.string().regex(/^doc\d{3,}$/),section:r.z.string().min(1).max(120)}).optional().describe("An addition to a standard the user gave as a document (a rule whose source is a document): its documentId and the section it belongs under (an existing heading, or a new one). Adopting it also writes it into that document. For a general judgement made in this project that the standard does not cover yet."),revises:r.z.string().regex(/^(Q-[A-Z0-9]+-\d{3}|(pr|wf|you|rule):[A-Za-z0-9_:.-]{1,120}|[A-Za-z0-9_.-]{6,80})$/).optional().describe("What this proposal replaces. Guidance in force that it changes: its key (pr:..., wf:...:..., as your rules block and get-info list it); adopting it rewords that item in place instead of adding a second one beside it. Always name it when your proposal changes guidance that stands. Or a proposal of yours still waiting: its code (Q-...), which is withdrawn. Without it, nothing is replaced.")})
2707
- exports.def={name:"guidance_propose",description:'Propose guidance you just learned the hard way, for the user to adopt (guidance of your own is only ever a proposal). Use it when you hit something non-obvious that the next agent touching the same files would hit too: a build or check that fails silently, a command that must be run a certain way, a generated file that must not be edited by hand, a trap in the tooling. Pin it to the files it is about (scope "files"). Not for taste or style, not for what existing guidance already says (to change guidance that stands, pass revises with its key), not for anything specific to this one request, and at most two per request. The user adopts or skips it from their inbox; you are not told the answer and should not wait for it.',schema:n,annotations:{title:"Propose Guidance",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:async(e,t)=>{const r=e,n=t.context.projectId
2728
+ const r=t(11569),n=r.z.object({body:r.z.string().min(1).max(800).describe('One fact or instruction for agents, in words the user knows (they decide on it): what it means for their files, a command only where needed, no internal format terms. e.g. "Build with npm run build:dev; a bare tsc -b exits 0 and leaves dist stale."'),scope:r.z.enum(["files","area","project","workflow","you"]).describe('"files" (the usual case), "area", "project", "workflow" (every project on it), or "you" (how the user wants to work everywhere; only for something they said about themselves)'),enforcement:r.z.enum(["told","reviewed","proven"]).optional().describe('"told" (default: agents are told where it applies), "reviewed" (the Rules reviewer checks each change against it), "proven" (every request it applies to must show it)'),pathAnchors:r.z.array(r.z.string()).max(8).optional().describe('scope "files": repo-relative files or folders; globs work'),area:r.z.string().optional().describe('scope "area": the area name'),evidence:r.z.string().min(1).max(1500).describe("What shows it: the command, what it printed, the file. The user judges from this."),requestId:r.z.string().optional().describe("The request you were working on (req...)"),standard:r.z.object({documentId:r.z.string().regex(/^doc\d{3,}$/),section:r.z.string().min(1).max(120)}).optional().describe("An addition to a standard the user gave as a document, for a general judgement it does not cover yet: its documentId and the section it belongs under (an existing heading or a new one). Adopting it also writes it into that document."),revises:r.z.string().regex(/^(Q-[A-Z0-9]+-\d{3}|(pr|wf|you|rule):[A-Za-z0-9_:.-]{1,120}|[A-Za-z0-9_.-]{6,80})$/).optional().describe("What this replaces: the key of guidance in force that it changes (pr:..., wf:...:..., as your rules block lists it), reworded in place on adoption, always given when you change guidance that stands; or the Q-code of a proposal of yours still waiting, which is withdrawn.")})
2729
+ exports.def={name:"guidance_propose",description:'Propose guidance for the user to adopt: something non-obvious the next agent touching the same files would hit too (a check that fails silently, a command that must run a certain way, a generated file not to edit by hand). Pin it to those files (scope "files"). Not for taste, not for this one request, not for what guidance already says (to change guidance that stands, pass revises); at most two per request. The user adopts or skips it from a card; do not wait for the answer.',schema:n,annotations:{title:"Propose Guidance",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:async(e,t)=>{const r=e,n=t.context.projectId
2708
2730
  if(!t.apiClient||!n)return{content:[{type:"text",text:"guidance_propose unavailable: no project context."}],isError:!0}
2709
2731
  try{const e=await t.apiClient.proxyPost(`/api/projects/${n}/guidance/propose`,{body:r.body,scope:r.scope,enforcement:r.enforcement,pathAnchors:r.pathAnchors,area:r.area,evidence:r.evidence,requestId:r.requestId,revises:r.revises,...r.standard?{standard:r.standard}:{},sessionExternalId:t.cliOrigin?.sessionId??t.context.currentSessionId??void 0}),o=e?.data?.messageCode??e?.messageCode,s=e?.data?.revised??e?.revised
2710
2732
  return{content:[{type:"text",text:`Proposed${o?` (${o})`:""}${s?`, replacing ${s}`:""}. It waits in the user's inbox; if they adopt it, agents get it where it applies from then on. Carry on with your work: you will not be told the answer.`}]}}catch(e){return{content:[{type:"text",text:`Not proposed: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}},91472(e,exports,t){function r(e,t){const r=e.path.length?e.path.join("."):"<root>",n=function(e,t){let r=e
@@ -2733,7 +2755,7 @@ return(0,w.generateVisibleToolsList)(r)}
2733
2755
  let M=O(),D=[]
2734
2756
  M.then(e=>{D=e}).catch(e=>{j.error("Error generating initial tools list",{error:(0,d.getErrorMessage)(e)}),D=[]}),e.setRequestHandler(c.ListToolsRequestSchema,async()=>{try{return D=await O(),{tools:D}}catch(e){return j.error("Error generating tools list",{error:(0,d.getErrorMessage)(e)}),{tools:[]}}}),e.setRequestHandler(c.CallToolRequestSchema,async(e,c)=>{let g
2735
2757
  try{const{name:r,arguments:l={},_meta:$}=e.params
2736
- x&&(0,y.logSessionIdentity)(r,"_meta-debug",{has_meta:!!$,sessionId:$?.sessionId||null,messageId:$?.messageId||null,paramKeys:Object.keys(e.params)}),j.info(`Tool call received: ${r}`,{tool:r}),i.incrementCounter("tool_calls_total",1,{tool:r})
2758
+ _&&(0,y.logSessionIdentity)(r,"_meta-debug",{has_meta:!!$,sessionId:$?.sessionId||null,messageId:$?.messageId||null,paramKeys:Object.keys(e.params)}),j.info(`Tool call received: ${r}`,{tool:r}),i.incrementCounter("tool_calls_total",1,{tool:r})
2737
2759
  const D=i.startTimer("tool_execution_time")
2738
2760
  Date.now()
2739
2761
  let F=Date.now()
@@ -2741,7 +2763,7 @@ if(g=(0,k.readRawArgs)(l),Date.now(),F=Date.now(),R){const e=await A()
2741
2763
  if(Date.now(),!e)return j.warn("Unauthenticated tool call attempt",{tool:r}),{content:[{type:"text",text:"Authentication required. Please run 'alchemylab setup' to authenticate and configure your project."}],isError:!0}}const N=(0,w.getToolByName)(r)
2742
2764
  let z
2743
2765
  if(F=Date.now(),o?(o.setOptions({createRecords:N?.createsRecords??!1}),j.info(`Setting record creation for tool ${r}: ${N?.createsRecords??!1}`)):j.debug(`No API client available for tool ${r} (minimal mode)`),Date.now(),Date.now(),F=Date.now(),$?.sessionId){const e=$.sessionId,t=$.messageId,s=p.sessionIdentity.getIdentity()
2744
- if(s)s.sessionId!==e&&(x&&(0,y.logSessionIdentity)(r,"_meta-session-switch",{prevSessionId:s.sessionId,prevAgentType:s.agentType,newSessionId:e}),p.sessionIdentity.setIdentity({providerId:"opencode",sessionId:e,serverUrl:s.serverUrl}),n?.workspaceManager?.resetCurrentContext?.(e))
2766
+ if(s)s.sessionId!==e&&(_&&(0,y.logSessionIdentity)(r,"_meta-session-switch",{prevSessionId:s.sessionId,prevAgentType:s.agentType,newSessionId:e}),p.sessionIdentity.setIdentity({providerId:"opencode",sessionId:e,serverUrl:s.serverUrl}),n?.workspaceManager?.resetCurrentContext?.(e))
2745
2767
  else{const t=await(0,u.getServerUrl)(n,j)
2746
2768
  p.sessionIdentity.setIdentity({providerId:"opencode",sessionId:e,serverUrl:t||""})}try{const t=await p.sessionIdentity.getSessionRequestId(o,j,e),r=(0,h.sessionMovedRequest)(q,e,t)
2747
2769
  r&&(n?.workspaceManager?.resetCurrentContext?.(e),j.info("session changed request: workspace context reset",{sessionId:e,from:r.from,to:t}))}catch{}await async function(e,t,r,n){try{if(!e?.workspaceManager?.setSessionWorktree||!e.basePath)return
@@ -2751,7 +2773,7 @@ if(!o)return
2751
2773
  const s=await fetch(`${o.replace(/\/$/,"")}/session/${encodeURIComponent(t)}`,{signal:AbortSignal.timeout(3e3)})
2752
2774
  if(!s.ok)return void C.set(t,null)
2753
2775
  const i=await s.json(),a="string"==typeof i?.path?.cwd?i.path.cwd:"",c=["setup-worktrees","test-worktrees"].map(t=>m.join(e.basePath,".alchemy_lab",t)+m.sep)
2754
- a&&c.some(e=>a.startsWith(e))?(C.set(t,a),e.workspaceManager.setSessionWorktree(t,a)):C.set(t,null)}catch{}}(n,e,p.sessionIdentity.getIdentity()?.serverUrl,j),z={providerId:"opencode",sessionId:e,messageId:t,createdAt:(new Date).toISOString()},x&&(0,y.logSessionIdentity)(r,"_meta",{sessionId:e,messageId:t})}else if(O=r,M=$,P||(P=!0,j.warn("No _meta.sessionId on tool call: caller not on alchemylab-opencode fork.",{tool:O,metaKeys:M?Object.keys(M):null})),!1!==N?.requiresSession)throw(0,f.createMcpProtocolError)(`_meta.sessionId required for tool '${r}'`,{tool:r})
2776
+ a&&c.some(e=>a.startsWith(e))?(C.set(t,a),e.workspaceManager.setSessionWorktree(t,a)):C.set(t,null)}catch{}}(n,e,p.sessionIdentity.getIdentity()?.serverUrl,j),z={providerId:"opencode",sessionId:e,messageId:t,createdAt:(new Date).toISOString()},_&&(0,y.logSessionIdentity)(r,"_meta",{sessionId:e,messageId:t})}else if(O=r,M=$,P||(P=!0,j.warn("No _meta.sessionId on tool call: caller not on alchemylab-opencode fork.",{tool:O,metaKeys:M?Object.keys(M):null})),!1!==N?.requiresSession)throw(0,f.createMcpProtocolError)(`_meta.sessionId required for tool '${r}'`,{tool:r})
2755
2777
  p.sessionIdentity.setCurrentCallOrigin(z)
2756
2778
  const L=z?.sessionId||$?.sessionId
2757
2779
  if(n?.workspaceManager){F=Date.now()
@@ -2759,20 +2781,20 @@ try{const e=await(0,u.getServerUrl)(n,j)
2759
2781
  e&&o&&await n.workspaceManager.ensureReadonlyBranchIfNeeded(e,r,o,L)}catch(e){j.debug("Error during workspace check",{error:(0,d.getErrorMessage)(e)})}Date.now()}let B=n?{...n,currentSessionId:L??"__server__"}:null
2760
2782
  B?.documentFileManager&&o&&(B.documentFileManager=(0,a.requestScopedDocuments)(B.documentFileManager,L,(0,a.heldDocumentDepsFrom)(o,j),j))
2761
2783
  let H=!1
2762
- if(B&&N?.readsProjectFiles){const e=await(0,S.committedReadView)(B,L,function(e){return{agentTypeOf:e=>p.sessionIdentity.getAgentTypeFor(e),sessionWorkdir:async e=>{const{getActionServerBaseUrl:r}=t(79414),n=await fetch(`${r()}/api/session-workdir?sessionId=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(15e3)})
2784
+ if(B&&N?.readsProjectFiles){const e=await(0,E.committedReadView)(B,L,function(e){return{agentTypeOf:e=>p.sessionIdentity.getAgentTypeFor(e),sessionWorkdir:async e=>{const{getActionServerBaseUrl:r}=t(79414),n=await fetch(`${r()}/api/session-workdir?sessionId=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(15e3)})
2763
2785
  if(!n.ok)return null
2764
2786
  const o=await n.json()
2765
2787
  return o?.success&&o.data?.workdir?{workdir:o.data.workdir,kind:String(o.data.kind??"")}:null},warn:(t,r)=>e.warn(t,r)}}(j))
2766
- B=e.context,e.applied&&L&&!_.has(L)&&(_.add(L),H=!0)}const U=`${Date.now()}-${Math.random().toString(36).slice(2,10)}`,G=e=>{const{getActionServerBaseUrl:r}=t(79414)
2788
+ B=e.context,e.applied&&L&&!x.has(L)&&(x.add(L),H=!0)}const U=`${Date.now()}-${Math.random().toString(36).slice(2,10)}`,G=e=>{const{getActionServerBaseUrl:r}=t(79414)
2767
2789
  fetch(`${r()}/api/mcp/tool-call-event`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:e,callId:U}),signal:AbortSignal.timeout(500)}).catch(()=>{})}
2768
2790
  let W
2769
2791
  G("start")
2770
2792
  try{if(N){(0,v.preNormalize)(r,g),await(0,I.expandTaskRefsInArgs)(g,o,z?.sessionId,j),g=(0,k.parseToolArgs)((0,w.resolveSchema)(N),g,e=>(0,v.preNormalize)(r,e))
2771
2793
  const t=e.params?._meta?.progressToken,n=c?.sendNotification,a={context:B,apiClient:o,logger:s,metrics:i,mcpServer:T,cliOrigin:z,progressToken:t,sendNotification:n}
2772
2794
  W=await N.handler(g,a)}else j.info(`Tool ${r} not found, returning negative response`),W={content:[{type:"text",text:`The tool '${r}' does not exist.`}]}}finally{G("end")}Date.now(),W=await(0,b.enrichWithAnchoredAnnotations)(r,g,W,o??void 0,B?.projectId,j,B??void 0,z?.sessionId)
2773
- const V=(0,E.noteNudgeFor)({toolName:r,operation:g?.operation,sessionId:z?.sessionId,requestId:z?.sessionId?q.get(z.sessionId)??void 0:void 0})
2795
+ const V=(0,S.noteNudgeFor)({toolName:r,operation:g?.operation,sessionId:z?.sessionId,requestId:z?.sessionId?q.get(z.sessionId)??void 0:void 0})
2774
2796
  if(V){const{getActionServerBaseUrl:e}=t(79414)
2775
- fetch(`${e()}/api/mcp/tool-call-event`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"note-nudge",tool:r,sessionId:z?.sessionId,requestId:z?.sessionId?q.get(z.sessionId)??void 0:void 0}),signal:AbortSignal.timeout(500)}).catch(()=>{})}return W=(0,E.withNoteNudge)(W,V),H&&Array.isArray(W?.content)&&W.content.push({type:"text",text:S.COMMITTED_VIEW_NOTE}),Date.now(),D({tool:r,success:!0}),function(e){const t=e=>/[\uD800-\uDFFF]/.test(e)?e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\ufffd"):e,r=e?.content
2797
+ fetch(`${e()}/api/mcp/tool-call-event`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"note-nudge",tool:r,sessionId:z?.sessionId,requestId:z?.sessionId?q.get(z.sessionId)??void 0:void 0}),signal:AbortSignal.timeout(500)}).catch(()=>{})}return W=(0,S.withNoteNudge)(W,V),H&&Array.isArray(W?.content)&&W.content.push({type:"text",text:E.COMMITTED_VIEW_NOTE}),Date.now(),D({tool:r,success:!0}),function(e){const t=e=>/[\uD800-\uDFFF]/.test(e)?e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\ufffd"):e,r=e?.content
2776
2798
  if(Array.isArray(r))for(const e of r)e&&"string"==typeof e.text&&(e.text=t(e.text))
2777
2799
  return e}(W)}catch(t){j.error("Error executing tool:",{error:t instanceof Error?t.stack:String(t),tool:e.params.name}),i.incrementCounter("tool_errors_total",1,{tool:e.params.name,error:t instanceof Error?t.name:"unknown"})
2778
2800
  let n="Unknown error"
@@ -2783,7 +2805,7 @@ var g=t(87280)
2783
2805
  Object.defineProperty(exports,"logSessionIdentity",{enumerable:!0,get:function(){return g.logSessionIdentity}})
2784
2806
  const y=t(87280)
2785
2807
  t(52462)
2786
- const w=t(82245),b=t(58185),v=t(58622),k=t(79236),I=t(94466),$=(i(t(70857)),t(11569)),E=t(97896),S=t(98537),x="true"===process.env.ALCHEMY_DEBUG_LOGS,_=new Set,C=new Map,q=new Map},65948(e,exports,t){exports.mcpConnectionDefs=void 0
2808
+ const w=t(82245),b=t(58185),v=t(58622),k=t(79236),I=t(94466),$=(i(t(70857)),t(11569)),S=t(97896),E=t(98537),_="true"===process.env.ALCHEMY_DEBUG_LOGS,x=new Set,C=new Map,q=new Map},65948(e,exports,t){exports.mcpConnectionDefs=void 0
2787
2809
  const r=t(76183),n=t(57034)
2788
2810
  exports.mcpConnectionDefs=[{name:"mcp_connections",description:'The project\'s MCP connections: external tool servers the fleet may reach (Runtime > MCP Servers in the product). list = what is registered and whether it is on; propose = register one, switched off, for the user to switch on and grant to agents. Use propose when your work is or needs an MCP server: a server you built in this repository is proposed as transport "repo" with its Dockerfile; a published npm server as "boxed" with a pinned version; a hosted one as "remote" with its url. Never point the user at a config file: the product has no config file for this, and the generated ones are not theirs to edit.',schema:r.McpConnectionsSchema,annotations:{title:"MCP connections",readOnlyHint:!1},createsRecords:!0,handler:(e,t)=>(0,n.handleMcpConnections)(e,t.context,t.apiClient,t.logger,t.metrics)}]},57034(e,exports,t){exports.G=void 0,exports.handleMcpConnections=async function(e,t,i,a,c){const l=c.startTimer("tool_execution_time")
2789
2811
  try{if("list"===e.operation){const e=await i.proxyGet(`/api/projects/${t.projectId}/mcp-servers`),r=Array.isArray(e)?e:e?.data??[]
@@ -2887,11 +2909,11 @@ return t},y(e)},function(e){if(e&&e.__esModule)return e
2887
2909
  var t={}
2888
2910
  if(null!=e)for(var r=y(e),n=0;n<r.length;n++)"default"!==r[n]&&w(t,e,r[n])
2889
2911
  return b(t,e),t})
2890
- Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleMetaWorkflowList=r,exports.handleMetaWorkflowGet=n,exports.handleMetaWorkflowCreate=s,exports.handleMetaWorkflowUpdate=i,exports.handleMetaRecipeList=a,exports.handleMetaRecipeGet=c,exports.handleMetaRecipeCreate=l,exports.handleMetaRecipeAddHook=d,exports.handleMetaRecipeRemoveHook=u,exports.handleMetaHookList=h,exports.handleMetaHookCreate=p,exports.handleMetaWorkflowAddRecipe=f,exports.handleMetaWorkflowRemoveRecipe=m,exports.handleMetaAgentList=g,exports.handleMetaOperations=async function(e,t,r,n,o){const{operation:s,...i}=e??{},a=E[String(s)]
2891
- if(!a)return{content:[{type:"text",text:`Unknown operation '${s}'. Valid: ${Object.keys(E).join(", ")}`}],isError:!0}
2912
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleMetaWorkflowList=r,exports.handleMetaWorkflowGet=n,exports.handleMetaWorkflowCreate=s,exports.handleMetaWorkflowUpdate=i,exports.handleMetaRecipeList=a,exports.handleMetaRecipeGet=c,exports.handleMetaRecipeCreate=l,exports.handleMetaRecipeAddHook=d,exports.handleMetaRecipeRemoveHook=u,exports.handleMetaHookList=h,exports.handleMetaHookCreate=p,exports.handleMetaWorkflowAddRecipe=f,exports.handleMetaWorkflowRemoveRecipe=m,exports.handleMetaAgentList=g,exports.handleMetaOperations=async function(e,t,r,n,o){const{operation:s,...i}=e??{},a=S[String(s)]
2913
+ if(!a)return{content:[{type:"text",text:`Unknown operation '${s}'. Valid: ${Object.keys(S).join(", ")}`}],isError:!0}
2892
2914
  const c=a.schema.safeParse(i)
2893
2915
  return c.success?a.fn(c.data,t,r,n,o):{content:[{type:"text",text:`Invalid params for '${s}': ${c.error.issues.map(e=>`${e.path.join(".")}: ${e.message}`).join("; ")}`}],isError:!0}}
2894
- const k=t(58824),I={order:["test","review","signoff"],test:{enabled:!0,resolver:"auto"},review:{enabled:!1,resolver:"agent"},signoff:{enabled:!1,resolver:"human"}},$=v(t(76183)),E={"workflow-list":{schema:$.MetaWorkflowListSchema,fn:r},"workflow-get":{schema:$.MetaWorkflowGetSchema,fn:n},"workflow-create":{schema:$.MetaWorkflowCreateSchema,fn:s},"workflow-update":{schema:$.MetaWorkflowUpdateSchema,fn:i},"recipe-list":{schema:$.MetaRecipeListSchema,fn:a},"recipe-get":{schema:$.MetaRecipeGetSchema,fn:c},"recipe-create":{schema:$.MetaRecipeCreateSchema,fn:l},"recipe-add-hook":{schema:$.MetaRecipeAddHookSchema,fn:d},"recipe-remove-hook":{schema:$.MetaRecipeRemoveHookSchema,fn:u},"hook-list":{schema:$.MetaHookListSchema,fn:h},"hook-create":{schema:$.MetaHookCreateSchema,fn:p},"workflow-add-recipe":{schema:$.MetaWorkflowAddRecipeSchema,fn:f},"workflow-remove-recipe":{schema:$.MetaWorkflowRemoveRecipeSchema,fn:m},"agent-list":{schema:$.MetaAgentListSchema,fn:g}}},66808(e,exports,t){exports.listDef=exports.cancelDef=exports.followupDef=exports.def=void 0
2916
+ const k=t(58824),I={order:["test","review","signoff"],test:{enabled:!0,resolver:"auto"},review:{enabled:!1,resolver:"agent"},signoff:{enabled:!1,resolver:"human"}},$=v(t(76183)),S={"workflow-list":{schema:$.MetaWorkflowListSchema,fn:r},"workflow-get":{schema:$.MetaWorkflowGetSchema,fn:n},"workflow-create":{schema:$.MetaWorkflowCreateSchema,fn:s},"workflow-update":{schema:$.MetaWorkflowUpdateSchema,fn:i},"recipe-list":{schema:$.MetaRecipeListSchema,fn:a},"recipe-get":{schema:$.MetaRecipeGetSchema,fn:c},"recipe-create":{schema:$.MetaRecipeCreateSchema,fn:l},"recipe-add-hook":{schema:$.MetaRecipeAddHookSchema,fn:d},"recipe-remove-hook":{schema:$.MetaRecipeRemoveHookSchema,fn:u},"hook-list":{schema:$.MetaHookListSchema,fn:h},"hook-create":{schema:$.MetaHookCreateSchema,fn:p},"workflow-add-recipe":{schema:$.MetaWorkflowAddRecipeSchema,fn:f},"workflow-remove-recipe":{schema:$.MetaWorkflowRemoveRecipeSchema,fn:m},"agent-list":{schema:$.MetaAgentListSchema,fn:g}}},66808(e,exports,t){exports.listDef=exports.cancelDef=exports.followupDef=exports.def=void 0
2895
2917
  const r=t(11569),n=r.z.object({conditionType:r.z.enum(["command-check","delivery-verify","deadline","session-idle"]),externalSessionId:r.z.string().optional().describe("session-idle: the session whose turn-completion to watch"),deadlineSeconds:r.z.number().int().positive().optional().describe("deadline: fire after this many seconds"),commandName:r.z.string().optional().describe("command-check: a REGISTERED command name (e.g. ops:stack-health) -- run it and fire on the watched outcome."),environmentId:r.z.string().optional().describe("delivery-verify: the environment whose RUNNING delivery stack to watch. The runner re-verifies it (health + declared checks) every minIntervalSeconds and fires on the watched outcome -- no command needed; this reads the delivery primitive directly."),command:r.z.string().max(400).optional().describe("command-check AD-HOC alternative: any one-line shell command, no registration needed. It runs through the normal command gate: safe read-only shapes run immediately; anything else asks the user ONCE (then remembered). If the gate refuses, the watch ends with a reasoned expiry instead of retrying."),fireOn:r.z.enum(["fail","pass","change"]).optional().describe("command-check: fire when the command exits nonzero (fail, default), exits zero (pass), or when its OUTPUT differs from the previous check (change; the first check sets the baseline). delivery-verify: fail (default) = fire when the stack stops verifying; pass = fire when it verifies. Fires carry the evidence."),minIntervalSeconds:r.z.number().int().positive().optional().describe("command-check / delivery-verify: do not probe more often than this (default 300s, floor 30s). Pick the loosest interval that still catches what you care about."),prompt:r.z.string().min(1).describe("What to tell you when it fires -- write it for your future self, with enough context to act immediately"),note:r.z.string().optional().describe("Why you are waiting (shown in the wake and to the operator)"),ttlSeconds:r.z.number().int().positive().optional().describe("Watch window; if the condition has not happened by then you are woken with an EXPIRED notice (default 4h, max 24h)")})
2896
2918
  exports.def={name:"monitor_create",description:"Arm a monitor on something OUTSIDE the pipeline: a registered check command run against what the project built (fireOn fail or pass), a plain deadline, or another session going quiet. You get woken with your prompt when it fires -- or an expiry notice if it never does. Do NOT arm monitors for pipeline facts: request completions and question answers are delivered to you automatically by the system. Use this instead of polling; arm it, then end your turn cleanly.",schema:n,annotations:{title:"Arm Monitor",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:async(e,t)=>{const r=t.context.currentSessionId
2897
2919
  if(!r||"__server__"===r)return{content:[{type:"text",text:"monitor_create requires a session identity (none on this call)."}],isError:!0}
@@ -3059,7 +3081,7 @@ e.activeOnly=e.activeOnly??t.activeOnly,e.limit=e.limit??t.limit,delete e.list}!
3059
3081
  for(const r of t)e?.[r]&&"object"==typeof e[r]&&(Object.assign(e,e[r]),delete e[r])},task_inspect:function(e){e?.list?.filters&&"object"==typeof e.list.filters?(Object.assign(e,e.list.filters),delete e.list):e?.list&&"object"==typeof e.list&&(Object.assign(e,e.list),delete e.list)},plan_write:function(e){const t=["create","update","append","replace","createSnapshot","restore","setActive"]
3060
3082
  for(const r of t)e?.[r]&&"object"==typeof e[r]&&(Object.assign(e,e[r]),delete e[r])},plan_read:function(e){e?.get?.planId&&!e.planId&&(e.planId=e.get.planId,delete e.get),e?.getHistory?.planId&&!e.planId&&(e.planId=e.getHistory.planId,delete e.getHistory),e?.getRequests?.planId&&!e.planId&&(e.planId=e.getRequests.planId,delete e.getRequests),e?.list&&!e.searchQuery&&(Object.assign(e,e.list),delete e.list)},create_request:function(e){t(e,"acceptanceCriteria"),t(e,"areas")},verify_acceptance_criteria:function(e){t(e,"verifications")}}},56946(e,exports,t){exports.def=void 0
3061
3083
  const r=t(11569),n=r.z.object({description:r.z.string().min(1).max(500).describe('What every such request must SHOW about its result, checkable by a gate: "Every final residual is below 1e-5", not "run the solver carefully"'),kind:r.z.enum(["objective","subjective"]).describe("objective: proven by running something; subjective: judged by the criteria reviewer"),verify:r.z.array(r.z.string().min(1).max(500)).max(4).optional().describe("Objective only: commands that prove it on any such request (they run at the test gate); leave out when each request needs its own evidence")}),o=r.z.object({process:r.z.string().min(1).max(60).describe("The process's name. One the workflow already has proposes adding your criteria to it; a new name proposes a new process."),kind:r.z.enum(["code","engineering","writing"]).optional().describe("Required for a new process: the kind of work it is"),description:r.z.string().max(300).optional().describe('New process: one line on which requests follow it, for whoever decides ("A new or changed simulation case whose numbers go into a write-up")'),criteria:r.z.array(n).min(1).max(12).describe("What every request that follows it must show. Outcomes, not steps: a better agent than you still has to meet these"),why:r.z.string().min(10).max(1500).describe("What you saw that makes this worth standardising: the requests it recurs in, what went missing, what the user said. The user decides from this."),requestId:r.z.string().optional().describe("The request you were working when you saw it (req...), if any")})
3062
- exports.def={name:"process_propose",description:"Propose a workflow process for the user to adopt: a named kind of work (a CFD case, a release note) with what every request that follows it must show, or criteria to add to a process the workflow has. Use it when the same kind of work keeps coming back with the same must-shows, when the user describes how a kind of work must always be done, or when you find a check an existing process is missing. Criteria are outcomes a gate can check, not steps. Not for one request's needs (those are its own criteria). The user adopts or skips it from their inbox; you are not told the answer and should not wait for it.",schema:o,annotations:{title:"Propose Process",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:async(e,t)=>{const r=e,n=t.context.projectId
3084
+ exports.def={name:"process_propose",description:"Propose a workflow process for the user to adopt: a named kind of work with what every request following it must show, or criteria to add to a process the workflow has. Use it when the same kind of work keeps coming back with the same must-shows, when the user describes how a kind of work must always be done, or when a process misses a check. Criteria are outcomes a gate can check, not steps; one request's needs are its own criteria. The user adopts or skips it from a card; do not wait for the answer.",schema:o,annotations:{title:"Propose Process",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:async(e,t)=>{const r=e,n=t.context.projectId
3063
3085
  if(!t.apiClient||!n)return{content:[{type:"text",text:"process_propose unavailable: no project context."}],isError:!0}
3064
3086
  try{const e=await t.apiClient.proxyPost(`/api/projects/${n}/processes/propose`,{process:r.process,kind:r.kind,description:r.description,criteria:r.criteria,why:r.why,requestId:r.requestId,sessionExternalId:t.cliOrigin?.sessionId??t.context.currentSessionId??void 0}),o=e?.data??e
3065
3087
  return{content:[{type:"text",text:`Proposed${o?.messageCode?` (${o.messageCode})`:""}: ${"add"===o?.op?`adding to the process "${o?.process}"`:`a new process "${o?.process}"`}. It waits in the user's inbox; if they adopt it, requests put on the process carry it from then on. Carry on with your work: you will not be told the answer.`}]}}catch(e){return{content:[{type:"text",text:`Not proposed: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}},43707(e,exports,t){function r(e,t=3e3){const r=String(e??"").split("\n").filter(e=>!/^(diff --git |index |new file mode|deleted file mode|similarity index|old mode|new mode)/.test(e)).map(e=>e.replace(/^--- a\//,"--- ").replace(/^\+\+\+ b\//,"+++ ")).join("\n").trim()
@@ -3070,8 +3092,8 @@ return Array.isArray(t)&&0!==t.length?t.map(e=>{const t=[`- ${e.target??"a file"
3070
3092
  "string"==typeof e.error&&e.error&&t.push(` - error: ${e.error}`)
3071
3093
  const r=Array.isArray(e.misses)?e.misses:[]
3072
3094
  for(const e of r)t.push(` - no match: \`${String(e)}\``)
3073
- return t.join("\n")}).join("\n"):"The operation reported no per-file detail."}exports.handleQuickChange=async function(e,I,$,E,S,x,_,C){const q=S.child({tool:"quick_edit"}),T=Date.now()
3074
- try{const P=E.getRequestClient(),R=E.getTaskClient(),A=E.getFileClient(),O=E.getToolRecordClient(),{title:M,description:D,follows:F,...N}=e,z=e.comment||M
3095
+ return t.join("\n")}).join("\n"):"The operation reported no per-file detail."}exports.handleQuickChange=async function(e,I,$,S,E,_,x,C){const q=E.child({tool:"quick_edit"}),T=Date.now()
3096
+ try{const P=S.getRequestClient(),R=S.getTaskClient(),A=S.getFileClient(),O=S.getToolRecordClient(),{title:M,description:D,follows:F,...N}=e,z=e.comment||M
3075
3097
  q.info("Starting quick_edit workflow",{title:M,operationType:I})
3076
3098
  let L=!1
3077
3099
  try{const e=await P.listRequests({limit:2,sortBy:"created_at",sortDirection:"desc"})
@@ -3087,7 +3109,7 @@ null!==o?t.push({path:r.path,before:n,after:o}):B.push({path:r.path,before:n})}}
3087
3109
  t.push({path:N.path,before:r,after:N.append?`${r??""}${n}`:n})}const r=(0,c.quickEditShapeRefusal)((0,c.quickEditShape)(t))
3088
3110
  if(r)return q.info("quick edit refused: not a small edit to an existing file",{files:t.map(e=>e.path)}),{content:[{type:"text",text:r}],isError:!0}
3089
3111
  H=(0,a.emptiedFiles)(t),U=(0,c.contentLoss)(t)}catch(e){q.warn("quick edit pre-check could not run; proceeding",{error:e instanceof Error?e.message:String(e)})}if(H.length>0){let e=[]
3090
- try{const t=await E.proxyGet(`/api/projects/${$.projectId}/test-processes`),r=Array.isArray(t)?t:Array.isArray(t?.data)?t.data:[],n=async e=>(await V(["ls-tree","-r","--name-only",`refs/heads/${K}`,"--",e])??"").split("\n").filter(Boolean)
3112
+ try{const t=await S.proxyGet(`/api/projects/${$.projectId}/test-processes`),r=Array.isArray(t)?t:Array.isArray(t?.data)?t.data:[],n=async e=>(await V(["ls-tree","-r","--name-only",`refs/heads/${K}`,"--",e])??"").split("\n").filter(Boolean)
3091
3113
  e=await(0,a.checksLosingFiles)((0,a.emptiedAsDeletes)(H),r,n)}catch(e){return q.warn("quick edit refused: the project's checks could not be read",{error:(0,w.getErrorMessage)(e)}),{content:[{type:"text",text:"Not done: this edit empties files, and the project's checks could not be read to see whether any of them covers those files. File it with create_request instead. Nothing was changed or created."}],isError:!0}}if(e.length>0)return q.info("quick edit refused: it would take files away from a check",{losses:e}),{content:[{type:"text",text:(0,a.checkLossRefusal)(e)}],isError:!0}}if(U)return q.info("quick edit refused: it takes content out",{removed:U.removed,added:U.added,files:U.files.map(e=>e.path)}),{content:[{type:"text",text:(0,c.contentLossRefusal)(U)}],isError:!0}
3092
3114
  q.debug("Creating quick_edit request")
3093
3115
  const Y=C?.sessionId??$.currentSessionId,J="string"==typeof Y&&/^ses_/.test(Y)?Y:void 0,Q=await(0,v.exchangeKeyOfCall)($.mainBasePath,C),Z=await P.createRequest({title:M,description:D,type:"quick-edit",acceptanceCriteria:[],...J?{creatorSessionId:J}:{},...void 0!==Q?{exchangeKey:Q}:{},..."string"==typeof F&&/^req\d{6}$/.test(F)?{originRequestId:F}:{}})
@@ -3097,18 +3119,18 @@ let ee,te
3097
3119
  q.info("Created request",{requestId:X})
3098
3120
  let re=$
3099
3121
  try{if(!$.workspaceManager)throw new Error("Quick edits need the workspace manager to create their worktree")
3100
- te=await $.workspaceManager.bindSessionToRequestWorktree(o(X),X),re={...$,currentSessionId:o(X),basePath:te,git:(0,y.simpleGit)(te),...$.documentFileManager&&E?{documentFileManager:(0,k.requestScopedDocuments)($.documentFileManager,o(X),{...(0,k.heldDocumentDepsFrom)(E,q),requestFor:async()=>X},q)}:{}},q.info("Quick edit worktree ready",{requestId:X,worktreePath:te}),q.debug("Creating task")
3122
+ te=await $.workspaceManager.bindSessionToRequestWorktree(o(X),X),re={...$,currentSessionId:o(X),basePath:te,git:(0,y.simpleGit)(te),...$.documentFileManager&&S?{documentFileManager:(0,k.requestScopedDocuments)($.documentFileManager,o(X),{...(0,k.heldDocumentDepsFrom)(S,q),requestFor:async()=>X},q)}:{}},q.info("Quick edit worktree ready",{requestId:X,worktreePath:te}),q.debug("Creating task")
3101
3123
  const e=await R.createTask({requestId:X,projectId:$.projectId,title:`Execute ${I} operation`,description:`Quick change: ${M}`})
3102
3124
  if(!e||!e.id)throw new Error("Failed to create task")
3103
3125
  let b
3104
- if(ee=e.id,q.info("Created task",{taskId:ee}),q.debug("Executing file operation",{operationType:I}),"write"===I)b=await(0,u.handleWriteFile)({taskId:ee,...N,comment:z},re,A,R,O,P,S,x,_)
3126
+ if(ee=e.id,q.info("Created task",{taskId:ee}),q.debug("Executing file operation",{operationType:I}),"write"===I)b=await(0,u.handleWriteFile)({taskId:ee,...N,comment:z},re,A,R,O,P,E,_,x)
3105
3127
  else{if("replace"!==I)throw new l.ValidationError(`Unsupported operation type: ${I}`)
3106
- b=await(0,h.handleReplaceInFiles)({taskId:ee,...N,comment:z},re,R,O,P,S,x,_)}if(b.isError){q.error("File operation failed, cleaning up request and task",{requestId:X,taskId:ee})
3128
+ b=await(0,h.handleReplaceInFiles)({taskId:ee,...N,comment:z},re,R,O,P,E,_,x)}if(b.isError){q.error("File operation failed, cleaning up request and task",{requestId:X,taskId:ee})
3107
3129
  try{await R.updateTaskStatus(ee,"canceled"),q.info("Canceled task after operation failure",{taskId:ee})}catch(e){q.warn("Failed to cancel task during cleanup",{taskId:ee,error:e instanceof Error?e.message:String(e)})}try{await P.updateRequest(X,{status:"canceled"}),q.info("Canceled request after operation failure",{requestId:X}),$.workspaceManager&&(await $.workspaceManager.cancelRequest(X),q.info("Cleaned up branch/worktree after operation failure",{requestId:X}))}catch(e){q.warn("Failed to cancel request during cleanup",{requestId:X,error:e instanceof Error?e.message:String(e)})}return{content:[{type:"text",text:`\u274c Quick change failed during ${I} operation:\n\n${b.content[0].text}\n\nThe request (${X}) and task have been canceled and cleaned up.`}],isError:!0}}if(B.length>0)try{const e=await Promise.resolve().then(()=>t.t(91943,23)),r=await Promise.resolve().then(()=>t.t(16928,23)),n=[]
3108
3130
  for(const t of B){const o=await e.readFile(r.join(te??re.basePath,t.path),"utf8").catch(()=>null)
3109
3131
  n.push({path:t.path,before:t.before,after:o})}const o=(0,a.emptiedFiles)(n)
3110
3132
  if(o.length>0){let e=null
3111
- try{const t=await E.proxyGet(`/api/projects/${$.projectId}/test-processes`),r=Array.isArray(t)?t:Array.isArray(t?.data)?t.data:[]
3133
+ try{const t=await S.proxyGet(`/api/projects/${$.projectId}/test-processes`),r=Array.isArray(t)?t:Array.isArray(t?.data)?t.data:[]
3112
3134
  e=await(0,a.checksLosingFiles)((0,a.emptiedAsDeletes)(o),r,async e=>[e])}catch{e=null}if(null===e||e.length>0)return await R.updateTaskStatus(ee,"canceled").catch(()=>{}),await P.updateRequest(X,{status:"canceled"}).catch(()=>{}),await($.workspaceManager?.cancelRequest(X).catch(()=>{})),q.warn("quick edit emptied a checked file after an approximate match; discarded",{requestId:X,emptiedAfter:o}),{content:[{type:"text",text:`${e?(0,a.checkLossRefusal)(e).replace(/^Not done:/,"Not kept:").replace(/ Nothing was changed or created\.$/,""):"Not kept: this edit left files empty, and the project's checks could not be read to see whether any of them covers those files."}\n\nThe edit was discarded before it reached ${K} and ${X} is canceled.`}],isError:!0}}}catch(e){q.warn("quick edit post-check could not run; keeping the edit",{requestId:X,error:e instanceof Error?e.message:String(e)})}const v=(0,d.readAlchemyMeta)(b),C=(j=v,!(!1===j?.failed))
3113
3135
  if(C?q.warn("File operation completed with partial failures",{requestId:X}):q.info("File operation completed successfully"),C)return await R.updateTaskStatus(ee,"canceled").catch(()=>{}),await P.updateRequest(X,{status:"canceled"}).catch(()=>{}),await($.workspaceManager?.cancelRequest(X).catch(()=>{})),$.workspaceManager?.releaseSession(o(X)),q.warn("quick edit partially failed; discarded whole",{requestId:X}),{content:[{type:"text",text:`\u274c Not kept: part of this edit failed, and a quick edit is merged whole or not at all, so ${K} is unchanged. ${X} is canceled.\n\nRead the whole file again (its current text, not what you expected), then retry with replacements that all match. The parts that matched were discarded too: no file below changed on ${K}.\n\n${i(v)}`}],isError:!0}
3114
3136
  let D=""
@@ -3120,19 +3142,19 @@ d.push({path:t,before:n,after:a})}return d}(te,K)
3120
3142
  n=(0,c.quickEditShapeRefusal)((0,c.quickEditShape)(r),{discarded:s}),e=(0,c.contentLoss)(r)}catch(e){q.warn("quick edit content check could not run; keeping the edit",{requestId:X,error:(0,w.getErrorMessage)(e)})}if(n)return await R.updateTaskStatus(ee,"canceled").catch(()=>{}),await P.updateRequest(X,{status:"canceled"}).catch(()=>{}),await($.workspaceManager?.cancelRequest(X).catch(()=>{})),$.workspaceManager?.releaseSession(o(X)),q.warn("quick edit was not a small edit to an existing file; discarded",{requestId:X}),{content:[{type:"text",text:n}],isError:!0}
3121
3143
  if(D=await async function(e,n){const{execFile:o}=await Promise.resolve().then(()=>t.t(35317,23))
3122
3144
  return r(await new Promise((t,r)=>o("git",["diff","--no-color","--no-renames","-U1",`refs/heads/${n}`],{cwd:e,maxBuffer:8388608},(e,n)=>e?r(e):t(String(n)))))}(te,K).catch(()=>""),e)return await R.updateTaskStatus(ee,"canceled").catch(()=>{}),await P.updateRequest(X,{status:"canceled"}).catch(()=>{}),await($.workspaceManager?.cancelRequest(X).catch(()=>{})),$.workspaceManager?.releaseSession(o(X)),q.warn("quick edit took content out; discarded",{requestId:X,removed:e.removed,added:e.added,files:e.files.map(e=>e.path)}),{content:[{type:"text",text:(0,c.contentLossRefusal)(e,{discarded:`The edit was discarded before it reached ${K} and ${X} is canceled.`})}],isError:!0}}q.debug("Completing task")
3123
- try{await(0,p.handleCompleteTask)({taskId:ee,summary:`${M}${C?" (partial success)":""}`},re,E,S,x),q.info("Task completed successfully",{taskId:ee})}catch(e){q.error("Task completion via handleCompleteTask failed, attempting direct status update",{taskId:ee,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0})
3124
- try{await R.updateTaskStatus(ee,"completed"),q.info("Task completed via direct updateTaskStatus call",{taskId:ee})}catch(e){return q.error("Direct task completion ALSO failed - task may be stuck in in_progress",{taskId:ee,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0,operationType:I,isDocumentOperation:!!N.documentId}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change operation succeeded but task completion failed.\n\n**Request:** ${X}\n**Task:** ${ee}\n**Issue:** Could not mark task as completed - it remains in 'in_progress' state\n\nThe ${I} operation was successful, but we couldn't automatically complete the task. Please use the complete-task tool manually with taskId: ${ee}\n\nError: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}const F=(()=>{try{return E.getProjectId?.()??null}catch{return null}})(),H=F?await(0,k.requestDocumentEdits)({get:e=>E.proxyGet(e)},F,X).then(e=>r(e.map(e=>e.diff).join(""))).catch(()=>""):""
3145
+ try{await(0,p.handleCompleteTask)({taskId:ee,summary:`${M}${C?" (partial success)":""}`},re,S,E,_),q.info("Task completed successfully",{taskId:ee})}catch(e){q.error("Task completion via handleCompleteTask failed, attempting direct status update",{taskId:ee,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0})
3146
+ try{await R.updateTaskStatus(ee,"completed"),q.info("Task completed via direct updateTaskStatus call",{taskId:ee})}catch(e){return q.error("Direct task completion ALSO failed - task may be stuck in in_progress",{taskId:ee,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0,operationType:I,isDocumentOperation:!!N.documentId}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change operation succeeded but task completion failed.\n\n**Request:** ${X}\n**Task:** ${ee}\n**Issue:** Could not mark task as completed - it remains in 'in_progress' state\n\nThe ${I} operation was successful, but we couldn't automatically complete the task. Please use the complete-task tool manually with taskId: ${ee}\n\nError: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}const F=(()=>{try{return S.getProjectId?.()??null}catch{return null}})(),H=F?await(0,k.requestDocumentEdits)({get:e=>S.proxyGet(e)},F,X).then(e=>r(e.map(e=>e.diff).join(""))).catch(()=>""):""
3125
3147
  H&&(D=D?`${D}\n${H}`:H),q.debug("Completing request")
3126
3148
  const U=C?`Quick change completed with partial success: ${M}. Some replacements failed - see details below.`:`${M}${D?`\n\n\`\`\`diff\n${D}\n\`\`\``:""}`,G=C?`${I} operation partially completed - some operations succeeded, some failed`:`${I} operation executed successfully`
3127
3149
  let W=!1
3128
3150
  const Y=await V(["rev-list","--count",`refs/heads/${K}..refs/heads/requests/${X}`])
3129
3151
  let J
3130
- if(null!==Y&&Number(Y.trim())>0?W=!0:await($.workspaceManager?.cancelRequest(X).catch(e=>{q.warn("Could not discard the empty quick-edit worktree",{requestId:X,error:(0,w.getErrorMessage)(e)})})),$.workspaceManager?.releaseSession(o(X)),W){try{const e=await(0,f.handleCompleteRequest)({requestId:X,summary:U},$,E,S,x,_)
3152
+ if(null!==Y&&Number(Y.trim())>0?W=!0:await($.workspaceManager?.cancelRequest(X).catch(e=>{q.warn("Could not discard the empty quick-edit worktree",{requestId:X,error:(0,w.getErrorMessage)(e)})})),$.workspaceManager?.releaseSession(o(X)),W){try{const e=await(0,f.handleCompleteRequest)({requestId:X,summary:U},$,S,E,_,x)
3131
3153
  e.isError&&q.warn("complete_request reported an error; attempting merge anyway (merge revalidates)",{requestId:X,error:e.content[0]?.text})}catch(e){q.warn("complete_request threw; attempting merge anyway (merge revalidates)",{requestId:X,error:(0,w.getErrorMessage)(e)})}const e=await P.getRequest(X).catch(()=>null)
3132
3154
  if("ready_for_review"===e?.status)return q.info("Quick edit waits for the user's sign-off",{requestId:X}),{content:[{type:"text",text:n(X,M,K)}],...(0,d.alchemyMeta)({kind:`quick-${I}`,toolRecordId:(0,d.readAlchemyMeta)(b)?.toolRecordId})}
3133
3155
  let t=[]
3134
3156
  try{const e=await P.getRequest(X),r=e?.acceptanceCriteria||[]
3135
- t=Array.from((0,g.buildCriteriaKeyMap)(r).keys()).map(e=>({key:e,evidence:G}))}catch(e){q.debug("Could not build criteria verifications",{requestId:X,error:(0,w.getErrorMessage)(e)})}const r=await(0,m.handleMergeRequest)({requestId:X,verifications:t},$,E,S,x,_)
3157
+ t=Array.from((0,g.buildCriteriaKeyMap)(r).keys()).map(e=>({key:e,evidence:G}))}catch(e){q.debug("Could not build criteria verifications",{requestId:X,error:(0,w.getErrorMessage)(e)})}const r=await(0,m.handleMergeRequest)({requestId:X,verifications:t},$,S,E,_,x)
3136
3158
  let s=null
3137
3159
  try{s=await P.getRequest(X)}catch{}if(r.isError||"completed"!==s?.status){const e=r.content?.[0]?.text||"unknown merge failure"
3138
3160
  q.error("Quick change merge to main failed",{requestId:X,status:s?.status,mergeText:e})
@@ -3141,7 +3163,7 @@ return n?{content:[{type:"text",text:`\u26a0\ufe0f Quick change applied but its
3141
3163
  const i=`refs/heads/requests/${X}`,a=(await V(["rev-parse","--verify","--quiet",i]))?.trim()
3142
3164
  a&&null!==await V(["merge-base","--is-ancestor",a,`refs/heads/${K}`])&&await V(["update-ref","-d",i,a])}else{if(("write"===I||"replace"===I||"manage"===I)&&!s(I,N))return q.error("Quick-edit file change produced no commit; refusing to complete",{requestId:X}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change ${X} produced no commit, so nothing reached ${K}. The request is left open on purpose: completing it would claim a delivery git does not have. Retry the edit, or file it with create_request.`}],isError:!0}
3143
3165
  let e=!1
3144
- try{const t=await(0,f.handleCompleteRequest)({requestId:X,summary:U},$,E,S,x,_),r=t.isError?null:await P.getRequest(X).catch(()=>null)
3166
+ try{const t=await(0,f.handleCompleteRequest)({requestId:X,summary:U},$,S,E,_,x),r=t.isError?null:await P.getRequest(X).catch(()=>null)
3145
3167
  if(t.isError)q.error("Request completion failed, attempting direct completion",{requestId:X,error:t.content[0].text})
3146
3168
  else{if("ready_for_review"===r?.status)return{content:[{type:"text",text:n(X,M,K)}]}
3147
3169
  try{await P.updateRequest(X,{status:"completed",completionSummary:U}),q.info("Request finalized to completed",{requestId:X})}catch(e){q.warn("Failed to finalize request to completed, may be stuck at ready_for_merge",{requestId:X,error:(0,w.getErrorMessage)(e)})}e=!0,q.info("Request completed successfully",{requestId:X})}}catch(e){q.error("Request completion threw exception, attempting direct completion",{requestId:X,error:e instanceof Error?e.message:String(e)})}if(!e)try{q.info("Attempting direct request completion via API",{requestId:X}),await P.updateRequest(X,{status:"completed",completionSummary:U}),e=!0,q.info("Request completed successfully via direct API call",{requestId:X})}catch(e){return q.error("Direct request completion also failed",{requestId:X,error:e instanceof Error?e.message:String(e)}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change operation succeeded but request completion failed.\n\n**Request:** ${X}\n**Status:** Still in 'in_progress' - please complete manually\n\nThe file operation was successful, but we couldn't automatically complete the request. Please use the complete-request tool manually.`}],isError:!0}}}const Q=Date.now()-T
@@ -3153,9 +3175,9 @@ try{await P.updateRequest(X,{status:"canceled"}),q.info("Canceled request after
3153
3175
  return q.error("Quick change failed",{error:(0,w.getErrorMessage)(e),duration:t}),e instanceof l.ValidationError?{content:[{type:"text",text:`\u274c ${e.message}`}],isError:!0}:{content:[{type:"text",text:`\u274c Quick change failed: ${(0,w.getErrorMessage)(e)}`}],isError:!0}}var j}
3154
3176
  const a=t(95122),c=t(81563),l=t(59860),d=t(82272),u=t(14079),h=t(69442),p=t(80355),f=t(15715),m=t(34306),g=t(65454),y=t(83807),w=t(58824),b=t(52232),v=t(62032),k=t(95040)},57250(e,exports,t){exports.def=void 0
3155
3177
  const r=t(76183),n=t(43707)
3156
- exports.def={name:"quick_replace",description:"A quick edit is a request of kind quick edit. Under Manual Autonomy it waits for the user's sign-off on a card that shows the change; on any level it also waits when their Strictness is Thorough or it changes a file a recent request's check of theirs covers; otherwise it merges on its own. An edit to a shared document (documentId) is held on the quick edit and written when it merges. Use it only for a change the user wants kept in their project, never for copies, scratch or preview files (to show unmerged work, use preview_site with the request's requestId). WHAT IT IS: a small edit (at most 20 lines changed) to a file that already exists, of text or settings (docs, copy, config values), never code: code goes to the worker on it (request_operations hand-over) or a request. A new file, a larger edit, or an edit that removes more lines than it puts in is refused: file it with create_request. Reordering, moving and rewriting lines one for one are fine.\n\nReplace text in files or documents. Wraps the change in a quick-edit request.\n\nProvide an array of operations, each with path/documentId and replacements [{old, new}].\nSupports fuzzy matching (default: true) and match_mode ('unique' or 'all').\n\nRequires: title, description, comment, operations.",schema:r.QuickReplaceSchema,annotations:{title:"Quick Replace",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleQuickChange)(e,"replace",t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},40213(e,exports,t){exports.def=void 0
3178
+ exports.def={name:"quick_replace",description:"Replace text in files or documents as a quick edit: operations [{ path | documentId, replacements [{ old, new, match_mode? }] }], fuzzy matching on by default. Requires title, description, comment, operations. The quick-edit rules are quick_write's: a small change to text or settings in a file that exists, never code, never a new file or a net removal; it merges on its own unless the user's settings or checks hold it for their sign-off.",schema:r.QuickReplaceSchema,annotations:{title:"Quick Replace",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleQuickChange)(e,"replace",t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},40213(e,exports,t){exports.def=void 0
3157
3179
  const r=t(76183),n=t(43707)
3158
- exports.def={name:"quick_write",description:"A quick edit is a request of kind quick edit. Under Manual Autonomy it waits for the user's sign-off on a card that shows the change; on any level it also waits when their Strictness is Thorough or it changes a file a recent request's check of theirs covers; otherwise it merges on its own. An edit to a shared document (documentId) is held on the quick edit and written when it merges. Use it only for a change the user wants kept in their project, never for copies, scratch or preview files (to show unmerged work, use preview_site with the request's requestId). WHAT IT IS: a small edit (at most 20 lines changed) to a file that already exists, of text or settings (docs, copy, config values), never code: code goes to the worker on it (request_operations hand-over) or a request. A new file, a larger edit, or an edit that removes more lines than it puts in is refused: file it with create_request. Reordering, moving and rewriting lines one for one are fine.\n\nUpdate a file that exists, or a document. Wraps the change in a quick-edit request.\n\nUse 'path' for filesystem files or 'documentId' for tracked documents.\nSet append:true to add content to existing content.\n\n`content` accepts a single string OR an array of lines (joined with \\n). PREFER the array form for files containing brackets, quotes, or code-like syntax (TOML, JSON, Python, YAML): sidesteps the JSON-escaping foot-gun where multi-line content with `[` or `{` confuses the parser.\n\nRequires: title, description, path/documentId, content.",schema:r.QuickWriteSchema,annotations:{title:"Quick Write",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleQuickChange)(e,"write",t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},52462(e,exports,t){const r=t(82245),n=t(19080),o=t(5692),s=t(9030),i=t(12903),a=t(54607),c=t(49499),l=t(16770),d=t(8045),u=t(89697),h=t(39196),p=t(5459),f=t(94805),m=t(260),g=t(32283),y=t(74165),w=t(96111),b=t(80700),v=t(45837),k=t(56700),I=t(91535),$=t(57257),E=t(9230),S=t(34150),x=t(26473),_=t(6309),C=t(86317),q=t(61391),T=t(57713),j=t(96252),P=t(40213),R=t(57250),A=t(15510),O=t(94669),M=t(56946),D=t(90346),F=t(87133),N=t(66808),z=t(1540),L=t(44973),B=t(48155),H=t(17613),U=t(4961),G=t(68456),W=t(75850),V=t(23322),K=t(50003),Y=t(55247),J=t(57507),Q=t(5169),Z=t(65948),X=t(98095),ee=t(78002),te=t(88849),re=t(70542);(0,r.registerTool)(n.def),(0,r.registerTool)(o.def),(0,r.registerTool)(s.def),(0,r.registerTool)(i.def),(0,r.registerTool)(a.def),(0,r.registerTool)(c.def),(0,r.registerTool)(l.def),(0,r.registerTool)(d.def),(0,r.registerTool)(u.def),(0,r.registerTool)(h.def),(0,r.registerTool)(p.def),(0,r.registerTool)(f.def),(0,r.registerTool)(m.def),(0,r.registerTool)(g.def),(0,r.registerTool)(y.def),(0,r.registerTool)(w.def),(0,r.registerTool)(b.def),(0,r.registerTool)(v.def),(0,r.registerTool)(k.def),(0,r.registerTool)(I.def),(0,r.registerTool)(S.def),(0,r.registerTool)($.def),(0,r.registerTool)(E.def),(0,r.registerTool)(x.def),(0,r.registerTool)(_.def),(0,r.registerTool)(C.def),(0,r.registerTool)(q.def),(0,r.registerTool)(T.def),(0,r.registerTool)(j.def),(0,r.registerTool)(P.def),(0,r.registerTool)(R.def),(0,r.registerTool)(A.def),(0,r.registerTool)(O.def),(0,r.registerTool)(M.def),(0,r.registerTool)(D.def),(0,r.registerTool)(F.def),(0,r.registerTool)(N.def),(0,r.registerTool)(N.cancelDef),(0,r.registerTool)(N.listDef),(0,r.registerTool)(N.followupDef),(0,r.registerTool)(z.def),(0,r.registerTool)(L.def),(0,r.registerTool)(B.def),(0,r.registerTool)(H.def),(0,r.registerTool)(U.def),(0,r.registerTool)(G.def),(0,r.registerTool)(W.def),(0,r.registerTools)(V.metaDefs),(0,r.registerTools)(K.contractDefs),(0,r.registerTools)(Y.testingDefs),(0,r.registerTools)(J.environmentDefs),(0,r.registerTools)(Q.resultDefs),(0,r.registerTools)(Z.mcpConnectionDefs),(0,r.registerTools)(X.experimentDefs),(0,r.registerTools)(ee.visualDefs),(0,r.registerTools)(te.checkingDefs),(0,r.registerTools)(re.debugDefs),(0,t(82245).freezeRegistry)()},82245(e,exports,t){function r(e){if(d)throw new Error(`Tool registry is frozen: cannot register '${e.name}'. Core tools must be added in registrations.ts at build time.`)
3180
+ exports.def={name:"quick_write",description:"Update a file that exists (path) or a document (documentId) as a quick edit; append:true adds to the end. content is a string or an array of lines (prefer the array for JSON, TOML, YAML or anything with brackets and quotes). Requires title, description, content.\n\nA quick edit is a small change (at most 20 lines) to text or settings in a file that exists (docs, copy, config values), never code: code goes to the worker on it (request_operations hand-over) or to a request. A new file, a larger edit, or one that removes more lines than it adds is refused: file it with create_request. Reordering, moving and rewriting lines one for one are fine. Only for a change the user wants kept in their project, never copies, scratch or preview files (to show unmerged work, preview_site with the request's requestId). It becomes a request of kind quick edit that merges on its own, except that it waits for the user's sign-off on a card under Manual Autonomy, under Thorough Strictness, or when it changes a file one of their recent checks covers. An edit to a shared document is written when it merges.",schema:r.QuickWriteSchema,annotations:{title:"Quick Write",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleQuickChange)(e,"write",t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},52462(e,exports,t){const r=t(82245),n=t(19080),o=t(5692),s=t(9030),i=t(12903),a=t(54607),c=t(49499),l=t(16770),d=t(8045),u=t(89697),h=t(39196),p=t(5459),f=t(94805),m=t(260),g=t(32283),y=t(74165),w=t(96111),b=t(80700),v=t(45837),k=t(56700),I=t(91535),$=t(57257),S=t(9230),E=t(34150),_=t(26473),x=t(6309),C=t(86317),q=t(61391),T=t(57713),j=t(96252),P=t(40213),R=t(57250),A=t(15510),O=t(94669),M=t(56946),D=t(90346),F=t(87133),N=t(66808),z=t(1540),L=t(44973),B=t(48155),H=t(17613),U=t(4961),G=t(68456),W=t(75850),V=t(23322),K=t(50003),Y=t(55247),J=t(57507),Q=t(5169),Z=t(65948),X=t(98095),ee=t(78002),te=t(88849),re=t(70542);(0,r.registerTool)(n.def),(0,r.registerTool)(o.def),(0,r.registerTool)(s.def),(0,r.registerTool)(i.def),(0,r.registerTool)(a.def),(0,r.registerTool)(c.def),(0,r.registerTool)(l.def),(0,r.registerTool)(d.def),(0,r.registerTool)(u.def),(0,r.registerTool)(h.def),(0,r.registerTool)(p.def),(0,r.registerTool)(f.def),(0,r.registerTool)(m.def),(0,r.registerTool)(g.def),(0,r.registerTool)(y.def),(0,r.registerTool)(w.def),(0,r.registerTool)(b.def),(0,r.registerTool)(v.def),(0,r.registerTool)(k.def),(0,r.registerTool)(I.def),(0,r.registerTool)(E.def),(0,r.registerTool)($.def),(0,r.registerTool)(S.def),(0,r.registerTool)(_.def),(0,r.registerTool)(x.def),(0,r.registerTool)(C.def),(0,r.registerTool)(q.def),(0,r.registerTool)(T.def),(0,r.registerTool)(j.def),(0,r.registerTool)(P.def),(0,r.registerTool)(R.def),(0,r.registerTool)(A.def),(0,r.registerTool)(O.def),(0,r.registerTool)(M.def),(0,r.registerTool)(D.def),(0,r.registerTool)(F.def),(0,r.registerTool)(N.def),(0,r.registerTool)(N.cancelDef),(0,r.registerTool)(N.listDef),(0,r.registerTool)(N.followupDef),(0,r.registerTool)(z.def),(0,r.registerTool)(L.def),(0,r.registerTool)(B.def),(0,r.registerTool)(H.def),(0,r.registerTool)(U.def),(0,r.registerTool)(G.def),(0,r.registerTool)(W.def),(0,r.registerTools)(V.metaDefs),(0,r.registerTools)(K.contractDefs),(0,r.registerTools)(Y.testingDefs),(0,r.registerTools)(J.environmentDefs),(0,r.registerTools)(Q.resultDefs),(0,r.registerTools)(Z.mcpConnectionDefs),(0,r.registerTools)(X.experimentDefs),(0,r.registerTools)(ee.visualDefs),(0,r.registerTools)(te.checkingDefs),(0,r.registerTools)(re.debugDefs),(0,t(82245).freezeRegistry)()},82245(e,exports,t){function r(e){if(d)throw new Error(`Tool registry is frozen: cannot register '${e.name}'. Core tools must be added in registrations.ts at build time.`)
3159
3181
  if(l.has(e.name))throw new Error(`Duplicate tool registration: '${e.name}'`)
3160
3182
  c.push(e),l.set(e.name,e)}function n(e){const t=e.replace(/\s+/g," ").trim(),r=/^(.{20,200}?[.!?])(\s|$)/.exec(t)
3161
3183
  return r?r[1]:t.slice(0,200)}function o(e){return"function"==typeof e.schema?e.schema():e.schema}exports.SHORT_FORM_TOOLS=void 0,exports.registerTool=r,exports.registerTools=function(e){for(const t of e)r(t)},exports.freezeRegistry=function(){d=!0
@@ -3166,17 +3188,17 @@ return c.filter(r=>!(r.debugOnly&&!t||("function"==typeof r.hidden?r.hidden(e):r
3166
3188
  return exports.SHORT_FORM_TOOLS.has(t.name)?{name:t.name,description:`${n(r)} (Short form: call tool_contract with { name: "${t.name}" } for its arguments before the first call.)`,inputSchema:{type:"object",properties:{},additionalProperties:!0},annotations:t.annotations}:{name:t.name,description:r,inputSchema:(0,s.zodToJsonSchema)(o(t)),annotations:t.annotations}})},exports.resolveSchema=o
3167
3189
  const s=t(25106),i=t(3444),a=t(92040),c=[],l=new Map
3168
3190
  let d=!1
3169
- exports.SHORT_FORM_TOOLS=new Set(["monitor_list","monitor_create","monitor_cancel","request_visual_feedback","ask_user_visual","fetch_asset","mcp_connections","show_user","guidance_feedback","followup_create","research_fanout"])},15715(e,exports,t){function r(e,t){return`Not completed yet: ${e} was completed before, and the summary you pass now REPLACES the one the user reads when it lands. The earlier one:\n "${t.length>600?`${t.slice(0,597)}...`:t}"\nWrite a summary that describes everything ${e} delivers, with this round's fixes folded in (not only the fixes), and call complete again with replacesSummary: true.`}exports.handleCompleteRequest=async function(e,t,E,S,x,_,C){const q=S.child({tool:"complete_request"}),T=x.startTimer("tool_execution_time")
3191
+ exports.SHORT_FORM_TOOLS=new Set(["monitor_list","monitor_create","monitor_cancel","request_visual_feedback","ask_user_visual","fetch_asset","mcp_connections","show_user","guidance_feedback","followup_create","research_fanout"])},15715(e,exports,t){function r(e,t){return`Not completed yet: ${e} was completed before, and the summary you pass now REPLACES the one the user reads when it lands. The earlier one:\n "${t.length>600?`${t.slice(0,597)}...`:t}"\nWrite a summary that describes everything ${e} delivers, with this round's fixes folded in (not only the fixes), and call complete again with replacesSummary: true.`}exports.handleCompleteRequest=async function(e,t,S,E,_,x,C){const q=E.child({tool:"complete_request"}),T=_.startTimer("tool_execution_time")
3170
3192
  try{const{requestId:s,summary:i,worthKnowing:j,skipTestsReason:P,skipTests:R,criteriaComments:A,couldNotCheck:O,guidance:M,replacesSummary:D}=e
3171
3193
  if(!s)throw(0,o.createValidationError)("Request ID is required")
3172
3194
  if(!i)throw(0,o.createValidationError)("Summary is required")
3173
3195
  if(P&&R&&R.length>0)throw(0,o.createValidationError)("Provide either skipTestsReason (skip all) or skipTests (per-command), not both.")
3174
- if(!0!==D)try{const e=await E.getRequestClient().getRequest(s),t=String(e?.completionSummary??"").trim()
3196
+ if(!0!==D)try{const e=await S.getRequestClient().getRequest(s),t=String(e?.completionSummary??"").trim()
3175
3197
  if(t)return q.info("re-completion refused until the summary is confirmed to cover the whole request",{requestId:s}),{content:[{type:"text",text:r(s,t)}],isError:!0}}catch(e){q.debug("earlier-summary check could not run; completion continues",{requestId:s,error:(0,h.getErrorMessage)(e)})}q.info("Starting request completion",{requestId:s,skipAllRequested:!!P,perCommandSkipCount:R?.length||0})
3176
3198
  const F=C?.sessionId??t.currentSessionId,N=C??(F?{providerId:"opencode",sessionId:F,createdAt:(new Date).toISOString()}:void 0)
3177
3199
  try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/commit-worktree-leftovers`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:s}),signal:AbortSignal.timeout(2e4)})
3178
3200
  if(e.ok){const t=await e.json()
3179
- t?.data?.committed&&q.info("Committed uncommitted worktree changes before completing",{requestId:s,sha:t.data.sha?.slice(0,8),files:t.data.files?.length??0})}}catch(e){q.debug("commit-worktree-leftovers unavailable; completing as before",{requestId:s,error:(0,h.getErrorMessage)(e)})}t.mainBasePath&&await(0,c.foldAtDoor)({repoDir:t.mainBasePath,branch:`requests/${s}`,defaultBranch:await(0,b.resolveDefaultBranch)(t.mainBasePath,t.gitBinary).catch(()=>"main"),requestId:s,door:"complete",gitBinary:t.gitBinary,postRecord:e=>E.proxyPost(`/api/projects/${t.projectId}/tool-records`,e),log:q})
3201
+ t?.data?.committed&&q.info("Committed uncommitted worktree changes before completing",{requestId:s,sha:t.data.sha?.slice(0,8),files:t.data.files?.length??0})}}catch(e){q.debug("commit-worktree-leftovers unavailable; completing as before",{requestId:s,error:(0,h.getErrorMessage)(e)})}t.mainBasePath&&await(0,c.foldAtDoor)({repoDir:t.mainBasePath,branch:`requests/${s}`,defaultBranch:await(0,b.resolveDefaultBranch)(t.mainBasePath,t.gitBinary).catch(()=>"main"),requestId:s,door:"complete",gitBinary:t.gitBinary,postRecord:e=>S.proxyPost(`/api/projects/${t.projectId}/tool-records`,e),log:q})
3180
3202
  let z=null,L=null
3181
3203
  try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/branch-head?requestId=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(5e3)})
3182
3204
  if(e.ok){const t=await e.json()
@@ -3185,10 +3207,10 @@ let H=null
3185
3207
  try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/request-diff-stats?requestId=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(5e3)})
3186
3208
  if(e.ok){const r=await e.json()
3187
3209
  if(r?.data&&"number"==typeof r.data.additions&&"number"==typeof r.data.deletions&&"number"==typeof r.data.filesChanged){const e=Array.isArray(r.data.files)?r.data.files.filter(e=>"string"==typeof e):void 0,n=Array.isArray(r.data.deleted)?r.data.deleted.filter(e=>"string"==typeof e):void 0
3188
- if(H={additions:r.data.additions,deletions:r.data.deletions,filesChanged:r.data.filesChanged,...e?{files:e}:{},docsOnly:!0===r.data.docsOnly,...n?{deleted:n}:{}},n&&n.length>0)try{const e=await E.proxyGet(`/api/projects/${t.projectId}/test-processes`),r=Array.isArray(e)?e:Array.isArray(e?.data)?e.data:[],o=await E.getRequestClient().getRequest(s).catch(()=>null),i="string"==typeof o?.exchangeMessageCode&&o.exchangeMessageCode?await(E.getSessionMessageClient()?.getMessage(o.exchangeMessageCode).catch(()=>null)):null
3210
+ if(H={additions:r.data.additions,deletions:r.data.deletions,filesChanged:r.data.filesChanged,...e?{files:e}:{},docsOnly:!0===r.data.docsOnly,...n?{deleted:n}:{}},n&&n.length>0)try{const e=await S.proxyGet(`/api/projects/${t.projectId}/test-processes`),r=Array.isArray(e)?e:Array.isArray(e?.data)?e.data:[],o=await S.getRequestClient().getRequest(s).catch(()=>null),i="string"==typeof o?.exchangeMessageCode&&o.exchangeMessageCode?await(S.getSessionMessageClient()?.getMessage(o.exchangeMessageCode).catch(()=>null)):null
3189
3211
  H.checkLosses=await(0,l.signoffCheckLosses)(n,r,o?{...o,askedAt:i?.createdAt??null}:null)}catch(e){q.warn("checks could not be read; the sign-off card will not name deleted files a check covered",{requestId:s,error:(0,h.getErrorMessage)(e)})}}}}catch(e){q.debug("request-diff-stats fetch failed; review scaling falls back to the full panel",{error:(0,h.getErrorMessage)(e)})}const U=H?{diffStats:H}:{}
3190
3212
  let G,W=null
3191
- try{const e=await E.proxyGet(`/api/projects/${t.projectId}/requests/${s}/kind`)
3213
+ try{const e=await S.proxyGet(`/api/projects/${t.projectId}/requests/${s}/kind`)
3192
3214
  W=(e?.data??e)?.kind??null}catch(e){q.debug("request kind unavailable; kind checks skipped",{error:(0,h.getErrorMessage)(e)})}try{if("writing"===W){const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/sibling-overlap?requestId=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(1e4)}),t=(e.ok?(await e.json())?.data??[]:[]).filter(e=>!g.DOC_PATH_RE.test(e.path))
3193
3215
  if(t.length>0){const e=t.slice(0,5).map(e=>{const t=e.mine.map(e=>e[0]===e[1]?`line ${e[0]}`:`lines ${e[0]}-${e[1]}`).join(", ")
3194
3216
  return`- ${e.path} (${t}) is also changed by ${e.sibling}${e.siblingTitle?` ("${e.siblingTitle}"`:""}${e.siblingStatus?`, ${e.siblingStatus.replace(/_/g," ")})`:e.siblingTitle?")":""}`}),r=[...new Set(t.map(e=>e.sibling))]
@@ -3202,7 +3224,7 @@ return!(c&&s.has(c)&&i.has(c))})
3202
3224
  if(0===c.length)return null
3203
3225
  const l=c.slice(0,8).map(e=>{const t=r.indexOf(e),n=a(e,t),o=String(e?.description??"").trim()
3204
3226
  return` - ${n?`[${n}] `:""}${o.length>120?`${o.slice(0,117)}...`:o}`}).join("\n")
3205
- return`Completion NOT recorded: ${c.length} of this drive's ${r.length} claims ${1===c.length?"has":"have"} no verdict:\n${l}\n\nThe claims are the drive's result, and the card counts them from the stamps. Call record_criteria_verification with a verdict for each claim you checked (VERIFIED stamps met, DIVERGED stamps unmet, with the evidence), then complete again. A claim you could NOT check goes in couldNotCheck on this call, by its key, with the reason in that criterion's criteriaComments entry: it is not a verdict, and it reads as unchecked.`}(await E.getRequestClient().getRequest(s),{couldNotCheck:O,commentedKeys:(A??[]).map(e=>e.key)})
3227
+ return`Completion NOT recorded: ${c.length} of this drive's ${r.length} claims ${1===c.length?"has":"have"} no verdict:\n${l}\n\nThe claims are the drive's result, and the card counts them from the stamps. Call record_criteria_verification with a verdict for each claim you checked (VERIFIED stamps met, DIVERGED stamps unmet, with the evidence), then complete again. A claim you could NOT check goes in couldNotCheck on this call, by its key, with the reason in that criterion's criteriaComments entry: it is not a verdict, and it reads as unchecked.`}(await S.getRequestClient().getRequest(s),{couldNotCheck:O,commentedKeys:(A??[]).map(e=>e.key)})
3206
3228
  if(e)return{content:[{type:"text",text:e}],isError:!0}
3207
3229
  const r=await async function(e,t,r,o){const s=(Array.isArray(r)?r:[]).map(e=>String(e??"").trim().toLowerCase()).filter(Boolean)
3208
3230
  if(0===s.length)return[]
@@ -3211,9 +3233,9 @@ return a.forEach((e,t)=>{if(!e?.id)return
3211
3233
  const r=(0,n.criterionShortKey)(String(e.id),t).toLowerCase()
3212
3234
  if(!s.includes(r))return
3213
3235
  const i=(o??[]).find(e=>String(e?.key??"").trim().toLowerCase()===r)
3214
- c.push({criterionId:String(e.id),reason:i?.comment??null})}),c}(E.getRequestClient(),s,O,A)
3215
- r.length>0&&await E.proxyPost(`/api/projects/${t.projectId}/requests/${s}/drive-not-checked`,{items:r})}}catch(e){q.debug("drive-claim stamp check could not run; completion continues",{error:(0,h.getErrorMessage)(e)})}const V=[]
3216
- if(A&&A.length>0)try{const e=E.getRequestClient(),r=await e.getRequest(s),n=r?.acceptanceCriteria||[]
3236
+ c.push({criterionId:String(e.id),reason:i?.comment??null})}),c}(S.getRequestClient(),s,O,A)
3237
+ r.length>0&&await S.proxyPost(`/api/projects/${t.projectId}/requests/${s}/drive-not-checked`,{items:r})}}catch(e){q.debug("drive-claim stamp check could not run; completion continues",{error:(0,h.getErrorMessage)(e)})}const V=[]
3238
+ if(A&&A.length>0)try{const e=S.getRequestClient(),r=await e.getRequest(s),n=r?.acceptanceCriteria||[]
3217
3239
  if(q.info("Recording coder criteria comments",{requestId:s,comments:A.length,criteriaFetched:n.length}),0===n.length)return q.error("Criteria comments provided but the request came back with no criteria",{requestId:s}),{content:[{type:"text",text:`Completion NOT recorded: your criteria comments could not be attached because ${s} came back with no acceptance criteria. This is a read failure on our side, not a mistake in your call. Retry complete once; if it repeats, report it to your parent as blocked and say the criteria read is failing.`}],isError:!0}
3218
3240
  {const e=(0,p.buildCriteriaKeyMap)(n),r=[],o=[]
3219
3241
  for(const t of A){const n=e.get(t.key),s=Array.isArray(t.verify)?t.verify.filter(e=>"string"==typeof e&&e.trim()):[]
@@ -3234,33 +3256,33 @@ i.length>0&&q.info("verify preflight: some proofs did not finish or could not ru
3234
3256
  const d=n?.available&&Array.isArray(n.results)?n.results.filter(e=>!e.ok&&!e.timedOut&&!e.couldNotRun):[],u=d.filter(e=>!1!==(e.authoritative??n?.authoritative))
3235
3257
  if(d.length>0&&0===u.length&&q.warn("verify preflight failed but the gate environment is ambiguous; completing anyway",{requestId:s,failed:d.length,sha:n?.sha}),u.length>0){q.warn("verify preflight failed; completion refused",{requestId:s,failed:u.length,sha:n?.sha})
3236
3258
  const e=u.map(e=>` ${e.command}\n -> exit ${e.exit}${e.timedOut?" (timed out)":""}${e.output?`: ${e.output.split("\n")[0]}`:""}`).join("\n")
3237
- return{content:[{type:"text",text:`Completion NOT recorded: ${u.length} of the criteria's verify command(s) (yours, and any a guidance check put there) FAIL on the tree you are completing (${String(n?.sha??"").slice(0,8)}).\n\n`+e+((n?.dropped??0)>0?`\n\n(${n.dropped} further command(s) were over the 20-command ceiling and were not run.)`:"")+"\n\nThis is the box your checks are judged in, so this is exactly what the sign-off card would show. Two things cause it: the check is wrong (it pins something you changed after writing it, such as a renamed local, or it matches by accident, such as a word found inside a longer word), or the code genuinely does not do what the criterion promises.\n\nFix whichever is wrong -- adjust the command to grep the observable thing (an id, a name, an exported symbol), or fix the code -- then complete again. Nothing was recorded."}],isError:!0}}}catch(e){q.debug("verify preflight unavailable; completing without it",{requestId:s,error:(0,h.getErrorMessage)(e)})}if(r.length>0&&(await E.proxyPost(`/api/projects/${t.projectId}/requests/${s}/verify-criteria`,{verifications:r,dimension:"coder"}),V.push(...r.map(e=>e.criterionId))),l?.sha&&l.results?.length){const e=r.flatMap(e=>a(e).flatMap(t=>{const r=l.results.find(e=>e.command===t)
3259
+ return{content:[{type:"text",text:`Completion NOT recorded: ${u.length} of the criteria's verify command(s) (yours, and any a guidance check put there) FAIL on the tree you are completing (${String(n?.sha??"").slice(0,8)}).\n\n`+e+((n?.dropped??0)>0?`\n\n(${n.dropped} further command(s) were over the 20-command ceiling and were not run.)`:"")+"\n\nThis is the box your checks are judged in, so this is exactly what the sign-off card would show. Two things cause it: the check is wrong (it pins something you changed after writing it, such as a renamed local, or it matches by accident, such as a word found inside a longer word), or the code genuinely does not do what the criterion promises.\n\nFix whichever is wrong -- adjust the command to grep the observable thing (an id, a name, an exported symbol), or fix the code -- then complete again. Nothing was recorded."}],isError:!0}}}catch(e){q.debug("verify preflight unavailable; completing without it",{requestId:s,error:(0,h.getErrorMessage)(e)})}if(r.length>0&&(await S.proxyPost(`/api/projects/${t.projectId}/requests/${s}/verify-criteria`,{verifications:r,dimension:"coder"}),V.push(...r.map(e=>e.criterionId))),l?.sha&&l.results?.length){const e=r.flatMap(e=>a(e).flatMap(t=>{const r=l.results.find(e=>e.command===t)
3238
3260
  return r?[{criterionId:e.criterionId,command:t,ok:r.ok,...r.timedOut?{timedOut:!0}:{},...r.couldNotRun?{couldNotRun:!0}:{},output:String(r.output??"").split("\n")[0].slice(0,200)}]:[]}))
3239
- e.length>0&&await E.proxyPost(`/api/projects/${t.projectId}/requests/${s}/proof-evidence`,{commitSha:l.sha,tree:l.tree??null,proofs:e}).catch(e=>q.warn("the preflight's results were not recorded as evidence (the gate runs the proofs again)",{requestId:s,error:(0,h.getErrorMessage)(e)}))}}}catch(e){return q.error("Failed to persist coder criteria comments",{requestId:s,error:(0,h.getErrorMessage)(e)}),{content:[{type:"text",text:`Completion NOT recorded: your criteria comments could not be saved (${(0,h.getErrorMessage)(e)}). Retry complete -- without them, objective criteria stay unproven and completion is refused.`}],isError:!0}}if(t.git){try{const e=await(0,a.findCommittedMarkers)(t.git,`requests/${s}`)
3261
+ e.length>0&&await S.proxyPost(`/api/projects/${t.projectId}/requests/${s}/proof-evidence`,{commitSha:l.sha,tree:l.tree??null,proofs:e}).catch(e=>q.warn("the preflight's results were not recorded as evidence (the gate runs the proofs again)",{requestId:s,error:(0,h.getErrorMessage)(e)}))}}}catch(e){return q.error("Failed to persist coder criteria comments",{requestId:s,error:(0,h.getErrorMessage)(e)}),{content:[{type:"text",text:`Completion NOT recorded: your criteria comments could not be saved (${(0,h.getErrorMessage)(e)}). Retry complete -- without them, objective criteria stay unproven and completion is refused.`}],isError:!0}}if(t.git){try{const e=await(0,a.findCommittedMarkers)(t.git,`requests/${s}`)
3240
3262
  if(e.determined&&e.files.length>0)return q.warn("completion refused: committed conflict markers on the branch",{requestId:s,files:e.files}),T({success:!1,reason:"unresolved_markers"}),{content:[{type:"text",text:`Completion NOT recorded: ${s}'s branch still contains committed conflict markers in ${e.files.join(", ")}.\n\nThe system merged the default branch into yours and committed the conflicted state so you could see it; those <<<<<<< / ======= / >>>>>>> lines are plain file content now. Resolve every block with your file tools, re-run your checks, then complete again.\n\nStopping here on purpose: a reviewer would only tell you the same thing a minute later.`}],isError:!0}}catch(e){q.warn("conflict-marker precheck could not run; leaving it to the merge gate",{requestId:s,error:(0,h.getErrorMessage)(e)})}if(!$.has(s))try{const e=await(0,d.scanStaleProse)(t.git,`requests/${s}`),r=(0,d.renderStaleProse)(e)
3241
3263
  if(r)return $.add(s),q.info("completion paused once: prose the branch made stale",{requestId:s,gone:e.goneReferences.length,previewUrls:e.previewUrls.length}),T({success:!1,reason:"stale_prose_shown"}),{content:[{type:"text",text:`Completion paused for one look (${s}): your change may have made prose in the repository untrue.\n\n${r}\n\nFix what is wrong (READMEs and entry points are part of the work), then complete again. If a line is fine as it is, completing again goes straight through; this notice is shown once.`}],isError:!0}}catch(e){q.warn("stale-prose scan could not run; completing without it",{requestId:s,error:(0,h.getErrorMessage)(e)})}}let K,Y,J=null
3242
- try{const e=await E.getRequestClient().getRequest(s),r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[],o=r.map(e=>(0,n.latestRow)(e,["reviewer"])).filter(e=>!(!e||"met"!==e.verdict&&"unmet"!==e.verdict||"string"!=typeof e.commit||!e.commit)).sort((e,t)=>String(t.at??"").localeCompare(String(e.at??"")))[0],i=t.mainBasePath?await(0,b.resolveDefaultBranch)(t.mainBasePath):null,a=`refs/heads/requests/${s}`
3264
+ try{const e=await S.getRequestClient().getRequest(s),r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[],o=r.map(e=>(0,n.latestRow)(e,["reviewer"])).filter(e=>!(!e||"met"!==e.verdict&&"unmet"!==e.verdict||"string"!=typeof e.commit||!e.commit)).sort((e,t)=>String(t.at??"").localeCompare(String(e.at??"")))[0],i=t.mainBasePath?await(0,b.resolveDefaultBranch)(t.mainBasePath):null,a=`refs/heads/requests/${s}`
3243
3265
  if(o&&t.mainBasePath&&i&&(J=await(0,y.measureChangesSinceReview)(t.mainBasePath,{branchRef:a,reviewedSha:o.commit,defaultBranch:i,acceptedSha:"string"==typeof e?.diffSummary?.reviewAcceptedSha?e.diffSummary.reviewAcceptedSha:null}),J&&(K=J.authoredCommits)),z&&t.mainBasePath&&i){const e=new Set
3244
3266
  for(const t of r){const r=(0,n.latestRow)(t,[(0,n.isUserOwnCheck)(t)?"you":"reviewer"])
3245
3267
  "met"===r?.verdict&&"string"==typeof r.commit&&r.commit&&r.commit!==z&&e.add(r.commit)}Y=[]
3246
3268
  for(const r of[...e].slice(0,6)){const e=await(0,y.measureChangesSinceReview)(t.mainBasePath,{branchRef:a,reviewedSha:r,defaultBranch:i}).catch(()=>null)
3247
- Y.push({sha:r,since:e?e.newLines+e.droppedLines>0?"changed":"same":"unknown"})}}}catch(e){q.debug("changes-since-review measure unavailable; the server keeps the review as it is",{error:(0,h.getErrorMessage)(e)})}if(H&&H.filesChanged>0)try{const e=await E.getRequestClient().getRequest(s)
3248
- if("merge"===(0,v.kindProfile)(e?.type).finalize){const r=await(0,k.closeOneTask)(E.getTaskClient(),{projectId:t.projectId,requestId:s,title:String(e?.title||s),summary:i})
3249
- r&&((0,I.clearActiveTaskCache)(),q.info("work in one piece: the request's one task closed",{requestId:s,taskId:r}))}}catch(e){q.warn("could not record the one task; completion continues as before",{requestId:s,error:(0,h.getErrorMessage)(e)})}try{const e=await E.proxyPost(`/api/projects/${t.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:F,skipTestsReason:P,skipTests:R,headSha:z,...void 0!==K?{authoredSinceReview:K}:{},...J?{reviewDelta:J}:{},...Y?{intentMoves:Y}:{},...H?.files?{files:H.files}:{}}))
3269
+ Y.push({sha:r,since:e?e.newLines+e.droppedLines>0?"changed":"same":"unknown"})}}}catch(e){q.debug("changes-since-review measure unavailable; the server keeps the review as it is",{error:(0,h.getErrorMessage)(e)})}if(H&&H.filesChanged>0)try{const e=await S.getRequestClient().getRequest(s)
3270
+ if("merge"===(0,v.kindProfile)(e?.type).finalize){const r=await(0,k.closeOneTask)(S.getTaskClient(),{projectId:t.projectId,requestId:s,title:String(e?.title||s),summary:i})
3271
+ r&&((0,I.clearActiveTaskCache)(),q.info("work in one piece: the request's one task closed",{requestId:s,taskId:r}))}}catch(e){q.warn("could not record the one task; completion continues as before",{requestId:s,error:(0,h.getErrorMessage)(e)})}try{const e=await S.proxyPost(`/api/projects/${t.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:F,skipTestsReason:P,skipTests:R,headSha:z,...void 0!==K?{authoredSinceReview:K}:{},...J?{reviewDelta:J}:{},...Y?{intentMoves:Y}:{},...H?.files?{files:H.files}:{}}))
3250
3272
  G=e?.data||e}catch(e){const t=(0,h.getErrorMessage)(e)
3251
3273
  return q.error("Server validation failed",{requestId:s,error:t}),T({success:!1,error:"validation_request_failed"}),{content:[{type:"text",text:`Failed to validate completion: ${t}`}],isError:!0}}if(!G.canComplete){let e=`Cannot complete request ${s}: ${G.reason}\n`
3252
- if(G.blockedByTasks&&G.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",G.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`})),G.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",G.lifoViolation.blockingRequests.forEach((t,r)=>{e+=`${r+1}. **${t.requestId}** - ${t.title} (Status: ${t.status})\n`})),V.length>0)try{const t=await E.getRequestClient().getRequest(s)
3274
+ if(G.blockedByTasks&&G.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",G.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`})),G.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",G.lifoViolation.blockingRequests.forEach((t,r)=>{e+=`${r+1}. **${t.requestId}** - ${t.title} (Status: ${t.status})\n`})),V.length>0)try{const t=await S.getRequestClient().getRequest(s)
3253
3275
  e+=`\n${function(e,t){const r=(Array.isArray(e)?e:[]).map((e,t)=>({c:e,i:t})).filter(({c:e})=>e?.id&&t.includes(String(e.id))).map(({c:e,i:t})=>{const r=Array.isArray(e.verify)?e.verify.filter(e=>"string"==typeof e):[],o=(0,n.latestRow)(e,["verify","check"]),s=o?"met"===o.verdict?"passed":"unmet"===o.verdict?"failed":o.verdict:"not run yet"
3254
3276
  return`- [${(0,n.criterionShortKey)(String(e.id),t)}] ${r.length?`check now: ${r.map(e=>`\`${e.slice(0,120)}\``).join("; ")} (newest result: ${s})`:"no check of its own"}`})
3255
3277
  return 0===r.length?"":`Recorded all the same, as the request reads now: your account on these criteria, and their checks as they stand:\n${r.join("\n")}\nOnly the completion was refused; say so if you tell the user.`}(t?.acceptanceCriteria,V)}`}catch{}return T({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}const Q=G.plan
3256
3278
  if("review-submit"===Q?.action&&z&&H&&(H.reviewAcceptedSha=z),Q?.reviewChange&&J&&H&&(H.afterReview={...J,outcome:Q.reviewChange},"restarted"===Q.reviewChange&&(H.sinceReview=J)),!Q)return T({success:!1,error:"plan_missing"}),{content:[{type:"text",text:`Server validation returned no completion plan for ${s} -- the remote is likely mid-deploy. Retry complete.`}],isError:!0}
3257
3279
  const Z="testing"===Q.action||"human-review"===Q.action||"merge-cleared"===Q.action
3258
- if(Z&&G.testsSkipped&&(P||R&&R.length>0))try{const e=E.getToolRecordClient()
3259
- e&&await e.createToolRecord({projectId:t.projectId,requestId:s,type:"tests-skipped",metadata:{...P?{reason:P}:{},...R&&R.length>0?{skipTests:R}:{},testGate:G.gates.test,timestamp:(new Date).toISOString()}})}catch(e){q.warn("Failed to persist tests-skipped tool_record",{error:(0,h.getErrorMessage)(e)})}if(Z&&j&&j.length>0&&F){const e=E.getSessionMessageClient()
3280
+ if(Z&&G.testsSkipped&&(P||R&&R.length>0))try{const e=S.getToolRecordClient()
3281
+ e&&await e.createToolRecord({projectId:t.projectId,requestId:s,type:"tests-skipped",metadata:{...P?{reason:P}:{},...R&&R.length>0?{skipTests:R}:{},testGate:G.gates.test,timestamp:(new Date).toISOString()}})}catch(e){q.warn("Failed to persist tests-skipped tool_record",{error:(0,h.getErrorMessage)(e)})}if(Z&&j&&j.length>0&&F){const e=S.getSessionMessageClient()
3260
3282
  if(e){let r=new Set
3261
- try{const e=await E.proxyGet(`/api/projects/${t.projectId}/session-messages`,{requestId:s,messageType:"report",limit:200}),n=Array.isArray(e)?e:e?.data??[]
3283
+ try{const e=await S.proxyGet(`/api/projects/${t.projectId}/session-messages`,{requestId:s,messageType:"report",limit:200}),n=Array.isArray(e)?e:e?.data??[]
3262
3284
  r=new Set(n.filter(e=>(e?.content?.requestId??e?.content?.anchor?.id)===s).map(e=>String(e?.content?.summary??"")))}catch{}for(const t of j.slice(0,5)){const n=t.length>140?`${t.slice(0,137)}\u2026`:t
3263
- if(!r.has(n))try{await e.createReport({fromSessionId:F,status:"success",summary:n,...t.length>140?{details:t}:{},attention:"fyi",requestId:s})}catch(e){q.warn("worthKnowing note failed (completion continues)",{error:(0,h.getErrorMessage)(e)})}}q.info("worthKnowing notes processed",{count:Math.min(j.length,5),requestId:s})}}if(M&&M.length>0&&F)for(const e of M.slice(0,2))try{await E.proxyPost(`/api/projects/${t.projectId}/guidance/propose`,{body:e.body,scope:"files",pathAnchors:e.files,evidence:e.evidence,requestId:s,sessionExternalId:F}),q.info("guidance proposed at completion",{requestId:s})}catch(e){q.info("guidance at completion not proposed (completion continues)",{requestId:s,reason:(0,h.getErrorMessage)(e)})}const X=E.getRequestClient()
3285
+ if(!r.has(n))try{await e.createReport({fromSessionId:F,status:"success",summary:n,...t.length>140?{details:t}:{},attention:"fyi",requestId:s})}catch(e){q.warn("worthKnowing note failed (completion continues)",{error:(0,h.getErrorMessage)(e)})}}q.info("worthKnowing notes processed",{count:Math.min(j.length,5),requestId:s})}}if(M&&M.length>0&&F)for(const e of M.slice(0,2))try{await S.proxyPost(`/api/projects/${t.projectId}/guidance/propose`,{body:e.body,scope:"files",pathAnchors:e.files,evidence:e.evidence,requestId:s,sessionExternalId:F}),q.info("guidance proposed at completion",{requestId:s})}catch(e){q.info("guidance at completion not proposed (completion continues)",{requestId:s,reason:(0,h.getErrorMessage)(e)})}const X=S.getRequestClient()
3264
3286
  for(const t of Q.patches)try{const r=await X.updateRequest(s,{...t,completionSummary:i,cliOrigin:N,...B,...U})
3265
3287
  e.refsSeen&&r?.namedRefs&&(e.refsSeen.namedRefs=r.namedRefs)}catch(e){return T({success:!1,error:"plan_patch_failed"}),{content:[{type:"text",text:`Failed to apply completion transition (${Object.keys(t).join(", ")}): ${(0,h.getErrorMessage)(e)}`}],isError:!0}}if(T({success:!0}),"review-escalated"===Q.action)return{content:[{type:"text",text:`Request ${s}: agent review hit its pass cap and was escalated for human sign-off. No further action from you; a human finalizes or sends back must_fix items.`}]}
3266
3288
  if("review-start"===Q.action)return{content:[{type:"text",text:`Request ${s}: review started. Read-only reviewers are reading your diff. STOP here and wait: do NOT poll, sleep, or call tools to pass the time; each reviewer's report is delivered to you automatically and wakes you, so just end your turn between reports. When their reports arrive (a detailed handoff follows with the specifics), act on the findings, finish your tasks, and call complete again. After review passes, the server runs any remaining gates and messages you the exact next step (re-run tests if your fixes changed code, await human sign-off, or merge) -- wait for that and don't merge until it tells you to.`}]}
@@ -3276,11 +3298,11 @@ if("settle"===re.finalize)return{content:[{type:"text",text:`Request ${s} is com
3276
3298
  if(void 0!==te)return{content:[{type:"text",text:`Request ${s} is cleared to merge, but fetching its criteria failed: ${(0,h.getErrorMessage)(te)}. Call merge with verification evidence for each acceptance criterion.`}]}
3277
3299
  const ne=ee?.acceptanceCriteria||[]
3278
3300
  if(!1===G?.gates?.review?.enabled&&!1===G?.gates?.test?.enabled&&!1===G?.gates?.signoff?.enabled&&"merge"===re.finalize&&(0===ne.length||Array.isArray(A)&&A.length>0)){q.info("gates off and account recorded: merging in the same call",{requestId:s})
3279
- const e=await(0,u.handleMergeRequest)({requestId:s,verifications:[]},t,E,S,x,_)
3301
+ const e=await(0,u.handleMergeRequest)({requestId:s,verifications:[]},t,S,E,_,x)
3280
3302
  return e?.isError?e:{...e,content:[{type:"text",text:`Request ${s}: nothing gates this project (review, tests and sign-off are off) and your account per criterion is on the record, so it merged in this call.\n\n${e.content?.[0]?.text??""}`},...e.content?.slice(1)??[]]}}const oe=Q.skippedReason?`\n\n_Skipped re-running tests: ${Q.skippedReason}._\n`:"",se=Q.noTestsAutoMerge?"\n\n_No test processes are configured for this project, so the test gate has nothing to run; this completion merges without tests._\n":""
3281
3303
  return 0===ne.length?{content:[{type:"text",text:`Request ${s} is ready. ${oe}${se}No acceptance criteria defined: call merge directly.`}]}:{content:[{type:"text",text:`Request ${s}: all gates are clear.${oe}${se}\n\n## Next Step: Verify & Merge\n\n${(0,p.formatVerifyInstructions)(ne)}\n\nUse the merge operation with your verification evidence to merge to main.`}]}}catch(r){let n="An unexpected error occurred",o=""
3282
3304
  r instanceof s.ZodError?(n="Validation error",o=r.errors.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")):r instanceof Error?(n=r.message,o=r.stack||""):o=String(r),q.error("Complete request failed",{error:n,details:o})
3283
- try{await(0,i.createFailureToolRecord)("complete-request",r,{error:n,details:o,requestId:e?.requestId},E.getToolRecordClient(),q,x,{requestId:e?.requestId,projectId:t.projectId,note:"Failed to complete request"})}catch{}return x.startTimer("tool_execution_time")({success:!1,error:r instanceof Error?r.name:"unknown"}),{content:[{type:"text",text:`Error: ${n}`}],isError:!0}}}
3305
+ try{await(0,i.createFailureToolRecord)("complete-request",r,{error:n,details:o,requestId:e?.requestId},S.getToolRecordClient(),q,_,{requestId:e?.requestId,projectId:t.projectId,note:"Failed to complete request"})}catch{}return _.startTimer("tool_execution_time")({success:!1,error:r instanceof Error?r.name:"unknown"}),{content:[{type:"text",text:`Error: ${n}`}],isError:!0}}}
3284
3306
  const n=t(65454),o=t(59860),s=t(11569),i=t(18616),a=t(35510),c=t(82860),l=t(95122),d=t(7161),u=t(34306),h=t(58824),p=t(65454),f=t(79414),m=t(67071),g=t(92456),y=t(80331),w=t(63580),b=t(52232),v=t(87780),k=t(45391),I=t(43451),$=new Set},32283(e,exports,t){exports.def=void 0
3285
3307
  const r=t(76183),n=t(47334)
3286
3308
  exports.def={name:"create_request",description:"Create a request. Working on it switches you to its branch. delegate: true with draft has a background planner write the description and criteria from your title and this session's context; it stays a draft. When the request comes out of an existing record (a drive's finding, a report, a decision, another request), name that record by id in the description (\"Found by drive req000042\"), looking the id up if needed: ids are the only followable trail.",schema:r.CreateRequestSchema,annotations:{title:"Create Request",readOnlyHint:!1,destructiveHint:!1},handler:(e,t)=>(0,n.handleCreateRequest)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},47334(e,exports,t){exports.q=void 0,exports.handleCreateRequest=async function(e,f,m,g,y,w,b){const v=g.child({tool:"create_request"})
@@ -3300,19 +3322,19 @@ if(!g.areas||0===g.areas.length)throw new i.ValidationError('At least one work a
3300
3322
  {const t=(0,n.gateCriteriaIn)(g.acceptanceCriteria)
3301
3323
  if(t.length>0){const r="string"==typeof e?.userSaid?e.userSaid:""
3302
3324
  if(!r||!await(0,o.userSaidOnRequest)(m,I,"",r,{anywhere:!0}))return v.info("create refused: a gate criterion without the user asking for one",{count:t.length}),{content:[{type:"text",text:(0,n.gateRefusal)(t,"created",!!r)}],isError:!0}}}const $=m.getRequestClient()
3303
- let E=b,S=null
3325
+ let S=b,E=null
3304
3326
  if(b?.sessionId)try{const e=m.getCLISessionClient()
3305
3327
  if(e){const t=await e.getByExternalId(b.sessionId)
3306
- S=t?.agentType??null,"planner"!==t?.agentType&&"auto-planner"!==t?.agentType||(E=void 0,v.info("Skipping cliOrigin for planner-created request",{agentType:t.agentType}))}}catch{}S||(S=await p.sessionIdentity.getAgentTypeFor(b?.sessionId||f.currentSessionId))
3307
- const x=!g.draft&&("planner"===S||"auto-planner"===S)
3308
- x&&(g.draft=!0,v.info("Planner-created request filed as a draft",{agentType:S}))
3309
- const _=g.draft?"draft":"open",C=g.scheduleDocumentId||g.questDocumentId
3328
+ E=t?.agentType??null,"planner"!==t?.agentType&&"auto-planner"!==t?.agentType||(S=void 0,v.info("Skipping cliOrigin for planner-created request",{agentType:t.agentType}))}}catch{}E||(E=await p.sessionIdentity.getAgentTypeFor(b?.sessionId||f.currentSessionId))
3329
+ const _=!g.draft&&("planner"===E||"auto-planner"===E)
3330
+ _&&(g.draft=!0,v.info("Planner-created request filed as a draft",{agentType:E}))
3331
+ const x=g.draft?"draft":"open",C=g.scheduleDocumentId||g.questDocumentId
3310
3332
  let q=!1
3311
3333
  g.planDocumentId&&C&&g.planDocumentId===C&&(g.planDocumentId=void 0,q=!0)
3312
3334
  let T=g.planDocumentId
3313
3335
  if(!T&&b?.sessionId)try{const e=await(0,u.getActiveSessionPlanId)(m,I,b.sessionId,v)
3314
3336
  e&&await m.getDocumentClient().getDocument(e)&&(T=e,v.info("Auto-filled planDocumentId from session-plan association",{sessionId:b.sessionId,planDocumentId:e}))}catch(e){v.warn("planDocumentId auto-fill lookup failed (request created without plan link)",{error:(0,d.getErrorMessage)(e)})}T&&C&&T===C&&(T=void 0,q=!0)
3315
- const j=await(0,h.exchangeKeyOfCall)(f.mainBasePath,b,"string"==typeof e?.userSaid?e.userSaid:void 0),P=j?await(0,h.exchangeKeysWithCall)(f.mainBasePath,b):[],R={title:g.title,description:g.description||"",type:g.type,acceptanceCriteria:g.acceptanceCriteria||[],status:_,...T&&{planDocumentId:T},...C&&{scheduleDocumentId:C},...g.areas&&g.areas.length>0&&{areas:g.areas},...g.waitsOn&&g.waitsOn.length>0&&{waitsOn:g.waitsOn},...!0===g.startsNow&&{startsNow:!0},..."string"==typeof g.replaces&&g.replaces&&{replaces:g.replaces},...Array.isArray(g.asked)&&g.asked.length>0&&{asked:g.asked},...Array.isArray(g.userChoices)&&g.userChoices.length>0&&{userChoices:g.userChoices},..."string"==typeof g.process&&g.process.trim()&&{process:g.process.trim()},...void 0!==j&&{exchangeKey:j},...P.length>0&&{exchangeKeysWith:P},...E&&{cliOrigin:E},...b?.sessionId&&{creatorSessionId:b.sessionId},...g.follows&&"new"!==g.follows&&{originRequestId:String(g.follows).replace(/^round:/,"")},..."string"==typeof g.follows&&g.follows.startsWith("round:")&&{roundOf:g.follows.slice(6)}}
3337
+ const j=await(0,h.exchangeKeyOfCall)(f.mainBasePath,b,"string"==typeof e?.userSaid?e.userSaid:void 0),P=j?await(0,h.exchangeKeysWithCall)(f.mainBasePath,b):[],R={title:g.title,description:g.description||"",type:g.type,acceptanceCriteria:g.acceptanceCriteria||[],status:x,...T&&{planDocumentId:T},...C&&{scheduleDocumentId:C},...g.areas&&g.areas.length>0&&{areas:g.areas},...g.waitsOn&&g.waitsOn.length>0&&{waitsOn:g.waitsOn},...!0===g.startsNow&&{startsNow:!0},..."string"==typeof g.replaces&&g.replaces&&{replaces:g.replaces},...Array.isArray(g.asked)&&g.asked.length>0&&{asked:g.asked},...Array.isArray(g.userChoices)&&g.userChoices.length>0&&{userChoices:g.userChoices},..."string"==typeof g.process&&g.process.trim()&&{process:g.process.trim()},...void 0!==j&&{exchangeKey:j},...P.length>0&&{exchangeKeysWith:P},...S&&{cliOrigin:S},...b?.sessionId&&{creatorSessionId:b.sessionId},...g.follows&&"new"!==g.follows&&{originRequestId:String(g.follows).replace(/^round:/,"")},..."string"==typeof g.follows&&g.follows.startsWith("round:")&&{roundOf:g.follows.slice(6)}}
3316
3338
  v.info("Creating request via request client",{projectId:I,title:g.title,type:g.type,criteriaCount:g.acceptanceCriteria?.length??0})
3317
3339
  const A=await $.createRequest(R)
3318
3340
  v.debug("Create request response structure",{type:typeof A,isArray:Array.isArray(A),hasData:null!=A,properties:A?Object.keys(A):[],dataPreview:A?JSON.stringify(A).substring(0,200)+"...":"null"})
@@ -3325,7 +3347,7 @@ v.info("Request created successfully",{requestId:M.id,title:M.title,branchCreate
3325
3347
  try{const e=m.getToolRecordClient()
3326
3348
  if(e){const t=await e.createToolRecord({requestId:M.id,projectId:I,type:"create-request",metadata:{status:"completed",timestamp:(new Date).toISOString(),requestType:M.type,requestTitle:M.title,requestDescription:M.description,criteriaCount:M.acceptanceCriteria.length,branchCreated:N.created,branchName:N.created?N.branchName:null},note:`Filed "${M.title}" (${M.type})${N.created?`, on its own branch ${N.branchName}`:"; its branch is made at its first file change"}`})
3327
3349
  F=t?.id}}catch(e){v.warn("Failed to create tool record for request creation",{error:(0,d.getErrorMessage)(e),requestId:M.id})}if(k({success:!0,type:g.type,hasProjectId:!0}),v.info("Request created successfully",{requestId:M.id,title:M.title,type:M.type,projectId:I}),g.draft&&g.delegate)try{const{getActionServerBaseUrl:e}=t(79414)
3328
- await fetch(`${e()}/api/flesh-out-request`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:M.id,sourceSessionId:b?.sessionId}),signal:AbortSignal.timeout(8e3)}),v.info("Delegated draft flesh-out to a request-shaping planner",{requestId:M.id})}catch(e){v.warn("Failed to delegate draft flesh-out (draft still created)",{requestId:M.id,error:(0,d.getErrorMessage)(e)})}const z=M.localId||M.id,L=D?`Created Request ${z}, held at the intake gate: this workflow reviews agent-created work before it starts. It is a draft for now; work begins if the intake resolver (the user or an independent reviewer) approves it. No action needed from you.`:x?`Created draft Request ${z}. A planner's requests are drafts: nothing starts until the user picks Start Building on your hand-off (session_message_operations offer-handoff with every request id you filed), which starts each one in the order its waitsOn gives.`:g.draft?g.delegate?`Created draft Request ${z} and handed it to a background planner to flesh out. It starts when the user presses Start.`:`Created draft Request ${z} (private; no branch, no work started). It starts when the user presses Start.`:`Created Request ${z}`,B=T&&!g.planDocumentId?" (linked to your session's plan)":q?" (note: the schedule document is the request's one guiding link; a duplicate plan link to the same doc was dropped)":"",H=M.asked,U=(H?.requested??[]).filter(e=>!(H?.linked??[]).includes(e)),G=H?`\n\n${H.linked?.length?`Put on ${z}: ${H.linked.join(", ")}. Its worker is told they are asked, and the user's answer reaches it.`:""}${U.length?` Not put on it (not your open question to the user, or on another request): ${U.join(", ")}.`:""}`:"",W=M.replaces,V=W?.requestId?`\n\nIt replaces ${W.requestId} (canceled). ${W.nowWaiting&&W.nowWaiting.length?`${W.nowWaiting.join(", ")} waited on ${W.requestId} and now ${1===W.nowWaiting.length?"waits":"wait"} for ${z} instead.`:`Nothing was waiting on ${W.requestId}.`}`:"",K=M.planStep,Y=K?.plan?`\n\nIt is filed as a DRAFT, like the other steps of ${K.plan}, which nobody has started yet: it starts with them when the user presses Start Building${K.startBuildingOffered?` (the card is on their timeline now, ${K.startBuildingOffered})`:""}. Do not start it yourself.`:""
3350
+ await fetch(`${e()}/api/flesh-out-request`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:M.id,sourceSessionId:b?.sessionId}),signal:AbortSignal.timeout(8e3)}),v.info("Delegated draft flesh-out to a request-shaping planner",{requestId:M.id})}catch(e){v.warn("Failed to delegate draft flesh-out (draft still created)",{requestId:M.id,error:(0,d.getErrorMessage)(e)})}const z=M.localId||M.id,L=D?`Created Request ${z}, held at the intake gate: this workflow reviews agent-created work before it starts. It is a draft for now; work begins if the intake resolver (the user or an independent reviewer) approves it. No action needed from you.`:_?`Created draft Request ${z}. A planner's requests are drafts: nothing starts until the user picks Start Building on your hand-off (session_message_operations offer-handoff with every request id you filed), which starts each one in the order its waitsOn gives.`:g.draft?g.delegate?`Created draft Request ${z} and handed it to a background planner to flesh out. It starts when the user presses Start.`:`Created draft Request ${z} (private; no branch, no work started). It starts when the user presses Start.`:`Created Request ${z}`,B=T&&!g.planDocumentId?" (linked to your session's plan)":q?" (note: the schedule document is the request's one guiding link; a duplicate plan link to the same doc was dropped)":"",H=M.asked,U=(H?.requested??[]).filter(e=>!(H?.linked??[]).includes(e)),G=H?`\n\n${H.linked?.length?`Put on ${z}: ${H.linked.join(", ")}. Its worker is told they are asked, and the user's answer reaches it.`:""}${U.length?` Not put on it (not your open question to the user, or on another request): ${U.join(", ")}.`:""}`:"",W=M.replaces,V=W?.requestId?`\n\nIt replaces ${W.requestId} (canceled). ${W.nowWaiting&&W.nowWaiting.length?`${W.nowWaiting.join(", ")} waited on ${W.requestId} and now ${1===W.nowWaiting.length?"waits":"wait"} for ${z} instead.`:`Nothing was waiting on ${W.requestId}.`}`:"",K=M.planStep,Y=K?.plan?`\n\nIt is filed as a DRAFT, like the other steps of ${K.plan}, which nobody has started yet: it starts with them when the user presses Start Building${K.startBuildingOffered?` (the card is on their timeline now, ${K.startBuildingOffered})`:""}. Do not start it yourself.`:""
3329
3351
  return{content:[{type:"text",text:L+B+(M.process?.name?`\n\nIt follows the process "${M.process.name}": its criteria are on the request, listed first, and every request that follows the process carries them.`:M.processHint?`\n\n${M.processHint}`:"")+V+G+Y}],...(0,a.alchemyMeta)({kind:"create-request",toolRecordId:F})}}catch(t){if(v.error("Failed to create request",{error:t instanceof Error?t.stack:String(t),args:e}),f.projectId)try{if(t instanceof Error&&(t.message.includes("Project is disabled")||t.message.includes("Project cannot accept new requests")||t.message.includes("disabled due to tier limits")))v.info("Skipping tool record creation for disabled project error",{projectId:f.projectId,error:t.message})
3330
3352
  else{const r=m.getToolRecordClient()
3331
3353
  r&&await r.createToolRecord({projectId:f.projectId,type:"create-request",metadata:{status:"failed",timestamp:(new Date).toISOString(),requestTitle:e.title,requestType:e.type,requestDescription:e.description?.substring(0,100)+(e.description?.length>100?"...":""),criteriaCount:e.acceptanceCriteria?e.acceptanceCriteria.length:0,branchCreated:!1,branchName:null,error:(0,d.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:String(t)},note:`Failed to create request: ${e.title}`})}}catch(e){v.error("Failed to create failure record for request creation",{error:e instanceof Error?e.message:String(e)})}if(k({success:!1,error:t instanceof Error?t.name:"unknown"}),t instanceof l.ZodError&&t.errors.length>0){const e=t.errors[0]
@@ -3352,11 +3374,11 @@ y.info("Raw request response data",{responseData:JSON.stringify(b)}),y.debug("Re
3352
3374
  const k=(0,i.extractDataFromResponse)(b,{logger:y,operationName:"getRequestInformation",throwOnNoData:!0}),I="data"in k?k.data:k;(0,u.noteRequestRead)(t?.currentSessionId,s,I.updatedAt),(0,u.noteCriteriaRead)(t?.currentSessionId,s,I.criteriaVersion),y.info("Successfully extracted request data using data extraction utility"),y.info("Raw tasks response data",{responseData:JSON.stringify(v)}),y.debug("Tasks response structure",{type:typeof v,isArray:Array.isArray(v),hasData:null!=v,properties:v?Object.keys(v):[],dataPreview:v?JSON.stringify(v).substring(0,200)+"...":"null"})
3353
3375
  const $=(0,i.extractArrayFromResponse)(v,{logger:y,operationName:"listTasksForRequest",defaultValue:[]})
3354
3376
  y.info(`Successfully extracted ${$.length} tasks using data extraction utility`)
3355
- const E={pending:0,in_progress:0,blocked:0,ready_for_review:0,completed:0,reverted:0,total:$.filter(e=>"canceled"!==e?.status&&"cancelled"!==e?.status).length}
3377
+ const S={pending:0,in_progress:0,blocked:0,ready_for_review:0,completed:0,reverted:0,total:$.filter(e=>"canceled"!==e?.status&&"cancelled"!==e?.status).length}
3356
3378
  $.forEach(e=>{const t=e.status
3357
- t in E&&E[t]++})
3358
- const S=I.acceptanceCriteria||[],x=S.filter(e=>(0,c.criterionProven)(e)).length
3359
- let _=I.testVerification||null
3379
+ t in S&&S[t]++})
3380
+ const E=I.acceptanceCriteria||[],_=E.filter(e=>(0,c.criterionProven)(e)).length
3381
+ let x=I.testVerification||null
3360
3382
  const C=I.title,q=I.type,T=I.status,j=new Date(I.createdAt).toLocaleString(),P=new Date(I.updatedAt).toLocaleString(),R=(0,r.kindProfile)(q).label,A=["canceled","reverted","completed"].includes(String(T))&&I.completionSummary?`${"completed"===T?"Outcome":"Why it ended"}: ${String(I.completionSummary).slice(0,600)}\n`:""
3361
3383
  let O=""
3362
3384
  if(["canceled","cancelled","reverted"].includes(String(T))&&"request"===(0,r.kindProfile)(q).branch)try{const e=t?.mainBasePath||t?.basePath
@@ -3369,10 +3391,10 @@ const t=e,r="user"===t.by?"the user":"workflow"===t.by?"the workflow":"agent"===
3369
3391
  if(!r)return""
3370
3392
  const n=t.at&&!Number.isNaN(Date.parse(t.at))?`${new Date(t.at).toLocaleString()} `:""
3371
3393
  return`${t.discarded?"Discarded":"Canceled"}: ${n}by ${r}${t.via?`, with ${t.via}`:""}`}(I.canceledBy):""
3372
- let F=`# ${R} Request: ${C} (${s})\nStatus: ${T}\n${A}${O}Created: ${j}${M?`\n${M}`:""}${D?`\n${D}`:""}\nUpdated: ${P}\n\n## Description\n${I.description||"_No description provided._"}\n\n## Progress Summary\n- Tasks: ${E.completed}/${E.total} completed (${Math.round(E.completed/E.total*100)||0}%)\n- Acceptance Criteria: ${x}/${S.length} proven (${Math.round(x/S.length*100)||0}%)\n`
3373
- F+=`\n## Task Status\n- Pending: ${E.pending}\n- In Progress: ${E.in_progress}\n- Blocked: ${E.blocked}\n- Ready for Review: ${E.ready_for_review}\n- Completed: ${E.completed}\n- Reverted: ${E.reverted}\n`,F+="\n## Tasks\n",0===$.length?F+="No tasks found for this request.\n":$.forEach(e=>{F+=`**[${e.id}]** ${e.title} (Status: ${e.status})\n`}),y.info(`Added task listing with ${$.length} tasks`),F+="\n## Acceptance Criteria\n",F+=(0,c.formatCriteriaWithHashes)(S)+"\n","ready_for_review"===I.status&&S.some(e=>!(0,c.criterionProven)(e))&&(F+="\n> **Status: ready_for_review**: Use the merge operation to verify criteria and merge to main.\n")
3394
+ let F=`# ${R} Request: ${C} (${s})\nStatus: ${T}\n${A}${O}Created: ${j}${M?`\n${M}`:""}${D?`\n${D}`:""}\nUpdated: ${P}\n\n## Description\n${I.description||"_No description provided._"}\n\n## Progress Summary\n- Tasks: ${S.completed}/${S.total} completed (${Math.round(S.completed/S.total*100)||0}%)\n- Acceptance Criteria: ${_}/${E.length} proven (${Math.round(_/E.length*100)||0}%)\n`
3395
+ F+=`\n## Task Status\n- Pending: ${S.pending}\n- In Progress: ${S.in_progress}\n- Blocked: ${S.blocked}\n- Ready for Review: ${S.ready_for_review}\n- Completed: ${S.completed}\n- Reverted: ${S.reverted}\n`,F+="\n## Tasks\n",0===$.length?F+="No tasks found for this request.\n":$.forEach(e=>{F+=`**[${e.id}]** ${e.title} (Status: ${e.status})\n`}),y.info(`Added task listing with ${$.length} tasks`),F+="\n## Acceptance Criteria\n",F+=(0,c.formatCriteriaWithHashes)(E)+"\n","ready_for_review"===I.status&&E.some(e=>!(0,c.criterionProven)(e))&&(F+="\n> **Status: ready_for_review**: Use the merge operation to verify criteria and merge to main.\n")
3374
3396
  try{const e=await f.proxyGet(`/api/projects/${t.projectId}/requests/${s}/record-for-agents`),r=String((e?.data??e)?.text??"").trim()
3375
- r&&(F+=`\n${r}\n`)}catch(e){y.debug("record for agents unavailable",{error:(0,a.getErrorMessage)(e)})}if(_&&(F+=`\n## Test Coverage\n- Test Cases: ${_.totalTestCases||0}\n- Criteria Tested: ${_.criteriaTestedCount||0}/${S.length}\n`,_.criterionCoverage&&Array.isArray(_.criterionCoverage)&&(F+="\n### Detailed Test Coverage:\n",_.criterionCoverage.forEach(e=>{F+=`- Criterion ${e.criterionId}: ${e.coverage} coverage\n`}))),!["completed","reverted","canceled","cancelled","draft"].includes(String(T)))try{const e=await f.proxyGet(`/api/projects/${t.projectId}/requests/${s}/sessions`),r=Array.isArray(e)?e:e?.data??[]
3397
+ r&&(F+=`\n${r}\n`)}catch(e){y.debug("record for agents unavailable",{error:(0,a.getErrorMessage)(e)})}if(x&&(F+=`\n## Test Coverage\n- Test Cases: ${x.totalTestCases||0}\n- Criteria Tested: ${x.criteriaTestedCount||0}/${E.length}\n`,x.criterionCoverage&&Array.isArray(x.criterionCoverage)&&(F+="\n### Detailed Test Coverage:\n",x.criterionCoverage.forEach(e=>{F+=`- Criterion ${e.criterionId}: ${e.coverage} coverage\n`}))),!["completed","reverted","canceled","cancelled","draft"].includes(String(T)))try{const e=await f.proxyGet(`/api/projects/${t.projectId}/requests/${s}/sessions`),r=Array.isArray(e)?e:e?.data??[]
3376
3398
  if(r.length>0){const e=await(0,d.getServerUrl)(t,y).catch(()=>null),n=await(0,l.readLiveWorker)(e??null,r)
3377
3399
  F+=`\n## Live\n${n?(0,l.formatLiveWorker)(n):`- ${r.length} session(s) worked this request; the engine did not answer about the latest one.`}\n`
3378
3400
  let o=null
@@ -3460,73 +3482,73 @@ const r=t(51725),n=t(87780),o=/^\s*(?:Error:|\u26d4|\u274c|Not filed|Not started
3460
3482
  if(null===t)return`${r}. Before you tell the user anything about it, read which card holds it (request_operations get-info); do not assume it is a sign-off.`
3461
3483
  if(0===t.length)return`${r}, but no card is open for the user on it yet. Do not tell the user a sign-off is waiting: there is none. Stop here; you will be told when it moves.`
3462
3484
  const n=t.map(e=>`${e.code} (${v[e.kind]??e.kind})`).join(", ")
3463
- return t.some(e=>"signoff-park"===e.kind)?`${r}: the user's sign-off card is open: ${n}. The user finalizes it from there.`:`${r}: the user's open card is ${n}. That card, not a sign-off, decides what happens next; if you report, say exactly that.`}exports.handleMergeRequest=async function(e,v,k,I,$){const E=I.child({tool:"merge_request"}),S=$.startTimer("tool_execution_time"),{requestId:x,verifications:_}=e,C=v.basePath
3485
+ return t.some(e=>"signoff-park"===e.kind)?`${r}: the user's sign-off card is open: ${n}. The user finalizes it from there.`:`${r}: the user's open card is ${n}. That card, not a sign-off, decides what happens next; if you report, say exactly that.`}exports.handleMergeRequest=async function(e,v,k,I,$){const S=I.child({tool:"merge_request"}),E=$.startTimer("tool_execution_time"),{requestId:_,verifications:x}=e,C=v.basePath
3464
3486
  let q=!1,T=!1
3465
- try{if(!x)throw(0,i.createValidationError)("requestId is required")
3466
- E.info("Starting merge request",{requestId:x,verificationCount:_.length})
3467
- const e=k.getRequestClient(),l=await e.getRequest(x),h=(0,w.kindProfile)(l?.type)
3468
- if("none"===h.branch)return S({success:!0,reason:"no_branch_kind"}),{content:[{type:"text",text:`Nothing to merge for ${x}: a ${h.label.toLowerCase()} request has no branch. It closes on its own record when you complete it with request_operations complete.`}]}
3469
- if("running"===l?.testStatus||"choice-pending"===l?.testStatus){S({success:!1,reason:"running"===l.testStatus?"testing_in_progress":"testing_choice_pending"})
3487
+ try{if(!_)throw(0,i.createValidationError)("requestId is required")
3488
+ S.info("Starting merge request",{requestId:_,verificationCount:x.length})
3489
+ const e=k.getRequestClient(),l=await e.getRequest(_),h=(0,w.kindProfile)(l?.type)
3490
+ if("none"===h.branch)return E({success:!0,reason:"no_branch_kind"}),{content:[{type:"text",text:`Nothing to merge for ${_}: a ${h.label.toLowerCase()} request has no branch. It closes on its own record when you complete it with request_operations complete.`}]}
3491
+ if("running"===l?.testStatus||"choice-pending"===l?.testStatus){E({success:!1,reason:"running"===l.testStatus?"testing_in_progress":"testing_choice_pending"})
3470
3492
  const e="running"===l.testStatus?"tests are still running. Wait for the test report":"the user has not made the testing choice yet. Wait for their decision"
3471
- return{content:[{type:"text",text:`STOP: Cannot merge request ${x}: ${e}; you'll be cleared to merge when the Test gate resolves.`}],isError:!0}}if("ready_for_review"===l?.status){S({success:!1,reason:"awaiting_human"})
3493
+ return{content:[{type:"text",text:`STOP: Cannot merge request ${_}: ${e}; you'll be cleared to merge when the Test gate resolves.`}],isError:!0}}if("ready_for_review"===l?.status){E({success:!1,reason:"awaiting_human"})
3472
3494
  let e=null
3473
- try{const t=await k.proxyGet(`/api/projects/${v.projectId}/requests/${x}/wait-state`),r=t?.data??t??{}
3474
- Array.isArray(r.userParks)&&(e=r.userParks.filter(e=>"string"==typeof e?.code))}catch{}return{content:[{type:"text",text:r(x,e)}],isError:!0}}if("escalated"===l?.reviewStatus)return S({success:!1,reason:"review_escalated"}),{content:[{type:"text",text:`STOP: Cannot merge request ${x}: agent review hit its pass cap and was escalated to a human. A human either finalizes it ("Merge anyway") or sends back must_fix items; this tool cannot override that. If you were resolving merge conflicts: finish resolving, commit, and report: do not merge.`}],isError:!0}
3475
- if(_.length>0)try{const e=(0,g.buildCriteriaKeyMap)(l?.acceptanceCriteria??[]),t=[],r=[]
3476
- for(const n of _){const o=e.get(n.key)
3477
- o?t.push({criterionId:o.id,evidence:n.evidence}):r.push(n.key)}r.length>0&&E.warn("merge_request: verification keys did not match any criterion (dropped)",{requestId:x,unmatched:r}),t.length>0&&(await k.proxyPost(`/api/projects/${v.projectId}/requests/${x}/verify-criteria`,{verifications:t,dimension:"coder"}),E.info("Criteria verified",{requestId:x,count:t.length}))}catch(e){const t=(0,d.getErrorMessage)(e)
3478
- E.warn("Criteria verification failed, continuing with merge",{requestId:x,error:t})}let I,$,j
3479
- try{const e=await k.proxyPost(`/api/projects/${v.projectId}/requests/${x}/validate-completion`,(0,b.validateCompletionBody)("merge"))
3495
+ try{const t=await k.proxyGet(`/api/projects/${v.projectId}/requests/${_}/wait-state`),r=t?.data??t??{}
3496
+ Array.isArray(r.userParks)&&(e=r.userParks.filter(e=>"string"==typeof e?.code))}catch{}return{content:[{type:"text",text:r(_,e)}],isError:!0}}if("escalated"===l?.reviewStatus)return E({success:!1,reason:"review_escalated"}),{content:[{type:"text",text:`STOP: Cannot merge request ${_}: agent review hit its pass cap and was escalated to a human. A human either finalizes it ("Merge anyway") or sends back must_fix items; this tool cannot override that. If you were resolving merge conflicts: finish resolving, commit, and report: do not merge.`}],isError:!0}
3497
+ if(x.length>0)try{const e=(0,g.buildCriteriaKeyMap)(l?.acceptanceCriteria??[]),t=[],r=[]
3498
+ for(const n of x){const o=e.get(n.key)
3499
+ o?t.push({criterionId:o.id,evidence:n.evidence}):r.push(n.key)}r.length>0&&S.warn("merge_request: verification keys did not match any criterion (dropped)",{requestId:_,unmatched:r}),t.length>0&&(await k.proxyPost(`/api/projects/${v.projectId}/requests/${_}/verify-criteria`,{verifications:t,dimension:"coder"}),S.info("Criteria verified",{requestId:_,count:t.length}))}catch(e){const t=(0,d.getErrorMessage)(e)
3500
+ S.warn("Criteria verification failed, continuing with merge",{requestId:_,error:t})}let I,$,j
3501
+ try{const e=await k.proxyPost(`/api/projects/${v.projectId}/requests/${_}/validate-completion`,(0,b.validateCompletionBody)("merge"))
3480
3502
  I=e?.data||e}catch(e){const t=(0,d.getErrorMessage)(e)
3481
- return S({success:!1,error:"validation_failed"}),{content:[{type:"text",text:`Failed to validate merge: ${t}`}],isError:!0}}if(!I.canComplete){let e=`Cannot merge request ${x}: ${I.reason}\n`
3482
- return I.blockedByTasks?.length&&(e+="\n**Tasks that must be completed first:**\n",I.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (${t.status})\n`})),S({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if(!v.workspaceManager)throw new Error("WorkspaceManager not available")
3503
+ return E({success:!1,error:"validation_failed"}),{content:[{type:"text",text:`Failed to validate merge: ${t}`}],isError:!0}}if(!I.canComplete){let e=`Cannot merge request ${_}: ${I.reason}\n`
3504
+ return I.blockedByTasks?.length&&(e+="\n**Tasks that must be completed first:**\n",I.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (${t.status})\n`})),E({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if(!v.workspaceManager)throw new Error("WorkspaceManager not available")
3483
3505
  try{const e=v.workspaceManager.getCurrentContext(v.currentSessionId)?.basePath||v.basePath,t=v.git??(0,o.simpleGit)({baseDir:e,binary:v.gitBinary}),r=await async function(e){for(const t of["main","master"])try{return await e.raw(["rev-parse","--verify",t]),t}catch{}return"main"}(t)
3484
- $=(await t.raw(["merge-base",r,`requests/${x}`])).trim()||void 0
3485
- const n=await(0,u.countCommitsAhead)(t,r,`requests/${x}`),s=(0,u.emptyBranchBlockMessage)(n,x,r)
3486
- if(s)return S({success:!1,reason:"empty_branch"}),{content:[{type:"text",text:s}],isError:!0}
3487
- if(await(0,u.branchAddsNoContent)(t,r,`requests/${x}`))return S({success:!1,reason:"identical_tree"}),{content:[{type:"text",text:(0,u.identicalTreeBlockMessage)(x,r)}],isError:!0}}catch(e){E.warn("Could not compute merge-base / ahead count; proceeding (the merge path surfaces a real branch error)",{requestId:x,error:(0,d.getErrorMessage)(e)})}let P="workspace-manager"
3488
- const R=await e.getRequest(x),A=R?.title||`Request ${x}`
3506
+ $=(await t.raw(["merge-base",r,`requests/${_}`])).trim()||void 0
3507
+ const n=await(0,u.countCommitsAhead)(t,r,`requests/${_}`),s=(0,u.emptyBranchBlockMessage)(n,_,r)
3508
+ if(s)return E({success:!1,reason:"empty_branch"}),{content:[{type:"text",text:s}],isError:!0}
3509
+ if(await(0,u.branchAddsNoContent)(t,r,`requests/${_}`))return E({success:!1,reason:"identical_tree"}),{content:[{type:"text",text:(0,u.identicalTreeBlockMessage)(_,r)}],isError:!0}}catch(e){S.warn("Could not compute merge-base / ahead count; proceeding (the merge path surfaces a real branch error)",{requestId:_,error:(0,d.getErrorMessage)(e)})}let P="workspace-manager"
3510
+ const R=await e.getRequest(_),A=R?.title||`Request ${_}`
3489
3511
  let O="main"
3490
- try{v.git&&(await v.git.branch(),O=await(0,y.resolveDefaultBranch)(v.mainBasePath,v.gitBinary))}catch{}const M=v.git?await(0,a.findCommittedMarkers)(v.git,`requests/${x}`):{files:[],determined:!1}
3491
- if(!M.determined)return S({success:!1,reason:"marker_check_unavailable"}),{content:[{type:"text",text:`Cannot merge ${x}: the conflict-marker check could not run, so it is unknown whether unresolved markers remain on requests/${x}. Nothing was merged.`}],isError:!0}
3492
- if(M.files.length>0)return S({success:!1,reason:"unresolved_markers"}),{content:[{type:"text",text:`Cannot merge ${x}: committed conflict markers remain in ${M.files.join(", ")}. Resolve every <<<<<<< block with your file tools, verify, then merge again.`}],isError:!0}
3512
+ try{v.git&&(await v.git.branch(),O=await(0,y.resolveDefaultBranch)(v.mainBasePath,v.gitBinary))}catch{}const M=v.git?await(0,a.findCommittedMarkers)(v.git,`requests/${_}`):{files:[],determined:!1}
3513
+ if(!M.determined)return E({success:!1,reason:"marker_check_unavailable"}),{content:[{type:"text",text:`Cannot merge ${_}: the conflict-marker check could not run, so it is unknown whether unresolved markers remain on requests/${_}. Nothing was merged.`}],isError:!0}
3514
+ if(M.files.length>0)return E({success:!1,reason:"unresolved_markers"}),{content:[{type:"text",text:`Cannot merge ${_}: committed conflict markers remain in ${M.files.join(", ")}. Resolve every <<<<<<< block with your file tools, verify, then merge again.`}],isError:!0}
3493
3515
  let D
3494
- v.mainBasePath&&await(0,c.foldAtDoor)({repoDir:v.mainBasePath,branch:`requests/${x}`,defaultBranch:O,requestId:x,door:"merge",gitBinary:v.gitBinary,postRecord:e=>k.proxyPost(`/api/projects/${v.projectId}/tool-records`,e),log:E})
3516
+ v.mainBasePath&&await(0,c.foldAtDoor)({repoDir:v.mainBasePath,branch:`requests/${_}`,defaultBranch:O,requestId:_,door:"merge",gitBinary:v.gitBinary,postRecord:e=>k.proxyPost(`/api/projects/${v.projectId}/tool-records`,e),log:S})
3495
3517
  let F=null
3496
- {const e=await(0,p.runMergeQueueGate)({projectPath:C,requestId:x,branchName:`requests/${x}`,defaultBranch:O,requestTitle:R?.title,criteria:R?.acceptanceCriteria??R?.acceptance_criteria,testGateOff:!1===R?.gates?.test?.enabled,source:"merge_request",gitBinary:v.gitBinary,logger:E,fetchRequestRuns:async()=>{const e=await k.proxyGet(`/api/projects/${v.projectId}/requests/${x}/test-runs`),t=e?.data??e??[]
3518
+ {const e=await(0,p.runMergeQueueGate)({projectPath:C,requestId:_,branchName:`requests/${_}`,defaultBranch:O,requestTitle:R?.title,criteria:R?.acceptanceCriteria??R?.acceptance_criteria,testGateOff:!1===R?.gates?.test?.enabled,source:"merge_request",gitBinary:v.gitBinary,logger:S,fetchRequestRuns:async()=>{const e=await k.proxyGet(`/api/projects/${v.projectId}/requests/${_}/test-runs`),t=e?.data??e??[]
3497
3519
  return Array.isArray(t)?t:[]},fetchActiveProcesses:async()=>{const e=await k.proxyGet(`/api/projects/${v.projectId}/test-processes/active`),t=e?.data??e??[]
3498
- return(Array.isArray(t)?t:[t]).filter(Boolean)},evaluatePromises:async({baseSha:e,headSha:r})=>{const{getActionServerBaseUrl:n}=await Promise.resolve().then(()=>t(79414)),o=await fetch(`${n()}/api/promise-checks-at-merge`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:x,baseSha:e,headSha:r}),signal:AbortSignal.timeout(6e5)})
3520
+ return(Array.isArray(t)?t:[t]).filter(Boolean)},evaluatePromises:async({baseSha:e,headSha:r})=>{const{getActionServerBaseUrl:n}=await Promise.resolve().then(()=>t(79414)),o=await fetch(`${n()}/api/promise-checks-at-merge`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:_,baseSha:e,headSha:r}),signal:AbortSignal.timeout(6e5)})
3499
3521
  if(!o.ok)return null
3500
3522
  const s=await o.json()
3501
- return s?.data??null},evaluateMainChange:async({landingSha:e})=>{const{getActionServerBaseUrl:r}=await Promise.resolve().then(()=>t(79414)),n=await fetch(`${r()}/api/main-change-at-landing`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:x,landingSha:e}),signal:AbortSignal.timeout(2e5)})
3523
+ return s?.data??null},evaluateMainChange:async({landingSha:e})=>{const{getActionServerBaseUrl:r}=await Promise.resolve().then(()=>t(79414)),n=await fetch(`${r()}/api/main-change-at-landing`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:_,landingSha:e}),signal:AbortSignal.timeout(2e5)})
3502
3524
  if(!n.ok)return null
3503
3525
  const o=await n.json()
3504
- return o?.data??null},dispatchCatchUp:async e=>{const{getActionServerBaseUrl:r}=await Promise.resolve().then(()=>t(79414)),n=await fetch(`${r()}/api/run-tests-now`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:x,parentSessionId:v.currentSessionId,catchUp:e}),signal:AbortSignal.timeout(6e4)})
3526
+ return o?.data??null},dispatchCatchUp:async e=>{const{getActionServerBaseUrl:r}=await Promise.resolve().then(()=>t(79414)),n=await fetch(`${r()}/api/run-tests-now`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:_,parentSessionId:v.currentSessionId,catchUp:e}),signal:AbortSignal.timeout(6e4)})
3505
3527
  if(!n.ok){const e=await n.text().catch(()=>"")
3506
3528
  return{success:!1,error:`action server returned ${n.status}: ${e.slice(0,300)}`}}const o=await n.json()
3507
3529
  return o?.success?{success:!0,testRunIds:o?.data?.testRunIds??[],settled:o?.data?.catchUp?.settled??null}:{success:!1,error:o?.error||"unknown error"}}})
3508
3530
  if("wait-slot"===e.action){const t=e.holder
3509
- return S({success:!1,reason:"merge_slot_busy"}),{content:[{type:"text",text:`STOP: Request ${x} was NOT merged: the project's merge slot is held by ${t?.requestId??"another request"} (${t?.phase??"merging"}, since ${t?.acquiredAt??"unknown"}). Merges land one at a time so each request lands exactly the tree its tests certified. Wait about a minute, then call merge again with the same arguments.`}],isError:!0}}if(q=!0,"catchup-running"===e.action)return T=!0,S({success:!1,reason:"merge_catchup_running"}),{content:[{type:"text",text:`Request ${x} is NOT merged yet: your recorded test runs no longer certify the exact tree this merge would land (another request landed first: ${O} moved, or its changes were freshened into your branch, after your tests ran). A catch-up test run of the applicable test processes is ${e.dispatched?"now running":"already running"} against the EXACT tree that would land (commit ${e.mergeCommitSha.slice(0,8)}), and the merge slot is held for you so ${O} cannot move again. STOP and wait: the test report is delivered to you automatically. On pass you'll be cleared to merge; that merge call lands the very tree that was tested. On failure the failing run is your rework evidence: fix, close the rework task, and re-complete.`}],isError:!1}
3510
- if("env-gap"===e.action){S({success:!1,reason:"merge_env_gap"})
3531
+ return E({success:!1,reason:"merge_slot_busy"}),{content:[{type:"text",text:`STOP: Request ${_} was NOT merged: the project's merge slot is held by ${t?.requestId??"another request"} (${t?.phase??"merging"}, since ${t?.acquiredAt??"unknown"}). Merges land one at a time so each request lands exactly the tree its tests certified. Wait about a minute, then call merge again with the same arguments.`}],isError:!0}}if(q=!0,"catchup-running"===e.action)return T=!0,E({success:!1,reason:"merge_catchup_running"}),{content:[{type:"text",text:`Request ${_} is NOT merged yet: your recorded test runs no longer certify the exact tree this merge would land (another request landed first: ${O} moved, or its changes were freshened into your branch, after your tests ran). A catch-up test run of the applicable test processes is ${e.dispatched?"now running":"already running"} against the EXACT tree that would land (commit ${e.mergeCommitSha.slice(0,8)}), and the merge slot is held for you so ${O} cannot move again. STOP and wait: the test report is delivered to you automatically. On pass you'll be cleared to merge; that merge call lands the very tree that was tested. On failure the failing run is your rework evidence: fix, close the rework task, and re-complete.`}],isError:!1}
3532
+ if("env-gap"===e.action){E({success:!1,reason:"merge_env_gap"})
3511
3533
  const t=e.missingCapabilities.length?e.missingCapabilities.join(", "):"a required tool"
3512
- return{content:[{type:"text",text:`Request ${x}'s real tests PASSED, but one acceptance check could not run: the confined box lacks ${t} (a missing tool, not a code defect). This is not a red gate and there is nothing for you to fix in the code. It waits on a card for the user to extend the box or merge anyway -- do NOT rewrite the code or re-run in a loop.`}],isError:!0}}if("catchup-failed"===e.action)return S({success:!1,reason:"merge_catchup_failed"}),{content:[{type:"text",text:`Cannot merge request ${x}: the merge-boundary catch-up test run FAILED against the prospective merge commit ${e.mergeCommitSha.slice(0,8)}${e.failedChecks?.length?` (failing: ${e.failedChecks.join(", ")})`:""}. The merge slot was released; the failing run is recorded as evidence and the request goes into the normal test-rework lane (the predecessor's changes were reconciled into your branch so your fix sees the real interaction). Fix the reported failures, complete the rework task, then re-complete the request.`}],isError:!0}
3513
- if("error"===e.action)return S({success:!1,error:"merge_queue_error"}),{content:[{type:"text",text:`Merge queue check failed: ${e.message}`}],isError:!0}
3514
- if("sent-back"===e.action)return S({success:!1,error:"sent_back"}),await(0,f.bringMainIn)({projectPath:C,requestId:x,defaultBranch:O,logger:E}),{content:[{type:"text",text:`Not merged: ${e.summary}\n\n${x} is back in progress with you and main's newer work is now on your branch: make your work hold alongside it, then complete again.`}],isError:!0}
3515
- if("review-held"===e.action){S({success:!1,error:"review_held"})
3516
- const t=await(0,f.holdForMainChangeReview)({projectPath:C,projectId:v.projectId,requestId:x,defaultBranch:O,touched:e.touched,logger:E,post:(e,t)=>k.proxyPost(e,t)})
3517
- return{content:[{type:"text",text:`Not merged yet: ${e.summary}\n\n${t.reviewing?`Main's newer work is now on your branch, and a reviewer is checking ${x}'s criteria against that change. Wait for its report: apply what it asks for, then complete again.`:"A review of that change could not be started; make sure your work holds alongside main's newer work, then merge again."}`}],isError:!0}}if("promises-held"===e.action)return S({success:!1,error:"promises_held"}),{content:[{type:"text",text:`Not merged: ${e.summary}.\n\nThe user decides on the promise card now in front of them, and only they answer it. Their answer sends the work back to you, or ends the promise (or keeps the new check) and merges it at once, with no sign-off after it. To change a promise's check yourself, add a criterion to this request with the promise's words, carries: "promise:<request>:<criterion>", and the new command; a reviewer checks it like any other.`}],isError:!0}
3518
- "conflicts"===e.action&&(F=new Error(`Merge of requests/${x} into ${O} has conflicts (detected on the prospective merge tree):\n${e.summary}`),F.conflictInfo={conflictedFiles:[],workingDirectory:void 0}),"land"===e.action&&(D=e.certifiedCommitSha,E.info("merge-queue: landing certified catch-up commit (tested tree == merged tree)",{requestId:x,certifiedCommitSha:D.slice(0,8)}))}try{if(F)throw F
3519
- j=await v.workspaceManager.completeRequest(x,A,D?{certifiedCommitSha:D}:void 0),P=D?"merge-queue-certified":j.autoMergeInfo?.strategy||"workspace-manager",E.info("Merge completed",{requestId:x,mergeCommitHash:j.mergeCommitHash,mergeMethod:P})}catch(t){if(!t.conflictInfo){const e=(0,d.getErrorMessage)(t)
3520
- return S({success:!1,error:"merge_failure"}),{content:[{type:"text",text:`Merge failed: ${e}`}],isError:!0}}{const r=t.conflictInfo.workingDirectory||v.workspaceManager?.getCurrentContext(v.currentSessionId)?.basePath||void 0,n=`requests/${x}`,o=await(0,a.reconcileBranchWithMain)({projectPath:v.basePath,requestId:x,branchName:n,defaultBranch:O,worktreePath:r})
3521
- if("clean"!==o.outcome){if("conflicts-committed"===o.outcome){try{await e.updateRequest(x,{status:"resolving_conflicts"}),E.info("Set status to resolving_conflicts",{requestId:x})}catch(e){E.error("Failed to set resolving_conflicts status",{requestId:x,error:(0,d.getErrorMessage)(e)})}const t=(0,a.buildResolveInstruction)({requestId:x,branchName:n,defaultBranch:O,landed:o.landed,conflictedFiles:o.conflictedFiles,retryVia:"merge"})
3522
- return S({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:t}],isError:!1}}return S({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:`Merge conflicts detected and auto-reconcile was not possible (${o.outcome}${o.error?`: ${o.error}`:""}). The branch worktree may be missing or dirty: resolve manually and merge again.`}],isError:!0}}E.info("Branch reconciled with main cleanly: retrying merge",{requestId:x})
3523
- try{j=await v.workspaceManager.completeRequest(x,A),P="reconcile-retry"}catch(e){return S({success:!1,error:"merge_failure_after_reconcile"}),{content:[{type:"text",text:`Merge failed after clean reconcile: ${(0,d.getErrorMessage)(e)}`}],isError:!0}}}}const N=v.workspaceManager.getCurrentContext(v.currentSessionId)?.basePath||v.basePath,z=D??j?.mergeCommitHash
3534
+ return{content:[{type:"text",text:`Request ${_}'s real tests PASSED, but one acceptance check could not run: the confined box lacks ${t} (a missing tool, not a code defect). This is not a red gate and there is nothing for you to fix in the code. It waits on a card for the user to extend the box or merge anyway -- do NOT rewrite the code or re-run in a loop.`}],isError:!0}}if("catchup-failed"===e.action)return E({success:!1,reason:"merge_catchup_failed"}),{content:[{type:"text",text:`Cannot merge request ${_}: the merge-boundary catch-up test run FAILED against the prospective merge commit ${e.mergeCommitSha.slice(0,8)}${e.failedChecks?.length?` (failing: ${e.failedChecks.join(", ")})`:""}. The merge slot was released; the failing run is recorded as evidence and the request goes into the normal test-rework lane (the predecessor's changes were reconciled into your branch so your fix sees the real interaction). Fix the reported failures, complete the rework task, then re-complete the request.`}],isError:!0}
3535
+ if("error"===e.action)return E({success:!1,error:"merge_queue_error"}),{content:[{type:"text",text:`Merge queue check failed: ${e.message}`}],isError:!0}
3536
+ if("sent-back"===e.action)return E({success:!1,error:"sent_back"}),await(0,f.bringMainIn)({projectPath:C,requestId:_,defaultBranch:O,logger:S}),{content:[{type:"text",text:`Not merged: ${e.summary}\n\n${_} is back in progress with you and main's newer work is now on your branch: make your work hold alongside it, then complete again.`}],isError:!0}
3537
+ if("review-held"===e.action){E({success:!1,error:"review_held"})
3538
+ const t=await(0,f.holdForMainChangeReview)({projectPath:C,projectId:v.projectId,requestId:_,defaultBranch:O,touched:e.touched,logger:S,post:(e,t)=>k.proxyPost(e,t)})
3539
+ return{content:[{type:"text",text:`Not merged yet: ${e.summary}\n\n${t.reviewing?`Main's newer work is now on your branch, and a reviewer is checking ${_}'s criteria against that change. Wait for its report: apply what it asks for, then complete again.`:"A review of that change could not be started; make sure your work holds alongside main's newer work, then merge again."}`}],isError:!0}}if("promises-held"===e.action)return E({success:!1,error:"promises_held"}),{content:[{type:"text",text:`Not merged: ${e.summary}.\n\nThe user decides on the promise card now in front of them, and only they answer it. Their answer sends the work back to you, or ends the promise (or keeps the new check) and merges it at once, with no sign-off after it. To change a promise's check yourself, add a criterion to this request with the promise's words, carries: "promise:<request>:<criterion>", and the new command; a reviewer checks it like any other.`}],isError:!0}
3540
+ "conflicts"===e.action&&(F=new Error(`Merge of requests/${_} into ${O} has conflicts (detected on the prospective merge tree):\n${e.summary}`),F.conflictInfo={conflictedFiles:[],workingDirectory:void 0}),"land"===e.action&&(D=e.certifiedCommitSha,S.info("merge-queue: landing certified catch-up commit (tested tree == merged tree)",{requestId:_,certifiedCommitSha:D.slice(0,8)}))}try{if(F)throw F
3541
+ j=await v.workspaceManager.completeRequest(_,A,D?{certifiedCommitSha:D}:void 0),P=D?"merge-queue-certified":j.autoMergeInfo?.strategy||"workspace-manager",S.info("Merge completed",{requestId:_,mergeCommitHash:j.mergeCommitHash,mergeMethod:P})}catch(t){if(!t.conflictInfo){const e=(0,d.getErrorMessage)(t)
3542
+ return E({success:!1,error:"merge_failure"}),{content:[{type:"text",text:`Merge failed: ${e}`}],isError:!0}}{const r=t.conflictInfo.workingDirectory||v.workspaceManager?.getCurrentContext(v.currentSessionId)?.basePath||void 0,n=`requests/${_}`,o=await(0,a.reconcileBranchWithMain)({projectPath:v.basePath,requestId:_,branchName:n,defaultBranch:O,worktreePath:r})
3543
+ if("clean"!==o.outcome){if("conflicts-committed"===o.outcome){try{await e.updateRequest(_,{status:"resolving_conflicts"}),S.info("Set status to resolving_conflicts",{requestId:_})}catch(e){S.error("Failed to set resolving_conflicts status",{requestId:_,error:(0,d.getErrorMessage)(e)})}const t=(0,a.buildResolveInstruction)({requestId:_,branchName:n,defaultBranch:O,landed:o.landed,conflictedFiles:o.conflictedFiles,retryVia:"merge"})
3544
+ return E({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:t}],isError:!1}}return E({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:`Merge conflicts detected and auto-reconcile was not possible (${o.outcome}${o.error?`: ${o.error}`:""}). The branch worktree may be missing or dirty: resolve manually and merge again.`}],isError:!0}}S.info("Branch reconciled with main cleanly: retrying merge",{requestId:_})
3545
+ try{j=await v.workspaceManager.completeRequest(_,A),P="reconcile-retry"}catch(e){return E({success:!1,error:"merge_failure_after_reconcile"}),{content:[{type:"text",text:`Merge failed after clean reconcile: ${(0,d.getErrorMessage)(e)}`}],isError:!0}}}}const N=v.workspaceManager.getCurrentContext(v.currentSessionId)?.basePath||v.basePath,z=D??j?.mergeCommitHash
3524
3546
  z&&($=await(0,s.firstParentSha)((0,s.gitFor)(N,v.git,v.gitBinary),z)??$)
3525
3547
  const L=await(0,s.computeDiffStats)((0,s.gitFor)(N,v.git,v.gitBinary),$,j?.mergeCommitHash)
3526
- try{await k.proxyPost(`/api/projects/${v.projectId}/requests/${x}/finalize-completion`,{summary:R?.completionSummary||`Merged request ${x}`,mergeResult:{mergeCommitHash:j.mergeCommitHash,mergeBaseSha:$,mergedFiles:j.mergedFiles,cleanedUp:j.cleanedUp,method:P,diffStats:L}})}catch(e){const t=(0,d.getErrorMessage)(e)
3527
- return E.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:x,mergeCommitHash:j.mergeCommitHash,error:t}),S({success:!1,error:"api_update_failed"}),{content:[{type:"text",text:`CRITICAL: Code was merged (commit: ${j.mergeCommitHash.substring(0,8)}) but failed to update server: ${t}`}],isError:!0}}return(0,n.maybePublishReceiptOnMerge)(x),(0,m.clearMergeCandidate)(C,x,v.gitBinary).catch(()=>{}),S({success:!0}),{content:[{type:"text",text:`Request ${x} merged successfully.\nCommit: ${j.mergeCommitHash.substring(0,8)}`}]}}catch(e){const t=(0,d.getErrorMessage)(e)
3528
- E.error("Merge request failed",{error:t})
3529
- try{await(0,l.createFailureToolRecord)("merge-request",e,{error:t,requestId:x},k.getToolRecordClient(),E,$,{requestId:x,projectId:v.projectId,note:"Failed to merge request"})}catch{}return S({success:!1,error:e instanceof Error?e.name:"unknown"}),{content:[{type:"text",text:`Error: ${t}`}],isError:!0}}finally{if(q&&!T)try{(0,h.releaseMergeLease)(C,x)}catch{}}}
3548
+ try{await k.proxyPost(`/api/projects/${v.projectId}/requests/${_}/finalize-completion`,{summary:R?.completionSummary||`Merged request ${_}`,mergeResult:{mergeCommitHash:j.mergeCommitHash,mergeBaseSha:$,mergedFiles:j.mergedFiles,cleanedUp:j.cleanedUp,method:P,diffStats:L}})}catch(e){const t=(0,d.getErrorMessage)(e)
3549
+ return S.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:_,mergeCommitHash:j.mergeCommitHash,error:t}),E({success:!1,error:"api_update_failed"}),{content:[{type:"text",text:`CRITICAL: Code was merged (commit: ${j.mergeCommitHash.substring(0,8)}) but failed to update server: ${t}`}],isError:!0}}return(0,n.maybePublishReceiptOnMerge)(_),(0,m.clearMergeCandidate)(C,_,v.gitBinary).catch(()=>{}),E({success:!0}),{content:[{type:"text",text:`Request ${_} merged successfully.\nCommit: ${j.mergeCommitHash.substring(0,8)}`}]}}catch(e){const t=(0,d.getErrorMessage)(e)
3550
+ S.error("Merge request failed",{error:t})
3551
+ try{await(0,l.createFailureToolRecord)("merge-request",e,{error:t,requestId:_},k.getToolRecordClient(),S,$,{requestId:_,projectId:v.projectId,note:"Failed to merge request"})}catch{}return E({success:!1,error:e instanceof Error?e.name:"unknown"}),{content:[{type:"text",text:`Error: ${t}`}],isError:!0}}finally{if(q&&!T)try{(0,h.releaseMergeLease)(C,_)}catch{}}}
3530
3552
  const n=t(19817),o=t(83807),s=t(92456),i=t(59860),a=t(35510),c=t(82860),l=t(18616),d=t(58824),u=t(27478),h=t(56509),p=t(14128),f=t(13922),m=t(87192),g=t(65454),y=t(52232),w=t(87780),b=t(63580),v={"signoff-park":"the sign-off","test-process-broken":"a test check that cannot run","test-cap-escalation":"tests that keep failing","review-cap-escalation":"review findings still open","review-gap":"a review that did not run","environment-gap":"the test box missing a tool","unprovable-criteria":"criteria nothing can prove"}},95994(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
3531
3553
  var o=Object.getOwnPropertyDescriptor(t,r)
3532
3554
  o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),s=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var t=[]
@@ -3558,16 +3580,16 @@ let k=null
3558
3580
  try{const e=await fetch(`${(0,s.getActionServerBaseUrl)()}/api/branch-head?requestId=${encodeURIComponent(c)}`,{signal:AbortSignal.timeout(5e3)})
3559
3581
  e.ok&&(k=(await e.json())?.data?.sha??null)}catch(e){d.debug("branch-head fetch failed; verdicts recorded without sha",{error:(0,n.getErrorMessage)(e)})}let I="review"
3560
3582
  const $=e?.cliOrigin?.sessionId||o.sessionIdentity.getIdentity()?.sessionId||null
3561
- try{"driver"===($?await o.sessionIdentity.getAgentTypeFor($):null)&&(I="drive")}catch{}const E="drive"===I?l.filter(e=>"unmet"===e.status&&!("string"==typeof e.fixTitle&&e.fixTitle.trim())).map(e=>e.key):[]
3562
- if(E.length>0)return u({success:!1,reason:"drive_unmet_without_fix_title"}),{content:[{type:"text",text:`Give each claim that did not hold a fixTitle: ${E.map(e=>`[${e}]`).join(", ")}. It is the title of the fix request the user can file from the sign-off card, 3 to 8 plain words saying what will work afterwards (e.g. "Browser Back returns to the list"). Nothing was recorded.`}],isError:!0}
3583
+ try{"driver"===($?await o.sessionIdentity.getAgentTypeFor($):null)&&(I="drive")}catch{}const S="drive"===I?l.filter(e=>"unmet"===e.status&&!("string"==typeof e.fixTitle&&e.fixTitle.trim())).map(e=>e.key):[]
3584
+ if(S.length>0)return u({success:!1,reason:"drive_unmet_without_fix_title"}),{content:[{type:"text",text:`Give each claim that did not hold a fixTitle: ${S.map(e=>`[${e}]`).join(", ")}. It is the title of the fix request the user can file from the sign-off card, 3 to 8 plain words saying what will work afterwards (e.g. "Browser Back returns to the list"). Nothing was recorded.`}],isError:!0}
3563
3585
  if(b.length>0&&"drive"!==I)return u({success:!1,reason:"misleads_not_drive"}),{content:[{type:"text",text:"misleads files a drive finding, and only a driver files those. Drop misleads and record the verdicts. Nothing was recorded."}],isError:!0}
3564
- let S
3586
+ let E
3565
3587
  try{const e=await a.proxyPost(`/api/projects/${t.projectId}/requests/${c}/verify-criteria`,{verifications:w,dimension:I,headSha:k})
3566
- S=e?.data||e}catch(e){return u({success:!1,error:"record_failed"}),{content:[{type:"text",text:`Failed to record criteria verification: ${(0,n.getErrorMessage)(e)}`}],isError:!0}}d.info("Recorded criteria verification",{requestId:c,verified:S.verified,remaining:S.remainingUnverified})
3567
- let x=""
3588
+ E=e?.data||e}catch(e){return u({success:!1,error:"record_failed"}),{content:[{type:"text",text:`Failed to record criteria verification: ${(0,n.getErrorMessage)(e)}`}],isError:!0}}d.info("Recorded criteria verification",{requestId:c,verified:E.verified,remaining:E.remainingUnverified})
3589
+ let _=""
3568
3590
  for(const e of b)try{const r=await a.proxyPost(`/api/projects/${t.projectId}/session-messages/drive-findings`,{driveRequestId:c,criterionId:m.get(e.key).id,observed:e.misleads,fixTitle:"string"==typeof e.fixTitle&&e.fixTitle.trim()?e.fixTitle.trim():void 0,sourceRequestId:e.sourceRequestId,sessionExternalId:$??void 0}),n=r?.data?.messageCode??r?.messageCode
3569
- x+=`\n\nFinding for [${e.key}] filed for the user as ${n}: file a fix, retire the claim, or dismiss. You are not told the answer.`}catch(t){x+=`\n\nFinding for [${e.key}] NOT filed: ${(0,n.getErrorMessage)(t)}`}const _=w.filter(e=>"met"===e.status).length,C=w.filter(e=>"reword"===e.status).length,q=w.length-_-C
3570
- return u({success:!0}),{content:[{type:"text",text:"drive"===I?`Recorded drive verdicts for ${w.length} claim(s) on ${c} (${_} held, ${q} did not). Finish with request_operations complete: every claim is either stamped or named in couldNotCheck.${x}`:`Recorded verdicts for ${w.length} criterion/criteria on ${c} (${_} met, ${q} unmet${C?`, ${C} needing a reword`:""}). ${S.remainingUnverified} remain unmet overall; the server opens a rework task for the worker when any are unmet. This does NOT merge; the worker finalizes after review.${x}`}]}}catch(e){const t=(0,n.getErrorMessage)(e)
3591
+ _+=`\n\nFinding for [${e.key}] filed for the user as ${n}: file a fix, retire the claim, or dismiss. You are not told the answer.`}catch(t){_+=`\n\nFinding for [${e.key}] NOT filed: ${(0,n.getErrorMessage)(t)}`}const x=w.filter(e=>"met"===e.status).length,C=w.filter(e=>"reword"===e.status).length,q=w.length-x-C
3592
+ return u({success:!0}),{content:[{type:"text",text:"drive"===I?`Recorded drive verdicts for ${w.length} claim(s) on ${c} (${x} held, ${q} did not). Finish with request_operations complete: every claim is either stamped or named in couldNotCheck.${_}`:`Recorded verdicts for ${w.length} criterion/criteria on ${c} (${x} met, ${q} unmet${C?`, ${C} needing a reword`:""}). ${E.remainingUnverified} remain unmet overall; the server opens a rework task for the worker when any are unmet. This does NOT merge; the worker finalizes after review.${_}`}]}}catch(e){const t=(0,n.getErrorMessage)(e)
3571
3593
  return d.error("record_criteria_verification failed",{error:t}),u({success:!1,error:"unexpected"}),{content:[{type:"text",text:`Error: ${t}`}],isError:!0}}}
3572
3594
  const r=t(59860),n=t(58824),o=t(6688),s=t(79414),i=t(65454)},56700(e,exports,t){exports.def=void 0
3573
3595
  const r=t(11569),n=t(18357),o=r.z.object({requestId:r.z.string().describe("The held draft request being ruled on (e.g. req000123)."),verdict:r.z.enum(["approve","object"]).describe("'approve' lets the work start (the draft registers); 'object' holds it and puts your reasoning in front of the user."),reasoning:r.z.string().optional().describe("Required for object: plain-language reasoning written FOR THE USER (what you found, why it gives pause, what would change your mind). Optional for approve.")})
@@ -3591,70 +3613,70 @@ const t=new Map,r=(e,t)=>`${e}\0${t}`
3591
3613
  exports.SHAPING_AGENTS=new Set(["request-architect","planner"])
3592
3614
  const n=new Map},74165(e,exports,t){exports.def=void 0
3593
3615
  const r=t(76183),n=t(84648)
3594
- exports.def={name:"request_operations",description:"Operations on existing requests (create_request creates one).\n\n\u2022 complete - Phase one of completion: the summary; returns the criteria keys to verify.\n\u2022 merge - Phase two: evidence per criterion key; merges when all are met.\n\u2022 revert - Undo all changes of a completed request.\n\u2022 reopen - Send a finished, unmerged request back to rework with your reason; the worker is told. If the result already satisfies the feedback, say so instead. A merged request gets a follow-up instead.\n\u2022 update - Change title, description, type, criteria or process.\n\u2022 activate - Start a draft when the user asks. Refused for drafts held by intake: answer the intake ask instead.\n\u2022 get-info - Everything about a request.\n\u2022 list / search - List, or full-text search title, description and criteria, with filters.\n\u2022 list-areas - The project's work areas, most recently used first; pick from them for create_request.\n\u2022 update-areas - Replace the request's work areas.\n\u2022 wait-on - Record that this request waits for another (waitsOn: req######, reason in the user's words) whenever work must queue; a report does not record a wait. The wait shows on the request, tells the stall watchdog it is idle on purpose, and ends when the blocker merges. A canceled blocker never merges: move the wait to the replacement (create_request replaces, or wait-on it) or clear it.\n\u2022 clear-wait - Drop a wait (waitsOn; omit to clear all).\n\u2022 cancel - Cancel a request the user no longer wants (before merge), with reason. It is recorded and never restarts; telling a worker in prose cancels nothing.\n\u2022 delete-empty - Delete a request that has no tasks.\n\u2022 dispute-criterion - Ask the user to waive a process criterion that does not fit this request.\n\u2022 asked-no-change - After answering a user message on this request that asked for no change (userSaid): it is no longer listed as a change they made. Not for a message you acted on.\n\u2022 hand-over - Give the user's message you are answering to the worker already on requestId's code (its request is live, or merged with its worker still warm), instead of filing it yourself: for an adjustment to what that worker just did. The system copies their words; the worker answers them itself on that request's thread. Refused when no worker is live there: then file it as usual.",schema:r.RequestOperationsSchema,annotations:{title:"Request Operations",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},handler:(e,t)=>(0,n.handleRequestOperations)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},84648(e,exports,t){exports.handleRequestOperations=async function(e,t,k,I,$,E,S){const x=I.child({tool:"request_operations"})
3595
- try{const _=e
3596
- let{requestId:C}=_
3597
- const{operation:q}=_
3598
- if("list"!==q&&"search"!==q&&"list-areas"!==q&&!C){const e=await(0,f.resolveRequestIdOrError)(k,x,t.currentSessionId,`Error: requestId is required for '${q}' and could not be resolved from session`)
3616
+ exports.def={name:"request_operations",description:"Operations on existing requests (create_request creates one).\n\n\u2022 complete - Phase one of completion: the summary; returns the criteria keys to verify.\n\u2022 merge - Phase two: evidence per criterion key; merges when all are met.\n\u2022 revert - Undo all changes of a completed request.\n\u2022 reopen - Send a finished, unmerged request back to rework with your reason; the worker is told. If the result already satisfies the feedback, say so instead. A merged request gets a follow-up instead.\n\u2022 update - Change title, description, type, criteria or process.\n\u2022 activate - Start a draft when the user asks. Refused for drafts held by intake: answer the intake ask instead.\n\u2022 get-info - Everything about a request.\n\u2022 list / search - List, or full-text search title, description and criteria.\n\u2022 list-areas / update-areas - The project's work areas, most recently used first / replace the request's areas.\n\u2022 wait-on - Record that this request waits for another (waitsOn: req######, reason in the user's words) whenever work must queue; a report does not record a wait. It ends when the blocker merges; a canceled blocker never merges, so move the wait to its replacement or clear it. clear-wait drops a wait (omit waitsOn to clear all).\n\u2022 cancel - Cancel a request the user no longer wants (before merge), with reason. Telling a worker in prose cancels nothing.\n\u2022 delete-empty - Delete a request that has no tasks.\n\u2022 dispute-criterion - Ask the user to waive a process criterion that does not fit this request.\n\u2022 asked-no-change - After answering a user message on this request that asked for no change (userSaid), so it is not listed as a change they made.\n\u2022 hand-over - Give the user's message you are answering to the worker still warm on requestId, for an adjustment to what that worker just did; it answers them on that request's thread. Refused when no worker is live there: then file it as usual.",schema:r.RequestOperationsSchema,annotations:{title:"Request Operations",readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1},handler:(e,t)=>(0,n.handleRequestOperations)(e,t.context,t.apiClient,t.logger,t.metrics,t.mcpServer,t.cliOrigin)}},84648(e,exports,t){exports.handleRequestOperations=async function(e,t,k,I,$,S,E){const _=I.child({tool:"request_operations"})
3617
+ try{const x=e
3618
+ let{requestId:C}=x
3619
+ const{operation:q}=x
3620
+ if("list"!==q&&"search"!==q&&"list-areas"!==q&&!C){const e=await(0,f.resolveRequestIdOrError)(k,_,t.currentSessionId,`Error: requestId is required for '${q}' and could not be resolved from session`)
3599
3621
  if(!e.ok)return e.errorResult
3600
- C=e.requestId}switch(x.info("Processing request operation",{requestId:C,operation:q}),q){case"update":return x.info("Routing to update-request handler"),(0,y.withAlchemyKind)(await(0,a.handleUpdateRequest)({requestId:C,title:_.update?.title,description:_.update?.description,type:_.update?.type,acceptanceCriteria:_.update?.acceptanceCriteria,arcName:_.update?.arcName,process:_.update?.process,userSaid:_.update?.userSaid??_.userSaid,removesCriteria:!0===_.update?.removesCriteria},t,k,I,$),v.update)
3601
- case"activate":{x.info("Routing to activate-request (draft -> open)")
3622
+ C=e.requestId}switch(_.info("Processing request operation",{requestId:C,operation:q}),q){case"update":return _.info("Routing to update-request handler"),(0,y.withAlchemyKind)(await(0,a.handleUpdateRequest)({requestId:C,title:x.update?.title,description:x.update?.description,type:x.update?.type,acceptanceCriteria:x.update?.acceptanceCriteria,arcName:x.update?.arcName,process:x.update?.process,userSaid:x.update?.userSaid??x.userSaid,removesCriteria:!0===x.update?.removesCriteria},t,k,I,$),v.update)
3623
+ case"activate":{_.info("Routing to activate-request (draft -> open)")
3602
3624
  const e=k.getRequestClient(),r=await e.getRequest(C)
3603
3625
  if(!r)return(0,y.withAlchemyKind)({content:[{type:"text",text:`Request not found: ${C}`}],isError:!0},v.activate)
3604
3626
  if("draft"!==r.status)return(0,y.withAlchemyKind)({content:[{type:"text",text:`Cannot activate ${C}: it is '${r.status}', not a draft. Activation only registers drafts.`}],isError:!0},v.activate)
3605
3627
  const n=r.intake?.status
3606
3628
  if("pending"===n||"objection"===n)return(0,y.withAlchemyKind)({content:[{type:"text",text:`Cannot activate ${C}: its intake gate is ${"objection"===n?"holding a reviewer objection":"awaiting a verdict"}. The intake ask must be answered first -- activation cannot go around it.`}],isError:!0},v.activate)
3607
- if(t.currentSessionId&&/^ses_/.test(t.currentSessionId)){const e="string"==typeof _.userSaid?_.userSaid:""
3608
- if(!e||!await(0,a.userSaidOnRequest)(k,t.projectId,C,e,{orUnanchored:!0}))return(0,y.withAlchemyKind)({content:[{type:"text",text:`\u26d4 Not started: ${C} is a draft, and starting it is the user's call (their Start button). `+(e?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":"If they told you to start it, pass their words in userSaid (quoted exactly) and it starts. If they are describing what they want, write it into the draft with update and leave starting it to them.")}],isError:!0},v.activate)}return await e.updateRequest(C,{status:"open"}),(0,y.withAlchemyKind)({content:[{type:"text",text:`Activated ${C} (draft -> open). Assignment and gates proceed per the project's flow.`}]},v.activate)}case"complete":{x.info("Routing to complete-request handler")
3629
+ if(t.currentSessionId&&/^ses_/.test(t.currentSessionId)){const e="string"==typeof x.userSaid?x.userSaid:""
3630
+ if(!e||!await(0,a.userSaidOnRequest)(k,t.projectId,C,e,{orUnanchored:!0}))return(0,y.withAlchemyKind)({content:[{type:"text",text:`\u26d4 Not started: ${C} is a draft, and starting it is the user's call (their Start button). `+(e?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":"If they told you to start it, pass their words in userSaid (quoted exactly) and it starts. If they are describing what they want, write it into the draft with update and leave starting it to them.")}],isError:!0},v.activate)}return await e.updateRequest(C,{status:"open"}),(0,y.withAlchemyKind)({content:[{type:"text",text:`Activated ${C} (draft -> open). Assignment and gates proceed per the project's flow.`}]},v.activate)}case"complete":{_.info("Routing to complete-request handler")
3609
3631
  const e=function(e,t){const r=t&&"object"==typeof t?t:{},n={}
3610
3632
  for(const[e,t]of Object.entries(r))null!=t&&(n[e]=t)
3611
- return n.requestId=e,n.replacesSummary=!0===r.replacesSummary,n}(C,_.complete)
3612
- if(_.complete?.afterRun){const e=(0,b.latestRun)(t.currentSessionId),r=e?await e:null
3613
- if(r&&!r.ok)return(0,y.withAlchemyKind)({content:[{type:"text",text:`Not completed: the run it waited on did not pass (${r.detail}). Its result is in your transcript: fix what it shows, run the check again, and complete again.`}],isError:!0},v.complete)}const r={},o=await(0,n.handleCompleteRequest)({...e,refsSeen:r},t,k,I,$,E),i=(0,s.namedRefsNote)(r.namedRefs)
3614
- return i&&"text"===o.content?.[0]?.type&&(o.content[0].text+=i),(0,y.withAlchemyKind)(o,v.complete)}case"merge":return x.info("Routing to merge handler"),(0,y.withAlchemyKind)(await(0,m.handleMergeRequest)({requestId:C,verifications:_.merge?.verifications||[]},t,k,I,$,E),v.merge)
3615
- case"revert":{x.info("Routing to revert-request handler")
3616
- const e={requestId:C,reason:_.revert.reason,confirmDestructive:_.revert.confirmDestructive}
3617
- return(0,y.withAlchemyKind)(await(0,o.handleRevertRequest)(e,t,k,I,$),v.revert)}case"reopen":{x.info("Routing to reopen handler")
3633
+ return n.requestId=e,n.replacesSummary=!0===r.replacesSummary,n}(C,x.complete)
3634
+ if(x.complete?.afterRun){const e=(0,b.latestRun)(t.currentSessionId),r=e?await e:null
3635
+ if(r&&!r.ok)return(0,y.withAlchemyKind)({content:[{type:"text",text:`Not completed: the run it waited on did not pass (${r.detail}). Its result is in your transcript: fix what it shows, run the check again, and complete again.`}],isError:!0},v.complete)}const r={},o=await(0,n.handleCompleteRequest)({...e,refsSeen:r},t,k,I,$,S),i=(0,s.namedRefsNote)(r.namedRefs)
3636
+ return i&&"text"===o.content?.[0]?.type&&(o.content[0].text+=i),(0,y.withAlchemyKind)(o,v.complete)}case"merge":return _.info("Routing to merge handler"),(0,y.withAlchemyKind)(await(0,m.handleMergeRequest)({requestId:C,verifications:x.merge?.verifications||[]},t,k,I,$,S),v.merge)
3637
+ case"revert":{_.info("Routing to revert-request handler")
3638
+ const e={requestId:C,reason:x.revert.reason,confirmDestructive:x.revert.confirmDestructive}
3639
+ return(0,y.withAlchemyKind)(await(0,o.handleRevertRequest)(e,t,k,I,$),v.revert)}case"reopen":{_.info("Routing to reopen handler")
3618
3640
  const e=!!t.currentSessionId&&/^ses_/.test(t.currentSessionId)
3619
3641
  let r=null
3620
3642
  if(e&&C){const e=await async function(e,t,r){if(!t)return[]
3621
3643
  try{const n=await e.proxyGet(`/api/projects/${t}/session-messages?requestId=${encodeURIComponent(r)}&messageType=question&status=pending&limit=20`),o=Array.isArray(n?.data)?n.data:Array.isArray(n)?n:[]
3622
3644
  return[...new Set(o.map(e=>String(e?.kind??"question")).filter(e=>"opening-brief"!==e))]}catch{return[]}}(k,t.projectId,C)
3623
- if(e.length>0){const n="string"==typeof _.userSaid?_.userSaid:"",o=!!n&&await(0,a.userSaidOnRequest)(k,t.projectId,C,n,{orUnanchored:!0})
3624
- if(o&&(r=n.trim()),!o)return{content:[{type:"text",text:`\u26d4 Not reopened: ${C} is waiting on the user's answer (${e.join(", ")}), and reopening it would take that decision from them. `+(n?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":"Leave it to their answer. If they already told you to rework it, pass their words in userSaid (quoted exactly) and it goes through.")}],isError:!0}}}const n=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/reopen`,{reason:_.reason??"",...e?{by:"agent"}:{}}),o=n?.data??n,s=o?.data??o
3625
- if(s?.reopened&&r)try{await(0,a.recordUserWordDecision)(k,{fromSessionId:t.currentSessionId,requestId:C,question:`What happens to ${C} now?`,outcome:"Back to rework",quote:r})}catch(e){x.warn("reopen decision not recorded (the request is reopened)",{requestId:C,error:(0,g.getErrorMessage)(e)})}return s?.reopened?(0,y.withAlchemyKind)({content:[{type:"text",text:`Reopened ${C} for rework: it is back in progress${s.coderNotified?" and the working coder has been told why":s.workerStarted?", and since no coder was still on it, one has been asked to pick it up":" (nobody is on it: no live coder, and a worker could not be started)"}. Route the change to the coder, or create a task on it.`}]},v.update):(0,y.withAlchemyKind)({content:[{type:"text",text:`Could not reopen ${C}: ${o?.error??"unknown error"}`}],isError:!0},v.update)}case"get-info":{x.info("Routing to get-request-information handler")
3645
+ if(e.length>0){const n="string"==typeof x.userSaid?x.userSaid:"",o=!!n&&await(0,a.userSaidOnRequest)(k,t.projectId,C,n,{orUnanchored:!0})
3646
+ if(o&&(r=n.trim()),!o)return{content:[{type:"text",text:`\u26d4 Not reopened: ${C} is waiting on the user's answer (${e.join(", ")}), and reopening it would take that decision from them. `+(n?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":"Leave it to their answer. If they already told you to rework it, pass their words in userSaid (quoted exactly) and it goes through.")}],isError:!0}}}const n=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/reopen`,{reason:x.reason??"",...e?{by:"agent"}:{}}),o=n?.data??n,s=o?.data??o
3647
+ if(s?.reopened&&r)try{await(0,a.recordUserWordDecision)(k,{fromSessionId:t.currentSessionId,requestId:C,question:`What happens to ${C} now?`,outcome:"Back to rework",quote:r})}catch(e){_.warn("reopen decision not recorded (the request is reopened)",{requestId:C,error:(0,g.getErrorMessage)(e)})}return s?.reopened?(0,y.withAlchemyKind)({content:[{type:"text",text:`Reopened ${C} for rework: it is back in progress${s.coderNotified?" and the working coder has been told why":s.workerStarted?", and since no coder was still on it, one has been asked to pick it up":" (nobody is on it: no live coder, and a worker could not be started)"}. Route the change to the coder, or create a task on it.`}]},v.update):(0,y.withAlchemyKind)({content:[{type:"text",text:`Could not reopen ${C}: ${o?.error??"unknown error"}`}],isError:!0},v.update)}case"get-info":{_.info("Routing to get-request-information handler")
3626
3648
  const e={requestId:C}
3627
- return(0,y.withAlchemyKind)(await(0,i.handleGetRequestInformation)(e,t,k,I,$),v["get-info"])}case"list":{x.info("Routing to list-requests handler")
3628
- const e={filters:_.list?.filters}
3629
- return(0,y.withAlchemyKind)(await(0,l.handleListRequests)(e,t,k,I,$),v.list)}case"list-areas":return x.info("Routing to list-areas handler"),(0,y.withAlchemyKind)(await(0,u.handleListAreas)(t,k,I,$),v["list-areas"])
3630
- case"wait-on":case"clear-wait":return x.info("Routing to request-wait handler",{operation:q}),(0,y.withAlchemyKind)(await(0,p.handleWaitOn)({requestId:C,waitsOn:_.waitsOn,reason:_.reason,clear:"clear-wait"===q},t,k,I,$),v[q])
3631
- case"update-areas":return x.info("Routing to update-areas handler"),(0,y.withAlchemyKind)(await(0,h.handleUpdateAreas)({requestId:C,areas:_.areas},t,k,I,$),v["update-areas"])
3632
- case"search":{x.info("Routing to search-requests handler")
3633
- const e={search:_.search}
3649
+ return(0,y.withAlchemyKind)(await(0,i.handleGetRequestInformation)(e,t,k,I,$),v["get-info"])}case"list":{_.info("Routing to list-requests handler")
3650
+ const e={filters:x.list?.filters}
3651
+ return(0,y.withAlchemyKind)(await(0,l.handleListRequests)(e,t,k,I,$),v.list)}case"list-areas":return _.info("Routing to list-areas handler"),(0,y.withAlchemyKind)(await(0,u.handleListAreas)(t,k,I,$),v["list-areas"])
3652
+ case"wait-on":case"clear-wait":return _.info("Routing to request-wait handler",{operation:q}),(0,y.withAlchemyKind)(await(0,p.handleWaitOn)({requestId:C,waitsOn:x.waitsOn,reason:x.reason,clear:"clear-wait"===q},t,k,I,$),v[q])
3653
+ case"update-areas":return _.info("Routing to update-areas handler"),(0,y.withAlchemyKind)(await(0,h.handleUpdateAreas)({requestId:C,areas:x.areas},t,k,I,$),v["update-areas"])
3654
+ case"search":{_.info("Routing to search-requests handler")
3655
+ const e={search:x.search}
3634
3656
  return(0,y.withAlchemyKind)(await(0,d.handleSearchRequests)(e,t,k,I,$),v.search)}case"cancel":if(!C)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** requestId is required for cancel"}],isError:!0}
3635
3657
  {let e=null
3636
3658
  try{e=(await k.getRequestClient().getRequest(C))?.status??null}catch{}if("completed"===e||"canceled"===e||"reverted"===e)return{content:[{type:"text",text:"completed"===e?`\u26d4 Not canceled: ${C} has already merged, so there is nothing left to cancel. What undoes it is a revert (request_operations revert, with the reason), or, when later work built on it and the revert is refused, a follow-up request that removes the change: if the user's words say they want it gone, do that and tell them what you did; if that is not clear, ask them. Never leave them with only "it cannot be canceled".`:`\u26d4 Not canceled: ${C} is already ${e}. Nothing to do; tell the user.`}],isError:!0}
3637
- if(e&&c.has(e)&&t.currentSessionId&&/^ses_/.test(t.currentSessionId)){const r="string"==typeof _.userSaid?_.userSaid:""
3638
- if(!r||!await(0,a.userSaidOnRequest)(k,t.projectId,C,r,{orUnanchored:!0}))return{content:[{type:"text",text:`\u26d4 Not canceled: ${C} is ${e.replace(/_/g," ")}, so its work is finished and waiting on the user. Throwing it away is their call. `+(r?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":"Ask them, naming what would be lost. If they already asked for it, pass their words in userSaid (quoted exactly) and it goes through.")}],isError:!0}}}try{const e="string"==typeof t.currentSessionId&&/^ses_/.test(t.currentSessionId)?t.currentSessionId:void 0,r="string"==typeof _.userSaid?_.userSaid.trim():"",n=!!r&&await(0,a.userSaidOnRequest)(k,t.projectId,C,r,{orUnanchored:!0}).catch(()=>!1),o=await k.getProxyClient().proxyPatch(`/api/projects/${t.projectId}/requests/${C}`,{status:"canceled",..._.reason?{completionSummary:_.reason}:{},...e?{endedBy:{sessionId:e,...n?{userSaid:r}:{}}}:{}})
3639
- return x.info("Request canceled",{requestId:C}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${C} is CANCELED as a record; nothing will start it again.${(0,s.namedRefsNote)((o?.data??o)?.namedRefs)}`}]},v.cancel)}catch(e){return{content:[{type:"text",text:`\u274c **Failed to cancel request:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}case"hand-over":{if(!C)return{content:[{type:"text",text:"\u274c hand-over needs requestId: the request whose worker takes the message."}],isError:!0}
3659
+ if(e&&c.has(e)&&t.currentSessionId&&/^ses_/.test(t.currentSessionId)){const r="string"==typeof x.userSaid?x.userSaid:""
3660
+ if(!r||!await(0,a.userSaidOnRequest)(k,t.projectId,C,r,{orUnanchored:!0}))return{content:[{type:"text",text:`\u26d4 Not canceled: ${C} is ${e.replace(/_/g," ")}, so its work is finished and waiting on the user. Throwing it away is their call. `+(r?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":"Ask them, naming what would be lost. If they already asked for it, pass their words in userSaid (quoted exactly) and it goes through.")}],isError:!0}}}try{const e="string"==typeof t.currentSessionId&&/^ses_/.test(t.currentSessionId)?t.currentSessionId:void 0,r="string"==typeof x.userSaid?x.userSaid.trim():"",n=!!r&&await(0,a.userSaidOnRequest)(k,t.projectId,C,r,{orUnanchored:!0}).catch(()=>!1),o=await k.getProxyClient().proxyPatch(`/api/projects/${t.projectId}/requests/${C}`,{status:"canceled",...x.reason?{completionSummary:x.reason}:{},...e?{endedBy:{sessionId:e,...n?{userSaid:r}:{}}}:{}})
3661
+ return _.info("Request canceled",{requestId:C}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${C} is CANCELED as a record; nothing will start it again.${(0,s.namedRefsNote)((o?.data??o)?.namedRefs)}`}]},v.cancel)}catch(e){return{content:[{type:"text",text:`\u274c **Failed to cancel request:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}case"hand-over":{if(!C)return{content:[{type:"text",text:"\u274c hand-over needs requestId: the request whose worker takes the message."}],isError:!0}
3640
3662
  if("1"===process.env.ALCHEMY_HANDOVER_OFF)return{content:[{type:"text",text:"Not handed over: hand-over is off in this project. File it as usual."}],isError:!0}
3641
- const e=await(0,w.userTextOfCall)(t.mainBasePath,S,"string"==typeof _.userSaid?_.userSaid:void 0)
3663
+ const e=await(0,w.userTextOfCall)(t.mainBasePath,E,"string"==typeof x.userSaid?x.userSaid:void 0)
3642
3664
  if(!e)return{content:[{type:"text",text:"Not handed over: the user's message this turn answers could not be read. File it as usual."}],isError:!0}
3643
3665
  try{const r=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/inbox-chat`,{text:e,anchor:{type:"request",id:C},handedOverBy:"alchemist"}),n=r?.data??r
3644
- return n?.delivered?(0,y.withAlchemyKind)({content:[{type:"text",text:`Handed to the ${n?.speaker??"worker"} on ${C}, with the user's words as they wrote them. It answers them itself, on that request's thread. End your turn with one line saying so and where they can follow it; do not do or file the work yourself.`}]},v["hand-over"]):(0,y.withAlchemyKind)({content:[{type:"text",text:`Not handed over: ${n?.reason??"the worker could not take it"}. File it as usual.`}]},v["hand-over"])}catch(e){return{content:[{type:"text",text:`Not handed over: ${(0,g.getErrorMessage)(e)}. File it as usual.`}],isError:!0}}}case"asked-no-change":{const e="string"==typeof _.userSaid?_.userSaid.trim():""
3666
+ return n?.delivered?(0,y.withAlchemyKind)({content:[{type:"text",text:`Handed to the ${n?.speaker??"worker"} on ${C}, with the user's words as they wrote them. It answers them itself, on that request's thread. End your turn with one line saying so and where they can follow it; do not do or file the work yourself.`}]},v["hand-over"]):(0,y.withAlchemyKind)({content:[{type:"text",text:`Not handed over: ${n?.reason??"the worker could not take it"}. File it as usual.`}]},v["hand-over"])}catch(e){return{content:[{type:"text",text:`Not handed over: ${(0,g.getErrorMessage)(e)}. File it as usual.`}],isError:!0}}}case"asked-no-change":{const e="string"==typeof x.userSaid?x.userSaid.trim():""
3645
3667
  if(!C||!e)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** asked-no-change needs requestId and userSaid (the user's message, quoted exactly)"}],isError:!0}
3646
3668
  try{const r=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/steers/asked-no-change`,{userSaid:e}),n=r?.data??r,o=n?.marked?`Recorded: that message on ${C} asked for no change, so it is not listed as one.`:String(n?.reason??"Nothing to record.")
3647
- return(0,y.withAlchemyKind)({content:[{type:"text",text:o}]},v["asked-no-change"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}}case"dispute-criterion":{const e="string"==typeof _.criterion?_.criterion.trim():""
3648
- if(!C||!e||!_.reason?.trim())return{content:[{type:"text",text:"\u274c **Request Operations Failed:** dispute-criterion needs requestId, criterion (its key, e.g. 1a) and reason (why it does not fit this request, for the user to read)"}],isError:!0}
3649
- try{const r=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/criteria/${encodeURIComponent(e)}/dispute`,{reason:_.reason,sessionId:t.currentSessionId}),n=r?.data??r,o={asked:`The user has a card: waive ${n?.key??e} for ${C}, or keep it. Carry on with the rest of the work; you are told the answer. Until it is waived, the criterion still binds.`,"already-asked":`The user already has a card for ${n?.key??e} on ${C}; you are told the answer. Until it is waived, the criterion still binds.`,waived:`${n?.key??e} is already waived for ${C}; it no longer binds.`,kept:`The user already chose to keep ${n?.key??e} on ${C}: it applies here. Satisfy it.`}
3669
+ return(0,y.withAlchemyKind)({content:[{type:"text",text:o}]},v["asked-no-change"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}}case"dispute-criterion":{const e="string"==typeof x.criterion?x.criterion.trim():""
3670
+ if(!C||!e||!x.reason?.trim())return{content:[{type:"text",text:"\u274c **Request Operations Failed:** dispute-criterion needs requestId, criterion (its key, e.g. 1a) and reason (why it does not fit this request, for the user to read)"}],isError:!0}
3671
+ try{const r=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/criteria/${encodeURIComponent(e)}/dispute`,{reason:x.reason,sessionId:t.currentSessionId}),n=r?.data??r,o={asked:`The user has a card: waive ${n?.key??e} for ${C}, or keep it. Carry on with the rest of the work; you are told the answer. Until it is waived, the criterion still binds.`,"already-asked":`The user already has a card for ${n?.key??e} on ${C}; you are told the answer. Until it is waived, the criterion still binds.`,waived:`${n?.key??e} is already waived for ${C}; it no longer binds.`,kept:`The user already chose to keep ${n?.key??e} on ${C}: it applies here. Satisfy it.`}
3650
3672
  return(0,y.withAlchemyKind)({content:[{type:"text",text:o[n?.status]??"Dispute recorded."}]},v["dispute-criterion"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}}case"premise-block":if(!C)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** requestId is required for premise-block"}],isError:!0}
3651
- if(!_.reason||!_.reason.trim())return{content:[{type:"text",text:"\u274c **Request Operations Failed:** reason is required for premise-block -- name exactly what the request references that does not exist"}],isError:!0}
3652
- {const e=(0,r.existingPathsNamedIn)(_.reason,process.cwd())
3653
- if(e.length>0)return{content:[{type:"text",text:`\u26a0\ufe0f Premise block refused: ${e.join(", ")} exist${1===e.length?"s":""} in this project. Re-read the request against the actual files; if the premise still fails for a different reason, name that reason instead. Nothing was changed.`}],isError:!0}}try{const e=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/premise-block`,{reason:_.reason,fromSessionId:t.currentSessionId}),r=e?.data??e
3654
- return!1===r?.blocked?(0,y.withAlchemyKind)({content:[{type:"text",text:`\u26a0\ufe0f Premise block refused: ${r?.reason??"the request is not open"}. Nothing changed.`}]},v["premise-block"]):(x.info("Request premise-blocked",{requestId:C}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${C} is back in DRAFTS and the user has been asked (reword / start as written / cancel). Do not start it or report further -- the question is the record, and the user's answer will come back through the system.`}]},v["premise-block"]))}catch(e){return{content:[{type:"text",text:`\u274c **Failed to premise-block request:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}case"delete-empty":if(x.info("Routing to delete-empty handler"),!C)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** requestId is required for delete-empty"}],isError:!0}
3673
+ if(!x.reason||!x.reason.trim())return{content:[{type:"text",text:"\u274c **Request Operations Failed:** reason is required for premise-block -- name exactly what the request references that does not exist"}],isError:!0}
3674
+ {const e=(0,r.existingPathsNamedIn)(x.reason,process.cwd())
3675
+ if(e.length>0)return{content:[{type:"text",text:`\u26a0\ufe0f Premise block refused: ${e.join(", ")} exist${1===e.length?"s":""} in this project. Re-read the request against the actual files; if the premise still fails for a different reason, name that reason instead. Nothing was changed.`}],isError:!0}}try{const e=await k.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${C}/premise-block`,{reason:x.reason,fromSessionId:t.currentSessionId}),r=e?.data??e
3676
+ return!1===r?.blocked?(0,y.withAlchemyKind)({content:[{type:"text",text:`\u26a0\ufe0f Premise block refused: ${r?.reason??"the request is not open"}. Nothing changed.`}]},v["premise-block"]):(_.info("Request premise-blocked",{requestId:C}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${C} is back in DRAFTS and the user has been asked (reword / start as written / cancel). Do not start it or report further -- the question is the record, and the user's answer will come back through the system.`}]},v["premise-block"]))}catch(e){return{content:[{type:"text",text:`\u274c **Failed to premise-block request:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}case"delete-empty":if(_.info("Routing to delete-empty handler"),!C)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** requestId is required for delete-empty"}],isError:!0}
3655
3677
  try{const e=k.getProxyClient(),r=await e.proxyGet(`/api/projects/${t.projectId}/tasks`,{requestId:C,limit:1}),n=r?.data||r||[]
3656
- return(Array.isArray(n)?n.length:0)>0?{content:[{type:"text",text:`\u274c Cannot delete ${C}: it has tasks. Only empty requests (no tasks created) can be deleted.`}],isError:!0}:(await e.proxyDelete(`/api/projects/${t.projectId}/requests/${C}`),x.info("Empty request deleted",{requestId:C}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${C} deleted (had no tasks).`}]},v["delete-empty"]))}catch(e){return{content:[{type:"text",text:`\u274c **Failed to delete request:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}default:{const e=`Unknown operation type: ${q}`
3657
- return x.error(e),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${e}`}],isError:!0}}}}catch(e){return x.error("Request operations failed",{error:e instanceof Error?e.stack:String(e)}),$.incrementCounter("request_operations_errors",1),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}}
3678
+ return(Array.isArray(n)?n.length:0)>0?{content:[{type:"text",text:`\u274c Cannot delete ${C}: it has tasks. Only empty requests (no tasks created) can be deleted.`}],isError:!0}:(await e.proxyDelete(`/api/projects/${t.projectId}/requests/${C}`),_.info("Empty request deleted",{requestId:C}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${C} deleted (had no tasks).`}]},v["delete-empty"]))}catch(e){return{content:[{type:"text",text:`\u274c **Failed to delete request:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}default:{const e=`Unknown operation type: ${q}`
3679
+ return _.error(e),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${e}`}],isError:!0}}}}catch(e){return _.error("Request operations failed",{error:e instanceof Error?e.stack:String(e)}),$.incrementCounter("request_operations_errors",1),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,g.getErrorMessage)(e)}`}],isError:!0}}}
3658
3680
  const r=t(95994),n=t(15715),o=t(16416),s=t(19670),i=t(5434),a=t(29729),c=new Set(["ready_for_review","ready_for_merge","resolving_conflicts","conflicted"]),l=t(98037),d=t(80233),u=t(49229),h=t(23130),p=t(72271),f=t(36651),m=t(34306),g=t(58824),y=t(82272),w=t(62032),b=t(12286),v={update:"update-request",activate:"activate-request",complete:"complete-request",merge:"finalize-request",revert:"revert-request","get-info":"get-request-info",list:"list-requests",search:"search-requests","list-areas":"list-areas","update-areas":"update-areas","delete-empty":"delete-empty-request",cancel:"cancel-request","premise-block":"premise-block-request","dispute-criterion":"dispute-criterion","asked-no-change":"asked-no-change","hand-over":"hand-over","wait-on":"declare-request-wait","clear-wait":"clear-request-wait"}},50129(e,exports){exports.requestPage=async function(e,t){return"function"==typeof e.listRequestsPage?e.listRequestsPage(t):{requests:await e.listRequests(t),total:null}},exports.countWords=function(e,t,r="request"){const n=e=>`${r}${1===e?"":"s"}`
3659
3681
  return null!==t&&t>e?`${e} of ${t} ${n(t)}`:`${e} ${n(e)}`},exports.statusParams=function(e){return e&&"undefined"!==e?"in_flight"===e?{inFlight:!0}:{status:e}:{}},exports.dateParams=function(e,t){const r={},n=e=>/^\d{4}-\d{2}-\d{2}$/.test(e)
3660
3682
  if(e){const t=String(e)
@@ -3723,14 +3745,14 @@ const k=[],I=c.toLowerCase()
3723
3745
  for(const e of b){const t=[]
3724
3746
  if("all"!==d&&"title"!==d||!e.title||e.title.toLowerCase().includes(I)&&t.push({request:e,matchType:"title",matchSnippet:r(e.title,c)}),"all"!==d&&"description"!==d||!e.description||e.description.toLowerCase().includes(I)&&t.push({request:e,matchType:"description",matchSnippet:n(e.description,I,150)}),("all"===d||"criteria"===d)&&e.acceptanceCriteria)for(const r of e.acceptanceCriteria){const o="string"==typeof r?r:r.description
3725
3747
  if(o&&o.toLowerCase().includes(I)){t.push({request:e,matchType:"criteria",matchSnippet:n(o,I,150)})
3726
- break}}0===t.length&&"all"===d&&String(e.id??"").toLowerCase().includes(I)&&t.push({request:e,matchType:"id"}),t.length>0&&k.push(t[0])}const $=k.slice(0,g),E="all"===d&&k.length===b.length?v:null
3748
+ break}}0===t.length&&"all"===d&&String(e.id??"").toLowerCase().includes(I)&&t.push({request:e,matchType:"id"}),t.length>0&&k.push(t[0])}const $=k.slice(0,g),S="all"===d&&k.length===b.length?v:null
3727
3749
  if(0===$.length)return p({success:!0,resultCount:0}),{content:[{type:"text",text:"all"!==d&&v?`No requests found matching "${c}" in ${d}. ${v} match it in another field (title, description, criteria or id): search with searchIn "all" to see them.`:`No requests found matching "${c}" (every request searched, drafts aside).`}]}
3728
- let S=`Found ${(0,l.countWords)($.length,E)} matching "${c}"${"all"===d?"":` in ${d}`}:\n\n`
3750
+ let E=`Found ${(0,l.countWords)($.length,S)} matching "${c}"${"all"===d?"":` in ${d}`}:\n\n`
3729
3751
  for(const e of $){const t=e.request,r=o(t.status)
3730
- S+=`${s(t.type)} **${t.title}** (${t.id}) ${r}\n`,S+=` \u{1f4cd} Match in ${e.matchType}`,e.matchSnippet&&(S+=`: ${e.matchSnippet}`),S+="\n\n"}const x=Number(y.offset)||0
3731
- "all"===d&&null!==v&&v>x+b.length?S+=`More ${null!==E?"match":"may match"}: pass offset ${x+b.length} for the next page.\n\n`:"all"!==d&&null!==v&&v>b.length&&(S+=`Picked from the ${b.length} most recently active of ${v} requests matching anywhere; narrow the search to reach the rest.\n\n`),S+="---\nSearched every request, drafts aside (filtered by: "
3732
- const _=[]
3733
- return f.status&&_.push(`status=${f.status}`),f.type&&_.push(`type=${f.type}`),f.dateFrom&&_.push(`from=${f.dateFrom}`),f.dateTo&&_.push(`to=${f.dateTo}`),S+=_.length>0?_.join(", "):"none",S+=")",p({success:!0,resultCount:$.length}),u.incrementCounter("search_requests_success",1),{content:[{type:"text",text:S}]}}catch(e){return h.error("Search requests failed",{error:e instanceof Error?e.stack:String(e)}),p({success:!1,error:(0,c.getErrorMessage)(e)}),u.incrementCounter("search_requests_errors",1),{content:[{type:"text",text:`\u274c **Search Failed:** ${(0,c.getErrorMessage)(e)}`}],isError:!0}}}
3752
+ E+=`${s(t.type)} **${t.title}** (${t.id}) ${r}\n`,E+=` \u{1f4cd} Match in ${e.matchType}`,e.matchSnippet&&(E+=`: ${e.matchSnippet}`),E+="\n\n"}const _=Number(y.offset)||0
3753
+ "all"===d&&null!==v&&v>_+b.length?E+=`More ${null!==S?"match":"may match"}: pass offset ${_+b.length} for the next page.\n\n`:"all"!==d&&null!==v&&v>b.length&&(E+=`Picked from the ${b.length} most recently active of ${v} requests matching anywhere; narrow the search to reach the rest.\n\n`),E+="---\nSearched every request, drafts aside (filtered by: "
3754
+ const x=[]
3755
+ return f.status&&x.push(`status=${f.status}`),f.type&&x.push(`type=${f.type}`),f.dateFrom&&x.push(`from=${f.dateFrom}`),f.dateTo&&x.push(`to=${f.dateTo}`),E+=x.length>0?x.join(", "):"none",E+=")",p({success:!0,resultCount:$.length}),u.incrementCounter("search_requests_success",1),{content:[{type:"text",text:E}]}}catch(e){return h.error("Search requests failed",{error:e instanceof Error?e.stack:String(e)}),p({success:!1,error:(0,c.getErrorMessage)(e)}),u.incrementCounter("search_requests_errors",1),{content:[{type:"text",text:`\u274c **Search Failed:** ${(0,c.getErrorMessage)(e)}`}],isError:!0}}}
3734
3756
  const i=t(87780),a=t(59860),c=t(58824),l=t(50129)},23130(e,exports,t){exports.handleUpdateAreas=async function(e,t,o,s,i){const a=s.child({tool:"update_areas"}),c=i.startTimer("tool_execution_time")
3735
3757
  try{const n=t.projectId
3736
3758
  if(!n)throw new r.ValidationError("Project ID is required for updating areas")
@@ -3767,8 +3789,8 @@ if(o.length>0)return{content:[{type:"text",text:`Refused: this update would drop
3767
3789
  try{const e=await d.proxyPatch(`/api/projects/${t.projectId}/requests/${p}/arc-name`,{name:w.trim(),namedBy:"hub"}),r=e?.data??e
3768
3790
  return!1===r?.changed?` The thread keeps the name the user gave it ("${r?.arcName}").`:` The thread is now called "${r?.arcName}".`}catch(e){return` The thread name could not be set (${(0,s.getErrorMessage)(e)}).`}}
3769
3791
  if(0===Object.keys(v).length)return k?{content:[{type:"text",text:`Nothing else to update on ${p}.${await I()}`}]}:{content:[{type:"text",text:"Error: nothing to update. Provide title, description, type, process, acceptanceCriteria and/or arcName."}],isError:!0}
3770
- const $=t?.currentSessionId,E=/^ses_/.test($??"")&&l.SHAPING_AGENTS.has(String(await a.sessionIdentity.getAgentTypeFor($)))
3771
- if(E){const e=(0,l.lastRequestRead)($,p)
3792
+ const $=t?.currentSessionId,S=/^ses_/.test($??"")&&l.SHAPING_AGENTS.has(String(await a.sessionIdentity.getAgentTypeFor($)))
3793
+ if(S){const e=(0,l.lastRequestRead)($,p)
3772
3794
  if(e)v.ifUnchangedSince=e
3773
3795
  else{let e=null
3774
3796
  try{e=await d.getRequestClient().getRequest(p)}catch{}const t=String(e?.updatedAt??"")
@@ -3779,10 +3801,10 @@ try{e=await d.getRequestClient().getRequest(p)}catch{}if(e&&"draft"!==String(e.s
3779
3801
  let o=b
3780
3802
  !o&&Array.isArray(v.acceptanceCriteria)&&"string"==typeof e.userSaid&&e.userSaid.trim()&&await r(d,t?.projectId,p,e.userSaid,{orUnanchored:!0}).catch(()=>!1)&&(o=e.userSaid.trim())
3781
3803
  const s=await n.updateRequest(p,{...v,...o&&Array.isArray(v.acceptanceCriteria)?{userSaid:o}:{},..."string"==typeof $&&$?{actingSessionId:$}:{}})
3782
- delete v.ifUnchangedSince,delete v.criteriaBase,E&&(0,l.noteRequestRead)($,p,(s?.data??s)?.updatedAt),(0,l.noteCriteriaRead)($,p,(s?.data??s)?.criteriaVersion)
3804
+ delete v.ifUnchangedSince,delete v.criteriaBase,S&&(0,l.noteRequestRead)($,p,(s?.data??s)?.updatedAt),(0,l.noteCriteriaRead)($,p,(s?.data??s)?.criteriaVersion)
3783
3805
  const i=await I(),a=Object.keys(v).join(", "),c=(s?.data??s)?.processHint,u="string"==typeof c&&c?` ${c}`:""
3784
3806
  return h.info("Updated request fields",{requestId:p,fields:a}),{content:[{type:"text",text:`Updated request ${p} (${a}).${i}${u}`}]}}catch(e){const t=409===e?.status?e?.errorDetails?.details:null
3785
- return t?.criteriaVersion?((0,l.noteCriteriaRead)($,p,t.criteriaVersion),{content:[{type:"text",text:(0,s.getErrorMessage)(e)}],isError:!0}):E&&t?.updatedAt?((0,l.noteRequestRead)($,p,t.updatedAt),{content:[{type:"text",text:(0,s.getErrorMessage)(e)}],isError:!0}):(h.error("Failed to update request",{requestId:p,error:(0,s.getErrorMessage)(e)}),{content:[{type:"text",text:`Error updating request ${p}: ${(0,s.getErrorMessage)(e)}`}],isError:!0})}}
3807
+ return t?.criteriaVersion?((0,l.noteCriteriaRead)($,p,t.criteriaVersion),{content:[{type:"text",text:(0,s.getErrorMessage)(e)}],isError:!0}):S&&t?.updatedAt?((0,l.noteRequestRead)($,p,t.updatedAt),{content:[{type:"text",text:(0,s.getErrorMessage)(e)}],isError:!0}):(h.error("Failed to update request",{requestId:p,error:(0,s.getErrorMessage)(e)}),{content:[{type:"text",text:`Error updating request ${p}: ${(0,s.getErrorMessage)(e)}`}],isError:!0})}}
3786
3808
  const s=t(58824),i=t(65454),a=t(6688),c=t(57115),l=t(12978)
3787
3809
  exports.uZ='pass their words in userSaid: a message of theirs quoted exactly, or an answer they gave on a card cited by its code (`Q-...#section` for a brief question; get-info lists them under "The user\'s answers")'},72271(e,exports,t){exports.handleWaitOn=async function(e,t,o,s,i){const a=s.child?s.child({tool:"wait-on"}):s,c=i.startTimer("tool_execution_duration"),l=t.projectId
3788
3810
  try{if(!l)throw new r.ValidationError("No project selected")
@@ -3799,7 +3821,7 @@ const n=await o.proxyPost(`/api/projects/${l}/requests/${e.requestId}/waits`,{wa
3799
3821
  return c({operation:"wait-on",success:!0}),{content:[{type:"text",text:d?`\u2705 Recorded: ${e.requestId} is waiting on ${e.waitsOn}. It now shows on the request itself and clears when ${e.waitsOn} merges.`:`\u2705 Recorded, but ${e.waitsOn} is already finished, so ${e.requestId} is not held by it.`+(i.length?` Still waiting on ${i.map(e=>e?.waitsOn?.localId).filter(Boolean).join(", ")}.`:" Nothing is blocking this request.")}]}}catch(t){return c({operation:e.clear?"clear-wait":"wait-on",success:!1}),a.error("wait-on failed",{error:(0,n.getErrorMessage)(t)}),{content:[{type:"text",text:`Error: ${(0,n.getErrorMessage)(t)}`}],isError:!0}}}
3800
3822
  const r=t(59860),n=t(58824)},5169(e,exports,t){exports.resultDefs=void 0
3801
3823
  const r=t(76183),n=t(50441),o=t(14079),s=e=>t=>(0,n.handleResults)(t,e.context,e.apiClient,e.logger,e.metrics,e.cliOrigin,t=>(0,o.handleWriteFile)(t,e.context,e.apiClient.getFileClient(),e.apiClient.getTaskClient(),e.apiClient.getToolRecordClient(),e.apiClient.getRequestClient(),e.logger,e.metrics,e.mcpServer))
3802
- exports.resultDefs=[{name:"results",description:"Record work that does not fit in git: simulation results, model checkpoints, large tables, renders, sequence data. The bytes are stored beside the repository and content-addressed; what you get back is an address, a size, and the small summary a later reader gets INSTEAD of the bytes.\n\nOperations:\n- record - store a file you produced, with a summary (required: it is what makes the result referable without being read)\n- fetch - put a stored result BACK ON DISK so tar, a solver, a script or any real tool can work on it. This is how you get at a recorded archive: read only ever gives you a bounded window. Two destinations: the host, under `.alchemy_lab/scratch/` (gitignored, and the faster option), or INSIDE a sealed box with `box` (its own /scratch, on disk and removed with the box) when the work must stay confined.\n- pin - write a small pointer file into your worktree and commit it, so a branch, a merge and a review carry the result without the repository holding it\n- list / read - also available here; see result_inspect for what they do\n\nUse this when a run produces something too large or too opaque to check in a diff. Ordinary source files belong in git as usual.\n\nA file a run left OUTSIDE your worktree can still be recorded: `record` looks in your worktree first and then in the main project, so pass the path you were given even when your other tools cannot see the file.\n\nTHIS IS WHERE BULK DATA LIVES. You work in a git worktree, which holds TRACKED files only. A previous run's output is untracked by nature, so `search` and the file readers will not find it and its absence there means nothing. If the data was recorded, `list` finds it and `read` reaches it by address from ANY worktree. Look here before concluding that input data is missing.",schema:r.ResultsSchema,annotations:{title:"Results",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},createsRecords:!0,handler:(e,t)=>s(t)(e)},{name:"result_inspect",description:"Read what has been recorded as a result, without loading it.\n\nOperations:\n- list - what this project has recorded, or every version under one name (newest first)\n- read - a range of lines, or the lines matching some text, from a stored result\n\nReading is bounded on purpose: a large result reached this way still cannot fill a context window. Use it to check a claim about a result against the result itself rather than against its summary.\n\nIt also reaches bulk data your other tools cannot see. A worktree holds TRACKED files only, so a previous run's output is not in yours and never will be; `list` is how you find out whether it exists at all.",schema:r.ResultInspectSchema,annotations:{title:"Inspect Results",readOnlyHint:!0},handler:(e,t)=>s(t)(e)}]},50441(e,exports,t){function r(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1048576).toFixed(1)} MB`:`${(e/1073741824).toFixed(2)} GB`}function n(e,t){if(t===e)return!0
3824
+ exports.resultDefs=[{name:"results",description:"Record work that does not fit in git: simulation output, checkpoints, large tables, renders. The bytes are stored beside the repository by content address; you get back an address, a size and the small summary a later reader gets instead of the bytes. Ordinary source files belong in git.\n\nA worktree holds tracked files only, so a previous run's output is found here (list), never with search.\n\nOperations:\n- record - store a file you produced, with a summary (required). Looked for in your worktree, then the main project; with box, streamed out of that sealed box.\n- fetch - put a stored result back on disk so real tools (tar, a solver, a script) can work on it: under `.alchemy_lab/scratch/` on the host, or inside a box's /scratch with box.\n- pin - commit a small pointer file, so a branch, a merge and a review carry the result without the repository holding it.\n- list / read - what has been recorded; a bounded window into one (a line range, or the lines matching find).",schema:r.ResultsSchema,annotations:{title:"Results",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0},createsRecords:!0,handler:(e,t)=>s(t)(e)},{name:"result_inspect",description:"Read what has been recorded as a result, without loading it: list (what the project has recorded, or every version under one name, newest first) and read (a bounded line range, or the lines matching find). Check a claim about a result against the result, not its summary. A worktree holds tracked files only, so list is how you find a previous run's output.",schema:r.ResultInspectSchema,annotations:{title:"Inspect Results",readOnlyHint:!0},handler:(e,t)=>s(t)(e)}]},50441(e,exports,t){function r(e){return e<1024?`${e} B`:e<1048576?`${(e/1024).toFixed(1)} KB`:e<1073741824?`${(e/1048576).toFixed(1)} MB`:`${(e/1073741824).toFixed(2)} GB`}function n(e,t){if(t===e)return!0
3803
3825
  const r=(0,d.relative)(e,t)
3804
3826
  return""!==r&&!r.startsWith("..")&&!(0,d.isAbsolute)(r)}async function o(e,r,n,o){try{const{resolveRequestIdOrError:s}=await Promise.resolve().then(()=>t(36651)),i=o?.sessionId||n?.currentSessionId,a=await s(e,r,i,"no active request")
3805
3827
  if(a?.ok)return a.requestId}catch{}}function s(e){return!!e&&"object"==typeof e&&!Array.isArray(e)}async function i(e,t){if("string"!=typeof t||!/^[0-9a-f]+$/.test(t)||t.length<8)return{ok:!1,message:`"${t}" is not a result address. Give the address a record or a list showed you (at least 8 characters of it).`}
@@ -3830,10 +3852,10 @@ t&&(b={address:t.address,locator:(0,p.locatorFor)(t.address),bytes:t.bytes,dedup
3830
3852
  let I
3831
3853
  const $=await o(s,i,n,c)
3832
3854
  try{const t=await s.getToolRecordClient().createToolRecord({type:"record-result",projectId:n.projectId,...$?{requestId:$}:{},metadata:{name:w,address:b.address,locator:b.locator,bytes:b.bytes,sourcePath:e.path,fromBox:e.box,summary:e.summary,tree:{fileCount:y.fileCount,totalBytes:y.totalBytes,transferredBytes:y.transferredBytes}},note:e.note?.trim()||`Recorded ${w} out of sealed box ${e.box} (${r(y.totalBytes)})`})
3833
- I=t?.id}catch(e){i.warn("results: producing record failed (bytes unaffected)",{error:(0,u.getErrorMessage)(e)})}const E=await s.proxyPost(`/api/projects/${n.projectId}/results`,{address:b.address,locator:b.locator,bytes:b.bytes,name:w,mediaType:k,summary:{...e.summary??{},...v?{}:{tree:{files:y.fileCount,bytes:y.totalBytes,skippedLinks:y.skippedLinks}},extractedFrom:{box:e.box,path:e.path,transferredBytes:y.transferredBytes}},...I?{producedBy:I}:{},...e.note?{producerNote:e.note}:{},...Array.isArray(e.inputs)&&e.inputs.length?{inputAddresses:e.inputs}:{}})
3855
+ I=t?.id}catch(e){i.warn("results: producing record failed (bytes unaffected)",{error:(0,u.getErrorMessage)(e)})}const S=await s.proxyPost(`/api/projects/${n.projectId}/results`,{address:b.address,locator:b.locator,bytes:b.bytes,name:w,mediaType:k,summary:{...e.summary??{},...v?{}:{tree:{files:y.fileCount,bytes:y.totalBytes,skippedLinks:y.skippedLinks}},extractedFrom:{box:e.box,path:e.path,transferredBytes:y.transferredBytes}},...I?{producedBy:I}:{},...e.note?{producerNote:e.note}:{},...Array.isArray(e.inputs)&&e.inputs.length?{inputAddresses:e.inputs}:{}})
3834
3856
  l({success:!0})
3835
- const S=E?.data??E
3836
- return f([1===y.fileCount?`Took **${w}** out of sealed box \`${e.box}\`.`:`Took **${w}** out of sealed box \`${e.box}\` as a tree of ${y.fileCount.toLocaleString()} files.`,"",`- address: \`${b.address}\``,`- size: ${r(y.totalBytes)}`+(y.dedupedFiles?`, of which ${y.dedupedFiles.toLocaleString()} files (${r(y.dedupedBytes)}) were already stored`:""),`- crossed out of confinement: ${r(y.transferredBytes)} streamed`,...y.skippedLinks?[`- skipped ${y.skippedLinks} symlink(s): a link is a reference, not content`]:[],I?`- produced by: ${I}`:"",S?.id?`- reference: ${S.id}`:"","","The box still holds its own copy; this one is on the host, addressed and recorded.",1===y.fileCount?`To make it a file in the repository: write_file({ path, fromResult: "${b.address.slice(0,12)}", comment }). To look at a PDF or image: view_asset({ result: "${b.address.slice(0,12)}" }).`:""].filter(Boolean).join("\n"))}(e,i,a,c,0,y,w)
3857
+ const E=S?.data??S
3858
+ return f([1===y.fileCount?`Took **${w}** out of sealed box \`${e.box}\`.`:`Took **${w}** out of sealed box \`${e.box}\` as a tree of ${y.fileCount.toLocaleString()} files.`,"",`- address: \`${b.address}\``,`- size: ${r(y.totalBytes)}`+(y.dedupedFiles?`, of which ${y.dedupedFiles.toLocaleString()} files (${r(y.dedupedBytes)}) were already stored`:""),`- crossed out of confinement: ${r(y.transferredBytes)} streamed`,...y.skippedLinks?[`- skipped ${y.skippedLinks} symlink(s): a link is a reference, not content`]:[],I?`- produced by: ${I}`:"",E?.id?`- reference: ${E.id}`:"","","The box still holds its own copy; this one is on the host, addressed and recorded.",1===y.fileCount?`To make it a file in the repository: write_file({ path, fromResult: "${b.address.slice(0,12)}", comment }). To look at a PDF or image: view_asset({ result: "${b.address.slice(0,12)}" }).`:""].filter(Boolean).join("\n"))}(e,i,a,c,0,y,w)
3837
3859
  const g=await async function(e,t){if(!t||"string"!=typeof t)return{ok:!1,message:"path is required"}
3838
3860
  const r=[e.basePath,e.mainBasePath].filter(Boolean),o=[]
3839
3861
  for(const e of r){const r=(0,d.isAbsolute)(t)?(0,d.normalize)(t):(0,d.resolve)(e,t)
@@ -3848,10 +3870,10 @@ const k=(e.name||e.path.replace(/\/+$/,"").split("/").pop()||"result").trim()
3848
3870
  let I
3849
3871
  const $=await o(a,c,i,y)
3850
3872
  try{const t=await a.getToolRecordClient().createToolRecord({type:"record-result",projectId:i.projectId,...$?{requestId:$}:{},metadata:{name:k,address:b.address,locator:b.locator,bytes:b.bytes,deduped:b.deduped,sourcePath:e.path,summary:e.summary,...v?{tree:{fileCount:v.fileCount,totalBytes:v.totalBytes,dedupedFiles:v.dedupedFiles}}:{}},note:e.note?.trim()||`Recorded ${k} (${r(b.bytes)})`})
3851
- I=t?.id}catch(e){c.warn("results: producing record failed (bytes unaffected)",{error:(0,u.getErrorMessage)(e)})}const E=v?{...e.summary,tree:{files:v.fileCount,bytes:v.totalBytes,skippedLinks:v.skippedLinks}}:e.summary,S=await a.proxyPost(`/api/projects/${i.projectId}/results`,{address:b.address,locator:b.locator,bytes:b.bytes,name:k,mediaType:v?p.TREE_MEDIA_TYPE:e.mediaType||(0,h.mimeFor)(k),summary:E,...I?{producedBy:I}:{},...e.note?{producerNote:e.note}:{},...Array.isArray(e.inputs)&&e.inputs.length?{inputAddresses:e.inputs}:{}})
3873
+ I=t?.id}catch(e){c.warn("results: producing record failed (bytes unaffected)",{error:(0,u.getErrorMessage)(e)})}const S=v?{...e.summary,tree:{files:v.fileCount,bytes:v.totalBytes,skippedLinks:v.skippedLinks}}:e.summary,E=await a.proxyPost(`/api/projects/${i.projectId}/results`,{address:b.address,locator:b.locator,bytes:b.bytes,name:k,mediaType:v?p.TREE_MEDIA_TYPE:e.mediaType||(0,h.mimeFor)(k),summary:S,...I?{producedBy:I}:{},...e.note?{producerNote:e.note}:{},...Array.isArray(e.inputs)&&e.inputs.length?{inputAddresses:e.inputs}:{}})
3852
3874
  w({success:!0})
3853
- const x=S?.data??S
3854
- return f([v?`Recorded **${k}** as a tree of ${v.fileCount.toLocaleString()} files`:`Recorded **${k}** ${b.deduped?"(same content already stored, so nothing was copied)":""}`.trim(),"",`- address: \`${b.address}\``,v?`- size: ${r(v.totalBytes)} across ${v.fileCount.toLocaleString()} files`+(v.dedupedFiles?`, of which ${v.dedupedFiles.toLocaleString()} (${r(v.dedupedBytes)}) were already stored and cost nothing`:""):`- size: ${r(b.bytes)}`,...v&&v.skippedLinks?[`- skipped ${v.skippedLinks} symlink(s): a link is a reference, not content`]:[],"- stored beside the repo, not in it (git will not see it)",I?`- produced by: ${I}`:"- produced by: not linked (no open record for this call)",x?.id?`- reference: ${x.id}`:"","",'Read a part of it with `results` operation "read" and this address. To make it part of the',`work a branch carries, use operation "pin" -- that writes a small pointer file (${p.POINTER_SUFFIX})`,"into your worktree and commits it, so a reviewer sees WHAT changed without the repository","holding the bytes."].filter(Boolean).join("\n"))}catch(e){return w({success:!1,error:"record_failed"}),m(`Could not record the result: ${(0,u.getErrorMessage)(e)}`)}}(e,g,y,w,b,v)
3875
+ const _=E?.data??E
3876
+ return f([v?`Recorded **${k}** as a tree of ${v.fileCount.toLocaleString()} files`:`Recorded **${k}** ${b.deduped?"(same content already stored, so nothing was copied)":""}`.trim(),"",`- address: \`${b.address}\``,v?`- size: ${r(v.totalBytes)} across ${v.fileCount.toLocaleString()} files`+(v.dedupedFiles?`, of which ${v.dedupedFiles.toLocaleString()} (${r(v.dedupedBytes)}) were already stored and cost nothing`:""):`- size: ${r(b.bytes)}`,...v&&v.skippedLinks?[`- skipped ${v.skippedLinks} symlink(s): a link is a reference, not content`]:[],"- stored beside the repo, not in it (git will not see it)",I?`- produced by: ${I}`:"- produced by: not linked (no open record for this call)",_?.id?`- reference: ${_.id}`:"","",'Read a part of it with `results` operation "read" and this address. To make it part of the',`work a branch carries, use operation "pin" -- that writes a small pointer file (${p.POINTER_SUFFIX})`,"into your worktree and commits it, so a reviewer sees WHAT changed without the repository","holding the bytes."].filter(Boolean).join("\n"))}catch(e){return w({success:!1,error:"record_failed"}),m(`Could not record the result: ${(0,u.getErrorMessage)(e)}`)}}(e,g,y,w,b,v)
3855
3877
  case"pin":return async function(e,t,n,o,a,c){const l=a.startTimer("tool_execution_time")
3856
3878
  try{const o=await i(t.mainBasePath,e.address)
3857
3879
  if(!o.ok)return l({success:!1,error:"bad_address"}),m(o.message)
@@ -3895,16 +3917,16 @@ return l({success:!0}),f([`${o.slice(0,12)} is ${r(e?.onDisk??0)} of BINARY data
3895
3917
  let w=0,b=0,v=0,k=!1,I=!1
3896
3918
  const $=await(0,p.openResult)(t.mainBasePath,o)
3897
3919
  if(!$)return l({success:!1,error:"not_stored"}),m("the result went away while being read")
3898
- const E=(0,c.createInterface)({input:$,crlfDelay:1/0})
3899
- for await(const e of E){if(b++,g){if(!e.includes(g))continue
3920
+ const S=(0,c.createInterface)({input:$,crlfDelay:1/0})
3921
+ for await(const e of S){if(b++,g){if(!e.includes(g))continue
3900
3922
  if(v++,v>100){I=!0
3901
3923
  break}}else{if(b<=u)continue
3902
3924
  if(y.length>=h){k=!0
3903
3925
  break}}const t=`${b}: ${e}`
3904
3926
  if(w+t.length>2e4){I=!0
3905
- break}y.push(t),w+=t.length+1}E.close(),l({success:!0})
3906
- const S=g?`Lines containing ${JSON.stringify(g)} in ${o.slice(0,12)}`:`Lines ${u+1}-${u+y.length} of ${o.slice(0,12)}`,x=I?" (stopped at the cap -- narrow the range or the search)":k?g?"":` (more follows -- ask for offset ${u+y.length} to continue)`:""
3907
- return f([S+x,"",y.length?y.join("\n"):"(nothing matched)"].join("\n"))}catch(e){return l({success:!1,error:"read_failed"}),m(`Could not read the result: ${(0,u.getErrorMessage)(e)}`)}}(e,g,0,0,b)
3927
+ break}y.push(t),w+=t.length+1}S.close(),l({success:!0})
3928
+ const E=g?`Lines containing ${JSON.stringify(g)} in ${o.slice(0,12)}`:`Lines ${u+1}-${u+y.length} of ${o.slice(0,12)}`,_=I?" (stopped at the cap -- narrow the range or the search)":k?g?"":` (more follows -- ask for offset ${u+y.length} to continue)`:""
3929
+ return f([E+_,"",y.length?y.join("\n"):"(nothing matched)"].join("\n"))}catch(e){return l({success:!1,error:"read_failed"}),m(`Could not read the result: ${(0,u.getErrorMessage)(e)}`)}}(e,g,0,0,b)
3908
3930
  default:return m(`Unknown operation "${e?.operation}". Use record, fetch, pin, list or read.`)}}
3909
3931
  const c=t(23785),l=t(79896),d=t(16928),u=t(58824),h=t(34803),p=t(13080),f=e=>({content:[{type:"text",text:e}]}),m=e=>({content:[{type:"text",text:e}],isError:!0})},40285(e,exports,t){function r(e){return"string"==typeof e.id&&e.id.startsWith(l)}exports.isRuleRow=r,exports.reportRuleAnchors=async function(e,t,n,i){const l=n?.mainBasePath||n?.basePath,u=t.filter(r)
3910
3932
  if(!l||0===u.length)return void(d.get(e)&&0===u.length&&(d.delete(e),await i(`/api/projects/${e}/rules/anchor-status`,{reports:[]}).catch(()=>{})))
@@ -3914,22 +3936,22 @@ return n.length>2e5?null:n}catch{return null}}(l,n?.gitBinary):null),f=JSON.stri
3914
3936
  d.get(e)!==f&&(d.set(e,f),await i(`/api/projects/${e}/rules/anchor-status`,{reports:p}).catch(()=>{d.delete(e)}))}
3915
3937
  const n=t(35317),o=t(79896),s=t(16928),i=t(39023),a=t(20979),c=(0,i.promisify)(n.execFile),l="rule:",d=new Map},19080(e,exports,t){exports.def=void 0
3916
3938
  const r=t(76183),n=t(41281)
3917
- exports.def={name:"search",description:"Search for text patterns in files on the currently active branch, newest files first. The needle is a regular expression, as in grep (a|b, name\\(); options.useRegex false searches it as literal text.\n\nFile extensions: 'ts', '.ts', '*.ts' (all valid). Use '*' for any file type.\n\nSEVERAL PLACES AT ONCE: pass directories (a list of directories or files) instead of directory to search them all in one call; the same needle and options apply to each, and results come back grouped per path.\n\nCONTEXT LINES: Use contextLines option (0-10) to show surrounding lines before and after matches, reducing need for follow-up reads.\n\nLIST-ALL MODE: Omit 'needle' to list all files in a directory with tree structure (like ls/tree). Options: levels (1-4 depth), sortBy (name/modified/size/type).",schema:r.SearchSchema,readsProjectFiles:!0,annotations:{title:"Search Files",readOnlyHint:!0},handler:(e,t)=>(0,n.handleSearch)(e,t.context,t.apiClient,t.logger,t.metrics)}},41281(e,exports,t){async function r(e,t,o,s,i,a,c,l,d,u=0,f,m){let y=0,w=0,b=0
3939
+ exports.def={name:"search",description:"Search file contents on your branch, newest files first. needle is a regular expression as in grep (a|b, name\\(); options.useRegex false searches literal text. options.fileExtension: 'ts', '.ts' or '*.ts'; '*' for any. directories (a list of directories or files) searches several places in one call, grouped per path. options.contextLines (0-10) shows surrounding lines. Omit needle to list a directory as a tree with line counts (options.levels 1-4, sortBy).",schema:r.SearchSchema,readsProjectFiles:!0,annotations:{title:"Search Files",readOnlyHint:!0},handler:(e,t)=>(0,n.handleSearch)(e,t.context,t.apiClient,t.logger,t.metrics)}},41281(e,exports,t){async function r(e,t,o,s,i,a,c,l,d,u=0,f,m){let y=0,w=0,b=0
3918
3940
  const v=[]
3919
- if(o.length>=E||c>10)return a.debug("Stopping search in directory - hard cap or depth limit reached",{dir:e,matchesCount:o.length,hardCap:E,depth:c}),{filesCount:y,fileMatchesCount:w,subdirCount:b,subdirStats:v}
3920
- try{const I=await p.default.readdir(e,{withFileTypes:!0}),S=[],x=[]
3941
+ if(o.length>=S||c>10)return a.debug("Stopping search in directory - hard cap or depth limit reached",{dir:e,matchesCount:o.length,hardCap:S,depth:c}),{filesCount:y,fileMatchesCount:w,subdirCount:b,subdirStats:v}
3942
+ try{const I=await p.default.readdir(e,{withFileTypes:!0}),E=[],_=[]
3921
3943
  for(const t of I){const r=h.default.join(e,t.name)
3922
3944
  if(f&&g.isPathExcluded(h.default.relative(f,r)))a.debug(`Skipping system path: ${t.name}`)
3923
3945
  else{if(m){const e=h.default.relative(m.root,r)
3924
3946
  if(t.isDirectory()?!m.filter.visibleDir(e):!m.filter.visibleFile(e))continue}if(t.isDirectory()){if($.includes(t.name)||t.name.startsWith(".")){a.debug(`Skipping excluded directory: ${t.name}`)
3925
- continue}x.push({entry:t,fullPath:r})}else if(t.isFile()){if("*"!==i&&!t.name.endsWith(i)){a.debug("Skipping file due to extension mismatch",{filename:t.name,requiredExtension:i})
3926
- continue}S.push({entry:t,fullPath:r})}}}if(S.length>0){const e=[]
3927
- for(let t=0;t<S.length;t+=16)e.push(S.slice(t,t+16))
3928
- for(const r of e){if(o.length>=E)break
3947
+ continue}_.push({entry:t,fullPath:r})}else if(t.isFile()){if("*"!==i&&!t.name.endsWith(i)){a.debug("Skipping file due to extension mismatch",{filename:t.name,requiredExtension:i})
3948
+ continue}E.push({entry:t,fullPath:r})}}}if(E.length>0){const e=[]
3949
+ for(let t=0;t<E.length;t+=16)e.push(E.slice(t,t+16))
3950
+ for(const r of e){if(o.length>=S)break
3929
3951
  const e=await Promise.all(r.map(({entry:e,fullPath:r})=>n(r,t,0,o,l,d,e.name,u)))
3930
3952
  for(const t of e){y++,t.hasMatches&&w++
3931
- for(const e of t.matches){if(o.length>=E)return{filesCount:y,fileMatchesCount:w,subdirCount:b,subdirStats:v}
3932
- o.push(e)}}}}for(const{entry:e,fullPath:n}of x){if(o.length>=E)break
3953
+ for(const e of t.matches){if(o.length>=S)return{filesCount:y,fileMatchesCount:w,subdirCount:b,subdirStats:v}
3954
+ o.push(e)}}}}for(const{entry:e,fullPath:n}of _){if(o.length>=S)break
3933
3955
  b++
3934
3956
  const e={path:n,filesProcessed:0,filesMatched:0,subdirectories:0,errors:[]}
3935
3957
  try{const l=await r(n,t,o,s,i,a,c+1,e,d,u,f,m)
@@ -3940,7 +3962,7 @@ let l,d=!1
3940
3962
  const u=async()=>{if(void 0===l)try{const t=await p.default.stat(e)
3941
3963
  l=t.mtime.getTime()}catch(e){l=void 0}return l}
3942
3964
  if("filename"!==s&&"both"!==s||t.test(i)&&(d=!0,await u(),c.push({file:e,line:0,text:i,modifiedTime:l,matchType:"filename"})),"content"===s||"both"===s)try{const r=(await p.default.readFile(e,"utf8")).split("\n")
3943
- for(let o=0;o<r.length&&!(n.length>=E);o++)if(t.test(r[o])){d=!0,await u()
3965
+ for(let o=0;o<r.length&&!(n.length>=S);o++)if(t.test(r[o])){d=!0,await u()
3944
3966
  const t={file:e,line:o+1,text:(0,y.truncate)(r[o].trim(),120),modifiedTime:l,matchType:"content"}
3945
3967
  if(a>0){const e=Math.max(0,o-a)
3946
3968
  t.contextBefore=r.slice(e,o).map(e=>(0,y.truncate)(e.trim(),120))
@@ -3956,7 +3978,7 @@ for(let t=Math.max(s,e)-e;t<r.contextBefore.length;t++){const n=e+t
3956
3978
  n>=s&&n<=a&&!i.some(e=>e.lineNumber===n)&&i.push({lineNumber:n,content:r.contextBefore[t],isMatch:!1})}}}}i.push({lineNumber:r.line,content:r.text,isMatch:!0})}if(n.contextAfter&&n.contextAfter.length>0){const e=n.line+1
3957
3979
  n.contextAfter.forEach((t,r)=>{i.push({lineNumber:e+r,content:t,isMatch:!1})})}const a=Array.from(new Map(i.map(e=>[e.lineNumber,e])).values()).sort((e,t)=>e.lineNumber-t.lineNumber)
3958
3980
  return{file:r.file,relativePath:r.relativePath||r.file,startLine:o,endLine:s,lines:a}}async function i(e){try{const t=await p.default.stat(e),r=h.default.extname(e).toLowerCase()
3959
- if(!S.has(r))return
3981
+ if(!E.has(r))return
3960
3982
  if(t.size>1048576)return
3961
3983
  if(0===t.size)return 0
3962
3984
  const n=await p.default.readFile(e,"utf-8")
@@ -3970,34 +3992,34 @@ if(null!=e)for(var r=a(e),n=0;n<r.length;n++)"default"!==r[n]&&c(t,e,r[n])
3970
3992
  return l(t,e),t}),u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
3971
3993
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleSearch=async function e(t,n,a,c,l){const d=c.child({tool:"search"}),u=l.startTimer("tool_execution_time"),I=Date.now()
3972
3994
  let $=0
3973
- const E={searchDirectory:"",resolvedPath:"",patternInfo:{needle:"",regex:"",caseSensitive:!1,fileExtension:"",normalizedExtension:""},environmentInfo:{platform:process.platform,nodeVersion:process.version,hostname:f.default.hostname(),username:f.default.userInfo().username,workingDirectory:process.cwd(),contextBasePath:n?.basePath||"unknown"},startTime:I,endTime:0,phases:{initializationTime:0,searchTime:0,directoryListingTime:0},totalFiles:0,totalMatches:0,directoryStats:[],errors:[]},S=Array.isArray(t?.directories)?Array.from(new Set(t.directories.filter(e=>"string"==typeof e&&""!==e.trim()).map(e=>e.trim()))):[]
3974
- if(S.length>1&&"string"==typeof t?.needle&&""!==t.needle.trim()){const r=Math.max(10,Math.ceil((t.options?.maxResults||50)/S.length)),o=[]
3975
- for(const s of S)try{const i=await e({needle:t.needle,directory:s,options:{...t.options??{},maxResults:r}},n,a,c,l)
3976
- o.push(`=== ${s} ===\n${i.content.map(e=>e.text).join("\n")}`)}catch(e){o.push(`=== ${s} ===\nNot searched: ${(0,k.getErrorMessage)(e)}`)}return{content:[{type:"text",text:o.join("\n\n")}]}}1!==S.length||t.directory||(t={...t,directory:S[0]})
3995
+ const S={searchDirectory:"",resolvedPath:"",patternInfo:{needle:"",regex:"",caseSensitive:!1,fileExtension:"",normalizedExtension:""},environmentInfo:{platform:process.platform,nodeVersion:process.version,hostname:f.default.hostname(),username:f.default.userInfo().username,workingDirectory:process.cwd(),contextBasePath:n?.basePath||"unknown"},startTime:I,endTime:0,phases:{initializationTime:0,searchTime:0,directoryListingTime:0},totalFiles:0,totalMatches:0,directoryStats:[],errors:[]},E=Array.isArray(t?.directories)?Array.from(new Set(t.directories.filter(e=>"string"==typeof e&&""!==e.trim()).map(e=>e.trim()))):[]
3996
+ if(E.length>1&&"string"==typeof t?.needle&&""!==t.needle.trim()){const r=Math.max(10,Math.ceil((t.options?.maxResults||50)/E.length)),o=[]
3997
+ for(const s of E)try{const i=await e({needle:t.needle,directory:s,options:{...t.options??{},maxResults:r}},n,a,c,l)
3998
+ o.push(`=== ${s} ===\n${i.content.map(e=>e.text).join("\n")}`)}catch(e){o.push(`=== ${s} ===\nNot searched: ${(0,k.getErrorMessage)(e)}`)}return{content:[{type:"text",text:o.join("\n\n")}]}}1!==E.length||t.directory||(t={...t,directory:E[0]})
3977
3999
  try{const{needle:e,directory:a,options:f={}}=t
3978
4000
  if(!e||""===e.trim())return d.info("Empty needle detected, switching to list-all mode"),await(0,v.handleListDirectory)({path:a||".",levels:f.levels||1,sortBy:f.sortBy||"name"},n,c,l)
3979
- if("string"!=typeof e)throw E.errors.push("Search term (needle) must be a string"),(0,m.createValidationError)("Search term (needle) must be a string")
3980
- let S
3981
- if(E.searchDirectory=a||".",a){const{normalizedPath:e,fullPath:t}=g.validateAndNormalizePath(a,n)
3982
- S=t}else S=n.workspaceManager?.getCurrentContext(n.currentSessionId)?.basePath||n.basePath
3983
- E.resolvedPath=S
3984
- const x=n.workspaceManager?.getCurrentContext(n.currentSessionId)?.basePath||n.basePath
3985
- let _=!1
3986
- try{_=(await p.default.stat(S)).isFile()}catch(e){const t=`Cannot access path ${S}: ${(0,k.getErrorMessage)(e)}`
3987
- throw E.errors.push(t),new Error(t)}const C=f.maxResults||50,q=f.caseSensitive||!1
4001
+ if("string"!=typeof e)throw S.errors.push("Search term (needle) must be a string"),(0,m.createValidationError)("Search term (needle) must be a string")
4002
+ let E
4003
+ if(S.searchDirectory=a||".",a){const{normalizedPath:e,fullPath:t}=g.validateAndNormalizePath(a,n)
4004
+ E=t}else E=n.workspaceManager?.getCurrentContext(n.currentSessionId)?.basePath||n.basePath
4005
+ S.resolvedPath=E
4006
+ const _=n.workspaceManager?.getCurrentContext(n.currentSessionId)?.basePath||n.basePath
4007
+ let x=!1
4008
+ try{x=(await p.default.stat(E)).isFile()}catch(e){const t=`Cannot access path ${E}: ${(0,k.getErrorMessage)(e)}`
4009
+ throw S.errors.push(t),new Error(t)}const C=f.maxResults||50,q=f.caseSensitive||!1
3988
4010
  let T=!1!==f.useRegex
3989
4011
  if(T)try{new RegExp(e)}catch{T=!1}const j=f.fileExtension,P=f.searchIn||"content"
3990
4012
  let R=void 0!==f.contextLines?f.contextLines:0
3991
4013
  R<0?R=0:R>10&&(d.warn(`contextLines ${R} exceeds maximum of 10, clamping to 10`),R=10)
3992
4014
  let A,O=""
3993
- if(_)O="*"
4015
+ if(x)O="*"
3994
4016
  else if(j&&"*"!==j){if(j){let e=j;(e.startsWith("*.")||e.startsWith("*"))&&(e=e.substring(1)),O=e.startsWith(".")?e:`.${e}`}}else O="*"
3995
4017
  try{A=T?q?new RegExp(e):new RegExp(e,"i"):q?new RegExp(o(e)):new RegExp(o(e),"i")}catch(e){const t=`Invalid ${T?"regex":"search"} pattern: ${(0,k.getErrorMessage)(e)}`
3996
- throw E.errors.push(t),(0,m.createValidationError)(t)}if(E.patternInfo={needle:e,regex:A.toString(),caseSensitive:q,fileExtension:j,normalizedExtension:O},!_)try{const e=Date.now(),t=await p.default.readdir(S,{withFileTypes:!0}),r=[],n=[]
4018
+ throw S.errors.push(t),(0,m.createValidationError)(t)}if(S.patternInfo={needle:e,regex:A.toString(),caseSensitive:q,fileExtension:j,normalizedExtension:O},!x)try{const e=Date.now(),t=await p.default.readdir(E,{withFileTypes:!0}),r=[],n=[]
3997
4019
  for(const e of t)e.isDirectory()?r.push(e.name):e.isFile()&&n.push(e.name)
3998
- E.initialScan={totalEntries:t.length,directories:r,files:n,firstFewFiles:n.slice(0,10),scanTime:Date.now()-e},E.phases.directoryListingTime=E.initialScan.scanTime}catch(e){const t=`Failed to scan directory contents: ${(0,k.getErrorMessage)(e)}`
3999
- E.errors.push(t),d.error(t)}const M=_?"file":"directory"
4000
- d.info(`Searching for "${e}" in ${M} ${S} (mode: ${P})`,{needle:e,searchTarget:M,path:S,options:{maxResults:C,caseSensitive:q,fileExtension:O,searchIn:P}}),$=Date.now(),E.phases.initializationTime=$-I
4020
+ S.initialScan={totalEntries:t.length,directories:r,files:n,firstFewFiles:n.slice(0,10),scanTime:Date.now()-e},S.phases.directoryListingTime=S.initialScan.scanTime}catch(e){const t=`Failed to scan directory contents: ${(0,k.getErrorMessage)(e)}`
4021
+ S.errors.push(t),d.error(t)}const M=x?"file":"directory"
4022
+ d.info(`Searching for "${e}" in ${M} ${E} (mode: ${P})`,{needle:e,searchTarget:M,path:E,options:{maxResults:C,caseSensitive:q,fileExtension:O,searchIn:P}}),$=Date.now(),S.phases.initializationTime=$-I
4001
4023
  const D=Date.now()
4002
4024
  let F=[],N=0,z=0
4003
4025
  try{const e=await async function(e,t,n,o,s,i,a){const{maxResults:c,fileExtension:l,searchIn:d,isFile:u,contextLines:f}=n,m=[]
@@ -4015,15 +4037,15 @@ o.errors.push(r),n.error(r)}return{matches:i,totalFiles:1}}(e,t,d,s,i,f)
4015
4037
  const g={path:e,filesProcessed:0,filesMatched:0,subdirectories:0,errors:[]}
4016
4038
  try{const n=a||e,l=await(0,b.gitVisibleFilter)(n),{filesCount:u,fileMatchesCount:h,subdirCount:p,subdirStats:y}=await r(e,t,m,c,o,s,0,g,d,f,a,l?{filter:l,root:n}:void 0)
4017
4039
  return g.filesProcessed=u,g.filesMatched=h,g.subdirectories=p,i.directoryStats.push(g),y&&y.length>0&&i.directoryStats.push(...y),s.info("performLocalSearch completed",{totalFiles:u,matchesFound:m.length,directories:p+1}),{matches:m,totalFiles:u}}catch(t){const r=`Error in performLocalSearch: ${(0,k.getErrorMessage)(t)}`
4018
- return g.errors.push(r),i.directoryStats.push(g),i.errors.push(r),s.error(r,{error:t instanceof Error?t.stack:String(t),searchPath:e}),{matches:m,totalFiles:g.filesProcessed}}}(S,A,{maxResults:C,fileExtension:O,searchIn:P,isFile:_,contextLines:R},O,d,E,x)
4040
+ return g.errors.push(r),i.directoryStats.push(g),i.errors.push(r),s.error(r,{error:t instanceof Error?t.stack:String(t),searchPath:e}),{matches:m,totalFiles:g.filesProcessed}}}(E,A,{maxResults:C,fileExtension:O,searchIn:P,isFile:x,contextLines:R},O,d,S,_)
4019
4041
  F=e.matches,N=e.totalFiles
4020
4042
  const t=1e3
4021
4043
  F.length>0&&F.length<=t?(F.sort((e,t)=>void 0!==e.modifiedTime&&void 0!==t.modifiedTime?t.modifiedTime-e.modifiedTime:void 0!==e.modifiedTime&&void 0===t.modifiedTime?-1:void 0===e.modifiedTime&&void 0!==t.modifiedTime?1:0),d.info(`Sorted ${F.length} matches by modification time (newest first)`)):F.length>t&&d.info(`Skipped sorting ${F.length} matches (exceeds hard cap of ${t} for performance)`),z=F.length,F.length>C&&(F=F.slice(0,C),d.info(`Limited results to ${C} (from ${z} total matches)`)),F.forEach(e=>{e.relativePath=function(e,t){const r=h.default.relative(e,t)
4022
- return r?r===h.default.basename(t)?r:r.startsWith("./")?r.substring(2):r:h.default.basename(t)}(S,e.file)}),d.info(`Local search completed: found ${z||F.length} total matches in ${N} files, returning ${F.length} results`),E.phases.searchTime=Date.now()-D}catch(e){const t=`Error during search: ${(0,k.getErrorMessage)(e)}`
4023
- E.errors.push(t),d.error(t),E.phases.searchTime=Date.now()-D}E.totalFiles=N,E.totalMatches=F.length,E.endTime=Date.now()
4044
+ return r?r===h.default.basename(t)?r:r.startsWith("./")?r.substring(2):r:h.default.basename(t)}(E,e.file)}),d.info(`Local search completed: found ${z||F.length} total matches in ${N} files, returning ${F.length} results`),S.phases.searchTime=Date.now()-D}catch(e){const t=`Error during search: ${(0,k.getErrorMessage)(e)}`
4045
+ S.errors.push(t),d.error(t),S.phases.searchTime=Date.now()-D}S.totalFiles=N,S.totalMatches=F.length,S.endTime=Date.now()
4024
4046
  let L,B=""
4025
4047
  try{if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
4026
- e.branchName&&!e.permanentBranch&&(B=e.isMainBranch?` (searched on main branch: ${e.branchName})`:` (searched on request branch: ${e.branchName}${e.requestId?` for ${e.requestId}`:""})`)}}catch(e){d.debug("Could not get branch context",{error:e}),B=" (branch context unavailable)"}d.debug("Search diagnostic information",{diagnostic:E})
4048
+ e.branchName&&!e.permanentBranch&&(B=e.isMainBranch?` (searched on main branch: ${e.branchName})`:` (searched on request branch: ${e.branchName}${e.requestId?` for ${e.requestId}`:""})`)}}catch(e){d.debug("Could not get branch context",{error:e}),B=" (branch context unavailable)"}d.debug("Search diagnostic information",{diagnostic:S})
4027
4049
  const H="content"===P?"content":"filename"===P?"filenames":"content and filenames"
4028
4050
  if(z>F.length?(L=`Found ${z} results for "${e}" (searching ${H})`,a&&(L+=` in ${a}`),j&&(L+=` with extension ${j}`),L+=`${B}. Showing first ${F.length}:\n\n`):(L=`Found ${F.length} results for "${e}" (searching ${H})`,a&&(L+=` in ${a}`),j&&(L+=` with extension ${j}`),L+=`${B}:\n\n`),F.length>0)if(R>0){const{filenameMatches:e,mergedGroups:t}=function(e,t){const r=e.filter(e=>"filename"===e.matchType),n=e.filter(e=>"content"===e.matchType)
4029
4051
  if(0===n.length)return{filenameMatches:r,mergedGroups:[]}
@@ -4046,10 +4068,10 @@ void 0!==s&&(a+=` (${s} lines)`),a+=` - ${o} ${1===o?"match":"matches"}:`
4046
4068
  const c=t.map(e=>` :${e.line} - ${e.text}`)
4047
4069
  n.push(a+"\n"+c.join("\n"))}return n.join("\n\n")}(F)
4048
4070
  else L+="No matches found."
4049
- return u({success:!0}),{content:[{type:"text",text:L}],...(0,w.alchemyMeta)({kind:"search"})}}catch(e){d.error("Search failed",{args:t,error:e instanceof Error?e.stack:String(e)}),E.errors.push((0,k.getErrorMessage)(e)),E.endTime=Date.now(),d.debug("Search diagnostic information on error",{diagnostic:E})
4071
+ return u({success:!0}),{content:[{type:"text",text:L}],...(0,w.alchemyMeta)({kind:"search"})}}catch(e){d.error("Search failed",{args:t,error:e instanceof Error?e.stack:String(e)}),S.errors.push((0,k.getErrorMessage)(e)),S.endTime=Date.now(),d.debug("Search diagnostic information on error",{diagnostic:S})
4050
4072
  let r=`Search failed: ${(0,k.getErrorMessage)(e)}`
4051
4073
  return u({success:!1,error:e instanceof Error?e.name:"unknown"}),{content:[{type:"text",text:r}],isError:!0}}}
4052
- const h=u(t(16928)),p=u(t(91943)),f=u(t(70857)),m=t(59860),g=d(t(85967)),y=t(85967),w=t(82272),b=t(2443),v=t(2537),k=t(58824),I=t(43255),$=["node_modules"],E=1e4,S=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".py",".rb",".go",".rs",".java",".kt",".scala",".c",".cpp",".h",".hpp",".cs",".html",".css",".scss",".sass",".less",".json",".yaml",".yml",".toml",".xml",".md",".txt",".csv",".sql",".sh",".bash",".zsh",".fish",".vim",".lua",".php",".swift",".m",".vue",".svelte",".astro",".env",".gitignore",".dockerignore"])},36651(e,exports,t){exports.resolveRequestIdOrError=async function(e,t,o,s){if(o&&!n.has(o)){const n=await r.sessionIdentity.getSessionRequestId(e,t,o)
4074
+ const h=u(t(16928)),p=u(t(91943)),f=u(t(70857)),m=t(59860),g=d(t(85967)),y=t(85967),w=t(82272),b=t(2443),v=t(2537),k=t(58824),I=t(43255),$=["node_modules"],S=1e4,E=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".py",".rb",".go",".rs",".java",".kt",".scala",".c",".cpp",".h",".hpp",".cs",".html",".css",".scss",".sass",".less",".json",".yaml",".yml",".toml",".xml",".md",".txt",".csv",".sql",".sh",".bash",".zsh",".fish",".vim",".lua",".php",".swift",".m",".vue",".svelte",".astro",".env",".gitignore",".dockerignore"])},36651(e,exports,t){exports.resolveRequestIdOrError=async function(e,t,o,s){if(o&&!n.has(o)){const n=await r.sessionIdentity.getSessionRequestId(e,t,o)
4053
4075
  if(n)return{ok:!0,requestId:n}}return{ok:!1,errorResult:(i=r.sessionIdentity.noRequestReason(o)??s,{content:[{type:"text",text:i}],isError:!0})}
4054
4076
  var i}
4055
4077
  const r=t(6688),n=new Set(["__unknown__","__server__",""])},87280(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
@@ -4114,8 +4136,8 @@ t.info("commit_to_viewed_session:posting",{callId:a,viewedId:f,heraldId:u,anchor
4114
4136
  try{const e=await i.proxyPost(`/api/projects/${s}/inbox-chat`,{text:l,anchor:m}),r=e?.data??e
4115
4137
  if(!r?.delivered){const n=r?.reason?String(r.reason):e?.error?.message??"not delivered"
4116
4138
  return t.warn("commit_to_viewed_session:refused",{callId:a,viewedId:f,reason:n}),{content:[{type:"text",text:`Commit failed: ${n}`}],isError:!0}}}catch(e){return t.warn("commit_to_viewed_session:post-failed",{callId:a,error:(0,r.getErrorMessage)(e)}),{content:[{type:"text",text:`Commit failed: ${(0,r.getErrorMessage)(e)}`}],isError:!0}}return t.info("Translator forwarded message to viewed session",{callId:a,heraldId:u,viewedId:f,messageLength:l.length,preview:l.slice(0,120),durationMs:Date.now()-c}),{content:[{type:"text",text:"Committed."}]}}
4117
- const r=t(58824),n=t(6688)},22316(e,exports){function t(e){return"autonomous"===e||"collaborative"===e?e:"balanced"}exports.me=exports.Ow=void 0,exports.normalizePosture=t,exports.resolveQuestionGate=function(e,r){const n=t(r)
4118
- return e.targeted?{disposition:"ask-now",reason:"targeted at a session (agent-to-agent), not the human"}:e.humanInputFloor?{disposition:"ask-now",reason:"user-input criterion on this request -- the user must be asked; outranks the posture"}:e.userAskedToBeAsked?{disposition:"ask-now",reason:"the user asked to be asked -- their words outrank the posture"}:"high"===e.priority?{disposition:"ask-now",reason:"high priority -- treated as blocking; always reaches the human"}:"escalate"===e.onExpiry?{disposition:"ask-now",reason:"asker requested escalate-on-expiry -- a human must see it"}:"collaborative"===n?{disposition:"ask-now",reason:"collaborative posture: the human wants to be consulted",hint:"Collaborative posture: keep surfacing the design choices in this work as specific, defaulted questions (options with tradeoffs, recommendation first) -- the user wants to weigh in."}:"autonomous"===n?e.hasDefault?{disposition:"self-answer",reason:"autonomous posture and the question carries a default -- the question is closed on it and the user can still change it"}:{disposition:"bounce",reason:"autonomous posture and no default -- the asker must choose for itself"}:e.hasDefault&&"low"===e.priority?{disposition:"defer-batch",reason:"balanced posture, low priority, and a default to proceed on -- held for the next checkpoint bundle"}:{disposition:"ask-now",reason:"balanced posture: questions post normally (non-blocking when a default is set)"}},exports.resolveBriefHoldPolicy=function(e){return"collaborative"===t(e)?{holdMs:exports.me,reason:"collaborative posture: the user decides the brief before the work happens"}:{holdMs:0,reason:"the worker proceeds on its stated defaults; only collaborative holds a brief"}},exports.proceedsOnDefault=function(e){return("string"==typeof e.defaultOption?""!==e.defaultOption.trim():null!=e.defaultOption)&&"escalate"!==e.onExpiry&&"how"!==e.steer&&"high"!==e.priority&&!(e.namedCriteria&&e.namedCriteria>0)},exports.resolveHoldPolicy=function(e,r={}){const n=t(e)
4139
+ const r=t(58824),n=t(6688)},22316(e,exports){function t(e){return"autonomous"===e||"collaborative"===e?e:"balanced"}exports.me=exports.Ow=void 0,exports.offersNotYet=function(e){return Array.isArray(e)&&e.some(e=>!!e&&"object"==typeof e&&("not-yet"===e.effect||!0===e.notChecked))},exports.normalizePosture=t,exports.resolveQuestionGate=function(e,r){const n=t(r)
4140
+ return e.targeted?{disposition:"ask-now",reason:"targeted at a session (agent-to-agent), not the human"}:e.userAskedToBeAsked?{disposition:"ask-now",reason:"the user asked to be asked -- their words outrank the posture"}:e.namesCriteria&&!e.offersNotYet?{disposition:"ask-now",reason:'blocking user-input question (no "not yet" option) -- the user must answer before the build goes on'}:"autonomous"===n?e.hasDefault||e.namesCriteria?{disposition:"self-answer",reason:e.namesCriteria?'autonomous posture and the question names [user-input] criteria -- answered "not yet"; the work goes on with its placeholder and the end card asks':"autonomous posture and the question carries a default -- the question is closed on it and the user can still change it"}:"high"===e.priority?{disposition:"ask-now",reason:"high priority and no default -- nothing to take, so it reaches the human"}:{disposition:"bounce",reason:"autonomous posture and no default -- the asker must choose for itself"}:e.humanInputFloor||e.namesCriteria?{disposition:"ask-now",reason:"user-input criterion on this request -- the user must be asked"}:"high"===e.priority?{disposition:"ask-now",reason:"high priority -- treated as blocking; always reaches the human"}:"escalate"===e.onExpiry?{disposition:"ask-now",reason:"asker requested escalate-on-expiry -- a human must see it"}:"collaborative"===n?{disposition:"ask-now",reason:"collaborative posture: the human wants to be consulted",hint:"Collaborative posture: keep surfacing the design choices in this work as specific, defaulted questions (options with tradeoffs, recommendation first) -- the user wants to weigh in."}:e.hasDefault&&"low"===e.priority?{disposition:"defer-batch",reason:"balanced posture, low priority, and a default to proceed on -- held for the next checkpoint bundle"}:{disposition:"ask-now",reason:"balanced posture: questions post normally (non-blocking when a default is set)"}},exports.resolveBriefHoldPolicy=function(e){return"collaborative"===t(e)?{holdMs:exports.me,reason:"collaborative posture: the user decides the brief before the work happens"}:{holdMs:0,reason:"the worker proceeds on its stated defaults; only collaborative holds a brief"}},exports.proceedsOnDefault=function(e){return("string"==typeof e.defaultOption?""!==e.defaultOption.trim():null!=e.defaultOption)&&"escalate"!==e.onExpiry&&"how"!==e.steer&&"high"!==e.priority&&!(e.namedCriteria&&e.namedCriteria>0)},exports.resolveHoldPolicy=function(e,r={}){const n=t(e)
4119
4141
  return"balanced"===n&&r.proceedsOnDefault?{holdMs:0,reason:"balanced posture and the question goes ahead on its default: no wait"}:"collaborative"===n?{holdMs:exports.me,reason:"collaborative posture: courtesy window for an inline answer; then end-turn-and-wait indefinitely"}:"balanced"===n?{holdMs:exports.Ow,reason:"balanced posture: give the human a bounded window, then degrade to async"}:{holdMs:0,reason:"autonomous posture: never hold -- async delivery"}},exports.Ow=6e5,exports.me=18e4},70508(e,exports){exports.requestNeedingAnchor=function(e,n){if(r(n.requestId)||r(n.area))return null
4120
4142
  if(r(n.anchor)&&(!(o=n.anchor)||"object"!=typeof o||"project"!==o.type))return null
4121
4143
  var o
@@ -4127,12 +4149,12 @@ case"hub-digest":return"\n\n\u{1fab5} Recorded for the user and the request reco
4127
4149
  case"settles":return"\n\n\u{1fab5} Recorded on your setup request. Whoever waits on it is woken with this report the moment your request settles (when you end your session), so send it once and end."
4128
4150
  case"explicit":case"parent-waits":return t?"\n\n\u{1f4ec} The receiving session has been woken with this report.":"\n\n\u26a0\ufe0f The receiving session could NOT be woken (delivery failed). The report is recorded, but nobody is acting on it: say so, or send it again."}},exports.HUB_AGENT_TYPES=new Set(["alchemist","hub"])},17613(e,exports,t){exports.def=void 0
4129
4151
  const r=t(76183),n=t(9072),o=t(31375)
4130
- exports.def={name:"session_message_operations",description:"Notes, questions, reports and decisions between sessions and the user.\n\n\u2022 note - One or two plain sentences about what you are doing (text). It lands on your request, wakes nobody and stays out of the user's stream. Write one when you start a stretch of work, when a check surprises you, when you change approach, and when a piece works.\n\u2022 send - A question or a report (type). A report is for something the user must act on or would regret not seeing; narration is a note.\n\u2022 reply - Answer a question or acknowledge a report (messageCode).\n\u2022 record-decision - Record a call you already made without asking (decision, why). Wakes nobody. A call the user could still flip is a question instead.\n\u2022 brief - Post the one opening brief for a request (requestId, sections): its predictable design choices, sent to the user. The result says whether to wait: if the user decides first, end your turn and you are resumed with their answers; otherwise proceed on your defaults and answers arrive later as steering. Posting again returns the existing brief. Do not re-ask what it covers; later questions are for real discoveries.\n\u2022 list / get - List messages, or get one by messageCode.\n\u2022 offer-handoff - Offer the user the choice to start building or keep planning.\n\u2022 withdraw - Settle a question you asked that no longer needs an answer (messageCode, withdrawReason); the user's copy settles too, and a [user-input] criterion it named is asked on the card at the end of the work.\n\nCodes: Q-{id}-{seq} questions, R-{id}-{seq} reports.\n\nBefore asking the user:\n1. Answerable from context and cheap to fix if wrong: proceed without asking.\n2. Real but not blocking: ask with a defaultOption and expiresInHours. The cost depends on the project's posture: 'autonomous' returns your default at once and files an FYI for the user; 'balanced' (the fallback) holds this call up to ten minutes, then continues without an answer; 'collaborative' holds briefly, then ends your turn to wait. Give a defaultOption unless only the user can answer.\n3. Truly blocking or high stakes: priority \"high\".\nOne decision per question; bundle related uncertainties into one. Several candidate next steps go as one alternatives question (allowMultiple when independent), not a prose list in a report. What the user reads is plain language at outcome level; technical evidence goes in details. A question the user already decided comes back with their decision: proceed on it.",schema:r.SessionMessageOperationsSchema,annotations:{title:"Session Message Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>function(e,t){return(0,o.withHeartbeat)({progressToken:e.progressToken,sendNotification:e.sendNotification,intervalMs:void 0,message:()=>"waiting for the user's answer"},t)}(t,()=>(0,n.handleSessionMessageOperations)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin))}},9072(e,exports,t){function r(e,t){if("send"===e)return"report"===t?.type?"send-report":"question"===t?.type?"send-question":"send-message"
4152
+ exports.def={name:"session_message_operations",description:"Notes, questions, reports and decisions between sessions and the user.\n\n\u2022 note - One or two plain sentences about what you are doing (text). Lands on your request, wakes nobody, stays out of the user's stream.\n\u2022 send - A question or a report (type). A report is for something the user must act on or would regret not seeing; narration is a note.\n\u2022 reply - Answer a question or acknowledge a report (messageCode).\n\u2022 record-decision - Record a call you already made without asking (decision, why). Wakes nobody. A call the user could still flip is a question instead.\n\u2022 brief - Post the one opening brief for a request (requestId, sections). The result says whether to wait for answers; posting again returns the existing brief.\n\u2022 list / get - List messages, or get one by messageCode.\n\u2022 offer-handoff - Offer the user the choice to start building or keep planning.\n\u2022 withdraw - Settle a question you asked that no longer needs an answer (messageCode, withdrawReason).\n\nCodes: Q-{id}-{seq} questions, R-{id}-{seq} reports.\n\nBefore asking the user: when context answers it and a wrong guess is cheap to fix, proceed. Otherwise ask with a defaultOption unless only the user can answer; what that costs depends on the project's posture: 'autonomous' returns your default at once; 'balanced' (the fallback) holds this call up to ten minutes, then continues without an answer; 'collaborative' holds briefly, then ends your turn to wait. priority \"high\" only when blocked with real stakes. One decision per question, related uncertainties bundled; several candidate next steps go as one alternatives question, not a prose list in a report. A question the user already decided comes back with their decision: proceed on it.",schema:r.SessionMessageOperationsSchema,annotations:{title:"Session Message Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>function(e,t){return(0,o.withHeartbeat)({progressToken:e.progressToken,sendNotification:e.sendNotification,intervalMs:void 0,message:()=>"waiting for the user's answer"},t)}(t,()=>(0,n.handleSessionMessageOperations)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin))}},9072(e,exports,t){function r(e,t){if("send"===e)return"report"===t?.type?"send-report":"question"===t?.type?"send-question":"send-message"
4131
4153
  if("reply"===e){const e=t?.messageCode
4132
- return"string"==typeof e&&e.startsWith("R-")?"acknowledge-report":"string"==typeof e&&e.startsWith("Q-")?"answer-question":"reply-message"}return"note"===e?!0===t?.milestone?"send-milestone":"send-note":"list"===e?"list-messages":"get"===e?"get-message":"offer-handoff"===e?"offer-handoff":"record-decision"===e?"record-decision":"brief"===e?"opening-brief":"session-message-operations"}async function n(e){return E.sessionIdentity.getAgentTypeFor(e)}async function o(e,t,r,n,o){const s=o?.alchemy,i=await(0,C.deliverToSession)(e,t,{kind:s?.kind??"inserted-prompt",...s?.payload?{partPayload:s.payload}:{}})
4154
+ return"string"==typeof e&&e.startsWith("R-")?"acknowledge-report":"string"==typeof e&&e.startsWith("Q-")?"answer-question":"reply-message"}return"note"===e?!0===t?.milestone?"send-milestone":"send-note":"list"===e?"list-messages":"get"===e?"get-message":"offer-handoff"===e?"offer-handoff":"record-decision"===e?"record-decision":"brief"===e?"opening-brief":"session-message-operations"}async function n(e){return E.sessionIdentity.getAgentTypeFor(e)}async function o(e,t,r,n,o){const s=o?.alchemy,i=await(0,q.deliverToSession)(e,t,{kind:s?.kind??"inserted-prompt",...s?.payload?{partPayload:s.payload}:{}})
4133
4155
  return i.ok?(n.info("Notified session",{sessionId:e,messageId:i.messageId}),!0):(n.warn("Failed to notify session",{sessionId:e,error:i.error,detail:i.detail}),!1)}function s(e,t,r){return E.sessionIdentity.getParentSessionId(e,t,r)}function i(e,t,r,n,o){const s=o?.sessionId||E.sessionIdentity.getIdentity()?.sessionId
4134
- if(!s)throw new g.ValidationError("No session identity available. Make sure you are running within an OpenCode session.")
4135
- return(0,T.logSessionIdentity)(`${r}`,"identity",{sessionId:s,fromCliOrigin:!!o?.sessionId}),s}function a(e){const t=[],r=e.content||{}
4156
+ if(!s)throw new y.ValidationError("No session identity available. Make sure you are running within an OpenCode session.")
4157
+ return(0,j.logSessionIdentity)(`${r}`,"identity",{sessionId:s,fromCliOrigin:!!o?.sessionId}),s}function a(e){const t=[],r=e.content||{}
4136
4158
  if(t.push(`**Message Code:** ${e.messageCode}`),t.push(`**Type:** question (${r.questionType})`),t.push(`**Status:** ${e.status}`),t.push(`**Priority:** ${e.priority}`),t.push(`**From:** ${e.fromSessionId}`),t.push(e.toSessionId?`**To:** ${e.toSessionId}`:"**To:** (broadcast)"),t.push(`**Created:** ${e.createdAt}`),t.push(`**Expires:** ${e.expiresAt}`),t.push(""),t.push(`**Question:** ${r.question}`),r.context&&t.push(`**Context:** ${r.context}`),"alternatives"===r.questionType&&r.options){t.push(""),t.push("**Options:**")
4137
4159
  for(const e of r.options)t.push(` - [${e.id}] ${e.label}${e.description?` - ${e.description}`:""}`)}return"responded"===e.status&&e.response&&(t.push(""),t.push("---"),t.push(`**Answered by:** ${function(e){switch(e.answeredBy){case"user":return"the user (in the app)"
4138
4160
  case"user-in-discussion":return"the user, in conversation (an agent recorded their words)"
@@ -4144,113 +4166,116 @@ return t.push(`**Status:** ${r.reportStatus||"unknown"}`),t.push(`**Summary:** $
4144
4166
  return t.push(`**Message Code:** ${e.messageCode}`),t.push("**Type:** instruction"),t.push(`**Status:** ${e.status}`),t.push(`**From:** ${e.fromSessionId}`),t.push(`**To:** ${e.toSessionId}`),t.push(`**Created:** ${e.createdAt}`),r.requestId&&t.push(`**Request:** ${r.requestId}`),t.push(""),t.push("**Body:**"),t.push(r.body||""),t.join("\n")}(e):l(e)}function u(e){const t=e.content||{},r=["\u{1f4e8} **You have a new question!**","",`**Question Code:** ${e.messageCode}`,`**From:** ${e.fromSessionId}`,`**Priority:** ${e.priority}`,"",`**Question:** ${t.question}`]
4145
4167
  if(t.context&&r.push(`**Context:** ${t.context}`),"alternatives"===t.questionType&&t.options){r.push(""),r.push("**Options:**")
4146
4168
  for(const e of t.options)r.push(` - [${e.id}] ${e.label}${e.description?` - ${e.description}`:""}`)}return r.push(""),r.push(`Use \`session_message_operations\` with \`reply\` operation and message code \`${e.messageCode}\` to respond.`),r.join("\n")}function h(e){const t=String(e??"").trim().replace(/^plan\s+(?:is\s+)?(?:ready|complete|completed|done|finished)\s*[:.\-]\s*/i,"").trim()
4147
- return t?`Plan ready: ${t}`:"Plan ready"}async function p(e,t,r,c,l,d,h){const{toSessionId:p,questionType:f,questionContent:m,priority:w,expiresInHours:b,relatesTo:v}=e
4148
- let{forceAsk:$}=e
4169
+ return t?`Plan ready: ${t}`:"Plan ready"}function p(e,t){const r=e?.content??{},n=String(e?.response?.selectedOptions?.[0]??r.defaultOption??t.defaultOption??""),o=(Array.isArray(r.options)?r.options:t.options||[]).find(e=>e?.id===n),s=o?`${o.label??o.id}${o.description?`: ${o.description}`:""}`:n
4170
+ return Array.isArray(r.criteria)&&r.criteria.length>0?`\u{1f916} **Answered "not yet"** (Self-driving): **${s}**\n\nBuild with a clearly marked placeholder and go on: do not wait for the user. Nothing was recorded as their check; the criterion stays open and the card at the end of the work asks them for it. It is on the record as question **${e.messageCode}**; if they answer it, the answer reaches you like any answer.`:`\u{1f916} **Taken on its default** (Self-driving): **${s}**\n\nProceed on it now: do not wait for the user. It is on the record as question **${e.messageCode}**, closed on its default; the user can still change it from its card, and a change reaches you like any answer. Do NOT also call record-decision for it.`}async function f(e,t,r,c,l,d,h){const{toSessionId:f,questionType:m,questionContent:g,priority:b,expiresInHours:v,relatesTo:k}=e
4171
+ let{forceAsk:S}=e
4149
4172
  const E={...Array.isArray(e.replaces)&&e.replaces.length>0?{replaces:e.replaces}:{},...!0===e.keepsOpen?{keepsOpen:!0}:{}}
4150
- if(!f||!m?.question)throw new g.ValidationError("Question requires questionType and questionContent.question")
4151
- const S=Array.isArray(e.criteria)?[...new Set(e.criteria.map(e=>String(e).trim()).filter(Boolean))]:[]
4152
- S.length>0&&(delete m.defaultOption,m.onExpiry="escalate",$=!0)
4153
- const _=i(0,0,"session_message_operations",0,d)
4154
- let C=await async function(e,t,r,n){if("parent"===e){const e=await s(t,r,n)
4155
- return e?(n.info('Resolved toSessionId "parent" to actual ID',{parentId:e}),e):void n.warn('report toSessionId "parent" but this session has no parent; recording without a parent target',{fromSessionId:t})}return void 0===e?void 0:(0,q.asExternal)(e)}(p,_,c,r),T=!1
4156
- if(C)try{const[e,t]=await Promise.all([n(_),n(C)])
4157
- "planner"===e&&"alchemist"===t&&(C=void 0,T=!0)}catch{}if(!("alternatives"!==f||m.options&&Array.isArray(m.options)))throw new g.ValidationError("Not sent: an alternatives question needs questionContent.options, a list of { id, label, description } INSIDE questionContent (options anywhere else are dropped). Nothing was posted.")
4158
- let R="",A=0,O=null,M=!1
4159
- if(!C){let n="balanced",o=!1
4160
- try{const e=await l.getInteractionPosture(_)
4161
- n=e.posture,o=!0===e.humanInputFloor}catch(e){r.warn("Question gate: posture lookup failed, defaulting to balanced",{error:(0,y.getErrorMessage)(e)})}const s="string"==typeof e.userSaid?e.userSaid:"",i=!!s&&await(0,j.userSaidOnRequest)(c,t.projectId,"",s,{anywhere:!0}).catch(()=>!1)
4162
- i&&(m.onExpiry="escalate"),"how"===m.steer&&m.defaultOption&&(m.onExpiry="escalate")
4163
- const d=(0,k.resolveQuestionGate)({targeted:!1,hasDefault:!!m.defaultOption,priority:w,onExpiry:m.onExpiry,humanInputFloor:o,userAskedToBeAsked:i},n)
4164
- if(o&&"ask-now"===d.disposition&&($=!0),r.info("Question gate resolved",{posture:n,disposition:d.disposition,reason:d.reason}),"self-answer"===d.disposition){const e=m.defaultOption,t=(m.options||[]).find(t=>t?.id===e),n=t?`${t.label}${t.description?`: ${t.description}`:""}`:e
4165
- try{const e=await l.createQuestionSettleAware({fromSessionId:_,questionType:f,question:m.question,options:m.options,context:m.context,defaultOption:m.defaultOption,onExpiry:m.onExpiry,priority:w,...v?{relatesTo:v}:{},takenOnDefault:!0}),t="created"===e.kind?e.message:null
4166
- if(t&&"responded"===t.status)return{content:[{type:"text",text:`\u{1f916} **Taken on its default** (Self-driving): **${n}**\n\nProceed on it now: do not wait for the user. It is on the record as question **${t.messageCode}**, closed on its default; the user can still change it from its card, and a change reaches you like any answer. Do NOT also call record-decision for it.`}]}
4167
- if(t)return{content:[{type:"text",text:`\u2753 Question **${t.messageCode}** was posted to the user and is open: its default could not be taken for them. ${a(t)}`}]}}catch(e){r.warn("Question gate: taking the default failed, posting the question instead",{error:(0,y.getErrorMessage)(e)})}}else{if("bounce"===d.disposition)return{content:[{type:"text",text:[`\u{1f916} **Question bounced by the question gate** (interaction posture: ${n}).`,"","Nobody is routinely answering questions at this posture -- you have to choose.","Pick your best option yourself, then re-call this tool with questionContent.defaultOption set to it: the question is closed on it at once, and the user can still change it from its card.",'Only if this genuinely cannot be decided without the user (credentials, out-of-band knowledge, irreversible external effects): re-ask with priority "high" -- that always reaches the user at any posture. Use it sparingly.'].join("\n")}]}
4168
- if("defer-batch"===d.disposition){const e=m.defaultOption,t=(m.options||[]).find(t=>t?.id===e),n=t?t.label:e
4169
- try{const e=await l.createQuestionSettleAware({fromSessionId:_,questionType:f,question:m.question,options:m.options,context:m.context,allowMultiple:m.allowMultiple,...Array.isArray(m.fields)&&m.fields.length>0?{fields:m.fields}:{},defaultOption:m.defaultOption,onExpiry:m.onExpiry,proceededOnDefault:!0,...m.steer?{steer:m.steer}:{},priority:w,expiresInHours:b,...v?{relatesTo:v}:{},...$?{forceAsk:!0}:{},...E})
4173
+ if(!m||!g?.question)throw new y.ValidationError("Question requires questionType and questionContent.question")
4174
+ const _=Array.isArray(e.criteria)?[...new Set(e.criteria.map(e=>String(e).trim()).filter(Boolean))]:[]
4175
+ _.length>0&&(delete g.defaultOption,g.onExpiry="escalate",S=!0)
4176
+ const C=i(0,0,"session_message_operations",0,d)
4177
+ let q=await async function(e,t,r,n){if("parent"===e){const e=await s(t,r,n)
4178
+ return e?(n.info('Resolved toSessionId "parent" to actual ID',{parentId:e}),e):void n.warn('report toSessionId "parent" but this session has no parent; recording without a parent target',{fromSessionId:t})}return void 0===e?void 0:(0,T.asExternal)(e)}(f,C,c,r),j=!1
4179
+ if(q)try{const[e,t]=await Promise.all([n(C),n(q)])
4180
+ "planner"===e&&"alchemist"===t&&(q=void 0,j=!0)}catch{}if(!("alternatives"!==m||g.options&&Array.isArray(g.options)))throw new y.ValidationError("Not sent: an alternatives question needs questionContent.options, a list of { id, label, description } INSIDE questionContent (options anywhere else are dropped). Nothing was posted.")
4181
+ let A="",O=0,M=null,D=!1,F=!1
4182
+ if(!q){let n="balanced",o=!1
4183
+ try{const e=await l.getInteractionPosture(C)
4184
+ n=e.posture,o=!0===e.humanInputFloor}catch(e){r.warn("Question gate: posture lookup failed, defaulting to balanced",{error:(0,w.getErrorMessage)(e)})}const s="string"==typeof e.userSaid?e.userSaid:"",i=!!s&&await(0,P.userSaidOnRequest)(c,t.projectId,"",s,{anywhere:!0}).catch(()=>!1)
4185
+ i&&(g.onExpiry="escalate",F=!0),"how"===g.steer&&g.defaultOption&&(g.onExpiry="escalate")
4186
+ const d=(0,I.resolveQuestionGate)({targeted:!1,hasDefault:!!g.defaultOption,priority:b,onExpiry:g.onExpiry,humanInputFloor:o,namesCriteria:_.length>0,offersNotYet:(0,I.offersNotYet)(g.options),userAskedToBeAsked:i},n)
4187
+ if(o&&"ask-now"===d.disposition&&(S=!0),r.info("Question gate resolved",{posture:n,disposition:d.disposition,reason:d.reason}),"self-answer"===d.disposition)try{const e=await l.createQuestionSettleAware({fromSessionId:C,questionType:m,question:g.question,options:g.options,context:g.context,defaultOption:g.defaultOption,onExpiry:g.onExpiry,...g.steer?{steer:g.steer}:{},priority:b,expiresInHours:v,...k?{relatesTo:k}:{},...Array.isArray(g.fields)&&g.fields.length>0?{fields:g.fields}:{},...E,..._.length>0?{criteria:_}:{},takenOnDefault:!0})
4188
+ if("settled"===e.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${e.settled.guidance}`}]}
4189
+ const t=e.message
4190
+ if(t&&"responded"===t.status)return{content:[{type:"text",text:p(t,g)}]}
4191
+ if(t)return{content:[{type:"text",text:`\u2753 Question **${t.messageCode}** was posted to the user and is open: its default could not be taken for them. ${a(t)}`}]}}catch(e){r.warn("Question gate: taking the default failed, posting the question instead",{error:(0,w.getErrorMessage)(e)})}else{if("bounce"===d.disposition)return{content:[{type:"text",text:[`\u{1f916} **Question bounced by the question gate** (interaction posture: ${n}).`,"","Nobody is routinely answering questions at this posture -- you have to choose.","Pick your best option yourself, then re-call this tool with questionContent.defaultOption set to it: the question is closed on it at once, and the user can still change it from its card.",'Only if this genuinely cannot be decided without the user (credentials, out-of-band knowledge, irreversible external effects): re-ask with priority "high" and no default -- that reaches the user. Use it sparingly.'].join("\n")}]}
4192
+ if("defer-batch"===d.disposition){const e=g.defaultOption,t=(g.options||[]).find(t=>t?.id===e),n=t?t.label:e
4193
+ try{const e=await l.createQuestionSettleAware({fromSessionId:C,questionType:m,question:g.question,options:g.options,context:g.context,allowMultiple:g.allowMultiple,...Array.isArray(g.fields)&&g.fields.length>0?{fields:g.fields}:{},defaultOption:g.defaultOption,onExpiry:g.onExpiry,proceededOnDefault:!0,...g.steer?{steer:g.steer}:{},priority:b,expiresInHours:v,...k?{relatesTo:k}:{},...S?{forceAsk:!0}:{},...E})
4170
4194
  if("settled"===e.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${e.settled.guidance}`}]}
4171
4195
  const t=e.message
4172
- return{content:[{type:"text",text:`\u{1f4e5} **Question held for the next checkpoint** (${d.reason}).\n\n**Proceed on your default now: ${n}**\n\nIt was recorded as **${t.messageCode}** and will be presented to the user as part of the next checkpoint bundle (e.g. when a request reaches review or a plan is handed off). If it expires first it resolves to your default. If the eventual answer differs from your default you will receive a correction: fold it in then.`}]}}catch(e){r.warn("Question gate: deferred create failed, posting the question normally",{error:(0,y.getErrorMessage)(e)})}}else d.hint&&(R=`\n\n\u{1f4a1} ${d.hint}`)}const u=m.context
4173
- if(!("object"==typeof u&&null!==u||"string"==typeof u&&/^[\[{]/.test(u.trim()))){const e=(0,k.resolveHoldPolicy)(n,{proceedsOnDefault:(0,k.proceedsOnDefault)({defaultOption:m.defaultOption,onExpiry:m.onExpiry,steer:m.steer,priority:w,namedCriteria:S.length})})
4174
- A=e.holdMs,O=n,M=0===A&&"balanced"===(0,k.normalizePosture)(n),A>0&&r.info("Question will sync-hold",{holdMs:A,posture:n,reason:e.reason})}}r.info("Creating question",{fromSessionId:_,toSessionId:C,questionType:f})
4175
- const D=await l.createQuestionSettleAware({fromSessionId:_,toSessionId:C,questionType:f,question:m.question,options:m.options,context:m.context,allowMultiple:m.allowMultiple,...Array.isArray(m.fields)&&m.fields.length>0?{fields:m.fields}:{},defaultOption:m.defaultOption,onExpiry:"collaborative"===O?"escalate":m.onExpiry,...m.steer?{steer:m.steer}:{},priority:w,expiresInHours:b,...v?{relatesTo:v}:{},...A>0?{holdUntil:new Date(Date.now()+A).toISOString()}:{},...M?{proceededOnDefault:!0}:{},...$?{forceAsk:!0}:{},...E,...S.length>0?{criteria:S}:{}})
4176
- if("settled"===D.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${D.settled.guidance}`}]}
4177
- const F=D.message
4178
- if(h?.afterCreate)try{await h.afterCreate({messageCode:F.messageCode})}catch(e){r.warn("sendQuestion afterCreate hook failed (question already posted)",{code:F.messageCode,error:(0,y.getErrorMessage)(e)})}if(A>0&&!F?.content?.holdUntil){r.info("Sync-hold skipped by server (no UI client connected)",{code:F.messageCode}),A=0
4179
- const e=F.messageCode
4180
- if("collaborative"===O)return{content:[{type:"text",text:`\u23f3 **Question posted as ${e}; the user is not on the page right now.** The interaction posture is collaborative: this decision stays with the user: do NOT proceed or assume a default. Wrap up your turn cleanly now; you will be automatically resumed with the answer (or a dismissal) whenever it arrives.${(0,P.namedRefsNote)(F?.content?.namedRefs)}`}]}
4181
- if(m.defaultOption)return{content:[{type:"text",text:`\u{1f4e8} **Question posted as ${e}; the user is not on the page right now.**\n\n**Proceed on your default now: ${String(m.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.${(0,P.namedRefsNote)(F?.content?.namedRefs)}`}]}}if(A>0){const e=F.messageCode,t=await(0,I.runSyncHold)({client:l,logger:r,messageCode:e,holdMs:A,proceededOnDefault:"collaborative"!==O&&!!m.defaultOption})
4182
- if("answered"===t.kind)return{content:[{type:"text",text:`\u2705 **Answered while you waited.**\n\n${a(t.message)}\n\nContinue based on this answer now.${(0,P.namedRefsNote)(F?.content?.namedRefs)}`}]}
4196
+ return{content:[{type:"text",text:`\u{1f4e5} **Question held for the next checkpoint** (${d.reason}).\n\n**Proceed on your default now: ${n}**\n\nIt was recorded as **${t.messageCode}** and will be presented to the user as part of the next checkpoint bundle (e.g. when a request reaches review or a plan is handed off). If it expires first it resolves to your default. If the eventual answer differs from your default you will receive a correction: fold it in then.`}]}}catch(e){r.warn("Question gate: deferred create failed, posting the question normally",{error:(0,w.getErrorMessage)(e)})}}else d.hint&&(A=`\n\n\u{1f4a1} ${d.hint}`)}const u=g.context
4197
+ if(!("object"==typeof u&&null!==u||"string"==typeof u&&/^[\[{]/.test(u.trim()))){const e=(0,I.resolveHoldPolicy)(n,{proceedsOnDefault:(0,I.proceedsOnDefault)({defaultOption:g.defaultOption,onExpiry:g.onExpiry,steer:g.steer,priority:b,namedCriteria:_.length})})
4198
+ O=e.holdMs,M=n,D=0===O&&"balanced"===(0,I.normalizePosture)(n),O>0&&r.info("Question will sync-hold",{holdMs:O,posture:n,reason:e.reason})}}r.info("Creating question",{fromSessionId:C,toSessionId:q,questionType:m})
4199
+ const N=await l.createQuestionSettleAware({fromSessionId:C,toSessionId:q,questionType:m,question:g.question,options:g.options,context:g.context,allowMultiple:g.allowMultiple,...Array.isArray(g.fields)&&g.fields.length>0?{fields:g.fields}:{},defaultOption:g.defaultOption,onExpiry:"collaborative"===M?"escalate":g.onExpiry,...g.steer?{steer:g.steer}:{},priority:b,expiresInHours:v,...k?{relatesTo:k}:{},...O>0?{holdUntil:new Date(Date.now()+O).toISOString()}:{},...D?{proceededOnDefault:!0}:{},...F?{userAsked:!0}:{},...S?{forceAsk:!0}:{},...E,..._.length>0?{criteria:_}:{}})
4200
+ if("settled"===N.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${N.settled.guidance}`}]}
4201
+ const z=N.message
4202
+ if(!q&&"responded"===z?.status&&"self-driving"===z?.response?.settledBy)return{content:[{type:"text",text:p(z,g)}]}
4203
+ if(h?.afterCreate)try{await h.afterCreate({messageCode:z.messageCode})}catch(e){r.warn("sendQuestion afterCreate hook failed (question already posted)",{code:z.messageCode,error:(0,w.getErrorMessage)(e)})}if(O>0&&!z?.content?.holdUntil){r.info("Sync-hold skipped by server (no UI client connected)",{code:z.messageCode}),O=0
4204
+ const e=z.messageCode
4205
+ if("collaborative"===M)return{content:[{type:"text",text:`\u23f3 **Question posted as ${e}; the user is not on the page right now.** The interaction posture is collaborative: this decision stays with the user: do NOT proceed or assume a default. Wrap up your turn cleanly now; you will be automatically resumed with the answer (or a dismissal) whenever it arrives.${(0,R.namedRefsNote)(z?.content?.namedRefs)}`}]}
4206
+ if(g.defaultOption)return{content:[{type:"text",text:`\u{1f4e8} **Question posted as ${e}; the user is not on the page right now.**\n\n**Proceed on your default now: ${String(g.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.${(0,R.namedRefsNote)(z?.content?.namedRefs)}`}]}}if(O>0){const e=z.messageCode,t=await(0,$.runSyncHold)({client:l,logger:r,messageCode:e,holdMs:O,proceededOnDefault:"collaborative"!==M&&!!g.defaultOption})
4207
+ if("answered"===t.kind)return{content:[{type:"text",text:`\u2705 **Answered while you waited.**\n\n${a(t.message)}\n\nContinue based on this answer now.${(0,R.namedRefsNote)(z?.content?.namedRefs)}`}]}
4183
4208
  let n
4184
- return n="expired"===t.kind?`\u23f3 **The question expired unanswered** (**${e}**). Decide yourself now: pick the best option, record it with the record-decision operation, and proceed. No automatic resume will come for this question.`:"collaborative"===O?`\u23f3 **No answer yet.** The interaction posture is collaborative: this decision stays with the user: do NOT proceed or assume a default. Wrap up your turn cleanly now; the question remains open as **${e}** and you will be automatically resumed with the answer (or a dismissal) whenever it arrives.`:m.defaultOption?`\u23f3 **No answer within the hold window.** The question stays open as **${e}** (inbox).\n\n**Proceed on your default now: ${String(m.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.`:`\u23f3 **No answer within the hold window** and no default was stated. **Decide yourself now**: pick the best option, record it with the record-decision operation (so the user can review or correct it later), and proceed. The question stays open as **${e}**; if the user answers later you will be resumed: treat it as confirmation or a correction.`,{content:[{type:"text",text:n+(0,P.namedRefsNote)(F?.content?.namedRefs)}]}}if(M)return{content:[{type:"text",text:`\u2705 Question posted as **${F.messageCode}**. It goes ahead on its default, so nothing waits for it.\n\n**Proceed on your default now: ${String(m.defaultOption)}**\n\nIf the user answers differently, you will be resumed with a correction: fold it in then.${R}${(0,P.namedRefsNote)(F?.content?.namedRefs)}`}]}
4185
- let N=""
4186
- if(C){const e=await n(_)??"agent",s={alchemy:{kind:"question-received",payload:{messageCode:F.messageCode,question:m.question,questionType:f,options:m.options,context:m.context,allowMultiple:m.allowMultiple,...Array.isArray(m.fields)&&m.fields.length>0?{fields:m.fields}:{},fromAgent:e,fromSessionId:_,priority:F.priority,expiresAt:F.expiresAt,...v?{relatesTo:v}:{}}}},i=x.HUB_AGENT_TYPES.has(String(await n(C)??""))?`${u(F)}\n\n${exports.zk}${await async function(e,t,r){if(!t)return""
4209
+ return n="expired"===t.kind?`\u23f3 **The question expired unanswered** (**${e}**). Decide yourself now: pick the best option, record it with the record-decision operation, and proceed. No automatic resume will come for this question.`:"collaborative"===M?`\u23f3 **No answer yet.** The interaction posture is collaborative: this decision stays with the user: do NOT proceed or assume a default. Wrap up your turn cleanly now; the question remains open as **${e}** and you will be automatically resumed with the answer (or a dismissal) whenever it arrives.`:g.defaultOption?`\u23f3 **No answer within the hold window.** The question stays open as **${e}** (inbox).\n\n**Proceed on your default now: ${String(g.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.`:`\u23f3 **No answer within the hold window** and no default was stated. **Decide yourself now**: pick the best option, record it with the record-decision operation (so the user can review or correct it later), and proceed. The question stays open as **${e}**; if the user answers later you will be resumed: treat it as confirmation or a correction.`,{content:[{type:"text",text:n+(0,R.namedRefsNote)(z?.content?.namedRefs)}]}}if(D)return{content:[{type:"text",text:`\u2705 Question posted as **${z.messageCode}**. It goes ahead on its default, so nothing waits for it.\n\n**Proceed on your default now: ${String(g.defaultOption)}**\n\nIf the user answers differently, you will be resumed with a correction: fold it in then.${A}${(0,R.namedRefsNote)(z?.content?.namedRefs)}`}]}
4210
+ let L=""
4211
+ if(q){const e=await n(C)??"agent",s={alchemy:{kind:"question-received",payload:{messageCode:z.messageCode,question:g.question,questionType:m,options:g.options,context:g.context,allowMultiple:g.allowMultiple,...Array.isArray(g.fields)&&g.fields.length>0?{fields:g.fields}:{},fromAgent:e,fromSessionId:C,priority:z.priority,expiresAt:z.expiresAt,...k?{relatesTo:k}:{}}}},i=x.HUB_AGENT_TYPES.has(String(await n(q)??""))?`${u(z)}\n\n${exports.zk}${await async function(e,t,r){if(!t)return""
4187
4212
  try{const n=await e.proxyGet(`/api/projects/${t}/hub-digest/${encodeURIComponent(r)}`),o=String((n?.data??n)?.text??"")
4188
- return o?`\n${o}`:""}catch{return""}}(c,t.projectId,C)}`:u(F)
4189
- N=await o(C,i,0,r,s)?"\n\n\u{1f4ec} The target session has been notified.":"\n\n\u26a0\ufe0f Could not notify the target session (may be offline)."}return{content:[{type:"text",text:`\u2705 Question created!${N}${R}${(0,P.namedRefsNote)(F?.content?.namedRefs)}${T?"\n\n\u21aa Sent to the user, not the Alchemist: a planner plans with the user, and design questions are theirs to answer.":""}\n\n**Message Code: ${F.messageCode}**\n\nUse this code to check for answers or share with other sessions.\n\n${a(F)}`}]}}exports.zk=void 0,exports.handleSendQuestion=p,exports.handleSessionMessageOperations=async function(e,c,u,C,j,M){const D=u.child({component:"session_message_operations"})
4190
- if(!j)throw new g.ValidationError("API client not available")
4213
+ return o?`\n${o}`:""}catch{return""}}(c,t.projectId,q)}`:u(z)
4214
+ L=await o(q,i,0,r,s)?"\n\n\u{1f4ec} The target session has been notified.":"\n\n\u26a0\ufe0f Could not notify the target session (may be offline)."}return{content:[{type:"text",text:`\u2705 Question created!${L}${A}${(0,R.namedRefsNote)(z?.content?.namedRefs)}${j?"\n\n\u21aa Sent to the user, not the Alchemist: a planner plans with the user, and design questions are theirs to answer.":""}\n\n**Message Code: ${z.messageCode}**\n\nUse this code to check for answers or share with other sessions.\n\n${a(z)}`}]}}exports.zk=void 0,exports.handleSendQuestion=f,exports.handleSessionMessageOperations=async function(e,c,u,p,q,P){const D=u.child({component:"session_message_operations"})
4215
+ if(!q)throw new y.ValidationError("API client not available")
4191
4216
  const{operation:F}=e
4192
- D.info("Session message operation",{operation:F}),C.incrementCounter("session_message_operations",1,{operation:F})
4193
- try{switch(F){case"send":if(!e.type)throw new g.ValidationError('send operation requires type ("question" or "report")')
4194
- return(0,b.withAlchemyKind)(await async function(e,r,a,c,d){const{type:u}=e
4195
- if(!u)throw new g.ValidationError('send operation requires type ("question" or "report")')
4217
+ D.info("Session message operation",{operation:F}),p.incrementCounter("session_message_operations",1,{operation:F})
4218
+ try{switch(F){case"send":if(!e.type)throw new y.ValidationError('send operation requires type ("question" or "report")')
4219
+ return(0,v.withAlchemyKind)(await async function(e,r,a,c,d){const{type:u}=e
4220
+ if(!u)throw new y.ValidationError('send operation requires type ("question" or "report")')
4196
4221
  const h=c.getSessionMessageClient()
4197
- if(!h)throw new g.ValidationError("Session message client not available")
4198
- if("question"===u)return await p(e,r,a,c,h,d)
4199
- if("report"===u)return await async function(e,r,a,c,d,u){const{status:h,summary:b,details:k,finish:I,relatesTo:$,silent:C,attention:T,reportKind:j}=e
4200
- if(!h||!b)throw new g.ValidationError("Report requires status and summary")
4201
- const R=(0,m.requestNeedingAnchor)({summary:b,details:k},e)
4202
- if(R)throw new g.ValidationError((0,m.misfiledReportMessage)(R))
4222
+ if(!h)throw new y.ValidationError("Session message client not available")
4223
+ if("question"===u)return await f(e,r,a,c,h,d)
4224
+ if("report"===u)return await async function(e,r,a,c,d,u){const{status:h,summary:p,details:v,finish:I,relatesTo:$,silent:S,attention:q,reportKind:j}=e
4225
+ if(!h||!p)throw new y.ValidationError("Report requires status and summary")
4226
+ const P=(0,g.requestNeedingAnchor)({summary:p,details:v},e)
4227
+ if(P)throw new y.ValidationError((0,g.misfiledReportMessage)(P))
4203
4228
  const A=i(0,0,"session_message_operations",0,u)
4204
- if("blocked"!==h&&"act"!==T)try{const e=await(0,w.getServerUrl)(r,a)
4229
+ if("blocked"!==h&&"act"!==q)try{const e=await(0,b.getServerUrl)(r,a)
4205
4230
  if(e){const t=await fetch(`${e.replace(/\/$/,"")}/session/${A}/message?limit=12`,{signal:AbortSignal.timeout(5e3)})
4206
- if(t.ok){const e=(0,_.answerEchoOf)(await t.json())
4207
- if(e)return{content:[{type:"text",text:(0,_.ANSWER_ECHO_REFUSAL)(e.messageCode)}],isError:!0}}}}catch{}if("blocked"===h||"act"===T&&"success"!==h){const t=await n(A),o="setup-architect"===t&&"request"===(0,S.parseBlockedOn)(e.blockedOn)?.kind
4208
- if((0,f.isWorkerAgentType)(t)||o){const t=await async function(e,t,r,n,o,s,i){const a=r.projectId
4231
+ if(t.ok){const e=(0,C.answerEchoOf)(await t.json())
4232
+ if(e)return{content:[{type:"text",text:(0,C.ANSWER_ECHO_REFUSAL)(e.messageCode)}],isError:!0}}}}catch{}if("blocked"===h||"act"===q&&"success"!==h){const t=await n(A),o="setup-architect"===t&&"request"===(0,_.parseBlockedOn)(e.blockedOn)?.kind
4233
+ if((0,m.isWorkerAgentType)(t)||o){const t=await async function(e,t,r,n,o,s,i){const a=r.projectId
4209
4234
  if(!a)return null
4210
4235
  let c="string"==typeof e.requestId&&/^req\d{6}$/.test(e.requestId.trim())?e.requestId.trim():null,l=!1
4211
4236
  try{const e=await o.proxyGet(`/api/projects/${a}/session-request-associations/by-session/${encodeURIComponent(t)}?activeOnly=true`),r=(Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[]).map(e=>e.requestLocalId??e.request_local_id).filter(Boolean)
4212
- c||(c=r[0]??null),l=!!c&&r.includes(c)}catch(e){return n.warn("blocked report: association lookup failed; filing as a plain report",{error:(0,y.getErrorMessage)(e)}),null}if(!c)return null
4237
+ c||(c=r[0]??null),l=!!c&&r.includes(c)}catch(e){return n.warn("blocked report: association lookup failed; filing as a plain report",{error:(0,w.getErrorMessage)(e)}),null}if(!c)return null
4213
4238
  const d=r.workspaceManager?.getCurrentContext(r.currentSessionId)?.basePath||r.basePath
4214
4239
  let u=[],h=[]
4215
- const f={requestId:c,owns:l,defaultBranch:"main",mainAheadBy:null,markerFiles:[],testRunning:!1,questionPending:!1}
4216
- try{Object.assign(f,await(0,S.gitBlockFacts)(d,c,r.gitBinary))}catch(e){n.warn("blocked report: git facts unavailable",{error:(0,y.getErrorMessage)(e)})}try{const e=await o.proxyGet(`/api/projects/${a}/requests/${c}/wait-state`),r=e?.data??e??{}
4217
- f.testRunning=!0===r.runInFlight,f.questionPending=Array.isArray(r.pendingQuestionSessions)&&r.pendingQuestionSessions.includes(t),Array.isArray(r.userQuestionCodes)&&(u=r.userQuestionCodes.filter(e=>"string"==typeof e)),Array.isArray(r.userParkCodes)&&(h=r.userParkCodes.filter(e=>"string"==typeof e))}catch{}const m=`${e.summary??""}\n${e.details??""}`,g=(0,S.disproveBlock)(m,f)
4218
- if(g.length>0)return n.info("blocked report disproved from the record; not filed",{sessionId:t,requestId:c,disproofs:g.length}),{content:[{type:"text",text:`\u26d4 Not filed: the record says this does not block you.\n\n${g.map(e=>`- ${e}`).join("\n")}\n\n${(0,S.renderBlockFacts)(f)}`}]}
4219
- const w=(0,S.parseBlockedOn)(e.blockedOn)
4220
- if(!w)return{content:[{type:"text",text:`\u26d4 Not filed. ${S.BLOCKED_ON_HELP}\n\n${(0,S.renderBlockFacts)(f)}`}],isError:!0}
4221
- if("request"===w.kind)return w.requestId===c?{content:[{type:"text",text:`\u26d4 Not filed: ${c} cannot wait on itself. ${S.BLOCKED_ON_HELP}`}],isError:!0}:(await o.proxyPost(`/api/projects/${a}/requests/${c}/waits`,{waitsOn:w.requestId,reason:String(e.summary).slice(0,300),sessionId:t}),{content:[{type:"text",text:`\u23f3 Wait declared: ${c} waits on ${w.requestId} (${String(e.summary).slice(0,120)}). This is a registered wait: you are idle by design until ${w.requestId} settles, and you will be told the moment it does. End your turn now; do nothing else on ${c}.`}]})
4222
- if("decision"===w.kind&&u.length>0)return await s.createReport({fromSessionId:t,status:"blocked",attention:"act",summary:String(e.summary??"").slice(0,2e3),...e.details?{details:String(e.details)}:{},requestId:c}),{content:[{type:"text",text:`\u{1f4ec} Posted to the user on ${c}, beside the question they already have open (${u.join(", ")}). That question is the decision you are waiting for: no second wait was filed. Stop here; you will be resumed when they answer.`}]}
4223
- const b={toSessionId:"parent",questionType:"free-form",priority:"high",expiresInHours:1,questionContent:{question:`${c} is blocked on a decision: ${String(e.summary).slice(0,400)}`,context:`[blocked on a decision, from the worker of ${c}] ${String(e.details??"").slice(0,1500)}\n\nIf you cannot decide this, do not acknowledge it: let it escalate to the user.`,onExpiry:"escalate"}},v=await p({...b,requestId:c,forceAsk:!0,keepsOpen:!0},r,n,o,s,i)
4240
+ const p={requestId:c,owns:l,defaultBranch:"main",mainAheadBy:null,markerFiles:[],testRunning:!1,questionPending:!1}
4241
+ try{Object.assign(p,await(0,_.gitBlockFacts)(d,c,r.gitBinary))}catch(e){n.warn("blocked report: git facts unavailable",{error:(0,w.getErrorMessage)(e)})}try{const e=await o.proxyGet(`/api/projects/${a}/requests/${c}/wait-state`),r=e?.data??e??{}
4242
+ p.testRunning=!0===r.runInFlight,p.questionPending=Array.isArray(r.pendingQuestionSessions)&&r.pendingQuestionSessions.includes(t),Array.isArray(r.userQuestionCodes)&&(u=r.userQuestionCodes.filter(e=>"string"==typeof e)),Array.isArray(r.userParkCodes)&&(h=r.userParkCodes.filter(e=>"string"==typeof e))}catch{}const m=`${e.summary??""}\n${e.details??""}`,g=(0,_.disproveBlock)(m,p)
4243
+ if(g.length>0)return n.info("blocked report disproved from the record; not filed",{sessionId:t,requestId:c,disproofs:g.length}),{content:[{type:"text",text:`\u26d4 Not filed: the record says this does not block you.\n\n${g.map(e=>`- ${e}`).join("\n")}\n\n${(0,_.renderBlockFacts)(p)}`}]}
4244
+ const y=(0,_.parseBlockedOn)(e.blockedOn)
4245
+ if(!y)return{content:[{type:"text",text:`\u26d4 Not filed. ${_.BLOCKED_ON_HELP}\n\n${(0,_.renderBlockFacts)(p)}`}],isError:!0}
4246
+ if("request"===y.kind)return y.requestId===c?{content:[{type:"text",text:`\u26d4 Not filed: ${c} cannot wait on itself. ${_.BLOCKED_ON_HELP}`}],isError:!0}:(await o.proxyPost(`/api/projects/${a}/requests/${c}/waits`,{waitsOn:y.requestId,reason:String(e.summary).slice(0,300),sessionId:t}),{content:[{type:"text",text:`\u23f3 Wait declared: ${c} waits on ${y.requestId} (${String(e.summary).slice(0,120)}). This is a registered wait: you are idle by design until ${y.requestId} settles, and you will be told the moment it does. End your turn now; do nothing else on ${c}.`}]})
4247
+ if("decision"===y.kind&&u.length>0)return await s.createReport({fromSessionId:t,status:"blocked",attention:"act",summary:String(e.summary??"").slice(0,2e3),...e.details?{details:String(e.details)}:{},requestId:c}),{content:[{type:"text",text:`\u{1f4ec} Posted to the user on ${c}, beside the question they already have open (${u.join(", ")}). That question is the decision you are waiting for: no second wait was filed. Stop here; you will be resumed when they answer.`}]}
4248
+ const b={toSessionId:"parent",questionType:"free-form",priority:"high",expiresInHours:1,questionContent:{question:`${c} is blocked on a decision: ${String(e.summary).slice(0,400)}`,context:`[blocked on a decision, from the worker of ${c}] ${String(e.details??"").slice(0,1500)}\n\nIf you cannot decide this, do not acknowledge it: let it escalate to the user.`,onExpiry:"escalate"}},v=await f({...b,requestId:c,forceAsk:!0,keepsOpen:!0},r,n,o,s,i)
4224
4249
  return{...v,content:[...v.content,{type:"text",text:"\n\n\u23f8 Filed as a QUESTION to your parent (a registered wait), not a report: it must be answered, and it escalates to the user if the parent cannot. You will get the answer as a message. Meanwhile work on anything not blocked, then end your turn."}]}}(e,A,r,a,c,d,u)
4225
- if(t)return t}}const O=await(async()=>{if(e.toSessionId&&"parent"!==e.toSessionId)return a.info("Using explicit toSessionId for report",{parentSessionId:e.toSessionId}),(0,q.asExternal)(e.toSessionId)
4250
+ if(t)return t}}const O=await(async()=>{if(e.toSessionId&&"parent"!==e.toSessionId)return a.info("Using explicit toSessionId for report",{parentSessionId:e.toSessionId}),(0,T.asExternal)(e.toSessionId)
4226
4251
  if("parent"===e.toSessionId)return await s(A,c,a)||void a.warn('toSessionId "parent" but no parent is recorded; filing target-less',{sessionId:A})
4227
4252
  const t=await s(A,c,a)
4228
4253
  if(t)return a.info("Auto-resolved parent session",{parentSessionId:t}),t
4229
4254
  a.info("No parent session; filing report as broadcast",{sessionId:A})})()
4230
4255
  a.info("Submitting report",{sessionId:A,parentSessionId:O,status:h})
4231
- const M=await d.createReport({fromSessionId:A,toSessionId:O,status:h,summary:b,details:k,...$?{relatesTo:$}:{},...T?{attention:T}:{},...j?{reportKind:j}:{},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:"string"==typeof e.area&&e.area.trim()?{area:e.area.trim()}:{},...Array.isArray(e.ruleVerdicts)&&e.ruleVerdicts.length?{extraContent:{ruleVerdicts:e.ruleVerdicts}}:{}}),D=await n(A)??"agent",F=M.content,N="string"==typeof F?.requestId?F.requestId:"request"===F?.anchor?.type&&"string"==typeof F.anchor.id?F.anchor.id:void 0,z={alchemy:{kind:"worker-report",payload:{status:h,summary:b,details:k,fromAgent:D,fromSessionId:A,...$?{relatesTo:$}:{},...N&&/^req\d+$/.test(N)?{requestId:N}:{}}}},L=!(!e.toSessionId||"parent"===e.toSessionId),B=O??(await s(A,c,a).catch(()=>null)||void 0),H=B?await n(B):null
4256
+ const M=await d.createReport({fromSessionId:A,toSessionId:O,status:h,summary:p,details:v,...$?{relatesTo:$}:{},...q?{attention:q}:{},...j?{reportKind:j}:{},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:"string"==typeof e.area&&e.area.trim()?{area:e.area.trim()}:{},...Array.isArray(e.ruleVerdicts)&&e.ruleVerdicts.length?{extraContent:{ruleVerdicts:e.ruleVerdicts}}:{}}),D=await n(A)??"agent",F=M.content,N="string"==typeof F?.requestId?F.requestId:"request"===F?.anchor?.type&&"string"==typeof F.anchor.id?F.anchor.id:void 0,z={alchemy:{kind:"worker-report",payload:{status:h,summary:p,details:v,fromAgent:D,fromSessionId:A,...$?{relatesTo:$}:{},...N&&/^req\d+$/.test(N)?{requestId:N}:{}}}},L=!(!e.toSessionId||"parent"===e.toSessionId),B=O??(await s(A,c,a).catch(()=>null)||void 0),H=B?await n(B):null
4232
4257
  let U=!1
4233
4258
  try{const e=await E.sessionIdentity.getSessionRequestId(c,a,A),n=r.projectId
4234
4259
  if(e&&n){const r=await c.proxyGet(`/api/projects/${n}/requests/${e}`).catch(()=>null),o=(r?.data??r)?.type,{kindProfile:s}=await Promise.resolve().then(()=>t(87780))
4235
- U=!!o&&"settle"===s(o).finalize&&"setup"===s(o).kind}}catch{}const G=(0,x.decideParentWake)({attention:T,explicitTarget:L,silent:C,hasParent:!!B,recipientAgentType:H,senderSettles:U}),W=!(!G.notify||!B)&&await o(B,function(e,t="agent"){const r=e.content||{}
4236
- return(0,v.formatReportNotification)({messageCode:e.messageCode,status:r.reportStatus,summary:r.summary,details:r.details,fromSessionId:e.fromSessionId,senderAgent:t})}(M,D),0,a,z)
4237
- return a.info("report routing",{messageCode:M.messageCode,attention:T,recipientAgentType:H,reason:G.reason,notified:W}),{content:[{type:"text",text:`\u2705 Report submitted.${(0,x.describeParentWake)(G,W)}${"blocked"===String(M.reportStatus??M.status??"")?"\n\n\u23f8 A blocked report registers no wait: the request is still yours and still watched. An acknowledgement from your parent is not a decision. If the block is a merge conflict, complete the request and the markers come to you. If you need a decision, ask a question (it registers a wait); otherwise keep working.":""}${(0,P.namedRefsNote)(M?.content?.namedRefs)}\n\n${l(M)}`}]}}(e,r,a,c,h,d)
4238
- throw new g.ValidationError(`Unknown message type: ${u}. Use "question" or "report".`)}(e,c,D,j,M),r("send",e))
4239
- case"note":return(0,b.withAlchemyKind)(await async function(e,t,r,n,o){const s=n.getSessionMessageClient()
4240
- if(!s)throw new g.ValidationError("Session message client not available")
4260
+ U=!!o&&"settle"===s(o).finalize&&"setup"===s(o).kind}}catch{}const G=(0,x.decideParentWake)({attention:q,explicitTarget:L,silent:S,hasParent:!!B,recipientAgentType:H,senderSettles:U}),W=!(!G.notify||!B)&&await o(B,function(e,t="agent"){const r=e.content||{}
4261
+ return(0,k.formatReportNotification)({messageCode:e.messageCode,status:r.reportStatus,summary:r.summary,details:r.details,fromSessionId:e.fromSessionId,senderAgent:t})}(M,D),0,a,z)
4262
+ return a.info("report routing",{messageCode:M.messageCode,attention:q,recipientAgentType:H,reason:G.reason,notified:W}),{content:[{type:"text",text:`\u2705 Report submitted.${(0,x.describeParentWake)(G,W)}${"blocked"===String(M.reportStatus??M.status??"")?"\n\n\u23f8 A blocked report registers no wait: the request is still yours and still watched. An acknowledgement from your parent is not a decision. If the block is a merge conflict, complete the request and the markers come to you. If you need a decision, ask a question (it registers a wait); otherwise keep working.":""}${(0,R.namedRefsNote)(M?.content?.namedRefs)}\n\n${l(M)}`}]}}(e,r,a,c,h,d)
4263
+ throw new y.ValidationError(`Unknown message type: ${u}. Use "question" or "report".`)}(e,c,D,q,P),r("send",e))
4264
+ case"note":return(0,v.withAlchemyKind)(await async function(e,t,r,n,o){const s=n.getSessionMessageClient()
4265
+ if(!s)throw new y.ValidationError("Session message client not available")
4241
4266
  const a="string"==typeof e?.text?e.text.trim():""
4242
- if(!a)throw new g.ValidationError("note requires text (one or two plain sentences in your own voice)")
4243
- const c=!0===e?.milestone,l=i(0,0,"session_message_operations",0,o),d=(0,m.requestNeedingAnchor)({summary:a},e)
4244
- if(d)throw new g.ValidationError((0,m.misfiledReportMessage)(d))
4267
+ if(!a)throw new y.ValidationError("note requires text (one or two plain sentences in your own voice)")
4268
+ const c=!0===e?.milestone,l=i(0,0,"session_message_operations",0,o),d=(0,g.requestNeedingAnchor)({summary:a},e)
4269
+ if(d)throw new y.ValidationError((0,g.misfiledReportMessage)(d))
4245
4270
  const u=await s.createReport({fromSessionId:l,status:"success",summary:a,attention:"fyi",reportKind:c?"milestone":"progress",extraContent:{note:!0},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:{}}),h=u?.content?.anchor,p="request"===h?.type&&h?.id?`on ${h.id}`:"at the project (no request is associated with this session)"
4246
- return{content:[{type:"text",text:c?`\u2691 Milestone recorded ${p} (${u?.messageCode??"recorded"}). It shows in the request's flow and as one line in the user's stream.${(0,P.namedRefsNote)(u?.content?.namedRefs)}`:`\u2022 Note recorded ${p} (${u?.messageCode??"recorded"}). It shows in the request's flow; nobody was woken.${(0,P.namedRefsNote)(u?.content?.namedRefs)}`}]}}(e,0,0,j,M),r("note",e))
4247
- case"reply":if(!e.messageCode)throw new g.ValidationError("reply operation requires messageCode")
4248
- return(0,b.withAlchemyKind)(await async function(e,t,r,s,i){const{messageCode:c}=e
4249
- if(!c)throw new g.ValidationError("reply operation requires messageCode")
4271
+ return{content:[{type:"text",text:c?`\u2691 Milestone recorded ${p} (${u?.messageCode??"recorded"}). It shows in the request's flow and as one line in the user's stream.${(0,R.namedRefsNote)(u?.content?.namedRefs)}`:`\u2022 Note recorded ${p} (${u?.messageCode??"recorded"}). It shows in the request's flow; nobody was woken.${(0,R.namedRefsNote)(u?.content?.namedRefs)}`}]}}(e,0,0,q,P),r("note",e))
4272
+ case"reply":if(!e.messageCode)throw new y.ValidationError("reply operation requires messageCode")
4273
+ return(0,v.withAlchemyKind)(await async function(e,t,r,s,i){const{messageCode:c}=e
4274
+ if(!c)throw new y.ValidationError("reply operation requires messageCode")
4250
4275
  const d=s.getSessionMessageClient()
4251
- if(!d)throw new g.ValidationError("Session message client not available")
4276
+ if(!d)throw new y.ValidationError("Session message client not available")
4252
4277
  if(c.startsWith("Q-")){const{text:t,selectedOptions:n,notes:o,quote:s,sectionAnswers:l}=e,u=i?.sessionId
4253
- if(!u)throw new g.ValidationError("Cannot answer: this call carries no session identity.")
4278
+ if(!u)throw new y.ValidationError("Cannot answer: this call carries no session identity.")
4254
4279
  if(!0===e.escalate){const{escalatedTo:e}=await d.escalateQuestion(c,{escalatedBySessionId:u,notes:o})
4255
4280
  return{content:[{type:"text",text:`\u2934\ufe0f Question ${c} escalated to the user as **${e}**.\n\nYour recommendation${o?" was attached":" was not attached (none given)"}. The original asker keeps working and will receive the user's answer directly: you are done with this question.`}]}}r.info("Answering question",{messageCode:c,respondedBySessionId:u,quoted:!!s})
4256
4281
  const h=await d.answerQuestion(c,{respondedBySessionId:u,text:t,selectedOptions:n,notes:o,...s?{quote:s}:{},...l&&"object"==typeof l?{sectionAnswers:l}:{}})
@@ -4261,75 +4286,75 @@ const t="string"==typeof e.text&&e.text.trim()?e.text.trim():void 0,s=await d.ac
4261
4286
  let a=""
4262
4287
  if(!0===s?.alreadyAcknowledged)return{content:[{type:"text",text:`Report ${c} was already acknowledged earlier -- nothing more to do (the reporter was notified the first time).`}]}
4263
4288
  if(s?.fromSessionId){const t=i?.sessionId,c=t?await n(t)??"agent":"agent",l=s.content||{},d={alchemy:{kind:"report-acknowledged",payload:{messageCode:s.messageCode,summary:l.summary,acknowledgedByAgent:c,acknowledgedBySessionId:t}}},u="string"==typeof e.text&&e.text.trim()?e.text.trim():""
4264
- a=await o(s.fromSessionId,`\u2705 Your report **${s.messageCode}** was acknowledged by ${c}.${u?`\n\n${c} replies: ${u}`:""}`,0,r,d)?"\n\n\u{1f4ec} The reporting session has been notified.":"\n\n\u26a0\ufe0f Could not notify the reporting session (may be offline)."}return{content:[{type:"text",text:`\u2705 Report acknowledged!${a}\n\n${l(s)}`}]}}throw new g.ValidationError(`Cannot determine message type from code "${c}". Expected Q-xxx (question) or R-xxx (report).`)}(e,0,D,j,M),r("reply",e))
4265
- case"offer-handoff":{const t=e.offerHandoff||{summary:e.summary||"",requestIds:e.requestIds||[]},r=i(0,0,"session_message_operations",0,M),o=await s(r,j,D)
4289
+ a=await o(s.fromSessionId,`\u2705 Your report **${s.messageCode}** was acknowledged by ${c}.${u?`\n\n${c} replies: ${u}`:""}`,0,r,d)?"\n\n\u{1f4ec} The reporting session has been notified.":"\n\n\u26a0\ufe0f Could not notify the reporting session (may be offline)."}return{content:[{type:"text",text:`\u2705 Report acknowledged!${a}\n\n${l(s)}`}]}}throw new y.ValidationError(`Cannot determine message type from code "${c}". Expected Q-xxx (question) or R-xxx (report).`)}(e,0,D,q,P),r("reply",e))
4290
+ case"offer-handoff":{const t=e.offerHandoff||{summary:e.summary||"",requestIds:e.requestIds||[]},r=i(0,0,"session_message_operations",0,P),o=await s(r,q,D)
4266
4291
  o||D.warn("offer-handoff: no parent session found: notification will be global",{fromSessionId:r})
4267
4292
  let a=!1
4268
- const l="auto-planner"===await n(r)||await async function(e,t,r){const n=O.get(e)
4293
+ const l="auto-planner"===await n(r)||await async function(e,t,r){const n=M.get(e)
4269
4294
  if(void 0!==n)return n
4270
- try{const n=await(0,w.getServerUrl)(t,r)
4295
+ try{const n=await(0,b.getServerUrl)(t,r)
4271
4296
  if(!n)return!1
4272
4297
  const o=await fetch(`${n.replace(/\/$/,"")}/session/${encodeURIComponent(e)}/message`)
4273
4298
  if(!o.ok)return!1
4274
- const s=(await o.json()).find(e=>"user"===e.info?.role),i=(s?.parts?.find(e=>"text"===e.type)?.text||"").includes($.AUTONOMOUS_PLANNER_MARKER)
4275
- return O.set(e,i),i}catch{return!1}}(r,c,D)
4299
+ const s=(await o.json()).find(e=>"user"===e.info?.role),i=(s?.parts?.find(e=>"text"===e.type)?.text||"").includes(S.AUTONOMOUS_PLANNER_MARKER)
4300
+ return M.set(e,i),i}catch{return!1}}(r,c,D)
4276
4301
  let d,u=null
4277
4302
  try{const e=c?.projectId
4278
- if(e){const t=await j.proxyGet(`/api/projects/${e}/session-plan-associations/by-session/${encodeURIComponent(r)}`),n=t?.data??t??[]
4279
- if(u=n[0]?.documentLocalId??null,u)try{const t=await j.proxyGet(`/api/projects/${e}/documents/${encodeURIComponent(u)}`),r=t?.data??t;(!0===r?.metadata?.isStub||!String(r?.content??"").trim())&&(await j.proxyPatch(`/api/projects/${e}/documents/${encodeURIComponent(u)}/archive`,{}),D.info("offer-handoff: archived the empty stub plan document (no plan was written)",{planDocumentId:u}),u=null)}catch(e){D.warn("offer-handoff: stub plan check failed (handoff unaffected)",{planDocumentId:u,error:e instanceof Error?e.message:String(e)})}}}catch{}if(u&&Array.isArray(t.requestIds)&&t.requestIds.length>0){let e=[]
4280
- try{e=(0,R.draftsLeftOut)(await(0,R.planLinkedRequests)(j.getRequestClient(),u),t.requestIds)}catch(e){D.warn("offer-handoff: plan-linked drafts unreadable (hand-off not checked)",{planDocumentId:u,error:(0,y.getErrorMessage)(e)})}if(e.length>0)throw new g.ValidationError(`Hand-off NOT offered: ${u} also guides draft${1===e.length?"":"s"} ${e.join(", ")}, which this hand-off leaves out. Each draft is a step of this plan. Include it in requestIds if it is still part of the plan (update it in place first if the plan changed it), or cancel it with request_operations cancel and a reason if your change replaced it. Then offer the hand-off again.`)}try{const e=j.getSessionMessageClient()
4303
+ if(e){const t=await q.proxyGet(`/api/projects/${e}/session-plan-associations/by-session/${encodeURIComponent(r)}`),n=t?.data??t??[]
4304
+ if(u=n[0]?.documentLocalId??null,u)try{const t=await q.proxyGet(`/api/projects/${e}/documents/${encodeURIComponent(u)}`),r=t?.data??t;(!0===r?.metadata?.isStub||!String(r?.content??"").trim())&&(await q.proxyPatch(`/api/projects/${e}/documents/${encodeURIComponent(u)}/archive`,{}),D.info("offer-handoff: archived the empty stub plan document (no plan was written)",{planDocumentId:u}),u=null)}catch(e){D.warn("offer-handoff: stub plan check failed (handoff unaffected)",{planDocumentId:u,error:e instanceof Error?e.message:String(e)})}}}catch{}if(u&&Array.isArray(t.requestIds)&&t.requestIds.length>0){let e=[]
4305
+ try{e=(0,A.draftsLeftOut)(await(0,A.planLinkedRequests)(q.getRequestClient(),u),t.requestIds)}catch(e){D.warn("offer-handoff: plan-linked drafts unreadable (hand-off not checked)",{planDocumentId:u,error:(0,w.getErrorMessage)(e)})}if(e.length>0)throw new y.ValidationError(`Hand-off NOT offered: ${u} also guides draft${1===e.length?"":"s"} ${e.join(", ")}, which this hand-off leaves out. Each draft is a step of this plan. Include it in requestIds if it is still part of the plan (update it in place first if the plan changed it), or cancel it with request_operations cancel and a reason if your change replaced it. Then offer the hand-off again.`)}try{const e=q.getSessionMessageClient()
4281
4306
  if(e){const n=await e.createQuestion({fromSessionId:r,toSessionId:o||void 0,questionType:"alternatives",question:h(t.summary),options:[{id:"build",label:"Start Building",description:"Start every draft step the plan has when you press it"},{id:"plan",label:"Keep Planning",description:"Continue refining the plan"},{id:"keep",label:"Keep the plan, no build",description:"Keep the plan and its drafts as a reference; nothing starts and the planner ends"}],context:JSON.stringify({type:"plan-completion",plannerSessionId:r,parentSessionId:o,requestIds:t.requestIds,summary:t.summary,planDocumentId:u}),priority:"high"})
4282
- d=n?.messageCode,D.info("offer-handoff: notification question created",{fromSessionId:r,parentSessionId:o,messageCode:d})}else D.warn("offer-handoff: skipping notification question: no message client")}catch(e){D.warn("offer-handoff: failed to create notification question",{error:(0,y.getErrorMessage)(e)})}if(l&&d)try{const e=j.getSessionMessageClient()
4283
- e&&(await e.answerQuestion(d,{respondedBySessionId:"system",selectedOptions:["build"],notes:"auto-confirmed: the workflow plans automatically, so building starts without a hand-off question"}),a=!0,D.info("offer-handoff: auto-confirmed for autonomous planner",{fromSessionId:r,messageCode:d}))}catch(e){D.warn("offer-handoff: failed to auto-confirm for autonomous planner",{error:(0,y.getErrorMessage)(e)})}return(0,b.withAlchemyKind)({content:[{type:"text",text:a?"Handoff auto-confirmed (autonomous planner). Start-building has been queued: you can stop now.":'Handoff offered to user. STOP here and wait: the user will choose "Start Building" or "Keep Planning". You will receive their choice as a new message. Do NOT generate any further output until then.'}],isError:!1,...(0,b.alchemyMeta)({pendingAction:{kind:"plan-handoff",payload:{summary:t.summary,requestIds:t.requestIds,parentSessionId:o,fromSessionId:r,autoConfirmed:a}}})},"offer-handoff")}case"record-decision":{if(!e.decision||"string"!=typeof e.decision||!e.decision.trim())throw new g.ValidationError("record-decision requires 'decision' (the one-line call you made)")
4284
- const t=j.getSessionMessageClient()
4285
- if(!t)throw new g.ValidationError("No session message client available")
4286
- const r=i(0,0,"session_message_operations",0,M),n=e.decision.trim(),o="string"==typeof e.context&&e.context.trim()?e.context.trim():n,s=await t.createReport({fromSessionId:r,status:"success",summary:`Decision: ${n}`,details:[`**Question:** ${o}`,"string"==typeof e.why&&e.why.trim()?`**Why:** ${e.why.trim()}`:"",`**Decided:** ${n}`].filter(Boolean).join("\n"),extraContent:{decision:{question:o,answer:n},decisionRecord:!0},...e.attention?{attention:e.attention}:{},...e.relatesTo?{relatesTo:e.relatesTo}:{},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:"string"==typeof e.area&&e.area.trim()?{area:e.area.trim()}:{}})
4287
- return D.info("Decision recorded",{fromSessionId:r,messageCode:s.messageCode,connection:e.requestId||e.area||"project"}),(0,b.withAlchemyKind)({content:[{type:"text",text:`Decision recorded as ${s.messageCode}: on the record for the human; no session was woken.`}]},"record-decision")}case"brief":return(0,b.withAlchemyKind)(await async function(e,t,r,n,o){const{requestId:s,sections:a}=e
4288
- if(!s||"string"!=typeof s)throw new g.ValidationError('brief operation requires requestId (the request local id, e.g. "req000007")')
4289
- if(!Array.isArray(a)||0===a.length)throw new g.ValidationError("brief operation requires a non-empty sections array")
4307
+ d=n?.messageCode,D.info("offer-handoff: notification question created",{fromSessionId:r,parentSessionId:o,messageCode:d})}else D.warn("offer-handoff: skipping notification question: no message client")}catch(e){D.warn("offer-handoff: failed to create notification question",{error:(0,w.getErrorMessage)(e)})}if(l&&d)try{const e=q.getSessionMessageClient()
4308
+ e&&(await e.answerQuestion(d,{respondedBySessionId:"system",selectedOptions:["build"],notes:"auto-confirmed: the workflow plans automatically, so building starts without a hand-off question"}),a=!0,D.info("offer-handoff: auto-confirmed for autonomous planner",{fromSessionId:r,messageCode:d}))}catch(e){D.warn("offer-handoff: failed to auto-confirm for autonomous planner",{error:(0,w.getErrorMessage)(e)})}return(0,v.withAlchemyKind)({content:[{type:"text",text:a?"Handoff auto-confirmed (autonomous planner). Start-building has been queued: you can stop now.":'Handoff offered to user. STOP here and wait: the user will choose "Start Building" or "Keep Planning". You will receive their choice as a new message. Do NOT generate any further output until then.'}],isError:!1,...(0,v.alchemyMeta)({pendingAction:{kind:"plan-handoff",payload:{summary:t.summary,requestIds:t.requestIds,parentSessionId:o,fromSessionId:r,autoConfirmed:a}}})},"offer-handoff")}case"record-decision":{if(!e.decision||"string"!=typeof e.decision||!e.decision.trim())throw new y.ValidationError("record-decision requires 'decision' (the one-line call you made)")
4309
+ const t=q.getSessionMessageClient()
4310
+ if(!t)throw new y.ValidationError("No session message client available")
4311
+ const r=i(0,0,"session_message_operations",0,P),n=e.decision.trim(),o="string"==typeof e.context&&e.context.trim()?e.context.trim():n,s=await t.createReport({fromSessionId:r,status:"success",summary:`Decision: ${n}`,details:[`**Question:** ${o}`,"string"==typeof e.why&&e.why.trim()?`**Why:** ${e.why.trim()}`:"",`**Decided:** ${n}`].filter(Boolean).join("\n"),extraContent:{decision:{question:o,answer:n},decisionRecord:!0},...e.attention?{attention:e.attention}:{},...e.relatesTo?{relatesTo:e.relatesTo}:{},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:"string"==typeof e.area&&e.area.trim()?{area:e.area.trim()}:{}})
4312
+ return D.info("Decision recorded",{fromSessionId:r,messageCode:s.messageCode,connection:e.requestId||e.area||"project"}),(0,v.withAlchemyKind)({content:[{type:"text",text:`Decision recorded as ${s.messageCode}: on the record for the human; no session was woken.`}]},"record-decision")}case"brief":return(0,v.withAlchemyKind)(await async function(e,t,r,n,o){const{requestId:s,sections:a}=e
4313
+ if(!s||"string"!=typeof s)throw new y.ValidationError('brief operation requires requestId (the request local id, e.g. "req000007")')
4314
+ if(!Array.isArray(a)||0===a.length)throw new y.ValidationError("brief operation requires a non-empty sections array")
4290
4315
  const c=i(0,0,"session_message_operations",0,o),l=n.getSessionMessageClient()
4291
- if(!l)throw new g.ValidationError("Session message client not available")
4292
- const d=await(0,A.messagesArrivedMidStep)(t.mainBasePath,o)
4316
+ if(!l)throw new y.ValidationError("Session message client not available")
4317
+ const d=await(0,O.messagesArrivedMidStep)(t.mainBasePath,o)
4293
4318
  if(d.length>0)return{content:[{type:"text",text:`\u26d4 Not posted: ${1===d.length?"a message":`${d.length} messages`} reached you while you wrote this brief, so it was written without ${1===d.length?"it":"them"}: ${d.slice(0,3).map(e=>`"${e.slice(0,200)}"`).join("; ")}. Read ${1===d.length?"it":"them"} (${1===d.length?"it is":"they are"} in your conversation now) and post the brief again with what ${1===d.length?"it says":"they say"}: a choice the user already made is not a question.`}],isError:!0}
4294
4319
  let u=null,h=0
4295
4320
  try{const e=await l.getInteractionPosture(c,s)
4296
4321
  u=e.posture
4297
- const t=(0,k.resolveBriefHoldPolicy)(e.posture)
4298
- h=t.holdMs,h>0&&r.info("Brief will sync-hold",{requestId:s,posture:e.posture,holdMs:h,reason:t.reason})}catch(e){r.warn("Brief hold: posture lookup failed, posting without a hold",{error:(0,y.getErrorMessage)(e)})}const p=await l.createOpeningBrief({fromSessionId:c,requestId:s,sections:a,...h>0?{holdUntil:new Date(Date.now()+h).toISOString()}:{}})
4322
+ const t=(0,I.resolveBriefHoldPolicy)(e.posture)
4323
+ h=t.holdMs,h>0&&r.info("Brief will sync-hold",{requestId:s,posture:e.posture,holdMs:h,reason:t.reason})}catch(e){r.warn("Brief hold: posture lookup failed, posting without a hold",{error:(0,w.getErrorMessage)(e)})}const p=await l.createOpeningBrief({fromSessionId:c,requestId:s,sections:a,...h>0?{holdUntil:new Date(Date.now()+h).toISOString()}:{}})
4299
4324
  if(p?.priorExisting){const e=p?.response?.sectionAnswers??{},t=Object.entries(e).map(([e,t])=>`- ${e}: ${t}`)
4300
- return{content:[{type:"text",text:[`This request is ALREADY BRIEFED (**${p.messageCode}**, status ${p?.status??"unknown"}). Do NOT post another brief.`,t.length>0?"Settled answers -- proceed on these:":"No answers recorded yet -- proceed on that brief's stated defaults.",...t,"Never hold work OR COMPLETION waiting on a brief."].filter(Boolean).join("\n")}]}}const f=p?.content?.sections?.map(e=>e.id)??a.map(e=>e.id),m=h>0,w=m&&!!p?.content?.holdUntil
4301
- if(m&&!w&&r.info("Brief hold not granted (no tab open); the worker ends its turn and waits for the answer",{requestId:s,code:p.messageCode}),m){const e=w?await(0,I.runSyncHold)({client:l,logger:r,messageCode:p.messageCode,holdMs:h}):null
4325
+ return{content:[{type:"text",text:[`This request is ALREADY BRIEFED (**${p.messageCode}**, status ${p?.status??"unknown"}). Do NOT post another brief.`,t.length>0?"Settled answers -- proceed on these:":"No answers recorded yet -- proceed on that brief's stated defaults.",...t,"Never hold work OR COMPLETION waiting on a brief."].filter(Boolean).join("\n")}]}}const f=p?.content?.sections?.map(e=>e.id)??a.map(e=>e.id),m=h>0,g=m&&!!p?.content?.holdUntil
4326
+ if(m&&!g&&r.info("Brief hold not granted (no tab open); the worker ends its turn and waits for the answer",{requestId:s,code:p.messageCode}),m){const e=g?await(0,$.runSyncHold)({client:l,logger:r,messageCode:p.messageCode,holdMs:h}):null
4302
4327
  if("answered"===e?.kind){const t=e.message?.response?.sectionAnswers??{},r=Object.entries(t).map(([e,t])=>`- ${e}: ${t}`)
4303
- return{content:[{type:"text",text:[`\u2705 **The user answered your brief while you waited** (**${p.messageCode}**).`,...r.length>0?["Their choices:",...r]:["No section choices were recorded; proceed on your stated defaults."],"Build on these now. Do not re-ask these sections."].join("\n")}]}}return{content:[{type:"text",text:[`\u23f3 **No answer yet** on your opening brief (**${p.messageCode}**, sections: ${f.join(", ")}).`,"",'This project is set to "you decide first": these choices are the user\'s, so do NOT proceed on your defaults and do NOT start the work they affect. Wrap up your turn cleanly now -- you will be resumed automatically with their answers whenever they arrive.',"If some of the work is genuinely independent of every section above, you may do that part; anything a section touches waits."].join("\n")}]}}return{content:[{type:"text",text:[`\u2705 Opening brief posted as **${p.messageCode}** for ${s} (sections: ${f.join(", ")}).`,"","PROCEED IMMEDIATELY on your stated defaults -- this brief does not block you and there is nothing to wait for. Implement and let answers land as steers. Do not re-ask these sections; mid-flight questions are for genuine discoveries only.",..."balanced"===u?["One thing DOES wait: completing this request. While a section is unanswered, completion is held -- keep building, and if you reach the end with answers still outstanding, say so rather than completing around them."]:[],"One brief per request, any status: a repeat post returns the existing brief and its answers."].join("\n")}]}}(e,c,D,j,M),"opening-brief")
4328
+ return{content:[{type:"text",text:[`\u2705 **The user answered your brief while you waited** (**${p.messageCode}**).`,...r.length>0?["Their choices:",...r]:["No section choices were recorded; proceed on your stated defaults."],"Build on these now. Do not re-ask these sections."].join("\n")}]}}return{content:[{type:"text",text:[`\u23f3 **No answer yet** on your opening brief (**${p.messageCode}**, sections: ${f.join(", ")}).`,"",'This project is set to "you decide first": these choices are the user\'s, so do NOT proceed on your defaults and do NOT start the work they affect. Wrap up your turn cleanly now -- you will be resumed automatically with their answers whenever they arrive.',"If some of the work is genuinely independent of every section above, you may do that part; anything a section touches waits."].join("\n")}]}}return{content:[{type:"text",text:[`\u2705 Opening brief posted as **${p.messageCode}** for ${s} (sections: ${f.join(", ")}).`,"","PROCEED IMMEDIATELY on your stated defaults -- this brief does not block you and there is nothing to wait for. Implement and let answers land as steers. Do not re-ask these sections; mid-flight questions are for genuine discoveries only.",..."balanced"===u?["One thing DOES wait: completing this request. While a section is unanswered, completion is held -- keep building, and if you reach the end with answers still outstanding, say so rather than completing around them."]:[],"One brief per request, any status: a repeat post returns the existing brief and its answers."].join("\n")}]}}(e,c,D,q,P),"opening-brief")
4304
4329
  case"withdraw":{const t=String(e.messageCode??"").trim()
4305
- if(!t)throw new g.ValidationError("withdraw needs messageCode (Q-...)")
4306
- if(!j||!c.projectId)throw new g.ValidationError("withdraw needs the remote")
4307
- const r=await j.proxyPost(`/api/projects/${c.projectId}/session-messages/${encodeURIComponent(t)}/withdraw`,{sessionId:M?.sessionId??E.sessionIdentity.getIdentity()?.sessionId,reason:e.withdrawReason}),n=(r?.data??r)?.settled??[]
4308
- return(0,b.withAlchemyKind)({content:[{type:"text",text:n.length?`Withdrawn: ${n.join(", ")}. Nothing waits on the user for ${n.length>1?"these":"it"} any more. A [user-input] criterion it named stays owed, and the card at the end of the work asks the user for it.`:`${t} was not pending; nothing changed.`}]},"withdraw-question")}case"list":return(0,b.withAlchemyKind)(await async function(e,t,r,n,o){let{sessionId:s,messageType:i,direction:a,status:c,readStatus:l,reportStatus:u,limit:h,offset:p}=e
4330
+ if(!t)throw new y.ValidationError("withdraw needs messageCode (Q-...)")
4331
+ if(!q||!c.projectId)throw new y.ValidationError("withdraw needs the remote")
4332
+ const r=await q.proxyPost(`/api/projects/${c.projectId}/session-messages/${encodeURIComponent(t)}/withdraw`,{sessionId:P?.sessionId??E.sessionIdentity.getIdentity()?.sessionId,reason:e.withdrawReason}),n=(r?.data??r)?.settled??[]
4333
+ return(0,v.withAlchemyKind)({content:[{type:"text",text:n.length?`Withdrawn: ${n.join(", ")}. Nothing waits on the user for ${n.length>1?"these":"it"} any more. A [user-input] criterion it named stays owed, and the card at the end of the work asks the user for it.`:`${t} was not pending; nothing changed.`}]},"withdraw-question")}case"list":return(0,v.withAlchemyKind)(await async function(e,t,r,n,o){let{sessionId:s,messageType:i,direction:a,status:c,readStatus:l,reportStatus:u,limit:h,offset:p}=e
4309
4334
  const f=n.getSessionMessageClient()
4310
- if(!f)throw new g.ValidationError("Session message client not available")
4335
+ if(!f)throw new y.ValidationError("Session message client not available")
4311
4336
  if(!s){const e=o?.sessionId||E.sessionIdentity.getIdentity()?.sessionId
4312
4337
  if(!e)return r.warn("No session identity for list operation"),{content:[{type:"text",text:"\u26a0\ufe0f Could not determine current session ID. Unable to list messages without a session context."}]}
4313
- s=e,(0,T.logSessionIdentity)("session_message_operations.list","identity",{sessionId:s,fromCliOrigin:!!o?.sessionId})}const m=o?.sessionId||E.sessionIdentity.getIdentity()?.sessionId
4314
- let y="string"==typeof e.requestId&&e.requestId?e.requestId:void 0
4315
- if(!y&&m&&s&&s!==m)try{y=await E.sessionIdentity.getSessionRequestId(n,r,m)??void 0}catch{}r.debug("Listing messages",{sessionId:s,messageType:i,direction:a,status:c,readStatus:l,requestId:y})
4316
- const w={sessionId:s,direction:a,limit:h,offset:p,...y?{requestId:y}:{}}
4338
+ s=e,(0,j.logSessionIdentity)("session_message_operations.list","identity",{sessionId:s,fromCliOrigin:!!o?.sessionId})}const m=o?.sessionId||E.sessionIdentity.getIdentity()?.sessionId
4339
+ let g="string"==typeof e.requestId&&e.requestId?e.requestId:void 0
4340
+ if(!g&&m&&s&&s!==m)try{g=await E.sessionIdentity.getSessionRequestId(n,r,m)??void 0}catch{}r.debug("Listing messages",{sessionId:s,messageType:i,direction:a,status:c,readStatus:l,requestId:g})
4341
+ const w={sessionId:s,direction:a,limit:h,offset:p,...g?{requestId:g}:{}}
4317
4342
  i&&"all"!==i&&(w.messageType=i),c&&"all"!==c&&("report"===i?w.reportStatus=c:w.status=c),l&&"all"!==l&&(w.readStatus=l),u&&"all"!==u&&(w.reportStatus=u)
4318
4343
  const b=await f.listMessages(w)
4319
4344
  if(0===b.length)return{content:[{type:"text",text:"No messages found matching the criteria."}]}
4320
4345
  const v=[`Found ${b.length} message(s):\n`]
4321
4346
  for(const e of b)v.push("---\n"),v.push(d(e)),v.push("")
4322
- return{content:[{type:"text",text:v.join("\n")}]}}({...e.list||{},...e.requestId&&!(e.list||{}).requestId?{requestId:e.requestId}:{}},0,D,j,M),"list-messages")
4323
- case"get":if(!e.messageCode)throw new g.ValidationError("get operation requires messageCode")
4324
- return(0,b.withAlchemyKind)(await async function(e,t,r){const{messageCode:n}=e
4325
- if(!n)throw new g.ValidationError("get operation requires messageCode")
4347
+ return{content:[{type:"text",text:v.join("\n")}]}}({...e.list||{},...e.requestId&&!(e.list||{}).requestId?{requestId:e.requestId}:{}},0,D,q,P),"list-messages")
4348
+ case"get":if(!e.messageCode)throw new y.ValidationError("get operation requires messageCode")
4349
+ return(0,v.withAlchemyKind)(await async function(e,t,r){const{messageCode:n}=e
4350
+ if(!n)throw new y.ValidationError("get operation requires messageCode")
4326
4351
  const o=r.getSessionMessageClient()
4327
- if(!o)throw new g.ValidationError("Session message client not available")
4352
+ if(!o)throw new y.ValidationError("Session message client not available")
4328
4353
  t.debug("Getting message",{messageCode:n})
4329
4354
  const s=await o.getMessage(n)
4330
- return s?{content:[{type:"text",text:d(s)}]}:{content:[{type:"text",text:`\u274c Message with code ${n} not found.`}],isError:!0}}(e,D,j),"get-message")
4331
- default:throw new g.ValidationError(`Unknown operation: ${F}. Valid operations: send, reply, list, get, offer-handoff, record-decision, brief`)}}catch(e){return D.error("Session message operation failed",{operation:F,error:(0,y.getErrorMessage)(e)}),{content:[{type:"text",text:`\u274c Error: ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}
4332
- const f=t(87780),m=t(70508),g=t(59860),y=t(58824),w=t(47617),b=t(82272),v=t(67904),k=t(22316),I=t(22746),$=t(4871),E=t(6688),S=t(69883),x=t(69048),_=t(34221),C=t(39844),q=t(43545),T=t(87280),j=t(29729),P=t(19670),R=t(53847),A=t(62032),O=new Map
4355
+ return s?{content:[{type:"text",text:d(s)}]}:{content:[{type:"text",text:`\u274c Message with code ${n} not found.`}],isError:!0}}(e,D,q),"get-message")
4356
+ default:throw new y.ValidationError(`Unknown operation: ${F}. Valid operations: send, reply, list, get, offer-handoff, record-decision, brief`)}}catch(e){return D.error("Session message operation failed",{operation:F,error:(0,w.getErrorMessage)(e)}),{content:[{type:"text",text:`\u274c Error: ${(0,w.getErrorMessage)(e)}`}],isError:!0}}}
4357
+ const m=t(87780),g=t(70508),y=t(59860),w=t(58824),b=t(47617),v=t(82272),k=t(67904),I=t(22316),$=t(22746),S=t(4871),E=t(6688),_=t(69883),x=t(69048),C=t(34221),q=t(39844),T=t(43545),j=t(87280),P=t(29729),R=t(19670),A=t(53847),O=t(62032),M=new Map
4333
4358
  exports.zk="A worker asks you this while it builds. Answer from what the user said and what the record holds: reply with your answer (quote the user's words when they settle it). If the answer changes what the request asks, update its criteria first (request_operations update). If only the user can decide, reply with escalate: true and your recommended default in notes; the default must be safe to happen without them."},22746(e,exports,t){exports.runSyncHold=async function(e){const{client:t,logger:n,messageCode:o,holdMs:s}=e,i=Date.now()+s,a=async e=>{try{await t.stampHoldOutcome(o,{holdDelivered:!0,clearHold:!0})}catch(e){n.warn("Hold claim failed; the arbiter may also inject this answer",{messageCode:o,error:(0,r.getErrorMessage)(e)})}return{kind:"answered",message:e}}
4334
4359
  let c=!1
4335
4360
  for(;Date.now()<i;){await new Promise(e=>setTimeout(e,3e3))
@@ -4346,7 +4371,7 @@ if(!t.apiClient||!n)return{content:[{type:"text",text:"show_user unavailable: no
4346
4371
  try{const e=await t.apiClient.proxyPost(`/api/projects/${n}/canvases`,{title:r.title,html:r.html,note:r.note,requestId:r.requestId,sessionExternalId:t.cliOrigin?.sessionId??t.context.currentSessionId??void 0}),o=e?.data??e
4347
4372
  return{content:[{type:"text",text:`Shown to the user in Steer${o?.messageCode?` (${o.messageCode})`:""}: "${r.title}". You do not need to describe it again; say what it means.`}]}}catch(e){return{content:[{type:"text",text:`Not shown: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}}},44973(e,exports,t){exports.def=void 0
4348
4373
  const r=t(76183),n=t(4592)
4349
- exports.def={name:"planner_ops",description:"WHEN TO USE request: when asked to plan/design/architect. Do NOT manually create requests from the plan: the planner does that.\n\nRequest a planning session and check on planner progress.\n\nAll params are flat (no nested objects). The operation field determines which params apply.\n\nOperations:\n\u2022 request: prompt required. Optional: description, userRequested. REFINING AN EXISTING PLAN? Pass documentId (the plan's doc/plan id): never describe the plan only in prose. With documentId, an active planner already bound to that plan is RESUMED (reuse-first); without it a new planner and a new plan document are created.\n\u2022 end: sessionId or documentId required. ENDS planning: closes the planner session(s) and their plan binding. Call this when the user exits/closes planning mode: never claim planning is closed without calling it.\n\u2022 check-status: sessionId required\n\u2022 list: all optional: activeOnly, limit\n\nSet userRequested: true ONLY when the user explicitly asked for this plan themselves: their ask is the decision and the planner starts immediately. Leave it unset for planning you initiate: project settings may route the choice to the user, and until they choose, NOTHING is running (never tell the user planning has started in that case).",schema:r.PlannerOpsSchema,annotations:{title:"Planner Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handlePlannerOps)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin)}},4592(e,exports,t){exports.handlePlannerOps=async function(e,t,h,p,f,m){const g=h.child({tool:"planner_ops"}),y=p.startTimer("tool_execution_time")
4374
+ exports.def={name:"planner_ops",description:"Request a planning session and check on it; when asked to plan, design or architect, use request here and do not create requests from the plan yourself (the planner does).\n\n\u2022 request: prompt required; optional description, userRequested. To refine an existing plan pass its documentId (never describe the plan only in prose): a planner already bound to it is resumed, otherwise a new planner and plan document are created.\n\u2022 end: sessionId or documentId. Ends planning (closes the planner sessions and their plan binding); call it when the user leaves planning, and never say planning is closed without it.\n\u2022 check-status: sessionId.\n\u2022 list: optional activeOnly, limit.\n\nuserRequested: true only when the user asked for this plan themselves: the planner starts at once. Leave it unset for planning you initiate: the project may route the choice to the user, and until they choose nothing runs, so never say planning has started.",schema:r.PlannerOpsSchema,annotations:{title:"Planner Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handlePlannerOps)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin)}},4592(e,exports,t){exports.handlePlannerOps=async function(e,t,h,p,f,m){const g=h.child({tool:"planner_ops"}),y=p.startTimer("tool_execution_time")
4350
4375
  try{const h=e,{operation:w,sessionId:b}=h
4351
4376
  let v
4352
4377
  switch(g.info("Processing planner_ops",{operation:w,sessionId:b}),w){case"request":{const e=!0===h.userRequested
@@ -4367,7 +4392,7 @@ if("interactive"===n)return"interactive"}catch(e){t.warn("Failed to read project
4367
4392
  if("ask"===p||"interactive"===p){g.info("Returning planner choice pending",{planningMode:p})
4368
4393
  try{const e=m?.sessionId||i.sessionIdentity.getIdentity()?.sessionId,t=f?.getSessionMessageClient()
4369
4394
  if(t&&e){const r=await i.sessionIdentity.getParentSessionId(e,f,g),n=h.description||h.prompt
4370
- await t.createQuestion({fromSessionId:e,toSessionId:r||void 0,questionType:"alternatives",question:`Planning requested: ${n}`,options:[{id:"auto",label:"Auto Plan",description:"Spawn an autonomous planner agent"},{id:"manual",label:"Manual Plan",description:"Open the interactive plan editor"},{id:"cancel",label:"Skip",description:"Dismiss this planning request"}],context:{type:"planner-request",prompt:h.prompt,description:h.description},priority:"high"}),g.info("planner-request: notification created",{fromSessionId:e})}else g.warn("planner-request: skipping notification",{hasClient:!!t,hasCallerSessionId:!!e})}catch(e){g.warn("planner-request: failed to create notification",{error:(0,n.getErrorMessage)(e)})}v={content:[{type:"text",text:"Planning request submitted: nothing is running yet. The user must pick the planning approach (Auto Plan / Manual Plan / Skip) from their inbox before any planner starts. If you reply to the user, tell them the planning choice is waiting on them; do NOT say planning has started. (If the user themselves explicitly asked for this plan, pass userRequested: true to start it immediately.) This is non-blocking: continue with your other work."}],...(0,d.alchemyMeta)({pendingAction:{kind:"planner-choice",payload:{prompt:h.prompt,description:h.description}}})}
4395
+ await t.createQuestion({fromSessionId:e,toSessionId:r||void 0,questionType:"alternatives",question:`Planning requested: ${n}`,options:[{id:"auto",label:"Auto Plan",description:"Spawn an autonomous planner agent"},{id:"manual",label:"Manual Plan",description:"Open the interactive plan editor"},{id:"cancel",label:"Skip",description:"Dismiss this planning request"}],context:{type:"planner-request",prompt:h.prompt,description:h.description},priority:"high"}),g.info("planner-request: notification created",{fromSessionId:e})}else g.warn("planner-request: skipping notification",{hasClient:!!t,hasCallerSessionId:!!e})}catch(e){g.warn("planner-request: failed to create notification",{error:(0,n.getErrorMessage)(e)})}v={content:[{type:"text",text:"Planning request submitted: nothing is running yet. The user must pick the planning approach (Auto Plan / Manual Plan / Skip) from a card in Steer before any planner starts. If you reply to the user, tell them the planning choice is waiting on them; do NOT say planning has started. (If the user themselves explicitly asked for this plan, pass userRequested: true to start it immediately.) This is non-blocking: continue with your other work."}],...(0,d.alchemyMeta)({pendingAction:{kind:"planner-choice",payload:{prompt:h.prompt,description:h.description}}})}
4371
4396
  break}const w="planner"
4372
4397
  let b=null
4373
4398
  if(h.documentId){const e=await f.getDocumentClient().getDocument(h.documentId)
@@ -4430,7 +4455,7 @@ const h=await i.proxyPost(`/api/projects/${d}/research-fanout`,{coordinatorSessi
4430
4455
  return s.incrementCounter("research_fanout_request_success",1),c.info("research_fanout enqueued",{runId:f,projectId:d,angleCount:m.length}),{content:[{type:"text",text:`Research fan-out started (runId ${f??"unknown"}) across ${m.length} angle(s): ${m.join(", ")}.\n\nRead-only workers investigate each angle in parallel; a Research Report document is saved when done and you will be nudged back with the findings. NON-BLOCKING -- continue your work. To check progress meanwhile, call research_fanout with checkRunId "${f??""}".`}]}}catch(e){return s.incrementCounter("research_fanout_request_error",1),c.error("research_fanout failed",{error:(0,r.getErrorMessage)(e)}),{isError:!0,content:[{type:"text",text:`research_fanout failed: ${(0,r.getErrorMessage)(e)}`}]}}}
4431
4456
  const r=t(58824),n=t(6688)},1540(e,exports,t){exports.def=void 0
4432
4457
  const r=t(76183),n=t(51725),o=t(8955)
4433
- exports.def={name:"subagent_operations",description:`WHEN TO USE spawn (agent: coder): any change touching 2+ files, feature work, bug fixes, or anything needing acceptance-criteria verification.\n\nManage background subagents that work independently.\n\nOperations:\n\u2022 spawn - Create a new detached subagent to work in the background. Include a short 'description' for display in the UI.\n\u2022 continue - Send new instructions/feedback to an existing subagent session\n\u2022 check-status - Check if a subagent is idle or busy\n\u2022 abort - Stop a running subagent\n\u2022 read-messages - Read a subagent's messages. Default: the latest message in full (its text AND tool calls), skipping trailing empty turns so you always get the substantive result. Add compact:true for a one-line index of EVERY message with tool-call markers (its trajectory: what it actually did), then select:N to pull one message in full ({tool, input, status, output} per step).\n\nThe spawned subagent runs as a separate OpenCode process.\n\nAvailable agents: ${(0,o.formatAgentsForDescription)()}`,schema:r.SubagentOperationsSchema,annotations:{title:"Subagent Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleSubagentOperations)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin)}},51725(e,exports,t){function r(e,t,r){if(x)try{const n=`[${(new Date).toISOString()}] ${t}${r?" "+JSON.stringify(r):""}\n`,o=(0,I.join)(e.basePath||process.cwd(),".alchemy_lab","spawn-debug.log");(0,k.appendFileSync)(o,n)}catch{}}async function n(e,r,n,o,s,i){if(!(e&&r&&n&&o))return null
4458
+ exports.def={name:"subagent_operations",description:`WHEN TO USE spawn (agent: coder): any change touching 2+ files, feature work, bug fixes, or anything needing acceptance-criteria verification.\n\nManage background subagents that work independently.\n\nOperations:\n\u2022 spawn - Create a new detached subagent to work in the background. Include a short 'description' for display in the UI.\n\u2022 continue - Send new instructions/feedback to an existing subagent session\n\u2022 check-status - Check if a subagent is idle or busy\n\u2022 abort - Stop a running subagent\n\u2022 read-messages - Read a subagent's messages. Default: the latest message in full (its text AND tool calls), skipping trailing empty turns so you always get the substantive result. Add compact:true for a one-line index of EVERY message with tool-call markers (its trajectory: what it actually did), then select:N to pull one message in full ({tool, input, status, output} per step).\n\nThe spawned subagent runs as a separate OpenCode process.\n\nAvailable agents: ${(0,o.formatAgentsForDescription)()}`,schema:r.SubagentOperationsSchema,annotations:{title:"Subagent Operations",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleSubagentOperations)(e,t.context,t.logger,t.metrics,t.apiClient??void 0,t.cliOrigin)}},51725(e,exports,t){function r(e,t,r){if(_)try{const n=`[${(new Date).toISOString()}] ${t}${r?" "+JSON.stringify(r):""}\n`,o=(0,I.join)(e.basePath||process.cwd(),".alchemy_lab","spawn-debug.log");(0,k.appendFileSync)(o,n)}catch{}}async function n(e,r,n,o,s,i){if(!(e&&r&&n&&o))return null
4434
4459
  try{const s=await e.proxyPost(`/api/projects/${r}/requests/${n}/pickup-compose`,{sessionId:o}),a=s?.data?.text??s?.text,c="string"==typeof a&&a.trim()?a.trim():null
4435
4460
  if(c&&/Project map/.test(c))return c
4436
4461
  let l=null
@@ -4441,12 +4466,12 @@ const a=t.getSessionMessageClient()
4441
4466
  if(a)try{const e=await a.createInstruction({fromSessionId:r,toSessionId:n,body:o,...s?{requestId:s}:{}})
4442
4467
  return e?.messageCode}catch(e){return void i.warn("Failed to mint instruction code",{error:(0,v.getErrorMessage)(e),fromSessionId:r,toSessionId:n})}}async function s(e,t,r,n,o){if(e&&t&&r)try{await e.proxyPost(`/api/projects/${t}/session-messages/${encodeURIComponent(r)}/delivered`,{ok:n.ok,error:n.detail||n.error,messageId:n.messageId})}catch(e){o.warn("instruction delivery receipt not written",{instructionCode:r,error:(0,v.getErrorMessage)(e)})}}async function i(e,t,i,a,c){const{agent:l="work",prompt:d,requestId:u,allowParallel:h=!1,useExistingSession:p}=e
4443
4468
  if(r(t,"handleSpawn called",{agent:l,promptLength:d.length,requestId:u,useExistingSession:p,cliOriginSessionId:c?.sessionId||null}),!(0,$.isValidAgent)(l))return{content:[{type:"text",text:`\u274c Unknown agent '${l}'.\n\n**Available agents:** ${(0,$.getAgentNames)().join(", ")}\n\nChoose one of the available agents listed above to see detailed descriptions of each agent.`}],isError:!0}
4444
- const k=c?.sessionId,I=k?w.sessionIdentity.getAgentTypeFor(k).then(e=>e||void 0):Promise.resolve(void 0),S=async function(e){try{const t=`http://localhost:${_}/api/agents/by-name/${encodeURIComponent(e)}`,r=await fetch(t,{signal:AbortSignal.timeout(8e3)})
4469
+ const k=c?.sessionId,I=k?w.sessionIdentity.getAgentTypeFor(k).then(e=>e||void 0):Promise.resolve(void 0),E=async function(e){try{const t=`http://localhost:${x}/api/agents/by-name/${encodeURIComponent(e)}`,r=await fetch(t,{signal:AbortSignal.timeout(8e3)})
4445
4470
  if(!r.ok)return null
4446
4471
  const n=await r.json()
4447
4472
  return n.success?n.data??null:null}catch{return null}}(l).catch(()=>null),C=(0,g.getServerUrl)(t,i),q=(async()=>{if(!a||!u)return null
4448
4473
  const e=a.getSessionRequestAssociationClient()
4449
- return e?await e.getSessionsForRequest(u,!0):null})().catch(e=>(i.debug("Failed to check for existing workers",{error:(0,v.getErrorMessage)(e)}),null)),[T,j,P,R]=await Promise.all([I,S,C,q])
4474
+ return e?await e.getSessionsForRequest(u,!0):null})().catch(e=>(i.debug("Failed to check for existing workers",{error:(0,v.getErrorMessage)(e)}),null)),[T,j,P,R]=await Promise.all([I,E,C,q])
4450
4475
  try{if(r(t,"Spawn agent type check",{callerSessionId:k||null,callerAgentType:T||null,pid:process.pid}),T){const e=(0,$.getAllowedSubagents)(T)
4451
4476
  if(e&&!e.includes(l))return{content:[{type:"text",text:`\u274c Agent type '${T}' cannot spawn '${l}'.\n\n**Allowed subagents for ${T}:** ${e.join(", ")}\n\nUse one of the allowed agent types instead.`}],isError:!0}}}catch(e){r(t,"allowedSubagents check failed",{error:(0,v.getErrorMessage)(e)})}let A=j?.requestContextMode
4452
4477
  if(!A){const e=(0,$.getBuiltInAgentConfig)(l)
@@ -4464,7 +4489,7 @@ u&&(D=`requests/${u}`,r(t,"Setting initialBranch for agent",{agent:l,requestId:u
4464
4489
  let z,L=!1
4465
4490
  if(p){if(F=p,i.info("Using existing session for spawn",{sessionId:F,requestId:u}),!P)throw new f.ValidationError("Could not determine OpenCode server URL")
4466
4491
  try{let e
4467
- try{const t=await fetch(`http://localhost:${_}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
4492
+ try{const t=await fetch(`http://localhost:${x}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
4468
4493
  if(t.ok){const r=await t.json()
4469
4494
  if(r.data?.defaultModel){const[t,...n]=r.data.defaultModel.split("/")
4470
4495
  e={providerID:t,modelID:n.join("/")}}}}catch{}z=await o({apiClient:a,fromSessionId:c?.sessionId,toSessionId:F,body:N,requestId:u,logger:i})
@@ -4475,8 +4500,8 @@ const h={body:N,fromSessionId:c?.sessionId,fromAgent:T,...u?{requestId:u}:{},...
4475
4500
  if(await s(a,t.projectId,z,p,i),!p.ok)throw new Error((0,y.deliveryFailureText)(F,p))
4476
4501
  i.info("Prompt delivered to existing session",{sessionId:F,agent:l,messageId:p.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,v.getErrorMessage)(e)}`)}}else{L=!0,r(t,"Creating session via spawn-session endpoint")
4477
4502
  const s=O
4478
- x&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:s})
4479
- try{const p=`http://localhost:${_}/api/spawn-session`,f=await fetch(p,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:l,prompt:d,requestId:u,parentSessionId:s,title:e.title,allowParallel:h,callerAgentType:T})})
4503
+ _&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:s})
4504
+ try{const p=`http://localhost:${x}/api/spawn-session`,f=await fetch(p,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:l,prompt:d,requestId:u,parentSessionId:s,title:e.title,allowParallel:h,callerAgentType:T})})
4480
4505
  if(!f.ok){const e=await f.text().catch(()=>"Unknown error")
4481
4506
  throw new Error(`Spawn session endpoint failed: ${f.status} ${e}`)}const g=await f.json()
4482
4507
  if(!g.success||!g.data)throw new Error(g.error||"Spawn session returned unsuccessful")
@@ -4490,7 +4515,7 @@ if(!$.success)throw new Error(`Failed to spawn subagent: ${$.error}`)
4490
4515
  F=y,r(t,"Spawn successful via unified endpoint",{sessionId:y,parentSessionId:s,model:w,instructionCode:z}),i.info("Background subagent spawned via unified endpoint",{agent:l,requestId:u,sessionId:y,parentSessionId:s,instructionCode:z})}catch(e){throw new Error(`Failed to spawn subagent: ${(0,v.getErrorMessage)(e)}`)}}let B=M
4491
4516
  B+=p?`Prompt sent to existing session ${F}.`:`Background ${l} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),z&&(B+=`\nInstruction code: ${z} (the agent will cite this on its reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
4492
4517
  const H=!!F&&"unknown"!==F&&"spawning"!==F
4493
- return{content:[{type:"text",text:B}],...(0,E.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:H?F:void 0,agentType:l,requestId:u||void 0,description:e.title||d.slice(0,50)||l,...z?{instructionCode:z}:{}}),...z?{instructionCode:z}:{}}}function a(e){const t=Math.floor(e/1e3)
4518
+ return{content:[{type:"text",text:B}],...(0,S.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:H?F:void 0,agentType:l,requestId:u||void 0,description:e.title||d.slice(0,50)||l,...z?{instructionCode:z}:{}}),...z?{instructionCode:z}:{}}}function a(e){const t=Math.floor(e/1e3)
4494
4519
  if(t<60)return`${t}s`
4495
4520
  const r=Math.floor(t/60)
4496
4521
  return r<60?`${r}m ${t%60}s`:`${Math.floor(r/60)}h ${r%60}m`}function c(e){const t=[]
@@ -4517,23 +4542,23 @@ b=e.length
4517
4542
  for(const t of e)"assistant"===t.info.role&&t.info.tokens&&(g.input+=t.info.tokens.input||0,g.output+=t.info.tokens.output||0,g.reasoning+=t.info.tokens.reasoning||0)
4518
4543
  const t=c(e)
4519
4544
  w=d(t.lastActivityTime,p.time.updated),t.current&&(y+=`\nCurrent: ${t.current}`),t.recent.length>0&&(y+=`\nRecent: ${t.recent.join(", ")}`)}const v=Date.now()-p.time.created,k=w.status,I=w.isReady,$=g.input+g.output+g.reasoning
4520
- let E="",S="",x=null
4545
+ let S="",E="",_=null
4521
4546
  if(o)try{const t=o.getSessionRequestAssociationClient()
4522
4547
  if(t){const r=await t.getActiveRequestsForSession(e,!1)
4523
4548
  if(r.length>0){const t=r.filter(e=>!e.endedAt).sort((e,t)=>new Date(t.startedAt).getTime()-new Date(e.startedAt).getTime()),o=r.filter(e=>e.endedAt).sort((e,t)=>new Date(t.endedAt).getTime()-new Date(e.endedAt).getTime()),s=o[0],i=t[0]
4524
- s&&i?x=new Date(s.endedAt).getTime()>new Date(i.startedAt).getTime()?s.requestLocalId:i.requestLocalId:s?x=s.requestLocalId:i&&(x=i.requestLocalId)
4549
+ s&&i?_=new Date(s.endedAt).getTime()>new Date(i.startedAt).getTime()?s.requestLocalId:i.requestLocalId:s?_=s.requestLocalId:i&&(_=i.requestLocalId)
4525
4550
  const a=[]
4526
- t.length>0&&a.push(`Active: ${t.map(e=>e.requestLocalId).join(", ")}`),o.length>0&&a.push(`Previous: ${o.map(e=>e.requestLocalId).join(", ")}`),a.length>0&&(S=`\nRequests: ${a.join(" | ")}`),n.debug("Found request associations",{sessionId:e,activeCount:t.length,historicalCount:o.length})}}}catch(e){n.debug("Failed to get request associations, falling back to title extraction",{error:e instanceof Error?e.message:String(e)})}if(x||(x=function(e){const t=e.match(/\[(req\d{6})\]/)
4527
- return t?t[1]:null}(p.title||"")),x&&r.basePath){const e=await async function(e,r){try{const{execFileSync:n}=await Promise.resolve().then(()=>t.t(35317,23)),o=`requests/${e}`
4551
+ t.length>0&&a.push(`Active: ${t.map(e=>e.requestLocalId).join(", ")}`),o.length>0&&a.push(`Previous: ${o.map(e=>e.requestLocalId).join(", ")}`),a.length>0&&(E=`\nRequests: ${a.join(" | ")}`),n.debug("Found request associations",{sessionId:e,activeCount:t.length,historicalCount:o.length})}}}catch(e){n.debug("Failed to get request associations, falling back to title extraction",{error:e instanceof Error?e.message:String(e)})}if(_||(_=function(e){const t=e.match(/\[(req\d{6})\]/)
4552
+ return t?t[1]:null}(p.title||"")),_&&r.basePath){const e=await async function(e,r){try{const{execFileSync:n}=await Promise.resolve().then(()=>t.t(35317,23)),o=`requests/${e}`
4528
4553
  let s=!1
4529
4554
  try{n("git",["rev-parse","--verify",o],{cwd:r,stdio:"pipe"}),s=!0}catch{s=!1}if(s){const e=n("git",["diff",`main...${o}`,"--numstat"],{cwd:r,encoding:"utf8"})
4530
4555
  if(e.trim())return u(e)}try{const t=n("git",["log","main","--merges","--oneline","-n","50",`--grep=${e}`],{cwd:r,encoding:"utf8"}).trim()
4531
4556
  if(t){const e=t.split("\n")[0].split(" ")[0],o=n("git",["diff",`${e}^1...${e}`,"--numstat"],{cwd:r,encoding:"utf8"})
4532
- if(o.trim())return{...u(o),merged:!0}}}catch{}return null}catch{return null}}(x,r.basePath)
4557
+ if(o.trim())return{...u(o),merged:!0}}}catch{}return null}catch{return null}}(_,r.basePath)
4533
4558
  if(e){const t=e.additions+e.deletions,r=e.merged?" (merged)":""
4534
- E=`\nDiff vs main (${x}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${r}`}}const _=function(e,t,r,n){const o=e.get(t)
4559
+ S=`\nDiff vs main (${_}): +${e.additions}/-${e.deletions} lines (${t} total) in ${e.files} file${1!==e.files?"s":""}${r}`}}const x=function(e,t,r,n){const o=e.get(t)
4535
4560
  return e.set(t,{at:n,fingerprint:r}),o&&o.fingerprint===r?`Nothing changed since your last check at ${new Date(o.at).toISOString().slice(11,19)} UTC (${l(n-o.at)}): same activity, same messages, same tokens.`:null}(C,`${r.currentSessionId??""}>${e}`,`${p.time.updated}:${b}:${$}`,Date.now())
4536
- return{content:[{type:"text",text:`${_?`${_}\n\n`:""}Session ${e}\nTitle: ${p.title||"Untitled"}\nStatus: ${k} (${w.detail})\nReady for input: ${I?"Yes":"No"}\nAge: ${a(v)}\nTokens: ${$.toLocaleString()} (in: ${g.input.toLocaleString()}, out: ${g.output.toLocaleString()}${g.reasoning>0?`, reasoning: ${g.reasoning.toLocaleString()}`:""})${y}${S}${E}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,v.getErrorMessage)(e)}`)}}async function p(e,t,r,n={}){const{activeOnly:o=!1,requestId:s,limit:i,minTokens:l,maxTokens:u,maxAgeHours:h}=n,p=await(0,g.getServerUrl)(e,t)
4561
+ return{content:[{type:"text",text:`${x?`${x}\n\n`:""}Session ${e}\nTitle: ${p.title||"Untitled"}\nStatus: ${k} (${w.detail})\nReady for input: ${I?"Yes":"No"}\nAge: ${a(v)}\nTokens: ${$.toLocaleString()} (in: ${g.input.toLocaleString()}, out: ${g.output.toLocaleString()}${g.reasoning>0?`, reasoning: ${g.reasoning.toLocaleString()}`:""})${y}${E}${S}`}]}}catch(e){throw new Error(`Failed to check session status: ${(0,v.getErrorMessage)(e)}`)}}async function p(e,t,r,n={}){const{activeOnly:o=!1,requestId:s,limit:i,minTokens:l,maxTokens:u,maxAgeHours:h}=n,p=await(0,g.getServerUrl)(e,t)
4537
4562
  if(!p)throw new f.ValidationError("Could not determine OpenCode server URL")
4538
4563
  t.info("Listing sessions",{filters:n})
4539
4564
  try{const e=`${p.replace(/\/$/,"")}/session?limit=1000`,t=await fetch(e,{signal:AbortSignal.timeout(8e3)})
@@ -4569,11 +4594,11 @@ v.push(...t,...r)
4569
4594
  const n=[]
4570
4595
  if(t.length>0&&n.push(`active: ${t.join(", ")}`),r.length>0){const e=5,t=r.slice(0,e),o=r.length-t.length
4571
4596
  n.push(`prev: ${t.join(", ")}${o>0?` +${o} more`:""}`)}n.length>0&&(y=` [${n.join(" | ")}]`)}if(s&&!v.includes(s))continue
4572
- const E=t.title||"Untitled",S=E.length>50?E.slice(0,47)+"...":E
4573
- $.push({id:t.id,title:S,status:m,age:r,totalTokens:o,requestsInfo:y,allRequestIds:v,statusDetail:h.detail,currentAction:i,recentActions:a})}if(0===$.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
4574
- const E=i?$.slice(0,i):$,S=E.map(e=>{const t=e.currentAction?`\n Current: ${e.currentAction}`:"",r=e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
4575
- return`\u2022 ${e.id}\n ${e.title}\n Status: ${e.status} (${e.statusDetail}) | Age: ${a(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${t}${r}`}),x=i&&$.length>i?` (showing ${i} of ${$.length})`:""
4576
- return{content:[{type:"text",text:`Found ${E.length} session${1!==E.length?"s":""}${x}:\n\n${S.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,v.getErrorMessage)(e)}`)}}exports.handleSpawn=i,exports.extractRecentActivity=c,exports.formatTimeSince=l,exports.deriveActivityStatus=d,exports.handleCheckStatus=h,exports.handleListSessions=p,exports.handleSubagentOperations=async function(e,t,r,n,a,c){const l=r.child({tool:"subagent_operations"}),d=n.startTimer("tool_execution_time")
4597
+ const S=t.title||"Untitled",E=S.length>50?S.slice(0,47)+"...":S
4598
+ $.push({id:t.id,title:E,status:m,age:r,totalTokens:o,requestsInfo:y,allRequestIds:v,statusDetail:h.detail,currentAction:i,recentActions:a})}if(0===$.length)return{content:[{type:"text",text:"No sessions match the specified filters."}]}
4599
+ const S=i?$.slice(0,i):$,E=S.map(e=>{const t=e.currentAction?`\n Current: ${e.currentAction}`:"",r=e.recentActions.length>0?`\n Recent: ${e.recentActions.join(", ")}`:""
4600
+ return`\u2022 ${e.id}\n ${e.title}\n Status: ${e.status} (${e.statusDetail}) | Age: ${a(e.age)} | ${e.totalTokens.toLocaleString()} tokens${e.requestsInfo}${t}${r}`}),_=i&&$.length>i?` (showing ${i} of ${$.length})`:""
4601
+ return{content:[{type:"text",text:`Found ${S.length} session${1!==S.length?"s":""}${_}:\n\n${E.join("\n\n")}`}]}}catch(e){throw new Error(`Failed to list sessions: ${(0,v.getErrorMessage)(e)}`)}}exports.handleSpawn=i,exports.extractRecentActivity=c,exports.formatTimeSince=l,exports.deriveActivityStatus=d,exports.handleCheckStatus=h,exports.handleListSessions=p,exports.handleSubagentOperations=async function(e,t,r,n,a,c){const l=r.child({tool:"subagent_operations"}),d=n.startTimer("tool_execution_time")
4577
4602
  try{const r=e,{sessionId:u,operation:m}=r
4578
4603
  let b
4579
4604
  switch(l.info("Processing subagent_operations",{operation:m,sessionId:u}),m){case"spawn":b=await i({agent:r.agent,prompt:r.prompt,requestId:r.requestId,allowParallel:r.allowParallel,useExistingSession:r.useExistingSession},t,l,a,c)
@@ -4584,11 +4609,11 @@ if(t){const r=i.getCLISessionClient()
4584
4609
  if(r){let o=await r.getByExternalId(t)
4585
4610
  if(o||(o=await r.createOrUpdateSession({externalSessionId:t,providerId:"opencode",status:"active"}),n.info("Created caller session in DB for re-parenting",{callerSessionId:t,dbId:o?.id})),o){const s=await r.getByExternalId(e).catch(()=>null),i=s?.parentSessionId??null
4586
4611
  i===o.id?n.debug("Parent unchanged for continued agent; no re-parent write",{sessionId:e}):(await r.createOrUpdateSession({externalSessionId:e,providerId:"opencode",parentSessionId:o.id,status:"active"}),n.info("RE-PARENTED continued agent: reports now flow to the current caller",{sessionId:e,fromParentDbId:i,toParentDbId:o.id,toCallerSessionId:t}))}}}}catch(e){n.warn("Failed to update parent session - reports may go to wrong parent",{error:(0,v.getErrorMessage)(e)})}let l
4587
- if(!l)try{const t=`http://localhost:${_}/api/sessions/${encodeURIComponent(e)}/agent-type`,r=await fetch(t,{signal:AbortSignal.timeout(8e3)})
4612
+ if(!l)try{const t=`http://localhost:${x}/api/sessions/${encodeURIComponent(e)}/agent-type`,r=await fetch(t,{signal:AbortSignal.timeout(8e3)})
4588
4613
  if(r.ok){const e=await r.json()
4589
4614
  e.success&&e.agentType&&(l=e.agentType)}}catch{n.warn("Agent type lookup failed for session",{sessionId:e})}if(!l)throw new f.ValidationError(`Cannot continue session ${e}: unable to determine agent type. The session's agent type could not be looked up from the action server.`)
4590
4615
  try{let a
4591
- try{const e=await fetch(`http://localhost:${_}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
4616
+ try{const e=await fetch(`http://localhost:${x}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
4592
4617
  if(e.ok){const t=await e.json()
4593
4618
  if(t.data?.defaultModel){const[e,...r]=t.data.defaultModel.split("/")
4594
4619
  a={providerID:e,modelID:r.join("/")}}}}catch{}const d=await o({apiClient:i,fromSessionId:c?.sessionId,toSessionId:e,body:t,logger:n}),u=t,h={body:t,fromSessionId:c?.sessionId,fromAgent:await w.sessionIdentity.getAgentTypeFor(c?.sessionId)||void 0,...d?{instructionCode:d}:{}},p=await(0,y.deliverToSession)(e,u,{kind:"agent-followup",partPayload:h})
@@ -4635,9 +4660,9 @@ break}case"list-sessions":const e={activeOnly:r.activeOnly,requestId:r.requestId
4635
4660
  b=await p(t,l,a,e)
4636
4661
  break
4637
4662
  default:throw new f.ValidationError(`Unknown operation: ${m}`)}d({success:!0}),n.incrementCounter(`subagent_operations_${m}_success`,1)
4638
- const k=S[m]
4639
- return k?(0,E.withAlchemyKind)(b,k):b}catch(e){return l.error("Subagent operations failed",{error:e instanceof Error?e.stack:String(e)}),d({success:!1}),n.incrementCounter("subagent_operations_errors",1),{content:[{type:"text",text:`Subagent operation failed: ${(0,v.getErrorMessage)(e)}`}],isError:!0}}}
4640
- const f=t(59860),m=t(15899),g=t(47617),y=t(39844),w=t(6688),b=t(87280),v=t(58824),k=t(79896),I=t(16928),$=t(8955),E=t(82272),S={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},x="true"===process.env.ALCHEMY_DEBUG_LOGS,_=(0,t(79414).getActionServerPort)(),C=new Map},3126(e,exports){exports.allTasksOf=async function(e,t){const r=new Map
4663
+ const k=E[m]
4664
+ return k?(0,S.withAlchemyKind)(b,k):b}catch(e){return l.error("Subagent operations failed",{error:e instanceof Error?e.stack:String(e)}),d({success:!1}),n.incrementCounter("subagent_operations_errors",1),{content:[{type:"text",text:`Subagent operation failed: ${(0,v.getErrorMessage)(e)}`}],isError:!0}}}
4665
+ const f=t(59860),m=t(15899),g=t(47617),y=t(39844),w=t(6688),b=t(87280),v=t(58824),k=t(79896),I=t(16928),$=t(8955),S=t(82272),E={spawn:"spawn-subagent",continue:"continue-subagent","check-status":"check-subagent-status",abort:"abort-subagent","read-messages":"read-subagent-messages","list-sessions":"list-subagent-sessions"},_="true"===process.env.ALCHEMY_DEBUG_LOGS,x=(0,t(79414).getActionServerPort)(),C=new Map},3126(e,exports){exports.allTasksOf=async function(e,t){const r=new Map
4641
4666
  for(let n=0;n<2e3;n+=100){const o=await e.listTasks({requestId:t,sortBy:"created",sortDirection:"asc",limit:100,offset:n}),s=Array.isArray(o)?o:[]
4642
4667
  for(const e of s)e?.id&&!r.has(e.id)&&r.set(e.id,e)
4643
4668
  if(s.length<100)break}const n=e=>{const t=Date.parse(String(e?.createdAt??""))
@@ -4664,16 +4689,16 @@ throw m.warn(e,{taskId:p,currentStatus:v.status}),new o.ValidationError(e)}let k
4664
4689
  if(u.workspaceManager){const e=u.workspaceManager.getCurrentContext(u.currentSessionId)
4665
4690
  if("multi-agent"===e.mode&&e.requestId){const{simpleGit}=t(83807)
4666
4691
  k=simpleGit({baseDir:e.basePath,binary:u.gitBinary}),m.debug("Using workspace-aware git instance for task completion",{taskId:p,mode:e.mode,requestId:e.requestId,basePath:e.basePath})}}let I=null,$=null
4667
- if(k)try{(await k.status()).isClean()||(await(0,n.assertGitNotUserFolder)(k,"task-completion commit"),await k.add("./*"),I=(await k.commit(`${y} (${p})`)).commit||null,I||($="commit produced no hash with a dirty tree"),m.info("Created commit for task completion",{taskId:p,commitId:I}))}catch(e){$=(0,l.getErrorMessage)(e),m.warn("Failed to commit changes",{error:(0,l.getErrorMessage)(e)})}let E=null
4692
+ if(k)try{(await k.status()).isClean()||(await(0,n.assertGitNotUserFolder)(k,"task-completion commit"),await k.add("./*"),I=(await k.commit(`${y} (${p})`)).commit||null,I||($="commit produced no hash with a dirty tree"),m.info("Created commit for task completion",{taskId:p,commitId:I}))}catch(e){$=(0,l.getErrorMessage)(e),m.warn("Failed to commit changes",{error:(0,l.getErrorMessage)(e)})}let S=null
4668
4693
  try{const e=h.getToolRecordClient()
4669
- e&&(E={id:(await e.createToolRecord({taskId:p,requestId:v.requestId,projectId:w,type:"complete-task",metadata:{status:"completed",timestamp:(new Date).toISOString(),taskTitle:v.title,completionSummary:y,lastCommit:I,completedAt:(new Date).toISOString()},note:`Completed task: ${y.substring(0,100)}${y.length>100?"...":""}`})).id})}catch(e){m.error("Failed to create tool record for task completion, but continuing",{error:e instanceof Error?e.stack:String(e),taskId:p,requestId:v.requestId})}try{const e=v.metadata||{},t=(new Date).toISOString(),n={status:"completed",metadata:{...e,lastCommit:I,completionSummary:y,completionRecordId:E?.id,completedAt:t}},o=await b.updateTask(p,n)
4670
- m.info("Raw task update response data",{responseData:JSON.stringify(o)}),m.debug("Task update response structure",{type:typeof o,isArray:Array.isArray(o),hasData:null!=o,properties:o?Object.keys(o):[],dataPreview:o?JSON.stringify(o).substring(0,200)+"...":"null"}),(0,a.extractDataFromResponse)(o,{logger:m,operationName:"updateTask",defaultValue:{id:p,status:"completed"}}),m.info("Successfully completed task",{taskId:p,recordId:E?.id})
4694
+ e&&(S={id:(await e.createToolRecord({taskId:p,requestId:v.requestId,projectId:w,type:"complete-task",metadata:{status:"completed",timestamp:(new Date).toISOString(),taskTitle:v.title,completionSummary:y,lastCommit:I,completedAt:(new Date).toISOString()},note:`Completed task: ${y.substring(0,100)}${y.length>100?"...":""}`})).id})}catch(e){m.error("Failed to create tool record for task completion, but continuing",{error:e instanceof Error?e.stack:String(e),taskId:p,requestId:v.requestId})}try{const e=v.metadata||{},t=(new Date).toISOString(),n={status:"completed",metadata:{...e,lastCommit:I,completionSummary:y,completionRecordId:S?.id,completedAt:t}},o=await b.updateTask(p,n)
4695
+ m.info("Raw task update response data",{responseData:JSON.stringify(o)}),m.debug("Task update response structure",{type:typeof o,isArray:Array.isArray(o),hasData:null!=o,properties:o?Object.keys(o):[],dataPreview:o?JSON.stringify(o).substring(0,200)+"...":"null"}),(0,a.extractDataFromResponse)(o,{logger:m,operationName:"updateTask",defaultValue:{id:p,status:"completed"}}),m.info("Successfully completed task",{taskId:p,recordId:S?.id})
4671
4696
  const i=v.requestId,c=v.requestId
4672
4697
  let u=`Task ${v.sequenceNumber&&c?(0,d.formatTaskRef)(c,v.sequenceNumber):p} (${v.title}) has been marked as completed.`
4673
4698
  if($&&(u+=`\nWARNING: the working tree had changes that could NOT be committed (${$}). The task record shows no commit; do not treat this work as delivered until the tree is committed.`),I&&(u+=`\nCommit: ${I.substring(0,8)}`),i)try{const e=await(0,r.allTasksOf)(b,i)
4674
4699
  m.info("Raw tasks list response data",{responseData:JSON.stringify(e)}),m.debug("Tasks list response structure",{type:typeof e,isArray:Array.isArray(e),hasData:null!=e,properties:e?Object.keys(e):[],dataPreview:e?JSON.stringify(e).substring(0,200)+"...":"null"})
4675
4700
  const t=(0,a.extractArrayFromResponse)(e,{logger:m,operationName:"listTasksForProgress",defaultValue:[]}),n=t.length,o=t.filter(e=>"completed"===e.status).length
4676
- m.info("Task progress check",{requestId:i,totalTasks:n,completedTasks:o}),u+=`\n\nTask Progress: ${o}/${n} tasks completed`,o===n&&(u+=`\n\nAll tasks for request ${i} are now completed! You may consider marking the request as completed using the complete-request tool.`)}catch(e){m.warn("Failed to check task progress",{error:(0,l.getErrorMessage)(e),requestId:i})}return g({success:!0}),{content:[{type:"text",text:u}],...(0,s.alchemyMeta)({kind:"complete-task",toolRecordId:E?.id})}}catch(e){throw m.error("Failed to complete task",{error:e instanceof Error?e.stack:String(e),taskId:p}),new Error(`Failed to complete task: ${(0,l.getErrorMessage)(e)}`)}}catch(t){if(m.error("Failed to complete task",{error:t instanceof Error?t.stack:String(t),args:e}),u.projectId)try{const r=h.getToolRecordClient()
4701
+ m.info("Task progress check",{requestId:i,totalTasks:n,completedTasks:o}),u+=`\n\nTask Progress: ${o}/${n} tasks completed`,o===n&&(u+=`\n\nAll tasks for request ${i} are now completed! You may consider marking the request as completed using the complete-request tool.`)}catch(e){m.warn("Failed to check task progress",{error:(0,l.getErrorMessage)(e),requestId:i})}return g({success:!0}),{content:[{type:"text",text:u}],...(0,s.alchemyMeta)({kind:"complete-task",toolRecordId:S?.id})}}catch(e){throw m.error("Failed to complete task",{error:e instanceof Error?e.stack:String(e),taskId:p}),new Error(`Failed to complete task: ${(0,l.getErrorMessage)(e)}`)}}catch(t){if(m.error("Failed to complete task",{error:t instanceof Error?t.stack:String(t),args:e}),u.projectId)try{const r=h.getToolRecordClient()
4677
4702
  if(r){const n=t instanceof o.NotFoundError||t instanceof o.ValidationError||t instanceof c.ZodError
4678
4703
  await r.createToolRecord({...n?{}:{taskId:e.taskId},projectId:u.projectId,type:"complete-task",metadata:{status:"failed",timestamp:(new Date).toISOString(),taskTitle:e.summary?.substring(0,100)+(e.summary?.length>100?"...":""),error:(0,l.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:String(t),attemptedTaskId:e.taskId,taskIdValidationFailed:n},note:`Failed to complete task ${e.taskId}`})}}catch(e){m.error("Failed to create failure record for task completion",{error:e instanceof Error?e.message:String(e)})}g({success:!1,error:t instanceof Error?t.name:"unknown"})
4679
4704
  let r=(0,l.getErrorMessage)(t)
@@ -4751,7 +4776,7 @@ for(const t of c)await e.updateTaskStatus(o(t),"completed")
4751
4776
  return c.map(o).join(", ")}
4752
4777
  const n=t(3126),o=e=>e.localId||e.id,s=e=>(e=>!0===e?.metadata?.onePiece)(e)||!0===e?.metadata?.systemOpened,i=e=>!["completed","canceled","cancelled","reverted"].includes(String(e.status)),a=new Map},91535(e,exports,t){exports.def=void 0
4753
4778
  const r=t(76183),n=t(37434)
4754
- exports.def={name:"task_inspect",description:"Read-only inspection of tasks within a request. Use to monitor coder progress; cannot mutate task state.\n\nOperations:\n\u2022 get-info - Get detailed information about a task (taskId)\n\u2022 list - List and filter tasks for a request (requestId)\n\nTask IDs use req######+#NN format (e.g. req000005#03). Task creation and completion happen exclusively through the coder's `tasks` tool.",schema:r.TaskInspectSchema,annotations:{title:"Task Inspect",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0},handler:(e,t)=>(0,n.handleTaskInspect)(e,t.context,t.apiClient,t.logger,t.metrics)}},37434(e,exports,t){exports.handleTaskInspect=async function(e,t,a,c,l){const d=c.child({tool:"task_inspect"})
4779
+ exports.def={name:"task_inspect",description:"Read-only view of a request's tasks: get-info (taskId, e.g. req000005#03) or list (requestId). Tasks are created and completed only by the worker's `tasks` tool.",schema:r.TaskInspectSchema,annotations:{title:"Task Inspect",readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0},handler:(e,t)=>(0,n.handleTaskInspect)(e,t.context,t.apiClient,t.logger,t.metrics)}},37434(e,exports,t){exports.handleTaskInspect=async function(e,t,a,c,l){const d=c.child({tool:"task_inspect"})
4755
4780
  try{const{taskId:o,operation:c}=e
4756
4781
  let u
4757
4782
  switch(d.info("Processing task_inspect",{taskId:o,operation:c}),c){case"get-info":d.info("Routing to get-task-information handler",{taskId:o}),u=await(0,r.handleGetTaskInformation)({taskId:o},t,a,d,l)
@@ -4763,7 +4788,7 @@ const h=i[c]
4763
4788
  return h?(0,s.withAlchemyKind)(u,h):u}catch(e){return d.error("task_inspect failed",{error:e instanceof Error?e.stack:String(e)}),l.incrementCounter("task_inspect_errors",1),{content:[{type:"text",text:`\u274c **Task Inspect Failed:** ${(0,o.getErrorMessage)(e)}`}],isError:!0}}}
4764
4789
  const r=t(93420),n=t(99863),o=t(58824),s=t(82272),i={"get-info":"get-task-info",list:"list-tasks"}},34150(e,exports,t){exports.def=exports.k=void 0
4765
4790
  const r=t(11569),n=t(93247)
4766
- exports.k=r.z.object({operation:r.z.enum(["create","done","cancel","list","current","start"]).describe("Operation to perform:\n\u2022 create - Create a new task (auto-starts if no other task is in_progress)\n\u2022 done - Complete the active (in_progress) task with a summary; auto-starts the next pending task\n\u2022 cancel - Drop a task that should not be done (the active one, or taskId) with a reason; it is recorded as canceled, not completed\n\u2022 current - Show only the active (in_progress) task\n\u2022 start - Switch which task is active (pauses any currently in_progress task back to pending)\n\u2022 list - List all tasks for the current request"),title:r.z.string().optional().describe("Task title (required for create)"),description:r.z.string().optional().describe("Task description (required for create)"),summary:r.z.string().optional().describe("Completion summary (required for done)"),reason:r.z.string().optional().describe("Why the task is dropped (required for cancel)"),taskId:r.z.string().optional().describe("Task to start (required for start), or to cancel (default: the active task). Accepts short forms #01 / 01 / 1 or canonical req######+#NN.")}),exports.def={name:"tasks",description:"Manage tasks within your current request, for work in several pieces: one task per piece. Work in one piece needs no call here; complete records it as the request's one task.\n\n\u2022 create { title, description }: creates a task. First task auto-starts.\n\u2022 done { summary }: completes the active task. Next pending task auto-starts.\n\u2022 cancel { reason, taskId? }: drops a task that turned out not to be needed or not to belong here. Recorded as canceled with your reason; never mark such a task done.\n\u2022 current: shows just the active task (the one your file edits will attach to).\n\u2022 start { taskId }: switch which pending task is active. Pauses the current active task.\n\u2022 list: shows all tasks and their status.\n\nNo requestId needed: resolved automatically from your session. taskId only required for start (accepts short forms like #01 / 01 / 1).",schema:exports.k,annotations:{title:"Tasks",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleTasks)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)}},93247(e,exports,t){function r(e){return{taskId:e.localId||e.id,title:e.title,sequenceNumber:e.sequenceNumber,status:e.status}}function n(e){return e.sequenceNumber?`#${e.sequenceNumber}`:`${e.taskId}`}async function o(e,t){const n=e.getTaskClient(),o=await(0,u.allTasksOf)(n,t),s=o.find(e=>"in_progress"===e.status),i=o.find(e=>"pending"===e.status)
4791
+ exports.k=r.z.object({operation:r.z.enum(["create","done","cancel","list","current","start"]),title:r.z.string().optional(),description:r.z.string().optional(),summary:r.z.string().optional(),reason:r.z.string().optional(),taskId:r.z.string().optional()}),exports.def={name:"tasks",description:"Tasks within your current request, for work in several pieces: one task per piece. Work in one piece needs no call here; complete records it as the request's one task.\n\n\u2022 create { title, description }: the first task starts at once.\n\u2022 done { summary }: completes the active task and starts the next pending one.\n\u2022 cancel { reason, taskId? }: drops a task that should not be done (the active one by default); never mark such a task done.\n\u2022 current: the active task, the one your file edits attach to.\n\u2022 start { taskId }: switch the active task.\n\u2022 list: all tasks and their status.\n\nThe request comes from your session; taskId accepts #01 / 01 / 1.",schema:exports.k,annotations:{title:"Tasks",readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1},handler:(e,t)=>(0,n.handleTasks)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)}},93247(e,exports,t){function r(e){return{taskId:e.localId||e.id,title:e.title,sequenceNumber:e.sequenceNumber,status:e.status}}function n(e){return e.sequenceNumber?`#${e.sequenceNumber}`:`${e.taskId}`}async function o(e,t){const n=e.getTaskClient(),o=await(0,u.allTasksOf)(n,t),s=o.find(e=>"in_progress"===e.status),i=o.find(e=>"pending"===e.status)
4767
4792
  return{active:s?r(s):null,nextPending:i?r(i):null}}async function s(e,t){try{const r=e.getTaskClient(),n=e.getRequestClient(),[o,s]=await Promise.all([(0,u.allTasksOf)(r,t),n.getRequest(t)]),i=o.filter(e=>"completed"===e.status).length,a=o.find(e=>"in_progress"===e.status),c=s?.status?` (${s.status})`:"",l=a?`, current: #${a.sequenceNumber??"?"} ${a.title}`:""
4768
4793
  return`\n\nRequest ${t}${c}: ${i}/${o.length} tasks done${l}`}catch{return""}}exports.handleTasks=async function(e,t,h,p,f,m){const g=p.child({tool:"tasks"}),{operation:y}=e
4769
4794
  try{const f=t.projectId
@@ -4826,7 +4851,7 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
4826
4851
  return o(t,e),t})
4827
4852
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.testingDefs=void 0
4828
4853
  const i=s(t(76183)),a=t(89671),c=e=>(t,r)=>e(t,r.context,r.apiClient,r.logger,r.metrics,r.cliOrigin,{progressToken:r.progressToken,sendNotification:r.sendNotification})
4829
- exports.testingDefs=[{name:"testing_process_list",description:"List all test processes for this project. Shows active/inactive status, command count, and scope.",schema:i.TestingProcessListSchema,annotations:{title:"List Test Processes",readOnlyHint:!0},handler:c(a.handleTestingProcessList)},{name:"testing_process_get",description:"Get detailed configuration of a test process including setup and test commands.",schema:i.TestingProcessGetSchema,annotations:{title:"Get Test Process",readOnlyHint:!0},handler:c(a.handleTestingProcessGet)},{name:"testing_process_create",description:"Create a new test process with optional setup commands and one or more test commands. Each command has a name, shell command, required flag, and timeout.",schema:i.TestingProcessCreateSchema,annotations:{title:"Create Test Process",readOnlyHint:!1},handler:c(a.handleTestingProcessCreate)},{name:"testing_process_update",description:"Update an existing test process. Can modify name, description, scope, active status, and test configuration.",schema:i.TestingProcessUpdateSchema,annotations:{title:"Update Test Process",readOnlyHint:!1},handler:c(a.handleTestingProcessUpdate)},{name:"testing_process_delete",description:"Delete a test process. Removes the registered process from the project; existing test runs against it stay in the database. Use when you want to fully reset bootstrapping (e.g. before re-running `tests setup`) rather than toggling active state.",schema:i.TestingProcessDeleteSchema,annotations:{title:"Delete Test Process",readOnlyHint:!1,destructiveHint:!0},handler:c(a.handleTestingProcessDelete)},{name:"execute_test_command",description:'Execute a specific command from a configured test process. Use commandName="setup" to run all setup commands, or specify a test command name to run it. Only commands defined in the test process can be executed.',schema:i.ExecuteTestCommandSchema,annotations:{title:"Execute Test Command",readOnlyHint:!1},handler:c(a.handleExecuteTestCommand)},{name:"report_test_results",description:"Report the results of a test run. Call this after running all test commands to record pass/fail status and per-command results. The test run is auto-discovered from the current request context.",schema:i.ReportTestResultsSchema,annotations:{title:"Report Test Results",readOnlyHint:!1},handler:c(a.handleReportTestResults)},{name:"tests",description:'Manage testing for this project. Operation "list" returns the registered test processes (use this before doing test-related work to see what is available). Operation "setup" spawns an interactive setup-architect session that proposes a test process for the user to confirm; from inside a request it opens only when the request carries a gate criterion (kind "gate"); call it once the check that criterion names is written on your branch. Completion of such a request refuses until the process is registered. It does not open while the user\'s test-check card or a setup from it is still open. You do not block waiting for it to finish; you will receive a report when the session completes. Operations "switch-off" / "switch-on" turn one registered check off or back on, on the user\'s quoted words (userSaid).',schema:i.TestsSchema,annotations:{title:"Tests",readOnlyHint:!1},handler:c(a.handleTests)}]},89671(e,exports,t){function r(e){return e.map(e=>{const t=Array.isArray(e.config?.commands)?e.config.commands:[],r=t.map(e=>"string"==typeof e?.command&&e.command.trim()?`\n - runs \`${e.command.trim()}\`${"number"==typeof e?.timeout?` (up to ${e.timeout}s)`:""}`:"").join("")
4854
+ exports.testingDefs=[{name:"testing_process_list",description:"List all test processes for this project. Shows active/inactive status, command count, and scope.",schema:i.TestingProcessListSchema,annotations:{title:"List Test Processes",readOnlyHint:!0},handler:c(a.handleTestingProcessList)},{name:"testing_process_get",description:"Get detailed configuration of a test process including setup and test commands.",schema:i.TestingProcessGetSchema,annotations:{title:"Get Test Process",readOnlyHint:!0},handler:c(a.handleTestingProcessGet)},{name:"testing_process_create",description:"Create a new test process with optional setup commands and one or more test commands. Each command has a name, shell command, required flag, and timeout.",schema:i.TestingProcessCreateSchema,annotations:{title:"Create Test Process",readOnlyHint:!1},handler:c(a.handleTestingProcessCreate)},{name:"testing_process_update",description:"Update an existing test process. Can modify name, description, scope, active status, and test configuration.",schema:i.TestingProcessUpdateSchema,annotations:{title:"Update Test Process",readOnlyHint:!1},handler:c(a.handleTestingProcessUpdate)},{name:"testing_process_delete",description:"Delete a test process. Removes the registered process from the project; existing test runs against it stay in the database. Use when you want to fully reset bootstrapping (e.g. before re-running `tests setup`) rather than toggling active state.",schema:i.TestingProcessDeleteSchema,annotations:{title:"Delete Test Process",readOnlyHint:!1,destructiveHint:!0},handler:c(a.handleTestingProcessDelete)},{name:"execute_test_command",description:'Execute a specific command from a configured test process. Use commandName="setup" to run all setup commands, or specify a test command name to run it. Only commands defined in the test process can be executed.',schema:i.ExecuteTestCommandSchema,annotations:{title:"Execute Test Command",readOnlyHint:!1},handler:c(a.handleExecuteTestCommand)},{name:"report_test_results",description:"Report the results of a test run. Call this after running all test commands to record pass/fail status and per-command results. The test run is auto-discovered from the current request context.",schema:i.ReportTestResultsSchema,annotations:{title:"Report Test Results",readOnlyHint:!1},handler:c(a.handleReportTestResults)},{name:"tests",description:"Test processes for this project. list: the registered processes. setup: spawns a setup-architect session that proposes a process for the user to confirm; inside a request it opens only when the request carries a gate criterion, once the check it names is on your branch; you get a report when it finishes. run: an async test run for your request, reported on your next turn. switch-off / switch-on: turn one check off or back on, on the user's quoted words (userSaid).",schema:i.TestsSchema,annotations:{title:"Tests",readOnlyHint:!1},handler:c(a.handleTests)}]},89671(e,exports,t){function r(e){return e.map(e=>{const t=Array.isArray(e.config?.commands)?e.config.commands:[],r=t.map(e=>"string"==typeof e?.command&&e.command.trim()?`\n - runs \`${e.command.trim()}\`${"number"==typeof e?.timeout?` (up to ${e.timeout}s)`:""}`:"").join("")
4830
4855
  return`**${e.name}** (${e.id})${e.scope?` [scope: ${e.scope}]`:""}\n ${e.isActive?"\u2705 Active":"\u23f8 Inactive"}: ${t.length} command${1===t.length?"":"s"}${r}${e.description?`\n ${e.description}`:""}`}).join("\n\n")}async function n(e,t,r,n,o){if(!e||!n.git)return{}
4831
4856
  let s
4832
4857
  try{const t=await o.proxyGet(`/api/projects/${n.projectId}/environments/${encodeURIComponent(e)}`)
@@ -4902,16 +4927,16 @@ if(g.push(`session: ${o||"NONE"}`),o){const t=await e.getSessionRequestId(n,s,o)
4902
4927
  if(g.push(`reqId: ${t||"NONE"}`),t){const e=await n.proxyGet(`/api/projects/${r.projectId}/requests/${t}/test-runs`),o=e?.data||e||[],s=Array.isArray(o)?o.find(e=>e.worktreePath||e.worktree_path):null
4903
4928
  g.push(`runs: ${Array.isArray(o)?o.length:0}, withWorktree: ${s?.id||"NONE"}, path: ${s?.worktreePath||s?.worktree_path||"NONE"}`),s&&(m=s.worktreePath||s.worktree_path,u.info("Using test worktree from request test run",{worktreePath:m,reqId:t}))}}}catch(e){u.warn("Failed to resolve test worktree",{error:(0,o.getErrorMessage)(e)})}if(!m)return d({success:!1,error:"no_worktree"}),{content:[{type:"text",text:`No test worktree found.\n\nDiagnostics:\n${g.length>0?g.map(e=>`\u2022 ${e}`).join("\n"):"\u2022 No session identity found"}\n\nEnsure this session is linked to a test process and a test run has been created.`}],isError:!0}
4904
4929
  const y=[],{default:w}=await Promise.resolve().then(()=>t.t(16928,23)),{default:b}=await Promise.resolve().then(()=>t.t(79896,23)),{ALCHEMY_DIR_NAME:v}=await Promise.resolve().then(()=>t(14139)),k=w.join(m,v,"command-logs")
4905
- try{b.mkdirSync(k,{recursive:!0})}catch{}const{runTrackedShellCommand:I}=await Promise.resolve().then(()=>t(44600)),{formatCombinedOutput:$}=await t.e(816).then(()=>t(1816)),{sessionIdentity:E}=await Promise.resolve().then(()=>t(6688)),S=c?.sessionId??E.getIdentity()?.sessionId
4930
+ try{b.mkdirSync(k,{recursive:!0})}catch{}const{runTrackedShellCommand:I}=await Promise.resolve().then(()=>t(44600)),{formatCombinedOutput:$}=await t.e(816).then(()=>t(1816)),{sessionIdentity:S}=await Promise.resolve().then(()=>t(6688)),E=c?.sessionId??S.getIdentity()?.sessionId
4906
4931
  for(const t of h){u.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:m})
4907
4932
  const r=`${t.name.replace(/[^a-zA-Z0-9._-]/g,"_")}-${Date.now()}`,n=w.join(k,`${r}.log`)
4908
4933
  let o=null
4909
- try{o=b.createWriteStream(n,{flags:"w"}),o.write(`# ${(new Date).toISOString()} starting: ${t.command}\n# cwd: ${m}\n# timeout: ${t.timeout}s\n\n`)}catch(e){u.warn("Failed to open command log file",{logPath:n,error:String(e)})}const s=await(0,a.runTrackedShellWithHeartbeat)(t.command,{cwd:m,timeoutMs:1e3*t.timeout,env:{...f.env,FORCE_COLOR:"0"},onChunk:o?(e,t)=>{o?.write(t)}:void 0},{id:r,name:t.name,sessionId:S,logPath:n},l)
4934
+ try{o=b.createWriteStream(n,{flags:"w"}),o.write(`# ${(new Date).toISOString()} starting: ${t.command}\n# cwd: ${m}\n# timeout: ${t.timeout}s\n\n`)}catch(e){u.warn("Failed to open command log file",{logPath:n,error:String(e)})}const s=await(0,a.runTrackedShellWithHeartbeat)(t.command,{cwd:m,timeoutMs:1e3*t.timeout,env:{...f.env,FORCE_COLOR:"0"},onChunk:o?(e,t)=>{o?.write(t)}:void 0},{id:r,name:t.name,sessionId:E,logPath:n},l)
4910
4935
  o&&(o.write(`\n# exit: ${s.exitCode}${s.timedOut?" (timed out)":""}\n`),o.end())
4911
4936
  const i=(s.stdout+(s.stderr?"\n"+s.stderr:"")).trim(),{output:c,truncatedNote:d,logFooter:h}=$(i,{invocationId:r})
4912
- if(y.push({name:t.name,command:t.command,exitCode:s.exitCode,output:c,timedOut:s.timedOut,invocationId:r,truncatedNote:d,logFooter:h}),"setup"===e.commandName&&0!==s.exitCode)break}const x=y.every(e=>0===e.exitCode),_=y.map(e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
4937
+ if(y.push({name:t.name,command:t.command,exitCode:s.exitCode,output:c,timedOut:s.timedOut,invocationId:r,truncatedNote:d,logFooter:h}),"setup"===e.commandName&&0!==s.exitCode)break}const _=y.every(e=>0===e.exitCode),x=y.map(e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
4913
4938
  return`### ${e.name}: ${t}\n\`\`\`\n$ ${e.command}\n${e.output}\n\`\`\`${e.truncatedNote}\n${e.logFooter}`}).join("\n\n")
4914
- return d({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName}: ${x?"PASSED":"FAILED"}\n\n${_}`}]}}catch(e){return d({success:!1,error:"execution_error"}),{content:[{type:"text",text:`Failed to execute test command: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleReportTestResults=async function(e,r,n,i,a,c){const l=a.startTimer("tool_execution_time")
4939
+ return d({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName}: ${_?"PASSED":"FAILED"}\n\n${x}`}]}}catch(e){return d({success:!1,error:"execution_error"}),{content:[{type:"text",text:`Failed to execute test command: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleReportTestResults=async function(e,r,n,i,a,c){const l=a.startTimer("tool_execution_time")
4915
4940
  try{const{resolveRequestIdOrError:a}=await Promise.resolve().then(()=>t(36651)),d=await a(n,i,c?.sessionId,"Cannot report test results: this session is not associated with a request.")
4916
4941
  if(!d.ok)return l({success:!1,error:"no_request_association"}),d.errorResult
4917
4942
  const u=d.requestId,h=await n.proxyGet(`/api/projects/${r.projectId}/requests/${u}/test-runs`),p=h?.data||h||[],f=Array.isArray(p)?p:[],m="string"==typeof e.testRunId&&e.testRunId.trim()?e.testRunId.trim():void 0,g=(m?f.find(e=>e.id===m):void 0)||f.find(e=>"pending"===e.status||"running"===e.status)||f[0]
@@ -5046,12 +5071,12 @@ if(r.startsWith(`${m}/`))try{return await a.access(c.resolve(t.mainBasePath,r)),
5046
5071
  for(const e of n)try{const n=await a.readFile(c.resolve(e,r)),o=`${m}/ask-${Date.now()}${c.extname(r).toLowerCase()}`,s=c.resolve(t.mainBasePath,o)
5047
5072
  return await a.mkdir(c.dirname(s),{recursive:!0}),await a.writeFile(s,n),{ok:!0,relPath:o}}catch{}return{ok:!1,error:`imagePath not found: ${r}`}}(String(e.imagePath),n)
5048
5073
  if(!v.ok)return w(`Could not resolve the image: ${v.error}`)
5049
- const k=`file:${v.relPath}`,I=Array.isArray(e.options)?e.options.filter(e=>"string"==typeof e&&!!e.trim()).map(e=>e.trim()):[],$=I.length>0?I.map((e,t)=>({id:`opt${t+1}`,label:e})):void 0,E=["string"==typeof e.context&&e.context.trim()?e.context.trim():null,`Anchored to ${k} -- open it to see the marked region this question is about.`].filter(Boolean).join("\n\n"),S=o.getSessionMessageClient()
5050
- if(!S)return w("Session message client not available.")
5051
- const x=await u.sessionIdentity.getAgentTypeFor(i)||"agent",_=await(0,h.resolveRequestIdOrError)(o,s,i,"no active request"),C=_.ok?_.requestId:void 0
5074
+ const k=`file:${v.relPath}`,I=Array.isArray(e.options)?e.options.filter(e=>"string"==typeof e&&!!e.trim()).map(e=>e.trim()):[],$=I.length>0?I.map((e,t)=>({id:`opt${t+1}`,label:e})):void 0,S=["string"==typeof e.context&&e.context.trim()?e.context.trim():null,`Anchored to ${k} -- open it to see the marked region this question is about.`].filter(Boolean).join("\n\n"),E=o.getSessionMessageClient()
5075
+ if(!E)return w("Session message client not available.")
5076
+ const _=await u.sessionIdentity.getAgentTypeFor(i)||"agent",x=await(0,h.resolveRequestIdOrError)(o,s,i,"no active request"),C=x.ok?x.requestId:void 0
5052
5077
  let q=!1,T=null
5053
- const j=await(0,d.handleSendQuestion)({questionType:$?"alternatives":"free-form",questionContent:{question:l,context:E,options:$},...e.priority?{priority:e.priority}:{}},n,s,o,S,g,{afterCreate:async e=>{q=!0
5054
- const t=await o.proxyPost(`/api/projects/${n.projectId}/artifact-annotations`,{ref:k,selectedText:"",anchor:{...b?{box:b}:{},questionCode:e.messageCode,...C?{viaRequest:C}:{}},body:l,annotationType:"comment",createdByAgent:x,createdBySessionId:i}),r=t?.data||t
5078
+ const j=await(0,d.handleSendQuestion)({questionType:$?"alternatives":"free-form",questionContent:{question:l,context:S,options:$},...e.priority?{priority:e.priority}:{}},n,s,o,E,g,{afterCreate:async e=>{q=!0
5079
+ const t=await o.proxyPost(`/api/projects/${n.projectId}/artifact-annotations`,{ref:k,selectedText:"",anchor:{...b?{box:b}:{},questionCode:e.messageCode,...C?{viaRequest:C}:{}},body:l,annotationType:"comment",createdByAgent:_,createdBySessionId:i}),r=t?.data||t
5055
5080
  T=r?.id||null}})
5056
5081
  if(q&&j&&Array.isArray(j.content)){const e=b?` (box ${[b.x,b.y,b.w,b.h].map(e=>e.toFixed(2)).join(",")})`:"",t=T?` When the answer arrives, resolve the annotation (document_operations, operation resolve-annotation, annotationId ${T}).`:""
5057
5082
  j.content.unshift({type:"text",text:`\u{1f4cd} Visual question anchored to ${k}${e}. The user sees it in the inbox, one click from the marked image.${t}`})}return y({success:!0}),(0,p.withAlchemyKind)(j,"send-question")}catch(e){return y({success:!1,error:"exception"}),s.warn("ask_user_visual failed",{error:(0,l.getErrorMessage)(e)}),{content:[{type:"text",text:`Visual question failed: ${(0,l.getErrorMessage)(e)}`}],isError:!0}}}
@@ -5076,7 +5101,7 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
5076
5101
  return o(t,e),t})
5077
5102
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.visualDefs=void 0
5078
5103
  const i=s(t(76183)),a=t(55432),c=t(93978),l=t(70089),d=t(70901),u=t(20339),h=t(75010),p=t(36399)
5079
- exports.visualDefs=[{name:"request_visual_feedback",description:'Ask the USER to mark up a screenshot of the UI and get their annotations back. EASIEST: start a box on a screenshot-capable env (experiment_start), then call this with `box` + `route` (e.g. "/") -- I render + screenshot that route for you. For FULL control (navigate, click, fill, scroll, log in, multi-step), pass `box` + `script`: a Playwright snippet (body of async (page, baseUrl) => {...}) -- I serve the app, launch headless Chromium, run your script, then screenshot the final state. You write ONLY the interaction; no serve/launch/flags. (Also: `fullPage` for the whole page; `box`+`path` for a screenshot you already wrote; `assetFilename` for an uploaded asset.) The user paints on it in the app (the action-required bell) and their annotated image comes back so you can SEE the changes they want. Returns the annotated image (or notes the user skipped / did not respond). Advisory -- never gates a merge.',schema:i.RequestVisualFeedbackSchema,annotations:{title:"Request Visual Feedback",readOnlyHint:!1},handler:(e,t)=>(0,a.handleRequestVisualFeedback)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"ask_user_visual",description:'Ask the USER a question ANCHORED TO A REGION OF AN IMAGE -- point at the thing instead of describing it. Give `imagePath` (a screenshot you produced, repo-relative) or `previewName` (a registered live preview; I freeze it into a snapshot), plus `box` ("x,y,w,h", normalized 0..1) marking the region, your `question`, and optionally `options` for multiple choice. The question is pinned to that region and waits in the user inbox: they open the marked image in one click and answer on the record. The answer returns like any question answer (inline if they respond during the hold window, otherwise you are resumed automatically -- do not poll). Unlike request_visual_feedback (which returns an image the user paints), this returns the user\'s WORDS about a region YOU marked.',schema:i.AskUserVisualSchema,annotations:{title:"Ask User (Visual)",readOnlyHint:!1},handler:(e,t)=>(0,c.handleAskUserVisual)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"capture_site",description:"Screenshot ANY http(s) URL -- an external/public website, or a locally served one -- using the platform's HOST-SIDE browser (works even though confined boxes have no internet). Stores the image in the project asset store and returns it so you can SEE it, plus a durable asset:<filename> ref to cite in documents or acceptance criteria (e.g. capture the ORIGINAL site once; later the acceptance judge views it via view_asset with no network). Options: `fullPage` for the whole scrollable page, `width` to emulate a phone (390). Reads the page only; adds one asset.",schema:i.CaptureSiteSchema,annotations:{title:"Capture Site Screenshot",readOnlyHint:!1},handler:(e,t)=>(0,l.handleCaptureSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"read_site",description:"Load ANY http(s) URL in the platform's HOST-SIDE browser and return the page's RENDERED visible text -- the verification tool for live claims. Booking engines, price pages, and opening-hours pages bot-wall the plain webfetch; a real rendered page shows what a human sees. Use this BEFORE labeling availability, price, or schedule claims as verified (cite the returned final URL); anything sourced only from search snippets stays INFERRED. Reads the page only; returns up to 60k chars of text.",schema:i.ReadSiteSchema,annotations:{title:"Read Site (Rendered Text)",readOnlyHint:!0},handler:(e,t)=>(0,d.handleReadSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"drive_site",description:'DRIVE a running page in the platform\'s host browser: click, type, press keys, scroll -- then read what the page says and looks like. read_site and capture_site show what a page renders; this one touches it, which is what "use it the way a user would" actually requires. Give it the URL and up to 20 steps; every step reports whether it found its target, so "the Build button does nothing" becomes evidence instead of a guess. Use clickat (x,y) for canvas or game surfaces with no DOM to select. Pass `session` to keep the page open between calls and continue where you left off -- that is how a drive becomes a session rather than repeated first impressions, and the only way to answer anything about accumulated state or elapsed time; pass close:true on the last call. A drive, not a test suite: persistent checks belong in a test process. The browser is HEADLESS on the platform host: the user never sees it and cannot take it over, so "the page is open for you" is never true. To hand a page to the user, post the final screenshot on the record (ask_user_visual with imagePath, or cite the screenshot path in a reply report) together with the URL and the exact steps left for them in THEIR browser. Never type a password, a key or an acceptance on the user\'s behalf.',schema:i.DriveSiteSchema,annotations:{title:"Drive Site (Interact)",readOnlyHint:!1},handler:(e,t)=>(0,u.handleDriveSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"fetch_asset",description:"Download an image or font from an http(s) URL straight INTO the working tree (e.g. `sites/acme/assets/hero.jpg`). Use it to vendor the assets a page needs so the result is self-contained -- confined runs have no network, so a page that hotlinks an image renders broken in the test gate and to anyone offline. webfetch cannot do this: it returns a page as text, or an image into the conversation, and neither writes bytes to a file. Images and fonts only, 12MB cap, path confined to the working tree.",schema:i.FetchAssetSchema,annotations:{title:"Fetch Asset",readOnlyHint:!1},handler:(e,t)=>(0,h.handleFetchAsset)(e,t.context,t.logger,t.metrics)},{name:"preview_site",description:"Serve a BUILT static directory (one containing index.html) so the user can actually open it -- returns a link they can click, and registers it as a live preview that renders in the artifact viewer. Use it when the deliverable is a page: after building or changing a static site, preview it and tell the user the link. The site is served from the local machine only (nothing is published); the preview lasts as long as the server runs. To show a request that has not merged yet (the user wants to see it before signing off), pass its requestId: the page is served from that request's own working copy. Never write copies or scratch files into main to preview something.",schema:i.PreviewSiteSchema,annotations:{title:"Preview Site",readOnlyHint:!1},handler:(e,t)=>(0,p.handlePreviewSite)(e,t.logger,t.metrics,t.context)}]},20339(e,exports,t){exports.handleDriveSite=async function(e,n,o,s,i,a){const c=o.startTimer("tool_execution_time"),l=e=>(c({success:!1,error:"bad"}),{content:[{type:"text",text:e}],isError:!0})
5104
+ exports.visualDefs=[{name:"request_visual_feedback",description:'Ask the USER to mark up a screenshot of the UI and get their annotations back. EASIEST: start a box on a screenshot-capable env (experiment_start), then call this with `box` + `route` (e.g. "/") -- I render + screenshot that route for you. For FULL control (navigate, click, fill, scroll, log in, multi-step), pass `box` + `script`: a Playwright snippet (body of async (page, baseUrl) => {...}) -- I serve the app, launch headless Chromium, run your script, then screenshot the final state. You write ONLY the interaction; no serve/launch/flags. (Also: `fullPage` for the whole page; `box`+`path` for a screenshot you already wrote; `assetFilename` for an uploaded asset.) The user paints on it in the app (the action-required bell) and their annotated image comes back so you can SEE the changes they want. Returns the annotated image (or notes the user skipped / did not respond). Advisory -- never gates a merge.',schema:i.RequestVisualFeedbackSchema,annotations:{title:"Request Visual Feedback",readOnlyHint:!1},handler:(e,t)=>(0,a.handleRequestVisualFeedback)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"ask_user_visual",description:'Ask the USER a question ANCHORED TO A REGION OF AN IMAGE -- point at the thing instead of describing it. Give `imagePath` (a screenshot you produced, repo-relative) or `previewName` (a registered live preview; I freeze it into a snapshot), plus `box` ("x,y,w,h", normalized 0..1) marking the region, your `question`, and optionally `options` for multiple choice. The question is pinned to that region and waits in the user inbox: they open the marked image in one click and answer on the record. The answer returns like any question answer (inline if they respond during the hold window, otherwise you are resumed automatically -- do not poll). Unlike request_visual_feedback (which returns an image the user paints), this returns the user\'s WORDS about a region YOU marked.',schema:i.AskUserVisualSchema,annotations:{title:"Ask User (Visual)",readOnlyHint:!1},handler:(e,t)=>(0,c.handleAskUserVisual)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"capture_site",description:"Screenshot an http(s) URL, public or locally served, with the platform's host browser (it reaches the internet when boxes cannot). Stores the image as a project asset and returns it with an asset:<filename> ref to cite in documents or criteria, which a judge can later view with view_asset offline.",schema:i.CaptureSiteSchema,annotations:{title:"Capture Site Screenshot",readOnlyHint:!1},handler:(e,t)=>(0,l.handleCaptureSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"read_site",description:"Load an http(s) URL in the platform's host browser and return the page's rendered visible text (up to 60k chars); pages that bot-wall webfetch render here. Use it before calling an availability, price or schedule claim verified, and cite the final URL; a claim sourced only from search snippets stays inferred.",schema:i.ReadSiteSchema,annotations:{title:"Read Site (Rendered Text)",readOnlyHint:!0},handler:(e,t)=>(0,d.handleReadSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"drive_site",description:"DRIVE a running page in the platform's headless host browser: click, type, press keys, scroll, then read what the page says and looks like. Up to 20 steps per call; each reports whether it found its target. clickat (x,y) for canvas or game surfaces with no DOM. Pass `session` to keep the page open between calls (the only way to check accumulated state or elapsed time) and close:true on the last call. Persistent checks belong in a test process. The user never sees this browser: to hand a page to them, give the URL, the steps left for them in their own browser, and the final screenshot (ask_user_visual with imagePath, or cite it in a report). Never type a password, a key or an acceptance on the user's behalf.",schema:i.DriveSiteSchema,annotations:{title:"Drive Site (Interact)",readOnlyHint:!1},handler:(e,t)=>(0,u.handleDriveSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"fetch_asset",description:"Download an image or font from an http(s) URL straight INTO the working tree (e.g. `sites/acme/assets/hero.jpg`). Use it to vendor the assets a page needs so the result is self-contained -- confined runs have no network, so a page that hotlinks an image renders broken in the test gate and to anyone offline. webfetch cannot do this: it returns a page as text, or an image into the conversation, and neither writes bytes to a file. Images and fonts only, 12MB cap, path confined to the working tree.",schema:i.FetchAssetSchema,annotations:{title:"Fetch Asset",readOnlyHint:!1},handler:(e,t)=>(0,h.handleFetchAsset)(e,t.context,t.logger,t.metrics)},{name:"preview_site",description:"Serve a built static directory (containing index.html) from this machine and return a link the user can open; it is also registered as a live preview in the artifact viewer. Use it when the deliverable is a page. Pass requestId to serve an unmerged request's own working copy. Never copy files into main to preview them.",schema:i.PreviewSiteSchema,annotations:{title:"Preview Site",readOnlyHint:!1},handler:(e,t)=>(0,p.handlePreviewSite)(e,t.logger,t.metrics,t.context)}]},20339(e,exports,t){exports.handleDriveSite=async function(e,n,o,s,i,a){const c=o.startTimer("tool_execution_time"),l=e=>(c({success:!1,error:"bad"}),{content:[{type:"text",text:e}],isError:!0})
5080
5105
  try{const o=(e.url||"").trim()
5081
5106
  if(!/^https?:\/\//i.test(o))return l("`url` must be an http(s) URL.")
5082
5107
  const d=Array.isArray(e.steps)?e.steps:[]
@@ -5172,21 +5197,24 @@ o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:
5172
5197
  Object.defineProperty(exports,"__esModule",{value:!0}),n(t(70441),exports),n(t(70270),exports)},70270(e,exports){Object.defineProperty(exports,"__esModule",{value:!0})},26540(e,exports){var t
5173
5198
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.LogLevel=void 0,function(e){e.ERROR="error",e.WARN="warn",e.INFO="info",e.DEBUG="debug"}(t||(exports.LogLevel=t={}))},76183(e,exports,t){Object.defineProperty(exports,"__esModule",{value:!0}),exports.DocumentOperationsSchema=exports.SearchDocumentsSchema=exports.CreateDocumentSchema=exports.ReplaceLinesSchema=exports.InsertLinesSchema=exports.PlannerOpsSchema=exports.ResearchFanoutSchema=exports.SubagentOperationsSchema=exports.TaskInspectSchema=exports.CompleteTaskSchema=exports.GetTaskInformationSchema=exports.GetRequestInformationSchema=exports.ListRequestsSchema=exports.ListTasksSchema=exports.SearchSchema=exports.QueryHistorySchema=exports.RequestDiffSchema=exports.MergePreviewSchema=exports.FileRecoverSchema=exports.FileHistorySchema=exports.DeleteFilesSchema=exports.DeleteFileSchema=exports.ReplaceInFilesSchema=exports.ReplaceStringsSchema=exports.ReplaceStringSchema=exports.MoveFilesSchema=exports.CreateDirectorySchema=exports.GetFileInfoSchema=exports.ListDirectorySchema=exports.ReadFilesSchema=exports.ViewAssetSchema=exports.WriteFileSchema=exports.UpdateTaskStatusSchema=exports.CreateTaskSchema=exports.CreateRequestSchema=exports.CompleteRequestSchema=exports.skipTestsSchema=exports.skipTestsReasonSchema=exports.ExecutedTestSchema=exports.TestVerificationSchema=exports.TestDefinitionSchema=exports.AcceptanceCriterionSchema=exports.RecordTypeEnum=exports.RequestStatusEnum=exports.TaskStatusEnum=exports.RequestStatusFilterEnum=exports.SettableRequestTypeEnum=exports.RequestTypeEnum=exports.TestCoverageTypeEnum=exports.CompletionStatusEnum=void 0,exports.EnvironmentSetupSchema=exports.EnvironmentVerifySchema=exports.EnvironmentDeleteSchema=exports.EnvironmentUpdateSchema=exports.EnvironmentCreateSchema=exports.EnvironmentVerifyStackSchema=exports.EnvironmentStatusSchema=exports.EnvironmentTeardownSchema=exports.EnvironmentProvisionSchema=exports.EnvironmentGetSchema=exports.AskUserVisualSchema=exports.RequestVisualFeedbackSchema=exports.FetchAssetSchema=exports.PreviewSiteSchema=exports.CaptureSiteSchema=exports.DriveSiteSchema=exports.ReadSiteSchema=exports.ExperimentListSchema=exports.ExperimentStopSchema=exports.ExperimentStartSchema=exports.ResultInspectSchema=exports.ResultsSchema=exports.EnvironmentListSchema=exports.TestingProcessDeleteSchema=exports.TestingProcessCreateSchema=exports.TestingProcessGetSchema=exports.TestingProcessListSchema=exports.MetaOperationsSchema=exports.MetaAgentListSchema=exports.MetaWorkflowRemoveRecipeSchema=exports.MetaWorkflowAddRecipeSchema=exports.MetaHookCreateSchema=exports.MetaHookListSchema=exports.MetaRecipeRemoveHookSchema=exports.MetaRecipeAddHookSchema=exports.MetaRecipeCreateSchema=exports.MetaRecipeGetSchema=exports.MetaRecipeListSchema=exports.MetaWorkflowUpdateSchema=exports.MetaWorkflowCreateSchema=exports.MetaWorkflowGetSchema=exports.MetaWorkflowListSchema=exports.QuickReplaceSchema=exports.QuickWriteSchema=exports.RequestOperationsSchema=exports.ManageFilesSchema=exports.ModifyFileSchema=exports.RevertRequestSchema=exports.PlanWriteSchema=exports.PlanReadSchema=void 0,exports.AnswerQuestionSchema=exports.McpConnectionsSchema=exports.DirectiveOperationsSchema=exports.CommitToViewedSessionSchema=exports.SessionMessageOperationsSchema=exports.OpeningBriefSectionSchema=exports.ReportTestResultsSchema=exports.CommandsSchema=exports.RunSchema=exports.ExecuteTestCommandSchema=exports.TestsSchema=exports.TestingProcessUpdateSchema=void 0
5174
5199
  const r=t(11569),n=t(8955),o=t(94466),s=(e,t,n={})=>r.z.string().refine(t=>!(!n.allowEmpty||""!==t)||!!t&&e.some(e=>e.test(t)),{message:t}),i=s([o.TASK_REF_PATTERN,o.TASK_REF_SHORT_PATTERN],"Task ID must be req######+#NN (e.g. req000005#03) or a short form like #01, 01, or 1 (resolved against the active request)."),a=s([/^req\d{6}$/],"Request ID must match format: req###### (e.g. req000001)"),c=s([/^doc_[0-9A-Z]{26}$/i,/^(doc|plan|quest|jour)\d{6}$/],"Document ID must match format: doc_[26 alphanumeric] (ULID) or doc######/plan######/quest######/jour###### (local ID)",{allowEmpty:!0}),l=e=>e.path&&e.documentId?{...e,documentId:void 0}:e,d=e=>Boolean(e.path)||Boolean(e.documentId)
5175
- exports.CompletionStatusEnum=r.z.enum(["complete","incomplete","partial"]),exports.TestCoverageTypeEnum=r.z.enum(["unit","e2e","integration","manual"]),exports.RequestTypeEnum=r.z.enum(["code","quick-edit","drive","writing","engineering","setup"]),exports.SettableRequestTypeEnum=r.z.enum(["code","drive","writing","engineering"]),exports.RequestStatusFilterEnum=r.z.enum(["draft","open","in_progress","ready_for_review","ready_for_merge","resolving_conflicts","conflicted","completed","canceled","reverted","in_flight"]).describe("A status, or in_flight: every request under way."),exports.TaskStatusEnum=r.z.enum(["pending","in_progress","ready_for_review","completed","canceled","reverted"]),exports.RequestStatusEnum=r.z.enum(["open","in_progress","ready_for_review","ready_for_merge","completed","canceled","reverted","conflicted"]),exports.RecordTypeEnum=r.z.enum(["write","modify","blocker","resolution","discovery","test","delete"]),exports.AcceptanceCriterionSchema=r.z.object({description:r.z.string(),createdAt:r.z.string().optional(),updatedAt:r.z.string().optional()}),exports.TestDefinitionSchema=r.z.object({id:r.z.string(),description:r.z.string(),status:exports.CompletionStatusEnum,coverageType:exports.TestCoverageTypeEnum,accepts:r.z.array(r.z.string())}),exports.TestVerificationSchema=r.z.object({criteria:r.z.array(r.z.string()),tests:r.z.array(r.z.string()),coverageType:exports.TestCoverageTypeEnum,coverageRatio:r.z.number(),createdAt:r.z.string().optional(),updatedAt:r.z.string().optional()}),exports.ExecutedTestSchema=r.z.object({id:r.z.string(),testId:r.z.string(),status:exports.CompletionStatusEnum,results:r.z.string(),coverage:r.z.array(r.z.string()),executedAt:r.z.string()}),exports.skipTestsReasonSchema=r.z.string().min(20).optional().describe("Opt out of all tests (reason of 20+ characters). Refused under the strict test gate and when any test command is required. For some commands, use skipTests."),exports.skipTestsSchema=r.z.array(r.z.object({commandName:r.z.string().min(1).describe("A registered test command's name."),reason:r.z.string().min(20).describe("Why it is irrelevant to the change (20+ characters).")})).optional().describe("Skip test commands by name; not with skipTestsReason. Refused under the strict test gate and for required commands."),exports.CompleteRequestSchema=r.z.object({status:exports.RequestStatusEnum,acceptanceCriteria:r.z.array(exports.AcceptanceCriterionSchema),testVerification:exports.TestVerificationSchema.optional(),completionSummary:r.z.string().optional()}),exports.CreateRequestSchema=r.z.object({title:r.z.string(),process:r.z.string().optional().describe('The workflow process this request follows, by name (e.g. "CFD case"): it sets the kind and adds the process\'s rules as criteria. Name one when the request clearly is that kind of work; with none, the reply lists those that fit. Unknown names are refused with the list.'),userSaid:r.z.string().min(8).max(600).optional().describe("The user's words asking for this request, copied exactly in their language: the phrase that asks, not the whole message (matched as an exact excerpt). Pass it whenever the request comes from what the user wrote; with several messages at once, the request is filed under the one it quotes. Required for a 'gate' criterion, whose words must ask for the check to keep running."),type:exports.SettableRequestTypeEnum.describe("'code': builds or fixes behaviour: a branch, a worker, the workflow's gates, a merge. 'writing': prose that lives in the repo (docs, page copy), handled the same way. 'drive': a verification errand: the driver exercises the product through its user doors against the criteria as claims; no branch, no merge, gates off, except that an UNMET claim waits for the user's sign-off (driveFindings: 'unmet' default, 'unchecked' also stops on claims never reached, 'off' always closes). 'engineering': a model, simulation or analysis whose numbers are the product; handled like code, reviewed on its idealisation and evidence, and the worker validates against something independent. Prefer it over 'code' when the deliverable is a result and its justification."),description:r.z.string().optional().describe('What to build or find out and why, for whoever picks it up; the record, not a reply to the user, so rules about how you talk to them do not apply. The user reads it on the request page: call them "you", never "the user"; name agents by their job (the worker, the Alchemist).'),quotesMessage:r.z.boolean().optional().describe("true when the user's message is the specification (a brief, a pasted spec, text for the product): it is copied into the description word for word under 'Your words:', and your description is only what you add. Do not write their message out yourself. With several messages, userSaid picks which."),acceptanceCriteria:r.z.array(r.z.union([r.z.string(),r.z.object({description:r.z.string().min(1),beforeMerge:r.z.boolean().optional().describe("[user-input] criteria only: true when the user asked to check it themselves before it merges. The merge then waits for them at every Autonomy level; otherwise Self-driving treats no answer as not checked by them."),command:r.z.string().min(1).max(500).optional().describe("The command that decides this criterion, if one does (e.g. `.venv/bin/python -m pytest tests`), as it runs in the project's box. The worker may add checks, not replace it. Keep it out of the text."),kind:r.z.enum(["objective","subjective","gate"]).describe('objective: provable by running something (strings, files, exit codes, return values). subjective: needs judgment (looks, reads, compares), made by the acceptance judge. The kind is what the claim is, not what the project can run today: "returns an int" is objective even with no tests, and the user is told nothing can prove it. gate: a check that runs on every merge from now on, met only by a test process registered for this request; completion is refused until one exists, and its gate run is the verdict.')})])).optional().describe('Verifiable done-conditions: a string, or { description, kind } to say how it is proven. Split a mixed claim into one of each kind. A criterion says what must be true of the result, never that a check was run or its output recorded ("prints the tracked-file count", not "was run and its output recorded"). Put what must come from the user in its own criterion marked [user-input]; completion is blocked until the worker asks them. Criteria show on the user\'s cards, so address them as "you": "[user-input] You have tried it and say it works".'),draft:r.z.boolean().optional().describe("A draft: on the timeline, no branch, nothing runs until the user starts it (a title alone is fine). How a worker hands over work nobody asked for (with follows: your request)."),delegate:r.z.boolean().optional(),follows:r.z.string({required_error:"Required. Every request either follows earlier work or starts fresh: pass the origin request id (req######) or the word 'new'."}).refine(e=>"new"===e||/^(round:)?req\d{6}$/.test(e),{message:"Pass a request id like req000031, 'round:req000031' for another round of work that has landed, or 'new' for genuinely new work."}).describe("What this grows out of. 'round:req######': another pass at what that merged request built (bigger, another colour, the wording back); shown with it as one item and built by its builder. 'req######': a follow-up, a fix for its fallout or a continuation, only when the user pointed at it (anchored the message to it, named it, said 'that change') or you are working it; the request you are on is usually the origin. 'new': fresh work, including a new feature on an existing app and anything the user asks for on its own, even if it touches an earlier request's work (name that one in the description). The project's founding request is not an origin for later work. Only this field links requests."),planDocumentId:r.z.string().optional().describe("The plan document that decomposes this work (plan######); not the schedule that dispatched you (that is scheduleDocumentId)."),scheduleDocumentId:r.z.string().optional().describe("The schedule document that dispatched this request; only when a schedule told you to create it, with the id it gave."),questDocumentId:r.z.string().optional(),waitsOn:r.z.array(r.z.string().regex(/^req\d{6}$/)).max(5).optional().describe("Requests this one waits for: registered now, started once all have landed. A canceled one never lands; the wait holds until moved or cleared. Declare it here, not afterwards: work is dispatched at registration. When this grows out of a request that has not landed, choose this or startsNow."),startsNow:r.z.boolean().optional().describe("The other answer to waitsOn: true starts this now, alongside the unlanded request. The user's words decide (at once: start now; one after the other: wait). If they said nothing about order, wait when both change the same code."),asked:r.z.array(r.z.string().regex(/^Q-[A-Z0-9]+-\d+$/)).max(5).optional().describe("Your open questions to the user (Q-codes) that decide something here: the worker does not ask them again and gets the answer too. Pass them whenever the request builds on an answer you are waiting for."),userChoices:r.z.array(r.z.string().regex(/^Q-[A-Za-z0-9]+-\d+$/)).max(10).optional().describe("Cards the user answered that this request builds on (e.g. Q-ABCD1234-001); their picks count as sources in review, so carry the picked words into the description. A card still waiting for the user may be named (file on its default); it counts once answered. A card closed by an agent, a default or a withdrawal is refused."),replaces:r.z.string().regex(/^req\d{6}$/).optional().describe("The canceled request this one refiles. What waited on it waits on this one instead. Only a canceled request can be replaced."),areas:r.z.array(r.z.string()).max(3).optional().describe("Work-area labels, 1-3, required unless draft: lowercase kebab paths (e.g. 'graphics/opengl/shader-cache'). Pick from request_operations list-areas; add a new name only when nothing fits, nested under an existing parent. New names are registered automatically.")}),exports.CreateTaskSchema=r.z.object({requestId:a,title:r.z.string(),description:r.z.string()}),exports.UpdateTaskStatusSchema=r.z.object({taskId:i,status:exports.TaskStatusEnum,note:r.z.string()}),exports.WriteFileSchema=r.z.object({path:r.z.string().optional(),documentId:c.optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())]).optional(),fromResult:r.z.string().optional().describe('Instead of content: a recorded result (its address, or a unique prefix) whose bytes become this file, unchanged. For a file made somewhere you cannot write the worktree from, e.g. a .docx made in a sealed box and recorded with results({ operation: "record", box }). Files only (with path), up to 20 MB; bulk stays in the store (results pin).'),format:r.z.enum(["text","json"]).optional(),append:r.z.boolean().optional(),comment:r.z.string()}).transform(l).refine(d,{message:"path or documentId required"}).refine(e=>void 0!==e.content!==Boolean(e.fromResult),{message:"pass content or fromResult (exactly one)"}),exports.ViewAssetSchema=r.z.object({filename:r.z.string().optional().describe("An image in .alchemy_lab/assets/ (e.g., 01JMXYZ.png), as named by an asset:<filename> ref"),path:r.z.string().optional().describe("An image, PDF or office file (Word, Excel, PowerPoint, OpenDocument, .doc/.xls/.ppt/.rtf) in the working tree, e.g. sources/connection-code-v7.2.pdf or docs/memo.docx. Office files are rendered to PDF pages."),result:r.z.string().optional().describe('A recorded result (its address, or a unique prefix): a PDF, an image or an office file, e.g. one made in the office box and recorded with results({ operation: "record", box }). Pass exactly one of filename, path or result.'),pages:r.z.string().optional().describe('PDF and office files: which pages to show, e.g. "3", "12-18" or "1,4,7-9" (at most 20 per view). Required for documents over 20 pages.'),revision:r.z.string().optional().describe('With path: show the file as committed at this commit, branch or tag instead of as it is now. "base" is the version before this branch changed it, so a changed document can be compared: view it with revision "base", then without.')}),exports.ReadFilesSchema=r.z.object({paths:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),lines:r.z.string().optional(),symbol:r.z.string().max(200).optional()}).transform(l).refine(d,{message:"path or documentId required"})),encoding:r.z.enum(["utf8","utf-8","ascii","binary"]).optional()}),exports.ListDirectorySchema=r.z.object({path:r.z.string(),levels:r.z.number().min(1).max(4).optional(),sortBy:r.z.enum(["name","modified","size","type"]).optional()}),exports.GetFileInfoSchema=r.z.object({path:r.z.string()}),exports.CreateDirectorySchema=r.z.object({path:r.z.string(),comment:r.z.string().optional()}),exports.MoveFilesSchema=r.z.object({operations:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()})),comment:r.z.string()}),exports.ReplaceStringSchema=r.z.object({path:r.z.string(),replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional(),comment:r.z.string()}),exports.ReplaceStringsSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional()}).transform(l).refine(d,{message:"path or documentId required"})),comment:r.z.string()}),exports.ReplaceInFilesSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),replacements:r.z.array(r.z.object({old:r.z.string().min(1,"old string cannot be empty"),new:r.z.string(),match_mode:r.z.enum(["unique","all"]).optional()})).min(1,"At least one replacement required"),fuzzy_match:r.z.boolean().optional()}).transform(l).refine(d,{message:"path or documentId required"})).min(1,"At least one operation required"),fuzzy_match:r.z.boolean().optional().default(!0),comment:r.z.string()}),exports.DeleteFileSchema=r.z.object({path:r.z.string(),comment:r.z.string()}),exports.DeleteFilesSchema=r.z.object({paths:r.z.array(r.z.string()).optional(),operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional()}).transform(l).refine(d,{message:"path or documentId required"})).optional(),comment:r.z.string()}).refine(e=>e.paths&&!e.operations||!e.paths&&e.operations,{message:"paths or operations required"}).refine(e=>e.paths?.length||e.operations?.length,{message:"path/operation required"}),exports.FileHistorySchema=r.z.object({path:r.z.string(),limit:r.z.number().min(1).max(100).default(10),showDiff:r.z.boolean().default(!1),showTraceability:r.z.boolean().default(!0),branch:r.z.string().optional(),commit:r.z.string().optional(),since:r.z.string().optional()}),exports.FileRecoverSchema=r.z.object({path:r.z.string(),commitHash:r.z.string(),createBackup:r.z.boolean().default(!0)}),exports.MergePreviewSchema=r.z.object({requestId:a,format:r.z.enum(["summary","unified"]).default("summary").describe("summary = per-file counts; unified = the diff the merge would add to your branch"),path:r.z.string().optional().describe('Optional git pathspec to scope the preview (e.g. "src/hud.css", "src/", "**/*.ts")')}),exports.RequestDiffSchema=r.z.object({requestId:a,format:r.z.enum(["summary","unified"]).default("summary"),path:r.z.string().optional()}),exports.QueryHistorySchema=r.z.object({queryType:r.z.enum(["file-history","task-file-history","request-file-history","recover","blame"],{errorMap:()=>({message:"Invalid queryType. Valid options: file-history, task-file-history, request-file-history, recover, blame"})}),fileHistory:r.z.object({path:r.z.string(),limit:r.z.number().min(1).max(100).default(10),showDiff:r.z.boolean().default(!1),showTraceability:r.z.boolean().default(!0),branch:r.z.string().optional(),commit:r.z.string().optional(),since:r.z.string().optional()}).optional(),taskHistory:r.z.object({taskId:i,showDiff:r.z.boolean().optional()}).optional(),requestHistory:r.z.object({requestId:a,detailLevel:r.z.enum(["summary","detailed"]).default("summary"),taskId:i.optional(),showDiff:r.z.boolean().optional(),fileFilter:r.z.string().optional()}).optional(),recover:r.z.object({path:r.z.string(),commitHash:r.z.string().optional(),createBackup:r.z.boolean().default(!0)}).optional(),blame:r.z.object({path:r.z.string(),lines:r.z.string().optional(),branch:r.z.string().optional()}).optional()}).refine(e=>"file-history"===e.queryType?e.fileHistory&&!e.requestHistory&&!e.recover:"task-file-history"===e.queryType?e.taskHistory&&!e.fileHistory&&!e.requestHistory&&!e.recover:"request-file-history"===e.queryType?e.requestHistory&&!e.fileHistory&&!e.recover:"recover"===e.queryType?e.recover&&!e.fileHistory&&!e.requestHistory&&!e.blame:"blame"===e.queryType&&e.blame&&!e.fileHistory&&!e.requestHistory&&!e.recover,{message:"Invalid query data"}),exports.SearchSchema=r.z.object({needle:r.z.string().optional(),directory:r.z.string().optional(),directories:r.z.array(r.z.string()).max(8).optional(),options:r.z.object({caseSensitive:r.z.boolean().optional(),fileExtension:r.z.string().optional(),maxResults:r.z.number().optional(),useRegex:r.z.boolean().optional(),searchIn:r.z.enum(["content","filename","both"]).default("content").optional(),contextLines:r.z.number().min(0).optional(),levels:r.z.number().min(1).max(4).optional(),sortBy:r.z.enum(["name","modified","size","type"]).optional()}).optional()}),exports.ListTasksSchema=r.z.object({requestId:a,filters:r.z.object({status:r.z.enum(["pending","in_progress","ready_for_review","completed","canceled","reverted"]).optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}),exports.ListRequestsSchema=r.z.object({filters:r.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:r.z.string().optional(),scheduleDocumentId:r.z.string().optional().describe("Requests a schedule fired: that schedule document."),questDocumentId:r.z.string().optional().describe("Legacy name of scheduleDocumentId."),dateFrom:r.z.string().optional().describe("Only requests with activity from this date on (YYYY-MM-DD means that whole day in the user's clock, or an ISO timestamp)."),dateTo:r.z.string().optional().describe("Only requests with activity up to this date (YYYY-MM-DD includes the whole day)."),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}),exports.GetRequestInformationSchema=r.z.object({requestId:a}),exports.GetTaskInformationSchema=r.z.object({taskId:i}),exports.CompleteTaskSchema=r.z.object({taskId:i,summary:r.z.string()}),exports.TaskInspectSchema=r.z.object({operation:r.z.enum(["get-info","list"]),taskId:i.optional(),requestId:a.optional(),status:r.z.enum(["pending","in_progress","ready_for_review","completed","canceled","reverted"]).optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).superRefine((e,t)=>{"get-info"!==e.operation||e.taskId||t.addIssue({code:"custom",message:'taskId is required for get-info. Example: { "operation": "get-info", "taskId": "req000001#01" }'}),"list"!==e.operation||e.requestId||t.addIssue({code:"custom",message:'requestId is required for list. Example: { "operation": "list", "requestId": "req000001" }'})})
5200
+ exports.CompletionStatusEnum=r.z.enum(["complete","incomplete","partial"]),exports.TestCoverageTypeEnum=r.z.enum(["unit","e2e","integration","manual"]),exports.RequestTypeEnum=r.z.enum(["code","quick-edit","drive","writing","engineering","setup"]),exports.SettableRequestTypeEnum=r.z.enum(["code","drive","writing","engineering"]),exports.RequestStatusFilterEnum=r.z.enum(["draft","open","in_progress","ready_for_review","ready_for_merge","resolving_conflicts","conflicted","completed","canceled","reverted","in_flight"]).describe("A status, or in_flight: every request under way."),exports.TaskStatusEnum=r.z.enum(["pending","in_progress","ready_for_review","completed","canceled","reverted"]),exports.RequestStatusEnum=r.z.enum(["open","in_progress","ready_for_review","ready_for_merge","completed","canceled","reverted","conflicted"]),exports.RecordTypeEnum=r.z.enum(["write","modify","blocker","resolution","discovery","test","delete"]),exports.AcceptanceCriterionSchema=r.z.object({description:r.z.string(),createdAt:r.z.string().optional(),updatedAt:r.z.string().optional()}),exports.TestDefinitionSchema=r.z.object({id:r.z.string(),description:r.z.string(),status:exports.CompletionStatusEnum,coverageType:exports.TestCoverageTypeEnum,accepts:r.z.array(r.z.string())}),exports.TestVerificationSchema=r.z.object({criteria:r.z.array(r.z.string()),tests:r.z.array(r.z.string()),coverageType:exports.TestCoverageTypeEnum,coverageRatio:r.z.number(),createdAt:r.z.string().optional(),updatedAt:r.z.string().optional()}),exports.ExecutedTestSchema=r.z.object({id:r.z.string(),testId:r.z.string(),status:exports.CompletionStatusEnum,results:r.z.string(),coverage:r.z.array(r.z.string()),executedAt:r.z.string()}),exports.skipTestsReasonSchema=r.z.string().min(20).optional().describe("Opt out of all tests (reason of 20+ characters). Refused under the strict test gate and when any test command is required. For some commands, use skipTests."),exports.skipTestsSchema=r.z.array(r.z.object({commandName:r.z.string().min(1).describe("A registered test command's name."),reason:r.z.string().min(20).describe("Why it is irrelevant to the change (20+ characters).")})).optional().describe("Skip test commands by name; not with skipTestsReason. Refused under the strict test gate and for required commands."),exports.CompleteRequestSchema=r.z.object({status:exports.RequestStatusEnum,acceptanceCriteria:r.z.array(exports.AcceptanceCriterionSchema),testVerification:exports.TestVerificationSchema.optional(),completionSummary:r.z.string().optional()}),exports.CreateRequestSchema=r.z.object({title:r.z.string(),process:r.z.string().optional().describe('The workflow process this request follows, by name (e.g. "CFD case"): it sets the kind and adds the process\'s rules as criteria. Name one when the request clearly is that kind of work; with none, the reply lists those that fit. Unknown names are refused with the list.'),userSaid:r.z.string().min(8).max(600).optional().describe("The user's words asking for this request, copied exactly in their language: the phrase that asks, not the whole message (matched as an exact excerpt). Pass it whenever the request comes from what the user wrote; with several messages at once, the request is filed under the one it quotes. Required for a 'gate' criterion, whose words must ask for the check to keep running."),type:exports.SettableRequestTypeEnum.describe("'code': builds or fixes behaviour (a branch, a worker, the workflow's gates, a merge). 'writing': prose that lives in the repo (docs, page copy), handled the same way. 'drive': a verification errand: the driver exercises the product through its user doors against the criteria as claims; no branch, no merge, and an UNMET claim waits for the user's sign-off. 'engineering': a model, simulation or analysis whose numbers are the product, reviewed on its idealisation and evidence and validated against something independent; prefer it over 'code' when the deliverable is a result and its justification."),description:r.z.string().optional().describe('What to build or find out and why, for whoever picks it up; the record, not a reply to the user, so rules about how you talk to them do not apply. The user reads it on the request page: call them "you", never "the user"; name agents by their job (the worker, the Alchemist).'),quotesMessage:r.z.boolean().optional().describe("true when the user's message is the specification (a brief, a pasted spec, text for the product): it is copied into the description word for word under 'Your words:', and your description is only what you add. Do not write their message out yourself. With several messages, userSaid picks which."),acceptanceCriteria:r.z.array(r.z.union([r.z.string(),r.z.object({description:r.z.string().min(1),beforeMerge:r.z.boolean().optional().describe("[user-input] criteria only: true when the user asked to check it themselves before it merges. The merge then waits for them at every Autonomy level; otherwise Self-driving treats no answer as not checked by them."),command:r.z.string().min(1).max(500).optional().describe("The command that decides this criterion, if one does (e.g. `.venv/bin/python -m pytest tests`), as it runs in the project's box. The worker may add checks, not replace it. Keep it out of the text."),kind:r.z.enum(["objective","subjective","gate"]).describe("objective: provable by running something (strings, files, exit codes, return values), even when the project has no tests yet. subjective: needs judgment (looks, reads, compares). gate: a check that runs on every merge from now on, met only by a test process registered for this request; its gate run is the verdict.")})])).optional().describe('Verifiable done-conditions: a string, or { description, kind } to say how it is proven. Split a mixed claim into one of each kind. A criterion says what must be true of the result, never that a check was run or its output recorded ("prints the tracked-file count", not "was run and its output recorded"). Put what must come from the user in its own criterion marked [user-input]; the card at the end of the work asks them for it. Criteria show on the user\'s cards, so address them as "you": "[user-input] You have tried it and say it works".'),draft:r.z.boolean().optional().describe("A draft: on the timeline, no branch, nothing runs until the user starts it (a title alone is fine). How a worker hands over work nobody asked for (with follows: your request)."),delegate:r.z.boolean().optional(),follows:r.z.string({required_error:"Required. Every request either follows earlier work or starts fresh: pass the origin request id (req######) or the word 'new'."}).refine(e=>"new"===e||/^(round:)?req\d{6}$/.test(e),{message:"Pass a request id like req000031, 'round:req000031' for another round of work that has landed, or 'new' for genuinely new work."}).describe("What this grows out of. 'round:req######': another pass at what that merged request built (bigger, another colour, the wording back); shown with it as one item and built by its builder. 'req######': a follow-up, a fix for its fallout or a continuation, only when the user pointed at it (anchored the message to it, named it, said 'that change') or you are working it; the request you are on is usually the origin. 'new': fresh work, including a new feature on an existing app and anything the user asks for on its own, even if it touches an earlier request's work (name that one in the description). The project's founding request is not an origin for later work. Only this field links requests."),planDocumentId:r.z.string().optional().describe("The plan document that decomposes this work (plan######); not the schedule that dispatched you (that is scheduleDocumentId)."),scheduleDocumentId:r.z.string().optional().describe("The schedule document that dispatched this request; only when a schedule told you to create it, with the id it gave."),questDocumentId:r.z.string().optional(),waitsOn:r.z.array(r.z.string().regex(/^req\d{6}$/)).max(5).optional().describe("Requests this one waits for: registered now, started once all have landed. A canceled one never lands; the wait holds until moved or cleared. Declare it here, not afterwards: work is dispatched at registration. When this grows out of a request that has not landed, choose this or startsNow."),startsNow:r.z.boolean().optional().describe("The other answer to waitsOn: true starts this now, alongside the unlanded request. The user's words decide (at once: start now; one after the other: wait). If they said nothing about order, wait when both change the same code."),asked:r.z.array(r.z.string().regex(/^Q-[A-Z0-9]+-\d+$/)).max(5).optional().describe("Your open questions to the user (Q-codes) that decide something here: the worker does not ask them again and gets the answer too. Pass them whenever the request builds on an answer you are waiting for."),userChoices:r.z.array(r.z.string().regex(/^Q-[A-Za-z0-9]+-\d+$/)).max(10).optional().describe("Cards the user answered that this request builds on (e.g. Q-ABCD1234-001); their picks count as sources in review, so carry the picked words into the description. A card still waiting for the user may be named (file on its default); it counts once answered. A card closed by an agent, a default or a withdrawal is refused."),replaces:r.z.string().regex(/^req\d{6}$/).optional().describe("The canceled request this one refiles. What waited on it waits on this one instead. Only a canceled request can be replaced."),areas:r.z.array(r.z.string()).max(3).optional().describe("Work-area labels, 1-3, required unless draft: lowercase kebab paths (e.g. 'graphics/opengl/shader-cache'). Pick from request_operations list-areas; add a new name only when nothing fits, nested under an existing parent. New names are registered automatically.")}),exports.CreateTaskSchema=r.z.object({requestId:a,title:r.z.string(),description:r.z.string()}),exports.UpdateTaskStatusSchema=r.z.object({taskId:i,status:exports.TaskStatusEnum,note:r.z.string()}),exports.WriteFileSchema=r.z.object({path:r.z.string().optional(),documentId:c.optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())]).optional(),fromResult:r.z.string().optional().describe('Instead of content: a recorded result (its address, or a unique prefix) whose bytes become this file, unchanged. For a file made somewhere you cannot write the worktree from, e.g. a .docx made in a sealed box and recorded with results({ operation: "record", box }). Files only (with path), up to 20 MB; bulk stays in the store (results pin).'),format:r.z.enum(["text","json"]).optional(),append:r.z.boolean().optional(),comment:r.z.string()}).transform(l).refine(d,{message:"path or documentId required"}).refine(e=>void 0!==e.content!==Boolean(e.fromResult),{message:"pass content or fromResult (exactly one)"}),exports.ViewAssetSchema=r.z.object({filename:r.z.string().optional().describe("An image in .alchemy_lab/assets/, as an asset:<filename> ref names it."),path:r.z.string().optional().describe("An image, PDF or office file (Word, Excel, PowerPoint, OpenDocument, .doc/.xls/.ppt/.rtf) in the working tree."),result:r.z.string().optional().describe("A recorded result: its address or a unique prefix."),pages:r.z.string().optional().describe('e.g. "3", "12-18" or "1,4,7-9"; at most 20 per view, required for documents over 20 pages.'),revision:r.z.string().optional().describe('With path: the file as committed at this commit, branch or tag; "base" is the version before this branch changed it.')}),exports.ReadFilesSchema=r.z.object({paths:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),lines:r.z.string().optional(),symbol:r.z.string().max(200).optional()}).transform(l).refine(d,{message:"path or documentId required"})),encoding:r.z.enum(["utf8","utf-8","ascii","binary"]).optional()}),exports.ListDirectorySchema=r.z.object({path:r.z.string(),levels:r.z.number().min(1).max(4).optional(),sortBy:r.z.enum(["name","modified","size","type"]).optional()}),exports.GetFileInfoSchema=r.z.object({path:r.z.string()}),exports.CreateDirectorySchema=r.z.object({path:r.z.string(),comment:r.z.string().optional()}),exports.MoveFilesSchema=r.z.object({operations:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()})),comment:r.z.string()}),exports.ReplaceStringSchema=r.z.object({path:r.z.string(),replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional(),comment:r.z.string()}),exports.ReplaceStringsSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional()}).transform(l).refine(d,{message:"path or documentId required"})),comment:r.z.string()}),exports.ReplaceInFilesSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),replacements:r.z.array(r.z.object({old:r.z.string().min(1,"old string cannot be empty"),new:r.z.string(),match_mode:r.z.enum(["unique","all"]).optional()})).min(1,"At least one replacement required"),fuzzy_match:r.z.boolean().optional()}).transform(l).refine(d,{message:"path or documentId required"})).min(1,"At least one operation required"),fuzzy_match:r.z.boolean().optional().default(!0),comment:r.z.string()}),exports.DeleteFileSchema=r.z.object({path:r.z.string(),comment:r.z.string()}),exports.DeleteFilesSchema=r.z.object({paths:r.z.array(r.z.string()).optional(),operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional()}).transform(l).refine(d,{message:"path or documentId required"})).optional(),comment:r.z.string()}).refine(e=>e.paths&&!e.operations||!e.paths&&e.operations,{message:"paths or operations required"}).refine(e=>e.paths?.length||e.operations?.length,{message:"path/operation required"}),exports.FileHistorySchema=r.z.object({path:r.z.string(),limit:r.z.number().min(1).max(100).default(10),showDiff:r.z.boolean().default(!1),showTraceability:r.z.boolean().default(!0),branch:r.z.string().optional(),commit:r.z.string().optional(),since:r.z.string().optional()}),exports.FileRecoverSchema=r.z.object({path:r.z.string(),commitHash:r.z.string(),createBackup:r.z.boolean().default(!0)}),exports.MergePreviewSchema=r.z.object({requestId:a,format:r.z.enum(["summary","unified"]).default("summary").describe("summary = per-file counts; unified = the diff the merge would add to your branch"),path:r.z.string().optional().describe('Optional git pathspec to scope the preview (e.g. "src/hud.css", "src/", "**/*.ts")')}),exports.RequestDiffSchema=r.z.object({requestId:a,format:r.z.enum(["summary","unified"]).default("summary"),path:r.z.string().optional()}),exports.QueryHistorySchema=r.z.object({queryType:r.z.enum(["file-history","task-file-history","request-file-history","recover","blame"],{errorMap:()=>({message:"Invalid queryType. Valid options: file-history, task-file-history, request-file-history, recover, blame"})}),fileHistory:r.z.object({path:r.z.string(),limit:r.z.number().min(1).max(100).default(10),showDiff:r.z.boolean().default(!1),showTraceability:r.z.boolean().default(!0),branch:r.z.string().optional(),commit:r.z.string().optional(),since:r.z.string().optional()}).optional(),taskHistory:r.z.object({taskId:i,showDiff:r.z.boolean().optional()}).optional(),requestHistory:r.z.object({requestId:a,detailLevel:r.z.enum(["summary","detailed"]).default("summary"),taskId:i.optional(),showDiff:r.z.boolean().optional(),fileFilter:r.z.string().optional()}).optional(),recover:r.z.object({path:r.z.string(),commitHash:r.z.string().optional(),createBackup:r.z.boolean().default(!0)}).optional(),blame:r.z.object({path:r.z.string(),lines:r.z.string().optional(),branch:r.z.string().optional()}).optional()}).refine(e=>"file-history"===e.queryType?e.fileHistory&&!e.requestHistory&&!e.recover:"task-file-history"===e.queryType?e.taskHistory&&!e.fileHistory&&!e.requestHistory&&!e.recover:"request-file-history"===e.queryType?e.requestHistory&&!e.fileHistory&&!e.recover:"recover"===e.queryType?e.recover&&!e.fileHistory&&!e.requestHistory&&!e.blame:"blame"===e.queryType&&e.blame&&!e.fileHistory&&!e.requestHistory&&!e.recover,{message:"Invalid query data"}),exports.SearchSchema=r.z.object({needle:r.z.string().optional(),directory:r.z.string().optional(),directories:r.z.array(r.z.string()).max(8).optional(),options:r.z.object({caseSensitive:r.z.boolean().optional(),fileExtension:r.z.string().optional(),maxResults:r.z.number().optional(),useRegex:r.z.boolean().optional(),searchIn:r.z.enum(["content","filename","both"]).default("content").optional(),contextLines:r.z.number().min(0).optional(),levels:r.z.number().min(1).max(4).optional(),sortBy:r.z.enum(["name","modified","size","type"]).optional()}).optional()}),exports.ListTasksSchema=r.z.object({requestId:a,filters:r.z.object({status:r.z.enum(["pending","in_progress","ready_for_review","completed","canceled","reverted"]).optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}),exports.ListRequestsSchema=r.z.object({filters:r.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:r.z.string().optional(),scheduleDocumentId:r.z.string().optional().describe("Requests a schedule fired: that schedule document."),questDocumentId:r.z.string().optional().describe("Legacy name of scheduleDocumentId."),dateFrom:r.z.string().optional().describe("Only requests with activity from this date on (YYYY-MM-DD means that whole day in the user's clock, or an ISO timestamp)."),dateTo:r.z.string().optional().describe("Only requests with activity up to this date (YYYY-MM-DD includes the whole day)."),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}),exports.GetRequestInformationSchema=r.z.object({requestId:a}),exports.GetTaskInformationSchema=r.z.object({taskId:i}),exports.CompleteTaskSchema=r.z.object({taskId:i,summary:r.z.string()}),exports.TaskInspectSchema=r.z.object({operation:r.z.enum(["get-info","list"]),taskId:i.optional(),requestId:a.optional(),status:r.z.enum(["pending","in_progress","ready_for_review","completed","canceled","reverted"]).optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).superRefine((e,t)=>{"get-info"!==e.operation||e.taskId||t.addIssue({code:"custom",message:'taskId is required for get-info. Example: { "operation": "get-info", "taskId": "req000001#01" }'}),"list"!==e.operation||e.requestId||t.addIssue({code:"custom",message:'requestId is required for list. Example: { "operation": "list", "requestId": "req000001" }'})})
5176
5201
  const u=r.z.string().min(1).describe("OpenCode session ID")
5177
5202
  exports.SubagentOperationsSchema=r.z.object({sessionId:u.optional(),operation:r.z.enum(["spawn","continue","check-status","abort","read-messages","list-sessions"]),prompt:r.z.string().min(1).describe("Instructions for the subagent (required for spawn and continue)").optional(),agent:r.z.string().default("work").refine(e=>(0,n.isValidAgent)(e),{message:"Invalid agent. Check tool description for available agents."}).describe("The subagent type to spawn (see tool description for available agents)").optional(),description:r.z.string().max(60).describe("Brief label for the UI (max 60 chars, spawn only)").optional(),requestId:r.z.string().describe("Request ID: associates work (spawn) or filters results (list-sessions)").optional(),allowParallel:r.z.boolean().describe("Allow spawning even if request already has active workers (spawn only)").optional(),useExistingSession:r.z.string().describe("Existing session ID to send prompt to instead of creating new session (spawn only)").optional(),limit:r.z.number().min(1).max(100).describe("Max items to return (read-messages: max 50, list-sessions: max 100)").optional(),compact:r.z.boolean().describe("read-messages: return a one-line index of EACH message with tool-call markers (the subagent`s trajectory at a glance: which tools ran, in what order, which failed), instead of full text. Use to see what a subagent actually DID, then `select` one for detail.").optional(),select:r.z.number().min(1).describe("read-messages: return ONLY this message number (from the compact list) in full structured detail: each tool call as {tool, input, status, output}, plus its text. The objective record of one step.").optional(),activeOnly:r.z.boolean().describe("Only show sessions that are currently active/busy (list-sessions only)").optional(),minTokens:r.z.number().min(0).describe("Only show sessions with at least this many tokens (list-sessions only)").optional(),maxTokens:r.z.number().min(0).describe("Only show sessions with at most this many tokens (list-sessions only)").optional(),maxAgeHours:r.z.number().min(0).describe("Only show sessions newer than X hours (list-sessions only)").optional()}).refine(e=>"spawn"===e.operation?!!e.prompt:"continue"===e.operation?!!e.prompt&&!!e.sessionId:"check-status"===e.operation||"abort"===e.operation||"read-messages"===e.operation?!!e.sessionId:"list-sessions"===e.operation,{message:"spawn requires prompt; continue requires prompt+sessionId; check-status/abort/read-messages require sessionId"}),exports.ResearchFanoutSchema=r.z.object({goal:r.z.string().min(1).optional().describe("What to research; passed to every angle. Required to START a run."),angles:r.z.array(r.z.string()).optional().describe("Research angles to fan out over. Omit to use the default set (data-model, call-sites, prior-art/history, external/web, test-surface)."),planId:r.z.string().optional().describe("Plan document to reference the research report from when the run completes."),checkRunId:r.z.string().optional().describe("Pass a runId from a previous start to CHECK that run live progress instead of starting a new one.")}).refine(e=>!(!e.goal||!e.goal.trim())||!!e.checkRunId,{message:"Provide goal to start a run, or checkRunId to check one."}),exports.PlannerOpsSchema=r.z.object({operation:r.z.enum(["request","check-status","list","end"]),sessionId:u.optional(),prompt:r.z.string().min(1).describe("What to plan: feature description, architecture question, etc. (required for request)").optional(),description:r.z.string().max(60).describe("Brief label for the UI (max 60 chars, request only)").optional(),userRequested:r.z.boolean().describe("Set true ONLY when the user explicitly asked for this plan in their own words: their message IS the decision, so the planner starts immediately instead of asking them to choose a planning approach (request only)").optional(),documentId:r.z.string().describe("Plan an EXISTING document instead of creating a new one: the planner binds to this document, reads it first, and evolves it in place with plan_write (request only)").optional(),activeOnly:r.z.boolean().describe("Only show active/busy planning sessions (list only)").optional(),limit:r.z.number().min(1).max(100).describe("Max sessions to return (list only)").optional()}).refine(e=>"request"===e.operation?!!e.prompt:"check-status"===e.operation?!!e.sessionId:"list"===e.operation||"end"===e.operation&&(!!e.sessionId||!!e.documentId),{message:"request requires prompt; check-status requires sessionId; end requires sessionId or documentId"}),exports.InsertLinesSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),insertions:r.z.array(r.z.object({type:r.z.enum(["before","after","between"]),line:r.z.number().optional(),afterLine:r.z.number().optional(),beforeLine:r.z.number().optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}).refine(e=>"between"===e.type?void 0!==e.afterLine&&void 0!==e.beforeLine&&void 0===e.line:void 0!==e.line&&void 0===e.afterLine&&void 0===e.beforeLine,{message:"Invalid line parameters"}))}).transform(l).refine(d,{message:"path or documentId required"})),comment:r.z.string()}),exports.ReplaceLinesSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),replacements:r.z.array(r.z.object({lines:r.z.string(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}))}).transform(l).refine(d,{message:"path or documentId required"})),comment:r.z.string()}),exports.CreateDocumentSchema=r.z.object({requestId:a.optional(),filename:r.z.string().refine(e=>/\.(md|html?)$/i.test(e),{message:"Filename must end in .md (prose) or .html (a rendered page)"}),content:r.z.string(),comment:r.z.string(),scheduleIntervalSeconds:r.z.number().int().min(300).describe("Fire interval in seconds (min 300). Setting this makes the document a SCHEDULE: the runner periodically asks the alchemist to read it and create one linked request. 86400 = daily. Prefer scheduleAtTimes when the goal wants a time of day.").optional(),scheduleAtTimes:r.z.string().describe("Local time(s) of day to fire, e.g. '09:00' or '09:00,17:00'. Setting this makes the document a SCHEDULE that runs at that clock time instead of on an interval, and it starts at the next such time (never the moment it is created).").optional(),scheduleOnDays:r.z.string().describe("Which days scheduleAtTimes applies to: 'weekdays', 'weekends', or names like 'mon,thu'. Omit for every day.").optional(),schedulePrompt:r.z.string().describe('What each firing should do -- the instruction the alchemist receives per tick (e.g. "validate quotes.ndjson and file a short report; flag the user only on real breakage").').optional(),scheduleMaxPerDay:r.z.number().int().min(1).describe("Cap on firings per day (structural guard). Default 1 for daily-style schedules.").optional(),scheduleMaxInFlight:r.z.number().int().min(1).describe("Cap on live linked requests before firings pause (structural guard). Default 1.").optional()}),exports.SearchDocumentsSchema=r.z.object({query:r.z.string(),requestId:a.optional(),includeArchived:r.z.boolean().default(!1),maxResults:r.z.number().min(1).max(100).default(20),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),searchIn:r.z.enum(["content","filename","both"]).default("both")}),exports.DocumentOperationsSchema=r.z.object({operation:r.z.enum(["create","update","search","create-snapshot","get-history","get-version","restore-version","archive","unarchive","list-annotations","create-annotation","reply-to-annotation","resolve-annotation","create-artifact-annotation","propose-standard"]),standardRules:r.z.array(r.z.object({text:r.z.string().min(1).max(600).describe("One statement of the standard as agents should read it, in its own words where they are clear"),section:r.z.string().min(1).max(120).describe("The heading of the section it comes from"),checked:r.z.boolean().describe("true when a reviewer can judge it from the delivered work: its files, results and the worker's account (checked in review); false for practice agents are told, and for what only the final reply to the user shows (written after the review)"),kinds:r.z.array(r.z.enum(["code","engineering","writing","drive"])).max(4).optional().describe("The kinds of work it governs; left out, all")})).min(1).max(12).optional().describe("propose-standard: at most 12 per call; call again for the rest"),documentId:c.optional(),requestId:a.optional(),filename:r.z.string().optional(),content:r.z.string().optional(),comment:r.z.string().optional(),scheduleIntervalSeconds:r.z.number().int().min(300).optional(),scheduleAtTimes:r.z.string().optional(),scheduleOnDays:r.z.string().optional(),schedulePrompt:r.z.string().optional(),scheduleMaxPerDay:r.z.number().int().min(1).optional(),scheduleMaxInFlight:r.z.number().int().min(1).optional(),query:r.z.string().optional(),includeArchived:r.z.boolean().default(!1),maxResults:r.z.number().min(1).max(100).default(20),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),searchIn:r.z.enum(["content","filename","both"]).default("both"),replacements:r.z.array(r.z.object({old_str:r.z.string().describe("Exact text to find in the document"),new_str:r.z.string().describe("Replacement text")})).optional(),mode:r.z.enum(["unique","all"]).optional(),versionLabel:r.z.string().optional(),changeSummary:r.z.string().optional(),versionNumber:r.z.number().int().min(1).optional(),selectedText:r.z.string().optional(),prefixContext:r.z.string().optional(),suffixContext:r.z.string().optional(),lineHint:r.z.number().int().optional(),body:r.z.string().optional(),annotationId:r.z.string().optional(),ref:r.z.string().optional().describe('Artifact reference, e.g. "file:src/main.cpp"'),file:r.z.string().optional().describe("Repo-relative file path the comment is pinned to"),line:r.z.union([r.z.number().int(),r.z.string()]).optional().describe("1-based line number the comment is pinned to")}).superRefine((e,t)=>{const r=e.operation
5178
5203
  "propose-standard"!==r||e.standardRules&&e.standardRules.length||t.addIssue({code:"custom",message:"standardRules is required for propose-standard: [{ text, section, checked, kinds? }]"}),["create-snapshot","get-history","get-version","restore-version","list-annotations","create-annotation","propose-standard"].includes(r)&&!e.documentId&&t.addIssue({code:"custom",message:`documentId is required for '${r}'. Example: { "operation": "${r}", "documentId": "doc000001" }`}),"create"===r&&(e.filename?/\.(md|html?)$/i.test(e.filename)||t.addIssue({code:"custom",message:"Filename must end in .md (prose) or .html (a rendered page)"}):t.addIssue({code:"custom",message:"filename is required for create (must end in .md or .html)"}),e.content||t.addIssue({code:"custom",message:"content is required for create"})),"search"!==r||e.query||t.addIssue({code:"custom",message:'query is required for search. Example: { "operation": "search", "query": "search term" }'}),"get-version"!==r&&"restore-version"!==r||e.versionNumber||t.addIssue({code:"custom",message:`versionNumber is required for '${r}'`}),"create-annotation"===r&&(e.selectedText||t.addIssue({code:"custom",message:"selectedText is required for create-annotation"}),e.body||t.addIssue({code:"custom",message:"body is required for create-annotation"})),"reply-to-annotation"===r&&(e.annotationId||t.addIssue({code:"custom",message:"annotationId is required for reply-to-annotation"}),e.body||t.addIssue({code:"custom",message:"body is required for reply-to-annotation"})),"resolve-annotation"!==r||e.annotationId||t.addIssue({code:"custom",message:"annotationId is required for resolve-annotation"}),"create-artifact-annotation"===r&&(e.ref||t.addIssue({code:"custom",message:'ref is required for create-artifact-annotation. Example: { "operation": "create-artifact-annotation", "ref": "file:src/main.cpp", "line": 42, "body": "..." }'}),e.body||t.addIssue({code:"custom",message:"body is required for create-artifact-annotation"}))})
5179
5204
  const h=c
5180
5205
  exports.PlanReadSchema=r.z.object({operation:r.z.enum(["get","get-active","list","get-history","get-requests"]),planId:h.optional(),searchQuery:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["createdAt","updatedAt","filename"]).default("updatedAt"),sortDirection:r.z.enum(["ASC","DESC"]).default("DESC")}).superRefine((e,t)=>{["get","get-history","get-requests"].includes(e.operation)&&!e.planId&&t.addIssue({code:"custom",message:`planId is required for '${e.operation}'. Example: { "operation": "${e.operation}", "planId": "plan000001" }`})}),exports.PlanWriteSchema=r.z.object({operation:r.z.enum(["create","update","append","replace","create-snapshot","restore"]),planId:h.optional(),kind:r.z.enum(["plan"]).default("plan").describe("Document kind to create. Plans only."),content:r.z.string().optional(),name:r.z.string().optional().describe("Plan name (used in filename for create; optional new name for update)."),old:r.z.string().optional().describe("Text to find in the plan (for replace)"),new:r.z.string().optional().describe("Replacement text (for replace)"),versionLabel:r.z.string().optional(),changeSummary:r.z.string().optional(),versionNumber:r.z.number().int().min(1).optional()}).superRefine((e,t)=>{const r=e.operation
5181
- "create"===r?(e.name||t.addIssue({code:"custom",message:"name is required for 'create'. Letters, numbers, hyphens, underscores only."}),void 0===e.content&&t.addIssue({code:"custom",message:"content is required for 'create' (use empty string for an empty body)."})):e.planId||t.addIssue({code:"custom",message:`planId is required for '${r}'.`}),"append"!==r||e.content||t.addIssue({code:"custom",message:'content is required for \'append\'. Example: { "operation": "append", "planId": "plan000001", "content": "..." }'}),"update"===r&&(void 0!==e.content||e.name||t.addIssue({code:"custom",message:"update requires 'content' and/or 'name'."})),"replace"===r&&(e.old||t.addIssue({code:"custom",message:'old is required for replace. Example: { "operation": "replace", "planId": "plan000001", "old": "find this", "new": "replace with" }'}),void 0===e.new&&t.addIssue({code:"custom",message:"new is required for replace"})),"restore"!==r||e.versionNumber||t.addIssue({code:"custom",message:'versionNumber is required for restore. Example: { "operation": "restore", "planId": "plan000001", "versionNumber": 1 }'})}),exports.RevertRequestSchema=r.z.object({requestId:a,reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}),exports.ModifyFileSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),operationType:r.z.enum(["replace-string","insert-lines","replace-lines","delete-string","delete-lines"]),replaceString:r.z.object({replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional()}).optional(),insertLines:r.z.object({insertions:r.z.array(r.z.object({type:r.z.enum(["before","after","between"]),line:r.z.number().optional(),afterLine:r.z.number().optional(),beforeLine:r.z.number().optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}).refine(e=>"between"===e.type?void 0!==e.afterLine&&void 0!==e.beforeLine&&void 0===e.line:void 0!==e.line&&void 0===e.afterLine&&void 0===e.beforeLine,{message:"Invalid line parameters"}))}).optional(),replaceLines:r.z.object({replacements:r.z.array(r.z.object({lines:r.z.string(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}))}).optional(),deleteString:r.z.object({patterns:r.z.array(r.z.string()),mode:r.z.enum(["all","unique"]).optional()}).optional(),deleteLines:r.z.object({lines:r.z.string()}).optional()}).transform(l).refine(d,{message:"path or documentId required"}).refine(e=>"replace-string"===e.operationType?e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"insert-lines"===e.operationType?e.insertLines&&!e.replaceString&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"replace-lines"===e.operationType?e.replaceLines&&!e.replaceString&&!e.insertLines&&!e.deleteString&&!e.deleteLines:"delete-string"===e.operationType?e.deleteString&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteLines:"delete-lines"===e.operationType&&e.deleteLines&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString,{message:"Invalid operation data"})),comment:r.z.string()}),exports.ManageFilesSchema=r.z.object({operations:r.z.array(r.z.object({operationType:r.z.enum(["create-directory","move-files","delete-files","get-file-info"]),createDirectory:r.z.object({path:r.z.string()}).optional(),moveFiles:r.z.object({moves:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()}))}).optional(),deleteFiles:r.z.object({targets:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional()}).transform(l).refine(d,{message:"path or documentId required"}))}).optional(),getFileInfo:r.z.object({path:r.z.string()}).optional()}).refine(e=>"create-directory"===e.operationType?e.createDirectory&&!e.moveFiles&&!e.deleteFiles&&!e.getFileInfo:"move-files"===e.operationType?e.moveFiles&&!e.createDirectory&&!e.deleteFiles&&!e.getFileInfo:"delete-files"===e.operationType?e.deleteFiles&&!e.createDirectory&&!e.moveFiles&&!e.getFileInfo:"get-file-info"===e.operationType&&e.getFileInfo&&!e.createDirectory&&!e.moveFiles&&!e.deleteFiles,{message:"Invalid operation data"})),comment:r.z.string()}),exports.RequestOperationsSchema=r.z.object({requestId:a.optional(),operation:r.z.enum(["update","activate","complete","merge","revert","reopen","get-info","list","search","cancel","delete-empty","list-areas","update-areas","wait-on","clear-wait","premise-block","dispute-criterion","asked-no-change","hand-over"]),criterion:r.z.string().optional().describe("dispute-criterion: the key (e.g. 1a) of a criterion marked FROM THE PROCESS (or FROM THE WORKFLOW) that does not fit. You cannot drop, reword or waive it: the user waives it for this request or keeps it, and you are told; until then it binds. Put why in reason, plainly: the user reads it."),reason:r.z.string().optional().describe('cancel: why, to the user ("you"), in their language; name the replacing request if any. Also for revert, reopen and wait-on. premise-block (required): what the request builds on that does not exist in the project (a feature, file or surface); the user reads it. Not for what the request itself adds, nor for unclear wording: ask a question.'),userSaid:r.z.string().min(8).max(600).optional().describe("The user's words asking for this, quoted exactly and checked. Required for activate, for cancel when the work is finished and waiting on the user, and for reopen when a question to the user is pending on the request. asked-no-change: their message."),waitsOn:r.z.string().optional(),update:r.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,r.z.object({userSaid:r.z.string().min(8).max(600).optional().describe('The user\'s words asking for this change to the request, quoted exactly (e.g. "skip the HTML counter"). Without them a worker may not narrow or reword its own request.'),removesCriteria:r.z.boolean().optional().describe("acceptanceCriteria replaces the whole list: send every criterion you keep. Set true only to delete those missing; otherwise a list that drops criteria is refused."),arcName:r.z.string().min(2).max(120).optional().describe('The thread\'s name (the requests grown from one ask, read as one story): two to five plain words for the whole thread ("Weekly menu app"). Set it once; a name the user chose is never overwritten.'),title:r.z.string().optional(),description:r.z.string().optional(),process:r.z.string().nullable().optional().describe("A workflow process by name (sets the kind and its rules' criteria), or null to take it off. Only on a draft; after that only the user may change it."),type:exports.SettableRequestTypeEnum.optional(),acceptanceCriteria:r.z.array(r.z.union([r.z.string(),r.z.object({description:r.z.string().min(1),beforeMerge:r.z.boolean().optional().describe("[user-input] criteria only: true when the user asked to check it themselves before it merges. The merge then waits for them at every Autonomy level; otherwise Self-driving treats no answer as not checked by them."),command:r.z.string().min(1).max(500).optional().describe("The command that decides this criterion, if one does (e.g. `.venv/bin/python -m pytest tests`), as it runs in the project's box. The worker may add checks, not replace it. Keep it out of the text."),kind:r.z.enum(["objective","subjective","gate"]).describe("objective: provable by running something. subjective: needs judgment. gate: a check that runs on every merge, met only by a registered test process. A criterion states the result, never that a check was run or its output recorded."),key:r.z.string().optional().describe("The key of a criterion you reword or retype (e.g. '2u'), so it keeps its verdicts and verify commands. Omit for a new one."),carries:r.z.string().regex(/^promise:req\d+:\S+$/,"carries is a promise id: promise:<request>:<criterion id>, as the promise card and your rework task name it").optional().describe("Only to change the check of another request's promise (its id, \"promise:req000012:crt_x\", from your rework task or the promise card): state the promise's words; its command is the new check, used once this request merges. The only way to change a promise's check.")})])).optional()})).optional(),areas:r.z.array(r.z.string()).max(3).optional().describe("update-areas: up to 3 labels replacing the current ones, lowercase kebab paths (e.g. 'graphics/opengl'); [] clears. New names are registered automatically."),complete:r.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,r.z.object({summary:r.z.string().describe("The line the user reads when the request lands. If the request asked a question, the first sentence answers it; then what changed. Numbers at the precision that matters (values equal within noise are the same). Do not say tested or verified when that step failed. It replaces any earlier summary, so after a rework it still covers the whole request."),replacesSummary:r.z.boolean().optional().describe("Required on a re-completion: confirms the summary covers everything the request delivers, fixes included. Without it the re-completion is refused and shows the earlier summary."),worthKnowing:r.z.array(r.z.string().min(10)).max(5).optional().describe("What the user would regret not knowing, one per entry: a discovery, a caveat, a side effect, a follow-up worth doing. Always an entry when what was asked was already true or turned out different than the request implied. Not the summary again. Each lands alone in the user's stream: name the subject first, do not open with Also. Omit when there is nothing."),guidance:r.z.array(r.z.object({body:r.z.string().min(10).max(800).describe('One fact or rule for the next agent, not addressed to the user, e.g. "calc.py is generated from ops.json: edit ops.json and regenerate."'),files:r.z.array(r.z.string().min(1)).min(1).max(8).describe("Repo-relative files or folders (globs work) it is pinned to."),evidence:r.z.string().min(1).max(1500).describe("What shows it: the command, what it printed, the file.")})).max(2).optional().describe("A trap the next agent touching these files would hit too (a generated file not to edit by hand, a check that fails silently, a command that must run a certain way), proposed as guidance pinned to them for the user to adopt. Not taste, not specific to this request, not already in guidance. Usually none."),skipTestsReason:exports.skipTestsReasonSchema,skipTests:exports.skipTestsSchema,criteriaComments:r.z.array(r.z.object({key:r.z.string().min(1),comment:r.z.string().min(1),verify:r.z.array(r.z.string().min(1)).max(4).optional().describe('Commands that prove this criterion in the sealed acceptance box (a grep, a targeted test, a screenshot into .visual-check/); the acceptance judge rules on their output, else review or the user judges it. They run in a clean checkout of your commit with dependencies installed but nothing the test commands built: a check that needs a build runs it in the same command ("npm run build && grep -q X dist/app.js"). Each runs in the box of the check whose toolchain it uses; branching on whether a tool exists is refused. No git and no network: use file checks (grep, node -e, python -c). A proof you already saw pass on this commit is reused if written exactly as you ran it.')})).optional().describe('One entry per acceptance criterion, keyed as your pickup lists it (e.g. "1a"): what you did and how you saw it hold. A request with criteria completes with an entry for each.'),afterRun:r.z.boolean().optional().describe("Complete while your final check runs: call `run` with the check first, then this complete in the SAME response. The run starts as soon as it is written, so the completion is written while the tests run; it then waits for that run and is made only if the run passed. If the run failed, nothing is completed and you get the failure."),couldNotCheck:r.z.array(r.z.string().min(1)).max(20).optional().describe('Drive requests only: keys of claims you could not check (e.g. ["3c"]), reason in each criteriaComments entry. They read as unchecked; the only way to complete a drive without stamping a claim. Stamp what you checked with record_criteria_verification.')})).optional(),merge:r.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,r.z.object({verifications:r.z.array(r.z.object({key:r.z.string().min(2).max(4),evidence:r.z.string().min(1)})).min(1)})).optional(),revert:r.z.object({reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}).optional(),list:r.z.object({filters:r.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:r.z.string().optional().describe("Requests linked to this plan."),scheduleDocumentId:r.z.string().optional().describe("Requests a schedule fired: that schedule document."),questDocumentId:r.z.string().optional().describe("Legacy name of scheduleDocumentId."),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}).optional(),search:r.z.object({query:r.z.string().min(1),searchIn:r.z.enum(["title","description","criteria","all"]).default("all"),filters:r.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:r.z.string().optional().describe("Requests linked to this plan."),scheduleDocumentId:r.z.string().optional().describe("Requests a schedule fired: that schedule document."),questDocumentId:r.z.string().optional().describe("Legacy name of scheduleDocumentId."),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0)}).optional()}).optional()}).superRefine((e,t)=>{e.requestId
5206
+ "create"===r?(e.name||t.addIssue({code:"custom",message:"name is required for 'create'. Letters, numbers, hyphens, underscores only."}),void 0===e.content&&t.addIssue({code:"custom",message:"content is required for 'create' (use empty string for an empty body)."})):e.planId||t.addIssue({code:"custom",message:`planId is required for '${r}'.`}),"append"!==r||e.content||t.addIssue({code:"custom",message:'content is required for \'append\'. Example: { "operation": "append", "planId": "plan000001", "content": "..." }'}),"update"===r&&(void 0!==e.content||e.name||t.addIssue({code:"custom",message:"update requires 'content' and/or 'name'."})),"replace"===r&&(e.old||t.addIssue({code:"custom",message:'old is required for replace. Example: { "operation": "replace", "planId": "plan000001", "old": "find this", "new": "replace with" }'}),void 0===e.new&&t.addIssue({code:"custom",message:"new is required for replace"})),"restore"!==r||e.versionNumber||t.addIssue({code:"custom",message:'versionNumber is required for restore. Example: { "operation": "restore", "planId": "plan000001", "versionNumber": 1 }'})}),exports.RevertRequestSchema=r.z.object({requestId:a,reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}),exports.ModifyFileSchema=r.z.object({operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),operationType:r.z.enum(["replace-string","insert-lines","replace-lines","delete-string","delete-lines"]),replaceString:r.z.object({replacements:r.z.array(r.z.object({old_str:r.z.string(),new_str:r.z.string()})),mode:r.z.enum(["unique","all"]).optional()}).optional(),insertLines:r.z.object({insertions:r.z.array(r.z.object({type:r.z.enum(["before","after","between"]),line:r.z.number().optional(),afterLine:r.z.number().optional(),beforeLine:r.z.number().optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}).refine(e=>"between"===e.type?void 0!==e.afterLine&&void 0!==e.beforeLine&&void 0===e.line:void 0!==e.line&&void 0===e.afterLine&&void 0===e.beforeLine,{message:"Invalid line parameters"}))}).optional(),replaceLines:r.z.object({replacements:r.z.array(r.z.object({lines:r.z.string(),content:r.z.union([r.z.string(),r.z.array(r.z.string())])}))}).optional(),deleteString:r.z.object({patterns:r.z.array(r.z.string()),mode:r.z.enum(["all","unique"]).optional()}).optional(),deleteLines:r.z.object({lines:r.z.string()}).optional()}).transform(l).refine(d,{message:"path or documentId required"}).refine(e=>"replace-string"===e.operationType?e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"insert-lines"===e.operationType?e.insertLines&&!e.replaceString&&!e.replaceLines&&!e.deleteString&&!e.deleteLines:"replace-lines"===e.operationType?e.replaceLines&&!e.replaceString&&!e.insertLines&&!e.deleteString&&!e.deleteLines:"delete-string"===e.operationType?e.deleteString&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteLines:"delete-lines"===e.operationType&&e.deleteLines&&!e.replaceString&&!e.insertLines&&!e.replaceLines&&!e.deleteString,{message:"Invalid operation data"})),comment:r.z.string()}),exports.ManageFilesSchema=r.z.object({operations:r.z.array(r.z.object({operationType:r.z.enum(["create-directory","move-files","delete-files","get-file-info"]),createDirectory:r.z.object({path:r.z.string()}).optional(),moveFiles:r.z.object({moves:r.z.array(r.z.object({source:r.z.string(),destination:r.z.string()}))}).optional(),deleteFiles:r.z.object({targets:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional()}).transform(l).refine(d,{message:"path or documentId required"}))}).optional(),getFileInfo:r.z.object({path:r.z.string()}).optional()}).refine(e=>"create-directory"===e.operationType?e.createDirectory&&!e.moveFiles&&!e.deleteFiles&&!e.getFileInfo:"move-files"===e.operationType?e.moveFiles&&!e.createDirectory&&!e.deleteFiles&&!e.getFileInfo:"delete-files"===e.operationType?e.deleteFiles&&!e.createDirectory&&!e.moveFiles&&!e.getFileInfo:"get-file-info"===e.operationType&&e.getFileInfo&&!e.createDirectory&&!e.moveFiles&&!e.deleteFiles,{message:"Invalid operation data"})),comment:r.z.string()}),exports.RequestOperationsSchema=r.z.object({requestId:a.optional(),operation:r.z.enum(["update","activate","complete","merge","revert","reopen","get-info","list","search","cancel","delete-empty","list-areas","update-areas","wait-on","clear-wait","premise-block","dispute-criterion","asked-no-change","hand-over"]),criterion:r.z.string().optional().describe("dispute-criterion: the key (e.g. 1a) of a criterion marked FROM THE PROCESS (or FROM THE WORKFLOW) that does not fit. You cannot drop, reword or waive it: the user waives it for this request or keeps it, and you are told; until then it binds. Put why in reason, plainly: the user reads it."),reason:r.z.string().optional().describe('cancel: why, to the user ("you"), in their language; name the replacing request if any. Also for revert, reopen and wait-on. premise-block (required): what the request builds on that does not exist in the project (a feature, file or surface); the user reads it. Not for what the request itself adds, nor for unclear wording: ask a question.'),userSaid:r.z.string().min(8).max(600).optional().describe("The user's words asking for this, quoted exactly and checked. Required for activate, for cancel when the work is finished and waiting on the user, and for reopen when a question to the user is pending on the request. asked-no-change: their message."),waitsOn:r.z.string().optional(),update:r.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,r.z.object({userSaid:r.z.string().min(8).max(600).optional().describe('The user\'s words asking for this change to the request, quoted exactly (e.g. "skip the HTML counter"). Without them a worker may not narrow or reword its own request.'),removesCriteria:r.z.boolean().optional().describe("acceptanceCriteria replaces the whole list: send every criterion you keep. Set true only to delete those missing; otherwise a list that drops criteria is refused."),arcName:r.z.string().min(2).max(120).optional().describe('The thread\'s name (the requests grown from one ask, read as one story): two to five plain words for the whole thread ("Weekly menu app"). Set it once; a name the user chose is never overwritten.'),title:r.z.string().optional(),description:r.z.string().optional(),process:r.z.string().nullable().optional().describe("A workflow process by name (sets the kind and its rules' criteria), or null to take it off. Only on a draft; after that only the user may change it."),type:exports.SettableRequestTypeEnum.optional(),acceptanceCriteria:r.z.array(r.z.union([r.z.string(),r.z.object({description:r.z.string().min(1),beforeMerge:r.z.boolean().optional(),command:r.z.string().min(1).max(500).optional(),kind:r.z.enum(["objective","subjective","gate"]),key:r.z.string().optional().describe("The key of a criterion you reword or retype (e.g. '2u'), so it keeps its verdicts and verify commands. Omit for a new one."),carries:r.z.string().regex(/^promise:req\d+:\S+$/,"carries is a promise id: promise:<request>:<criterion id>, as the promise card and your rework task name it").optional().describe("Only to change the check of another request's promise (its id, \"promise:req000012:crt_x\", from your rework task or the promise card): state the promise's words; its command is the new check, used once this request merges. The only way to change a promise's check.")})])).optional().describe("Replaces the whole list; same item shape and rules as create_request's acceptanceCriteria.")})).optional(),areas:r.z.array(r.z.string()).max(3).optional().describe("update-areas: up to 3 labels replacing the current ones, lowercase kebab paths (e.g. 'graphics/opengl'); [] clears. New names are registered automatically."),complete:r.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,r.z.object({summary:r.z.string().describe("The line the user reads when the request lands: if the request asked a question, the first sentence answers it; then what changed. Never say tested or verified when that step failed. It replaces any earlier summary, so after a rework it covers the whole request."),replacesSummary:r.z.boolean().optional().describe("Required on a re-completion: confirms the summary covers everything the request delivers, fixes included. Without it the re-completion is refused and shows the earlier summary."),worthKnowing:r.z.array(r.z.string().min(10)).max(5).optional().describe("What the user would regret not knowing, one per entry: a discovery, a caveat, a side effect, a follow-up worth doing; always one when what was asked was already true or turned out different than the request implied. Not the summary again. Each lands alone in the stream: name the subject first. Omit when there is nothing."),guidance:r.z.array(r.z.object({body:r.z.string().min(10).max(800).describe('One fact or rule for the next agent, not addressed to the user, e.g. "calc.py is generated from ops.json: edit ops.json and regenerate."'),files:r.z.array(r.z.string().min(1)).min(1).max(8).describe("Repo-relative files or folders (globs work) it is pinned to."),evidence:r.z.string().min(1).max(1500).describe("What shows it: the command, what it printed, the file.")})).max(2).optional().describe("A trap the next agent touching these files would hit too, proposed as guidance pinned to them for the user to adopt. Not taste, not specific to this request, not already in guidance. Usually none."),skipTestsReason:exports.skipTestsReasonSchema,skipTests:exports.skipTestsSchema,criteriaComments:r.z.array(r.z.object({key:r.z.string().min(1),comment:r.z.string().min(1),verify:r.z.array(r.z.string().min(1)).max(4).optional().describe('Commands that prove this criterion in the sealed acceptance box (a grep, a targeted test, a screenshot into .visual-check/); the acceptance judge rules on their output. They run in a clean checkout of your commit with dependencies installed but nothing built, so a check that needs a build runs it in the same command ("npm run build && grep -q X dist/app.js"). No git, no network, no branching on whether a tool exists. A proof you already saw pass on this commit is reused if written exactly as you ran it.')})).optional().describe('One entry per acceptance criterion, keyed as your pickup lists it (e.g. "1a"): what you did and how you saw it hold. A request with criteria completes with an entry for each.'),afterRun:r.z.boolean().optional().describe("Complete while your final check runs: call `run` with the check first, then this complete in the SAME response. The completion waits for that run and is made only if it passed; otherwise you get the failure."),couldNotCheck:r.z.array(r.z.string().min(1)).max(20).optional().describe('Drive requests only: keys of claims you could not check (e.g. ["3c"]), reason in each criteriaComments entry. They read as unchecked; the only way to complete a drive without stamping a claim. Stamp what you checked with record_criteria_verification.')})).optional(),merge:r.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,r.z.object({verifications:r.z.array(r.z.object({key:r.z.string().min(2).max(4),evidence:r.z.string().min(1)})).min(1)})).optional(),revert:r.z.object({reason:r.z.string(),confirmDestructive:r.z.boolean().optional()}).optional(),list:r.z.object({filters:r.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:r.z.string().optional().describe("Requests linked to this plan."),scheduleDocumentId:r.z.string().optional().describe("Requests a schedule fired: that schedule document."),questDocumentId:r.z.string().optional(),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0),sortBy:r.z.enum(["created","updated"]).default("updated"),sortDirection:r.z.enum(["asc","desc"]).default("desc")}).optional()}).optional(),search:r.z.object({query:r.z.string().min(1),searchIn:r.z.enum(["title","description","criteria","all"]).default("all"),filters:r.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:r.z.string().optional().describe("Requests linked to this plan."),scheduleDocumentId:r.z.string().optional().describe("Requests a schedule fired: that schedule document."),questDocumentId:r.z.string().optional(),dateFrom:r.z.string().optional(),dateTo:r.z.string().optional(),limit:r.z.number().min(1).max(100).default(20),offset:r.z.number().min(0).default(0)}).optional()}).optional()}).superRefine((e,t)=>{e.requestId
5182
5207
  const r=e.operation,n={complete:{field:"complete",hint:'Provide: { complete: { summary: "..." } }'},merge:{field:"merge",hint:'Provide: { merge: { verifications: [{key: "1a", evidence: "..."}] } }'},revert:{field:"revert",hint:'Provide: { revert: { reason: "..." } }'},search:{field:"search",hint:'Provide: { search: { query: "..." } }'}}[r]
5183
5208
  n&&!e[n.field]&&t.addIssue({code:"custom",message:`'${r}' operation requires '${n.field}' data. ${n.hint}`})})
5184
5209
  const p=r.z.string({required_error:"Required. A quick edit either touches up an earlier request's result or stands on its own: pass that request id (req######) or the word 'new'."}).refine(e=>"new"===e||/^req\d{6}$/.test(e),{message:"Pass a request id like req000031, or 'new'."}).describe("The request whose result this edit adjusts, when it is one: the user pointed at it (anchored the message to it, named it, or said 'that change', 'also', 'one more thing on that' right after it landed) and the edit changes what that request built. It joins that request's thread. Pass 'new' for an edit that stands on its own, even when it touches a file an earlier request changed. The same rule as create_request's follows.")
5185
5210
  exports.QuickWriteSchema=r.z.object({title:r.z.string().min(1,"Title is required"),description:r.z.string().min(1,"Description is required"),follows:p,path:r.z.string().optional(),documentId:c.optional(),content:r.z.union([r.z.string(),r.z.array(r.z.string())]),format:r.z.enum(["text","json"]).optional(),append:r.z.boolean().optional()}).transform(l).refine(d,{message:"path or documentId required"}),exports.QuickReplaceSchema=r.z.object({title:r.z.string().min(1,"Title is required"),description:r.z.string().min(1,"Description is required"),follows:p,operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:c.optional(),replacements:r.z.array(r.z.object({old:r.z.string().min(1),new:r.z.string(),match_mode:r.z.enum(["unique","all"]).optional()})).min(1),fuzzy_match:r.z.boolean().optional()}).transform(l).refine(d,{message:"path or documentId required"})).min(1),fuzzy_match:r.z.boolean().optional(),comment:r.z.string().min(1,"Comment describing the change is required")}),exports.MetaWorkflowListSchema=r.z.object({}),exports.MetaWorkflowGetSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to retrieve")}),exports.MetaWorkflowCreateSchema=r.z.object({name:r.z.string().min(1).describe("Unique name for the workflow"),shortName:r.z.string().min(1).max(10).describe("Short display name (required, max 10 chars)"),description:r.z.string().optional().describe("Description of what the workflow does"),testGate:r.z.enum(["off","auto","ask","strict"]).optional().describe("Test gate: off (no test gate; completions merge without running tests), auto (tests run automatically; the agent may opt out with a reason), ask (the user picks auto/interactive/skip at each completion), strict (tests always run; agent opt-outs are rejected and any failed command blocks)"),reviewGate:r.z.enum(["off","criteria","full"]).optional().describe('Review gate, as the workflow page names it: off; criteria ("Criteria only": one reviewer checks each acceptance criterion and nothing else); full ("Full review": adds reviewers that read the work for quality, reuse and efficiency)'),signoffGate:r.z.enum(["off","agent","you"]).optional().describe('Sign-off gate, as the workflow page names it: off; agent ("Delivery review": an agent asks whether the delivery is what was asked for, then merges or brings it to the user); you ("You sign off": the user approves before merge)'),approvalMode:r.z.enum(["none","require-approval","agent-check"]).optional().describe("Pre-work approval: none (default), require-approval (the user approves each request), or agent-check (an independent intake reviewer rules on agent-created requests before work starts)"),interactionPosture:r.z.enum(["autonomous","balanced","collaborative"]).optional().describe("Interaction posture (question-gate dial): autonomous (defaulted questions are self-answered -- proceed + FYI report), balanced (default; questions post normally, non-blocking when defaulted), collaborative (always ask the human)")}),exports.MetaWorkflowUpdateSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to update"),name:r.z.string().min(1).optional().describe("New name"),shortName:r.z.string().max(10).optional().describe("New short name (max 10 chars)"),description:r.z.string().optional().describe("New description"),testGate:r.z.enum(["off","auto","ask","strict"]).optional().describe("Test gate: off (no test gate), auto (tests run automatically; agent may opt out with a reason), ask (the user picks auto/interactive/skip at each completion), strict (tests always run; agent opt-outs rejected, any failed command blocks)"),reviewGate:r.z.enum(["off","criteria","full"]).optional().describe('Review gate, as the workflow page names it: off; criteria ("Criteria only": one reviewer checks each acceptance criterion and nothing else); full ("Full review": adds reviewers that read the work for quality, reuse and efficiency)'),signoffGate:r.z.enum(["off","agent","you"]).optional().describe('Sign-off gate, as the workflow page names it: off; agent ("Delivery review": an agent asks whether the delivery is what was asked for, then merges or brings it to the user); you ("You sign off": the user approves before merge)'),approvalMode:r.z.enum(["none","require-approval","agent-check"]).optional().describe("Pre-work approval: none (default), require-approval (the user approves each request), or agent-check (an independent intake reviewer rules on agent-created requests before work starts)"),interactionPosture:r.z.enum(["autonomous","balanced","collaborative"]).optional().describe("Interaction posture (question-gate dial): autonomous (defaulted questions are self-answered -- proceed + FYI report), balanced (default; questions post normally, non-blocking when defaulted), collaborative (always ask the human)")}),exports.MetaRecipeListSchema=r.z.object({}),exports.MetaRecipeGetSchema=r.z.object({recipeId:r.z.string().describe("Recipe ID to retrieve")}),exports.MetaRecipeCreateSchema=r.z.object({name:r.z.string().min(1).describe("Unique name for the recipe"),description:r.z.string().optional().describe("Description of what the recipe does"),documentation:r.z.string().optional().describe("Detailed documentation")}),exports.MetaRecipeAddHookSchema=r.z.object({recipeId:r.z.string().describe("Recipe ID to add the hook to"),hookId:r.z.string().describe("Hook template ID to add"),position:r.z.number().int().min(0).optional().describe("Position in the recipe (0-based, appended if omitted)")}),exports.MetaRecipeRemoveHookSchema=r.z.object({recipeId:r.z.string().describe("Recipe ID to remove the hook from"),hookId:r.z.string().describe("Hook template ID to remove")}),exports.MetaHookListSchema=r.z.object({}),exports.MetaHookCreateSchema=r.z.object({name:r.z.string().min(1).describe("Unique name for the hook"),description:r.z.string().optional().describe("Description of what the hook does"),eventType:r.z.string().describe("Event type that triggers this hook (e.g., session_idle, request_completed, custom:plan_ready)"),category:r.z.enum(["alchemy","session","message","editing","reading","file_management","documents","history","tool"]).default("session").describe("Hook category"),agent:r.z.string().describe("Agent template to execute when hook fires"),promptTemplate:r.z.string().describe("Prompt template with {{variable}} substitution"),condition:r.z.record(r.z.any()).optional().describe("Condition object for filtering when hook fires"),timing:r.z.enum(["before","after"]).default("after").describe("When to fire relative to the event"),action:r.z.enum(["spawn_agent","inject_prompt"]).optional().describe("What the hook does: spawn_agent (default) starts a new session of `agent` with the prompt; inject_prompt sends the prompt into an existing session"),target:r.z.enum(["independent_session","child_session","same_session","parent_session","ancestor_chain"]).optional().describe("Where the prompt goes. With spawn_agent: independent_session (default, no parent) or child_session (under the session that fired the event). With inject_prompt: same_session (default, the session that fired the event), parent_session, or ancestor_chain"),maxIterations:r.z.number().int().min(1).optional().describe("Maximum times the hook fires for one session, counted from its firing record. Set it on every session_idle hook that sends into the same session. A session a hook started never triggers that hook again, whatever this says"),emitEvent:r.z.string().optional().describe("Custom event to emit on completion"),emitEventOnCondition:r.z.union([r.z.object({condition:r.z.object({field:r.z.string(),operator:r.z.enum(["contains","not_contains","equals","not_equals","matches","not_matches"]),value:r.z.string()}),event:r.z.string()}),r.z.array(r.z.object({condition:r.z.object({field:r.z.string(),operator:r.z.enum(["contains","not_contains","equals","not_equals","matches","not_matches"]),value:r.z.string()}),event:r.z.string()}))]).optional().describe("Conditional event emission based on output")}),exports.MetaWorkflowAddRecipeSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to add the recipe to"),recipeId:r.z.string().describe("Recipe ID to add"),position:r.z.number().int().min(0).optional().describe("Position in the workflow (0-based, appended if omitted)")}),exports.MetaWorkflowRemoveRecipeSchema=r.z.object({workflowId:r.z.string().describe("Workflow ID to remove the recipe from"),recipeId:r.z.string().describe("Recipe ID to remove")}),exports.MetaAgentListSchema=r.z.object({}),exports.MetaOperationsSchema=r.z.object({operation:r.z.enum(["workflow-list","workflow-get","workflow-create","workflow-update","recipe-list","recipe-get","recipe-create","recipe-add-hook","recipe-remove-hook","hook-list","hook-create","workflow-add-recipe","workflow-remove-recipe","agent-list"]).describe("Which meta operation to perform; the other params apply per operation (see tool description)"),...exports.MetaWorkflowGetSchema.partial().shape,...exports.MetaWorkflowCreateSchema.partial().shape,...exports.MetaWorkflowUpdateSchema.partial().shape,...exports.MetaRecipeGetSchema.partial().shape,...exports.MetaRecipeCreateSchema.partial().shape,...exports.MetaRecipeAddHookSchema.partial().shape,...exports.MetaRecipeRemoveHookSchema.partial().shape,...exports.MetaHookCreateSchema.partial().shape,...exports.MetaWorkflowAddRecipeSchema.partial().shape,...exports.MetaWorkflowRemoveRecipeSchema.partial().shape}),exports.TestingProcessListSchema=r.z.object({}),exports.TestingProcessGetSchema=r.z.object({processId:r.z.string().describe("Test process ID to retrieve")})
5186
5211
  const f=r.z.object({name:r.z.string().min(1).describe('Command name (e.g., "Build", "Unit Tests")'),command:r.z.string().min(1).describe('Shell command to run (e.g., "npm test")'),required:r.z.boolean().default(!0).describe("If true, failure blocks the request (default: true)"),timeout:r.z.number().int().min(10).max(3600).default(300).describe("Max execution time in seconds (default: 300)")})
5187
- exports.TestingProcessCreateSchema=r.z.object({name:r.z.string().min(1).describe("Name for the test process"),environmentId:r.z.string().optional().describe("ID of the environment (confined runtime) this process runs in. Get it from environment_list or environment_setup."),description:r.z.string().optional().describe("Description of what this process verifies"),scope:r.z.string().optional().describe('LABEL ONLY -- a monorepo path prefix recorded for display (e.g. "packages/api/"). It does NOT affect when the process runs; use pathGlobs for that.'),pathGlobs:r.z.array(r.z.string()).optional().describe('Which changes this process runs on: glob patterns matched against the changed files (e.g. ["sites/**"], ["src/**", "*.md"]). THIS is what limits when the process runs. Omit it and the process runs on EVERY change, which is the right choice only for a check that is cheap and universal.'),areas:r.z.array(r.z.string()).optional().describe('Work-area labels this process covers (e.g. ["graphics/opengl"]). An alternative to pathGlobs for work that is not file-shaped; a process applies if EITHER dimension matches.'),setup:r.z.array(r.z.string()).optional().describe('Commands to prepare the environment (e.g., ["ln -s ../../node_modules node_modules"])'),commands:r.z.array(f).min(1).describe("Test commands to execute in order"),verifyAcceptanceCriteria:r.z.boolean().optional().describe("When true, auto-tester verifies request acceptance criteria after running commands"),fingerprint:r.z.array(r.z.string()).optional().describe('Files whose content invalidates setup. Setup commands re-run only when these files change between runs (e.g. ["package-lock.json"], ["Cargo.lock"], ["uv.lock", "poetry.lock"]). Polyglot repos list multiple. Omit / empty list = always re-run setup (existing behavior).'),poolSize:r.z.number().int().min(1).max(8).optional().describe("Number of warm test worktrees in the pool. Default 2. Set to 1 for languages with very large build artifacts (Rust target/, C++ build/). Higher values allow more parallel test runs at the cost of disk."),runtimeKept:r.z.string().optional().describe('Only when the user chose to run the check on a runtime other than the lowest one the project declares: that runtime and version, e.g. "python 3.12". Without it, a check whose environment runs another version than the declared minimum is not registered.')}),exports.TestingProcessDeleteSchema=r.z.object({processId:r.z.string().describe("Test process ID to delete")}),exports.EnvironmentListSchema=r.z.object({}),exports.ResultsSchema=r.z.object({operation:r.z.enum(["record","fetch","pin","list","read"]).describe("record = store a file you produced and file its reference; fetch = put a stored result back on disk so real tools can work on it; pin = write a small git-carried pointer for one; list = what has been recorded; read = look inside one without loading it."),path:r.z.string().optional().describe('record: the file to record. Looked for in your WORKTREE first and then in the MAIN project, so a result a run left outside your worktree (e.g. "solver-out/run-14.vtu") can still be recorded even though your other tools cannot see it. It must be INSIDE the project, not /tmp. To stage bulk without it reaching a commit, a shell command can write under `.alchemy_lab/scratch/` (gitignored); the write tools refuse that directory, so anything they produce goes elsewhere in the worktree.'),name:r.z.string().optional().describe("record: the name a person uses for it (defaults to the filename). list: show every version recorded under this name, newest first."),summary:r.z.record(r.z.any()).optional().describe('record (REQUIRED): the small facts a reader wants INSTEAD of the bytes, e.g. {maxStress: 412, unit: "MPa", converged: true} or {rows: 2400000, columns: 18}. This is what lets the result be referred to without being opened.'),box:r.z.string().optional().describe("fetch: an experiment handle (exp_...) to put the bytes INSIDE that sealed box, under its own /scratch (on disk, removed with the box), instead of on the host. Use this when the work stays sealed: the box mounts your worktree READ-ONLY and cannot see the host scratch directory, so a plain fetch is no use to it. Omit it for host work, which is faster and usually what you want when the run does not need to be confined. record: an experiment handle (exp_...) when the result lives INSIDE that sealed box rather than on the host. The result is streamed out and stored in one step -- use this instead of copying it to the worktree first, which a sealed box cannot do anyway because its worktree is mounted read-only."),mediaType:r.z.string().optional().describe("record: MIME type, when you know it."),inputs:r.z.array(r.z.string()).optional().describe("record: addresses of the results this one was made from. The provenance claim: these inputs, this producer, this output."),note:r.z.string().optional().describe("record: one line on how it was produced."),address:r.z.string().optional().describe("pin / read / fetch: the full 64-character address the record returned (a unique prefix works too)."),at:r.z.string().optional().describe('pin: where the pointer goes in your worktree, e.g. "results/run-14.vtu" (the .result.json suffix is added). fetch: where the bytes land -- under `.alchemy_lab/scratch/` on the host (bulk must not be able to reach a commit), or under the box\'s `/scratch` when `box` is given (its worktree is read-only). Defaults to results/<name>.'),comment:r.z.string().optional().describe("pin: the commit comment for the pointer file."),offset:r.z.number().optional().describe("read: line to start at (default 0)."),limit:r.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:r.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ResultInspectSchema=r.z.object({operation:r.z.enum(["list","read"]).describe("list = what has been recorded; read = look inside one."),name:r.z.string().optional().describe("list: show every version recorded under this name, newest first."),address:r.z.string().optional().describe("read: the full 64-character address of the result."),offset:r.z.number().optional().describe("read: line to start at (default 0)."),limit:r.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:r.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ExperimentStartSchema=r.z.object({env:r.z.string().optional().describe("Environment to run in, BY NAME (e.g. 'node', 'Vanilla JS syntax check') -- matched against environment_list. Pick one whose image has the toolchain you need. No suitable env -> environment_setup."),environmentId:r.z.string().optional().describe("Environment by id (alternative to `env`). The primary env is used if neither is given."),size:r.z.enum(["light","standard","heavy"]).optional().describe("How much of the machine the box gets. light: 2 cores, for lint, a typecheck or one test file. standard (default): a share of the free cores, for a test suite or a build. heavy: every free core but 2, for work that uses them all (a CFD or FEM solve, meshing, a large parallel build); ask for it only then, other boxes get less. Unset: the environment's default. Asking for a size you do not hold opens another box."),confirmWorkspace:r.z.boolean().optional().describe('Required to open the LIVE-HOST tier (env "workspace" / pure-host): confirms the task genuinely needs the real machine. Building or trying out the app is NOT such a task -- use a confined env (or environment_setup to create one).')}),exports.ExperimentStopSchema=r.z.object({handle:r.z.string().describe("The experiment handle to tear down.")}),exports.ExperimentListSchema=r.z.object({}),exports.ReadSiteSchema=r.z.object({url:r.z.string().describe("http(s) URL to load in the host browser and read as rendered text")}),exports.DriveSiteSchema=r.z.object({url:r.z.string().describe("http(s) URL of the RUNNING product to open in the host browser (e.g. the environment's published URL, or http://localhost:5173)."),steps:r.z.array(r.z.object({action:r.z.enum(["click","clickat","type","press","move","scroll","wait","media"]).describe('click (selector) \xb7 clickat (x,y: for canvas/game surfaces with no DOM) \xb7 type (selector + text) \xb7 press (key) \xb7 move (x,y) \xb7 scroll (dy) \xb7 wait (ms) \xb7 media (media: "print" shows the page as it prints, with its print styles applied, so the text and screenshot after it are the printout; "screen" switches back)'),selector:r.z.string().optional().describe('Playwright selector, e.g. "text=Build" or "#start". Required for click and type.'),text:r.z.string().optional().describe('Text to type (action "type").'),key:r.z.string().optional().describe('Key to press, e.g. "Enter" or "Escape" (action "press").'),x:r.z.number().optional(),y:r.z.number().optional(),dy:r.z.number().optional().describe('Scroll distance in px (action "scroll").'),ms:r.z.number().optional().describe('Milliseconds to wait, max 5000 (action "wait").'),media:r.z.enum(["print","screen"]).optional().describe('CSS media to emulate (action "media"): "print" to check what a printout shows.')})).min(1).max(20).describe("Up to 20 steps, taken in order. Each reports whether it found its target."),shots:r.z.enum(["each","final"]).optional().describe('"each" screenshots after every step (use when you need to see how it got there); default is the final state only.'),session:r.z.string().optional().describe('Name a session to KEEP THE PAGE OPEN between calls (e.g. "cloudhaven"). The next call with the same name continues exactly where this one stopped -- same game, same state, nothing reloaded. Without it every call starts a fresh browser, so anything you built up is lost and claims about accumulated state or elapsed time cannot be answered. Idle sessions close after 10 minutes.'),close:r.z.boolean().optional().describe("Close the named session after this call (do it when the drive is finished)."),width:r.z.number().optional().describe("Viewport width in px for a NEW session (default 1280; 390 emulates a phone). Clamped to 320-1920; a resumed session keeps its viewport.")}),exports.CaptureSiteSchema=r.z.object({url:r.z.string().describe("The http(s) URL to screenshot -- an external/public website, or a locally served URL (e.g. http://localhost:5173)."),fullPage:r.z.boolean().optional().describe("Capture the entire scrollable page (stored as JPEG) instead of just the viewport (PNG)."),width:r.z.number().optional().describe("Viewport width in px (default 1280; 390 emulates a phone). Clamped to 320-1920.")}),exports.PreviewSiteSchema=r.z.object({dir:r.z.string().describe('The built static directory to serve, repo-relative (e.g. "sites/acme" or "dist", "." for the top of the repository). Must contain an index.html.'),requestId:r.z.string().regex(/^req\d{6}$/).optional().describe("Serve the directory from THIS request's own working copy (its branch, before it merges) instead of from main. This is how anyone previews unmerged work, e.g. the user wants to look at a request before signing it off. Never copy a request's files into main to preview them."),name:r.z.string().optional().describe("Handle name for the preview (defaults to a slug of the directory). Reusing a name replaces that preview."),label:r.z.string().optional().describe("Human-readable label shown with the preview.")}),exports.FetchAssetSchema=r.z.object({url:r.z.string().describe("The http(s) URL of the image or font to download."),destPath:r.z.string().describe('Where to write it, repo-relative (e.g. "sites/acme/assets/hero.jpg"). Must stay inside the working tree; parent directories are created.')}),exports.RequestVisualFeedbackSchema=r.z.object({box:r.z.string().optional().describe("The experiment handle (from experiment_start, on a screenshot-capable env) to render in / read from. The box must stay held until this returns."),route:r.z.string().optional().describe('With `box`: the route I render + screenshot FOR YOU (e.g. "/" or "/settings"). I serve `serveDir` and screenshot it -- you do NOT hand-roll serve/screenshot. This is the easiest path.'),script:r.z.string().optional().describe('With `box`, for FULL control: a Playwright snippet (the body of `async (page, baseUrl) => { ... }`). I serve the app, launch headless Chromium, and run your script -- navigate (page.goto(baseUrl + "/x")), click, fill, scroll, waitForSelector, etc. -- then screenshot the final page state. Reliable: you write only the interaction, no serve/launch/flags. Use this to reach a state that needs interaction (open a menu, log in, submit a form).'),fullPage:r.z.boolean().optional().describe("Capture the entire scrollable page instead of just the viewport."),serveDir:r.z.string().optional().describe("With `box`+`route`: the static dir to serve (default /work). For a built app, build first via run(), then point this at the build output (e.g. /work/dist)."),path:r.z.string().optional().describe("Alternative to route: a screenshot you ALREADY wrote inside the box (e.g. /tmp/shot.png); I read it out."),assetFilename:r.z.string().optional().describe("Alternative to box: the filename of an already-uploaded screenshot asset to annotate."),prompt:r.z.string().optional().describe("What you want the human to look at or mark up (shown with the request)."),timeoutMs:r.z.number().optional().describe("How long to wait for the human, in ms (default ~10 min).")}),exports.AskUserVisualSchema=r.z.object({question:r.z.string().describe('The question for the USER about the marked region (e.g. "Should this button use the primary color?").'),imagePath:r.z.string().optional().describe("Repo-relative path to an image you already produced (png/jpg/gif/webp), e.g. a screenshot you wrote. Provide this OR previewName."),previewName:r.z.string().optional().describe("Name of a registered live preview (a `live:` ref); I freeze it into a snapshot image for you. Provide this OR imagePath."),box:r.z.string().optional().describe('The region the question is about: "x,y,w,h" in normalized 0..1 image coordinates, origin top-left (e.g. "0.62,0.08,0.3,0.12"). Omit to ask about the whole image.'),options:r.z.array(r.z.string()).optional().describe("Optional answer choices (labels). Omit for a free-text answer."),context:r.z.string().optional().describe("Extra context shown with the question in the inbox (why you are asking, what you will do with the answer)."),priority:r.z.enum(["low","normal","high"]).optional().describe('Question priority; "high" always reaches the user regardless of autonomy dial.')}),exports.EnvironmentGetSchema=r.z.object({environmentId:r.z.string().describe("Environment ID to retrieve")}),exports.EnvironmentProvisionSchema=r.z.object({environmentId:r.z.string().describe("Environment (with a delivery config) whose disposable stack to provision + health-gate. Provisioning is a blast op: it waits for approval from the user."),why:r.z.string().optional().describe("Why provision now (shown on the approval).")}),exports.EnvironmentTeardownSchema=r.z.object({environmentId:r.z.string().describe("Environment whose delivery stack to tear down (named volumes kept). Blast op: waits for approval from the user.")}),exports.EnvironmentStatusSchema=r.z.object({environmentId:r.z.string().describe("Environment whose delivery stack status (services up + health) to read. Read-only.")}),exports.EnvironmentVerifyStackSchema=r.z.object({environmentId:r.z.string().describe("Environment whose RUNNING delivery stack to verify against its declared checks (correctness, not just readiness). Read-only.")})
5188
- const m=r.z.object({image:r.z.string().optional().describe("Container image carrying the project toolchain/deps, e.g. node:20-alpine, python:3.12-slim, golang:1.22, ruby:3.3"),install:r.z.string().optional().describe("Deps install command -- Node-only sugar that populates a /work/node_modules cache volume, run with registry egress before the confined gate, e.g. npm ci. Non-Node runtimes use `artifacts` instead (a cache volume + prepare command: .venv/uv sync, cargo, go, vendor/bundle). Omit if the project has no deps."),composeFile:r.z.string().optional().describe("Path to the project docker-compose file for its services. Omit if no services."),shellInit:r.z.string().optional().describe("Shell preamble run before EVERY command in the box, e.g. '. /usr/lib/openfoam/openfoam2312/etc/bashrc'. REQUIRED for a toolchain that publishes itself through a script rather than the image PATH (OpenFOAM etc/bashrc, oneAPI setvars.sh, conda activate, ROS setup.bash): `docker exec` runs no ENTRYPOINT and reads no login profile, so without this every command gets a bare PATH. Put the preamble HERE, never inside the smoke command -- a smoke command carrying its own preamble proves the preamble and not the environment, and the next agent gets nothing."),connEnv:r.z.record(r.z.string()).optional().describe("Connection env injected into the gate (e.g. DATABASE_URL) using SERVICE NAMES + internal ports, never localhost/host ports."),seed:r.z.array(r.z.string()).optional().describe("Optional commands run after services are up (e.g. migrations)."),artifacts:r.z.array(r.z.object({path:r.z.string().describe("Mount target for the writable, populated volume: relative to /work (e.g. node_modules, .venv, vendor/bundle) or absolute (e.g. /cache/cargo)."),prepare:r.z.string().optional().describe("Command that populates it -- the one phase with registry egress (e.g. npm ci, uv sync, cargo fetch). Omit for a writable empty volume."),manifests:r.z.array(r.z.string()).optional().describe("Lockfiles (relative to /work) whose content hash keys the cache; list them so the volume is reused across runs and only rebuilt when deps change (e.g. uv.lock, Cargo.lock). Omit => rebuilt every run."),env:r.z.record(r.z.string()).optional().describe('Env applied during prepare AND every run, e.g. { CARGO_HOME: "/cache/cargo" } for caches outside /work.')})).optional().describe("Per-runtime build/dependency artifacts: writable volumes populated by a prepare command, generalizing `install` beyond node_modules. Use for Python (.venv), Rust (target + $CARGO_HOME), Go, Ruby (vendor/bundle), etc. `install` is sugar for a single node_modules artifact; for any non-Node runtime use artifacts (install would write to node_modules, the wrong place)."),confinement:r.z.enum(["confined","egress-allowlist","privileged","unsafe","host","pure-host"]).optional().describe("Confinement level. Default 'confined' (fully sealed Docker; add egressAllowlist hosts to open exactly those -- stored as 'egress-allowlist'). privileged/unsafe relax the container. 'host' runs on the host machine (no Docker) inside an OS sandbox (Seatbelt/bubblewrap) for toolchains Docker can't run. 'pure-host' is the built-in workspace env (the live worktree) -- do not create new envs with it. Merge gating is independent (the Sign-off setting)."),commandGate:r.z.array(r.z.enum(["policy","agent","human"])).optional().describe("Gates an experiment command passes before it runs, in escalation order policy -> agent -> human. Empty = run freely; escalating past the last gate blocks (fail closed). policy = deterministic allowlist/deny-floor; agent = isolated LLM judge (allow or escalate only); human = ask. Registered commands are pre-approved. Defaults by level ([] for confined/egress-allowlist, ['human'] for privileged/unsafe)."),worktreeTarget:r.z.enum(["ephemeral","live"]).optional().describe("Worktree the env runs against: 'ephemeral' (a disposable copy at HEAD, default) or 'live' (the project's live working dir, read-write). The built-in workspace env is 'live'."),egressAllowlist:r.z.array(r.z.string()).optional().describe("Hosts the box may reach at the 'egress-allowlist' level (exact name or subdomain, e.g. registry.npmjs.org). Ignored at other levels."),addCaps:r.z.array(r.z.string()).optional().describe("Linux capabilities to add at the 'privileged' level (on top of cap-drop ALL), e.g. SYS_PTRACE for debuggers, IPC_LOCK for memlock. Ignored at other levels."),sysctls:r.z.record(r.z.string()).optional().describe("Namespaced sysctls to set at the 'privileged' level, e.g. net.ipv4.ip_local_port_range. Host-wide sysctls (vm.max_map_count) are NOT settable per-container."),runAsRoot:r.z.boolean().optional().describe("Run the box as root (privileged / unsafe levels). Needed for some caps + the docker socket."),dockerSocket:r.z.boolean().optional().describe("Mount the host docker socket for testcontainers-style tests. WARNING: full host control over the docker daemon. Only honored at the 'unsafe' level."),persistentVolumes:r.z.array(r.z.object({name:r.z.string(),target:r.z.string(),service:r.z.string().optional()})).optional().describe("Data volumes that survive between runs (keyed to this environment; off by default since experiments are ephemeral). Set service to mount into a compose service (e.g. a DB data dir), or omit it to mount into the box (e.g. a SQLite dir)."),resources:r.z.object({memory:r.z.string().optional().describe("Memory cap, e.g. '4g' (default 2g)."),cpus:r.z.number().optional().describe("A fixed CPU count. Usually leave it unset and set size instead."),size:r.z.enum(["light","standard","heavy"]).optional().describe("What this environment's boxes and checks are for, which sets their cores from what is free: light (2 cores: lint, small checks), standard (the default: a share of the free cores, for test suites and builds), heavy (every free core but 2: CFD or FEM solves, meshing, large parallel builds). An agent can ask for another size per box."),pidsLimit:r.z.number().optional().describe("Max processes (default 512)."),scratchMb:r.z.number().optional().describe("Size in MB of an in-memory /scratch, used only when the image cannot take the disk-backed one (default 512). /scratch is normally on disk and needs no size."),shmMb:r.z.number().optional().describe("/dev/shm size in MB -- bump for headless browsers (Chrome crashes on the 64m default).")}).optional().describe("Per-environment resource caps that override the confined defaults.")})
5189
- exports.EnvironmentCreateSchema=r.z.object({name:r.z.string().min(1).describe("Name for the environment, e.g. node + postgres"),description:r.z.string().optional().describe("What this environment provides"),scope:r.z.string().optional().describe("Subproject path prefix for monorepos (e.g. packages/api/). Omit for the whole repo."),preset:r.z.enum(["office"]).optional().describe('Start from a built-in preset; anything in config overrides it. "office": python-docx, openpyxl, python-pptx, pandoc, LibreOffice and segno/zbarimg (QR codes) in a sealed box, with `render-pdf FILE` to turn Word, Excel, PowerPoint, Markdown or HTML into PDF. Its image is built locally on first use (a few minutes). Pass the same preset (and config) to environment_verify and environment_create.'),config:m.optional().describe("How to stand up the confined runtime. Must have passed environment_verify in this session (exact config, after the preset is applied), unless skipVerify. Optional with a preset."),isPrimary:r.z.boolean().optional().describe("Make this the project primary environment (default for new test processes). The first environment is primary automatically."),skipVerify:r.z.boolean().optional().describe("Escape hatch: save WITHOUT a passing environment_verify. ONLY for configs that genuinely cannot be verified from here (e.g. they need credentials/secrets, external services, or hardware the confined verifier lacks). Requires skipVerifyReason; the skip is logged."),skipVerifyReason:r.z.string().optional().describe("Why this config cannot be verified (required with skipVerify: true; recorded in the log).")}),exports.EnvironmentUpdateSchema=r.z.object({environmentId:r.z.string().describe("Environment ID to update"),name:r.z.string().min(1).optional().describe("New name"),description:r.z.string().optional().describe("New description"),scope:r.z.string().optional().describe("New subproject scope"),config:m.optional().describe("New config (replaces the existing config wholesale). Must have passed environment_verify in this session (exact config), unless skipVerify. Metadata-only updates need no verify."),isPrimary:r.z.boolean().optional().describe("Make this the project primary environment"),skipVerify:r.z.boolean().optional().describe("Escape hatch: save WITHOUT a passing environment_verify. ONLY for configs that genuinely cannot be verified from here (e.g. they need credentials/secrets, external services, or hardware the confined verifier lacks). Requires skipVerifyReason; the skip is logged."),skipVerifyReason:r.z.string().optional().describe("Why this config cannot be verified (required with skipVerify: true; recorded in the log).")}),exports.EnvironmentDeleteSchema=r.z.object({environmentId:r.z.string().describe("Environment ID to delete")}),exports.EnvironmentVerifySchema=r.z.object({preset:r.z.enum(["office"]).optional().describe('Start from a built-in preset; anything in config overrides it. "office": python-docx, openpyxl, python-pptx, pandoc, LibreOffice and segno/zbarimg (QR codes) in a sealed box, with `render-pdf FILE` to turn Word, Excel, PowerPoint, Markdown or HTML into PDF. Its image is built locally on first use (a few minutes). Pass the same preset (and config) to environment_verify and environment_create.'),config:m.optional().describe("The proposed environment config to verify (image, install, services, connection). Optional with a preset."),smoke:r.z.string().optional().describe("Optional smoke command run confined after the environment is up (default: true). Use a connection probe to confirm services are reachable.")}),exports.EnvironmentSetupSchema=r.z.object({name:r.z.string().max(60).optional().describe("A short name for what you need, a few words the user reads as the setup request's title (e.g. 'Node 20 for syntax checks'). Without it the title is made from the first sentence of context."),context:r.z.string().optional().describe("What you need and why, forwarded to the setup-architect -- e.g. 'Need Node 20 to run node --check on the standalone .js modules; no package.json, no deps'. The clearer this is, the less the architect has to ask.")}),exports.TestingProcessUpdateSchema=r.z.object({processId:r.z.string().describe("Test process ID to update"),environmentId:r.z.string().optional().describe("ID of the environment this process runs in (replaces the current reference)."),name:r.z.string().min(1).optional().describe("New name"),description:r.z.string().optional().describe("New description"),scope:r.z.string().optional().describe("LABEL ONLY -- a monorepo path prefix recorded for display. It does NOT affect when the process runs; use pathGlobs for that. Omit to leave unchanged."),pathGlobs:r.z.array(r.z.string()).optional().describe('Which changes this process runs on: glob patterns matched against the changed files (e.g. ["sites/**"], ["src/**", "*.md"]). THIS is what limits when the process runs. Replaces the existing list; omit to leave unchanged. An empty list means the process runs on every change.'),areas:r.z.array(r.z.string()).optional().describe('Work-area labels this process covers (e.g. ["graphics/opengl"]). An alternative to pathGlobs for work that is not file-shaped; a process applies if EITHER dimension matches.'),isActive:r.z.boolean().optional().describe("Enable or disable the process"),setup:r.z.array(r.z.string()).optional().describe("New setup commands (replaces existing)"),commands:r.z.array(f).optional().describe("New test commands (replaces existing)"),verifyAcceptanceCriteria:r.z.boolean().optional().describe("When true, auto-tester verifies request acceptance criteria after running commands"),fingerprint:r.z.array(r.z.string()).optional().describe("Files whose content invalidates setup (replaces existing list)."),poolSize:r.z.number().int().min(1).max(8).optional().describe("Number of warm test worktrees in the pool (replaces existing)."),runtimeKept:r.z.string().optional().describe('With environmentId: only when the user chose a runtime other than the lowest one the project declares, e.g. "python 3.12".')}),exports.TestsSchema=r.z.object({operation:r.z.enum(["list","setup","run","switch-off","switch-on"]).describe('"list" to see registered test processes for this project. "setup" to spawn an interactive setup-architect session that helps configure a process. "run" to fire an async test run for your current request: optional `process` picks one configured process (by name or id), optional `name` filters to one registered command. The call returns immediately and you receive a report on your next turn. "switch-off" / "switch-on" turn one registered check (`process`) off or back on: the user\'s call, so it needs their words in `userSaid`; the user is told in Steer what stops being checked.'),process:r.z.string().optional().describe('Used by `run`, `switch-off` and `switch-on`. Picks a single test process by name (case-insensitive) or id. For `run`, omit to run every active test process for this project. Use `operation: "list"` to see available process names.'),userSaid:r.z.string().max(600).optional().describe("Only used by `switch-off` and `switch-on`: the user's own words asking for it, copied exactly from their message. Checked against what they wrote in this project."),name:r.z.string().optional().describe("Only used by `run`. Filters execution to a single test command by name (matches a command name registered in the test process). Omit to run all required commands."),ask:r.z.string().max(1500).optional().describe('Only used by `setup`, and required when you are not working a request: what the user asked the check to run, in their words ("run line-count, tracked-size and file-types on every merge; stop the merge if one fails"). The setup architect registers exactly that.')}),exports.ExecuteTestCommandSchema=r.z.object({processId:r.z.string().describe("Test process ID containing the command"),commandName:r.z.string().describe('Name of the test command to run, or "setup" to run all setup commands in sequence')}),exports.RunSchema=r.z.object({command:r.z.string().optional().describe('A shell command. It runs INSIDE a held experiment -- pass `box`. There is no bare-host execution: hold a box first with experiment_start (prefer the most confined env that fits). A sealed box is the containment, so anything runs there safely; the live host is just the least-safe env (experiment_start({env:"workspace"})).'),name:r.z.string().optional().describe("Run a SAVED command by name. Also requires a `box`: a saved command runs in a held experiment like everything else. If it is registered for that box's environment it runs WITHOUT the gate (registration is a per-env gate-skip, not a box-skip)."),box:r.z.string().optional().describe("An experiment handle from experiment_start: the command runs inside that held box; state persists across runs (start a server once, then poke it). Pass this or env."),env:r.z.string().optional().describe("Instead of box: an environment by name. Its box is started for you (or your open one on it returned), then the command runs in it, in one call; the result gives the handle for later runs."),size:r.z.enum(["light","standard","heavy"]).optional().describe("With env: the size of the box to start (see experiment_start). light for lint or one test file, heavy only for work that uses every core (CFD, meshing, a large parallel build)."),args:r.z.string().optional().describe("Extra arguments appended to the command/saved command."),grep:r.z.string().optional().describe("Filter output through grep -i with this pattern."),timeout:r.z.number().optional().describe("Timeout in seconds."),why:r.z.string().optional().describe("One short sentence: why you are running this command. When the command needs the user's approval, this reason is shown on the approval card -- a clear why gets a faster decision. Skip it for obviously-safe checks."),cwd:r.z.string().optional().describe('A subdirectory to run an ad-hoc `command` in, relative to the worktree root (e.g. "apps/web"). The box already runs at the root, so omit it there; a command starting with `cd` is refused in favour of this. A saved `name` runs in its own registered directory.')}),exports.CommandsSchema=r.z.object({operation:r.z.enum(["run","list","remove","register","setup"]).default("run").describe('"run" (default) runs a CHECK on the live worktree -- either a saved command by `name` or any shell command via `command`. Best for read-only inspection/verification against your edits (grep, cat, lint, typecheck, git status): those auto-pass the gate and are fast. Build/test and other side-effectful commands are NOT sandboxed on the workspace -- run those in an experiment (a confined box) instead; if you run them here they will ask the user first. "list" shows saved commands. "register" SUGGESTS a command for the catalog (lands inactive for a human to activate -- never required to run; prefer one general project-wide check over per-file variants). "remove" deletes one. "setup" spawns the setup-architect.'),name:r.z.string().optional().describe('Command name (required for "run", "add", and "remove")'),args:r.z.string().optional().describe('Additional arguments appended to the base command (optional, for "run")'),grep:r.z.string().optional().describe('Filter output through grep (optional, for "run"). Pipes command output through grep -i with this pattern.'),command:r.z.string().optional().describe('For "run": any shell command to execute on the workspace through the gate (no registration needed). For "add": the shell command to register (e.g. "tsc --noEmit")'),description:r.z.string().optional().describe('What this command verifies. REQUIRED when operation is "add": must articulate the command\'s purpose so the audit trail (added_by_session_id + description) is meaningful in the project commands UI.'),timeout:r.z.number().optional().describe('Timeout in seconds (optional, for "add", default 60)'),cwd:r.z.string().optional().describe('Working directory relative to project root (optional, for "add", e.g. "apps/web"). Omit to run at project root: use this for monorepos where the command lives in a subdir.'),context:r.z.string().optional().describe('Optional caller hint for the "setup" operation: short note about what the calling agent thinks the user needs (e.g. "the worker asked for npm run build with strict TS"). The architect treats this as a hint but still does its own discovery + user confirmation.'),commands:r.z.array(r.z.object({name:r.z.string().min(1).describe('Command name (e.g. "test:web")'),command:r.z.string().min(1).describe("Shell command to execute"),description:r.z.string().min(1).describe("What this command verifies"),cwd:r.z.string().optional().describe("Working directory relative to project root (omit for root)"),timeout:r.z.number().optional().describe("Timeout in seconds (default 60)")})).optional().describe('Batch of commands to register (required for "register", ignored otherwise). Each item must have name, command, and description. cwd is optional and supports monorepo subdirs.')}),exports.ReportTestResultsSchema=r.z.object({status:r.z.enum(["passed","failed","error"]).describe("Overall result"),testRunId:r.z.string().optional().describe("The test run this verdict belongs to (given in your instructions). Strongly preferred: without it the latest run is guessed, which misattributes results when several testers run concurrently."),results:r.z.object({commands:r.z.array(r.z.object({name:r.z.string().describe("Command name"),status:r.z.enum(["passed","failed","skipped","error"]).describe("Command result"),output:r.z.string().optional().describe("Relevant output snippet"),diagnosis:r.z.string().optional().describe("Why it failed, if applicable")})).describe("Per-command results"),summary:r.z.string().describe("Brief overall summary"),acceptanceCriteria:r.z.array(r.z.object({criterion:r.z.string().describe("The acceptance criterion text"),status:r.z.enum(["verified","unverified","unclear"]),evidence:r.z.string().describe("How verified, or why not")})).optional().describe("AC verification results, if applicable"),faultClass:r.z.enum(["fail_code","fail_evidence","fail_environment","fail_process"]).optional().describe("On a FAILED verdict, WHY it failed so the gate routes to the right actor: 'fail_code' (the change is wrong), 'fail_evidence' (the run did not prove a criterion the coder could have proven: attach/adjust verify commands), or 'fail_environment' (a required tool/runtime is MISSING from the confined box, e.g. 'node: not found': the coder cannot fix this; the environment must be extended). Use 'fail_environment' ONLY when the evidence shows a command/runtime absent from the box, never for a genuine defect or a fixable evidence gap. 'fail_process' is for a test process whose OWN command names a file the repository does not have (a deleted script): nobody's code is wrong and extending the box cannot help; the process itself must be fixed or turned off."),missingCapabilities:r.z.array(r.z.string()).optional().describe("With faultClass 'fail_environment', the binaries/runtimes the box lacks (e.g. ['node']) so the environment can be extended to provide them.")}).describe("Structured test results")}).superRefine((e,t)=>{"fail_environment"!==e.results?.faultClass||e.results.missingCapabilities&&e.results.missingCapabilities.length>0||t.addIssue({code:r.z.ZodIssueCode.custom,path:["results","missingCapabilities"],message:"faultClass 'fail_environment' requires results.missingCapabilities: name the absent binaries/runtimes (the deterministic capability probe in your evidence lists them, e.g. ABSENT from the box: node). Without the names, the environment cannot be extended and the request strands."})}),exports.OpeningBriefSectionSchema=r.z.object({id:r.z.string().min(1).describe('Stable id, unique within the brief (e.g. "date-format"). "testing-mode" is reserved for the server.'),question:r.z.string().min(1).describe("The choice, as one plain question the user can answer without your session."),context:r.z.string().min(1).describe("What the choice shapes, the consequence of each option, and why your default is recommended, citing the facts in the request and its sources (notes, numbers, an accepted plan) as they state them. Where those sources answer the choice, recommend their answer."),options:r.z.array(r.z.object({id:r.z.string().min(1).describe("Option id"),label:r.z.string().min(1).describe("Short option label"),description:r.z.string().optional().describe("What it means and its tradeoff, in 1-2 plain sentences.")})).min(2).describe("At least two genuine alternatives."),default:r.z.string().min(1).describe("The option id you are proceeding on now."),criteria:r.z.array(r.z.string().min(1)).optional().describe('Keys of the criteria whose wording this choice decides (e.g. ["2b"]), worded for your default. If the user picks another answer, each is marked for rewording and is not proven until reworded.')}).superRefine((e,t)=>{e.options.some(t=>t.id===e.default)||t.addIssue({code:r.z.ZodIssueCode.custom,path:["default"],message:`default '${e.default}' names no option in this section (option ids: ${e.options.map(e=>e.id).join(", ")})`}),"testing-mode"===e.id&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["id"],message:"section id 'testing-mode' is reserved: the server composes the testing section when the project's gate needs it -- remove it from your sections"})}),exports.SessionMessageOperationsSchema=r.z.object({operation:r.z.enum(["send","note","reply","list","get","offer-handoff","record-decision","brief","withdraw"]),milestone:r.z.boolean().optional().describe("note: a real boundary (it works, the approach changed, the goal is met), a few per request at most. Also puts one quiet line in the user's stream."),withdrawReason:r.z.string().optional().describe("withdraw: why the answer is no longer needed."),type:r.z.enum(["question","report"]).optional().describe("Required for send."),toSessionId:r.z.string().optional().describe('Target session id, or "parent". Omit to ask the user (a broadcast).'),questionType:r.z.enum(["free-form","alternatives"]).optional().describe("Required for a question."),questionContent:r.z.object({question:r.z.string().min(1).describe("The question text"),context:r.z.preprocess(e=>null!==e&&"object"==typeof e?JSON.stringify(e):e,r.z.string().optional()).optional().describe("What the user needs to answer without your session: what you were doing and found, why it matters, the consequence of each answer, and why you recommend what you do. Plain language, no paths or identifiers. Output you ask them to judge goes in a fenced code block, as printed."),options:r.z.array(r.z.object({id:r.z.string(),label:r.z.string(),description:r.z.string().optional().describe("What it means and its tradeoff, in 1-2 plain sentences. Fill it on every option."),recommended:r.z.boolean().optional().describe("Your advice, highlighted on the card; not what happens if nobody answers (that is defaultOption). Not on a question that names criteria."),effect:r.z.enum(["choose","send-back","not-yet"]).optional().describe('"choose" (default): records the pick for you. "send-back": asks for a change to the work; on a request waiting for the user\'s sign-off the pick itself sends it back to you (do not call reopen). "not-yet": only with [user-input] criteria; they stay owed.')})).optional().describe("Each a genuine alternative, complete in one tap (one needing a second answer is two options). Recommended option first. When the way the user asked cannot work, include one that reaches their goal another way."),allowMultiple:r.z.boolean().optional().describe("Allow picking several options."),fields:r.z.array(r.z.object({id:r.z.string().min(1),label:r.z.string().min(1),placeholder:r.z.string().optional()})).min(1).optional().describe('free-form: for several values (a name, an email, a phone), one labelled input each; the answer arrives as "Label: value" lines. Never ask for passwords or keys: answers are on the record.'),defaultOption:r.z.string().optional().describe("What happens if nobody answers before it expires (the option id, or the text for free-form); the asker then resumes. It happens without the user, so it must be safe: when options differ in risk (values silently changed, data loss, anything hard to undo), the default is the safe one even if you recommend another, and the context says so."),onExpiry:r.z.enum(["assume-default","escalate"]).optional().describe('"assume-default" (default with a defaultOption): resolve to it and resume the asker. "escalate": leave it pending, flagged for the user.'),steer:r.z.enum(["what","how"]).optional().describe('Tag every question: "what" shapes the work product (spec, wording, UX, scope); "how" steers the process (testing, sequencing, completion). Most "how" choices are yours to make, not to ask.')}).optional().describe("Required for a question."),priority:r.z.enum(["low","normal","high"]).optional().describe('"high": blocked with real stakes; reaches the user at once, past the question gate. "low" with a defaultOption: under \'balanced\', batched to the next checkpoint (request review or plan handoff) without holding; you proceed on your default.'),expiresInHours:r.z.number().optional().describe("Question expiry in hours."),userSaid:r.z.string().min(8).max(600).optional().describe('question: when the user asked you to ask them (a card, options, "ask me first"), their words copied exactly. Checked against what they wrote; the question then reaches them whatever the Autonomy setting.'),replaces:r.z.array(r.z.string().regex(/^Q-[A-Za-z0-9]+-\d+$/)).max(10).optional().describe("question: codes of your open questions on the same request that this one supersedes; they close when it posts. With a question already open on the request, pass this or keepsOpen."),keepsOpen:r.z.boolean().optional().describe("question: true when this asks something different from your open questions on the request, so they stay open."),forceAsk:r.z.boolean().optional().describe("question: re-ask something the decision record already answers, only because circumstances changed; say what changed in questionContent.context."),status:r.z.enum(["success","partial","failure","blocked"]).optional().describe("Required for a report."),blockedOn:r.z.string().optional().describe('Required with status "blocked" on a request you own: "decision" (a question to your parent, escalating to the user) or a request id like "req000123" (declares a wait; you are told when it settles). A block the record disproves (main moved, a conflict without markers, a missing association) is answered with facts and not filed.'),criteria:r.z.array(r.z.string().min(1)).max(10).optional().describe('question: keys of the [user-input] criteria you ask for (e.g. ["1a"]); only such a question counts as asking them. It goes to the user with no default and no recommended option, and the answer is their proof unless the picked option is "not-yet". The user\'s check of finished work is asked on the card at the end, not here.'),attention:r.z.enum(["fyi","glance","act"]).optional().describe("'fyi' (logged, no badge), 'glance' (look soon), 'act' (the user must act). Default from status: failure/blocked act, partial glance, success fyi. It does not decide who is woken: a report to a hub never wakes it (read in its digest); to any other parent or named session, it does. 'fyi' milestones go to the user's stream."),reportKind:r.z.enum(["milestone","reply"]).optional().describe("'milestone': narration at a boundary. 'reply': a reply to the user's inbox message."),summary:r.z.string().optional().describe('Required for a report. To the user: plain language, outcome first; paths and identifiers go in details. It shows alone in the stream, often hours later: name the subject first ("Reviewing the flash work, I found..."), do not open with Also or Then. Reports to agents can be technical.'),details:r.z.string().optional().describe("With blockedOn \"decision\" these become the question's context, and the user's card if your parent cannot decide: plain words, no tool names or call syntax; explain a refusal you got instead of pasting it."),ruleVerdicts:r.z.array(r.z.object({rule:r.z.string().min(1).max(80).describe("The rule id as your spawn prompt lists it, without brackets."),verdict:r.z.enum(["held","broken","not_applicable"]),note:r.z.string().max(300).optional().describe("broken: what breaks it and where (file:line). Omit otherwise.")})).max(40).optional().describe('report, Rules reviewer only: a verdict on every rule you were given. Only "broken" is a finding.'),finish:r.z.boolean().optional().describe("Release request associations after the report."),silent:r.z.boolean().optional().describe("Record the report for the coordinator to drain without waking it (fan-out research workers)."),relatesTo:r.z.string().regex(/^[IQ]-[A-Za-z0-9]+-\d+$/,"relatesTo must be an instruction code (I-...) or question code (Q-...)").optional().describe("An instruction code (I-...) this relates to; for record-decision, a question code (Q-...) it resolves or supersedes."),decision:r.z.string().optional().describe("record-decision (required): the one-line call, recorded as the answer."),why:r.z.string().optional().describe("record-decision: the reasoning."),context:r.z.string().optional().describe("record-decision: what the choice was about, recorded as the question (defaults to decision)."),requestId:r.z.string().optional().describe('The request this is about (e.g. "req000007"), so it shows on that request. note: defaults to your session\'s request. report and record-decision: pass it when about one request, or it is filed only at the project. brief: required.'),area:r.z.string().optional().describe('record-decision: a work area (e.g. "graphics/opengl") when the call is about that area. Ignored if requestId is set.'),sections:r.z.array(exports.OpeningBriefSectionSchema).min(1).optional().describe("brief (required): your real design choices, each with at least two options, a default and context."),messageCode:r.z.string().optional().describe("Required for reply, get and withdraw."),sectionAnswers:r.z.record(r.z.string(),r.z.string().nullable()).optional().describe("reply to an opening brief: { sectionId: optionId } for sections the user answered, changed or confirmed, with quote (their words). Sections not named stay open. null when the user says a section no longer applies. On a settled brief, 'your-words' when no option says what they asked; a changed choice acts on the work as it is now (finished work goes back to rework). Tell the user what the result says, nothing more."),quote:r.z.string().optional().describe("reply to a question: the user's words this answer records, copied exactly; it is then recorded as decided with the user, and refused if they never wrote them. Omit for your own decision. Options that are the user's own statement (\"I checked, it works\") only the user can pick."),text:r.z.string().optional().describe("reply: the answer to a free-form question. note (required): the note."),selectedOptions:r.z.array(r.z.string()).optional().describe("reply: the picked option ids."),notes:r.z.string().optional().describe("Additional notes"),escalate:r.z.boolean().optional().describe("reply to a question asked of you that needs the user: a copy reaches the user with your recommendation (in notes); the asker gets their answer directly. Not for broadcasts."),list:r.z.object({sessionId:r.z.string().optional().describe("Defaults to your session."),messageType:r.z.enum(["question","report","instruction","all"]).default("all").describe("Filter by message type"),direction:r.z.enum(["incoming","outgoing","all"]).default("all"),status:r.z.enum(["pending","answered","expired","success","partial","failure","blocked","all"]).default("all").describe("Filter by status"),readStatus:r.z.enum(["unread","read","acknowledged","all"]).default("all").describe("Filter by read status (reports only)"),requestId:r.z.string().optional().describe("Only messages about this request. Listing another session's inbox defaults to the request you are on."),limit:r.z.number().min(1).max(100).default(50),offset:r.z.number().min(0).default(0)}).optional().describe("list filters."),offerHandoff:r.z.object({summary:r.z.string().describe('One sentence: what the plan builds. The card is already titled "Plan ready"; do not start with that.'),requestIds:r.z.array(r.z.string()).describe("Request ids created from the plan.")}).optional().describe("offer-handoff details.")}).refine(e=>"send"===e.operation?!!e.type:"note"===e.operation?"string"==typeof e.text&&e.text.trim().length>0:"reply"===e.operation?!!e.messageCode:"list"===e.operation||("get"===e.operation?!!e.messageCode:"offer-handoff"===e.operation||("record-decision"===e.operation?!!e.decision:"brief"===e.operation?!!e.requestId&&Array.isArray(e.sections)&&e.sections.length>0:"withdraw"===e.operation&&!!e.messageCode)),{message:"Invalid operation data: send requires type, note requires text, reply requires messageCode, get requires messageCode, record-decision requires decision, brief requires requestId and a non-empty sections array, withdraw requires messageCode"}),exports.CommitToViewedSessionSchema=r.z.object({message:r.z.coerce.string().min(1).describe("The cleaned-up user message to post to the viewed session. Fix obvious dictation errors, add punctuation, but preserve the user's intent exactly.")}),exports.DirectiveOperationsSchema=r.z.object({operation:r.z.enum(["set","list","search-decisions"]).describe("set = propose standing guidance in the user's words (they adopt it); list = list the project's guidance; search-decisions = free-text search the project's decision + annotation record (ask-the-history)"),targetType:r.z.enum(["project","area","request"]).optional().describe('set: what the guidance is addressed to. "project" = the whole project; "area" = a work-area name (targetId); "request" = a specific request (targetId = its local id).'),targetId:r.z.string().optional().describe('set: the area name (for targetType "area") or request local id (for targetType "request"). Omit for project-wide guidance.'),body:r.z.string().max(2e3).optional().describe("set: the standing guidance itself: a preference that shapes future work in scope (required for set). Max 2000 chars."),note:r.z.string().optional().describe("set: optional short note about why this guidance exists."),userSaid:r.z.string().optional().describe("set (required): the user's own words stating this guidance, copied exactly from their message. The card quotes them; guidance the user did not state is proposed with guidance_propose instead."),revises:r.z.string().max(160).optional().describe('set: when the user\'s words change guidance that stands ("onion skins are fine now" against "no onions"), its key as list shows it (pr:...). Adopting then rewords that item in place; without it a second item is added beside the old one.'),status:r.z.string().optional().describe("list: optional status filter (active | dormant | archived)."),query:r.z.string().max(200).optional().describe("search-decisions: the search text, matched against recorded decisions (question + answer) and annotations (comment body + quoted text). Required for search-decisions."),limit:r.z.number().int().min(1).max(50).optional().describe("search-decisions: max matches per record type (default 10).")}),exports.McpConnectionsSchema=r.z.object({operation:r.z.enum(["list","propose"]).describe("list = the connections this project has; propose = register one, switched off, for the user to switch on."),name:r.z.string().optional().describe("propose: the connection name, lower case letters, digits and dashes. It becomes the tool prefix (name_tool)."),transport:r.z.enum(["repo","boxed","remote"]).optional().describe("propose: repo = a server in THIS repository, built from its Dockerfile and run in a confined box (the repository is mounted read-only at /work, /scratch is writable memory, the image ENTRYPOINT is what runs, rebuilt when its files change on main); boxed = a published npm package at an exact version, installed once and run in a box; remote = a hosted server reached by url."),dockerfile:r.z.string().optional().describe("propose repo: path of the Dockerfile inside the repository, e.g. mcp/server.Dockerfile."),context:r.z.string().optional().describe("propose repo: the docker build context directory inside the repository (default: the repository root)."),package:r.z.string().optional().describe("propose boxed: npm package with an exact version, e.g. @scope/server@1.2.3."),bin:r.z.string().optional().describe("propose boxed: the bin to run, the name under node_modules/.bin."),url:r.z.string().optional().describe("propose remote: the http(s) url."),environment:r.z.record(r.z.string()).optional().describe("propose: plain KEY=value configuration (headers for remote). Never put a secret here; name it in secretKeys instead."),secretKeys:r.z.array(r.z.string()).optional().describe("propose: names of environment variables (or headers) whose VALUES are secret. The user sets the values on the machine that runs the work; they never reach the database.")}),exports.AnswerQuestionSchema=r.z.object({messageCode:r.z.string().min(1).describe("The code in square brackets from <waiting_on_you>, e.g. Q-51209THD-005."),optionId:r.z.string().optional().describe("The id of the option the user picked, when the question has options."),text:r.z.string().optional().describe("The user's answer in their own words, cleaned up, when the question has no options or they said something the options do not cover.")})},19567(e,exports,t){exports.Server=exports.SSEServerTransport=exports.StdioServerTransport=void 0
5212
+ exports.TestingProcessCreateSchema=r.z.object({name:r.z.string().min(1).describe("Name for the test process"),environmentId:r.z.string().optional().describe("ID of the environment (confined runtime) this process runs in. Get it from environment_list or environment_setup."),description:r.z.string().optional().describe("Description of what this process verifies"),scope:r.z.string().optional().describe('LABEL ONLY -- a monorepo path prefix recorded for display (e.g. "packages/api/"). It does NOT affect when the process runs; use pathGlobs for that.'),pathGlobs:r.z.array(r.z.string()).optional().describe('Which changes this process runs on: glob patterns matched against the changed files (e.g. ["sites/**"], ["src/**", "*.md"]). THIS is what limits when the process runs. Omit it and the process runs on EVERY change, which is the right choice only for a check that is cheap and universal.'),areas:r.z.array(r.z.string()).optional().describe('Work-area labels this process covers (e.g. ["graphics/opengl"]). An alternative to pathGlobs for work that is not file-shaped; a process applies if EITHER dimension matches.'),setup:r.z.array(r.z.string()).optional().describe('Commands to prepare the environment (e.g., ["ln -s ../../node_modules node_modules"])'),commands:r.z.array(f).min(1).describe("Test commands to execute in order"),verifyAcceptanceCriteria:r.z.boolean().optional().describe("When true, auto-tester verifies request acceptance criteria after running commands"),fingerprint:r.z.array(r.z.string()).optional().describe('Files whose content invalidates setup. Setup commands re-run only when these files change between runs (e.g. ["package-lock.json"], ["Cargo.lock"], ["uv.lock", "poetry.lock"]). Polyglot repos list multiple. Omit / empty list = always re-run setup (existing behavior).'),poolSize:r.z.number().int().min(1).max(8).optional().describe("Number of warm test worktrees in the pool. Default 2. Set to 1 for languages with very large build artifacts (Rust target/, C++ build/). Higher values allow more parallel test runs at the cost of disk."),runtimeKept:r.z.string().optional().describe('Only when the user chose to run the check on a runtime other than the lowest one the project declares: that runtime and version, e.g. "python 3.12". Without it, a check whose environment runs another version than the declared minimum is not registered.')}),exports.TestingProcessDeleteSchema=r.z.object({processId:r.z.string().describe("Test process ID to delete")}),exports.EnvironmentListSchema=r.z.object({}),exports.ResultsSchema=r.z.object({operation:r.z.enum(["record","fetch","pin","list","read"]),path:r.z.string().optional().describe("record: the file, inside the project (not /tmp); your worktree first, then the main project. Bulk that must not reach a commit can be written by a command under `.alchemy_lab/scratch/` (gitignored)."),name:r.z.string().optional().describe("record: the name a person uses for it (defaults to the filename). list: show every version recorded under this name, newest first."),summary:r.z.record(r.z.any()).optional().describe('record (required): the small facts a reader wants instead of the bytes, e.g. {maxStress: 412, unit: "MPa", converged: true}.'),box:r.z.string().optional().describe("An experiment handle (exp_...). fetch: put the bytes inside that sealed box under its /scratch (a box cannot see the host scratch; omit for host work, which is faster). record: the result lives inside that box and is streamed out and stored in one step (a box cannot write your worktree)."),mediaType:r.z.string().optional().describe("record: MIME type, when you know it."),inputs:r.z.array(r.z.string()).optional().describe("record: addresses of the results this one was made from (provenance)."),note:r.z.string().optional().describe("record: one line on how it was produced."),address:r.z.string().optional().describe("pin / read / fetch: the full 64-character address the record returned (a unique prefix works too)."),at:r.z.string().optional().describe("pin: the pointer path in your worktree (.result.json is added). fetch: where the bytes land, under `.alchemy_lab/scratch/` on the host or the box's `/scratch`. Defaults to results/<name>."),comment:r.z.string().optional().describe("pin: the commit comment for the pointer file."),offset:r.z.number().optional().describe("read: line to start at (default 0)."),limit:r.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:r.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ResultInspectSchema=r.z.object({operation:r.z.enum(["list","read"]),name:r.z.string().optional().describe("list: show every version recorded under this name, newest first."),address:r.z.string().optional().describe("read: the full 64-character address of the result."),offset:r.z.number().optional().describe("read: line to start at (default 0)."),limit:r.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:r.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ExperimentStartSchema=r.z.object({env:r.z.string().optional().describe("Environment by name (from environment_list), one whose image has the toolchain you need."),environmentId:r.z.string().optional().describe("Environment by id (alternative to `env`). The primary env is used if neither is given."),size:r.z.enum(["light","standard","heavy"]).optional().describe("light: 2 cores (lint, a typecheck, one test file). standard (default): a share of the free cores (a suite, a build). heavy: every free core but 2, only for work that uses them all (a CFD solve, meshing, a large parallel build). A size you do not hold opens another box."),confirmWorkspace:r.z.boolean().optional().describe('Required for env "workspace" (the live host): the task genuinely needs the real machine. Building or trying out the app does not.')}),exports.ExperimentStopSchema=r.z.object({handle:r.z.string().describe("The experiment handle to tear down.")}),exports.ExperimentListSchema=r.z.object({}),exports.ReadSiteSchema=r.z.object({url:r.z.string()}),exports.DriveSiteSchema=r.z.object({url:r.z.string().describe("The running product, e.g. the environment's published URL or http://localhost:5173."),steps:r.z.array(r.z.object({action:r.z.enum(["click","clickat","type","press","move","scroll","wait","media"]).describe('click (selector) \xb7 clickat (x,y) \xb7 type (selector + text) \xb7 press (key) \xb7 move (x,y) \xb7 scroll (dy) \xb7 wait (ms, max 5000) \xb7 media ("print" shows the printout, "screen" switches back)'),selector:r.z.string().optional().describe('Playwright selector, e.g. "text=Build" or "#start".'),text:r.z.string().optional(),key:r.z.string().optional().describe('e.g. "Enter"'),x:r.z.number().optional(),y:r.z.number().optional(),dy:r.z.number().optional(),ms:r.z.number().optional(),media:r.z.enum(["print","screen"]).optional()})).min(1).max(20),shots:r.z.enum(["each","final"]).optional().describe('"each" screenshots every step; default the final state only.'),session:r.z.string().optional().describe("Keeps the page open: the next call with this name continues where this one stopped. Idle sessions close after 10 minutes."),close:r.z.boolean().optional().describe("Close the named session after this call."),width:r.z.number().optional().describe("Viewport width for a new session (default 1280; 390 is a phone).")}),exports.CaptureSiteSchema=r.z.object({url:r.z.string(),fullPage:r.z.boolean().optional().describe("The whole scrollable page instead of the viewport."),width:r.z.number().optional().describe("Viewport width (default 1280; 390 is a phone).")}),exports.PreviewSiteSchema=r.z.object({dir:r.z.string().describe('Repo-relative, e.g. "dist" or "." for the top of the repository.'),requestId:r.z.string().regex(/^req\d{6}$/).optional().describe("Serve from this unmerged request's working copy instead of main."),name:r.z.string().optional().describe("Preview handle (default: a slug of dir); reusing one replaces that preview."),label:r.z.string().optional()}),exports.FetchAssetSchema=r.z.object({url:r.z.string().describe("The http(s) URL of the image or font to download."),destPath:r.z.string().describe('Where to write it, repo-relative (e.g. "sites/acme/assets/hero.jpg"). Must stay inside the working tree; parent directories are created.')}),exports.RequestVisualFeedbackSchema=r.z.object({box:r.z.string().optional().describe("The experiment handle (from experiment_start, on a screenshot-capable env) to render in / read from. The box must stay held until this returns."),route:r.z.string().optional().describe('With `box`: the route I render + screenshot FOR YOU (e.g. "/" or "/settings"). I serve `serveDir` and screenshot it -- you do NOT hand-roll serve/screenshot. This is the easiest path.'),script:r.z.string().optional().describe('With `box`, for FULL control: a Playwright snippet (the body of `async (page, baseUrl) => { ... }`). I serve the app, launch headless Chromium, and run your script -- navigate (page.goto(baseUrl + "/x")), click, fill, scroll, waitForSelector, etc. -- then screenshot the final page state. Reliable: you write only the interaction, no serve/launch/flags. Use this to reach a state that needs interaction (open a menu, log in, submit a form).'),fullPage:r.z.boolean().optional().describe("Capture the entire scrollable page instead of just the viewport."),serveDir:r.z.string().optional().describe("With `box`+`route`: the static dir to serve (default /work). For a built app, build first via run(), then point this at the build output (e.g. /work/dist)."),path:r.z.string().optional().describe("Alternative to route: a screenshot you ALREADY wrote inside the box (e.g. /tmp/shot.png); I read it out."),assetFilename:r.z.string().optional().describe("Alternative to box: the filename of an already-uploaded screenshot asset to annotate."),prompt:r.z.string().optional().describe("What you want the human to look at or mark up (shown with the request)."),timeoutMs:r.z.number().optional().describe("How long to wait for the human, in ms (default ~10 min).")}),exports.AskUserVisualSchema=r.z.object({question:r.z.string().describe('The question for the USER about the marked region (e.g. "Should this button use the primary color?").'),imagePath:r.z.string().optional().describe("Repo-relative path to an image you already produced (png/jpg/gif/webp), e.g. a screenshot you wrote. Provide this OR previewName."),previewName:r.z.string().optional().describe("Name of a registered live preview (a `live:` ref); I freeze it into a snapshot image for you. Provide this OR imagePath."),box:r.z.string().optional().describe('The region the question is about: "x,y,w,h" in normalized 0..1 image coordinates, origin top-left (e.g. "0.62,0.08,0.3,0.12"). Omit to ask about the whole image.'),options:r.z.array(r.z.string()).optional().describe("Optional answer choices (labels). Omit for a free-text answer."),context:r.z.string().optional().describe("Extra context shown with the question in the inbox (why you are asking, what you will do with the answer)."),priority:r.z.enum(["low","normal","high"]).optional().describe('Question priority; "high" always reaches the user regardless of autonomy dial.')}),exports.EnvironmentGetSchema=r.z.object({environmentId:r.z.string().describe("Environment ID to retrieve")}),exports.EnvironmentProvisionSchema=r.z.object({environmentId:r.z.string(),why:r.z.string().optional().describe("Why provision now (shown on the approval).")}),exports.EnvironmentTeardownSchema=r.z.object({environmentId:r.z.string()}),exports.EnvironmentStatusSchema=r.z.object({environmentId:r.z.string()}),exports.EnvironmentVerifyStackSchema=r.z.object({environmentId:r.z.string()})
5213
+ const m=r.z.object({image:r.z.string().optional().describe("Container image carrying the project toolchain/deps, e.g. node:20-alpine, python:3.12-slim, golang:1.22, ruby:3.3"),install:r.z.string().optional().describe("Deps install command -- Node-only sugar that populates a /work/node_modules cache volume, run with registry egress before the confined gate, e.g. npm ci. Non-Node runtimes use `artifacts` instead (a cache volume + prepare command: .venv/uv sync, cargo, go, vendor/bundle). Omit if the project has no deps."),composeFile:r.z.string().optional().describe("Path to the project docker-compose file for its services. Omit if no services."),shellInit:r.z.string().optional().describe("Shell preamble run before EVERY command in the box, e.g. '. /usr/lib/openfoam/openfoam2312/etc/bashrc'. REQUIRED for a toolchain that publishes itself through a script rather than the image PATH (OpenFOAM etc/bashrc, oneAPI setvars.sh, conda activate, ROS setup.bash): `docker exec` runs no ENTRYPOINT and reads no login profile, so without this every command gets a bare PATH. Put the preamble HERE, never inside the smoke command -- a smoke command carrying its own preamble proves the preamble and not the environment, and the next agent gets nothing."),connEnv:r.z.record(r.z.string()).optional().describe("Connection env injected into the gate (e.g. DATABASE_URL) using SERVICE NAMES + internal ports, never localhost/host ports."),seed:r.z.array(r.z.string()).optional().describe("Optional commands run after services are up (e.g. migrations)."),artifacts:r.z.array(r.z.object({path:r.z.string().describe("Mount target for the writable, populated volume: relative to /work (e.g. node_modules, .venv, vendor/bundle) or absolute (e.g. /cache/cargo)."),prepare:r.z.string().optional().describe("Command that populates it -- the one phase with registry egress (e.g. npm ci, uv sync, cargo fetch). Omit for a writable empty volume."),manifests:r.z.array(r.z.string()).optional().describe("Lockfiles (relative to /work) whose content hash keys the cache; list them so the volume is reused across runs and only rebuilt when deps change (e.g. uv.lock, Cargo.lock). Omit => rebuilt every run."),env:r.z.record(r.z.string()).optional().describe('Env applied during prepare AND every run, e.g. { CARGO_HOME: "/cache/cargo" } for caches outside /work.')})).optional().describe("Per-runtime build/dependency artifacts: writable volumes populated by a prepare command, generalizing `install` beyond node_modules. Use for Python (.venv), Rust (target + $CARGO_HOME), Go, Ruby (vendor/bundle), etc. `install` is sugar for a single node_modules artifact; for any non-Node runtime use artifacts (install would write to node_modules, the wrong place)."),confinement:r.z.enum(["confined","egress-allowlist","privileged","unsafe","host","pure-host"]).optional().describe("Confinement level. Default 'confined' (fully sealed Docker; add egressAllowlist hosts to open exactly those -- stored as 'egress-allowlist'). privileged/unsafe relax the container. 'host' runs on the host machine (no Docker) inside an OS sandbox (Seatbelt/bubblewrap) for toolchains Docker can't run. 'pure-host' is the built-in workspace env (the live worktree) -- do not create new envs with it. Merge gating is independent (the Sign-off setting)."),commandGate:r.z.array(r.z.enum(["policy","agent","human"])).optional().describe("Gates an experiment command passes before it runs, in escalation order policy -> agent -> human. Empty = run freely; escalating past the last gate blocks (fail closed). policy = deterministic allowlist/deny-floor; agent = isolated LLM judge (allow or escalate only); human = ask. Registered commands are pre-approved. Defaults by level ([] for confined/egress-allowlist, ['human'] for privileged/unsafe)."),worktreeTarget:r.z.enum(["ephemeral","live"]).optional().describe("Worktree the env runs against: 'ephemeral' (a disposable copy at HEAD, default) or 'live' (the project's live working dir, read-write). The built-in workspace env is 'live'."),egressAllowlist:r.z.array(r.z.string()).optional().describe("Hosts the box may reach at the 'egress-allowlist' level (exact name or subdomain, e.g. registry.npmjs.org). Ignored at other levels."),addCaps:r.z.array(r.z.string()).optional().describe("Linux capabilities to add at the 'privileged' level (on top of cap-drop ALL), e.g. SYS_PTRACE for debuggers, IPC_LOCK for memlock. Ignored at other levels."),sysctls:r.z.record(r.z.string()).optional().describe("Namespaced sysctls to set at the 'privileged' level, e.g. net.ipv4.ip_local_port_range. Host-wide sysctls (vm.max_map_count) are NOT settable per-container."),runAsRoot:r.z.boolean().optional().describe("Run the box as root (privileged / unsafe levels). Needed for some caps + the docker socket."),dockerSocket:r.z.boolean().optional().describe("Mount the host docker socket for testcontainers-style tests. WARNING: full host control over the docker daemon. Only honored at the 'unsafe' level."),persistentVolumes:r.z.array(r.z.object({name:r.z.string(),target:r.z.string(),service:r.z.string().optional()})).optional().describe("Data volumes that survive between runs (keyed to this environment; off by default since experiments are ephemeral). Set service to mount into a compose service (e.g. a DB data dir), or omit it to mount into the box (e.g. a SQLite dir)."),resources:r.z.object({memory:r.z.string().optional().describe("Memory cap, e.g. '4g' (default 2g)."),cpus:r.z.number().optional().describe("A fixed CPU count. Usually leave it unset and set size instead."),size:r.z.enum(["light","standard","heavy"]).optional().describe("What this environment's boxes and checks are for, which sets their cores from what is free: light (2 cores: lint, small checks), standard (the default: a share of the free cores, for test suites and builds), heavy (every free core but 2: CFD or FEM solves, meshing, large parallel builds). An agent can ask for another size per box."),pidsLimit:r.z.number().optional().describe("Max processes (default 512)."),scratchMb:r.z.number().optional().describe("Size in MB of an in-memory /scratch, used only when the image cannot take the disk-backed one (default 512). /scratch is normally on disk and needs no size."),shmMb:r.z.number().optional().describe("/dev/shm size in MB -- bump for headless browsers (Chrome crashes on the 64m default).")}).optional().describe("Per-environment resource caps that override the confined defaults.")}),g=function e(t){const n={...t._def,description:void 0}
5214
+ if(t instanceof r.z.ZodObject){const r=Object.fromEntries(Object.entries(t.shape).map(([t,r])=>[t,e(r)]))
5215
+ n.shape=()=>r}else t instanceof r.z.ZodOptional||t instanceof r.z.ZodNullable||t instanceof r.z.ZodDefault?n.innerType=e(n.innerType):t instanceof r.z.ZodArray?n.type=e(n.type):t instanceof r.z.ZodRecord&&(n.valueType=e(n.valueType))
5216
+ return new t.constructor(n)}(m),y='Start from a built-in preset; anything in config overrides it. "office": python-docx, openpyxl, python-pptx, pandoc, LibreOffice and segno/zbarimg (QR codes) in a sealed box, with `render-pdf FILE` to turn Word, Excel, PowerPoint, Markdown or HTML into PDF. Its image is built locally on first use (a few minutes). Pass the same preset (and config) to environment_verify and environment_create.',w="Save without a passing environment_verify, only for a config that cannot be verified from here (it needs credentials, external services or hardware the confined verifier lacks). Requires skipVerifyReason; the skip is logged."
5217
+ exports.EnvironmentCreateSchema=r.z.object({name:r.z.string().min(1).describe("Name for the environment, e.g. node + postgres"),description:r.z.string().optional().describe("What this environment provides"),scope:r.z.string().optional().describe("Subproject path prefix for monorepos (e.g. packages/api/). Omit for the whole repo."),preset:r.z.enum(["office"]).optional().describe(y),config:g.optional().describe("The exact config (after the preset) that passed environment_verify in this session, unless skipVerify; its fields are described on environment_verify. Optional with a preset."),isPrimary:r.z.boolean().optional().describe("Make this the project primary environment (default for new test processes). The first environment is primary automatically."),skipVerify:r.z.boolean().optional().describe(w),skipVerifyReason:r.z.string().optional().describe("Why this config cannot be verified (required with skipVerify: true; recorded in the log).")}),exports.EnvironmentUpdateSchema=r.z.object({environmentId:r.z.string(),name:r.z.string().min(1).optional(),description:r.z.string().optional(),scope:r.z.string().optional(),config:g.optional().describe("Replaces the config wholesale: the exact config that passed environment_verify in this session, unless skipVerify (fields described on environment_verify). Metadata-only updates need no verify."),isPrimary:r.z.boolean().optional().describe("Make this the project primary environment"),skipVerify:r.z.boolean().optional().describe(w),skipVerifyReason:r.z.string().optional().describe("Why this config cannot be verified (required with skipVerify: true; recorded in the log).")}),exports.EnvironmentDeleteSchema=r.z.object({environmentId:r.z.string().describe("Environment ID to delete")}),exports.EnvironmentVerifySchema=r.z.object({preset:r.z.enum(["office"]).optional().describe(y),config:m.optional().describe("The proposed environment config to verify (image, install, services, connection). Optional with a preset."),smoke:r.z.string().optional().describe("Optional smoke command run confined after the environment is up (default: true). Use a connection probe to confirm services are reachable.")}),exports.EnvironmentSetupSchema=r.z.object({name:r.z.string().max(60).optional().describe("A few words the user reads as the setup request's title (e.g. 'Node 20 for syntax checks')."),context:r.z.string().optional().describe("What you need and why, for the setup-architect (e.g. 'Node 20 to run node --check on standalone .js files; no deps').")}),exports.TestingProcessUpdateSchema=r.z.object({processId:r.z.string(),environmentId:r.z.string().optional(),name:r.z.string().min(1).optional(),description:r.z.string().optional(),scope:r.z.string().optional().describe("A display label only; pathGlobs decides when the process runs."),pathGlobs:r.z.array(r.z.string()).optional().describe('Globs matched against changed files (e.g. ["src/**", "*.md"]): the changes this process runs on. Replaces the list; [] means every change.'),areas:r.z.array(r.z.string()).optional().describe("Work-area labels this process covers; it applies when pathGlobs or areas match."),isActive:r.z.boolean().optional(),setup:r.z.array(r.z.string()).optional().describe("Replaces the setup commands."),commands:r.z.array(f).optional().describe("Replaces the test commands."),verifyAcceptanceCriteria:r.z.boolean().optional().describe("The auto-tester also verifies the request's acceptance criteria."),fingerprint:r.z.array(r.z.string()).optional().describe("Files whose content invalidates setup."),poolSize:r.z.number().int().min(1).max(8).optional().describe("Warm test worktrees in the pool."),runtimeKept:r.z.string().optional().describe('With environmentId: only when the user chose a runtime other than the lowest one the project declares, e.g. "python 3.12".')}),exports.TestsSchema=r.z.object({operation:r.z.enum(["list","setup","run","switch-off","switch-on"]),process:r.z.string().optional().describe("run, switch-off, switch-on: one test process by name or id (run: omit for all active ones)."),userSaid:r.z.string().max(600).optional().describe("switch-off / switch-on: the user's words asking for it, copied exactly."),name:r.z.string().optional().describe("run: one test command by name; omit for all required commands."),ask:r.z.string().max(1500).optional().describe("setup, required outside a request: what the user asked the check to run, in their words; the setup architect registers exactly that.")}),exports.ExecuteTestCommandSchema=r.z.object({processId:r.z.string().describe("Test process ID containing the command"),commandName:r.z.string().describe('Name of the test command to run, or "setup" to run all setup commands in sequence')}),exports.RunSchema=r.z.object({command:r.z.string().optional().describe("A shell command, run in the box (pass box or env)."),name:r.z.string().optional().describe("A saved command by name, instead of command."),box:r.z.string().optional().describe("An experiment handle from experiment_start."),env:r.z.string().optional().describe("Instead of box: an environment by name; its box is started (or your open one reused) and the result gives the handle."),size:r.z.enum(["light","standard","heavy"]).optional().describe("With env: the box size (see experiment_start)."),args:r.z.string().optional(),grep:r.z.string().optional().describe("Filter output through grep -i with this pattern."),timeout:r.z.number().optional().describe("Seconds."),why:r.z.string().optional().describe("One short sentence, shown on the approval card when the command needs the user's approval."),cwd:r.z.string().optional().describe('A subdirectory for an ad-hoc command (e.g. "apps/web"); a command starting with `cd` is refused in favour of this.')}),exports.CommandsSchema=r.z.object({operation:r.z.enum(["run","list","remove","register","setup"]).default("run").describe('list, register, remove or setup ("run" is the run tool\'s job). register suggests commands; prefer one general project-wide check over per-file variants.'),name:r.z.string().optional().describe("remove: the command name."),args:r.z.string().optional(),grep:r.z.string().optional(),command:r.z.string().optional(),timeout:r.z.number().optional(),cwd:r.z.string().optional(),context:r.z.string().optional().describe("setup: what you think the user needs, a hint for the architect."),commands:r.z.array(r.z.object({name:r.z.string().min(1).describe('e.g. "test:web"'),command:r.z.string().min(1),description:r.z.string().min(1).describe("What this command verifies."),cwd:r.z.string().optional().describe("Relative to the project root (monorepo subdirs); omit for the root."),timeout:r.z.number().optional().describe("Seconds (default 60).")})).optional().describe("register: the commands to suggest.")}),exports.ReportTestResultsSchema=r.z.object({status:r.z.enum(["passed","failed","error"]).describe("Overall result"),testRunId:r.z.string().optional().describe("The test run this verdict belongs to (given in your instructions). Strongly preferred: without it the latest run is guessed, which misattributes results when several testers run concurrently."),results:r.z.object({commands:r.z.array(r.z.object({name:r.z.string().describe("Command name"),status:r.z.enum(["passed","failed","skipped","error"]).describe("Command result"),output:r.z.string().optional().describe("Relevant output snippet"),diagnosis:r.z.string().optional().describe("Why it failed, if applicable")})).describe("Per-command results"),summary:r.z.string().describe("Brief overall summary"),acceptanceCriteria:r.z.array(r.z.object({criterion:r.z.string().describe("The acceptance criterion text"),status:r.z.enum(["verified","unverified","unclear"]),evidence:r.z.string().describe("How verified, or why not")})).optional().describe("AC verification results, if applicable"),faultClass:r.z.enum(["fail_code","fail_evidence","fail_environment","fail_process"]).optional().describe("On a FAILED verdict, WHY it failed so the gate routes to the right actor: 'fail_code' (the change is wrong), 'fail_evidence' (the run did not prove a criterion the coder could have proven: attach/adjust verify commands), or 'fail_environment' (a required tool/runtime is MISSING from the confined box, e.g. 'node: not found': the coder cannot fix this; the environment must be extended). Use 'fail_environment' ONLY when the evidence shows a command/runtime absent from the box, never for a genuine defect or a fixable evidence gap. 'fail_process' is for a test process whose OWN command names a file the repository does not have (a deleted script): nobody's code is wrong and extending the box cannot help; the process itself must be fixed or turned off."),missingCapabilities:r.z.array(r.z.string()).optional().describe("With faultClass 'fail_environment', the binaries/runtimes the box lacks (e.g. ['node']) so the environment can be extended to provide them.")}).describe("Structured test results")}).superRefine((e,t)=>{"fail_environment"!==e.results?.faultClass||e.results.missingCapabilities&&e.results.missingCapabilities.length>0||t.addIssue({code:r.z.ZodIssueCode.custom,path:["results","missingCapabilities"],message:"faultClass 'fail_environment' requires results.missingCapabilities: name the absent binaries/runtimes (the deterministic capability probe in your evidence lists them, e.g. ABSENT from the box: node). Without the names, the environment cannot be extended and the request strands."})}),exports.OpeningBriefSectionSchema=r.z.object({id:r.z.string().min(1).describe('Stable id, unique within the brief (e.g. "date-format"). "testing-mode" is reserved for the server.'),question:r.z.string().min(1).describe("The choice, as one plain question the user can answer without your session."),context:r.z.string().min(1).describe("What the choice shapes, the consequence of each option, and why your default is recommended, citing the request and its sources as they state them; where they answer the choice, recommend their answer."),options:r.z.array(r.z.object({id:r.z.string().min(1),label:r.z.string().min(1),description:r.z.string().optional().describe("What it means and its tradeoff, in 1-2 plain sentences.")})).min(2).describe("At least two genuine alternatives."),default:r.z.string().min(1).describe("The option id you are proceeding on now."),criteria:r.z.array(r.z.string().min(1)).optional().describe('Keys of the criteria whose wording this choice decides (e.g. ["2b"]), worded for your default. If the user picks another answer, each is marked for rewording and is not proven until reworded.')}).superRefine((e,t)=>{e.options.some(t=>t.id===e.default)||t.addIssue({code:r.z.ZodIssueCode.custom,path:["default"],message:`default '${e.default}' names no option in this section (option ids: ${e.options.map(e=>e.id).join(", ")})`}),"testing-mode"===e.id&&t.addIssue({code:r.z.ZodIssueCode.custom,path:["id"],message:"section id 'testing-mode' is reserved: the server composes the testing section when the project's gate needs it -- remove it from your sections"})}),exports.SessionMessageOperationsSchema=r.z.object({operation:r.z.enum(["send","note","reply","list","get","offer-handoff","record-decision","brief","withdraw"]),milestone:r.z.boolean().optional().describe("note: a real boundary (it works, the approach changed, the goal is met), a few per request at most; one quiet line in the user's stream."),withdrawReason:r.z.string().optional().describe("withdraw: why the answer is no longer needed."),type:r.z.enum(["question","report"]).optional().describe("Required for send."),toSessionId:r.z.string().optional().describe('Target session id, or "parent". Omit to ask the user (a broadcast).'),questionType:r.z.enum(["free-form","alternatives"]).optional().describe("Required for a question."),questionContent:r.z.object({question:r.z.string().min(1),context:r.z.preprocess(e=>null!==e&&"object"==typeof e?JSON.stringify(e):e,r.z.string().optional()).optional().describe("What the user needs to answer without your session: what you found, why it matters, the consequence of each answer, why you recommend what you do. No paths or identifiers; output you ask them to judge goes in a fenced code block, as printed."),options:r.z.array(r.z.object({id:r.z.string(),label:r.z.string(),description:r.z.string().optional().describe("What it means and its tradeoff, in 1-2 plain sentences. Fill it on every option."),recommended:r.z.boolean().optional().describe("Your advice, highlighted on the card; not what happens if nobody answers (that is defaultOption). Not on a question that names criteria. Never an option that makes up what the user has not given (estimated numbers, sample values, invented names): where it is missing, the way forward leaves it visibly empty or marked unknown."),effect:r.z.enum(["choose","send-back","not-yet"]).optional().describe('"choose" (default): records the pick for you. "send-back": asks for a change to the work; on a request waiting for the user\'s sign-off the pick itself sends it back to you (do not call reopen). "not-yet": only with [user-input] criteria; they stay owed.')})).optional().describe("Each a genuine alternative, complete in one tap (one needing a second answer is two options). Recommended option first. When the way the user asked cannot work, include one that reaches their goal another way."),allowMultiple:r.z.boolean().optional(),fields:r.z.array(r.z.object({id:r.z.string().min(1),label:r.z.string().min(1),placeholder:r.z.string().optional()})).min(1).optional().describe('free-form: one labelled input per value (a name, an email); the answer arrives as "Label: value" lines. Never ask for passwords or keys: answers are on the record.'),defaultOption:r.z.string().optional().describe("What happens if nobody answers before it expires (the option id, or the text for free-form); the asker then resumes. It happens without the user, so it must be safe: when options differ in risk (values silently changed, data loss, made-up data, anything hard to undo), the default is the safe one even if you recommend another, and the context says so."),onExpiry:r.z.enum(["assume-default","escalate"]).optional().describe('"assume-default" (default with a defaultOption): resolve to it and resume the asker. "escalate": leave it pending for the user (on Self-driving only priority "high" reaches them).'),steer:r.z.enum(["what","how"]).optional().describe('Tag every question: "what" shapes the work product (spec, wording, UX, scope); "how" steers the process (testing, sequencing, completion). Most "how" choices are yours to make, not to ask.')}).optional().describe("Required for a question."),priority:r.z.enum(["low","normal","high"]).optional().describe('"high": blocked with real stakes; reaches the user at once, past the question gate. "low" with a defaultOption: under \'balanced\', batched to the next checkpoint (request review or plan handoff) without holding; you proceed on your default.'),expiresInHours:r.z.number().optional(),userSaid:r.z.string().min(8).max(600).optional().describe('question: when the user asked you to ask them (a card, options, "ask me first"), their words copied exactly. Checked against what they wrote; the question then reaches them whatever the Autonomy setting.'),replaces:r.z.array(r.z.string().regex(/^Q-[A-Za-z0-9]+-\d+$/)).max(10).optional().describe("question: codes of your open questions on the same request that this one supersedes; they close when it posts. With a question already open on the request, pass this or keepsOpen."),keepsOpen:r.z.boolean().optional().describe("question: true when this asks something different from your open questions on the request, so they stay open."),forceAsk:r.z.boolean().optional().describe("question: re-ask something the decision record already answers, only because circumstances changed; say what changed in questionContent.context."),status:r.z.enum(["success","partial","failure","blocked"]).optional().describe("Required for a report."),blockedOn:r.z.string().optional().describe('Required with status "blocked" on a request you own: "decision" (a question to your parent, escalating to the user) or a request id like "req000123" (declares a wait; you are told when it settles).'),criteria:r.z.array(r.z.string().min(1)).max(10).optional().describe('question: keys of the [user-input] criteria you ask for (e.g. ["1a"]); only such a question counts as asking them. It goes to the user with no default and no recommended option, and the answer is their proof unless the picked option is "not-yet". With a "not-yet" option you build with a placeholder meanwhile (Self-driving answers "not yet" at once); without one you wait for the answer at every Autonomy level. The user\'s check of finished work is asked on the card at the end, not here.'),attention:r.z.enum(["fyi","glance","act"]).optional().describe("'fyi' (logged, no badge), 'glance' (look soon), 'act' (the user must act). Default from status: failure/blocked act, partial glance, success fyi. It does not decide who is woken."),reportKind:r.z.enum(["milestone","reply"]).optional().describe("'milestone': narration at a boundary. 'reply': a reply to the user's inbox message."),summary:r.z.string().optional().describe("Required for a report. Outcome first, paths and identifiers in details. It shows alone in the stream, often hours later: name the subject first, never open with Also or Then. Reports to agents can be technical."),details:r.z.string().optional().describe("With blockedOn \"decision\" these become the question's context, and the user's card if your parent cannot decide: plain words, no tool names or call syntax; explain a refusal you got instead of pasting it."),ruleVerdicts:r.z.array(r.z.object({rule:r.z.string().min(1).max(80).describe("The rule id as your spawn prompt lists it, without brackets."),verdict:r.z.enum(["held","broken","not_applicable"]),note:r.z.string().max(300).optional().describe("broken: what breaks it and where (file:line). Omit otherwise.")})).max(40).optional().describe('report, Rules reviewer only: a verdict on every rule you were given. Only "broken" is a finding.'),finish:r.z.boolean().optional().describe("Release your request associations after the report."),silent:r.z.boolean().optional().describe("Record the report for the coordinator to drain without waking it (fan-out research workers)."),relatesTo:r.z.string().regex(/^[IQ]-[A-Za-z0-9]+-\d+$/,"relatesTo must be an instruction code (I-...) or question code (Q-...)").optional().describe("An instruction code (I-...) this relates to; for record-decision, a question code (Q-...) it resolves or supersedes."),decision:r.z.string().optional().describe("record-decision (required): the one-line call, recorded as the answer."),why:r.z.string().optional().describe("record-decision: the reasoning."),context:r.z.string().optional().describe("record-decision: what the choice was about, recorded as the question (defaults to decision)."),requestId:r.z.string().optional().describe('The request this is about (e.g. "req000007"), so it shows on that request. note: defaults to your session\'s request. report and record-decision: pass it when about one request, or it is filed only at the project. brief: required.'),area:r.z.string().optional().describe('record-decision: a work area (e.g. "graphics/opengl") when the call is about that area. Ignored if requestId is set.'),sections:r.z.array(exports.OpeningBriefSectionSchema).min(1).optional().describe("brief (required): your real design choices, each with at least two options, a default and context."),messageCode:r.z.string().optional().describe("Required for reply, get and withdraw."),sectionAnswers:r.z.record(r.z.string(),r.z.string().nullable()).optional().describe("reply to an opening brief: { sectionId: optionId } for sections the user answered, changed or confirmed, with quote (their words); null when a section no longer applies; 'your-words' when no option says what they asked. Sections not named stay open. A changed choice acts on the work as it is now (finished work goes back to rework)."),quote:r.z.string().optional().describe("reply to a question: the user's words this answer records, copied exactly; it is then recorded as decided with the user, and refused if they never wrote them. Omit for your own decision. Options that are the user's own statement (\"I checked, it works\") only the user can pick."),text:r.z.string().optional().describe("reply: the answer to a free-form question. note (required): the note."),selectedOptions:r.z.array(r.z.string()).optional().describe("reply: the picked option ids."),notes:r.z.string().optional(),escalate:r.z.boolean().optional().describe("reply to a question asked of you that needs the user: a copy reaches the user with your recommendation (in notes); the asker gets their answer directly. Not for broadcasts."),list:r.z.object({sessionId:r.z.string().optional().describe("Defaults to your session."),messageType:r.z.enum(["question","report","instruction","all"]).default("all"),direction:r.z.enum(["incoming","outgoing","all"]).default("all"),status:r.z.enum(["pending","answered","expired","success","partial","failure","blocked","all"]).default("all"),readStatus:r.z.enum(["unread","read","acknowledged","all"]).default("all"),requestId:r.z.string().optional().describe("Only messages about this request. Listing another session's inbox defaults to the request you are on."),limit:r.z.number().min(1).max(100).default(50),offset:r.z.number().min(0).default(0)}).optional(),offerHandoff:r.z.object({summary:r.z.string().describe('One sentence: what the plan builds. The card is already titled "Plan ready"; do not start with that.'),requestIds:r.z.array(r.z.string()).describe("Request ids created from the plan.")}).optional()}).refine(e=>"send"===e.operation?!!e.type:"note"===e.operation?"string"==typeof e.text&&e.text.trim().length>0:"reply"===e.operation?!!e.messageCode:"list"===e.operation||("get"===e.operation?!!e.messageCode:"offer-handoff"===e.operation||("record-decision"===e.operation?!!e.decision:"brief"===e.operation?!!e.requestId&&Array.isArray(e.sections)&&e.sections.length>0:"withdraw"===e.operation&&!!e.messageCode)),{message:"Invalid operation data: send requires type, note requires text, reply requires messageCode, get requires messageCode, record-decision requires decision, brief requires requestId and a non-empty sections array, withdraw requires messageCode"}),exports.CommitToViewedSessionSchema=r.z.object({message:r.z.coerce.string().min(1).describe("The cleaned-up user message to post to the viewed session. Fix obvious dictation errors, add punctuation, but preserve the user's intent exactly.")}),exports.DirectiveOperationsSchema=r.z.object({operation:r.z.enum(["set","list","search-decisions"]).describe("set = propose standing guidance in the user's words (they adopt it); list = list the project's guidance; search-decisions = free-text search the project's decision + annotation record (ask-the-history)"),targetType:r.z.enum(["project","area","request"]).optional().describe('set: what the guidance is addressed to. "project" = the whole project; "area" = a work-area name (targetId); "request" = a specific request (targetId = its local id).'),targetId:r.z.string().optional().describe('set: the area name (for targetType "area") or request local id (for targetType "request"). Omit for project-wide guidance.'),body:r.z.string().max(2e3).optional().describe("set: the standing guidance itself: a preference that shapes future work in scope (required for set). Max 2000 chars."),note:r.z.string().optional().describe("set: optional short note about why this guidance exists."),userSaid:r.z.string().optional().describe("set (required): the user's own words stating this guidance, copied exactly from their message. The card quotes them; guidance the user did not state is proposed with guidance_propose instead."),revises:r.z.string().max(160).optional().describe('set: when the user\'s words change guidance that stands ("onion skins are fine now" against "no onions"), its key as list shows it (pr:...). Adopting then rewords that item in place; without it a second item is added beside the old one.'),status:r.z.string().optional().describe("list: optional status filter (active | dormant | archived)."),query:r.z.string().max(200).optional().describe("search-decisions: the search text, matched against recorded decisions (question + answer) and annotations (comment body + quoted text). Required for search-decisions."),limit:r.z.number().int().min(1).max(50).optional().describe("search-decisions: max matches per record type (default 10).")}),exports.McpConnectionsSchema=r.z.object({operation:r.z.enum(["list","propose"]).describe("list = the connections this project has; propose = register one, switched off, for the user to switch on."),name:r.z.string().optional().describe("propose: the connection name, lower case letters, digits and dashes. It becomes the tool prefix (name_tool)."),transport:r.z.enum(["repo","boxed","remote"]).optional().describe("propose: repo = a server in THIS repository, built from its Dockerfile and run in a confined box (the repository is mounted read-only at /work, /scratch is writable memory, the image ENTRYPOINT is what runs, rebuilt when its files change on main); boxed = a published npm package at an exact version, installed once and run in a box; remote = a hosted server reached by url."),dockerfile:r.z.string().optional().describe("propose repo: path of the Dockerfile inside the repository, e.g. mcp/server.Dockerfile."),context:r.z.string().optional().describe("propose repo: the docker build context directory inside the repository (default: the repository root)."),package:r.z.string().optional().describe("propose boxed: npm package with an exact version, e.g. @scope/server@1.2.3."),bin:r.z.string().optional().describe("propose boxed: the bin to run, the name under node_modules/.bin."),url:r.z.string().optional().describe("propose remote: the http(s) url."),environment:r.z.record(r.z.string()).optional().describe("propose: plain KEY=value configuration (headers for remote). Never put a secret here; name it in secretKeys instead."),secretKeys:r.z.array(r.z.string()).optional().describe("propose: names of environment variables (or headers) whose VALUES are secret. The user sets the values on the machine that runs the work; they never reach the database.")}),exports.AnswerQuestionSchema=r.z.object({messageCode:r.z.string().min(1).describe("The code in square brackets from <waiting_on_you>, e.g. Q-51209THD-005."),optionId:r.z.string().optional().describe("The id of the option the user picked, when the question has options."),text:r.z.string().optional().describe("The user's answer in their own words, cleaned up, when the question has no options or they said something the options do not cover.")})},19567(e,exports,t){exports.Server=exports.SSEServerTransport=exports.StdioServerTransport=void 0
5190
5218
  const r=t(66865)
5191
5219
  Object.defineProperty(exports,"Server",{enumerable:!0,get:function(){return r.Server}})
5192
5220
  const n=t(25802)
@@ -5388,24 +5416,24 @@ const r=t(11253),n=t(65681),o=t(86595),s=t(3895),i=t(26848),a=t(86153),c=t(64710
5388
5416
  exports.ClientFactory=class{static async createClient(e,g,y,w={}){(g=g.child({component:"client-factory"})).info("Creating API client with components")
5389
5417
  const b=w.tokenManager||new r.TokenManager(g,w.workingDir||process.cwd())
5390
5418
  await b.initialize()
5391
- const v=w.tokenSynchronizer||new i.ClientTokenSynchronizer(g),k=new s.AuthenticationManager(b,g,v),I=await k.getTokenString(),$=w.connectionStatusManager||new n.ConnectionStatusManager(e,g,y,{alchemyPath:w.alchemyPath,checkIntervalMs:w.offlineCheckIntervalMs,timeoutMs:w.timeout,retryCount:w.retryAttempts}),E=new c.ProjectClient(e,g,y,$,I||void 0,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5392
- v.registerClient(E),w.alchemyPath&&E.setAlchemyPath(w.alchemyPath)
5393
- const S=new f.TaskClient(e,g,y,E,$,I||void 0,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5394
- v.registerClient(S)
5395
- const x=new a.FileOperationsClient(e,g,y,E,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5396
- v.registerClient(x),g.info("RequestClient constructor received token: "+(I?"present":"missing"))
5397
- const _=new u.RequestClient(e,g,y,E,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5398
- v.registerClient(_),g.info("RequestClient post-constructor token: "+(_.getAuthToken()?"present":"missing"))
5399
- const C=new p.DocumentClient(e,g,y,E,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5419
+ const v=w.tokenSynchronizer||new i.ClientTokenSynchronizer(g),k=new s.AuthenticationManager(b,g,v),I=await k.getTokenString(),$=w.connectionStatusManager||new n.ConnectionStatusManager(e,g,y,{alchemyPath:w.alchemyPath,checkIntervalMs:w.offlineCheckIntervalMs,timeoutMs:w.timeout,retryCount:w.retryAttempts}),S=new c.ProjectClient(e,g,y,$,I||void 0,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5420
+ v.registerClient(S),w.alchemyPath&&S.setAlchemyPath(w.alchemyPath)
5421
+ const E=new f.TaskClient(e,g,y,S,$,I||void 0,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5422
+ v.registerClient(E)
5423
+ const _=new a.FileOperationsClient(e,g,y,S,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5424
+ v.registerClient(_),g.info("RequestClient constructor received token: "+(I?"present":"missing"))
5425
+ const x=new u.RequestClient(e,g,y,S,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5426
+ v.registerClient(x),g.info("RequestClient post-constructor token: "+(x.getAuthToken()?"present":"missing"))
5427
+ const C=new p.DocumentClient(e,g,y,S,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5400
5428
  v.registerClient(C)
5401
- const q=new m.ToolRecordClient(e,g,y,E,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5429
+ const q=new m.ToolRecordClient(e,g,y,S,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5402
5430
  v.registerClient(q)
5403
- const T=new d.ProxyClient(e,g,y,E,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5431
+ const T=new d.ProxyClient(e,g,y,S,I||void 0,$,{timeout:w.timeout,retryAttempts:w.retryAttempts,retryDelay:w.retryDelay,circuitBreakerThreshold:w.circuitBreakerThreshold,circuitBreakerResetTime:w.circuitBreakerResetTime})
5404
5432
  if(v.registerClient(T),!w.alchemyPath)throw new Error("alchemyPath is required for ProjectSynchronizer creation. Cannot create API client without project synchronization capabilities.")
5405
5433
  try{g.info("Creating ProjectSynchronizer components")
5406
- const{ProjectRegistry,ProjectFileSystem,ProjectSynchronizer}=await t.e(478).then(()=>t(75478)),e=new ProjectRegistry(g),r=new ProjectFileSystem(g),n=new ProjectSynchronizer(g,e,new l.ProjectClientAdapter(E,g),r,{createRemoteProjects:!0})
5407
- E.setProjectSynchronizer(n),g.info("ProjectSynchronizer created and connected to ProjectClient")}catch(e){const t=`Failed to create required ProjectSynchronizer components: ${(0,h.getErrorMessage)(e)}`
5408
- throw g.error(t,{error:e instanceof Error?e.stack:String(e)}),new Error(t)}const j=new o.ApiClient(k,E,S,x,_,C,q,T,g)
5434
+ const{ProjectRegistry,ProjectFileSystem,ProjectSynchronizer}=await t.e(478).then(()=>t(75478)),e=new ProjectRegistry(g),r=new ProjectFileSystem(g),n=new ProjectSynchronizer(g,e,new l.ProjectClientAdapter(S,g),r,{createRemoteProjects:!0})
5435
+ S.setProjectSynchronizer(n),g.info("ProjectSynchronizer created and connected to ProjectClient")}catch(e){const t=`Failed to create required ProjectSynchronizer components: ${(0,h.getErrorMessage)(e)}`
5436
+ throw g.error(t,{error:e instanceof Error?e.stack:String(e)}),new Error(t)}const j=new o.ApiClient(k,S,E,_,x,C,q,T,g)
5409
5437
  g.info("API client initialized with specialized clients"),I?(j.setAuthToken(I),g.info("Auth token set on ApiClient during creation",{hasToken:!0,tokenLength:I.length})):g.warn("No auth token available during ApiClient creation",{info:"Client will need to authenticate before making requests"})
5410
5438
  const P=v.verifyAllClients()
5411
5439
  P.success?g.info("Token consistency verified across all clients"):g.error(`Token inconsistency detected in ${P.failedClients.length} clients: ${P.failedClients.join(", ")}`)
@@ -5968,7 +5996,7 @@ if(null===v)return{outcome:"skipped",reason:"could not check the branch head for
5968
5996
  if(v.length>0)return{outcome:"skipped",reason:`markers remain in ${v.join(", ")}`,oldHead:m}
5969
5997
  const k=[],I=new Set
5970
5998
  let $=w[b].parents[0]
5971
- const E=(new Date).toISOString()
5999
+ const S=(new Date).toISOString()
5972
6000
  for(let e=b;e<w.length;){const t=w[e]
5973
6001
  if(p(t)){let n=e
5974
6002
  for(;n+1<w.length&&w[n+1].parents.length<2;)n++
@@ -5976,20 +6004,20 @@ const l=w[n]
5976
6004
  if(n<w.length-1){const e=await o(d,l.sha,c)
5977
6005
  if(null===e||e.length>0)return{outcome:"skipped",reason:`the conflict at ${t.sha.slice(0,7)} was not resolved before the next merge; history kept as it is`,oldHead:m}}const u=await o(d,t.sha,c)??[]
5978
6006
  u.forEach(e=>I.add(e))
5979
- const h=t.subject.match(/^Merge (\S+) into (\S+)/),p=w.slice(e,n+1),f=[`Merge ${h?.[1]??i} into ${h?.[2]??s}${u.length?` (conflicts resolved in ${u.join(", ")})`:" (conflicts resolved)"}`,"",`Folded ${p.length} commit${1===p.length?"":"s"}: the merge that committed conflict markers and its resolution. The tree is the resolved one; the originals stay under refs/alchemy/folded/${a}/.`,...p.map(e=>`Folded: ${e.sha.slice(0,12)} ${e.subject}`)].join("\n"),g=await r(d,["commit-tree",l.tree,"-p",$,...t.parents.slice(1).flatMap(e=>["-p",e]),"-m",f],{GIT_AUTHOR_NAME:t.an,GIT_AUTHOR_EMAIL:t.ae,GIT_AUTHOR_DATE:t.ad,GIT_COMMITTER_NAME:t.cn,GIT_COMMITTER_EMAIL:t.ce,GIT_COMMITTER_DATE:E},c)
6007
+ const h=t.subject.match(/^Merge (\S+) into (\S+)/),p=w.slice(e,n+1),f=[`Merge ${h?.[1]??i} into ${h?.[2]??s}${u.length?` (conflicts resolved in ${u.join(", ")})`:" (conflicts resolved)"}`,"",`Folded ${p.length} commit${1===p.length?"":"s"}: the merge that committed conflict markers and its resolution. The tree is the resolved one; the originals stay under refs/alchemy/folded/${a}/.`,...p.map(e=>`Folded: ${e.sha.slice(0,12)} ${e.subject}`)].join("\n"),g=await r(d,["commit-tree",l.tree,"-p",$,...t.parents.slice(1).flatMap(e=>["-p",e]),"-m",f],{GIT_AUTHOR_NAME:t.an,GIT_AUTHOR_EMAIL:t.ae,GIT_AUTHOR_DATE:t.ad,GIT_COMMITTER_NAME:t.cn,GIT_COMMITTER_EMAIL:t.ce,GIT_COMMITTER_DATE:S},c)
5980
6008
  if(0!==g.code)return{outcome:"error",reason:`commit-tree failed: ${g.stderr.trim().slice(0,200)}`,oldHead:m}
5981
6009
  const y=g.stdout.trim()
5982
6010
  for(const e of p)k.push({from:e.sha,to:y,subject:e.subject,kind:"folded"})
5983
- $=y,e=n+1}else{const n=t.body||t.subject,o=await r(d,["commit-tree",t.tree,"-p",$,...t.parents.slice(1).flatMap(e=>["-p",e]),"-m",n],{GIT_AUTHOR_NAME:t.an,GIT_AUTHOR_EMAIL:t.ae,GIT_AUTHOR_DATE:t.ad,GIT_COMMITTER_NAME:t.cn,GIT_COMMITTER_EMAIL:t.ce,GIT_COMMITTER_DATE:E},c)
6011
+ $=y,e=n+1}else{const n=t.body||t.subject,o=await r(d,["commit-tree",t.tree,"-p",$,...t.parents.slice(1).flatMap(e=>["-p",e]),"-m",n],{GIT_AUTHOR_NAME:t.an,GIT_AUTHOR_EMAIL:t.ae,GIT_AUTHOR_DATE:t.ad,GIT_COMMITTER_NAME:t.cn,GIT_COMMITTER_EMAIL:t.ce,GIT_COMMITTER_DATE:S},c)
5984
6012
  if(0!==o.code)return{outcome:"error",reason:`commit-tree failed: ${o.stderr.trim().slice(0,200)}`,oldHead:m}
5985
6013
  const s=o.stdout.trim()
5986
- k.push({from:t.sha,to:s,subject:t.subject,kind:"replayed"}),$=s,e++}}const S=$,[x,_]=await Promise.all([r(d,["rev-parse",`${m}^{tree}`],void 0,c),r(d,["rev-parse",`${S}^{tree}`],void 0,c)])
5987
- if(0!==x.code||0!==_.code||x.stdout.trim()!==_.stdout.trim())return{outcome:"error",reason:"the folded head would not carry the same tree; branch left as it is",oldHead:m}
6014
+ k.push({from:t.sha,to:s,subject:t.subject,kind:"replayed"}),$=s,e++}}const E=$,[_,x]=await Promise.all([r(d,["rev-parse",`${m}^{tree}`],void 0,c),r(d,["rev-parse",`${E}^{tree}`],void 0,c)])
6015
+ if(0!==_.code||0!==x.code||_.stdout.trim()!==x.stdout.trim())return{outcome:"error",reason:"the folded head would not carry the same tree; branch left as it is",oldHead:m}
5988
6016
  const C=`refs/alchemy/folded/${a}/${m.slice(0,12)}`,q=await r(d,["update-ref",C,m],void 0,c)
5989
6017
  if(0!==q.code)return{outcome:"error",reason:`could not keep the originals: ${q.stderr.trim().slice(0,200)}`,oldHead:m}
5990
6018
  e.beforeMove&&await e.beforeMove()
5991
- const T=await r(d,["update-ref","-m",`fold conflict hand-off for ${a}`,`refs/heads/${s}`,S,m],void 0,c)
5992
- return 0!==T.code?(await r(d,["update-ref","-d",C],void 0,c),{outcome:"skipped",reason:`${s} moved while folding; left as it is (${T.stderr.trim().slice(0,160)})`,oldHead:m}):{outcome:"folded",oldHead:m,newHead:S,mapping:k,keptAt:C,conflictedFiles:[...I]}}function i(e){if("folded"!==e.outcome||!e.mapping)return""
6019
+ const T=await r(d,["update-ref","-m",`fold conflict hand-off for ${a}`,`refs/heads/${s}`,E,m],void 0,c)
6020
+ return 0!==T.code?(await r(d,["update-ref","-d",C],void 0,c),{outcome:"skipped",reason:`${s} moved while folding; left as it is (${T.stderr.trim().slice(0,160)})`,oldHead:m}):{outcome:"folded",oldHead:m,newHead:E,mapping:k,keptAt:C,conflictedFiles:[...I]}}function i(e){if("folded"!==e.outcome||!e.mapping)return""
5993
6021
  const t=e.mapping.filter(e=>"folded"===e.kind)
5994
6022
  return`Folded the conflict hand-off into ${[...new Set(t.map(e=>e.to.slice(0,7)))].join(", ")}: ${t.length} commit${1===t.length?"":"s"} (the merge that committed conflict markers${e.conflictedFiles?.length?` in ${e.conflictedFiles.join(", ")}`:""} and its resolution) became one clean merge with the same files. The originals stay readable at ${e.keptAt}.`}var a,c=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
5995
6023
  var o=Object.getOwnPropertyDescriptor(t,r)
@@ -7261,7 +7289,7 @@ try{if(!n.default.existsSync(t))return null
7261
7289
  const r=JSON.parse(n.default.readFileSync(t,"utf-8"))
7262
7290
  if(r.port)return(0,s.noteServe)(r.port,(0,s.currentServePassword)(e)),`http://127.0.0.1:${r.port}`}catch{}return null}
7263
7291
  const n=r(t(79896)),o=r(t(16928)),s=t(69706)},4871(e,exports){exports.R=exports.AUTONOMOUS_PLANNER_MARKER=void 0,exports.buildAutonomousPlannerContext=function(e){const t=e?.requestId?`"${e.requestId}"`:"the request you were spawned for"
7264
- return`${!1===e?.autoBuild?exports.R:exports.AUTONOMOUS_PLANNER_MARKER}\nYou are running as an AUTONOMOUS background planner. No user is watching this session \u2014 deliver a complete draft without user interaction.\n- FIRST MOVE, before any research: decide whether ${t} is ONE piece of work. If one worker could make the change as written, do NOT write a plan -- call session_message_operations offer-handoff immediately with { summary: "single piece of work, no plan", requestIds: [${t}] } and STOP. A plan is a guiding document that yields SEVERAL requests, never a document per request; planning a one-line change costs minutes and adds nothing (measured 2026-09-02).\n- Research first: spawn research agents (subagent_operations spawn { agent: "research", prompt: "..." }) or use research_fanout, and wait for the reports before writing.\n- Fill in the plan document section by section (plan_write update for the title + first section, then append one section at a time). Your session context gives the planId; if none was given, create the document with plan_write create first.\n- Include a "Risks & Assumptions" section: when something is ambiguous, make a reasonable choice and note your reasoning there. Never write questions into the plan \u2014 plans contain decisions, not questions.\n- Snapshot when done: plan_write create-snapshot with a label like "Draft v1".\n- Decompose the plan into requests WITHOUT asking permission first.\n- Only if you genuinely cannot proceed without a scope or priority decision, ask your PARENT session (session_message_operations send, type "question", toSessionId "parent") \u2014 never wait on the user directly.\n- Finish by calling session_message_operations offer-handoff ({ summary, requestIds }), then STOP and wait. If you idle after writing the plan without offering, the server nudges you once (idle rail).`},exports.AUTONOMOUS_PLANNER_MARKER="[Planner Flavor: autonomous]",exports.R="[Planner Flavor: autonomous-plan, human-build]"},49939(e,exports,t){function r(e){return a.default.createHash("md5").update(e).digest("hex")}async function n(e){try{return r(await s.default.promises.readFile(e))}catch{return null}}var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
7292
+ return`${!1===e?.autoBuild?exports.R:exports.AUTONOMOUS_PLANNER_MARKER}\nYou are running as an AUTONOMOUS background planner. No user is watching this session \u2014 deliver a complete draft without user interaction.\n- FIRST MOVE, before any research: decide whether ${t} is ONE piece of work. If one worker could make the change as written, do NOT write a plan -- call session_message_operations offer-handoff immediately with { summary: "single piece of work, no plan", requestIds: [${t}] } and STOP. A plan is a guiding document that yields SEVERAL requests, never a document per request; planning a one-line change costs minutes and adds nothing (measured 2026-09-02).\n- Research first: spawn research agents (subagent_operations spawn { agent: "research", prompt: "..." }) or use research_fanout, and wait for the reports before writing.\n- Write the plan document in a few writes (plan_write update for the title and first sections, then append the rest, several sections per call). Your session context gives the planId; if none was given, create the document with plan_write create first.\n- Include a "Risks & Assumptions" section: when something is ambiguous, make a reasonable choice and note your reasoning there. Never write questions into the plan \u2014 plans contain decisions, not questions.\n- Snapshot when done: plan_write create-snapshot with a label like "Draft v1".\n- Decompose the plan into requests WITHOUT asking permission first.\n- Only if you genuinely cannot proceed without a scope or priority decision, ask your PARENT session (session_message_operations send, type "question", toSessionId "parent") \u2014 never wait on the user directly.\n- Finish by calling session_message_operations offer-handoff ({ summary, requestIds }), then STOP and wait. If you idle after writing the plan without offering, the server nudges you once (idle rail).`},exports.AUTONOMOUS_PLANNER_MARKER="[Planner Flavor: autonomous]",exports.R="[Planner Flavor: autonomous-plan, human-build]"},49939(e,exports,t){function r(e){return a.default.createHash("md5").update(e).digest("hex")}async function n(e){try{return r(await s.default.promises.readFile(e))}catch{return null}}var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}}
7265
7293
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.syncPluginsGlobal=async function(e,t,o){const a={synced:[],skipped:[],errors:[]},d=o||function(){const e=process.argv[1]
7266
7294
  let t=e
7267
7295
  try{t=s.default.realpathSync(e)}catch{}return i.default.dirname(t)}(),u=i.default.join((0,c.getGlobalConfigDir)(e),"plugin")
@@ -7649,7 +7677,7 @@ const t=null!==await this.getToken()
7649
7677
  return this.TOKEN_CACHE.hasValidToken={value:t,timestamp:e,expiresAt:e+this.CACHE_TTL_MS,lastCheck:e,exists:t},t}getTokenDirectoryPath(){return this.tokenDirPath}getManualTokenFilePath(){return n.default.join(this.tokenDirPath,"manual-token.json")}logTokenLocationInstructions(){const e=this.getManualTokenFilePath()
7650
7678
  console.log("========================================"),console.log("To use the alchemy CLI, you need to authenticate."),console.log(`Token file location: ${e}`),console.log("You can either:"),console.log('\n1. Use the authenticate tool (recommended)\n2. Manually place a token file at the above location\n3. Set up automatic authentication flow\n\nFor manual token placement, create a JSON file with:\n{\n "token": "your_token_here",\n "userId": "your_user_id",\n "username": "your_username", \n "role": "user",\n "expiresAt": "2025-12-31T23:59:59.000Z"\n}\n'),console.log("For more information, see the authentication documentation."),console.log("========================================")}async startAuthenticationFlow(){this.pendingAuthCheck=!0
7651
7679
  try{const{tempCode:e,authUrl:t}=await this.authFlow.startAuthFlow()
7652
- return this.tempAuthCode=e,this.logger.info("Started external authentication flow",{tempCode:e,authUrl:t}),this.authFlow.startPolling(e,async t=>{this.logger.info("Authentication flow completed",{tempCode:e.substring(0,4)+"***",username:t.username}),await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),console.log("Authentication successful!"),console.log(`Welcome, ${t.username}!`),console.log("You can now use the alchemy CLI."),console.log("========================================")},()=>{this.logger.info("Authentication flow timed out",{tempCode:e.substring(0,4)+"***"}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow timed out."),console.log("Please try again or manually place a token file."),console.log("Token file location:"),console.log(`${this.getManualTokenFilePath()}`),console.log("========================================")},t=>{this.logger.error("Authentication flow error",{tempCode:e.substring(0,4)+"***",error:t}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow error."),console.log(`Error: ${t}`),console.log("Please try again or manually place a token file."),console.log(`${this.getManualTokenFilePath()}`),console.log("========================================")}),{tempCode:e,authUrl:t}}catch(e){throw this.logger.error("Error starting authentication flow",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,e}}hasPendingAuth(){return this.pendingAuthCheck||!!this.tempAuthCode}getAuthStatus(){return this.hasPendingAuth()?{hasToken:!1,isPendingAuth:!0,tempCode:this.tempAuthCode,authUrl:this.authFlow.getAuthUrl(this.tempAuthCode)}:{hasToken:!1,isPendingAuth:!1}}async checkAuthStatus(e){if(!e)return null
7680
+ return this.tempAuthCode=e,this.logger.info("Started external authentication flow",{tempCode:e,authUrl:t}),this.authFlow.startPolling(e,async t=>{this.logger.info("Authentication flow completed",{tempCode:e.substring(0,4)+"***",username:t.username}),await this.storeToken(t),this.pendingAuthCheck=!1,this.tempAuthCode=null,this.invalidateCache(),console.log(`\u2705 Signed in as ${t.username}.`)},()=>{this.logger.info("Authentication flow timed out",{tempCode:e.substring(0,4)+"***"}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow timed out."),console.log("Please try again or manually place a token file."),console.log("Token file location:"),console.log(`${this.getManualTokenFilePath()}`),console.log("========================================")},t=>{this.logger.error("Authentication flow error",{tempCode:e.substring(0,4)+"***",error:t}),this.pendingAuthCheck=!1,this.tempAuthCode=null,console.log("Authentication flow error."),console.log(`Error: ${t}`),console.log("Please try again or manually place a token file."),console.log(`${this.getManualTokenFilePath()}`),console.log("========================================")}),{tempCode:e,authUrl:t}}catch(e){throw this.logger.error("Error starting authentication flow",{error:(0,c.getErrorMessage)(e)}),this.pendingAuthCheck=!1,this.tempAuthCode=null,e}}hasPendingAuth(){return this.pendingAuthCheck||!!this.tempAuthCode}getAuthStatus(){return this.hasPendingAuth()?{hasToken:!1,isPendingAuth:!0,tempCode:this.tempAuthCode,authUrl:this.authFlow.getAuthUrl(this.tempAuthCode)}:{hasToken:!1,isPendingAuth:!1}}async checkAuthStatus(e){if(!e)return null
7653
7681
  try{const t=this.authFlow.getPollingStatus()
7654
7682
  if(t.status===a.PollingStatus.AUTHENTICATED&&t.tokenInfo)return this.logger.info("Authentication polling has token available",{tempCode:e.substring(0,4)+"***",username:t.tokenInfo.username}),t.tokenInfo
7655
7683
  if(t.status===a.PollingStatus.ERROR||t.status===a.PollingStatus.TIMEOUT)return this.pendingAuthCheck=!1,this.tempAuthCode=null,null