agents-relay 2.0.142 → 2.0.144
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-workflow.js +10 -1
- package/dist/dashboard-html.js +2 -2
- package/package.json +1 -1
- package/workflow/main.mmd +31 -12
package/dist/cli-workflow.js
CHANGED
|
@@ -17,6 +17,15 @@ export function workflowCliResult(args) {
|
|
|
17
17
|
const index = buildWorkflowUsageIndex(graphs);
|
|
18
18
|
if (verb === 'discover')
|
|
19
19
|
return { graphs: [...graphs.keys()], symbols: [...new Set(index.map(x => x.id))].sort() };
|
|
20
|
+
if (verb === 'actions') {
|
|
21
|
+
const actionIds = [...new Set(index.filter(item => item.kind === 'action').map(item => item.id))].sort();
|
|
22
|
+
return {
|
|
23
|
+
count: actionIds.length,
|
|
24
|
+
actions: actionIds.map(id => ({ id, graphReferences: whereWorkflowSymbol(index, id, 'action'), cli: 'inspect-only; execution requires owning Graph runtime' })),
|
|
25
|
+
taskControls: ['task.retry', 'task.recover', 'task.cancel', 'task.unblock', 'task.escalate', 'task.resume-troubleshooter'],
|
|
26
|
+
warning: 'Task controls are not equivalent to arbitrary internal Graph Action execution. No direct CLI bypass of guards or SQLite fencing.',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
20
29
|
if (verb === 'where')
|
|
21
30
|
return whereWorkflowSymbol(index, value('--id'));
|
|
22
31
|
if (verb === 'inspect') {
|
|
@@ -84,5 +93,5 @@ export function workflowCliResult(args) {
|
|
|
84
93
|
}
|
|
85
94
|
if (verb === 'action')
|
|
86
95
|
throw new Error('durable graph authorization required; unsafe action request refused');
|
|
87
|
-
throw new Error('workflow supports discover, inspect, where, evaluate
|
|
96
|
+
throw new Error('workflow supports discover, actions, inspect, where, evaluate, verify, trace; mutations require durable authorization');
|
|
88
97
|
}
|
package/dist/dashboard-html.js
CHANGED
|
@@ -308,10 +308,10 @@ function renderGraphNavigation(){const graph=selectedWorkflowGraph();initializeG
|
|
|
308
308
|
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
309
|
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
310
|
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();}});}
|
|
311
|
-
function graphNodeDefinitions(source){const result=new Map();for(const line of source.split('\\n')){const pattern=/([A-Za-z0-9_]+)
|
|
311
|
+
function graphNodeDefinitions(source){const result=new Map();for(const line of source.split('\\n')){const pattern=/([A-Za-z0-9_]+)(?:\\["([^"\\n]+)"\\]|\\{"([^"\\n]+)"\\})/g;for(const match of line.matchAll(pattern))result.set(match[1],match[2]||match[3]);}return result;}
|
|
312
312
|
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
313
|
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
|
-
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');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');}
|
|
314
|
+
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
315
|
async function renderSelectedGraph(){const graph=selectedWorkflowGraph();renderGraphNavigation();document.querySelector('#graph-detail').hidden=true;if(!graph){graphTitle.textContent='Graph not found';graphPath.textContent='';graphSource.textContent='';graphCanvas.replaceChildren(element('p','error-state',text('Graph not found: '+selectedGraphId+'. Select Entry or another graph.')));return;}graphTitle.textContent=graph.name;graphPath.textContent=graph.path;graphSource.textContent=graph.source;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);if(graph.id!=='entry'){const events=[...new Set([...graph.source.matchAll(/\\|on:\\s*([A-Za-z0-9_.-]+)\\|/g)].map(m=>m[1]))];const links=document.querySelector('#graph-links');for(const event of events){const button=element('button','refresh-button',text('Event: '+event));button.addEventListener('click',()=>graphDetail('Event',event));links.append(button);}}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+'. Mermaid source remains available below.')));}}
|
|
316
316
|
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
317
|
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);}}
|
package/package.json
CHANGED
package/workflow/main.mmd
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
%% dashboard: presentation-only — not registered with GraphRuntime or the executable Graph DSL.
|
|
2
|
-
%%
|
|
2
|
+
%% PRINCIPLES (this graph is a readable overview, not an executable policy):
|
|
3
|
+
%% CONTRACT defines typed events, permissions and invariants; GRAPH defines legal transitions,
|
|
4
|
+
%% guards and ordering; CODE supplies verified facts and implements authorized effects.
|
|
5
|
+
%% GUARD checks SQLite-backed facts without mutation; ACTION requests a Graph-authorized
|
|
6
|
+
%% mutation; EFFECT performs/verifies an external side effect, never decides policy.
|
|
7
|
+
%% Unknown effects must reconcile before retry. CLI can inspect/trace graph symbols;
|
|
8
|
+
%% only explicitly authorized task controls are callable as CLI workflow actions.
|
|
9
|
+
%% Nodes link to canonical executable workflow graphs. SQLite remains authoritative.
|
|
3
10
|
flowchart LR
|
|
4
|
-
objective["
|
|
5
|
-
planner["Planner"]
|
|
6
|
-
worker["Worker"]
|
|
7
|
-
reviewer["Reviewer"]
|
|
8
|
-
delivery["Merge / Delivery"]
|
|
9
|
-
verify["Objective
|
|
10
|
-
completed["Completed"]
|
|
11
|
-
trouble["Troubleshooter"]
|
|
12
|
-
recover{"Recovery
|
|
13
|
-
escalation["
|
|
14
|
-
fix["
|
|
11
|
+
objective["state: Job Objective"]
|
|
12
|
+
planner["graph: Planner"]
|
|
13
|
+
worker["graph: Worker"]
|
|
14
|
+
reviewer["graph: Reviewer"]
|
|
15
|
+
delivery["graph: Merge / Delivery"]
|
|
16
|
+
verify["guard: Objective verified?"]
|
|
17
|
+
completed["state: Completed"]
|
|
18
|
+
trouble["graph: Troubleshooter"]
|
|
19
|
+
recover{"guard: Recovery verified?"}
|
|
20
|
+
escalation["graph: Human escalation"]
|
|
21
|
+
fix["graph: Review changes loop"]
|
|
15
22
|
|
|
16
23
|
objective --> planner
|
|
17
24
|
planner --> worker
|
|
@@ -36,6 +43,14 @@ flowchart LR
|
|
|
36
43
|
recover -->|human action required| escalation
|
|
37
44
|
escalation -->|resolved: reconcile and resume| trouble
|
|
38
45
|
|
|
46
|
+
subgraph principles["Contract / Graph / Code — principal rules"]
|
|
47
|
+
contractNote["contract: Event schema + authority + permissions"]
|
|
48
|
+
graphNote["graph: Transitions + Guards + Action ordering"]
|
|
49
|
+
codeNote["code: Facts + SQLite + guarded effects"]
|
|
50
|
+
contractNote ~~~ graphNote ~~~ codeNote
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
39
54
|
click objective href "job-lifecycle.mmd" "Job lifecycle"
|
|
40
55
|
click planner href "planner-orchestration.mmd" "Planner orchestration"
|
|
41
56
|
click worker href "task-lifecycle.mmd" "Task lifecycle"
|
|
@@ -51,3 +66,7 @@ flowchart LR
|
|
|
51
66
|
classDef normal fill:#eff6ff,stroke:#2563eb,color:#1e40af
|
|
52
67
|
class trouble,recover,escalation exception
|
|
53
68
|
class objective,planner,worker,reviewer,delivery,verify,completed,fix normal
|
|
69
|
+
class contractNote,graphNote,codeNote normal
|
|
70
|
+
|
|
71
|
+
%% Failed/unknown transitions are red; verified retries remain blue.
|
|
72
|
+
linkStyle 8,9,10,11,12,18 stroke:#be3030,stroke-width:2px
|