Files
cowork-local/tools/audit_handwritten.py
1419587401
CI / test (push) Canceled after 0s
Feature/fsg gamma team ui fix (#3)
## Summary

What changed and why?

## Change Type

- [ ] Cowork feature
- [ ] Bug fix
- [x] Core AI contribution
- [ ] Test / hardening
- [ ] Performance
- [ ] Documentation

## Related Work

Cowork Task:

Core Repo: http://34.143.229.138/gitea-admin/fsg-ai-core-assets

Core AI Issue:

Core Task:

Related PR:

## Scope

What is intentionally included?

What is intentionally NOT included?

## Validation

- [ ] Unit tests
- [ ] Integration tests
- [ ] Manual verification
- [ ] Regression check

Commands / evidence:

## Security Impact

Permission / credential / network / customer data impact:

## Compatibility

- [ ] No breaking change
- [ ] Breaking change documented

## Reviewer Notes

Anything Cowork reviewers should pay attention to.

---------

Co-authored-by: Hiep Ha Van <hiephv3@fpt.com>
Co-authored-by: Nam Pham Dinh Thanh <nampdt@fpt.com>
Co-authored-by: Lam Hoang Van <lamhv7@fpt.com>
Co-authored-by: NamPDT <minhanhpkpro@gmail.com>
Reviewed-on: #3
2026-08-20 12:12:56 +00:00

24 lines
91 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Hand-written audit sections, extracted from 10-18.ui-audit.html.
GENERATED by tools/extract_handwritten.py — do not edit by hand; edit the
source HTML and re-run it. build_audit_page.py substitutes {{SHOT}} and
{{CONTROLS}} so those stay generated.
"""
SECTIONS = {
'monitoring-sự-kiện-bảo-mật': '<div class="bd">\n<p class="lead">Nhật ký lần agent chạm thứ nhạy cảm: lệnh bị chặn, truy cập ngoài sandbox.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n<p class="rg"><b>Trên</b>: 4 thẻ KPI · <b>Giữa</b>: ô tìm + nút ✨AI, lọc Agent/Hành động/Thời gian · <b>Dưới</b>: bảng cột Hành động dạng badge màu (thay cho ✕/✓ luôn giống nhau) + phân trang, nhấp dòng mở panel chi tiết bên phải.</p>\n<div class="cap">Đề xuất — bố cục mới</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab on">Bảo mật</div><div class="tab">MCP</div><div class="tab">Hành động</div><div class="tab">Agent</div><div class="tab">Agents Admin</div><div class="tab">Công cụ</div><div class="tab">Icon</div></div><div class="c grow"><div class="r tb"><div class="ttl">Sự kiện bảo mật</div><div class="grow"></div><div class="btn">⭳ Xuất CSV</div><div class="btn pri">⟳ Làm mới</div></div><div class="r"><div class="kpi2"><div class="ic blue">▦</div><div><b>1.247</b><span class="s">Tổng sự kiện · +12.3%</span></div></div><div class="kpi2"><div class="ic grn">◔</div><div><b>84</b><span class="s">Hôm nay · -8.1%</span></div></div><div class="kpi2"><div class="ic red">⛔</div><div><b>23</b><span class="s">Chặn lệnh · +3 mới</span></div></div><div class="kpi2"><div class="ic pur">🛡</div><div><b>156</b><span class="s">Chặn prompt · 12.5%</span></div></div></div><div class="r tb"><div class="srchwrap grow"><input class="inp" id="s10q" placeholder="🔍 Gõ câu hỏi hoặc từ khoá…"><span class="pill teal aibtn" id="s10ai">✨ AI</span></div><select class="btn" id="s10agent"><option value="">Agent</option><option>Security Agent</option><option>Cowork Agent</option><option value="schedule">schedule</option><option value="graphrag">graphrag</option><option>Code Agent</option></select><select class="btn" id="s10action"><option value="">Hành động</option><option value="dangerous_command">Lệnh nguy hiểm</option><option value="path_outside_sandbox">Path ngoài sandbox</option><option value="network_blocked">Mạng bị chặn</option><option value="secret_in_output">Tiết lộ bí mật</option></select><select class="btn" id="s10date"><option value="">Thời gian</option><option value="today">Hôm nay</option><option value="week">Tuần này</option><option value="month">Tháng này</option></select><div class="btn" id="s10clear">✕ Xoá lọc</div></div><div class="tblwrap grow"><table class="evtbl"><thead><tr><th>☐</th><th>Thời gian</th><th>Agent</th><th>Tài khoản</th><th>Máy</th><th>Hành động</th><th>Chi tiết chặn</th></tr></thead><tbody id="s10tb"></tbody></table></div><div class="r tb"><div class="s" id="s10count">Hiển thị 24 / 24 sự kiện</div><div class="grow"></div><div class="btn">‹</div><div class="btn pri">1</div><div class="btn">›</div></div></div></div><div class="c pane overlay hide" id="s10detail"></div><div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n<script>\n(function(){\n var events=[\n {time:\'2026-08-11T17:29:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-11T12:01:00\',agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-11T11:01:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-11T06:05:00\',agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-10T19:42:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-10T17:18:00\',agent:\'graphrag\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-10T11:53:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-10T15:47:00\',agent:\'graphrag\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'network_blocked\',actionLabel:\'Mạng bị chặn\',detail:\'Chặn kết nối ra 203.0.113.44:8080\'},\n {time:\'2026-08-10T08:00:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-09T19:37:00\',agent:\'graphrag\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'network_blocked\',actionLabel:\'Mạng bị chặn\',detail:\'Chặn kết nối ra 203.0.113.44:8080\'},\n {time:\'2026-08-09T14:53:00\',agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'secret_in_output\',actionLabel:\'Tiết lộ bí mật\',detail:\'Phát hiện chuỗi giống API key trong đầu ra, đã che\'},\n {time:\'2026-08-09T13:00:00\',agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-08T14:41:00\',agent:\'graphrag\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'secret_in_output\',actionLabel:\'Tiết lộ bí mật\',detail:\'Phát hiện chuỗi giống API key trong đầu ra, đã che\'},\n {time:\'2026-08-08T11:38:00\',agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-07T12:50:00\',agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'network_blocked\',actionLabel:\'Mạng bị chặn\',detail:\'Chặn kết nối ra 203.0.113.44:8080\'},\n {time:\'2026-08-07T08:45:00\',agent:\'graphrag\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'secret_in_output\',actionLabel:\'Tiết lộ bí mật\',detail:\'Phát hiện chuỗi giống API key trong đầu ra, đã che\'},\n {time:\'2026-08-06T10:07:00\',agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-06T11:15:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'secret_in_output\',actionLabel:\'Tiết lộ bí mật\',detail:\'Phát hiện chuỗi giống API key trong đầu ra, đã che\'},\n {time:\'2026-08-05T11:00:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'network_blocked\',actionLabel:\'Mạng bị chặn\',detail:\'Chặn kết nối ra 203.0.113.44:8080\'},\n {time:\'2026-08-05T17:16:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-05T09:07:00\',agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-04T15:46:00\',agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'dangerous_command\',actionLabel:\'Lệnh nguy hiểm\',detail:\'Chặn lệnh: rm -rf / --no-preserve-root\'},\n {time:\'2026-08-04T10:13:00\',agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'path_outside_sandbox\',actionLabel:\'Path ngoài sandbox\',detail:\'Chặn đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx\'},\n {time:\'2026-08-03T11:45:00\',agent:\'graphrag\',account:\'local\',machine:\'DESKTOP-DEMO\',action:\'secret_in_output\',actionLabel:\'Tiết lộ bí mật\',detail:\'Phát hiện chuỗi giống API key trong đầu ra, đã che\'}\n ];\n function pad(n){return String(n).padStart(2,\'0\');}\n function fmt(iso){var d=new Date(iso);return pad(d.getDate())+\'/\'+pad(d.getMonth()+1)+\' \'+pad(d.getHours())+\':\'+pad(d.getMinutes());}\n function fmtFull(iso){var d=new Date(iso);return pad(d.getDate())+\'/\'+pad(d.getMonth()+1)+\'/\'+d.getFullYear()+\' · \'+pad(d.getHours())+\':\'+pad(d.getMinutes())+\':\'+pad(d.getSeconds());}\n function ini(n){return n.split(\' \').map(function(w){return w[0];}).join(\'\').substring(0,2).toUpperCase();}\n function ac(n){if(n.indexOf(\'Security\')>=0)return \'red\';if(n.indexOf(\'Cowork\')>=0)return \'blue\';if(n===\'schedule\')return \'amb\';if(n===\'graphrag\')return \'pur\';if(n.indexOf(\'Code\')>=0)return \'grn\';return \'blue\';}\n function pcls(a){return ({dangerous_command:\'red\',path_outside_sandbox:\'grn\',network_blocked:\'teal\',secret_in_output:\'org\'})[a]||\'red\';}\n function statusInfo(a){return ({\n dangerous_command:{cls:\'pur\',icon:\'⊗\',label:\'Đã chặn\'},\n path_outside_sandbox:{cls:\'grn\',icon:\'▤\',label:\'Path chặn\'},\n network_blocked:{cls:\'teal\',icon:\'⦿\',label:\'Mạng chặn\'},\n secret_in_output:{cls:\'org\',icon:\'🔒\',label:\'Bí mật lộ\'}\n })[a]||{cls:\'pur\',icon:\'⊗\',label:\'Đã chặn\'};}\n var maxTs=events.reduce(function(m,e){return Math.max(m,new Date(e.time).getTime());},0);\n function render(list){\n var tb=document.getElementById(\'s10tb\');\n tb.innerHTML=list.map(function(ev,i){\n return \'<tr data-i="\'+i+\'"><td>☐</td><td>\'+fmt(ev.time)+\'</td>\'+\n \'<td><span class="av \'+ac(ev.agent)+\'">\'+ini(ev.agent)+\'</span>\'+ev.agent+\'</td>\'+\n \'<td>\'+ev.account+\'</td><td>\'+ev.machine+\'</td>\'+\n \'<td><span class="pill \'+pcls(ev.action)+\'">\'+ev.actionLabel+\'</span></td>\'+\n \'<td class="dt">\'+ev.detail+\'</td></tr>\';\n }).join(\'\');\n Array.prototype.forEach.call(tb.querySelectorAll(\'tr\'),function(tr){\n tr.addEventListener(\'click\',function(e){e.stopPropagation();openDetail(list[+tr.dataset.i],+tr.dataset.i);});\n });\n document.getElementById(\'s10count\').textContent=\'Hiển thị \'+list.length+\' / \'+events.length+\' sự kiện\';\n }\n function openDetail(ev,index){\n var d=document.getElementById(\'s10detail\');\n d.classList.remove(\'hide\');\n var st=statusInfo(ev.action);\n var sev=ev.action===\'dangerous_command\'?{t:\'CRITICAL\',c:\'red\'}:{t:\'MEDIUM\',c:\'amb\'};\n var evtId=\'evt_\'+ev.time.replace(/[^0-9]/g,\'\').substring(0,12)+\'_\'+String(index).padStart(3,\'0\');\n d.innerHTML=\n \'<div class="hd2 row">Chi tiết sự kiện<span class="pchev" id="s10close">✕</span></div>\'+\n \'<div class="dtl grow">\'+\n \'<div class="hd3">THÔNG TIN CHUNG</div>\'+\n \'<div class="fld"><span class="lbl">Thời gian</span><span class="val">\'+fmtFull(ev.time)+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Agent</span><span class="val"><span class="pill \'+ac(ev.agent)+\'">\'+ini(ev.agent)+\' \'+ev.agent+\'</span></span></div>\'+\n \'<div class="fld"><span class="lbl">Tài khoản</span><span class="val">\'+ev.account+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Máy</span><span class="val"><span class="mono">\'+ev.machine+\'</span></span></div>\'+\n \'<div class="hd3">HÀNH ĐỘNG</div>\'+\n \'<div class="fld"><span class="lbl">Loại</span><span class="val"><span class="tagn">\'+ev.actionLabel+\'</span></span></div>\'+\n \'<div class="fld"><span class="lbl">Trạng thái</span><span class="val"><span class="pill \'+st.cls+\'">\'+st.icon+\' \'+st.label+\'</span></span></div>\'+\n \'<div class="hd3">CHI TIẾT CHẶN</div>\'+\n \'<div class="code"><span>\'+ev.detail+\'</span><span class="cpy" id="s10copy">📋 Copy</span></div>\'+\n \'<div class="hd3">METADATA</div>\'+\n \'<div class="fld"><span class="lbl">Event ID</span><span class="val mono">\'+evtId+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Policy</span><span class="val">security_policy_v2</span></div>\'+\n \'<div class="fld"><span class="lbl">Severity</span><span class="val"><span class="pill \'+sev.c+\'">\'+sev.t+\'</span></span></div>\'+\n \'</div>\'+\n \'<div class="r tb pnlfoot"><div class="btn pri grow" id="s10closebtn">⭳ Đóng</div></div>\';\n document.getElementById(\'s10close\').addEventListener(\'click\',function(e){e.stopPropagation();closeDetail();});\n document.getElementById(\'s10closebtn\').addEventListener(\'click\',function(e){e.stopPropagation();closeDetail();});\n document.getElementById(\'s10copy\').addEventListener(\'click\',function(e){\n e.stopPropagation();\n var text=ev.detail;\n if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(text).catch(function(){});}\n var btn=document.getElementById(\'s10copy\');\n btn.textContent=\'✓ Copied\';\n setTimeout(function(){if(document.getElementById(\'s10copy\'))document.getElementById(\'s10copy\').textContent=\'📋 Copy\';},1500);\n });\n }\n function closeDetail(){\n var d=document.getElementById(\'s10detail\');\n d.classList.add(\'hide\');\n d.innerHTML=\'\';\n }\n document.addEventListener(\'click\',function(e){\n var d=document.getElementById(\'s10detail\');\n if(d.classList.contains(\'hide\'))return;\n if(d.contains(e.target))return;\n closeDetail();\n });\n function inRange(ev,bucket){\n if(!bucket)return true;\n var t=new Date(ev.time).getTime();\n var day=86400000;\n if(bucket===\'today\')return maxTs-t<day;\n if(bucket===\'week\')return maxTs-t<7*day;\n if(bucket===\'month\')return maxTs-t<31*day;\n return true;\n }\n function apply(){\n var ag=document.getElementById(\'s10agent\').value;\n var act=document.getElementById(\'s10action\').value;\n var dt=document.getElementById(\'s10date\').value;\n var q=document.getElementById(\'s10q\').value.trim().toLowerCase();\n var list=events.filter(function(ev){\n if(ag&&ev.agent!==ag)return false;\n if(act&&ev.action!==act)return false;\n if(!inRange(ev,dt))return false;\n if(q&&!(ev.agent.toLowerCase().indexOf(q)>=0||ev.actionLabel.toLowerCase().indexOf(q)>=0||ev.detail.toLowerCase().indexOf(q)>=0))return false;\n return true;\n });\n render(list);\n }\n document.getElementById(\'s10agent\').addEventListener(\'change\',apply);\n document.getElementById(\'s10action\').addEventListener(\'change\',apply);\n document.getElementById(\'s10date\').addEventListener(\'change\',apply);\n document.getElementById(\'s10q\').addEventListener(\'input\',apply);\n document.getElementById(\'s10ai\').addEventListener(\'click\',apply);\n document.getElementById(\'s10clear\').addEventListener(\'click\',function(){\n document.getElementById(\'s10agent\').value=\'\';\n document.getElementById(\'s10action\').value=\'\';\n document.getElementById(\'s10date\').value=\'\';\n document.getElementById(\'s10q\').value=\'\';\n render(events);\n });\n render(events);\n})();\n</script>\n\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Dải tab Monitoring bị ẩn sau accordion — không thấy 7 màn Giám sát còn lại từ đây.</li><li><b>Cột "Kết quả"</b> (✓/✕) trong bảng thật luôn hiện ✕ vì <code>audit_log.record("security_block",…)</code> luôn truyền <code>ok=False</code> — dư thừa, không phân biệt được gì.</li><li>KPI · lọc Agent/Hành động/Thời gian · phân trang · panel chi tiết đều là <b>tính năng mới</b>: bản thật hiện chỉ có 1 ô tìm + nút ✨AI + bảng cuộn (xem <code>ui/monitoring_tab.py:_wrap_with_filter</code>).</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Bỏ ẩn dải tab Monitoring (8 mục ngang hàng) — không đổi accordion nav rail.</li><li>Bỏ cột Kết quả luôn-giống-nhau; thay bằng cột <b>Hành động</b> dạng badge màu theo <code>name</code> thật (prompt/run_command/install_package).</li><li><span class="bad">Vượt phạm vi "chỉ sắp xếp lại"</span>: 4 thẻ KPI, 3 dropdown lọc, phân trang và panel chi tiết trượt — cần bổ sung logic đếm/lọc/phân trang ở <code>ui/monitoring_tab.py</code> trước khi hiện thực, và 2/4 loại "Hành động" (path ngoài sandbox, mạng chặn) chưa có nguồn ghi log thật.</li></ul></div>\n</div></div>',
'monitoring-lịch-sử-gọi-mcp': '<div class="bd">\n<p class="lead">Mọi lần agent gọi MCP server ngoài.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n<p class="rg"><b>Trên</b>: 4 thẻ KPI · <b>Giữa</b>: ô tìm + nút ✨AI, lọc Server/Kết quả/Thời gian · <b>Dưới</b>: bảng có badge Kết quả (✓/✕ phân biệt thật theo <code>ok</code>) + phân trang, nhấp dòng mở panel chi tiết bên phải. <b>Bảng hiện tại</b> (ảnh trên) không có ô lọc/tìm kiếm nào — khác biệt không chủ đích so với 2 màn log kia.</p>\n<div class="cap">Đề xuất — bố cục mới</div>\n<div class="wf">\n<div class="rail">\n<div class="menutog"><span>MENU</span><span class="chev">‹</span></div>\n<div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div>\n<div class="newbtn">+ Đoạn chat mới</div>\n<div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div>\n<div class="sep"></div>\n<div class="hd">RECENTS</div>\n<div class="scope">📁 Báo cáo tài chính Q3</div>\n<div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div>\n<div class="grow"></div>\n<div class="sep"></div>\n<div class="i">Dashboard</div><div class="i">Monitoring</div>\n<div class="acct">👤 local · Ollama ▾</div>\n</div>\n<div class="main">\n<div class="ttl">Monitoring</div>\n<div class="r tb">\n<div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab on">MCP</div><div class="tab">Hành động</div><div class="tab">Agent</div><div class="tab">Agents Admin</div><div class="tab">Công cụ</div><div class="tab">Icon</div>\n</div>\n<div class="c grow">\n<div class="r tb">\n<div class="ttl">Lịch sử gọi MCP</div>\n<div class="grow"></div>\n<div class="btn">⭳ Xuất CSV</div><div class="btn pri">⟳ Làm mới</div>\n</div>\n<div class="r">\n<div class="kpi2"><div class="ic blue">▦</div><div><b>892</b><span class="s">Tổng cuộc gọi · +8.4%</span></div></div>\n<div class="kpi2"><div class="ic grn">✓</div><div><b>823</b><span class="s">Thành công · 92.3%</span></div></div>\n<div class="kpi2"><div class="ic red">✕</div><div><b>69</b><span class="s">Thất bại · 7.7%</span></div></div>\n<div class="kpi2"><div class="ic pur">◔</div><div><b>7</b><span class="s">Hôm nay · cuộc gọi</span></div></div>\n</div>\n<div class="r tb">\n<div class="srchwrap grow"><input class="inp" id="s11q" placeholder="🔍 Gõ câu hỏi hoặc từ khoá…"><span class="pill teal aibtn" id="s11ai">✨ AI</span></div>\n<select class="btn" id="s11server"><option value="">Server</option><option value="ms365">Microsoft 365</option><option value="github">GitHub</option><option value="filesystem">Filesystem</option><option value="search">Brave Search</option><option value="db">PostgreSQL</option></select>\n<select class="btn" id="s11status"><option value="">Kết quả</option><option value="ok">Thành công</option><option value="fail">Thất bại</option></select>\n<select class="btn" id="s11date"><option value="">Thời gian</option><option value="today">Hôm nay</option><option value="week">Tuần này</option><option value="month">Tháng này</option></select>\n<div class="btn" id="s11clear">✕ Xoá lọc</div>\n</div>\n<div class="tblwrap grow"><table class="evtbl"><thead><tr><th>Thời gian</th><th>Agent</th><th>Tài khoản</th><th>Máy</th><th>Tool</th><th>Kết quả</th><th>Chi tiết</th></tr></thead><tbody id="s11tb"></tbody></table></div>\n<div class="r tb"><div class="s" id="s11count">Hiển thị 25 / 25 cuộc gọi</div><div class="grow"></div><div class="btn">‹</div><div class="btn pri">1</div><div class="btn">›</div></div>\n</div>\n</div>\n<div class="c pane overlay hide" id="s11detail"></div>\n<div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n<script>\n(function(){\n var calls=[\n {time:\'2026-08-15T09:42:00\',tool:\'ms365__send_mail\',server:\'ms365\',ok:true,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã gửi email đến nam.pdt@company.com — chủ đề "Báo cáo tuần Q3". Message-ID: <abc123@mail.company.com>\'},\n {time:\'2026-08-15T09:38:00\',tool:\'ms365__get_calendar_events\',server:\'ms365\',ok:true,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Trả về 5 sự kiện trong 7 ngày tới. Cuộc họp kế tiếp: "Review Sprint 12" lúc 14:00 hôm nay.\'},\n {time:\'2026-08-15T09:31:00\',tool:\'github__search_repos\',server:\'github\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Tìm thấy 12 repo khớp từ khóa "vibeflow". Repo nổi bật: vibeflow/core (★ 1.2k).\'},\n {time:\'2026-08-15T09:20:00\',tool:\'filesystem__read_file\',server:\'filesystem\',ok:false,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Không thể đọc C:\\\\Users\\\\NamPDT\\\\Documents\\\\personal.xlsx — nằm ngoài sandbox được phép.\'},\n {time:\'2026-08-15T08:55:00\',tool:\'brave__web_search\',server:\'search\',ok:true,agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Trả về 8 kết quả cho "CVE-2026-1234". Nguồn hàng đầu: nvd.nist.gov.\'},\n {time:\'2026-08-15T08:40:00\',tool:\'postgres__query\',server:\'db\',ok:true,agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'SELECT 42 rows từ bảng usage_tracker. 1.2ms.\'},\n {time:\'2026-08-15T08:12:00\',tool:\'ms365__send_mail\',server:\'ms365\',ok:false,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Gửi thất bại: SMTP 550 — mailbox người nhận không tồn tại (unknown@invalid.com).\'},\n {time:\'2026-08-14T17:45:00\',tool:\'github__create_issue\',server:\'github\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã tạo issue #1284 "Fix flaky test in routing/orchestrator".\'},\n {time:\'2026-08-14T17:02:00\',tool:\'filesystem__write_file\',server:\'filesystem\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã ghi 1.024 bytes vào ./docs/ADR-004.md.\'},\n {time:\'2026-08-14T16:30:00\',tool:\'brave__web_search\',server:\'search\',ok:true,agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Trả về 6 kết quả cho "best practice API key rotation".\'},\n {time:\'2026-08-14T15:18:00\',tool:\'postgres__query\',server:\'db\',ok:false,agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Lỗi SQL: relation "audit_log_archive" does not exist. LINE 3: FROM audit_log_archive\'},\n {time:\'2026-08-14T14:44:00\',tool:\'ms365__get_user_profile\',server:\'ms365\',ok:true,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Hồ sơ người dùng: Nam Phạm Đình Tú (nam.pdt@company.com), phòng ban: Engineering.\'},\n {time:\'2026-08-14T11:20:00\',tool:\'github__get_pull_request\',server:\'github\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'PR #1279: 3 files changed, +214 −38. Trạng thái: MERGED.\'},\n {time:\'2026-08-14T10:05:00\',tool:\'filesystem__list_dir\',server:\'filesystem\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Liệt kê 28 mục trong ./src/components.\'},\n {time:\'2026-08-13T18:22:00\',tool:\'brave__web_search\',server:\'search\',ok:true,agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Trả về 10 kết quả cho "OWASP Top 10 2026".\'},\n {time:\'2026-08-13T16:09:00\',tool:\'ms365__send_mail\',server:\'ms365\',ok:true,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã gửi email đến hr@company.com — chủ đề "Đề xuất tuyển dụng Q4".\'},\n {time:\'2026-08-13T14:55:00\',tool:\'postgres__query\',server:\'db\',ok:true,agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'INSERT 1 row vào bảng daily_report. 0.8ms.\'},\n {time:\'2026-08-13T11:41:00\',tool:\'github__search_repos\',server:\'github\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Tìm thấy 5 repo khớp "react-query".\'},\n {time:\'2026-08-12T17:33:00\',tool:\'filesystem__read_file\',server:\'filesystem\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã đọc ./core/mcp_client.py (172 dòng).\'},\n {time:\'2026-08-12T15:07:00\',tool:\'ms365__get_calendar_events\',server:\'ms365\',ok:true,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Trả về 3 sự kiện. Không có xung đột lịch.\'},\n {time:\'2026-08-12T12:30:00\',tool:\'brave__web_search\',server:\'search\',ok:false,agent:\'Security Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Hết thời gian chờ (timeout) sau 30s — server tìm kiếm không phản hồi.\'},\n {time:\'2026-08-12T09:15:00\',tool:\'github__create_issue\',server:\'github\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã tạo issue #1276 "Update model pricing table".\'},\n {time:\'2026-08-11T17:48:00\',tool:\'postgres__query\',server:\'db\',ok:true,agent:\'schedule\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'SELECT 128 rows từ bảng audit_log. 3.4ms.\'},\n {time:\'2026-08-11T14:26:00\',tool:\'ms365__send_mail\',server:\'ms365\',ok:true,agent:\'Cowork Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã gửi email đến team@company.com — chủ đề "Biên bản họp 11/08".\'},\n {time:\'2026-08-11T10:03:00\',tool:\'filesystem__write_file\',server:\'filesystem\',ok:true,agent:\'Code Agent\',account:\'local\',machine:\'DESKTOP-DEMO\',detail:\'Đã ghi 2.048 bytes vào ./docs/installation.md.\'}\n ];\n function pad(n){return String(n).padStart(2,\'0\');}\n function fmt(iso){var d=new Date(iso);return pad(d.getDate())+\'/\'+pad(d.getMonth()+1)+\' \'+pad(d.getHours())+\':\'+pad(d.getMinutes());}\n function fmtFull(iso){var d=new Date(iso);return pad(d.getDate())+\'/\'+pad(d.getMonth()+1)+\'/\'+d.getFullYear()+\' · \'+pad(d.getHours())+\':\'+pad(d.getMinutes())+\':\'+pad(d.getSeconds());}\n function ini(n){return n.split(\' \').map(function(w){return w[0];}).join(\'\').substring(0,2).toUpperCase();}\n function ac(n){if(n.indexOf(\'Security\')>=0)return \'red\';if(n.indexOf(\'Cowork\')>=0)return \'blue\';if(n===\'schedule\')return \'amb\';if(n===\'graphrag\')return \'pur\';if(n.indexOf(\'Code\')>=0)return \'grn\';return \'blue\';}\n var SERVER_META={\n ms365:{label:\'Microsoft 365\',badge:\'M365\',cls:\'blue\'},\n github:{label:\'GitHub\',badge:\'GH\',cls:\'dark\'},\n filesystem:{label:\'Filesystem\',badge:\'FS\',cls:\'olv\'},\n search:{label:\'Brave Search\',badge:\'BR\',cls:\'pur\'},\n db:{label:\'PostgreSQL\',badge:\'DB\',cls:\'teal\'}\n };\n function serverMeta(s){return SERVER_META[s]||{label:s,badge:s.substring(0,2).toUpperCase(),cls:\'amb\'};}\n function toolShort(t){return t.indexOf(\'__\')>=0?t.split(\'__\')[1]:t;}\n var maxTs=calls.reduce(function(m,c){return Math.max(m,new Date(c.time).getTime());},0);\n function render(list){\n var tb=document.getElementById(\'s11tb\');\n tb.innerHTML=list.map(function(c,i){\n var meta=serverMeta(c.server);\n var resultPill=c.ok?\'<span class="pill grn">✓ Thành công</span>\':\'<span class="pill red">✕ Thất bại</span>\';\n return \'<tr data-i="\'+i+\'">\'+\n \'<td>\'+fmt(c.time)+\'</td>\'+\n \'<td><span class="av \'+ac(c.agent)+\'">\'+ini(c.agent)+\'</span>\'+c.agent+\'</td>\'+\n \'<td>\'+c.account+\'</td>\'+\n \'<td>\'+c.machine+\'</td>\'+\n \'<td><span class="av \'+meta.cls+\'">\'+meta.badge+\'</span>\'+toolShort(c.tool)+\' <span class="s">\'+meta.label+\'</span></td>\'+\n \'<td>\'+resultPill+\'</td>\'+\n \'<td class="dt">\'+c.detail+\'</td>\'+\n \'</tr>\';\n }).join(\'\');\n Array.prototype.forEach.call(tb.querySelectorAll(\'tr\'),function(tr){\n tr.addEventListener(\'click\',function(e){e.stopPropagation();openDetail(list[+tr.dataset.i],+tr.dataset.i);});\n });\n document.getElementById(\'s11count\').textContent=\'Hiển thị \'+list.length+\' / \'+calls.length+\' cuộc gọi\';\n }\n function openDetail(c,index){\n var d=document.getElementById(\'s11detail\');\n d.classList.remove(\'hide\');\n var meta=serverMeta(c.server);\n var resultPill=c.ok?\'<span class="pill grn">✓ Thành công</span>\':\'<span class="pill red">✕ Thất bại</span>\';\n var evtId=\'evt_\'+c.time.replace(/[^0-9]/g,\'\').substring(0,12)+\'_\'+String(index).padStart(3,\'0\');\n d.innerHTML=\n \'<div class="hd2 row">Chi tiết cuộc gọi MCP<span class="pchev" id="s11close">✕</span></div>\'+\n \'<div class="dtl grow">\'+\n \'<div class="hd3">THÔNG TIN CHUNG</div>\'+\n \'<div class="fld"><span class="lbl">Thời gian</span><span class="val">\'+fmtFull(c.time)+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Tool</span><span class="val mono">\'+c.tool+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Server</span><span class="val"><span class="av \'+meta.cls+\'">\'+meta.badge+\'</span> \'+meta.label+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Agent</span><span class="val"><span class="pill \'+ac(c.agent)+\'">\'+ini(c.agent)+\' \'+c.agent+\'</span></span></div>\'+\n \'<div class="fld"><span class="lbl">Tài khoản</span><span class="val">\'+c.account+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Máy</span><span class="val"><span class="mono">\'+c.machine+\'</span></span></div>\'+\n \'<div class="hd3">TRẠNG THÁI</div>\'+\n \'<div class="fld"><span class="lbl">Kết quả</span><span class="val">\'+resultPill+\'</span></div>\'+\n \'<div class="hd3">OUTPUT / CHI TIẾT</div>\'+\n \'<div class="code"><span>\'+c.detail+\'</span><span class="cpy" id="s11copy">📋 Copy</span></div>\'+\n \'<div class="hd3">METADATA</div>\'+\n \'<div class="fld"><span class="lbl">Call ID</span><span class="val mono">\'+evtId+\'</span></div>\'+\n \'<div class="fld"><span class="lbl">Loại log</span><span class="val"><span class="tagn">mcp_call</span></span></div>\'+\n \'<div class="fld"><span class="lbl">Namespace</span><span class="val mono">\'+c.server+\'__\'+toolShort(c.tool)+\'</span></div>\'+\n \'</div>\'+\n \'<div class="r tb pnlfoot"><div class="btn pri grow" id="s11closebtn">⭳ Đóng</div></div>\';\n document.getElementById(\'s11close\').addEventListener(\'click\',function(e){e.stopPropagation();closeDetail();});\n document.getElementById(\'s11closebtn\').addEventListener(\'click\',function(e){e.stopPropagation();closeDetail();});\n document.getElementById(\'s11copy\').addEventListener(\'click\',function(e){\n e.stopPropagation();\n var text=c.detail;\n if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(text).catch(function(){});}\n var btn=document.getElementById(\'s11copy\');\n btn.textContent=\'✓ Copied\';\n setTimeout(function(){if(document.getElementById(\'s11copy\'))document.getElementById(\'s11copy\').textContent=\'📋 Copy\';},1500);\n });\n }\n function closeDetail(){\n var d=document.getElementById(\'s11detail\');\n d.classList.add(\'hide\');\n d.innerHTML=\'\';\n }\n document.addEventListener(\'click\',function(e){\n var d=document.getElementById(\'s11detail\');\n if(d.classList.contains(\'hide\'))return;\n if(d.contains(e.target))return;\n closeDetail();\n });\n function inRange(c,bucket){\n if(!bucket)return true;\n var t=new Date(c.time).getTime();\n var day=86400000;\n if(bucket===\'today\')return maxTs-t<day;\n if(bucket===\'week\')return maxTs-t<7*day;\n if(bucket===\'month\')return maxTs-t<31*day;\n return true;\n }\n function apply(){\n var srv=document.getElementById(\'s11server\').value;\n var st=document.getElementById(\'s11status\').value;\n var dt=document.getElementById(\'s11date\').value;\n var q=document.getElementById(\'s11q\').value.trim().toLowerCase();\n var list=calls.filter(function(c){\n if(srv&&c.server!==srv)return false;\n if(st===\'ok\'&&!c.ok)return false;\n if(st===\'fail\'&&c.ok)return false;\n if(!inRange(c,dt))return false;\n if(q&&!((c.tool+\' \'+c.server+\' \'+c.agent+\' \'+c.detail).toLowerCase().indexOf(q)>=0))return false;\n return true;\n });\n render(list);\n }\n document.getElementById(\'s11server\').addEventListener(\'change\',apply);\n document.getElementById(\'s11status\').addEventListener(\'change\',apply);\n document.getElementById(\'s11date\').addEventListener(\'change\',apply);\n document.getElementById(\'s11q\').addEventListener(\'input\',apply);\n document.getElementById(\'s11ai\').addEventListener(\'click\',apply);\n document.getElementById(\'s11clear\').addEventListener(\'click\',function(){\n document.getElementById(\'s11server\').value=\'\';\n document.getElementById(\'s11status\').value=\'\';\n document.getElementById(\'s11date\').value=\'\';\n document.getElementById(\'s11q\').value=\'\';\n render(calls);\n });\n render(calls);\n})();\n</script>\n\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Dải tab Monitoring bị ẩn sau accordion — không thấy 7 màn Giám sát còn lại từ đây.</li><li>Bảng <b>Lịch sử gọi MCP</b> thật hiện <b>không có ô tìm/lọc nào</b> — còn thiếu hơn cả Sự kiện bảo mật: <code>ui/monitoring_tab.py:166-168</code> tạo thẳng <code>self.mcp_table = _EventTable()</code> rồi thêm vào tab, không bọc qua <code>_wrap_with_filter</code> như Sự kiện bảo mật/Nhật ký hành động — đúng như đã ghi ở ảnh Hiện tại.</li><li>KPI · lọc Server/Kết quả/Thời gian · phân trang · panel chi tiết đều là <b>tính năng mới</b>: bản thật chỉ có bảng 7 cột (Thời gian/Vai trò/Tài khoản/Máy/Tên/Kết quả/Chi tiết), không tìm không lọc không phân trang (xem <code>ui/monitoring_tab.py:_EventTable</code>).</li><li>5 server trong bảng mẫu (M365/GitHub/Filesystem/Brave Search/PostgreSQL) chỉ là ví dụ minh hoạ lấy từ docstring <code>core/mcp_client.py</code> — server thật phụ thuộc Admin đã cấu hình connector nào ở Settings, không có sẵn mặc định.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Bỏ ẩn dải tab Monitoring (8 mục ngang hàng) — không đổi accordion nav rail.</li><li>Thêm ô tìm kiếm + nút ✨AI cho bảng này để đồng bộ với 2 bảng log kia — hiện là bảng log duy nhất trong 3 bảng không có tìm kiếm.</li><li><span class="bad">Vượt phạm vi "chỉ sắp xếp lại"</span>: 4 thẻ KPI, 3 dropdown lọc (Server/Kết quả/Thời gian), phân trang và panel chi tiết trượt — cần bổ sung logic đếm/lọc/phân trang ở <code>ui/monitoring_tab.py</code> trước khi hiện thực; danh sách server chỉ minh hoạ, không phải cấu hình mặc định.</li></ul></div>\n</div></div>',
'monitoring-nhật-ký-hành-động': '<div class="bd">\n<p class="lead">Nhật ký cấp ứng dụng: ai đổi cấu hình, ai chạy task.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n\n<div class="cap">Đề xuất — bố cục mới</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab">MCP</div><div class="tab on">Hành động</div><div class="tab">Agent</div><div class="tab">Agents Admin</div><div class="tab">Công cụ</div><div class="tab">Icon</div></div><div class="c grow"><div class="r tb"><div class="ttl">Nhật ký hành động</div><div class="grow"></div><div class="btn">⭳ Xuất CSV</div><div class="btn pri">⟳ Làm mới</div></div><div class="r"><div class="kpi2"><div class="ic blue">▤</div><div><b>25</b><span class="s">Tổng sự kiện · +6.2%</span></div></div><div class="kpi2"><div class="ic grn">✓</div><div><b>19</b><span class="s">Thành công · 76%</span></div></div><div class="kpi2"><div class="ic red">✕</div><div><b>6</b><span class="s">Thất bại · 24%</span></div></div><div class="kpi2"><div class="ic pur">🛡</div><div><b>4</b><span class="s">Chặn bảo mật</span></div></div></div><div class="r tb"><div class="srchwrap grow"><input class="inp" placeholder="🔍 Tìm hành động, agent, chi tiết…"><span class="pill teal aibtn">✨ AI</span></div><select class="btn"><option value="">Tất cả loại</option><option>Công cụ</option><option>Gọi MCP</option><option>Chặn bảo mật</option><option>Quyền</option></select><select class="btn"><option value="">Tất cả trạng thái</option><option>Thành công</option><option>Thất bại</option></select><select class="btn"><option value="">Tất cả thời gian</option><option>Hôm nay</option><option>Tuần này</option><option>Tháng này</option></select><div class="btn">✕ Xóa lọc</div></div><div class="tblwrap grow"><table class="evtbl"><thead><tr><th>Thời gian</th><th>Agent</th><th>Loại</th><th>Máy</th><th>Hành động</th><th>Kết quả</th><th>Chi tiết</th></tr></thead><tbody><tr><td>15/08 · 09:42</td><td><span class="av blue">CA</span>Cowork Agent</td><td><span class="pill teal">Gọi MCP</span></td><td>DESKTOP-DEMO</td><td>ms365__send_mail</td><td><span class="pill grn">✓ Thành công</span></td><td class="dt">Đã gửi email đến nam.pdt@company.com — chủ đề "Báo cáo tuần Q3".</td></tr><tr><td>15/08 · 09:38</td><td><span class="av blue">CA</span>Cowork Agent</td><td><span class="pill blue">Công cụ</span></td><td>DESKTOP-DEMO</td><td>read_file</td><td><span class="pill grn">✓ Thành công</span></td><td class="dt">Đã đọc ./docs/installation.md (172 dòng).</td></tr><tr><td>15/08 · 09:31</td><td><span class="av red">SA</span>Security Agent</td><td><span class="pill pur">Chặn bảo mật</span></td><td>DESKTOP-DEMO</td><td>dangerous_command</td><td><span class="pill red">✕ Thất bại</span></td><td class="dt">Chặn lệnh: rm -rf / --no-preserve-root</td></tr><tr><td>15/08 · 09:20</td><td><span class="av blue">CA</span>Cowork Agent</td><td><span class="pill gry">Quyền</span></td><td>DESKTOP-DEMO</td><td>path_outside_sandbox</td><td><span class="pill red">✕ Thất bại</span></td><td class="dt">Chặn đọc C:\\Users\\NamPDT\\Documents\\personal.xlsx — ngoài sandbox.</td></tr><tr><td>15/08 · 08:55</td><td><span class="av red">SA</span>Security Agent</td><td><span class="pill teal">Gọi MCP</span></td><td>DESKTOP-DEMO</td><td>brave__web_search</td><td><span class="pill grn">✓ Thành công</span></td><td class="dt">Trả về 8 kết quả cho "CVE-2026-1234".</td></tr><tr><td>14/08 · 17:45</td><td><span class="av amb">SC</span>schedule</td><td><span class="pill blue">Công cụ</span></td><td>DESKTOP-DEMO</td><td>run_command</td><td><span class="pill grn">✓ Thành công</span></td><td class="dt">python scripts/report.py — thoát mã 0.</td></tr></tbody></table></div><div class="r tb"><div class="s">Hiển thị 1-25 của 25 sự kiện</div><div class="grow"></div><div class="btn">‹</div><div class="btn pri">1</div><div class="btn">›</div></div></div></div><div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Bảng thật (<code>ui/monitoring_tab.py:_EventTable</code>) gộp cả 4 loại sự kiện (tool_call/mcp_call/security_block/permission) vào tab này nhưng không có cột phân loại — phải tự suy ra loại hành động từ cột "Tên".</li><li>Bộ lọc thật (<code>_wrap_with_filter</code>) chỉ có 1 ô tìm + nút ✨AI; 3 dropdown Loại/Trạng thái/Thời gian, 4 thẻ KPI, phân trang và nút Xuất CSV trong <code>12.ui-redesign-action-logs.html</code> đều là <b>tính năng mới</b>, chưa có trong code.</li><li>Bảng thật giới hạn ngầm <code>_MAX_ROWS</code> dòng mới nhất và không phân trang — không biết còn bao nhiêu sự kiện bị ẩn.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Thêm cột <b>Loại</b> dạng pill màu (Công cụ/Gọi MCP/Chặn bảo mật/Quyền) trước cột Hành động, tái dùng đúng bảng màu <code>.pill</code> đã áp cho Sự kiện bảo mật (mục 10).</li><li><span class="bad">Vượt phạm vi "chỉ sắp xếp lại"</span>: 4 thẻ KPI, 3 dropdown lọc, phân trang và nút Xuất CSV cần bổ sung logic đếm/lọc/phân trang/export ở <code>ui/monitoring_tab.py</code> trước khi hiện thực — hiện bảng thật chỉ lọc bằng 1 ô tìm text.</li></ul></div>\n</div></div>',
'monitoring-trạng-thái-agent': '<div class="bd">\n<p class="lead">Agent nào đang bật và nguồn định nghĩa.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n\n<div class="cap">Đề xuất — bố cục mới</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab">MCP</div><div class="tab">Hành động</div><div class="tab on">Agent</div><div class="tab">Agents Admin</div><div class="tab">Công cụ</div><div class="tab">Icon</div></div><div class="c grow"><div class="r tb"><div class="ttl">Trạng thái Agent</div><div class="grow"></div><div class="btn">⟳ Làm mới</div></div><div class="tblwrap grow"><table class="evtbl"><thead><tr><th>Agent</th><th>Trạng thái</th><th>Nguồn</th></tr></thead><tbody><tr><td><span class="ic blue" style="display:inline-flex;vertical-align:middle;margin-right:6px">👤</span>Cowork Agent</td><td><span class="pill gry">● Rảnh</span></td><td class="dt">Lượt đang chạy của tab Cowork</td></tr><tr><td><span class="ic amb" style="display:inline-flex;vertical-align:middle;margin-right:6px">⏱</span>Task Agent</td><td><span class="pill gry">● Rảnh</span></td><td class="dt">Task đang chạy trong Schedule Task</td></tr><tr><td><span class="ic pur" style="display:inline-flex;vertical-align:middle;margin-right:6px">◈</span>Knowledge Agent</td><td><span class="pill gry">● Rảnh</span></td><td class="dt">Ô hỏi của GraphRAG</td></tr><tr><td><span class="ic gry" style="display:inline-flex;vertical-align:middle;margin-right:6px">▦</span>Planner Agent</td><td><span class="pill gry">—</span></td><td class="dt">Một giai đoạn trong lượt Cowork/Task đang chạy (update_plan) — không theo dõi riêng</td></tr><tr><td><span class="ic gry" style="display:inline-flex;vertical-align:middle;margin-right:6px">⬡</span>Reasoning Agent</td><td><span class="pill gry">—</span></td><td class="dt">Luồng suy luận (reasoning) của model trong lượt đang chạy — không theo dõi riêng</td></tr><tr><td><span class="ic red" style="display:inline-flex;vertical-align:middle;margin-right:6px">🛡</span>Security Agent</td><td><span class="pill grn">● Bật</span></td><td class="dt">Kiểm duyệt prompt/đính kèm/lệnh của Agent Security — chạy inline trong lượt đang chạy</td></tr></tbody></table></div></div></div><div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Bảng thật 3 cột (Agent · Đang chạy · Nguồn, <code>ui/monitoring_tab.py:_refresh_agent_status</code>) chỉ dùng chữ/icon đơn cho trạng thái — khó quét nhanh dòng nào đang chạy khi bảng dài.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Giữ nguyên 3 cột <b>Agent · Trạng thái · Nguồn</b> (không chuyển sang lưới thẻ, không thêm cột mới) — chỉ thêm icon màu theo loại agent trước tên và badge màu cho Trạng thái (xanh khi đang chạy/bật, xám khi "—") thay cho chữ thuần.</li></ul></div>\n</div></div>',
'monitoring-agents-admin': '<div class="bd">\n<p class="lead">Quản trị agent hệ thống. Cũng là nơi chọn model cho robot trợ giúp.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n<p class="rg"><b>Nút Kiểm tra</b>: probe provider thật</p>\n<div class="cap">Đề xuất — bố cục mới</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab">MCP</div><div class="tab">Hành động</div><div class="tab">Agent</div><div class="tab on">Agents Admin</div><div class="tab">Công cụ</div><div class="tab">Icon</div></div><div class="c grow"><div class="r tb"><div class="ttl">Agents Admin</div><div class="grow"></div><div class="btn">⟳ Làm mới</div><div class="btn pri">+ Thêm</div></div><div class="s">Agent quản lý hệ thống, dùng chung mọi máy (lưu trong thư mục dùng chung): agent trợ giúp và agent chạy Schedule Task. Đây <b>KHÔNG</b> phải agent để chọn trong Cowork hay Co4E.</div><div class="tblwrap grow"><table class="evtbl"><thead><tr><th>Tên</th><th>Vai trò</th><th>Model</th><th>Kích hoạt</th><th>Trạng thái</th><th>Cập nhật</th><th></th></tr></thead><tbody><tr><td><span class="av red">SA</span>Security Agent</td><td><span class="pill red">Bảo mật</span></td><td class="dt">Ollama — qwen2.5:7b</td><td><span class="tsw on"><i></i></span></td><td><span class="pill grn">OK</span></td><td class="dt">11/08 09:30</td><td>✎&nbsp;🗑</td></tr><tr><td><span class="av pur">GA</span>GraphRAG Agent</td><td><span class="pill pur">Tri thức</span></td><td class="dt">Ollama — qwen2.5:14b</td><td><span class="tsw on"><i></i></span></td><td><span class="pill grn">OK</span></td><td class="dt">10/08 14:22</td><td>✎&nbsp;🗑</td></tr><tr><td><span class="av amb">MA</span>Monitor Agent</td><td><span class="pill amb">Giám sát</span></td><td class="dt">Mặc định — qwen2.5:7b</td><td><span class="tsw on"><i></i></span></td><td><span class="pill amb">Kiểm tra…</span></td><td class="dt">09/08 11:05</td><td>✎&nbsp;🗑</td></tr><tr><td><span class="av teal">HA</span>Help Assistant</td><td><span class="pill teal">Hỗ trợ</span></td><td class="dt">Mặc định — qwen2.5:7b</td><td><span class="tsw"><i></i></span></td><td><span class="pill gry">Chưa kiểm tra</span></td><td class="dt">08/08 16:40</td><td>✎&nbsp;🗑</td></tr></tbody></table></div></div></div><div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n{{CONTROLS}}\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Cột Vai trò/Kích hoạt/Trạng thái chỉ là chữ hoặc icon đơn — khó phân biệt nhanh giữa các dòng khi bảng dài.</li><li>Bỏ [Kiểm tra tất cả] khỏi thanh công cụ chính làm mất lối vào hành động thật (<code>_check_all</code> trong <code>ui/agents_admin_tab.py</code>) — cần chỗ khác để gọi (icon riêng theo dòng, hoặc menu ⋯).</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Vai trò/Trạng thái chuyển sang badge màu + avatar viết tắt tên trước Tên agent; nút Sửa/Xoá dời vào từng dòng thay vì thanh công cụ rời bên dưới.</li><li>Thanh công cụ chính chỉ còn 2 nút: <b>Làm mới</b> (giống nút <code>monitoring.refresh</code> toàn màn hình) và <b>+ Thêm</b> (giống nút <code>agents_admin.add_btn</code> thật) — Sửa/Xoá đã dời vào từng dòng nên không cần ở đây nữa.</li></ul></div>\n</div></div>',
'monitoring-công-cụ': '<div class="bd">\n<p class="lead">Bật/tắt công cụ dựng sẵn và khai báo kết nối ngoài. Màn <b>duy nhất</b> còn hiện dải tab bên trong.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n<p class="rg"><b>Tool</b>: công cụ dựng sẵn + tự kiểm tra Internet · <b>Connector</b>: MCP · REST · MS365 · Jira</p>\n<div class="cap">Đề xuất — bố cục mới</div><div class="cap" style="margin:0 0 3px">Tab Tool</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab">MCP</div><div class="tab">Hành động</div><div class="tab">Agent</div><div class="tab">Agents Admin</div><div class="tab on">Công cụ</div><div class="tab">Icon</div></div><div class="r tb"><div class="tab on">Tool</div><div class="tab">Connector</div><div class="grow"></div></div><div class="c grow pane"><div class="r wrap"><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic blue">📄</div><b>read_file</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Đọc nội dung file text trong thư mục làm việc</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic blue">📁</div><b>list_dir</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Liệt kê file/thư mục con tại một đường dẫn</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic blue">✎</div><b>write_file</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Tạo file mới hoặc ghi đè toàn bộ file</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic blue">✏</div><b>edit_file</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Sửa một đoạn chính xác trong file có sẵn</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic grn">▤</div><b>run_command</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Chạy lệnh shell trong thư mục làm việc</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic pur">⭳</div><b>install_package</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Cài package Python (pip) vào môi trường app</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic amb">⤓</div><b>fetch_url</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Lấy nội dung trang web/tài liệu theo URL</span><div class="r tb" style="margin-top:3px"><div class="btn" id="fu-test">🌐 Kiểm tra Internet</div><span class="s" id="fu-status" style="color:var(--ok);margin-left:6px">✓ Kết nối OK · 42ms</span></div></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic pur">🔍</div><b>jira_search</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Tìm issue Jira bằng JQL, trả về danh sách tóm tắt</span></div><div class="card" style="flex:0 0 190px"><div class="r tb"><div class="ic pur">🔗</div><b>jira_get_issue</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Đọc chi tiết 1 issue Jira theo mã (vd ABX-123)</span></div></div><div class="grow"></div></div></div></div>\n<script>\n(function(){\n var btn=document.getElementById(\'fu-test\');\n if(!btn)return;\n btn.addEventListener(\'click\',function(){\n var st=document.getElementById(\'fu-status\');\n st.style.color=\'var(--fnt)\';\n st.textContent=\'Đang kiểm tra Internet…\';\n setTimeout(function(){\n st.style.color=\'var(--ok)\';\n st.textContent=\'✓ Kết nối OK · \'+(30+Math.floor(Math.random()*40))+\'ms\';\n },700);\n });\n})();\n</script>\n<div class="cap" style="margin:10px 0 3px">Tab Connector</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab">MCP</div><div class="tab">Hành động</div><div class="tab">Agent</div><div class="tab">Agents Admin</div><div class="tab on">Công cụ</div><div class="tab">Icon</div></div><div class="r tb"><div class="tab">Tool</div><div class="tab on">Connector</div><div class="grow"></div></div><div class="c grow pane"><div class="r tb"><span class="tsw on"><i></i></span><span class="lbl">Kết nối tới connector bên ngoài</span></div><div class="s">Một nơi duy nhất cho mọi nguồn tool ngoài — nhóm theo CAD (NX/CATIA/SolidWorks/AutoCAD), CAE (ANSA/ABAQUS/HyperWorks/ANSYS), MS365 (Microsoft 365/OneDrive/SharePoint) và Other (MCP server bất kỳ). MS365 tự kết nối qua server tích hợp sau khi đăng nhập; còn lại bạn trỏ mỗi connector tới MCP server bạn đã có hoặc REST API nó cung cấp (không kèm SDK hãng nào).</div><div class="grow" style="overflow-y:auto"><div class="r tb" style="gap:6px;margin-top:4px"><span>🔧</span><b>CAD</b><span class="lbl">(NX / CATIA / SolidWorks / AutoCAD)</span></div><div class="r wrap" style="margin-left:18px"><div class="card" style="flex:0 0 auto"><div class="r tb"><b>AutoCAD</b><span class="grow"></span><span class="tsw"><i></i></span></div><span class="s">MCP server (stdio)</span><span class="s" style="margin-top:2px">✎ Sửa &nbsp;·&nbsp; 🗑 Xóa</span></div></div><div class="r tb" style="gap:6px;margin-top:4px"><span>📐</span><b>CAE</b><span class="lbl">(ANSA / ABAQUS / HyperWorks / ANSYS)</span></div><div class="r wrap" style="margin-left:18px"><div class="card" style="flex:0 0 auto"><div class="r tb"><b>ANSA</b><span class="grow"></span><span class="tsw"><i></i></span></div><span class="s">MCP server (stdio)</span><span class="s" style="margin-top:2px">✎ Sửa &nbsp;·&nbsp; 🗑 Xóa</span></div></div><div class="r tb" style="gap:6px;margin-top:4px"><span>☁</span><b>MS365</b><span class="lbl">(Microsoft 365 / OneDrive / SharePoint)</span></div><div class="r wrap" style="margin-left:18px"><div class="card" style="flex:0 0 auto"><div class="r tb"><b>OneDrive</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Tích hợp, tự kết nối</span></div><div class="card" style="flex:0 0 auto"><div class="r tb"><b>SharePoint</b><span class="grow"></span><span class="tsw on"><i></i></span></div><span class="s">Tích hợp, tự kết nối</span></div></div><div class="r tb" style="gap:6px;margin-top:4px"><span>🔌</span><b>Other</b><span class="lbl">(any generic MCP server)</span></div><div class="r wrap" style="margin-left:18px"><div class="card" style="flex:0 0 auto"><div class="r tb"><b>Jira</b><span class="grow"></span><span class="tsw"><i></i></span></div><span class="s">Tích hợp, tự kết nối · chưa cấu hình</span><span class="s" style="margin-top:2px">✎ Sửa</span></div></div></div><div class="r tb" style="margin-top:4px"><div class="btn pri">+ Thêm connector…</div></div></div></div><div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n{{CONTROLS}}\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Màn <b>duy nhất còn hiện dải tab</b> — 7 màn Monitoring kia bị ẩn. Không nhất quán.</li><li>Tab <b>Tool</b>/<b>Connector</b> lọt thỏm trong một mục nav.</li><li>Cấu hình kết nối tách khỏi Settings → thiết lập ở 2 nơi.</li><li>Tab <b>Tool</b> liệt kê <b>9 tool thật</b> (<code>core/tools.py:TOOL_SPECS</code>) dạng chữ phẳng (☑/☐) — không thấy ngay tool nào đang bật khi lướt nhanh.</li><li>Tab <b>Connector</b> là cây phân cấp 4 nhóm (CAD/CAE/MS365/Other, <code>ui/connectors_panel.py</code>) — phải bung từng nhóm mới thấy có gì bên trong.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Bỏ ẩn dải tab cho toàn bộ Monitoring → 8 tab một hàng; Tool/Connector ngang hàng.</li><li>Tab Tool: 9 tool thành <b>lưới thẻ căn trái</b> (không kéo giãn lấp đầy hàng) — icon màu theo loại + công tắc gạt bật/tắt, thay ô tick trong list phẳng.</li><li>Tab Connector: 4 nhóm thật thành khối <b>nhóm theo catalog</b> (không gộp vào tab Tool) — mỗi catalog là 1 tiêu đề (icon + tên + loại máy/phần mềm), bên dưới là hàng thẻ nhỏ căn trái, mỗi thẻ 1 option thật kèm <b>công tắc gạt</b> bật/tắt (không phải badge tĩnh) — đúng bản chất mỗi option đều chuyển được trạng thái, nhất quán với công tắc ở tab Tool; giữ đúng cấu trúc phân cấp 2 tầng của cây thật thay vì gộp phẳng vào 1 thẻ/catalog. Công tắc "Kết nối ngoài" tổng vẫn ở trên cùng. Thẻ của connector do người dùng tự thêm (AutoCAD, ANSA) có thêm ✎ Sửa/🗑 Xóa đúng như <code>_ext_edit</code>/<code>_ext_delete</code> thật; Jira (built-in) chỉ có ✎ Sửa (mở dialog cấu hình riêng, không xóa được); OneDrive/SharePoint (built-in MS365) không có nút nào — thật cũng chỉ bật/tắt, không sửa/xóa.</li><li>Nút <b>Kiểm tra Internet</b> dời từ thanh trên (tách khỏi tool nào) vào đúng bên trong thẻ <b>fetch_url</b>, khớp <code>tools_admin_tab.py:_fetch_url_desc_cell</code> — nút này gắn với khả năng fetch_url, không phải một hành động chung của cả tab.</li></ul></div>\n</div></div>',
'monitoring-icon': '<div class="bd">\n<p class="lead">Thư viện icon, dùng lại khi đặt icon cho agent Co4E.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n\n<div class="cap">Đề xuất — bố cục mới</div><div class="wf"><div class="rail"><div class="menutog"><span>MENU</span><span class="chev">‹</span></div><div class="rpick"><span>📁 Báo cáo tài chính Q3</span><span class="cv">▾</span></div><div class="newbtn">+ Đoạn chat mới</div><div class="i">Project</div><div class="i">Cowork</div><div class="i">Co4E</div><div class="i">Folder</div><div class="i">GraphRAG</div><div class="i">Schedule Task</div><div class="sep"></div><div class="hd">RECENTS</div><div class="scope">📁 Báo cáo tài chính Q3</div><div class="i sm">📌 Gom số liệu doanh thu</div><div class="i sm">Dựng slide trình bày Q3</div><div class="i sm allp">Tất cả project…</div><div class="grow"></div><div class="sep"></div><div class="i">Dashboard</div><div class="i">Monitoring</div><div class="acct">👤 local · Ollama ▾</div></div><div class="main"><div class="ttl">Monitoring</div><div class="r tb"><div class="tab">Tổng quan</div><div class="tab">Bảo mật</div><div class="tab">MCP</div><div class="tab">Hành động</div><div class="tab">Agent</div><div class="tab">Agents Admin</div><div class="tab">Công cụ</div><div class="tab on">Icon</div></div><div class="c grow"><div class="r tb"><div class="ttl">Icon</div><div class="grow"></div><div class="btn">+ Thêm icon</div><div class="btn">Dán SVG</div><div class="btn">Xoá</div></div><div class="srchwrap"><div class="inp grow">🔍 Tìm icon theo tên…</div></div><div class="hd2">ICON TÍCH HỢP</div><div class="r wrap"><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">✛</div><span class="s">plus</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">✓</div><span class="s">check</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">✕</div><span class="s">close</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">✎</div><span class="s">edit</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">🗑</div><span class="s">trash</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">⟳</div><span class="s">refresh</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">🔍</div><span class="s">search</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">⚙</div><span class="s">settings</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">🤖</div><span class="s">robot</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">🛡</div><span class="s">shield</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">🔧</div><span class="s">wrench</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">★</div><span class="s">star</span></div></div><div class="hd2" style="margin-top:4px">ICON TÙY CHỈNH</div><div class="r wrap"><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">🧠</div><span class="s">brain</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">⌥</div><span class="s">code</span></div><div class="card" style="flex:0 0 56px;text-align:center;padding:6px 2px"><div style="font-size:16px">☁</div><span class="s">cloud</span></div></div></div></div><div class="dock fab" title="AI Assistant — trợ lý cách dùng app"><span class="spark">✨</span></div></div>\n{{CONTROLS}}\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Ô icon trong lưới hiện tại không có viền/hover rõ khi rê chuột hay khi đang chọn.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Ô tìm kiếm thêm icon kính lúp bên trái; mỗi ô icon có viền accent khi hover/chọn — cùng ngôn ngữ thẻ với Agent/Công cụ.</li></ul></div>\n</div></div>',
'dialog-settings': '<div class="bd">\n<p class="lead">Thiết lập toàn app. Cuộn dọc, không mục lục.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n<p class="rg"><b>5 nhóm</b>: Ngôn ngữ · Provider · Bảo mật (khoá mật khẩu) · Tham số · Routing</p>\n<div class="cap">Đề xuất — bố cục mới</div><div class="wf" id="setwf"><div class="main dlg"><div class="ttl">Cài đặt</div><div class="r grow"><div class="c w26 pane"><div class="li on" id="setl-chung">Chung<span class="s">ngôn ngữ · giao diện · khay</span></div><div class="li" id="setl-provider">AI Provider<span class="s">Ollama · qwen2.5-coder</span></div><div class="li" id="setl-sandbox">Bảo mật sandbox<span class="s">🔒 cần mở khoá</span></div><div class="li" id="setl-param">Tham số<span class="s">đính kèm · GraphRAG · giới hạn</span></div><div class="li" id="setl-routing">Auto Model Routing<span class="s">đang Tắt</span></div><div class="grow"></div></div><div class="c grow"><div id="setp-chung"><div class="frow"><span class="lbl">Ngôn ngữ hiển thị</span><div class="grow"></div><div class="seg" id="set-lang"><span data-v="en">English</span><span class="on" data-v="vi">Tiếng Việt</span><span data-v="ja">日本語</span></div></div><div class="frow"><span class="lbl">Giao diện</span><div class="grow"></div><div class="seg" id="set-theme"><span data-v="light">Sáng</span><span class="on" data-v="system">Hệ thống</span><span data-v="dark">Tối</span></div></div><div class="frow"><span class="lbl">Giữ chạy nền trong khay hệ thống khi đóng</span><div class="grow"></div><div class="tsw on"><i></i></div></div><div class="frow"><span class="lbl">Hiện thông báo khay khi tác vụ xong hoặc lỗi</span><div class="grow"></div><div class="tsw on"><i></i></div></div></div><div id="setp-provider" class="hide"><div class="lbl">Nhà cung cấp AI</div><div class="inp">Ollama (local models)</div><div class="lbl">Base URL</div><div class="inp">http://localhost:11434</div><div class="lbl">API Key</div><div class="inp">••••••••</div><div class="lbl">Model</div><div class="inp">qwen2.5-coder:7b <span class="s">⭳ Tải · ⚗ Test kết nối</span></div></div><div id="setp-sandbox" class="hide"><div class="r tb"><div class="inp grow">Nhập mật khẩu…</div><div class="btn">Unlock</div><span class="pill red">🔒 Locked</span></div><div class="frow"><span class="lbl">Xác nhận trước khi Cowork chạy lệnh</span><div class="grow"></div><div class="tsw"><i></i></div></div><div class="frow"><span class="lbl">Chặn mạng cho lệnh do agent chạy</span><div class="grow"></div><div class="tsw on"><i></i></div></div><div class="frow"><span class="lbl">Enable Agent Security (kiểm tra lệnh)</span><div class="grow"></div><div class="tsw on"><i></i></div></div><div class="frow"><span class="lbl">AI check commands</span><div class="grow"></div><div class="tsw"><i></i></div></div></div><div id="setp-param" class="hide"><div class="card"><div class="cap">Đính kèm</div><div class="frow"><span class="lbl">Số tệp tối đa</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-files" data-d="-1">−</span><span class="sv" id="sp-files">20</span><span class="sb" data-t="sp-files" data-d="1">+</span></div><span class="s">tệp</span></div><div class="frow"><span class="lbl">Token tối đa mỗi tệp</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-tokens" data-d="-5">−</span><span class="sv" id="sp-tokens">500</span><span class="sb" data-t="sp-tokens" data-d="5">+</span></div><span class="s">nghìn</span></div></div><div class="card"><div class="cap">Cấu trúc &amp; GraphRAG</div><div class="frow"><span class="lbl">Số node tối đa</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-nodes" data-d="-1">−</span><span class="sv" id="sp-nodes">500</span><span class="sb" data-t="sp-nodes" data-d="1">+</span></div><span class="s">node</span></div><div class="frow"><span class="lbl">Số cạnh tối đa</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-edges" data-d="-1">−</span><span class="sv" id="sp-edges">500</span><span class="sb" data-t="sp-edges" data-d="1">+</span></div><span class="s">cạnh</span></div></div><div class="card"><div class="cap">Giới hạn sandbox</div><div class="frow"><span class="lbl">CPU</span><div class="grow"></div><b>Không giới hạn</b></div><div class="frow"><span class="lbl">Bộ nhớ</span><div class="grow"></div><b>2.048 MB</b></div><div class="frow"><span class="lbl">Disk I/O</span><div class="grow"></div><b>2.048 MB</b></div></div></div><div id="setp-routing" class="hide"><div class="frow"><span class="lbl">Chế độ</span><div class="grow"></div><div class="seg" id="set-rmode"><span class="on" data-v="off">Tắt</span><span data-v="auto">Auto</span><span data-v="manual">Manual</span></div></div><div class="frow"><span class="lbl">Chính sách</span><div class="grow"></div><div class="seg" id="set-rpolicy"><span data-v="quality">Chất lượng</span><span data-v="cost">Chi phí</span><span data-v="latency">Độ trễ</span><span class="on" data-v="balanced">Cân bằng</span></div></div><div class="frow"><span class="lbl">Ngưỡng tăng điểm tối thiểu</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-mingain" data-d="-1">−</span><span class="sv" id="sp-mingain">5</span><span class="sb" data-t="sp-mingain" data-d="1">+</span></div><span class="s">%</span></div><div class="frow"><span class="lbl">Timeout xác nhận</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-timeout" data-d="-1">−</span><span class="sv" id="sp-timeout">60</span><span class="sb" data-t="sp-timeout" data-d="1">+</span></div><span class="s">giây</span></div><div class="frow"><span class="lbl">Chu kỳ đánh giá lại</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-interval" data-d="-1">−</span><span class="sv" id="sp-interval">24</span><span class="sb" data-t="sp-interval" data-d="1">+</span></div><span class="s">giờ</span></div><div class="frow"><span class="lbl">Đồng thời mỗi provider</span><div class="grow"></div><div class="stp"><span class="sb" data-t="sp-concurrency" data-d="-1">−</span><span class="sv" id="sp-concurrency">2</span><span class="sb" data-t="sp-concurrency" data-d="1">+</span></div></div><div class="lbl">Judge model</div><div class="inp">(để trống · dùng model đang chọn)</div><div class="r tb"><div class="btn">⟳ Đánh giá lại ngay</div></div></div><div class="grow"></div><div class="r tb end"><div class="btn">Huỷ</div><div class="btn pri">Lưu</div></div></div></div></div></div>\n<script>\n(function(){\n var panels=[\'chung\',\'provider\',\'sandbox\',\'param\',\'routing\'];\n panels.forEach(function(p){\n document.getElementById(\'setl-\'+p).addEventListener(\'click\',function(){\n panels.forEach(function(q){\n document.getElementById(\'setl-\'+q).classList.toggle(\'on\',q===p);\n document.getElementById(\'setp-\'+q).classList.toggle(\'hide\',q!==p);\n });\n });\n });\n Array.prototype.forEach.call(document.querySelectorAll(\'#setwf .tsw\'),function(t){\n t.addEventListener(\'click\',function(){t.classList.toggle(\'on\');});\n });\n Array.prototype.forEach.call(document.querySelectorAll(\'#setwf .seg\'),function(seg){\n Array.prototype.forEach.call(seg.querySelectorAll(\'span\'),function(s){\n s.addEventListener(\'click\',function(){\n Array.prototype.forEach.call(seg.querySelectorAll(\'span\'),function(x){x.classList.remove(\'on\');});\n s.classList.add(\'on\');\n });\n });\n });\n Array.prototype.forEach.call(document.querySelectorAll(\'#setwf .stp .sb\'),function(btn){\n btn.addEventListener(\'click\',function(){\n var target=document.getElementById(btn.dataset.t);\n var v=parseInt(target.textContent,10)+parseInt(btn.dataset.d,10);\n if(v<0)v=0;\n target.textContent=String(v);\n });\n });\n})();\n</script>\n{{CONTROLS}}\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li>Năm group cuộn dọc, không mục lục.</li><li>Sandbox khoá bằng <b>mật khẩu hard-code</b> (<code>settings_dialog.py:115</code>).</li><li>Provider/Ngôn ngữ/Giao diện ở top bar, tách khỏi Settings.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Thêm cột mục lục bên trái; gom Ngôn ngữ/Giao diện vào nhóm Chung (AI Provider vẫn là nhóm riêng như hiện tại, không gộp).</li><li>Đổi cách hiển thị field theo đúng loại dữ liệu: checkbox ☑/☐ → toggle switch, dropdown ít lựa chọn (2–4 giá trị) → segmented control, số nhập tay → stepper, 3 nhóm con trong Tham số → gom thành card — vẫn đúng field/giá trị mặc định thật từ <code>ui/settings_dialog.py</code>, chỉ đổi cách trình bày chứ không đổi dữ liệu.</li></ul></div>\n</div></div>',
'dialog-task-editor': '<div class="bd">\n<p class="lead">Khai báo tác vụ hẹn giờ: nội dung, lịch lặp, phụ thuộc, thông báo.</p>\n<div class="cap">Hiện tại</div>{{SHOT}}\n<p class="rg"><b>5 nhóm</b>: Cơ bản · Lịch · Đầu vào · Phụ thuộc · Thực thi</p>\n<div class="cap">Đề xuất — bố cục mới</div>\n<p class="mut">Cùng 1 dialog <code>TaskEditorDialog</code> cho cả <b>Thêm task</b> và <b>Sửa task</b> — chỉ đổi tiêu đề (Thêm/Sửa), bố cục dưới đây dùng chung cho cả hai. Layout theo đúng kiểu màn <b>Cài đặt</b>: danh sách 5 nhóm bên trái thay cho 3 tab wizard, đúng 5 <code>QGroupBox</code> thật (không phải 3 bước tự đặt ra).</p>\n<div class="wf" id="taskwf"><div class="main dlg"><div class="ttl">Sửa task</div><div class="r grow"><div class="c w26 pane"><div class="li on" id="tel-basic">Cơ bản<span class="s">tiêu đề · loại chạy · model</span></div><div class="li" id="tel-sched">Lịch<span class="s">chạy 1 lần · lặp lại</span></div><div class="li" id="tel-input">Đầu vào<span class="s">prompt · tệp · liên kết</span></div><div class="li" id="tel-dep">Phụ thuộc<span class="s">task kế tiếp · chờ hoàn tất</span></div><div class="li" id="tel-exec">Thực thi<span class="s">thử lại · timeout · phê duyệt</span></div><div class="grow"></div></div><div class="c grow"><div id="tep-basic"><div class="frow"><span class="lbl">Chế độ</span><div class="grow"></div><div class="seg" id="tsk-mode"><span class="on" data-v="normal">Bình thường</span><span data-v="automation">Tự động</span></div></div><div class="lbl">Tiêu đề</div><div class="inp">Gửi báo cáo doanh thu hằng ngày 08:00</div><div class="lbl">Mô tả</div><div class="inp tall">Gom số liệu ngày hôm trước, dựng bảng và gửi email cho nhóm kế toán. <span class="s">✨ Sinh prompt từ mô tả</span></div><div class="lbl">Project</div><div class="inp">Báo cáo tài chính Q3</div><div class="frow"><span class="lbl">Loại chạy</span><div class="grow"></div><div class="seg" id="tsk-runkind"><span class="on" data-v="agent">AI Agent</span><span data-v="flow">Co4E Flow</span></div></div><div class="lbl">Nhà cung cấp</div><div class="inp">Ollama (local models)</div><div class="lbl">Model</div><div class="inp">qwen2.5-coder:7b <span class="s">⭳ Tải model</span></div><div class="lbl">Skill</div><div class="inp">(Không dùng)</div><div class="lbl">Ưu tiên</div><div class="inp">medium</div><div class="lbl">Trạng thái</div><div class="inp">backlog</div></div><div id="tep-sched" class="hide"><div class="frow"><span class="lbl">Bật lịch chạy</span><div class="grow"></div><div class="tsw on"><i></i></div></div><div class="lbl">Thời điểm chạy</div><div class="inp">2026-08-17 08:00 AM</div><div class="lbl">Lặp lại</div><div class="inp">Hằng ngày</div><div class="lbl">Cron (khi lặp = tuỳ chỉnh)</div><div class="inp">0 8 * * * <span class="s">— chọn mẫu có sẵn</span></div><div class="frow"><span class="lbl">Chỉ ngày làm việc (bỏ T7/CN)</span><div class="grow"></div><div class="tsw"><i></i></div></div><div class="frow"><span class="lbl">Bỏ qua ngày nghỉ lễ</span><div class="grow"></div><div class="tsw on"><i></i></div></div><div class="lbl">Mã quốc gia lễ</div><div class="inp">VN</div><div class="frow"><span class="lbl">Kênh thông báo</span><div class="grow"></div><div class="seg" id="tsk-notify"><span class="on" data-v="none">Không</span><span data-v="teams">Teams</span><span data-v="outlook">Outlook</span></div></div><div class="lbl">Email nhận thông báo</div><div class="inp">ketoan@company.com</div></div><div id="tep-input" class="hide"><div class="lbl">Prompt thủ công</div><div class="inp tall">Tổng hợp số liệu doanh thu ngày hôm trước từ file đính kèm, dựng bảng tóm tắt.</div><div class="lbl">Tệp đính kèm</div><div class="li">📄 sales_template.xlsx</div><div class="li">📄 Q3_report_outline.docx</div><div class="r tb"><div class="btn">+ Thêm tệp</div><div class="btn">🗑 Xoá</div></div><div class="lbl">Liên kết</div><div class="li">🔗 https://intranet.company.com/sales-dashboard</div><div class="r tb"><div class="btn">+ Thêm liên kết</div><div class="btn">🗑 Xoá</div></div></div><div id="tep-dep" class="hide"><div class="lbl">Task kế tiếp</div><div class="inp">(Không có)</div><div class="lbl">Chế độ chạy tiếp</div><div class="inp">Không tự động chạy tiếp</div><div class="frow"><span class="lbl">Dùng output làm input task sau</span><div class="grow"></div><div class="tsw"><i></i></div></div><div class="lbl">Chờ các task này xong (fan-in)</div><div class="li">☑ Gom số liệu doanh thu</div><div class="li">☐ Dựng slide trình bày Q3</div></div><div id="tep-exec" class="hide"><div class="lbl">Số lần thử lại</div><div class="inp">0 lần</div><div class="lbl">Timeout</div><div class="inp">600 giây</div><div class="frow"><span class="lbl">Cần phê duyệt (chờ bấm Chạy ngay)</span><div class="grow"></div><div class="tsw"><i></i></div></div></div><div class="grow"></div><div class="r tb end"><div class="btn">Huỷ</div><div class="btn pri">Lưu</div></div></div></div></div></div>\n<script>\n(function(){\n var panels=[\'basic\',\'sched\',\'input\',\'dep\',\'exec\'];\n panels.forEach(function(p){\n document.getElementById(\'tel-\'+p).addEventListener(\'click\',function(){\n panels.forEach(function(q){\n document.getElementById(\'tel-\'+q).classList.toggle(\'on\',q===p);\n document.getElementById(\'tep-\'+q).classList.toggle(\'hide\',q!==p);\n });\n });\n });\n Array.prototype.forEach.call(document.querySelectorAll(\'#taskwf .tsw\'),function(t){\n t.addEventListener(\'click\',function(){t.classList.toggle(\'on\');});\n });\n Array.prototype.forEach.call(document.querySelectorAll(\'#taskwf .seg\'),function(seg){\n Array.prototype.forEach.call(seg.querySelectorAll(\'span\'),function(s){\n s.addEventListener(\'click\',function(){\n Array.prototype.forEach.call(seg.querySelectorAll(\'span\'),function(x){x.classList.remove(\'on\');});\n s.classList.add(\'on\');\n });\n });\n });\n})();\n</script>\n{{CONTROLS}}\n<div class="cols pc">\n<div><div class="cap">Vấn đề</div><ul class="pr"><li><b>Năm group</b> dọc — form dài nhất app, không thấy đang ở bước nào.</li></ul></div>\n<div><div class="cap">Thay đổi</div><ul class="pr"><li>Đổi sang layout <b>danh sách bên trái + panel bên phải</b> giống màn Cài đặt (thay vì chia tab) — 5 mục danh sách khớp đúng 5 <code>QGroupBox</code> thật (<b>Cơ bản · Lịch · Đầu vào · Phụ thuộc · Thực thi</b>, xem <code>ui/task_editor_dialog.py</code>), luôn thấy đang ở nhóm nào và còn bao nhiêu nhóm nữa — nhất quán với cách điều hướng ở Cài đặt thay vì mỗi màn một kiểu.</li></ul></div>\n</div></div>',
}
EXTRA_CSS = '.embed{width:100%;height:760px;border:1px solid var(--bd);border-radius:var(--r);\nbackground:#fff;display:block}\np.hint{margin:4px 0 8px}\n.wf .acct{margin-top:6px;padding:6px 7px;border-top:1px solid var(--navb);color:var(--mut);font-size:10px}\npadding:6px 8px;color:var(--tx);height:26px;box-sizing:border-box;overflow:hidden}\n.wf .inp.tall{min-height:52px;height:auto}\npadding:4px 9px;white-space:nowrap;align-self:center;height:26px;box-sizing:border-box;\ndisplay:inline-flex;align-items:center;justify-content:center;line-height:1}\n/* ---- section-10 interactive-preview vocabulary: literal palette from 10SuKienBaoMat.html ---- */\n.wf .kpi2{flex:1;background:var(--rz);border:1px solid var(--bd);border-radius:6px;padding:7px 9px;display:flex;align-items:center;gap:7px;min-width:0}\n.wf .kpi2 b{font-size:15px;display:block;line-height:1.2}\n.wf .ic{width:20px;height:20px;border-radius:5px;flex:none;display:flex;align-items:center;justify-content:center;font-size:10px}\n.wf .ic.blue{background:#DEECF9;color:#0078D4}.wf .ic.grn{background:#DFF6DD;color:#107C10}\n.wf .ic.red{background:#FDE7E9;color:#D13438}.wf .ic.pur{background:#F3E8FD;color:#8764B8}\n.wf .ic.amb{background:#FFF4CE;color:#795548}.wf .ic.gry{background:#F3F2F1;color:#605E5C}\n.wf .pill{display:inline-block;padding:2px 8px;border-radius:99px;font-size:9px;font-weight:700;white-space:nowrap}\n.wf .pill.red{background:#FDE7E9;color:#D13438}.wf .pill.grn{background:#E4F7C7;color:#498205}\n.wf .pill.teal{background:#D2F0EE;color:#008272}.wf .pill.org{background:#FDE6D9;color:#DA3B01}\n.wf .pill.blue{background:#DEECF9;color:#0078D4}.wf .pill.amb{background:#FFF4CE;color:#795548}\n.wf .pill.pur{background:#F3E8FD;color:#8764B8}.wf .pill.gry{background:#F3F2F1;color:#605E5C}\n.wf .r.wrap{flex-wrap:wrap}\n.wf .av{display:inline-flex;width:16px;height:16px;border-radius:50%;flex:none;align-items:center;\njustify-content:center;font-size:7px;font-weight:700;color:#fff;margin-right:4px}\n.wf .av.red{background:#D13438}.wf .av.blue{background:#0078D4}\n.wf .av.pur{background:#8764B8}.wf .av.grn{background:#107C10}.wf .av.amb{background:#FFB900}\n.wf .av.teal{background:#008272}.wf .av.dark{background:#24292F}.wf .av.olv{background:#498205}\n.wf .tblwrap{overflow-y:auto;overflow-x:hidden}\n.wf .evtbl{width:100%;border-collapse:collapse;font-size:9.5px}\n.wf .evtbl thead{position:sticky;top:0;background:var(--bg)}\n.wf .evtbl th{text-align:left;padding:4px 6px;color:var(--fnt);font-weight:700;text-transform:uppercase;\nfont-size:8px;letter-spacing:.04em;border-bottom:1px solid var(--bd);white-space:nowrap;cursor:default}\n.wf .evtbl td{padding:4px 6px;border-bottom:1px solid var(--bd);white-space:nowrap;vertical-align:middle}\n.wf .evtbl td.dt{white-space:normal;color:var(--fnt)}\n.wf .evtbl tbody tr{cursor:pointer}.wf .evtbl tbody tr:hover{background:var(--sf)}\n.wf .pane.hide{display:none}\n.wf .hide{display:none}\n/* ---- Settings field-display alternatives (toggle switch / stepper) ---- */\n.wf .tsw{display:inline-flex;align-items:center;width:32px;height:17px;border-radius:99px;\nbackground:var(--bds);position:relative;cursor:pointer;flex:none;transition:background .15s}\n.wf .tsw i{position:absolute;top:2px;left:2px;width:13px;height:13px;border-radius:50%;\nbackground:#fff;transition:left .15s;box-shadow:0 1px 2px rgba(0,0,0,.25)}\n.wf .tsw.on{background:var(--ac)}\n.wf .tsw.on i{left:17px}\n.wf .stp{display:inline-flex;align-items:center;border:1px solid var(--bds);border-radius:4px;\noverflow:hidden;height:26px;box-sizing:border-box;flex:none}\n.wf .stp .sb{width:22px;height:100%;display:flex;align-items:center;justify-content:center;\nbackground:var(--rz);cursor:pointer;font-weight:700;color:var(--tx);user-select:none}\n.wf .stp .sb:hover{background:var(--sf)}\n.wf .stp .sv{padding:0 10px;min-width:44px;text-align:center;font-weight:600;background:var(--bg);\nheight:100%;display:flex;align-items:center;justify-content:center;\nborder-left:1px solid var(--bds);border-right:1px solid var(--bds)}\n.wf .frow{display:flex;align-items:center;gap:6px;min-height:0;padding:2px 0}\n.wf .seg{display:inline-flex;border:1px solid var(--bds);border-radius:4px;overflow:hidden;flex:none}\n.wf .seg span{background:var(--bg);color:var(--mut);padding:3px 10px;cursor:pointer;font-size:10px;white-space:nowrap}\n.wf .seg span.on{background:var(--ac);color:#fff;font-weight:600}\n.wf .pane.overlay{position:absolute;top:0;right:0;bottom:0;width:38%;z-index:5;\nbackground:var(--bg);border-left:1px solid var(--bd);box-shadow:-6px 0 14px rgba(0,0,0,.18)}\n.wf .dtl{padding:2px 0 0}\n.wf .dtl.grow{overflow-y:auto}\n.wf .pnlfoot{border-top:1px solid var(--bd);flex:none;padding-top:6px}\n#s10close{cursor:pointer}\n#s11close{cursor:pointer}\n.wf .dtl .hd3{font-size:8px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--fnt);\npadding-bottom:3px;margin:8px 0 3px;border-bottom:1px solid var(--bd)}\n.wf .dtl .hd3:first-child{margin-top:0}\n.wf .dtl .fld{display:flex;justify-content:space-between;align-items:center;padding:3px 0;gap:6px}\n.wf .dtl .fld .lbl{color:var(--mut)}\n.wf .dtl .fld .val{font-weight:600;text-align:right}\n.wf .dtl .mono{font-family:"Cascadia Code",Consolas,monospace;background:var(--sf);border:1px solid var(--bd);\nborder-radius:3px;padding:1px 6px;font-size:9px;font-weight:400}\n.wf .dtl .tagn{background:var(--sf);border-radius:3px;padding:1px 7px;font-size:9px;font-weight:400}\n.wf .dtl .code{background:#1b1a19;color:#CCFF00;font-family:"Cascadia Code",Consolas,monospace;\nfont-size:9px;padding:6px 8px;border-radius:4px;margin:4px 0;display:flex;align-items:center;\njustify-content:space-between;gap:6px}\n.wf .dtl .code .cpy{background:rgba(255,255,255,.15);color:#fff;border-radius:3px;padding:2px 6px;\nfont-size:8px;white-space:nowrap;flex:none;cursor:pointer}\n.wf select.btn{appearance:none;-webkit-appearance:none;font:inherit;color:inherit;padding-right:16px;cursor:pointer;\nbackground-image:url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'8\' height=\'8\' viewBox=\'0 0 10 10\'%3E%3Cpath d=\'M1 3.5L5 7.5L9 3.5\' stroke=\'%23888\' stroke-width=\'1.5\' fill=\'none\' stroke-linecap=\'round\'/%3E%3C/svg%3E");\nbackground-repeat:no-repeat;background-position:right 4px center}\n.wf input.inp{font:inherit;color:inherit;outline:none;width:100%}\n.wf .srchwrap{position:relative;display:flex;align-items:center;min-width:0}\n.wf .srchwrap .inp{padding-right:48px}\n.wf .srchwrap .aibtn{position:absolute;right:3px;top:50%;transform:translateY(-50%);cursor:pointer;\nline-height:1;padding:5px 10px;border-radius:4px;font-size:11px}\n.wf .edge{background:var(--sf);border:1px solid var(--bds);border-right:none;border-radius:5px 0 0 5px;padding:14px 5px;color:var(--mut)}\n.wf .aibadge{background:#E6F6F4;border:1px solid #7FD0C4;border-radius:6px;padding:7px 11px;font-weight:700;color:#0F6E62;white-space:nowrap}\n.wf .dock.badge{background:#E6F6F4;border:1px solid #7FD0C4;border-radius:6px;padding:7px 11px;font-weight:700;color:#0F6E62;white-space:nowrap}\n.wf .dock.pnl{width:74%;height:76%;background:var(--sf);border:1px solid var(--bds);border-radius:6px;box-shadow:0 3px 10px rgba(16,32,64,.13)}\n.wf .edge{background:var(--sf);border:1px solid var(--bds);border-right:none;border-radius:5px 0 0 5px;padding:14px 5px;color:var(--mut)}\n.wf .aibadge{background:#E6F6F4;border:1px solid #7FD0C4;border-radius:6px;padding:7px 11px;font-weight:700;color:#0F6E62;white-space:nowrap}\n.wf .dock .badge{background:#E6F6F4;border:1px solid #7FD0C4;border-radius:6px;\n/* Menu ⋯ trong panel tro ly — ghep tu ui-audit.html */\n/* Chấm trợ lý 26px — ghép từ ui-audit.html */'
EXTRA_JS = [
]