closed-loop-cli 1.0.0

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.

Potentially problematic release.


This version of closed-loop-cli might be problematic. Click here for more details.

Files changed (86) hide show
  1. package/dist/dashboard/server.js +237 -0
  2. package/dist/index.js +272 -0
  3. package/dist/orchestrator/agent-prompts.js +42 -0
  4. package/dist/orchestrator/autogenesis.js +973 -0
  5. package/dist/orchestrator/dgm-archive.js +223 -0
  6. package/dist/orchestrator/event-stream.js +103 -0
  7. package/dist/orchestrator/fitness-evaluator.js +99 -0
  8. package/dist/orchestrator/meta-agent.js +421 -0
  9. package/dist/orchestrator/microagent-registry.js +134 -0
  10. package/dist/orchestrator/mutation-strategies.js +174 -0
  11. package/dist/orchestrator/prompt-benchmark.js +102 -0
  12. package/dist/orchestrator/prompt-optimizer.js +169 -0
  13. package/dist/orchestrator/refactor-scanner.js +222 -0
  14. package/dist/orchestrator/research-manager.js +104 -0
  15. package/dist/orchestrator/rulez.js +135 -0
  16. package/dist/orchestrator/sahoo-gateway.js +261 -0
  17. package/dist/orchestrator/state-manager.js +121 -0
  18. package/dist/orchestrator/task-agent.js +444 -0
  19. package/dist/orchestrator/telegram-bot.js +374 -0
  20. package/dist/orchestrator/types.js +2 -0
  21. package/dist/tests/dynamic/dependencies.test.js +37 -0
  22. package/dist/tests/dynamic/dummy.test.js +7 -0
  23. package/dist/tests/dynamic/fuzzy-patch.test.js +68 -0
  24. package/dist/tests/dynamic/indexer.test.js +60 -0
  25. package/dist/tests/dynamic/openhands.test.js +83 -0
  26. package/dist/tests/dynamic/skills.test.js +88 -0
  27. package/dist/tests/run-tests.js +294 -0
  28. package/dist/tools/diff-tools.js +24 -0
  29. package/dist/tools/file-tools.js +191 -0
  30. package/dist/tools/indexer.js +301 -0
  31. package/dist/tools/math-helper.js +6 -0
  32. package/dist/tools/repo-map.js +122 -0
  33. package/dist/tools/search-tools.js +271 -0
  34. package/dist/tools/shell-tools.js +75 -0
  35. package/dist/tools/skills.js +122 -0
  36. package/dist/tools/tui-tools.js +82 -0
  37. package/docs/AI_Arch_Opt_Anti_Gaming.md +227 -0
  38. package/docs/AI_Self_Improvement_Safety.md +457 -0
  39. package/docs/Anthropic AI Agents_ Capabilities and Concerns.md +134 -0
  40. package/docs/Auto_ClosedLoop_AI_Agent.md +415 -0
  41. package/docs/Autonomous AI Agents_ Closing the Loop.docx +0 -0
  42. package/docs/Secure_AI_Sandbox_Framework.md +358 -0
  43. package/docs/skills/add-file-existence-check-utility.json +9 -0
  44. package/docs/skills/add-utility-function-for-file-existence-check.json +9 -0
  45. package/docs/skills/add-utility-function-to-module.json +9 -0
  46. package/docs/skills/extract-command-runner-utility.json +9 -0
  47. package/docs/skills/file-existence-check-utility.json +9 -0
  48. package/package.json +36 -0
  49. package/src/dashboard/public/index.css +1334 -0
  50. package/src/dashboard/public/index.html +385 -0
  51. package/src/dashboard/public/index.js +1059 -0
  52. package/src/dashboard/server.ts +209 -0
  53. package/src/index.ts +256 -0
  54. package/src/orchestrator/agent-prompts.ts +43 -0
  55. package/src/orchestrator/autogenesis.ts +1078 -0
  56. package/src/orchestrator/dgm-archive.ts +257 -0
  57. package/src/orchestrator/event-stream.ts +90 -0
  58. package/src/orchestrator/fitness-evaluator.ts +154 -0
  59. package/src/orchestrator/meta-agent.ts +434 -0
  60. package/src/orchestrator/microagent-registry.ts +115 -0
  61. package/src/orchestrator/microagents/git-helper.md +11 -0
  62. package/src/orchestrator/microagents/test-fixer.md +10 -0
  63. package/src/orchestrator/microagents/typescript-expert.md +11 -0
  64. package/src/orchestrator/mutation-strategies.ts +214 -0
  65. package/src/orchestrator/research-manager.ts +88 -0
  66. package/src/orchestrator/rulez.ts +118 -0
  67. package/src/orchestrator/sahoo-gateway.ts +300 -0
  68. package/src/orchestrator/state-manager.ts +161 -0
  69. package/src/orchestrator/system-prompt.txt +1 -0
  70. package/src/orchestrator/task-agent.ts +461 -0
  71. package/src/orchestrator/telegram-bot.ts +358 -0
  72. package/src/tests/dynamic/dependencies.test.ts +48 -0
  73. package/src/tests/dynamic/dummy.test.ts +4 -0
  74. package/src/tests/dynamic/fuzzy-patch.test.ts +42 -0
  75. package/src/tests/dynamic/indexer.test.ts +31 -0
  76. package/src/tests/dynamic/openhands.test.ts +59 -0
  77. package/src/tests/dynamic/skills.test.ts +63 -0
  78. package/src/tests/run-tests.ts +296 -0
  79. package/src/tools/diff-tools.ts +27 -0
  80. package/src/tools/file-tools.ts +187 -0
  81. package/src/tools/indexer.ts +325 -0
  82. package/src/tools/repo-map.ts +96 -0
  83. package/src/tools/search-tools.ts +258 -0
  84. package/src/tools/shell-tools.ts +90 -0
  85. package/src/tools/skills.ts +101 -0
  86. package/src/tools/tui-tools.ts +87 -0
@@ -0,0 +1,1059 @@
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ // Tab Switching Logic
3
+ const tabButtons = document.querySelectorAll('.tab-btn');
4
+ const tabContents = document.querySelectorAll('.tab-content');
5
+
6
+ tabButtons.forEach(btn => {
7
+ btn.addEventListener('click', () => {
8
+ const targetTab = btn.getAttribute('data-tab');
9
+
10
+ // Update active nav button
11
+ tabButtons.forEach(b => b.classList.remove('active'));
12
+ btn.classList.add('active');
13
+
14
+ // Update active content panel
15
+ tabContents.forEach(content => {
16
+ if (content.id === targetTab) {
17
+ content.classList.add('active');
18
+ } else {
19
+ content.classList.remove('active');
20
+ }
21
+ });
22
+ });
23
+ });
24
+
25
+ // Auto Scroll logs toggler
26
+ let autoScroll = true;
27
+ const btnScroll = document.getElementById('btn-autoscroll');
28
+ btnScroll.addEventListener('click', () => {
29
+ autoScroll = !autoScroll;
30
+ btnScroll.classList.toggle('active', autoScroll);
31
+ });
32
+
33
+ // State Fetching and UI Rendering
34
+ async function fetchState() {
35
+ try {
36
+ const res = await fetch('/api/state');
37
+ if (!res.ok) throw new Error('Network error');
38
+ const state = await res.json();
39
+ renderState(state);
40
+ } catch (err) {
41
+ console.error('Error fetching state:', err);
42
+ document.getElementById('status-dot').className = 'dot err animate-pulse';
43
+ document.getElementById('status-text').innerText = 'CONNECTION ERROR';
44
+ }
45
+ }
46
+
47
+ function renderState(state) {
48
+ // 1. Status Indicator
49
+ const statusDot = document.getElementById('status-dot');
50
+ const statusText = document.getElementById('status-text');
51
+ const status = state.status || 'idle';
52
+
53
+ statusText.innerText = status.replace('_', ' ').toUpperCase();
54
+
55
+ let dotClass = 'dot ';
56
+ if (status === 'idle') dotClass += 'idle';
57
+ else if (status === 'cooldown') dotClass += 'warn animate-pulse';
58
+ else if (status === 'error') dotClass += 'err animate-pulse';
59
+ else dotClass += 'active animate-pulse'; // reflecting, refactoring, evaluating, committing, rolling_back
60
+
61
+ statusDot.className = dotClass;
62
+
63
+ // 2. Metric Cards
64
+ // Cycle
65
+ const maxCyclesText = state.maxCycles === 0 ? 'Infinite' : state.maxCycles;
66
+ document.getElementById('metric-cycle').innerText = `${state.currentCycle} / ${maxCyclesText}`;
67
+
68
+ // Tokens
69
+ const tokens = state.tokensUsed || { input: 0, output: 0, total: 0 };
70
+ const budget = state.tokenBudget || 500000;
71
+ document.getElementById('metric-tokens').innerText = tokens.total.toLocaleString();
72
+ document.getElementById('tokens-detail').innerText = `${tokens.input.toLocaleString()} in / ${tokens.output.toLocaleString()} out`;
73
+ document.getElementById('tokens-limit').innerText = `Limit: ${budget.toLocaleString()}`;
74
+
75
+ const pct = Math.min((tokens.total / budget) * 100, 100);
76
+ document.getElementById('tokens-progress').style.width = `${pct}%`;
77
+
78
+ // Last Refactor Status
79
+ const refactorResult = state.lastRefactorResult;
80
+ const refactorStatusMetric = document.getElementById('metric-refactor-status');
81
+ const refactorTargetMetric = document.getElementById('metric-refactor-target');
82
+
83
+ if (refactorResult) {
84
+ refactorStatusMetric.innerText = refactorResult.success ? 'Success ✅' : 'Failed ❌';
85
+ refactorStatusMetric.className = refactorResult.success ? 'metric-value font-md text-bright' : 'metric-value font-md text-dim';
86
+ refactorTargetMetric.innerText = refactorResult.targetFile;
87
+ } else {
88
+ refactorStatusMetric.innerText = 'No Runs';
89
+ refactorTargetMetric.innerText = 'Waiting for cycle...';
90
+ }
91
+
92
+ // Last sync update time
93
+ const lastUpdate = state.lastUpdated ? new Date(state.lastUpdated) : new Date();
94
+ document.getElementById('metric-sync').innerText = lastUpdate.toLocaleTimeString();
95
+
96
+ // 3. Active Task Section
97
+ const activeSection = document.getElementById('active-task-section');
98
+ const activeText = document.getElementById('active-task-text');
99
+ if (status !== 'idle' && status !== 'cooldown' && state.currentTask) {
100
+ activeSection.classList.remove('hidden');
101
+ activeText.innerText = state.currentTask;
102
+ } else {
103
+ activeSection.classList.add('hidden');
104
+ }
105
+
106
+ // 4. Tab: Overview Panel Details
107
+ const refactorEmpty = document.getElementById('refactor-empty');
108
+ const refactorDetails = document.getElementById('refactor-details');
109
+ if (refactorResult) {
110
+ refactorEmpty.classList.add('hidden');
111
+ refactorDetails.classList.remove('hidden');
112
+
113
+ document.getElementById('refactor-file').innerText = refactorResult.targetFile;
114
+ document.getElementById('refactor-goal').innerText = state.lastRefactorProposal ? state.lastRefactorProposal.refactorGoal : '-';
115
+
116
+ const badge = document.getElementById('refactor-success');
117
+ badge.innerText = refactorResult.success ? 'PASSED' : 'REJECTED';
118
+ badge.className = refactorResult.success ? 'value badge success' : 'value badge failed';
119
+
120
+ document.getElementById('refactor-message').innerText = refactorResult.message || '-';
121
+ document.getElementById('refactor-time').innerText = new Date(refactorResult.time).toLocaleString();
122
+ } else {
123
+ refactorEmpty.classList.remove('hidden');
124
+ refactorDetails.classList.add('hidden');
125
+ }
126
+
127
+ // Tab: Overview System Metrics Summary
128
+ let metrics = refactorResult?.metrics;
129
+ if (metrics) {
130
+ document.getElementById('overview-gdi').innerText = metrics.goalDriftIndex.toFixed(3);
131
+ document.getElementById('overview-gdi').className = metrics.goalDriftIndex > 0.440 ? 'value text-dim' : 'value text-bright';
132
+
133
+ document.getElementById('overview-cps').innerText = metrics.constraintPreservationScore.toFixed(2);
134
+ document.getElementById('overview-cps').className = metrics.constraintPreservationScore < 1.0 ? 'value text-dim' : 'value text-bright';
135
+
136
+ document.getElementById('overview-risk').innerText = `${(metrics.regressionRisk * 100).toFixed(1)}%`;
137
+ document.getElementById('overview-risk').className = metrics.regressionRisk > 0.80 ? 'value text-dim' : 'value text-bright';
138
+ } else {
139
+ document.getElementById('overview-gdi').innerText = '-';
140
+ document.getElementById('overview-cps').innerText = '-';
141
+ document.getElementById('overview-risk').innerText = '-';
142
+ }
143
+
144
+ const lastPromptOpt = state.lastPromptOptimization;
145
+ if (lastPromptOpt) {
146
+ document.getElementById('overview-prompt').innerText = lastPromptOpt.approved ? 'Approved ✅' : 'Rejected ❌';
147
+ document.getElementById('overview-prompt-detail').innerText = `Candidate: ${lastPromptOpt.candidateScore.toFixed(1)} vs Base: ${lastPromptOpt.baselineScore.toFixed(1)}`;
148
+ } else {
149
+ document.getElementById('overview-prompt').innerText = '-';
150
+ document.getElementById('overview-prompt-detail').innerText = '-';
151
+ }
152
+
153
+ // 5. Tab: SAHOO Metrics
154
+ if (metrics) {
155
+ // Set drift values
156
+ document.getElementById('drift-val-semantic').innerText = metrics.semanticDrift.toFixed(3);
157
+ document.getElementById('drift-progress-semantic').style.width = `${metrics.semanticDrift * 100}%`;
158
+
159
+ document.getElementById('drift-val-lexical').innerText = metrics.lexicalDrift.toFixed(3);
160
+ document.getElementById('drift-progress-lexical').style.width = `${metrics.lexicalDrift * 100}%`;
161
+
162
+ document.getElementById('drift-val-structural').innerText = metrics.structuralDrift.toFixed(3);
163
+ document.getElementById('drift-progress-structural').style.width = `${metrics.structuralDrift * 100}%`;
164
+
165
+ document.getElementById('drift-val-distributional').innerText = metrics.distributionalDrift.toFixed(3);
166
+ document.getElementById('drift-progress-distributional').style.width = `${metrics.distributionalDrift * 100}%`;
167
+
168
+ // Set halting rules checks
169
+ const ruleGdi = document.getElementById('rule-status-gdi');
170
+ if (metrics.goalDriftIndex <= 0.440) {
171
+ ruleGdi.innerText = 'PASS';
172
+ ruleGdi.className = 'rule-badge pass';
173
+ } else {
174
+ ruleGdi.innerText = 'HALTED';
175
+ ruleGdi.className = 'rule-badge fail';
176
+ }
177
+
178
+ const ruleCps = document.getElementById('rule-status-cps');
179
+ if (metrics.constraintPreservationScore === 1.0) {
180
+ ruleCps.innerText = 'PASS';
181
+ ruleCps.className = 'rule-badge pass';
182
+ } else {
183
+ ruleCps.innerText = 'HALTED';
184
+ ruleCps.className = 'rule-badge fail';
185
+ }
186
+
187
+ const ruleRisk = document.getElementById('rule-status-risk');
188
+ if (metrics.regressionRisk <= 0.80) {
189
+ ruleRisk.innerText = 'PASS';
190
+ ruleRisk.className = 'rule-badge pass';
191
+ } else {
192
+ ruleRisk.innerText = 'HALTED';
193
+ ruleRisk.className = 'rule-badge fail';
194
+ }
195
+ } else {
196
+ document.querySelectorAll('.drift-bar-item .bar-inner').forEach(bar => bar.style.width = '0%');
197
+ document.querySelectorAll('.drift-bar-item .value-badge').forEach(badge => badge.innerText = '-');
198
+ document.querySelectorAll('.rule-badge').forEach(badge => {
199
+ badge.innerText = '-';
200
+ badge.className = 'rule-badge';
201
+ });
202
+ }
203
+
204
+ // 6. Tab: Prompt Details
205
+ const promptEmpty = document.getElementById('prompt-empty');
206
+ const promptDetails = document.getElementById('prompt-details');
207
+ if (lastPromptOpt) {
208
+ promptEmpty.classList.add('hidden');
209
+ promptDetails.classList.remove('hidden');
210
+
211
+ document.getElementById('prompt-val-baseline').innerText = lastPromptOpt.baselineScore.toFixed(2);
212
+ document.getElementById('prompt-val-candidate').innerText = lastPromptOpt.candidateScore.toFixed(2);
213
+
214
+ const outcomeVal = document.getElementById('prompt-val-outcome');
215
+ const outcomeCard = document.getElementById('prompt-card-result');
216
+ if (lastPromptOpt.approved) {
217
+ outcomeVal.innerText = 'APPROVED';
218
+ outcomeCard.className = 'score-card glass approved text-bright';
219
+ } else {
220
+ outcomeVal.innerText = lastPromptOpt.success ? 'REJECTED' : 'FAILED';
221
+ outcomeCard.className = 'score-card glass rejected text-dim';
222
+ }
223
+ document.getElementById('prompt-time').innerText = new Date(lastPromptOpt.time).toLocaleString();
224
+ } else {
225
+ promptEmpty.classList.remove('hidden');
226
+ promptDetails.classList.add('hidden');
227
+ }
228
+
229
+ // 7. Render History Timeline
230
+ const timeline = document.getElementById('evolution-timeline');
231
+ const history = state.history || [];
232
+ if (history.length > 0) {
233
+ timeline.innerHTML = '';
234
+ // Show latest at the top
235
+ [...history].reverse().forEach(item => {
236
+ const div = document.createElement('div');
237
+ div.className = `timeline-item ${item.success ? 'success' : 'failed'}`;
238
+
239
+ const card = document.createElement('div');
240
+ card.className = 'timeline-card';
241
+
242
+ const content = document.createElement('div');
243
+ content.className = 'content';
244
+
245
+ const title = document.createElement('span');
246
+ title.className = 'title';
247
+ title.innerText = `Cycle ${item.cycle}: ${item.description}`;
248
+
249
+ const desc = document.createElement('span');
250
+ desc.className = 'desc';
251
+ desc.innerText = item.details || '';
252
+
253
+ content.appendChild(title);
254
+ content.appendChild(desc);
255
+
256
+ const meta = document.createElement('div');
257
+ meta.className = 'meta';
258
+
259
+ const time = document.createElement('span');
260
+ time.className = 'time';
261
+ time.innerText = new Date(item.time).toLocaleTimeString();
262
+
263
+ const type = document.createElement('span');
264
+ type.className = 'type';
265
+ type.innerText = item.type === 'refactor' ? 'REFACTOR' : 'PROMPT';
266
+
267
+ meta.appendChild(time);
268
+ meta.appendChild(type);
269
+
270
+ card.appendChild(content);
271
+ card.appendChild(meta);
272
+ div.appendChild(card);
273
+ timeline.appendChild(div);
274
+ });
275
+ } else {
276
+ timeline.innerHTML = '<div class="timeline-empty">Waiting for evolution cycles to commit...</div>';
277
+ }
278
+
279
+ // 8. Render DGM Archive Tree (from state.archive — legacy)
280
+ // Note: DGM-enhanced archive is rendered by fetchDGMArchive() separately
281
+ if (!window._dgmArchiveLoaded) {
282
+ renderArchive(state.archive);
283
+ }
284
+
285
+ // 9. Update DGM Fitness metric card
286
+ const fitnessCurrent = state.currentFitness ?? 0;
287
+ const fitnessBest = state.bestFitness ?? 0;
288
+ const dgmPop = state.dgmPopulationSize ?? 0;
289
+ const fitnessEl = document.getElementById('metric-fitness');
290
+ const fitnessProgressEl = document.getElementById('fitness-progress');
291
+ const fitnessDetailEl = document.getElementById('fitness-detail');
292
+ const fitnessBestEl = document.getElementById('fitness-best');
293
+ if (fitnessEl) {
294
+ fitnessEl.innerText = fitnessCurrent > 0 ? `${(fitnessCurrent * 100).toFixed(1)}%` : '-';
295
+ if (fitnessProgressEl) fitnessProgressEl.style.width = `${fitnessCurrent * 100}%`;
296
+ if (fitnessDetailEl) fitnessDetailEl.innerText = `Population: ${dgmPop} nodes`;
297
+ if (fitnessBestEl) fitnessBestEl.innerText = fitnessBest > 0 ? `Best: ${(fitnessBest * 100).toFixed(1)}%` : 'Best: -';
298
+ }
299
+ }
300
+
301
+ function renderArchive(archive) {
302
+ const emptyEl = document.getElementById('archive-empty');
303
+ const containerEl = document.getElementById('archive-tree');
304
+ if (!archive || archive.length === 0) {
305
+ emptyEl.classList.remove('hidden');
306
+ containerEl.classList.add('hidden');
307
+ return;
308
+ }
309
+ emptyEl.classList.add('hidden');
310
+ containerEl.classList.remove('hidden');
311
+
312
+ // Clear and redraw
313
+ containerEl.innerHTML = '';
314
+
315
+ const treeList = document.createElement('div');
316
+ treeList.className = 'archive-nodes-list';
317
+
318
+ archive.forEach((node, idx) => {
319
+ const card = document.createElement('div');
320
+ card.className = 'archive-node-card glass';
321
+ card.id = `archive-node-${idx}`;
322
+
323
+ // Node Header: Title, Cycle, Commit Hash
324
+ const nodeHeader = document.createElement('div');
325
+ nodeHeader.className = 'archive-node-header flex-between';
326
+
327
+ const titleWrapper = document.createElement('div');
328
+ titleWrapper.className = 'title-wrapper';
329
+ const nodeTitle = document.createElement('h4');
330
+ nodeTitle.className = 'node-title';
331
+ nodeTitle.innerText = `Generation Node #${idx + 1}`;
332
+ const cycleSpan = document.createElement('span');
333
+ cycleSpan.className = 'node-cycle-badge';
334
+ cycleSpan.innerText = `Cycle ${node.cycle}`;
335
+ titleWrapper.appendChild(nodeTitle);
336
+ titleWrapper.appendChild(cycleSpan);
337
+
338
+ const rightWrapper = document.createElement('div');
339
+ rightWrapper.className = 'right-wrapper flex-center';
340
+
341
+ const timeSpan = document.createElement('span');
342
+ timeSpan.className = 'node-time-stamp';
343
+ timeSpan.innerText = new Date(node.timestamp).toLocaleTimeString();
344
+
345
+ const hashSpan = document.createElement('span');
346
+ hashSpan.className = 'node-hash-badge';
347
+ hashSpan.innerText = node.commitHash.substring(0, 8);
348
+ hashSpan.title = node.commitHash;
349
+
350
+ rightWrapper.appendChild(timeSpan);
351
+ rightWrapper.appendChild(hashSpan);
352
+
353
+ nodeHeader.appendChild(titleWrapper);
354
+ nodeHeader.appendChild(rightWrapper);
355
+ card.appendChild(nodeHeader);
356
+
357
+ // Task Description
358
+ const taskP = document.createElement('p');
359
+ taskP.className = 'node-task-desc';
360
+ taskP.innerHTML = `<span class="label">Evolution Goal:</span> ${node.task}`;
361
+ card.appendChild(taskP);
362
+
363
+ // Metrics
364
+ if (node.metrics) {
365
+ const metricsContainer = document.createElement('div');
366
+ metricsContainer.className = 'node-metrics-container';
367
+
368
+ // Primary metrics: GDI, CPS, Regression Risk
369
+ const primaryMetrics = document.createElement('div');
370
+ primaryMetrics.className = 'primary-metrics-row flex-between';
371
+
372
+ // GDI Metric Card (threshold is 0.440)
373
+ const gdiCard = document.createElement('div');
374
+ gdiCard.className = 'metric-mini-card';
375
+ const gdiValue = node.metrics.goalDriftIndex;
376
+ const gdiStatus = gdiValue > 0.40 ? 'warn' : 'good';
377
+ gdiCard.innerHTML = `
378
+ <div class="metric-label">Goal Drift Index</div>
379
+ <div class="metric-value ${gdiStatus}">${gdiValue.toFixed(3)}</div>
380
+ <div class="metric-bar-bg"><div class="metric-bar ${gdiStatus}" style="width: ${Math.min((gdiValue / 0.44) * 100, 100)}%"></div></div>
381
+ `;
382
+
383
+ // CPS Metric Card (threshold is 1.0)
384
+ const cpsCard = document.createElement('div');
385
+ cpsCard.className = 'metric-mini-card';
386
+ const cpsValue = node.metrics.constraintPreservationScore;
387
+ const cpsStatus = cpsValue === 1.0 ? 'good' : 'bad';
388
+ cpsCard.innerHTML = `
389
+ <div class="metric-label">Constraint Preservation</div>
390
+ <div class="metric-value ${cpsStatus}">${(cpsValue * 100).toFixed(0)}%</div>
391
+ <div class="metric-bar-bg"><div class="metric-bar ${cpsStatus}" style="width: ${cpsValue * 100}%"></div></div>
392
+ `;
393
+
394
+ // Regression Risk Card (threshold is 0.80)
395
+ const riskCard = document.createElement('div');
396
+ riskCard.className = 'metric-mini-card';
397
+ const riskValue = node.metrics.regressionRisk;
398
+ const riskStatus = riskValue > 0.70 ? 'bad' : riskValue > 0.40 ? 'warn' : 'good';
399
+ riskCard.innerHTML = `
400
+ <div class="metric-label">Regression Risk</div>
401
+ <div class="metric-value ${riskStatus}">${(riskValue * 100).toFixed(0)}%</div>
402
+ <div class="metric-bar-bg"><div class="metric-bar ${riskStatus}" style="width: ${riskValue * 100}%"></div></div>
403
+ `;
404
+
405
+ primaryMetrics.appendChild(gdiCard);
406
+ primaryMetrics.appendChild(cpsCard);
407
+ primaryMetrics.appendChild(riskCard);
408
+ metricsContainer.appendChild(primaryMetrics);
409
+
410
+ // Detailed metrics breakdown toggle
411
+ const toggleBtn = document.createElement('button');
412
+ toggleBtn.className = 'drift-breakdown-toggle';
413
+ toggleBtn.innerText = 'Show Drift Breakdown ▼';
414
+
415
+ const breakdownDiv = document.createElement('div');
416
+ breakdownDiv.className = 'drift-breakdown-panel hidden';
417
+ breakdownDiv.innerHTML = `
418
+ <div class="breakdown-grid">
419
+ <div class="breakdown-item flex-between">
420
+ <span class="breakdown-label">Semantic Drift:</span>
421
+ <span class="breakdown-value">${(node.metrics.semanticDrift * 100).toFixed(1)}%</span>
422
+ </div>
423
+ <div class="breakdown-item flex-between">
424
+ <span class="breakdown-label">Lexical Drift:</span>
425
+ <span class="breakdown-value">${(node.metrics.lexicalDrift * 100).toFixed(1)}%</span>
426
+ </div>
427
+ <div class="breakdown-item flex-between">
428
+ <span class="breakdown-label">Structural Drift:</span>
429
+ <span class="breakdown-value">${(node.metrics.structuralDrift * 100).toFixed(1)}%</span>
430
+ </div>
431
+ <div class="breakdown-item flex-between">
432
+ <span class="breakdown-label">Distributional Drift:</span>
433
+ <span class="breakdown-value">${(node.metrics.distributionalDrift * 100).toFixed(1)}%</span>
434
+ </div>
435
+ </div>
436
+ `;
437
+
438
+ toggleBtn.addEventListener('click', () => {
439
+ const isHidden = breakdownDiv.classList.contains('hidden');
440
+ if (isHidden) {
441
+ breakdownDiv.classList.remove('hidden');
442
+ toggleBtn.innerText = 'Hide Drift Breakdown ▲';
443
+ } else {
444
+ breakdownDiv.classList.add('hidden');
445
+ toggleBtn.innerText = 'Show Drift Breakdown ▼';
446
+ }
447
+ });
448
+
449
+ metricsContainer.appendChild(toggleBtn);
450
+ metricsContainer.appendChild(breakdownDiv);
451
+ card.appendChild(metricsContainer);
452
+ }
453
+
454
+ treeList.appendChild(card);
455
+
456
+ // Connecting line to next node
457
+ if (idx < archive.length - 1) {
458
+ const connector = document.createElement('div');
459
+ connector.className = 'tree-connector-line';
460
+
461
+ const dot = document.createElement('div');
462
+ dot.className = 'tree-connector-dot';
463
+ connector.appendChild(dot);
464
+
465
+ treeList.appendChild(connector);
466
+ }
467
+ });
468
+
469
+ containerEl.appendChild(treeList);
470
+ }
471
+
472
+
473
+ // Logs fetching
474
+ async function fetchLogs() {
475
+ try {
476
+ const res = await fetch('/api/logs');
477
+ if (!res.ok) throw new Error();
478
+ const logs = await res.text();
479
+ const logArea = document.getElementById('console-output');
480
+
481
+ // Clear escape characters and color symbols if present for terminal rendering
482
+ const cleanedLogs = logs.replace(/\x1b\[\d+m/g, '');
483
+ logArea.innerText = cleanedLogs;
484
+
485
+ if (autoScroll) {
486
+ logArea.scrollTop = logArea.scrollHeight;
487
+ }
488
+ } catch (e) {
489
+ document.getElementById('console-output').innerText = 'Unable to fetch logs from workspace.';
490
+ }
491
+ }
492
+
493
+ // Learnings fetching & markdown rendering
494
+ async function fetchLearnings() {
495
+ try {
496
+ const res = await fetch('/api/learnings');
497
+ if (!res.ok) throw new Error();
498
+ const md = await res.text();
499
+ renderLearnings(md);
500
+ } catch (e) {
501
+ document.getElementById('learnings-registry').innerText = 'Learnings.md not found or unreachable.';
502
+ }
503
+ }
504
+
505
+ function renderLearnings(md) {
506
+ const container = document.getElementById('learnings-registry');
507
+
508
+ // Tiny parser to render headings, links, lists cleanly without a heavy library
509
+ const lines = md.split('\n');
510
+ let html = '';
511
+ let inList = false;
512
+
513
+ lines.forEach(line => {
514
+ const trimmed = line.trim();
515
+
516
+ // Header matching
517
+ if (trimmed.startsWith('###')) {
518
+ if (inList) { html += '</ul>'; inList = false; }
519
+ html += `<h3>${trimmed.replace(/^###\s*/, '')}</h3>`;
520
+ } else if (trimmed.startsWith('##')) {
521
+ if (inList) { html += '</ul>'; inList = false; }
522
+ html += `<h2>${trimmed.replace(/^##\s*/, '')}</h2>`;
523
+ } else if (trimmed.startsWith('#')) {
524
+ if (inList) { html += '</ul>'; inList = false; }
525
+ html += `<h1>${trimmed.replace(/^#\s*/, '')}</h1>`;
526
+ } else if (trimmed.startsWith('-') || trimmed.startsWith('*')) {
527
+ // Unordered lists
528
+ if (!inList) { html += '<ul>'; inList = true; }
529
+
530
+ // Match bold elements
531
+ let content = trimmed.replace(/^[-*]\s*/, '');
532
+ content = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
533
+ content = content.replace(/`(.*?)`/g, '<code>$1</code>');
534
+ html += `<li>${content}</li>`;
535
+ } else if (trimmed === '---') {
536
+ if (inList) { html += '</ul>'; inList = false; }
537
+ html += '<hr style="border: none; border-top: 1px solid rgba(255,255,255,0.05); margin: 1rem 0;">';
538
+ } else {
539
+ if (trimmed === '') return;
540
+ if (inList) { html += '</ul>'; inList = false; }
541
+
542
+ let content = trimmed;
543
+ content = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
544
+ content = content.replace(/`(.*?)`/g, '<code>$1</code>');
545
+ html += `<p style="margin-bottom: 0.5rem; color: var(--text-dim);">${content}</p>`;
546
+ }
547
+ });
548
+
549
+ if (inList) html += '</ul>';
550
+ container.innerHTML = html;
551
+ }
552
+
553
+ // Skills fetching
554
+ async function fetchSkills() {
555
+ try {
556
+ const res = await fetch('/api/skills');
557
+ if (!res.ok) throw new Error();
558
+ const skills = await res.json();
559
+ renderSkills(skills);
560
+ } catch (e) {
561
+ document.getElementById('skills-list').innerHTML = '<div class="empty-state">Unable to fetch skills from workspace.</div>';
562
+ }
563
+ }
564
+
565
+ function renderSkills(skills) {
566
+ const container = document.getElementById('skills-list');
567
+ if (!skills || skills.length === 0) {
568
+ container.innerHTML = '<div class="empty-state">No registered skills yet. Playbooks will be listed here as the agent evolves.</div>';
569
+ return;
570
+ }
571
+
572
+ container.innerHTML = '';
573
+ skills.forEach(skill => {
574
+ const card = document.createElement('div');
575
+ card.className = 'skill-card';
576
+
577
+ // Header
578
+ const header = document.createElement('div');
579
+ header.className = 'skill-header';
580
+
581
+ const title = document.createElement('h3');
582
+ title.className = 'skill-title';
583
+ title.innerText = skill.title || 'Untitled Skill';
584
+
585
+ const id = document.createElement('span');
586
+ id.className = 'skill-id';
587
+ id.innerText = skill.id || '';
588
+
589
+ header.appendChild(title);
590
+ header.appendChild(id);
591
+ card.appendChild(header);
592
+
593
+ // Description
594
+ const desc = document.createElement('p');
595
+ desc.className = 'skill-desc';
596
+ desc.innerText = skill.description || 'No description provided.';
597
+ card.appendChild(desc);
598
+
599
+ // Files Modified
600
+ if (skill.filesModified && skill.filesModified.length > 0) {
601
+ const filesDiv = document.createElement('div');
602
+ filesDiv.className = 'skill-files';
603
+
604
+ const label = document.createElement('span');
605
+ label.className = 'skill-files-label';
606
+ label.innerText = 'Files Modified:';
607
+ filesDiv.appendChild(label);
608
+
609
+ const list = document.createElement('div');
610
+ list.className = 'skill-files-list';
611
+ skill.filesModified.forEach(file => {
612
+ const badge = document.createElement('span');
613
+ badge.className = 'skill-file-badge';
614
+ badge.innerText = file;
615
+ list.appendChild(badge);
616
+ });
617
+ filesDiv.appendChild(list);
618
+ card.appendChild(filesDiv);
619
+ }
620
+
621
+ // Recipe / Steps
622
+ if (skill.recipe) {
623
+ const recipeDiv = document.createElement('div');
624
+ recipeDiv.className = 'skill-recipe';
625
+
626
+ const label = document.createElement('span');
627
+ label.className = 'skill-recipe-label';
628
+ label.innerText = 'Evolution Playbook Recipe:';
629
+ recipeDiv.appendChild(label);
630
+
631
+ const stepsDiv = document.createElement('div');
632
+ stepsDiv.className = 'skill-recipe-steps';
633
+
634
+ const rawRecipe = skill.recipe;
635
+ // Split by step markers like "1. ", "2. "
636
+ const steps = rawRecipe.split(/(?=\b\d+\.\s)/g).map(s => s.trim()).filter(Boolean);
637
+
638
+ if (steps.length > 0) {
639
+ steps.forEach(stepText => {
640
+ const stepItem = document.createElement('div');
641
+ stepItem.className = 'skill-recipe-step-item';
642
+
643
+ const match = stepText.match(/^(\d+)\.\s*(.*)/);
644
+ if (match) {
645
+ stepItem.setAttribute('data-step', match[1] + '.');
646
+ stepItem.innerText = match[2];
647
+ } else {
648
+ stepItem.innerText = stepText;
649
+ }
650
+ stepsDiv.appendChild(stepItem);
651
+ });
652
+ } else {
653
+ const stepItem = document.createElement('div');
654
+ stepItem.innerText = rawRecipe;
655
+ stepsDiv.appendChild(stepItem);
656
+ }
657
+
658
+ recipeDiv.appendChild(stepsDiv);
659
+ card.appendChild(recipeDiv);
660
+ }
661
+
662
+ container.appendChild(card);
663
+ });
664
+ }
665
+
666
+ // Live Research Logs Fetching & Rendering
667
+ async function fetchResearch() {
668
+ try {
669
+ const res = await fetch('/api/research');
670
+ if (!res.ok) throw new Error();
671
+ const sessions = await res.json();
672
+ renderResearch(sessions);
673
+ } catch (e) {
674
+ console.error('Error fetching research logs:', e);
675
+ }
676
+ }
677
+
678
+ function renderResearch(sessions) {
679
+ const emptyEl = document.getElementById('research-empty');
680
+ const containerEl = document.getElementById('research-sessions');
681
+
682
+ if (!sessions || sessions.length === 0) {
683
+ emptyEl.classList.remove('hidden');
684
+ containerEl.classList.add('hidden');
685
+ return;
686
+ }
687
+
688
+ emptyEl.classList.add('hidden');
689
+ containerEl.classList.remove('hidden');
690
+
691
+ containerEl.innerHTML = '';
692
+
693
+ [...sessions].reverse().forEach(session => {
694
+ const sessionDiv = document.createElement('div');
695
+ sessionDiv.className = `research-session-card glass ${session.status}`;
696
+
697
+ const header = document.createElement('div');
698
+ header.className = 'session-header flex-between';
699
+
700
+ const title = document.createElement('h3');
701
+ title.innerText = `Task: ${session.task}`;
702
+
703
+ const meta = document.createElement('div');
704
+ meta.className = 'session-meta';
705
+
706
+ const timeSpan = document.createElement('span');
707
+ timeSpan.className = 'time-meta';
708
+ timeSpan.innerText = new Date(session.startedAt).toLocaleString();
709
+
710
+ const effortBadge = document.createElement('span');
711
+ effortBadge.className = `badge effort-${session.effort}`;
712
+ effortBadge.innerText = session.effort.toUpperCase();
713
+
714
+ const statusBadge = document.createElement('span');
715
+ statusBadge.className = `badge status-${session.status}`;
716
+ statusBadge.innerText = session.status.toUpperCase();
717
+
718
+ meta.appendChild(timeSpan);
719
+ meta.appendChild(effortBadge);
720
+ meta.appendChild(statusBadge);
721
+
722
+ header.appendChild(title);
723
+ header.appendChild(meta);
724
+ sessionDiv.appendChild(header);
725
+
726
+ const decisionsList = document.createElement('div');
727
+ decisionsList.className = 'decisions-list';
728
+
729
+ session.decisions.forEach(decision => {
730
+ const item = document.createElement('div');
731
+ item.className = `decision-item-node ${decision.result}`;
732
+
733
+ const nodeDot = document.createElement('div');
734
+ nodeDot.className = 'node-dot';
735
+
736
+ const content = document.createElement('div');
737
+ content.className = 'node-content';
738
+
739
+ const nodeHeader = document.createElement('div');
740
+ nodeHeader.className = 'node-header flex-between';
741
+
742
+ const phaseSpan = document.createElement('span');
743
+ phaseSpan.className = 'phase';
744
+ phaseSpan.innerText = decision.phase;
745
+
746
+ const timeSpanNode = document.createElement('span');
747
+ timeSpanNode.className = 'time';
748
+ timeSpanNode.innerText = new Date(decision.timestamp).toLocaleTimeString();
749
+
750
+ nodeHeader.appendChild(phaseSpan);
751
+ nodeHeader.appendChild(timeSpanNode);
752
+
753
+ const actionText = document.createElement('p');
754
+ actionText.className = 'action';
755
+ actionText.innerText = decision.action;
756
+
757
+ content.appendChild(nodeHeader);
758
+ content.appendChild(actionText);
759
+
760
+ if (decision.details) {
761
+ const detailsPre = document.createElement('pre');
762
+ detailsPre.className = 'details';
763
+ detailsPre.innerText = decision.details;
764
+ content.appendChild(detailsPre);
765
+ }
766
+
767
+ item.appendChild(nodeDot);
768
+ item.appendChild(content);
769
+ decisionsList.appendChild(item);
770
+ });
771
+
772
+ sessionDiv.appendChild(decisionsList);
773
+ containerEl.appendChild(sessionDiv);
774
+ });
775
+ }
776
+
777
+ // DGM Archive Fetching
778
+ async function fetchDGMArchive() {
779
+ try {
780
+ const res = await fetch('/api/dgm-archive');
781
+ if (!res.ok) throw new Error();
782
+ const data = await res.json();
783
+ if (data && Array.isArray(data.entries) && data.entries.length > 0) {
784
+ window._dgmArchiveLoaded = true;
785
+ renderDGMArchive(data.entries);
786
+ }
787
+ } catch (e) {
788
+ // silently ignore — DGM may not have run yet
789
+ }
790
+ }
791
+
792
+ function renderDGMArchive(entries) {
793
+ const emptyEl = document.getElementById('archive-empty');
794
+ const containerEl = document.getElementById('archive-tree');
795
+ if (!entries || entries.length === 0) {
796
+ emptyEl.classList.remove('hidden');
797
+ containerEl.classList.add('hidden');
798
+ return;
799
+ }
800
+ emptyEl.classList.add('hidden');
801
+ containerEl.classList.remove('hidden');
802
+ containerEl.innerHTML = '';
803
+
804
+ const treeList = document.createElement('div');
805
+ treeList.className = 'archive-nodes-list';
806
+
807
+ entries.forEach((entry, idx) => {
808
+ const card = document.createElement('div');
809
+ card.className = 'archive-node-card glass';
810
+
811
+ const header = document.createElement('div');
812
+ header.className = 'archive-node-header flex-between';
813
+
814
+ const titleWrapper = document.createElement('div');
815
+ titleWrapper.className = 'title-wrapper';
816
+ const nodeTitle = document.createElement('h4');
817
+ nodeTitle.className = 'node-title';
818
+ nodeTitle.innerText = `DGM Node #${idx + 1} — ${entry.mutationStrategy || 'baseline'}`;
819
+ const idSpan = document.createElement('span');
820
+ idSpan.className = 'node-cycle-badge';
821
+ idSpan.innerText = entry.id || '';
822
+ titleWrapper.appendChild(nodeTitle);
823
+ titleWrapper.appendChild(idSpan);
824
+
825
+ const rightWrapper = document.createElement('div');
826
+ rightWrapper.className = 'right-wrapper flex-center';
827
+
828
+ const timeSpan = document.createElement('span');
829
+ timeSpan.className = 'node-time-stamp';
830
+ timeSpan.innerText = new Date(entry.timestamp).toLocaleString();
831
+
832
+ const hashSpan = document.createElement('span');
833
+ hashSpan.className = 'node-hash-badge';
834
+ hashSpan.innerText = entry.commitHash ? entry.commitHash.substring(0, 8) : '?';
835
+ hashSpan.title = entry.commitHash || '';
836
+
837
+ rightWrapper.appendChild(timeSpan);
838
+ rightWrapper.appendChild(hashSpan);
839
+ header.appendChild(titleWrapper);
840
+ header.appendChild(rightWrapper);
841
+ card.appendChild(header);
842
+
843
+ // Fitness bar
844
+ const fitnessDiv = document.createElement('div');
845
+ fitnessDiv.className = 'node-fitness-bar';
846
+ const fitnessVal = (entry.fitness || 0) * 100;
847
+ const fitnessColor = fitnessVal >= 90 ? '#00ff88' : fitnessVal >= 70 ? '#00d4aa' : '#f59e0b';
848
+ fitnessDiv.innerHTML = `
849
+ <div class="fitness-bar-label flex-between">
850
+ <span>Fitness (Test Pass Rate)</span>
851
+ <span style="color: ${fitnessColor}; font-weight: 600">${fitnessVal.toFixed(1)}%</span>
852
+ </div>
853
+ <div class="bar-outer">
854
+ <div class="bar-inner" style="width:${fitnessVal}%; background: linear-gradient(90deg, #00d4aa, ${fitnessColor})"></div>
855
+ </div>
856
+ `;
857
+ card.appendChild(fitnessDiv);
858
+
859
+ // Task
860
+ const taskP = document.createElement('p');
861
+ taskP.className = 'node-task-desc';
862
+ taskP.innerHTML = `<span class="label">Task:</span> ${entry.task}`;
863
+ card.appendChild(taskP);
864
+
865
+ // Metadata
866
+ if (entry.metadata && entry.metadata.totalTests > 0) {
867
+ const metaDiv = document.createElement('div');
868
+ metaDiv.className = 'node-task-desc';
869
+ metaDiv.innerHTML = `<span class="label">Tests:</span> ${entry.metadata.passCount}/${entry.metadata.totalTests} passed`;
870
+ card.appendChild(metaDiv);
871
+ }
872
+
873
+ treeList.appendChild(card);
874
+
875
+ if (idx < entries.length - 1) {
876
+ const connector = document.createElement('div');
877
+ connector.className = 'tree-connector-line';
878
+ const dot = document.createElement('div');
879
+ dot.className = 'tree-connector-dot';
880
+ connector.appendChild(dot);
881
+ treeList.appendChild(connector);
882
+ }
883
+ });
884
+
885
+ containerEl.appendChild(treeList);
886
+ }
887
+
888
+ // OpenHands Event Stream Fetching & Rendering
889
+ async function fetchEventStream() {
890
+ try {
891
+ const res = await fetch('/api/event-stream');
892
+ if (!res.ok) throw new Error();
893
+ const events = await res.json();
894
+ renderEventStream(events);
895
+ } catch (e) {
896
+ console.error('Error fetching event stream:', e);
897
+ }
898
+ }
899
+
900
+ function renderEventStream(events) {
901
+ const container = document.getElementById('event-stream-list');
902
+ if (!container) return;
903
+ if (!events || events.length === 0) {
904
+ container.innerHTML = '<div class="empty-state">No events recorded in the stream yet.</div>';
905
+ return;
906
+ }
907
+
908
+ container.innerHTML = '';
909
+
910
+ // Show events
911
+ events.forEach(evt => {
912
+ const div = document.createElement('div');
913
+ div.className = `event-item ${evt.source}`;
914
+ div.style.marginBottom = '0.75rem';
915
+ div.style.padding = '0.5rem 0.75rem';
916
+ div.style.borderRadius = '4px';
917
+ div.style.borderLeft = '4px solid';
918
+
919
+ const colors = {
920
+ user: { border: '#10b981', bg: 'rgba(16,185,129,0.1)' },
921
+ agent: { border: '#06b6d4', bg: 'rgba(6,182,212,0.1)' },
922
+ environment: { border: '#f59e0b', bg: 'rgba(245,158,11,0.1)' },
923
+ system: { border: '#ec4899', bg: 'rgba(236,72,153,0.1)' }
924
+ };
925
+
926
+ const style = colors[evt.source] || { border: '#9ca3af', bg: 'rgba(156,163,175,0.1)' };
927
+ div.style.borderLeftColor = style.border;
928
+ div.style.backgroundColor = style.bg;
929
+
930
+ const timeSpan = document.createElement('span');
931
+ timeSpan.style.color = '#6b7280';
932
+ timeSpan.style.fontSize = '0.75rem';
933
+ timeSpan.style.marginRight = '0.5rem';
934
+ timeSpan.innerText = new Date(evt.timestamp).toLocaleTimeString();
935
+
936
+ const sourceBadge = document.createElement('strong');
937
+ sourceBadge.style.color = style.border;
938
+ sourceBadge.style.marginRight = '0.5rem';
939
+ sourceBadge.innerText = evt.source.toUpperCase();
940
+
941
+ const typeBadge = document.createElement('span');
942
+ typeBadge.style.color = '#9ca3af';
943
+ typeBadge.style.fontSize = '0.75rem';
944
+ typeBadge.style.marginRight = '0.5rem';
945
+ typeBadge.innerText = `[${evt.type.toUpperCase()}]`;
946
+
947
+ const nameSpan = document.createElement('span');
948
+ nameSpan.style.fontWeight = 'bold';
949
+ nameSpan.style.color = '#f3f4f6';
950
+ nameSpan.innerText = `${evt.name}: `;
951
+
952
+ const contentSpan = document.createElement('span');
953
+ contentSpan.style.color = '#d1d5db';
954
+ contentSpan.innerText = evt.content;
955
+
956
+ div.appendChild(timeSpan);
957
+ div.appendChild(sourceBadge);
958
+ div.appendChild(typeBadge);
959
+ div.appendChild(nameSpan);
960
+ div.appendChild(contentSpan);
961
+
962
+ container.appendChild(div);
963
+ });
964
+
965
+ container.scrollTop = container.scrollHeight;
966
+ }
967
+
968
+ // Microagents Fetching & Rendering
969
+ async function fetchMicroagents() {
970
+ try {
971
+ const res = await fetch('/api/microagents');
972
+ if (!res.ok) throw new Error();
973
+ const agents = await res.json();
974
+ renderMicroagents(agents);
975
+ } catch (e) {
976
+ console.error('Error fetching microagents:', e);
977
+ }
978
+ }
979
+
980
+ function renderMicroagents(agents) {
981
+ const container = document.getElementById('microagents-list');
982
+ if (!container) return;
983
+ if (!agents || agents.length === 0) {
984
+ container.innerHTML = '<div class="empty-state">No microagents found in registry.</div>';
985
+ return;
986
+ }
987
+
988
+ container.innerHTML = '';
989
+ agents.forEach(agent => {
990
+ const card = document.createElement('div');
991
+ card.className = 'microagent-card glass';
992
+ card.style.padding = '1.25rem';
993
+ card.style.borderRadius = '8px';
994
+ card.style.border = '1px solid rgba(255,255,255,0.05)';
995
+ card.style.background = 'rgba(255,255,255,0.02)';
996
+
997
+ const title = document.createElement('h3');
998
+ title.style.margin = '0 0 0.5rem 0';
999
+ title.style.color = '#fff';
1000
+ title.innerText = agent.name;
1001
+
1002
+ const trigger = document.createElement('div');
1003
+ trigger.style.fontSize = '0.8rem';
1004
+ trigger.style.color = '#10b981';
1005
+ trigger.style.marginBottom = '0.5rem';
1006
+ trigger.innerHTML = `<span style="color:#6b7280">Trigger Regex:</span> <code>${agent.trigger}</code>`;
1007
+
1008
+ const desc = document.createElement('p');
1009
+ desc.style.margin = '0 0 1rem 0';
1010
+ desc.style.fontSize = '0.9rem';
1011
+ desc.style.color = '#d1d5db';
1012
+ desc.innerText = agent.description;
1013
+
1014
+ const instTitle = document.createElement('strong');
1015
+ instTitle.style.display = 'block';
1016
+ instTitle.style.fontSize = '0.8rem';
1017
+ instTitle.style.color = '#6b7280';
1018
+ instTitle.style.marginBottom = '0.25rem';
1019
+ instTitle.innerText = 'Instructions:';
1020
+
1021
+ const instructions = document.createElement('pre');
1022
+ instructions.style.margin = '0';
1023
+ instructions.style.fontSize = '0.8rem';
1024
+ instructions.style.color = '#9ca3af';
1025
+ instructions.style.whiteSpace = 'pre-wrap';
1026
+ instructions.style.background = 'rgba(0,0,0,0.15)';
1027
+ instructions.style.padding = '0.5rem';
1028
+ instructions.style.borderRadius = '4px';
1029
+ instructions.innerText = agent.instructions;
1030
+
1031
+ card.appendChild(title);
1032
+ card.appendChild(trigger);
1033
+ card.appendChild(desc);
1034
+ card.appendChild(instTitle);
1035
+ card.appendChild(instructions);
1036
+
1037
+ container.appendChild(card);
1038
+ });
1039
+ }
1040
+
1041
+ // Initial and Periodic Updates
1042
+ fetchState();
1043
+ fetchLogs();
1044
+ fetchLearnings();
1045
+ fetchSkills();
1046
+ fetchResearch();
1047
+ fetchDGMArchive();
1048
+ fetchEventStream();
1049
+ fetchMicroagents();
1050
+
1051
+ setInterval(fetchState, 2000);
1052
+ setInterval(fetchLogs, 3000);
1053
+ setInterval(fetchLearnings, 10000); // Learnings update less frequently
1054
+ setInterval(fetchSkills, 10000); // Skills update less frequently
1055
+ setInterval(fetchResearch, 3000);
1056
+ setInterval(fetchDGMArchive, 5000);
1057
+ setInterval(fetchEventStream, 2000);
1058
+ setInterval(fetchMicroagents, 10000);
1059
+ });