alchemylab 3.0.1 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +10 -6
- package/dist/http-server.js +1058 -1050
- package/dist/index.js +202 -194
- package/dist/mcp-direct.js +50 -42
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -362,13 +362,13 @@ const M=new I.FileOperationsClient(T,n.child({component:"file-client"}),o,A,j,P)
|
|
|
362
362
|
R.registerClient(M)
|
|
363
363
|
const D=new $.ProxyClient(T,n.child({component:"proxy-client"}),o,A,j,P)
|
|
364
364
|
R.registerClient(D)
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
R.registerClient(
|
|
365
|
+
const N=new x.RequestClient(T,n.child({component:"request-client"}),o,A,j,P)
|
|
366
|
+
N.setAuthToken(j),(0,d.isDebugToolsEnabled)()&&n.debug("Explicitly set token on RequestClient",{tokenPresent:!!j}),R.registerClient(N)
|
|
367
|
+
const F=new S.DocumentClient(T,n.child({component:"document-client"}),o,A,j,P)
|
|
368
|
+
R.registerClient(F)
|
|
369
369
|
const z=new E.ToolRecordClient(T,n.child({component:"tool-record-client"}),o,A,j,P)
|
|
370
370
|
R.registerClient(z)
|
|
371
|
-
const L=new _.ApiClient(q,A,O,M,F,
|
|
371
|
+
const L=new _.ApiClient(q,A,O,M,N,F,z,D,n.child({component:"api-client"})),B=a.default.Router();(0,h.registerAuthRoutes)(B,{projectClient:A,taskClient:O,fileClient:M,authManager:q,apiClient:L},i,n)
|
|
372
372
|
const H=r.getContext();(0,p.registerConfigRoutes)(B,n,H.basePath),e.use("/api",B)
|
|
373
373
|
const U=e=>{const t=o.startTimer(e)
|
|
374
374
|
return e=>t(e)},W=(e,t,n)=>{const o=e.query.path||e.body.path
|
|
@@ -403,11 +403,11 @@ return r({endpoint:"files/content",method:"GET",success:!0,encoding:"binary"}),t
|
|
|
403
403
|
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"})
|
|
404
404
|
const s=U("api_request_duration"),{content:i,taskId:a,format:c="text"}=e.body
|
|
405
405
|
if(void 0===i)throw(0,u.createValidationError)("Content parameter is required")
|
|
406
|
-
const l=await(0,m.handleWriteFile)({taskId:a,path:e.relativePath,content:i,format:c},r.getContext(),M,O,z,
|
|
406
|
+
const l=await(0,m.handleWriteFile)({taskId:a,path:e.relativePath,content:i,format:c},r.getContext(),M,O,z,N,n,o)
|
|
407
407
|
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"})
|
|
408
408
|
const s=U("api_request_duration"),{replacements:i,mode:a="unique",taskId:c}=e.body
|
|
409
409
|
if(!i||!Array.isArray(i))throw(0,u.createValidationError)("Replacements array is required")
|
|
410
|
-
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,
|
|
410
|
+
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,N,n,o)
|
|
411
411
|
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"})
|
|
412
412
|
const r=U("api_request_duration"),{taskId:n}=e.body
|
|
413
413
|
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"})
|
|
@@ -1184,7 +1184,7 @@ const c=new Set(a),l="string"==typeof n.operation?n.operation:void 0
|
|
|
1184
1184
|
if(l&&i?.includes(l)&&!c.has(l))return o[`${r} ${l}`]??`${r} ${l} is not an operation the ${e} uses. Its operations here: ${i.filter(e=>c.has(e)).join(", ")}.`
|
|
1185
1185
|
for(const[o,i]of Object.entries(s??{}))if(!i.some(e=>c.has(e))&&t(n,o.split(".")))return`${r}: ${o} is not something the ${e} sets.`
|
|
1186
1186
|
return null},exports.FIELD_TAGS_META="alchemylab/fieldTags"
|
|
1187
|
-
const r=["run","list","setup","remove"],n={session_message_operations:["send","note","reply","list","get","record-decision","brief","withdraw","question","user-answers","escalate"],request_operations:["update","complete","merge","reopen","get-info","list","search","status","cancel","wait-on","clear-wait","premise-block","
|
|
1187
|
+
const r=["run","list","setup","remove"],n={session_message_operations:["send","note","reply","list","get","record-decision","brief","withdraw","question","user-answers","escalate"],request_operations:["update","complete","merge","reopen","get-info","list","search","status","cancel","wait-on","clear-wait","premise-block","bring-main-in","list-areas","update-areas","check-target","round-piece"],create_request:[],guidance:["propose","exception","conflict","outdated","list"],document_operations:["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"],commands:r}
|
|
1188
1188
|
exports.b={alchemist:{session_message_operations:["send","note","reply","list","get","record-decision","brief","withdraw","question","user-answers","escalate","plan-budget"],request_operations:["update","activate","complete","merge","revert","reopen","get-info","list","search","status","cancel","hold","premise-block","delete-empty","list-areas","update-areas","wait-on","clear-wait","hand-over","check-target","budget","target","arc-name","process","estimate"],create_request:["budget","estimate","schedule","delegate","refile","asked","process"],guidance:["propose","outdated","exception","conflict","list","standard","from-document","process"],document_operations:[...n.document_operations,"schedule"],commands:r},worker:n,coder:n,writer:n,reviewer:{session_message_operations:["send","note","reply","list","get","rule-verdicts"],guidance:["propose","list","standard"],document_operations:["search","get-history","get-version","list-annotations","create-annotation","reply-to-annotation","resolve-annotation","create-artifact-annotation"],record_criteria_verification:[]},driver:{commands:r},"auto-tester":{commands:r}},exports.REGISTER_BY_SETUP_RUN='Commands are saved by a setup run, not here: commands({ operation: "setup", context: "<the command, where it runs, what it is for>" }). Its setup architect tries the command in a box and saves it; you are told when it ends.'
|
|
1189
1189
|
const o={"commands register":exports.REGISTER_BY_SETUP_RUN},s=e=>null!=e&&""!==e&&!(Array.isArray(e)&&0===e.length)},82272(e,exports){exports.alchemyMeta=function(e){return{_meta:{alchemy:e}}},exports.withAlchemyKind=function(e,t,r){const n=e._meta,o=n?.alchemy??{}
|
|
1190
1190
|
return{...e,_meta:{...n,alchemy:{...o,...r,kind:t}}}},exports.readAlchemyMeta=function(e){return e?._meta?.alchemy}},58185(e,exports,t){function r(e){if($.has(e))$.delete(e)
|
|
@@ -1799,10 +1799,10 @@ if(!j){await a.default.mkdir(E,{recursive:!0}),m.info(`Successfully created dire
|
|
|
1799
1799
|
try{const e=c.default.join(E,".gitkeep")
|
|
1800
1800
|
await a.default.writeFile(e,"# This file makes the directory trackable by Git\n"),O=!0,m.info("Created .gitkeep file for Git tracking")}catch(e){m.warn("Failed to create .gitkeep file",{error:e instanceof Error?e.message:String(e)}),O=!1}}const M=Date.now()-g
|
|
1801
1801
|
p.recordValue("create_directory_time",M),p.incrementCounter("create_directory_success",1),b=l.FileOperationPhase.GIT_COMMIT,p.incrementCounter("create_directory_phase",1,{phase:b})
|
|
1802
|
-
const D=`Create directory: ${S} - ${h||"Create directory"}`,
|
|
1803
|
-
b=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,p.incrementCounter("create_directory_phase",1,{phase:b}),await l.updateFileOperationRecords($,y,w,S,v.recordType,`Successfully ${j?"verified":"created"} directory: ${S}`,{status:"completed",commitHash:
|
|
1802
|
+
const D=`Create directory: ${S} - ${h||"Create directory"}`,N=!j&&O?`${S}/.gitkeep`:S,F=await l.commitToGit($,N,P,A,D,r,m,p,v.metricsPrefix)
|
|
1803
|
+
b=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,p.incrementCounter("create_directory_phase",1,{phase:b}),await l.updateFileOperationRecords($,y,w,S,v.recordType,`Successfully ${j?"verified":"created"} directory: ${S}`,{status:"completed",commitHash:F.commitHash,previousCommitHash:F.previousCommitHash,directoryExists:j,isDirectory:!0,userComment:h},`Successfully ${j?"verified":"created"} directory: ${S}`,n,o,m,p,v.metricsPrefix)
|
|
1804
1804
|
const z=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
|
|
1805
|
-
return{content:[{type:"text",text:j?"Directory already exists":`Successfully created directory${
|
|
1805
|
+
return{content:[{type:"text",text:j?"Directory already exists":`Successfully created directory${F.commitHash?`\nCommit: ${F.commitHash.substring(0,8)}`:""}${z.requestId&&!z.permanentBranch?`\nBranch: ${z.requestId}`:""}`}]}}catch(t){if(e.taskId&&o)try{const n=t instanceof d.NotFoundError||t instanceof d.ValidationError||t instanceof u.ZodError
|
|
1806
1806
|
await o.createToolRecord({...n?{}:{taskId:e.taskId},projectId:r.projectId,type:"create-directory",metadata:{status:"failed",timestamp:(new Date).toISOString(),path:e.path,userComment:e.comment,error:(0,h.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:String(t),phase:b,attemptedTaskId:e.taskId,taskIdValidationFailed:n},note:`Failed to create directory ${e.path} - ${(0,h.getErrorMessage)(t)}`})}catch(t){m.error("Failed to create failure record for create directory operation",{error:t instanceof Error?t.message:String(t),taskId:e.taskId,path:e.path})}const s=Date.now()-g
|
|
1807
1807
|
if(p.recordValue("create_directory_time",s),p.incrementCounter("create_directory_errors",1),m.error("Error creating directory:",{error:(0,h.getErrorMessage)(t),time:s}),(y||w)&&e.taskId){b=l.FileOperationPhase.RECORD_UPDATE_FAILURE
|
|
1808
1808
|
try{await l.updateFileOperationRecords(e.taskId,y,w,e.path,v.recordType,`Failed to create directory ${e.path}`,{status:"failed",error:(0,h.getErrorMessage)(t),userComment:e.comment},`Failed to create directory ${e.path}`,n,o,m,p,v.metricsPrefix)}catch(t){m.error("Failed to update record with failure status",{taskId:e.taskId,recordId:y,error:t instanceof Error?t.message:String(t)})}}return l.handleFileOperationError(t,b,m,p,e,v)}}(...e))}
|
|
@@ -1834,16 +1834,16 @@ const P=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null
|
|
|
1834
1834
|
f.info("Initial branch context",{initialBranch:P.branch,initialRequestId:P.requestId,isMain:P.isMain})
|
|
1835
1835
|
const A=q.map(e=>String(e.target).replace(/\\/g,"/")),O=[]
|
|
1836
1836
|
w=c.FileOperationPhase.TASK_VALIDATION,s.incrementCounter("delete_files_phase",1,{phase:w})
|
|
1837
|
-
const M=A.length>0?A[0]:"document_operations",D=await c.validateTaskId(C,M,d,f,s,b.metricsPrefix),
|
|
1838
|
-
z.taskPromoted&&f.info(`Auto-promoted task ${C} (${D.title||"Untitled"}) to in_progress for mixed operations`,{taskId:C,taskTitle:D.title,previousStatus:D.status,newStatus:z.newTaskStatus,totalOperations:T.length,fileOperations:q.length,documentOperations:R.length}),z.requestPromoted&&f.info(`Auto-promoted request ${
|
|
1839
|
-
const L=await c.ensureCorrectWorkspaceContext({taskPromoted:z.taskPromoted,requestId:
|
|
1837
|
+
const M=A.length>0?A[0]:"document_operations",D=await c.validateTaskId(C,M,d,f,s,b.metricsPrefix),N=D?.requestId,F=await c.validateWorkItemStates(C,M,d,k,r,f,s,b.metricsPrefix),z=await c.autoPromoteTaskStatus(C,D.status,N,F.request?.status,M,d,k,f,s,b.metricsPrefix)
|
|
1838
|
+
z.taskPromoted&&f.info(`Auto-promoted task ${C} (${D.title||"Untitled"}) to in_progress for mixed operations`,{taskId:C,taskTitle:D.title,previousStatus:D.status,newStatus:z.newTaskStatus,totalOperations:T.length,fileOperations:q.length,documentOperations:R.length}),z.requestPromoted&&f.info(`Auto-promoted request ${N} to in_progress for file operations`,{requestId:N,previousStatus:F.request?.status,newStatus:z.newRequestStatus,triggeringTaskId:C})
|
|
1839
|
+
const L=await c.ensureCorrectWorkspaceContext({taskPromoted:z.taskPromoted,requestId:N,context:r,operation:"delete-files",logger:f,metrics:s,metricsPrefix:b.metricsPrefix})
|
|
1840
1840
|
if(!L.success)throw new Error(L.error)
|
|
1841
1841
|
for(const e of A){const{normalizedPath:t,fullPath:n}=c.validateAndNormalizePath(e,r)
|
|
1842
1842
|
O.push({normalized:t,full:n})}let B={hadUserMods:!1,userModsCommitHash:void 0}
|
|
1843
1843
|
if(r.git)try{f.debug("[DELETE-FILES] Checking for user changes before mixed delete operations")
|
|
1844
|
-
const e=await(0,u.commitUserChangesBeforeToolOperation)(r.git,"delete-files",{requestId:
|
|
1844
|
+
const e=await(0,u.commitUserChangesBeforeToolOperation)(r.git,"delete-files",{requestId:N,taskId:C,workspaceManager:r.workspaceManager},f,s)
|
|
1845
1845
|
e.userChangesCommitted&&(f.debug("[DELETE-FILES] User changes committed before mixed delete operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),f.info("User changes committed before mixed delete-files operation",{taskId:C,totalOperations:T.length,fileOperations:q.length,documentOperations:R.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}),B={hadUserMods:!0,userModsCommitHash:e.commitHash})}catch(e){f.debug("[DELETE-FILES] Pre-tool commit failed, continuing with mixed delete operations",{error:e instanceof Error?e.message:String(e)}),f.warn("Pre-tool user changes commit failed, continuing with mixed delete operation",{taskId:C,totalOperations:T.length,fileOperations:q.length,documentOperations:R.length,error:e instanceof Error?e.message:String(e)})}f.debug("[DELETE-FILES] Proceeding with mixed delete operations",{totalOperations:T.length,fileDeletes:q.length,documentDeletes:R.length}),w=c.FileOperationPhase.RECORD_CREATION,s.incrementCounter("delete_files_phase",1,{phase:w})
|
|
1846
|
-
const{toolRecordId:H,localToolRecordId:U}=await c.createFileOperationRecords(C,
|
|
1846
|
+
const{toolRecordId:H,localToolRecordId:U}=await c.createFileOperationRecords(C,N,r.projectId,b.recordType,M,{totalOperations:T.length,fileOperations:q.length,documentOperations:R.length,filePaths:O.map(e=>e.normalized),documentIds:R.map(e=>e.target),userComment:S,hadUserMods:B.hadUserMods,userModsCommitHash:B.userModsCommitHash},d,v,f,s,b.metricsPrefix,i)
|
|
1847
1847
|
g=H,y=H,w=b.fileOperationPhase,s.incrementCounter("delete_files_phase",1,{phase:w})
|
|
1848
1848
|
const W=[]
|
|
1849
1849
|
let G=0
|
|
@@ -1942,13 +1942,13 @@ let M=null
|
|
|
1942
1942
|
R.length>0&&O&&(M=await l.validateWorkItemStates(C,R[0].path,o,i,n,y,m,I.metricsPrefix))
|
|
1943
1943
|
let D={taskPromoted:!1,requestPromoted:!1}
|
|
1944
1944
|
R.length>0&&O&&M&&(D=await l.autoPromoteTaskStatus(C,A.status,O,M.request?.status,R[0].path,o,i,y,m,I.metricsPrefix),D.taskPromoted&&y.info("Auto-promoted task to in_progress for insert-lines operation",{taskId:C,requestId:O,previousStatus:A.status,newStatus:D.newTaskStatus,triggeringOperation:"insert-lines"}))
|
|
1945
|
-
const
|
|
1946
|
-
if(!
|
|
1945
|
+
const N=await l.ensureCorrectWorkspaceContext({taskPromoted:D.taskPromoted,requestId:O,context:n,operation:"insert-lines",logger:y,metrics:m,metricsPrefix:I.metricsPrefix})
|
|
1946
|
+
if(!N.success)throw new Error(N.error)
|
|
1947
1947
|
if(R.length>0){y.debug("Re-validating file paths with correct workspace context",{operationCount:R.length,workspaceBasePath:n.workspaceManager?.getCurrentContext(n.currentSessionId).basePath})
|
|
1948
1948
|
for(let e=0;e<R.length;e++){const t=R[e],r=T[e],{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(r.path,n)
|
|
1949
1949
|
R[e]={...t,path:o,fullPath:s}}}if(j.length>0&&O)try{await(0,u.validateRequestStatusForDocumentOperation)(O,i,y,m,"insert-lines-document")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
|
|
1950
1950
|
throw e}k=l.FileOperationPhase.RECORD_CREATION,m.incrementCounter("insert_lines_phase",1,{phase:k})
|
|
1951
|
-
const
|
|
1951
|
+
const F=[...R.map(e=>({type:"file",path:e.path,insertions:e.insertions})),...j.map(e=>({type:"document",documentId:e.documentId,insertions:e.insertions}))],{toolRecordId:z}=await l.createFileOperationRecords(C,O,n.projectId,I.recordType,P,{operationCount:x.length,fileOperations:R.length,documentOperations:j.length,operations:F,userComment:S},o,s,y,m,I.metricsPrefix,g)
|
|
1952
1952
|
if(b=z,v=z,k=I.fileOperationPhase,m.incrementCounter("insert_lines_phase",1,{phase:k}),R.length>0&&n.git)try{y.debug("[INSERT-LINES] Checking for user changes before insert operations")
|
|
1953
1953
|
const e=await(0,h.commitUserChangesBeforeToolOperation)(n.git,"insert-lines",{requestId:O,taskId:C,workspaceManager:n.workspaceManager},y,m)
|
|
1954
1954
|
e.userChangesCommitted&&(y.debug("[INSERT-LINES] User changes committed before insert operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),y.info("User changes committed before insert-lines operation",{taskId:C,fileOperationsCount:R.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){y.debug("[INSERT-LINES] Pre-tool commit failed, continuing with insertions",{error:e instanceof Error?e.message:String(e)}),y.warn("Pre-tool user changes commit failed, continuing with insert operation",{taskId:C,fileOperationsCount:R.length,error:e instanceof Error?e.message:String(e)})}y.debug("[INSERT-LINES] Proceeding with file operations",{fileCount:R.length})
|
|
@@ -1963,7 +1963,7 @@ t.filename=s?.filename
|
|
|
1963
1963
|
const i=await c.default.readFile(o,"utf8"),{newContent:a,results:l}=await r(i,e.insertions)
|
|
1964
1964
|
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(j.length>0)try{for(const e of j){const t=L.find(t=>t.documentId===e.documentId)
|
|
1965
1965
|
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(C,R,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
|
|
1966
|
-
try{await s.updateToolRecord(v,{metadata:{path:R.length>0?R[0].path:`document:${j[0]?.documentId}`,status:J?"completed":"failed",error:J?void 0:`${K} operations failed`,totalOperations:L.length,successfulFiles:Q,failedFiles:Z,operations:
|
|
1966
|
+
try{await s.updateToolRecord(v,{metadata:{path:R.length>0?R[0].path:`document:${j[0]?.documentId}`,status:J?"completed":"failed",error:J?void 0:`${K} operations failed`,totalOperations:L.length,successfulFiles:Q,failedFiles:Z,operations:F,totalSuccessfulInsertions:Y,totalFailedInsertions:B,totalLinesAdded:H,operationsWithChanges:U,processingTime:Date.now()-w,userComment:S,...R.length>0&&W.success&&W.commitHash&&{commitHash:W.commitHash,previousCommitHash:W.previousCommitHash||void 0}},note:J?`Line insertion completed: ${V}/${L.length} operations successful${R.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"
|
|
1967
1967
|
if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
|
|
1968
1968
|
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]
|
|
1969
1969
|
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"
|
|
@@ -2158,25 +2158,25 @@ e.userChangesCommitted&&(g.debug("[MOVE-FILES] User changes committed before mov
|
|
|
2158
2158
|
const{toolRecordId:O,localToolRecordId:M}=await l.createFileOperationRecords(S,q,r.projectId,k.recordType,C[0].source,{operationCount:u.length,operations:C.map(e=>({source:e.source,destination:e.destination})),userComment:I,hadUserMods:A.hadUserMods,userModsCommitHash:A.userModsCommitHash},n,o,g,f,k.metricsPrefix,m)
|
|
2159
2159
|
w=O,b=O,v=k.fileOperationPhase,f.incrementCounter("move_files_phase",1,{phase:v})
|
|
2160
2160
|
const D=[]
|
|
2161
|
-
let
|
|
2161
|
+
let N=0
|
|
2162
2162
|
for(const e of C){const t={operation:{source:e.source,destination:e.destination},success:!1}
|
|
2163
2163
|
try{try{await a.default.access(e.fullSource)}catch(t){throw new Error(`Source path not accessible: ${e.source}`)}const r=(await a.default.stat(e.fullSource)).isDirectory()
|
|
2164
2164
|
t.isDirectory=r
|
|
2165
2165
|
const n=c.default.dirname(e.fullDestination)
|
|
2166
|
-
try{await a.default.mkdir(n,{recursive:!0})}catch(e){throw new Error(`Failed to create destination directory: ${n}`)}try{throw await a.default.access(e.fullDestination),new Error(`Destination already exists: ${e.destination}`)}catch(e){if(!(e instanceof Error&&e.message.includes("ENOENT")))throw e}await a.default.rename(e.fullSource,e.fullDestination),t.success=!0,
|
|
2167
|
-
f.recordValue("move_files_time",
|
|
2166
|
+
try{await a.default.mkdir(n,{recursive:!0})}catch(e){throw new Error(`Failed to create destination directory: ${n}`)}try{throw await a.default.access(e.fullDestination),new Error(`Destination already exists: ${e.destination}`)}catch(e){if(!(e instanceof Error&&e.message.includes("ENOENT")))throw e}await a.default.rename(e.fullSource,e.fullDestination),t.success=!0,N++,g.info("Successfully moved "+(r?"directory":"file"),{source:e.source,destination:e.destination,isDirectory:r})}catch(r){t.error=(0,p.getErrorMessage)(r),g.warn(`Failed to move ${e.source} to ${e.destination}`,{error:t.error})}D.push(t)}const F=Date.now()-y
|
|
2167
|
+
f.recordValue("move_files_time",F),f.incrementCounter("move_files_operations_total",u.length),f.incrementCounter("move_files_operations_success",N),f.incrementCounter("move_files_operations_failed",u.length-N),N===u.length?f.incrementCounter("move_files_success",1):N>0?f.incrementCounter("move_files_partial_success",1):f.incrementCounter("move_files_total_failure",1)
|
|
2168
2168
|
let z=null
|
|
2169
|
-
if(
|
|
2169
|
+
if(N>0){v=l.FileOperationPhase.GIT_COMMIT,f.incrementCounter("move_files_phase",1,{phase:v})
|
|
2170
2170
|
const e=D.filter(e=>e.success),t=1===e.length?`Move ${e[0].isDirectory?"directory":"file"} from ${e[0].operation.source} to ${e[0].operation.destination} - ${I}`:`Move ${e.length} ${1===e.length?"item":"items"} - ${I}`
|
|
2171
2171
|
z=await l.commitToGit(S,e.flatMap(e=>[e.operation.source,e.operation.destination]),O,M,t,r,g,f,k.metricsPrefix)}v=l.FileOperationPhase.RECORD_UPDATE_SUCCESS,f.incrementCounter("move_files_phase",1,{phase:v})
|
|
2172
2172
|
const L=D.filter(e=>e.success),B=D.filter(e=>!e.success)
|
|
2173
|
-
await l.updateFileOperationRecords(S,w,b,C[0].source,k.recordType,
|
|
2173
|
+
await l.updateFileOperationRecords(S,w,b,C[0].source,k.recordType,N===u.length?`Successfully moved all ${u.length} items`:`Moved ${N}/${u.length} items successfully`,{status:N===u.length?"completed":"partial_success",commitHash:z?.commitHash,previousCommitHash:z?.previousCommitHash,operationCount:u.length,successCount:N,failureCount:u.length-N,successfulOperations:L.map(e=>({source:e.operation.source,destination:e.operation.destination,isDirectory:e.isDirectory})),failedOperations:B.map(e=>({source:e.operation.source,destination:e.operation.destination,error:e.error})),userComment:I,hadUserMods:A.hadUserMods,userModsCommitHash:A.userModsCommitHash},N===u.length?`Successfully moved all ${u.length} items`:`Moved ${N}/${u.length} items successfully`,n,o,g,f,k.metricsPrefix)
|
|
2174
2174
|
const H=r.getCurrentBranchContext?await r.getCurrentBranchContext():{branch:null,requestId:null,isMain:!0}
|
|
2175
2175
|
let U=""
|
|
2176
|
-
if(
|
|
2177
|
-
else if(
|
|
2178
|
-
U=`Successfully moved ${
|
|
2179
|
-
return z?.commitHash&&(U+=`\nCommit: ${z.commitHash.substring(0,8)}`),H.requestId&&(U+=`\nBranch: ${H.requestId}`),{content:[{type:"text",text:U}],isError:0===
|
|
2176
|
+
if(N===u.length)U=`Successfully moved all ${u.length} ${1===u.length?"item":"items"}`
|
|
2177
|
+
else if(N>0){const e=D.filter(e=>!e.success)
|
|
2178
|
+
U=`Successfully moved ${N}/${u.length} items.\n\nFailed operations:\n`+e.map(e=>`\u2022 ${e.operation.source} \u2192 ${e.operation.destination}: ${e.error}`).join("\n")}else U="All move operations failed:\n"+D.map(e=>`\u2022 ${e.operation.source} \u2192 ${e.operation.destination}: ${e.error}`).join("\n")
|
|
2179
|
+
return z?.commitHash&&(U+=`\nCommit: ${z.commitHash.substring(0,8)}`),H.requestId&&(U+=`\nBranch: ${H.requestId}`),{content:[{type:"text",text:U}],isError:0===N}}catch(t){if(e.taskId&&o)try{const n=t instanceof d.NotFoundError||t instanceof d.ValidationError||t instanceof u.ZodError
|
|
2180
2180
|
await o.createToolRecord({...n?{}:{taskId:e.taskId},projectId:r.projectId,type:"move-files",metadata:{status:"failed",timestamp:(new Date).toISOString(),operationCount:e.operations?.length||0,operations:e.operations||[],userComment:e.comment,error:(0,p.getErrorMessage)(t),errorDetails:t instanceof Error?t.stack:String(t),phase:v,attemptedTaskId:e.taskId,taskIdValidationFailed:n},note:`Failed to move files - ${(0,p.getErrorMessage)(t)}`})}catch(t){g.error("Failed to create failure record for move-files operation",{error:t instanceof Error?t.message:String(t),taskId:e.taskId,operationCount:e.operations?.length||0})}const s=Date.now()-y
|
|
2181
2181
|
if(f.recordValue("move_files_time",s),f.incrementCounter("move_files_errors",1),g.error("Error in move-files operation:",{error:(0,p.getErrorMessage)(t),time:s}),(w||b)&&e.taskId){v=l.FileOperationPhase.RECORD_UPDATE_FAILURE
|
|
2182
2182
|
try{await l.updateFileOperationRecords(e.taskId,w,b,e.operations?.[0]?.source||"unknown",k.recordType,"Failed to move files",{status:"failed",error:(0,p.getErrorMessage)(t),operationCount:e.operations?.length||0,operations:e.operations||[],userComment:e.comment},"Failed to move files",n,o,g,f,k.metricsPrefix)}catch(t){g.error("Failed to update record with failure status",{taskId:e.taskId,recordId:w,error:t instanceof Error?t.message:String(t)})}}return l.handleFileOperationError(t,v,g,f,e,k)}}(...e))}
|
|
@@ -2281,27 +2281,27 @@ const A=q[0],O=A.documentId?`document:${A.documentId}`:A.path
|
|
|
2281
2281
|
let M=Date.now()
|
|
2282
2282
|
const D=await h.validateTaskId(T,O,s,I,l,"replace_in_files")
|
|
2283
2283
|
Date.now()
|
|
2284
|
-
const
|
|
2284
|
+
const N=D?.requestId,F=q.some(e=>e.path)
|
|
2285
2285
|
let z=!1
|
|
2286
|
-
if(N
|
|
2286
|
+
if(F&&N){M=Date.now()
|
|
2287
2287
|
const e=await h.validateWorkItemStates(T,O,s,a,r,I,l,"replace_in_files")
|
|
2288
2288
|
Date.now(),M=Date.now()
|
|
2289
|
-
const t=await h.autoPromoteTaskStatus(T,D.status,
|
|
2290
|
-
t.taskPromoted&&I.info("Auto-promoted task to in_progress",{taskId:T,requestId:
|
|
2291
|
-
const n=await h.ensureCorrectWorkspaceContext({taskPromoted:t.taskPromoted,requestId:
|
|
2289
|
+
const t=await h.autoPromoteTaskStatus(T,D.status,N,e.request?.status,O,s,a,I,l,"replace_in_files")
|
|
2290
|
+
t.taskPromoted&&I.info("Auto-promoted task to in_progress",{taskId:T,requestId:N,previousStatus:D.status,newStatus:t.newTaskStatus}),Date.now(),M=Date.now()
|
|
2291
|
+
const n=await h.ensureCorrectWorkspaceContext({taskPromoted:t.taskPromoted,requestId:N,context:r,operation:"replace_in_files",logger:I,metrics:l,metricsPrefix:"replace_in_files"})
|
|
2292
2292
|
if(!n.success)throw new Error(n.error)
|
|
2293
|
-
Date.now(),z=!0}if(P.length>0&&
|
|
2293
|
+
Date.now(),z=!0}if(P.length>0&&N)try{await(0,m.validateRequestStatusForDocumentOperation)(N,a,I,l,"replace_in_files-documents")}catch(e){if(e instanceof f.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
|
|
2294
2294
|
throw e}M=Date.now()
|
|
2295
2295
|
const L=new Map
|
|
2296
|
-
if(I.info("\u{1f50d} Starting external edit detection",{hasFileOperations:
|
|
2296
|
+
if(I.info("\u{1f50d} Starting external edit detection",{hasFileOperations:F,hasGit:!!r.git,operationCount:q.length}),F&&r.git)for(const e of q)if(e.path&&!e.documentId)try{const t=h.validateAndNormalizePath(e.path,r),n=await d.default.readFile(t.fullPath,"utf8"),o=void 0!==e.fuzzy_match?e.fuzzy_match:R,s=[]
|
|
2297
2297
|
for(const t of e.replacements){const r=(0,p.findMatches)(n,t.old_str,o,100,e.path)
|
|
2298
2298
|
s.push(...r)}if(s.length>0){const o=(0,w.calculateReplacementRegion)(n,s,t.fullPath)
|
|
2299
2299
|
I.info("\u{1f50d} Checking for external edits",{path:t.normalizedPath,affectedLines:o.lineRanges.map(e=>`${e.start}-${e.end}`).join(", "),matchCount:s.length})
|
|
2300
2300
|
const i=await(0,b.detectExternalEdits)(r.git,o,I)
|
|
2301
|
-
I.info("\u{1f50d} External edit detection result",{path:t.normalizedPath,hasExternalEdits:i.hasExternalEdits,conflictingLines:i.conflictingLines,hasMessage:!!i.message}),i.hasExternalEdits&&i.message?(L.set(e.path,{message:i.message,debugInfo:i.debugInfo}),I.warn("\u26a0\ufe0f External edits detected before auto-commit",{path:t.normalizedPath,conflictingLines:i.conflictingLines})):(i.debugInfo&&L.set(e.path,{message:"",debugInfo:i.debugInfo}),I.info("\u2705 No external edit conflicts detected",{path:t.normalizedPath}))}else I.info("\u2139\ufe0f No matches found for external edit detection",{path:t.normalizedPath}),L.set(e.path,{message:"",debugInfo:{detectionRan:!1,reason:"No matches found - patterns not found in file",affectedRegion:"none",uncommittedChanges:"not checked",overlapDetected:!1}})}catch(t){I.warn("Failed to check for external edits before auto-commit",{path:e.path,error:(0,v.getErrorMessage)(t)})}if(Date.now(),M=Date.now(),
|
|
2302
|
-
const e=await(0,g.commitUserChangesBeforeToolOperation)(r.git,"replace_in_files",{requestId:
|
|
2301
|
+
I.info("\u{1f50d} External edit detection result",{path:t.normalizedPath,hasExternalEdits:i.hasExternalEdits,conflictingLines:i.conflictingLines,hasMessage:!!i.message}),i.hasExternalEdits&&i.message?(L.set(e.path,{message:i.message,debugInfo:i.debugInfo}),I.warn("\u26a0\ufe0f External edits detected before auto-commit",{path:t.normalizedPath,conflictingLines:i.conflictingLines})):(i.debugInfo&&L.set(e.path,{message:"",debugInfo:i.debugInfo}),I.info("\u2705 No external edit conflicts detected",{path:t.normalizedPath}))}else I.info("\u2139\ufe0f No matches found for external edit detection",{path:t.normalizedPath}),L.set(e.path,{message:"",debugInfo:{detectionRan:!1,reason:"No matches found - patterns not found in file",affectedRegion:"none",uncommittedChanges:"not checked",overlapDetected:!1}})}catch(t){I.warn("Failed to check for external edits before auto-commit",{path:e.path,error:(0,v.getErrorMessage)(t)})}if(Date.now(),M=Date.now(),F&&r.git)try{I.debug("[REPLACE-IN-FILES] Checking for user changes before batch operation")
|
|
2302
|
+
const e=await(0,g.commitUserChangesBeforeToolOperation)(r.git,"replace_in_files",{requestId:N,taskId:T,workspaceManager:r.workspaceManager},I,l)
|
|
2303
2303
|
e.userChangesCommitted&&(I.debug("[REPLACE-IN-FILES] User changes committed",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),I.info("User changes committed before batch operation",{taskId:T,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){I.warn("Pre-tool user changes commit failed",{taskId:T,error:e instanceof Error?e.message:String(e)})}Date.now(),M=Date.now(),S=h.FileOperationPhase.RECORD_CREATION,l.incrementCounter("replace_in_files_phase",1,{phase:S})
|
|
2304
|
-
const{toolRecordId:B}=await h.createFileOperationRecords(T,
|
|
2304
|
+
const{toolRecordId:B}=await h.createFileOperationRecords(T,N,r.projectId,"replace-in-files",O,{operationCount:q.length,totalReplacements:q.reduce((e,t)=>e+t.replacements.length,0),globalFuzzyMatch:R,userComment:j},s,i,I,l,"replace_in_files",k)
|
|
2305
2305
|
x=B,Date.now(),M=Date.now(),S=h.FileOperationPhase.STRING_REPLACEMENT,l.incrementCounter("replace_in_files_phase",1,{phase:S})
|
|
2306
2306
|
const H=[],U=[]
|
|
2307
2307
|
for(const e of q){const t=e.path?L.get(e.path):void 0,o=await n(e,R,r,0,0,t?.message,t?.debugInfo)
|
|
@@ -2361,10 +2361,10 @@ throw e}}I=l.FileOperationPhase.TASK_VALIDATION,m.incrementCounter("replace_line
|
|
|
2361
2361
|
const A=j.length>0?j[0].path:"document-operation",O=await l.validateTaskId(T,A,o,w,m,$.metricsPrefix),M=O?.requestId
|
|
2362
2362
|
let D=null
|
|
2363
2363
|
j.length>0&&M&&(D=await l.validateWorkItemStates(T,j[0].path,o,i,n,w,m,$.metricsPrefix))
|
|
2364
|
-
let
|
|
2365
|
-
j.length>0&&M&&D&&(
|
|
2366
|
-
const
|
|
2367
|
-
if(!
|
|
2364
|
+
let N={taskPromoted:!1,requestPromoted:!1}
|
|
2365
|
+
j.length>0&&M&&D&&(N=await l.autoPromoteTaskStatus(T,O.status,M,D.request?.status,j[0].path,o,i,w,m,$.metricsPrefix),N.taskPromoted&&w.info("Auto-promoted task to in_progress for replace-lines operation",{taskId:T,requestId:M,previousStatus:O.status,newStatus:N.newTaskStatus,triggeringOperation:"replace-lines"}))
|
|
2366
|
+
const F=await l.ensureCorrectWorkspaceContext({taskPromoted:N.taskPromoted,requestId:M,context:n,operation:"replace-lines",logger:w,metrics:m,metricsPrefix:$.metricsPrefix})
|
|
2367
|
+
if(!F.success)throw new Error(F.error)
|
|
2368
2368
|
if(j.length>0){w.debug("Re-validating file paths with correct workspace context",{operationCount:j.length,workspaceBasePath:n.workspaceManager?.getCurrentContext(n.currentSessionId).basePath})
|
|
2369
2369
|
for(let e=0;e<j.length;e++){const t=j[e],r=q[e],{normalizedPath:o,fullPath:s}=l.validateAndNormalizePath(r.path,n)
|
|
2370
2370
|
j[e]={...t,path:o,fullPath:s}}}if(P.length>0&&M)try{await(0,u.validateRequestStatusForDocumentOperation)(M,i,w,m,"replace-lines-document")}catch(e){if(e instanceof d.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
|
|
@@ -2435,13 +2435,13 @@ let M=null
|
|
|
2435
2435
|
R.length>0&&O&&(M=await d.validateWorkItemStates(C,R[0].path,o,i,r,y,c,I.metricsPrefix))
|
|
2436
2436
|
let D={taskPromoted:!1,requestPromoted:!1}
|
|
2437
2437
|
R.length>0&&O&&M&&(D=await d.autoPromoteTaskStatus(C,A.status,O,M.request?.status,R[0].path,o,i,y,c,I.metricsPrefix),D.taskPromoted&&y.info("Auto-promoted task to in_progress for replace-strings operation",{taskId:C,requestId:O,previousStatus:A.status,newStatus:D.newTaskStatus,triggeringOperation:"replace-strings"}))
|
|
2438
|
-
const
|
|
2439
|
-
if(!
|
|
2438
|
+
const N=await d.ensureCorrectWorkspaceContext({taskPromoted:D.taskPromoted,requestId:O,context:r,operation:"replace-strings",logger:y,metrics:c,metricsPrefix:I.metricsPrefix})
|
|
2439
|
+
if(!N.success)throw new Error(N.error)
|
|
2440
2440
|
if(R.length>0){y.debug("Re-validating file paths with correct workspace context",{operationCount:R.length,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})
|
|
2441
2441
|
for(let e=0;e<R.length;e++){const t=R[e],n=T[e],{normalizedPath:o,fullPath:s}=d.validateAndNormalizePath(n.path,r)
|
|
2442
2442
|
R[e]={...t,path:o,fullPath:s}}}if(j.length>0&&O)try{await(0,h.validateRequestStatusForDocumentOperation)(O,i,y,c,"replace-strings-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
|
|
2443
2443
|
throw e}k=d.FileOperationPhase.RECORD_CREATION,c.incrementCounter("replace_strings_phase",1,{phase:k})
|
|
2444
|
-
const
|
|
2444
|
+
const F=[...R.map(e=>({type:"file",path:e.path,replacements:e.replacements,mode:e.mode})),...j.map(e=>({type:"document",documentId:e.documentId,replacements:e.replacements,mode:e.mode}))],{toolRecordId:z}=await d.createFileOperationRecords(C,O,r.projectId,I.recordType,P,{operationCount:x.length,fileOperations:R.length,documentOperations:j.length,operations:F,userComment:S},o,s,y,c,I.metricsPrefix,g)
|
|
2445
2445
|
if(b=z,v=z,k=I.fileOperationPhase,c.incrementCounter("replace_strings_phase",1,{phase:k}),R.length>0&&r.git)try{y.debug("[REPLACE-STRINGS] Checking for user changes before replace operations")
|
|
2446
2446
|
const e=await(0,p.commitUserChangesBeforeToolOperation)(r.git,"replace-strings",{requestId:O,taskId:C,workspaceManager:r.workspaceManager},y,c)
|
|
2447
2447
|
e.userChangesCommitted&&(y.debug("[REPLACE-STRINGS] User changes committed before replace operations",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),y.info("User changes committed before replace-strings operation",{taskId:C,fileOperationsCount:R.length,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){y.debug("[REPLACE-STRINGS] Pre-tool commit failed, continuing with replacements",{error:e instanceof Error?e.message:String(e)}),y.warn("Pre-tool user changes commit failed, continuing with replace operation",{taskId:C,fileOperationsCount:R.length,error:e instanceof Error?e.message:String(e)})}y.debug("[REPLACE-STRINGS] Proceeding with file operations",{fileCount:R.length})
|
|
@@ -2456,7 +2456,7 @@ t.filename=s?.filename
|
|
|
2456
2456
|
const i=await l.default.readFile(o,"utf8"),{newContent:a,results:c}=await n(i,e.replacements,e.mode)
|
|
2457
2457
|
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(j.length>0)try{for(const e of j){const t=L.find(t=>t.documentId===e.documentId)
|
|
2458
2458
|
t?.success&&await r.documentFileManager.syncDocument(e.documentId)}}catch(e){y.warn("Some documents may not have synced properly",{error:e})}const U=await d.handleMultiFileGitCommit(C,R,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
|
|
2459
|
-
try{await s.updateToolRecord(v,{metadata:{path:R.length>0?R[0].path:`document:${j[0]?.documentId}`,status:Y?"completed":"failed",error:Y?void 0:`${V} operations failed`,totalOperations:L.length,successfulFiles:J,failedFiles:Q,operations:
|
|
2459
|
+
try{await s.updateToolRecord(v,{metadata:{path:R.length>0?R[0].path:`document:${j[0]?.documentId}`,status:Y?"completed":"failed",error:Y?void 0:`${V} operations failed`,totalOperations:L.length,successfulFiles:J,failedFiles:Q,operations:F,totalSuccessfulReplacements:K,totalFailedReplacements:B,operationsWithChanges:H,processingTime:Date.now()-w,userComment:S,...R.length>0&&U.success&&U.commitHash&&{commitHash:U.commitHash,previousCommitHash:U.previousCommitHash||void 0}},note:Y?`String replacement completed: ${G}/${L.length} operations successful${R.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,N)
|
|
2460
2460
|
let Z="## String Replacement Results\n\n"
|
|
2461
2461
|
if(r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
|
|
2462
2462
|
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]
|
|
@@ -2536,27 +2536,27 @@ const T={toolName:"write_file",operationName:"write",fileOperationPhase:d.FileOp
|
|
|
2536
2536
|
try{const n=e
|
|
2537
2537
|
let{path:q,documentId:R,content:j,format:P,append:A,comment:O,fromResult:M}=n
|
|
2538
2538
|
Array.isArray(j)&&(j=j.join("\n"))
|
|
2539
|
-
const{enforceActiveTask:D}=await Promise.resolve().then(()=>t(43451)),
|
|
2540
|
-
if("errorResult"in
|
|
2541
|
-
const{taskId:
|
|
2539
|
+
const{enforceActiveTask:D}=await Promise.resolve().then(()=>t(43451)),N=await D(r,o,w,e.taskId)
|
|
2540
|
+
if("errorResult"in N)return N.errorResult
|
|
2541
|
+
const{taskId:F}=N
|
|
2542
2542
|
let z,L
|
|
2543
2543
|
if(void 0!==M){if(!q)throw new u.ValidationError("`fromResult` writes a file: pass it with `path`, not `documentId`.")
|
|
2544
2544
|
if(void 0!==j||A)throw new u.ValidationError("`fromResult` takes the place of `content`: pass one or the other, without `append`.")
|
|
2545
2545
|
const e=await(0,y.readResultBytes)(r.mainBasePath,M,20971520)
|
|
2546
2546
|
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)
|
|
2547
2547
|
z=e.bytes,L=e.address}const B=z?z.length:"string"==typeof j?j.length:JSON.stringify(j??"").length,H=!!R,U=!!q
|
|
2548
|
-
$.info("Writing content to "+(H?"document":"file"),{taskId:
|
|
2548
|
+
$.info("Writing content to "+(H?"document":"file"),{taskId:F,filePath:U?q:void 0,documentId:H?R:void 0,contentLength:B,fromResult:L,format:P,append:A,comment:O})
|
|
2549
2549
|
let W,G,V,K="",Y=!1,J=!1
|
|
2550
2550
|
const Q=U?q:void 0
|
|
2551
2551
|
if(H){if(!r.documentFileManager)throw new Error("Document file manager not available")
|
|
2552
2552
|
K=await r.documentFileManager.getDocumentAsFile(R)
|
|
2553
2553
|
const e=r.documentFileManager.getCachedDocumentInfo(R)
|
|
2554
2554
|
V=e?.filename,J=!0,Y=!1}C=d.FileOperationPhase.TASK_VALIDATION,k.incrementCounter("write_file_phase",1,{phase:C})
|
|
2555
|
-
const Z=U?Q:`document:${R}`,X=await d.validateTaskId(
|
|
2555
|
+
const Z=U?Q:`document:${R}`,X=await d.validateTaskId(F,Z,o,$,k,T.metricsPrefix),ee=X?.requestId
|
|
2556
2556
|
let te=null
|
|
2557
|
-
U&&ee&&(te=await d.validateWorkItemStates(
|
|
2557
|
+
U&&ee&&(te=await d.validateWorkItemStates(F,Z,o,i,r,$,k,T.metricsPrefix))
|
|
2558
2558
|
let re={taskPromoted:!1,requestPromoted:!1}
|
|
2559
|
-
U&&ee&&te&&(re=await d.autoPromoteTaskStatus(
|
|
2559
|
+
U&&ee&&te&&(re=await d.autoPromoteTaskStatus(F,X.status,ee,te.request?.status,Z,o,i,$,k,T.metricsPrefix),re.taskPromoted&&$.info("Auto-promoted task to in_progress for file operation",{taskId:F,requestId:ee,previousStatus:X.status,newStatus:re.newTaskStatus,triggeringOperation:"write_file"}))
|
|
2560
2560
|
const ne=await d.ensureCorrectWorkspaceContext({taskPromoted:re.taskPromoted,requestId:ee,context:r,operation:"write_file",logger:$,metrics:k,metricsPrefix:T.metricsPrefix,requireRequestContext:U})
|
|
2561
2561
|
if(!ne.success)throw new Error(ne.error)
|
|
2562
2562
|
if(ne.switched&&$.info("Workspace context successfully established",{requestId:ee,branchName:ne.branchName,operation:"write_file"}),U&&Q){const e=d.validateAndNormalizePath(Q,r)
|
|
@@ -2567,22 +2567,22 @@ if(J&&!A){const e=r.workspaceManager?.getCurrentContext(r.currentSessionId)
|
|
|
2567
2567
|
if(e?.requestId&&e.basePath){const t=await(0,b.staleWriteProblem)({sessionId:r.currentSessionId,worktree:e.basePath,fullPath:G,relPath:W,defaultBranch:await(0,v.resolveDefaultBranch)(r.basePath)})
|
|
2568
2568
|
if(t)throw new u.ValidationError(`${W} was not written: ${t}`)}}$.debug("Path validated with correct workspace context",{rawPath:Q,normalizedPath:W,fullPath:G,workspaceBasePath:r.workspaceManager?.getCurrentContext(r.currentSessionId).basePath})}if(H&&ee)try{await(0,h.validateRequestStatusForDocumentOperation)(ee,i,$,k,"write_file-document")}catch(e){if(e instanceof u.ValidationError)return{content:[{type:"text",text:e.message}],isError:!0}
|
|
2569
2569
|
throw e}C=d.FileOperationPhase.RECORD_CREATION,k.incrementCounter("write_file_phase",1,{phase:C})
|
|
2570
|
-
const{toolRecordId:oe}=await d.createFileOperationRecords(
|
|
2570
|
+
const{toolRecordId:oe}=await d.createFileOperationRecords(F,ee,r.projectId,T.recordType,Z,{operationType:H?"document":"file",documentId:H?R:void 0,documentFilename:V,filePath:U?W:void 0,contentLength:B,...L?{fromResult:L}:{},format:P||"text",append:A||!1,wasFileCreated:!J,userComment:O},o,s,$,k,T.metricsPrefix,I)
|
|
2571
2571
|
E=oe,_=oe,C=T.fileOperationPhase,k.incrementCounter("write_file_phase",1,{phase:C})
|
|
2572
2572
|
let se=""
|
|
2573
2573
|
if(z);else if("json"===P)if("string"==typeof j)try{JSON.parse(j),se=j}catch(e){throw new u.ValidationError("Content is not valid JSON")}else se=JSON.stringify(j,null,2)
|
|
2574
2574
|
else se="string"==typeof j?j:JSON.stringify(j,null,2)
|
|
2575
2575
|
let ie=se
|
|
2576
2576
|
if(U&&r.git)try{$.debug("[WRITE-FILE] Checking for user changes before write operation")
|
|
2577
|
-
const e=await(0,p.commitUserChangesBeforeToolOperation)(r.git,"write_file",{requestId:r.workspaceManager?.getCurrentContext(r.currentSessionId)?.requestId,taskId:
|
|
2578
|
-
e.userChangesCommitted&&($.debug("[WRITE-FILE] User changes committed before write operation",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),$.info("User changes committed before write-file operation",{taskId:
|
|
2577
|
+
const e=await(0,p.commitUserChangesBeforeToolOperation)(r.git,"write_file",{requestId:r.workspaceManager?.getCurrentContext(r.currentSessionId)?.requestId,taskId:F},$,k)
|
|
2578
|
+
e.userChangesCommitted&&($.debug("[WRITE-FILE] User changes committed before write operation",{commitHash:e.commitHash?.substring(0,8),filesCommitted:e.filesCommitted}),$.info("User changes committed before write-file operation",{taskId:F,path:W,userCommitHash:e.commitHash?.substring(0,8),userFilesCommitted:e.filesCommitted}))}catch(e){$.debug("[WRITE-FILE] Pre-tool commit failed, continuing with write",{error:e instanceof Error?e.message:String(e)}),$.warn("Pre-tool user changes commit failed, continuing with write operation",{taskId:F,path:W,error:e instanceof Error?e.message:String(e)})}if(!K)throw new Error("Target path not properly initialized")
|
|
2579
2579
|
if($.debug("[WRITE-FILE] Proceeding with file write operation",{targetPath:K}),A&&J)try{ie=await c.default.readFile(K,"utf8")+se}catch(e){$.warn("Failed to read existing content for append",{error:e})}const ae=a.default.dirname(K)
|
|
2580
2580
|
await c.default.mkdir(ae,{recursive:!0}),z?await c.default.writeFile(K,z):await c.default.writeFile(K,ie,"utf8")
|
|
2581
2581
|
const ce=z?z.length:ie.length,le=!(!U||z||A)&&await(0,f.markExecutableIfShebang)(K,ie)
|
|
2582
2582
|
if($.info("Content written successfully",{path:U?W:`document:${R}`,finalLength:ce,append:A,wasCreated:Y}),H){r.documentFileManager.markForSync(R)
|
|
2583
2583
|
try{await r.documentFileManager.syncDocument(R),$.info("Document synced successfully",{documentId:R})}catch(e){$.warn("Failed to sync document immediately",{documentId:R,error:e})}}let de,ue,he=null,pe=null,fe=!1
|
|
2584
2584
|
if(U){if(C=d.FileOperationPhase.GIT_COMMIT,k.incrementCounter("write_file_phase",1,{phase:C}),r.workspaceManager){const e=r.workspaceManager.getCurrentContext(r.currentSessionId)
|
|
2585
|
-
ue=e.branchName,fe=e.permanentBranch,$.debug("Retrieved branch name from workspace manager",{branchName:ue,requestId:e.requestId,mode:e.mode,isMainBranch:e.isMainBranch})}try{const e=`${Y?"Create":A?"Append to":"Update"} ${W} (${_})`,t=await d.commitToGit(
|
|
2585
|
+
ue=e.branchName,fe=e.permanentBranch,$.debug("Retrieved branch name from workspace manager",{branchName:ue,requestId:e.requestId,mode:e.mode,isMainBranch:e.isMainBranch})}try{const e=`${Y?"Create":A?"Append to":"Update"} ${W} (${_})`,t=await d.commitToGit(F,W,_,_,e,r,$,k,T.metricsPrefix)
|
|
2586
2586
|
de=t.commitHash,pe=t.previousCommitHash,$.info("Changes committed to git via shared utility",{path:W,commitMessage:e,commitHash:de?.substring(0,8),previousCommitHash:pe?.substring(0,8),branchName:ue,toolRecordId:_})}catch(e){$.warn("Git operations failed via shared utility",{error:e}),he=(0,m.getErrorMessage)(e)}}try{await s.updateToolRecord(_,{metadata:{path:U?W:`document:${R}`,status:"completed",finalContentLength:ce,...L?{fromResult:L}:{},wasFileCreated:Y,wasAppended:A&&J,operationType:H?"document":"file",processingTime:Date.now()-S,userComment:O,...U&&de&&{commitHash:de,previousCommitHash:pe||void 0},...U&&he?{commitFailed:!0,commitFailedReason:he}:null},note:H?"Write operation completed successfully (document synced)":he?"Write landed on disk but the git commit FAILED -- the change is uncommitted":"Write operation completed successfully (committed to git)"})}catch(e){$.warn("Failed to update tool record",{recordError:e})}if(U&&G&&de){const e=r.workspaceManager?.getCurrentContext(r.currentSessionId)
|
|
2587
2587
|
e?.requestId&&await(0,b.noteFileSeen)(r.currentSessionId,e.basePath,G)}let me=`**${Y?"Created":A?"Appended to":"Updated"} ${H?`document ${R}${V?` (${V})`:""}`:`file ${W}`}**\n`
|
|
2588
2588
|
me+=L?`**From result:** ${L.slice(0,12)} (${ce.toLocaleString()} bytes, unchanged)\n`:`**Content Length:** ${ie.length} characters\n`,U&&ue&&(fe||(me+=`**Branch:** ${ue}\n`)),le&&(me+="**Mode:** executable (100755), because it starts with #!\n"),U&&he&&(me+=`\u26a0\ufe0f **NOT COMMITTED:** the file is written on disk but the git commit failed (${he}). Do not complete this work until the change is committed.\n`),U&&de&&(me+=`**Commit Hash:** ${de.substring(0,8)}\n`,pe&&(me+=`**Previous Commit:** ${pe.substring(0,8)}\n`)),H&&(me+=`\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 \`${R}\`.`)
|
|
@@ -2980,47 +2980,47 @@ const r=x.get(t)
|
|
|
2980
2980
|
r&&r.push({filePath:t,recordId:e.id,type:e.type,timestamp:e.createdAt,commitHash:e.metadata.commitHash,status:e.metadata.status||"",record:e})}),s.info(`Grouped records by file path. Found ${x.size} unique files.`)
|
|
2981
2981
|
for(const[M,D]of x.entries())D.sort((e,t)=>new Date(e.timestamp).getTime()-new Date(t.timestamp).getTime()),s.info(`File ${M} has ${D.length} records.`),s.info(`Records with commit hashes: ${D.filter(e=>e.commitHash).length}`)
|
|
2982
2982
|
const S=new Map
|
|
2983
|
-
for(const[F
|
|
2983
|
+
for(const[N,F]of x.entries()){s.info(`Processing file: ${N} (${F.length} records)`)
|
|
2984
2984
|
const z=new Map
|
|
2985
|
-
|
|
2986
|
-
const L=
|
|
2987
|
-
if(s.info(`Found ${L.length} records with commit hash for ${
|
|
2988
|
-
try{if(t.git){const Y={file:
|
|
2989
|
-
if(J&&J.all&&J.all.length>0){s.info(`Found ${J.all.length} commits for ${
|
|
2990
|
-
const Q=J.all.map(e=>({filePath:
|
|
2985
|
+
F.forEach(e=>{e.recordId&&e.record?.metadata?.status&&(z.has(e.recordId)||z.set(e.recordId,[]),z.get(e.recordId)?.push(e.record.metadata.status))}),z.forEach((e,t)=>{s.info(`Record ${t} status updates: ${e.join(" -> ")}`)})
|
|
2986
|
+
const L=F.filter(e=>e.commitHash)
|
|
2987
|
+
if(s.info(`Found ${L.length} records with commit hash for ${N}`),0===L.length){s.info(`No commit hashes found in record metadata for ${N}, trying git log`)
|
|
2988
|
+
try{if(t.git){const Y={file:N,n:10},J=await t.git.log(Y)
|
|
2989
|
+
if(J&&J.all&&J.all.length>0){s.info(`Found ${J.all.length} commits for ${N} using git log`)
|
|
2990
|
+
const Q=J.all.map(e=>({filePath:N,recordId:"git-"+e.hash.substring(0,8),type:"git-commit",timestamp:new Date(e.date).toISOString(),commitHash:e.hash,status:"completed",record:{id:"git-"+e.hash.substring(0,8),type:"git-commit",content:e.message,createdAt:new Date(e.date).toISOString(),metadata:{commitHash:e.hash}}}))
|
|
2991
2991
|
if(Q.length>0){const Z=Q.length>1?Q[Q.length-1]:Q[0],X=Q[0],ee=1===Q.length
|
|
2992
|
-
s.info(ee?`Using single git commit for ${
|
|
2992
|
+
s.info(ee?`Using single git commit for ${N}: ${X.commitHash?X.commitHash.substring(0,8):"unknown"}`:`Using git commits for ${N}: ${Z.commitHash?Z.commitHash.substring(0,8):"unknown"} to ${X.commitHash?X.commitHash.substring(0,8):"unknown"}`)
|
|
2993
2993
|
let te=""
|
|
2994
|
-
try{if(ee)try{const re=[`${X.commitHash}:${
|
|
2995
|
-
ne&&ne.trim().length>0?(te=ne.split("\n").map(e=>`+${e}`).join("\n"),S.set(
|
|
2996
|
-
te=await t.git.diff(se),S.set(
|
|
2994
|
+
try{if(ee)try{const re=[`${X.commitHash}:${N}`],ne=await t.git.show(re)
|
|
2995
|
+
ne&&ne.trim().length>0?(te=ne.split("\n").map(e=>`+${e}`).join("\n"),S.set(N,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:te})):S.set(N,{firstChange:Z,lastChange:X,hasDiff:!1,diffText:"File exists in git history but has no content"})}catch(oe){s.warn(`Error getting file content for ${N}`,{error:oe instanceof Error?oe.message:String(oe)}),S.set(N,{firstChange:Z,lastChange:X,hasDiff:!1,diffText:"Error getting file content from git"})}else{const se=[`${Z.commitHash}..${X.commitHash}`,"--",N]
|
|
2996
|
+
te=await t.git.diff(se),S.set(N,{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 ${N}`,{error:ie instanceof Error?ie.message:String(ie)}),S.set(N,{firstChange:Z,lastChange:X,hasDiff:!0,diffText:"Error generating diff, but git history is available"})}continue}}else s.info(`No git history found for ${N}`)}}catch(ae){s.warn(`Error getting git history for ${N}`,{error:(0,u.getErrorMessage)(ae)})}S.set(N,{firstChange:F[0],lastChange:F[F.length-1],hasDiff:!1,diffText:"No commit hashes available for this file"})
|
|
2997
2997
|
continue}const B=L[0],H=L[L.length-1]
|
|
2998
2998
|
s.info(`First commit: ${B.commitHash?B.commitHash:"unknown"}, Last commit: ${H.commitHash?H.commitHash:"unknown"}`)
|
|
2999
2999
|
let U=null,W=!1
|
|
3000
|
-
if(t.git&&B.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,
|
|
3001
|
-
if(s.info(`Using ${W?"pre-task":"first task"} commit as baseline for ${
|
|
3002
|
-
s.info(`Getting new file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${
|
|
3000
|
+
if(t.git&&B.commitHash)try{U=await(0,c.findPreviousCommit)(t.git,N,B.commitHash),U?(W=!0,s.info(`Found previous commit: ${U} for ${N}`)):s.info(`No previous commit found for ${N}, likely a new file`)}catch(ce){s.warn(`Error finding previous commit for ${N}`,{error:(0,u.getErrorMessage)(ce)})}const G=W&&U?{...B,commitHash:U,status:"pre-task"}:B
|
|
3001
|
+
if(s.info(`Using ${W?"pre-task":"first task"} commit as baseline for ${N}: ${G.commitHash}`),s.info(`Last commit for ${N}: ${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}:${N}`]
|
|
3002
|
+
s.info(`Getting new file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${N}`)
|
|
3003
3003
|
const de=await t.git.show(le)
|
|
3004
3004
|
if(de&&de.trim().length>0){let ue=""
|
|
3005
3005
|
const he=de.split("\n")
|
|
3006
3006
|
for(const pe of he)ue+=`+${pe}\n`
|
|
3007
|
-
S.set(
|
|
3008
|
-
continue}}catch(fe){s.warn(`Error getting new file content for ${
|
|
3009
|
-
continue}if(B.commitHash===H.commitHash&&W){if(U===B.commitHash){S.set(
|
|
3007
|
+
S.set(N,{firstChange:G,lastChange:H,hasDiff:!0,diffText:ue}),s.info(`Formatted new file content as diff, ${he.length} lines`)
|
|
3008
|
+
continue}}catch(fe){s.warn(`Error getting new file content for ${N}`,{error:(0,u.getErrorMessage)(fe)})}S.set(N,{firstChange:G,lastChange:H,hasDiff:!1,diffText:"Only one commit available for this file"})
|
|
3009
|
+
continue}if(B.commitHash===H.commitHash&&W){if(U===B.commitHash){S.set(N,{firstChange:G,lastChange:H,hasDiff:!1,diffText:"No changes detected"})
|
|
3010
3010
|
continue}s.info("Special case: First and last task commits are the same, but pre-task commit is different")}let V="",K=!1
|
|
3011
|
-
if(m||"pre-task"!==G.status){try{if(t.git){try{await t.git.show([`${H.commitHash}:${
|
|
3011
|
+
if(m||"pre-task"!==G.status){try{if(t.git){try{await t.git.show([`${H.commitHash}:${N}`])}catch(ge){s.warn(`File ${N} does not exist in commit ${H.commitHash}, might be deleted or moved`),V="File was deleted or moved from this location during the task",K=!0,S.set(N,{firstChange:G,lastChange:H,hasDiff:K,diffText:V})
|
|
3012
3012
|
continue}let me
|
|
3013
|
-
if("pre-task"===G.status?(me=[`${G.commitHash}..${H.commitHash}`,"--",
|
|
3014
|
-
try{const ye=await t.git.show([`${G.commitHash}:${
|
|
3013
|
+
if("pre-task"===G.status?(me=[`${G.commitHash}..${H.commitHash}`,"--",N],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 ${N}`)):(me=[`4b825dc642cb6eb9a060e54bf8d69288fbee4904..${H.commitHash}`,"--",N],s.info(`File created during task, showing full file diff using empty tree object: ${H.commitHash?H.commitHash.substring(0,8):"unknown"} for ${N}`)),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")
|
|
3014
|
+
try{const ye=await t.git.show([`${G.commitHash}:${N}`]),we=await t.git.show([`${H.commitHash}:${N}`])
|
|
3015
3015
|
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")
|
|
3016
|
-
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 ${
|
|
3017
|
-
s.info(`Trying fallback to show file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${
|
|
3016
|
+
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 ${N}: hasDiff=${K}, length=${V.length}`)}}catch(Ie){if(s.warn(`Error getting diff for ${N}`,{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}:${N}`]
|
|
3017
|
+
s.info(`Trying fallback to show file content using git show: ${H.commitHash?H.commitHash.substring(0,8):"unknown"}:${N}`)
|
|
3018
3018
|
const xe=await t.git.show($e)
|
|
3019
3019
|
if(xe&&xe.trim().length>0){if(m){let Se=""
|
|
3020
3020
|
const Ee=xe.split("\n")
|
|
3021
3021
|
for(const _e of Ee)Se+=`+${_e}\n`
|
|
3022
3022
|
V=Se}else V="File created during this task. Content:\n"+xe
|
|
3023
|
-
K=!0,s.info(`Fallback succeeded, got file content of length ${xe.length}`)}}catch(Ce){s.warn(`Fallback also failed for ${
|
|
3023
|
+
K=!0,s.info(`Fallback succeeded, got file content of length ${xe.length}`)}}catch(Ce){s.warn(`Fallback also failed for ${N}`,{error:Ce instanceof Error?Ce.message:String(Ce)})}}S.set(N,{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",S.set(N,{firstChange:G,lastChange:H,hasDiff:K,diffText:V})}let E=`# File Changes for Task: ${f}\n\n`
|
|
3024
3024
|
b&&"object"==typeof b&&(E+="## Task Information\n",E+=`- **Title**: ${b.title||"Unknown"}\n`,E+=`- **Status**: ${b.status||"Unknown"}\n`,E+=`- **Description**: ${b.description||"No description"}\n\n`)
|
|
3025
3025
|
const _=3e3
|
|
3026
3026
|
let C=0,T=!1
|
|
@@ -3032,8 +3032,8 @@ let Oe=!1
|
|
|
3032
3032
|
try{await a.access(Ae),Oe=!0}catch{}E+=`### ${Re} ${Oe?"":"(Deleted)"}\n\n`
|
|
3033
3033
|
const Me=new Date(je.firstChange.timestamp).toLocaleString(),De=new Date(je.lastChange.timestamp).toLocaleString()
|
|
3034
3034
|
E+=`**First Change**: ${Me}\n`,E+=`**Last Change**: ${De}\n`,"pre-task"===je.firstChange.status&&je.firstChange.commitHash?E+=`**Pre-Task Commit**: ${je.firstChange.commitHash.substring(0,8)}\n`:je.firstChange.commitHash&&(E+=`**File Created During Task**: ${je.firstChange.commitHash.substring(0,8)}\n`),je.lastChange.commitHash&&(E+=`**Final Commit**: ${je.lastChange.commitHash.substring(0,8)}\n`)
|
|
3035
|
-
const
|
|
3036
|
-
|
|
3035
|
+
const Ne=I.sourceToMove.get(Re),Fe=I.destToMove.get(Re)
|
|
3036
|
+
Ne?E+=`**\u2139\ufe0f Move Operation**: This file was moved TO **${Ne.destination}**\n`:Fe&&(E+=`**\u2139\ufe0f Move Operation**: This file was moved FROM **${Fe.source}**\n`),m&&!T?je.hasDiff?(E+="\n**Changes**:\n",E+="```diff\n",E+=je.diffText,E+="\n```\n\n"):E+=`\n**Note**: ${je.diffText}\n\n`:T&&je.hasDiff?(E+=`\n**Note**: File has changes (${je.diffText?je.diffText.split("\n").length:0} lines). `,E+=`Compare ${je.firstChange.commitHash?je.firstChange.commitHash.substring(0,8):"unknown"}..${je.lastChange.commitHash?je.lastChange.commitHash.substring(0,8):"unknown"} to see changes.\n\n`):je.hasDiff?E+="\n**Note**: File has changes. Use showDiff=true to see the diff.\n\n":E+=`\n**Note**: ${je.diffText}\n\n`,E+="---\n\n"}return p({success:!0,fileCount:x.size}),{content:[{type:"text",text:E}]}}catch(ze){return s.error("Error in task file history tool",{error:ze instanceof Error?ze.stack:String(ze)}),p({success:!1,error:ze instanceof Error?ze.name:"unknown"}),{content:[{type:"text",text:`Error: ${(0,u.getErrorMessage)(ze)}`}],isError:!0}}}
|
|
3037
3037
|
const i=t(59860),a=s(t(91943)),c=t(79671),l=t(82960),d=s(t(85967)),u=t(58824),h=t(94466)
|
|
3038
3038
|
exports.PRECISION_EDIT_TYPES=["replace-strings","insert-lines","replace-lines"]},20979(e,exports){function t(e,t){const n=e.trim().replace(/^\.\//,"").replace(/\/+$/,""),o=t.trim().replace(/^\.\//,"")
|
|
3039
3039
|
if(!n||!o)return!1
|
|
@@ -3100,17 +3100,17 @@ try{return await g.hasValidToken()}catch(e){return j.error("Error checking authe
|
|
|
3100
3100
|
j.info(`Generating tools list with testsRequired=${e}, authenticated=${t}`)
|
|
3101
3101
|
const r={testsRequired:e,authenticated:t}
|
|
3102
3102
|
return(0,w.generateVisibleToolsList)(r)}
|
|
3103
|
-
let D=M(),
|
|
3104
|
-
D.then(e=>{
|
|
3103
|
+
let D=M(),N=[]
|
|
3104
|
+
D.then(e=>{N=e}).catch(e=>{j.error("Error generating initial tools list",{error:(0,d.getErrorMessage)(e)}),N=[]}),e.setRequestHandler(c.ListToolsRequestSchema,async()=>{try{return N=await M(),{tools:N}}catch(e){return j.error("Error generating tools list",{error:(0,d.getErrorMessage)(e)}),{tools:[]}}}),e.setRequestHandler(c.CallToolRequestSchema,(e,c)=>p.sessionIdentity.inCallScope(async()=>{let g
|
|
3105
3105
|
try{const{name:r,arguments:l={},_meta:x}=e.params
|
|
3106
3106
|
_&&(0,y.logSessionIdentity)(r,"_meta-debug",{has_meta:!!x,sessionId:x?.sessionId||null,messageId:x?.messageId||null,paramKeys:Object.keys(e.params)}),j.info(`Tool call received: ${r}`,{tool:r}),i.incrementCounter("tool_calls_total",1,{tool:r})
|
|
3107
|
-
const
|
|
3107
|
+
const N=i.startTimer("tool_execution_time")
|
|
3108
3108
|
Date.now()
|
|
3109
|
-
let
|
|
3110
|
-
if(g=(0,I.readRawArgs)(l),Date.now(),
|
|
3109
|
+
let F=Date.now()
|
|
3110
|
+
if(g=(0,I.readRawArgs)(l),Date.now(),F=Date.now(),A){const e=await O()
|
|
3111
3111
|
if(Date.now(),!e)return j.warn("Unauthenticated tool call attempt",{tool:r}),{content:[{type:"text",text:"Authentication required. Please run 'alchemylab setup' to authenticate and configure your project."}],isError:!0}}const z=(0,w.getToolByName)(r)
|
|
3112
3112
|
let L
|
|
3113
|
-
if(
|
|
3113
|
+
if(F=Date.now(),o?(o.setOptions({createRecords:z?.createsRecords??!1}),j.info(`Setting record creation for tool ${r}: ${z?.createsRecords??!1}`)):j.debug(`No API client available for tool ${r} (minimal mode)`),Date.now(),Date.now(),F=Date.now(),x?.sessionId){const e=x.sessionId,t=x.messageId,s=p.sessionIdentity.getIdentity()
|
|
3114
3114
|
if(s)s.sessionId!==e&&(_&&(0,y.logSessionIdentity)(r,"_meta-session-switch",{prevSessionId:s.sessionId,prevAgentType:s.agentType,newSessionId:e}),p.sessionIdentity.setIdentity({providerId:"opencode",sessionId:e,serverUrl:s.serverUrl}),n?.workspaceManager?.resetCurrentContext?.(e))
|
|
3115
3115
|
else{const t=await(0,u.getServerUrl)(n,j)
|
|
3116
3116
|
p.sessionIdentity.setIdentity({providerId:"opencode",sessionId:e,serverUrl:t||""})}try{const t=await p.sessionIdentity.getSessionRequestId(o,j,e),r=(0,h.sessionMovedRequest)(q,e,t)
|
|
@@ -3124,7 +3124,7 @@ const i=await s.json(),a="string"==typeof i?.path?.cwd?i.path.cwd:"",c=e.mainBas
|
|
|
3124
3124
|
a&&((0,m.isInWorkDir)(c,"setup-worktrees",a)||(0,m.isInWorkDir)(c,"test-worktrees",a))?(T.set(t,a),e.workspaceManager.setSessionWorktree(t,a)):T.set(t,null)}catch{}}(n,e,p.sessionIdentity.getIdentity()?.serverUrl,j),L={providerId:"opencode",sessionId:e,messageId:t,createdAt:(new Date).toISOString()},_&&(0,y.logSessionIdentity)(r,"_meta",{sessionId:e,messageId:t})}else if(M=r,D=x,P||(P=!0,j.warn("No _meta.sessionId on tool call: caller not on alchemylab-opencode fork.",{tool:M,metaKeys:D?Object.keys(D):null})),!1!==z?.requiresSession)throw(0,f.createMcpProtocolError)(`_meta.sessionId required for tool '${r}'`,{tool:r})
|
|
3125
3125
|
p.sessionIdentity.setCurrentCallOrigin(L)
|
|
3126
3126
|
const B=L?.sessionId||x?.sessionId
|
|
3127
|
-
if(n?.workspaceManager){
|
|
3127
|
+
if(n?.workspaceManager){F=Date.now()
|
|
3128
3128
|
try{const e=await(0,u.getServerUrl)(n,j)
|
|
3129
3129
|
e&&o&&await n.workspaceManager.ensureReadonlyBranchIfNeeded(e,r,o,B)}catch(e){j.debug("Error during workspace check",{error:(0,d.getErrorMessage)(e)})}Date.now()}let H=n?{...n,currentSessionId:B??"__server__"}:null
|
|
3130
3130
|
H?.documentFileManager&&o&&(H.documentFileManager=(0,a.requestScopedDocuments)(H.documentFileManager,B,(0,a.heldDocumentDepsFrom)(o,j),j))
|
|
@@ -3147,7 +3147,7 @@ if(K){const{getActionServerBaseUrl:e}=t(79414)
|
|
|
3147
3147
|
fetch(`${e()}/api/mcp/tool-call-event`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:"note-nudge",tool:r,sessionId:L?.sessionId,requestId:L?.sessionId?q.get(L.sessionId)??void 0:void 0}),signal:AbortSignal.timeout(500)}).catch(()=>{})}let Y=K
|
|
3148
3148
|
const J=L?.sessionId?q.get(L.sessionId):void 0
|
|
3149
3149
|
if(K&&J&&o&&H?.projectId)try{const e=await o.proxyPost(`/api/projects/${H.projectId}/progress/offer`,{sessionId:L.sessionId}),t=e?.data??e
|
|
3150
|
-
t?.invite&&(Y=(0,S.nudgeText)(J,{lastPercent:"number"==typeof t.lastPercent?t.lastPercent:null}))}catch{}return V=(0,S.withNoteNudge)(V,Y),U&&Array.isArray(V?.content)&&V.content.push({type:"text",text:E.COMMITTED_VIEW_NOTE}),Date.now(),
|
|
3150
|
+
t?.invite&&(Y=(0,S.nudgeText)(J,{lastPercent:"number"==typeof t.lastPercent?t.lastPercent:null}))}catch{}return V=(0,S.withNoteNudge)(V,Y),U&&Array.isArray(V?.content)&&V.content.push({type:"text",text:E.COMMITTED_VIEW_NOTE}),Date.now(),N({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
|
|
3151
3151
|
if(Array.isArray(r))for(const e of r)e&&"string"==typeof e.text&&(e.text=t(e.text))
|
|
3152
3152
|
return e}(V)}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"})
|
|
3153
3153
|
let n="Unknown error"
|
|
@@ -3476,7 +3476,7 @@ return Array.isArray(t)&&0!==t.length?t.map(e=>{const t=[`- ${e.target??"a file"
|
|
|
3476
3476
|
const r=Array.isArray(e.misses)?e.misses:[]
|
|
3477
3477
|
for(const e of r)t.push(` - no match: \`${String(e)}\``)
|
|
3478
3478
|
return t.join("\n")}).join("\n"):"The operation reported no per-file detail."}exports.handleQuickChange=async function(e,$,x,S,E,_,C,T){const q=E.child({tool:"quick_edit"}),R=Date.now()
|
|
3479
|
-
try{const P=S.getRequestClient(),A=S.getTaskClient(),O=S.getFileClient(),M=S.getToolRecordClient(),{title:D,description:
|
|
3479
|
+
try{const P=S.getRequestClient(),A=S.getTaskClient(),O=S.getFileClient(),M=S.getToolRecordClient(),{title:D,description:N,follows:F,...z}=e,L=e.comment||D
|
|
3480
3480
|
q.info("Starting quick_edit workflow",{title:D,operationType:$})
|
|
3481
3481
|
let B=!1
|
|
3482
3482
|
try{const e=await P.listRequests({limit:2,sortBy:"created_at",sortDirection:"desc"})
|
|
@@ -3496,7 +3496,7 @@ U=(0,a.emptiedFiles)(t),W=(0,c.contentLoss)(t)}catch(e){q.warn("quick edit pre-c
|
|
|
3496
3496
|
try{const t=await S.proxyGet(`/api/projects/${x.projectId}/test-processes`),r=Array.isArray(t)?t:Array.isArray(t?.data)?t.data:[],n=async e=>(await K(["ls-tree","-r","--name-only",`refs/heads/${Y}`,"--",e])??"").split("\n").filter(Boolean)
|
|
3497
3497
|
e=await(0,a.checksLosingFiles)((0,a.emptiedAsDeletes)(U),r,n)}catch(e){return q.warn("quick edit refused: the project's checks could not be read",{error:(0,w.getErrorMessage)(e)}),{content:[{type:"text",text:"Not done: this edit empties files, and the project's checks could not be read to see whether any of them covers those files. File it with create_request instead. Nothing was changed or created."}],isError:!0}}if(e.length>0)return q.info("quick edit refused: it would take files away from a check",{losses:e}),{content:[{type:"text",text:(0,a.checkLossRefusal)(e)}],isError:!0}}if(W)return q.info("quick edit refused: it takes content out",{removed:W.removed,added:W.added,files:W.files.map(e=>e.path)}),{content:[{type:"text",text:(0,c.contentLossRefusal)(W)}],isError:!0}
|
|
3498
3498
|
q.debug("Creating quick_edit request")
|
|
3499
|
-
const Z=T?.sessionId??x.currentSessionId,X="string"==typeof Z&&/^ses_/.test(Z)?Z:void 0,ee=(await(0,k.messagesToChooseFrom)(x.mainBasePath,T)).length>0?null:await(0,k.exchangeKeyOfCall)(x.mainBasePath,T),te=await P.createRequest({title:D,description:
|
|
3499
|
+
const Z=T?.sessionId??x.currentSessionId,X="string"==typeof Z&&/^ses_/.test(Z)?Z:void 0,ee=(await(0,k.messagesToChooseFrom)(x.mainBasePath,T)).length>0?null:await(0,k.exchangeKeyOfCall)(x.mainBasePath,T),te=await P.createRequest({title:D,description:N,type:"quick-edit",acceptanceCriteria:[],...X?{creatorSessionId:X}:{},...void 0!==ee?{exchangeKey:ee}:{},..."string"==typeof F&&/^req\d{6}$/.test(F)?{originRequestId:F}:{}})
|
|
3500
3500
|
if(!te||!te.id)throw new Error("Failed to create request")
|
|
3501
3501
|
const re=te.id
|
|
3502
3502
|
let ne,oe
|
|
@@ -3517,19 +3517,19 @@ if(o.length>0){let e=null
|
|
|
3517
3517
|
try{const t=await S.proxyGet(`/api/projects/${x.projectId}/test-processes`),r=Array.isArray(t)?t:Array.isArray(t?.data)?t.data:[]
|
|
3518
3518
|
e=await(0,a.checksLosingFiles)((0,a.emptiedAsDeletes)(o),r,async e=>[e])}catch{e=null}if(null===e||e.length>0)return await A.updateTaskStatus(ne,"canceled").catch(()=>{}),await P.updateRequest(re,{status:"canceled"}).catch(()=>{}),await(x.workspaceManager?.cancelRequest(re).catch(()=>{})),q.warn("quick edit emptied a checked file after an approximate match; discarded",{requestId:re,emptiedAfter:o}),{content:[{type:"text",text:`${e?(0,a.checkLossRefusal)(e).replace(/^Not done:/,"Not kept:").replace(/ Nothing was changed or created\.$/,""):"Not kept: this edit left files empty, and the project's checks could not be read to see whether any of them covers those files."}\n\nThe edit was discarded before it reached ${Y} and ${re} is canceled.`}],isError:!0}}}catch(e){q.warn("quick edit post-check could not run; keeping the edit",{requestId:re,error:e instanceof Error?e.message:String(e)})}const k=(0,d.readAlchemyMeta)(b),T=(j=k,!(!1===j?.failed))
|
|
3519
3519
|
if(T?q.warn("File operation completed with partial failures",{requestId:re}):q.info("File operation completed successfully"),T)return await A.updateTaskStatus(ne,"canceled").catch(()=>{}),await P.updateRequest(re,{status:"canceled"}).catch(()=>{}),await(x.workspaceManager?.cancelRequest(re).catch(()=>{})),x.workspaceManager?.releaseSession(o(re)),q.warn("quick edit partially failed; discarded whole",{requestId:re}),{content:[{type:"text",text:`\u274c Not kept: part of this edit failed, and a quick edit is merged whole or not at all, so ${Y} is unchanged. ${re} is canceled.\n\nRead the whole file again (its current text, not what you expected), then retry with replacements that all match. The parts that matched were discarded too: no file below changed on ${Y}.\n\n${i(k)}`}],isError:!0}
|
|
3520
|
-
let
|
|
3520
|
+
let N=""
|
|
3521
3521
|
if(oe&&!s($,z)){let e=null,n=null
|
|
3522
3522
|
const s=`The edit was discarded before it reached ${Y} and ${re} is canceled.`
|
|
3523
3523
|
try{const r=await async function(e,r){const{execFile:n}=await Promise.resolve().then(()=>t.t(35317,23)),o=await Promise.resolve().then(()=>t.t(91943,23)),s=await Promise.resolve().then(()=>t.t(16928,23)),i=t=>new Promise((r,o)=>n("git",t,{cwd:e,maxBuffer:67108864},(e,t)=>e?o(e):r(String(t)))),a=e=>e.split("\0").filter(Boolean),c=a(await i(["diff","--no-renames","--name-only","-z",`refs/heads/${r}`])),l=a(await i(["ls-files","--others","--exclude-standard","-z"])),d=[]
|
|
3524
3524
|
for(const t of[...new Set([...c,...l])]){const n=await i(["show",`refs/heads/${r}:${t}`]).catch(()=>null),a=await o.readFile(s.join(e,t),"utf8").catch(()=>null)
|
|
3525
3525
|
d.push({path:t,before:n,after:a})}return d}(oe,Y)
|
|
3526
3526
|
n=(0,c.quickEditShapeRefusal)((0,c.quickEditShape)(r),{discarded:s}),e=(0,c.contentLoss)(r)}catch(e){q.warn("quick edit content check could not run; keeping the edit",{requestId:re,error:(0,w.getErrorMessage)(e)})}if(n)return await A.updateTaskStatus(ne,"canceled").catch(()=>{}),await P.updateRequest(re,{status:"canceled"}).catch(()=>{}),await(x.workspaceManager?.cancelRequest(re).catch(()=>{})),x.workspaceManager?.releaseSession(o(re)),q.warn("quick edit was not a small edit to an existing file; discarded",{requestId:re}),{content:[{type:"text",text:n}],isError:!0}
|
|
3527
|
-
if(
|
|
3527
|
+
if(N=await async function(e,n){const{execFile:o}=await Promise.resolve().then(()=>t.t(35317,23))
|
|
3528
3528
|
return r(await new Promise((t,r)=>o("git",["diff","--no-color","--no-renames","-U1",`refs/heads/${n}`],{cwd:e,maxBuffer:8388608},(e,n)=>e?r(e):t(String(n)))))}(oe,Y).catch(()=>""),e)return await A.updateTaskStatus(ne,"canceled").catch(()=>{}),await P.updateRequest(re,{status:"canceled"}).catch(()=>{}),await(x.workspaceManager?.cancelRequest(re).catch(()=>{})),x.workspaceManager?.releaseSession(o(re)),q.warn("quick edit took content out; discarded",{requestId:re,removed:e.removed,added:e.added,files:e.files.map(e=>e.path)}),{content:[{type:"text",text:(0,c.contentLossRefusal)(e,{discarded:`The edit was discarded before it reached ${Y} and ${re} is canceled.`})}],isError:!0}}q.debug("Completing task")
|
|
3529
3529
|
try{await(0,p.handleCompleteTask)({taskId:ne,summary:`${D}${T?" (partial success)":""}`},se,S,E,_),q.info("Task completed successfully",{taskId:ne})}catch(e){q.error("Task completion via handleCompleteTask failed, attempting direct status update",{taskId:ne,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0})
|
|
3530
|
-
try{await A.updateTaskStatus(ne,"completed"),q.info("Task completed via direct updateTaskStatus call",{taskId:ne})}catch(e){return q.error("Direct task completion ALSO failed - task may be stuck in in_progress",{taskId:ne,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0,operationType:$,isDocumentOperation:!!z.documentId}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change operation succeeded but task completion failed.\n\n**Request:** ${re}\n**Task:** ${ne}\n**Issue:** Could not mark task as completed - it remains in 'in_progress' state\n\nThe ${$} operation was successful, but we couldn't automatically complete the task. Please use the complete-task tool manually with taskId: ${ne}\n\nError: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}const
|
|
3531
|
-
U&&(
|
|
3532
|
-
const W=T?`Quick change completed with partial success: ${D}. Some replacements failed - see details below.`:`${D}${
|
|
3530
|
+
try{await A.updateTaskStatus(ne,"completed"),q.info("Task completed via direct updateTaskStatus call",{taskId:ne})}catch(e){return q.error("Direct task completion ALSO failed - task may be stuck in in_progress",{taskId:ne,error:e instanceof Error?e.message:String(e),errorStack:e instanceof Error?e.stack:void 0,operationType:$,isDocumentOperation:!!z.documentId}),{content:[{type:"text",text:`\u26a0\ufe0f Quick change operation succeeded but task completion failed.\n\n**Request:** ${re}\n**Task:** ${ne}\n**Issue:** Could not mark task as completed - it remains in 'in_progress' state\n\nThe ${$} operation was successful, but we couldn't automatically complete the task. Please use the complete-task tool manually with taskId: ${ne}\n\nError: ${e instanceof Error?e.message:String(e)}`}],isError:!0}}}const F=(()=>{try{return S.getProjectId?.()??null}catch{return null}})(),U=F?await(0,I.requestDocumentEdits)({get:e=>S.proxyGet(e)},F,re).then(e=>r(e.map(e=>e.diff).join(""))).catch(()=>""):""
|
|
3531
|
+
U&&(N=N?`${N}\n${U}`:U),q.debug("Completing request")
|
|
3532
|
+
const W=T?`Quick change completed with partial success: ${D}. Some replacements failed - see details below.`:`${D}${N?`\n\n\`\`\`diff\n${N}\n\`\`\``:""}`,G=T?`${$} operation partially completed - some operations succeeded, some failed`:`${$} operation executed successfully`
|
|
3533
3533
|
let Q=!1
|
|
3534
3534
|
const Z=await K(["rev-list","--count",`refs/heads/${Y}..refs/heads/requests/${re}`])
|
|
3535
3535
|
let X
|
|
@@ -3564,7 +3564,7 @@ const a=t(95122),c=t(81563),l=t(59860),d=t(82272),u=t(14079),h=t(69442),p=t(8035
|
|
|
3564
3564
|
const r=t(76183),n=t(43707)
|
|
3565
3565
|
exports.def={name:"quick_replace",description:"Replace text in files or documents as a quick edit: operations [{ path | documentId, replacements [{ old_str, new_str, match_mode? }] }], fuzzy matching on by default. Requires title, description, comment, operations. The quick-edit rules are quick_write's.",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
|
|
3566
3566
|
const r=t(76183),n=t(43707)
|
|
3567
|
-
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). Requires title, description, content.\n\nA quick edit is a small change (at most 20 lines) to text or settings in an existing file (docs, copy, config values), never code (that goes to the worker on it via hand-over, or to a request). A new file, a larger edit or a net removal is refused: file a request. It is made on a file as you last read it with read_files: one you have not read, or that other work changed since, is refused until you read it again. Only for a change the user wants kept, never copies, scratch or preview files. It becomes a quick-edit request that merges on its own, unless Manual Autonomy or one of the user's recent checks on that file holds it for their sign-off.",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),x=t(9230),S=t(34150),E=t(26473),_=t(6309),C=t(86317),T=t(61391),q=t(57713),R=t(96252),j=t(40213),P=t(57250),A=t(41197),O=t(15510),M=t(94669),D=t(56946),
|
|
3567
|
+
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). Requires title, description, content.\n\nA quick edit is a small change (at most 20 lines) to text or settings in an existing file (docs, copy, config values), never code (that goes to the worker on it via hand-over, or to a request). A new file, a larger edit or a net removal is refused: file a request. It is made on a file as you last read it with read_files: one you have not read, or that other work changed since, is refused until you read it again. Only for a change the user wants kept, never copies, scratch or preview files. It becomes a quick-edit request that merges on its own, unless Manual Autonomy or one of the user's recent checks on that file holds it for their sign-off.",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),x=t(9230),S=t(34150),E=t(26473),_=t(6309),C=t(86317),T=t(61391),q=t(57713),R=t(96252),j=t(40213),P=t(57250),A=t(41197),O=t(15510),M=t(94669),D=t(56946),N=t(90346),F=t(28196),z=t(66808),L=t(1540),B=t(44973),H=t(48155),U=t(17613),W=t(4961),G=t(68456),V=t(75850),K=t(23322),Y=t(50003),J=t(55247),Q=t(57507),Z=t(8411),X=t(5169),ee=t(65948),te=t(98095),re=t(78002),ne=t(88849),oe=t(70542);(0,r.registerTool)(n.def),(0,r.registerTool)(o.def),(0,r.registerTool)(s.def),(0,r.registerTool)(i.def),(0,r.registerTool)(a.def),(0,r.registerTool)(c.def),(0,r.registerTool)(l.def),(0,r.registerTool)(d.def),(0,r.registerTool)(u.def),(0,r.registerTool)(h.def),(0,r.registerTool)(p.def),(0,r.registerTool)(f.def),(0,r.registerTool)(m.def),(0,r.registerTool)(g.def),(0,r.registerTool)(y.def),(0,r.registerTool)(w.def),(0,r.registerTool)(b.def),(0,r.registerTool)(v.def),(0,r.registerTool)(k.def),(0,r.registerTool)(I.def),(0,r.registerTool)(S.def),(0,r.registerTool)($.def),(0,r.registerTool)(x.def),(0,r.registerTool)(E.def),(0,r.registerTool)(_.def),(0,r.registerTool)(C.def),(0,r.registerTool)(T.def),(0,r.registerTool)(q.def),(0,r.registerTool)(R.def),(0,r.registerTool)(j.def),(0,r.registerTool)(P.def),(0,r.registerTool)(A.def),(0,r.registerTool)(O.def),(0,r.registerTool)(M.def),(0,r.registerTool)(D.def),(0,r.registerTool)(N.def),(0,r.registerTool)(F.def),(0,r.registerTool)(F.previewDef),(0,r.registerTool)(z.def),(0,r.registerTool)(z.cancelDef),(0,r.registerTool)(z.listDef),(0,r.registerTool)(z.followupDef),(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.registerTool)(V.def),(0,r.registerTools)(K.metaDefs),(0,r.registerTools)(Y.contractDefs),(0,r.registerTools)(J.testingDefs),(0,r.registerTools)(Q.environmentDefs),(0,r.registerTools)(Z.setupDefs),(0,r.registerTools)(X.resultDefs),(0,r.registerTools)(ee.mcpConnectionDefs),(0,r.registerTools)(te.experimentDefs),(0,r.registerTools)(re.visualDefs),(0,r.registerTools)(ne.checkingDefs),(0,r.registerTools)(oe.debugDefs),(0,t(82245).freezeRegistry)()},82245(e,exports,t){function r(e){if(h)throw new Error(`Tool registry is frozen: cannot register '${e.name}'. Core tools must be added in registrations.ts at build time.`)
|
|
3568
3568
|
if(u.has(e.name))throw new Error(`Duplicate tool registration: '${e.name}'`)
|
|
3569
3569
|
d.push(e),u.set(e.name,e)}function n(e){const t=e.replace(/\s+/g," ").trim(),r=/^(.{20,200}?[.!?])(\s|$)/.exec(t)
|
|
3570
3570
|
return r?r[1]:t.slice(0,200)}function o(e){if(Array.isArray(e))return e.map(o)
|
|
@@ -3596,7 +3596,7 @@ if("completed"!==d.status)return{text:`The target of the thread of ${o} was not
|
|
|
3596
3596
|
const u=await e.proxyGet(`/api/projects/${n}/threads/${encodeURIComponent(o)}`),h=(u?.data??u)?.target
|
|
3597
3597
|
return h?.state?{text:t(o,h.state)}:{text:`The thread of ${o} has no target.`}}
|
|
3598
3598
|
const r=new Set(["completed","failed","canceled","cancelled","timeout","expired"])},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,_,C,T,q,R,j){const P=T.child({tool:"complete_request"}),A=q.startTimer("tool_execution_time")
|
|
3599
|
-
try{const{requestId:s,summary:i,change:O,worthKnowing:M,skipTestsReason:D,skipTests:
|
|
3599
|
+
try{const{requestId:s,summary:i,change:O,worthKnowing:M,skipTestsReason:D,skipTests:N,criteriaComments:F,couldNotCheck:z,guidance:L,replacesSummary:B,notChecked:H,show:U}=e
|
|
3600
3600
|
if(!s)throw(0,o.createValidationError)("Request ID is required")
|
|
3601
3601
|
if(!i&&!O)throw(0,o.createValidationError)("Summary is required")
|
|
3602
3602
|
let W=null
|
|
@@ -3604,9 +3604,9 @@ try{W=await C.getRequestClient().getRequest(s)}catch(e){P.debug("the request cou
|
|
|
3604
3604
|
if(!i&&!W)return{content:[{type:"text",text:`Not completed: ${s} could not be read just now, so nothing was recorded. Call complete again with the same arguments.`}],isError:!0}
|
|
3605
3605
|
if(!i&&!G)throw(0,o.createValidationError)(`Summary is required: ${s} is not a round, and \`change\` is one change on a round.`)
|
|
3606
3606
|
let V=i||O||""
|
|
3607
|
-
if(D&&
|
|
3607
|
+
if(D&&N&&N.length>0)throw(0,o.createValidationError)("Provide either skipTestsReason (skip all) or skipTests (per-command), not both.")
|
|
3608
3608
|
if(!0!==B){const e=String(W?.completionSummary??"").trim()
|
|
3609
|
-
if(e)return P.info("re-completion refused until the summary is confirmed to cover the whole request",{requestId:s}),{content:[{type:"text",text:r(s,e)}],isError:!0}}P.info("Starting request completion",{requestId:s,skipAllRequested:!!D,perCommandSkipCount:
|
|
3609
|
+
if(e)return P.info("re-completion refused until the summary is confirmed to cover the whole request",{requestId:s}),{content:[{type:"text",text:r(s,e)}],isError:!0}}P.info("Starting request completion",{requestId:s,skipAllRequested:!!D,perCommandSkipCount:N?.length||0})
|
|
3610
3610
|
const K=j?.sessionId??_.currentSessionId,Y=j??(K?{providerId:"opencode",sessionId:K,createdAt:(new Date).toISOString()}:void 0)
|
|
3611
3611
|
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)})
|
|
3612
3612
|
if(e.ok){const t=await e.json()
|
|
@@ -3636,7 +3636,7 @@ return!(c&&s.has(c)&&i.has(c))})
|
|
|
3636
3636
|
if(0===c.length)return null
|
|
3637
3637
|
const l=c.slice(0,8).map(e=>{const t=r.indexOf(e),n=a(e,t),o=String(e?.description??"").trim()
|
|
3638
3638
|
return` - ${n?`[${n}] `:""}${o.length>120?`${o.slice(0,117)}...`:o}`}).join("\n")
|
|
3639
|
-
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 C.getRequestClient().getRequest(s),{couldNotCheck:z,commentedKeys:(
|
|
3639
|
+
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 C.getRequestClient().getRequest(s),{couldNotCheck:z,commentedKeys:(F??[]).map(e=>e.key)})
|
|
3640
3640
|
if(e)return{content:[{type:"text",text:e}],isError:!0}
|
|
3641
3641
|
const t=await async function(e,t,r,o){const s=(Array.isArray(r)?r:[]).map(e=>String(e??"").trim().toLowerCase()).filter(Boolean)
|
|
3642
3642
|
if(0===s.length)return[]
|
|
@@ -3645,12 +3645,12 @@ return a.forEach((e,t)=>{if(!e?.id)return
|
|
|
3645
3645
|
const r=(0,n.criterionShortKey)(String(e.id),t).toLowerCase()
|
|
3646
3646
|
if(!s.includes(r))return
|
|
3647
3647
|
const i=(o??[]).find(e=>String(e?.key??"").trim().toLowerCase()===r)
|
|
3648
|
-
c.push({criterionId:String(e.id),reason:i?.comment??null})}),c}(C.getRequestClient(),s,z,
|
|
3648
|
+
c.push({criterionId:String(e.id),reason:i?.comment??null})}),c}(C.getRequestClient(),s,z,F)
|
|
3649
3649
|
t.length>0&&await C.proxyPost(`/api/projects/${_.projectId}/requests/${s}/drive-not-checked`,{items:t})}}catch(e){P.debug("drive-claim stamp check could not run; completion continues",{error:(0,h.getErrorMessage)(e)})}const ne=[]
|
|
3650
|
-
if(
|
|
3651
|
-
if(P.info("Recording coder criteria comments",{requestId:s,comments:
|
|
3650
|
+
if(F&&F.length>0)try{const e=C.getRequestClient(),t=await e.getRequest(s),r=t?.acceptanceCriteria||[]
|
|
3651
|
+
if(P.info("Recording coder criteria comments",{requestId:s,comments:F.length,criteriaFetched:r.length}),0===r.length)return P.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}
|
|
3652
3652
|
{const e=(0,p.buildCriteriaKeyMap)(r),n=[],o=[]
|
|
3653
|
-
for(const t of
|
|
3653
|
+
for(const t of F){const r=e.get(t.key),s=Array.isArray(t.verify)?t.verify.filter(e=>"string"==typeof e&&e.trim()):[]
|
|
3654
3654
|
if(r&&s.length>0&&"subjective"===r.kind){n.push({criterionId:r.id,evidence:`${t.comment}\n\nWhat I ran (a judgment decides this criterion, not these): ${s.join(" ; ")}`})
|
|
3655
3655
|
continue}const i=s.length&&Array.isArray(t.testFiles)?t.testFiles.filter(e=>"string"==typeof e&&e.trim()).map(e=>e.trim().replace(/^\.\//,"")):[]
|
|
3656
3656
|
r?n.push({criterionId:r.id,evidence:t.comment,...s.length?{verify:s}:{},...i.length?{testFiles:i}:{}}):o.push(t.key)}if(o.length>0){const e=(0,p.formatCriteriaWithKeys)(r)
|
|
@@ -3693,22 +3693,22 @@ const de=async(e,r)=>(await Promise.resolve().then(()=>t(79414))).actionServerFe
|
|
|
3693
3693
|
if(G){const e="string"==typeof W?.sitting?.head?W.sitting.head:""
|
|
3694
3694
|
let t=e?null:X
|
|
3695
3695
|
if(e)try{const r=await fetch(`${(0,f.getActionServerBaseUrl)()}/api/request-diff-stats?requestId=${encodeURIComponent(s)}&since=${encodeURIComponent(e)}`,{signal:AbortSignal.timeout(5e3)}),n=r.ok?await r.json():null
|
|
3696
|
-
n?.data&&(t={files:Array.isArray(n.data.files)?n.data.files.filter(e=>"string"==typeof e):[],additions:n.data.additions,deletions:n.data.deletions})}catch(e){P.debug("the change's own diff could not be read; it is recorded without its files",{requestId:s,error:(0,h.getErrorMessage)(e)})}le={...O?{account:O}:{},files:t?.files??[],..."number"==typeof t?.additions?{additions:t.additions}:{},..."number"==typeof t?.deletions?{deletions:t.deletions}:{}},ce=await(0,x.lookNow)(s,ae,de).catch(()=>{})}const ue=async e=>{const t=await C.proxyPost(`/api/projects/${_.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:K,skipTestsReason:D,skipTests:
|
|
3696
|
+
n?.data&&(t={files:Array.isArray(n.data.files)?n.data.files.filter(e=>"string"==typeof e):[],additions:n.data.additions,deletions:n.data.deletions})}catch(e){P.debug("the change's own diff could not be read; it is recorded without its files",{requestId:s,error:(0,h.getErrorMessage)(e)})}le={...O?{account:O}:{},files:t?.files??[],..."number"==typeof t?.additions?{additions:t.additions}:{},..."number"==typeof t?.deletions?{deletions:t.deletions}:{}},ce=await(0,x.lookNow)(s,ae,de).catch(()=>{})}const ue=G&&("settle"===(0,v.kindProfile)(re??W?.type).finalize||!!X&&0===X.filesChanged&&0===X.additions&&0===X.deletions),he=async e=>{const t=await C.proxyPost(`/api/projects/${_.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:K,skipTestsReason:D,skipTests:N,headSha:J,...ue?{diffNone:!0}:{},...le?{change:le}:{},...e?{look:e}:{},...void 0!==oe?{authoredSinceReview:oe}:{},...ie?{reviewDelta:ie}:{},...se?{intentMoves:se}:{},...X?.files?{files:X.files}:{}}))
|
|
3697
3697
|
return t?.data||t}
|
|
3698
|
-
let
|
|
3699
|
-
try{if(te=await
|
|
3700
|
-
|
|
3698
|
+
let pe=null
|
|
3699
|
+
try{if(te=await he(ce),te.canComplete&&"sitting-look"===te.plan?.action){const e=await(0,x.settleLook)(s,ae,de)
|
|
3700
|
+
pe=e.served,te=await he(e.look)}}catch(e){const t=(0,h.getErrorMessage)(e)
|
|
3701
3701
|
return P.error("Server validation failed",{requestId:s,error:t}),A({success:!1,error:"validation_request_failed"}),{content:[{type:"text",text:`Failed to validate completion: ${t}`}],isError:!0}}if(!te.canComplete){let e=`Cannot complete request ${s}: ${te.reason}\n`
|
|
3702
3702
|
if(te.blockedByTasks&&te.blockedByTasks.length>0&&(e+="\n**Tasks that must be completed or canceled first:**\n",te.blockedByTasks.forEach((t,r)=>{e+=`${r+1}. **${t.taskId}** - ${t.title} (Status: ${t.status})\n`})),te.lifoViolation&&(e+="\n**Requests that must be re-completed first (LIFO order):**\n",te.lifoViolation.blockingRequests.forEach((t,r)=>{e+=`${r+1}. **${t.requestId}** - ${t.title} (Status: ${t.status})\n`})),ne.length>0)try{const t=await C.getRequestClient().getRequest(s)
|
|
3703
3703
|
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"
|
|
3704
3704
|
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"}`})
|
|
3705
|
-
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,ne)}`}catch{}return A({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if("sitting-held"===te.plan?.action)return P.info("completion held: the round stays open while the user is on its page",{requestId:s,until:te.plan.detail,look:ce,preview:
|
|
3705
|
+
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,ne)}`}catch{}return A({success:!1,reason:"server_blocked"}),{content:[{type:"text",text:e}],isError:!0}}if("sitting-held"===te.plan?.action)return P.info("completion held: the round stays open while the user is on its page",{requestId:s,until:te.plan.detail,look:ce,preview:pe}),await(0,k.reopenOneTask)(C.getTaskClient(),s).then(()=>(0,I.clearActiveTaskCache)(),e=>P.warn("the round's one task could not be opened again; its worker opens one for its next edit",{requestId:s,error:(0,h.getErrorMessage)(e)})),A({success:!0}),{content:[{type:"text",text:[`The change is recorded on ${s}, and the round stays open: the user is on its page and may ask for more. Nothing is checked or completed yet. That happens once, for everything this round changed, when they go quiet, leave the page or press Complete, and you are told then.`,"Between changes they look at what this round made: a preview of your working copy when it serves a page, and the pictures and documents it changed, as committed. What they see must be current, so after every change run the build or the script that makes it.","When they ask for another change on this round, make it and call complete with `change`: one line saying what that change did. That records it under their words and loads what they are looking at again. Do not call merge. End your turn with that one line."].join("\n")}]}
|
|
3706
3706
|
if(G&&!i)try{const e=await C.proxyGet(`/api/projects/${_.projectId}/requests/${encodeURIComponent(s)}/rounds`)
|
|
3707
3707
|
V=function(e,t){const r=[]
|
|
3708
3708
|
for(const t of Array.isArray(e)?e:[]){const e="string"==typeof t?.changed?t.changed.trim():""
|
|
3709
3709
|
e&&!r.includes(e)&&r.push(e)}const n=(t??"").replace(/\s+/g," ").trim()
|
|
3710
|
-
return n&&!r.includes(n)&&r.push(n),r.join("\n")}(((e?.data??e)?.rounds??[]).find(e=>e?.id===s)?.changes,O)||V}catch(e){P.debug("the round's changes could not be read for its summary; this change's line stands for it",{requestId:s,error:(0,h.getErrorMessage)(e)})}const
|
|
3711
|
-
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[]
|
|
3710
|
+
return n&&!r.includes(n)&&r.push(n),r.join("\n")}(((e?.data??e)?.rounds??[]).find(e=>e?.id===s)?.changes,O)||V}catch(e){P.debug("the round's changes could not be read for its summary; this change's line stands for it",{requestId:s,error:(0,h.getErrorMessage)(e)})}const fe=(0,x.showGiven)(U),me=te.plan,ge="answer"===me?.action
|
|
3711
|
+
ge||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[]
|
|
3712
3712
|
const r="string"==typeof e.command?e.command.trim():""
|
|
3713
3713
|
if(!r)return[]
|
|
3714
3714
|
const n=Array.isArray(e.ledger)?e.ledger:[]
|
|
@@ -3716,35 +3716,36 @@ return t&&n.some(e=>"verify"===e?.source&&"string"==typeof e?.instrument&&e.inst
|
|
|
3716
3716
|
if(0===s.length)return
|
|
3717
3717
|
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)})
|
|
3718
3718
|
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)})}}(C,s,J,P)
|
|
3719
|
-
const me
|
|
3720
|
-
if(
|
|
3721
|
-
const
|
|
3722
|
-
if(
|
|
3723
|
-
e&&await e.createToolRecord({projectId:_.projectId,requestId:s,type:"tests-skipped",metadata:{...D?{reason:D}:{},...
|
|
3719
|
+
const ye=me&&fe.artifact?await C.getRequestClient().getRequest(s).then(e=>String(e?.title??"")).catch(()=>""):"",we=me&&!ge?await(0,x.startShowing)({projectId:_.projectId,post:(e,t)=>C.proxyPost(e,t),log:P,sessionExternalId:K??void 0,assetThere:e=>(0,x.assetIsThere)(_.basePath,e),resultsOf:e=>C.proxyGet(`/api/projects/${_.projectId}/results?requestId=${encodeURIComponent(e)}`).then(e=>Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[])},s,fe,ye).catch(()=>""):""
|
|
3720
|
+
if(we&&P.info("showing started",{requestId:s,said:we}),"review-submit"===me?.action&&J&&X&&(X.reviewAcceptedSha=J),me?.reviewChange&&ie&&X&&(X.afterReview={...ie,outcome:me.reviewChange},"restarted"===me.reviewChange&&(X.sinceReview=ie)),!me)return A({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}
|
|
3721
|
+
const be="testing"===me.action||"human-review"===me.action||"merge-cleared"===me.action
|
|
3722
|
+
if(be&&te.testsSkipped&&(D||N&&N.length>0))try{const e=C.getToolRecordClient()
|
|
3723
|
+
e&&await e.createToolRecord({projectId:_.projectId,requestId:s,type:"tests-skipped",metadata:{...D?{reason:D}:{},...N&&N.length>0?{skipTests:N}:{},testGate:te.gates.test,timestamp:(new Date).toISOString()}})}catch(e){P.warn("Failed to persist tests-skipped tool_record",{error:(0,h.getErrorMessage)(e)})}if(be&&M&&M.length>0&&K){const e=C.getSessionMessageClient()
|
|
3724
3724
|
if(e){let t=new Set
|
|
3725
3725
|
try{const e=await C.proxyGet(`/api/projects/${_.projectId}/session-messages`,{requestId:s,messageType:"report",limit:200}),r=Array.isArray(e)?e:e?.data??[]
|
|
3726
3726
|
t=new Set(r.filter(e=>(e?.content?.requestId??e?.content?.anchor?.id)===s).map(e=>String(e?.content?.summary??"")))}catch{}for(const r of M.slice(0,5)){const n=r.length>140?`${r.slice(0,137)}\u2026`:r
|
|
3727
3727
|
if(!t.has(n))try{await e.createReport({fromSessionId:K,status:"success",summary:n,...r.length>140?{details:r}:{},attention:"fyi",requestId:s})}catch(e){P.warn("worthKnowing note failed (completion continues)",{error:(0,h.getErrorMessage)(e)})}}P.info("worthKnowing notes processed",{count:Math.min(M.length,5),requestId:s})}}if(L&&L.length>0&&K)for(const e of L.slice(0,2))try{const t=Array.isArray(e.files)&&e.files.length>0
|
|
3728
|
-
await C.proxyPost(`/api/projects/${_.projectId}/guidance/propose`,{body:e.body,scope:t?"files":"project",...t?{pathAnchors:e.files}:{},...e.evidence?{evidence:e.evidence}:{},...e.userSaid?{userSaid:e.userSaid}:{},requestId:s,sessionExternalId:K}),P.info("guidance proposed at completion",{requestId:s})}catch(e){P.info("guidance at completion not proposed (completion continues)",{requestId:s,reason:(0,h.getErrorMessage)(e)})}const
|
|
3729
|
-
|
|
3730
|
-
e.refsSeen&&
|
|
3731
|
-
|
|
3732
|
-
if("review-
|
|
3733
|
-
if("
|
|
3734
|
-
if("
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
return{content:[{type:"text",text:`Request ${s}
|
|
3739
|
-
return{content:[{type:"text",text:`Request ${s}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
if(
|
|
3743
|
-
|
|
3744
|
-
|
|
3728
|
+
await C.proxyPost(`/api/projects/${_.projectId}/guidance/propose`,{body:e.body,scope:t?"files":"project",...t?{pathAnchors:e.files}:{},...e.evidence?{evidence:e.evidence}:{},...e.userSaid?{userSaid:e.userSaid}:{},requestId:s,sessionExternalId:K}),P.info("guidance proposed at completion",{requestId:s})}catch(e){P.info("guidance at completion not proposed (completion continues)",{requestId:s,reason:(0,h.getErrorMessage)(e)})}const ve=C.getRequestClient()
|
|
3729
|
+
if("answer"===me.action){try{const t=await ve.updateRequest(s,{completionSummary:V,completionFormat:"markdown",completionNotChecked:"string"==typeof H&&H.trim()?H.trim():null,cliOrigin:Y,...Z,...ee})
|
|
3730
|
+
e.refsSeen&&t?.namedRefs&&(e.refsSeen.namedRefs=t.namedRefs),await C.proxyPost(`/api/projects/${_.projectId}/requests/${s}/finalize-completion`,{summary:V,acceptanceEvidence:[{evidence:"This changed nothing: its branch holds no change and no record writes into the project."}],mergeResult:{mergeCommitHash:"unknown",cleanedUp:!1,method:"answer-only",diffStats:{additions:0,deletions:0,filesChanged:0}}})}catch(e){return A({success:!1,error:"answer_finish_failed"}),{content:[{type:"text",text:`Your reply on ${s} could not be recorded: ${(0,h.getErrorMessage)(e)}. Call complete again.`}],isError:!0}}return A({success:!0}),{content:[{type:"text",text:`Your summary is the reply to the user on ${s}, and it is with them now. Nothing in the project changed, so nothing is checked and nothing waits for a sign-off: it is finished. Stop here.`}]}}for(const t of me.patches)try{const r=await ve.updateRequest(s,{...t,completionSummary:V,completionFormat:"markdown",completionNotChecked:"string"==typeof H&&H.trim()?H.trim():null,cliOrigin:Y,...Z,...ee})
|
|
3731
|
+
e.refsSeen&&r?.namedRefs&&(e.refsSeen.namedRefs=r.namedRefs)}catch(e){return A({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(A({success:!0}),"review-escalated"===me.action)return{content:[{type:"text",text:`Request ${s}: review stopped the work and it went to the user (out of review tries, or completed again unchanged). No further action from you; a human finalizes or sends back must_fix items.`}]}
|
|
3732
|
+
if("review-start"===me.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. If no reviewer finds anything, you are told the review was clean and the request went on with this completion: then do not 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.`}]}
|
|
3733
|
+
if("review-submit"===me.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.`}]}
|
|
3734
|
+
if("signoff-park"===me.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.`}]}
|
|
3735
|
+
if("promise-hold"===me.action){const e=me.detail?` (${me.detail})`:""
|
|
3736
|
+
return{content:[{type:"text",text:`Request ${s}: your change touches the test behind a promise an earlier request made${e}. Only the user changes a promise's test: on the promise card they accept your test, send the work back to put the test back, or retire the promise. Stop here and end your turn; do not call complete or merge again. You are told their answer.`}]}}if("merge-cleared-after-review"===me.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).`}]}
|
|
3737
|
+
if("testing"===me.action){const e="human"===me.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."
|
|
3738
|
+
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"===me.action){const e=te.testsSkipped&&D?`\n\n_Tests skipped: ${D}_`:""
|
|
3739
|
+
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. End your turn with one short line; your completion summary is what the user reads, so do not write it out again.`}]}}if("human-review"===me.action){const e=te.testsSkipped&&D?`\n\n_Tests skipped: ${D}_`:""
|
|
3740
|
+
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 ke,Ie
|
|
3741
|
+
try{ke=await ve.getRequest(s)}catch(e){Ie=e}const $e=(0,v.kindProfile)(re??ke?.type)
|
|
3742
|
+
if("settle"===$e.finalize)return{content:[{type:"text",text:`Request ${s} is complete. A ${$e.label.toLowerCase()} request has no branch, so nothing merges: it closes on its own record now. Stop here and do not call merge.`}]}
|
|
3743
|
+
if(void 0!==Ie)return{content:[{type:"text",text:`Request ${s} is cleared to merge, but fetching its criteria failed: ${(0,h.getErrorMessage)(Ie)}. Call merge with verification evidence for each acceptance criterion.`}]}
|
|
3744
|
+
const xe=ke?.acceptanceCriteria||[]
|
|
3745
|
+
if(!1===te?.gates?.review?.enabled&&(0,S.checksOff)(te)&&!1===te?.gates?.signoff?.enabled&&"merge"===$e.finalize&&(0===xe.length||Array.isArray(F)&&F.length>0)){P.info("gates off and account recorded: merging in the same call",{requestId:s})
|
|
3745
3746
|
const e=await(0,u.handleMergeRequest)({requestId:s,verifications:[]},_,C,T,q,R)
|
|
3746
|
-
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
|
|
3747
|
-
return 0===
|
|
3747
|
+
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 Se=me.skippedReason?`\n\n_Skipped re-running tests: ${me.skippedReason}._\n`:"",Ee=me.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":""
|
|
3748
|
+
return 0===xe.length?{content:[{type:"text",text:`Request ${s} is ready. ${Se}${Ee}No acceptance criteria defined: call merge directly.`}]}:{content:[{type:"text",text:`Request ${s}: all gates are clear.${Se}${Ee}\n\n## Next Step: Verify & Merge\n\n${(0,p.formatVerifyInstructions)(xe)}\n\nUse the merge operation with your verification evidence to merge to main.`}]}}catch(t){let r="An unexpected error occurred",n=""
|
|
3748
3749
|
t instanceof s.ZodError?(r="Validation error",n=t.errors.map(e=>`${e.path.join(".")}: ${e.message}`).join(", ")):t instanceof Error?(r=t.message,n=t.stack||""):n=String(t),P.error("Complete request failed",{error:r,details:n})
|
|
3749
3750
|
try{await(0,i.createFailureToolRecord)("complete-request",t,{error:r,details:n,requestId:e?.requestId},C.getToolRecordClient(),P,q,{requestId:e?.requestId,projectId:_.projectId,note:"Failed to complete request"})}catch{}return q.startTimer("tool_execution_time")({success:!1,error:t instanceof Error?t.name:"unknown"}),{content:[{type:"text",text:`Error: ${r}`}],isError:!0}}}
|
|
3750
3751
|
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),x=t(42692),S=t(64721),E=new Set},32283(e,exports,t){exports.def=void 0
|
|
@@ -3788,7 +3789,7 @@ b.planDocumentId&&A&&b.planDocumentId===A&&(b.planDocumentId=void 0,O=!0)
|
|
|
3788
3789
|
let M=b.planDocumentId
|
|
3789
3790
|
if(!M&&I?.sessionId)try{const e=await(0,h.getActiveSessionPlanId)(w,E,I.sessionId,$)
|
|
3790
3791
|
e&&await w.getDocumentClient().getDocument(e)&&(M=e,$.info("Auto-filled planDocumentId from session-plan association",{sessionId:I.sessionId,planDocumentId:e}))}catch(e){$.warn("planDocumentId auto-fill lookup failed (request created without plan link)",{error:(0,u.getErrorMessage)(e)})}M&&A&&M===A&&(M=void 0,O=!0)
|
|
3791
|
-
const D=await(0,p.exchangeKeyOfCall)(y.mainBasePath,I,"string"==typeof e?.userSaid?e.userSaid:void 0),
|
|
3792
|
+
const D=await(0,p.exchangeKeyOfCall)(y.mainBasePath,I,"string"==typeof e?.userSaid?e.userSaid:void 0),N=D?await(0,p.exchangeKeysWithCall)(y.mainBasePath,I):[],F=r(b.target),z=A?await(0,p.goalCheckOfCall)(y.mainBasePath,I):null,L={title:b.title,description:b.description||"",type:b.type,acceptanceCriteria:[...F,...b.acceptanceCriteria||[]],status:P,...M&&{planDocumentId:M},...A&&{scheduleDocumentId:A},...z&&{goalCheck:z},...b.areas&&b.areas.length>0&&{areas:b.areas},...b.waitsOn&&b.waitsOn.length>0&&{waitsOn:b.waitsOn,waitReason:String(b.waitReason).trim().slice(0,300)},...!0===b.startsNow&&{startsNow:!0},..."string"==typeof b.replaces&&b.replaces&&{replaces:b.replaces},...Array.isArray(b.asked)&&b.asked.length>0&&{asked:b.asked},...Array.isArray(b.userChoices)&&b.userChoices.length>0&&{userChoices:b.userChoices},..."string"==typeof b.process&&b.process.trim()&&{process:b.process.trim()},...void 0!==D&&{exchangeKey:D},...N.length>0&&{exchangeKeysWith:N},...q&&{cliOrigin:q},...I?.sessionId&&{creatorSessionId:I.sessionId},...b.follows&&"new"!==b.follows&&{originRequestId:String(b.follows).replace(/^(round|new):/,"")},..."string"==typeof b.follows&&b.follows.startsWith("new:")&&{ownThread:!0},..."string"==typeof b.follows&&b.follows.startsWith("round:")&&{roundOf:b.follows.slice(6)},..."number"==typeof b.budget&&{budget:b.budget,budgetSaid:"string"==typeof e?.userSaid?e.userSaid:""},...F.length>0&&"string"==typeof e?.userSaid&&{targetSaid:e.userSaid},..."number"==typeof b.estimate&&{estimate:b.estimate}}
|
|
3792
3793
|
$.info("Creating request via request client",{projectId:E,title:b.title,type:b.type,criteriaCount:b.acceptanceCriteria?.length??0})
|
|
3793
3794
|
const B=await T.createRequest(L)
|
|
3794
3795
|
$.debug("Create request response structure",{type:typeof B,isArray:Array.isArray(B),hasData:null!=B,properties:B?Object.keys(B):[],dataPreview:B?JSON.stringify(B).substring(0,200)+"...":"null"})
|
|
@@ -3846,17 +3847,17 @@ const t=e,r="user"===t.by?"the user":"workflow"===t.by?"the workflow":"agent"===
|
|
|
3846
3847
|
if(!r)return""
|
|
3847
3848
|
const n=t.at&&!Number.isNaN(Date.parse(t.at))?`${new Date(t.at).toLocaleString()} `:""
|
|
3848
3849
|
return`${t.discarded?"Discarded":"Canceled"}: ${n}by ${r}${t.via?`, with ${t.via}`:""}`}(I.canceledBy):""
|
|
3849
|
-
let
|
|
3850
|
-
|
|
3850
|
+
let N=`# ${P} Request: ${C} (${s})\nStatus: ${q}${I.endedWithout?`, ended without ${I.endedWithout}`:""}\n${A}${O}Created: ${R}${M?`\n${M}`:""}${D?`\n${D}`:""}\nUpdated: ${j}\n\n## Description\n${I.description||"_No description provided._"}\n\n## Progress Summary\n- Tasks: ${x.completed}/${x.total} completed (${Math.round(x.completed/x.total*100)||0}%)\n- Acceptance Criteria: ${E}/${S.length} proven (${Math.round(E/S.length*100)||0}%)\n`
|
|
3851
|
+
N+=`\n## Task Status\n- Pending: ${x.pending}\n- In Progress: ${x.in_progress}\n- Blocked: ${x.blocked}\n- Ready for Review: ${x.ready_for_review}\n- Completed: ${x.completed}\n- Reverted: ${x.reverted}\n`,N+="\n## Tasks\n",0===$.length?N+="No tasks found for this request.\n":$.forEach(e=>{N+=`**[${e.id}]** ${e.title} (Status: ${e.status})\n`}),y.info(`Added task listing with ${$.length} tasks`),N+="\n## Acceptance Criteria\n",N+=(0,c.formatCriteriaWithHashes)(S)+"\n","ready_for_review"===I.status&&S.some(e=>!(0,c.criterionProven)(e))&&(N+="\n> **Status: ready_for_review**: Use the merge operation to verify criteria and merge to main.\n")
|
|
3851
3852
|
try{const e=await f.proxyGet(`/api/projects/${t.projectId}/requests/${s}/record-for-agents`),r=String((e?.data??e)?.text??"").trim()
|
|
3852
|
-
r&&(
|
|
3853
|
+
r&&(N+=`\n${r}\n`)}catch(e){y.debug("record for agents unavailable",{error:(0,a.getErrorMessage)(e)})}if(_&&(N+=`\n## Test Coverage\n- Test Cases: ${_.totalTestCases||0}\n- Criteria Tested: ${_.criteriaTestedCount||0}/${S.length}\n`,_.criterionCoverage&&Array.isArray(_.criterionCoverage)&&(N+="\n### Detailed Test Coverage:\n",_.criterionCoverage.forEach(e=>{N+=`- Criterion ${e.criterionId}: ${e.coverage} coverage\n`}))),!["completed","reverted","canceled","cancelled","draft"].includes(String(q)))try{const e=await f.proxyGet(`/api/projects/${t.projectId}/requests/${s}/sessions`),r=Array.isArray(e)?e:e?.data??[]
|
|
3853
3854
|
if(r.length>0){const e=await(0,d.getServerUrl)(t,y).catch(()=>null),n=await(0,l.readLiveWorker)(e??null,r)
|
|
3854
|
-
|
|
3855
|
+
N+=`\n## Live\n${n?(0,l.formatLiveWorker)(n):`- ${r.length} session(s) worked this request; the engine did not answer about the latest one.`}\n`
|
|
3855
3856
|
let o=null
|
|
3856
3857
|
try{const e=await f.proxyGet(`/api/projects/${t.projectId}/provider-block`),r=e?.data??e
|
|
3857
3858
|
r&&r.providerID&&(o=r)}catch{}const s=await(0,l.readSessionModels)(e??null,r).catch(()=>[]),i=(0,l.formatSessionModels)(s,o)
|
|
3858
|
-
i&&(
|
|
3859
|
-
|
|
3859
|
+
i&&(N+=`\n${i}\n`)}}catch(e){y.debug("live worker view unavailable",{error:(0,a.getErrorMessage)(e)})}return I.recentActivity&&Array.isArray(I.recentActivity)&&(N+="\n## Recent Activity\n",I.recentActivity.slice(0,5).forEach(e=>{const t=new Date(e.timestamp).toLocaleString()
|
|
3860
|
+
N+=`- ${t}: ${e.description}\n`})),w({success:!0}),{content:[{type:"text",text:N}]}}catch(t){return y.error("Error getting request information",{args:e,error:t instanceof Error?t.stack:String(t)}),w({success:!1,error:t instanceof Error?t.name:"unknown"}),t instanceof s.ConnectionError?{content:[{type:"text",text:"Unable to get request information: Connection to the server failed. Please check your network connection and try again."}],isError:!0}:{content:[{type:"text",text:`Error getting request information: ${(0,a.getErrorMessage)(t)}`}],isError:!0}}}
|
|
3860
3861
|
const r=t(87780),n=t(3126),o=t(59860),s=t(15674),i=t(82960),a=t(58824),c=t(65454),l=t(80024),d=t(47617),u=t(12978),h=t(44558),p=t(52232)},49229(e,exports,t){exports.handleListAreas=async function(e,t,o,s){const i=o.child({tool:"list_areas"}),a=s.startTimer("tool_execution_time")
|
|
3861
3862
|
try{const n=e.projectId
|
|
3862
3863
|
if(!n)throw new r.ValidationError("Project ID is required for listing areas")
|
|
@@ -3964,10 +3965,10 @@ if(o)return _({success:!1,reason:"empty_branch"}),{content:[{type:"text",text:o}
|
|
|
3964
3965
|
if(await(0,h.branchAddsNoContent)(t,r,`requests/${C}`))return _({success:!1,reason:"identical_tree"}),{content:[{type:"text",text:(0,h.identicalTreeBlockMessage)(C,r)}],isError:!0}}catch(e){E.warn("Could not compute merge-base / ahead count; proceeding (the merge path surfaces a real branch error)",{requestId:C,error:(0,u.getErrorMessage)(e)})}let A="workspace-manager"
|
|
3965
3966
|
const O=await e.getRequest(C),M=O?.title||`Request ${C}`
|
|
3966
3967
|
let D="main"
|
|
3967
|
-
try{D=await(0,w.resolveDefaultBranch)(I.mainBasePath,I.gitBinary)}catch{}const
|
|
3968
|
-
if(!
|
|
3969
|
-
if(
|
|
3970
|
-
let
|
|
3968
|
+
try{D=await(0,w.resolveDefaultBranch)(I.mainBasePath,I.gitBinary)}catch{}const N=I.git?await(0,c.findCommittedMarkers)(I.git,`requests/${C}`):{files:[],determined:!1}
|
|
3969
|
+
if(!N.determined)return _({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}
|
|
3970
|
+
if(N.files.length>0)return _({success:!1,reason:"unresolved_markers"}),{content:[{type:"text",text:`Cannot merge ${C}: committed conflict markers remain in ${N.files.join(", ")}. Resolve every <<<<<<< block with your file tools, verify, then merge again.`}],isError:!0}
|
|
3971
|
+
let F
|
|
3971
3972
|
I.mainBasePath&&await(0,l.foldAtDoor)({repoDir:I.mainBasePath,branch:`requests/${C}`,defaultBranch:D,requestId:C,door:"merge",gitBinary:I.gitBinary,postRecord:e=>$.proxyPost(`/api/projects/${I.projectId}/tool-records`,e),log:E})
|
|
3972
3973
|
let z=null
|
|
3973
3974
|
{const e=await(0,f.runMergeQueueGate)({projectPath:q,requestId:C,branchName:`requests/${C}`,defaultBranch:D,requestTitle:O?.title,criteria:O?.acceptanceCriteria??O?.acceptance_criteria,testGateOff:(0,n.checksOff)(O),source:"merge_request",gitBinary:I.gitBinary,logger:E,fetchRequestRuns:async()=>{const e=await $.proxyGet(`/api/projects/${I.projectId}/requests/${C}/test-runs`),t=e?.data??e??[]
|
|
@@ -3988,12 +3989,12 @@ if("error"===e.action)return _({success:!1,error:"merge_queue_error"}),{content:
|
|
|
3988
3989
|
if("sent-back"===e.action)return _({success:!1,error:"sent_back"}),await(0,m.bringMainIn)({projectPath:q,requestId:C,defaultBranch:D,logger:E}),{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}
|
|
3989
3990
|
if("review-held"===e.action){_({success:!1,error:"review_held"})
|
|
3990
3991
|
const t=await(0,m.holdForMainChangeReview)({projectPath:q,projectId:I.projectId,requestId:C,defaultBranch:D,touched:e.touched,logger:E,post:(e,t)=>$.proxyPost(e,t)})
|
|
3991
|
-
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}}"conflicts"===e.action&&(z=new Error(`Merge of requests/${C} into ${D} has conflicts (detected on the prospective merge tree):\n${e.summary}`),z.conflictInfo={conflictedFiles:[],workingDirectory:void 0}),"land"===e.action&&(
|
|
3992
|
-
P=await I.workspaceManager.completeRequest(C,M,
|
|
3992
|
+
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}}"conflicts"===e.action&&(z=new Error(`Merge of requests/${C} into ${D} has conflicts (detected on the prospective merge tree):\n${e.summary}`),z.conflictInfo={conflictedFiles:[],workingDirectory:void 0}),"land"===e.action&&(F=e.certifiedCommitSha,E.info("merge-queue: landing certified catch-up commit (tested tree == merged tree)",{requestId:C,certifiedCommitSha:F.slice(0,8)}))}try{if(z)throw z
|
|
3993
|
+
P=await I.workspaceManager.completeRequest(C,M,F?{certifiedCommitSha:F}:void 0),A=F?"merge-queue-certified":P.autoMergeInfo?.strategy||"workspace-manager",E.info("Merge completed",{requestId:C,mergeCommitHash:P.mergeCommitHash,mergeMethod:A})}catch(t){if(!t.conflictInfo){const e=(0,u.getErrorMessage)(t)
|
|
3993
3994
|
return _({success:!1,error:"merge_failure"}),{content:[{type:"text",text:`Merge failed: ${e}`}],isError:!0}}{const r=t.conflictInfo.workingDirectory||I.workspaceManager?.getCurrentContext(I.currentSessionId)?.basePath||void 0,n=`requests/${C}`,o=await(0,c.reconcileBranchWithMain)({projectPath:I.basePath,requestId:C,branchName:n,defaultBranch:D,worktreePath:r})
|
|
3994
3995
|
if("clean"!==o.outcome){if("conflicts-committed"===o.outcome){try{await e.updateRequest(C,{status:"resolving_conflicts"}),E.info("Set status to resolving_conflicts",{requestId:C})}catch(e){E.error("Failed to set resolving_conflicts status",{requestId:C,error:(0,u.getErrorMessage)(e)})}const t=(0,c.buildResolveInstruction)({requestId:C,branchName:n,defaultBranch:D,landed:o.landed,conflictedFiles:o.conflictedFiles,blocks:o.blocks,keptBoth:o.keptBoth,retryVia:"merge"})
|
|
3995
3996
|
return _({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:t}],isError:!1}}return _({success:!1,reason:"merge_conflicts"}),{content:[{type:"text",text:`${C} conflicts with ${D}, and ${D} could not be brought into its branch: ${(0,c.reconcileRefusal)(o)}. Fix that, then merge again.`}],isError:!0}}E.info("Branch reconciled with main cleanly: retrying merge",{requestId:C})
|
|
3996
|
-
try{P=await I.workspaceManager.completeRequest(C,M),A="reconcile-retry"}catch(e){return _({success:!1,error:"merge_failure_after_reconcile"}),{content:[{type:"text",text:`Merge failed after clean reconcile: ${(0,u.getErrorMessage)(e)}`}],isError:!0}}}}const L=I.workspaceManager.getCurrentContext(I.currentSessionId)?.basePath||I.basePath,B=
|
|
3997
|
+
try{P=await I.workspaceManager.completeRequest(C,M),A="reconcile-retry"}catch(e){return _({success:!1,error:"merge_failure_after_reconcile"}),{content:[{type:"text",text:`Merge failed after clean reconcile: ${(0,u.getErrorMessage)(e)}`}],isError:!0}}}}const L=I.workspaceManager.getCurrentContext(I.currentSessionId)?.basePath||I.basePath,B=F??P?.mergeCommitHash
|
|
3997
3998
|
B&&(S=await(0,i.firstParentSha)((0,i.gitFor)(L,I.git,I.gitBinary),B)??S)
|
|
3998
3999
|
const H=await(0,i.computeDiffStats)((0,i.gitFor)(L,I.git,I.gitBinary),S,P?.mergeCommitHash),U=B&&(0,n.checksOff)(O)?await(0,b.landedOnTopOf)(L,B,C).catch(()=>[]):[]
|
|
3999
4000
|
try{await $.proxyPost(`/api/projects/${I.projectId}/requests/${C}/finalize-completion`,{summary:O?.completionSummary||`Merged request ${C}`,mergeResult:{mergeCommitHash:P.mergeCommitHash,mergeBaseSha:S,mergedFiles:P.mergedFiles,cleanedUp:P.cleanedUp,method:A,diffStats:H,...U.length?{notCheckedWith:U}:{}}})}catch(e){const t=(0,u.getErrorMessage)(e)
|
|
@@ -4064,7 +4065,7 @@ const t=new Map,r=(e,t)=>`${e}\0${t}`
|
|
|
4064
4065
|
exports.SHAPING_AGENTS=new Set(["request-architect","planner"])
|
|
4065
4066
|
const n=new Map},74165(e,exports,t){exports.def=void 0
|
|
4066
4067
|
const r=t(76183),n=t(84648)
|
|
4067
|
-
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 a completed request.\n\u2022 reopen - Send a finished, unmerged request back to rework with your reason (if the result already satisfies the feedback, say so instead; a merged one gets a follow-up).\n\u2022 update - Title, description, type, criteria, process, budget, target.\n\u2022 activate - Start a draft when the user asks (a draft held by intake: answer its ask).\n\u2022 get-info - Everything about a request.\n\u2022 list / search - List, or full-text search.\n\u2022 status - Where live work stands, per thread: done, running, next, what waits on the user, spend, anything stuck (requestId: its thread; planId: that plan's; neither: everything live).\n\u2022 list-areas / update-areas - The project's work areas / replace the request's.\n\u2022 wait-on / clear-wait - Record that this request waits for another (waitsOn, reason in the user's words) whenever work must queue; it ends when the blocker merges (a canceled one never does: move the wait). clear-wait without waitsOn clears all.\n\u2022 cancel - A request the user no longer wants (before merge), with reason; prose to a worker cancels nothing.\n\u2022 hold - Back to draft for an open request no worker has picked up (plan steps too); Start resumes it.\n\u2022 delete-empty - Delete a request with no tasks.\n\u2022
|
|
4068
|
+
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 a completed request.\n\u2022 reopen - Send a finished, unmerged request back to rework with your reason (if the result already satisfies the feedback, say so instead; a merged one gets a follow-up).\n\u2022 update - Title, description, type, criteria, process, budget, target.\n\u2022 activate - Start a draft when the user asks (a draft held by intake: answer its ask).\n\u2022 get-info - Everything about a request.\n\u2022 list / search - List, or full-text search.\n\u2022 status - Where live work stands, per thread: done, running, next, what waits on the user, spend, anything stuck (requestId: its thread; planId: that plan's; neither: everything live).\n\u2022 list-areas / update-areas - The project's work areas / replace the request's.\n\u2022 wait-on / clear-wait - Record that this request waits for another (waitsOn, reason in the user's words) whenever work must queue; it ends when the blocker merges (a canceled one never does: move the wait). clear-wait without waitsOn clears all.\n\u2022 cancel - A request the user no longer wants (before merge), with reason; prose to a worker cancels nothing.\n\u2022 hold - Back to draft for an open request no worker has picked up (plan steps too); Start resumes it.\n\u2022 delete-empty - Delete a request with no tasks.\n\u2022 hand-over - Give the user's message to the worker still warm on requestId, for an adjustment to its work; refused when none is live.\n\u2022 round-piece - On a round, when the user's message is a separate piece of work, not a change to this request: the round becomes its own request (update.title: what it makes).\n\u2022 bring-main-in - Merge main's newer work into your branch now; conflicts come back marked for you. Main also comes in when it changed a file you changed, and at sign-off.\n\u2022 check-target - Read the target of a thread on main now, met or missed.",schema:r.RequestOperationsSchema,fieldTags:{planId:["status","check-target"],criterion:["dispute-criterion"],reason:["cancel","revert","reopen","wait-on","premise-block","hold"],waitsOn:["wait-on","clear-wait"],update:["update"],"update.budget":["budget"],"update.target":["target"],"update.arcName":["arc-name"],"update.process":["process"],"update.estimate":["estimate"],areas:["update-areas"],complete:["complete"],"complete.couldNotCheck":["drive-claims"],merge:["merge"],revert:["revert"],list:["list"],search:["search"]},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,x,S,E,_,C){const T=S.child({tool:"request_operations"})
|
|
4068
4069
|
try{const q=e
|
|
4069
4070
|
let{requestId:R}=q
|
|
4070
4071
|
const{operation:j}=q
|
|
@@ -4130,10 +4131,7 @@ try{const t=await(0,I.checkThreadTarget)(x.getProxyClient(),e,r)
|
|
|
4130
4131
|
return(0,w.withAlchemyKind)({content:[{type:"text",text:t.text}],...t.isError?{isError:!0}:{}},$["check-target"])}catch(e){return{content:[{type:"text",text:`\u274c The target was not checked: ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}case"round-piece":{const e="string"==typeof q.update?.title?q.update.title.trim():""
|
|
4131
4132
|
if(!R||!e)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** round-piece needs requestId (the round) and update.title (what the separate work makes, in a few words)"}],isError:!0}
|
|
4132
4133
|
try{const n=await x.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${R}/round-piece`,{title:e,...t.currentSessionId?{sessionId:t.currentSessionId}:{}}),o=n?.data??n
|
|
4133
|
-
return(0,w.withAlchemyKind)({content:[{type:"text",text:(0,r.ownRequestResult)(R,String(o?.was??""),String(o?.title??e),"string"==typeof o?.line?o.line:"")}]},$["round-piece"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}case"
|
|
4134
|
-
if(!R||!e)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** asked-no-change needs requestId and userSaid (the user's message, quoted exactly)"}],isError:!0}
|
|
4135
|
-
try{const r=await x.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${R}/steers/asked-no-change`,{userSaid:e}),n=r?.data??r,o=n?.marked?`Recorded: that message on ${R} asked for no change, so it is not listed as one.`:String(n?.reason??"Nothing to record.")
|
|
4136
|
-
return(0,w.withAlchemyKind)({content:[{type:"text",text:o}]},$["asked-no-change"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}case"dispute-criterion":{const e="string"==typeof q.criterion?q.criterion.trim():""
|
|
4134
|
+
return(0,w.withAlchemyKind)({content:[{type:"text",text:(0,r.ownRequestResult)(R,String(o?.was??""),String(o?.title??e),"string"==typeof o?.line?o.line:"")}]},$["round-piece"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}case"dispute-criterion":{const e="string"==typeof q.criterion?q.criterion.trim():""
|
|
4137
4135
|
if(!R||!e||!q.reason?.trim())return{content:[{type:"text",text:"\u274c **Request Operations Failed:** dispute-criterion needs requestId, criterion (its key, e.g. 1a) and reason (why it does not fit this request, for the user to read)"}],isError:!0}
|
|
4138
4136
|
try{const r=await x.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${R}/criteria/${encodeURIComponent(e)}/dispute`,{reason:q.reason,sessionId:t.currentSessionId}),n=r?.data??r,o={asked:`The user has a card: waive ${n?.key??e} for ${R}, or keep it. Carry on with the rest of the work; you are told the answer. Until it is waived, the criterion still binds.`,"already-asked":`The user already has a card for ${n?.key??e} on ${R}; you are told the answer. Until it is waived, the criterion still binds.`,waived:`${n?.key??e} is already waived for ${R}; it no longer binds.`,kept:`The user already chose to keep ${n?.key??e} on ${R}: it applies here. Satisfy it.`}
|
|
4139
4137
|
return(0,w.withAlchemyKind)({content:[{type:"text",text:o[n?.status]??"Dispute recorded."}]},$["dispute-criterion"])}catch(e){return{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}case"premise-block":if(!R)return{content:[{type:"text",text:"\u274c **Request Operations Failed:** requestId is required for premise-block"}],isError:!0}
|
|
@@ -4144,7 +4142,7 @@ return!1===r?.blocked?(0,w.withAlchemyKind)({content:[{type:"text",text:`\u26a0\
|
|
|
4144
4142
|
try{const e=x.getProxyClient(),r=await e.proxyGet(`/api/projects/${t.projectId}/tasks`,{requestId:R,limit:1}),n=r?.data||r||[]
|
|
4145
4143
|
return(Array.isArray(n)?n.length:0)>0?{content:[{type:"text",text:`\u274c Cannot delete ${R}: it has tasks. Only empty requests (no tasks created) can be deleted.`}],isError:!0}:(await e.proxyDelete(`/api/projects/${t.projectId}/requests/${R}`),T.info("Empty request deleted",{requestId:R}),(0,w.withAlchemyKind)({content:[{type:"text",text:`\u2705 Request ${R} deleted (had no tasks).`}]},$["delete-empty"]))}catch(e){return{content:[{type:"text",text:`\u274c **Failed to delete request:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}default:{const e=`Unknown operation type: ${j}`
|
|
4146
4144
|
return T.error(e),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${e}`}],isError:!0}}}}catch(e){return T.error("Request operations failed",{error:e instanceof Error?e.stack:String(e)}),E.incrementCounter("request_operations_errors",1),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}
|
|
4147
|
-
const r=t(95765),n=t(95994),o=t(15715),s=t(16416),i=t(19670),a=t(5434),c=t(29729),l=new Set(["ready_for_review","completing","resolving_conflicts","conflicted"]),d=t(98037),u=t(80233),h=t(49229),p=t(23130),f=t(72271),m=t(36651),g=t(34306),y=t(58824),w=t(82272),b=t(62032),v=t(12286),k=t(79414),I=t(83326),$={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",hold:"hold-request","premise-block":"premise-block-request","dispute-criterion":"dispute-criterion","
|
|
4145
|
+
const r=t(95765),n=t(95994),o=t(15715),s=t(16416),i=t(19670),a=t(5434),c=t(29729),l=new Set(["ready_for_review","completing","resolving_conflicts","conflicted"]),d=t(98037),u=t(80233),h=t(49229),p=t(23130),f=t(72271),m=t(36651),g=t(34306),y=t(58824),w=t(82272),b=t(62032),v=t(12286),k=t(79414),I=t(83326),$={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",hold:"hold-request","premise-block":"premise-block-request","dispute-criterion":"dispute-criterion","hand-over":"hand-over","bring-main-in":"bring-main-in","check-target":"check-target","round-piece":"round-piece","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"}`
|
|
4148
4146
|
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)
|
|
4149
4147
|
if(e){const t=String(e)
|
|
4150
4148
|
r.updatedAfter=n(t)?new Date(`${t}T00:00:00`).toISOString():new Date(t).toISOString()}if(t){const e=String(t)
|
|
@@ -4532,7 +4530,7 @@ x.initialScan={totalEntries:t.length,directories:r,files:n,firstFewFiles:n.slice
|
|
|
4532
4530
|
x.errors.push(t),d.error(t)}const M=_?"file":"directory"
|
|
4533
4531
|
d.info(`Searching for "${e}" in ${M} ${S} (mode: ${j})`,{needle:e,searchTarget:M,path:S,options:{maxResults:C,caseSensitive:T,fileExtension:O,searchIn:j}}),$=Date.now(),x.phases.initializationTime=$-I
|
|
4534
4532
|
const D=Date.now()
|
|
4535
|
-
let
|
|
4533
|
+
let N=[],F=0,z=0
|
|
4536
4534
|
try{const e=await async function(e,t,n,o,s,i,a){const{maxResults:c,fileExtension:l,searchIn:d,isFile:u,contextLines:f}=n,m=[]
|
|
4537
4535
|
if(s.info("performLocalSearch started",{searchPath:e,pattern:t.toString(),options:n}),u)return await async function(e,t,r,n,o,s=0){const i=[],a=h.default.basename(e)
|
|
4538
4536
|
let c
|
|
@@ -4549,16 +4547,16 @@ const g={path:e,filesProcessed:0,filesMatched:0,subdirectories:0,errors:[]}
|
|
|
4549
4547
|
try{const n=a||e,l=await(0,b.gitVisibleFilter)(n),{filesCount:u,fileMatchesCount:h,subdirCount:p,subdirStats:y}=await r(e,t,m,c,o,s,0,g,d,f,a,l?{filter:l,root:n}:void 0)
|
|
4550
4548
|
return g.filesProcessed=u,g.filesMatched=h,g.subdirectories=p,i.directoryStats.push(g),y&&y.length>0&&i.directoryStats.push(...y),s.info("performLocalSearch completed",{totalFiles:u,matchesFound:m.length,directories:p+1}),{matches:m,totalFiles:u}}catch(t){const r=`Error in performLocalSearch: ${(0,k.getErrorMessage)(t)}`
|
|
4551
4549
|
return g.errors.push(r),i.directoryStats.push(g),i.errors.push(r),s.error(r,{error:t instanceof Error?t.stack:String(t),searchPath:e}),{matches:m,totalFiles:g.filesProcessed}}}(S,A,{maxResults:C,fileExtension:O,searchIn:j,isFile:_,contextLines:P},O,d,x,E)
|
|
4552
|
-
|
|
4550
|
+
N=e.matches,F=e.totalFiles
|
|
4553
4551
|
const t=1e3
|
|
4554
|
-
|
|
4555
|
-
return r?r===h.default.basename(t)?r:r.startsWith("./")?r.substring(2):r:h.default.basename(t)}(S,e.file)}),d.info(`Local search completed: found ${z||
|
|
4556
|
-
x.errors.push(t),d.error(t),x.phases.searchTime=Date.now()-D}x.totalFiles=
|
|
4552
|
+
N.length>0&&N.length<=t?(N.sort((e,t)=>void 0!==e.modifiedTime&&void 0!==t.modifiedTime?t.modifiedTime-e.modifiedTime:void 0!==e.modifiedTime&&void 0===t.modifiedTime?-1:void 0===e.modifiedTime&&void 0!==t.modifiedTime?1:0),d.info(`Sorted ${N.length} matches by modification time (newest first)`)):N.length>t&&d.info(`Skipped sorting ${N.length} matches (exceeds hard cap of ${t} for performance)`),z=N.length,N.length>C&&(N=N.slice(0,C),d.info(`Limited results to ${C} (from ${z} total matches)`)),N.forEach(e=>{e.relativePath=function(e,t){const r=h.default.relative(e,t)
|
|
4553
|
+
return r?r===h.default.basename(t)?r:r.startsWith("./")?r.substring(2):r:h.default.basename(t)}(S,e.file)}),d.info(`Local search completed: found ${z||N.length} total matches in ${F} files, returning ${N.length} results`),x.phases.searchTime=Date.now()-D}catch(e){const t=`Error during search: ${(0,k.getErrorMessage)(e)}`
|
|
4554
|
+
x.errors.push(t),d.error(t),x.phases.searchTime=Date.now()-D}x.totalFiles=F,x.totalMatches=N.length,x.endTime=Date.now()
|
|
4557
4555
|
let L,B=""
|
|
4558
4556
|
try{if(n.workspaceManager){const e=n.workspaceManager.getCurrentContext(n.currentSessionId)
|
|
4559
4557
|
e.branchName&&!e.permanentBranch&&(B=e.isMainBranch?` (searched on main branch: ${e.branchName})`:` (searched on request branch: ${e.branchName}${e.requestId?` for ${e.requestId}`:""})`)}}catch(e){d.debug("Could not get branch context",{error:e}),B=" (branch context unavailable)"}d.debug("Search diagnostic information",{diagnostic:x})
|
|
4560
4558
|
const H="content"===j?"content":"filename"===j?"filenames":"content and filenames"
|
|
4561
|
-
if(z>
|
|
4559
|
+
if(z>N.length?(L=`Found ${z} results for "${e}" (searching ${H})`,a&&(L+=` in ${a}`),R&&(L+=` with extension ${R}`),L+=`${B}. Showing first ${N.length}:\n\n`):(L=`Found ${N.length} results for "${e}" (searching ${H})`,a&&(L+=` in ${a}`),R&&(L+=` with extension ${R}`),L+=`${B}:\n\n`),N.length>0)if(P>0){const{filenameMatches:e,mergedGroups:t}=function(e,t){const r=e.filter(e=>"filename"===e.matchType),n=e.filter(e=>"content"===e.matchType)
|
|
4562
4560
|
if(0===n.length)return{filenameMatches:r,mergedGroups:[]}
|
|
4563
4561
|
const o=new Map
|
|
4564
4562
|
for(const e of n){const t=o.get(e.file)||[]
|
|
@@ -4566,7 +4564,7 @@ t.push(e),o.set(e.file,t)}const i=[]
|
|
|
4566
4564
|
for(const[e,r]of o.entries()){r.sort((e,t)=>e.line-t.line)
|
|
4567
4565
|
let e=[r[0]]
|
|
4568
4566
|
for(let n=1;n<r.length;n++){const o=e[e.length-1],a=r[n],c=o.line+t
|
|
4569
|
-
a.line-t<=c+1?e.push(a):(i.push(s(e,t)),e=[a])}e.length>0&&i.push(s(e,t))}return{filenameMatches:r,mergedGroups:i}}(
|
|
4567
|
+
a.line-t<=c+1?e.push(a):(i.push(s(e,t)),e=[a])}e.length>0&&i.push(s(e,t))}return{filenameMatches:r,mergedGroups:i}}(N,P),r=[]
|
|
4570
4568
|
e.forEach(e=>{r.push(`${e.relativePath} [filename match]`)}),t.forEach(e=>{const t=[]
|
|
4571
4569
|
e.lines.forEach(e=>{e.isMatch?t.push(`> ${e.lineNumber}: ${e.content}`):t.push(` ${e.lineNumber}: ${e.content}`)}),r.push(`${e.relativePath}:\n${t.join("\n")}`)}),L+=r.join("\n\n")}else L+=await async function(e){const t=e.filter(e=>"filename"===e.matchType),r=e.filter(e=>"content"===e.matchType),n=[]
|
|
4572
4570
|
t.length>0&&t.forEach(e=>{n.push(`${e.relativePath} [filename match]`)})
|
|
@@ -4577,7 +4575,7 @@ const r=t[0].relativePath||e,o=t.length,s=await i(e)
|
|
|
4577
4575
|
let a=r
|
|
4578
4576
|
void 0!==s&&(a+=` (${s} lines)`),a+=` - ${o} ${1===o?"match":"matches"}:`
|
|
4579
4577
|
const c=t.map(e=>` :${e.line} - ${e.text}`)
|
|
4580
|
-
n.push(a+"\n"+c.join("\n"))}return n.join("\n\n")}(
|
|
4578
|
+
n.push(a+"\n"+c.join("\n"))}return n.join("\n\n")}(N)
|
|
4581
4579
|
else L+="No matches found."
|
|
4582
4580
|
return u({success:!0}),{content:[{type:"text",text:L}],...(0,w.alchemyMeta)({kind:"search"})}}catch(e){d.error("Search failed",{args:t,error:e instanceof Error?e.stack:String(e)}),x.errors.push((0,k.getErrorMessage)(e)),x.endTime=Date.now(),d.debug("Search diagnostic information on error",{diagnostic:x})
|
|
4583
4581
|
let r=`Search failed: ${(0,k.getErrorMessage)(e)}`
|
|
@@ -4693,11 +4691,11 @@ let T=await async function(e,t,r,n){if("parent"===e){const e=await s(t,r,n)
|
|
|
4693
4691
|
return e?(n.info('Resolved toSessionId "parent" to actual ID',{parentId:e}),e):void n.warn('report toSessionId "parent" but this session has no parent; recording without a parent target',{fromSessionId:t})}return void 0===e?void 0:(0,P.asExternal)(e)}(m,C,c,r),R=!1
|
|
4694
4692
|
if(T)try{const[e,t]=await Promise.all([n(C),n(T)])
|
|
4695
4693
|
"planner"===e&&"alchemist"===t&&(T=void 0,R=!0)}catch{}if(!("alternatives"!==g||y.options&&Array.isArray(y.options)))throw new v.ValidationError("Not sent: an alternatives question needs questionContent.options, a list of { id, label, description } INSIDE questionContent (options anywhere else are dropped). Nothing was posted.")
|
|
4696
|
-
let j="",A=0,D=null,
|
|
4694
|
+
let j="",A=0,D=null,N=!1,F=!1
|
|
4697
4695
|
if(!T){let n="balanced",o=!1
|
|
4698
4696
|
try{const e=await l.getInteractionPosture(C)
|
|
4699
4697
|
n=e.posture,o=!0===e.humanInputFloor}catch(e){r.warn("Question gate: posture lookup failed, defaulting to balanced",{error:(0,k.getErrorMessage)(e)})}const s="string"==typeof e.userSaid?e.userSaid:"",i=!!s&&await(0,O.userSaidOnRequest)(c,t.projectId,"",s,{anywhere:!0}).catch(()=>!1)
|
|
4700
|
-
i&&(y.onExpiry="escalate",
|
|
4698
|
+
i&&(y.onExpiry="escalate",F=!0),"how"===y.steer&&y.defaultOption&&(y.onExpiry="escalate")
|
|
4701
4699
|
const u=(0,S.resolveQuestionGate)({targeted:!1,hasDefault:!!y.defaultOption,priority:w,onExpiry:y.onExpiry,humanInputFloor:o,namesCriteria:_.length>0,offersNotYet:(0,S.offersNotYet)(y.options),userAskedToBeAsked:i},n)
|
|
4702
4700
|
if(o&&"ask-now"===u.disposition&&($=!0),r.info("Question gate resolved",{posture:n,disposition:u.disposition,reason:u.reason}),"self-answer"===u.disposition)try{const e=await l.createQuestionSettleAware({fromSessionId:C,questionType:g,question:y.question,options:y.options,context:y.context,defaultOption:y.defaultOption,onExpiry:y.onExpiry,...y.steer?{steer:y.steer}:{},...!0===y.changeableLater?{changeableLater:!0}:{},priority:w,expiresInHours:b,...I?{relatesTo:I}:{},...Array.isArray(y.fields)&&y.fields.length>0?{fields:y.fields}:{},...x,..._.length>0?{criteria:_}:{},takenOnDefault:!0})
|
|
4703
4701
|
if("settled"===e.kind)return{content:[{type:"text",text:d(e.settled)}]}
|
|
@@ -4710,8 +4708,8 @@ if("settled"===e.kind)return{content:[{type:"text",text:d(e.settled)}]}
|
|
|
4710
4708
|
const t=e.message
|
|
4711
4709
|
return{content:[{type:"text",text:`\u{1f4e5} **Question held for the next checkpoint** (${u.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,k.getErrorMessage)(e)})}}else u.hint&&(j=`\n\n\u{1f4a1} ${u.hint}`)}const h=y.context
|
|
4712
4710
|
if(!("object"==typeof h&&null!==h||"string"==typeof h&&/^[\[{]/.test(h.trim()))){const e=(0,S.resolveHoldPolicy)(n,{proceedsOnDefault:(0,S.proceedsOnDefault)({defaultOption:y.defaultOption,onExpiry:y.onExpiry,steer:y.steer,priority:w,namedCriteria:_.length})})
|
|
4713
|
-
A=e.holdMs,D=n,
|
|
4714
|
-
const z=await l.createQuestionSettleAware({fromSessionId:C,toSessionId:T,questionType:g,question:y.question,options:y.options,context:y.context,allowMultiple:y.allowMultiple,...Array.isArray(y.fields)&&y.fields.length>0?{fields:y.fields}:{},defaultOption:y.defaultOption,onExpiry:"collaborative"===D?"escalate":y.onExpiry,...y.steer?{steer:y.steer}:{},...!0===y.changeableLater?{changeableLater:!0}:{},priority:w,expiresInHours:b,...I?{relatesTo:I}:{},...A>0?{holdUntil:new Date(Date.now()+A).toISOString()}:{},...
|
|
4711
|
+
A=e.holdMs,D=n,N=0===A&&"balanced"===(0,S.normalizePosture)(n),A>0&&r.info("Question will sync-hold",{holdMs:A,posture:n,reason:e.reason})}}r.info("Creating question",{fromSessionId:C,toSessionId:T,questionType:g})
|
|
4712
|
+
const z=await l.createQuestionSettleAware({fromSessionId:C,toSessionId:T,questionType:g,question:y.question,options:y.options,context:y.context,allowMultiple:y.allowMultiple,...Array.isArray(y.fields)&&y.fields.length>0?{fields:y.fields}:{},defaultOption:y.defaultOption,onExpiry:"collaborative"===D?"escalate":y.onExpiry,...y.steer?{steer:y.steer}:{},...!0===y.changeableLater?{changeableLater:!0}:{},priority:w,expiresInHours:b,...I?{relatesTo:I}:{},...A>0?{holdUntil:new Date(Date.now()+A).toISOString()}:{},...N?{proceededOnDefault:!0}:{},...F?{userAsked:!0}:{},...$?{forceAsk:!0}:{},...x,..._.length>0?{criteria:_}:{}})
|
|
4715
4713
|
if("settled"===z.kind)return{content:[{type:"text",text:d(z.settled)}]}
|
|
4716
4714
|
const L=z.message
|
|
4717
4715
|
if(!T&&"responded"===L?.status&&"self-driving"===L?.response?.settledBy)return{content:[{type:"text",text:f(L,y)}]}
|
|
@@ -4721,7 +4719,7 @@ if("collaborative"===D)return{content:[{type:"text",text:`\u23f3 **Question post
|
|
|
4721
4719
|
if(y.defaultOption)return{content:[{type:"text",text:`\u{1f4e8} **Question posted as ${e}; the user is not on the page right now.**\n\n**Proceed on your default now: ${String(y.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.${(0,M.namedRefsNote)(L?.content?.namedRefs)}`}]}}if(A>0){const e=L.messageCode,t=await(0,E.runSyncHold)({client:l,logger:r,messageCode:e,holdMs:A,proceededOnDefault:"collaborative"!==D&&!!y.defaultOption})
|
|
4722
4720
|
if("answered"===t.kind)return{content:[{type:"text",text:`\u2705 **Answered while you waited.**\n\n${a(t.message)}\n\nContinue based on this answer now.${(0,M.namedRefsNote)(L?.content?.namedRefs)}`}]}
|
|
4723
4721
|
let n
|
|
4724
|
-
return n="expired"===t.kind?`\u23f3 **The question expired unanswered** (**${e}**). Decide yourself now: pick the best option, record it with the record-decision operation, and proceed. No automatic resume will come for this question.`:"collaborative"===D?`\u23f3 **No answer yet.** The interaction posture is collaborative: this decision stays with the user: do NOT proceed or assume a default. Wrap up your turn cleanly now; the question remains open as **${e}** and you will be automatically resumed with the answer (or a dismissal) whenever it arrives.`:y.defaultOption?`\u23f3 **No answer within the hold window.** The question stays open as **${e}** (inbox).\n\n**Proceed on your default now: ${String(y.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.`:`\u23f3 **No answer within the hold window** and no default was stated. **Decide yourself now**: pick the best option, record it with the record-decision operation (so the user can review or correct it later), and proceed. The question stays open as **${e}**; if the user answers later you will be resumed: treat it as confirmation or a correction.`,{content:[{type:"text",text:n+(0,M.namedRefsNote)(L?.content?.namedRefs)}]}}if(
|
|
4722
|
+
return n="expired"===t.kind?`\u23f3 **The question expired unanswered** (**${e}**). Decide yourself now: pick the best option, record it with the record-decision operation, and proceed. No automatic resume will come for this question.`:"collaborative"===D?`\u23f3 **No answer yet.** The interaction posture is collaborative: this decision stays with the user: do NOT proceed or assume a default. Wrap up your turn cleanly now; the question remains open as **${e}** and you will be automatically resumed with the answer (or a dismissal) whenever it arrives.`:y.defaultOption?`\u23f3 **No answer within the hold window.** The question stays open as **${e}** (inbox).\n\n**Proceed on your default now: ${String(y.defaultOption)}**\n\nIf the eventual answer differs, you will be resumed with a correction: fold it in then.`:`\u23f3 **No answer within the hold window** and no default was stated. **Decide yourself now**: pick the best option, record it with the record-decision operation (so the user can review or correct it later), and proceed. The question stays open as **${e}**; if the user answers later you will be resumed: treat it as confirmation or a correction.`,{content:[{type:"text",text:n+(0,M.namedRefsNote)(L?.content?.namedRefs)}]}}if(N)return{content:[{type:"text",text:`\u2705 Question posted as **${L.messageCode}**. It goes ahead on its default, so nothing waits for it.\n\n**Proceed on your default now: ${String(y.defaultOption)}**\n\nIf the user answers differently, you will be resumed with a correction: fold it in then.${j}${(0,M.namedRefsNote)(L?.content?.namedRefs)}`}]}
|
|
4725
4723
|
let B=""
|
|
4726
4724
|
if(T){const e=await n(C)??"agent",s={alchemy:{kind:"question-received",payload:{messageCode:L.messageCode,question:y.question,questionType:g,options:y.options,context:y.context,allowMultiple:y.allowMultiple,...Array.isArray(y.fields)&&y.fields.length>0?{fields:y.fields}:{},fromAgent:e,fromSessionId:C,priority:L.priority,expiresAt:L.expiresAt,...I?{relatesTo:I}:{}}}},i=q.HUB_AGENT_TYPES.has(String(await n(T)??""))?`${h(L)}\n\n${exports.zk}${await async function(e,t,r){if(!t)return""
|
|
4727
4725
|
try{const n=await e.proxyGet(`/api/projects/${t}/hub-digest/${encodeURIComponent(r)}`),o=String((n?.data??n)?.text??"")
|
|
@@ -4772,7 +4770,7 @@ const t=await s(E,c,a)
|
|
|
4772
4770
|
if(t)return a.info("Auto-resolved parent session",{parentSessionId:t}),t
|
|
4773
4771
|
a.info("No parent session; filing report as broadcast",{sessionId:E})})()
|
|
4774
4772
|
a.info("Submitting report",{sessionId:E,parentSessionId:A,status:S})
|
|
4775
|
-
const O=await d.createReport({fromSessionId:E,toSessionId:A,status:S,summary:h,details:p,...f?{relatesTo:f}:{},...w?{attention:w}:{},...$?{reportKind:$}:{},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:"string"==typeof e.area&&e.area.trim()?{area:e.area.trim()}:{},...Array.isArray(e.ruleVerdicts)&&e.ruleVerdicts.length?{extraContent:{ruleVerdicts:e.ruleVerdicts}}:{}}),D=await n(E)??"agent",
|
|
4773
|
+
const O=await d.createReport({fromSessionId:E,toSessionId:A,status:S,summary:h,details:p,...f?{relatesTo:f}:{},...w?{attention:w}:{},...$?{reportKind:$}:{},..."string"==typeof e.requestId&&e.requestId.trim()?{requestId:e.requestId.trim()}:"string"==typeof e.area&&e.area.trim()?{area:e.area.trim()}:{},...Array.isArray(e.ruleVerdicts)&&e.ruleVerdicts.length?{extraContent:{ruleVerdicts:e.ruleVerdicts}}:{}}),D=await n(E)??"agent",N=O.content,F="string"==typeof N?.requestId?N.requestId:"request"===N?.anchor?.type&&"string"==typeof N.anchor.id?N.anchor.id:void 0,z={alchemy:{kind:"worker-report",payload:{status:S,summary:h,details:p,fromAgent:D,fromSessionId:E,...f?{relatesTo:f}:{},...F&&/^req\d+$/.test(F)?{requestId:F}:{}}}},L=!(!e.toSessionId||"parent"===e.toSessionId),B=A??(await s(E,c,a).catch(()=>null)||void 0),H=B?await n(B):null
|
|
4776
4774
|
let U=!1
|
|
4777
4775
|
try{const e=await C.sessionIdentity.getSessionRequestId(c,a,E),n=r.projectId
|
|
4778
4776
|
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))
|
|
@@ -4830,7 +4828,7 @@ if(!s||"string"!=typeof s)throw new v.ValidationError('brief operation requires
|
|
|
4830
4828
|
if(!Array.isArray(a)||0===a.length)throw new v.ValidationError("brief operation requires a non-empty sections array")
|
|
4831
4829
|
const c=i(0,0,"session_message_operations",0,o),l=n.getSessionMessageClient()
|
|
4832
4830
|
if(!l)throw new v.ValidationError("Session message client not available")
|
|
4833
|
-
const d=await(0,
|
|
4831
|
+
const d=await(0,N.messagesArrivedMidStep)(t.mainBasePath,o)
|
|
4834
4832
|
if(d.length>0)return{content:[{type:"text",text:`\u26d4 Not posted: ${1===d.length?"a message":`${d.length} messages`} reached you while you wrote this brief, so it was written without ${1===d.length?"it":"them"}: ${d.slice(0,3).map(e=>`"${e.slice(0,200)}"`).join("; ")}. Read ${1===d.length?"it":"them"} (${1===d.length?"it is":"they are"} in your conversation now) and post the brief again with what ${1===d.length?"it says":"they say"}: a choice the user already made is not a question.`}],isError:!0}
|
|
4835
4833
|
let u=null,h=0
|
|
4836
4834
|
try{const e=await l.getInteractionPosture(c,s)
|
|
@@ -4876,7 +4874,7 @@ t.debug("Getting message",{messageCode:n})
|
|
|
4876
4874
|
const s=await o.getMessage(n)
|
|
4877
4875
|
return s?{content:[{type:"text",text:u(s)}]}:{content:[{type:"text",text:`\u274c Message with code ${n} not found.`}],isError:!0}}(e,j,h),"get-message")
|
|
4878
4876
|
default:throw new v.ValidationError(`Unknown operation: ${O}. Valid operations: send, reply, list, get, offer-handoff, record-decision, brief`)}}catch(e){return j.error("Session message operation failed",{operation:O,error:(0,k.getErrorMessage)(e)}),{content:[{type:"text",text:`\u274c Error: ${(0,k.getErrorMessage)(e)}`}],isError:!0}}}
|
|
4879
|
-
const y=t(87780),w=t(64545),b=t(70508),v=t(59860),k=t(58824),I=t(47617),$=t(82272),x=t(67904),S=t(22316),E=t(22746),_=t(4871),C=t(6688),T=t(69883),q=t(69048),R=t(34221),j=t(39844),P=t(43545),A=t(87280),O=t(29729),M=t(19670),D=t(53847),
|
|
4877
|
+
const y=t(87780),w=t(64545),b=t(70508),v=t(59860),k=t(58824),I=t(47617),$=t(82272),x=t(67904),S=t(22316),E=t(22746),_=t(4871),C=t(6688),T=t(69883),q=t(69048),R=t(34221),j=t(39844),P=t(43545),A=t(87280),O=t(29729),M=t(19670),D=t(53847),N=t(62032)
|
|
4880
4878
|
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}}
|
|
4881
4879
|
let c=!1
|
|
4882
4880
|
for(;Date.now()<i;){await new Promise(e=>setTimeout(e,3e3))
|
|
@@ -5027,21 +5025,21 @@ let M=""
|
|
|
5027
5025
|
if(P){const e=(O?P.filter(e=>e.opencodeSessionId!==O):P).filter(e=>!1!==e.working)
|
|
5028
5026
|
if(e.length>0){const t=e.map(e=>e.opencodeSessionId).join(", ")
|
|
5029
5027
|
if(!h)return i.warn("Blocked spawn: request already has active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map(e=>e.opencodeSessionId)}),{content:[{type:"text",text:`\u274c Cannot spawn: Request ${u} already has ${e.length} active worker(s): ${t}\n\nOptions:\n\u2022 Use \`useExistingSession: "${e[0].opencodeSessionId}"\` to send work to the existing session\n\u2022 Use \`allowParallel: true\` if you really need multiple workers on the same request`}],isError:!0}
|
|
5030
|
-
M=`\u26a0\ufe0f Warning: Request ${u} already has ${e.length} active worker(s): ${t}. Proceeding anyway (allowParallel=true).\n\n`,i.warn("Spawning parallel subagent for request with existing active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map(e=>e.opencodeSessionId),allowParallel:!0})}}let D,F
|
|
5031
|
-
u&&(D=`requests/${u}`,r(t,"Setting initialBranch for agent",{agent:l,requestId:u,initialBranch:D,agentRequestContextMode:A||"undefined"}),
|
|
5028
|
+
M=`\u26a0\ufe0f Warning: Request ${u} already has ${e.length} active worker(s): ${t}. Proceeding anyway (allowParallel=true).\n\n`,i.warn("Spawning parallel subagent for request with existing active workers",{requestId:u,existingWorkerCount:e.length,existingSessionIds:e.map(e=>e.opencodeSessionId),allowParallel:!0})}}let D,N,F=d
|
|
5029
|
+
u&&(D=`requests/${u}`,r(t,"Setting initialBranch for agent",{agent:l,requestId:u,initialBranch:D,agentRequestContextMode:A||"undefined"}),F="readonly"===A?`Working on request ${u} (READONLY mode - you cannot modify files, only explore and report).\n\n${d}`:`Working on request ${u}.\n\n${d}`)
|
|
5032
5030
|
let z,L=!1
|
|
5033
|
-
if(p){if(
|
|
5031
|
+
if(p){if(N=p,i.info("Using existing session for spawn",{sessionId:N,requestId:u}),!j)throw new f.ValidationError("Could not determine OpenCode server URL")
|
|
5034
5032
|
try{let e
|
|
5035
5033
|
try{const t=await fetch(`http://localhost:${T}/api/project-settings`,{signal:AbortSignal.timeout(8e3)})
|
|
5036
5034
|
if(t.ok){const r=await t.json()
|
|
5037
5035
|
if(r.data?.defaultModel){const[t,...n]=r.data.defaultModel.split("/")
|
|
5038
|
-
e={providerID:t,modelID:n.join("/")}}}}catch{}z=await o({apiClient:a,fromSessionId:c?.sessionId,toSessionId:
|
|
5039
|
-
let r=
|
|
5040
|
-
const d=await n(a,t.projectId,u,
|
|
5036
|
+
e={providerID:t,modelID:n.join("/")}}}}catch{}z=await o({apiClient:a,fromSessionId:c?.sessionId,toSessionId:N,body:F,requestId:u,logger:i})
|
|
5037
|
+
let r=F
|
|
5038
|
+
const d=await n(a,t.projectId,u,N,i,t.mainBasePath||t.basePath)
|
|
5041
5039
|
d&&(r=`${d}\n\n---\n\n${r}`)
|
|
5042
|
-
const h={body:
|
|
5043
|
-
if(await s(a,t.projectId,z,p,i),!p.ok)throw new Error((0,y.deliveryFailureText)(
|
|
5044
|
-
i.info("Prompt delivered to existing session",{sessionId:
|
|
5040
|
+
const h={body:F,fromSessionId:c?.sessionId,fromAgent:q,...u?{requestId:u}:{},...z?{instructionCode:z}:{}},p=await(0,y.deliverToSession)(N,r,{kind:"agent-delegation",partPayload:h})
|
|
5041
|
+
if(await s(a,t.projectId,z,p,i),!p.ok)throw new Error((0,y.deliveryFailureText)(N,p))
|
|
5042
|
+
i.info("Prompt delivered to existing session",{sessionId:N,agent:l,messageId:p.messageId})}catch(e){throw new Error(`Failed to use existing session: ${(0,v.getErrorMessage)(e)}`)}}else{L=!0,r(t,"Creating session via spawn-session endpoint")
|
|
5045
5043
|
const s=O
|
|
5046
5044
|
E&&(0,b.logSessionIdentity)("subagent_operations.spawn","_meta",{parentSessionId:s})
|
|
5047
5045
|
try{const p=`http://localhost:${T}/api/spawn-session`,f=await fetch(p,{method:"POST",signal:AbortSignal.timeout(15e3),headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:l,prompt:d,requestId:u,parentSessionId:s,title:e.title,allowParallel:h,callerAgentType:q})})
|
|
@@ -5049,16 +5047,16 @@ if(!f.ok){const e=await f.text().catch(()=>"Unknown error")
|
|
|
5049
5047
|
throw new Error(`Spawn session endpoint failed: ${f.status} ${e}`)}const g=await f.json()
|
|
5050
5048
|
if(!g.success||!g.data)throw new Error(g.error||"Spawn session returned unsuccessful")
|
|
5051
5049
|
const{sessionId:y,model:w,serverUrl:b,initialBranch:v}=g.data
|
|
5052
|
-
D=v,z=await o({apiClient:a,fromSessionId:c?.sessionId,toSessionId:y,body:
|
|
5053
|
-
let k=
|
|
5050
|
+
D=v,z=await o({apiClient:a,fromSessionId:c?.sessionId,toSessionId:y,body:F,requestId:u,logger:i})
|
|
5051
|
+
let k=F
|
|
5054
5052
|
const I="pickupGuidance"in(g?.data??{})?g.data.pickupGuidance??null:await n(a,t.projectId,u,y,i,t.mainBasePath||t.basePath)
|
|
5055
5053
|
I&&(k=`${I}\n\n---\n\n${k}`)
|
|
5056
|
-
const $=await(0,m.deliverInitialPrompt)({serverUrl:b,sessionId:y,agent:l,prompt:k,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:
|
|
5054
|
+
const $=await(0,m.deliverInitialPrompt)({serverUrl:b,sessionId:y,agent:l,prompt:k,model:w,directory:t.mainBasePath,metadata:{alchemy:{kind:"agent-delegation",payload:{body:F,fromSessionId:c?.sessionId,fromAgent:q,...u?{requestId:u}:{},...z?{instructionCode:z}:{}}}}})
|
|
5057
5055
|
if(!$.success)throw new Error(`Failed to spawn subagent: ${$.error}`)
|
|
5058
|
-
|
|
5059
|
-
B+=p?`Prompt sent to existing session ${
|
|
5060
|
-
const H=!!
|
|
5061
|
-
return{content:[{type:"text",text:B}],...(0,x.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:H?
|
|
5056
|
+
N=y,r(t,"Spawn successful via unified endpoint",{sessionId:y,parentSessionId:s,model:w,instructionCode:z}),i.info("Background subagent spawned via unified endpoint",{agent:l,requestId:u,sessionId:y,parentSessionId:s,instructionCode:z})}catch(e){throw new Error(`Failed to spawn subagent: ${(0,v.getErrorMessage)(e)}`)}}let B=M
|
|
5057
|
+
B+=p?`Prompt sent to existing session ${N}.`:`Background ${l} subagent spawned successfully.`,u&&(B+=`\nWorking on: ${u}`),z&&(B+=`\nInstruction code: ${z} (the agent will cite this on its reports/questions via relatesTo)`),B+="\n\nThe subagent is now working independently. You can continue with other tasks."
|
|
5058
|
+
const H=!!N&&"unknown"!==N&&"spawning"!==N
|
|
5059
|
+
return{content:[{type:"text",text:B}],...(0,x.alchemyMeta)({kind:"spawn-subagent",spawnedSessionId:H?N:void 0,agentType:l,requestId:u||void 0,description:e.title||d.slice(0,50)||l,...z?{instructionCode:z}:{}}),...z?{instructionCode:z}:{}}}function a(e){const t=Math.floor(e/1e3)
|
|
5062
5060
|
if(t<60)return`${t}s`
|
|
5063
5061
|
const r=Math.floor(t/60)
|
|
5064
5062
|
return r<60?`${r}m ${t%60}s`:`${Math.floor(r/60)}h ${r%60}m`}function c(e){const t=[]
|
|
@@ -5652,7 +5650,7 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
|
|
|
5652
5650
|
return o(t,e),t})
|
|
5653
5651
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.visualDefs=void 0
|
|
5654
5652
|
const i=s(t(76183)),a=t(55432),c=t(93978),l=t(70089),d=t(70901),u=t(20339),h=t(75010),p=t(36399)
|
|
5655
|
-
exports.visualDefs=[{name:"request_visual_feedback",description:'Ask the USER to mark up a screenshot of the UI and get their annotations back. EASIEST: start a box on a screenshot-capable env (experiment_start), then call this with `box` + `route` (e.g. "/") -- I render + screenshot that route for you. For FULL control (navigate, click, fill, scroll, log in, multi-step), pass `box` + `script`: a Playwright snippet (body of async (page, baseUrl) => {...}) -- I serve the app, launch headless Chromium, run your script, then screenshot the final state. You write ONLY the interaction; no serve/launch/flags. (Also: `fullPage` for the whole page; `box`+`path` for a screenshot you already wrote; `assetFilename` for an uploaded asset.) The user paints on it in the app (the action-required bell) and their annotated image comes back so you can SEE the changes they want. Returns the annotated image (or notes the user skipped / did not respond). Advisory -- never gates a merge.',schema:i.RequestVisualFeedbackSchema,annotations:{title:"Request Visual Feedback",readOnlyHint:!1},handler:(e,t)=>(0,a.handleRequestVisualFeedback)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"ask_user_visual",description:'Ask the USER a question ANCHORED TO A REGION OF AN IMAGE -- point at the thing instead of describing it. Give `imagePath` (a screenshot you produced, repo-relative) or `previewName` (a registered live preview; I freeze it into a snapshot), plus `box` ("x,y,w,h", normalized 0..1) marking the region, your `question`, and optionally `options` for multiple choice. The question is pinned to that region and waits in the user inbox: they open the marked image in one click and answer on the record. The answer returns like any question answer (inline if they respond during the hold window, otherwise you are resumed automatically -- do not poll). Unlike request_visual_feedback (which returns an image the user paints), this returns the user\'s WORDS about a region YOU marked.',schema:i.AskUserVisualSchema,annotations:{title:"Ask User (Visual)",readOnlyHint:!1},handler:(e,t)=>(0,c.handleAskUserVisual)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"capture_site",description:"Screenshot an http(s) URL, public or locally served, with the platform's host browser (it reaches the internet when boxes cannot). Stores the image as a project asset and returns it with an asset:<filename> ref to cite in documents or criteria, which a judge can later view with view_asset offline.",schema:i.CaptureSiteSchema,annotations:{title:"Capture Site Screenshot",readOnlyHint:!1},handler:(e,t)=>(0,l.handleCaptureSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"read_site",description:"Load an http(s) URL in the platform's host browser and return the page's rendered visible text (up to 60k chars); pages that bot-wall webfetch render here. The result carries a ready `Source:` line and its read time: a text that rests on the page ends with that line. A claim checked here is verified as of that time; one from search summaries alone stays inferred, and say so.",schema:i.ReadSiteSchema,annotations:{title:"Read Site (Rendered Text)",readOnlyHint:!0},handler:(e,t)=>(0,d.handleReadSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"drive_site",description:"DRIVE a running page in the platform's headless host browser: click, type, press keys, scroll, then read what the page says and looks like. Up to 20 steps per call; each reports whether it found its target. clickat (x,y) for canvas or game surfaces with no DOM. Pass `session` to keep the page open between calls (the only way to check accumulated state or elapsed time) and close:true on the last call. Persistent checks belong in a test process. The user never sees this browser: to hand a page to them, give the URL, the steps left for them in their own browser, and the final screenshot (ask_user_visual with imagePath, or cite it in a report). Never type a password, a key or an acceptance on the user's behalf.",schema:i.DriveSiteSchema,annotations:{title:"Drive Site (Interact)",readOnlyHint:!1},handler:(e,t)=>(0,u.handleDriveSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"fetch_asset",description:"Download an image or font from an http(s) URL straight INTO the working tree (e.g. `sites/acme/assets/hero.jpg`). Use it to vendor the assets a page needs so the result is self-contained -- confined runs have no network, so a page that hotlinks an image renders broken in the test gate and to anyone offline. webfetch cannot do this: it returns a page as text, or an image into the conversation, and neither writes bytes to a file. Images and fonts only, 12MB cap, path confined to the working tree.",schema:i.FetchAssetSchema,annotations:{title:"Fetch Asset",readOnlyHint:!1},handler:(e,t)=>(0,h.handleFetchAsset)(e,t.context,t.logger,t.metrics)},{name:"preview_site",description:"Serve a built static directory (containing index.html) from this machine and return a link the user can open; it is also registered as a live preview in the artifact viewer. Use it when the deliverable is a page. Pass requestId to serve an unmerged request's own working copy. Build into a folder git ignores (the project's build folder, e.g. dist/): a box run commits every file it writes outside ignored folders to your branch, and the preview serves the folder only while it exists. Never copy files into main to preview them.",schema:i.PreviewSiteSchema,annotations:{title:"Preview Site",readOnlyHint:!1},handler:(e,t)=>(0,p.handlePreviewSite)(e,t.logger,t.metrics,t.context)}]},20339(e,exports,t){exports.handleDriveSite=async function(e,n,o,s,i,a){const c=o.startTimer("tool_execution_time"),l=e=>(c({success:!1,error:"bad"}),{content:[{type:"text",text:e}],isError:!0})
|
|
5653
|
+
exports.visualDefs=[{name:"request_visual_feedback",description:'Ask the USER to mark up a screenshot of the UI and get their annotations back. EASIEST: start a box on a screenshot-capable env (experiment_start), then call this with `box` + `route` (e.g. "/") -- I render + screenshot that route for you. For FULL control (navigate, click, fill, scroll, log in, multi-step), pass `box` + `script`: a Playwright snippet (body of async (page, baseUrl) => {...}) -- I serve the app, launch headless Chromium, run your script, then screenshot the final state. You write ONLY the interaction; no serve/launch/flags. (Also: `fullPage` for the whole page; `box`+`path` for a screenshot you already wrote; `assetFilename` for an uploaded asset.) The user paints on it in the app (the action-required bell) and their annotated image comes back so you can SEE the changes they want. Returns the annotated image (or notes the user skipped / did not respond). Advisory -- never gates a merge.',schema:i.RequestVisualFeedbackSchema,annotations:{title:"Request Visual Feedback",readOnlyHint:!1},handler:(e,t)=>(0,a.handleRequestVisualFeedback)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"ask_user_visual",description:'Ask the USER a question ANCHORED TO A REGION OF AN IMAGE -- point at the thing instead of describing it. Give `imagePath` (a screenshot you produced, repo-relative) or `previewName` (a registered live preview; I freeze it into a snapshot), plus `box` ("x,y,w,h", normalized 0..1) marking the region, your `question`, and optionally `options` for multiple choice. The question is pinned to that region and waits in the user inbox: they open the marked image in one click and answer on the record. The answer returns like any question answer (inline if they respond during the hold window, otherwise you are resumed automatically -- do not poll). Unlike request_visual_feedback (which returns an image the user paints), this returns the user\'s WORDS about a region YOU marked.',schema:i.AskUserVisualSchema,annotations:{title:"Ask User (Visual)",readOnlyHint:!1},handler:(e,t)=>(0,c.handleAskUserVisual)(e,t.context,t.apiClient,t.logger,t.metrics,t.cliOrigin)},{name:"capture_site",description:"Screenshot an http(s) URL, public or locally served, with the platform's host browser (it reaches the internet when boxes cannot). Stores the image as a project asset and returns it with an asset:<filename> ref to cite in documents or criteria, which a judge can later view with view_asset offline.",schema:i.CaptureSiteSchema,annotations:{title:"Capture Site Screenshot",readOnlyHint:!1},handler:(e,t)=>(0,l.handleCaptureSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"read_site",description:"Load an http(s) URL in the platform's host browser and return the page's rendered visible text (up to 60k chars); pages that bot-wall webfetch render here. The result carries a ready `Source:` line and its read time: a text that rests on the page ends with that line. A claim checked here is verified as of that time; one from search summaries alone stays inferred, and say so.",schema:i.ReadSiteSchema,annotations:{title:"Read Site (Rendered Text)",readOnlyHint:!0},handler:(e,t)=>(0,d.handleReadSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"drive_site",description:"DRIVE a running page in the platform's headless host browser: click, type, press keys, scroll, then read what the page says and looks like. Up to 20 steps per call; each reports whether it found its target. clickat (x,y) for canvas or game surfaces with no DOM. Pass `session` to keep the page open between calls (the only way to check accumulated state or elapsed time) and close:true on the last call. Persistent checks belong in a test process. The user never sees this browser: to hand a page to them, give the URL, the steps left for them in their own browser, and the final screenshot (ask_user_visual with imagePath, or cite it in a report). Never type a password, a key or an acceptance on the user's behalf.",schema:i.DriveSiteSchema,annotations:{title:"Drive Site (Interact)",readOnlyHint:!1},handler:(e,t)=>(0,u.handleDriveSite)(e,t.logger,t.metrics,t.context,t.apiClient,t.cliOrigin)},{name:"fetch_asset",description:"Download an image or font from an http(s) URL straight INTO the working tree (e.g. `sites/acme/assets/hero.jpg`). Use it to vendor the assets a page needs so the result is self-contained -- confined runs have no network, so a page that hotlinks an image renders broken in the test gate and to anyone offline. webfetch cannot do this: it returns a page as text, or an image into the conversation, and neither writes bytes to a file. Images and fonts only, 12MB cap, path confined to the working tree.",schema:i.FetchAssetSchema,annotations:{title:"Fetch Asset",readOnlyHint:!1},handler:(e,t)=>(0,h.handleFetchAsset)(e,t.context,t.logger,t.metrics)},{name:"preview_site",description:"Serve a built static directory (containing index.html) from this machine and return a link the user can open; it is also registered as a live preview in the artifact viewer. Use it when the deliverable is a page. With stop: true it stops a running preview (name it when several run). Pass requestId to serve an unmerged request's own working copy. Build into a folder git ignores (the project's build folder, e.g. dist/): a box run commits every file it writes outside ignored folders to your branch, and the preview serves the folder only while it exists. Never copy files into main to preview them.",schema:i.PreviewSiteSchema,annotations:{title:"Preview Site",readOnlyHint:!1},handler:(e,t)=>(0,p.handlePreviewSite)(e,t.logger,t.metrics,t.context)}]},20339(e,exports,t){exports.handleDriveSite=async function(e,n,o,s,i,a){const c=o.startTimer("tool_execution_time"),l=e=>(c({success:!1,error:"bad"}),{content:[{type:"text",text:e}],isError:!0})
|
|
5656
5654
|
try{const o=(e.url||"").trim()
|
|
5657
5655
|
if(!/^https?:\/\//i.test(o))return l("`url` must be an http(s) URL.")
|
|
5658
5656
|
const d=Array.isArray(e.steps)?e.steps:[]
|
|
@@ -5699,7 +5697,17 @@ if(a.skipped)return l({success:!0}),{content:[{type:"text",text:"The human skipp
|
|
|
5699
5697
|
const u=[{type:"text",text:`Human visual feedback received${a.note?`. Note: ${a.note}`:""}. The annotated screenshot follows (red/markup = what to change).`}],h=a.annotationDataUrl,p="string"==typeof h?h.match(/^data:([^;]+);base64,(.*)$/s):null
|
|
5700
5698
|
return p?u.push({type:"image",data:p[2],mimeType:p[1]}):a.annotationAssetFilename&&u.push({type:"text",text:`Annotated image asset: ${a.annotationAssetFilename} (view with view_asset).`}),l({success:!0}),{content:u}}catch(e){return l({success:!1,error:"exception"}),i.warn("request_visual_feedback failed",{error:(0,n.getErrorMessage)(e)}),{content:[{type:"text",text:`Visual feedback request failed: ${(0,n.getErrorMessage)(e)}`}],isError:!0}}}
|
|
5701
5699
|
const n=t(58824),o=e=>new Promise(t=>setTimeout(t,e))},36399(e,exports,t){exports.handlePreviewSite=async function(e,o,s,i){const a=s.startTimer("tool_execution_time")
|
|
5702
|
-
try{
|
|
5700
|
+
try{if(!0===e.stop){const r=await async function(e){const{actionServerFetch:r}=await Promise.resolve().then(()=>t(79414)),n=await r("/api/previews",{signal:AbortSignal.timeout(5e3)}).then(e=>e.json()).catch(()=>null)
|
|
5701
|
+
if(!n?.success||!Array.isArray(n.data))return{text:"The running previews could not be read just now, so nothing was stopped. Try again.",isError:!0}
|
|
5702
|
+
const o=function(e,t){const r=(t??"").trim().toLowerCase()
|
|
5703
|
+
if(0===e.length)return{none:!0}
|
|
5704
|
+
if(r){const t=e.find(e=>e.name.toLowerCase()===r)??e.find(e=>(e.label??"").trim().toLowerCase()===r)
|
|
5705
|
+
return t?{name:t.name}:{which:e.map(e=>e.label?`${e.name} ("${e.label}")`:e.name)}}return 1===e.length?{name:e[0].name}:{which:e.map(e=>e.label?`${e.name} ("${e.label}")`:e.name)}}(n.data,e)
|
|
5706
|
+
if("none"in o)return{text:"No preview is running, so there is nothing to stop."}
|
|
5707
|
+
if("which"in o)return{text:`${e?`No running preview is called "${e}".`:"More than one preview is running."} Nothing was stopped. Running now: ${o.which.join(", ")}. Call again with stop and the name of the one to stop.`}
|
|
5708
|
+
const s=await r(`/api/previews/${encodeURIComponent(o.name)}`,{method:"DELETE",signal:AbortSignal.timeout(1e4)}),i=await s.json().catch(()=>({}))
|
|
5709
|
+
return s.ok&&i?.success?{text:i?.data?.stopped?`Stopped the preview ${o.name}: it is no longer served and is gone from Previews.`:`Removed the preview ${o.name} from Previews. AlchemyLab was not the one serving it (a server the project runs is its own), so that server is untouched.`}:{text:`The preview ${o.name} could not be stopped: ${i?.error||"HTTP "+s.status}`,isError:!0}}("string"==typeof e.name?e.name:void 0)
|
|
5710
|
+
return a({success:!r.isError}),{content:[{type:"text",text:r.text}],...r.isError?{isError:!0}:{}}}let r=(e.dir||"").trim(),o="string"==typeof e.requestId?e.requestId.trim():""
|
|
5703
5711
|
if(o){const e=function(e,t){if(!/^req\d{6}$/.test(e))return null
|
|
5704
5712
|
const r=t.replace(/^\.?\/+/,"").replace(/\/+$/,"")
|
|
5705
5713
|
return r.startsWith("/")||r.split("/").includes("..")?null:r&&"."!==r?r:"."}(o,r||".")
|
|
@@ -5757,13 +5765,13 @@ exports.SubagentOperationsSchema=n.z.object({sessionId:p.optional(),operation:n.
|
|
|
5757
5765
|
"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"}))})
|
|
5758
5766
|
const f=d
|
|
5759
5767
|
exports.PlanReadSchema=n.z.object({operation:n.z.enum(["get","get-active","list","get-history","get-requests"]),planId:f.optional(),searchQuery:n.z.string().optional(),limit:n.z.number().min(1).max(100).default(20),offset:n.z.number().min(0).default(0),sortBy:n.z.enum(["createdAt","updatedAt","filename"]).default("updatedAt"),sortDirection:n.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=n.z.object({operation:n.z.enum(["create","update","append","replace","create-snapshot","restore"]),planId:f.optional(),kind:n.z.enum(["plan"]).default("plan").describe("Document kind to create. Plans only."),content:n.z.string().optional(),name:n.z.string().optional().describe("Plan name (used in filename for create; optional new name for update)."),old_str:n.z.string().optional().describe("Text to find in the plan (for replace)"),new_str:n.z.string().optional().describe("Replacement text (for replace)"),versionLabel:n.z.string().optional(),changeSummary:n.z.string().optional(),versionNumber:n.z.number().int().min(1).optional()}).superRefine((e,t)=>{const r=e.operation
|
|
5760
|
-
"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_str||t.addIssue({code:"custom",message:'old_str is required for replace. Example: { "operation": "replace", "planId": "plan000001", "old_str": "find this", "new_str": "replace with" }'}),void 0===e.new_str&&t.addIssue({code:"custom",message:"new_str 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=n.z.object({requestId:l,reason:n.z.string(),confirmDestructive:n.z.boolean().optional()}),exports.ModifyFileSchema=n.z.object({operations:n.z.array(n.z.object({path:n.z.string().optional(),documentId:d.optional(),operationType:n.z.enum(["replace-string","insert-lines","replace-lines","delete-string","delete-lines"]),replaceString:n.z.object({replacements:n.z.array(n.z.object({old_str:n.z.string(),new_str:n.z.string()})),mode:n.z.enum(["unique","all"]).optional()}).optional(),insertLines:n.z.object({insertions:n.z.array(n.z.object({type:n.z.enum(["before","after","between"]),line:n.z.number().optional(),afterLine:n.z.number().optional(),beforeLine:n.z.number().optional(),content:n.z.union([n.z.string(),n.z.array(n.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:n.z.object({replacements:n.z.array(n.z.object({lines:n.z.string(),content:n.z.union([n.z.string(),n.z.array(n.z.string())])}))}).optional(),deleteString:n.z.object({patterns:n.z.array(n.z.string()),mode:n.z.enum(["all","unique"]).optional()}).optional(),deleteLines:n.z.object({lines:n.z.string()}).optional()}).transform(u).refine(h,{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:n.z.string()}),exports.ManageFilesSchema=n.z.object({operations:n.z.array(n.z.object({operationType:n.z.enum(["create-directory","move-files","delete-files","get-file-info"]),createDirectory:n.z.object({path:n.z.string()}).optional(),moveFiles:n.z.object({moves:n.z.array(n.z.object({source:n.z.string(),destination:n.z.string()}))}).optional(),deleteFiles:n.z.object({targets:n.z.array(n.z.object({path:n.z.string().optional(),documentId:d.optional()}).transform(u).refine(h,{message:"path or documentId required"}))}).optional(),getFileInfo:n.z.object({path:n.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:n.z.string()}),exports.RequestOperationsSchema=n.z.object({requestId:l.optional(),operation:n.z.enum(["update","activate","complete","merge","revert","reopen","get-info","list","search","status","cancel","hold","delete-empty","list-areas","update-areas","wait-on","clear-wait","premise-block","dispute-criterion","asked-no-change","hand-over","bring-main-in","check-target","round-piece"]),planId:n.z.string().max(64).optional().describe("status / check-target: a plan's thread."),criterion:n.z.string().optional(),reason:n.z.string().optional().describe('cancel, revert, reopen, wait-on: why, to the user ("you"), in their language (a replacing request named). premise-block (required): what the request builds on that the project lacks; not what it adds, not unclear wording (ask instead).'),userSaid:n.z.string().min(8).max(600).optional().describe("The user's words asking for this, exact (checked). Required for activate, cancel of finished work waiting on them, and reopen with a question pending on them; asked-no-change: their message."),waitsOn:n.z.string().optional(),update:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({userSaid:n.z.string().min(8).max(600).optional().describe("The user's words asking for this change, exact; a worker narrows or rewords its own request only with them."),budget:n.z.number().positive().max(1e5).nullable().optional().describe("The budget the user named for this request's thread (or plan), dollars at list price, their words in userSaid; null removes it."),target:exports.TargetConditionsSchema.optional(),estimate:n.z.number().positive().max(1e5).optional().describe("Expected cost, dollars at list price."),removesCriteria:n.z.boolean().optional().describe("true to delete the criteria your list leaves out; otherwise a shorter list is refused."),arcName:n.z.string().min(2).max(120).optional().describe("The thread's name, two to five plain words (\"Weekly menu app\"); set once, never over the user's."),title:n.z.string().optional(),description:n.z.string().optional(),process:n.z.string().nullable().optional().describe("A workflow process by name, or null; drafts only."),type:exports.SettableRequestTypeEnum.optional(),acceptanceCriteria:n.z.array(n.z.union([n.z.string(),n.z.object({description:n.z.string().min(1),beforeMerge:n.z.boolean().optional(),command:n.z.string().min(1).max(500).optional().describe("With the criterion's key: replaces its command, and the old line is kept on the record beside it. A worker replaces one that cannot pass where it runs with a line that checks the same thing, never less; the user's own command only with their answer in userSaid."),environment:n.z.string().min(1).max(120).optional().describe("As on create_request; set it instead of dropping a command the box cannot run."),kind:n.z.enum(["objective","subjective","gate"]),baseline:r(exports.BaselineSchema).optional(),key:n.z.string().optional().describe("The key of a criterion you reword (it keeps its verdicts); omit for a new one.")})])).optional().describe("Replaces the whole list; items as on create_request.")})).optional(),areas:n.z.array(n.z.string()).max(3).optional().describe("update-areas: up to 3 replacing the current ones; [] clears."),complete:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({change:n.z.preprocess(i,n.z.string().min(3).max(200).optional()).describe('On a round (its description opens "Round N of req######"), instead of summary while the round is open: what this one change did, in one line. The user reads it under the message that asked for it.'),summary:n.z.preprocess(i,n.z.string().optional()).describe('Markdown the user reads when it lands. First line: one sentence with the answer (if the request asked a question) or what changed. Then, only if there is more, one short "- " line per thing; no paragraphs. Never "tested" or "verified" when that failed. It replaces earlier summaries (after rework it covers the whole request) and stays after sign-off, so it says what was delivered, not what waits for the user. On a round it is written once, when you are told the round is being completed; until then pass change.'),replacesSummary:n.z.boolean().optional().describe("Required on a re-completion: the summary covers everything, fixes included."),notChecked:n.z.preprocess(i,n.z.string().min(3).max(160).optional()).describe('What you did not check that the user could assume you did ("the page on a phone"); the card leads with it. Not what registered checks run or say they leave out (the card shows those).'),show:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({commands:n.z.array(n.z.string().min(1).max(400)).max(5).optional().describe("Command lines that show the result working. AlchemyLab runs each in a box on the work as it was and on the completed work; the user sees both."),artifact:n.z.string().min(1).max(200).optional().describe("A picture, page or document on the record: a result's address, a document id (doc000012) or asset:<file>.")}).optional()).describe("Optional: something more to show with the finished result. What the request already delivered is shown by itself."),worthKnowing:n.z.array(n.z.string().min(10)).max(5).optional().describe("What the user would regret not knowing, one per entry (a discovery, caveat, side effect, follow-up); always one when what was asked was already true or turned out different. Not the summary again; subject first."),guidance:n.z.array(n.z.object({body:n.z.string().min(10).max(800).describe('One fact or instruction for the next agent ("calc.py is generated from ops.json: edit ops.json and regenerate.")'),files:n.z.array(n.z.string().min(1)).min(1).max(8).optional().describe("Files or folders (globs) it is pinned to; without them (userSaid only) the whole project."),evidence:n.z.string().min(1).max(1500).optional().describe("What shows it (command and output, file); not with userSaid."),userSaid:n.z.string().min(8).max(600).optional().describe("A correction of the user's that should hold later: their words, exact.")}).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 in these files would hit too, or a user correction, proposed as guidance. Not taste, not request-specific, not already guidance. Usually none."),skipTestsReason:exports.skipTestsReasonSchema,skipTests:exports.skipTestsSchema,criteriaComments:n.z.array(n.z.object({key:n.z.string().min(1),comment:n.z.string().min(1),verify:n.z.array(n.z.string().min(1)).max(4).optional().describe("Commands that prove it in the sealed acceptance box (a grep, a targeted test, a screenshot into .visual-check/), judged on their output. Clean checkout of your commit, dependencies installed, nothing built: a check needing a build runs it in the same command. No git, no network, no branching on whether a tool exists. A proof you saw pass on this commit, written exactly as you ran it, is reused."),testFiles:n.z.array(n.z.string().min(1)).max(10).optional().describe("With verify, when it runs just these tests (not the whole suite): their repo paths. Only the user changes a test a promise names.")})).optional().describe("One entry per criterion, keyed as your pickup lists it: what you did and how you saw it hold."),afterRun:n.z.boolean().optional().describe("Call `run` with your final check, then this complete in the SAME response: it completes only if that run passes."),couldNotCheck:n.z.array(n.z.string().min(1)).max(20).optional().describe("Drives only: keys of claims you could not check (reason in criteriaComments); they read as unchecked. Stamp the rest with record_criteria_verification.")})).optional(),merge:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({verifications:n.z.array(n.z.object({key:n.z.string().min(2).max(4),evidence:n.z.string().min(1)})).min(1)})).optional(),revert:n.z.object({reason:n.z.string(),confirmDestructive:n.z.boolean().optional()}).optional(),list:n.z.object({filters:n.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:n.z.string().optional(),scheduleDocumentId:n.z.string().optional(),questDocumentId:n.z.string().optional(),dateFrom:n.z.string().optional(),dateTo:n.z.string().optional(),limit:n.z.number().min(1).max(100).default(20),offset:n.z.number().min(0).default(0),sortBy:n.z.enum(["created","updated"]).default("updated"),sortDirection:n.z.enum(["asc","desc"]).default("desc")}).optional()}).optional(),search:n.z.object({query:n.z.string().min(1),searchIn:n.z.enum(["title","description","criteria","all"]).default("all"),filters:n.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:n.z.string().optional(),scheduleDocumentId:n.z.string().optional(),questDocumentId:n.z.string().optional(),dateFrom:n.z.string().optional(),dateTo:n.z.string().optional(),limit:n.z.number().min(1).max(100).default(20),offset:n.z.number().min(0).default(0)}).optional()}).optional()}).superRefine((e,t)=>{e.requestId
|
|
5768
|
+
"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_str||t.addIssue({code:"custom",message:'old_str is required for replace. Example: { "operation": "replace", "planId": "plan000001", "old_str": "find this", "new_str": "replace with" }'}),void 0===e.new_str&&t.addIssue({code:"custom",message:"new_str 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=n.z.object({requestId:l,reason:n.z.string(),confirmDestructive:n.z.boolean().optional()}),exports.ModifyFileSchema=n.z.object({operations:n.z.array(n.z.object({path:n.z.string().optional(),documentId:d.optional(),operationType:n.z.enum(["replace-string","insert-lines","replace-lines","delete-string","delete-lines"]),replaceString:n.z.object({replacements:n.z.array(n.z.object({old_str:n.z.string(),new_str:n.z.string()})),mode:n.z.enum(["unique","all"]).optional()}).optional(),insertLines:n.z.object({insertions:n.z.array(n.z.object({type:n.z.enum(["before","after","between"]),line:n.z.number().optional(),afterLine:n.z.number().optional(),beforeLine:n.z.number().optional(),content:n.z.union([n.z.string(),n.z.array(n.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:n.z.object({replacements:n.z.array(n.z.object({lines:n.z.string(),content:n.z.union([n.z.string(),n.z.array(n.z.string())])}))}).optional(),deleteString:n.z.object({patterns:n.z.array(n.z.string()),mode:n.z.enum(["all","unique"]).optional()}).optional(),deleteLines:n.z.object({lines:n.z.string()}).optional()}).transform(u).refine(h,{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:n.z.string()}),exports.ManageFilesSchema=n.z.object({operations:n.z.array(n.z.object({operationType:n.z.enum(["create-directory","move-files","delete-files","get-file-info"]),createDirectory:n.z.object({path:n.z.string()}).optional(),moveFiles:n.z.object({moves:n.z.array(n.z.object({source:n.z.string(),destination:n.z.string()}))}).optional(),deleteFiles:n.z.object({targets:n.z.array(n.z.object({path:n.z.string().optional(),documentId:d.optional()}).transform(u).refine(h,{message:"path or documentId required"}))}).optional(),getFileInfo:n.z.object({path:n.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:n.z.string()}),exports.RequestOperationsSchema=n.z.object({requestId:l.optional(),operation:n.z.enum(["update","activate","complete","merge","revert","reopen","get-info","list","search","status","cancel","hold","delete-empty","list-areas","update-areas","wait-on","clear-wait","premise-block","dispute-criterion","hand-over","bring-main-in","check-target","round-piece"]),planId:n.z.string().max(64).optional().describe("status / check-target: a plan's thread."),criterion:n.z.string().optional(),reason:n.z.string().optional().describe('cancel, revert, reopen, wait-on: why, to the user ("you"), in their language (a replacing request named). premise-block (required): what the request builds on that the project lacks; not what it adds, not unclear wording (ask instead).'),userSaid:n.z.string().min(8).max(600).optional().describe("The user's words asking for this, exact (checked). Required for activate, cancel of finished work waiting on them, and reopen with a question pending on them."),waitsOn:n.z.string().optional(),update:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({userSaid:n.z.string().min(8).max(600).optional().describe("The user's words asking for this change, exact; a worker narrows or rewords its own request only with them."),budget:n.z.number().positive().max(1e5).nullable().optional().describe("The budget the user named for this request's thread (or plan), dollars at list price, their words in userSaid; null removes it."),target:exports.TargetConditionsSchema.optional(),estimate:n.z.number().positive().max(1e5).optional().describe("Expected cost, dollars at list price."),removesCriteria:n.z.boolean().optional().describe("true to delete the criteria your list leaves out; otherwise a shorter list is refused."),arcName:n.z.string().min(2).max(120).optional().describe("The thread's name, two to five plain words (\"Weekly menu app\"); set once, never over the user's."),title:n.z.string().optional(),description:n.z.string().optional(),process:n.z.string().nullable().optional().describe("A workflow process by name, or null; drafts only."),type:exports.SettableRequestTypeEnum.optional(),acceptanceCriteria:n.z.array(n.z.union([n.z.string(),n.z.object({description:n.z.string().min(1),beforeMerge:n.z.boolean().optional(),command:n.z.string().min(1).max(500).optional().describe("With the criterion's key: replaces its command, and the old line is kept on the record beside it. A worker replaces one that cannot pass where it runs with a line that checks the same thing, never less; the user's own command only with their answer in userSaid."),environment:n.z.string().min(1).max(120).optional().describe("As on create_request; set it instead of dropping a command the box cannot run."),kind:n.z.enum(["objective","subjective","gate"]),baseline:r(exports.BaselineSchema).optional(),key:n.z.string().optional().describe("The key of a criterion you reword (it keeps its verdicts); omit for a new one.")})])).optional().describe("Replaces the whole list; items as on create_request.")})).optional(),areas:n.z.array(n.z.string()).max(3).optional().describe("update-areas: up to 3 replacing the current ones; [] clears."),complete:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({change:n.z.preprocess(i,n.z.string().min(3).max(200).optional()).describe('On a round (its description opens "Round N of req######"), instead of summary while the round is open: what this one change did, in one line. The user reads it under the message that asked for it.'),summary:n.z.preprocess(i,n.z.string().optional()).describe('Markdown the user reads when it lands. First line: one sentence with the answer (if the request asked a question) or what changed. Then, only if there is more, one short "- " line per thing; no paragraphs. Never "tested" or "verified" when that failed. It replaces earlier summaries (after rework it covers the whole request) and stays after sign-off, so it says what was delivered, not what waits for the user. On a round it is written once, when you are told the round is being completed; until then pass change.'),replacesSummary:n.z.boolean().optional().describe("Required on a re-completion: the summary covers everything, fixes included."),notChecked:n.z.preprocess(i,n.z.string().min(3).max(160).optional()).describe('What you did not check that the user could assume you did ("the page on a phone"); the card leads with it. Not what registered checks run or say they leave out (the card shows those).'),show:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({commands:n.z.array(n.z.string().min(1).max(400)).max(5).optional().describe("Command lines that show the result working. AlchemyLab runs each in a box on the work as it was and on the completed work; the user sees both."),artifact:n.z.string().min(1).max(200).optional().describe("A picture, page or document on the record: a result's address, a document id (doc000012) or asset:<file>.")}).optional()).describe("Optional: something more to show with the finished result. What the request already delivered is shown by itself."),worthKnowing:n.z.array(n.z.string().min(10)).max(5).optional().describe("What the user would regret not knowing, one per entry (a discovery, caveat, side effect, follow-up); always one when what was asked was already true or turned out different. Not the summary again; subject first."),guidance:n.z.array(n.z.object({body:n.z.string().min(10).max(800).describe('One fact or instruction for the next agent ("calc.py is generated from ops.json: edit ops.json and regenerate.")'),files:n.z.array(n.z.string().min(1)).min(1).max(8).optional().describe("Files or folders (globs) it is pinned to; without them (userSaid only) the whole project."),evidence:n.z.string().min(1).max(1500).optional().describe("What shows it (command and output, file); not with userSaid."),userSaid:n.z.string().min(8).max(600).optional().describe("A correction of the user's that should hold later: their words, exact.")}).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 in these files would hit too, or a user correction, proposed as guidance. Not taste, not request-specific, not already guidance. Usually none."),skipTestsReason:exports.skipTestsReasonSchema,skipTests:exports.skipTestsSchema,criteriaComments:n.z.array(n.z.object({key:n.z.string().min(1),comment:n.z.string().min(1),verify:n.z.array(n.z.string().min(1)).max(4).optional().describe("Commands that prove it in the sealed acceptance box (a grep, a targeted test, a screenshot into .visual-check/), judged on their output. Clean checkout of your commit, dependencies installed, nothing built: a check needing a build runs it in the same command. No git, no network, no branching on whether a tool exists. A proof you saw pass on this commit, written exactly as you ran it, is reused."),testFiles:n.z.array(n.z.string().min(1)).max(10).optional().describe("With verify, when it runs just these tests (not the whole suite): their repo paths. Only the user changes a test a promise names.")})).optional().describe("One entry per criterion, keyed as your pickup lists it: what you did and how you saw it hold."),afterRun:n.z.boolean().optional().describe("Call `run` with your final check, then this complete in the SAME response: it completes only if that run passes."),couldNotCheck:n.z.array(n.z.string().min(1)).max(20).optional().describe("Drives only: keys of claims you could not check (reason in criteriaComments); they read as unchecked. Stamp the rest with record_criteria_verification.")})).optional(),merge:n.z.preprocess(e=>"string"==typeof e?(()=>{try{return JSON.parse(e)}catch{return e}})():e,n.z.object({verifications:n.z.array(n.z.object({key:n.z.string().min(2).max(4),evidence:n.z.string().min(1)})).min(1)})).optional(),revert:n.z.object({reason:n.z.string(),confirmDestructive:n.z.boolean().optional()}).optional(),list:n.z.object({filters:n.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:n.z.string().optional(),scheduleDocumentId:n.z.string().optional(),questDocumentId:n.z.string().optional(),dateFrom:n.z.string().optional(),dateTo:n.z.string().optional(),limit:n.z.number().min(1).max(100).default(20),offset:n.z.number().min(0).default(0),sortBy:n.z.enum(["created","updated"]).default("updated"),sortDirection:n.z.enum(["asc","desc"]).default("desc")}).optional()}).optional(),search:n.z.object({query:n.z.string().min(1),searchIn:n.z.enum(["title","description","criteria","all"]).default("all"),filters:n.z.object({type:exports.RequestTypeEnum.optional(),status:exports.RequestStatusFilterEnum.optional(),planDocumentId:n.z.string().optional(),scheduleDocumentId:n.z.string().optional(),questDocumentId:n.z.string().optional(),dateFrom:n.z.string().optional(),dateTo:n.z.string().optional(),limit:n.z.number().min(1).max(100).default(20),offset:n.z.number().min(0).default(0)}).optional()}).optional()}).superRefine((e,t)=>{e.requestId
|
|
5761
5769
|
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]
|
|
5762
5770
|
n&&!e[n.field]&&t.addIssue({code:"custom",message:`'${r}' operation requires '${n.field}' data. ${n.hint}`})})
|
|
5763
5771
|
const m=n.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 (the user pointed at it), or 'new': the same rule as create_request's follows.")
|
|
5764
5772
|
exports.QuickWriteSchema=n.z.object({title:n.z.string().min(1,"Title is required"),description:n.z.string().min(1,"Description is required"),follows:m,path:n.z.string().optional(),documentId:d.optional(),content:n.z.union([n.z.string(),n.z.array(n.z.string())]),format:n.z.enum(["text","json"]).optional(),append:n.z.boolean().optional()}).transform(u).refine(h,{message:"path or documentId required"}),exports.QuickReplaceSchema=n.z.object({title:n.z.string().min(1,"Title is required"),description:n.z.string().min(1,"Description is required"),follows:m,operations:n.z.array(n.z.object({path:n.z.string().optional(),documentId:d.optional(),replacements:n.z.array(n.z.object({old_str:n.z.string().min(1),new_str:n.z.string(),match_mode:n.z.enum(["unique","all"]).optional()})).min(1),fuzzy_match:n.z.boolean().optional()}).transform(u).refine(h,{message:"path or documentId required"})).min(1),fuzzy_match:n.z.boolean().optional(),comment:n.z.string().min(1,"Comment describing the change is required")}),exports.MetaWorkflowListSchema=n.z.object({}),exports.MetaWorkflowGetSchema=n.z.object({workflowId:n.z.string().describe("Workflow ID to retrieve")}),exports.MetaWorkflowCreateSchema=n.z.object({name:n.z.string().min(1).describe("Unique name for the workflow"),shortName:n.z.string().min(1).max(10).describe("Short display name (required, max 10 chars)"),description:n.z.string().optional().describe("Description of what the workflow does"),testGate:n.z.enum(["off","auto","ask"]).optional().describe("Test gate: off (no test gate), auto (checks run automatically; a blocking command must pass, an advisory one may be skipped with a reason), ask (the user picks auto/interactive/skip at each completion)"),reviewGate:n.z.enum(["off","on"]).optional().describe("Review gate: off, or on (one reviewer checks each acceptance criterion, with the reviewers the kind assigns: guidance, model on engineering, any the workflow added)"),signoffGate:n.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:n.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:n.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=n.z.object({workflowId:n.z.string().describe("Workflow ID to update"),name:n.z.string().min(1).optional().describe("New name"),shortName:n.z.string().max(10).optional().describe("New short name (max 10 chars)"),description:n.z.string().optional().describe("New description"),testGate:n.z.enum(["off","auto","ask"]).optional().describe("Test gate: off (no test gate), auto (checks run automatically; a blocking command must pass, an advisory one may be skipped with a reason), ask (the user picks auto/interactive/skip at each completion)"),reviewGate:n.z.enum(["off","on"]).optional().describe("Review gate: off, or on (one reviewer checks each acceptance criterion, with the reviewers the kind assigns: guidance, model on engineering, any the workflow added)"),signoffGate:n.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:n.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:n.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=n.z.object({}),exports.MetaRecipeGetSchema=n.z.object({recipeId:n.z.string().describe("Recipe ID to retrieve")}),exports.MetaRecipeCreateSchema=n.z.object({name:n.z.string().min(1).describe("Unique name for the recipe"),description:n.z.string().optional().describe("Description of what the recipe does"),documentation:n.z.string().optional().describe("Detailed documentation")}),exports.MetaRecipeAddHookSchema=n.z.object({recipeId:n.z.string().describe("Recipe ID to add the hook to"),hookId:n.z.string().describe("Hook template ID to add"),position:n.z.number().int().min(0).optional().describe("Position in the recipe (0-based, appended if omitted)")}),exports.MetaRecipeRemoveHookSchema=n.z.object({recipeId:n.z.string().describe("Recipe ID to remove the hook from"),hookId:n.z.string().describe("Hook template ID to remove")}),exports.MetaHookListSchema=n.z.object({}),exports.MetaHookCreateSchema=n.z.object({name:n.z.string().min(1).describe("Unique name for the hook"),description:n.z.string().optional().describe("Description of what the hook does"),eventType:n.z.string().describe("Event type that triggers this hook (e.g., session_idle, request_completed, custom:plan_ready)"),category:n.z.enum(["alchemy","session","message","editing","reading","file_management","documents","history","tool"]).default("session").describe("Hook category"),agent:n.z.string().describe("Agent template to execute when hook fires"),promptTemplate:n.z.string().describe("Prompt template with {{variable}} substitution"),condition:n.z.record(n.z.any()).optional().describe("Condition object for filtering when hook fires"),timing:n.z.enum(["before","after"]).default("after").describe("When to fire relative to the event"),action:n.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:n.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:n.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:n.z.string().optional().describe("Custom event to emit on completion"),emitEventOnCondition:n.z.union([n.z.object({condition:n.z.object({field:n.z.string(),operator:n.z.enum(["contains","not_contains","equals","not_equals","matches","not_matches"]),value:n.z.string()}),event:n.z.string()}),n.z.array(n.z.object({condition:n.z.object({field:n.z.string(),operator:n.z.enum(["contains","not_contains","equals","not_equals","matches","not_matches"]),value:n.z.string()}),event:n.z.string()}))]).optional().describe("Conditional event emission based on output")}),exports.MetaWorkflowAddRecipeSchema=n.z.object({workflowId:n.z.string().describe("Workflow ID to add the recipe to"),recipeId:n.z.string().describe("Recipe ID to add"),position:n.z.number().int().min(0).optional().describe("Position in the workflow (0-based, appended if omitted)")}),exports.MetaWorkflowRemoveRecipeSchema=n.z.object({workflowId:n.z.string().describe("Workflow ID to remove the recipe from"),recipeId:n.z.string().describe("Recipe ID to remove")}),exports.MetaAgentListSchema=n.z.object({}),exports.MetaOperationsSchema=n.z.object({operation:n.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=n.z.object({}),exports.TestingProcessGetSchema=n.z.object({processId:n.z.string().describe("Test process ID to retrieve")})
|
|
5765
5773
|
const g=n.z.object({name:n.z.string().min(1).describe('Command name (e.g., "Build", "Unit Tests")'),command:n.z.string().min(1).describe('Shell command to run (e.g., "npm test")'),required:n.z.boolean().default(!0).describe("If true, failure blocks the request (default: true)"),timeout:n.z.number().int().min(10).max(3600).default(300).describe("Max execution time in seconds (default: 300)")})
|
|
5766
|
-
exports.TestingProcessCreateSchema=n.z.object({name:n.z.string().min(1).describe("Name for the test process"),environmentId:n.z.string().optional().describe("ID of the environment (confined runtime) this process runs in. Get it from environment_list or environment_setup."),description:n.z.string().optional().describe("Description of what this process verifies"),scope:n.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:n.z.array(n.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:n.z.array(n.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:n.z.array(n.z.string()).optional().describe("Commands that prepare the tree for the tests (install the package itself, generate files the tests import), e.g. [\"python -m pip install --no-deps -e .\"]. They run in its box before its commands on every run, and also before a request's criteria checks, which run in a clean checkout of the commit where `commands` do not run: a preparation step put in `commands` is missing there and those checks fail. If one fails, the run fails as the environment's fault and the commands do not run."),commands:n.z.array(g).min(1).describe("Test commands to execute in order"),verifyAcceptanceCriteria:n.z.boolean().optional().describe("When true, auto-tester verifies request acceptance criteria after running commands"),fingerprint:n.z.array(n.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:n.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:n.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:n.z.array(n.z.object({what:n.z.string().describe('The left-out part in plain words, e.g. "the Valkey backend tests"'),paths:n.z.array(n.z.string()).min(1).describe('Globs for the code that part checks, so a change there goes unchecked by this check: for a backend\'s tests, that backend (e.g. ["aiocache/backends/valkey.py"]); for browser or end-to-end tests, the app source they drive (e.g. ["src/**"]). Not only the left-out test files.'),command:n.z.string().min(1).max(300).optional().describe('The command that runs that part, e.g. "pnpm test:e2e": a request whose criterion runs it is not told this part was left out.')})).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 code it checks. A change to that code is reported to the user as not checked by this check, unless the request ran that part itself.')}),exports.TestingProcessDeleteSchema=n.z.object({processId:n.z.string().describe("Test process ID to delete")}),exports.EnvironmentListSchema=n.z.object({}),exports.ResultsSchema=n.z.object({operation:n.z.enum(["record","fetch","pin","list","read"]),path:n.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:n.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:n.z.record(n.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:n.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:n.z.string().optional().describe("record: MIME type, when you know it."),inputs:n.z.array(n.z.string()).optional().describe("record: addresses of the results this one was made from (provenance)."),note:n.z.string().optional().describe("record: one line on how it was produced."),address:n.z.string().optional().describe("pin / read / fetch: the full 64-character address the record returned (a unique prefix works too)."),at:n.z.string().optional().describe("pin: the pointer path in your worktree (.result.json is added). fetch: where the bytes land, under AlchemyLab's scratch folder for the project on the host (the answer gives the full path) or the box's `/scratch`. Defaults to results/<name>."),comment:n.z.string().optional().describe("pin: the commit comment for the pointer file."),offset:n.z.number().optional().describe("read: line to start at (default 0)."),limit:n.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:n.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ResultInspectSchema=n.z.object({operation:n.z.enum(["list","read"]),name:n.z.string().optional().describe("list: show every version recorded under this name, newest first."),address:n.z.string().optional().describe("read: the full 64-character address of the result, a check run's id (tr_...) or a command run's record id (trec...), to read what its commands printed."),offset:n.z.number().optional().describe("read: line to start at (default 0)."),limit:n.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:n.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ExperimentStartSchema=n.z.object({env:n.z.string().optional().describe("Environment by name (from environment_list), one whose image has the toolchain you need."),environmentId:n.z.string().optional().describe("Environment by id (alternative to `env`). The primary env is used if neither is given."),size:n.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 and 6 GB of memory (2 GB otherwise, unless the environment sets its own), for work that uses them (a CFD solve, meshing, a large parallel build, a model that runs out of memory). A size you do not hold opens another box."),confirmWorkspace:n.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=n.z.object({handle:n.z.string().describe("The experiment handle to tear down.")}),exports.ExperimentListSchema=n.z.object({}),exports.ReadSiteSchema=n.z.object({url:n.z.string()}),exports.DriveSiteSchema=n.z.object({url:n.z.string().describe("The running product, e.g. the environment's published URL or http://localhost:5173."),steps:n.z.array(n.z.object({action:n.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:n.z.string().optional().describe('Playwright selector, e.g. "text=Build" or "#start".'),text:n.z.string().optional(),key:n.z.string().optional().describe('e.g. "Enter"'),x:n.z.number().optional(),y:n.z.number().optional(),dy:n.z.number().optional(),ms:n.z.number().optional(),media:n.z.enum(["print","screen"]).optional()})).min(1).max(20),shots:n.z.enum(["each","final"]).optional().describe('"each" screenshots every step; default the final state only.'),session:n.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:n.z.boolean().optional().describe("Close the named session after this call."),width:n.z.number().optional().describe("Viewport width for a new session (default 1280; 390 is a phone).")}),exports.CaptureSiteSchema=n.z.object({url:n.z.string(),fullPage:n.z.boolean().optional().describe("The whole scrollable page instead of the viewport."),width:n.z.number().optional().describe("Viewport width (default 1280; 390 is a phone).")}),exports.PreviewSiteSchema=n.z.object({dir:n.z.string().describe('Repo-relative, e.g. "dist" or "." for the top of the repository.'),requestId:n.z.string().regex(/^req\d{6}$/).optional().describe("Serve from this unmerged request's working copy instead of main."),name:n.z.string().optional().describe("Preview handle (default: a slug of dir); reusing one replaces that preview."),label:n.z.string().optional()}),exports.FetchAssetSchema=n.z.object({url:n.z.string().describe("The http(s) URL of the image or font to download."),destPath:n.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=n.z.object({box:n.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:n.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:n.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:n.z.boolean().optional().describe("Capture the entire scrollable page instead of just the viewport."),serveDir:n.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:n.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:n.z.string().optional().describe("Alternative to box: the filename of an already-uploaded screenshot asset to annotate."),prompt:n.z.string().optional().describe("What you want the human to look at or mark up (shown with the request)."),timeoutMs:n.z.number().optional().describe("How long to wait for the human, in ms (default ~10 min).")}),exports.AskUserVisualSchema=n.z.object({question:n.z.string().describe('The question for the USER about the marked region (e.g. "Should this button use the primary color?").'),imagePath:n.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:n.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:n.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:n.z.array(n.z.string()).optional().describe("Optional answer choices (labels). Omit for a free-text answer."),context:n.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:n.z.enum(["low","normal","high"]).optional().describe('Question priority; "high" always reaches the user regardless of autonomy dial.')}),exports.EnvironmentGetSchema=n.z.object({environmentId:n.z.string().describe("Environment ID to retrieve")}),exports.EnvironmentProvisionSchema=n.z.object({environmentId:n.z.string(),why:n.z.string().optional().describe("Why provision now (shown on the approval).")}),exports.EnvironmentTeardownSchema=n.z.object({environmentId:n.z.string()}),exports.EnvironmentStatusSchema=n.z.object({environmentId:n.z.string()}),exports.EnvironmentVerifyStackSchema=n.z.object({environmentId:n.z.string()})
|
|
5774
|
+
exports.TestingProcessCreateSchema=n.z.object({name:n.z.string().min(1).describe("Name for the test process"),environmentId:n.z.string().optional().describe("ID of the environment (confined runtime) this process runs in. Get it from environment_list or environment_setup."),description:n.z.string().optional().describe("Description of what this process verifies"),scope:n.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:n.z.array(n.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:n.z.array(n.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:n.z.array(n.z.string()).optional().describe("Commands that prepare the tree for the tests (install the package itself, generate files the tests import), e.g. [\"python -m pip install --no-deps -e .\"]. They run in its box before its commands on every run, and also before a request's criteria checks, which run in a clean checkout of the commit where `commands` do not run: a preparation step put in `commands` is missing there and those checks fail. If one fails, the run fails as the environment's fault and the commands do not run."),commands:n.z.array(g).min(1).describe("Test commands to execute in order"),verifyAcceptanceCriteria:n.z.boolean().optional().describe("When true, auto-tester verifies request acceptance criteria after running commands"),fingerprint:n.z.array(n.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:n.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:n.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:n.z.array(n.z.object({what:n.z.string().describe('The left-out part in plain words, e.g. "the Valkey backend tests"'),paths:n.z.array(n.z.string()).min(1).describe('Globs for the code that part checks, so a change there goes unchecked by this check: for a backend\'s tests, that backend (e.g. ["aiocache/backends/valkey.py"]); for browser or end-to-end tests, the app source they drive (e.g. ["src/**"]). Not only the left-out test files.'),command:n.z.string().min(1).max(300).optional().describe('The command that runs that part, e.g. "pnpm test:e2e": a request whose criterion runs it is not told this part was left out.')})).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 code it checks. A change to that code is reported to the user as not checked by this check, unless the request ran that part itself.')}),exports.TestingProcessDeleteSchema=n.z.object({processId:n.z.string().describe("Test process ID to delete")}),exports.EnvironmentListSchema=n.z.object({}),exports.ResultsSchema=n.z.object({operation:n.z.enum(["record","fetch","pin","list","read"]),path:n.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:n.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:n.z.record(n.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:n.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:n.z.string().optional().describe("record: MIME type, when you know it."),inputs:n.z.array(n.z.string()).optional().describe("record: addresses of the results this one was made from (provenance)."),note:n.z.string().optional().describe("record: one line on how it was produced."),address:n.z.string().optional().describe("pin / read / fetch: the full 64-character address the record returned (a unique prefix works too)."),at:n.z.string().optional().describe("pin: the pointer path in your worktree (.result.json is added). fetch: where the bytes land, under AlchemyLab's scratch folder for the project on the host (the answer gives the full path) or the box's `/scratch`. Defaults to results/<name>."),comment:n.z.string().optional().describe("pin: the commit comment for the pointer file."),offset:n.z.number().optional().describe("read: line to start at (default 0)."),limit:n.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:n.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ResultInspectSchema=n.z.object({operation:n.z.enum(["list","read"]),name:n.z.string().optional().describe("list: show every version recorded under this name, newest first."),address:n.z.string().optional().describe("read: the full 64-character address of the result, a check run's id (tr_...) or a command run's record id (trec...), to read what its commands printed."),offset:n.z.number().optional().describe("read: line to start at (default 0)."),limit:n.z.number().optional().describe("read: how many lines (default 200). list: how many results (default 25)."),find:n.z.string().optional().describe("read: return only lines containing this text, instead of a range.")}),exports.ExperimentStartSchema=n.z.object({env:n.z.string().optional().describe("Environment by name (from environment_list), one whose image has the toolchain you need."),environmentId:n.z.string().optional().describe("Environment by id (alternative to `env`). The primary env is used if neither is given."),size:n.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 and 6 GB of memory (2 GB otherwise, unless the environment sets its own), for work that uses them (a CFD solve, meshing, a large parallel build, a model that runs out of memory). A size you do not hold opens another box."),confirmWorkspace:n.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=n.z.object({handle:n.z.string().describe("The experiment handle to tear down.")}),exports.ExperimentListSchema=n.z.object({}),exports.ReadSiteSchema=n.z.object({url:n.z.string()}),exports.DriveSiteSchema=n.z.object({url:n.z.string().describe("The running product, e.g. the environment's published URL or http://localhost:5173."),steps:n.z.array(n.z.object({action:n.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:n.z.string().optional().describe('Playwright selector, e.g. "text=Build" or "#start".'),text:n.z.string().optional(),key:n.z.string().optional().describe('e.g. "Enter"'),x:n.z.number().optional(),y:n.z.number().optional(),dy:n.z.number().optional(),ms:n.z.number().optional(),media:n.z.enum(["print","screen"]).optional()})).min(1).max(20),shots:n.z.enum(["each","final"]).optional().describe('"each" screenshots every step; default the final state only.'),session:n.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:n.z.boolean().optional().describe("Close the named session after this call."),width:n.z.number().optional().describe("Viewport width for a new session (default 1280; 390 is a phone).")}),exports.CaptureSiteSchema=n.z.object({url:n.z.string(),fullPage:n.z.boolean().optional().describe("The whole scrollable page instead of the viewport."),width:n.z.number().optional().describe("Viewport width (default 1280; 390 is a phone).")}),exports.PreviewSiteSchema=n.z.object({dir:n.z.string().optional().describe('Repo-relative, e.g. "dist" or "." for the top of the repository. Not needed with stop.'),requestId:n.z.string().regex(/^req\d{6}$/).optional().describe("Serve from this unmerged request's working copy instead of main."),name:n.z.string().optional().describe("Preview handle (default: a slug of dir); reusing one replaces that preview. With stop: the preview to stop."),stop:n.z.boolean().optional().describe("Stop a running preview instead of serving one: the one named, or the only one running."),label:n.z.string().optional()}),exports.FetchAssetSchema=n.z.object({url:n.z.string().describe("The http(s) URL of the image or font to download."),destPath:n.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=n.z.object({box:n.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:n.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:n.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:n.z.boolean().optional().describe("Capture the entire scrollable page instead of just the viewport."),serveDir:n.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:n.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:n.z.string().optional().describe("Alternative to box: the filename of an already-uploaded screenshot asset to annotate."),prompt:n.z.string().optional().describe("What you want the human to look at or mark up (shown with the request)."),timeoutMs:n.z.number().optional().describe("How long to wait for the human, in ms (default ~10 min).")}),exports.AskUserVisualSchema=n.z.object({question:n.z.string().describe('The question for the USER about the marked region (e.g. "Should this button use the primary color?").'),imagePath:n.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:n.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:n.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:n.z.array(n.z.string()).optional().describe("Optional answer choices (labels). Omit for a free-text answer."),context:n.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:n.z.enum(["low","normal","high"]).optional().describe('Question priority; "high" always reaches the user regardless of autonomy dial.')}),exports.EnvironmentGetSchema=n.z.object({environmentId:n.z.string().describe("Environment ID to retrieve")}),exports.EnvironmentProvisionSchema=n.z.object({environmentId:n.z.string(),why:n.z.string().optional().describe("Why provision now (shown on the approval).")}),exports.EnvironmentTeardownSchema=n.z.object({environmentId:n.z.string()}),exports.EnvironmentStatusSchema=n.z.object({environmentId:n.z.string()}),exports.EnvironmentVerifyStackSchema=n.z.object({environmentId:n.z.string()})
|
|
5767
5775
|
const y=n.z.object({image:n.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:n.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:n.z.string().optional().describe("Path to the project docker-compose file for its services. Omit if no services."),shellInit:n.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:n.z.record(n.z.string()).optional().describe("Connection env injected into the gate (e.g. DATABASE_URL) using SERVICE NAMES + internal ports, never localhost/host ports."),seed:n.z.array(n.z.string()).optional().describe("Optional commands run after services are up (e.g. migrations)."),artifacts:n.z.array(n.z.object({path:n.z.string().describe("Mount target for the writable, populated volume: relative to /work for a dependency folder the project's toolchain looks for there (node_modules, .venv, vendor/bundle), or absolute (e.g. /cache/cargo). Tools you install (apt/apk packages, binaries) go at an absolute path outside /work, such as /tools: /work is the project's folder, and its test runners see anything in it."),prepare:n.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:n.z.array(n.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:n.z.record(n.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:n.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:n.z.array(n.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:n.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:n.z.array(n.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:n.z.array(n.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:n.z.record(n.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:n.z.boolean().optional().describe("Run the box as root (privileged / unsafe levels). Needed for some caps + the docker socket."),dockerSocket:n.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:n.z.array(n.z.object({name:n.z.string(),target:n.z.string(),service:n.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:n.z.object({memory:n.z.string().optional().describe("Memory cap for this environment's boxes and its checks' boxes, e.g. '4g' (default 2g; 6g at size heavy). Set it when the test suite or build needs more."),cpus:n.z.number().optional().describe("A fixed CPU count. Usually leave it unset and set size instead."),size:n.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:n.z.number().optional().describe("Max processes (default 512)."),scratchMb:n.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:n.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.")}),w=r(y),b='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.',v="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."
|
|
5768
5776
|
exports.EnvironmentCreateSchema=n.z.object({name:n.z.string().min(1).describe("Name for the environment, e.g. node + postgres"),description:n.z.string().optional().describe("What this environment provides"),scope:n.z.string().optional().describe("Subproject path prefix for monorepos (e.g. packages/api/). Omit for the whole repo."),preset:n.z.enum(["office"]).optional().describe(b),config:w.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:n.z.boolean().optional().describe("Make this the project primary environment (default for new test processes). The first environment is primary automatically."),skipVerify:n.z.boolean().optional().describe(v),skipVerifyReason:n.z.string().optional().describe("Why this config cannot be verified (required with skipVerify: true; recorded in the log).")}),exports.EnvironmentUpdateSchema=n.z.object({environmentId:n.z.string(),name:n.z.string().min(1).optional(),description:n.z.string().optional(),scope:n.z.string().optional(),config:w.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:n.z.boolean().optional().describe("Make this the project primary environment"),skipVerify:n.z.boolean().optional().describe(v),skipVerifyReason:n.z.string().optional().describe("Why this config cannot be verified (required with skipVerify: true; recorded in the log).")}),exports.EnvironmentDeleteSchema=n.z.object({environmentId:n.z.string().describe("Environment ID to delete")}),exports.EnvironmentVerifySchema=n.z.object({preset:n.z.enum(["office"]).optional().describe(b),config:y.optional().describe("The proposed environment config to verify (image, install, services, connection). Optional with a preset."),smoke:n.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=n.z.object({name:n.z.string().max(60).optional().describe("A few words naming what you need (e.g. 'Node 20 for syntax checks')."),context:n.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.SetupFinishSchema=n.z.object({outcome:n.z.enum(["ready","declined","could_not_finish","no_code","not_yet"]).describe("How the run ended. 'ready' needs something saved in this run."),summary:n.z.string().min(1).describe("What was set up (names and ids), and an answer to every ask the run carries: done, or not done and why. Plain words; it reaches the waiting workers and the user."),startsAfter:n.z.string().regex(/^req\d{6}$/).optional().describe("With 'not_yet': the request still writing what this setup registers.")}),exports.TestingProcessUpdateSchema=n.z.object({processId:n.z.string(),environmentId:n.z.string().optional(),name:n.z.string().min(1).optional(),description:n.z.string().optional(),scope:n.z.string().optional().describe("A display label only; pathGlobs decides when the process runs."),pathGlobs:n.z.array(n.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:n.z.array(n.z.string()).optional().describe("Work-area labels this process covers; it applies when pathGlobs or areas match."),isActive:n.z.boolean().optional(),setup:n.z.array(n.z.string()).optional().describe("Replaces the setup commands (run in the check's box before its commands, and before the criteria checks in their clean checkout)."),commands:n.z.array(g).optional().describe("Replaces the test commands."),verifyAcceptanceCriteria:n.z.boolean().optional().describe("The auto-tester also verifies the request's acceptance criteria."),fingerprint:n.z.array(n.z.string()).optional().describe("Files whose content invalidates setup."),leavesOut:n.z.array(n.z.object({what:n.z.string().describe('The left-out part in plain words, e.g. "the Valkey backend tests"'),paths:n.z.array(n.z.string()).min(1).describe("Globs for the code that part checks (for end-to-end tests, the app source they drive), not only its test files."),command:n.z.string().min(1).max(300).optional().describe('The command that runs that part, e.g. "pnpm test:e2e".')})).optional().describe("Replaces what this check deliberately does not run, with the code each part checks; [] clears it."),poolSize:n.z.number().int().min(1).max(8).optional().describe("Warm test worktrees in the pool."),runtimeKept:n.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=n.z.object({operation:n.z.enum(["list","setup","run","switch-off","switch-on"]),process:n.z.string().optional().describe("run, switch-off, switch-on: one test process by name or id (run: omit for all active ones)."),userSaid:n.z.string().max(600).optional().describe("switch-off / switch-on: the user's words asking for it, copied exactly."),name:n.z.string().optional().describe("run: one test command by name; omit for all required commands."),ask:n.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=n.z.object({processId:n.z.string().describe("Test process ID containing the command"),commandName:n.z.string().describe('Name of the test command to run, or "setup" to run all setup commands in sequence')}),exports.RunSchema=n.z.object({command:n.z.string().optional().describe("A shell command, run in the box (pass box or env)."),name:n.z.string().optional().describe("A saved command by name, instead of command."),box:n.z.string().optional().describe("An experiment handle from experiment_start."),env:n.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:n.z.enum(["light","standard","heavy"]).optional().describe("With env: the box size (see experiment_start)."),args:n.z.string().optional(),grep:n.z.string().optional().describe("Filter output through grep -i with this pattern."),timeout:n.z.number().optional().describe("Seconds."),why:n.z.string().optional().describe("One short sentence, shown on the approval card when the command needs the user's approval."),cwd:n.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=n.z.object({operation:n.z.enum(["run","list","remove","register","setup"]).default("run").describe('list, setup, register or remove ("run" is the run tool\'s job).'),name:n.z.string().optional().describe("remove: the command name."),args:n.z.string().optional(),grep:n.z.string().optional(),command:n.z.string().optional(),timeout:n.z.number().optional(),cwd:n.z.string().optional(),context:n.z.string().optional().describe("setup: the command wanted, where it runs and what it is for."),commands:n.z.array(n.z.object({name:n.z.string().min(1).describe('e.g. "test:web"'),command:n.z.string().min(1),description:n.z.string().min(1).describe("What this command verifies."),cwd:n.z.string().optional().describe("Relative to the project root (monorepo subdirs); omit for the root."),timeout:n.z.number().optional().describe("Seconds (default 60)."),environment:n.z.string().optional().describe('The environment it runs in, by name (environment_list). Omitted, it goes where you ran it (a bare box with no saved environment is refused, so name one). Name "Workspace" only when it truly needs the host.'),target:n.z.string().optional().describe("The id your setup instructions give for a target this command prints the number for.")})).optional().describe("register: the commands to register.")}),exports.ReportTestResultsSchema=n.z.object({status:n.z.enum(["passed","failed","error"]).describe("Overall result"),testRunId:n.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:n.z.object({commands:n.z.array(n.z.object({name:n.z.string().describe("Command name"),status:n.z.enum(["passed","failed","skipped","error"]).describe("Command result"),output:n.z.string().optional().describe("Relevant output snippet"),diagnosis:n.z.string().optional().describe("Why it failed, if applicable")})).describe("Per-command results"),summary:n.z.string().describe("Brief overall summary"),acceptanceCriteria:n.z.array(n.z.object({criterion:n.z.string().describe("The acceptance criterion text"),status:n.z.enum(["verified","unverified","unclear"]),evidence:n.z.string().describe("How verified, or why not")})).optional().describe("AC verification results, if applicable"),faultClass:n.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:n.z.array(n.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:n.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."})})
|
|
5769
5777
|
const k=n.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 it gets and costs."),I=n.z.string().min(1).max(120,"because is one clause: 120 characters at most")
|