Add scoped Agent-mode pipeline as experimental Classic fork.

Wire specialist waves, Brain consolidation, and Classic-compatible reports so Agent mode can run end-to-end via OpenRouter without changing the default Classic path.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-18 14:31:13 +00:00
co-authored by Cursor
parent e30522af9a
commit 82a48d99cf
24 changed files with 1522 additions and 22 deletions
+29 -2
View File
@@ -97,6 +97,15 @@
<input type="text" id="occupancy" placeholder="Occupancy (e.g. Business, Assembly)" style="width:100%;margin-top:8px" />
<input type="text" id="work_type" placeholder="Work type (new building, remodel, TI, addition)" style="width:100%;margin-top:8px" />
</details>
<div class="email-card">
<label>&#129302; Pipeline</label>
<label style="display:block;font-weight:400;margin-top:6px">
<input type="radio" name="pipeline_mode" value="classic" checked>
Classic pipeline &mdash; current production workflow</label>
<label style="display:block;font-weight:400;margin-top:6px">
<input type="radio" name="pipeline_mode" value="agent">
Agent pipeline &mdash; experimental specialist-agent workflow</label>
</div>
<div class="email-card">
<label>&#9881;&#65039; Compute <span class="opt">(text stages; vision always runs on the API)</span></label>
<label style="display:block;font-weight:400;margin-top:6px">
@@ -146,6 +155,8 @@ runBtn.addEventListener('click',async e=>{
});
const compute=(document.querySelector('input[name="compute"]:checked')||{}).value;
fd.append('text_local', compute==='local' ? 'true' : 'false');
const pipelineMode=(document.querySelector('input[name="pipeline_mode"]:checked')||{}).value||'classic';
fd.append('pipeline_mode',pipelineMode);
try{
const res=await fetch('/check',{method:'POST',body:fd});
if(!res.ok){ const err=await res.json().catch(()=>({detail:res.statusText}));
@@ -186,6 +197,15 @@ function poll(jobId){
function esc(s){ return (s==null?'':String(s)).replace(/[&<>]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[c])); }
function syncPipelineOptions(){
const agent=(document.querySelector('input[name="pipeline_mode"]:checked')||{}).value==='agent';
const local=document.querySelector('input[name="compute"][value="local"]');
local.disabled=agent;
if(agent&&local.checked) document.querySelector('input[name="compute"][value="openrouter"]').checked=true;
}
document.querySelectorAll('input[name="pipeline_mode"]').forEach(el=>el.addEventListener('change',syncPipelineOptions));
syncPipelineOptions();
// --- sheet viewer ---
function pageFor(num){ return sheetPage[num] || sheetPage[(num||'').toUpperCase()] || null; }
function sheetSpan(num){
@@ -226,7 +246,9 @@ function render(rep){
if(textModel) modelLine+=' · text: '+esc(textModel);
if(fallbacks) modelLine+=' ('+fallbacks+' cloud fallback'+(fallbacks>1?'s':'')+')';
}
statusEl.textContent='Analyzed '+s.sheets_analyzed+' sheets ('+(s.disciplines.join(', ')||'none')+')'+modelLine+'.';
const mode=s.pipeline_mode||'classic';
statusEl.textContent=(mode==='agent'?'Agent':'Classic')+' pipeline analyzed '+s.sheets_analyzed+
' sheets ('+(s.disciplines.join(', ')||'none')+')'+modelLine+'.';
let html='<div class="summary">'+
stat(s.conflicts_found,'conflicts')+
stat(s.by_severity.high,'high')+
@@ -235,7 +257,12 @@ function render(rep){
stat(s.assertions_extracted,'facts')+
stat(s.clusters_checked,'clusters')+
(s.cost_usd!=null?stat('$'+Number(s.cost_usd).toFixed(2),'cost'):'')+'</div>';
if(!rep.conflicts.length){ html+='<div class="empty">No cross-discipline conflicts detected.</div>'; }
if(s.agent_status==='skeleton'){
html+='<div class="note"><b>Agent pipeline skeleton:</b> routing and artifacts are active; '+
'specialist analysis is added in the next implementation phases.</div>';
} else if(!rep.conflicts.length){
html+='<div class="empty">No cross-discipline conflicts detected.</div>';
}
for(const c of rep.conflicts){
html+='<div class="conflict '+esc(c.severity)+'">'+
'<div class="row"><span class="cat">'+esc(c.category)+'</span>'+