aria-code 4.1.3__py3-none-any.whl

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.
Files changed (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
safety/permissions.py ADDED
@@ -0,0 +1,275 @@
1
+ """Unified permission and command-risk policy for Aria Code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shlex
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+ from typing import Any, Dict
9
+
10
+
11
+ SAFE_POLICIES = {"safe", "balanced", "full"}
12
+
13
+
14
+ class PermissionMode(str, Enum):
15
+ READ_ONLY = "read-only"
16
+ WORKSPACE_WRITE = "workspace-write"
17
+ FULL_ACCESS = "full-access"
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class PolicyDecision:
22
+ allowed: bool
23
+ normalized_command: str
24
+ policy: str
25
+ risk: str
26
+ reason: str = ""
27
+ requires_approval: bool = False
28
+ network: bool = False
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class PermissionDecision:
33
+ allowed: bool
34
+ requires_approval: bool
35
+ risk: str
36
+ reason: str
37
+ normalized_command: str = ""
38
+ network: bool = False
39
+
40
+
41
+ def normalize_command(command) -> str:
42
+ """Normalize common macOS command aliases used by models/users."""
43
+ if isinstance(command, list):
44
+ import shlex as _shlex
45
+ command = _shlex.join(str(c) for c in command)
46
+ raw = (command or "").strip()
47
+ if not raw:
48
+ return ""
49
+ try:
50
+ parts = shlex.split(raw)
51
+ except ValueError:
52
+ parts = []
53
+ if parts:
54
+ if parts[0] == "python":
55
+ parts[0] = "python3"
56
+ elif parts[0] == "pip":
57
+ parts[0] = "pip3"
58
+ return shlex.join(parts)
59
+ if raw.startswith("python ") and not raw.startswith("python3"):
60
+ return "python3" + raw[6:]
61
+ if raw == "python":
62
+ return "python3"
63
+ if raw.startswith("pip ") and not raw.startswith("pip3"):
64
+ return "pip3" + raw[3:]
65
+ if raw == "pip":
66
+ return "pip3"
67
+ return raw
68
+
69
+
70
+ def command_uses_network(command: str) -> bool:
71
+ stripped = command.lower().strip()
72
+ network_prefixes = (
73
+ "curl ", "wget ", "http ", "https ", "gh ", "git fetch", "git pull",
74
+ "git push", "pip3 install", "pip install", "npm install", "npm i ",
75
+ "pnpm install", "yarn install", "brew install",
76
+ )
77
+ return any(stripped.startswith(prefix) for prefix in network_prefixes)
78
+
79
+
80
+ def is_verification_command(command: str) -> bool:
81
+ stripped = command.lower().strip()
82
+ prefixes = (
83
+ "python3 -m py_compile",
84
+ "python -m py_compile",
85
+ "python3 -m pytest",
86
+ "python -m pytest",
87
+ "pytest",
88
+ "npm test",
89
+ "npm run test",
90
+ "npm run build",
91
+ "npx tsc --noemit",
92
+ "npx tsc --noEmit".lower(),
93
+ "tsc --noemit",
94
+ "go test",
95
+ "cargo test",
96
+ "mypy",
97
+ "ruff check",
98
+ )
99
+ return any(stripped.startswith(prefix) for prefix in prefixes)
100
+
101
+
102
+ def classify_command_risk(command) -> str:
103
+ """Classify command risk into low/medium/high."""
104
+ if isinstance(command, list):
105
+ import shlex as _shlex
106
+ command = _shlex.join(str(c) for c in command)
107
+ normalized = f" {str(command).lower().strip()} "
108
+ stripped = normalized.strip()
109
+
110
+ high_risk_patterns = (
111
+ " rm ", " rm -", "chmod ", "chown ", "mkfs", "dd if=", "docker ", "kubectl ",
112
+ "shutdown", "reboot", "systemctl ", "launchctl ", "passwd", "sudo ",
113
+ "git push", "git reset --hard", "git checkout --", "mv ",
114
+ "> /dev/", ":(){ :", "fork bomb",
115
+ )
116
+ low_risk_prefixes = (
117
+ "ls", "pwd", "echo", "cat ", "head ", "tail ", "rg ", "find ", "git status",
118
+ "git diff", "git log", "which ", "whoami", "date", "uname ", "env",
119
+ "python3 --version", "node --version", "npm --version",
120
+ )
121
+ medium_risk_prefixes = (
122
+ "pip ", "pip3 ", "npm install", "npm i ", "npm run ", "python ", "python3 ",
123
+ "pytest", "make ", "go test", "cargo test", "git commit", "git pull", "git merge",
124
+ "gh ", "curl ", "wget ",
125
+ )
126
+
127
+ if any(pattern in normalized for pattern in high_risk_patterns):
128
+ return "high"
129
+ if stripped.startswith(low_risk_prefixes):
130
+ return "low"
131
+ if stripped.startswith(medium_risk_prefixes) or is_verification_command(stripped):
132
+ return "medium"
133
+ return "medium"
134
+
135
+
136
+ class PermissionService:
137
+ """Central policy for tools and shell-command execution."""
138
+
139
+ READ_TOOLS = {"read_file", "list_files", "search_code", "project_context", "git_status", "git_diff"}
140
+ WRITE_TOOLS = {"write_file", "edit_file", "apply_change", "apply_patch", "reject_change"}
141
+
142
+ def __init__(
143
+ self,
144
+ mode: PermissionMode | str = PermissionMode.WORKSPACE_WRITE,
145
+ command_policy: str = "safe",
146
+ network_enabled: bool = True,
147
+ ) -> None:
148
+ try:
149
+ self.mode = PermissionMode(mode)
150
+ except ValueError:
151
+ self.mode = PermissionMode.WORKSPACE_WRITE
152
+ self.command_policy = command_policy if command_policy in SAFE_POLICIES else "safe"
153
+ self.network_enabled = network_enabled
154
+
155
+ def evaluate_tool(self, tool_name: str, params: Dict[str, Any] | None = None) -> PermissionDecision:
156
+ name = (tool_name or "").strip()
157
+ if name in self.READ_TOOLS:
158
+ return PermissionDecision(True, False, "low", "Read-only tool allowed.")
159
+ if name in self.WRITE_TOOLS:
160
+ if self.mode == PermissionMode.READ_ONLY:
161
+ return PermissionDecision(False, False, "medium", "Writes are blocked in read-only mode.")
162
+ return PermissionDecision(
163
+ True,
164
+ self.mode != PermissionMode.FULL_ACCESS,
165
+ "medium",
166
+ "Write requires review or explicit approval before disk mutation.",
167
+ )
168
+ if name == "run_command":
169
+ return self.evaluate_command(str((params or {}).get("command", "")))
170
+ return PermissionDecision(False, False, "unknown", f"Unknown tool '{name}' is blocked.")
171
+
172
+ def evaluate_command(self, command: str, policy: str | None = None) -> PermissionDecision:
173
+ decision = evaluate_command_policy(
174
+ command,
175
+ policy or self.command_policy,
176
+ mode=self.mode.value,
177
+ network_enabled=self.network_enabled,
178
+ )
179
+ return PermissionDecision(
180
+ allowed=decision.allowed,
181
+ requires_approval=decision.requires_approval,
182
+ risk=decision.risk,
183
+ reason=decision.reason or "Command allowed.",
184
+ normalized_command=decision.normalized_command,
185
+ network=decision.network,
186
+ )
187
+
188
+
189
+ def evaluate_command_policy(
190
+ command: str,
191
+ policy: str = "safe",
192
+ *,
193
+ mode: str = PermissionMode.WORKSPACE_WRITE.value,
194
+ network_enabled: bool = True,
195
+ ) -> PolicyDecision:
196
+ """Return policy decision for command execution."""
197
+ normalized = normalize_command(command)
198
+ selected_policy = (policy or "safe").strip().lower()
199
+ if selected_policy not in SAFE_POLICIES:
200
+ selected_policy = "safe"
201
+ try:
202
+ selected_mode = PermissionMode(mode)
203
+ except ValueError:
204
+ selected_mode = PermissionMode.WORKSPACE_WRITE
205
+
206
+ risk = classify_command_risk(normalized)
207
+ network = command_uses_network(normalized)
208
+ if network and not network_enabled:
209
+ return PolicyDecision(
210
+ allowed=False,
211
+ normalized_command=normalized,
212
+ policy=selected_policy,
213
+ risk=risk,
214
+ reason=f"Network command blocked by policy: {normalized}",
215
+ requires_approval=False,
216
+ network=True,
217
+ )
218
+ if selected_mode == PermissionMode.READ_ONLY and risk != "low":
219
+ return PolicyDecision(
220
+ allowed=False,
221
+ normalized_command=normalized,
222
+ policy=selected_policy,
223
+ risk=risk,
224
+ reason=f"Command blocked by read-only mode (risk={risk}): {normalized}",
225
+ requires_approval=False,
226
+ network=network,
227
+ )
228
+ if risk == "high" and selected_policy != "full":
229
+ return PolicyDecision(
230
+ allowed=False,
231
+ normalized_command=normalized,
232
+ policy=selected_policy,
233
+ risk=risk,
234
+ reason=(
235
+ f"Command blocked by policy '{selected_policy}' (risk={risk}): {normalized}. "
236
+ "Use /config set command_policy=full only if you understand the risk."
237
+ ),
238
+ requires_approval=selected_policy == "balanced",
239
+ network=network,
240
+ )
241
+ if selected_policy == "safe" and risk != "low":
242
+ extra = ""
243
+ norm_low = normalized.lower()
244
+ if "pip3" in norm_low or "pip " in norm_low or norm_low.startswith("pip"):
245
+ extra = (
246
+ "\n\n💡 依赖安装提示:运行 `/config set command_policy=balanced` 后重试,"
247
+ "或手动在终端执行该命令。不要重试相同命令!"
248
+ )
249
+ elif "python" in norm_low and ("~/" in normalized or "/Desktop/" in normalized):
250
+ extra = (
251
+ "\n\n💡 脚本执行提示:运行 `/config set command_policy=balanced` 后重试。"
252
+ "不要重试相同命令!"
253
+ )
254
+ return PolicyDecision(
255
+ allowed=False,
256
+ normalized_command=normalized,
257
+ policy=selected_policy,
258
+ risk=risk,
259
+ reason=(
260
+ f"Command blocked by policy '{selected_policy}' (risk={risk}): {normalized}. "
261
+ "Use /config set command_policy=balanced or /run --dry-run <cmd> first."
262
+ + extra
263
+ ),
264
+ requires_approval=True,
265
+ network=network,
266
+ )
267
+ return PolicyDecision(
268
+ allowed=True,
269
+ normalized_command=normalized,
270
+ policy=selected_policy,
271
+ risk=risk,
272
+ reason="",
273
+ requires_approval=False,
274
+ network=network,
275
+ )