@kenhkl/agent-moss 0.15.4 → 0.15.7

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.
@@ -1,105 +0,0 @@
1
- """三方 agent 定制化注册中心
2
-
3
- 按 `agent_id` 加载该 agent 的专属规则集(JSON 单一事实源)。
4
- 规则文件位置:`agent_moss/agents/<agent_id>/rules.json`
5
-
6
- Python 与 TypeScript 两端共享同一份 rules.json:
7
- - py: agent_moss/agents/__init__.py:get_agent_extensions
8
- - ts: ts/src/agents/index.ts:getAgentExtensions
9
-
10
- 使用:
11
- from agent_moss.agents import get_agent_extensions
12
- ext = get_agent_extensions("xiaoo")
13
- if ext:
14
- # 把 ext.extra_command_patterns 追加到通用规则之后
15
- ...
16
-
17
- 无 agent_id 或 rules.json 不存在时返回 None(通用模式)。
18
- """
19
-
20
- from pathlib import Path
21
- import threading
22
- import logging
23
-
24
- from .base import AgentExtensions, InjectionKeyword, CommandPattern, SensitivePath
25
-
26
- logger = logging.getLogger(__name__)
27
-
28
- _AGENTS_DIR = Path(__file__).parent
29
- _cache: dict[str, AgentExtensions | None] = {}
30
- _lock = threading.Lock()
31
-
32
-
33
- def get_agent_extensions(agent_id: str) -> AgentExtensions | None:
34
- """按 agent_id 加载该 agent 的专属规则集。
35
-
36
- Args:
37
- agent_id: 调用方 agent 标识,如 "xiaoo" / "opencode" / "opendesk"。
38
- 空字符串返回 None(通用模式)。
39
-
40
- Returns:
41
- AgentExtensions 实例,或 None(无 agent 专属规则)。
42
- """
43
- if not agent_id:
44
- return None
45
-
46
- # 双重检查锁定,避免并发重复 IO
47
- if agent_id in _cache:
48
- return _cache[agent_id]
49
-
50
- with _lock:
51
- if agent_id in _cache:
52
- return _cache[agent_id]
53
-
54
- # 统一存储:优先读 runtime_config 的 agent_rules 分区(用户可在 console
55
- # 查看/编辑/禁用/新增,享受与通用规则一致的合并升级)。回退 rules.json
56
- # (无 runtime_config 时,如直接起服务未 seed、或旧部署)。
57
- ext = None
58
- try:
59
- from ..infra.runtime_config import has_runtime_config, load_runtime_config, get_enabled_agent_extensions
60
- if has_runtime_config():
61
- ext = get_enabled_agent_extensions(load_runtime_config(), agent_id)
62
- except Exception:
63
- pass # runtime_config 不可用时回退 rules.json
64
-
65
- if ext is None:
66
- rules_file = _AGENTS_DIR / agent_id / "rules.json"
67
- if not rules_file.exists():
68
- logger.debug("agent %s 无 rules.json,走通用模式", agent_id)
69
- _cache[agent_id] = None
70
- return None
71
- try:
72
- ext = AgentExtensions.from_json(rules_file)
73
- except Exception as e:
74
- logger.error("加载 agent %s rules.json 失败: %s", agent_id, e)
75
- _cache[agent_id] = None
76
- return None
77
-
78
- _cache[agent_id] = ext
79
- logger.info(
80
- "加载 agent %s 定制规则: %d cmd patterns, %d injection keywords, "
81
- "%d sensitive paths, %d builtin safe skills, %d builtin safe action types",
82
- agent_id,
83
- len(ext.extra_command_patterns),
84
- len(ext.extra_injection_keywords),
85
- len(ext.extra_sensitive_paths),
86
- len(ext.builtin_safe_skills),
87
- len(ext.builtin_safe_action_types),
88
- )
89
- return ext
90
-
91
-
92
- def clear_agent_extensions_cache() -> None:
93
- """清空 agent 扩展缓存(用于测试或 rules.json 热加载)"""
94
- with _lock:
95
- _cache.clear()
96
-
97
-
98
- __all__ = [
99
- "AgentExtensions",
100
- "InjectionKeyword",
101
- "CommandPattern",
102
- "SensitivePath",
103
- "get_agent_extensions",
104
- "clear_agent_extensions_cache",
105
- ]
@@ -1,158 +0,0 @@
1
- """三方 agent 定制化扩展 — 数据载体
2
-
3
- `AgentExtensions` 描述某个宿主 agent(xiaoO / opencode / opendesk)在通用安全规则
4
- 之上需要追加的专属规则。规则以 JSON 单一事实源存储(agent_moss/agents/<id>/rules.json),
5
- Python 与 TypeScript 两端共享同一份文件。
6
-
7
- 设计原则:
8
- - 所有字段以 `extra_` 为前缀,表示"追加到通用规则之后",不覆盖通用规则。
9
- - 通用规则先匹配;命中即短路,避免重复检测。
10
- - agent 专属规则只做"通用规则没覆盖的宿主 agent 特有风险"。
11
-
12
- JSON schema(agent_moss/agents/<id>/rules.json):
13
- {
14
- "agent_id": "xiaoo",
15
- "description": "xiaoO 宿主 agent 专属安全规则",
16
- "extra_command_patterns": [CommandPattern...],
17
- "extra_injection_keywords": [InjectionKeyword...],
18
- "extra_sensitive_paths": [SensitivePath...],
19
- "builtin_safe_skills": ["xiaoo-guardian"],
20
- "builtin_safe_action_types": ["memory-", "todomgr-", "mailbox-getEmailList"],
21
- "extra_skills": []
22
- }
23
- """
24
-
25
- from dataclasses import dataclass, field
26
- from pathlib import Path
27
- import json
28
- import logging
29
-
30
- from agent_moss.profiles.base import CommandPattern, SensitivePath
31
-
32
- logger = logging.getLogger(__name__)
33
-
34
-
35
- @dataclass
36
- class InjectionKeyword:
37
- """Prompt 注入关键词条目"""
38
- keyword: str
39
- risk_level: str # low / medium / high / critical
40
- lang: str # en / zh
41
-
42
-
43
- @dataclass
44
- class AgentExtensions:
45
- """三方 agent 定制化扩展
46
-
47
- 由 `agent_moss.agents.get_agent_extensions(agent_id)` 按需加载。
48
- 缺失 rules.json 时返回 None(通用模式)。
49
- """
50
- agent_id: str
51
- description: str = ""
52
- extra_command_patterns: list[CommandPattern] = field(default_factory=list)
53
- extra_injection_keywords: list[InjectionKeyword] = field(default_factory=list)
54
- extra_sensitive_paths: list[SensitivePath] = field(default_factory=list)
55
- builtin_safe_skills: list[str] = field(default_factory=list)
56
- # agent 专属"完全安全操作"白名单(精确名或前缀,前缀以 '-' 结尾)→ 跳过 L2+L3。
57
- # 对齐 TS ts/src/agents/types.ts。前缀匹配(如 "memory-")走 startswith,精确名走 ==。
58
- builtin_safe_action_types: list[str] = field(default_factory=list)
59
- extra_skills: list[dict] = field(default_factory=list)
60
-
61
- @classmethod
62
- def merge(cls, *exts: "AgentExtensions | None") -> "AgentExtensions | None":
63
- """合并多个 AgentExtensions(如 agent 规则 + 场景规则),追加式拼接各 extra_* 列表。
64
-
65
- 全部为 None 时返回 None(通用模式)。非 None 时以第一个非空实例为骨架,
66
- 其余实例的 extra_* / builtin_safe_* 列表依次追加在后(不覆盖)。
67
- """
68
- non_null = [e for e in exts if e is not None]
69
- if not non_null:
70
- return None
71
- base = non_null[0]
72
- return cls(
73
- agent_id=base.agent_id,
74
- description=base.description,
75
- extra_command_patterns=[
76
- p for e in non_null for p in e.extra_command_patterns
77
- ],
78
- extra_injection_keywords=[
79
- k for e in non_null for k in e.extra_injection_keywords
80
- ],
81
- extra_sensitive_paths=[
82
- sp for e in non_null for sp in e.extra_sensitive_paths
83
- ],
84
- builtin_safe_skills=[
85
- s for e in non_null for s in e.builtin_safe_skills
86
- ],
87
- builtin_safe_action_types=[
88
- t for e in non_null for t in e.builtin_safe_action_types
89
- ],
90
- extra_skills=[
91
- sk for e in non_null for sk in e.extra_skills
92
- ],
93
- )
94
-
95
- @classmethod
96
- def from_json(cls, rules_file: Path, id_key: str = "agent_id") -> "AgentExtensions":
97
- """从 rules.json 加载 AgentExtensions。
98
-
99
- Args:
100
- rules_file: agent_moss/agents/<id>/rules.json 的绝对路径(场景规则传
101
- agent_moss/scenarios/<id>/rules.json,id_key="scenario_id")
102
- id_key: JSON 中的 id 字段名。agent 规则默认 "agent_id";
103
- 场景规则(agent_moss/scenarios/<id>/rules.json)用 "scenario_id"。
104
-
105
- Returns:
106
- AgentExtensions 实例
107
-
108
- Raises:
109
- ValueError: JSON 结构不合法
110
- """
111
- with open(rules_file, "r", encoding="utf-8") as f:
112
- data = json.load(f)
113
-
114
- agent_id = data.get(id_key, "")
115
- if not agent_id:
116
- raise ValueError(f"rules.json 缺少 {id_key} 字段: {rules_file}")
117
-
118
- # extra_command_patterns: dict → CommandPattern
119
- extra_cmd_patterns: list[CommandPattern] = []
120
- for p in data.get("extra_command_patterns", []):
121
- extra_cmd_patterns.append(CommandPattern(
122
- pattern=p["pattern"],
123
- risk_level=p["risk_level"],
124
- risk_type=p["risk_type"],
125
- reason=p["reason"],
126
- ))
127
-
128
- # extra_injection_keywords: dict → InjectionKeyword
129
- extra_inj_keywords: list[InjectionKeyword] = []
130
- for k in data.get("extra_injection_keywords", []):
131
- extra_inj_keywords.append(InjectionKeyword(
132
- keyword=k["keyword"],
133
- risk_level=k["risk_level"],
134
- lang=k.get("lang", "en"),
135
- ))
136
-
137
- # extra_sensitive_paths: dict → SensitivePath
138
- extra_sensitive_paths: list[SensitivePath] = []
139
- for sp in data.get("extra_sensitive_paths", []):
140
- extra_sensitive_paths.append(SensitivePath(
141
- path=sp["path"],
142
- risk_level=sp["risk_level"],
143
- desc=sp["desc"],
144
- credential=sp.get("credential", False),
145
- read_only=sp.get("read_only", False),
146
- deny_mode=sp.get("deny_mode", ""),
147
- ))
148
-
149
- return cls(
150
- agent_id=agent_id,
151
- description=data.get("description", ""),
152
- extra_command_patterns=extra_cmd_patterns,
153
- extra_injection_keywords=extra_inj_keywords,
154
- extra_sensitive_paths=extra_sensitive_paths,
155
- builtin_safe_skills=data.get("builtin_safe_skills", []),
156
- builtin_safe_action_types=data.get("builtin_safe_action_types", []),
157
- extra_skills=data.get("extra_skills", []),
158
- )