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/mcp-direct.js
CHANGED
|
@@ -1020,7 +1020,7 @@ const c=new Set(a),l="string"==typeof n.operation?n.operation:void 0
|
|
|
1020
1020
|
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(", ")}.`
|
|
1021
1021
|
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.`
|
|
1022
1022
|
return null},exports.FIELD_TAGS_META="alchemylab/fieldTags"
|
|
1023
|
-
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","
|
|
1023
|
+
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}
|
|
1024
1024
|
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.'
|
|
1025
1025
|
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??{}
|
|
1026
1026
|
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)
|
|
@@ -3529,22 +3529,22 @@ const de=async(e,r)=>(await Promise.resolve().then(()=>t(79414))).actionServerFe
|
|
|
3529
3529
|
if(G){const e="string"==typeof W?.sitting?.head?W.sitting.head:""
|
|
3530
3530
|
let t=e?null:X
|
|
3531
3531
|
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
|
|
3532
|
-
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/${E.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:K,skipTestsReason:D,skipTests:F,headSha:J,...le?{change:le}:{},...e?{look:e}:{},...void 0!==oe?{authoredSinceReview:oe}:{},...ie?{reviewDelta:ie}:{},...se?{intentMoves:se}:{},...X?.files?{files:X.files}:{}}))
|
|
3532
|
+
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/${E.projectId}/requests/${s}/validate-completion`,(0,w.validateCompletionBody)("tool",{sessionId:K,skipTestsReason:D,skipTests:F,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}:{}}))
|
|
3533
3533
|
return t?.data||t}
|
|
3534
|
-
let
|
|
3535
|
-
try{if(te=await
|
|
3536
|
-
|
|
3534
|
+
let pe=null
|
|
3535
|
+
try{if(te=await he(ce),te.canComplete&&"sitting-look"===te.plan?.action){const e=await(0,x.settleLook)(s,ae,de)
|
|
3536
|
+
pe=e.served,te=await he(e.look)}}catch(e){const t=(0,h.getErrorMessage)(e)
|
|
3537
3537
|
return P.error("Server validation failed",{requestId:s,error:t}),j({success:!1,error:"validation_request_failed"}),{content:[{type:"text",text:`Failed to validate completion: ${t}`}],isError:!0}}if(!te.canComplete){let e=`Cannot complete request ${s}: ${te.reason}\n`
|
|
3538
3538
|
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)
|
|
3539
3539
|
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"
|
|
3540
3540
|
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"}`})
|
|
3541
|
-
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 j({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:
|
|
3541
|
+
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 j({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)})),j({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")}]}
|
|
3542
3542
|
if(G&&!i)try{const e=await C.proxyGet(`/api/projects/${E.projectId}/requests/${encodeURIComponent(s)}/rounds`)
|
|
3543
3543
|
V=function(e,t){const r=[]
|
|
3544
3544
|
for(const t of Array.isArray(e)?e:[]){const e="string"==typeof t?.changed?t.changed.trim():""
|
|
3545
3545
|
e&&!r.includes(e)&&r.push(e)}const n=(t??"").replace(/\s+/g," ").trim()
|
|
3546
|
-
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
|
|
3547
|
-
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[]
|
|
3546
|
+
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
|
|
3547
|
+
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[]
|
|
3548
3548
|
const r="string"==typeof e.command?e.command.trim():""
|
|
3549
3549
|
if(!r)return[]
|
|
3550
3550
|
const n=Array.isArray(e.ledger)?e.ledger:[]
|
|
@@ -3552,35 +3552,36 @@ return t&&n.some(e=>"verify"===e?.source&&"string"==typeof e?.instrument&&e.inst
|
|
|
3552
3552
|
if(0===s.length)return
|
|
3553
3553
|
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)})
|
|
3554
3554
|
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)
|
|
3555
|
-
const me
|
|
3556
|
-
if(
|
|
3557
|
-
const
|
|
3558
|
-
if(
|
|
3559
|
-
e&&await e.createToolRecord({projectId:E.projectId,requestId:s,type:"tests-skipped",metadata:{...D?{reason:D}:{},...F&&F.length>0?{skipTests:F}:{},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(
|
|
3555
|
+
const ye=me&&fe.artifact?await C.getRequestClient().getRequest(s).then(e=>String(e?.title??"")).catch(()=>""):"",we=me&&!ge?await(0,x.startShowing)({projectId:E.projectId,post:(e,t)=>C.proxyPost(e,t),log:P,sessionExternalId:K??void 0,assetThere:e=>(0,x.assetIsThere)(E.basePath,e),resultsOf:e=>C.proxyGet(`/api/projects/${E.projectId}/results?requestId=${encodeURIComponent(e)}`).then(e=>Array.isArray(e?.data)?e.data:Array.isArray(e)?e:[])},s,fe,ye).catch(()=>""):""
|
|
3556
|
+
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 j({success:!1,error:"plan_missing"}),{content:[{type:"text",text:`Server validation returned no completion plan for ${s} -- the remote is likely mid-deploy. Retry complete.`}],isError:!0}
|
|
3557
|
+
const be="testing"===me.action||"human-review"===me.action||"merge-cleared"===me.action
|
|
3558
|
+
if(be&&te.testsSkipped&&(D||F&&F.length>0))try{const e=C.getToolRecordClient()
|
|
3559
|
+
e&&await e.createToolRecord({projectId:E.projectId,requestId:s,type:"tests-skipped",metadata:{...D?{reason:D}:{},...F&&F.length>0?{skipTests:F}:{},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()
|
|
3560
3560
|
if(e){let t=new Set
|
|
3561
3561
|
try{const e=await C.proxyGet(`/api/projects/${E.projectId}/session-messages`,{requestId:s,messageType:"report",limit:200}),r=Array.isArray(e)?e:e?.data??[]
|
|
3562
3562
|
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
|
|
3563
3563
|
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(B&&B.length>0&&K)for(const e of B.slice(0,2))try{const t=Array.isArray(e.files)&&e.files.length>0
|
|
3564
|
-
await C.proxyPost(`/api/projects/${E.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
|
|
3565
|
-
|
|
3566
|
-
e.refsSeen&&
|
|
3567
|
-
|
|
3568
|
-
if("review-
|
|
3569
|
-
if("
|
|
3570
|
-
if("
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
return{content:[{type:"text",text:`Request ${s}
|
|
3575
|
-
return{content:[{type:"text",text:`Request ${s}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
if(
|
|
3579
|
-
|
|
3580
|
-
|
|
3564
|
+
await C.proxyPost(`/api/projects/${E.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()
|
|
3565
|
+
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})
|
|
3566
|
+
e.refsSeen&&t?.namedRefs&&(e.refsSeen.namedRefs=t.namedRefs),await C.proxyPost(`/api/projects/${E.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 j({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 j({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})
|
|
3567
|
+
e.refsSeen&&r?.namedRefs&&(e.refsSeen.namedRefs=r.namedRefs)}catch(e){return j({success:!1,error:"plan_patch_failed"}),{content:[{type:"text",text:`Failed to apply completion transition (${Object.keys(t).join(", ")}): ${(0,h.getErrorMessage)(e)}`}],isError:!0}}if(j({success:!0}),"review-escalated"===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.`}]}
|
|
3568
|
+
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.`}]}
|
|
3569
|
+
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.`}]}
|
|
3570
|
+
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.`}]}
|
|
3571
|
+
if("promise-hold"===me.action){const e=me.detail?` (${me.detail})`:""
|
|
3572
|
+
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).`}]}
|
|
3573
|
+
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."
|
|
3574
|
+
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}_`:""
|
|
3575
|
+
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}_`:""
|
|
3576
|
+
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
|
|
3577
|
+
try{ke=await ve.getRequest(s)}catch(e){Ie=e}const $e=(0,v.kindProfile)(re??ke?.type)
|
|
3578
|
+
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.`}]}
|
|
3579
|
+
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.`}]}
|
|
3580
|
+
const xe=ke?.acceptanceCriteria||[]
|
|
3581
|
+
if(!1===te?.gates?.review?.enabled&&(0,S.checksOff)(te)&&!1===te?.gates?.signoff?.enabled&&"merge"===$e.finalize&&(0===xe.length||Array.isArray(N)&&N.length>0)){P.info("gates off and account recorded: merging in the same call",{requestId:s})
|
|
3581
3582
|
const e=await(0,u.handleMergeRequest)({requestId:s,verifications:[]},E,C,q,T,R)
|
|
3582
|
-
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
|
|
3583
|
-
return 0===
|
|
3583
|
+
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`:"",_e=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":""
|
|
3584
|
+
return 0===xe.length?{content:[{type:"text",text:`Request ${s} is ready. ${Se}${_e}No acceptance criteria defined: call merge directly.`}]}:{content:[{type:"text",text:`Request ${s}: all gates are clear.${Se}${_e}\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=""
|
|
3584
3585
|
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})
|
|
3585
3586
|
try{await(0,i.createFailureToolRecord)("complete-request",t,{error:r,details:n,requestId:e?.requestId},C.getToolRecordClient(),P,T,{requestId:e?.requestId,projectId:E.projectId,note:"Failed to complete request"})}catch{}return T.startTimer("tool_execution_time")({success:!1,error:t instanceof Error?t.name:"unknown"}),{content:[{type:"text",text:`Error: ${r}`}],isError:!0}}}
|
|
3586
3587
|
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),_=new Set},32283(e,exports,t){exports.def=void 0
|
|
@@ -3900,7 +3901,7 @@ const t=new Map,r=(e,t)=>`${e}\0${t}`
|
|
|
3900
3901
|
exports.SHAPING_AGENTS=new Set(["request-architect","planner"])
|
|
3901
3902
|
const n=new Map},74165(e,exports,t){exports.def=void 0
|
|
3902
3903
|
const r=t(76183),n=t(84648)
|
|
3903
|
-
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
|
|
3904
|
+
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 q=S.child({tool:"request_operations"})
|
|
3904
3905
|
try{const T=e
|
|
3905
3906
|
let{requestId:R}=T
|
|
3906
3907
|
const{operation:A}=T
|
|
@@ -3966,10 +3967,7 @@ try{const t=await(0,I.checkThreadTarget)(x.getProxyClient(),e,r)
|
|
|
3966
3967
|
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 T.update?.title?T.update.title.trim():""
|
|
3967
3968
|
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}
|
|
3968
3969
|
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
|
|
3969
|
-
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"
|
|
3970
|
-
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}
|
|
3971
|
-
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.")
|
|
3972
|
-
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 T.criterion?T.criterion.trim():""
|
|
3970
|
+
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 T.criterion?T.criterion.trim():""
|
|
3973
3971
|
if(!R||!e||!T.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}
|
|
3974
3972
|
try{const r=await x.getProxyClient().proxyPost(`/api/projects/${t.projectId}/requests/${R}/criteria/${encodeURIComponent(e)}/dispute`,{reason:T.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.`}
|
|
3975
3973
|
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}
|
|
@@ -3980,7 +3978,7 @@ return!1===r?.blocked?(0,w.withAlchemyKind)({content:[{type:"text",text:`\u26a0\
|
|
|
3980
3978
|
try{const e=x.getProxyClient(),r=await e.proxyGet(`/api/projects/${t.projectId}/tasks`,{requestId:R,limit:1}),n=r?.data||r||[]
|
|
3981
3979
|
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}`),q.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: ${A}`
|
|
3982
3980
|
return q.error(e),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${e}`}],isError:!0}}}}catch(e){return q.error("Request operations failed",{error:e instanceof Error?e.stack:String(e)}),_.incrementCounter("request_operations_errors",1),{content:[{type:"text",text:`\u274c **Request Operations Failed:** ${(0,y.getErrorMessage)(e)}`}],isError:!0}}}
|
|
3983
|
-
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","
|
|
3981
|
+
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"}`
|
|
3984
3982
|
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)
|
|
3985
3983
|
if(e){const t=String(e)
|
|
3986
3984
|
r.updatedAfter=n(t)?new Date(`${t}T00:00:00`).toISOString():new Date(t).toISOString()}if(t){const e=String(t)
|
|
@@ -5488,7 +5486,7 @@ if(null!=e)for(var s=r(e),i=0;i<s.length;i++)"default"!==s[i]&&n(t,e,s[i])
|
|
|
5488
5486
|
return o(t,e),t})
|
|
5489
5487
|
Object.defineProperty(exports,"__esModule",{value:!0}),exports.visualDefs=void 0
|
|
5490
5488
|
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)
|
|
5491
|
-
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})
|
|
5489
|
+
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})
|
|
5492
5490
|
try{const o=(e.url||"").trim()
|
|
5493
5491
|
if(!/^https?:\/\//i.test(o))return l("`url` must be an http(s) URL.")
|
|
5494
5492
|
const d=Array.isArray(e.steps)?e.steps:[]
|
|
@@ -5535,7 +5533,17 @@ if(a.skipped)return l({success:!0}),{content:[{type:"text",text:"The human skipp
|
|
|
5535
5533
|
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
|
|
5536
5534
|
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}}}
|
|
5537
5535
|
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")
|
|
5538
|
-
try{
|
|
5536
|
+
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)
|
|
5537
|
+
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}
|
|
5538
|
+
const o=function(e,t){const r=(t??"").trim().toLowerCase()
|
|
5539
|
+
if(0===e.length)return{none:!0}
|
|
5540
|
+
if(r){const t=e.find(e=>e.name.toLowerCase()===r)??e.find(e=>(e.label??"").trim().toLowerCase()===r)
|
|
5541
|
+
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)
|
|
5542
|
+
if("none"in o)return{text:"No preview is running, so there is nothing to stop."}
|
|
5543
|
+
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.`}
|
|
5544
|
+
const s=await r(`/api/previews/${encodeURIComponent(o.name)}`,{method:"DELETE",signal:AbortSignal.timeout(1e4)}),i=await s.json().catch(()=>({}))
|
|
5545
|
+
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)
|
|
5546
|
+
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():""
|
|
5539
5547
|
if(o){const e=function(e,t){if(!/^req\d{6}$/.test(e))return null
|
|
5540
5548
|
const r=t.replace(/^\.?\/+/,"").replace(/\/+$/,"")
|
|
5541
5549
|
return r.startsWith("/")||r.split("/").includes("..")?null:r&&"."!==r?r:"."}(o,r||".")
|
|
@@ -5590,13 +5598,13 @@ exports.SubagentOperationsSchema=n.z.object({sessionId:p.optional(),operation:n.
|
|
|
5590
5598
|
"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"}))})
|
|
5591
5599
|
const f=d
|
|
5592
5600
|
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
|
|
5593
|
-
"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
|
|
5601
|
+
"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
|
|
5594
5602
|
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]
|
|
5595
5603
|
n&&!e[n.field]&&t.addIssue({code:"custom",message:`'${r}' operation requires '${n.field}' data. ${n.hint}`})})
|
|
5596
5604
|
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.")
|
|
5597
5605
|
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")})
|
|
5598
5606
|
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)")})
|
|
5599
|
-
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()})
|
|
5607
|
+
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()})
|
|
5600
5608
|
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."
|
|
5601
5609
|
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."})})
|
|
5602
5610
|
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")
|