alchemylab 2.2.0 → 2.3.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.
@@ -67,26 +67,27 @@ 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"},44876(e,exports,t){var r,n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
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){async function r(e,t,r){const n=await(0,l.declaredRuntimeFloor)(t,t=>{try{return a.readFileSync(c.join(e,t),"utf8")}catch{return null}}),o=(0,l.imageRuntime)(r)
71
+ return n&&o?(0,l.floorImage)(o,n):r}var n,o=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
71
72
  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
+ 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]}),s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),i=this&&this.__importStar||(n=function(e){return n=Object.getOwnPropertyNames||function(e){var t=[]
73
74
  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
+ return t},n(e)},function(e){if(e&&e.__esModule)return e
75
76
  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}
77
+ if(null!=e)for(var r=n(e),i=0;i<r.length;i++)"default"!==r[i]&&o(t,e,r[i])
78
+ return s(t,e),t})
79
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.PYTHON_MANIFESTS=exports.NODE_IMAGE=exports.PYTHON_IMAGE=void 0,exports.defaultBoxFor=async function(e){if(!e)return{language:null}
79
80
  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))
81
+ try{t=a.readdirSync(e)}catch{return{language:null}}const n=e=>t.includes(e)
82
+ if(exports.PYTHON_MANIFESTS.some(n)||n("Pipfile")||!n("package.json")&&t.some(e=>e.endsWith(".py")))return{language:"python",image:await r(e,"python",exports.PYTHON_IMAGE),artifacts:[{path:".venv",prepare:d,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"}}]}
83
+ if(n("package.json")){const t=function(e){let t
84
+ try{t=JSON.parse(a.readFileSync(c.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
85
+ const r=t=>a.existsSync(c.join(e,t))
85
86
  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))
87
+ return{language:"node",image:await r(e,"node",exports.NODE_IMAGE),...t?{install:t}:{}}}return{language:null}}
88
+ const a=i(t(79896)),c=i(t(16928)),l=t(56844)
88
89
  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
90
+ const d=["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
90
91
  e.post("/auth/login",async(e,o)=>{try{const{username:r,password:a}=e.body
91
92
  if(!r||!a)return o.status(400).json({success:!1,error:"Username and password are required",timestamp:(new Date).toISOString()})
92
93
  s.info("Attempting login",{username:r})
@@ -133,11 +134,11 @@ j.registerClient(z)
133
134
  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)
134
135
  const H=r.getContext();(0,p.registerConfigRoutes)(B,n,H.basePath),e.use("/api",B)
135
136
  const U=e=>{const t=o.startTimer(e)
136
- return e=>t(e)},G=(e,t,n)=>{const o=e.query.path||e.body.path
137
+ return e=>t(e)},W=(e,t,n)=>{const o=e.query.path||e.body.path
137
138
  if(!o)return t.status(400).json({success:!1,error:"Path parameter is required"})
138
139
  const s=r.getContext()
139
140
  try{const{normalizedPath:t,fullPath:r}=y.validateAndNormalizePath(o,s)
140
- e.fullPath=r,e.relativePath=t}catch(e){return t.status(403).json({success:!1,error:"Access to paths outside the base directory is not allowed"})}n()},W=async(e,t,r)=>{const n=e.body.taskId
141
+ e.fullPath=r,e.relativePath=t}catch(e){return t.status(403).json({success:!1,error:"Access to paths outside the base directory is not allowed"})}n()},G=async(e,t,r)=>{const n=e.body.taskId
141
142
  if(!n)return r();(0,d.isDebugToolsEnabled)()&&s.debug("Validating task ID",{taskId:n})
142
143
  try{const e=await O.getTask(n)
143
144
  if((0,d.isDebugToolsEnabled)()&&s.debug("Task validation response received",{taskId:n,responseStatus:"received",hasData:!!e}),!e||!e.id)return s.info("Task ID validation failed - blocking operation",{taskId:n,validationResult:"invalid",reason:"no valid data"}),t.status(400).json({success:!1,error:{code:"TASK_NOT_FOUND",message:`Invalid task ID: ${n}`,retryable:!1,statusCode:400}});(0,d.isDebugToolsEnabled)()&&s.debug("Task ID validation successful - proceeding with operation",{taskId:n,validationResult:"valid"}),r()}catch(e){s.error("Error during task validation",{taskId:n,error:e instanceof Error?e.stack:String(e)})
@@ -146,33 +147,33 @@ if("object"==typeof e&&null!==e){404===e.statusCode&&(o=!0),404===e.response?.st
146
147
  const t=String(e.message||e.error||e);(t.includes("404")||t.toLowerCase().includes("not found"))&&(o=!0)}if(o)return s.info("Task ID not found - blocking operation",{taskId:n,statusCode:404,action:"blocking"}),t.status(400).json({success:!1,error:{code:"TASK_NOT_FOUND",message:`Task ID does not exist: ${n}`,retryable:!1,statusCode:400}})
147
148
  s.warn("Error validating task ID",{taskId:n,error:(0,v.getErrorMessage)(e)}),s.warn("Error validating task ID, but continuing as fallback",{taskId:n,action:"continuing"}),r()}}
148
149
  e.get("/health",(e,t)=>{o.incrementCounter("api_requests_total",1,{endpoint:"health",method:"GET"}),t.json({success:!0,data:{status:"ok",version:"0.1.0",serverType:"mcp-local-server",timestamp:(new Date).toISOString()}})}),e.get("/mcp/sse",async(e,t)=>{s.info("New SSE connection requested",{ip:e.ip,userAgent:e.headers["user-agent"]}),o.incrementCounter("mcp_connections_total",1,{type:"sse"})
149
- try{await r.createSseConnection(t,e.headers["user-agent"]),t.on("close",()=>{s.info("SSE connection closed"),o.setGauge("mcp_active_connections",0,{type:"sse"}),r.cleanupSSEConnection()})}catch(e){s.error("Failed to establish MCP SSE connection",{error:e instanceof Error?e.stack:String(e)}),o.incrementCounter("mcp_connection_errors",1,{type:"sse",error:e instanceof Error?e.name:"unknown"}),t.headersSent?t.end():t.status(500).json({success:!1,error:"Failed to establish SSE connection",message:(0,v.getErrorMessage)(e)})}}),e.get("/api/files/exists",G,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/exists",method:"GET"})
150
+ try{await r.createSseConnection(t,e.headers["user-agent"]),t.on("close",()=>{s.info("SSE connection closed"),o.setGauge("mcp_active_connections",0,{type:"sse"}),r.cleanupSSEConnection()})}catch(e){s.error("Failed to establish MCP SSE connection",{error:e instanceof Error?e.stack:String(e)}),o.incrementCounter("mcp_connection_errors",1,{type:"sse",error:e instanceof Error?e.name:"unknown"}),t.headersSent?t.end():t.status(500).json({success:!1,error:"Failed to establish SSE connection",message:(0,v.getErrorMessage)(e)})}}),e.get("/api/files/exists",W,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/exists",method:"GET"})
150
151
  const r=U("api_request_duration")
151
152
  try{const n=await l.default.stat(e.fullPath)
152
153
  r({endpoint:"files/exists",method:"GET",success:!0}),t.json({success:!0,data:{exists:!0,isDirectory:n.isDirectory(),isFile:n.isFile(),size:n.size,modified:n.mtime}})}catch(e){if("ENOENT"!==e.code)throw e
153
- r({endpoint:"files/exists",method:"GET",success:!0,result:"not_found"}),t.json({success:!0,data:{exists:!1}})}}catch(e){r(e)}}),e.get("/api/files/list",G,async(e,t,n)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/list",method:"GET"})
154
+ r({endpoint:"files/exists",method:"GET",success:!0,result:"not_found"}),t.json({success:!0,data:{exists:!1}})}}catch(e){r(e)}}),e.get("/api/files/list",W,async(e,t,n)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/list",method:"GET"})
154
155
  const n=U("api_request_duration"),s=r.getContext()
155
156
  try{if(!(await l.default.stat(e.fullPath)).isDirectory())throw n({endpoint:"files/list",method:"GET",success:!1,error:"not_directory"}),(0,u.createValidationError)("Path is not a directory")
156
157
  const r=await l.default.readdir(e.fullPath,{withFileTypes:!0}),o=await Promise.all(r.map(async t=>{const r=c.default.join(e.fullPath,t.name),n=await l.default.stat(r)
157
158
  return{name:t.name,path:c.default.relative(s.basePath,r),isDirectory:t.isDirectory(),isFile:t.isFile(),size:n.size,modified:n.mtime}}))
158
- n({endpoint:"files/list",method:"GET",success:!0}),t.json({success:!0,data:{path:e.relativePath,items:o}})}catch(t){throw"ENOENT"===t.code?(n({endpoint:"files/list",method:"GET",success:!1,error:"not_found"}),(0,u.createNotFoundError)(`Directory not found: ${e.relativePath}`)):t}}catch(e){n(e)}}),e.get("/api/files/content",G,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/content",method:"GET"})
159
+ n({endpoint:"files/list",method:"GET",success:!0}),t.json({success:!0,data:{path:e.relativePath,items:o}})}catch(t){throw"ENOENT"===t.code?(n({endpoint:"files/list",method:"GET",success:!1,error:"not_found"}),(0,u.createNotFoundError)(`Directory not found: ${e.relativePath}`)):t}}catch(e){n(e)}}),e.get("/api/files/content",W,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/content",method:"GET"})
159
160
  const r=U("api_request_duration")
160
161
  try{const n=await l.default.stat(e.fullPath)
161
162
  if(!n.isFile())throw r({endpoint:"files/content",method:"GET",success:!1,error:"not_file"}),(0,u.createValidationError)("Path is not a file")
162
163
  const o=e.query.encoding||"utf8"
163
164
  if("binary"===o){const n=await l.default.readFile(e.fullPath)
164
165
  return r({endpoint:"files/content",method:"GET",success:!0,encoding:"binary"}),t.setHeader("Content-Type","application/octet-stream"),t.setHeader("Content-Disposition",`attachment; filename="${c.default.basename(e.fullPath)}""`),t.setHeader("Content-Length",n.length),t.send(n)}const s=await l.default.readFile(e.fullPath,o)
165
- r({endpoint:"files/content",method:"GET",success:!0,encoding:o}),t.json({success:!0,data:{path:e.relativePath,content:s,size:n.size,modified:n.mtime}})}catch(t){throw"ENOENT"===t.code?(r({endpoint:"files/content",method:"GET",success:!1,error:"not_found"}),(0,u.createNotFoundError)(`File not found: ${e.relativePath}`)):t}}catch(e){r(e)}}),e.post("/api/files/write",G,W,async(e,t,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/write",method:"POST"})
166
+ r({endpoint:"files/content",method:"GET",success:!0,encoding:o}),t.json({success:!0,data:{path:e.relativePath,content:s,size:n.size,modified:n.mtime}})}catch(t){throw"ENOENT"===t.code?(r({endpoint:"files/content",method:"GET",success:!1,error:"not_found"}),(0,u.createNotFoundError)(`File not found: ${e.relativePath}`)):t}}catch(e){r(e)}}),e.post("/api/files/write",W,G,async(e,t,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/write",method:"POST"})
166
167
  const s=U("api_request_duration"),{content:i,taskId:a,format:c="text"}=e.body
167
168
  if(void 0===i)throw(0,u.createValidationError)("Content parameter is required")
168
169
  const l=await(0,m.handleWriteFile)({taskId:a,path:e.relativePath,content:i,format:c},r.getContext(),M,O,z,F,n,o)
169
- s({endpoint:"files/write",method:"POST",success:!0}),t.json({success:!0,data:l})}catch(e){s(e)}}),e.post("/api/files/replace",G,W,async(e,t,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/replace",method:"POST"})
170
+ s({endpoint:"files/write",method:"POST",success:!0}),t.json({success:!0,data:l})}catch(e){s(e)}}),e.post("/api/files/replace",W,G,async(e,t,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/replace",method:"POST"})
170
171
  const s=U("api_request_duration"),{replacements:i,mode:a="unique",taskId:c}=e.body
171
172
  if(!i||!Array.isArray(i))throw(0,u.createValidationError)("Replacements array is required")
172
173
  const l=await(0,f.handleReplaceStrings)({taskId:c,operations:[{path:e.relativePath,replacements:i,mode:a}],comment:"String replacement via API"},r.getContext(),O,z,F,n,o)
173
- s({endpoint:"files/replace",method:"POST",success:!0}),t.json({success:!0,data:l})}catch(e){s(e)}}),e.delete("/api/files/delete",G,W,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/delete",method:"DELETE"})
174
+ s({endpoint:"files/replace",method:"POST",success:!0}),t.json({success:!0,data:l})}catch(e){s(e)}}),e.delete("/api/files/delete",W,G,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"files/delete",method:"DELETE"})
174
175
  const r=U("api_request_duration"),{taskId:n}=e.body
175
- try{await l.default.access(e.fullPath)}catch(t){throw r({endpoint:"files/delete",method:"DELETE",success:!1,error:"not_found"}),(0,u.createNotFoundError)(`File not found: ${e.relativePath}`)}await l.default.unlink(e.fullPath),r({endpoint:"files/delete",method:"DELETE",success:!0}),t.json({success:!0,data:{path:e.relativePath,deleted:!0}})}catch(e){r(e)}}),e.post("/api/directories/create",G,W,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"directories/create",method:"POST"})
176
+ try{await l.default.access(e.fullPath)}catch(t){throw r({endpoint:"files/delete",method:"DELETE",success:!1,error:"not_found"}),(0,u.createNotFoundError)(`File not found: ${e.relativePath}`)}await l.default.unlink(e.fullPath),r({endpoint:"files/delete",method:"DELETE",success:!0}),t.json({success:!0,data:{path:e.relativePath,deleted:!0}})}catch(e){r(e)}}),e.post("/api/directories/create",W,G,async(e,t,r)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"directories/create",method:"POST"})
176
177
  const r=U("api_request_duration"),{taskId:n}=e.body
177
178
  await l.default.mkdir(e.fullPath,{recursive:!0})
178
179
  const s=await l.default.stat(e.fullPath)
@@ -180,7 +181,7 @@ r({endpoint:"directories/create",method:"POST",success:!0}),t.json({success:!0,d
180
181
  const e=U("api_request_duration"),n=r.getContext()
181
182
  if(!n.git)throw e({endpoint:"git/status",method:"GET",success:!1,error:"no_git"}),(0,u.createValidationError)("Git is not initialized for this directory")
182
183
  const s=await n.git.status()
183
- e({endpoint:"git/status",method:"GET",success:!0}),t.json({success:!0,data:{current:s.current,tracking:s.tracking,clean:s.isClean(),ahead:s.ahead,behind:s.behind,files:s.files}})}catch(e){n(e)}}),e.post("/api/git/commit",W,async(e,n,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"git/commit",method:"POST"})
184
+ e({endpoint:"git/status",method:"GET",success:!0}),t.json({success:!0,data:{current:s.current,tracking:s.tracking,clean:s.isClean(),ahead:s.ahead,behind:s.behind,files:s.files}})}catch(e){n(e)}}),e.post("/api/git/commit",G,async(e,n,s)=>{try{o.incrementCounter("api_requests_total",1,{endpoint:"git/commit",method:"POST"})
184
185
  const s=U("api_request_duration"),i=r.getContext(),{message:a,taskId:c}=e.body
185
186
  if(!a)throw(0,u.createValidationError)("Commit message is required")
186
187
  let l=i.git
@@ -199,10 +200,10 @@ try{const r=await A.pingDetailed(),n="online"===r.status,o=r.latency
199
200
  e({endpoint:"remote/status",method:"GET",success:!0,connected:n}),s.info("Remote server status check",{status:n?"ONLINE":"OFFLINE",latencyMs:o}),t.json({success:!0,data:{connected:n,url:q,latency:o,queueStats:{total:0,pending:0,inProgress:0,completed:0,failed:0},isOffline:A.isOffline(),serverTime:(new Date).toISOString()}})}catch(r){e({endpoint:"remote/status",method:"GET",success:!0,connected:!1}),t.json({success:!0,data:{connected:!1,url:q,error:(0,v.getErrorMessage)(r),serverTime:(new Date).toISOString()}})}}catch(e){r(e)}}),e.get("/api/proxy/*",async(e,t,r)=>{try{const r=e.path.substring(11)
200
201
  try{const n=await L.proxyGet(r,e.query)
201
202
  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})
202
- throw e}}catch(e){r(e)}}),e.post("/api/proxy/*",W,async(e,t,r)=>{try{const r=e.path.substring(11)
203
+ throw e}}catch(e){r(e)}}),e.post("/api/proxy/*",G,async(e,t,r)=>{try{const r=e.path.substring(11)
203
204
  try{const n=await L.proxyPost(r,e.body)
204
205
  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})
205
- throw e}}catch(e){r(e)}}),e.patch("/api/proxy/*",W,async(e,t,r)=>{try{const r=e.path.substring(11)
206
+ throw e}}catch(e){r(e)}}),e.patch("/api/proxy/*",G,async(e,t,r)=>{try{const r=e.path.substring(11)
206
207
  try{const n=await L.proxyPatch(r,e.body)
207
208
  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})
208
209
  throw e}}catch(e){r(e)}})}
@@ -852,7 +853,8 @@ if(s?.length>0){const e=s[0].requestLocalId||s[0].request_local_id
852
853
  return e&&this.sessionRequestCache.set(n,{requestId:e,at:Date.now()}),this.log("getSessionRequestId.resolved",{sessionId:n,requestId:e}),e||null}return this.log("getSessionRequestId.none",{sessionId:n}),null}catch(e){const r=e instanceof Error?e.message:String(e)
853
854
  return t.warn("Error getting session request",{sessionId:n,error:r}),null}}noRequestReason(e){const t=e?this.endedUnderSession.get(e):void 0
854
855
  return t?`${t} was canceled or reverted, so your tools are on no request now. Do not continue its work: no task, file or command lands for it. A message about another request moves your tools there.`:null}clearSessionRequestCache(e){e?this.sessionRequestCache.delete(e):this.sessionRequestCache.clear()}buildCliOrigin(e){if(this.identity)return{providerId:this.identity.providerId,sessionId:this.identity.sessionId,messageId:e,createdAt:(new Date).toISOString()}}setCurrentCallOrigin(e){this.currentCallOrigin=e}getCurrentCallOrigin(){return this.currentCallOrigin||this.buildCliOrigin()}log(e,t){if(i.SESSION_IDENTITY_LOG_ENABLED)try{const o=process.env.ALCHEMY_PROJECT_PATH||process.cwd(),s=(0,n.join)(o,".alchemy_lab");(0,r.existsSync)(s)||(0,r.mkdirSync)(s,{recursive:!0})
855
- const i=(0,n.join)(s,"mcp-identity-debug.log"),a=(new Date).toISOString(),c=process.pid,l=t?" "+JSON.stringify(t):"";(0,r.appendFileSync)(i,`[${a}] [PID:${c}] identity.${e}${l}\n`)}catch{}}}a.SESSION_REQUEST_TTL_MS=3e3,exports.sessionIdentity=new a,exports.sessionIdentity.log("module-load",{ALCHEMY_ALLOW_PARALLEL:process.env.ALCHEMY_ALLOW_PARALLEL||null})},95122(e,exports,t){function r(e,t){const r=Array.isArray(e.pathGlobs)?e.pathGlobs.filter(e=>"string"==typeof e&&e.trim()):[]
856
+ const i=(0,n.join)(s,"mcp-identity-debug.log"),a=(new Date).toISOString(),c=process.pid,l=t?" "+JSON.stringify(t):"";(0,r.appendFileSync)(i,`[${a}] [PID:${c}] identity.${e}${l}\n`)}catch{}}}a.SESSION_REQUEST_TTL_MS=3e3,exports.sessionIdentity=new a,exports.sessionIdentity.log("module-load",{ALCHEMY_ALLOW_PARALLEL:process.env.ALCHEMY_ALLOW_PARALLEL||null})},70715(e,exports){exports.JZ=exports.hasBaseline=exports.Ql=exports.rh=void 0,exports.rh={tolerancePct:10,repeats:3,timeAtLeast:.1},exports.Ql=9,exports.hasBaseline=e=>!!e?.baseline&&"object"==typeof e.baseline,exports.JZ=e=>{const t=[...e].sort((e,t)=>e-t),r=Math.floor(t.length/2)
857
+ return t.length%2?t[r]:(t[r-1]+t[r])/2}},95122(e,exports,t){function r(e,t){const r=Array.isArray(e.pathGlobs)?e.pathGlobs.filter(e=>"string"==typeof e&&e.trim()):[]
856
858
  return 0===r.length?t:t.filter(t=>(0,s.processApplies)({id:e.id??"",pathGlobs:r},[t],[]))}async function n(e,t,n){const o=t.filter(e=>e&&!1!==e.isActive&&!e.config?.isStub)
857
859
  if(0===o.length||!Array.isArray(e))return[]
858
860
  const s=[],a=[]
@@ -1117,15 +1119,15 @@ try{const s=await fetch(`${(0,r.getActionServerBaseUrl)()}/api/sessions/${encode
1117
1119
  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.`}
1118
1120
  const r=t(79414)},4961(e,exports,t){exports.def=void 0
1119
1121
  const r=t(76183),n=t(23540)
1120
- 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
1122
+ 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 or corrects how something was done in a way that should hold for later work, once per correction, 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
1121
1123
  try{const i=t.projectId
1122
1124
  if(!i)throw new r.ValidationError("Project ID is required for directive operations")
1123
1125
  if("set"===u){const n=e.targetType
1124
- if(!n||!["project","area","request"].includes(n))throw new r.ValidationError("targetType must be one of project | area | request")
1126
+ if(!n||!["project","area","request","you"].includes(n))throw new r.ValidationError("targetType must be one of project | area | request | you")
1125
1127
  if("string"!=typeof e.body||0===e.body.trim().length)throw new r.ValidationError("body (the guidance) is required")
1126
1128
  if(!("area"!==n&&"request"!==n||e.targetId&&e.targetId.trim()))throw new r.ValidationError(`targetId is required for ${n} guidance (${"area"===n?"the area name":"the request local id"})`)
1127
1129
  const o={targetType:n,body:e.body.trim(),createdBy:c?.sessionId??t.currentSessionId}
1128
- "project"!==n&&(o.targetId=e.targetId.trim()),"string"==typeof e.note&&e.note.trim()&&(o.note=e.note.trim()),"string"==typeof e.userSaid&&e.userSaid.trim()&&(o.userSaid=e.userSaid.trim()),"string"==typeof e.revises&&e.revises.trim()&&(o.revises=e.revises.trim())
1130
+ "area"!==n&&"request"!==n||(o.targetId=e.targetId.trim()),"string"==typeof e.note&&e.note.trim()&&(o.note=e.note.trim()),"string"==typeof e.userSaid&&e.userSaid.trim()&&(o.userSaid=e.userSaid.trim()),"string"==typeof e.revises&&e.revises.trim()&&(o.revises=e.revises.trim())
1129
1131
  const a=await s.proxyPost(`/api/projects/${i}/directives`,o),u=a?.data??a
1130
1132
  if(u?.proposed)return d({operation:"set",success:!0}),{content:[{type:"text",text:`Proposed as guidance, quoting the user's words (${u.messageCode}). It becomes guidance only when they press Adopt on its card; until then nothing is recorded. Tell them it waits for their Adopt.`}]}
1131
1133
  const h=u,p="project"===h.target_type?"project":`${h.target_type} ${h.target_id}`
@@ -1279,7 +1281,7 @@ d({success:!0}),i(await o(l),c.map(e=>e?.id))
1279
1281
  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??{}
1280
1282
  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))
1281
1283
  let p=""
1282
- if(!c.some(e=>e?.config?.image)){const e=(0,g.defaultBoxFor)(r.mainBasePath)
1284
+ if(!c.some(e=>e?.config?.image)){const e=await(0,g.defaultBoxFor)(r.mainBasePath)
1283
1285
  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}`:"")
1284
1286
  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")
1285
1287
  try{const n=await r.proxyGet(`/api/projects/${t.projectId}/environments/${e.environmentId}`),s=n?.data||n
@@ -1332,7 +1334,7 @@ u="setup"===e?.type}const h=u?d:void 0
1332
1334
  let f=!1
1333
1335
  if(o&&n)try{await t.getProxyClient().proxyPatch(`/api/projects/${n}/requests/${o}`,{status:"canceled",completionSummary:h?`Not needed: the same environment setup was already under way as ${h}.`:`Environment setup could not start: ${String(l?.error||"HTTP "+c).slice(0,300)}`}),f=!0}catch(e){r.warn("environment_setup: could not withdraw the unused setup request",{setupRequestId:o,error:(0,p.getErrorMessage)(e)})}const m=f?` ${o}, filed for this call, was withdrawn.`:""
1334
1336
  if(409===c&&h&&s&&n){let e=!1
1335
- try{await t.proxyPost(`/api/projects/${n}/requests/${s}/waits`,{waitsOn:h,reason:`environment setup: ${a}`.slice(0,300),...i?{sessionId:i}:{}}),e=!0}catch(e){r.warn("environment_setup: wait on the setup in progress not declared",{callerRequestId:s,covering:h,error:(0,p.getErrorMessage)(e)})}return{content:[{type:"text",text:`An environment setup is already under way as ${h}; a second architect would only ask the user the same thing twice.${m}`+(e?`\n\n\u23f3 Wait declared: ${s} waits on ${h}. You are idle by design until it settles, and you will be woken with its outcome. End your turn now, or check environment_list if it may already be done.`:" When it settles, whoever started it is woken with its outcome; check environment_list before you rely on it.")}]}}return{content:[{type:"text",text:`environment setup failed: ${l?.error||"HTTP "+c}.${m}`}],isError:!0}}({apiClient:n,logger:o,projectId:y,setupRequestId:c,callerRequestId:g,parentSessionId:m,firstLine:b,status:k.status,body:I})
1337
+ try{await t.proxyPost(`/api/projects/${n}/requests/${s}/waits`,{waitsOn:h,reason:`environment setup: ${a}`.slice(0,300),...i?{sessionId:i}:{}}),e=!0}catch(e){r.warn("environment_setup: wait on the setup in progress not declared",{callerRequestId:s,covering:h,error:(0,p.getErrorMessage)(e)})}return{content:[{type:"text",text:`A setup that builds this project's environment is already under way as ${h}; a second one would leave two environments and ask the user twice.${m}`+(e?`\n\n\u23f3 Wait declared: ${s} waits on ${h}. You are idle by design until it settles, and you will be woken with its outcome. End your turn now, or check environment_list if it may already be done.`:" When it settles, whoever started it is woken with its outcome; check environment_list before you rely on it.")}]}}return{content:[{type:"text",text:`environment setup failed: ${l?.error||"HTTP "+c}.${m}`}],isError:!0}}({apiClient:n,logger:o,projectId:y,setupRequestId:c,callerRequestId:g,parentSessionId:m,firstLine:b,status:k.status,body:I})
1336
1338
  if(c&&g&&y)try{await n.proxyPost(`/api/projects/${y}/requests/${g}/waits`,{waitsOn:c,reason:`environment setup: ${b}`.slice(0,300),...m?{sessionId:m}:{}}),l=!0}catch(e){o.warn("environment_setup: wait not declared",{callerRequestId:g,setupRequestId:c,error:(0,p.getErrorMessage)(e)})}a({success:!0})
1337
1339
  const $=l?`\n\n\u23f3 Wait declared: ${g} waits on ${c}. This is a registered wait: you are idle by design until ${c} settles, and you will be woken with its outcome the moment it does. End your turn now; do nothing else on ${g}.`:c?`\n\nFiled as ${c} (setup). When it settles you are woken with its outcome, so what the user asked for can go on then; end your turn if you have nothing else to do.`:"\n\nIts report wakes you when it has a verified environment saved (or when it gives up): end your turn if you have nothing else to do, or continue other work in the meantime."
1338
1340
  return{content:[{type:"text",text:`Environment setup started${c?` as ${c}`:""} (worktree ${I.data.worktreePath}). The setup-architect is inspecting the project.${$}`}],...h({kind:"spawn-subagent",spawnedSessionId:I.data.sessionId,agentType:"setup-architect",requestId:c??g??void 0,description:"Environment setup"})}}catch(e){return e instanceof Error&&/timeout|abort/i.test(e.message)?(a({success:!1,error:"spawn_slow"}),{content:[{type:"text",text:"Environment setup was requested and is probably still starting -- creating the setup worktree takes a while on a large repository. Do NOT call environment_setup again: the architect reports back on its own, and a second call would only ask the user the same question twice. Check with environment_list in a few minutes."}]}):(a({success:!1,error:"fetch_error"}),{content:[{type:"text",text:`environment setup failed: ${(0,p.getErrorMessage)(e)}`}],isError:!0})}},exports.handleEnvironmentProvision=async function(e,r,n,o,s,i){const a=s.startTimer("tool_execution_time")
@@ -1385,8 +1387,8 @@ const w=y.environmentName?`environment **${y.environmentName}** (${y.image}${y.h
1385
1387
  let b=""
1386
1388
  try{const e=y.environmentId||d
1387
1389
  if(o.projectId){const t=await s.proxyGet(`/api/projects/${o.projectId}/check-commands`),r=e?(t?.data||t||[]).filter(e=>e.isActive??e.is_active??!0).filter(t=>(t.environmentId??t.environment_id)===e).map(e=>`\`${e.name}\``):[]
1388
- r.length>0&&(b=`\nRegistered commands here: ${r.join(", ")} (run them via \`run\`).`)}}catch{}const v="write large output to /scratch (on disk) or /tmp (256 MB, in memory)",k=y.sourceWritable?` -- your live branch source. A command may change it (a formatter, a --fix, codegen): whatever a run changes is committed as that run's commit and listed in its result, so the tree stays clean; ${v}`:` -- your branch source, mounted READ-ONLY (edits you commit flow in; ${v})`,I=y.workdir?`Commands run in \`${y.workdir}\`${y.hostTier?" (a disposable writable worktree on the host)":k}. `:`The source is your branch${k}. `,$=function(e){return e.hostTier?"":e.hasGit&&!1===e.gitBinary?'This image has no git binary: a git command fails here ("git: not found"), so read the files themselves (grep, cat, or the project\'s own runtime); merge_preview and query_history show main and the history. ':e.hasGit?"git works here on your branch and its history, read-only (ls-files, log, diff, rev-parse); commits and other git writes fail. ":"git has no repository in this box: a command that needs one fails or reports nothing, so do not record such a run as proof. "}(y)
1389
- return{content:[{type:"text",text:`${y.reused?`\u{1f9ea} You already hold a box on ${w}; it is returned as it is (its files, servers and state are still there). To start clean, experiment_stop it and start again.`:`\u{1f9ea} Experiment started: ${w}.`}\nHandle: \`${y.handle}\`\n\n${I}${$}State persists across exec calls, including background processes (a server you start with \`&\` keeps running -- wait ~2s before its first request on a fresh box, and don't start it twice). Edits through the usual tools flow into the source live. It stops by itself when the request's work merges or it sits idle; experiment_stop frees it sooner.${b}`}]}}catch(e){return l({success:!1,error:"start_error"}),{content:[{type:"text",text:`Could not start experiment: ${(0,n.getErrorMessage)(e)}`}],isError:!0}}},exports.handleExperimentStop=async function(e,t,o,s,i,a){const c=i.startTimer("tool_execution_time")
1390
+ r.length>0&&(b=`\nRegistered commands here: ${r.join(", ")} (run them via \`run\`).`)}}catch{}const v="write large output to /scratch (on disk) or /tmp (256 MB, in memory)",k=y.sourceWritable?` -- your live branch source. A command may change it (a formatter, a --fix, codegen): whatever a run changes is committed as that run's commit and listed in its result, so the tree stays clean; ${v}`:` -- a copy of your branch made for this box: commands may write anywhere in it (build output, caches), and it goes when the box stops; ${v}`,I=y.workdir?`Commands run in \`${y.workdir}\`${y.hostTier?" (a disposable writable worktree on the host)":k}. `:`The source is your branch${k}. `,$=function(e){return e.hostTier?"":e.hasGit&&!1===e.gitBinary?'This image has no git binary: a git command fails here ("git: not found"), so read the files themselves (grep, cat, or the project\'s own runtime); merge_preview and query_history show main and the history. ':e.hasGit?"git works here on your branch and its history, read-only (ls-files, log, diff, rev-parse); commits and other git writes fail. ":"git has no repository in this box: a command that needs one fails or reports nothing, so do not record such a run as proof. "}(y)
1391
+ return{content:[{type:"text",text:`${y.reused?`\u{1f9ea} You already hold a box on ${w}; it is returned as it is (its files, servers and state are still there). To start clean, experiment_stop it and start again.`:`\u{1f9ea} Experiment started: ${w}.`}\nHandle: \`${y.handle}\`\n\n${I}${$}State persists across exec calls, including background processes (a server you start with \`&\` keeps running -- wait ~2s before its first request on a fresh box, and don't start it twice).${y.sourceWritable?" Edits through the usual tools flow into the source live.":""} It stops by itself when the request's work merges or it sits idle; experiment_stop frees it sooner.${b}`}]}}catch(e){return l({success:!1,error:"start_error"}),{content:[{type:"text",text:`Could not start experiment: ${(0,n.getErrorMessage)(e)}`}],isError:!0}}},exports.handleExperimentStop=async function(e,t,o,s,i,a){const c=i.startTimer("tool_execution_time")
1390
1392
  try{if(!e.handle)return c({success:!1,error:"bad_args"}),{content:[{type:"text",text:"experiment_stop needs a handle."}],isError:!0}
1391
1393
  const{ok:t,data:n}=await r("/api/experiments/stop","POST",{handle:e.handle,...a?.sessionId?{sessionId:a.sessionId}:{}},3e4)
1392
1394
  return c({success:t}),t&&n.success?{content:[{type:"text",text:n.data?.stopped?"\u{1f9ea} Experiment stopped and its environment torn down.":"That experiment was already gone."}]}:{content:[{type:"text",text:`experiment_stop failed: ${n.error||"unknown"}`}],isError:!0}}catch(e){return c({success:!1,error:"stop_error"}),{content:[{type:"text",text:`experiment_stop failed: ${(0,n.getErrorMessage)(e)}`}],isError:!0}}},exports.handleExperimentList=async function(e,t,o,s,i,a){const c=i.startTimer("tool_execution_time")
@@ -1473,32 +1475,32 @@ const e=await(0,u.commitUserChangesBeforeToolOperation)(r.git,"delete-files",{re
1473
1475
  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:E,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:E,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})
1474
1476
  const{toolRecordId:H,localToolRecordId:U}=await c.createFileOperationRecords(E,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:x,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},d,v,f,s,b.metricsPrefix,i)
1475
1477
  g=H,y=H,w=b.fileOperationPhase,s.incrementCounter("delete_files_phase",1,{phase:w})
1476
- const G=[]
1477
- let W=0
1478
+ const W=[]
1479
+ let G=0
1478
1480
  j.length>0&&n.setProjectId(r.projectId)
1479
1481
  for(let e=0;e<O.length;e++){const t=O[e],r={type:"file",path:t.normalized,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0}
1480
1482
  try{try{await a.default.access(t.full)}catch(e){throw new Error(`File not found: ${t.normalized}`)}if((await a.default.stat(t.full)).isDirectory())throw new Error(`Cannot 'delete' a directory: ${t.normalized}`)
1481
- await a.default.unlink(t.full),r.success=!0,W++,f.info("Successfully deleted file",{path:t.normalized})}catch(e){r.error=(0,p.getErrorMessage)(e),f.warn(`Failed to delete file ${t.normalized}`,{error:r.error})}G.push(r)}const V=j.length>0?n.getDocumentClient():null
1483
+ await a.default.unlink(t.full),r.success=!0,G++,f.info("Successfully deleted file",{path:t.normalized})}catch(e){r.error=(0,p.getErrorMessage)(e),f.warn(`Failed to delete file ${t.normalized}`,{error:r.error})}W.push(r)}const V=j.length>0?n.getDocumentClient():null
1482
1484
  for(const e of j){const t=e.target,r={type:"document",documentId:t,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0}
1483
1485
  try{let e
1484
1486
  try{if(e=await V.getDocument(t),r.filename=e.filename,f.info("Document found for archival",{documentId:e.id,filename:e.filename,requestId:e.requestId}),e.requestId)try{await(0,h.validateRequestStatusForDocumentOperation)(e.requestId,k,f,s,"delete-document")}catch(e){if(e instanceof l.ValidationError)throw new Error(e.message)
1485
1487
  throw e}}catch(e){throw new Error(`Document not found or validation failed: ${t}. ${(0,p.getErrorMessage)(e)}`)}const n=await V.archiveDocument(t)
1486
- r.success=!0,r.filename=n.filename,W++,f.info("Document archived successfully",{documentId:n.id,filename:n.filename,archivedAt:n.archived_at})}catch(e){r.error=(0,p.getErrorMessage)(e),f.warn(`Failed to delete document ${t}`,{error:r.error})}G.push(r)}const K=Date.now()-m
1487
- s.recordValue("delete_files_time",K),s.incrementCounter("delete_files_operations_total",P),s.incrementCounter("delete_files_operations_success",W),s.incrementCounter("delete_files_operations_failed",P-W),s.incrementCounter("delete_files_file_operations",T.length),s.incrementCounter("delete_files_document_operations",j.length),W===P?s.incrementCounter("delete_files_success",1):W>0?s.incrementCounter("delete_files_partial_success",1):s.incrementCounter("delete_files_total_failure",1)
1488
+ r.success=!0,r.filename=n.filename,G++,f.info("Document archived successfully",{documentId:n.id,filename:n.filename,archivedAt:n.archived_at})}catch(e){r.error=(0,p.getErrorMessage)(e),f.warn(`Failed to delete document ${t}`,{error:r.error})}W.push(r)}const K=Date.now()-m
1489
+ s.recordValue("delete_files_time",K),s.incrementCounter("delete_files_operations_total",P),s.incrementCounter("delete_files_operations_success",G),s.incrementCounter("delete_files_operations_failed",P-G),s.incrementCounter("delete_files_file_operations",T.length),s.incrementCounter("delete_files_document_operations",j.length),G===P?s.incrementCounter("delete_files_success",1):G>0?s.incrementCounter("delete_files_partial_success",1):s.incrementCounter("delete_files_total_failure",1)
1488
1490
  let Y=null
1489
- if(W>0){w=c.FileOperationPhase.GIT_COMMIT,s.incrementCounter("delete_files_phase",1,{phase:w})
1490
- const e=G.filter(e=>e.success),t=e.filter(e=>"file"===e.type).length,n=e.filter(e=>"document"===e.type).length
1491
+ if(G>0){w=c.FileOperationPhase.GIT_COMMIT,s.incrementCounter("delete_files_phase",1,{phase:w})
1492
+ const e=W.filter(e=>e.success),t=e.filter(e=>"file"===e.type).length,n=e.filter(e=>"document"===e.type).length
1491
1493
  let o
1492
1494
  o=t>0&&n>0?`Delete ${t} files and ${n} documents - ${x}`:t>0?1===t?`Delete file - ${x}`:`Delete ${t} files - ${x}`:1===n?`Delete document - ${x}`:`Delete ${n} documents - ${x}`,Y=await c.commitToGit(E,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})
1493
- const J=G.filter(e=>e.success),Q=G.filter(e=>!e.success)
1494
- await c.updateFileOperationRecords(E,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:x,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},W===P?`Successfully deleted all ${P} operations`:`Deleted ${W}/${P} operations successfully`,d,v,f,s,b.metricsPrefix)
1495
+ const J=W.filter(e=>e.success),Q=W.filter(e=>!e.success)
1496
+ await c.updateFileOperationRecords(E,g,y,M,b.recordType,G===P?`Successfully deleted all ${P} operations`:`Deleted ${G}/${P} operations successfully`,{status:G===P?"completed":"partial_success",commitHash:Y?.commitHash,previousCommitHash:Y?.previousCommitHash,totalOperations:P,fileOperations:T.length,documentOperations:j.length,successCount:G,failureCount:P-G,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:x,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},G===P?`Successfully deleted all ${P} operations`:`Deleted ${G}/${P} operations successfully`,d,v,f,s,b.metricsPrefix)
1495
1497
  const Z=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
1496
1498
  let X=""
1497
- if(W===P){const e=J.filter(e=>"file"===e.type).length,t=J.filter(e=>"document"===e.type).length
1498
- X=e>0&&t>0?`Successfully deleted ${e} files and ${t} documents`:e>0?`Successfully deleted all ${e} ${1===e?"file":"files"}`:`Successfully archived all ${t} ${1===t?"document":"documents"}`}else X=W>0?`Successfully deleted ${W}/${P} operations.\n\nFailed operations:\n`+G.filter(e=>!e.success).map(e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
1499
- return`\u2022 ${e.type}: ${t}: ${e.error}`}).join("\n"):"All delete operations failed:\n"+G.map(e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
1499
+ if(G===P){const e=J.filter(e=>"file"===e.type).length,t=J.filter(e=>"document"===e.type).length
1500
+ X=e>0&&t>0?`Successfully deleted ${e} files and ${t} documents`:e>0?`Successfully deleted all ${e} ${1===e?"file":"files"}`:`Successfully archived all ${t} ${1===t?"document":"documents"}`}else X=G>0?`Successfully deleted ${G}/${P} operations.\n\nFailed operations:\n`+W.filter(e=>!e.success).map(e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
1501
+ return`\u2022 ${e.type}: ${t}: ${e.error}`}).join("\n"):"All delete operations failed:\n"+W.map(e=>{const t="file"===e.type?e.path:`${e.filename||e.documentId} (${e.documentId})`
1500
1502
  return`\u2022 ${e.type}: ${t}: ${e.error}`}).join("\n")
1501
- return Y?.commitHash&&(X+=`\nCommit: ${Y.commitHash.substring(0,8)}`),Z.requestId&&(X+=`\nBranch: ${Z.requestId}`),{content:[{type:"text",text:X}],isError:0===W}}catch(t){if(e.taskId&&n)try{const o=n.getToolRecordClient(),s=t instanceof l.NotFoundError||t instanceof l.ValidationError||t instanceof d.ZodError
1503
+ return Y?.commitHash&&(X+=`\nCommit: ${Y.commitHash.substring(0,8)}`),Z.requestId&&(X+=`\nBranch: ${Z.requestId}`),{content:[{type:"text",text:X}],isError:0===G}}catch(t){if(e.taskId&&n)try{const o=n.getToolRecordClient(),s=t instanceof l.NotFoundError||t instanceof l.ValidationError||t instanceof d.ZodError
1502
1504
  await o.createToolRecord({...s?{}:{taskId:e.taskId},projectId:r.projectId,type:"delete-files",metadata:{status:"failed",timestamp:(new Date).toISOString(),totalOperations:e.operations?.length||e.paths?.length||0,fileOperations:e.paths?.length||e.operations?.filter(e=>e.path)?.length||0,documentOperations:e.operations?.filter(e=>e.documentId)?.length||0,paths:e.paths||e.operations?.filter(e=>e.path)?.map(e=>e.path)||[],documentIds:e.operations?.filter(e=>e.documentId)?.map(e=>e.documentId)||[],userComment:e.comment,error:(0,p.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:String(t),phase:w,attemptedTaskId:e.taskId,taskIdValidationFailed:s},note:`Failed to delete files - ${(0,p.getErrorMessage)(t)}`})}catch(t){f.error("Failed to create failure record for delete-files operation",{error:t instanceof Error?t.message:String(t),taskId:e.taskId,totalOperations:e.operations?.length||e.paths?.length||0,fileOperations:e.paths?.length||e.operations?.filter(e=>e.path)?.length||0,documentOperations:e.operations?.filter(e=>e.documentId)?.length||0})}const o=Date.now()-m
1503
1505
  if(s.recordValue("delete_files_time",o),s.incrementCounter("delete_files_errors",1),f.error("Error in delete-files operation:",{error:(0,p.getErrorMessage)(t),time:o}),(g||y)&&e.taskId){w=c.FileOperationPhase.RECORD_UPDATE_FAILURE
1504
1506
  try{const r=n.getTaskClient(),o=n.getToolRecordClient()
@@ -1590,10 +1592,10 @@ try{const o=await n.documentFileManager.getDocumentAsFile(e.documentId),s=n.docu
1590
1592
  t.filename=s?.filename
1591
1593
  const i=await c.default.readFile(o,"utf8"),{newContent:a,results:l}=await r(i,e.insertions)
1592
1594
  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)
1593
- 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(E,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
1594
- 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:x,...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"
1595
+ t?.success&&await n.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const W=await l.handleMultiFileGitCommit(E,j,L,U,{operationType:"Insert lines",toolRecordId:v,successField:"successfulInsertions",logger:y,metrics:m,metricsPrefix:"insert_lines"},n),G=l.processMultiFileOperationResults(L,"successfulInsertions"),{successfulOperations:V,failedOperations:K,totalSuccessfulChanges:Y,overallSuccess:J,successfulFiles:Q,failedFiles:Z}=G
1596
+ 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:x,...j.length>0&&W.success&&W.commitHash&&{commitHash:W.commitHash,previousCommitHash:W.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"
1595
1597
  if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
1596
- 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]
1598
+ 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",W.success&&W.commitHash&&(X+="**Git Commit:**\n",X+=`- **Commit Hash:** ${W.commitHash.substring(0,8)}\n`,X+=`- **Files Committed:** ${W.filesCommitted.length}\n`,W.previousCommitHash&&(X+=`- **Previous Commit:** ${W.previousCommitHash.substring(0,8)}\n`),X+="\n"),1===L.length){const e=L[0]
1597
1599
  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"
1598
1600
  for(const t of e.insertions.filter(e=>!e.success)){const e="between"===t.type?`between lines ${t.afterLine} and ${t.beforeLine}`:`${t.type} line ${t.line}`
1599
1601
  X+=`- ${t.type} insertion ${e}: ${t.error}\n`}}}else{X+="**Summary:**\n",X+=`- **Total Operations:** ${L.length}\n`,X+=`- **Successful:** ${V}\n`,X+=`- **Failed:** ${K}\n`,X+=`- **Total Insertions:** ${Y} successful, ${B} failed\n`,X+=`- **Total Lines Added:** ${H}\n\n`,X+="**Detailed Results:**\n\n"
@@ -1932,10 +1934,10 @@ S=B,Date.now(),M=Date.now(),x=h.FileOperationPhase.STRING_REPLACEMENT,l.incremen
1932
1934
  const H=[],U=[]
1933
1935
  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)
1934
1936
  H.push(o),o.successfulReplacements>0&&"file"===o.type&&o.path&&U.push(o.path)}Date.now(),M=Date.now()
1935
- 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
1937
+ const W=T.filter(e=>e.path&&!e.documentId).map(e=>({path:e.path})),G=await h.handleMultiFileGitCommit(q,W,H,U.length,{operationType:"Replace in files",toolRecordId:B||S||"unknown",successField:"successfulReplacements",logger:I,metrics:l,metricsPrefix:"replace_in_files"},r),V=G.commitHash
1936
1938
  Date.now(),Date.now()
1937
1939
  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}))
1938
- 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}}})
1940
+ 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,...G.success&&G.commitHash&&{commitHash:G.commitHash,previousCommitHash:G.previousCommitHash||void 0}}})
1939
1941
  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
1940
1942
  let l="## Replace In Files - Batch Results\n\n"
1941
1943
  if(s.workspaceManager)try{const e=s.workspaceManager.getCurrentContext(s.currentSessionId)
@@ -2000,21 +2002,21 @@ if(v=L,k=L,I=$.fileOperationPhase,m.incrementCounter("replace_lines_phase",1,{ph
2000
2002
  const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"replace-lines",{requestId:M,taskId:q,workspaceManager:n.workspaceManager},w,m)
2001
2003
  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})
2002
2004
  const B=[]
2003
- let H=0,U=0,G=0,W=0
2005
+ let H=0,U=0,W=0,G=0
2004
2006
  for(const e of P){const t={type:"file",path:e.path,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,totalLinesReplaced:0,totalLinesAdded:0}
2005
2007
  try{if(await c.default.access(e.fullPath),!(await c.default.stat(e.fullPath)).isFile())throw new Error(`Path is not a file: ${e.path}`)
2006
2008
  const n=await c.default.readFile(e.fullPath,"utf8"),{newContent:o,results:s}=await r(n,e.replacements)
2007
- t.replacements=s,t.successfulReplacements=s.filter(e=>e.success).length,t.failedReplacements=s.filter(e=>!e.success).length,t.totalLinesReplaced=s.filter(e=>e.success).reduce((e,t)=>e+(t.linesReplaced||0),0),t.totalLinesAdded=s.filter(e=>e.success).reduce((e,t)=>e+(t.linesAdded||0),0),o!==n&&t.successfulReplacements>0?(await c.default.writeFile(e.fullPath,o,"utf8"),t.success=!0,W++,w.info("File updated successfully",{path:e.path,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("File operation failed",{path:e.path,error:t.error})}B.push(t)}for(const e of R){const t={type:"document",documentId:e.documentId,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,totalLinesReplaced:0,totalLinesAdded:0}
2009
+ t.replacements=s,t.successfulReplacements=s.filter(e=>e.success).length,t.failedReplacements=s.filter(e=>!e.success).length,t.totalLinesReplaced=s.filter(e=>e.success).reduce((e,t)=>e+(t.linesReplaced||0),0),t.totalLinesAdded=s.filter(e=>e.success).reduce((e,t)=>e+(t.linesAdded||0),0),o!==n&&t.successfulReplacements>0?(await c.default.writeFile(e.fullPath,o,"utf8"),t.success=!0,G++,w.info("File updated successfully",{path:e.path,successfulReplacements:t.successfulReplacements,failedReplacements:t.failedReplacements,linesReplaced:t.totalLinesReplaced,linesAdded:t.totalLinesAdded})):0===t.failedReplacements&&(t.success=!0),H+=t.failedReplacements,U+=t.totalLinesReplaced,W+=t.totalLinesAdded}catch(r){t.error=(0,f.getErrorMessage)(r),t.failedReplacements=e.replacements.length,H+=t.failedReplacements,w.error("File operation failed",{path:e.path,error:t.error})}B.push(t)}for(const e of R){const t={type:"document",documentId:e.documentId,success:!1,replacements:[],successfulReplacements:0,failedReplacements:0,totalLinesReplaced:0,totalLinesAdded:0}
2008
2010
  try{const o=await n.documentFileManager.getDocumentAsFile(e.documentId),s=n.documentFileManager.getCachedDocumentInfo(e.documentId)
2009
2011
  t.filename=s?.filename
2010
2012
  const i=await c.default.readFile(o,"utf8"),{newContent:a,results:l}=await r(i,e.replacements)
2011
- 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)
2012
- 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
2013
- 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:C,...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"
2013
+ 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,G++,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,W+=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)
2014
+ 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,G,{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
2015
+ 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:W,operationsWithChanges:G,processingTime:Date.now()-b,userComment:C,...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"
2014
2016
  if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
2015
- 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]
2017
+ 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`,W>0&&(te+=`- **Total Lines Added:** ${W}\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]
2016
2018
  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"
2017
- for(const t of e.replacements.filter(e=>!e.success))te+=`- Replace ${"number"==typeof t.lines?`line ${t.lines}`:Array.isArray(t.lines)?`lines [${t.lines.join(", ")}]`:`lines ${t.lines.start}-${t.lines.end}`}: ${t.error}\n`}}else{te+="**Summary:**\n",te+=`- **Total Operations:** ${B.length}\n`,te+=`- **Successful:** ${Y}\n`,te+=`- **Failed:** ${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",te+="**Detailed Results:**\n\n"
2019
+ for(const t of e.replacements.filter(e=>!e.success))te+=`- Replace ${"number"==typeof t.lines?`line ${t.lines}`:Array.isArray(t.lines)?`lines [${t.lines.join(", ")}]`:`lines ${t.lines.start}-${t.lines.end}`}: ${t.error}\n`}}else{te+="**Summary:**\n",te+=`- **Total Operations:** ${B.length}\n`,te+=`- **Successful:** ${Y}\n`,te+=`- **Failed:** ${J}\n`,te+=`- **Total Replacements:** ${Q} successful, ${H} failed\n`,te+=`- **Total Lines Replaced:** ${U}\n`,W>0&&(te+=`- **Total Lines Added:** ${W}\n`),te+="\n",te+="**Detailed Results:**\n\n"
2018
2020
  for(const e of B){if("file"===e.type?te+=`### File: ${e.path}\n`:(te+=`### Document: ${e.documentId}`,e.filename&&(te+=` (${e.filename})`),te+="\n"),te+=`- **Status:** ${e.success?"\u2705 Success":"\u274c Failed"}\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"
2019
2021
  for(const t of e.replacements.filter(e=>!e.success))te+=` - Replace ${"number"==typeof t.lines?`line ${t.lines}`:Array.isArray(t.lines)?`lines [${t.lines.join(", ")}]`:`lines ${t.lines.start}-${t.lines.end}`}: ${t.error}\n`}te+="\n"}}R.length>0&&(te+="\u{1f4c4} **Document Changes:** Modified documents have been automatically synced back to the tracking system.\n\n")
2020
2022
  const re=Date.now()-b
@@ -2081,17 +2083,17 @@ try{const o=await r.documentFileManager.getDocumentAsFile(e.documentId),s=r.docu
2081
2083
  t.filename=s?.filename
2082
2084
  const i=await l.default.readFile(o,"utf8"),{newContent:a,results:c}=await n(i,e.replacements,e.mode)
2083
2085
  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)
2084
- 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(E,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
2085
- 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:x,...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)
2086
+ 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(E,j,L,H,{operationType:"Replace strings",toolRecordId:v,successField:"successfulReplacements",logger:y,metrics:c,metricsPrefix:"replace_strings"},r),W=d.processMultiFileOperationResults(L,"successfulReplacements"),{successfulOperations:G,failedOperations:V,totalSuccessfulChanges:K,overallSuccess:Y,successfulFiles:J,failedFiles:Q}=W
2087
+ 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:x,...j.length>0&&U.success&&U.commitHash&&{commitHash:U.commitHash,previousCommitHash:U.previousCommitHash||void 0}},note:Y?`String replacement completed: ${G}/${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:G,failedOperations:V,totalSuccessfulChanges:K,overallSuccess:Y,successfulFiles:J,failedFiles:Q},U,F)
2086
2088
  let Z="## String Replacement Results\n\n"
2087
2089
  if(r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2088
- 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]
2090
+ e.permanentBranch||(Z+=`**Branch:** \`${e.branchName}\`\n\n`)}if(Z+="**Summary:**\n",Z+=`- **Files processed:** ${G+V}\n`,Z+=`- **Successful operations:** ${G}\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]
2089
2091
  if(Z+=`**Operation:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,"file"===e.type?Z+=`**File:** ${e.path}\n`:(Z+=`**Document:** ${e.documentId}`,e.filename&&(Z+=` (${e.filename})`),Z+="\n"),e.error?Z+=`**Error:** ${e.error}\n`:(Z+=`**Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(Z+=`, ${e.failedReplacements} failed`),Z+="\n"),e.replacements.some(e=>!e.success)){Z+="**Failed Replacement Details:**\n"
2090
- for(const t of e.replacements.filter(e=>!e.success))Z+=`- "${t.old_str}" \u2192 "${t.new_str}": ${t.error}\n`}}else{Z+="**Summary:**\n",Z+=`- **Total Operations:** ${L.length}\n`,Z+=`- **Successful:** ${W}\n`,Z+=`- **Failed:** ${V}\n`,Z+=`- **Total Replacements:** ${K} successful, ${B} failed\n\n`,Z+="**Detailed Results:**\n\n"
2092
+ for(const t of e.replacements.filter(e=>!e.success))Z+=`- "${t.old_str}" \u2192 "${t.new_str}": ${t.error}\n`}}else{Z+="**Summary:**\n",Z+=`- **Total Operations:** ${L.length}\n`,Z+=`- **Successful:** ${G}\n`,Z+=`- **Failed:** ${V}\n`,Z+=`- **Total Replacements:** ${K} successful, ${B} failed\n\n`,Z+="**Detailed Results:**\n\n"
2091
2093
  for(const e of L){if("file"===e.type?Z+=`### File: ${e.path}\n`:(Z+=`### Document: ${e.documentId}`,e.filename&&(Z+=` (${e.filename})`),Z+="\n"),Z+=`- **Status:** ${e.success?"\u2705 Success":"\u274c Failed"}\n`,e.error?Z+=`- **Error:** ${e.error}\n`:(Z+=`- **Replacements:** ${e.successfulReplacements} successful`,e.failedReplacements>0&&(Z+=`, ${e.failedReplacements} failed`),Z+="\n"),e.replacements.some(e=>!e.success)){Z+="- **Failed Replacement Details:**\n"
2092
2094
  for(const t of e.replacements.filter(e=>!e.success))Z+=` - "${t.old_str}" \u2192 "${t.new_str}": ${t.error}\n`}Z+="\n"}}P.length>0&&(Z+="\u{1f4c4} **Document Changes:** Modified documents have been automatically synced back to the tracking system.\n\n")
2093
2095
  const X=Date.now()-w
2094
- return y.info("Replace strings operation completed",{duration:X,success:Y,totalOperations:L.length,successfulOperations:W,failedOperations:V}),c.incrementCounter("replace_strings_total",1,{success:Y}),{content:[{type:"text",text:Z}],isError:!Y}}catch(t){const r=Date.now()-w
2096
+ return y.info("Replace strings operation completed",{duration:X,success:Y,totalOperations:L.length,successfulOperations:G,failedOperations:V}),c.incrementCounter("replace_strings_total",1,{success:Y}),{content:[{type:"text",text:Z}],isError:!Y}}catch(t){const r=Date.now()-w
2095
2097
  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))}
2096
2098
  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
2097
2099
  const r=t(76183),n=t(94221)
@@ -2169,12 +2171,12 @@ const e=await(0,y.readResultBytes)(r.mainBasePath,A,20971520)
2169
2171
  if(!e.ok)throw new u.ValidationError(e.tooLarge?`${e.message} That is bulk, which stays in the results store: use results({ operation: "pin" }) to carry it on the branch.`:e.message)
2170
2172
  F=e.bytes,N=e.address}const z=F?F.length:"string"==typeof T?T.length:JSON.stringify(T??"").length,L=!!q,B=!!E
2171
2173
  k.info("Writing content to "+(L?"document":"file"),{taskId:D,filePath:B?E:void 0,documentId:L?q:void 0,contentLength:z,fromResult:N,format:j,append:P,comment:R})
2172
- let H,U,G,W="",V=!1,K=!1
2174
+ let H,U,W,G="",V=!1,K=!1
2173
2175
  const Y=B?E:void 0
2174
2176
  if(L){if(!r.documentFileManager)throw new Error("Document file manager not available")
2175
- W=await r.documentFileManager.getDocumentAsFile(q)
2177
+ G=await r.documentFileManager.getDocumentAsFile(q)
2176
2178
  const e=r.documentFileManager.getCachedDocumentInfo(q)
2177
- G=e?.filename,K=!0,V=!1}C=d.FileOperationPhase.TASK_VALIDATION,b.incrementCounter("write_file_phase",1,{phase:C})
2179
+ W=e?.filename,K=!0,V=!1}C=d.FileOperationPhase.TASK_VALIDATION,b.incrementCounter("write_file_phase",1,{phase:C})
2178
2180
  const J=B?Y:`document:${q}`,Q=await d.validateTaskId(D,J,o,k,b,_.metricsPrefix),Z=Q?.requestId
2179
2181
  let X=null
2180
2182
  B&&Z&&(X=await d.validateWorkItemStates(D,J,o,i,r,k,b,_.metricsPrefix))
@@ -2183,12 +2185,12 @@ B&&Z&&X&&(ee=await d.autoPromoteTaskStatus(D,Q.status,Z,X.request?.status,J,o,i,
2183
2185
  const te=await d.ensureCorrectWorkspaceContext({taskPromoted:ee.taskPromoted,requestId:Z,context:r,operation:"write_file",logger:k,metrics:b,metricsPrefix:_.metricsPrefix,requireRequestContext:B})
2184
2186
  if(!te.success)throw new Error(te.error)
2185
2187
  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)
2186
- H=e.normalizedPath,U=e.fullPath,W=U
2188
+ H=e.normalizedPath,U=e.fullPath,G=U
2187
2189
  try{await c.default.access(U),K=!0}catch{K=!1,V=!0}const t=(0,g.projectMapCreateProblem)(H,K)
2188
2190
  if(t)throw new u.ValidationError(t)
2189
2191
  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}
2190
2192
  throw e}C=d.FileOperationPhase.RECORD_CREATION,b.incrementCounter("write_file_phase",1,{phase:C})
2191
- 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)
2193
+ const{toolRecordId:re}=await d.createFileOperationRecords(D,Z,r.projectId,_.recordType,J,{operationType:L?"document":"file",documentId:L?q:void 0,documentFilename:W,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)
2192
2194
  S=re,x=re,C=_.fileOperationPhase,b.incrementCounter("write_file_phase",1,{phase:C})
2193
2195
  let ne=""
2194
2196
  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)
@@ -2196,15 +2198,15 @@ else ne="string"==typeof T?T:JSON.stringify(T,null,2)
2196
2198
  let oe=ne
2197
2199
  if(B&&r.git)try{k.debug("[WRITE-FILE] Checking for user changes before write operation")
2198
2200
  const e=await(0,p.commitUserChangesBeforeToolOperation)(r.git,"write_file",{requestId:r.workspaceManager?.getCurrentContext(r.currentSessionId)?.requestId,taskId:D},k,b)
2199
- e.userChangesCommitted&&(k.debug("[WRITE-FILE] User changes committed before write operation",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),k.info("User changes committed before write-file operation",{taskId:D,path:H,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){k.debug("[WRITE-FILE] Pre-tool commit failed, continuing with write",{error:e instanceof Error?e.message:String(e)}),k.warn("Pre-tool user changes commit failed, continuing with write operation",{taskId:D,path:H,error:e instanceof Error?e.message:String(e)})}if(!W)throw new Error("Target path not properly initialized")
2200
- if(k.debug("[WRITE-FILE] Proceeding with file write operation",{targetPath:W}),P&&K)try{oe=await c.default.readFile(W,"utf8")+ne}catch(e){k.warn("Failed to read existing content for append",{error:e})}const se=a.default.dirname(W)
2201
- await c.default.mkdir(se,{recursive:!0}),F?await c.default.writeFile(W,F):await c.default.writeFile(W,oe,"utf8")
2202
- const ie=F?F.length:oe.length,ae=!(!B||F||P)&&await(0,f.markExecutableIfShebang)(W,oe)
2201
+ e.userChangesCommitted&&(k.debug("[WRITE-FILE] User changes committed before write operation",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),k.info("User changes committed before write-file operation",{taskId:D,path:H,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){k.debug("[WRITE-FILE] Pre-tool commit failed, continuing with write",{error:e instanceof Error?e.message:String(e)}),k.warn("Pre-tool user changes commit failed, continuing with write operation",{taskId:D,path:H,error:e instanceof Error?e.message:String(e)})}if(!G)throw new Error("Target path not properly initialized")
2202
+ if(k.debug("[WRITE-FILE] Proceeding with file write operation",{targetPath:G}),P&&K)try{oe=await c.default.readFile(G,"utf8")+ne}catch(e){k.warn("Failed to read existing content for append",{error:e})}const se=a.default.dirname(G)
2203
+ await c.default.mkdir(se,{recursive:!0}),F?await c.default.writeFile(G,F):await c.default.writeFile(G,oe,"utf8")
2204
+ const ie=F?F.length:oe.length,ae=!(!B||F||P)&&await(0,f.markExecutableIfShebang)(G,oe)
2203
2205
  if(k.info("Content written successfully",{path:B?H:`document:${q}`,finalLength:ie,append:P,wasCreated:V}),L){r.documentFileManager.markForSync(q)
2204
2206
  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
2205
2207
  if(B){if(C=d.FileOperationPhase.GIT_COMMIT,b.incrementCounter("write_file_phase",1,{phase:C}),r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
2206
2208
  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} (${x})`,t=await d.commitToGit(D,H,x,x,e,r,k,b,_.metricsPrefix)
2207
- 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:x})}catch(e){k.warn("Git operations failed via shared utility",{error:e}),de=(0,m.getErrorMessage)(e)}}try{await s.updateToolRecord(x,{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`
2209
+ 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:x})}catch(e){k.warn("Git operations failed via shared utility",{error:e}),de=(0,m.getErrorMessage)(e)}}try{await s.updateToolRecord(x,{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}${W?` (${W})`:""}`:`file ${H}`}**\n`
2208
2210
  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}\`.`)
2209
2211
  const fe=Date.now()
2210
2212
  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})
@@ -2616,22 +2618,22 @@ ne&&ne.trim().length>0?(te=ne.split("\n").map(e=>`+${e}`).join("\n"),x.set(F,{fi
2616
2618
  te=await t.git.diff(se),x.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)}),x.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)})}x.set(F,{firstChange:N[0],lastChange:N[N.length-1],hasDiff:!1,diffText:"No commit hashes available for this file"})
2617
2619
  continue}const B=L[0],H=L[L.length-1]
2618
2620
  s.info(`First commit: ${B.commitHash?B.commitHash:"unknown"}, Last commit: ${H.commitHash?H.commitHash:"unknown"}`)
2619
- let U=null,G=!1
2620
- if(t.git&&B.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,F,B.commitHash),U?(G=!0,s.info(`Found previous commit: ${U} for ${F}`)):s.info(`No previous commit found for ${F}, likely a new file`)}catch(ce){s.warn(`Error finding previous commit for ${F}`,{error:(0,u.getErrorMessage)(ce)})}const W=G&&U?{...B,commitHash:U,status:"pre-task"}:B
2621
- if(s.info(`Using ${G?"pre-task":"first task"} commit as baseline for ${F}: ${W.commitHash}`),s.info(`Last commit for ${F}: ${H.commitHash}`),s.info("Commits are "+(W.commitHash===H.commitHash?"identical":"different")),B.commitHash===H.commitHash&&!G){if(m&&!G&&t.git&&H.commitHash)try{const le=[`${H.commitHash}:${F}`]
2621
+ let U=null,W=!1
2622
+ if(t.git&&B.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,F,B.commitHash),U?(W=!0,s.info(`Found previous commit: ${U} for ${F}`)):s.info(`No previous commit found for ${F}, likely a new file`)}catch(ce){s.warn(`Error finding previous commit for ${F}`,{error:(0,u.getErrorMessage)(ce)})}const G=W&&U?{...B,commitHash:U,status:"pre-task"}:B
2623
+ if(s.info(`Using ${W?"pre-task":"first task"} commit as baseline for ${F}: ${G.commitHash}`),s.info(`Last commit for ${F}: ${H.commitHash}`),s.info("Commits are "+(G.commitHash===H.commitHash?"identical":"different")),B.commitHash===H.commitHash&&!W){if(m&&!W&&t.git&&H.commitHash)try{const le=[`${H.commitHash}:${F}`]
2622
2624
  s.info(`Getting new file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${F}`)
2623
2625
  const de=await t.git.show(le)
2624
2626
  if(de&&de.trim().length>0){let ue=""
2625
2627
  const he=de.split("\n")
2626
2628
  for(const pe of he)ue+=`+${pe}\n`
2627
- x.set(F,{firstChange:W,lastChange:H,hasDiff:!0,diffText:ue}),s.info(`Formatted new file content as diff, ${he.length} lines`)
2628
- continue}}catch(fe){s.warn(`Error getting new file content for ${F}`,{error:(0,u.getErrorMessage)(fe)})}x.set(F,{firstChange:W,lastChange:H,hasDiff:!1,diffText:"Only one commit available for this file"})
2629
- continue}if(B.commitHash===H.commitHash&&G){if(U===B.commitHash){x.set(F,{firstChange:W,lastChange:H,hasDiff:!1,diffText:"No changes detected"})
2629
+ x.set(F,{firstChange:G,lastChange:H,hasDiff:!0,diffText:ue}),s.info(`Formatted new file content as diff, ${he.length} lines`)
2630
+ continue}}catch(fe){s.warn(`Error getting new file content for ${F}`,{error:(0,u.getErrorMessage)(fe)})}x.set(F,{firstChange:G,lastChange:H,hasDiff:!1,diffText:"Only one commit available for this file"})
2631
+ continue}if(B.commitHash===H.commitHash&&W){if(U===B.commitHash){x.set(F,{firstChange:G,lastChange:H,hasDiff:!1,diffText:"No changes detected"})
2630
2632
  continue}s.info("Special case: First and last task commits are the same, but pre-task commit is different")}let V="",K=!1
2631
- 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,x.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V})
2633
+ if(m||"pre-task"!==G.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,x.set(F,{firstChange:G,lastChange:H,hasDiff:K,diffText:V})
2632
2634
  continue}let me
2633
- 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")
2634
- try{const ye=await t.git.show([`${W.commitHash}:${F}`]),we=await t.git.show([`${H.commitHash}:${F}`])
2635
+ if("pre-task"===G.status?(me=[`${G.commitHash}..${H.commitHash}`,"--",F],s.info(`Comparing pre-task commit with final commit: ${G.commitHash?G.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&&G.commitHash!==H.commitHash){s.info("No diff found between commits but commits are different, trying direct file content comparison")
2636
+ try{const ye=await t.git.show([`${G.commitHash}:${F}`]),we=await t.git.show([`${H.commitHash}:${F}`])
2635
2637
  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")
2636
2638
  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}`]
2637
2639
  s.info(`Trying fallback to show file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${F}`)
@@ -2640,7 +2642,7 @@ if(Se&&Se.trim().length>0){if(m){let xe=""
2640
2642
  const Ce=Se.split("\n")
2641
2643
  for(const _e of Ce)xe+=`+${_e}\n`
2642
2644
  V=xe}else V="File created during this task. Content:\n"+Se
2643
- K=!0,s.info(`Fallback succeeded, got file content of length ${Se.length}`)}}catch(Ee){s.warn(`Fallback also failed for ${F}`,{error:Ee instanceof Error?Ee.message:String(Ee)})}}x.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",x.set(F,{firstChange:W,lastChange:H,hasDiff:K,diffText:V})}let C=`# File Changes for Task: ${f}\n\n`
2645
+ K=!0,s.info(`Fallback succeeded, got file content of length ${Se.length}`)}}catch(Ee){s.warn(`Fallback also failed for ${F}`,{error:Ee instanceof Error?Ee.message:String(Ee)})}}x.set(F,{firstChange:G,lastChange:H,hasDiff:K,diffText:V||"No changes detected between commits"})}else K=G.commitHash!==H.commitHash,V=K?"File has changes":"No changes detected between commits",x.set(F,{firstChange:G,lastChange:H,hasDiff:K,diffText:V})}let C=`# File Changes for Task: ${f}\n\n`
2644
2646
  b&&"object"==typeof b&&(C+="## Task Information\n",C+=`- **Title**: ${b.title||"Unknown"}\n`,C+=`- **Status**: ${b.status||"Unknown"}\n`,C+=`- **Description**: ${b.description||"No description"}\n\n`)
2645
2647
  const _=3e3
2646
2648
  let E=0,q=!1
@@ -2729,18 +2731,18 @@ if(B&&N?.readsProjectFiles){const e=await(0,x.committedReadView)(B,L,function(e)
2729
2731
  if(!n.ok)return null
2730
2732
  const o=await n.json()
2731
2733
  return o?.success&&o.data?.workdir?{workdir:o.data.workdir,kind:String(o.data.kind??"")}:null},warn:(t,r)=>e.warn(t,r)}}(j))
2732
- 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)
2734
+ B=e.context,e.applied&&L&&!_.has(L)&&(_.add(L),H=!0)}const U=`${Date.now()}-${Math.random().toString(36).slice(2,10)}`,W=e=>{const{getActionServerBaseUrl:r}=t(79414)
2733
2735
  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(()=>{})}
2734
- let W
2735
- G("start")
2736
+ let G
2737
+ W("start")
2736
2738
  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))
2737
2739
  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}
2738
- 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)
2740
+ G=await N.handler(g,a)}else j.info(`Tool ${r} not found, returning negative response`),G={content:[{type:"text",text:`The tool '${r}' does not exist.`}]}}finally{W("end")}Date.now(),G=await(0,b.enrichWithAnchoredAnnotations)(r,g,G,o??void 0,B?.projectId,j,B??void 0,z?.sessionId)
2739
2741
  const V=(0,S.noteNudgeFor)({toolName:r,operation:g?.operation,sessionId:z?.sessionId,requestId:z?.sessionId?q.get(z.sessionId)??void 0:void 0})
2740
2742
  if(V){const{getActionServerBaseUrl:e}=t(79414)
2741
- 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:x.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
2743
+ 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 G=(0,S.withNoteNudge)(G,V),H&&Array.isArray(G?.content)&&G.content.push({type:"text",text:x.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
2742
2744
  if(Array.isArray(r))for(const e of r)e&&"string"==typeof e.text&&(e.text=t(e.text))
2743
- 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"})
2745
+ return e}(G)}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"})
2744
2746
  let n="Unknown error"
2745
2747
  if(n=t instanceof $.ZodError?function(e,t){return 1===e.issues.length?r(e.issues[0],t):e.issues.map(e=>r(e,t)).join("; ")}(t,g):(0,d.getStructuredErrorMessage)(t),t instanceof $.ZodError&&w.SHORT_FORM_TOOLS.has(e.params.name)){const t=(0,w.fullContract)(e.params.name,{testsRequired:(0,l.areTestsRequiredForCompletion)(),authenticated:!0})
2746
2748
  t&&(n+=`\n\nThe full contract:\n${t}`)}return{content:[{type:"text",text:`Error: ${n}`}],isError:!0}}var O,M}),j.info("Tools registration complete")}
@@ -3043,7 +3045,7 @@ let L=!1
3043
3045
  try{const e=await P.listRequests({limit:2,sortBy:"created_at",sortDirection:"desc"})
3044
3046
  e&&2===e.length&&e.every(e=>"quick-edit"===e.type)&&(L=!0,q.info("Detected two consecutive quick-edits, will recommend normal request"))}catch(e){q.warn("Could not check recent requests for recommendation",{error:e})}const B=[]
3045
3047
  let H=[],U=null
3046
- const{execFile:G}=await Promise.resolve().then(()=>t.t(35317,23)),W=$.mainBasePath||$.basePath,V=e=>new Promise(t=>G("git",e,{cwd:W,maxBuffer:33554432},(e,r)=>t(e?null:String(r)))),K=await(0,b.resolveDefaultBranch)(W)
3048
+ const{execFile:W}=await Promise.resolve().then(()=>t.t(35317,23)),G=$.mainBasePath||$.basePath,V=e=>new Promise(t=>W("git",e,{cwd:G,maxBuffer:33554432},(e,r)=>t(e?null:String(r)))),K=await(0,b.resolveDefaultBranch)(G)
3047
3049
  try{const e=async e=>V(["show",`refs/heads/${K}:${e.replace(/^\.\//,"")}`]),t=[]
3048
3050
  if("write"!==I||"string"!=typeof N.path||N.documentId){if("replace"===I&&Array.isArray(N.operations))for(const r of N.operations){if("string"!=typeof r?.path||r.documentId||!Array.isArray(r.replacements))continue
3049
3051
  const n=await e(r.path)
@@ -3089,16 +3091,16 @@ return r(await new Promise((t,r)=>o("git",["diff","--no-color","--no-renames","-
3089
3091
  try{await(0,p.handleCompleteTask)({taskId:ee,summary:`${M}${E?" (partial success)":""}`},re,S,x,C),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})
3090
3092
  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(()=>""):""
3091
3093
  H&&(D=D?`${D}\n${H}`:H),q.debug("Completing request")
3092
- const U=E?`Quick change completed with partial success: ${M}. Some replacements failed - see details below.`:`${M}${D?`\n\n\`\`\`diff\n${D}\n\`\`\``:""}`,G=E?`${I} operation partially completed - some operations succeeded, some failed`:`${I} operation executed successfully`
3093
- let W=!1
3094
+ const U=E?`Quick change completed with partial success: ${M}. Some replacements failed - see details below.`:`${M}${D?`\n\n\`\`\`diff\n${D}\n\`\`\``:""}`,W=E?`${I} operation partially completed - some operations succeeded, some failed`:`${I} operation executed successfully`
3095
+ let G=!1
3094
3096
  const Y=await V(["rev-list","--count",`refs/heads/${K}..refs/heads/requests/${X}`])
3095
3097
  let J
3096
- 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,x,C,_)
3098
+ if(null!==Y&&Number(Y.trim())>0?G=!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)),G){try{const e=await(0,f.handleCompleteRequest)({requestId:X,summary:U},$,S,x,C,_)
3097
3099
  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)
3098
3100
  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})}
3099
3101
  let t=[]
3100
3102
  try{const e=await P.getRequest(X),r=e?.acceptanceCriteria||[]
3101
- 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,x,C,_)
3103
+ t=Array.from((0,g.buildCriteriaKeyMap)(r).keys()).map(e=>({key:e,evidence:W}))}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,x,C,_)
3102
3104
  let s=null
3103
3105
  try{s=await P.getRequest(X)}catch{}if(r.isError||"completed"!==s?.status){const e=r.content?.[0]?.text||"unknown merge failure"
3104
3106
  q.error("Quick change merge to main failed",{requestId:X,status:s?.status,mergeText:e})
@@ -3112,7 +3114,7 @@ if(t.isError)q.error("Request completion failed, attempting direct completion",{
3112
3114
  else{if("ready_for_review"===r?.status)return{content:[{type:"text",text:n(X,M,K)}]}
3113
3115
  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
3114
3116
  q.info("Quick fix completed",{requestId:X,duration:Q,hasPartialFailure:E})
3115
- const Z=(0,d.readAlchemyMeta)(b)?.toolRecordId,ne=W?`merged to main${J?` (merge commit ${J.substring(0,8)})`:""}.\n**Active branch:** main`:"recorded (a document edit; nothing in git to merge)."
3117
+ const Z=(0,d.readAlchemyMeta)(b)?.toolRecordId,ne=G?`merged to main${J?` (merge commit ${J.substring(0,8)})`:""}.\n**Active branch:** main`:"recorded (a document edit; nothing in git to merge)."
3116
3118
  let oe
3117
3119
  return oe=E?`\u26a0\ufe0f Quick change completed with partial success\n\n**Request:** ${X}\n**Title:** ${M}\n**Operation:** ${I}\n**Duration:** ${Q}ms\n**Status:** Some operations succeeded, some failed\n\nThe successful changes have been applied and ${ne}\n\n**\u26a0\ufe0f Note:** This quick-fix had partial failures. See operation details below:\n\n${b.content[0].text}`:`\u2705 Quick change completed successfully!\n\n**Request:** ${X}\n**Title:** ${M}\n**Operation:** ${I}\n**Duration:** ${Q}ms\n\nThe change has been applied and ${ne}`+(Z?`\n[RECORD:${Z}]`:""),L&&(oe+="\n\n\u{1f4a1} **Recommendation:** You've created several quick-edits in a row. For better traceability and organization, consider creating a proper code request with multiple tasks for your next change."),{content:[{type:"text",text:oe}],...(0,d.alchemyMeta)({kind:`quick-${I}`,toolRecordId:Z})}}catch(e){if(q.error("Quick change workflow failed after request creation, cleaning up",{requestId:X,error:(0,w.getErrorMessage)(e)}),ee)try{await R.updateTaskStatus(ee,"canceled"),q.info("Canceled task after workflow failure",{taskId:ee})}catch(e){q.warn("Failed to cancel task during cleanup",{taskId:ee,error:e instanceof Error?e.message:String(e)})}$.workspaceManager?.releaseSession(o(X))
3118
3120
  try{await P.updateRequest(X,{status:"canceled"}),q.info("Canceled request after workflow failure",{requestId:X}),$.workspaceManager&&(await $.workspaceManager.cancelRequest(X),q.info("Cleaned up branch/worktree after workflow failure",{requestId:X}))}catch(e){q.warn("Failed to cancel request during cleanup",{requestId:X,error:e instanceof Error?e.message:String(e)})}throw e}}catch(e){const t=Date.now()-T
@@ -3121,7 +3123,7 @@ const a=t(95122),c=t(81563),l=t(59860),d=t(82272),u=t(14079),h=t(69442),p=t(8035
3121
3123
  const r=t(76183),n=t(43707)
3122
3124
  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
3123
3125
  const r=t(76183),n=t(43707)
3124
- 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),x=t(34150),C=t(26473),_=t(6309),E=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)(x.def),(0,r.registerTool)($.def),(0,r.registerTool)(S.def),(0,r.registerTool)(C.def),(0,r.registerTool)(_.def),(0,r.registerTool)(E.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.`)
3126
+ 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),x=t(34150),C=t(26473),_=t(6309),E=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),W=t(68456),G=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)(x.def),(0,r.registerTool)($.def),(0,r.registerTool)(S.def),(0,r.registerTool)(C.def),(0,r.registerTool)(_.def),(0,r.registerTool)(E.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)(W.def),(0,r.registerTool)(G.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.`)
3125
3127
  if(l.has(e.name))throw new Error(`Duplicate tool registration: '${e.name}'`)
3126
3128
  c.push(e),l.set(e.name,e)}function n(e){const t=e.replace(/\s+/g," ").trim(),r=/^(.{20,200}?[.!?])(\s|$)/.exec(t)
3127
3129
  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
@@ -3132,33 +3134,33 @@ return c.filter(r=>!(r.debugOnly&&!t||("function"==typeof r.hidden?r.hidden(e):r
3132
3134
  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
3133
3135
  const s=t(25106),i=t(3444),a=t(92040),c=[],l=new Map
3134
3136
  let d=!1
3135
- 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,x,C,_,E){const q=x.child({tool:"complete_request"}),T=C.startTimer("tool_execution_time")
3136
- try{const{requestId:s,summary:i,worthKnowing:j,skipTestsReason:P,skipTests:R,criteriaComments:A,couldNotCheck:O,guidance:M,replacesSummary:D}=e
3137
+ 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,x,C,_,E,q){const T=C.child({tool:"complete_request"}),j=_.startTimer("tool_execution_time")
3138
+ try{const{requestId:s,summary:i,worthKnowing:P,skipTestsReason:R,skipTests:A,criteriaComments:O,couldNotCheck:M,guidance:D,replacesSummary:F,notChecked:N}=e
3137
3139
  if(!s)throw(0,o.createValidationError)("Request ID is required")
3138
3140
  if(!i)throw(0,o.createValidationError)("Summary is required")
3139
- if(P&&R&&R.length>0)throw(0,o.createValidationError)("Provide either skipTestsReason (skip all) or skipTests (per-command), not both.")
3140
- if(!0!==D)try{const e=await S.getRequestClient().getRequest(s),t=String(e?.completionSummary??"").trim()
3141
- 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})
3142
- const F=E?.sessionId??t.currentSessionId,N=E??(F?{providerId:"opencode",sessionId:F,createdAt:(new Date).toISOString()}:void 0)
3141
+ if(R&&A&&A.length>0)throw(0,o.createValidationError)("Provide either skipTestsReason (skip all) or skipTests (per-command), not both.")
3142
+ if(!0!==F)try{const e=await x.getRequestClient().getRequest(s),t=String(e?.completionSummary??"").trim()
3143
+ if(t)return T.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){T.debug("earlier-summary check could not run; completion continues",{requestId:s,error:(0,h.getErrorMessage)(e)})}T.info("Starting request completion",{requestId:s,skipAllRequested:!!R,perCommandSkipCount:A?.length||0})
3144
+ const z=q?.sessionId??t.currentSessionId,L=q??(z?{providerId:"opencode",sessionId:z,createdAt:(new Date).toISOString()}:void 0)
3143
3145
  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)})
3144
3146
  if(e.ok){const t=await e.json()
3145
- 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})
3146
- let z=null,L=null
3147
+ t?.data?.committed&&T.info("Committed uncommitted worktree changes before completing",{requestId:s,sha:t.data.sha?.slice(0,8),files:t.data.files?.length??0})}}catch(e){T.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=>x.proxyPost(`/api/projects/${t.projectId}/tool-records`,e),log:T})
3148
+ let B=null,H=null
3147
3149
  try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/branch-head?requestId=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(5e3)})
3148
3150
  if(e.ok){const t=await e.json()
3149
- z=t?.data?.sha??null,L=t?.data?.tree??null}}catch(e){q.debug("branch-head fetch failed; server will fall back",{error:(0,h.getErrorMessage)(e)})}const B=z?{headSha:z,...L?{headTree:L}:{}}:{}
3150
- let H=null
3151
+ B=t?.data?.sha??null,H=t?.data?.tree??null}}catch(e){T.debug("branch-head fetch failed; server will fall back",{error:(0,h.getErrorMessage)(e)})}const U=B?{headSha:B,...H?{headTree:H}:{}}:{}
3152
+ let W=null
3151
3153
  try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/request-diff-stats?requestId=${encodeURIComponent(s)}`,{signal:AbortSignal.timeout(5e3)})
3152
3154
  if(e.ok){const r=await e.json()
3153
3155
  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
3154
- 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
3155
- 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}:{}
3156
- let G,W=null
3157
- try{const e=await S.proxyGet(`/api/projects/${t.projectId}/requests/${s}/kind`)
3158
- 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))
3156
+ if(W={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 x.proxyGet(`/api/projects/${t.projectId}/test-processes`),r=Array.isArray(e)?e:Array.isArray(e?.data)?e.data:[],o=await x.getRequestClient().getRequest(s).catch(()=>null),i="string"==typeof o?.exchangeMessageCode&&o.exchangeMessageCode?await(x.getSessionMessageClient()?.getMessage(o.exchangeMessageCode).catch(()=>null)):null
3157
+ W.checkLosses=await(0,l.signoffCheckLosses)(n,r,o?{...o,askedAt:i?.createdAt??null}:null)}catch(e){T.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){T.debug("request-diff-stats fetch failed; review scaling falls back to the full panel",{error:(0,h.getErrorMessage)(e)})}const G=W?{diffStats:W}:{}
3158
+ let V,K=null
3159
+ try{const e=await x.proxyGet(`/api/projects/${t.projectId}/requests/${s}/kind`)
3160
+ K=(e?.data??e)?.kind??null}catch(e){T.debug("request kind unavailable; kind checks skipped",{error:(0,h.getErrorMessage)(e)})}try{if("writing"===K){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))
3159
3161
  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(", ")
3160
3162
  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))]
3161
- return{content:[{type:"text",text:`Completion NOT recorded: this is a writing request, and it changes code that a sibling request is changing.\n\n${e.join("\n")}\n\nA writing request documents the product as it is. Revert those changes on your branch. If your text depends on that work, wait on it: request_operations wait-on with waitsOn: ${JSON.stringify(r)}. Do not implement it and do not delete the text that describes it.`}],isError:!0}}}}catch(e){q.debug("sibling-overlap check could not run; completion continues",{error:(0,h.getErrorMessage)(e)})}try{if("drive"===W){const e=function(e,t){const r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[]
3163
+ return{content:[{type:"text",text:`Completion NOT recorded: this is a writing request, and it changes code that a sibling request is changing.\n\n${e.join("\n")}\n\nA writing request documents the product as it is. Revert those changes on your branch. If your text depends on that work, wait on it: request_operations wait-on with waitsOn: ${JSON.stringify(r)}. Do not implement it and do not delete the text that describes it.`}],isError:!0}}}}catch(e){T.debug("sibling-overlap check could not run; completion continues",{error:(0,h.getErrorMessage)(e)})}try{if("drive"===K){const e=function(e,t){const r=Array.isArray(e?.acceptanceCriteria)?e.acceptanceCriteria:[]
3162
3164
  if(0===r.length)return null
3163
3165
  const o=e=>String(e??"").trim().toLowerCase(),s=new Set((Array.isArray(t?.couldNotCheck)?t.couldNotCheck:[]).map(o).filter(Boolean)),i=new Set((t?.commentedKeys??[]).map(o).filter(Boolean)),a=(e,t)=>e?.id?(0,n.criterionShortKey)(String(e.id),t):"",c=r.filter((e,t)=>{if((0,n.isUserOwnCheck)(e))return!1
3164
3166
  const r=(0,n.latestRow)(e,["driver"])?.verdict
@@ -3168,7 +3170,7 @@ return!(c&&s.has(c)&&i.has(c))})
3168
3170
  if(0===c.length)return null
3169
3171
  const l=c.slice(0,8).map(e=>{const t=r.indexOf(e),n=a(e,t),o=String(e?.description??"").trim()
3170
3172
  return` - ${n?`[${n}] `:""}${o.length>120?`${o.slice(0,117)}...`:o}`}).join("\n")
3171
- 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)})
3173
+ 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 x.getRequestClient().getRequest(s),{couldNotCheck:M,commentedKeys:(O??[]).map(e=>e.key)})
3172
3174
  if(e)return{content:[{type:"text",text:e}],isError:!0}
3173
3175
  const r=await async function(e,t,r,o){const s=(Array.isArray(r)?r:[]).map(e=>String(e??"").trim().toLowerCase()).filter(Boolean)
3174
3176
  if(0===s.length)return[]
@@ -3177,129 +3179,140 @@ return a.forEach((e,t)=>{if(!e?.id)return
3177
3179
  const r=(0,n.criterionShortKey)(String(e.id),t).toLowerCase()
3178
3180
  if(!s.includes(r))return
3179
3181
  const i=(o??[]).find(e=>String(e?.key??"").trim().toLowerCase()===r)
3180
- c.push({criterionId:String(e.id),reason:i?.comment??null})}),c}(S.getRequestClient(),s,O,A)
3181
- 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=[]
3182
- if(A&&A.length>0)try{const e=S.getRequestClient(),r=await e.getRequest(s),n=r?.acceptanceCriteria||[]
3183
- 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}
3182
+ c.push({criterionId:String(e.id),reason:i?.comment??null})}),c}(x.getRequestClient(),s,M,O)
3183
+ r.length>0&&await x.proxyPost(`/api/projects/${t.projectId}/requests/${s}/drive-not-checked`,{items:r})}}catch(e){T.debug("drive-claim stamp check could not run; completion continues",{error:(0,h.getErrorMessage)(e)})}const Y=[]
3184
+ if(O&&O.length>0)try{const e=x.getRequestClient(),r=await e.getRequest(s),n=r?.acceptanceCriteria||[]
3185
+ if(T.info("Recording coder criteria comments",{requestId:s,comments:O.length,criteriaFetched:n.length}),0===n.length)return T.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}
3184
3186
  {const e=(0,p.buildCriteriaKeyMap)(n),r=[],o=[]
3185
- 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()):[]
3187
+ for(const t of O){const n=e.get(t.key),s=Array.isArray(t.verify)?t.verify.filter(e=>"string"==typeof e&&e.trim()):[]
3186
3188
  n&&s.length>0&&"subjective"===n.kind?r.push({criterionId:n.id,evidence:`${t.comment}\n\nWhat I ran (a judgment decides this criterion, not these): ${s.join(" ; ")}`}):n?r.push({criterionId:n.id,evidence:t.comment,...s.length?{verify:s}:{}}):o.push(t.key)}if(o.length>0){const e=(0,p.formatCriteriaWithKeys)(n)
3187
- return q.warn("criteriaComments keys did not match any criterion",{requestId:s,unmatched:o}),{content:[{type:"text",text:`Completion NOT recorded: ${o.length} criteriaComments key(s) match no criterion on ${s}: ${o.join(", ")}.\n\nThe key's letter is a checksum over the criterion's id, so a wrong letter is refused rather than guessed at -- otherwise your evidence would attach to the wrong criterion. Copy the keys exactly as listed:\n\n${e}\n\nRe-run complete with the exact keys. Nothing was recorded, so no verdict was attached to the wrong criterion.`}],isError:!0}}const i=[]
3189
+ return T.warn("criteriaComments keys did not match any criterion",{requestId:s,unmatched:o}),{content:[{type:"text",text:`Completion NOT recorded: ${o.length} criteriaComments key(s) match no criterion on ${s}: ${o.join(", ")}.\n\nThe key's letter is a checksum over the criterion's id, so a wrong letter is refused rather than guessed at -- otherwise your evidence would attach to the wrong criterion. Copy the keys exactly as listed:\n\n${e}\n\nRe-run complete with the exact keys. Nothing was recorded, so no verdict was attached to the wrong criterion.`}],isError:!0}}const i=[]
3188
3190
  for(const e of r.flatMap(e=>e.verify??[])){const r=(0,m.gitVerifyProblem)(e)??(t.git?await(0,m.nodeCheckProblem)(t.git,`requests/${s}`,e):null)
3189
- r&&i.push(` ${e}\n -> ${r}`)}if(i.length>0)return q.warn("verify commands that cannot prove anything; completion refused",{requestId:s,count:i.length}),{content:[{type:"text",text:`Completion NOT recorded: ${i.length} verify command(s) can never prove their criterion.\n\n${i.join("\n")}\n\nReplace them and complete again. Nothing was recorded.`}],isError:!0}
3190
- const a=e=>{const t=n.find(t=>t?.id===e.criterionId),r="string"==typeof t?.command&&t.command.trim()?[t.command.trim()]:[],o=t?.standing&&Array.isArray(t.verify)?t.verify.filter(e=>"string"==typeof e&&!!e.trim()):r
3191
+ r&&i.push(` ${e}\n -> ${r}`)}if(i.length>0)return T.warn("verify commands that cannot prove anything; completion refused",{requestId:s,count:i.length}),{content:[{type:"text",text:`Completion NOT recorded: ${i.length} verify command(s) can never prove their criterion.\n\n${i.join("\n")}\n\nReplace them and complete again. Nothing was recorded.`}],isError:!0}
3192
+ const a=e=>{const t=n.find(t=>t?.id===e.criterionId)
3193
+ if((0,$.hasBaseline)(t))return[]
3194
+ const r="string"==typeof t?.command&&t.command.trim()?[t.command.trim()]:[],o=t?.standing&&Array.isArray(t.verify)?t.verify.filter(e=>"string"==typeof e&&!!e.trim()):r
3191
3195
  return e.verify?.length?[...o,...e.verify.filter(e=>!o.includes(e))]:[]},c=[...new Set(r.flatMap(a))]
3192
3196
  let l=null
3193
- const d=function(e,t,r){return!(!t||0===r.length)&&r.every(r=>(Array.isArray(e)?e:[]).some(e=>(Array.isArray(e?.ledger)?e.ledger:[]).some(e=>"verify"===e?.source&&"met"===e?.verdict&&e?.ran===r&&"string"==typeof e?.commit&&!!e.commit&&(e.commit.startsWith(t)||t.startsWith(e.commit)))))}(n,z,c)
3194
- if(d&&q.info("verify preflight: every proof already passed on this head; not run again",{requestId:s,sha:z}),c.length>0&&!d)try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/verify-preflight`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:s,commands:c,background:!0,proofs:r.flatMap(e=>a(e).map(t=>({criterionId:e.criterionId,command:t})))}),signal:AbortSignal.timeout(18e4)}),t=e.ok?await e.json():null,n=t?.data
3195
- n?.pending&&q.info("verify preflight started beside the sign-off check; the merge waits for it",{requestId:s,sha:n.sha,commands:c.length}),n?.available&&Array.isArray(n.results)&&(l=n),n?.available&&(n.dropped??0)>0&&q.warn("verify preflight dropped commands over the ceiling",{requestId:s,dropped:n.dropped})
3197
+ const d=function(e,t,r){return!(!t||0===r.length)&&r.every(r=>(Array.isArray(e)?e:[]).some(e=>(Array.isArray(e?.ledger)?e.ledger:[]).some(e=>"verify"===e?.source&&"met"===e?.verdict&&e?.ran===r&&"string"==typeof e?.commit&&!!e.commit&&(e.commit.startsWith(t)||t.startsWith(e.commit)))))}(n,B,c)
3198
+ if(d&&T.info("verify preflight: every proof already passed on this head; not run again",{requestId:s,sha:B}),c.length>0&&!d)try{const e=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/verify-preflight`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:s,commands:c,background:!0,proofs:r.flatMap(e=>a(e).map(t=>({criterionId:e.criterionId,command:t})))}),signal:AbortSignal.timeout(18e4)}),t=e.ok?await e.json():null,n=t?.data
3199
+ n?.pending&&T.info("verify preflight started beside the sign-off check; the merge waits for it",{requestId:s,sha:n.sha,commands:c.length}),n?.available&&Array.isArray(n.results)&&(l=n),n?.available&&(n.dropped??0)>0&&T.warn("verify preflight dropped commands over the ceiling",{requestId:s,dropped:n.dropped})
3196
3200
  const o=n?.available&&Array.isArray(n.results)?n.results.filter(e=>!e.ok&&!0===e.unparsed):[]
3197
3201
  if(o.length>0)return{content:[{type:"text",text:`Completion NOT recorded: ${o.length} of your own verify command(s) do not parse in the shell, so they never ran and prove nothing:\n\n`+o.map(e=>` ${e.command}\n -> ${e.output?e.output.split("\n")[0]:"syntax error"}`).join("\n")+"\n\nFix the command (quoting, backquotes, parentheses), then complete again. Nothing was recorded."}],isError:!0}
3198
3202
  const i=n?.available&&Array.isArray(n.results)?n.results.filter(e=>!e.ok&&(e.timedOut||e.couldNotRun)):[]
3199
- i.length>0&&q.info("verify preflight: some proofs did not finish or could not run here; the gate decides them",{requestId:s,count:i.length,sha:n?.sha})
3203
+ i.length>0&&T.info("verify preflight: some proofs did not finish or could not run here; the gate decides them",{requestId:s,count:i.length,sha:n?.sha})
3200
3204
  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))
3201
- 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})
3205
+ if(d.length>0&&0===u.length&&T.warn("verify preflight failed but the gate environment is ambiguous; completing anyway",{requestId:s,failed:d.length,sha:n?.sha}),u.length>0){T.warn("verify preflight failed; completion refused",{requestId:s,failed:u.length,sha:n?.sha})
3202
3206
  const e=u.map(e=>` ${e.command}\n -> exit ${e.exit}${e.timedOut?" (timed out)":""}${e.output?`: ${e.output.split("\n")[0]}`:""}`).join("\n")
3203
- 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)
3207
+ 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){T.debug("verify preflight unavailable; completing without it",{requestId:s,error:(0,h.getErrorMessage)(e)})}if(r.length>0&&(await x.proxyPost(`/api/projects/${t.projectId}/requests/${s}/verify-criteria`,{verifications:r,dimension:"coder"}),Y.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)
3204
3208
  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)}]:[]}))
3205
- 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}`)
3206
- 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)
3207
- 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
3208
- 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}`
3209
- 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
3209
+ e.length>0&&await x.proxyPost(`/api/projects/${t.projectId}/requests/${s}/proof-evidence`,{commitSha:l.sha,tree:l.tree??null,proofs:e}).catch(e=>T.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 T.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}`)
3210
+ if(e.determined&&e.files.length>0)return T.warn("completion refused: committed conflict markers on the branch",{requestId:s,files:e.files}),j({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){T.warn("conflict-marker precheck could not run; leaving it to the merge gate",{requestId:s,error:(0,h.getErrorMessage)(e)})}if(!S.has(s))try{const e=await(0,d.scanStaleProse)(t.git,`requests/${s}`),r=(0,d.renderStaleProse)(e)
3211
+ if(r)return S.add(s),T.info("completion paused once: prose the branch made stale",{requestId:s,gone:e.goneReferences.length,previewUrls:e.previewUrls.length}),j({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){T.warn("stale-prose scan could not run; completing without it",{requestId:s,error:(0,h.getErrorMessage)(e)})}}let J,Q,Z=null
3212
+ try{const e=await x.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}`
3213
+ if(o&&t.mainBasePath&&i&&(Z=await(0,y.measureChangesSinceReview)(t.mainBasePath,{branchRef:a,reviewedSha:o.commit,defaultBranch:i,acceptedSha:"string"==typeof e?.diffSummary?.reviewAcceptedSha?e.diffSummary.reviewAcceptedSha:null}),Z&&(J=Z.authoredCommits)),B&&t.mainBasePath&&i){const e=new Set
3210
3214
  for(const t of r){const r=(0,n.latestRow)(t,[(0,n.isUserOwnCheck)(t)?"you":"reviewer"])
3211
- "met"===r?.verdict&&"string"==typeof r.commit&&r.commit&&r.commit!==z&&e.add(r.commit)}Y=[]
3215
+ "met"===r?.verdict&&"string"==typeof r.commit&&r.commit&&r.commit!==B&&e.add(r.commit)}Q=[]
3212
3216
  for(const r of[...e].slice(0,6)){const e=await(0,y.measureChangesSinceReview)(t.mainBasePath,{branchRef:a,reviewedSha:r,defaultBranch:i}).catch(()=>null)
3213
- 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)
3214
- 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})
3215
- 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}:{}}))
3216
- G=e?.data||e}catch(e){const t=(0,h.getErrorMessage)(e)
3217
- 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`
3218
- 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)
3217
+ Q.push({sha:r,since:e?e.newLines+e.droppedLines>0?"changed":"same":"unknown"})}}}catch(e){T.debug("changes-since-review measure unavailable; the server keeps the review as it is",{error:(0,h.getErrorMessage)(e)})}if(W&&W.filesChanged>0)try{const e=await x.getRequestClient().getRequest(s)
3218
+ if("merge"===(0,v.kindProfile)(e?.type).finalize){const r=await(0,k.closeOneTask)(x.getTaskClient(),{projectId:t.projectId,requestId:s,title:String(e?.title||s),summary:i})
3219
+ r&&((0,I.clearActiveTaskCache)(),T.info("work in one piece: the request's one task closed",{requestId:s,taskId:r}))}}catch(e){T.warn("could not record the one task; completion continues as before",{requestId:s,error:(0,h.getErrorMessage)(e)})}try{const e=await x.proxyPost(`/api/projects/${t.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:z,skipTestsReason:R,skipTests:A,headSha:B,...void 0!==J?{authoredSinceReview:J}:{},...Z?{reviewDelta:Z}:{},...Q?{intentMoves:Q}:{},...W?.files?{files:W.files}:{}}))
3220
+ V=e?.data||e}catch(e){const t=(0,h.getErrorMessage)(e)
3221
+ return T.error("Server validation failed",{requestId:s,error:t}),j({success:!1,error:"validation_request_failed"}),{content:[{type:"text",text:`Failed to validate completion: ${t}`}],isError:!0}}if(!V.canComplete){let e=`Cannot complete request ${s}: ${V.reason}\n`
3222
+ if(V.blockedByTasks&&V.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",V.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`})),V.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",V.lifoViolation.blockingRequests.forEach((t,r)=>{e+=`${r+1}. **${t.requestId}** - ${t.title} (Status: ${t.status})\n`})),Y.length>0)try{const t=await x.getRequestClient().getRequest(s)
3219
3223
  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"
3220
3224
  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"}`})
3221
- 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
3222
- 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}
3223
- const Z="testing"===Q.action||"human-review"===Q.action||"merge-cleared"===Q.action
3224
- if(Z&&G.testsSkipped&&(P||R&&R.length>0))try{const e=S.getToolRecordClient()
3225
- 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()
3225
+ 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,Y)}`}catch{}return j({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}const X=V.plan
3226
+ if(await async function(e,t,r,n){try{const o=await e.getRequestClient().getRequest(t),s=function(e,t){return(Array.isArray(e)?e:[]).flatMap(e=>{if(!e?.id||!(0,$.hasBaseline)(e)||e.retired||e.standing&&e.waived)return[]
3227
+ const r="string"==typeof e.command?e.command.trim():""
3228
+ if(!r)return[]
3229
+ const n=Array.isArray(e.ledger)?e.ledger:[]
3230
+ return t&&n.some(e=>"verify"===e?.source&&"string"==typeof e?.instrument&&e.instrument.startsWith("baseline")&&("met"===e.verdict||"unmet"===e.verdict)&&"string"==typeof e.commit&&!!e.commit&&(e.commit.startsWith(t)||t.startsWith(e.commit)))?[]:[{criterionId:String(e.id),command:r,baseline:e.baseline}]})}(o?.acceptanceCriteria??[],r)
3231
+ if(0===s.length)return
3232
+ const i=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/baseline-measure`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:t,baselines:s}),signal:AbortSignal.timeout(2e4)})
3233
+ n.info("baseline measurement started beside the sign-off check; the merge waits for it",{requestId:t,baselines:s.length,ok:i.ok})}catch(e){n.debug("baseline measurement could not start; the card says it was not measured",{requestId:t,error:(0,h.getErrorMessage)(e)})}}(x,s,B,T),"review-submit"===X?.action&&B&&W&&(W.reviewAcceptedSha=B),X?.reviewChange&&Z&&W&&(W.afterReview={...Z,outcome:X.reviewChange},"restarted"===X.reviewChange&&(W.sinceReview=Z)),!X)return j({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}
3234
+ const ee="testing"===X.action||"human-review"===X.action||"merge-cleared"===X.action
3235
+ if(ee&&V.testsSkipped&&(R||A&&A.length>0))try{const e=x.getToolRecordClient()
3236
+ e&&await e.createToolRecord({projectId:t.projectId,requestId:s,type:"tests-skipped",metadata:{...R?{reason:R}:{},...A&&A.length>0?{skipTests:A}:{},testGate:V.gates.test,timestamp:(new Date).toISOString()}})}catch(e){T.warn("Failed to persist tests-skipped tool_record",{error:(0,h.getErrorMessage)(e)})}if(ee&&P&&P.length>0&&z){const e=x.getSessionMessageClient()
3226
3237
  if(e){let r=new Set
3227
- 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??[]
3228
- 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
3229
- 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()
3230
- for(const t of Q.patches)try{const r=await X.updateRequest(s,{...t,completionSummary:i,cliOrigin:N,...B,...U})
3231
- 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.`}]}
3232
- 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.`}]}
3233
- if("review-submit"===Q.action)return{content:[{type:"text",text:`Request ${s}: review submitted. The server is evaluating it and will message you the exact next step (re-run tests if your fixes changed code, await human sign-off, or merge) -- wait for that before acting. If a criterion is still unmet, the criteria reviewer takes another pass and reports back; address any must_fix and call complete again.`}]}
3234
- if("signoff-park"===Q.action)return{content:[{type:"text",text:`Request ${s}: review is complete. This request now awaits the user's sign-off in the UI -- stop here and do NOT call merge.`}]}
3235
- if("merge-cleared-after-review"===Q.action)return{content:[{type:"text",text:`Request ${s}: review is complete and all gates are clear. Call merge to finalize (do not call complete again).`}]}
3236
- if("testing"===Q.action){const e="human"===Q.testResolver?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip). Stop here; you will receive a report when testing finishes.":"Tests are running automatically in the background. Stop here; each active test process will run and post its own report back to you."
3237
- return{content:[{type:"text",text:`Request ${s}: tests are now running. ${e}\n\n## When reports arrive\nEach test process reports separately: an individual passed report does NOT mean you are cleared.\n- When every process has passed, the server advances the remaining gates automatically: agent review may start (reviewers report their findings to you), the request may wait for sign-off from the user, or the request merges. With your account of every criterion on the record (criteriaComments in this call), the server merges it and tells you so; you call nothing. Only if you are told explicitly that you are "cleared to merge" do you call \`merge\`, with evidence for the criteria you had not accounted for.\n- **Failed**: fix the issues and call \`complete\` again.\n\nDo NOT call merge, finalize, or any other tool before that explicit instruction: stop here and wait.`}]}}if("delivery-review"===Q.action){const e=G.testsSkipped&&P?`\n\n_Tests skipped: ${P}_`:""
3238
- return{content:[{type:"text",text:`Request ${s} is handed to the DELIVERY review, which is the last gate on this project and resolves the sign-off.${e}\n\nStop here and do NOT call merge: the review finalizes the request, or comes back to you with what it found.`}]}}if("human-review"===Q.action){const e=G.testsSkipped&&P?`\n\n_Tests skipped: ${P}_`:""
3239
- return{content:[{type:"text",text:`Request ${s} submitted for human review.${e}\n\nReview is human-only for this project: the user approves the merge in the UI. Do NOT call merge yourself.`}]}}let ee,te
3240
- try{ee=await X.getRequest(s)}catch(e){te=e}const re=(0,v.kindProfile)(W??ee?.type)
3241
- if("settle"===re.finalize)return{content:[{type:"text",text:`Request ${s} is complete. A ${re.label.toLowerCase()} request has no branch, so nothing merges: it closes on its own record now. Stop here and do not call merge.`}]}
3242
- 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.`}]}
3243
- const ne=ee?.acceptanceCriteria||[]
3244
- 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})
3245
- const e=await(0,u.handleMergeRequest)({requestId:s,verifications:[]},t,S,x,C,_)
3246
- 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":""
3247
- 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=""
3248
- 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})
3249
- try{await(0,i.createFailureToolRecord)("complete-request",r,{error:n,details:o,requestId:e?.requestId},S.getToolRecordClient(),q,C,{requestId:e?.requestId,projectId:t.projectId,note:"Failed to complete request"})}catch{}return C.startTimer("tool_execution_time")({success:!1,error:r instanceof Error?r.name:"unknown"}),{content:[{type:"text",text:`Error: ${n}`}],isError:!0}}}
3250
- 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
3238
+ try{const e=await x.proxyGet(`/api/projects/${t.projectId}/session-messages`,{requestId:s,messageType:"report",limit:200}),n=Array.isArray(e)?e:e?.data??[]
3239
+ 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 P.slice(0,5)){const n=t.length>140?`${t.slice(0,137)}\u2026`:t
3240
+ if(!r.has(n))try{await e.createReport({fromSessionId:z,status:"success",summary:n,...t.length>140?{details:t}:{},attention:"fyi",requestId:s})}catch(e){T.warn("worthKnowing note failed (completion continues)",{error:(0,h.getErrorMessage)(e)})}}T.info("worthKnowing notes processed",{count:Math.min(P.length,5),requestId:s})}}if(D&&D.length>0&&z)for(const e of D.slice(0,2))try{const r=Array.isArray(e.files)&&e.files.length>0
3241
+ await x.proxyPost(`/api/projects/${t.projectId}/guidance/propose`,{body:e.body,scope:r?"files":"project",...r?{pathAnchors:e.files}:{},...e.evidence?{evidence:e.evidence}:{},...e.userSaid?{userSaid:e.userSaid}:{},requestId:s,sessionExternalId:z}),T.info("guidance proposed at completion",{requestId:s})}catch(e){T.info("guidance at completion not proposed (completion continues)",{requestId:s,reason:(0,h.getErrorMessage)(e)})}const te=x.getRequestClient()
3242
+ for(const t of X.patches)try{const r=await te.updateRequest(s,{...t,completionSummary:i,completionNotChecked:"string"==typeof N&&N.trim()?N.trim():null,cliOrigin:L,...U,...G})
3243
+ e.refsSeen&&r?.namedRefs&&(e.refsSeen.namedRefs=r.namedRefs)}catch(e){return j({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(j({success:!0}),"review-escalated"===X.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.`}]}
3244
+ if("review-start"===X.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.`}]}
3245
+ if("review-submit"===X.action)return{content:[{type:"text",text:`Request ${s}: review submitted. The server is evaluating it and will message you the exact next step (re-run tests if your fixes changed code, await human sign-off, or merge) -- wait for that before acting. If a criterion is still unmet, the criteria reviewer takes another pass and reports back; address any must_fix and call complete again.`}]}
3246
+ if("signoff-park"===X.action)return{content:[{type:"text",text:`Request ${s}: review is complete. This request now awaits the user's sign-off in the UI -- stop here and do NOT call merge.`}]}
3247
+ if("merge-cleared-after-review"===X.action)return{content:[{type:"text",text:`Request ${s}: review is complete and all gates are clear. Call merge to finalize (do not call complete again).`}]}
3248
+ if("testing"===X.action){const e="human"===X.testResolver?"A notification has been created asking the user to choose how to handle testing (auto-test, interactive-test, or skip). Stop here; you will receive a report when testing finishes.":"Tests are running automatically in the background. Stop here; each active test process will run and post its own report back to you."
3249
+ return{content:[{type:"text",text:`Request ${s}: tests are now running. ${e}\n\n## When reports arrive\nEach test process reports separately: an individual passed report does NOT mean you are cleared.\n- When every process has passed, the server advances the remaining gates automatically: agent review may start (reviewers report their findings to you), the request may wait for sign-off from the user, or the request merges. With your account of every criterion on the record (criteriaComments in this call), the server merges it and tells you so; you call nothing. Only if you are told explicitly that you are "cleared to merge" do you call \`merge\`, with evidence for the criteria you had not accounted for.\n- **Failed**: fix the issues and call \`complete\` again.\n\nDo NOT call merge, finalize, or any other tool before that explicit instruction: stop here and wait.`}]}}if("delivery-review"===X.action){const e=V.testsSkipped&&R?`\n\n_Tests skipped: ${R}_`:""
3250
+ return{content:[{type:"text",text:`Request ${s} is handed to the DELIVERY review, which is the last gate on this project and resolves the sign-off.${e}\n\nStop here and do NOT call merge: the review finalizes the request, or comes back to you with what it found.`}]}}if("human-review"===X.action){const e=V.testsSkipped&&R?`\n\n_Tests skipped: ${R}_`:""
3251
+ return{content:[{type:"text",text:`Request ${s} submitted for human review.${e}\n\nReview is human-only for this project: the user approves the merge in the UI. Do NOT call merge yourself.`}]}}let re,ne
3252
+ try{re=await te.getRequest(s)}catch(e){ne=e}const oe=(0,v.kindProfile)(K??re?.type)
3253
+ if("settle"===oe.finalize)return{content:[{type:"text",text:`Request ${s} is complete. A ${oe.label.toLowerCase()} request has no branch, so nothing merges: it closes on its own record now. Stop here and do not call merge.`}]}
3254
+ if(void 0!==ne)return{content:[{type:"text",text:`Request ${s} is cleared to merge, but fetching its criteria failed: ${(0,h.getErrorMessage)(ne)}. Call merge with verification evidence for each acceptance criterion.`}]}
3255
+ const se=re?.acceptanceCriteria||[]
3256
+ if(!1===V?.gates?.review?.enabled&&!1===V?.gates?.test?.enabled&&!1===V?.gates?.signoff?.enabled&&"merge"===oe.finalize&&(0===se.length||Array.isArray(O)&&O.length>0)){T.info("gates off and account recorded: merging in the same call",{requestId:s})
3257
+ const e=await(0,u.handleMergeRequest)({requestId:s,verifications:[]},t,x,C,_,E)
3258
+ 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 ie=X.skippedReason?`\n\n_Skipped re-running tests: ${X.skippedReason}._\n`:"",ae=X.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":""
3259
+ return 0===se.length?{content:[{type:"text",text:`Request ${s} is ready. ${ie}${ae}No acceptance criteria defined: call merge directly.`}]}:{content:[{type:"text",text:`Request ${s}: all gates are clear.${ie}${ae}\n\n## Next Step: Verify & Merge\n\n${(0,p.formatVerifyInstructions)(se)}\n\nUse the merge operation with your verification evidence to merge to main.`}]}}catch(r){let n="An unexpected error occurred",o=""
3260
+ 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),T.error("Complete request failed",{error:n,details:o})
3261
+ try{await(0,i.createFailureToolRecord)("complete-request",r,{error:n,details:o,requestId:e?.requestId},x.getToolRecordClient(),T,_,{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}}}
3262
+ 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),$=t(70715),S=new Set},32283(e,exports,t){exports.def=void 0
3251
3263
  const r=t(76183),n=t(47334)
3252
- 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"})
3264
+ 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.qg=void 0,exports.handleCreateRequest=async function(e,f,m,g,y,w,b){const v=g.child({tool:"create_request"})
3253
3265
  v.info("Creating new request",{args:e})
3254
3266
  const k=y.startTimer("tool_handler_duration")
3255
3267
  try{const g=function(e){try{return e}catch(e){if(e instanceof l.ZodError&&e.errors.length>0)throw new i.ValidationError(e.errors[0].message)
3256
- throw new i.ValidationError(`Invalid input: ${(0,d.getErrorMessage)(e)}`)}}(e)
3268
+ throw new i.ValidationError(`Invalid input: ${(0,d.getErrorMessage)(e)}`)}}(e),I=function(e){return Array.isArray(e.waitsOn)&&0!==e.waitsOn.length?"string"==typeof e.waitReason&&e.waitReason.trim()?null:"Not created: waitsOn needs waitReason, what this request needs from the one it waits for, in a sentence the user reads. If it needs nothing from it, pass startsNow: true instead.":null}(g)
3269
+ if(I)throw new i.ValidationError(I)
3257
3270
  y.incrementCounter("create_request_attempts",1,{type:g.type,criteriaCounts:g.acceptanceCriteria?.length??0})
3258
- const I=f.projectId
3259
- if(!I)throw new i.ValidationError("Project ID is required for creating requests.")
3271
+ const $=f.projectId
3272
+ if(!$)throw new i.ValidationError("Project ID is required for creating requests.")
3260
3273
  if(!0===e?.quotesMessage){const t=await(0,s.userTextOfCall)(f.mainBasePath,b,"string"==typeof e?.userSaid?e.userSaid:void 0)
3261
3274
  if(!t)throw new i.ValidationError("quotesMessage: the user's message this call answers could not be read, so nothing was filed. Write their words into the description yourself and call again without quotesMessage.")
3262
3275
  const r=(g.description??"").trim()
3263
- g.description=`${exports.q}\n\n${t}${r?`\n\n${r}`:""}`}if(!g.draft){if(!g.description||!g.description.trim())throw new i.ValidationError("Description is required. To capture a request to author later, pass draft: true.")
3276
+ g.description=`${exports.qg}\n\n${t}${r?`\n\n${r}`:""}`}if(!g.draft){if(!g.description||!g.description.trim())throw new i.ValidationError("Description is required. To capture a request to author later, pass draft: true.")
3264
3277
  if(!g.acceptanceCriteria||0===g.acceptanceCriteria.length)throw new i.ValidationError("At least one acceptance criterion is required. To capture a request to author later, pass draft: true.")
3265
- if(!g.areas||0===g.areas.length)throw new i.ValidationError('At least one work area is required (areas: ["parent/child"]). List existing areas with request_operations { operation: "list-areas" } and pick 1-3; add a new lowercase-kebab name (nested under an existing parent when possible) only when nothing fits. To capture a request to author later, pass draft: true.')}m.setProjectId(I)
3278
+ if(!g.areas||0===g.areas.length)throw new i.ValidationError('At least one work area is required (areas: ["parent/child"]). List existing areas with request_operations { operation: "list-areas" } and pick 1-3; add a new lowercase-kebab name (nested under an existing parent when possible) only when nothing fits. To capture a request to author later, pass draft: true.')}m.setProjectId($)
3266
3279
  {const t=(0,n.gateCriteriaIn)(g.acceptanceCriteria)
3267
3280
  if(t.length>0){const r="string"==typeof e?.userSaid?e.userSaid:""
3268
- 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()
3269
- let S=b,x=null
3281
+ if(!r||!await(0,o.userSaidOnRequest)(m,$,"",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 S=m.getRequestClient()
3282
+ let x=b,C=null
3270
3283
  if(b?.sessionId)try{const e=m.getCLISessionClient()
3271
3284
  if(e){const t=await e.getByExternalId(b.sessionId)
3272
- x=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{}x||(x=await p.sessionIdentity.getAgentTypeFor(b?.sessionId||f.currentSessionId))
3273
- const C=!g.draft&&("planner"===x||"auto-planner"===x)
3274
- C&&(g.draft=!0,v.info("Planner-created request filed as a draft",{agentType:x}))
3275
- const _=g.draft?"draft":"open",E=g.scheduleDocumentId||g.questDocumentId
3276
- let q=!1
3277
- g.planDocumentId&&E&&g.planDocumentId===E&&(g.planDocumentId=void 0,q=!0)
3278
- let T=g.planDocumentId
3279
- if(!T&&b?.sessionId)try{const e=await(0,u.getActiveSessionPlanId)(m,I,b.sessionId,v)
3280
- 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&&E&&T===E&&(T=void 0,q=!0)
3281
- 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},...E&&{scheduleDocumentId:E},...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)}}
3282
- v.info("Creating request via request client",{projectId:I,title:g.title,type:g.type,criteriaCount:g.acceptanceCriteria?.length??0})
3283
- const A=await $.createRequest(R)
3284
- 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"})
3285
- const O=(0,c.extractDataFromResponse)(A,{logger:v,operationName:"createRequest",throwOnNoData:!0}),M="data"in O?O.data:O,D=!g.draft&&"draft"===M.status
3286
- let F,N={created:!1}
3287
- if(g.draft||D)v.info("Draft request - no branch created until registered",{requestId:M.id})
3288
- else if("none"===(0,r.kindProfile)(g.type).branch)v.info("Drive request - no branch created (drives own no branch)",{requestId:M.id})
3289
- else if(w?.workspaceManager)try{await w.workspaceManager.initializeRequest(M.id,M.title),N={created:!0,branchName:`requests/${M.id}`,message:"Branch created successfully"},v.info("Request branch initialized successfully",{requestId:M.id,branchName:N.branchName,title:M.title})}catch(e){v.warn("Failed to initialize request branch, will retry on first operation",{requestId:M.id,error:(0,d.getErrorMessage)(e)})}else v.debug("No workspace manager available, branch creation deferred",{requestId:M.id})
3290
- v.info("Request created successfully",{requestId:M.id,title:M.title,branchCreated:N.created,branchName:N.branchName})
3285
+ C=t?.agentType??null,"planner"!==t?.agentType&&"auto-planner"!==t?.agentType||(x=void 0,v.info("Skipping cliOrigin for planner-created request",{agentType:t.agentType}))}}catch{}C||(C=await p.sessionIdentity.getAgentTypeFor(b?.sessionId||f.currentSessionId))
3286
+ const _=!g.draft&&("planner"===C||"auto-planner"===C)
3287
+ _&&(g.draft=!0,v.info("Planner-created request filed as a draft",{agentType:C}))
3288
+ const E=g.draft?"draft":"open",q=g.scheduleDocumentId||g.questDocumentId
3289
+ let T=!1
3290
+ g.planDocumentId&&q&&g.planDocumentId===q&&(g.planDocumentId=void 0,T=!0)
3291
+ let j=g.planDocumentId
3292
+ if(!j&&b?.sessionId)try{const e=await(0,u.getActiveSessionPlanId)(m,$,b.sessionId,v)
3293
+ e&&await m.getDocumentClient().getDocument(e)&&(j=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)})}j&&q&&j===q&&(j=void 0,T=!0)
3294
+ const P=await(0,h.exchangeKeyOfCall)(f.mainBasePath,b,"string"==typeof e?.userSaid?e.userSaid:void 0),R=P?await(0,h.exchangeKeysWithCall)(f.mainBasePath,b):[],A={title:g.title,description:g.description||"",type:g.type,acceptanceCriteria:g.acceptanceCriteria||[],status:E,...j&&{planDocumentId:j},...q&&{scheduleDocumentId:q},...g.areas&&g.areas.length>0&&{areas:g.areas},...g.waitsOn&&g.waitsOn.length>0&&{waitsOn:g.waitsOn,waitReason:String(g.waitReason).trim().slice(0,300)},...!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!==P&&{exchangeKey:P},...R.length>0&&{exchangeKeysWith:R},...x&&{cliOrigin:x},...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)}}
3295
+ v.info("Creating request via request client",{projectId:$,title:g.title,type:g.type,criteriaCount:g.acceptanceCriteria?.length??0})
3296
+ const O=await S.createRequest(A)
3297
+ v.debug("Create request 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"})
3298
+ const M=(0,c.extractDataFromResponse)(O,{logger:v,operationName:"createRequest",throwOnNoData:!0}),D="data"in M?M.data:M,F=!g.draft&&"draft"===D.status
3299
+ let N,z={created:!1}
3300
+ if(g.draft||F)v.info("Draft request - no branch created until registered",{requestId:D.id})
3301
+ else if("none"===(0,r.kindProfile)(g.type).branch)v.info("Drive request - no branch created (drives own no branch)",{requestId:D.id})
3302
+ else if(w?.workspaceManager)try{await w.workspaceManager.initializeRequest(D.id,D.title),z={created:!0,branchName:`requests/${D.id}`,message:"Branch created successfully"},v.info("Request branch initialized successfully",{requestId:D.id,branchName:z.branchName,title:D.title})}catch(e){v.warn("Failed to initialize request branch, will retry on first operation",{requestId:D.id,error:(0,d.getErrorMessage)(e)})}else v.debug("No workspace manager available, branch creation deferred",{requestId:D.id})
3303
+ v.info("Request created successfully",{requestId:D.id,title:D.title,branchCreated:z.created,branchName:z.branchName})
3291
3304
  try{const e=m.getToolRecordClient()
3292
- 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"}`})
3293
- 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)
3294
- 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.`:C?`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.`:""
3295
- 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})
3305
+ if(e){const t=await e.createToolRecord({requestId:D.id,projectId:$,type:"create-request",metadata:{status:"completed",timestamp:(new Date).toISOString(),requestType:D.type,requestTitle:D.title,requestDescription:D.description,criteriaCount:D.acceptanceCriteria.length,branchCreated:z.created,branchName:z.created?z.branchName:null},note:`Filed "${D.title}" (${D.type})${z.created?`, on its own branch ${z.branchName}`:"; its branch is made at its first file change"}`})
3306
+ N=t?.id}}catch(e){v.warn("Failed to create tool record for request creation",{error:(0,d.getErrorMessage)(e),requestId:D.id})}if(k({success:!0,type:g.type,hasProjectId:!0}),v.info("Request created successfully",{requestId:D.id,title:D.title,type:D.type,projectId:$}),g.draft&&g.delegate)try{const{getActionServerBaseUrl:e}=t(79414)
3307
+ await fetch(`${e()}/api/flesh-out-request`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:D.id,sourceSessionId:b?.sessionId}),signal:AbortSignal.timeout(8e3)}),v.info("Delegated draft flesh-out to a request-shaping planner",{requestId:D.id})}catch(e){v.warn("Failed to delegate draft flesh-out (draft still created)",{requestId:D.id,error:(0,d.getErrorMessage)(e)})}const L=D.localId||D.id,B=F?`Created Request ${L}, 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 ${L}. 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 ${L} and handed it to a background planner to flesh out. It starts when the user presses Start.`:`Created draft Request ${L} (private; no branch, no work started). It starts when the user presses Start.`:`Created Request ${L}`,H=j&&!g.planDocumentId?" (linked to your session's plan)":T?" (note: the schedule document is the request's one guiding link; a duplicate plan link to the same doc was dropped)":"",U=D.asked,W=(U?.requested??[]).filter(e=>!(U?.linked??[]).includes(e)),G=U?`\n\n${U.linked?.length?`Put on ${L}: ${U.linked.join(", ")}. Its worker is told they are asked, and the user's answer reaches it.`:""}${W.length?` Not put on it (not your open question to the user, or on another request): ${W.join(", ")}.`:""}`:"",V=D.replaces,K=V?.requestId?`\n\nIt replaces ${V.requestId} (canceled). ${V.nowWaiting&&V.nowWaiting.length?`${V.nowWaiting.join(", ")} waited on ${V.requestId} and now ${1===V.nowWaiting.length?"waits":"wait"} for ${L} instead.`:`Nothing was waiting on ${V.requestId}.`}`:"",Y=D.planStep,J=Y?.plan?`\n\nIt is filed as a DRAFT, like the other steps of ${Y.plan}, which nobody has started yet: it starts with them when the user presses Start Building${Y.startBuildingOffered?` (the card is on their timeline now, ${Y.startBuildingOffered})`:""}. Do not start it yourself.`:""
3308
+ return{content:[{type:"text",text:B+H+(D.process?.name?`\n\nIt follows the process "${D.process.name}": its criteria are on the request, listed first, and every request that follows the process carries them.`:D.processHint?`\n\n${D.processHint}`:"")+K+G+J}],...(0,a.alchemyMeta)({kind:"create-request",toolRecordId:N})}}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})
3296
3309
  else{const r=m.getToolRecordClient()
3297
3310
  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]
3298
3311
  let r=e.message
3299
3312
  return e.path.includes("acceptanceCriteria")&&(r=`Acceptance criteria: ${r}. Please provide a list of criteria descriptions.`),{content:[{type:"text",text:`Error: ${r}`}],isError:!0}}let r=(0,d.getErrorMessage)(t),n=/Not created: /.test(r)?"":"Unable to create request. Please check your input and try again."
3300
3313
  return(r.includes("Project cannot accept new requests")||r.includes("Project is inactive due to tier limits"))&&(n="This project is currently inactive due to tier limits. Only your oldest projects can accept new requests. Please use an active project or upgrade your tier for more simultaneous projects."),{content:[{type:"text",text:n?`Error: ${r}\n\n${n}`:r}],isError:!0}}}
3301
3314
  const r=t(87780),n=t(57115),o=t(29729),s=t(62032)
3302
- exports.q="Your words:"
3315
+ exports.qg="Your words:"
3303
3316
  const i=t(59860),a=t(82272),c=t(82960),l=t(11569),d=t(58824),u=t(53089),h=t(62032),p=t(6688)},44558(e,exports,t){exports.readEndedBranch=async function(e,t,r){if(null===await n(e,["rev-parse","--verify","--quiet",`refs/heads/${t}`]))return{branch:t,exists:!1,commits:[],count:0}
3304
3317
  const o=Number((await n(e,["rev-list","--count",`refs/heads/${r}..refs/heads/${t}`])??"0").trim())||0
3305
3318
  return{branch:t,exists:!0,commits:(await n(e,["log","--format=%h %s","-5",`refs/heads/${r}..refs/heads/${t}`])??"").split("\n").map(e=>e.trim()).filter(Boolean),count:o}},exports.endedBranchLine=function(e,t){if(!e.exists)return`Branch: ${e.branch} is gone; nothing of this request is on ${t} unless its outcome says it merged.`
@@ -3425,75 +3438,75 @@ n.push(i.length>0?`- PROVIDER CAP: ${t.providerID} is refusing turns${o}. Stoppe
3425
3438
  const r=t(51725),n=t(87780),o=/^\s*(?:Error:|\u26d4|\u274c|Not filed|Not started|Not kept|Cannot |STOP:)/},34306(e,exports,t){function r(e,t){const r=`STOP: Cannot merge request ${e}: it is awaiting human action`
3426
3439
  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.`
3427
3440
  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.`
3428
- const n=t.map(e=>`${e.code} (${v[e.kind]??e.kind})`).join(", ")
3429
- 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"}),x=$.startTimer("tool_execution_time"),{requestId:C,verifications:_}=e,E=v.basePath
3430
- let q=!1,T=!1
3431
- try{if(!C)throw(0,i.createValidationError)("requestId is required")
3432
- S.info("Starting merge request",{requestId:C,verificationCount:_.length})
3433
- const e=k.getRequestClient(),l=await e.getRequest(C),h=(0,w.kindProfile)(l?.type)
3434
- if("none"===h.branch)return x({success:!0,reason:"no_branch_kind"}),{content:[{type:"text",text:`Nothing to merge for ${C}: a ${h.label.toLowerCase()} request has no branch. It closes on its own record when you complete it with request_operations complete.`}]}
3435
- if("running"===l?.testStatus||"choice-pending"===l?.testStatus){x({success:!1,reason:"running"===l.testStatus?"testing_in_progress":"testing_choice_pending"})
3441
+ const n=t.map(e=>`${e.code} (${k[e.kind]??e.kind})`).join(", ")
3442
+ 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,k,I,$,S){const x=$.child({tool:"merge_request"}),C=S.startTimer("tool_execution_time"),{requestId:_,verifications:E}=e,q=k.basePath
3443
+ let T=!1,j=!1
3444
+ try{if(!_)throw(0,i.createValidationError)("requestId is required")
3445
+ x.info("Starting merge request",{requestId:_,verificationCount:E.length})
3446
+ const e=I.getRequestClient(),l=await e.getRequest(_),h=(0,b.kindProfile)(l?.type)
3447
+ if("none"===h.branch)return C({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.`}]}
3448
+ if("running"===l?.testStatus||"choice-pending"===l?.testStatus){C({success:!1,reason:"running"===l.testStatus?"testing_in_progress":"testing_choice_pending"})
3436
3449
  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"
3437
- return{content:[{type:"text",text:`STOP: Cannot merge request ${C}: ${e}; you'll be cleared to merge when the Test gate resolves.`}],isError:!0}}if("ready_for_review"===l?.status){x({success:!1,reason:"awaiting_human"})
3450
+ 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){C({success:!1,reason:"awaiting_human"})
3438
3451
  let e=null
3439
- try{const t=await k.proxyGet(`/api/projects/${v.projectId}/requests/${C}/wait-state`),r=t?.data??t??{}
3440
- Array.isArray(r.userParks)&&(e=r.userParks.filter(e=>"string"==typeof e?.code))}catch{}return{content:[{type:"text",text:r(C,e)}],isError:!0}}if("escalated"===l?.reviewStatus)return x({success:!1,reason:"review_escalated"}),{content:[{type:"text",text:`STOP: Cannot merge request ${C}: 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}
3441
- if(_.length>0)try{const e=(0,g.buildCriteriaKeyMap)(l?.acceptanceCriteria??[]),t=[],r=[]
3442
- for(const n of _){const o=e.get(n.key)
3443
- 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:C,unmatched:r}),t.length>0&&(await k.proxyPost(`/api/projects/${v.projectId}/requests/${C}/verify-criteria`,{verifications:t,dimension:"coder"}),S.info("Criteria verified",{requestId:C,count:t.length}))}catch(e){const t=(0,d.getErrorMessage)(e)
3444
- S.warn("Criteria verification failed, continuing with merge",{requestId:C,error:t})}let I,$,j
3445
- try{const e=await k.proxyPost(`/api/projects/${v.projectId}/requests/${C}/validate-completion`,(0,b.validateCompletionBody)("merge"))
3446
- I=e?.data||e}catch(e){const t=(0,d.getErrorMessage)(e)
3447
- return x({success:!1,error:"validation_failed"}),{content:[{type:"text",text:`Failed to validate merge: ${t}`}],isError:!0}}if(!I.canComplete){let e=`Cannot merge request ${C}: ${I.reason}\n`
3448
- 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`})),x({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if(!v.workspaceManager)throw new Error("WorkspaceManager not available")
3449
- 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)
3450
- $=(await t.raw(["merge-base",r,`requests/${C}`])).trim()||void 0
3451
- const n=await(0,u.countCommitsAhead)(t,r,`requests/${C}`),s=(0,u.emptyBranchBlockMessage)(n,C,r)
3452
- if(s)return x({success:!1,reason:"empty_branch"}),{content:[{type:"text",text:s}],isError:!0}
3453
- if(await(0,u.branchAddsNoContent)(t,r,`requests/${C}`))return x({success:!1,reason:"identical_tree"}),{content:[{type:"text",text:(0,u.identicalTreeBlockMessage)(C,r)}],isError:!0}}catch(e){S.warn("Could not compute merge-base / ahead count; proceeding (the merge path surfaces a real branch error)",{requestId:C,error:(0,d.getErrorMessage)(e)})}let P="workspace-manager"
3454
- const R=await e.getRequest(C),A=R?.title||`Request ${C}`
3455
- let O="main"
3456
- 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/${C}`):{files:[],determined:!1}
3457
- if(!M.determined)return x({success:!1,reason:"marker_check_unavailable"}),{content:[{type:"text",text:`Cannot merge ${C}: the conflict-marker check could not run, so it is unknown whether unresolved markers remain on requests/${C}. Nothing was merged.`}],isError:!0}
3458
- if(M.files.length>0)return x({success:!1,reason:"unresolved_markers"}),{content:[{type:"text",text:`Cannot merge ${C}: committed conflict markers remain in ${M.files.join(", ")}. Resolve every <<<<<<< block with your file tools, verify, then merge again.`}],isError:!0}
3459
- let D
3460
- v.mainBasePath&&await(0,c.foldAtDoor)({repoDir:v.mainBasePath,branch:`requests/${C}`,defaultBranch:O,requestId:C,door:"merge",gitBinary:v.gitBinary,postRecord:e=>k.proxyPost(`/api/projects/${v.projectId}/tool-records`,e),log:S})
3461
- let F=null
3462
- {const e=await(0,p.runMergeQueueGate)({projectPath:E,requestId:C,branchName:`requests/${C}`,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/${C}/test-runs`),t=e?.data??e??[]
3463
- return Array.isArray(t)?t:[]},fetchActiveProcesses:async()=>{const e=await k.proxyGet(`/api/projects/${v.projectId}/test-processes/active`),t=e?.data??e??[]
3464
- 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:C,baseSha:e,headSha:r}),signal:AbortSignal.timeout(6e5)})
3452
+ try{const t=await I.proxyGet(`/api/projects/${k.projectId}/requests/${_}/wait-state`),r=t?.data??t??{}
3453
+ 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 C({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}
3454
+ if(E.length>0)try{const e=(0,g.buildCriteriaKeyMap)(l?.acceptanceCriteria??[]),t=[],r=[]
3455
+ for(const n of E){const o=e.get(n.key)
3456
+ o?t.push({criterionId:o.id,evidence:n.evidence}):r.push(n.key)}r.length>0&&x.warn("merge_request: verification keys did not match any criterion (dropped)",{requestId:_,unmatched:r}),t.length>0&&(await I.proxyPost(`/api/projects/${k.projectId}/requests/${_}/verify-criteria`,{verifications:t,dimension:"coder"}),x.info("Criteria verified",{requestId:_,count:t.length}))}catch(e){const t=(0,d.getErrorMessage)(e)
3457
+ x.warn("Criteria verification failed, continuing with merge",{requestId:_,error:t})}let $,S,P
3458
+ try{const e=await I.proxyPost(`/api/projects/${k.projectId}/requests/${_}/validate-completion`,(0,v.validateCompletionBody)("merge"))
3459
+ $=e?.data||e}catch(e){const t=(0,d.getErrorMessage)(e)
3460
+ return C({success:!1,error:"validation_failed"}),{content:[{type:"text",text:`Failed to validate merge: ${t}`}],isError:!0}}if(!$.canComplete){let e=`Cannot merge request ${_}: ${$.reason}\n`
3461
+ return $.blockedByTasks?.length&&(e+="\n**Tasks that must be completed first:**\n",$.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (${t.status})\n`})),C({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if(!k.workspaceManager)throw new Error("WorkspaceManager not available")
3462
+ try{const e=k.workspaceManager.getCurrentContext(k.currentSessionId)?.basePath||k.basePath,t=k.git??(0,o.simpleGit)({baseDir:e,binary:k.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)
3463
+ S=(await t.raw(["merge-base",r,`requests/${_}`])).trim()||void 0
3464
+ const n=await(0,u.countCommitsAhead)(t,r,`requests/${_}`),s=(0,u.emptyBranchBlockMessage)(n,_,r)
3465
+ if(s)return C({success:!1,reason:"empty_branch"}),{content:[{type:"text",text:s}],isError:!0}
3466
+ if(await(0,u.branchAddsNoContent)(t,r,`requests/${_}`))return C({success:!1,reason:"identical_tree"}),{content:[{type:"text",text:(0,u.identicalTreeBlockMessage)(_,r)}],isError:!0}}catch(e){x.warn("Could not compute merge-base / ahead count; proceeding (the merge path surfaces a real branch error)",{requestId:_,error:(0,d.getErrorMessage)(e)})}let R="workspace-manager"
3467
+ const A=await e.getRequest(_),O=A?.title||`Request ${_}`
3468
+ let M="main"
3469
+ try{k.git&&(await k.git.branch(),M=await(0,y.resolveDefaultBranch)(k.mainBasePath,k.gitBinary))}catch{}const D=k.git?await(0,a.findCommittedMarkers)(k.git,`requests/${_}`):{files:[],determined:!1}
3470
+ if(!D.determined)return C({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}
3471
+ if(D.files.length>0)return C({success:!1,reason:"unresolved_markers"}),{content:[{type:"text",text:`Cannot merge ${_}: committed conflict markers remain in ${D.files.join(", ")}. Resolve every <<<<<<< block with your file tools, verify, then merge again.`}],isError:!0}
3472
+ let F
3473
+ k.mainBasePath&&await(0,c.foldAtDoor)({repoDir:k.mainBasePath,branch:`requests/${_}`,defaultBranch:M,requestId:_,door:"merge",gitBinary:k.gitBinary,postRecord:e=>I.proxyPost(`/api/projects/${k.projectId}/tool-records`,e),log:x})
3474
+ let N=null
3475
+ {const e=await(0,p.runMergeQueueGate)({projectPath:q,requestId:_,branchName:`requests/${_}`,defaultBranch:M,requestTitle:A?.title,criteria:A?.acceptanceCriteria??A?.acceptance_criteria,testGateOff:!1===A?.gates?.test?.enabled,source:"merge_request",gitBinary:k.gitBinary,logger:x,fetchRequestRuns:async()=>{const e=await I.proxyGet(`/api/projects/${k.projectId}/requests/${_}/test-runs`),t=e?.data??e??[]
3476
+ return Array.isArray(t)?t:[]},fetchActiveProcesses:async()=>{const e=await I.proxyGet(`/api/projects/${k.projectId}/test-processes/active?forRequest=${encodeURIComponent(_)}`),t=e?.data??e??[]
3477
+ 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)})
3465
3478
  if(!o.ok)return null
3466
3479
  const s=await o.json()
3467
- 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:C,landingSha:e}),signal:AbortSignal.timeout(2e5)})
3480
+ 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)})
3468
3481
  if(!n.ok)return null
3469
3482
  const o=await n.json()
3470
- 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:C,parentSessionId:v.currentSessionId,catchUp:e}),signal:AbortSignal.timeout(6e4)})
3483
+ 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:k.currentSessionId,catchUp:e}),signal:AbortSignal.timeout(6e4)})
3471
3484
  if(!n.ok){const e=await n.text().catch(()=>"")
3472
3485
  return{success:!1,error:`action server returned ${n.status}: ${e.slice(0,300)}`}}const o=await n.json()
3473
3486
  return o?.success?{success:!0,testRunIds:o?.data?.testRunIds??[],settled:o?.data?.catchUp?.settled??null}:{success:!1,error:o?.error||"unknown error"}}})
3474
3487
  if("wait-slot"===e.action){const t=e.holder
3475
- return x({success:!1,reason:"merge_slot_busy"}),{content:[{type:"text",text:`STOP: Request ${C} 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,x({success:!1,reason:"merge_catchup_running"}),{content:[{type:"text",text:`Request ${C} 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}
3476
- if("env-gap"===e.action){x({success:!1,reason:"merge_env_gap"})
3488
+ return C({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(T=!0,"catchup-running"===e.action)return j=!0,C({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: ${M} 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 ${M} 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}
3489
+ if("env-gap"===e.action){C({success:!1,reason:"merge_env_gap"})
3477
3490
  const t=e.missingCapabilities.length?e.missingCapabilities.join(", "):"a required tool"
3478
- return{content:[{type:"text",text:`Request ${C}'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 x({success:!1,reason:"merge_catchup_failed"}),{content:[{type:"text",text:`Cannot merge request ${C}: 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}
3479
- if("error"===e.action)return x({success:!1,error:"merge_queue_error"}),{content:[{type:"text",text:`Merge queue check failed: ${e.message}`}],isError:!0}
3480
- if("sent-back"===e.action)return x({success:!1,error:"sent_back"}),await(0,f.bringMainIn)({projectPath:E,requestId:C,defaultBranch:O,logger:S}),{content:[{type:"text",text:`Not merged: ${e.summary}\n\n${C} 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}
3481
- if("review-held"===e.action){x({success:!1,error:"review_held"})
3482
- const t=await(0,f.holdForMainChangeReview)({projectPath:E,projectId:v.projectId,requestId:C,defaultBranch:O,touched:e.touched,logger:S,post:(e,t)=>k.proxyPost(e,t)})
3483
- 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 ${C}'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 x({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}
3484
- "conflicts"===e.action&&(F=new Error(`Merge of requests/${C} 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:C,certifiedCommitSha:D.slice(0,8)}))}try{if(F)throw F
3485
- j=await v.workspaceManager.completeRequest(C,A,D?{certifiedCommitSha:D}:void 0),P=D?"merge-queue-certified":j.autoMergeInfo?.strategy||"workspace-manager",S.info("Merge completed",{requestId:C,mergeCommitHash:j.mergeCommitHash,mergeMethod:P})}catch(t){if(!t.conflictInfo){const e=(0,d.getErrorMessage)(t)
3486
- return x({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/${C}`,o=await(0,a.reconcileBranchWithMain)({projectPath:v.basePath,requestId:C,branchName:n,defaultBranch:O,worktreePath:r})
3487
- if("clean"!==o.outcome){if("conflicts-committed"===o.outcome){try{await e.updateRequest(C,{status:"resolving_conflicts"}),S.info("Set status to resolving_conflicts",{requestId:C})}catch(e){S.error("Failed to set resolving_conflicts status",{requestId:C,error:(0,d.getErrorMessage)(e)})}const t=(0,a.buildResolveInstruction)({requestId:C,branchName:n,defaultBranch:O,landed:o.landed,conflictedFiles:o.conflictedFiles,retryVia:"merge"})
3488
- return x({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:t}],isError:!1}}return x({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:C})
3489
- try{j=await v.workspaceManager.completeRequest(C,A),P="reconcile-retry"}catch(e){return x({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
3490
- z&&($=await(0,s.firstParentSha)((0,s.gitFor)(N,v.git,v.gitBinary),z)??$)
3491
- const L=await(0,s.computeDiffStats)((0,s.gitFor)(N,v.git,v.gitBinary),$,j?.mergeCommitHash)
3492
- try{await k.proxyPost(`/api/projects/${v.projectId}/requests/${C}/finalize-completion`,{summary:R?.completionSummary||`Merged request ${C}`,mergeResult:{mergeCommitHash:j.mergeCommitHash,mergeBaseSha:$,mergedFiles:j.mergedFiles,cleanedUp:j.cleanedUp,method:P,diffStats:L}})}catch(e){const t=(0,d.getErrorMessage)(e)
3493
- return S.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:C,mergeCommitHash:j.mergeCommitHash,error:t}),x({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)(C),(0,m.clearMergeCandidate)(E,C,v.gitBinary).catch(()=>{}),x({success:!0}),{content:[{type:"text",text:`Request ${C} merged successfully.\nCommit: ${j.mergeCommitHash.substring(0,8)}`}]}}catch(e){const t=(0,d.getErrorMessage)(e)
3494
- S.error("Merge request failed",{error:t})
3495
- try{await(0,l.createFailureToolRecord)("merge-request",e,{error:t,requestId:C},k.getToolRecordClient(),S,$,{requestId:C,projectId:v.projectId,note:"Failed to merge request"})}catch{}return x({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)(E,C)}catch{}}}
3496
- 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)
3491
+ 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 C({success:!1,reason:"merge_catchup_failed"}),{content:[{type:"text",text:`Cannot merge request ${_}: its checks FAILED on the tree the merge would land (${e.mainMergedIn?"the prospective merge commit":"the branch tip, which already contains main"} ${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${e.mainMergedIn?" (main's newer 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}
3492
+ if("error"===e.action)return C({success:!1,error:"merge_queue_error"}),{content:[{type:"text",text:`Merge queue check failed: ${e.message}`}],isError:!0}
3493
+ if("sent-back"===e.action)return C({success:!1,error:"sent_back"}),await(0,f.bringMainIn)({projectPath:q,requestId:_,defaultBranch:M,logger:x}),{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}
3494
+ if("review-held"===e.action){C({success:!1,error:"review_held"})
3495
+ const t=await(0,f.holdForMainChangeReview)({projectPath:q,projectId:k.projectId,requestId:_,defaultBranch:M,touched:e.touched,logger:x,post:(e,t)=>I.proxyPost(e,t)})
3496
+ 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 C({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}
3497
+ "conflicts"===e.action&&(N=new Error(`Merge of requests/${_} into ${M} has conflicts (detected on the prospective merge tree):\n${e.summary}`),N.conflictInfo={conflictedFiles:[],workingDirectory:void 0}),"land"===e.action&&(F=e.certifiedCommitSha,x.info("merge-queue: landing certified catch-up commit (tested tree == merged tree)",{requestId:_,certifiedCommitSha:F.slice(0,8)}))}try{if(N)throw N
3498
+ P=await k.workspaceManager.completeRequest(_,O,F?{certifiedCommitSha:F}:void 0),R=F?"merge-queue-certified":P.autoMergeInfo?.strategy||"workspace-manager",x.info("Merge completed",{requestId:_,mergeCommitHash:P.mergeCommitHash,mergeMethod:R})}catch(t){if(!t.conflictInfo){const e=(0,d.getErrorMessage)(t)
3499
+ return C({success:!1,error:"merge_failure"}),{content:[{type:"text",text:`Merge failed: ${e}`}],isError:!0}}{const r=t.conflictInfo.workingDirectory||k.workspaceManager?.getCurrentContext(k.currentSessionId)?.basePath||void 0,n=`requests/${_}`,o=await(0,a.reconcileBranchWithMain)({projectPath:k.basePath,requestId:_,branchName:n,defaultBranch:M,worktreePath:r})
3500
+ if("clean"!==o.outcome){if("conflicts-committed"===o.outcome){try{await e.updateRequest(_,{status:"resolving_conflicts"}),x.info("Set status to resolving_conflicts",{requestId:_})}catch(e){x.error("Failed to set resolving_conflicts status",{requestId:_,error:(0,d.getErrorMessage)(e)})}const t=(0,a.buildResolveInstruction)({requestId:_,branchName:n,defaultBranch:M,landed:o.landed,conflictedFiles:o.conflictedFiles,retryVia:"merge"})
3501
+ return C({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:t}],isError:!1}}return C({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}}x.info("Branch reconciled with main cleanly: retrying merge",{requestId:_})
3502
+ try{P=await k.workspaceManager.completeRequest(_,O),R="reconcile-retry"}catch(e){return C({success:!1,error:"merge_failure_after_reconcile"}),{content:[{type:"text",text:`Merge failed after clean reconcile: ${(0,d.getErrorMessage)(e)}`}],isError:!0}}}}const z=k.workspaceManager.getCurrentContext(k.currentSessionId)?.basePath||k.basePath,L=F??P?.mergeCommitHash
3503
+ L&&(S=await(0,s.firstParentSha)((0,s.gitFor)(z,k.git,k.gitBinary),L)??S)
3504
+ const B=await(0,s.computeDiffStats)((0,s.gitFor)(z,k.git,k.gitBinary),S,P?.mergeCommitHash),H=L&&!1===A?.gates?.test?.enabled?await(0,w.landedOnTopOf)(z,L,_).catch(()=>[]):[]
3505
+ try{await I.proxyPost(`/api/projects/${k.projectId}/requests/${_}/finalize-completion`,{summary:A?.completionSummary||`Merged request ${_}`,mergeResult:{mergeCommitHash:P.mergeCommitHash,mergeBaseSha:S,mergedFiles:P.mergedFiles,cleanedUp:P.cleanedUp,method:R,diffStats:B,...H.length?{notCheckedWith:H}:{}}})}catch(e){const t=(0,d.getErrorMessage)(e)
3506
+ return x.error("CRITICAL: Merge succeeded but server finalization failed",{requestId:_,mergeCommitHash:P.mergeCommitHash,error:t}),C({success:!1,error:"api_update_failed"}),{content:[{type:"text",text:`CRITICAL: Code was merged (commit: ${P.mergeCommitHash.substring(0,8)}) but failed to update server: ${t}`}],isError:!0}}return(0,n.maybePublishReceiptOnMerge)(_),(0,m.clearMergeCandidate)(q,_,k.gitBinary).catch(()=>{}),C({success:!0}),{content:[{type:"text",text:`Request ${_} merged successfully.\nCommit: ${P.mergeCommitHash.substring(0,8)}`}]}}catch(e){const t=(0,d.getErrorMessage)(e)
3507
+ x.error("Merge request failed",{error:t})
3508
+ try{await(0,l.createFailureToolRecord)("merge-request",e,{error:t,requestId:_},I.getToolRecordClient(),x,S,{requestId:_,projectId:k.projectId,note:"Failed to merge request"})}catch{}return C({success:!1,error:e instanceof Error?e.name:"unknown"}),{content:[{type:"text",text:`Error: ${t}`}],isError:!0}}finally{if(T&&!j)try{(0,h.releaseMergeLease)(q,_)}catch{}}}
3509
+ 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(82865),b=t(87780),v=t(63580),k={"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)
3497
3510
  var o=Object.getOwnPropertyDescriptor(t,r)
3498
3511
  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=[]
3499
3512
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
@@ -3557,11 +3570,11 @@ const t=new Map,r=(e,t)=>`${e}\0${t}`
3557
3570
  exports.SHAPING_AGENTS=new Set(["request-architect","planner"])
3558
3571
  const n=new Map},74165(e,exports,t){exports.def=void 0
3559
3572
  const r=t(76183),n=t(84648)
3560
- 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,x){const C=I.child({tool:"request_operations"})
3573
+ 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 status - Where live work stands, from the record, in one call: per thread or plan what is done, running and next, what waits on the user, spend at list price, anything stuck. requestId: that request's thread; planId: that plan; neither: everything live.\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,x){const C=I.child({tool:"request_operations"})
3561
3574
  try{const _=e
3562
3575
  let{requestId:E}=_
3563
3576
  const{operation:q}=_
3564
- if("list"!==q&&"search"!==q&&"list-areas"!==q&&!E){const e=await(0,f.resolveRequestIdOrError)(k,C,t.currentSessionId,`Error: requestId is required for '${q}' and could not be resolved from session`)
3577
+ if("list"!==q&&"search"!==q&&"list-areas"!==q&&"status"!==q&&!E){const e=await(0,f.resolveRequestIdOrError)(k,C,t.currentSessionId,`Error: requestId is required for '${q}' and could not be resolved from session`)
3565
3578
  if(!e.ok)return e.errorResult
3566
3579
  E=e.requestId}switch(C.info("Processing request operation",{requestId:E,operation:q}),q){case"update":return C.info("Routing to update-request handler"),(0,y.withAlchemyKind)(await(0,a.handleUpdateRequest)({requestId:E,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)
3567
3580
  case"activate":{C.info("Routing to activate-request (draft -> open)")
@@ -3592,7 +3605,12 @@ if(s?.reopened&&r)try{await(0,a.recordUserWordDecision)(k,{fromSessionId:t.curre
3592
3605
  const e={requestId:E}
3593
3606
  return(0,y.withAlchemyKind)(await(0,i.handleGetRequestInformation)(e,t,k,I,$),v["get-info"])}case"list":{C.info("Routing to list-requests handler")
3594
3607
  const e={filters:_.list?.filters}
3595
- return(0,y.withAlchemyKind)(await(0,l.handleListRequests)(e,t,k,I,$),v.list)}case"list-areas":return C.info("Routing to list-areas handler"),(0,y.withAlchemyKind)(await(0,u.handleListAreas)(t,k,I,$),v["list-areas"])
3608
+ return(0,y.withAlchemyKind)(await(0,l.handleListRequests)(e,t,k,I,$),v.list)}case"status":{const e=t.projectId
3609
+ if(!e)return{content:[{type:"text",text:"Error: no project for this session"}],isError:!0}
3610
+ const r=new URLSearchParams
3611
+ E&&r.set("requestId",E),"string"==typeof _.planId&&_.planId.trim()&&r.set("plan",_.planId.trim())
3612
+ const n=r.toString(),o=await k.proxyGet(`/api/projects/${e}/status-digest${n?`?${n}`:""}`),s=String(o?.data?.text??o?.text??"").trim()
3613
+ return(0,y.withAlchemyKind)({content:[{type:"text",text:s?`${s}\n\n(From the record. Answer from these lines, about three per thread; ids as written; no finishing time, it is not on the record.)`:"No status could be read."}]},v.status)}case"list-areas":return C.info("Routing to list-areas handler"),(0,y.withAlchemyKind)(await(0,u.handleListAreas)(t,k,I,$),v["list-areas"])
3596
3614
  case"wait-on":case"clear-wait":return C.info("Routing to request-wait handler",{operation:q}),(0,y.withAlchemyKind)(await(0,p.handleWaitOn)({requestId:E,waitsOn:_.waitsOn,reason:_.reason,clear:"clear-wait"===q},t,k,I,$),v[q])
3597
3615
  case"update-areas":return C.info("Routing to update-areas handler"),(0,y.withAlchemyKind)(await(0,h.handleUpdateAreas)({requestId:E,areas:_.areas},t,k,I,$),v["update-areas"])
3598
3616
  case"search":{C.info("Routing to search-requests handler")
@@ -3621,7 +3639,7 @@ return!1===r?.blocked?(0,y.withAlchemyKind)({content:[{type:"text",text:`\u26a0\
3621
3639
  try{const e=k.getProxyClient(),r=await e.proxyGet(`/api/projects/${t.projectId}/tasks`,{requestId:E,limit:1}),n=r?.data||r||[]
3622
3640
  return(Array.isArray(n)?n.length:0)>0?{content:[{type:"text",text:`\u274c Cannot delete ${E}: it has tasks. Only empty requests (no tasks created) can be deleted.`}],isError:!0}:(await e.proxyDelete(`/api/projects/${t.projectId}/requests/${E}`),C.info("Empty request deleted",{requestId:E}),(0,y.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${E} 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}`
3623
3641
  return C.error(e),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${e}`}],isError:!0}}}}catch(e){return C.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}}}
3624
- 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"}`
3642
+ 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",status:"status-digest","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"}`
3625
3643
  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)
3626
3644
  if(e){const t=String(e)
3627
3645
  r.updatedAfter=n(t)?new Date(`${t}T00:00:00`).toISOString():new Date(t).toISOString()}if(t){const e=String(t)
@@ -4081,7 +4099,7 @@ try{const e=await i.proxyPost(`/api/projects/${s}/inbox-chat`,{text:l,anchor:m})
4081
4099
  if(!r?.delivered){const n=r?.reason?String(r.reason):e?.error?.message??"not delivered"
4082
4100
  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."}]}}
4083
4101
  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)
4084
- 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)
4102
+ 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 (each option with its consequence, recommendation first with why) -- 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)
4085
4103
  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
4086
4104
  if(r(n.anchor)&&(!(o=n.anchor)||"object"!=typeof o||"project"!==o.type))return null
4087
4105
  var o
@@ -4128,19 +4146,19 @@ try{const e=await l.getInteractionPosture(E)
4128
4146
  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)
4129
4147
  i&&(g.onExpiry="escalate",F=!0),"how"===g.steer&&g.defaultOption&&(g.onExpiry="escalate")
4130
4148
  const d=(0,I.resolveQuestionGate)({targeted:!1,hasDefault:!!g.defaultOption,priority:b,onExpiry:g.onExpiry,humanInputFloor:o,namesCriteria:C.length>0,offersNotYet:(0,I.offersNotYet)(g.options),userAskedToBeAsked:i},n)
4131
- 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:E,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}:{},...x,...C.length>0?{criteria:C}:{},takenOnDefault:!0})
4149
+ 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:E,questionType:m,question:g.question,options:g.options,context:g.context,defaultOption:g.defaultOption,onExpiry:g.onExpiry,...g.steer?{steer:g.steer}:{},...!0===g.changeableLater?{changeableLater:!0}:{},priority:b,expiresInHours:v,...k?{relatesTo:k}:{},...Array.isArray(g.fields)&&g.fields.length>0?{fields:g.fields}:{},...x,...C.length>0?{criteria:C}:{},takenOnDefault:!0})
4132
4150
  if("settled"===e.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${e.settled.guidance}`}]}
4133
4151
  const t=e.message
4134
4152
  if(t&&"responded"===t.status)return{content:[{type:"text",text:p(t,g)}]}
4135
4153
  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")}]}
4136
4154
  if("defer-batch"===d.disposition){const e=g.defaultOption,t=(g.options||[]).find(t=>t?.id===e),n=t?t.label:e
4137
- try{const e=await l.createQuestionSettleAware({fromSessionId:E,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}:{},...x})
4155
+ try{const e=await l.createQuestionSettleAware({fromSessionId:E,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}:{},...!0===g.changeableLater?{changeableLater:!0}:{},priority:b,expiresInHours:v,...k?{relatesTo:k}:{},...S?{forceAsk:!0}:{},...x})
4138
4156
  if("settled"===e.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${e.settled.guidance}`}]}
4139
4157
  const t=e.message
4140
4158
  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
4141
4159
  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:C.length})})
4142
4160
  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:E,toSessionId:q,questionType:m})
4143
- const N=await l.createQuestionSettleAware({fromSessionId:E,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}:{},...x,...C.length>0?{criteria:C}:{}})
4161
+ const N=await l.createQuestionSettleAware({fromSessionId:E,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}:{},...!0===g.changeableLater?{changeableLater:!0}:{},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}:{},...x,...C.length>0?{criteria:C}:{}})
4144
4162
  if("settled"===N.kind)return{content:[{type:"text",text:`\u{1f4da} **Already decided: no question needed.**\n\n${N.settled.guidance}`}]}
4145
4163
  const z=N.message
4146
4164
  if(!q&&"responded"===z?.status&&"self-driving"===z?.response?.settledBy)return{content:[{type:"text",text:p(z,g)}]}
@@ -4201,9 +4219,9 @@ const M=await d.createReport({fromSessionId:A,toSessionId:O,status:h,summary:p,d
4201
4219
  let U=!1
4202
4220
  try{const e=await x.sessionIdentity.getSessionRequestId(c,a,A),n=r.projectId
4203
4221
  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))
4204
- U=!!o&&"settle"===s(o).finalize&&"setup"===s(o).kind}}catch{}const G=(0,_.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||{}
4222
+ U=!!o&&"settle"===s(o).finalize&&"setup"===s(o).kind}}catch{}const W=(0,_.decideParentWake)({attention:q,explicitTarget:L,silent:S,hasParent:!!B,recipientAgentType:H,senderSettles:U}),G=!(!W.notify||!B)&&await o(B,function(e,t="agent"){const r=e.content||{}
4205
4223
  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)
4206
- 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,_.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)
4224
+ return a.info("report routing",{messageCode:M.messageCode,attention:q,recipientAgentType:H,reason:W.reason,notified:G}),{content:[{type:"text",text:`\u2705 Report submitted.${(0,_.describeParentWake)(W,G)}${"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)
4207
4225
  throw new y.ValidationError(`Unknown message type: ${u}. Use "question" or "report".`)}(e,c,D,q,P),r("send",e))
4208
4226
  case"note":return(0,v.withAlchemyKind)(await async function(e,t,r,n,o){const s=n.getSessionMessageClient()
4209
4227
  if(!s)throw new y.ValidationError("Session message client not available")
@@ -4220,9 +4238,9 @@ const d=s.getSessionMessageClient()
4220
4238
  if(!d)throw new y.ValidationError("Session message client not available")
4221
4239
  if(c.startsWith("Q-")){const{text:t,selectedOptions:n,notes:o,quote:s,sectionAnswers:l}=e,u=i?.sessionId
4222
4240
  if(!u)throw new y.ValidationError("Cannot answer: this call carries no session identity.")
4223
- if(!0===e.escalate){const{escalatedTo:e}=await d.escalateQuestion(c,{escalatedBySessionId:u,notes:o})
4224
- 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})
4225
- const h=await d.answerQuestion(c,{respondedBySessionId:u,text:t,selectedOptions:n,notes:o,...s?{quote:s}:{},...l&&"object"==typeof l?{sectionAnswers:l}:{}})
4241
+ if(!0===e.escalate){const{escalatedTo:t}=await d.escalateQuestion(c,{escalatedBySessionId:u,notes:o,...Array.isArray(e.questionContent?.options)?{options:e.questionContent.options}:{}})
4242
+ return{content:[{type:"text",text:`\u2934\ufe0f Question ${c} escalated to the user as **${t}**.\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})
4243
+ const h=await d.answerQuestion(c,{respondedBySessionId:u,text:t,selectedOptions:n,notes:o,...s?{quote:s}:{},...l&&"object"==typeof l?{sectionAnswers:l}:{},..."number"==typeof e.spendCeiling?{spendCeiling:e.spendCeiling}:{}})
4226
4244
  if(h?.briefChange){const e=h.briefChange
4227
4245
  return{content:[{type:"text",text:`Recorded on the brief ${c} (${(e.recorded??[]).join(", ")}). ${e.message??""}`.trim()}]}}if(h?.points){const e=h.points
4228
4246
  return{content:[{type:"text",text:`Recorded on the brief ${c}: ${(e.answered??[]).join(", ")}. Still open on its card, not answered and not taken for the user: ${(e.open??[]).join(", ")}.`}]}}return{content:[{type:"text",text:`\u2705 Question answered! The asking session is resumed by the server with your answer.\n\n${a(h)}`}]}}if(c.startsWith("R-")){r.info("Acknowledging report",{messageCode:c})
@@ -4247,7 +4265,7 @@ try{const e=c?.projectId
4247
4265
  if(e){const t=await q.proxyGet(`/api/projects/${e}/session-plan-associations/by-session/${encodeURIComponent(r)}`),n=t?.data??t??[]
4248
4266
  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=[]
4249
4267
  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()
4250
- 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"})
4268
+ 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:"The planner keeps working on the plan; nothing starts yet"},{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"})
4251
4269
  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()
4252
4270
  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)")
4253
4271
  const t=q.getSessionMessageClient()
@@ -4299,7 +4317,7 @@ const s=await o.getMessage(n)
4299
4317
  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")
4300
4318
  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}}}
4301
4319
  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),x=t(6688),C=t(69883),_=t(69048),E=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
4302
- 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}}
4320
+ 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. When the question has no options, give the ways forward in questionContent.options."},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}}
4303
4321
  let c=!1
4304
4322
  for(;Date.now()<i;){await new Promise(e=>setTimeout(e,3e3))
4305
4323
  let e=null
@@ -4336,7 +4354,7 @@ if("interactive"===n)return"interactive"}catch(e){t.warn("Failed to read project
4336
4354
  if("ask"===p||"interactive"===p){g.info("Returning planner choice pending",{planningMode:p})
4337
4355
  try{const e=m?.sessionId||i.sessionIdentity.getIdentity()?.sessionId,t=f?.getSessionMessageClient()
4338
4356
  if(t&&e){const r=await i.sessionIdentity.getParentSessionId(e,f,g),n=h.description||h.prompt
4339
- 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}}})}
4357
+ await t.createQuestion({fromSessionId:e,toSessionId:r||void 0,questionType:"alternatives",question:`Planning requested: ${n}`,options:[{id:"auto",label:"Auto Plan",description:"A planner agent writes the plan; nothing is built until you start it"},{id:"manual",label:"Manual Plan",description:"You write the plan yourself in the plan editor"},{id:"cancel",label:"Skip",description:"No plan is made"}],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}}})}
4340
4358
  break}const w="planner"
4341
4359
  let b=null
4342
4360
  if(h.documentId){const e=await f.getDocumentClient().getDocument(h.documentId)
@@ -4400,11 +4418,11 @@ return s.incrementCounter("research_fanout_request_success",1),c.info("research_
4400
4418
  const r=t(58824),n=t(6688)},1540(e,exports,t){exports.def=void 0
4401
4419
  const r=t(76183),n=t(51725),o=t(8955)
4402
4420
  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(C)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
4403
- 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
4404
- if(c&&/Project map/.test(c))return c
4405
- let l=null
4421
+ try{const s=await e.proxyPost(`/api/projects/${r}/requests/${n}/pickup-compose`,{sessionId:o}),a=s?.data?.text??s?.text,{withInFlightFiles:c}=await t.e(912).then(()=>t(47912)),l=await c(i||process.cwd(),"string"==typeof a&&a.trim()?a.trim():null,s?.data?.inFlightWithoutFiles??s?.inFlightWithoutFiles)
4422
+ if(l&&/Project map/.test(l))return l
4423
+ let d=null
4406
4424
  try{const{generateProjectMap:e}=await t.e(356).then(()=>t.t(69356,23))
4407
- l=e(i||process.cwd())}catch{l=null}return c||l?[c,l].filter(Boolean).join("\n\n"):null}catch(e){return s.warn("pickup-compose failed; the association hook will deliver guidance as an inject",{requestId:n,error:(0,v.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:r,toSessionId:n,body:o,requestId:s,logger:i}=e
4425
+ d=e(i||process.cwd())}catch{d=null}return l||d?[l,d].filter(Boolean).join("\n\n"):null}catch(e){return s.warn("pickup-compose failed; the association hook will deliver guidance as an inject",{requestId:n,error:(0,v.getErrorMessage)(e)}),null}}async function o(e){const{apiClient:t,fromSessionId:r,toSessionId:n,body:o,requestId:s,logger:i}=e
4408
4426
  if(!t||!r)return
4409
4427
  const a=t.getSessionMessageClient()
4410
4428
  if(a)try{const e=await a.createInstruction({fromSessionId:r,toSessionId:n,body:o,...s?{requestId:s}:{}})
@@ -4795,134 +4813,135 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
4795
4813
  return o(t,e),t})
4796
4814
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.testingDefs=void 0
4797
4815
  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})
4798
- 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("")
4799
- 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{}
4816
+ 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){async function r(e,t,r){try{const n=await(0,h.resolveRequestIdOrError)(t,r,e.currentSessionId,"no active request")
4817
+ return n?.ok&&n.requestId?`?forRequest=${encodeURIComponent(n.requestId)}`:""}catch{return""}}function n(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("")
4818
+ 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 o(e,t,r,n,o){if(!e||!n.git)return{}
4800
4819
  let s
4801
4820
  try{const t=await o.proxyGet(`/api/projects/${n.projectId}/environments/${encodeURIComponent(e)}`)
4802
- s=(t?.data??t)?.config?.image}catch{return{}}const i=n.git,a=await(0,c.compareToFloor)(s,async e=>{try{return await i.raw(["show",`${t}:${e}`])}catch{return null}})
4821
+ s=(t?.data??t)?.config?.image}catch{return{}}const i=n.git,a=await(0,l.compareToFloor)(s,async e=>{try{return await i.raw(["show",`${t}:${e}`])}catch{return null}})
4803
4822
  if("unknown"===a.status||!a.runsOn)return{}
4804
- const l=`${a.runsOn.runtime} ${a.runsOn.version}`,d={runsOn:l,...a.floor?{floor:`${a.floor.runtime} ${a.floor.version}`,floorSource:a.floor.source}:{}}
4823
+ const c=`${a.runsOn.runtime} ${a.runsOn.version}`,d={runsOn:c,...a.floor?{floor:`${a.floor.runtime} ${a.floor.version}`,floorSource:a.floor.source}:{}}
4805
4824
  if("match"===a.status)return{record:d}
4806
- if(String(r||"").trim().toLowerCase().replace(/\s+/g," ")===l)return{record:{...d,keptByUser:!0}}
4807
- const u=c.RUNTIME_LABEL[a.runsOn.runtime],h=a.floor.version
4808
- return{refuse:`Not registered: ${a.message}\n\nAsk the user before registering. The proposal question says this in plain words and offers the floor as an option, e.g. {id:"floor", label:"Run it on ${u} ${h}"} next to {id:"keep", label:"Keep ${u} ${a.runsOn.version}"}. If they pick the floor, set up an environment from ${a.suggestedImage} (environment_verify with the check's commands, then create it) and register the check with that environment. If they keep ${u} ${a.runsOn.version}, call again with runtimeKept: "${l}". Offer one version, the floor; propose a matrix of versions only when the user asks for one.`}}exports.handleTestingProcessList=async function(e,t,n,s,i){const a=i.startTimer("tool_execution_time")
4809
- try{const e=await n.proxyGet(`/api/projects/${t.projectId}/test-processes`),o=e?.data||e||[],s=Array.isArray(o)?o:[]
4810
- if(0===s.length)return a({success:!0}),{content:[{type:"text",text:"No test processes configured for this project yet. Use `testing_process_create` to create one."}]}
4811
- const i=r(s)
4812
- return a({success:!0}),{content:[{type:"text",text:`## Test Processes (${s.length})\n\n${i}`}]}}catch(e){return a({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to list test processes: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessGet=async function(e,t,r,n,s){const i=s.startTimer("tool_execution_time")
4813
- try{const n=await r.proxyGet(`/api/projects/${t.projectId}/test-processes/${e.processId}`),process=n?.data||n
4814
- if(!process)return i({success:!1,error:"not_found"}),{content:[{type:"text",text:`Test process not found: ${e.processId}`}],isError:!0}
4815
- const o=process.config||{},s=o.commands||[]
4816
- let a=`## ${process.name}\n`
4817
- process.description&&(a+=`${process.description}\n`),process.scope&&(a+=`**Scope:** \`${process.scope}\`\n`),a+=`**Status:** ${process.isActive?"Active":"Inactive"}\n\n`,o.setup?.length&&(a+=`### Setup\n${o.setup.map(e=>`\`\`\`\n${e}\n\`\`\``).join("\n")}\n\n`),a+=`### Test Commands (${s.length})\n`
4818
- for(let e=0;e<s.length;e++){const t=s[e]
4819
- a+=`\n**${e+1}. ${t.name}**${t.required?": REQUIRED":": optional"}\n`,a+=` Command: \`${t.command}\`\n`,t.timeout&&(a+=` Timeout: ${t.timeout}s\n`)}return Array.isArray(o.fingerprint)&&o.fingerprint.length>0&&(a+=`\n### Fingerprint Files\n${o.fingerprint.map(e=>`- \`${e}\``).join("\n")}\n`),"number"==typeof o.poolSize&&(a+=`\n**Pool size:** ${o.poolSize}\n`),i({success:!0}),{content:[{type:"text",text:a}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to get test process: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessCreate=async function(e,r,s,i,a){const c=a.startTimer("tool_execution_time")
4825
+ if(String(r||"").trim().toLowerCase().replace(/\s+/g," ")===c)return{record:{...d,keptByUser:!0}}
4826
+ const u=l.RUNTIME_LABEL[a.runsOn.runtime],h=a.floor.version
4827
+ return{refuse:`Not registered: ${a.message}\n\nAsk the user before registering. The proposal question says this in plain words and offers the floor as an option, e.g. {id:"floor", label:"Run it on ${u} ${h}"} next to {id:"keep", label:"Keep ${u} ${a.runsOn.version}"}. If they pick the floor, set up an environment from ${a.suggestedImage} (environment_verify with the check's commands, then create it) and register the check with that environment. If they keep ${u} ${a.runsOn.version}, call again with runtimeKept: "${c}". Offer one version, the floor; propose a matrix of versions only when the user asks for one.`}}exports.handleTestingProcessList=async function(e,t,o,i,a){const c=a.startTimer("tool_execution_time")
4828
+ try{const e=await o.proxyGet(`/api/projects/${t.projectId}/test-processes${await r(t,o,i)}`),s=e?.data||e||[],a=Array.isArray(s)?s:[]
4829
+ if(0===a.length)return c({success:!0}),{content:[{type:"text",text:"No test processes configured for this project yet. Use `testing_process_create` to create one."}]}
4830
+ const l=n(a)
4831
+ return c({success:!0}),{content:[{type:"text",text:`## Test Processes (${a.length})\n\n${l}`}]}}catch(e){return c({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to list test processes: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessGet=async function(e,t,n,o,i){const a=i.startTimer("tool_execution_time")
4832
+ try{const s=await n.proxyGet(`/api/projects/${t.projectId}/test-processes/${e.processId}${await r(t,n,o)}`),process=s?.data||s
4833
+ if(!process)return a({success:!1,error:"not_found"}),{content:[{type:"text",text:`Test process not found: ${e.processId}`}],isError:!0}
4834
+ const i=process.config||{},c=i.commands||[]
4835
+ let l=`## ${process.name}\n`
4836
+ process.description&&(l+=`${process.description}\n`),process.scope&&(l+=`**Scope:** \`${process.scope}\`\n`),l+=`**Status:** ${process.isActive?"Active":"Inactive"}\n\n`,i.setup?.length&&(l+=`### Setup\n${i.setup.map(e=>`\`\`\`\n${e}\n\`\`\``).join("\n")}\n\n`),l+=`### Test Commands (${c.length})\n`
4837
+ for(let e=0;e<c.length;e++){const t=c[e]
4838
+ l+=`\n**${e+1}. ${t.name}**${t.required?": REQUIRED":": optional"}\n`,l+=` Command: \`${t.command}\`\n`,t.timeout&&(l+=` Timeout: ${t.timeout}s\n`)}return Array.isArray(i.fingerprint)&&i.fingerprint.length>0&&(l+=`\n### Fingerprint Files\n${i.fingerprint.map(e=>`- \`${e}\``).join("\n")}\n`),"number"==typeof i.poolSize&&(l+=`\n**Pool size:** ${i.poolSize}\n`),Array.isArray(i.leavesOut)&&i.leavesOut.length>0&&(l+=`\n### Left out (not run by this check)\n${i.leavesOut.map(e=>`- ${e?.what}: ${(e?.paths??[]).map(e=>`\`${e}\``).join(", ")}`).join("\n")}\n`),a({success:!0}),{content:[{type:"text",text:l}]}}catch(e){return a({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to get test process: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessCreate=async function(e,r,n,i,a){const c=a.startTimer("tool_execution_time")
4820
4839
  try{const a={setup:e.setup||[],commands:e.commands.map(e=>({name:e.name,command:e.command,required:!1!==e.required,timeout:e.timeout||300}))}
4821
- void 0!==e.fingerprint&&(a.fingerprint=e.fingerprint),void 0!==e.poolSize&&(a.poolSize=e.poolSize)
4840
+ void 0!==e.fingerprint&&(a.fingerprint=e.fingerprint),void 0!==e.poolSize&&(a.poolSize=e.poolSize),e.leavesOut?.length&&(a.leavesOut=e.leavesOut)
4822
4841
  try{const e=r.currentSessionId
4823
- if(e){const t=await s.proxyGet(`/api/projects/${r.projectId}/session-request-associations/by-session/${encodeURIComponent(e)}?activeOnly=true`),n=(Array.isArray(t?.data)?t.data:Array.isArray(t)?t:[]).map(e=>e.requestLocalId??e.request_local_id).find(e=>"string"==typeof e&&/^req\d{6}$/.test(e))
4824
- n&&(a.originRequest=n)}}catch{}let d=""
4825
- if(void 0===e.fingerprint){const e=a.originRequest?`requests/${a.originRequest}`:await(0,l.resolveDefaultBranch)(r.mainBasePath,r.gitBinary).catch(()=>"HEAD"),t=await async function(e,t,r){if(!e||!function(e){const t=e.flatMap(e=>e.split(/&&|;/)).map(e=>e.trim()).filter(Boolean)
4842
+ if(e){const t=await n.proxyGet(`/api/projects/${r.projectId}/session-request-associations/by-session/${encodeURIComponent(e)}?activeOnly=true`),o=(Array.isArray(t?.data)?t.data:Array.isArray(t)?t:[]).map(e=>e.requestLocalId??e.request_local_id).find(e=>"string"==typeof e&&/^req\d{6}$/.test(e))
4843
+ o&&(a.originRequest=o)}}catch{}let l=""
4844
+ if(void 0===e.fingerprint){const e=a.originRequest?`requests/${a.originRequest}`:await(0,d.resolveDefaultBranch)(r.mainBasePath,r.gitBinary).catch(()=>"HEAD"),t=await async function(e,t,r){if(!e||!function(e){const t=e.flatMap(e=>e.split(/&&|;/)).map(e=>e.trim()).filter(Boolean)
4826
4845
  return t.length>0&&t.every(e=>{const[t,...r]=e.split(/\s+/)
4827
4846
  if("go"===t)return"mod download"===r.join(" ")
4828
4847
  if(("pip"===t||"pip3"===t)&&"install"===r[0])return/^install -r \S+$/.test(r.join(" "))
4829
4848
  const n=r.filter(e=>!e.startsWith("-"))
4830
- return n.length<=1&&(h[t]??[]).includes(n[0]??"")})}(r))return null
4831
- try{const r=new Set((await e.raw(["ls-tree","--name-only",t])).split("\n").map(e=>e.trim())),n=u.filter(e=>r.has(e))
4849
+ return n.length<=1&&(f[t]??[]).includes(n[0]??"")})}(r))return null
4850
+ try{const r=new Set((await e.raw(["ls-tree","--name-only",t])).split("\n").map(e=>e.trim())),n=p.filter(e=>r.has(e))
4832
4851
  return n.length>0?n:null}catch{return null}}(r.git,e,a.setup)
4833
- t&&(a.fingerprint=t,d=`\nFingerprint not given, so it was set to the repository's lockfiles: ${t.map(e=>`\`${e}\``).join(", ")}. Setup now re-runs only when one of them changes; pass fingerprint: [] to run it every time.`)}const p=e.environmentId
4834
- {const t=a.originRequest?`requests/${a.originRequest}`:await(0,l.resolveDefaultBranch)(r.mainBasePath,r.gitBinary).catch(()=>"HEAD"),o=await n("string"==typeof p?p:void 0,t,e.runtimeKept,r,s)
4835
- if(o.refuse)return c({success:!1,error:"runtime_floor"}),{content:[{type:"text",text:o.refuse}],isError:!0}
4836
- o.record&&(a.runtime=o.record)}let f=""
4837
- if("string"==typeof p&&p&&r.currentSessionId){const{trialRun:e,trialFailures:n}=await t.e(579).then(()=>t(74579)),o=await e({sessionId:r.currentSessionId,environmentId:p,...a.originRequest?{branchName:`requests/${a.originRequest}`}:{},setup:a.setup,commands:a.commands})
4852
+ t&&(a.fingerprint=t,l=`\nFingerprint not given, so it was set to the repository's lockfiles: ${t.map(e=>`\`${e}\``).join(", ")}. Setup now re-runs only when one of them changes; pass fingerprint: [] to run it every time.`)}const u=e.environmentId
4853
+ {const t=a.originRequest?`requests/${a.originRequest}`:await(0,d.resolveDefaultBranch)(r.mainBasePath,r.gitBinary).catch(()=>"HEAD"),s=await o("string"==typeof u?u:void 0,t,e.runtimeKept,r,n)
4854
+ if(s.refuse)return c({success:!1,error:"runtime_floor"}),{content:[{type:"text",text:s.refuse}],isError:!0}
4855
+ s.record&&(a.runtime=s.record)}let h=""
4856
+ if("string"==typeof u&&u&&r.currentSessionId){const{trialRun:e,trialFailures:n}=await t.e(579).then(()=>t(74579)),o=await e({sessionId:r.currentSessionId,environmentId:u,...a.originRequest?{branchName:`requests/${a.originRequest}`}:{},setup:a.setup,commands:a.commands})
4838
4857
  if(o){const e=n(o)
4839
4858
  if(e.length>0)return c({success:!1,error:"trial_failed"}),{content:[{type:"text",text:`Not registered: the check was tried once in its environment first, and it cannot run there as written:\n${e.map(e=>`- ${e}`).join("\n")}\n\nPick an environment that has what the commands need (environment_list), or change the commands, then create it again. Tell the user in plain words if what they asked for cannot run in any environment yet.`}],isError:!0}
4840
- f=`\nTried once in its environment first: ${o.map(e=>`${e.name} exit ${e.exit}`).join(", ")}.`}else f="\n\u26a0\ufe0f Could not try it before registering (no box started); its first real run is its first trial."}const m=await s.proxyPost(`/api/projects/${r.projectId}/test-processes`,{name:e.name,description:e.description,scope:e.scope,pathGlobs:e.pathGlobs,areas:e.areas,config:a,environmentId:e.environmentId,createdBy:"setup-architect"}),process=m?.data||m
4841
- try{const{sessionIdentity:e}=await Promise.resolve().then(()=>t(6688)),n=e.getIdentity()
4842
- n?.sessionId&&process?.id&&await s.proxyPost(`/api/projects/${r.projectId}/session-test-process-associations`,{opencodeSessionId:n.sessionId,testProcessId:process.id})}catch(e){i.warn("Failed to write session-test-process association",{error:(0,o.getErrorMessage)(e)})}return c({success:!0}),{content:[{type:"text",text:`\u2705 Created test process **${process.name}** (${process.id}) with ${a.commands.length} test commands.${e.scope?`\nLabel (display only): \`${e.scope}\``:""}\n${e.pathGlobs?.length||e.areas?.length?`Runs when a change matches: ${[...e.pathGlobs||[],...(e.areas||[]).map(e=>`area ${e}`)].join(", ")}`:'\u26a0\ufe0f UNSCOPED: this runs on EVERY change to the project. If it should only run for some changes, set pathGlobs (e.g. ["sites/**"]).'}${f}${d}\n\nThe process is now active and will run when requests are completed.`}]}}catch(e){return c({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to create test process: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessUpdate=async function(e,r,s,i,a){const c=a.startTimer("tool_execution_time")
4843
- try{const o={}
4844
- if(void 0!==e.name&&(o.name=e.name),void 0!==e.description&&(o.description=e.description),void 0!==e.scope&&(o.scope=e.scope),void 0!==e.pathGlobs&&(o.pathGlobs=e.pathGlobs),void 0!==e.areas&&(o.areas=e.areas),void 0!==e.isActive){let t=""
4845
- try{const e=r.currentSessionId,n=e?await s.proxyGet(`/api/projects/${r.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`):null
4846
- t=String((n?.data??n)?.agentType??"")}catch{t=""}if("alchemist"!==t)return{content:[{type:"text",text:"Refused: activating or deactivating a test process is the user's call, not the worker's -- it decides which checks may prove criteria and satisfy the test gate. Ask the user (question tool); they can toggle it in Settings, or tell the Alchemist in so many words."}],isError:!0}
4847
- o.isActive=e.isActive,i.info("testing_process_update: activation changed by the hub on the user's instruction",{processId:e.processId,isActive:e.isActive})}let a
4848
- void 0!==e.environmentId&&(o.environmentId=e.environmentId)
4849
- const d=e.environmentId
4850
- if("string"==typeof d&&d){let t="HEAD"
4851
- try{const n=await s.proxyGet(`/api/projects/${r.projectId}/test-processes/${e.processId}`),o=(n?.data??n)?.config?.originRequest
4852
- t="string"==typeof o&&o?`requests/${o}`:await(0,l.resolveDefaultBranch)(r.mainBasePath,r.gitBinary).catch(()=>"HEAD")}catch{t=await(0,l.resolveDefaultBranch)(r.mainBasePath,r.gitBinary).catch(()=>"HEAD")}const o=await n(d,t,e.runtimeKept,r,s)
4853
- if(o.refuse)return c({success:!1,error:"runtime_floor"}),{content:[{type:"text",text:o.refuse}],isError:!0}
4854
- a=o.record??{}}if(void 0!==e.commands||void 0!==e.setup||void 0!==e.fingerprint||void 0!==e.poolSize||void 0!==a){const n=await s.proxyGet(`/api/projects/${r.projectId}/test-processes/${e.processId}`),c=n?.data||n,l=c?.config||{},d={setup:e.setup??l.setup??[],commands:e.commands?e.commands.map(e=>({name:e.name,command:e.command,required:!1!==e.required,timeout:e.timeout||300})):l.commands??[]}
4855
- void 0!==e.fingerprint?d.fingerprint=e.fingerprint:void 0!==l.fingerprint&&(d.fingerprint=l.fingerprint),void 0!==e.poolSize?d.poolSize=e.poolSize:void 0!==l.poolSize&&(d.poolSize=l.poolSize),void 0!==l.verifyAcceptanceCriteria&&(d.verifyAcceptanceCriteria=l.verifyAcceptanceCriteria),void 0!==l.originRequest&&(d.originRequest=l.originRequest),void 0!==a?Object.keys(a).length&&(d.runtime=a):void 0!==l.runtime&&(d.runtime=l.runtime),o.config=d
4856
- try{const{resolveRequestIdOrError:e}=await Promise.resolve().then(()=>t(36651)),n=await e(s,i,r.currentSessionId,"no active request")
4857
- n?.ok&&n.requestId&&(o.changedFor=n.requestId)}catch{}}const u=await s.proxyPatch(`/api/projects/${r.projectId}/test-processes/${e.processId}`,o),process=u?.data||u
4858
- return c({success:!0}),{content:[{type:"text",text:`\u2705 Updated test process **${process.name}** (${process.id}).`}]}}catch(e){return c({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to update test process: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleExecuteTestCommand=async function(e,r,n,s,i,c,l){const d=i.startTimer("tool_execution_time"),u=s.child({tool:"execute_test_command"})
4859
- try{const i=await n.proxyGet(`/api/projects/${r.projectId}/test-processes/${e.processId}`),process=i?.data||i
4860
- if(!process?.config)return d({success:!1,error:"not_found"}),{content:[{type:"text",text:`Test process ${e.processId} not found or has no config.`}],isError:!0}
4861
- let h
4859
+ h=`\nTried once in its environment first: ${o.map(e=>`${e.name} exit ${e.exit}`).join(", ")}.`}else h="\n\u26a0\ufe0f Could not try it before registering (no box started); its first real run is its first trial."}const m=await n.proxyPost(`/api/projects/${r.projectId}/test-processes`,{name:e.name,description:e.description,scope:e.scope,pathGlobs:e.pathGlobs,areas:e.areas,config:a,environmentId:e.environmentId,createdBy:"setup-architect"}),process=m?.data||m
4860
+ try{const{sessionIdentity:e}=await Promise.resolve().then(()=>t(6688)),o=e.getIdentity()
4861
+ o?.sessionId&&process?.id&&await n.proxyPost(`/api/projects/${r.projectId}/session-test-process-associations`,{opencodeSessionId:o.sessionId,testProcessId:process.id})}catch(e){i.warn("Failed to write session-test-process association",{error:(0,s.getErrorMessage)(e)})}return c({success:!0}),{content:[{type:"text",text:`\u2705 Created test process **${process.name}** (${process.id}) with ${a.commands.length} test commands.${e.scope?`\nLabel (display only): \`${e.scope}\``:""}\n${e.pathGlobs?.length||e.areas?.length?`Runs when a change matches: ${[...e.pathGlobs||[],...(e.areas||[]).map(e=>`area ${e}`)].join(", ")}`:'\u26a0\ufe0f UNSCOPED: this runs on EVERY change to the project. If it should only run for some changes, set pathGlobs (e.g. ["sites/**"]).'}${h}${l}\n\nThe process is now active and will run when requests are completed.`}]}}catch(e){return c({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to create test process: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessUpdate=async function(e,t,n,i,a){const c=a.startTimer("tool_execution_time")
4862
+ try{const s={}
4863
+ if(void 0!==e.name&&(s.name=e.name),void 0!==e.description&&(s.description=e.description),void 0!==e.scope&&(s.scope=e.scope),void 0!==e.pathGlobs&&(s.pathGlobs=e.pathGlobs),void 0!==e.areas&&(s.areas=e.areas),void 0!==e.isActive){let r=""
4864
+ try{const e=t.currentSessionId,o=e?await n.proxyGet(`/api/projects/${t.projectId}/cli-sessions/external/opencode/${encodeURIComponent(e)}`):null
4865
+ r=String((o?.data??o)?.agentType??"")}catch{r=""}if("alchemist"!==r)return{content:[{type:"text",text:"Refused: activating or deactivating a test process is the user's call, not the worker's -- it decides which checks may prove criteria and satisfy the test gate. Ask the user (question tool); they can toggle it in Settings, or tell the Alchemist in so many words."}],isError:!0}
4866
+ s.isActive=e.isActive,i.info("testing_process_update: activation changed by the hub on the user's instruction",{processId:e.processId,isActive:e.isActive})}let a
4867
+ void 0!==e.environmentId&&(s.environmentId=e.environmentId)
4868
+ const l=e.environmentId
4869
+ if("string"==typeof l&&l){let s="HEAD"
4870
+ try{const o=await n.proxyGet(`/api/projects/${t.projectId}/test-processes/${e.processId}${await r(t,n,i)}`),a=(o?.data??o)?.config?.originRequest
4871
+ s="string"==typeof a&&a?`requests/${a}`:await(0,d.resolveDefaultBranch)(t.mainBasePath,t.gitBinary).catch(()=>"HEAD")}catch{s=await(0,d.resolveDefaultBranch)(t.mainBasePath,t.gitBinary).catch(()=>"HEAD")}const u=await o(l,s,e.runtimeKept,t,n)
4872
+ if(u.refuse)return c({success:!1,error:"runtime_floor"}),{content:[{type:"text",text:u.refuse}],isError:!0}
4873
+ a=u.record??{}}if(void 0!==e.commands||void 0!==e.setup||void 0!==e.fingerprint||void 0!==e.poolSize||void 0!==e.leavesOut||void 0!==a){const o=await n.proxyGet(`/api/projects/${t.projectId}/test-processes/${e.processId}${await r(t,n,i)}`),c=o?.data||o,l=c?.config||{},d={setup:e.setup??l.setup??[],commands:e.commands?e.commands.map(e=>({name:e.name,command:e.command,required:!1!==e.required,timeout:e.timeout||300})):l.commands??[]}
4874
+ void 0!==e.fingerprint?d.fingerprint=e.fingerprint:void 0!==l.fingerprint&&(d.fingerprint=l.fingerprint),void 0!==e.poolSize?d.poolSize=e.poolSize:void 0!==l.poolSize&&(d.poolSize=l.poolSize),void 0!==l.verifyAcceptanceCriteria&&(d.verifyAcceptanceCriteria=l.verifyAcceptanceCriteria),void 0!==l.originRequest&&(d.originRequest=l.originRequest),void 0!==e.leavesOut?d.leavesOut=e.leavesOut:void 0!==l.leavesOut&&(d.leavesOut=l.leavesOut),void 0!==a?Object.keys(a).length&&(d.runtime=a):void 0!==l.runtime&&(d.runtime=l.runtime),s.config=d
4875
+ try{const e=await(0,h.resolveRequestIdOrError)(n,i,t.currentSessionId,"no active request")
4876
+ e?.ok&&e.requestId&&(s.changedFor=e.requestId)}catch{}}const u=await n.proxyPatch(`/api/projects/${t.projectId}/test-processes/${e.processId}`,s),process=u?.data||u
4877
+ return c({success:!0}),{content:[{type:"text",text:`\u2705 Updated test process **${process.name}** (${process.id}).`}]}}catch(e){return c({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to update test process: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleExecuteTestCommand=async function(e,n,o,i,a,l,d){const u=a.startTimer("tool_execution_time"),h=i.child({tool:"execute_test_command"})
4878
+ try{const a=await o.proxyGet(`/api/projects/${n.projectId}/test-processes/${e.processId}${await r(n,o,h)}`),process=a?.data||a
4879
+ if(!process?.config)return u({success:!1,error:"not_found"}),{content:[{type:"text",text:`Test process ${e.processId} not found or has no config.`}],isError:!0}
4880
+ let p
4862
4881
  if("setup"===e.commandName){const e=process.config.setup||[]
4863
- if(0===e.length)return d({success:!0}),{content:[{type:"text",text:"No setup commands configured for this test process."}]}
4864
- h=e.map((e,t)=>({name:`setup-${t+1}`,command:e,timeout:120}))}else{const t=process.config.commands||[],r=t.find(t=>t.name===e.commandName)
4882
+ if(0===e.length)return u({success:!0}),{content:[{type:"text",text:"No setup commands configured for this test process."}]}
4883
+ p=e.map((e,t)=>({name:`setup-${t+1}`,command:e,timeout:120}))}else{const t=process.config.commands||[],r=t.find(t=>t.name===e.commandName)
4865
4884
  if(!r){const r=t.map(e=>e.name).join(", ")
4866
- return d({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}h=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:p}=await Promise.resolve().then(()=>t(44600)),f=globalThis.process
4867
- let m=null
4868
- const g=[]
4869
- try{const{sessionIdentity:e}=await Promise.resolve().then(()=>t(6688)),o=c?.sessionId??e.getIdentity()?.sessionId
4870
- if(g.push(`session: ${o||"NONE"}`),o){const t=await e.getSessionRequestId(n,s,o)
4871
- 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
4872
- 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}
4873
- 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")
4874
- 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)),x=c?.sessionId??S.getIdentity()?.sessionId
4875
- for(const t of h){u.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:m})
4876
- const r=`${t.name.replace(/[^a-zA-Z0-9._-]/g,"_")}-${Date.now()}`,n=w.join(k,`${r}.log`)
4885
+ return u({success:!1,error:"command_not_found"}),{content:[{type:"text",text:`Command "${e.commandName}" not found in test process "${process.name}".\nAvailable commands: ${r}\nUse "setup" to run setup commands.`}],isError:!0}}p=[{name:r.name,command:r.command,timeout:r.timeout||300}]}const{runShellCommand:f}=await Promise.resolve().then(()=>t(44600)),m=globalThis.process
4886
+ let g=null
4887
+ const y=[]
4888
+ try{const{sessionIdentity:e}=await Promise.resolve().then(()=>t(6688)),r=l?.sessionId??e.getIdentity()?.sessionId
4889
+ if(y.push(`session: ${r||"NONE"}`),r){const t=await e.getSessionRequestId(o,i,r)
4890
+ if(y.push(`reqId: ${t||"NONE"}`),t){const e=await o.proxyGet(`/api/projects/${n.projectId}/requests/${t}/test-runs`),r=e?.data||e||[],s=Array.isArray(r)?r.find(e=>e.worktreePath||e.worktree_path):null
4891
+ y.push(`runs: ${Array.isArray(r)?r.length:0}, withWorktree: ${s?.id||"NONE"}, path: ${s?.worktreePath||s?.worktree_path||"NONE"}`),s&&(g=s.worktreePath||s.worktree_path,h.info("Using test worktree from request test run",{worktreePath:g,reqId:t}))}}}catch(e){h.warn("Failed to resolve test worktree",{error:(0,s.getErrorMessage)(e)})}if(!g)return u({success:!1,error:"no_worktree"}),{content:[{type:"text",text:`No test worktree found.\n\nDiagnostics:\n${y.length>0?y.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}
4892
+ const w=[],{default:b}=await Promise.resolve().then(()=>t.t(16928,23)),{default:v}=await Promise.resolve().then(()=>t.t(79896,23)),{ALCHEMY_DIR_NAME:k}=await Promise.resolve().then(()=>t(14139)),I=b.join(g,k,"command-logs")
4893
+ try{v.mkdirSync(I,{recursive:!0})}catch{}const{runTrackedShellCommand:$}=await Promise.resolve().then(()=>t(44600)),{formatCombinedOutput:S}=await t.e(816).then(()=>t(1816)),{sessionIdentity:x}=await Promise.resolve().then(()=>t(6688)),C=l?.sessionId??x.getIdentity()?.sessionId
4894
+ for(const t of p){h.info("Executing test command",{name:t.name,command:t.command,timeout:t.timeout,cwd:g})
4895
+ const r=`${t.name.replace(/[^a-zA-Z0-9._-]/g,"_")}-${Date.now()}`,n=b.join(I,`${r}.log`)
4877
4896
  let o=null
4878
- 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:x,logPath:n},l)
4897
+ try{o=v.createWriteStream(n,{flags:"w"}),o.write(`# ${(new Date).toISOString()} starting: ${t.command}\n# cwd: ${g}\n# timeout: ${t.timeout}s\n\n`)}catch(e){h.warn("Failed to open command log file",{logPath:n,error:String(e)})}const s=await(0,c.runTrackedShellWithHeartbeat)(t.command,{cwd:g,timeoutMs:1e3*t.timeout,env:{...m.env,FORCE_COLOR:"0"},onChunk:o?(e,t)=>{o?.write(t)}:void 0},{id:r,name:t.name,sessionId:C,logPath:n},d)
4879
4898
  o&&(o.write(`\n# exit: ${s.exitCode}${s.timedOut?" (timed out)":""}\n`),o.end())
4880
- const i=(s.stdout+(s.stderr?"\n"+s.stderr:"")).trim(),{output:c,truncatedNote:d,logFooter:h}=$(i,{invocationId:r})
4881
- 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 C=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})`
4899
+ const i=(s.stdout+(s.stderr?"\n"+s.stderr:"")).trim(),{output:a,truncatedNote:l,logFooter:u}=S(i,{invocationId:r})
4900
+ if(w.push({name:t.name,command:t.command,exitCode:s.exitCode,output:a,timedOut:s.timedOut,invocationId:r,truncatedNote:l,logFooter:u}),"setup"===e.commandName&&0!==s.exitCode)break}const _=w.every(e=>0===e.exitCode),E=w.map(e=>{const t=e.timedOut?"\u23f1 TIMEOUT":0===e.exitCode?"\u2705 PASS":`\u274c FAIL (exit ${e.exitCode})`
4882
4901
  return`### ${e.name}: ${t}\n\`\`\`\n$ ${e.command}\n${e.output}\n\`\`\`${e.truncatedNote}\n${e.logFooter}`}).join("\n\n")
4883
- return d({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName}: ${C?"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")
4884
- 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.")
4902
+ return u({success:!0}),{content:[{type:"text",text:`## ${"setup"===e.commandName?"Setup":e.commandName}: ${_?"PASSED":"FAILED"}\n\n${E}`}]}}catch(e){return u({success:!1,error:"execution_error"}),{content:[{type:"text",text:`Failed to execute test command: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleReportTestResults=async function(e,r,n,o,a,c){const l=a.startTimer("tool_execution_time")
4903
+ try{const{resolveRequestIdOrError:a}=await Promise.resolve().then(()=>t(36651)),d=await a(n,o,c?.sessionId,"Cannot report test results: this session is not associated with a request.")
4885
4904
  if(!d.ok)return l({success:!1,error:"no_request_association"}),d.errorResult
4886
4905
  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]
4887
4906
  if(!g)return l({success:!1,error:"no_test_run"}),{content:[{type:"text",text:`No test run found for request ${u}.`}],isError:!0}
4888
4907
  const y=g.id
4889
- return await n.proxyPatch(`/api/projects/${r.projectId}/test-runs/${y}`,{status:e.status,results:e.results}),g.worktreePath&&fetch(`${(0,s.getActionServerBaseUrl)()}/api/release-pool-slot`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({worktreePath:g.worktreePath}),signal:AbortSignal.timeout(5e3)}).catch(e=>i.debug("release-pool-slot fire-and-forget failed (non-fatal)",{error:(0,o.getErrorMessage)(e)})),l({success:!0}),{content:[{type:"text",text:`Test run for request ${u} recorded as **${e.status}**. The server reports the result to the parent session along with its next step.\n\n${e.results.summary}`}]}}catch(e){return l({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to report test results: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessDelete=async function(e,t,r,n,s){const i=s.startTimer("tool_execution_time")
4890
- try{return await r.proxyDelete(`/api/projects/${t.projectId}/test-processes/${e.processId}`),n.info("Deleted test process",{processId:e.processId}),i({success:!0}),{content:[{type:"text",text:`Deleted test process ${e.processId}.`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to delete test process: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTests=async function(e,n,a,c,l,u){const h=l.startTimer("tool_execution_time")
4908
+ return await n.proxyPatch(`/api/projects/${r.projectId}/test-runs/${y}`,{status:e.status,results:e.results}),g.worktreePath&&fetch(`${(0,i.getActionServerBaseUrl)()}/api/release-pool-slot`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({worktreePath:g.worktreePath}),signal:AbortSignal.timeout(5e3)}).catch(e=>o.debug("release-pool-slot fire-and-forget failed (non-fatal)",{error:(0,s.getErrorMessage)(e)})),l({success:!0}),{content:[{type:"text",text:`Test run for request ${u} recorded as **${e.status}**. The server reports the result to the parent session along with its next step.\n\n${e.results.summary}`}]}}catch(e){return l({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to report test results: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTestingProcessDelete=async function(e,t,r,n,o){const i=o.startTimer("tool_execution_time")
4909
+ try{return await r.proxyDelete(`/api/projects/${t.projectId}/test-processes/${e.processId}`),n.info("Deleted test process",{processId:e.processId}),i({success:!0}),{content:[{type:"text",text:`Deleted test process ${e.processId}.`}]}}catch(e){return i({success:!1,error:"api_error"}),{content:[{type:"text",text:`Failed to delete test process: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}},exports.handleTests=async function(e,o,c,l,d,h){const p=d.startTimer("tool_execution_time")
4891
4910
  try{if("switch-off"===e.operation||"switch-on"===e.operation){const t=await async function(e,t,r){const n="switch-on"===e.operation,o=await r.proxyGet(`/api/projects/${t.projectId}/test-processes`),s=Array.isArray(o)?o:Array.isArray(o?.data)?o.data:[],i=String(e.process??"").trim(),a=i?s.find(e=>e.id===i||"string"==typeof e.name&&e.name.toLowerCase()===i.toLowerCase()):1===s.length?s[0]:void 0
4892
4911
  if(!a)return{content:[{type:"text",text:`${i?`No check matched "${i}".`:"Name the check in `process` (its name or id)."} Registered: ${s.map(e=>`${e.name} (${e.id})`).join(", ")||"none"}.`}],isError:!0}
4893
4912
  if(!!a.isActive===n)return{content:[{type:"text",text:`"${a.name}" is already ${n?"on":"off"}; nothing changed.`}]}
4894
4913
  const c="string"==typeof e.userSaid?e.userSaid.trim():""
4895
- if(!c||!await(0,d.userSaidOnRequest)(r,t.projectId,"",c,{anywhere:!0})){if(c&&t.currentSessionId)try{await(r.getSessionMessageClient?.()?.createReport({fromSessionId:t.currentSessionId,status:"success",summary:`"${a.name}" was not switched ${n?"on":"off"}: the words given as yours ("${c.length>160?`${c.slice(0,157)}...`:c}") are not in anything you wrote here, so nothing changed. If you want it ${n?"on":"off"}, tell the Alchemist.`,extraContent:{reportKind:"outcome"}}))}catch{}return{content:[{type:"text",text:`Not switched ${n?"on":"off"}: that is the user's call. `+(c?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":`Pass their words asking for it in userSaid, quoted exactly. If they have not asked, ask them, saying what ${n?"it will check again":"stops being checked"}.`)}],isError:!0}}return await r.proxyPatch(`/api/projects/${t.projectId}/test-processes/${a.id}`,{isActive:n,userSaid:c}),{content:[{type:"text",text:n?`"${a.name}" is on again: every change it covers must pass it before it merges.`:`"${a.name}" is off: changes now merge without it running. The user was told in Steer what no longer gets checked. Say it in your reply in one sentence too.`}]}}(e,n,a)
4896
- return h({success:!t.isError}),t}if("list"===e.operation){const e=await a.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[],o=Array.isArray(t)?t:[]
4897
- if(0===o.length)return h({success:!0}),{content:[{type:"text",text:'No test processes registered for this project. The user sees the Tests gate greyed as "nothing registered" and decides whether to set one up; it is not yours to raise. Verify your work with fast `run` checks in a box, and say in your completion summary if the absence mattered. `tests` operation "setup" opens only from a request that carries a gate criterion (kind "gate": a check the request itself asks to run on every merge).'}]}
4898
- const s=r(o)
4899
- return h({success:!0}),{content:[{type:"text",text:`## Test Processes (${o.length})\n\n${s}`}]}}if("run"===e.operation){let r,l=[]
4900
- try{const e=await a.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[]
4901
- l=Array.isArray(t)?t:[]}catch(e){c.warn("tests run: existence check failed; proceeding optimistically",{error:(0,o.getErrorMessage)(e)})}if(0===l.length)return h({success:!1,error:"no_process"}),{content:[{type:"text",text:'No test process registered for this project. Run `tests` with `operation: "setup"` first to bootstrap one.'}],isError:!0}
4914
+ if(!c||!await(0,u.userSaidOnRequest)(r,t.projectId,"",c,{anywhere:!0})){if(c&&t.currentSessionId)try{await(r.getSessionMessageClient?.()?.createReport({fromSessionId:t.currentSessionId,status:"success",summary:`"${a.name}" was not switched ${n?"on":"off"}: the words given as yours ("${c.length>160?`${c.slice(0,157)}...`:c}") are not in anything you wrote here, so nothing changed. If you want it ${n?"on":"off"}, tell the Alchemist.`,extraContent:{reportKind:"outcome"}}))}catch{}return{content:[{type:"text",text:`Not switched ${n?"on":"off"}: that is the user's call. `+(c?"The words in userSaid are not in any message the user sent in this project; quote them exactly as they wrote them.":`Pass their words asking for it in userSaid, quoted exactly. If they have not asked, ask them, saying what ${n?"it will check again":"stops being checked"}.`)}],isError:!0}}return await r.proxyPatch(`/api/projects/${t.projectId}/test-processes/${a.id}`,{isActive:n,userSaid:c}),{content:[{type:"text",text:n?`"${a.name}" is on again: every change it covers must pass it before it merges.`:`"${a.name}" is off: changes now merge without it running. The user was told in Steer what no longer gets checked. Say it in your reply in one sentence too.`}]}}(e,o,c)
4915
+ return p({success:!t.isError}),t}if("list"===e.operation){const e=await c.proxyGet(`/api/projects/${o.projectId}/test-processes${await r(o,c,l)}`),t=e?.data||e||[],s=Array.isArray(t)?t:[]
4916
+ if(0===s.length)return p({success:!0}),{content:[{type:"text",text:'No test processes registered for this project. The user sees the Tests gate greyed as "nothing registered" and decides whether to set one up; it is not yours to raise. Verify your work with fast `run` checks in a box, and say in your completion summary if the absence mattered. `tests` operation "setup" opens only from a request that carries a gate criterion (kind "gate": a check the request itself asks to run on every merge).'}]}
4917
+ const i=n(s)
4918
+ return p({success:!0}),{content:[{type:"text",text:`## Test Processes (${s.length})\n\n${i}`}]}}if("run"===e.operation){let r,n=[]
4919
+ try{const e=await c.proxyGet(`/api/projects/${o.projectId}/test-processes`),t=e?.data||e||[]
4920
+ n=Array.isArray(t)?t:[]}catch(e){l.warn("tests run: existence check failed; proceeding optimistically",{error:(0,s.getErrorMessage)(e)})}if(0===n.length)return p({success:!1,error:"no_process"}),{content:[{type:"text",text:'No test process registered for this project. Run `tests` with `operation: "setup"` first to bootstrap one.'}],isError:!0}
4902
4921
  const d=e.process
4903
- if(d){const e=d.toLowerCase(),t=l.find(t=>t.id===d||"string"==typeof t.name&&t.name.toLowerCase()===e)
4904
- if(!t){const e=l.map(e=>`- ${e.name} (${e.id})`).join("\n")
4905
- return h({success:!1,error:"process_not_found"}),{content:[{type:"text",text:`No test process matched "${d}". Available:\n${e}`}],isError:!0}}r=[t.id]}const p=u?.sessionId,{resolveRequestIdOrError:f}=await Promise.resolve().then(()=>t(36651)),m=await f(a,c,p,"tests run requires an associated request: your session is not currently linked to one. Open or create a request, then try again.")
4906
- if(!m.ok)return h({success:!1,error:"no_request"}),m.errorResult
4922
+ if(d){const e=d.toLowerCase(),t=n.find(t=>t.id===d||"string"==typeof t.name&&t.name.toLowerCase()===e)
4923
+ if(!t){const e=n.map(e=>`- ${e.name} (${e.id})`).join("\n")
4924
+ return p({success:!1,error:"process_not_found"}),{content:[{type:"text",text:`No test process matched "${d}". Available:\n${e}`}],isError:!0}}r=[t.id]}const u=h?.sessionId,{resolveRequestIdOrError:f}=await Promise.resolve().then(()=>t(36651)),m=await f(c,l,u,"tests run requires an associated request: your session is not currently linked to one. Open or create a request, then try again.")
4925
+ if(!m.ok)return p({success:!1,error:"no_request"}),m.errorResult
4907
4926
  const g=m.requestId,y=e.name?[e.name]:void 0
4908
- try{const e=await fetch(`${(0,s.getActionServerBaseUrl)()}/api/run-tests-now`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:g,parentSessionId:p,testProcessIds:r,commandNames:y}),signal:AbortSignal.timeout(9e4)})
4927
+ try{const e=await fetch(`${(0,i.getActionServerBaseUrl)()}/api/run-tests-now`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:g,parentSessionId:u,testProcessIds:r,commandNames:y}),signal:AbortSignal.timeout(9e4)})
4909
4928
  if(!e.ok){const t=await e.text().catch(()=>"")
4910
- return h({success:!1,error:"run_failed"}),{content:[{type:"text",text:`tests run failed: action server returned ${e.status}: ${t.slice(0,300)}`}],isError:!0}}const t=await e.json()
4911
- if(!t.success||!t.data)return h({success:!1,error:"run_failed"}),{content:[{type:"text",text:`tests run failed: ${t.error||"unknown error"}`}],isError:!0}
4929
+ return p({success:!1,error:"run_failed"}),{content:[{type:"text",text:`tests run failed: action server returned ${e.status}: ${t.slice(0,300)}`}],isError:!0}}const t=await e.json()
4930
+ if(!t.success||!t.data)return p({success:!1,error:"run_failed"}),{content:[{type:"text",text:`tests run failed: ${t.error||"unknown error"}`}],isError:!0}
4912
4931
  const n=t.data.spawnedSessionIds?.[0],o=y?` (filtered to: ${y.join(", ")})`:""
4913
- return h({success:!0}),{content:[{type:"text",text:`Test run started${o}.\nSpawned ${t.data.agentsSpawned} runner(s) for ${t.data.processCount} process(es). Test runs: ${t.data.testRunIds.join(", ")}.\n\nThe auto-tester is executing the registered commands. You will receive a notification on your next turn when results are reported: continue with other work in the meantime. This voluntary run doesn't gate anything by itself; if you feel uncertain about the change, wait for the results before completing.`}],...(0,i.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:n,agentType:"auto-tester",requestId:g,description:y?`Test run: ${y.join(", ")}`:"Test run"})}}catch(e){const t=(0,o.getErrorMessage)(e)
4914
- return/abort|timeout/i.test(t)?(h({success:!0}),{content:[{type:"text",text:"The test run was handed to the action server, but its answer did not come back within 90 seconds. It may still be running: its result arrives as a report on a later turn. Do not treat this as a failed run."}]}):(h({success:!1,error:"fetch_error"}),{content:[{type:"text",text:`tests run failed: could not reach action server: ${t}`}],isError:!0})}}if("setup"===e.operation){let r
4915
- try{const e=await a.proxyGet(`/api/projects/${n.projectId}/test-processes`),t=e?.data||e||[],o=Array.isArray(t)?t:[]
4916
- if(o.length>0){const e=o.map(e=>`- ${e.name}${e.scope?` (scope: ${e.scope})`:""}: ${e.config?.commands?.length||0} command(s), ${e.isActive?"active":"inactive"}`).join("\n")
4917
- r=`${o.length} test process(es) already registered for this project:\n${e}\n\nPick a distinct name AND a non-overlapping scope for the new process. Confirm with the user what they want this new process to cover before registering.`}}catch(e){c.warn("tests setup: existing-processes lookup failed; proceeding without hint",{error:(0,o.getErrorMessage)(e)})}const{sessionIdentity:l}=await Promise.resolve().then(()=>t(6688)),d=u?.sessionId,p=d?await l.getSessionRequestId(a,c,d):void 0
4918
- if(p)try{const e=await a.proxyGet(`/api/projects/${n.projectId}/requests/${encodeURIComponent(p)}`),t=e?.data??e
4919
- if(!(Array.isArray(t?.acceptanceCriteria)?t.acceptanceCriteria:[]).some(e=>"gate"===e?.kind))return h({success:!1,error:"no_gate_criterion"}),{content:[{type:"text",text:`tests setup is not open from ${p}: none of its acceptance criteria is a gate criterion (kind "gate", a check the request asks to run on every merge). Whether the project gets a test process is asked of the user once per project, not by workers. If you believe this request does ask for a check, say so in a question to the user; if they agree they can edit the criterion's kind on the request card. Meanwhile verify your work with fast \`run\` checks in a box.`}],isError:!0}}catch(e){c.warn("tests setup: request lookup failed; proceeding",{requestId:p,error:(0,o.getErrorMessage)(e)})}if(!(p||"string"==typeof e.ask&&e.ask.trim()))return h({success:!1,error:"no_ask"}),{content:[{type:"text",text:"tests setup needs `ask`: what the user asked the check to run, in their words. The setup architect registers exactly that; without it, it can only guess from the repository."}],isError:!0}
4920
- try{const t=await fetch(`${(0,s.getActionServerBaseUrl)()}/api/setup-tests`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:p,parentSessionId:d,context:r,..."string"==typeof e.ask&&e.ask.trim()?{ask:e.ask.trim()}:{}}),signal:AbortSignal.timeout(15e3)})
4932
+ return p({success:!0}),{content:[{type:"text",text:`Test run started${o}.\nSpawned ${t.data.agentsSpawned} runner(s) for ${t.data.processCount} process(es). Test runs: ${t.data.testRunIds.join(", ")}.\n\nThe auto-tester is executing the registered commands. You will receive a notification on your next turn when results are reported: continue with other work in the meantime. This voluntary run doesn't gate anything by itself; if you feel uncertain about the change, wait for the results before completing.`}],...(0,a.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:n,agentType:"auto-tester",requestId:g,description:y?`Test run: ${y.join(", ")}`:"Test run"})}}catch(e){const t=(0,s.getErrorMessage)(e)
4933
+ return/abort|timeout/i.test(t)?(p({success:!0}),{content:[{type:"text",text:"The test run was handed to the action server, but its answer did not come back within 90 seconds. It may still be running: its result arrives as a report on a later turn. Do not treat this as a failed run."}]}):(p({success:!1,error:"fetch_error"}),{content:[{type:"text",text:`tests run failed: could not reach action server: ${t}`}],isError:!0})}}if("setup"===e.operation){let r
4934
+ try{const e=await c.proxyGet(`/api/projects/${o.projectId}/test-processes`),t=e?.data||e||[],n=Array.isArray(t)?t:[]
4935
+ if(n.length>0){const e=n.map(e=>`- ${e.name}${e.scope?` (scope: ${e.scope})`:""}: ${e.config?.commands?.length||0} command(s), ${e.isActive?"active":"inactive"}`).join("\n")
4936
+ r=`${n.length} test process(es) already registered for this project:\n${e}\n\nPick a distinct name AND a non-overlapping scope for the new process. Confirm with the user what they want this new process to cover before registering.`}}catch(e){l.warn("tests setup: existing-processes lookup failed; proceeding without hint",{error:(0,s.getErrorMessage)(e)})}const{sessionIdentity:n}=await Promise.resolve().then(()=>t(6688)),d=h?.sessionId,u=d?await n.getSessionRequestId(c,l,d):void 0
4937
+ if(u)try{const e=await c.proxyGet(`/api/projects/${o.projectId}/requests/${encodeURIComponent(u)}`),t=e?.data??e
4938
+ if(!(Array.isArray(t?.acceptanceCriteria)?t.acceptanceCriteria:[]).some(e=>"gate"===e?.kind))return p({success:!1,error:"no_gate_criterion"}),{content:[{type:"text",text:`tests setup is not open from ${u}: none of its acceptance criteria is a gate criterion (kind "gate", a check the request asks to run on every merge). Whether the project gets a test process is asked of the user once per project, not by workers. If you believe this request does ask for a check, say so in a question to the user; if they agree they can edit the criterion's kind on the request card. Meanwhile verify your work with fast \`run\` checks in a box.`}],isError:!0}}catch(e){l.warn("tests setup: request lookup failed; proceeding",{requestId:u,error:(0,s.getErrorMessage)(e)})}if(!(u||"string"==typeof e.ask&&e.ask.trim()))return p({success:!1,error:"no_ask"}),{content:[{type:"text",text:"tests setup needs `ask`: what the user asked the check to run, in their words. The setup architect registers exactly that; without it, it can only guess from the repository."}],isError:!0}
4939
+ try{const t=await fetch(`${(0,i.getActionServerBaseUrl)()}/api/setup-tests`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({requestId:u,parentSessionId:d,context:r,..."string"==typeof e.ask&&e.ask.trim()?{ask:e.ask.trim()}:{}}),signal:AbortSignal.timeout(15e3)})
4921
4940
  if(!t.ok){const e=await t.text().catch(()=>"")
4922
4941
  if(409===t.status){let t=e
4923
- try{t=JSON.parse(e)?.error??e}catch{}return h({success:!1,error:"setup_owned_elsewhere"}),{content:[{type:"text",text:`tests setup did not start: ${t}`}],isError:!0}}return h({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:`tests setup failed: action server returned ${t.status}: ${e.slice(0,300)}`}],isError:!0}}const n=await t.json()
4924
- return n.success&&n.data?(h({success:!0}),{content:[{type:"text",text:`Test setup session started.\nWorktree: ${n.data.worktreePath} (branch: ${n.data.branch})\n\nThe setup-architect agent is now interviewing the user. You will receive a notification on your next turn when configuration completes: continue with other work in the meantime.`}],...(0,i.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:n.data.sessionId,agentType:"setup-architect",requestId:p??void 0,description:"Test setup"})}):(h({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:`tests setup failed: ${n.error||"unknown error"}`}],isError:!0})}catch(e){return h({success:!1,error:"fetch_error"}),{content:[{type:"text",text:`tests setup failed: could not reach action server: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}}return h({success:!1,error:"unknown_operation"}),{content:[{type:"text",text:`Unknown operation: ${e.operation}`}],isError:!0}}catch(e){return h({success:!1,error:"api_error"}),{content:[{type:"text",text:`tests failed: ${(0,o.getErrorMessage)(e)}`}],isError:!0}}}
4925
- const o=t(58824),s=t(79414),i=t(82272),a=t(31375),c=t(56844),l=t(52232),d=t(29729),u=["package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml","bun.lock","bun.lockb","uv.lock","poetry.lock","Pipfile.lock","requirements.txt","Cargo.lock","go.sum","Gemfile.lock","composer.lock"],h={npm:["ci","install","i"],pnpm:["install","i"],bun:["install","i"],yarn:["","install"],uv:["sync"],pip:["install"],pip3:["install"],poetry:["install"],pipenv:["install","sync"],cargo:["fetch"],bundle:["install"],composer:["install"]}},56844(e,exports,t){function r(e,t){const r=String(t||"").trim().replace(/^v/i,"").match(/^(\d+)(?:\.(\d+))?/)
4942
+ try{t=JSON.parse(e)?.error??e}catch{}return p({success:!1,error:"setup_owned_elsewhere"}),{content:[{type:"text",text:`tests setup did not start: ${t}`}],isError:!0}}return p({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:`tests setup failed: action server returned ${t.status}: ${e.slice(0,300)}`}],isError:!0}}const n=await t.json()
4943
+ return n.success&&n.data?(p({success:!0}),{content:[{type:"text",text:`Test setup session started.\nWorktree: ${n.data.worktreePath} (branch: ${n.data.branch})\n\nThe setup-architect agent is now interviewing the user. You will receive a notification on your next turn when configuration completes: continue with other work in the meantime.`}],...(0,a.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:n.data.sessionId,agentType:"setup-architect",requestId:u??void 0,description:"Test setup"})}):(p({success:!1,error:"spawn_failed"}),{content:[{type:"text",text:`tests setup failed: ${n.error||"unknown error"}`}],isError:!0})}catch(e){return p({success:!1,error:"fetch_error"}),{content:[{type:"text",text:`tests setup failed: could not reach action server: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}}return p({success:!1,error:"unknown_operation"}),{content:[{type:"text",text:`Unknown operation: ${e.operation}`}],isError:!0}}catch(e){return p({success:!1,error:"api_error"}),{content:[{type:"text",text:`tests failed: ${(0,s.getErrorMessage)(e)}`}],isError:!0}}}
4944
+ const s=t(58824),i=t(79414),a=t(82272),c=t(31375),l=t(56844),d=t(52232),u=t(29729),h=t(36651),p=["package-lock.json","npm-shrinkwrap.json","yarn.lock","pnpm-lock.yaml","bun.lock","bun.lockb","uv.lock","poetry.lock","Pipfile.lock","requirements.txt","Cargo.lock","go.sum","Gemfile.lock","composer.lock"],f={npm:["ci","install","i"],pnpm:["install","i"],bun:["install","i"],yarn:["","install"],uv:["sync"],pip:["install"],pip3:["install"],poetry:["install"],pipenv:["install","sync"],cargo:["fetch"],bundle:["install"],composer:["install"]}},56844(e,exports,t){function r(e,t){const r=String(t||"").trim().replace(/^v/i,"").match(/^(\d+)(?:\.(\d+))?/)
4926
4945
  return r?"node"===e?r[1]:void 0!==r[2]?`${r[1]}.${r[2]}`:null:null}function n(e,t){const n=String(t||"").split("||"),s=[]
4927
4946
  for(const t of n){const n=t.split(/[,\s]+/).map(e=>e.trim()).filter(Boolean)
4928
4947
  let i=null
@@ -5136,26 +5155,28 @@ exports.GQ=["coder","writer","work"],exports.DY=Array.from(new Set([...Object.va
5136
5155
  var o=Object.getOwnPropertyDescriptor(t,r)
5137
5156
  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)}
5138
5157
  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
5139
- 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
5140
- 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)
5141
- 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" }'})})
5142
- const u=r.z.string().min(1).describe("OpenCode session ID")
5143
- 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
5158
+ 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.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.BaselineSchema=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.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=exports.DocumentOperationsSchema=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=exports.EnvironmentSetupSchema=void 0
5159
+ const r=t(11569),n=t(8955),o=t(94466),s=e=>"string"!=typeof e||e.trim()?e:void 0,i=(e,t,n={})=>r.z.string().refine(t=>!(!n.allowEmpty||""!==t)||!!t&&e.some(e=>e.test(t)),{message:t}),a=i([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)."),c=i([/^req\d{6}$/],"Request ID must match format: req###### (e.g. req000001)"),l=i([/^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}),d=e=>e.path&&e.documentId?{...e,documentId:void 0}:e,u=e=>Boolean(e.path)||Boolean(e.documentId)
5160
+ 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.BaselineSchema=r.z.object({metric:r.z.string().regex(/^[A-Za-z_][A-Za-z0-9_.-]{0,59}$/).describe('"time" for the command\'s own running time in seconds, or the name of a number the command prints ("items_per_s: 812", "passed=41", JSON {"size_kb": 88}).'),better:r.z.enum(["lower","higher"]).describe("Which way is better: lower for a time or a size, higher for a throughput or a test count."),tolerancePct:r.z.number().min(0).max(1e3).optional().describe("How much worse, in percent of the value before the change, still counts as no worse. Default 10; the user's number when they gave one."),atLeast:r.z.number().min(0).optional().describe("And worse by at least this much in the metric's unit before it counts (default 0.1 for time, else 0)."),repeats:r.z.number().int().min(1).max(9).optional().describe("Runs on each side, default 3; 1 for a number that never varies (a count, a size).")}).describe("Only when the user asks that something measured must not get worse (a benchmark time, a test count, a size). Needs `command`, kind objective. The criterion then holds while the command's number on this work is no worse than before it, measured side by side in the project's box; a regression waits for the user's sign-off."),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 assumptions and the evidence for each number; 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."),baseline:exports.BaselineSchema.optional()})])).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."),waitReason:r.z.string().optional().describe('With waitsOn (required there): what this request needs from the one it waits for, as the user reads it ("builds on the settings page req000012 adds"). When it needs nothing from it, it does not wait.'),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:c,title:r.z.string(),description:r.z.string()}),exports.UpdateTaskStatusSchema=r.z.object({taskId:a,status:exports.TaskStatusEnum,note:r.z.string()}),exports.WriteFileSchema=r.z.object({path:r.z.string().optional(),documentId:l.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(d).refine(u,{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:l.optional(),lines:r.z.string().optional(),symbol:r.z.string().max(200).optional()}).transform(d).refine(u,{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:l.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(d).refine(u,{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:l.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(d).refine(u,{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:l.optional()}).transform(d).refine(u,{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:c,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:c,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:a,showDiff:r.z.boolean().optional()}).optional(),requestHistory:r.z.object({requestId:c,detailLevel:r.z.enum(["summary","detailed"]).default("summary"),taskId:a.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:c,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:c}),exports.GetTaskInformationSchema=r.z.object({taskId:a}),exports.CompleteTaskSchema=r.z.object({taskId:a,summary:r.z.string()}),exports.TaskInspectSchema=r.z.object({operation:r.z.enum(["get-info","list"]),taskId:a.optional(),requestId:c.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" }'})})
5161
+ const h=r.z.string().min(1).describe("OpenCode session ID")
5162
+ exports.SubagentOperationsSchema=r.z.object({sessionId:h.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:h.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:l.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(d).refine(u,{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:l.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(d).refine(u,{message:"path or documentId required"})),comment:r.z.string()}),exports.CreateDocumentSchema=r.z.object({requestId:c.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:c.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:l.optional(),requestId:c.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
5144
5163
  "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"}))})
5145
- const h=c
5146
- 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
5147
- "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
5164
+ const p=l
5165
+ exports.PlanReadSchema=r.z.object({operation:r.z.enum(["get","get-active","list","get-history","get-requests"]),planId:p.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:p.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
5166
+ "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:c,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:l.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(d).refine(u,{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:l.optional()}).transform(d).refine(u,{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:c.optional(),operation:r.z.enum(["update","activate","complete","merge","revert","reopen","get-info","list","search","status","cancel","delete-empty","list-areas","update-areas","wait-on","clear-wait","premise-block","dispute-criterion","asked-no-change","hand-over"]),planId:r.z.string().max(64).optional().describe("status: the plan to report on (plan000005). Omit it and requestId for all live work."),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"]),baseline:exports.BaselineSchema.optional(),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."),notChecked:r.z.preprocess(s,r.z.string().min(3).max(160).optional()).describe('What you did not check that the user could assume you did, in a few plain words ("the page on a phone", "a real email arriving"). The card leads with it, beside what the record shows nothing checked. Omit when there is nothing.'),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).optional().describe("Repo-relative files or folders (globs work) it is pinned to. Without them (only with userSaid) it is for the whole project."),evidence:r.z.string().min(1).max(1500).optional().describe("What shows it: the command, what it printed, the file. Not needed with userSaid."),userSaid:r.z.string().min(8).max(600).optional().describe("A correction the user gave you on this request that should hold for later work: their words, copied exactly; the card quotes them.")}).refine(e=>!!e.userSaid||!!e.files?.length&&!!e.evidence,{message:"a trap needs files and evidence; a correction of the user's needs userSaid"})).max(2).optional().describe("A trap the next agent touching these files would hit too, or a correction the user gave you, proposed as guidance 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
5148
5167
  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]
5149
5168
  n&&!e[n.field]&&t.addIssue({code:"custom",message:`'${r}' operation requires '${n.field}' data. ${n.hint}`})})
5150
- 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.")
5151
- 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")})
5152
- 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)")})
5153
- 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()})
5154
- 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}
5169
+ const f=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.")
5170
+ exports.QuickWriteSchema=r.z.object({title:r.z.string().min(1,"Title is required"),description:r.z.string().min(1,"Description is required"),follows:f,path:r.z.string().optional(),documentId:l.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(d).refine(u,{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:f,operations:r.z.array(r.z.object({path:r.z.string().optional(),documentId:l.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(d).refine(u,{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")})
5171
+ const m=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)")})
5172
+ 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(m).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.'),leavesOut:r.z.array(r.z.object({what:r.z.string().describe('The left-out part in plain words, e.g. "the Valkey backend tests"'),paths:r.z.array(r.z.string()).min(1).describe('Globs for the files only that part covers: its test files and the code only they exercise, e.g. ["tests/ut/backends/test_valkey.py", "aiocache/backends/valkey.py"]')})).optional().describe('What this check deliberately does not run and no other check runs (the suites the card lists as "not run on every change"), each with the files only that part covers. A change to those files is reported to the user as not checked by this check.')}),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()})
5173
+ const g=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.")}),y=function e(t){const n={...t._def,description:void 0}
5155
5174
  if(t instanceof r.z.ZodObject){const r=Object.fromEntries(Object.entries(t.shape).map(([t,r])=>[t,e(r)]))
5156
5175
  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))
5157
- 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."
5158
- 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
5176
+ return new t.constructor(n)}(g),w='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.',b="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."
5177
+ 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(w),config:y.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(b),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:y.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(b),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(w),config:g.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(m).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."),leavesOut:r.z.array(r.z.object({what:r.z.string().describe('The left-out part in plain words, e.g. "the Valkey backend tests"'),paths:r.z.array(r.z.string()).min(1).describe('Globs for the files only that part covers: its test files and the code only they exercise, e.g. ["tests/ut/backends/test_valkey.py", "aiocache/backends/valkey.py"]')})).optional().describe("Replaces what this check deliberately does not run, with the files only that part covers; [] clears it."),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."})})
5178
+ const v=r.z.string().min(1,"every option needs its consequence (description)").max(200,"an option's description is its consequence in one line: 200 characters at most").describe("Its consequence in one line: what picking it gets and what it costs."),k=r.z.string().min(1).max(120,"because is one clause: 120 characters at most")
5179
+ 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, 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:v})).min(2).describe("At least two genuine alternatives."),default:r.z.string().min(1).describe("The option id you are proceeding on now: the card marks it recommended."),because:k.describe('Why your default, in one clause ("it matches the example in your notes").'),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 and why it matters. 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:v,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."),because:r.z.preprocess(s,k.optional()).describe("With recommended (required there): why, in one clause."),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.'),changeableLater:r.z.boolean().optional().describe("true when the answer can be changed later without losing anything (nothing sent, deleted or published on it); the card then says so.")}).superRefine((e,t)=>{(e.options??[]).forEach((e,n)=>{!0!==e.recommended||e.because||t.addIssue({code:r.z.ZodIssueCode.custom,path:["options",n,"because"],message:`option '${e.id}' is recommended: say why in one clause (because)`})})}).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."),spendCeiling:r.z.number().positive().max(1e5).optional().describe("reply 'build' on a Plan ready card: the spend limit the user named, in dollars at list price, with their words in quote. The plan pauses and asks when its spend passes it."),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. A question with no options needs them: give the ways forward in questionContent.options. 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","you"]).optional().describe('set: what the guidance is addressed to. "you" = how the user works, in every project they have; "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
5159
5180
  const r=t(66865)
5160
5181
  Object.defineProperty(exports,"Server",{enumerable:!0,get:function(){return r.Server}})
5161
5182
  const n=t(25802)
@@ -5596,7 +5617,7 @@ let a=n
5596
5617
  if(!this.isOffline())try{a=await this.projectClient.ensureProjectExists(n),a!==n&&this.logger.info("Project ID changed during verification",{requestedId:n,actualId:a}),this.logger.debug("Project verified on remote server",{projectId:a})}catch(e){this.logger.warn(`Failed to verify project ${n} on remote server`,{error:(0,i.getErrorMessage)(e)})}let c=[]
5597
5618
  Array.isArray(e.acceptanceCriteria)&&(c=r(e.acceptanceCriteria))
5598
5619
  const l={title:e.title,description:e.description,type:e.type,acceptanceCriteria:c,projectId:a}
5599
- if("status"in e&&e.status?l.status=e.status:l.status="open",Array.isArray(e.areas)&&e.areas.length>0&&(l.areas=e.areas),e.roundOf&&(l.roundOf=e.roundOf),e.originRequestId&&(l.originRequestId=e.originRequestId),Array.isArray(e.waitsOn)&&e.waitsOn.length>0&&(l.waitsOn=e.waitsOn),"string"==typeof e.process&&e.process.trim()&&(l.process=e.process.trim()),!0===e.startsNow&&(l.startsNow=!0),"string"==typeof e.replaces&&e.replaces.trim()&&(l.replaces=e.replaces.trim()),Array.isArray(e.asked)&&e.asked.length>0&&(l.asked=e.asked),Array.isArray(e.userChoices)&&e.userChoices.length>0&&(l.userChoices=e.userChoices),void 0!==e.exchangeKey&&(l.exchangeKey=e.exchangeKey),e.planDocumentId&&(l.planDocumentId=e.planDocumentId),e.scheduleDocumentId&&(l.scheduleDocumentId=e.scheduleDocumentId),"metadata"in e&&e.metadata?l.metadata=e.metadata:l.metadata={},e.cliOrigin&&(l.cliOrigin=e.cliOrigin),e.creatorSessionId&&(l.creatorSessionId=e.creatorSessionId),this.logger.info("Creating request",{title:e.title,type:e.type,projectId:a,criteriaCount:c.length}),this.authToken){const e={Authorization:`Bearer ${this.authToken}`}
5620
+ if("status"in e&&e.status?l.status=e.status:l.status="open",Array.isArray(e.areas)&&e.areas.length>0&&(l.areas=e.areas),e.roundOf&&(l.roundOf=e.roundOf),e.originRequestId&&(l.originRequestId=e.originRequestId),Array.isArray(e.waitsOn)&&e.waitsOn.length>0&&(l.waitsOn=e.waitsOn,"string"==typeof e.waitReason&&(l.waitReason=e.waitReason)),"string"==typeof e.process&&e.process.trim()&&(l.process=e.process.trim()),!0===e.startsNow&&(l.startsNow=!0),"string"==typeof e.replaces&&e.replaces.trim()&&(l.replaces=e.replaces.trim()),Array.isArray(e.asked)&&e.asked.length>0&&(l.asked=e.asked),Array.isArray(e.userChoices)&&e.userChoices.length>0&&(l.userChoices=e.userChoices),void 0!==e.exchangeKey&&(l.exchangeKey=e.exchangeKey),e.planDocumentId&&(l.planDocumentId=e.planDocumentId),e.scheduleDocumentId&&(l.scheduleDocumentId=e.scheduleDocumentId),"metadata"in e&&e.metadata?l.metadata=e.metadata:l.metadata={},e.cliOrigin&&(l.cliOrigin=e.cliOrigin),e.creatorSessionId&&(l.creatorSessionId=e.creatorSessionId),this.logger.info("Creating request",{title:e.title,type:e.type,projectId:a,criteriaCount:c.length}),this.authToken){const e={Authorization:`Bearer ${this.authToken}`}
5600
5621
  this.logger.debug("Auth header prepared for request creation",{tokenLength:this.authToken.length,tokenPrefix:this.authToken.substring(0,10)+"...",tokenSuffix:"..."+this.authToken.substring(this.authToken.length-10)})
5601
5622
  const r=await this.requestWithRetry("POST",`/api/projects/${a}/requests`,l,e),n=(0,s.safeGetResponseData)(r,{})
5602
5623
  if(!n||!n.id)throw this.logger.error("Received empty response data when creating request"),new Error("Failed to create request: Empty response data")
@@ -6053,7 +6074,10 @@ return`Not ${t}: ${1===e.length?"this criterion is":"these criteria are"} kind "
6053
6074
  for(let t=0;t<e.length;t++)r=(r<<5)-r+e.charCodeAt(t)|0
6054
6075
  return`${t+1}${String.fromCharCode(97+Math.abs(r)%26)}`}function r(e){return!(!e||e.standing&&e.waived)&&/\[(human|user)-input\]/i.test(String(e.description??""))}function n(e){const t=e?.proof??{},n=t.latest??{}
6055
6076
  if(r(e)){const t=n.you?.verdict
6056
- return"met"===t?"PROVEN:the user checked it":"unmet"===t?"FAILED:the user checked it and it does not work":"not-checked"===t?"NOT-CHECKED:accepted without the user under Self-driving":"dropped"===t?"DROPPED:the user dropped this check; it is not required":"withdrawn"===t?"THE-USER'S:they said it works on an earlier version and the work changed since, so the card at the end asks them again; do not judge it":"settles-unchecked"===e?.yourCheck?.atTheEnd?"THE-USER'S:their own check; if they do not answer, it settles at the end as not checked by them (Self-driving), never proof; do not judge it":"THE-USER'S:their own check, asked on the card at the end, and nothing merges past it; do not judge it"}return!0===e?.proven?"reviewer"===t.by?"PROVEN:review":"you"===t.by?"PROVEN:user-hand-check":"driver"===t.by?"PROVEN:drive":"PROVEN:test":"not-checked"===n.driver?.verdict?"NOT-CHECKED:drive":"answer"===t.reword?.from?`REWORD:${String(t.reword.note??"the answer it was written for changed").slice(0,300)}; reword it with request_operations update, its key and userSaid "${t.reword.ref}"`:t.notARealTest?"NOT-A-REAL-TEST":t.checkFails?"met"===n.reviewer?.verdict?"FAILED:test (review met, check fails)":"FAILED:test":t.disputed?`DISPUTED:its check passes, but the reviewer says it does not hold${t.disputed.note?` ("${String(t.disputed.note).slice(0,200)}")`:""}; strengthen the check so it tests what the criterion says, or fix the code`:"could-not-run"===n.instrument?.verdict?"COULD-NOT-RUN:test (its check could not run here; it says nothing either way)":"unmet"===n.reviewer?.verdict?"UNMET:review":!0===n.reviewer?.reword?"REWORD:a reviewer found its words no longer fit what was asked; reword it with the user's words, or ask them":"withdrawn"===n.reviewer?.verdict?"RE-REVIEW:its review was withdrawn (the work changed after it, or a new pass is due)":e?.coder?"ATTESTED":"UNPROVEN"}function o(e){const r=new Map
6077
+ return"met"===t?"PROVEN:the user checked it":"unmet"===t?"FAILED:the user checked it and it does not work":"not-checked"===t?"NOT-CHECKED:accepted without the user under Self-driving":"dropped"===t?"DROPPED:the user dropped this check; it is not required":"withdrawn"===t?"THE-USER'S:they said it works on an earlier version and the work changed since, so the card at the end asks them again; do not judge it":"settles-unchecked"===e?.yourCheck?.atTheEnd?"THE-USER'S:their own check; if they do not answer, it settles at the end as not checked by them (Self-driving), never proof; do not judge it":"THE-USER'S:their own check, asked on the card at the end, and nothing merges past it; do not judge it"}if(e?.baseline&&"object"==typeof e.baseline){const t=[...Array.isArray(e.ledger)?e.ledger:[]].reverse().find(e=>"verify"===e?.source&&"string"==typeof e?.instrument&&e.instrument.startsWith("baseline")),r="decided by its measurement only, never by a review or anyone's own timing"
6078
+ if(!t)return`NOT-MEASURED-YET:it is measured after the work is completed, on the work and on the version it started from; ${r}`
6079
+ const n=String(t.note??"").replace(/\s+/g," ").trim().slice(0,300)
6080
+ return`${"met"===t.verdict?"MEASURED-MET":"unmet"===t.verdict?"MEASURED-WORSE":"MEASURED"}:${n||"measured"}; ${r}`}return!0===e?.proven?"reviewer"===t.by?"PROVEN:review":"you"===t.by?"PROVEN:user-hand-check":"driver"===t.by?"PROVEN:drive":"PROVEN:test":"not-checked"===n.driver?.verdict?"NOT-CHECKED:drive":"answer"===t.reword?.from?`REWORD:${String(t.reword.note??"the answer it was written for changed").slice(0,300)}; reword it with request_operations update, its key and userSaid "${t.reword.ref}"`:t.notARealTest?"NOT-A-REAL-TEST":t.checkFails?"met"===n.reviewer?.verdict?"FAILED:test (review met, check fails)":"FAILED:test":t.disputed?`DISPUTED:its check passes, but the reviewer says it does not hold${t.disputed.note?` ("${String(t.disputed.note).slice(0,200)}")`:""}; strengthen the check so it tests what the criterion says, or fix the code`:"could-not-run"===n.instrument?.verdict?"COULD-NOT-RUN:test (its check could not run here; it says nothing either way)":"unmet"===n.reviewer?.verdict?"UNMET:review":!0===n.reviewer?.reword?"REWORD:a reviewer found its words no longer fit what was asked; reword it with the user's words, or ask them":"withdrawn"===n.reviewer?.verdict?"RE-REVIEW:its review was withdrawn (the work changed after it, or a new pass is due)":e?.coder?"ATTESTED":"UNPROVEN"}function o(e){const r=new Map
6057
6081
  for(let n=0;n<e.length;n++){const o=t(e[n].id,n)
6058
6082
  r.set(o,e[n])}return r}function s(e){if(0===e.length)return"No acceptance criteria defined."
6059
6083
  const t=o(e),r=[]
@@ -6980,7 +7004,19 @@ continue}if(t.startsWith("+++ ")){o=t.slice(4).replace(/^b\//,"")
6980
7004
  continue}if(t.startsWith("--- "))continue
6981
7005
  const e=/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(t)
6982
7006
  e?(c(),s=Number(e[1])):t.startsWith("-")?(a.length&&c(),i.push(t.slice(1))):t.startsWith("+")?(a.push({text:t.slice(1),line:s}),s++):(c(),t.startsWith(" ")&&s++)}return c(),n},exports.q=150
6983
- const t=e=>e.length>160?`${e.slice(0,160)}...`:e},13922(e,exports,t){async function r(e){const t=`requests/${e.requestId}`
7007
+ const t=e=>e.length>160?`${e.slice(0,160)}...`:e},82865(e,exports,t){function r(e,t){return new Promise(r=>{(0,o.execFile)("git",t,{cwd:e,maxBuffer:8388608},(e,t)=>{const n=e&&"number"==typeof e.code?e.code:e?1:0
7008
+ r({code:n,stdout:String(t??"")})})})}function n(e){const t=e.trim(),r=/^Merge (?:branch '|)requests\/(req\d{6})(?:'|)(?:-(.*))?$/.exec(t)
7009
+ return r?{requestId:r[1],title:(r[2]??"").trim()}:{requestId:null,title:t}}exports.landedOnTopOf=async function(e,t,o){if(!s.test(t))return[]
7010
+ const[,i,a]=(await r(e,["rev-list","--parents","-n","1",t])).stdout.trim().split(/\s+/)
7011
+ if(!(i&&a&&s.test(i)&&s.test(a)))return[]
7012
+ const c=(await r(e,["rev-list","--first-parent",a,`^${i}`])).stdout.trim().split("\n").filter(Boolean)
7013
+ if(0===c.length)return[]
7014
+ const l=(await r(e,["rev-parse","--verify","--quiet",`${c[c.length-1]}^1`])).stdout.trim()
7015
+ if(!s.test(l))return[]
7016
+ const d=await r(e,["log","--first-parent","--format=%s","-n","50",i,`^${l}`]),u=[]
7017
+ for(const e of d.stdout.split("\n").map(e=>e.trim()).filter(Boolean)){const t=n(e).requestId
7018
+ t&&t!==o&&!u.includes(t)&&u.push(t)}return u}
7019
+ const o=t(35317),s=(t(29418),t(82860),/^[0-9a-f]{40}$/)},13922(e,exports,t){async function r(e){const t=`requests/${e.requestId}`
6984
7020
  try{if(0===await function(e,t,r){return new Promise(o=>{(0,n.execFile)("git",["rev-list","--count",`${t}..${r}`],{cwd:e},(e,t)=>{o(e?0:Number(String(t).trim())||0)})})}(e.projectPath,t,e.defaultBranch))return null
6985
7021
  const r=await(0,o.reconcileBranchWithMain)({projectPath:e.projectPath,requestId:e.requestId,branchName:t,defaultBranch:e.defaultBranch})
6986
7022
  return e.logger.info("held landing: main brought into the branch",{requestId:e.requestId,outcome:r.outcome}),"clean"===r.outcome?await function(e,t){return new Promise(r=>{(0,n.execFile)("git",["rev-parse","--verify","--quiet",t],{cwd:e},(e,t)=>{const n=String(t??"").trim()
@@ -7060,15 +7096,15 @@ return await c.raw(["commit","--no-edit","-m",`Merge ${s} into ${o} (additive co
7060
7096
  const{note:l}=await(0,m.holdBackStagedCredentials)(a)
7061
7097
  return await c.raw(["commit","--no-edit","-m",`Merge ${s} into ${o} (${g.MARKER_MERGE_SUBJECT})${l?`\n\n${l}`:""}`]),{outcome:"conflicts-committed",conflictedFiles:t,landed:d}}catch(e){try{await c.merge(["--abort"])}catch{}return{outcome:"error",conflictedFiles:[],landed:d,error:String(e)}}}},exports.buildResolveInstruction=function(e){const{requestId:t,branchName:r,defaultBranch:n,landed:o,conflictedFiles:s,retryVia:i}=e,a=e.cause??{kind:"signoff"},c="sibling-merged"===a.kind?`${a.mergedRequestId} just merged into ${n}, and ${t} conflicts with it.`:"reached-signoff"===a.kind?`${t} reached sign-off, but ${n} moved since you branched.`:`The user signed off ${t}, but ${n} moved since you branched.`,l="complete"===i?"Resolve every marker block with your file tools so BOTH sides' intent holds (your change AND what landed above; intent, not text: when both sides carry the same function or declaration, keep ONE merged copy, never both), remove all markers, verify with the project's build/tests, then run request_operations complete again so the user can sign off a clean merge. That summary replaces your earlier one: describe everything the request delivers, not the conflict resolution, and pass replacesSummary: true.":`Resolve every marker block with your file tools so BOTH sides' intent holds (your change AND what landed above; intent, not text: when both sides carry the same function or declaration, keep ONE merged copy, never both), remove all markers, verify with the project's build/tests, then run the merge operation again \u2014 with ${n} already merged into your branch it will be conflict-free.`
7062
7098
  return["complete"===i?`[merge-conflict] ${c} The system merged ${n} into ${r} and COMMITTED the conflicted state: the files below contain <<<<<<< / ======= / >>>>>>> markers as plain content.`:`Merging ${r} into ${n} hit conflicts \u2014 ${n} moved since you branched. The system merged ${n} into your branch and COMMITTED the conflicted state: the files below contain <<<<<<< / ======= / >>>>>>> markers as plain content.`,o.length>0?`\nWhat landed on ${n} since you branched (honor THEIR intent, not just their text):\n${o.map(e=>`- ${e}`).join("\n")}\n`:"",`Conflicted files: ${s.join(", ")||"(see markers)"}`,l].filter(Boolean).join("\n")},exports.nearDuplicateInsertion=s,exports.resolveAdditiveConflicts=i
7063
- const u=t(75453),h=t(83807),p=d(t(16928)),f=d(t(79896)),m=t(96511),g=t(82860),y=t(66485)},87192(e,exports,t){function r(e,t,r={}){return new Promise(n=>{(0,h.execFile)(r.gitBinary||"git",t,{cwd:e,env:{...process.env,...r.env||{}},maxBuffer:16777216},(e,t,r)=>{n({code:e?"number"==typeof e.code?e.code:1:0,stdout:String(t??""),stderr:String(r??"")})})})}async function n(e,t,n){const o=await r(e,["rev-parse","--verify","--quiet",t],{gitBinary:n})
7064
- return 0===o.code?o.stdout.trim():null}function o(e){return`refs/alchemy/merge-candidates/${e.replace(/[^a-zA-Z0-9_-]/g,"_")}`}async function s(e,t,r){return n(e,o(t),r)}async function i(e,t,r){return n(e,`${t}^{tree}`,r)}var a,c=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
7099
+ const u=t(75453),h=t(83807),p=d(t(16928)),f=d(t(79896)),m=t(96511),g=t(82860),y=t(66485)},87192(e,exports,t){function r(e,t,r={}){return new Promise(n=>{(0,p.execFile)(r.gitBinary||"git",t,{cwd:e,env:{...process.env,...r.env||{}},maxBuffer:16777216},(e,t,r)=>{n({code:e?"number"==typeof e.code?e.code:1:0,stdout:String(t??""),stderr:String(r??"")})})})}async function n(e,t,n){const o=await r(e,["rev-parse","--verify","--quiet",t],{gitBinary:n})
7100
+ return 0===o.code?o.stdout.trim():null}function o(e){return`refs/alchemy/merge-candidates/${e.replace(/[^a-zA-Z0-9_-]/g,"_")}`}async function s(e,t,r){return n(e,o(t),r)}async function i(e,t,r){return n(e,`${t}^{tree}`,r)}function a(e){return!(!e||"failed"!==e.status&&"error"!==e.status)&&"fail_main"===(e.faultClass??e.results?.faultClass)}var c,l=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r)
7065
7101
  var o=Object.getOwnPropertyDescriptor(t,r)
7066
- 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]}),l=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),d=this&&this.__importStar||(a=function(e){return a=Object.getOwnPropertyNames||function(e){var t=[]
7102
+ 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]}),d=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),u=this&&this.__importStar||(c=function(e){return c=Object.getOwnPropertyNames||function(e){var t=[]
7067
7103
  for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&(t[t.length]=r)
7068
- return t},a(e)},function(e){if(e&&e.__esModule)return e
7104
+ return t},c(e)},function(e){if(e&&e.__esModule)return e
7069
7105
  var t={}
7070
- if(null!=e)for(var r=a(e),n=0;n<r.length;n++)"default"!==r[n]&&c(t,e,r[n])
7071
- return l(t,e),t})
7106
+ if(null!=e)for(var r=c(e),n=0;n<r.length;n++)"default"!==r[n]&&l(t,e,r[n])
7107
+ return d(t,e),t})
7072
7108
  Object.defineProperty(exports,"__esModule",{value:!0}),exports.dirtyTrackedFiles=async function(e,t){const n=await r(e,["status","--porcelain","--untracked-files=no"],{gitBinary:t})
7073
7109
  return 0!==n.code?[]:n.stdout.split("\n").map(e=>e.trimEnd()).filter(Boolean).map(e=>e.slice(3).trim()).filter(Boolean)},exports.assessMergeFreshness=async function(e,t,o,s){const i=await n(e,t,s),a=await n(e,o,s)
7074
7110
  if(!i)throw new Error(`assessMergeFreshness: cannot resolve ${t}`)
@@ -7079,30 +7115,30 @@ const l=c.stdout.trim()
7079
7115
  return{fresh:l===i,mainHead:i,branchTip:a,mergeBase:l}},exports.mergeCandidateRef=o,exports.readMergeCandidateRef=s,exports.clearMergeCandidate=async function(e,t,n){await r(e,["update-ref","-d",o(t)],{gitBinary:n})},exports.buildProspectiveMergeCommit=async function(e,t){const{defaultBranch:i,branchName:a,requestId:c,requestTitle:l,gitBinary:d}=t
7080
7116
  try{const t=await n(e,i,d),u=await n(e,a,d)
7081
7117
  if(!t||!u)return{ok:!1,reason:"error",error:`cannot resolve ${t?a:i}`}
7082
- const h=l?`Merge ${a}-${l}`:`Merge ${a}`,m=await async function(e,t,n){const o=await r(e,["log","-1","--format=%cI",t],{gitBinary:n}),s=0===o.code&&o.stdout.trim()?o.stdout.trim():"2000-01-01T00:00:00+00:00"
7083
- return{GIT_AUTHOR_NAME:g,GIT_AUTHOR_EMAIL:y,GIT_COMMITTER_NAME:g,GIT_COMMITTER_EMAIL:y,GIT_AUTHOR_DATE:s,GIT_COMMITTER_DATE:s}}(e,u,d)
7084
- let w=null
7118
+ const h=l?`Merge ${a}-${l}`:`Merge ${a}`,p=await async function(e,t,n){const o=await r(e,["log","-1","--format=%cI",t],{gitBinary:n}),s=0===o.code&&o.stdout.trim()?o.stdout.trim():"2000-01-01T00:00:00+00:00"
7119
+ return{GIT_AUTHOR_NAME:y,GIT_AUTHOR_EMAIL:w,GIT_COMMITTER_NAME:y,GIT_COMMITTER_EMAIL:w,GIT_AUTHOR_DATE:s,GIT_COMMITTER_DATE:s}}(e,u,d)
7120
+ let g=null
7085
7121
  const b=await r(e,["merge-tree","--write-tree",t,u],{gitBinary:d})
7086
- if(0!==b.code)return 1===b.code&&b.stdout.trim()?{ok:!1,reason:"conflicts",conflictSummary:b.stdout.split("\n").filter(e=>/CONFLICT|conflict/.test(e)).slice(0,10).join("\n")||"content conflicts detected by merge-tree"}:/unknown option|usage: git merge-tree/i.test(b.stderr)?await async function(e,t){const{mainHead:i,branchTip:a,message:c,env:l,requestId:d,gitBinary:u}=t,h=p.join(e,f.ALCHEMY_DIR_NAME,"merge-candidates",`wt-${process.pid}-${Date.now()}`),m=await r(e,["worktree","add","--detach",h,i],{gitBinary:u})
7087
- if(0!==m.code)return{ok:!1,reason:"error",error:`temp worktree add failed: ${m.stderr.trim().slice(0,400)}`}
7122
+ if(0!==b.code)return 1===b.code&&b.stdout.trim()?{ok:!1,reason:"conflicts",conflictSummary:b.stdout.split("\n").filter(e=>/CONFLICT|conflict/.test(e)).slice(0,10).join("\n")||"content conflicts detected by merge-tree"}:/unknown option|usage: git merge-tree/i.test(b.stderr)?await async function(e,t){const{mainHead:i,branchTip:a,message:c,env:l,requestId:d,gitBinary:u}=t,h=f.join(e,m.ALCHEMY_DIR_NAME,"merge-candidates",`wt-${process.pid}-${Date.now()}`),p=await r(e,["worktree","add","--detach",h,i],{gitBinary:u})
7123
+ if(0!==p.code)return{ok:!1,reason:"error",error:`temp worktree add failed: ${p.stderr.trim().slice(0,400)}`}
7088
7124
  try{const t=await r(h,["merge","--no-ff","-m",c,a],{env:l,gitBinary:u})
7089
7125
  if(0!==t.code){const e=await r(h,["diff","--name-only","--diff-filter=U"],{gitBinary:u})
7090
7126
  return await r(h,["merge","--abort"],{gitBinary:u}),{ok:!1,reason:"conflicts",conflictSummary:e.stdout.trim()||t.stdout.trim().slice(0,400)}}const p=await n(h,"HEAD",u)
7091
7127
  if(!p)return{ok:!1,reason:"error",error:"temp worktree merge produced no HEAD"}
7092
7128
  const f=await n(h,"HEAD^{tree}",u),m=await s(e,d,u)
7093
- return await r(e,["update-ref",o(d),p],{gitBinary:u}),{ok:!0,mergeCommitSha:p,treeSha:f||"",mainHead:i,branchTip:a,adopted:m===p}}finally{await r(e,["worktree","remove","--force",h],{gitBinary:u})}}(e,{mainHead:t,branchTip:u,message:h,env:m,requestId:c,gitBinary:d}):{ok:!1,reason:"error",error:`merge-tree failed (${b.code}): ${(b.stderr||b.stdout).trim().slice(0,400)}`}
7094
- if(w=b.stdout.trim().split("\n")[0]?.trim()||null,!w)return{ok:!1,reason:"error",error:"merge-tree returned no tree oid"}
7095
- const v=await r(e,["commit-tree",w,"-p",t,"-p",u,"-m",h],{env:m,gitBinary:d})
7129
+ return await r(e,["update-ref",o(d),p],{gitBinary:u}),{ok:!0,mergeCommitSha:p,treeSha:f||"",mainHead:i,branchTip:a,adopted:m===p}}finally{await r(e,["worktree","remove","--force",h],{gitBinary:u})}}(e,{mainHead:t,branchTip:u,message:h,env:p,requestId:c,gitBinary:d}):{ok:!1,reason:"error",error:`merge-tree failed (${b.code}): ${(b.stderr||b.stdout).trim().slice(0,400)}`}
7130
+ if(g=b.stdout.trim().split("\n")[0]?.trim()||null,!g)return{ok:!1,reason:"error",error:"merge-tree returned no tree oid"}
7131
+ const v=await r(e,["commit-tree",g,"-p",t,"-p",u,"-m",h],{env:p,gitBinary:d})
7096
7132
  if(0!==v.code)return{ok:!1,reason:"error",error:`commit-tree failed: ${v.stderr.trim().slice(0,400)}`}
7097
7133
  const k=v.stdout.trim(),I=await s(e,c,d),$=await r(e,["update-ref",o(c),k],{gitBinary:d})
7098
- return 0!==$.code?{ok:!1,reason:"error",error:`update-ref failed: ${$.stderr.trim().slice(0,400)}`}:{ok:!0,mergeCommitSha:k,treeSha:w,mainHead:t,branchTip:u,adopted:I===k}}catch(e){return{ok:!1,reason:"error",error:String(e?.message??e)}}},exports.landCertifiedMerge=async function(e,t){const{defaultBranch:r,mergeCommitSha:o,gitBinary:s}=t,i=await n(e,`${o}^1`,s),a=await n(e,r,s)
7134
+ return 0!==$.code?{ok:!1,reason:"error",error:`update-ref failed: ${$.stderr.trim().slice(0,400)}`}:{ok:!0,mergeCommitSha:k,treeSha:g,mainHead:t,branchTip:u,adopted:I===k}}catch(e){return{ok:!1,reason:"error",error:String(e?.message??e)}}},exports.landCertifiedMerge=async function(e,t){const{defaultBranch:r,mergeCommitSha:o,gitBinary:s}=t,i=await n(e,`${o}^1`,s),a=await n(e,r,s)
7099
7135
  if(!i||!a)return{landed:!1,reason:"cannot resolve candidate parent or default branch head"}
7100
7136
  if(i!==a)return{landed:!1,reason:`default branch moved since certification (${a.slice(0,8)} != candidate base ${i.slice(0,8)})`,refusal:"moved"}
7101
- const c=await(0,m.landOnDefaultBranch)(e,{defaultBranch:r,commitSha:o,expectedHead:a,gitBinary:s})
7137
+ const c=await(0,g.landOnDefaultBranch)(e,{defaultBranch:r,commitSha:o,expectedHead:a,gitBinary:s})
7102
7138
  return c.landed?{landed:!0,mergeCommitSha:o}:{landed:!1,reason:c.message,refusal:c.reason,files:c.files}},exports.deriveMergeChangedPaths=async function(e,t,n,o){const s=await r(e,["diff","--name-only",`${t}..${n}`],{gitBinary:o})
7103
7139
  return 0!==s.code?null:s.stdout.split("\n").map(e=>e.trim()).filter(Boolean)},exports.resolveTreeSha=i,exports.filterRunsCertifyingTree=async function(e,t,r,n){const o=new Map,s=[]
7104
7140
  for(const a of t){const t=a.commitSha
7105
- t&&(o.has(t)||o.set(t,await i(e,t,n)),o.get(t)===r&&s.push(a))}return s},exports.envMissingCapabilities=function(e){const t=new Set
7141
+ t&&(o.has(t)||o.set(t,await i(e,t,n)),o.get(t)===r&&s.push(a))}return s},exports.failsOnlyOnMain=a,exports.envMissingCapabilities=function(e){const t=new Set
7106
7142
  for(const r of e){const e=r.faultClass??r.results?.faultClass
7107
7143
  if(("failed"===r.status||"error"===r.status)&&"fail_environment"===e){const e=r.results?.missingCapabilities
7108
7144
  if(Array.isArray(e))for(const r of e)"string"==typeof r&&t.add(r)}}return[...t]},exports.scopeSkippedProcessIds=function(e){const t=new Map,r=e=>{const t=e.createdAt?new Date(e.createdAt).getTime():NaN
@@ -7121,16 +7157,16 @@ return n},exports.evaluateRunsAtHead=function(e,t,r){if(0===t.length)return"cert
7121
7157
  if(0===e.length)return"none"
7122
7158
  const n=e=>{const t=e.createdAt?new Date(e.createdAt).getTime():NaN
7123
7159
  return Number.isFinite(t)?t:0},o=e=>{const t=r?.get(e.testProcessId??"")
7124
- return!(!t||t===u.EMPTY_FINGERPRINT||!0===e.results?.scopeVerifyOnly||"passed"!==e.status&&"string"!=typeof e.executedFingerprint||e.executedFingerprint===t)}
7125
- let s=!1,i=!1,a=!1,c=!1
7126
- for(const r of t){const t=e.filter(e=>e.testProcessId===r).sort((e,t)=>n(e)-n(t)),l=t[t.length-1]
7127
- if(!l){a=!0
7128
- continue}if("skipped"===l.status&&!0===l.results?.scopeSkipped)continue
7129
- if(("passed"===l.status||"failed"===l.status||"error"===l.status||"skipped"===l.status)&&o(l)){a=!0
7130
- continue}if("passed"===l.status)continue
7131
- const d=l.faultClass??l.results?.faultClass
7132
- "failed"!==l.status&&"error"!==l.status||"fail_environment"!==d?"failed"!==l.status&&"error"!==l.status&&"skipped"!==l.status?i=!0:s=!0:c=!0}return s?"failed":i?"pending":c?"env":a?"none":"certified"}
7133
- const u=t(74146),h=t(35317),p=d(t(16928)),f=t(14139),m=t(75453),g="AlchemyLab Merge Queue",y="merge-queue@alchemylab.local"},56509(e,exports,t){function r(e){return p.join(e,f.ALCHEMY_DIR_NAME,"merge-lease.json")}function n(e){try{const t=u.readFileSync(r(e),"utf8"),n=JSON.parse(t)
7160
+ return!(!t||t===h.EMPTY_FINGERPRINT||!0===e.results?.scopeVerifyOnly||"passed"!==e.status&&"string"!=typeof e.executedFingerprint||e.executedFingerprint===t)}
7161
+ let s=!1,i=!1,c=!1,l=!1
7162
+ for(const r of t){const t=e.filter(e=>e.testProcessId===r).sort((e,t)=>n(e)-n(t)),d=t[t.length-1]
7163
+ if(!d){c=!0
7164
+ continue}if("skipped"===d.status&&!0===d.results?.scopeSkipped)continue
7165
+ if(("passed"===d.status||"failed"===d.status||"error"===d.status||"skipped"===d.status)&&o(d)){c=!0
7166
+ continue}if("passed"===d.status||a(d))continue
7167
+ const u=d.faultClass??d.results?.faultClass
7168
+ "failed"!==d.status&&"error"!==d.status||"fail_environment"!==u?"failed"!==d.status&&"error"!==d.status&&"skipped"!==d.status?i=!0:s=!0:l=!0}return s?"failed":i?"pending":l?"env":c?"none":"certified"}
7169
+ const h=t(74146),p=t(35317),f=u(t(16928)),m=t(14139),g=t(75453),y="AlchemyLab Merge Queue",w="merge-queue@alchemylab.local"},56509(e,exports,t){function r(e){return p.join(e,f.ALCHEMY_DIR_NAME,"merge-lease.json")}function n(e){try{const t=u.readFileSync(r(e),"utf8"),n=JSON.parse(t)
7134
7170
  return n&&"string"==typeof n.requestId?n:null}catch{return null}}function o(e,t=Date.now()){const r=Date.parse(e.expiresAt)
7135
7171
  return!!(Number.isFinite(r)&&t>r)||"merging"===e.phase&&e.holder?.hostname===h.hostname()&&"number"==typeof e.holder?.pid&&!function(e){try{return process.kill(e,0),!0}catch(e){return"EPERM"===e?.code}}(e.holder.pid)}function s(e,t,r,n){const o=new Date
7136
7172
  return{requestId:e,holder:{pid:process.pid,hostname:h.hostname(),source:t},phase:n?.phase??"merging",acquiredAt:n?.acquiredAt??o.toISOString(),renewedAt:o.toISOString(),ttlMs:r,expiresAt:new Date(o.getTime()+r).toISOString(),...n?.catchup?{catchup:n.catchup}:{}}}function i(e,t){const n=r(e)
@@ -7160,40 +7196,46 @@ if(o&&o.requestId!==t)return!1
7160
7196
  try{return u.unlinkSync(s),!0}catch{return!1}}
7161
7197
  const u=d(t(79896)),h=d(t(70857)),p=d(t(16928)),f=t(14139)
7162
7198
  exports.MERGE_LEASE_DEFAULT_TTL_MS=6e5,exports.MERGE_LEASE_CATCHUP_TTL_MS=36e5,exports.MERGE_LEASE_LAND_GRACE_TTL_MS=9e5
7163
- const m=e=>new Promise(t=>setTimeout(t,e))},14128(e,exports,t){async function r(e,t){let r,s
7164
- try{[r,s]=await Promise.all([e.fetchRequestRuns(),e.fetchActiveProcesses()])}catch(t){return e.logger.warn("merge-queue: could not read runs/processes for certification; treating as not yet dispatched",{requestId:e.requestId,error:String(t?.message??t)}),{state:"none",missingCapabilities:[],failedChecks:[]}}const a=await(0,i.filterRunsCertifyingTree)(e.projectPath,r||[],t,e.gitBinary),c=(s||[]).filter(e=>e&&"string"==typeof e.id),l=(0,i.evaluateRunsAtHead)(a,c.map(e=>e.id),(0,o.requiredFingerprints)(c,e.criteria,(0,i.scopeSkippedProcessIds)(a),(0,i.verifyOnlyProcessIds)(a)))
7165
- return{state:l,missingCapabilities:"env"===l?(0,i.envMissingCapabilities)(a):[],failedChecks:"failed"===l?n(a,c):[]}}function n(e,t){const r=e=>{const t=e.createdAt?new Date(e.createdAt).getTime():NaN
7166
- return Number.isFinite(t)?t:0},n=[]
7167
- for(const o of t){const t=e.filter(e=>e.testProcessId===o.id).sort((e,t)=>r(e)-r(t)).pop()
7168
- !t||"failed"!==t.status&&"error"!==t.status||"fail_environment"!==(t.faultClass??t.results?.faultClass)&&n.push(String(o.name??"").trim()||"a check")}return n}exports.runMergeQueueGate=async function(e){const{projectPath:t,requestId:n,branchName:o,defaultBranch:a,requestTitle:c,source:l,gitBinary:d,logger:u}=e,h=await(0,s.acquireMergeLease)(t,n,{source:l,waitMs:15e3})
7169
- if(!h.acquired)return{action:"wait-slot",holder:h.holder}
7170
- try{const l=await(0,i.assessMergeFreshness)(t,a,o,d),h=l.fresh
7171
- let p,f
7172
- if(h){const e=await(0,i.resolveTreeSha)(t,l.branchTip,d)
7173
- if(!e)return{action:"error",message:`cannot resolve the tree of ${o}'s tip`}
7174
- p=l.branchTip,f=e,(0,i.clearMergeCandidate)(t,n,d).catch(()=>{})}else{const e=await(0,i.buildProspectiveMergeCommit)(t,{defaultBranch:a,branchName:o,requestId:n,requestTitle:c,gitBinary:d})
7199
+ const m=e=>new Promise(t=>setTimeout(t,e))},14128(e,exports,t){async function r(e,t){let r,n
7200
+ try{[r,n]=await Promise.all([e.fetchRequestRuns(),e.fetchActiveProcesses()])}catch(t){return e.logger.warn("merge-queue: could not read runs/processes for certification; treating as not yet dispatched",{requestId:e.requestId,error:String(t?.message??t)}),{state:"none",missingCapabilities:[],failedChecks:[],failedProcessIds:[],runs:[]}}const i=await(0,a.filterRunsCertifyingTree)(e.projectPath,r||[],t,e.gitBinary),c=(n||[]).filter(e=>e&&"string"==typeof e.id),l=(0,a.evaluateRunsAtHead)(i,c.map(e=>e.id),(0,s.requiredFingerprints)(c,e.criteria,(0,a.scopeSkippedProcessIds)(i),(0,a.verifyOnlyProcessIds)(i))),d="failed"===l?function(e,t){const r=[]
7201
+ for(const n of t){const t=o(e.filter(e=>e.testProcessId===n.id))
7202
+ if(!t||"failed"!==t.status&&"error"!==t.status)continue
7203
+ const s=t.faultClass??t.results?.faultClass
7204
+ "fail_environment"!==s&&"fail_main"!==s&&r.push(n)}return r}(i,c):[]
7205
+ return{state:l,missingCapabilities:"env"===l?(0,a.envMissingCapabilities)(i):[],failedChecks:d.map(e=>String(e.name??"").trim()||"a check"),failedProcessIds:d.map(e=>e.id),runs:r||[]}}async function n(e,t,r,n,s){let i=!1
7206
+ if(!n&&t.failedProcessIds.length>0){const r=await(0,a.resolveTreeSha)(e.projectPath,s,e.gitBinary).catch(()=>null)
7207
+ if(r){const n=await(0,a.filterRunsCertifyingTree)(e.projectPath,t.runs,r,e.gitBinary)
7208
+ i=t.failedProcessIds.every(e=>"passed"===o(n.filter(t=>t.testProcessId===e))?.status)}}return{action:"catchup-failed",mergeCommitSha:r,failedChecks:t.failedChecks,mainMergedIn:!n,passedOnWork:i}}function o(e){const t=e=>{const t=e.createdAt?new Date(e.createdAt).getTime():NaN
7209
+ return Number.isFinite(t)?t:0}
7210
+ return[...e].sort((e,r)=>t(e)-t(r)).pop()}exports.runMergeQueueGate=async function(e){const{projectPath:t,requestId:o,branchName:s,defaultBranch:c,requestTitle:l,source:d,gitBinary:u,logger:h}=e,p=await(0,i.acquireMergeLease)(t,o,{source:d,waitMs:e.leaseWaitMs??15e3})
7211
+ if(!p.acquired)return{action:"wait-slot",holder:p.holder}
7212
+ try{const d=await(0,a.assessMergeFreshness)(t,c,s,u),p=d.fresh
7213
+ let f,m
7214
+ if(p){const e=await(0,a.resolveTreeSha)(t,d.branchTip,u)
7215
+ if(!e)return{action:"error",message:`cannot resolve the tree of ${s}'s tip`}
7216
+ f=d.branchTip,m=e,(0,a.clearMergeCandidate)(t,o,u).catch(()=>{})}else{const e=await(0,a.buildProspectiveMergeCommit)(t,{defaultBranch:c,branchName:s,requestId:o,requestTitle:l,gitBinary:u})
7175
7217
  if(!e.ok)return"conflicts"===e.reason?{action:"conflicts",summary:e.conflictSummary}:{action:"error",message:e.error}
7176
- if(p=e.mergeCommitSha,f=e.treeSha||await(0,i.resolveTreeSha)(t,e.mergeCommitSha,d)||"",!f)return{action:"error",message:"cannot resolve the candidate merge tree"}}const m=async t=>await async function(e,t){if(!e.evaluateMainChange)return null
7218
+ if(f=e.mergeCommitSha,m=e.treeSha||await(0,a.resolveTreeSha)(t,e.mergeCommitSha,u)||"",!m)return{action:"error",message:"cannot resolve the candidate merge tree"}}const g=async t=>await async function(e,t){if(!e.evaluateMainChange)return null
7177
7219
  try{const r=await e.evaluateMainChange({landingSha:t})
7178
- return r?.hold?r.sentBack||!r.touched?(e.logger.info("merge held: main's newer work broke a criterion check on the landing tree; back to its worker",{requestId:e.requestId}),{action:"sent-back",summary:r.summary}):(e.logger.info("merge held: a review of main's change goes first",{requestId:e.requestId}),{action:"review-held",summary:r.summary,touched:r.touched}):null}catch(t){return e.logger.warn("main-change evaluation failed; merge continues (it is an observer, not a lock)",{requestId:e.requestId,error:t instanceof Error?t.message:String(t)}),null}}(e,p)??await async function(e,t,r){if(!e.evaluatePromises)return null
7220
+ return r?.hold?r.sentBack||!r.touched?(e.logger.info("merge held: main's newer work broke a criterion check on the landing tree; back to its worker",{requestId:e.requestId}),{action:"sent-back",summary:r.summary}):(e.logger.info("merge held: a review of main's change goes first",{requestId:e.requestId}),{action:"review-held",summary:r.summary,touched:r.touched}):null}catch(t){return e.logger.warn("main-change evaluation failed; merge continues (it is an observer, not a lock)",{requestId:e.requestId,error:t instanceof Error?t.message:String(t)}),null}}(e,f)??await async function(e,t,r){if(!e.evaluatePromises)return null
7179
7221
  try{const n=await e.evaluatePromises({baseSha:t,headSha:r})
7180
- return n?.hold?(e.logger.info("merge held: an earlier promise's check fails on the tree it would land",{requestId:e.requestId,breaks:n.breaks}),{action:"promises-held",summary:n.summary,breaks:n.breaks}):null}catch(t){return e.logger.warn("promise evaluation failed; merge continues (it is an observer, not a lock)",{requestId:e.requestId,error:t instanceof Error?t.message:String(t)}),null}}(e,l.mainHead,p)??t
7181
- if(e.testGateOff)return m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p})
7182
- const g=await r(e,f),{state:y,missingCapabilities:w}=g
7183
- if("env"===y&&e.acceptEnvironmentGap)return m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p})
7184
- if("env"===y)return{action:"env-gap",mergeCommitSha:p,missingCapabilities:w}
7185
- if("certified"===y)return m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p})
7186
- if("failed"===y&&e.acceptFailingTests)return e.logger.warn("merge-queue: landing a request whose tests are RED because the user waived the gate",{requestId:n,mergeCommitSha:p}),m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p})
7187
- if("failed"===y)return await(0,i.clearMergeCandidate)(t,n,d).catch(()=>{}),{action:"catchup-failed",mergeCommitSha:p,failedChecks:g.failedChecks}
7188
- if("pending"===y)return(0,s.renewMergeLease)(t,n,{ttlMs:s.MERGE_LEASE_CATCHUP_TTL_MS,phase:"catchup"}),{action:"catchup-running",mergeCommitSha:p,baseSha:l.mainHead,branchTip:l.branchTip,dispatched:!1}
7189
- u.info("merge-queue: no green runs at the landing tree -- dispatching a catch-up at the certifying commit",{requestId:n,certifying:h?"branch-tip":"candidate",certifyingSha:p.slice(0,8),landingTree:f.slice(0,8),mainHead:l.mainHead.slice(0,8),mergeBase:l.mergeBase.slice(0,8)})
7190
- const b=e=>({mergeCommitSha:p,baseSha:l.mainHead,branchTipSha:l.branchTip,landingTreeSha:f,certifying:h?"branch-tip":"candidate",testRunIds:e,dispatchedAt:(new Date).toISOString()})
7191
- let v
7192
- try{v=await e.dispatchCatchUp({mergeCommitSha:p,baseSha:l.mainHead})}catch(e){return u.warn("merge-queue: catch-up dispatch outcome unknown; holding the ticket for adoption on retry",{requestId:n,error:String(e?.message??e)}),(0,s.renewMergeLease)(t,n,{ttlMs:s.MERGE_LEASE_CATCHUP_TTL_MS,phase:"catchup",catchup:b([])}),{action:"catchup-running",mergeCommitSha:p,baseSha:l.mainHead,branchTip:l.branchTip,dispatched:!0}}if(!v.success)return await(0,i.clearMergeCandidate)(t,n,d).catch(()=>{}),{action:"error",message:`catch-up test dispatch failed: ${v.error||"unknown error"}`}
7193
- if("green"===v.settled)return m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p})
7194
- if("failed"===v.settled){const t=await r(e,f)
7195
- return"env"===t.state&&e.acceptEnvironmentGap?m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p}):"env"===t.state?{action:"env-gap",mergeCommitSha:p,missingCapabilities:t.missingCapabilities}:e.acceptFailingTests?(e.logger.warn("merge-queue: landing a request whose catch-up run is RED because the user waived the gate",{requestId:n,mergeCommitSha:p}),m(h?{action:"proceed"}:{action:"land",certifiedCommitSha:p})):{action:"catchup-failed",mergeCommitSha:p,failedChecks:t.failedChecks}}return(0,s.renewMergeLease)(t,n,{ttlMs:s.MERGE_LEASE_CATCHUP_TTL_MS,phase:"catchup",catchup:b(v.testRunIds??[])}),{action:"catchup-running",mergeCommitSha:p,baseSha:l.mainHead,branchTip:l.branchTip,dispatched:!0}}catch(e){return{action:"error",message:String(e?.message??e)}}}
7196
- const o=t(74146),s=t(56509),i=t(87192)},41152(e,exports){exports.createMetricsCollector=function(e=!0){return new t(e)}
7222
+ return n?.hold?(e.logger.info("merge held: an earlier promise's check fails on the tree it would land",{requestId:e.requestId,breaks:n.breaks}),{action:"promises-held",summary:n.summary,breaks:n.breaks}):null}catch(t){return e.logger.warn("promise evaluation failed; merge continues (it is an observer, not a lock)",{requestId:e.requestId,error:t instanceof Error?t.message:String(t)}),null}}(e,d.mainHead,f)??t
7223
+ if(e.testGateOff)return g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f})
7224
+ const y=await r(e,m),{state:w,missingCapabilities:b}=y
7225
+ if("env"===w&&e.acceptEnvironmentGap)return g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f})
7226
+ if("env"===w)return{action:"env-gap",mergeCommitSha:f,missingCapabilities:b}
7227
+ if("certified"===w)return g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f})
7228
+ if("failed"===w&&e.acceptFailingTests)return e.logger.warn("merge-queue: landing a request whose tests are RED because the user waived the gate",{requestId:o,mergeCommitSha:f}),g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f})
7229
+ if("failed"===w)return await(0,a.clearMergeCandidate)(t,o,u).catch(()=>{}),n(e,y,f,p,d.branchTip)
7230
+ if("pending"===w)return(0,i.renewMergeLease)(t,o,{ttlMs:i.MERGE_LEASE_CATCHUP_TTL_MS,phase:"catchup"}),{action:"catchup-running",mergeCommitSha:f,baseSha:d.mainHead,branchTip:d.branchTip,dispatched:!1}
7231
+ h.info("merge-queue: no green runs at the landing tree -- dispatching a catch-up at the certifying commit",{requestId:o,certifying:p?"branch-tip":"candidate",certifyingSha:f.slice(0,8),landingTree:m.slice(0,8),mainHead:d.mainHead.slice(0,8),mergeBase:d.mergeBase.slice(0,8)})
7232
+ const v=e=>({mergeCommitSha:f,baseSha:d.mainHead,branchTipSha:d.branchTip,landingTreeSha:m,certifying:p?"branch-tip":"candidate",testRunIds:e,dispatchedAt:(new Date).toISOString()})
7233
+ let k
7234
+ try{k=await e.dispatchCatchUp({mergeCommitSha:f,baseSha:d.mainHead})}catch(e){return h.warn("merge-queue: catch-up dispatch outcome unknown; holding the ticket for adoption on retry",{requestId:o,error:String(e?.message??e)}),(0,i.renewMergeLease)(t,o,{ttlMs:i.MERGE_LEASE_CATCHUP_TTL_MS,phase:"catchup",catchup:v([])}),{action:"catchup-running",mergeCommitSha:f,baseSha:d.mainHead,branchTip:d.branchTip,dispatched:!0}}if(!k.success)return await(0,a.clearMergeCandidate)(t,o,u).catch(()=>{}),{action:"error",message:`catch-up test dispatch failed: ${k.error||"unknown error"}`}
7235
+ if("green"===k.settled)return g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f})
7236
+ if("failed"===k.settled){const t=await r(e,m)
7237
+ return"env"===t.state&&e.acceptEnvironmentGap?g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f}):"env"===t.state?{action:"env-gap",mergeCommitSha:f,missingCapabilities:t.missingCapabilities}:e.acceptFailingTests?(e.logger.warn("merge-queue: landing a request whose catch-up run is RED because the user waived the gate",{requestId:o,mergeCommitSha:f}),g(p?{action:"proceed"}:{action:"land",certifiedCommitSha:f})):n(e,t,f,p,d.branchTip)}return(0,i.renewMergeLease)(t,o,{ttlMs:i.MERGE_LEASE_CATCHUP_TTL_MS,phase:"catchup",catchup:v(k.testRunIds??[])}),{action:"catchup-running",mergeCommitSha:f,baseSha:d.mainHead,branchTip:d.branchTip,dispatched:!0}}catch(e){return{action:"error",message:String(e?.message??e)}}}
7238
+ const s=t(74146),i=t(56509),a=t(87192)},41152(e,exports){exports.createMetricsCollector=function(e=!0){return new t(e)}
7197
7239
  class t{constructor(e=!0){this.enabled=e,this.counters=new Map,this.gauges=new Map,this.histograms=new Map}getKey(e,t){const r=Object.entries(t).map(([e,t])=>`${e}="${String(t)}"`).join(",")
7198
7240
  return r?`${e}{${r}}`:e}incrementCounter(e,t=1,r={}){if(!this.enabled)return
7199
7241
  const n=this.getKey(e,r),o=this.counters.get(n)||0
@@ -7643,7 +7685,7 @@ for(;n<r.length&&(i.has(r[n])||/^[A-Za-z_][A-Za-z0-9_]*=/.test(r[n]));)n++
7643
7685
  if(n>0&&"command"===r[n-1]&&r[n]?.startsWith("-"))continue
7644
7686
  const o=(r[n]??"").replace(/^["']|["']$/g,""),a=s[o]??s[o.split("/").pop()??""]
7645
7687
  a&&t.add(a)}return[...t].sort()}function n(e){const t=e
7646
- return t?.standing&&t?.waived?[]:Array.isArray(t?.verify)?t.verify.filter(e=>"string"==typeof e&&e.trim().length>0&&null===(0,o.toolFallbackProblem)(e)):[]}exports.proofCommandsOf=n,exports.routingCandidates=function(e,t,r){const n=e.filter(e=>!t?.has(e.id))
7688
+ return t?.standing&&t?.waived||t?.baseline&&"object"==typeof t.baseline?[]:Array.isArray(t?.verify)?t.verify.filter(e=>"string"==typeof e&&e.trim().length>0&&null===(0,o.toolFallbackProblem)(e)):[]}exports.proofCommandsOf=n,exports.routingCandidates=function(e,t,r){const n=e.filter(e=>!t?.has(e.id))
7647
7689
  return[...n.filter(e=>!r?.has(e.id)),...n.filter(e=>!!r?.has(e.id))]},exports.proofCommandsFor=function(e,t,o){const s=Array.isArray(t)?t:[]
7648
7690
  return function(e,t){const o=Array.isArray(e)?e:[],s=t.find(e=>"experiment"===e.kind),i=t.filter(e=>"experiment"!==e.kind),a=new Map(i.map(e=>[e.id,r((e.config?.commands??[]).map(e=>e?.command))])),c=[]
7649
7691
  return o.forEach((e,t)=>{const o=n(e)
@@ -7941,7 +7983,7 @@ try{const t=(0,l.simpleGit)(r)
7941
7983
  if(await t.checkIsRepo()){const n=(await t.status()).current||"unknown",o=await a.default.stat(r),s={requestId:e,branchName:n,worktreePath:r,created:o.birthtime||o.ctime,lastAccessed:new Date,status:"active"}
7942
7984
  this.activeWorktrees.set(e,s),this.logger.debug("Registered existing worktree",{requestId:e,branchName:n,worktreePath:r})}else this.logger.warn("Found invalid worktree directory",{requestId:e,path:r})}catch(t){this.logger.warn("Error scanning worktree directory",{requestId:e,path:r,error:(0,h.getErrorMessage)(t)})}}this.logger.info("Worktree scan completed",{totalWorktrees:this.activeWorktrees.size})}catch(e){throw this.logger.error("Failed to scan existing worktrees",{error:(0,h.getErrorMessage)(e)}),e}}}},27634(e,exports,t){exports.WorktreeManager=void 0
7943
7985
  var r=t(21502)
7944
- Object.defineProperty(exports,"WorktreeManager",{enumerable:!0,get:function(){return r.WorktreeManager}})},66865(module){module.exports=require("@modelcontextprotocol/sdk/server/index.js")},62422(module){module.exports=require("@modelcontextprotocol/sdk/server/sse.js")},25802(module){module.exports=require("@modelcontextprotocol/sdk/server/stdio.js")},49150(module){module.exports=require("@modelcontextprotocol/sdk/types.js")},98577(module){module.exports=require("cors")},66261(module){module.exports=require("events")},7252(module){module.exports=require("express")},88194(module){module.exports=require("jszip")},47373(module){module.exports=require("micromatch")},10980(module){module.exports=require("pdf-lib")},83807(module){module.exports=require("simple-git")},36666(module){module.exports=require("ts-node/register/transpile-only")},9956(module){module.exports=require("undici")},88891(module){module.exports=require("unpdf")},55124(module){module.exports=require("winston")},11569(module){module.exports=require("zod")},25106(module){module.exports=require("zod-to-json-schema")},35317(module){module.exports=require("child_process")},76982(module){module.exports=require("crypto")},79896(module){module.exports=require("fs")},91943(module){module.exports=require("fs/promises")},69278(module){module.exports=require("net")},73024(module){module.exports=require("node:fs")},76760(module){module.exports=require("node:path")},70857(module){module.exports=require("os")},16928(module){module.exports=require("path")},23785(module){module.exports=require("readline")},49786(module){module.exports=require("stream/promises")},39023(module){module.exports=require("util")},43106(module){module.exports=require("zlib")}}
7986
+ Object.defineProperty(exports,"WorktreeManager",{enumerable:!0,get:function(){return r.WorktreeManager}})},66865(module){module.exports=require("@modelcontextprotocol/sdk/server/index.js")},62422(module){module.exports=require("@modelcontextprotocol/sdk/server/sse.js")},25802(module){module.exports=require("@modelcontextprotocol/sdk/server/stdio.js")},49150(module){module.exports=require("@modelcontextprotocol/sdk/types.js")},98577(module){module.exports=require("cors")},66261(module){module.exports=require("events")},7252(module){module.exports=require("express")},88194(module){module.exports=require("jszip")},47373(module){module.exports=require("micromatch")},10980(module){module.exports=require("pdf-lib")},83807(module){module.exports=require("simple-git")},36666(module){module.exports=require("ts-node/register/transpile-only")},9956(module){module.exports=require("undici")},88891(module){module.exports=require("unpdf")},55124(module){module.exports=require("winston")},11569(module){module.exports=require("zod")},25106(module){module.exports=require("zod-to-json-schema")},35317(module){module.exports=require("child_process")},76982(module){module.exports=require("crypto")},79896(module){module.exports=require("fs")},91943(module){module.exports=require("fs/promises")},69278(module){module.exports=require("net")},31421(module){module.exports=require("node:child_process")},73024(module){module.exports=require("node:fs")},76760(module){module.exports=require("node:path")},70857(module){module.exports=require("os")},16928(module){module.exports=require("path")},23785(module){module.exports=require("readline")},49786(module){module.exports=require("stream/promises")},39023(module){module.exports=require("util")},43106(module){module.exports=require("zlib")}}
7945
7987
  const r={}
7946
7988
  e.m=t,(()=>{const t=Object.getPrototypeOf
7947
7989
  let r