aria-code 0.167.0__py3-none-win_amd64.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 (601) hide show
  1. aria_bots/__init__.py +7 -0
  2. aria_bots/channels/README.md +97 -0
  3. aria_bots/channels/__init__.py +1 -0
  4. aria_bots/channels/approvals.py +263 -0
  5. aria_bots/channels/conversation.py +280 -0
  6. aria_bots/channels/digest.py +186 -0
  7. aria_bots/channels/registry.py +95 -0
  8. aria_bots/engine.py +144 -0
  9. aria_bots/feishu_bot.py +1385 -0
  10. aria_bots/logistics/__init__.py +1 -0
  11. aria_bots/logistics/carriers.py +253 -0
  12. aria_bots/logistics/freight.py +154 -0
  13. aria_bots/logistics/inventory.py +242 -0
  14. aria_bots/logistics/mcp_server.py +156 -0
  15. aria_bots/logistics/records.py +119 -0
  16. aria_bots/logistics/tenancy.py +152 -0
  17. aria_bots/market.py +91 -0
  18. aria_bots/relay_client.py +326 -0
  19. aria_bots/telegram_bot.py +299 -0
  20. aria_code/__init__.py +111 -0
  21. aria_code/_bin/aria-native.exe +0 -0
  22. aria_code/_bin/aria-runtime.exe +0 -0
  23. aria_code/_version.py +20 -0
  24. aria_code/account_memory.py +296 -0
  25. aria_code/account_session.py +166 -0
  26. aria_code/adk_apps/__init__.py +1 -0
  27. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  28. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  29. aria_code/adk_apps/aria_research/__init__.py +1 -0
  30. aria_code/adk_apps/aria_research/agent.py +31 -0
  31. aria_code/agents/__init__.py +49 -0
  32. aria_code/agents/base.py +329 -0
  33. aria_code/agents/code_review.py +200 -0
  34. aria_code/agents/deep/__init__.py +37 -0
  35. aria_code/agents/deep/calibration_loop.py +144 -0
  36. aria_code/agents/deep/critic.py +125 -0
  37. aria_code/agents/deep/deepen.py +193 -0
  38. aria_code/agents/deep/models.py +149 -0
  39. aria_code/agents/deep/pipeline.py +164 -0
  40. aria_code/agents/deep/quant_fusion.py +192 -0
  41. aria_code/agents/deep/themes.py +95 -0
  42. aria_code/agents/deep/tiers.py +106 -0
  43. aria_code/agents/engineering/__init__.py +14 -0
  44. aria_code/agents/engineering/coder.py +222 -0
  45. aria_code/agents/engineering/tester.py +145 -0
  46. aria_code/agents/financial/__init__.py +21 -0
  47. aria_code/agents/financial/cashflow_burn.py +119 -0
  48. aria_code/agents/financial/catalyst.py +279 -0
  49. aria_code/agents/financial/corporate_finance.py +175 -0
  50. aria_code/agents/financial/debate.py +224 -0
  51. aria_code/agents/financial/earnings.py +315 -0
  52. aria_code/agents/financial/fundamental.py +178 -0
  53. aria_code/agents/financial/macro.py +111 -0
  54. aria_code/agents/financial/news.py +231 -0
  55. aria_code/agents/financial/risk.py +146 -0
  56. aria_code/agents/financial/sector.py +279 -0
  57. aria_code/agents/financial/strategist.py +121 -0
  58. aria_code/agents/financial/stripe_revenue.py +127 -0
  59. aria_code/agents/financial/synthesis.py +274 -0
  60. aria_code/agents/financial/technical.py +277 -0
  61. aria_code/agents/orchestrator.py +44 -0
  62. aria_code/agents/portfolio_agent.py +392 -0
  63. aria_code/agents/realty/__init__.py +62 -0
  64. aria_code/agents/realty/asset_diagnosis.py +152 -0
  65. aria_code/agents/realty/business_match.py +166 -0
  66. aria_code/agents/realty/cashflow_verify.py +209 -0
  67. aria_code/agents/realty/contract_rules.py +210 -0
  68. aria_code/agents/realty/energy_anomaly.py +189 -0
  69. aria_code/agents/realty/exit_settlement.py +208 -0
  70. aria_code/agents/realty/fulfillment_risk.py +206 -0
  71. aria_code/agents/realty/ops_optimize.py +160 -0
  72. aria_code/agents/realty/revenue_share.py +215 -0
  73. aria_code/agents/registry.py +168 -0
  74. aria_code/agents/signal_scheme.py +111 -0
  75. aria_code/agents/sports/__init__.py +0 -0
  76. aria_code/agents/sports/football_agent.py +197 -0
  77. aria_code/agents/supervisor.py +113 -0
  78. aria_code/agents/team.py +500 -0
  79. aria_code/agents/video/__init__.py +16 -0
  80. aria_code/agents/video/director.py +376 -0
  81. aria_code/agents/video/qa_agent.py +143 -0
  82. aria_code/agents/video/script_doctor.py +112 -0
  83. aria_code/agents/warehouse/__init__.py +25 -0
  84. aria_code/agents/warehouse/contracts.py +26 -0
  85. aria_code/agents/warehouse/dashboard.py +155 -0
  86. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  87. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  88. aria_code/agents/warehouse/inventory_health.py +24 -0
  89. aria_code/agents/warehouse/local_run.py +71 -0
  90. aria_code/agents/warehouse/local_snapshot.py +132 -0
  91. aria_code/agents/warehouse/logistics_cost.py +84 -0
  92. aria_code/agents/warehouse/logistics_sync.py +21 -0
  93. aria_code/agents/warehouse/navigation.schema.json +110 -0
  94. aria_code/agents/warehouse/task_agent.py +172 -0
  95. aria_code/agents/warehouse/workflow.py +31 -0
  96. aria_code/aliyun_data_client.py +4 -0
  97. aria_code/apps/README.md +12 -0
  98. aria_code/apps/__init__.py +2 -0
  99. aria_code/apps/channels/README.md +9 -0
  100. aria_code/apps/channels/__init__.py +17 -0
  101. aria_code/apps/channels/approvals.py +11 -0
  102. aria_code/apps/channels/conversation.py +11 -0
  103. aria_code/apps/channels/digest.py +11 -0
  104. aria_code/apps/channels/intake.py +92 -0
  105. aria_code/apps/channels/registry.py +11 -0
  106. aria_code/apps/channels/tradingview.py +94 -0
  107. aria_code/apps/cli/README.md +13 -0
  108. aria_code/apps/cli/__init__.py +2 -0
  109. aria_code/apps/cli/_optional.py +120 -0
  110. aria_code/apps/cli/app_server.py +656 -0
  111. aria_code/apps/cli/auto_update.py +125 -0
  112. aria_code/apps/cli/bootstrap.py +206 -0
  113. aria_code/apps/cli/broker_render.py +206 -0
  114. aria_code/apps/cli/chat_turn.py +1327 -0
  115. aria_code/apps/cli/cloud_models.py +231 -0
  116. aria_code/apps/cli/codegen_paths.py +29 -0
  117. aria_code/apps/cli/commands/__init__.py +34 -0
  118. aria_code/apps/cli/commands/_business_render.py +78 -0
  119. aria_code/apps/cli/commands/_ui.py +71 -0
  120. aria_code/apps/cli/commands/analysis_cmds.py +378 -0
  121. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  122. aria_code/apps/cli/commands/auth_cmds.py +215 -0
  123. aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
  124. aria_code/apps/cli/commands/broker_cmds.py +1236 -0
  125. aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
  126. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  127. aria_code/apps/cli/commands/catalog.py +189 -0
  128. aria_code/apps/cli/commands/core_cmds.py +2748 -0
  129. aria_code/apps/cli/commands/data_cmds.py +521 -0
  130. aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
  131. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  132. aria_code/apps/cli/commands/engineering_cmds.py +399 -0
  133. aria_code/apps/cli/commands/file_cmds.py +312 -0
  134. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  135. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  136. aria_code/apps/cli/commands/market.py +544 -0
  137. aria_code/apps/cli/commands/market_cmds.py +1419 -0
  138. aria_code/apps/cli/commands/market_context.py +432 -0
  139. aria_code/apps/cli/commands/market_render.py +7 -0
  140. aria_code/apps/cli/commands/model_cmds.py +2374 -0
  141. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  142. aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
  143. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  144. aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
  145. aria_code/apps/cli/commands/report.py +476 -0
  146. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  147. aria_code/apps/cli/commands/session_cmds.py +236 -0
  148. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  149. aria_code/apps/cli/commands/team.py +723 -0
  150. aria_code/apps/cli/commands/team_render.py +8 -0
  151. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  152. aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
  153. aria_code/apps/cli/commands/workflow_cmds.py +629 -0
  154. aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
  155. aria_code/apps/cli/config_paths.py +73 -0
  156. aria_code/apps/cli/config_store.py +158 -0
  157. aria_code/apps/cli/constants.py +88 -0
  158. aria_code/apps/cli/context.py +34 -0
  159. aria_code/apps/cli/conversation_policy.py +53 -0
  160. aria_code/apps/cli/deterministic.py +200 -0
  161. aria_code/apps/cli/direct.py +48 -0
  162. aria_code/apps/cli/earnings_view.py +172 -0
  163. aria_code/apps/cli/endpoint_migration.py +64 -0
  164. aria_code/apps/cli/exec_events.py +255 -0
  165. aria_code/apps/cli/file_references.py +155 -0
  166. aria_code/apps/cli/finance_service_catalog.py +111 -0
  167. aria_code/apps/cli/football_reports.py +231 -0
  168. aria_code/apps/cli/github_app_auth.py +135 -0
  169. aria_code/apps/cli/google_login.py +239 -0
  170. aria_code/apps/cli/handlers/__init__.py +11 -0
  171. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  172. aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
  173. aria_code/apps/cli/handlers/market_handlers.py +2570 -0
  174. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  175. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  176. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  177. aria_code/apps/cli/headless.py +352 -0
  178. aria_code/apps/cli/helpers.py +326 -0
  179. aria_code/apps/cli/hooks.py +180 -0
  180. aria_code/apps/cli/i18n.py +310 -0
  181. aria_code/apps/cli/intent.py +136 -0
  182. aria_code/apps/cli/intent_router.py +305 -0
  183. aria_code/apps/cli/intent_shadow.py +72 -0
  184. aria_code/apps/cli/intent_signals.py +347 -0
  185. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  186. aria_code/apps/cli/local_tool_schemas.py +550 -0
  187. aria_code/apps/cli/main.py +39 -0
  188. aria_code/apps/cli/market_hours.py +100 -0
  189. aria_code/apps/cli/market_metadata.py +135 -0
  190. aria_code/apps/cli/market_universe.py +487 -0
  191. aria_code/apps/cli/message_processing.py +310 -0
  192. aria_code/apps/cli/mixin_binding.py +32 -0
  193. aria_code/apps/cli/model_catalog.py +652 -0
  194. aria_code/apps/cli/model_probe.py +169 -0
  195. aria_code/apps/cli/model_retirement.py +65 -0
  196. aria_code/apps/cli/native_branding.py +12 -0
  197. aria_code/apps/cli/native_bridge.py +118 -0
  198. aria_code/apps/cli/native_entry.py +58 -0
  199. aria_code/apps/cli/pdf_report.py +977 -0
  200. aria_code/apps/cli/plan_mode.py +139 -0
  201. aria_code/apps/cli/plotly_html.py +15 -0
  202. aria_code/apps/cli/prediction_feedback.py +202 -0
  203. aria_code/apps/cli/preflight.py +502 -0
  204. aria_code/apps/cli/project_aria.py +60 -0
  205. aria_code/apps/cli/prompt_assembly.py +78 -0
  206. aria_code/apps/cli/prompts/__init__.py +0 -0
  207. aria_code/apps/cli/prompts/coding.py +71 -0
  208. aria_code/apps/cli/prompts/select.py +193 -0
  209. aria_code/apps/cli/prompts/system_prompts.py +562 -0
  210. aria_code/apps/cli/prompts/ui.py +436 -0
  211. aria_code/apps/cli/provider_endpoints.py +237 -0
  212. aria_code/apps/cli/provider_keys.py +73 -0
  213. aria_code/apps/cli/providers/__init__.py +1 -0
  214. aria_code/apps/cli/providers/base.py +775 -0
  215. aria_code/apps/cli/providers/chat_routing.py +139 -0
  216. aria_code/apps/cli/providers/collaboration.py +98 -0
  217. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  218. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  219. aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
  220. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  221. aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
  222. aria_code/apps/cli/providers/runtime_bridge.py +586 -0
  223. aria_code/apps/cli/providers/vertexai_stream.py +592 -0
  224. aria_code/apps/cli/release_download.py +159 -0
  225. aria_code/apps/cli/review_runner.py +111 -0
  226. aria_code/apps/cli/runtime_consumer.py +769 -0
  227. aria_code/apps/cli/sandbox.py +32 -0
  228. aria_code/apps/cli/session_export.py +120 -0
  229. aria_code/apps/cli/session_jsonl.py +208 -0
  230. aria_code/apps/cli/session_refresh.py +123 -0
  231. aria_code/apps/cli/session_store.py +125 -0
  232. aria_code/apps/cli/skills_catalog.py +273 -0
  233. aria_code/apps/cli/task_isolation.py +211 -0
  234. aria_code/apps/cli/terminal_chrome.py +412 -0
  235. aria_code/apps/cli/todo_tracker.py +190 -0
  236. aria_code/apps/cli/tool_executor.py +932 -0
  237. aria_code/apps/cli/tool_registry.py +34 -0
  238. aria_code/apps/cli/tool_scope.py +113 -0
  239. aria_code/apps/cli/tools/__init__.py +40 -0
  240. aria_code/apps/cli/tools/context.py +46 -0
  241. aria_code/apps/cli/tools/file_tools.py +181 -0
  242. aria_code/apps/cli/tools/market_tools.py +626 -0
  243. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  244. aria_code/apps/cli/tools/system_tools.py +656 -0
  245. aria_code/apps/cli/tools/write_tools.py +961 -0
  246. aria_code/apps/cli/tradingview_bridge.py +529 -0
  247. aria_code/apps/cli/transactions.py +269 -0
  248. aria_code/apps/cli/turn_planning.py +137 -0
  249. aria_code/apps/cli/update_check.py +257 -0
  250. aria_code/apps/cli/updater.py +373 -0
  251. aria_code/apps/cli/utils/__init__.py +0 -0
  252. aria_code/apps/cli/utils/market_detect.py +1663 -0
  253. aria_code/apps/cli/web_login.py +136 -0
  254. aria_code/apps/cli/workflow_runner.py +127 -0
  255. aria_code/apps/cli/workspace_route.py +121 -0
  256. aria_code/apps/daemon/README.md +14 -0
  257. aria_code/apps/vscode/README.md +115 -0
  258. aria_code/apps/vscode/package.json +70 -0
  259. aria_code/aria_cli.py +5244 -0
  260. aria_code/aria_daemon.py +1372 -0
  261. aria_code/aria_feishu_bot.py +16 -0
  262. aria_code/aria_mcp_server.py +61 -0
  263. aria_code/aria_relay_client.py +4 -0
  264. aria_code/aria_telegram_bot.py +11 -0
  265. aria_code/ariarc.py +385 -0
  266. aria_code/artifacts.py +581 -0
  267. aria_code/backtest_engine.py +4 -0
  268. aria_code/backtest_report.py +4 -0
  269. aria_code/brokers/__init__.py +78 -0
  270. aria_code/brokers/automation.py +193 -0
  271. aria_code/brokers/base.py +231 -0
  272. aria_code/brokers/capabilities.py +264 -0
  273. aria_code/brokers/cn/__init__.py +10 -0
  274. aria_code/brokers/cn/easytrader_broker.py +193 -0
  275. aria_code/brokers/cn/futu_broker.py +194 -0
  276. aria_code/brokers/cn/longbridge_broker.py +190 -0
  277. aria_code/brokers/cn/tiger_broker.py +196 -0
  278. aria_code/brokers/cn/xtquant_broker.py +225 -0
  279. aria_code/brokers/config.py +414 -0
  280. aria_code/brokers/intl/__init__.py +5 -0
  281. aria_code/brokers/intl/alpaca_broker.py +183 -0
  282. aria_code/brokers/intl/ibkr_broker.py +215 -0
  283. aria_code/brokers/intl/trading212_broker.py +183 -0
  284. aria_code/brokers/intl/webull_broker.py +156 -0
  285. aria_code/brokers/paper_broker.py +266 -0
  286. aria_code/brokers/planning.py +296 -0
  287. aria_code/brokers/registry.py +213 -0
  288. aria_code/brokers/trading.py +267 -0
  289. aria_code/canva_client.py +4 -0
  290. aria_code/change_store.py +127 -0
  291. aria_code/clients/__init__.py +0 -0
  292. aria_code/clients/aliyun_data_client.py +660 -0
  293. aria_code/clients/aria_relay_client.py +16 -0
  294. aria_code/clients/canva_client.py +284 -0
  295. aria_code/clients/figma_client.py +100 -0
  296. aria_code/clients/football_data_client.py +1671 -0
  297. aria_code/clients/kling_video_client.py +182 -0
  298. aria_code/clients/market_data_client.py +1985 -0
  299. aria_code/clients/openai_image_client.py +242 -0
  300. aria_code/clients/runway_video_client.py +157 -0
  301. aria_code/clients/warehouse_erp_client.py +105 -0
  302. aria_code/cloud_memory.py +190 -0
  303. aria_code/command_safety.py +19 -0
  304. aria_code/computer_use_server.py +276 -0
  305. aria_code/computer_use_tools.py +4 -0
  306. aria_code/dashboard_generator.py +579 -0
  307. aria_code/data_analysis_tools.py +4 -0
  308. aria_code/data_cleaner.py +483 -0
  309. aria_code/data_service.py +513 -0
  310. aria_code/datasources/__init__.py +23 -0
  311. aria_code/datasources/base.py +166 -0
  312. aria_code/datasources/router.py +253 -0
  313. aria_code/datasources/sec_statements.py +217 -0
  314. aria_code/datasources/sources/__init__.py +15 -0
  315. aria_code/datasources/sources/akshare_source.py +269 -0
  316. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  317. aria_code/datasources/sources/edgar_source.py +219 -0
  318. aria_code/datasources/sources/finnhub_source.py +198 -0
  319. aria_code/datasources/sources/fred_source.py +223 -0
  320. aria_code/datasources/sources/tushare_source.py +143 -0
  321. aria_code/datasources/sources/web_scraper_source.py +278 -0
  322. aria_code/datasources/sources/world_bank_source.py +206 -0
  323. aria_code/datasources/sources/yfinance_source.py +152 -0
  324. aria_code/doctor.py +860 -0
  325. aria_code/domain/__init__.py +0 -0
  326. aria_code/domain/_finance_core/__init__.py +5 -0
  327. aria_code/domain/_finance_core/backtest_engine.py +325 -0
  328. aria_code/domain/_finance_core/performance_stats.py +101 -0
  329. aria_code/domain/backtest_engine.py +75 -0
  330. aria_code/domain/backtest_report.py +488 -0
  331. aria_code/domain/finance_formulas.py +389 -0
  332. aria_code/domain/performance_stats.py +6 -0
  333. aria_code/domain/portfolio_ledger.py +307 -0
  334. aria_code/domain/strategy_vault.py +421 -0
  335. aria_code/evals/__init__.py +37 -0
  336. aria_code/evals/behavior.py +113 -0
  337. aria_code/evals/gap.py +93 -0
  338. aria_code/evals/harness.py +949 -0
  339. aria_code/evals/review_probe.py +132 -0
  340. aria_code/evals/runner.py +425 -0
  341. aria_code/evals/scorecard.py +322 -0
  342. aria_code/evals/summary.py +125 -0
  343. aria_code/external_agent_runner.py +115 -0
  344. aria_code/figma_client.py +4 -0
  345. aria_code/file_analysis_tools.py +4 -0
  346. aria_code/finance_formulas.py +4 -0
  347. aria_code/football_data_client.py +4 -0
  348. aria_code/image_gen_tools.py +4 -0
  349. aria_code/intent_classifier.py +459 -0
  350. aria_code/kling_video_client.py +4 -0
  351. aria_code/licensing.py +151 -0
  352. aria_code/local_finance_tools.py +4 -0
  353. aria_code/local_image_provider.py +4 -0
  354. aria_code/local_llm_provider.py +4 -0
  355. aria_code/macro_tools.py +4 -0
  356. aria_code/markdown_pdf.py +289 -0
  357. aria_code/market_data_client.py +4 -0
  358. aria_code/mcp_client.py +744 -0
  359. aria_code/memory_manager.py +273 -0
  360. aria_code/model_capability.py +512 -0
  361. aria_code/notification_tools.py +4 -0
  362. aria_code/openai_image_client.py +4 -0
  363. aria_code/packages/__init__.py +26 -0
  364. aria_code/packages/adk_bridge/__init__.py +11 -0
  365. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  366. aria_code/packages/adk_bridge/market_tools.py +125 -0
  367. aria_code/packages/aria_agents/__init__.py +5 -0
  368. aria_code/packages/aria_agents/manifest.py +78 -0
  369. aria_code/packages/aria_core/__init__.py +34 -0
  370. aria_code/packages/aria_core/architecture.py +269 -0
  371. aria_code/packages/aria_core/export.py +147 -0
  372. aria_code/packages/aria_core/manifest.py +65 -0
  373. aria_code/packages/aria_core/paths.py +57 -0
  374. aria_code/packages/aria_core/secure_file.py +68 -0
  375. aria_code/packages/aria_infra/__init__.py +15 -0
  376. aria_code/packages/aria_infra/arthera.py +58 -0
  377. aria_code/packages/aria_infra/doctor.py +246 -0
  378. aria_code/packages/aria_infra/product.py +40 -0
  379. aria_code/packages/aria_mcp/__init__.py +25 -0
  380. aria_code/packages/aria_mcp/bridge.py +232 -0
  381. aria_code/packages/aria_mcp/circuit.py +82 -0
  382. aria_code/packages/aria_mcp/config.py +162 -0
  383. aria_code/packages/aria_mcp/installer.py +95 -0
  384. aria_code/packages/aria_mcp/server.py +1705 -0
  385. aria_code/packages/aria_mcp/tools.py +81 -0
  386. aria_code/packages/aria_sdk/__init__.py +19 -0
  387. aria_code/packages/aria_sdk/client.py +393 -0
  388. aria_code/packages/aria_sdk/providers.py +70 -0
  389. aria_code/packages/aria_sdk/streaming.py +75 -0
  390. aria_code/packages/aria_sdk/types.py +86 -0
  391. aria_code/packages/aria_services/__init__.py +92 -0
  392. aria_code/packages/aria_services/cache.py +60 -0
  393. aria_code/packages/aria_services/context.py +356 -0
  394. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  395. aria_code/packages/aria_services/data.py +11 -0
  396. aria_code/packages/aria_services/provider_health.py +304 -0
  397. aria_code/packages/aria_services/references.py +247 -0
  398. aria_code/packages/aria_services/registry.py +249 -0
  399. aria_code/packages/aria_services/research_protocol.py +137 -0
  400. aria_code/packages/aria_services/research_quality.py +186 -0
  401. aria_code/packages/aria_services/settings.py +160 -0
  402. aria_code/packages/aria_services/usage.py +151 -0
  403. aria_code/packages/aria_skills/__init__.py +46 -0
  404. aria_code/packages/aria_skills/catalog.py +149 -0
  405. aria_code/packages/aria_skills/loader.py +455 -0
  406. aria_code/packages/aria_skills/registry.py +84 -0
  407. aria_code/packages/aria_tools/__init__.py +5 -0
  408. aria_code/packages/aria_tools/financial/__init__.py +5 -0
  409. aria_code/packages/aria_tools/financial/_paths.py +92 -0
  410. aria_code/packages/aria_tools/registry.py +129 -0
  411. aria_code/packages/quant_engine/__init__.py +33 -0
  412. aria_code/packages/quant_engine/_namespace.py +24 -0
  413. aria_code/packages/quant_engine/backtest/__init__.py +10 -0
  414. aria_code/packages/quant_engine/backtest/core.py +53 -0
  415. aria_code/packages/quant_engine/backtest/engine.py +102 -0
  416. aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
  417. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  418. aria_code/packages/quant_engine/sports/__init__.py +76 -0
  419. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  420. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  421. aria_code/packages/quant_engine/sports/elo.py +300 -0
  422. aria_code/packages/quant_engine/sports/form.py +188 -0
  423. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  424. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  425. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  426. aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
  427. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  428. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  429. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  430. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  431. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  432. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  433. aria_code/packs/__init__.py +85 -0
  434. aria_code/packs/base.py +168 -0
  435. aria_code/packs/finance/__init__.py +250 -0
  436. aria_code/packs/healthcare/__init__.py +40 -0
  437. aria_code/packs/logistics/__init__.py +171 -0
  438. aria_code/packs/payments/__init__.py +139 -0
  439. aria_code/packs/realty/__init__.py +190 -0
  440. aria_code/packs/registry.py +202 -0
  441. aria_code/packs/rules.py +90 -0
  442. aria_code/plan_utils.py +194 -0
  443. aria_code/plugin_loader.py +328 -0
  444. aria_code/portfolio_ledger.py +4 -0
  445. aria_code/preview_server.py +449 -0
  446. aria_code/privacy/__init__.py +5 -0
  447. aria_code/privacy/feedback.py +123 -0
  448. aria_code/project_review.py +278 -0
  449. aria_code/project_review_api.py +235 -0
  450. aria_code/project_review_server.py +18 -0
  451. aria_code/project_tools.py +4 -0
  452. aria_code/providers/__init__.py +30 -0
  453. aria_code/providers/google_video_provider.py +256 -0
  454. aria_code/providers/llm/__init__.py +19 -0
  455. aria_code/providers/llm/anthropic.py +184 -0
  456. aria_code/providers/llm/arthera_gateway.py +90 -0
  457. aria_code/providers/llm/autoconfig.py +213 -0
  458. aria_code/providers/llm/base.py +139 -0
  459. aria_code/providers/llm/custom.py +158 -0
  460. aria_code/providers/llm/ollama.py +137 -0
  461. aria_code/providers/llm/openai_compat.py +350 -0
  462. aria_code/providers/llm/registry.py +457 -0
  463. aria_code/providers/local_image_provider.py +202 -0
  464. aria_code/providers/local_llm_provider.py +549 -0
  465. aria_code/providers/ollama_models.py +79 -0
  466. aria_code/providers/openai_params.py +32 -0
  467. aria_code/providers/tool_messages.py +100 -0
  468. aria_code/realty_data_tools.py +4 -0
  469. aria_code/report_exporters.py +150 -0
  470. aria_code/report_generator.py +1764 -0
  471. aria_code/review_service.py +497 -0
  472. aria_code/runtime/__init__.py +176 -0
  473. aria_code/runtime/acceptance.py +632 -0
  474. aria_code/runtime/agent_loop.py +1851 -0
  475. aria_code/runtime/agent_profiles.py +75 -0
  476. aria_code/runtime/approval.py +71 -0
  477. aria_code/runtime/baseline.py +219 -0
  478. aria_code/runtime/budget.py +232 -0
  479. aria_code/runtime/checkpoints.py +481 -0
  480. aria_code/runtime/consult.py +65 -0
  481. aria_code/runtime/contract.py +282 -0
  482. aria_code/runtime/delivery.py +432 -0
  483. aria_code/runtime/events.py +113 -0
  484. aria_code/runtime/gateway.py +167 -0
  485. aria_code/runtime/lsp.py +484 -0
  486. aria_code/runtime/native_index.py +153 -0
  487. aria_code/runtime/orchestrator.py +55 -0
  488. aria_code/runtime/processes.py +343 -0
  489. aria_code/runtime/project_graph.py +614 -0
  490. aria_code/runtime/repo_map.py +712 -0
  491. aria_code/runtime/research_bundle.py +105 -0
  492. aria_code/runtime/review.py +269 -0
  493. aria_code/runtime/run_state.py +127 -0
  494. aria_code/runtime/run_store.py +522 -0
  495. aria_code/runtime/scoped_runs.py +113 -0
  496. aria_code/runtime/self_healing.py +422 -0
  497. aria_code/runtime/semantic_diff.py +171 -0
  498. aria_code/runtime/subagent.py +721 -0
  499. aria_code/runtime/symbol_edit.py +117 -0
  500. aria_code/runtime/task_brief.py +46 -0
  501. aria_code/runtime/task_context.py +145 -0
  502. aria_code/runtime/task_ledger.py +108 -0
  503. aria_code/runtime/task_orchestrator.py +90 -0
  504. aria_code/runtime/task_outcome.py +33 -0
  505. aria_code/runtime/task_verification.py +266 -0
  506. aria_code/runtime/task_worktree.py +448 -0
  507. aria_code/runtime/tool_executor.py +329 -0
  508. aria_code/runtime/tool_policy.py +107 -0
  509. aria_code/runtime/transactions.py +216 -0
  510. aria_code/runtime/windows_job.py +115 -0
  511. aria_code/runtime/workflows.py +222 -0
  512. aria_code/runtime/worktrees.py +215 -0
  513. aria_code/runway_video_client.py +4 -0
  514. aria_code/safety/__init__.py +27 -0
  515. aria_code/safety/client_isolation.py +178 -0
  516. aria_code/safety/permissions.py +339 -0
  517. aria_code/safety/risk.py +699 -0
  518. aria_code/safety/sandbox.py +148 -0
  519. aria_code/safety/service.py +102 -0
  520. aria_code/service_endpoints.py +14 -0
  521. aria_code/setup_wizard.py +684 -0
  522. aria_code/spreadsheet_tools.py +4 -0
  523. aria_code/strategy_vault.py +4 -0
  524. aria_code/tools/__init__.py +0 -0
  525. aria_code/tools/artifact_tools.py +252 -0
  526. aria_code/tools/broker_tools.py +92 -0
  527. aria_code/tools/build_quant_engine.py +97 -0
  528. aria_code/tools/code_audit_tools.py +192 -0
  529. aria_code/tools/computer_use_tools.py +509 -0
  530. aria_code/tools/data_analysis_tools.py +831 -0
  531. aria_code/tools/enterprise_finance_tools.py +202 -0
  532. aria_code/tools/extended_tools.py +240 -0
  533. aria_code/tools/file_analysis_tools.py +880 -0
  534. aria_code/tools/image_gen_tools.py +114 -0
  535. aria_code/tools/local_finance_tools.py +3488 -0
  536. aria_code/tools/logistics_carriers.py +11 -0
  537. aria_code/tools/logistics_inventory.py +11 -0
  538. aria_code/tools/logistics_io.py +11 -0
  539. aria_code/tools/logistics_tenancy.py +11 -0
  540. aria_code/tools/logistics_tools.py +11 -0
  541. aria_code/tools/macro_tools.py +412 -0
  542. aria_code/tools/notification_tools.py +249 -0
  543. aria_code/tools/project_tools.py +525 -0
  544. aria_code/tools/realty_data_tools.py +662 -0
  545. aria_code/tools/spreadsheet_tools.py +399 -0
  546. aria_code/tools/stripe_tools.py +115 -0
  547. aria_code/tools/video_tools.py +500 -0
  548. aria_code/training/__init__.py +1 -0
  549. aria_code/training/compare.py +117 -0
  550. aria_code/training/prelude_commands.py +111 -0
  551. aria_code/training/prelude_eval.py +390 -0
  552. aria_code/training/prelude_real.py +183 -0
  553. aria_code/training/prelude_train.py +248 -0
  554. aria_code/training/sft_dataset.py +230 -0
  555. aria_code/training/trajectory_dataset.py +263 -0
  556. aria_code/ui/__init__.py +105 -0
  557. aria_code/ui/banner.py +404 -0
  558. aria_code/ui/completer.py +571 -0
  559. aria_code/ui/console.py +285 -0
  560. aria_code/ui/input_box.py +505 -0
  561. aria_code/ui/mentions.py +206 -0
  562. aria_code/ui/picker.py +248 -0
  563. aria_code/ui/render/__init__.py +11 -0
  564. aria_code/ui/render/actions.py +297 -0
  565. aria_code/ui/render/finance.py +1732 -0
  566. aria_code/ui/render/market.py +253 -0
  567. aria_code/ui/render/output.py +954 -0
  568. aria_code/ui/render/responsive.py +75 -0
  569. aria_code/ui/render/team.py +367 -0
  570. aria_code/ui/robot.py +239 -0
  571. aria_code/ui/startup_dashboard.py +110 -0
  572. aria_code/video_analysis.py +147 -0
  573. aria_code/video_editor.py +518 -0
  574. aria_code/video_workflow/__init__.py +8 -0
  575. aria_code/video_workflow/__main__.py +61 -0
  576. aria_code/video_workflow/engines/__init__.py +18 -0
  577. aria_code/video_workflow/engines/base.py +69 -0
  578. aria_code/video_workflow/engines/custom_model.py +204 -0
  579. aria_code/video_workflow/engines/google_veo.py +94 -0
  580. aria_code/video_workflow/engines/local_engine.py +71 -0
  581. aria_code/video_workflow/engines/registry.py +98 -0
  582. aria_code/video_workflow/google_models.py +278 -0
  583. aria_code/video_workflow/music/__init__.py +5 -0
  584. aria_code/video_workflow/music/foley_service.py +132 -0
  585. aria_code/video_workflow/music/service.py +212 -0
  586. aria_code/video_workflow/pipeline.py +620 -0
  587. aria_code/video_workflow/plan.py +202 -0
  588. aria_code/video_workflow/session.py +297 -0
  589. aria_code/workspace/__init__.py +6 -0
  590. aria_code/workspace/files.py +219 -0
  591. aria_code/workspace/verify.py +167 -0
  592. aria_code/workspace_changes.py +199 -0
  593. aria_code/workspace_service.py +196 -0
  594. aria_code/workspace_tools.py +150 -0
  595. aria_code-0.167.0.dist-info/METADATA +330 -0
  596. aria_code-0.167.0.dist-info/RECORD +601 -0
  597. aria_code-0.167.0.dist-info/WHEEL +5 -0
  598. aria_code-0.167.0.dist-info/entry_points.txt +5 -0
  599. aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
  600. aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
  601. aria_code-0.167.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,178 @@
1
+ """Keep one client's document free of other clients' data.
2
+
3
+ A 3PL's stock file, a fund's holdings export or a merchant ledger often holds
4
+ several clients side by side. A document written *for one of them* (named for
5
+ it: ``report_BETA.md``, ``statement-acme.csv``) must not carry another
6
+ client's name, id or item codes. The prompt says so, and in an eval run the
7
+ agent still copied two notes from Beta's own rows that named Acme and Gamma
8
+ SKUs into Beta's report.
9
+
10
+ This is the check behind that rule, the way a liquid handler's checker
11
+ tracks what each tip has touched rather than trusting the protocol's
12
+ comments: before a client document is written, its text is scanned for the
13
+ identifiers of every *other* client found in the workspace's data files, and
14
+ the write is refused with a list of what leaked.
15
+
16
+ Clients are found in CSV files near the document that have an owner column
17
+ (``owner_id``, ``shipper``, ``client``, ``customer``…). A document is a
18
+ client's when its file name names exactly one of them. Code is not checked:
19
+ a script that processes every client legitimately names them all, and so
20
+ does an internal report whose name does not single one out.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import csv
26
+ import re
27
+ from dataclasses import dataclass, field
28
+ from pathlib import Path
29
+ from typing import Iterable
30
+
31
+ OWNER_COLUMNS = ("owner_id", "owner", "shipper_id", "shipper", "client_id", "client",
32
+ "customer_id", "customer", "account_id", "货主", "客户")
33
+ NAME_COLUMNS = ("owner_name", "shipper_name", "client_name", "customer_name", "account_name",
34
+ "货主名称", "客户名称")
35
+ ITEM_COLUMNS = ("sku", "item", "item_id", "item_code", "product_id", "product_code", "sku_id")
36
+ DOCUMENT_SUFFIXES = {".md", ".markdown", ".txt", ".csv", ".tsv", ".json", ".html", ".htm",
37
+ ".xml", ".yaml", ".yml", ".rst", ".eml"}
38
+ MIN_TERM = 3 # "A" or "AB" as an owner id would match everywhere
39
+ MAX_FILE_BYTES = 5 * 1024 * 1024
40
+ MAX_FILES = 25
41
+
42
+
43
+ @dataclass
44
+ class Client:
45
+ id: str
46
+ names: set[str] = field(default_factory=set)
47
+ items: set[str] = field(default_factory=set)
48
+
49
+ def terms(self) -> list[str]:
50
+ return sorted({t for t in {self.id, *self.names, *self.items} if len(t) >= MIN_TERM}, key=len, reverse=True)
51
+
52
+
53
+ _CACHE: dict[tuple[str, float, int], dict[str, Client]] = {}
54
+
55
+
56
+ def _column(header: Iterable[str], wanted: tuple[str, ...]) -> str | None:
57
+ lowered = {h.strip().lower(): h for h in header if h}
58
+ for name in wanted:
59
+ if name in lowered:
60
+ return lowered[name]
61
+ return None
62
+
63
+
64
+ def _clients_in(path: Path) -> dict[str, Client]:
65
+ try:
66
+ stat = path.stat()
67
+ except OSError:
68
+ return {}
69
+ if stat.st_size > MAX_FILE_BYTES:
70
+ return {}
71
+ key = (str(path), stat.st_mtime, stat.st_size)
72
+ if key in _CACHE:
73
+ return _CACHE[key]
74
+ found: dict[str, Client] = {}
75
+ try:
76
+ with path.open(newline="", encoding="utf-8-sig", errors="replace") as fh:
77
+ reader = csv.DictReader(fh)
78
+ header = reader.fieldnames or []
79
+ owner = _column(header, OWNER_COLUMNS)
80
+ if owner:
81
+ name = _column(header, NAME_COLUMNS)
82
+ item = _column(header, ITEM_COLUMNS)
83
+ for row in reader:
84
+ cid = (row.get(owner) or "").strip()
85
+ if not cid:
86
+ continue
87
+ client = found.setdefault(cid, Client(cid))
88
+ if name and (row.get(name) or "").strip():
89
+ client.names.add(row[name].strip())
90
+ if item and (row.get(item) or "").strip():
91
+ client.items.add(row[item].strip())
92
+ except (OSError, csv.Error, UnicodeError):
93
+ found = {}
94
+ _CACHE[key] = found
95
+ return found
96
+
97
+
98
+ def client_index(roots: Iterable[Path]) -> dict[str, Client]:
99
+ """Every client named in the CSV data files in *roots* and their immediate subfolders."""
100
+ index: dict[str, Client] = {}
101
+ seen = 0
102
+ for root in dict.fromkeys(Path(r) for r in roots):
103
+ if not root.is_dir():
104
+ continue
105
+ candidates = sorted(root.glob("*.csv")) + sorted(root.glob("*/*.csv"))
106
+ for path in candidates:
107
+ if seen >= MAX_FILES:
108
+ return index
109
+ seen += 1
110
+ for cid, client in _clients_in(path).items():
111
+ merged = index.setdefault(cid, Client(cid))
112
+ merged.names |= client.names
113
+ merged.items |= client.items
114
+ return index if len(index) > 1 else {}
115
+
116
+
117
+ def _words(text: str) -> set[str]:
118
+ return {w.lower() for w in re.split(r"[^0-9A-Za-z一-鿿]+", text) if w}
119
+
120
+
121
+ def target_client(path: Path, index: dict[str, Client]) -> str | None:
122
+ """The one client a document's file name singles out, if it singles out exactly one."""
123
+ words = _words(Path(path).stem)
124
+ compact = re.sub(r"[^0-9a-z一-鿿]", "", Path(path).stem.lower())
125
+ hits = set()
126
+ for cid, client in index.items():
127
+ if cid.lower() in words:
128
+ hits.add(cid)
129
+ continue
130
+ for name in client.names:
131
+ squashed = re.sub(r"[^0-9a-z一-鿿]", "", name.lower())
132
+ if len(squashed) >= MIN_TERM and squashed in compact:
133
+ hits.add(cid)
134
+ return next(iter(hits)) if len(hits) == 1 else None
135
+
136
+
137
+ def _mentions(text: str, term: str) -> bool:
138
+ if re.search(r"[一-鿿]", term):
139
+ return term in text
140
+ return re.search(rf"(?<![0-9A-Za-z]){re.escape(term)}(?![0-9A-Za-z])", text, re.IGNORECASE) is not None
141
+
142
+
143
+ def foreign_mentions(text: str, target: str, index: dict[str, Client]) -> dict[str, list[str]]:
144
+ """Other clients' identifiers that appear in *text*, by client."""
145
+ own = {t.lower() for t in index[target].terms()} if target in index else set()
146
+ leaks: dict[str, list[str]] = {}
147
+ for cid, client in sorted(index.items()):
148
+ if cid == target:
149
+ continue
150
+ hits = [t for t in client.terms() if t.lower() not in own and _mentions(text, t)]
151
+ if hits:
152
+ leaks[cid] = hits[:4]
153
+ return leaks
154
+
155
+
156
+ def check_write(path: Path | str, content: str, roots: Iterable[Path | str] = ()) -> str | None:
157
+ """Why this write must be refused, or None when it may go ahead."""
158
+ path = Path(path)
159
+ if path.suffix.lower() not in DOCUMENT_SUFFIXES:
160
+ return None
161
+ search = [path.parent, *(Path(r) for r in roots)]
162
+ index = client_index(search)
163
+ if not index:
164
+ return None
165
+ target = target_client(path, index)
166
+ if target is None:
167
+ return None
168
+ leaks = foreign_mentions(content, target, index)
169
+ if not leaks:
170
+ return None
171
+ listed = "; ".join(f"{cid} ({', '.join(terms)})" for cid, terms in leaks.items())
172
+ return (f"Not written: {path.name} is for client {target}, but it contains other clients' data: {listed}. "
173
+ f"A client's document holds only that client's data. Remove these, including any that came "
174
+ f"from notes or comments in {target}'s own rows, and write it again. If this is meant to be an "
175
+ f"internal cross-client document, name it without singling out one client.")
176
+
177
+
178
+ __all__ = ["Client", "check_write", "client_index", "foreign_mentions", "target_client"]
@@ -0,0 +1,339 @@
1
+ """Unified permission and command-risk policy for Aria Code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ import shlex
7
+ from dataclasses import dataclass
8
+ from enum import Enum
9
+ from typing import Any, Dict
10
+
11
+
12
+ SAFE_POLICIES = {"safe", "balanced", "full"}
13
+
14
+
15
+ class PermissionMode(str, Enum):
16
+ READ_ONLY = "read-only"
17
+ WORKSPACE_WRITE = "workspace-write"
18
+ FULL_ACCESS = "full-access"
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class PolicyDecision:
23
+ allowed: bool
24
+ normalized_command: str
25
+ policy: str
26
+ risk: str
27
+ reason: str = ""
28
+ requires_approval: bool = False
29
+ network: bool = False
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class PermissionDecision:
34
+ allowed: bool
35
+ requires_approval: bool
36
+ risk: str
37
+ reason: str
38
+ normalized_command: str = ""
39
+ network: bool = False
40
+
41
+
42
+ _ALIASES = {"python": "python3", "pip": "pip3"}
43
+
44
+
45
+ def normalize_command(command) -> str:
46
+ """Rewrite a leading `python`/`pip` to `python3`/`pip3`; leave the rest exactly as written.
47
+
48
+ This split the whole command with shlex and joined it again, which
49
+ quotes every shell operator: `cd src && ls` ran as `cd src '&&' ls`,
50
+ `a | b` passed "|" to a as an argument, `> out.txt` wrote nothing, and
51
+ `ls *.py` and `echo $HOME` lost their expansion. Any command with a pipe,
52
+ redirect, chain, glob or variable did something other than what it said.
53
+ """
54
+ if isinstance(command, list):
55
+ import shlex as _shlex
56
+ command = _shlex.join(str(c) for c in command)
57
+ raw = (command or "").strip()
58
+ head, sep, rest = raw.partition(" ")
59
+ alias = _ALIASES.get(head)
60
+ return f"{alias}{sep}{rest}" if alias else raw
61
+
62
+
63
+ # Shell operators that chain, pipe, redirect or substitute. A command holding
64
+ # one is more than its first word: `cat notes > ~/.zshrc` starts like a read,
65
+ # `pytest && curl … | sh` like a test run.
66
+ _SHELL_CONTROL = re.compile(r"[|;&<>`\n]|\$\(")
67
+ _SEGMENT_SPLIT = re.compile(r"\|\||&&|[|;&\n]|\$\(|`")
68
+ # Output trimming a test run may carry and still count as one.
69
+ _TRIM_SUFFIX = re.compile(r"(\s+2>&1)?(\s*\|\s*(tail|head)(\s+-n)?\s+-?\d+)?\s*$")
70
+
71
+
72
+ _DEVICE_WRITE = re.compile(r">\s*/dev/(?!null\b|stdout\b|stderr\b|tty\b|fd/)")
73
+
74
+
75
+ def writes_a_device(command: str) -> bool:
76
+ """`> /dev/disk0`, not `> /dev/null` or `2>/dev/null`."""
77
+ return bool(_DEVICE_WRITE.search(command or ""))
78
+
79
+
80
+ def has_shell_control(command: str) -> bool:
81
+ return bool(_SHELL_CONTROL.search(command or ""))
82
+
83
+
84
+ def _segments(command: str) -> list[str]:
85
+ return [part.strip() for part in _SEGMENT_SPLIT.split(command) if part.strip()]
86
+
87
+
88
+ def command_uses_network(command: str) -> bool:
89
+ """True if any part of the command, not only the first, reaches the network."""
90
+ return any(_segment_uses_network(part) for part in _segments(command.lower()))
91
+
92
+
93
+ def _segment_uses_network(stripped: str) -> bool:
94
+ network_prefixes = (
95
+ "curl ", "wget ", "http ", "https ", "gh ", "git fetch", "git pull",
96
+ "git push", "pip3 install", "pip install", "npm install", "npm i ",
97
+ "pnpm install", "yarn install", "brew install",
98
+ )
99
+ return any(stripped.startswith(prefix) for prefix in network_prefixes)
100
+
101
+
102
+ def is_verification_command(command: str) -> bool:
103
+ stripped = _TRIM_SUFFIX.sub("", command.lower().strip(), count=1)
104
+ if has_shell_control(stripped):
105
+ return False
106
+ prefixes = (
107
+ "python3 -m py_compile",
108
+ "python -m py_compile",
109
+ "python3 -m pytest",
110
+ "python -m pytest",
111
+ "pytest",
112
+ # The standard library's runner. Missing here, so `safe` blocked the
113
+ # plainest way to test a Python project and the agent reported "blocked
114
+ # by a security policy" instead of a test result.
115
+ "python3 -m unittest",
116
+ "python -m unittest",
117
+ "npm test",
118
+ "npm run test",
119
+ "yarn test",
120
+ "pnpm test",
121
+ "npm run build",
122
+ "npx tsc --noemit",
123
+ "npx tsc --noEmit".lower(),
124
+ "tsc --noemit",
125
+ "go test",
126
+ "cargo test",
127
+ "mypy",
128
+ "ruff check",
129
+ )
130
+ return any(stripped.startswith(prefix) for prefix in prefixes)
131
+
132
+
133
+ def classify_command_risk(command) -> str:
134
+ """Classify command risk into low/medium/high."""
135
+ if isinstance(command, list):
136
+ import shlex as _shlex
137
+ command = _shlex.join(str(c) for c in command)
138
+ normalized = f" {str(command).lower().strip()} "
139
+ stripped = normalized.strip()
140
+
141
+ high_risk_patterns = (
142
+ " rm ", " rm -", "chmod ", "chown ", "mkfs", "dd if=", "docker ", "kubectl ",
143
+ "shutdown", "reboot", "systemctl ", "launchctl ", "passwd", "sudo ",
144
+ "git push", "git reset --hard", "git checkout --", "mv ",
145
+ ":(){ :", "fork bomb",
146
+ )
147
+ low_risk_prefixes = (
148
+ "ls", "pwd", "echo", "cat ", "head ", "tail ", "rg ", "find ", "git status",
149
+ "git diff", "git log", "which ", "whoami", "date", "uname ", "env",
150
+ "python3 --version", "node --version", "npm --version",
151
+ )
152
+ medium_risk_prefixes = (
153
+ "pip ", "pip3 ", "npm install", "npm i ", "npm run ", "python ", "python3 ",
154
+ "pytest", "make ", "go test", "cargo test", "git commit", "git pull", "git merge",
155
+ "gh ", "curl ", "wget ",
156
+ )
157
+
158
+ if any(pattern in normalized for pattern in high_risk_patterns) or writes_a_device(normalized):
159
+ return "high"
160
+ if has_shell_control(stripped):
161
+ return "medium"
162
+ if stripped.startswith(low_risk_prefixes):
163
+ return "low"
164
+ if stripped.startswith(medium_risk_prefixes) or is_verification_command(stripped):
165
+ return "medium"
166
+ return "medium"
167
+
168
+
169
+ class PermissionService:
170
+ """Central policy for tools and shell-command execution."""
171
+
172
+ READ_TOOLS = {"read_file", "list_files", "search_code", "project_context", "git_status", "git_diff"}
173
+ WRITE_TOOLS = {"write_file", "edit_file", "apply_change", "apply_patch", "reject_change"}
174
+
175
+ def __init__(
176
+ self,
177
+ mode: PermissionMode | str = PermissionMode.WORKSPACE_WRITE,
178
+ command_policy: str = "safe",
179
+ network_enabled: bool = True,
180
+ ) -> None:
181
+ try:
182
+ self.mode = PermissionMode(mode)
183
+ except ValueError:
184
+ self.mode = PermissionMode.WORKSPACE_WRITE
185
+ self.command_policy = command_policy if command_policy in SAFE_POLICIES else "safe"
186
+ self.network_enabled = network_enabled
187
+
188
+ def evaluate_tool(self, tool_name: str, params: Dict[str, Any] | None = None) -> PermissionDecision:
189
+ name = (tool_name or "").strip()
190
+ if name in self.READ_TOOLS:
191
+ return PermissionDecision(True, False, "low", "Read-only tool allowed.")
192
+ if name in self.WRITE_TOOLS:
193
+ if self.mode == PermissionMode.READ_ONLY:
194
+ return PermissionDecision(False, False, "medium", "Writes are blocked in read-only mode.")
195
+ return PermissionDecision(
196
+ True,
197
+ self.mode != PermissionMode.FULL_ACCESS,
198
+ "medium",
199
+ "Write requires review or explicit approval before disk mutation.",
200
+ )
201
+ if name == "run_command":
202
+ return self.evaluate_command(str((params or {}).get("command", "")))
203
+ return PermissionDecision(False, False, "unknown", f"Unknown tool '{name}' is blocked.")
204
+
205
+ def evaluate_command(self, command: str, policy: str | None = None) -> PermissionDecision:
206
+ decision = evaluate_command_policy(
207
+ command,
208
+ policy or self.command_policy,
209
+ mode=self.mode.value,
210
+ network_enabled=self.network_enabled,
211
+ )
212
+ return PermissionDecision(
213
+ allowed=decision.allowed,
214
+ requires_approval=decision.requires_approval,
215
+ risk=decision.risk,
216
+ reason=decision.reason or "Command allowed.",
217
+ normalized_command=decision.normalized_command,
218
+ network=decision.network,
219
+ )
220
+
221
+
222
+ def evaluate_command_policy(
223
+ command: str,
224
+ policy: str = "safe",
225
+ *,
226
+ mode: str = PermissionMode.WORKSPACE_WRITE.value,
227
+ network_enabled: bool = True,
228
+ ) -> PolicyDecision:
229
+ """Return policy decision for command execution."""
230
+ normalized = normalize_command(command)
231
+ selected_policy = (policy or "safe").strip().lower()
232
+ if selected_policy not in SAFE_POLICIES:
233
+ selected_policy = "safe"
234
+ try:
235
+ selected_mode = PermissionMode(mode)
236
+ except ValueError:
237
+ # Fail closed. An unrecognised mode used to resolve to WORKSPACE_WRITE,
238
+ # which is more permissive than READ_ONLY, not less: under read-only a
239
+ # write command is a hard block, while under workspace-write it is a
240
+ # block the user can unlock by approving. Resolving an invalid value to
241
+ # the laxer of the two turns a typo — or a caller passing a *policy*
242
+ # name into the *mode* slot — into an approval prompt that should never
243
+ # have been offered.
244
+ selected_mode = PermissionMode.READ_ONLY
245
+
246
+ risk = classify_command_risk(normalized)
247
+ network = command_uses_network(normalized)
248
+ if network and not network_enabled:
249
+ return PolicyDecision(
250
+ allowed=False,
251
+ normalized_command=normalized,
252
+ policy=selected_policy,
253
+ risk=risk,
254
+ reason=f"Network command blocked by policy: {normalized}",
255
+ requires_approval=False,
256
+ network=True,
257
+ )
258
+ if selected_mode == PermissionMode.READ_ONLY and risk != "low":
259
+ return PolicyDecision(
260
+ allowed=False,
261
+ normalized_command=normalized,
262
+ policy=selected_policy,
263
+ risk=risk,
264
+ reason=f"Command blocked by read-only mode (risk={risk}): {normalized}",
265
+ requires_approval=False,
266
+ network=network,
267
+ )
268
+ if risk == "high" and selected_policy != "full":
269
+ return PolicyDecision(
270
+ allowed=False,
271
+ normalized_command=normalized,
272
+ policy=selected_policy,
273
+ risk=risk,
274
+ reason=(
275
+ f"Command blocked by policy '{selected_policy}' (risk={risk}): {normalized}. "
276
+ "Use /config set command_policy=full only if you understand the risk."
277
+ ),
278
+ requires_approval=selected_policy == "balanced",
279
+ network=network,
280
+ )
281
+ # Verification is the one class of command the agent must be able to run
282
+ # under the default policy. `safe` blocked pytest, py_compile, npm test and
283
+ # tsc --noEmit, so the loop could write a fix and then had no way to find
284
+ # out whether it worked — and the model said so out loud: "由于安全策略的
285
+ # 限制,我无法执行 pytest ... 但我相信测试应该能够通过". An agent that
286
+ # cannot check its own work is guessing, and the whole acceptance gate rests
287
+ # on being able to run exactly these commands.
288
+ #
289
+ # The exposure is small and bounded by what `is_verification_command`
290
+ # matches: test and type-check runners, anchored at the start of the
291
+ # command. It stays subject to read-only mode and to the network rule
292
+ # above, and `high` risk was already rejected before this point — so a
293
+ # command like `pytest && rm -rf /` never reaches here.
294
+ if selected_policy == "safe" and risk != "low" and is_verification_command(normalized):
295
+ return PolicyDecision(
296
+ allowed=True,
297
+ normalized_command=normalized,
298
+ policy=selected_policy,
299
+ risk=risk,
300
+ reason="",
301
+ requires_approval=False,
302
+ network=network,
303
+ )
304
+
305
+ if selected_policy == "safe" and risk != "low":
306
+ extra = ""
307
+ norm_low = normalized.lower()
308
+ if "pip3" in norm_low or "pip " in norm_low or norm_low.startswith("pip"):
309
+ extra = (
310
+ "\n\n💡 依赖安装提示:运行 `/config set command_policy=balanced` 后重试,"
311
+ "或手动在终端执行该命令。不要重试相同命令!"
312
+ )
313
+ elif "python" in norm_low and ("~/" in normalized or "/Desktop/" in normalized):
314
+ extra = (
315
+ "\n\n💡 脚本执行提示:运行 `/config set command_policy=balanced` 后重试。"
316
+ "不要重试相同命令!"
317
+ )
318
+ return PolicyDecision(
319
+ allowed=False,
320
+ normalized_command=normalized,
321
+ policy=selected_policy,
322
+ risk=risk,
323
+ reason=(
324
+ f"Command blocked by policy '{selected_policy}' (risk={risk}): {normalized}. "
325
+ "Use /config set command_policy=balanced or /run --dry-run <cmd> first."
326
+ + extra
327
+ ),
328
+ requires_approval=True,
329
+ network=network,
330
+ )
331
+ return PolicyDecision(
332
+ allowed=True,
333
+ normalized_command=normalized,
334
+ policy=selected_policy,
335
+ risk=risk,
336
+ reason="",
337
+ requires_approval=False,
338
+ network=network,
339
+ )