@kl-c/matrixos 0.1.7 → 0.1.10
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/.agents/command/adopt.md +50 -0
- package/.agents/command/features.md +35 -0
- package/.opencode/command/adopt.md +50 -0
- package/.opencode/command/features.md +35 -0
- package/dist/cli/config-manager/write-gateway-env.d.ts +7 -0
- package/dist/cli/index.js +942 -789
- package/dist/cli/install-validators.d.ts +1 -0
- package/dist/cli/skills/debugging/references/methodology/02-investigate.md +1 -1
- package/dist/cli/skills/refactor/SKILL.md +1 -1
- package/dist/cli/skills/superpowers-brainstorming/SKILL.md +162 -0
- package/dist/cli/skills/superpowers-brainstorming/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/frame-template.html +213 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/helper.js +167 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/server.cjs +723 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/start-server.sh +209 -0
- package/dist/cli/skills/superpowers-brainstorming/scripts/stop-server.sh +120 -0
- package/dist/cli/skills/superpowers-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/dist/cli/skills/superpowers-brainstorming/visual-companion.md +291 -0
- package/dist/cli/skills/superpowers-receiving-code-review/SKILL.md +216 -0
- package/dist/cli/skills/superpowers-receiving-code-review/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-systematic-debugging/CREATION-LOG.md +119 -0
- package/dist/cli/skills/superpowers-systematic-debugging/SKILL.md +299 -0
- package/dist/cli/skills/superpowers-systematic-debugging/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/dist/cli/skills/superpowers-systematic-debugging/condition-based-waiting.md +115 -0
- package/dist/cli/skills/superpowers-systematic-debugging/defense-in-depth.md +122 -0
- package/dist/cli/skills/superpowers-systematic-debugging/find-polluter.sh +63 -0
- package/dist/cli/skills/superpowers-systematic-debugging/root-cause-tracing.md +169 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-academic.md +14 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-pressure-1.md +58 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-pressure-2.md +68 -0
- package/dist/cli/skills/superpowers-systematic-debugging/test-pressure-3.md +69 -0
- package/dist/cli/skills/superpowers-writing-skills/SKILL.md +692 -0
- package/dist/cli/skills/superpowers-writing-skills/SUPERPOWERS_LICENSE +21 -0
- package/dist/cli/skills/superpowers-writing-skills/anthropic-best-practices.md +1150 -0
- package/dist/cli/skills/superpowers-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/dist/cli/skills/superpowers-writing-skills/graphviz-conventions.dot +172 -0
- package/dist/cli/skills/superpowers-writing-skills/persuasion-principles.md +187 -0
- package/dist/cli/skills/superpowers-writing-skills/render-graphs.js +168 -0
- package/dist/cli/skills/superpowers-writing-skills/testing-skills-with-subagents.md +384 -0
- package/dist/cli/types.d.ts +4 -0
- package/dist/cli-node/index.js +942 -789
- package/dist/config/schema/oh-my-opencode-config.d.ts +6 -0
- package/dist/config/schema/watchdog.d.ts +6 -0
- package/dist/features/anti-loop/background-registry.d.ts +10 -0
- package/dist/features/dashboard/frontend/css/style.css +211 -0
- package/dist/features/dashboard/frontend/index.html +173 -0
- package/dist/features/dashboard/frontend/js/api.js +2 -0
- package/dist/features/dashboard/frontend/js/app.js +349 -0
- package/dist/features/matrix-inventory/features.d.ts +45 -0
- package/dist/hooks/agent-anti-loop/hook.d.ts +16 -0
- package/dist/index.js +220 -45
- package/dist/{oh-my-opencode.schema.json → matrixos.schema.json} +43 -1
- package/dist/plugin/agent-anti-loop.d.ts +6 -0
- package/dist/skills/debugging/references/methodology/02-investigate.md +1 -1
- package/dist/skills/refactor/SKILL.md +1 -1
- package/dist/skills/superpowers-brainstorming/SKILL.md +162 -0
- package/dist/skills/superpowers-brainstorming/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-brainstorming/scripts/frame-template.html +213 -0
- package/dist/skills/superpowers-brainstorming/scripts/helper.js +167 -0
- package/dist/skills/superpowers-brainstorming/scripts/server.cjs +723 -0
- package/dist/skills/superpowers-brainstorming/scripts/start-server.sh +209 -0
- package/dist/skills/superpowers-brainstorming/scripts/stop-server.sh +120 -0
- package/dist/skills/superpowers-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/dist/skills/superpowers-brainstorming/visual-companion.md +291 -0
- package/dist/skills/superpowers-receiving-code-review/SKILL.md +216 -0
- package/dist/skills/superpowers-receiving-code-review/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-systematic-debugging/CREATION-LOG.md +119 -0
- package/dist/skills/superpowers-systematic-debugging/SKILL.md +299 -0
- package/dist/skills/superpowers-systematic-debugging/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/dist/skills/superpowers-systematic-debugging/condition-based-waiting.md +115 -0
- package/dist/skills/superpowers-systematic-debugging/defense-in-depth.md +122 -0
- package/dist/skills/superpowers-systematic-debugging/find-polluter.sh +63 -0
- package/dist/skills/superpowers-systematic-debugging/root-cause-tracing.md +169 -0
- package/dist/skills/superpowers-systematic-debugging/test-academic.md +14 -0
- package/dist/skills/superpowers-systematic-debugging/test-pressure-1.md +58 -0
- package/dist/skills/superpowers-systematic-debugging/test-pressure-2.md +68 -0
- package/dist/skills/superpowers-systematic-debugging/test-pressure-3.md +69 -0
- package/dist/skills/superpowers-writing-skills/SKILL.md +692 -0
- package/dist/skills/superpowers-writing-skills/SUPERPOWERS_LICENSE +21 -0
- package/dist/skills/superpowers-writing-skills/anthropic-best-practices.md +1150 -0
- package/dist/skills/superpowers-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/dist/skills/superpowers-writing-skills/graphviz-conventions.dot +172 -0
- package/dist/skills/superpowers-writing-skills/persuasion-principles.md +187 -0
- package/dist/skills/superpowers-writing-skills/render-graphs.js +168 -0
- package/dist/skills/superpowers-writing-skills/testing-skills-with-subagents.md +384 -0
- package/dist/testing/create-gateway-relay.d.ts +26 -0
- package/dist/tui.js +13 -0
- package/package.json +2 -1
- package/packages/shared-skills/skills/debugging/references/methodology/02-investigate.md +1 -1
- package/packages/shared-skills/skills/refactor/SKILL.md +1 -1
- package/packages/shared-skills/skills/superpowers-brainstorming/SKILL.md +162 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/frame-template.html +213 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/helper.js +167 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/server.cjs +723 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/start-server.sh +209 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/scripts/stop-server.sh +120 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/packages/shared-skills/skills/superpowers-brainstorming/visual-companion.md +291 -0
- package/packages/shared-skills/skills/superpowers-receiving-code-review/SKILL.md +216 -0
- package/packages/shared-skills/skills/superpowers-receiving-code-review/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/CREATION-LOG.md +119 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/SKILL.md +299 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/condition-based-waiting.md +115 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/defense-in-depth.md +122 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/find-polluter.sh +63 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/root-cause-tracing.md +169 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-academic.md +14 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-pressure-1.md +58 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-pressure-2.md +68 -0
- package/packages/shared-skills/skills/superpowers-systematic-debugging/test-pressure-3.md +69 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/SKILL.md +692 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/SUPERPOWERS_LICENSE +21 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/anthropic-best-practices.md +1150 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/graphviz-conventions.dot +172 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/persuasion-principles.md +187 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/render-graphs.js +168 -0
- package/packages/shared-skills/skills/superpowers-writing-skills/testing-skills-with-subagents.md +384 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
const AGENTS=['Morpheus','Trinity','Keymaker','Oracle','Prometheus','Boulder','Sentinel','Ghost','Neo','Tank']
|
|
2
|
+
const AGENT_ROLES={Morpheus:'orchestrator',Trinity:'design',Keymaker:'integration',Oracle:'review',Prometheus:'planning',Boulder:'worker',Sentinel:'qa',Ghost:'search',Neo:'executor',Tank:'deep-work'}
|
|
3
|
+
const MODELS=['deepseek-v4-flash-free','gpt-5.5-pro','gpt-5.5','claude-opus-4-7','gemini-3.1-pro','glm-5.2']
|
|
4
|
+
|
|
5
|
+
function rand(min,max){return Math.floor(Math.random()*(max-min+1))+min}
|
|
6
|
+
function pick(a){return a[Math.floor(Math.random()*a.length)]}
|
|
7
|
+
|
|
8
|
+
// Tab switching
|
|
9
|
+
document.querySelectorAll('[data-tab]').forEach(el=>{
|
|
10
|
+
el.addEventListener('click',e=>{
|
|
11
|
+
e.preventDefault()
|
|
12
|
+
const tab=el.dataset.tab
|
|
13
|
+
if(!tab||!['architect','health','cost','kanban','agents','sessions','logs','config'].includes(tab))return
|
|
14
|
+
const realTab=tab==='agents'||tab==='sessions'?'architect':tab
|
|
15
|
+
document.querySelectorAll('.sidebar-item.active,.header-tab.active,.tab-panel.active').forEach(n=>n.classList.remove('active'))
|
|
16
|
+
document.querySelectorAll(`.sidebar-item[data-tab="${tab}"],.header-tab[data-tab="${realTab}"]`).forEach(n=>n.classList.add('active'))
|
|
17
|
+
document.getElementById(`tab-${realTab}`).classList.add('active')
|
|
18
|
+
loadTab(realTab)
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
// Tab loading dispatch
|
|
23
|
+
function loadTab(tab){const f={architect:loadArchitect,health:loadHealth,cost:loadCost,kanban:loadKanban,logs:loadLogs,config:loadConfig,memory:loadMemory,skills:loadSkills}[tab];f&&f()}
|
|
24
|
+
|
|
25
|
+
// Skeleton helpers
|
|
26
|
+
function showSkeleton(container,type,count=4){
|
|
27
|
+
container.innerHTML=''
|
|
28
|
+
if(type==='kpi'){const g=document.createElement('div');g.className='kpi-grid';for(let i=0;i<count;i++){const s=document.createElement('div');s.className='skeleton skeleton-kpi';g.appendChild(s)}container.appendChild(g)}
|
|
29
|
+
else if(type==='table'){for(let i=0;i<5;i++){const s=document.createElement('div');s.className='skeleton skeleton-row';container.appendChild(s)}}
|
|
30
|
+
else if(type==='card'){const g=document.createElement('div');g.className='agent-grid';for(let i=0;i<6;i++){const s=document.createElement('div');s.className='skeleton skeleton-card';g.appendChild(s)}container.appendChild(g)}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function showError(container,msg){
|
|
34
|
+
container.innerHTML=`<div class="error"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg><p>${msg}</p></div>`
|
|
35
|
+
}
|
|
36
|
+
function showEmpty(container,msg){
|
|
37
|
+
container.innerHTML=`<div class="empty"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="9" y1="21" x2="9" y2="9"/></svg><p>${msg}</p></div>`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function liveDot(){return'<span class="badge-dot"></span>'}
|
|
41
|
+
function fmtDur(s){if(!s||s===0)return'--';if(s<60)return s+'s';if(s<3600)return Math.floor(s/60)+'m '+s%60+'s';return Math.floor(s/3600)+'h '+Math.floor((s%3600)/60)+'m'}
|
|
42
|
+
function fmtTime(iso){const d=new Date(iso);return d.toLocaleTimeString()}
|
|
43
|
+
function fmtCurrency(n){return'$'+n.toFixed(2)}
|
|
44
|
+
|
|
45
|
+
// ===== ARCHITECT =====
|
|
46
|
+
async function loadArchitect(){
|
|
47
|
+
const kpi=document.getElementById('archKpis'),grid=document.getElementById('agentGrid'),tbl=document.getElementById('sessionsTable'),queue=document.getElementById('sessionQueue')
|
|
48
|
+
showSkeleton(kpi,'kpi');showSkeleton(grid,'card');showSkeleton(tbl,'table')
|
|
49
|
+
try{
|
|
50
|
+
const[health,agents,sessions]=await Promise.all([API.health(),API.agents(),API.sessions(10)])
|
|
51
|
+
// KPI
|
|
52
|
+
kpi.innerHTML=`
|
|
53
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Active Agents</span><div class="kpi-icon purple"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="6" r="3"/><path d="M2 14c0-3.3 2.7-5 6-5s6 1.7 6 5"/></svg></div></div><div class="kpi-value">${health.agentsOnline}<span style="font-size:var(--text-lg);color:var(--text-tertiary);margin-left:4px">/${health.totalAgents}</span></div><div class="kpi-trend up"><svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor"><path d="M5 2L9 7H1z"/></svg>+1 from yesterday</div></div>
|
|
54
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Active Sessions</span><div class="kpi-icon cyan"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 4h12M2 8h8M2 12h10"/></svg></div></div><div class="kpi-value">${health.activeSessions}</div><div class="kpi-trend up"><svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor"><path d="M5 2L9 7H1z"/></svg>+6 from last hour</div></div>
|
|
55
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Queue Depth</span><div class="kpi-icon amber"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M3 4h10M3 8h10M3 12h10"/></svg></div></div><div class="kpi-value">${rand(5,20)}</div><div class="kpi-trend flat"><svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor"><path d="M1 5h8" stroke="currentColor" stroke-width="1.5"/></svg>Stable</div></div>
|
|
56
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Avg Response</span><div class="kpi-icon green"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 4.5v3.5l2.5 1.5"/></svg></div></div><div class="kpi-value">${(Math.random()*2+0.5).toFixed(1)}s</div><div class="kpi-trend up"><svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor"><path d="M5 7L1 2h8z"/></svg>-0.3s faster</div></div>`
|
|
57
|
+
|
|
58
|
+
// Agent filters
|
|
59
|
+
const counts={all:agents.length,online:agents.filter(a=>a.status==='online').length,idle:agents.filter(a=>a.status==='idle').length,offline:agents.filter(a=>a.status==='offline'||a.status==='error').length}
|
|
60
|
+
document.querySelectorAll('#agentFilters .filter-chip').forEach(f=>{
|
|
61
|
+
const key=f.dataset.filter
|
|
62
|
+
f.textContent=key==='all'?`All (${counts.all})`:key==='online'?`Active (${counts.online})`:key==='idle'?`Idle (${counts.idle})`:key==='offline'?`Offline (${counts.offline})`:f.textContent
|
|
63
|
+
f.onclick=()=>{
|
|
64
|
+
document.querySelectorAll('#agentFilters .filter-chip').forEach(x=>x.classList.remove('active'))
|
|
65
|
+
f.classList.add('active')
|
|
66
|
+
renderAgents(agents,key)
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
function renderAgents(list,filter){
|
|
71
|
+
const filtered=filter==='all'?list:list.filter(a=>a.status===filter||(filter==='offline'&&(a.status==='offline'||a.status==='error')))
|
|
72
|
+
grid.innerHTML=filtered.map(a=>{
|
|
73
|
+
const cpuColor=a.cpuPercent>80?'red':a.cpuPercent>60?'amber':a.cpuPercent>20?'green':'purple'
|
|
74
|
+
const statusCls=a.status==='online'?'success':a.status==='idle'?'warning':'danger'
|
|
75
|
+
const statusDot=a.status==='online'?liveDot():''
|
|
76
|
+
return `<div class="agent-card"><div class="agent-card-header"><div><div class="agent-name">${a.name}</div><div class="agent-role">${AGENT_ROLES[a.name]||'agent'}</div></div><span class="badge badge-${statusCls}">${statusDot}${a.status}</span></div>
|
|
77
|
+
<div class="agent-meta"><div class="agent-meta-row"><span>Sessions</span><span class="agent-meta-val">${a.sessions}</span></div>
|
|
78
|
+
<div class="agent-meta-row"><span>Uptime</span><span class="agent-meta-val">${fmtDur(a.uptime)}</span></div>
|
|
79
|
+
<div class="agent-meta-row"><span>Last heartbeat</span><span class="agent-meta-val">${rand(1,30)}s ago</span></div></div>
|
|
80
|
+
<div class="agent-cpu"><div class="agent-cpu-header"><span class="agent-cpu-label">CPU</span><span class="agent-cpu-pct" style="color:var(--${cpuColor==='green'?'success':cpuColor==='amber'?'warning':cpuColor==='red'?'danger':'accent-primary'})">${a.cpuPercent}%</span></div>
|
|
81
|
+
<div class="progress-bar"><div class="progress-fill ${cpuColor}" style="width:${a.cpuPercent}%"></div></div></div></div>`
|
|
82
|
+
}).join('')
|
|
83
|
+
}
|
|
84
|
+
renderAgents(agents,'all')
|
|
85
|
+
|
|
86
|
+
// Sessions table
|
|
87
|
+
tbl.innerHTML=`<table class="data-table"><thead><tr><th>Session ID</th><th>Agent</th><th>Type</th><th>Status</th><th>Duration</th><th>Tokens</th><th>Cost</th><th>Started</th></tr></thead><tbody>${sessions.map(s=>{
|
|
88
|
+
const type=pick(['orchestrate','design','integrate','review','plan','debug'])
|
|
89
|
+
const tokens=rand(1000,25000)
|
|
90
|
+
const statusMap={active:'success',completed:'success',failed:'danger',timeout:'warning'}
|
|
91
|
+
return `<tr><td class="mono" style="color:var(--accent-secondary)">${s.id.slice(0,12)}</td><td>${s.agent}</td><td><span class="badge badge-${pick(['purple','cyan'])}">${type}</span></td><td><span class="badge badge-${statusMap[s.status]||'info'}">${s.status}</span></td><td class="mono">${fmtDur(s.duration)}</td><td class="mono">${tokens.toLocaleString()}</td><td class="mono">${fmtCurrency(s.cost)}</td><td class="mono" style="color:var(--text-secondary)">${fmtTime(s.startedAt)}</td></tr>`
|
|
92
|
+
}).join('')}</tbody></table>`
|
|
93
|
+
|
|
94
|
+
// Session Queue
|
|
95
|
+
queue.innerHTML=`<div class="queue-bars"><div class="queue-col"><div class="queue-header"><span class="queue-label">High Priority</span><span class="queue-count" style="color:var(--danger)">${rand(2,5)} items</span></div><div class="queue-bar-group">${[4,3,2,1].map(w=>`<div class="queue-bar" style="flex:${w};height:24px;background:var(--danger);opacity:${0.7-w*0.13}"></div>`).join('')}</div></div>
|
|
96
|
+
<div class="queue-col"><div class="queue-header"><span class="queue-label">Normal</span><span class="queue-count" style="color:var(--warning)">${rand(3,6)} items</span></div><div class="queue-bar-group">${[3,2,2,1,1].map(w=>`<div class="queue-bar" style="flex:${w};height:24px;background:var(--warning);opacity:${0.6-w*0.1}"></div>`).join('')}</div></div>
|
|
97
|
+
<div class="queue-col"><div class="queue-header"><span class="queue-label">Low Priority</span><span class="queue-count" style="color:var(--info)">${rand(1,4)} items</span></div><div class="queue-bar-group">${[3,2,1].map(w=>`<div class="queue-bar" style="flex:${w};height:24px;background:var(--info);opacity:${0.5-w*0.1}"></div>`).join('')}</div></div></div>`
|
|
98
|
+
}catch(e){showError(kpi,'Failed to load');showError(grid,'');showError(tbl,'')}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ===== HEALTH =====
|
|
102
|
+
async function loadHealth(){
|
|
103
|
+
const kpi=document.getElementById('healthKpis'),gauges=document.getElementById('gaugeGrid'),hb=document.getElementById('heartbeatGrid'),inc=document.getElementById('incidentList')
|
|
104
|
+
showSkeleton(kpi,'kpi')
|
|
105
|
+
try{
|
|
106
|
+
const[health,metrics,incidents]=await Promise.all([API.health(),API.metrics(),API.incidents(8)])
|
|
107
|
+
const cpu=metrics.length?metrics[metrics.length-1].cpuPercent:45
|
|
108
|
+
const mem=health.memoryUsage?health.memoryUsage.percent||50:50
|
|
109
|
+
const disk=65
|
|
110
|
+
const cpuColor=cpu>80?'var(--danger)':cpu>60?'var(--warning)':'var(--success)'
|
|
111
|
+
const memColor=mem>80?'var(--danger)':mem>60?'var(--warning)':'var(--success)'
|
|
112
|
+
const diskColor=disk>80?'var(--danger)':disk>60?'var(--warning)':'var(--info)'
|
|
113
|
+
|
|
114
|
+
kpi.innerHTML=`
|
|
115
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">System Status</span><div class="kpi-icon green"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 8l3 3 5-6"/></svg></div></div><div class="kpi-value" style="color:var(--success)">Healthy</div><div class="kpi-trend flat"><span class="live-dot" style="margin-right:4px"></span>All systems operational</div></div>
|
|
116
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Uptime</span><div class="kpi-icon cyan"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 4.5v3.5l2.5 1.5"/></svg></div></div><div class="kpi-value">99.97%</div><div class="kpi-trend up">Last 30d: ${rand(5,20)}m downtime</div></div>
|
|
117
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Active Incidents</span><div class="kpi-icon red"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 2L1.5 13h13L8 2z"/><path d="M8 7v2.5M8 11.5v.5"/></svg></div></div><div class="kpi-value" style="color:var(--danger)">${incidents.length}</div><div class="kpi-trend down"><svg width="10" height="10" viewBox="0 0 10 10" fill="currentColor"><path d="M5 2L9 7H1z"/></svg>+${rand(0,3)} in last hour</div></div>
|
|
118
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Watchdog Cycles</span><div class="kpi-icon blue"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 5v3"/><circle cx="8" cy="11" r="0.5" fill="currentColor"/></svg></div></div><div class="kpi-value">${rand(800,2000)}</div><div class="kpi-trend flat">Last check: ${rand(1,10)}s ago</div></div>`
|
|
119
|
+
|
|
120
|
+
// Resource gauges (SVG)
|
|
121
|
+
function gauge(pct,color,label,sub){
|
|
122
|
+
const r=16,c=2*Math.PI*r,offset=c-(c*pct/100)
|
|
123
|
+
return `<div class="gauge-card"><div class="gauge-ring"><svg viewBox="0 0 40 40"><circle class="track" cx="20" cy="20" r="${r}"/><circle stroke="${color}" cx="20" cy="20" r="${r}" stroke-dasharray="${c}" stroke-dashoffset="${offset}" style="transition:stroke-dashoffset 500ms"/></svg><div class="gauge-val" style="color:${color}">${pct}%</div></div><div class="gauge-label">${label}</div><div class="gauge-sub">${sub}</div></div>`
|
|
124
|
+
}
|
|
125
|
+
const totalMem=(health.memoryUsage&&health.memoryUsage.total)||32
|
|
126
|
+
const usedMem=Math.round(totalMem*(mem||0)/100)
|
|
127
|
+
gauges.innerHTML=gauge(Math.round(cpu),cpuColor,'CPU Usage','8 cores / 16 threads')+gauge(mem,memColor,'Memory',`${usedMem} / ${totalMem} GB`)+gauge(disk,diskColor,'Disk','270 / 500 GB')
|
|
128
|
+
|
|
129
|
+
// Heartbeat Grid
|
|
130
|
+
const agents=['Morpheus','Keymaker','Trinity','Oracle','Boulder']
|
|
131
|
+
const hbLegendEl=document.getElementById('hbLegend')
|
|
132
|
+
hbLegendEl.innerHTML=`<span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--success)"><span style="width:6px;height:6px;border-radius:50%;background:var(--success)"></span>Healthy</span><span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--warning)"><span style="width:6px;height:6px;border-radius:50%;background:var(--warning)"></span>Degraded</span><span style="display:flex;align-items:center;gap:4px;font-size:var(--text-xs);color:var(--danger)"><span style="width:6px;height:6px;border-radius:50%;background:var(--danger)"></span>Missed</span>`
|
|
133
|
+
hb.innerHTML=agents.map((name,i)=>{
|
|
134
|
+
const initial=name[0]
|
|
135
|
+
const cells=[]
|
|
136
|
+
for(let t=0;t<8;t++){
|
|
137
|
+
const r=Math.random()
|
|
138
|
+
const cls=r>0.85?'fail':r>0.7&&i>2?'warn':'healthy'
|
|
139
|
+
cells.push(`<div class="hb-cell ${cls}" title="${name} t-${(7-t)*10}s">${initial}</div>`)
|
|
140
|
+
}
|
|
141
|
+
return cells.join('')
|
|
142
|
+
}).join('')
|
|
143
|
+
hb.insertAdjacentHTML('afterend',`<div style="margin-top:var(--space-3);font-size:var(--text-xs);color:var(--text-tertiary)">Agents: ${agents.map(a=>a[0]+'='+a).join(', ')}</div>`)
|
|
144
|
+
|
|
145
|
+
// Incidents
|
|
146
|
+
if(!incidents.length){showEmpty(inc,'No incidents recorded');return}
|
|
147
|
+
inc.innerHTML=incidents.slice(0,5).map(i=>{
|
|
148
|
+
const icon=i.outcome==='failed'||i.trigger==='heartbeat_loss'?'danger':i.outcome==='warning'?'warning':'info'
|
|
149
|
+
const iconsvg=i.trigger==='heartbeat_loss'?'<path d="M2 2l12 12M14 2L2 14"/>':i.trigger==='memory_exceeded'||i.trigger==='cpu_exceeded'?'<circle cx="8" cy="8" r="6"/><path d="M8 5v3.5M8 10.5v.5"/>':'<path d="M8 2L1.5 13h13L8 2z"/><path d="M8 7v2.5M8 11.5v.5"/>'
|
|
150
|
+
const severity=i.outcome==='failed'?'Critical':i.outcome==='warning'?'Warning':'Info'
|
|
151
|
+
return `<div class="incident-item"><div class="incident-icon" style="background:var(--${icon==='danger'?'danger':icon==='warning'?'warning':'info'}-muted);color:var(--${icon==='danger'?'danger':icon==='warning'?'warning':'info'})"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round">${iconsvg}</svg></div>
|
|
152
|
+
<div class="incident-content"><div class="incident-title">Watchdog: ${i.trigger} — ${i.actionTaken}</div><div class="incident-desc">Level ${i.fromLevel} → ${i.toLevel}. ${i.detail||'Auto-recovery engaged.'}</div><div class="incident-time">${new Date(i.timestamp).toISOString()}</div></div>
|
|
153
|
+
<span class="badge badge-${icon==='danger'?'danger':icon==='warning'?'warning':'info'}">${severity}</span></div>`
|
|
154
|
+
}).join('')
|
|
155
|
+
}catch(e){showError(kpi,'Failed to load Health data')}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ===== COST =====
|
|
159
|
+
async function loadCost(){
|
|
160
|
+
const kpi=document.getElementById('costKpis'),tbl=document.getElementById('costByAgentTable'),alerts=document.getElementById('alertList')
|
|
161
|
+
showSkeleton(kpi,'kpi')
|
|
162
|
+
try{
|
|
163
|
+
const cost=await API.cost()
|
|
164
|
+
kpi.innerHTML=`
|
|
165
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Total Cost</span><div class="kpi-icon purple"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 4.5v7M5.5 6.5h4"/></svg></div></div><div class="kpi-value small">${fmtCurrency(cost.total)}</div><div class="kpi-trend up">All time</div></div>
|
|
166
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Daily</span><div class="kpi-icon cyan"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="6"/><path d="M8 4.5v3.5l2.5 1.5"/></svg></div></div><div class="kpi-value small">${fmtCurrency(cost.daily)}</div><div class="kpi-trend up">Today</div></div>
|
|
167
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Monthly</span><div class="kpi-icon amber"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M2 6h12M4 2v2M12 2v2"/><rect x="2" y="4" width="12" height="10" rx="1"/></svg></div></div><div class="kpi-value small">${fmtCurrency(cost.monthly)}</div><div class="kpi-trend up">This month</div></div>
|
|
168
|
+
<div class="kpi-card"><div class="kpi-card-header"><span class="kpi-label">Budget Left</span><div class="kpi-icon ${cost.budgetRemaining<1000?'red':'green'}"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M4 8l3 3 5-6"/></svg></div></div><div class="kpi-value small" style="color:var(${cost.budgetRemaining<1000?'--danger':'--success'})">${fmtCurrency(cost.budgetRemaining)}</div><div class="kpi-trend ${cost.budgetRemaining<1000?'down':'up'}">Remaining</div></div>`
|
|
169
|
+
|
|
170
|
+
tbl.innerHTML=`<table class="data-table"><thead><tr><th>Agent</th><th>Cost</th><th>Share</th><th>Distribution</th></tr></thead><tbody>${(cost.byAgent||[]).map(a=>`<tr><td>${a.agent}</td><td class="mono">${fmtCurrency(a.cost)}</td><td class="mono">${a.percentage}%</td><td><div class="progress-bar"><div class="progress-fill purple" style="width:${a.percentage}%"></div></div></td></tr>`).join('')}</tbody></table>`
|
|
171
|
+
|
|
172
|
+
if(!cost.alerts||!cost.alerts.length){showEmpty(alerts,'No active alerts');return}
|
|
173
|
+
alerts.innerHTML=cost.alerts.map(a=>`<div class="alert-item ${a.severity}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>${a.message}</div>`).join('')
|
|
174
|
+
}catch(e){showError(kpi,'Failed to load Cost data')}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ===== KANBAN =====
|
|
178
|
+
async function loadKanban(){
|
|
179
|
+
const board=document.getElementById('kanbanBoard')
|
|
180
|
+
try{
|
|
181
|
+
const data=await API.kanban()
|
|
182
|
+
const labels={pending:'Pending',in_progress:'In Progress',completed:'Completed',failed:'Failed'}
|
|
183
|
+
const colors={pending:'dim',in_progress:'info',completed:'success',failed:'danger'}
|
|
184
|
+
const cols=data.columns||{}
|
|
185
|
+
board.innerHTML=Object.entries(labels).map(([key,label])=>{
|
|
186
|
+
const tasks=cols[key]||[]
|
|
187
|
+
return `<div class="kanban-col"><div class="kanban-header"><span>${label}</span><span class="kanban-count">${tasks.length}</span></div><div class="kanban-body">${
|
|
188
|
+
tasks.length?tasks.map(t=>`<div class="kanban-card"><div class="kanban-card-title">${t.title}</div><div class="kanban-card-meta"><span>${t.agent}</span><span class="prio ${t.priority}">${t.priority}</span></div></div>`).join(''):'<div style="padding:20px;text-align:center;color:var(--text-tertiary);font-size:var(--text-xs)">No tasks</div>'
|
|
189
|
+
}</div></div>`
|
|
190
|
+
}).join('')
|
|
191
|
+
}catch(e){board.innerHTML=`<div class="error" style="grid-column:1/-1"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg><p>Failed to load Kanban</p></div>`}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ===== LOGS =====
|
|
195
|
+
let logStream=null
|
|
196
|
+
async function loadLogs(){
|
|
197
|
+
const container=document.getElementById('logContainer')
|
|
198
|
+
container.innerHTML='<div style="padding:8px;color:var(--text-tertiary)">Connecting...</div>'
|
|
199
|
+
try{
|
|
200
|
+
const res=await fetch('/api/logs/stream')
|
|
201
|
+
const reader=res.body.getReader()
|
|
202
|
+
const decoder=new TextDecoder()
|
|
203
|
+
let buffer=''
|
|
204
|
+
container.innerHTML=''
|
|
205
|
+
function appendLog(entry){
|
|
206
|
+
const level=entry.level||'info'
|
|
207
|
+
const colorMap={info:'var(--info)',warn:'var(--warning)',error:'var(--danger)',debug:'var(--text-tertiary)'}
|
|
208
|
+
const div=document.createElement('div');div.className='log-entry'
|
|
209
|
+
const activeFilter=document.querySelector('#logFilters .filter-chip.active')
|
|
210
|
+
if(activeFilter&&activeFilter.dataset.level!=='all'&&activeFilter.dataset.level!==level)div.classList.add('hidden')
|
|
211
|
+
div.innerHTML=`<span class="log-time">${(entry.timestamp||'').slice(11,19)}</span><span class="log-level ${level}">${level}</span><span class="log-source">${entry.source||'system'}</span><span class="log-msg">${entry.message||''}</span>`
|
|
212
|
+
container.appendChild(div)
|
|
213
|
+
container.scrollTop=container.scrollHeight
|
|
214
|
+
}
|
|
215
|
+
// Log level filters
|
|
216
|
+
document.querySelectorAll('#logFilters .filter-chip').forEach(f=>{
|
|
217
|
+
f.onclick=()=>{
|
|
218
|
+
document.querySelectorAll('#logFilters .filter-chip').forEach(x=>x.classList.remove('active'))
|
|
219
|
+
f.classList.add('active')
|
|
220
|
+
const level=f.dataset.level
|
|
221
|
+
document.querySelectorAll('.log-entry').forEach(e=>{
|
|
222
|
+
if(level==='all')e.classList.remove('hidden')
|
|
223
|
+
else e.querySelector('.log-level').classList.contains(level)?e.classList.remove('hidden'):e.classList.add('hidden')
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
})
|
|
227
|
+
document.getElementById('logClearBtn').onclick=()=>{container.innerHTML=''}
|
|
228
|
+
while(true){
|
|
229
|
+
const{done,value}=await reader.read()
|
|
230
|
+
if(done)break
|
|
231
|
+
buffer+=decoder.decode(value,{stream:true})
|
|
232
|
+
const lines=buffer.split('\n')
|
|
233
|
+
buffer=lines.pop()||''
|
|
234
|
+
for(const line of lines){
|
|
235
|
+
if(!line.startsWith('data:'))continue
|
|
236
|
+
try{
|
|
237
|
+
const data=JSON.parse(line.slice(5))
|
|
238
|
+
if(data.type==='init'&&Array.isArray(data.logs))data.logs.forEach(l=>appendLog(l))
|
|
239
|
+
else if(!data.type)appendLog(data)
|
|
240
|
+
}catch(e){/* skip parse errors */}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}catch(e){container.innerHTML=`<div class="error"><svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg><p>Failed to connect: ${e.message}</p></div>`}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ===== CONFIG =====
|
|
247
|
+
function stripJsonc(text){return text.replace(/\/\/.*$/gm,'').replace(/\/\*[\s\S]*?\*\//g,'')}
|
|
248
|
+
async function loadConfig(){
|
|
249
|
+
const editor=document.getElementById('configEditor')
|
|
250
|
+
const status=document.getElementById('configStatus')
|
|
251
|
+
try{
|
|
252
|
+
const cfg=await API.config()
|
|
253
|
+
if(cfg&&cfg.error){editor.value='';status.textContent='Error: '+cfg.error;return}
|
|
254
|
+
const raw=cfg&&cfg._raw?cfg._raw:JSON.stringify(cfg,null,2)
|
|
255
|
+
editor.value=raw
|
|
256
|
+
status.textContent='Loaded'
|
|
257
|
+
}catch(e){editor.value='';status.textContent='Error loading config'}
|
|
258
|
+
document.getElementById('configSaveBtn').onclick=async()=>{
|
|
259
|
+
try{
|
|
260
|
+
if(!editor.value.trim())throw new Error('Editor empty')
|
|
261
|
+
const parsed=JSON.parse(stripJsonc(editor.value))
|
|
262
|
+
const r=await fetch('/api/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(parsed)})
|
|
263
|
+
const res=await r.json()
|
|
264
|
+
status.textContent=res.ok?'Saved ✓':`Error: ${res.error}`
|
|
265
|
+
status.style.color=res.ok?'var(--success)':'var(--danger)'
|
|
266
|
+
}catch(e){status.textContent=`Invalid JSON: ${e.message}`;status.style.color='var(--danger)'}
|
|
267
|
+
}
|
|
268
|
+
document.getElementById('configRefreshBtn').onclick=loadConfig
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ===== MEMORY =====
|
|
272
|
+
async function loadMemory(){
|
|
273
|
+
const grid=document.getElementById('memoryGrid')
|
|
274
|
+
grid.innerHTML='<div style="padding:20px;color:var(--text-tertiary)">Loading...</div>'
|
|
275
|
+
try{
|
|
276
|
+
const mem=await API.memory()
|
|
277
|
+
const typeMap={episodic:'Episodic',semantic:'Semantic',procedural:'Procedural',reflection:'Reflection'}
|
|
278
|
+
const colors={episodic:'episodic',semantic:'semantic',procedural:'procedural',reflection:'reflection'}
|
|
279
|
+
const allTypes=new Set(mem.map(m=>m.type))
|
|
280
|
+
document.querySelectorAll('#memFilters .filter-chip').forEach(f=>{
|
|
281
|
+
const mt=f.dataset.mtype
|
|
282
|
+
f.textContent=mt==='all'?'All':(typeMap[mt]||mt)
|
|
283
|
+
if(mt!=='all'&&!allTypes.has(mt))f.style.display='none'
|
|
284
|
+
f.onclick=()=>{
|
|
285
|
+
document.querySelectorAll('#memFilters .filter-chip').forEach(x=>x.classList.remove('active'))
|
|
286
|
+
f.classList.add('active')
|
|
287
|
+
const filter=f.dataset.mtype
|
|
288
|
+
document.querySelectorAll('.memory-card').forEach(c=>{
|
|
289
|
+
c.dataset.mtype===filter||filter==='all'?c.classList.remove('hidden'):c.classList.add('hidden')
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
if(!mem||!mem.length){grid.innerHTML='<div class="empty"><p>No memories recorded yet</p></div>';return}
|
|
294
|
+
grid.innerHTML=mem.map(m=>`<div class="memory-card" data-mtype="${m.type}">
|
|
295
|
+
<span class="mem-type ${colors[m.type]||'episodic'}">${typeMap[m.type]||m.type}</span>
|
|
296
|
+
<div class="mem-content">${m.content}</div>
|
|
297
|
+
<div class="mem-meta"><span>${m.id}</span><span>conf: ${m.confidence}% · ${new Date(m.created_at).toLocaleDateString()}</span></div>
|
|
298
|
+
</div>`).join('')
|
|
299
|
+
}catch(e){grid.innerHTML=`<div class="error"><p>Failed to load memory: ${e.message}</p></div>`}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// ===== SKILLS =====
|
|
303
|
+
async function loadSkills(){
|
|
304
|
+
const grid=document.getElementById('skillsGrid')
|
|
305
|
+
grid.innerHTML='<div style="padding:20px;color:var(--text-tertiary)">Loading...</div>'
|
|
306
|
+
try{
|
|
307
|
+
const skills=await API.skills()
|
|
308
|
+
const cats=new Set(skills.map(s=>s.category))
|
|
309
|
+
document.querySelectorAll('#skillFilters .filter-chip').forEach(f=>{
|
|
310
|
+
const sc=f.dataset.scat
|
|
311
|
+
if(sc!=='all'&&sc!=='builtin'&&!cats.has(sc))f.style.display='none'
|
|
312
|
+
f.onclick=()=>{
|
|
313
|
+
document.querySelectorAll('#skillFilters .filter-chip').forEach(x=>x.classList.remove('active'))
|
|
314
|
+
f.classList.add('active')
|
|
315
|
+
const filter=f.dataset.scat
|
|
316
|
+
document.querySelectorAll('.skill-card').forEach(c=>{
|
|
317
|
+
c.dataset.scat===filter||filter==='all'?c.classList.remove('hidden'):c.classList.add('hidden')
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
// Add category filters dynamically
|
|
322
|
+
cats.forEach(c=>{
|
|
323
|
+
if(c==='uncategorized'||c==='builtin')return
|
|
324
|
+
const btn=document.createElement('button');btn.className='filter-chip';btn.dataset.scat=c;btn.textContent=c
|
|
325
|
+
btn.onclick=()=>{
|
|
326
|
+
document.querySelectorAll('#skillFilters .filter-chip').forEach(x=>x.classList.remove('active'))
|
|
327
|
+
btn.classList.add('active')
|
|
328
|
+
document.querySelectorAll('.skill-card').forEach(card=>card.dataset.scat===c||c==='all'?card.classList.remove('hidden'):card.classList.add('hidden'))
|
|
329
|
+
}
|
|
330
|
+
document.getElementById('skillFilters').appendChild(btn)
|
|
331
|
+
})
|
|
332
|
+
if(!skills||!skills.length){grid.innerHTML='<div class="empty"><p>No skills installed</p></div>';return}
|
|
333
|
+
grid.innerHTML=skills.map(s=>`<div class="skill-card" data-scat="${s.category}">
|
|
334
|
+
<div class="skill-name">${s.name}</div>
|
|
335
|
+
<div class="skill-desc">${s.description}</div>
|
|
336
|
+
<div class="skill-meta">${s.category} · ${s.path}</div>
|
|
337
|
+
</div>`).join('')
|
|
338
|
+
}catch(e){grid.innerHTML=`<div class="error"><p>Failed to load skills: ${e.message}</p></div>`}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ===== INIT =====
|
|
342
|
+
document.getElementById('refreshBtn').onclick=()=>{const t=document.querySelector('.header-tab.active');t&&loadTab(t.dataset.tab)}
|
|
343
|
+
document.getElementById('archRefresh').onclick=()=>loadArchitect()
|
|
344
|
+
document.getElementById('deployBtn').onclick=()=>alert('Deploy Agent — coming soon')
|
|
345
|
+
document.getElementById('diagBtn').onclick=()=>alert('Diagnostics — coming soon')
|
|
346
|
+
document.getElementById('notifBtn').onclick=()=>alert('3 notifications pending')
|
|
347
|
+
|
|
348
|
+
loadTab('architect')
|
|
349
|
+
setInterval(()=>{const t=document.querySelector('.header-tab.active');t&&loadTab(t.dataset.tab)},30000)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaTrixOS — Feature Inventory (user-facing capabilities)
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for what MaTrixOS can do. Morpheus reads this to:
|
|
5
|
+
* - present available features during `/adopt` (only those NOT enabledByDefault)
|
|
6
|
+
* - answer "what can you do?" via `/features` (lists everything with details)
|
|
7
|
+
* - explain any specific feature on demand without guessing/fouling the codebase
|
|
8
|
+
*
|
|
9
|
+
* This module is intentionally dependency-free and harness-neutral so it can be
|
|
10
|
+
* imported by slash-commands, the adoption ritual, and any future UI.
|
|
11
|
+
*
|
|
12
|
+
* Field meanings:
|
|
13
|
+
* - enabledByDefault: true => ships active, no opt-in needed
|
|
14
|
+
* - status:
|
|
15
|
+
* "native" => always on, part of the core runtime
|
|
16
|
+
* "opt-in" => off by default, user chooses to activate (via /adopt or config)
|
|
17
|
+
* "cli-only" => a CLI/server exists but needs provisioning (e.g. UI build) to be usable
|
|
18
|
+
* - provisioning: concrete steps Morpheus follows to make the feature available to the user.
|
|
19
|
+
* For "native" features this is empty. For others it carries the exact recipe.
|
|
20
|
+
*/
|
|
21
|
+
export type FeatureStatus = "native" | "opt-in" | "cli-only";
|
|
22
|
+
export interface FeatureProvisioning {
|
|
23
|
+
/** How to turn it on (command, config key, or build step). */
|
|
24
|
+
activation: string;
|
|
25
|
+
/** Where to read live state / config for this feature. */
|
|
26
|
+
configPath?: string;
|
|
27
|
+
/** Extra notes Morpheus should relay to the user (honest caveats). */
|
|
28
|
+
notes?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface MatrixFeature {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
/** One-line user-facing description (what the user gets). */
|
|
34
|
+
description: string;
|
|
35
|
+
enabledByDefault: boolean;
|
|
36
|
+
status: FeatureStatus;
|
|
37
|
+
/** What the user can actually do with it. */
|
|
38
|
+
userBenefit: string;
|
|
39
|
+
provisioning: FeatureProvisioning;
|
|
40
|
+
}
|
|
41
|
+
export declare const MATRIX_FEATURES: readonly MatrixFeature[];
|
|
42
|
+
/** Features the user must opt into (proposed during /adopt). */
|
|
43
|
+
export declare const OPT_IN_FEATURES: readonly MatrixFeature[];
|
|
44
|
+
/** Look up a single feature by id (case-insensitive). */
|
|
45
|
+
export declare function getFeature(id: string): MatrixFeature | undefined;
|
|
@@ -14,6 +14,14 @@ export interface AgentAntiLoopState {
|
|
|
14
14
|
fallbackTriggered: boolean;
|
|
15
15
|
avoidanceCount: number;
|
|
16
16
|
avoidanceEscalated: boolean;
|
|
17
|
+
/** Timestamps of recent hook executions (sliding window for hook_recursion). */
|
|
18
|
+
hookExecutionTimestamps: number[];
|
|
19
|
+
/** Last hook_recursion threshold breach timestamp, to avoid duplicate logs. */
|
|
20
|
+
hookRecursionBreachedAt: number;
|
|
21
|
+
/** Estimated current context size in tokens (for context_bloat). */
|
|
22
|
+
contextTokens: number;
|
|
23
|
+
/** Last context_bloat breach timestamp, to avoid duplicate logs. */
|
|
24
|
+
contextBloatBreachedAt: number;
|
|
17
25
|
}
|
|
18
26
|
export interface AgentAntiLoopHooks {
|
|
19
27
|
onToolAfter(tool: string, args: unknown, output: unknown): void;
|
|
@@ -22,6 +30,14 @@ export interface AgentAntiLoopHooks {
|
|
|
22
30
|
stuck: boolean;
|
|
23
31
|
secondsSinceLastMeaningful: number;
|
|
24
32
|
};
|
|
33
|
+
/** Record a hook execution for recursion detection (§4.12 0.1.8). */
|
|
34
|
+
onHookExecute(): void;
|
|
35
|
+
/** Update the estimated context size in tokens (§4.12 0.1.8). */
|
|
36
|
+
onContextUpdate(tokens: number): void;
|
|
37
|
+
/** True if hook executions exceeded the recursion threshold within the window. */
|
|
38
|
+
isHookRecursion(): boolean;
|
|
39
|
+
/** True if context is bloated AND no meaningful progress since contextBloatStuckSec. */
|
|
40
|
+
isContextBloat(): boolean;
|
|
25
41
|
reset(): void;
|
|
26
42
|
getState(): AgentAntiLoopState;
|
|
27
43
|
}
|