@kenhkl/agent-moss 0.10.4 → 0.10.5

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.
@@ -247,6 +247,7 @@ body {
247
247
  <div class="ag-tab" data-page="l1">L1 规则</div>
248
248
  <div class="ag-tab" data-page="l2">L2 规则</div>
249
249
  <div class="ag-tab" data-page="l3">L3 Skills</div>
250
+ <div class="ag-tab" data-page="agents">Agent 定制</div>
250
251
  <div class="ag-tab" data-page="token">Token</div>
251
252
  <div class="ag-tab" data-page="settings">设置</div>
252
253
  <div class="ag-tab-spacer"></div>
@@ -257,6 +258,7 @@ body {
257
258
  <div class="ag-panel hidden" id="page-l1"></div>
258
259
  <div class="ag-panel hidden" id="page-l2"></div>
259
260
  <div class="ag-panel hidden" id="page-l3"></div>
261
+ <div class="ag-panel hidden" id="page-agents"></div>
260
262
  <div class="ag-panel hidden" id="page-token"></div>
261
263
  <div class="ag-panel hidden" id="page-settings"></div>
262
264
  </div>
@@ -416,10 +418,11 @@ function closeModal() {
416
418
  async function refreshConfig() {
417
419
  state.config = await api('config');
418
420
  state.envOverrides = await api('env-overrides');
421
+ state.agentRules = await api('agents');
419
422
  renderCurrent();
420
423
  }
421
424
  function renderCurrent() {
422
- const pages = ['overview','search','l1','l2','l3','token','settings'];
425
+ const pages = ['overview','search','l1','l2','l3','agents','token','settings'];
423
426
  pages.forEach(p => {
424
427
  const panel = el('page-' + p);
425
428
  if (panel) panel.classList.toggle('hidden', p !== state.currentPage);
@@ -649,6 +652,94 @@ async function deleteRule(layer, cat, id) {
649
652
  } catch (e) { showToast('删除失败: ' + e.message, 'error'); }
650
653
  }
651
654
 
655
+ // ===== Agent 定制规则页 =====
656
+ const AGENT_CATEGORY_NAMES = {
657
+ builtin_safe_action_types: '安全操作白名单(跳 L2+L3)',
658
+ builtin_safe_skills: '信任内置 Skill(跳 L2+L3)',
659
+ extra_command_patterns: '专属危险命令正则(L1 追加)',
660
+ extra_injection_keywords: '专属注入关键词(L1 追加)',
661
+ extra_sensitive_paths: '专属敏感路径(L1 追加)',
662
+ };
663
+ function render_agents() {
664
+ const agents = (state.agentRules && state.agentRules.agents) || {};
665
+ const ids = Object.keys(agents);
666
+ if (!ids.length) {
667
+ el('page-agents').innerHTML = '<div class="ag-card"><div class="ag-empty">无 agent 定制规则</div></div>';
668
+ return;
669
+ }
670
+ let html = '';
671
+ for (const agentId of ids) {
672
+ const cats = agents[agentId] || {};
673
+ html += `<div class="ag-card"><div class="ag-section-title">Agent: ${escapeHtml(agentId)}</div>`;
674
+ const ordered = [
675
+ ...Object.keys(AGENT_CATEGORY_NAMES).filter(k => k in cats).map(k => [k, cats[k]]),
676
+ ...Object.entries(cats).filter(([k]) => !(k in AGENT_CATEGORY_NAMES)),
677
+ ];
678
+ for (const [catName, catData] of ordered) {
679
+ const rules = catData.rules || [];
680
+ const catEnabled = catData.category_enabled !== false;
681
+ const expanded = expandedCats.has(`agents-${agentId}-${catName}`);
682
+ const enCount = rules.filter(r => r.enabled).length;
683
+ html += `<div class="ag-cat-header" onclick="toggleExpand('agents-${agentId}-${catName}')">
684
+ <div class="ag-cat-name">${escapeHtml(AGENT_CATEGORY_NAMES[catName] || catName)} <span class="ag-cat-count">${enCount}/${rules.length} 启用</span></div>
685
+ <div class="ag-cat-controls"><div onclick="event.stopPropagation();toggleAgentCategory('${agentId}','${catName}')">${toggle(catEnabled, false, '')}</div><span class="ag-expand-icon ${expanded ? 'open' : ''}">▼</span></div>
686
+ </div>
687
+ <div class="ag-rule-list" id="list-agents-${agentId}-${catName}" style="display:${expanded ? '' : 'none'}">`;
688
+ if (!rules.length) {
689
+ html += '<div class="ag-empty">无规则</div>';
690
+ } else {
691
+ for (const r of rules) html += renderAgentRuleItem(agentId, catName, r);
692
+ }
693
+ html += `<div style="padding:8px 0"><button class="ag-btn ag-btn-ghost" onclick="showAddAgentRuleModal('${agentId}','${catName}')">+ 新增</button></div>`;
694
+ html += '</div>';
695
+ }
696
+ html += '</div>';
697
+ }
698
+ el('page-agents').innerHTML = html;
699
+ }
700
+ function renderAgentRuleItem(agentId, catName, r) {
701
+ const label = r.value || r.pattern || r.keyword || r.path || r.id || JSON.stringify(r).slice(0, 60);
702
+ const sub = r.reason || r.risk_level || r.lang || r.desc || r.deny_mode || '';
703
+ const removed = r.source_removed ? '<span class="ag-overridden-hint" title="源码已移除该规则">已移除</span>' : '';
704
+ const delBtn = r.builtin ? '' : `<button class="ag-btn ag-btn-ghost ag-btn-sm" onclick="deleteAgentRule('${agentId}','${catName}',${JSON.stringify(r).replace(/"/g,'&quot;')})">删除</button>`;
705
+ return `<div class="ag-rule-item">
706
+ <div class="ag-rule-name">${escapeHtml(label)} ${removed}${sub ? `<span class="ag-rule-sub">— ${escapeHtml(sub)}</span>` : ''}</div>
707
+ <div class="ag-rule-controls">${toggle(r.enabled, false, `toggleAgentRule('${agentId}','${catName}',${JSON.stringify(r).replace(/"/g,'&quot;')},${!r.enabled})`)}${delBtn}</div>
708
+ </div>`;
709
+ }
710
+ async function toggleAgentRule(agentId, cat, r, en) {
711
+ const fp = { value: r.value, pattern: r.pattern, keyword: r.keyword, path: r.path };
712
+ try {
713
+ state.agentRules = await apiPut('agents/rules/enabled', { agent_id: agentId, category: cat, ...fp, enabled: en });
714
+ render_agents();
715
+ } catch (e) { showToast(String(e), 'error'); }
716
+ }
717
+ async function toggleAgentCategory(agentId, cat) {
718
+ const cats = (state.agentRules.agents || {})[agentId] || {};
719
+ const en = cats[cat]?.category_enabled === false;
720
+ try {
721
+ state.agentRules = await apiPut('agents/categories/enabled', { agent_id: agentId, category: cat, enabled: en });
722
+ render_agents();
723
+ } catch (e) { showToast(String(e), 'error'); }
724
+ }
725
+ async function deleteAgentRule(agentId, cat, r) {
726
+ const fp = { value: r.value, pattern: r.pattern, keyword: r.keyword, path: r.path };
727
+ if (!confirm(`删除规则 ${r.value || r.pattern || r.keyword || r.path || ''}?`)) return;
728
+ try {
729
+ state.agentRules = await apiDelete('agents/rules', { agent_id: agentId, category: cat, ...fp });
730
+ render_agents();
731
+ } catch (e) { showToast(String(e), 'error'); }
732
+ }
733
+ function showAddAgentRuleModal(agentId, cat) {
734
+ const isValueCat = cat === 'builtin_safe_action_types' || cat === 'builtin_safe_skills';
735
+ const body = isValueCat
736
+ ? `<input class="ag-input" id="ar-value" placeholder="值(前缀尾随 - 或精确工具名)" style="width:100%">`
737
+ : `<input class="ag-input" id="ar-pattern" placeholder="正则/关键词/路径" style="width:100%;margin-bottom:8px">
738
+ <input class="ag-input" id="ar-risk_level" placeholder="risk_level (high/medium/...)" style="width:100%">`;
739
+ openModal(`新增 ${agentId} / ${cat} 规则`, body, true);
740
+ currentModalAction = { type: 'agent_rule', agentId, cat, isValueCat };
741
+ }
742
+
652
743
  // ===== L3 Skills 页 =====
653
744
  function render_l3() {
654
745
  const l3 = state.config.L3_skills || {};
@@ -1101,6 +1192,18 @@ async function submitModal() {
1101
1192
  content: document.getElementById('input-skill-content')?.value,
1102
1193
  });
1103
1194
  showToast('Skill 已添加');
1195
+ } else if (currentModalAction.type === 'agent_rule') {
1196
+ const { agentId, cat, isValueCat } = currentModalAction;
1197
+ const payload = { agent_id: agentId, category: cat };
1198
+ if (isValueCat) {
1199
+ payload.value = el('ar-value').value.trim();
1200
+ if (!payload.value) { showToast('值不能为空', 'error'); return; }
1201
+ } else {
1202
+ payload.pattern = el('ar-pattern').value.trim();
1203
+ payload.risk_level = el('ar-risk_level').value.trim() || undefined;
1204
+ }
1205
+ state.agentRules = await apiPost('agents/rules', payload);
1206
+ showToast('Agent 规则已添加');
1104
1207
  }
1105
1208
  renderCurrent();
1106
1209
  closeModal();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.10.4",
3
+ "version": "0.10.5",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",