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.
- aria_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +49 -0
- aria_code/agents/base.py +329 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +168 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +723 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +239 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +16 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1985 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sec_statements.py +217 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +459 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +58 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +304 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.167.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Sandbox execution module for Aria Code."""
|
|
2
|
+
import subprocess
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
def run_in_docker_sandbox(command: str, cwd: str = None, timeout: int = 120, image: str = "python:3.11-slim") -> subprocess.CompletedProcess:
|
|
6
|
+
"""Run a shell command securely inside an isolated Docker container.
|
|
7
|
+
|
|
8
|
+
This mounts the current workspace so scripts can operate on files,
|
|
9
|
+
but prevents access to the host's system files and network interfaces
|
|
10
|
+
if configured with --network none (optional).
|
|
11
|
+
"""
|
|
12
|
+
workspace = cwd or os.getcwd()
|
|
13
|
+
|
|
14
|
+
# We use docker run with a volume mount to the workspace.
|
|
15
|
+
# --rm: remove container after exit
|
|
16
|
+
# -v: mount workspace to /workspace
|
|
17
|
+
# -w: set working directory to /workspace
|
|
18
|
+
docker_cmd = [
|
|
19
|
+
"docker", "run", "--rm",
|
|
20
|
+
"-v", f"{workspace}:/workspace",
|
|
21
|
+
"-w", "/workspace",
|
|
22
|
+
# "--network", "none", # Uncomment to completely isolate network
|
|
23
|
+
image,
|
|
24
|
+
"sh", "-c", command
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
return subprocess.run(
|
|
28
|
+
docker_cmd,
|
|
29
|
+
capture_output=True,
|
|
30
|
+
text=True,
|
|
31
|
+
timeout=timeout
|
|
32
|
+
)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Session export helpers for Aria Code."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any, Optional, Sequence
|
|
8
|
+
|
|
9
|
+
from aria_code.artifacts import artifact_summary as build_artifact_summary
|
|
10
|
+
from aria_code.apps.cli.config_paths import config_snapshot
|
|
11
|
+
from aria_code.packages.aria_core import build_session_diagnostic_bundle
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _safe_title_from_conversation(conversation: Sequence[dict]) -> str:
|
|
15
|
+
for msg in conversation:
|
|
16
|
+
if msg.get("role") == "user":
|
|
17
|
+
return str(msg.get("content", ""))[:60]
|
|
18
|
+
return "Aria Code Session"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def build_session_export_payload(
|
|
22
|
+
fmt: str,
|
|
23
|
+
conversation: Sequence[dict],
|
|
24
|
+
*,
|
|
25
|
+
session_id: str = "",
|
|
26
|
+
config: Optional[dict] = None,
|
|
27
|
+
paths: Optional[dict] = None,
|
|
28
|
+
trace: Any = None,
|
|
29
|
+
provider_health: Optional[list] = None,
|
|
30
|
+
doctor_report: Any = None,
|
|
31
|
+
mcp_status: Optional[list] = None,
|
|
32
|
+
context_health: Optional[dict] = None,
|
|
33
|
+
) -> tuple[str, str, str]:
|
|
34
|
+
"""Return (content, extension, suggested_filename_prefix)."""
|
|
35
|
+
fmt = (fmt or "json").lower().strip()
|
|
36
|
+
|
|
37
|
+
if fmt == "json":
|
|
38
|
+
content = json.dumps(list(conversation), indent=2, ensure_ascii=False)
|
|
39
|
+
return content, "json", "aria_code_chat"
|
|
40
|
+
|
|
41
|
+
if fmt == "csv":
|
|
42
|
+
lines = ["role,content"]
|
|
43
|
+
for msg in conversation:
|
|
44
|
+
escaped = str(msg.get("content", "")).replace('"', '""').replace("\n", " ")
|
|
45
|
+
lines.append(f'{msg.get("role", "user")},"{escaped}"')
|
|
46
|
+
return "\n".join(lines), "csv", "aria_code_chat"
|
|
47
|
+
|
|
48
|
+
if fmt == "md":
|
|
49
|
+
lines = [f"# Aria Code Chat Export — {datetime.now().strftime('%Y-%m-%d %H:%M')}\n"]
|
|
50
|
+
for msg in conversation:
|
|
51
|
+
prefix = "**You:**" if msg.get("role") == "user" else "**Aria:**"
|
|
52
|
+
lines.append(f"{prefix}\n{msg.get('content', '')}\n")
|
|
53
|
+
return "\n".join(lines), "md", "aria_code_chat"
|
|
54
|
+
|
|
55
|
+
if fmt == "sft":
|
|
56
|
+
pairs = []
|
|
57
|
+
conv = list(conversation)
|
|
58
|
+
i = 0
|
|
59
|
+
while i < len(conv) - 1:
|
|
60
|
+
if conv[i].get("role") == "user" and conv[i + 1].get("role") == "assistant":
|
|
61
|
+
user_text = str(conv[i].get("content", "")).strip()
|
|
62
|
+
assistant_text = str(conv[i + 1].get("content", "")).strip()
|
|
63
|
+
if len(user_text) > 10 and len(assistant_text) > 20 and not user_text.startswith("Tool results:"):
|
|
64
|
+
pairs.append({
|
|
65
|
+
"instruction": user_text,
|
|
66
|
+
"input": "",
|
|
67
|
+
"output": assistant_text,
|
|
68
|
+
"source": "aria_cli_export",
|
|
69
|
+
"timestamp": datetime.now().strftime("%Y-%m-%d"),
|
|
70
|
+
})
|
|
71
|
+
i += 2
|
|
72
|
+
else:
|
|
73
|
+
i += 1
|
|
74
|
+
if not pairs:
|
|
75
|
+
raise ValueError("No user→assistant pairs to export")
|
|
76
|
+
return json.dumps(pairs, indent=2, ensure_ascii=False), "json", "aria_sft"
|
|
77
|
+
|
|
78
|
+
if fmt == "ipynb":
|
|
79
|
+
cells = [{
|
|
80
|
+
"cell_type": "markdown",
|
|
81
|
+
"metadata": {},
|
|
82
|
+
"source": [f"# Aria Code Chat Export — {datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n", "*(This notebook contains the conversation history. Math formulas and LaTeX will render natively here.)*"]
|
|
83
|
+
}]
|
|
84
|
+
for msg in conversation:
|
|
85
|
+
if msg.get("role") not in ("user", "assistant"):
|
|
86
|
+
continue
|
|
87
|
+
prefix = "**You:**\n\n" if msg.get("role") == "user" else "**Aria:**\n\n"
|
|
88
|
+
cells.append({
|
|
89
|
+
"cell_type": "markdown",
|
|
90
|
+
"metadata": {},
|
|
91
|
+
"source": [prefix + str(msg.get("content", ""))]
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
notebook = {
|
|
95
|
+
"cells": cells,
|
|
96
|
+
"metadata": {
|
|
97
|
+
"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
|
|
98
|
+
"language_info": {"name": "python"}
|
|
99
|
+
},
|
|
100
|
+
"nbformat": 4,
|
|
101
|
+
"nbformat_minor": 4
|
|
102
|
+
}
|
|
103
|
+
return json.dumps(notebook, indent=2, ensure_ascii=False), "ipynb", "aria_code_chat"
|
|
104
|
+
|
|
105
|
+
if fmt == "bundle":
|
|
106
|
+
bundle = build_session_diagnostic_bundle(
|
|
107
|
+
session_id=session_id,
|
|
108
|
+
conversation=conversation,
|
|
109
|
+
config=config,
|
|
110
|
+
paths=paths or config_snapshot(),
|
|
111
|
+
trace=trace,
|
|
112
|
+
provider_health=provider_health,
|
|
113
|
+
artifact_summary=build_artifact_summary(),
|
|
114
|
+
doctor_report=doctor_report,
|
|
115
|
+
mcp_status=mcp_status,
|
|
116
|
+
context_health=context_health,
|
|
117
|
+
)
|
|
118
|
+
return json.dumps(bundle, indent=2, ensure_ascii=False), "json", "aria_bundle"
|
|
119
|
+
|
|
120
|
+
raise ValueError("Unsupported export format")
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""JSONL-backed session persistence for Aria Code.
|
|
2
|
+
|
|
3
|
+
Why JSONL instead of JSON?
|
|
4
|
+
• Append-per-turn — no need to rewrite the whole file on every message
|
|
5
|
+
• Crash-safe — partial writes leave previous turns intact
|
|
6
|
+
• Streamable — readers can tail -f a live session
|
|
7
|
+
|
|
8
|
+
File layout: ~/.arthera/sessions/<session_id>.jsonl
|
|
9
|
+
Each line is one JSON object:
|
|
10
|
+
{"type": "meta", "id": "...", "title": "...", "created_at": "..."}
|
|
11
|
+
{"type": "message", "role": "user", "content": "...", "ts": "..."}
|
|
12
|
+
{"type": "message", "role": "assistant", "content": "...", "ts": "..."}
|
|
13
|
+
{"type": "meta", "updated_at": "..."} ← appended on each save
|
|
14
|
+
|
|
15
|
+
Reading: scan all lines, reconstruct conversation from "message" entries.
|
|
16
|
+
Last "meta" wins for title / timestamps.
|
|
17
|
+
"""
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
from datetime import datetime, timezone
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Optional
|
|
24
|
+
from aria_code.packages.aria_core.paths import aria_home
|
|
25
|
+
|
|
26
|
+
_SESSIONS_DIR = aria_home() / "sessions"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _now() -> str:
|
|
30
|
+
return datetime.now(timezone.utc).isoformat()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _session_path(session_id: str) -> Path:
|
|
34
|
+
return _SESSIONS_DIR / f"{session_id}.jsonl"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class JsonlSessionStore:
|
|
38
|
+
"""Read/write JSONL session files."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, sessions_dir: Optional[Path] = None) -> None:
|
|
41
|
+
self.root = sessions_dir or _SESSIONS_DIR
|
|
42
|
+
self.root.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
|
|
44
|
+
def _path(self, session_id: str) -> Path:
|
|
45
|
+
return self.root / f"{session_id}.jsonl"
|
|
46
|
+
|
|
47
|
+
# ── Write ─────────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
def init_session(self, session_id: str, title: str = "") -> None:
|
|
50
|
+
"""Write the opening meta line. Call once at session start."""
|
|
51
|
+
p = self._path(session_id)
|
|
52
|
+
if p.exists():
|
|
53
|
+
return
|
|
54
|
+
line = json.dumps({
|
|
55
|
+
"type": "meta",
|
|
56
|
+
"id": session_id,
|
|
57
|
+
"title": title or "",
|
|
58
|
+
"created_at": _now(),
|
|
59
|
+
}, ensure_ascii=False)
|
|
60
|
+
p.write_text(line + "\n", encoding="utf-8")
|
|
61
|
+
|
|
62
|
+
def append_message(self, session_id: str, role: str, content: str) -> None:
|
|
63
|
+
"""Append one message turn. Thread-safe for single-process use."""
|
|
64
|
+
p = self._path(session_id)
|
|
65
|
+
line = json.dumps({
|
|
66
|
+
"type": "message",
|
|
67
|
+
"role": role,
|
|
68
|
+
"content": content,
|
|
69
|
+
"ts": _now(),
|
|
70
|
+
}, ensure_ascii=False)
|
|
71
|
+
with p.open("a", encoding="utf-8") as f:
|
|
72
|
+
f.write(line + "\n")
|
|
73
|
+
|
|
74
|
+
def flush_meta(self, session_id: str, title: str = "", extra: Optional[dict] = None) -> None:
|
|
75
|
+
"""Append an updated meta line (title, timestamps)."""
|
|
76
|
+
p = self._path(session_id)
|
|
77
|
+
meta: dict = {"type": "meta", "updated_at": _now()}
|
|
78
|
+
if title:
|
|
79
|
+
meta["title"] = title
|
|
80
|
+
if extra:
|
|
81
|
+
meta.update(extra)
|
|
82
|
+
with p.open("a", encoding="utf-8") as f:
|
|
83
|
+
f.write(json.dumps(meta, ensure_ascii=False) + "\n")
|
|
84
|
+
|
|
85
|
+
def save_conversation(
|
|
86
|
+
self,
|
|
87
|
+
session_id: str,
|
|
88
|
+
conversation: list[dict],
|
|
89
|
+
title: str = "",
|
|
90
|
+
) -> None:
|
|
91
|
+
"""Full rewrite — used when bulk-importing a JSON session into JSONL."""
|
|
92
|
+
p = self._path(session_id)
|
|
93
|
+
lines = []
|
|
94
|
+
lines.append(json.dumps({
|
|
95
|
+
"type": "meta",
|
|
96
|
+
"id": session_id,
|
|
97
|
+
"title": title or "",
|
|
98
|
+
"created_at": _now(),
|
|
99
|
+
"updated_at": _now(),
|
|
100
|
+
}, ensure_ascii=False))
|
|
101
|
+
for msg in conversation:
|
|
102
|
+
lines.append(json.dumps({
|
|
103
|
+
"type": "message",
|
|
104
|
+
"role": msg.get("role", "user"),
|
|
105
|
+
"content": str(msg.get("content", "")),
|
|
106
|
+
"ts": _now(),
|
|
107
|
+
}, ensure_ascii=False))
|
|
108
|
+
p.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
109
|
+
|
|
110
|
+
# ── Read ──────────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
def load_session(self, session_id: str) -> Optional[dict]:
|
|
113
|
+
"""Load a session; returns None if not found."""
|
|
114
|
+
p = self._path(session_id)
|
|
115
|
+
if not p.exists():
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
messages: list[dict] = []
|
|
119
|
+
meta: dict = {"id": session_id}
|
|
120
|
+
|
|
121
|
+
for raw in p.read_text(encoding="utf-8").splitlines():
|
|
122
|
+
raw = raw.strip()
|
|
123
|
+
if not raw:
|
|
124
|
+
continue
|
|
125
|
+
try:
|
|
126
|
+
obj = json.loads(raw)
|
|
127
|
+
except json.JSONDecodeError:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
t = obj.get("type")
|
|
131
|
+
if t == "meta":
|
|
132
|
+
meta.update({k: v for k, v in obj.items() if k != "type"})
|
|
133
|
+
elif t == "message":
|
|
134
|
+
messages.append({
|
|
135
|
+
"role": obj.get("role", "user"),
|
|
136
|
+
"content": obj.get("content", ""),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
"id": session_id,
|
|
141
|
+
"messages": messages,
|
|
142
|
+
"metadata": {
|
|
143
|
+
"title": meta.get("title", "Untitled"),
|
|
144
|
+
"created_at": meta.get("created_at", ""),
|
|
145
|
+
"updated_at": meta.get("updated_at", ""),
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def list_sessions(self, limit: int = 20) -> list[dict]:
|
|
150
|
+
"""Return recent sessions sorted by mtime, newest first."""
|
|
151
|
+
sessions = []
|
|
152
|
+
for p in sorted(
|
|
153
|
+
self.root.glob("*.jsonl"),
|
|
154
|
+
key=lambda x: x.stat().st_mtime,
|
|
155
|
+
reverse=True,
|
|
156
|
+
):
|
|
157
|
+
session_id = p.stem
|
|
158
|
+
meta: dict = {}
|
|
159
|
+
msg_count = 0
|
|
160
|
+
try:
|
|
161
|
+
for raw in p.read_text(encoding="utf-8").splitlines():
|
|
162
|
+
raw = raw.strip()
|
|
163
|
+
if not raw:
|
|
164
|
+
continue
|
|
165
|
+
obj = json.loads(raw)
|
|
166
|
+
if obj.get("type") == "meta":
|
|
167
|
+
meta.update(obj)
|
|
168
|
+
elif obj.get("type") == "message":
|
|
169
|
+
msg_count += 1
|
|
170
|
+
except Exception:
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
sessions.append({
|
|
174
|
+
"id": session_id,
|
|
175
|
+
"title": meta.get("title", "Untitled"),
|
|
176
|
+
"messages": msg_count,
|
|
177
|
+
"updated": meta.get("updated_at", ""),
|
|
178
|
+
"created": meta.get("created_at", ""),
|
|
179
|
+
})
|
|
180
|
+
if len(sessions) >= limit:
|
|
181
|
+
break
|
|
182
|
+
|
|
183
|
+
return sessions
|
|
184
|
+
|
|
185
|
+
def delete_session(self, session_id: str) -> bool:
|
|
186
|
+
p = self._path(session_id)
|
|
187
|
+
if p.exists():
|
|
188
|
+
p.unlink()
|
|
189
|
+
return True
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
def search_sessions(self, keyword: str, limit: int = 10) -> list[dict]:
|
|
193
|
+
"""Full-text search across all session JSONL files."""
|
|
194
|
+
kw = keyword.lower()
|
|
195
|
+
matches = []
|
|
196
|
+
for p in self.root.glob("*.jsonl"):
|
|
197
|
+
try:
|
|
198
|
+
text = p.read_text(encoding="utf-8")
|
|
199
|
+
if kw not in text.lower():
|
|
200
|
+
continue
|
|
201
|
+
result = self.load_session(p.stem)
|
|
202
|
+
if result:
|
|
203
|
+
matches.append(result)
|
|
204
|
+
except Exception:
|
|
205
|
+
continue
|
|
206
|
+
if len(matches) >= limit:
|
|
207
|
+
break
|
|
208
|
+
return matches
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Keep a Firebase sign-in alive the way Claude Code and Codex keep theirs.
|
|
2
|
+
|
|
3
|
+
A Firebase ID token lasts an hour. /login stores it with a refresh token, but
|
|
4
|
+
nothing used the refresh token, so an hour after signing in every gateway call
|
|
5
|
+
failed and the user was told to /login again. Claude Code and Codex do not ask:
|
|
6
|
+
they exchange the refresh token for a new access token shortly before the old
|
|
7
|
+
one expires and carry on.
|
|
8
|
+
|
|
9
|
+
This does the same against Firebase's Secure Token API, which accepts the
|
|
10
|
+
project's public web API key (the one the web and desktop clients ship; see
|
|
11
|
+
google_login.FIREBASE_WEB_API_KEY) and the user's refresh token. The result is
|
|
12
|
+
written back to config.json with owner-only permissions, so every process that
|
|
13
|
+
reads the session, the gateway provider and the Commerce plugin among them,
|
|
14
|
+
sees the fresh token.
|
|
15
|
+
|
|
16
|
+
Failure is deliberately quiet: if the refresh cannot happen (offline, token
|
|
17
|
+
revoked) the stored token is returned unchanged, the request fails with the
|
|
18
|
+
gateway's own 401, and /login remains the way back in.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
import urllib.parse
|
|
25
|
+
import urllib.request
|
|
26
|
+
from datetime import datetime, timedelta, timezone
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any, Callable, Dict, Optional
|
|
29
|
+
|
|
30
|
+
SECURE_TOKEN_URL = "https://securetoken.googleapis.com/v1/token"
|
|
31
|
+
|
|
32
|
+
#: Refresh when the token has this long or less left, so a request that starts
|
|
33
|
+
#: just before expiry is not sent with a token that dies in flight.
|
|
34
|
+
REFRESH_WITHIN_SECONDS = 300
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _parse_expiry(value: Any) -> Optional[datetime]:
|
|
38
|
+
if not value:
|
|
39
|
+
return None
|
|
40
|
+
try:
|
|
41
|
+
return datetime.fromisoformat(str(value).replace("Z", "+00:00"))
|
|
42
|
+
except ValueError:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def needs_refresh(config: Dict[str, Any], now: Optional[datetime] = None) -> bool:
|
|
47
|
+
"""True when the stored token is expired or about to be, and can be renewed."""
|
|
48
|
+
if not config.get("auth_token") or not config.get("refresh_token"):
|
|
49
|
+
return False
|
|
50
|
+
expires = _parse_expiry(config.get("token_expires_at"))
|
|
51
|
+
if expires is None:
|
|
52
|
+
# A login from before expiry was recorded: nothing to compare against,
|
|
53
|
+
# so leave it alone rather than refresh on every call.
|
|
54
|
+
return False
|
|
55
|
+
now = now or datetime.now(timezone.utc)
|
|
56
|
+
return expires - now <= timedelta(seconds=REFRESH_WITHIN_SECONDS)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def exchange_refresh_token(refresh_token: str, api_key: str, *, timeout: int = 15,
|
|
60
|
+
opener: Callable = urllib.request.urlopen) -> Dict[str, Any]:
|
|
61
|
+
"""Trade a refresh token for a new ID token. Raises on any failure."""
|
|
62
|
+
body = urllib.parse.urlencode({"grant_type": "refresh_token", "refresh_token": refresh_token}).encode()
|
|
63
|
+
request = urllib.request.Request(
|
|
64
|
+
f"{SECURE_TOKEN_URL}?key={urllib.parse.quote(api_key)}", data=body,
|
|
65
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST",
|
|
66
|
+
)
|
|
67
|
+
with opener(request, timeout=timeout) as response:
|
|
68
|
+
return json.loads(response.read().decode("utf-8"))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def refresh_config_file(path: Path, *, now: Optional[datetime] = None,
|
|
72
|
+
exchange: Optional[Callable[[str, str], Dict[str, Any]]] = None) -> Optional[str]:
|
|
73
|
+
"""Renew the session stored in `path` if it is due; return the token to use.
|
|
74
|
+
|
|
75
|
+
Returns the current (possibly just refreshed) ID token, or None when there
|
|
76
|
+
is no session. Never raises: a failed refresh returns the stored token.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
config = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
80
|
+
except (OSError, ValueError):
|
|
81
|
+
return None
|
|
82
|
+
token = config.get("auth_token")
|
|
83
|
+
token = str(token) if token else None
|
|
84
|
+
if not token or not needs_refresh(config, now):
|
|
85
|
+
return token
|
|
86
|
+
|
|
87
|
+
from .google_login import FIREBASE_WEB_API_KEY
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
result = (exchange or exchange_refresh_token)(str(config["refresh_token"]), FIREBASE_WEB_API_KEY)
|
|
91
|
+
new_token = result.get("id_token") or result.get("idToken")
|
|
92
|
+
if not new_token:
|
|
93
|
+
return token
|
|
94
|
+
# Do not let a slow refresh for account A replace a later login to B.
|
|
95
|
+
target = Path(path)
|
|
96
|
+
current = json.loads(target.read_text(encoding="utf-8"))
|
|
97
|
+
keys = ("user_id", "auth_token", "refresh_token")
|
|
98
|
+
if any(current.get(key) != config.get(key) for key in keys):
|
|
99
|
+
return None
|
|
100
|
+
if config.get("user_id") and result.get("user_id") and result["user_id"] != config["user_id"]:
|
|
101
|
+
return None
|
|
102
|
+
config["auth_token"] = new_token
|
|
103
|
+
if result.get("refresh_token") or result.get("refreshToken"):
|
|
104
|
+
config["refresh_token"] = result.get("refresh_token") or result.get("refreshToken")
|
|
105
|
+
seconds = int(result.get("expires_in") or result.get("expiresIn") or 3600)
|
|
106
|
+
stamp = (now or datetime.now(timezone.utc)) + timedelta(seconds=seconds)
|
|
107
|
+
config["token_expires_at"] = stamp.isoformat().replace("+00:00", "Z")
|
|
108
|
+
from aria_code.packages.aria_core.secure_file import write_secret_json
|
|
109
|
+
|
|
110
|
+
# Replace the file in one step: this is the whole config, and a crash
|
|
111
|
+
# halfway through an in-place write would lose every setting in it.
|
|
112
|
+
target = Path(path)
|
|
113
|
+
scratch = target.with_name(target.name + ".refresh")
|
|
114
|
+
write_secret_json(scratch, config)
|
|
115
|
+
import os
|
|
116
|
+
|
|
117
|
+
os.replace(scratch, target)
|
|
118
|
+
return str(new_token)
|
|
119
|
+
except Exception:
|
|
120
|
+
return token
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
__all__ = ["needs_refresh", "refresh_config_file", "exchange_refresh_token", "REFRESH_WITHIN_SECONDS"]
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""JSON-backed session persistence for Aria Code."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
import tempfile
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
from aria_code.apps.cli.config_paths import resolve_config_dir
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SessionManager:
|
|
15
|
+
"""Manage chat sessions with local file persistence."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, sessions_dir: Optional[Path] = None):
|
|
18
|
+
self.root = sessions_dir or (resolve_config_dir() / "sessions")
|
|
19
|
+
self.root.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
def _path(self, session_id: str) -> Path:
|
|
22
|
+
return self.root / f"{session_id}.json"
|
|
23
|
+
|
|
24
|
+
def save_session(self, session_id: str, conversation: list, metadata: dict = None):
|
|
25
|
+
meta = dict(metadata or {})
|
|
26
|
+
if not meta.get("created_at"):
|
|
27
|
+
meta["created_at"] = datetime.now().isoformat()
|
|
28
|
+
for msg in conversation:
|
|
29
|
+
if msg.get("role") == "user":
|
|
30
|
+
meta.setdefault("title", str(msg.get("content", ""))[:60])
|
|
31
|
+
break
|
|
32
|
+
data = {
|
|
33
|
+
"id": session_id,
|
|
34
|
+
"messages": conversation,
|
|
35
|
+
"metadata": meta,
|
|
36
|
+
"updated_at": datetime.now().isoformat(),
|
|
37
|
+
}
|
|
38
|
+
path = self._path(session_id)
|
|
39
|
+
# A killed terminal must leave either the prior session or the new
|
|
40
|
+
# one, never a half-written JSON file that cannot be resumed.
|
|
41
|
+
temporary = None
|
|
42
|
+
try:
|
|
43
|
+
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=self.root,
|
|
44
|
+
prefix=".session-", suffix=".tmp", delete=False) as f:
|
|
45
|
+
temporary = f.name
|
|
46
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
47
|
+
f.flush()
|
|
48
|
+
os.fsync(f.fileno())
|
|
49
|
+
os.replace(temporary, path)
|
|
50
|
+
finally:
|
|
51
|
+
if temporary and os.path.exists(temporary):
|
|
52
|
+
os.unlink(temporary)
|
|
53
|
+
|
|
54
|
+
def load_session(self, session_id: str) -> Optional[dict]:
|
|
55
|
+
path = self._path(session_id)
|
|
56
|
+
if path.exists():
|
|
57
|
+
with open(path, encoding="utf-8") as f:
|
|
58
|
+
return json.load(f)
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
def list_sessions(self, limit: int = 20) -> list:
|
|
62
|
+
sessions = []
|
|
63
|
+
for path in sorted(self.root.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True):
|
|
64
|
+
try:
|
|
65
|
+
with open(path, encoding="utf-8") as f:
|
|
66
|
+
data = json.load(f)
|
|
67
|
+
sessions.append({
|
|
68
|
+
"id": data.get("id", path.stem),
|
|
69
|
+
"title": data.get("metadata", {}).get("title", "Untitled"),
|
|
70
|
+
"messages": len(data.get("messages", [])),
|
|
71
|
+
"updated": data.get("updated_at", ""),
|
|
72
|
+
})
|
|
73
|
+
except Exception:
|
|
74
|
+
continue
|
|
75
|
+
if len(sessions) >= limit:
|
|
76
|
+
break
|
|
77
|
+
return sessions
|
|
78
|
+
|
|
79
|
+
def search_sessions(self, query: str, limit: int = 20) -> list:
|
|
80
|
+
"""Full-text search through session message content."""
|
|
81
|
+
q = query.lower()
|
|
82
|
+
results = []
|
|
83
|
+
for path in sorted(self.root.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True):
|
|
84
|
+
try:
|
|
85
|
+
with open(path, encoding="utf-8") as f:
|
|
86
|
+
data = json.load(f)
|
|
87
|
+
messages = data.get("messages", [])
|
|
88
|
+
hits = []
|
|
89
|
+
for msg in messages:
|
|
90
|
+
content = msg.get("content", "")
|
|
91
|
+
if isinstance(content, str):
|
|
92
|
+
if q in content.lower():
|
|
93
|
+
idx = content.lower().index(q)
|
|
94
|
+
start = max(0, idx - 20)
|
|
95
|
+
end = min(len(content), idx + len(q) + 80)
|
|
96
|
+
hits.append(content[start:end])
|
|
97
|
+
elif isinstance(content, list):
|
|
98
|
+
for block in content:
|
|
99
|
+
if isinstance(block, dict):
|
|
100
|
+
text = block.get("text", "")
|
|
101
|
+
if text and q in text.lower():
|
|
102
|
+
idx = text.lower().index(q)
|
|
103
|
+
start = max(0, idx - 20)
|
|
104
|
+
end = min(len(text), idx + len(q) + 80)
|
|
105
|
+
hits.append(text[start:end])
|
|
106
|
+
if hits:
|
|
107
|
+
results.append({
|
|
108
|
+
"id": data.get("id", path.stem),
|
|
109
|
+
"title": data.get("metadata", {}).get("title", "Untitled"),
|
|
110
|
+
"updated": data.get("updated_at", ""),
|
|
111
|
+
"match_count": len(hits),
|
|
112
|
+
"preview": hits[0],
|
|
113
|
+
})
|
|
114
|
+
except Exception:
|
|
115
|
+
continue
|
|
116
|
+
if len(results) >= limit:
|
|
117
|
+
break
|
|
118
|
+
return sorted(results, key=lambda r: r["match_count"], reverse=True)
|
|
119
|
+
|
|
120
|
+
def delete_session(self, session_id: str) -> bool:
|
|
121
|
+
path = self._path(session_id)
|
|
122
|
+
if path.exists():
|
|
123
|
+
path.unlink()
|
|
124
|
+
return True
|
|
125
|
+
return False
|