agents-relay 2.0.147 → 2.0.149
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/dashboard-html.js
CHANGED
|
@@ -74,10 +74,8 @@ ${dashboardStyles}
|
|
|
74
74
|
<div class="graph-preview">
|
|
75
75
|
<div id="graph-navigation" class="graph-navigation" aria-label="Graph navigation"><button type="button" id="graph-back" class="refresh-button">Back</button><button type="button" id="graph-forward" class="refresh-button">Forward</button><button type="button" id="graph-entry" class="refresh-button">Entry</button><span id="graph-breadcrumb" class="subtle"></span></div>
|
|
76
76
|
<div id="graph-detail" class="graph-source" aria-live="polite" hidden></div>
|
|
77
|
-
<div id="graph-
|
|
78
|
-
<div class="graph-preview-head"><h3 id="graph-title">Select a graph</h3><button type="button" id="graph-maximize" class="refresh-button">Maximize graph</button><span id="graph-path" class="subtle"></span></div>
|
|
77
|
+
<div class="graph-preview-head"><h3 id="graph-title">Select a graph</h3><button type="button" id="graph-maximize" class="refresh-button">Maximize graph</button></div><p id="graph-description" class="subtle"></p><span id="graph-path" class="subtle"></span>
|
|
79
78
|
<div id="graph-canvas" class="graph-canvas"><p class="empty">Select a graph to inspect it.</p></div>
|
|
80
|
-
<details class="graph-source-details"><summary>Mermaid source</summary><pre id="graph-source" class="graph-source"></pre></details>
|
|
81
79
|
</div>
|
|
82
80
|
</div>
|
|
83
81
|
</div>
|
|
@@ -150,7 +148,7 @@ const graphList=document.querySelector('#graph-list');
|
|
|
150
148
|
const graphTitle=document.querySelector('#graph-title');
|
|
151
149
|
const graphPath=document.querySelector('#graph-path');
|
|
152
150
|
const graphCanvas=document.querySelector('#graph-canvas');
|
|
153
|
-
const
|
|
151
|
+
const graphDescription=document.querySelector('#graph-description');
|
|
154
152
|
const rulesText=document.querySelector('#rules-text');
|
|
155
153
|
const rulesMeta=document.querySelector('#rules-meta');
|
|
156
154
|
const rulesStatus=document.querySelector('#rules-status');
|
|
@@ -295,7 +293,6 @@ function githubUsageCard(effective,status){
|
|
|
295
293
|
async function drawUsage(){try{const overview=await loadJSON('/api/usage');usageRoot.replaceChildren();usageMeta.textContent='Real account telemetry when available';const providers=Array.isArray(overview.providers)?overview.providers:[];const credits=Array.isArray(overview.credits)?overview.credits:[];const grid=element('div','usage-providers');if(overview.githubEffective||overview.githubStatus)grid.append(githubUsageCard(overview.githubEffective,overview.githubStatus));for(const provider of providers)grid.append(usageProvider(provider));for(const provider of credits)grid.append(creditProvider(provider));if(!grid.children.length){usageRoot.append(element('p','empty',text('Usage telemetry is unavailable.')));return;}usageRoot.append(grid);}catch(error){usageMeta.textContent='Unavailable';usageRoot.replaceChildren(element('p','error-state',text('Usage unavailable: '+error.message)));}}
|
|
296
294
|
function selectedWorkflowGraph(){return workflowGraphs.find(graph=>graph.id===selectedGraphId)||null;}
|
|
297
295
|
function graphLinks(source){const links=new Map();for(const line of source.split('\\n')){const match=/^\\s*click\\s+([A-Za-z0-9_]+)\\s+href\\s+"([a-z0-9-]+\\.mmd)"(?:\\s+"[^"\\n]*")?\\s*$/.exec(line);if(!match)continue;const id=match[2].slice(0,-4);if(workflowGraphs.some(graph=>graph.id===id))links.set(match[1],id);}return links;}
|
|
298
|
-
function graphTargets(id){const graph=selectedWorkflowGraph();return graph?[...new Set(graphLinks(graph.source).values())]:[];}
|
|
299
296
|
function graphFromHash(){if(location.hash==='#/graphs'||location.hash==='#/graphs/entry')return 'main';if(!location.hash.startsWith('#/graphs/'))return null;const id=location.hash.slice(9);return /^[a-z0-9-]+$/.test(id)?id:null;}
|
|
300
297
|
const WORKFLOW_NAV_KEY='agents-relay:workflow-navigation';
|
|
301
298
|
function graphHistory(){try{const value=JSON.parse(sessionStorage.getItem(WORKFLOW_NAV_KEY)||'null');if(value&&Array.isArray(value.entries)&&value.entries.every(id=>typeof id==='string'&&/^[a-z0-9-]+$/.test(id)))return value;}catch{}return {entries:[]};}
|
|
@@ -304,7 +301,7 @@ function graphHistoryIndex(){const n=history.state?.workflowIndex;return Number.
|
|
|
304
301
|
function initializeGraphHistory(){const route=graphFromHash();if(route===null)return;let data=graphHistory();const index=graphHistoryIndex();if(index!==null&&data.entries[index]===route)return;data={entries:[route]};saveGraphHistory(data);history.replaceState({...history.state,workflowIndex:0,workflowPath:route==='main'?['main']:['main',route]},'');}
|
|
305
302
|
function navigateGraph(id){if(id!=='entry'&&!workflowGraphs.some(graph=>graph.id===id))return;if(graphFromHash()===null)return;initializeGraphHistory();const data=graphHistory();const current=graphHistoryIndex()??0;const path=history.state?.workflowPath;const valid=Array.isArray(path)&&path.at(-1)===graphFromHash()&&path[0]==='main';const nextPath=id==='main'?['main']:[...(valid?path:['main']),id];data.entries=data.entries.slice(0,current+1);data.entries.push(id);saveGraphHistory(data);history.pushState({workflowIndex:current+1,workflowPath:nextPath},'','#/graphs/'+id);syncGraphRoute();}
|
|
306
303
|
function syncGraphRoute(){const route=graphFromHash();if(route===null)return;initializeGraphHistory();selectedGraphId=route;renderGraphList();void renderSelectedGraph();}
|
|
307
|
-
function renderGraphNavigation(){const graph=selectedWorkflowGraph();initializeGraphHistory();const data=graphHistory();const index=graphHistoryIndex()??0;document.querySelector('#graph-back').disabled=index<=0;document.querySelector('#graph-forward').disabled=index>=data.entries.length-1;const path=history.state?.workflowPath;const names=Array.isArray(path)&&path.at(-1)===selectedGraphId?path:[selectedGraphId==='main'?'main':'main',...(selectedGraphId==='main'?[]:[selectedGraphId])];document.querySelector('#graph-breadcrumb').textContent=names.map(id=>id==='main'?'Main (conceptual)':workflowGraphs.find(g=>g.id===id)?.name||id).join(' / ');
|
|
304
|
+
function renderGraphNavigation(){const graph=selectedWorkflowGraph();initializeGraphHistory();const data=graphHistory();const index=graphHistoryIndex()??0;document.querySelector('#graph-back').disabled=index<=0;document.querySelector('#graph-forward').disabled=index>=data.entries.length-1;const path=history.state?.workflowPath;const names=Array.isArray(path)&&path.at(-1)===selectedGraphId?path:[selectedGraphId==='main'?'main':'main',...(selectedGraphId==='main'?[]:[selectedGraphId])];document.querySelector('#graph-breadcrumb').textContent=names.map(id=>id==='main'?'Main (conceptual)':workflowGraphs.find(g=>g.id===id)?.name||id).join(' / ');}
|
|
308
305
|
function renderGraphList(){graphList.replaceChildren();if(!workflowGraphs.length){graphList.append(element('p','empty',text('No workflow graphs found.')));return;}for(const graph of workflowGraphs){const button=document.createElement('button');button.type='button';button.className='graph-button';button.setAttribute('aria-current',String(graph.id===selectedGraphId));button.append(element('strong','',text(graph.name)),element('span','',text(graph.path)));button.addEventListener('click',()=>navigateGraph(graph.id));graphList.append(button);}}
|
|
309
306
|
async function graphDetail(kind,name){const panel=document.querySelector('#graph-detail');panel.hidden=false;panel.replaceChildren(element('strong','',text(kind+': '+name)),element('p','subtle',text('Loading authoritative references…')));try{const params=new URLSearchParams({graph:selectedGraphId,kind,name});const result=await loadJSON('/api/graph-symbol?'+params);panel.replaceChildren(element('strong','',text(kind+': '+name)));const sections=[['Graph',[result.graph]],['Canonical contract',result.contracts],['Implementation',result.implementation],['Tests',result.tests]];for(const [heading,refs] of sections){panel.append(element('p','detail-label',text(heading)));if(!refs?.length){panel.append(element('p','subtle',text('No exact symbol reference found.')));continue;}for(const ref of refs){const link=element('a','',text(ref.path+(ref.line>0?':'+ref.line:'')+' ['+(ref.evidence==='exact-token'?'verified graph token':'text-search suggestion')+']'));if(WORKFLOW_SOURCE_REF){link.href='https://github.com/lalalic/agents-relay/blob/'+WORKFLOW_SOURCE_REF+'/'+ref.path+(ref.line>0?'#L'+ref.line:'');link.target='_blank';link.rel='noopener noreferrer';}else{link.removeAttribute('href');link.title='Source revision unavailable';}link.target='_blank';link.rel='noopener noreferrer';panel.append(link,document.createTextNode(' '));}}panel.scrollIntoView({block:'nearest'});}catch(error){panel.append(element('p','error-state',text('Reference lookup unavailable: '+error.message)));}}
|
|
310
307
|
function interactiveNode(node,callback,label){node.classList.add('drillable');node.setAttribute('tabindex','0');node.setAttribute('role','button');node.setAttribute('aria-label',label);node.addEventListener('click',callback);node.addEventListener('keydown',event=>{if(event.key==='Enter'||event.key===' '){event.preventDefault();callback();}});}
|
|
@@ -312,7 +309,7 @@ function graphNodeDefinitions(source){const result=new Map();for(const line of s
|
|
|
312
309
|
function addNodeLabel(node,label){const ns='http://www.w3.org/2000/svg';const old=node.querySelector('g.label');if(old)old.remove();const textNode=document.createElementNS(ns,'text');textNode.setAttribute('x','0');textNode.setAttribute('y','0');textNode.setAttribute('text-anchor','middle');textNode.setAttribute('dominant-baseline','middle');textNode.setAttribute('fill','#182536');textNode.setAttribute('font-size','13');const rect=node.querySelector('rect.label-container,rect.basic,rect');const width=Math.max(65,Number(rect?.getAttribute('width')||135)-14);const maxChars=Math.max(9,Math.floor(width/7.2));const words=label.split(/\\s+/);const lines=[];let current='';for(const word of words){if(current&&(current+' '+word).length>maxChars){lines.push(current);current=word;}else current=current?current+' '+word:word;}if(current)lines.push(current);for(let i=0;i<lines.length;i++){const tspan=document.createElementNS(ns,'tspan');tspan.setAttribute('x','0');tspan.setAttribute('dy',i===0?String(-((lines.length-1)*7)):'14');tspan.textContent=lines[i];textNode.append(tspan);}node.append(textNode);}
|
|
313
310
|
function sanitizeGraphSvg(markup){const parsed=new DOMParser().parseFromString(markup.replace(/^<svg\\b/,'<svg xmlns:xlink="http://www.w3.org/1999/xlink"'),'image/svg+xml');const source=parsed.documentElement;if(source.localName!=='svg'||parsed.querySelector('parsererror'))throw new Error('Invalid SVG');const ns='http://www.w3.org/2000/svg';const elements=new Set(['svg','g','path','rect','circle','ellipse','line','polyline','polygon','text','tspan','defs','marker','title','desc','clipPath']);const attributes=new Set(['id','class','viewBox','width','height','x','y','x1','y1','x2','y2','cx','cy','r','rx','ry','d','points','transform','fill','stroke','stroke-width','stroke-dasharray','stroke-linecap','stroke-linejoin','opacity','fill-opacity','stroke-opacity','font-size','font-family','font-weight','text-anchor','dominant-baseline','markerWidth','markerHeight','markerUnits','refX','refY','orient','preserveAspectRatio','clip-path','marker-start','marker-mid','marker-end','role','aria-label']);const clean=node=>{if(node.nodeType===3)return document.createTextNode(node.textContent||'');if(node.nodeType!==1||node.namespaceURI!==ns||!elements.has(node.localName))return null;const out=document.createElementNS(ns,node.localName);for(const attr of node.attributes){if(!attr.namespaceURI&&attr.name==='style'){for(const declaration of attr.value.split(';')){const split=declaration.indexOf(':');if(split<0)continue;const property=declaration.slice(0,split).trim();const cssValue=declaration.slice(split+1).trim();if((property==='stroke'||property==='fill')&&/^(?:#[0-9a-fA-F]{3,8}|rgb\\([0-9.,%\\s]+\\)|none|currentColor)$/.test(cssValue))out.setAttribute(property,cssValue);else if(property==='stroke-width'&&/^[0-9]+(?:\\.[0-9]+)?(?:px)?$/.test(cssValue))out.setAttribute(property,cssValue);}continue;}if(attr.namespaceURI||!attributes.has(attr.name))continue;if(['marker-start','marker-mid','marker-end','clip-path'].includes(attr.name)){if(!attr.value.startsWith('url(#')||!attr.value.endsWith(')')||!(/^[a-zA-Z0-9_-]+$/.test(attr.value.slice(5,-1))))continue;}else if(attr.value.toLowerCase().includes('url(')||attr.value.includes('<')||attr.value.includes('>'))continue;out.setAttribute(attr.name,attr.value);}for(const child of node.childNodes){const safe=clean(child);if(safe)out.appendChild(safe);}return out;};return clean(source);}
|
|
314
311
|
function applyGraphPalette(svg){const ns='http://www.w3.org/2000/svg';for(const path of svg.querySelectorAll('path')){if(path.closest('marker')){if(!path.hasAttribute('fill'))path.setAttribute('fill','#65768a');continue;}if(!path.hasAttribute('fill'))path.setAttribute('fill','none');if(!path.hasAttribute('stroke'))path.setAttribute('stroke','#65768a');}for(const node of svg.querySelectorAll('g.node')){const error=node.classList.contains('failure')||node.classList.contains('exception');for(const shape of node.querySelectorAll('rect,polygon,circle,ellipse')){shape.setAttribute('fill',error?'#ffe4e4':'#f9fbff');shape.setAttribute('stroke',error?'#be3030':'#657f9e');shape.setAttribute('stroke-width','1.3');}for(const label of node.querySelectorAll('text'))label.setAttribute('fill',node.classList.contains('drillable')?'#2367b1':error?'#7c1818':'#182536');}for(const rect of svg.querySelectorAll('rect')){if(!rect.closest('g.node')&&!rect.hasAttribute('fill'))rect.setAttribute('fill','#ffffff');}for(const label of svg.querySelectorAll('text'))if(!label.hasAttribute('fill'))label.setAttribute('fill','#182536');}
|
|
315
|
-
async function renderSelectedGraph(){const graph=selectedWorkflowGraph();renderGraphNavigation();document.querySelector('#graph-detail').hidden=true;if(!graph){graphTitle.textContent='Graph not found';graphPath.textContent='';
|
|
312
|
+
async function renderSelectedGraph(){const graph=selectedWorkflowGraph();renderGraphNavigation();document.querySelector('#graph-detail').hidden=true;if(!graph){graphTitle.textContent='Graph not found';graphPath.textContent='';graphDescription.textContent='';graphCanvas.replaceChildren(element('p','error-state',text('Graph not found: '+selectedGraphId+'. Select Entry or another graph.')));return;}graphTitle.textContent=graph.name;graphDescription.textContent=graph.description||'';graphPath.textContent=graph.path;graphCanvas.replaceChildren(element('p','subtle',text('Rendering…')));try{if(!window.mermaid)throw new Error('Mermaid renderer unavailable');if(!window.__relayMermaidReady){window.mermaid.initialize({startOnLoad:false,securityLevel:'strict',flowchart:{htmlLabels:false}});window.__relayMermaidReady=true;}const rendered=await window.mermaid.render('relay-graph-'+graph.id.replace(/[^A-Za-z0-9_-]/g,'-'),graph.source.replace(/^\\s*click\\s+[^\\n]*$/gm,''));if(graph.id!==selectedGraphId)return;const svg=sanitizeGraphSvg(rendered.svg);graphCanvas.replaceChildren(svg);const definitions=graphNodeDefinitions(graph.source);for(const node of svg.querySelectorAll('g.node')){const entry=Array.from(definitions.entries()).find(([key])=>node.id.includes('-flowchart-'+key+'-'));const label=entry?.[1]||'';if(label)addNodeLabel(node,label);const target=entry?.[0];const next=target?graphLinks(graph.source).get(target):null;if(next){interactiveNode(node,()=>navigateGraph(next),'Explore '+target);}else{const match=/^(state|guard|action):\\s*([A-Za-z0-9_.-]+)/.exec(label);if(match)interactiveNode(node,()=>graphDetail(match[1]==='guard'?'Guard':match[1]==='action'?'Action':'State',match[2]),'Inspect '+match[1]+' '+match[2]);}}applyGraphPalette(svg);}catch(error){graphCanvas.replaceChildren(element('p','error-state',text('Visual preview unavailable: '+error.message+'.')));}}
|
|
316
313
|
async function drawGraphs(){try{const graphs=await loadJSON('/api/graphs');workflowGraphs=Array.isArray(graphs)?graphs:[];const requested=graphFromHash();selectedGraphId=requested||'main';graphsMeta.textContent=(workflowGraphs.length-1)+' executable workflow graphs · main is conceptual';renderGraphList();await renderSelectedGraph();}catch(error){graphsMeta.textContent='Unavailable';workflowGraphs=[];selectedGraphId='main';renderGraphList();await renderSelectedGraph();graphPath.textContent='Executable graphs unavailable: '+error.message;}}
|
|
317
314
|
function renderAudit(records){auditLog.replaceChildren();if(!Array.isArray(records)||!records.length){auditLog.append(element('p','empty',text('No schema v5 routing decisions yet.')));return;}for(const record of records){const card=element('article','audit-entry');const head=element('div','audit-entry-head');head.append(element('strong','',text(routeWorker(record)+' · '+value(record?.selected?.model?.model))),element('span','subtle',text(new Date(record.timestamp).toLocaleString())));card.append(head);const correlation=record.correlation||{};card.append(element('div','subtle',text([correlation.job_id,correlation.task_id].filter(Boolean).join(' / ')||'uncorrelated')));const probabilities=record.decision_evidence?.probabilities||{};const gates=(record.gate_results||[]).filter(item=>!item.eligible).map(item=>item.candidate_id+': '+item.rejection_reasons.join(', '));const nl=String.fromCharCode(10);const details=document.createElement('pre');details.textContent='fallback: '+String(Boolean(record.decision_evidence?.fallback))+(Object.keys(probabilities).length?nl+'probabilities: '+JSON.stringify(probabilities,null,2):'')+(gates.length?nl+'rejected: '+gates.join(nl):'');card.append(details);auditLog.append(card);}}
|
|
318
315
|
async function drawAudit(){auditRefresh.disabled=true;try{renderAudit(await loadJSON('/api/decision-audit'));}catch(error){auditLog.replaceChildren(element('p','error-state',text('Audit unavailable: '+error.message)));}finally{auditRefresh.disabled=false;}}
|
package/dist/dashboard.js
CHANGED
|
@@ -15,12 +15,17 @@ const SSE_HEARTBEAT_MS = 15000;
|
|
|
15
15
|
const WORKFLOW_DIR = fileURLToPath(new URL('../workflow/', import.meta.url));
|
|
16
16
|
export async function dashboardWorkflowGraphs() {
|
|
17
17
|
const names = (await readdir(WORKFLOW_DIR)).filter(name => name.endsWith('.mmd')).sort();
|
|
18
|
-
return Promise.all(names.map(async (name) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
return Promise.all(names.map(async (name) => {
|
|
19
|
+
const source = await readFile(join(WORKFLOW_DIR, name), 'utf8');
|
|
20
|
+
const metadata = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(source)?.[1] ?? '';
|
|
21
|
+
const field = (key) => metadata.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'))?.[1]?.trim() ?? '';
|
|
22
|
+
return {
|
|
23
|
+
id: name.replace(/\.mmd$/, ''),
|
|
24
|
+
name: field('title') || (name === 'main.mmd' ? 'Main / Happy Path' : name.replace(/\.mmd$/, '').split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join(' ')),
|
|
25
|
+
description: field('description'),
|
|
26
|
+
path: `workflow/${name}`, source,
|
|
27
|
+
};
|
|
28
|
+
}));
|
|
24
29
|
}
|
|
25
30
|
function effectiveGitHubUsage(snapshot, runtime) {
|
|
26
31
|
const candidates = [snapshot?.resources.core, snapshot?.resources.graphql, snapshot?.resources.search, runtime?.resources.core, runtime?.resources.graphql, runtime?.resources.search]
|
package/package.json
CHANGED
|
@@ -25,14 +25,16 @@ Read and follow the shared Task / Job contract and orchestrator P0/self-job work
|
|
|
25
25
|
- shared worker/runtime/tool/Agents Relay defect;
|
|
26
26
|
- genuine external/human blocker.
|
|
27
27
|
4. Diagnose the smallest root cause from the combined durable Task + event sequence + logs. The final `task.error` is only one piece of evidence. Never retry first and investigate later.
|
|
28
|
-
5.
|
|
28
|
+
5. For ChatGPT browser failures, run `browser-workspace platform run chatgpt validate --session-id SESSION_ID` in a caller-owned `Relay` session and close the session afterward. Parse the action JSON from the platform-run `result.stdout` (the outer `result.ok` only reports command execution). The action returns exactly JSON `true` on validation success or one failure ID: `PAGE_NOT_READY`, `COMPOSER_NOT_READY`, or `DELIVERY_ERROR`. Treat any unexpected output or runner error as validation unavailable. These are UI-readiness results, NOT authentication results. None permits a human login escalation. Never independently interpret DOM snapshots, sign-in buttons, or disabled Send; only confirmed authentication failure from the actual Worker authentication protocol can justify login escalation. Inspect original task delivery/start-ack evidence separately.
|
|
29
|
+
|
|
30
|
+
6. Repair:
|
|
29
31
|
- For a transient failure, verify the transient condition is gone.
|
|
30
32
|
- For task-local failure, make the smallest durable repair in the original Job/PR when that is within scope.
|
|
31
33
|
- For a shared platform/runtime defect, first create or reuse the owning blocker P0/self-job. If it can be completed inside this recovery execution, fix/verify it and retry the original Task. If it cannot be completed now, register the original Task with `agents-relay blocker add` on that blocker Job and finish this Troubleshooter task; do not stay running while waiting for another Job.
|
|
32
34
|
- For a suspected genuine human/external blocker, do not publish `task.blocked` and do not branch on Neo's result yourself. Invoke `agents-relay escalation` with the original intent, blocker, and relevant context. The canonical graph owns the `resolved`, `failed`, and `needs_user` outcomes. When it returns `needs_user`, call Relay's `task escalate` operation with the concrete required action, resolution condition, and diagnostic context. Relay records the escalation and blocks this Troubleshooter; no worker remains running while waiting.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
7. For an immediate repair, only after the blocker is removed run `agents-relay task recover` for the original Task with this Troubleshooter's recovery Task ID and reconcile its Job. An authorized human response resumes this same Troubleshooter with its diagnostic context; verify the action before requesting original-task recovery. For deferred repair, rely on the durable blocker relation: merge-mode blockers wake matching dependents after merge; release/deploy/runtime blockers use `--resolution verified` and `agents-relay blocker resolve` only after verification.
|
|
36
|
+
8. Verify durable evidence that the original Task moved: at minimum QUEUED after retry, preferably a new execution / task.started / observable progress.
|
|
37
|
+
9. Publish this Troubleshooter Task's terminal event only after the recovery outcome is durable.
|
|
36
38
|
|
|
37
39
|
## Rules
|
|
38
40
|
|