comate-cli 0.8.5__tar.gz → 0.8.6__tar.gz
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.
- {comate_cli-0.8.5 → comate_cli-0.8.6}/PKG-INFO +1 -1
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/error_display.py +40 -41
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/event_renderer.py +2 -2
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/status_bar.py +92 -46
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tool_view.py +2 -2
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui.py +15 -1
- {comate_cli-0.8.5 → comate_cli-0.8.6}/pyproject.toml +1 -1
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_format_error.py +69 -18
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_status_bar.py +103 -42
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_provider_error_shield.py +41 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/uv.lock +2 -2
- {comate_cli-0.8.5 → comate_cli-0.8.6}/.gitignore +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/CHANGELOG.md +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/CONTEXT.md +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/README.md +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/__main__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/main.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/mcp_cli.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/animations.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/app.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/assistant_render.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/builtin_commands/.keep +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/codenames.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/config/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/config/model.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/config/picker.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/config/picker_state.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/config/store.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/custom_slash_commands.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/env_utils.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/figures.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/file_ref_hint.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/fragment_utils.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/goal_resume_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/history_printer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/input_geometry.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/layout_coordinator.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/logging_adapter.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/logo.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/markdown_render.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/mention_completer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/message_style.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/models.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/path_context_hint.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/components/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/components/detail_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/components/plugin_list.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/components/search_box.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/components/tab_bar.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/marketplace_install_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/operation_state.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/plugin_picker.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/tabs/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/tabs/discover_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/tabs/errors_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/tabs/installed_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/plugins/tabs/marketplaces_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/preflight.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/preflight_wizard.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/print_mode.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/question_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/recap/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/recap/controller.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/resume_picker.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/resume_preview.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/resume_selector.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/rpc_protocol.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/rpc_stdio.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/selection_menu.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/slash_commands.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/startup.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/startup_profile.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/statusline/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/statusline/model.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/statusline/picker.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/statusline/picker_state.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/statusline/store.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/text_effects.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tips.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tool_fold.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tool_result_formatters.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tool_result_store.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tool_result_viewer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/transcript_viewer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/btw_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/commands.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/history_sync.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/input_behavior.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/key_bindings.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/mcp_connecting_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/render_panels.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/slash_command_registry.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/tui_parts/ui_mode.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/comate_cli/terminal_agent/update_check.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/dist-comate/.gitignore +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/dist-comate/comate_cli-0.7.10-py3-none-any.whl +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/dist-comate/comate_cli-0.7.10.tar.gz +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/dist-hico/.gitignore +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/dist-hico/hico_cli-0.7.17-py3-none-any.whl +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/dist-hico/hico_cli-0.7.17.tar.gz +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/docs/cli-rpc-stdio-guide.md +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/test_model.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/test_picker_state.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/test_picker_ui.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/test_roundtrip.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/test_store_load.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/config/test_store_save.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/conftest.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/fixtures/fake_mcp_misbehaving_stdout.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/recap/test_idle_recap_controller.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/recap/test_recap_history_rendering.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/recap/test_tui_idle_recap_integration.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/statusline/__init__.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/statusline/test_model.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/statusline/test_picker_state.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/statusline/test_store.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_animator_shuffle.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_mcp_preload.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_preflight_gate.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_print_mode.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_shutdown.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_startup_latency.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_token_cost_config.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_app_usage_line.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_btw_slash_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_cli_project_root.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_compact_command_semantics.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_completion_context_activation.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_completion_status_panel.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_context_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_custom_slash_commands.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_discover_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_errors_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_boundary.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_e2e.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_log_boundary.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_log_queue.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_streaming.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_tool_args_progress.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_event_renderer_tool_fold.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_file_ref_hint.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_goal_resume_tui.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_goal_resume_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_goal_slash_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_handle_error.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_history_printer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_history_printer_log.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_history_printer_subtitle_position.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_history_printer_tool_fold.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_history_sync.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_history_sync_tool_fold.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_input_behavior.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_input_history.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_installed_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_interrupt_exit_semantics.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_layout_coordinator.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_logging_adapter.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_logo.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_main_args.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_markdown_render.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_marketplaces_tab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_mcp_cli.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_mcp_slash_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_mention_completer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_model_switch_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_path_context_hint.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_plugin_operation_state.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_plugin_slash_commands.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_plugin_tui_components.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_preflight.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_preflight_copilot.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_question_key_bindings.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_question_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_resume_picker.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_resume_preview.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_resume_selector.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_rewind_command_semantics.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_rpc_protocol.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_rpc_stdio_bridge.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_selection_menu.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_session_query_token_summary.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_shutdown_noise_guard.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_shutdown_noise_integration.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_skills_slash_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_slash_argument_hint.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_slash_clear.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_slash_completer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_slash_registry.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_startup_import_budget.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_startup_profile.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_status_bar_transient.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_task_panel_format.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_task_panel_key_bindings.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_task_panel_rendering.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_task_poll.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tips.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_fold.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_fold_panel.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_result_formatters.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_result_store.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_result_viewer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_result_viewer_key_bindings.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tool_view.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_transcript_viewer.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_transcript_viewer_tool_fold.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_builtin_slash_chain.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_elapsed_status.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_esc_queue.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_llm_retry_panel.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_local_interactive_barrier.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_mcp_init_gate.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_paste_newline_guard.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_paste_placeholder.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_queue_preview.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_queue_sdk_source.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_scheduled_fire_log.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_send_undo_window.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_split_invariance.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_startup_latency.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_team_messages.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_thinking_display.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_tui_tool_result_registry_lifecycle.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_update_check.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_usage_command.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tests/test_wrap_user_text.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tools/logo_lab.py +0 -0
- {comate_cli-0.8.5 → comate_cli-0.8.6}/tools/nico_preview.py +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Unified error formatter for terminal agent."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import ast
|
|
5
|
+
import json
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def format_error(exc: Exception) -> tuple[str, str, str]:
|
|
@@ -42,27 +43,14 @@ def format_error(exc: Exception) -> tuple[str, str, str]:
|
|
|
42
43
|
|
|
43
44
|
if exc_type == "ModelProviderError":
|
|
44
45
|
code = getattr(exc, "status_code", None)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"error",
|
|
54
|
-
)
|
|
55
|
-
return f"API error: {exc_msg}", "API error", "error"
|
|
56
|
-
|
|
57
|
-
if code == 401:
|
|
58
|
-
return "Invalid or expired API key", "Auth failed", "error"
|
|
59
|
-
if code == 403:
|
|
60
|
-
return "Access denied to this model", "Access denied", "error"
|
|
61
|
-
if code == 404:
|
|
62
|
-
return "Model not found or invalid API path", "Model not found", "error"
|
|
63
|
-
if code and code >= 500:
|
|
64
|
-
return f"Server error ({code})", "Server error", "warning"
|
|
65
|
-
return f"API error: {exc_msg}", "API error", "error"
|
|
46
|
+
detail = _provider_error_detail(exc_msg)
|
|
47
|
+
status = f" (HTTP {code})" if code is not None else ""
|
|
48
|
+
message = f"Provider error{status}"
|
|
49
|
+
if detail:
|
|
50
|
+
message = f"{message}: {detail}"
|
|
51
|
+
transient = f"Provider error {code}" if code is not None else "Provider error"
|
|
52
|
+
severity = "warning" if code is not None and code >= 500 else "error"
|
|
53
|
+
return message, transient, severity
|
|
66
54
|
|
|
67
55
|
# Session errors
|
|
68
56
|
if exc_type == "ChatSessionClosedError":
|
|
@@ -79,23 +67,34 @@ def format_error(exc: Exception) -> tuple[str, str, str]:
|
|
|
79
67
|
return f"Error: {exc_msg}", "Error occurred", "error"
|
|
80
68
|
|
|
81
69
|
|
|
82
|
-
def
|
|
83
|
-
"""
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
70
|
+
def _provider_error_detail(msg: str) -> str:
|
|
71
|
+
"""Return provider-supplied detail, unwrapped from common SDK envelopes."""
|
|
72
|
+
text = str(msg or "").strip()
|
|
73
|
+
if not text:
|
|
74
|
+
return ""
|
|
75
|
+
|
|
76
|
+
candidates = [text]
|
|
77
|
+
if " - " in text:
|
|
78
|
+
candidates.append(text.split(" - ", 1)[1].strip())
|
|
79
|
+
|
|
80
|
+
for candidate in candidates:
|
|
81
|
+
detail = _error_message_from_mapping_text(candidate)
|
|
82
|
+
if detail:
|
|
83
|
+
return detail
|
|
84
|
+
return text
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _error_message_from_mapping_text(text: str) -> str | None:
|
|
88
|
+
for parser in (json.loads, ast.literal_eval):
|
|
89
|
+
try:
|
|
90
|
+
value = parser(text)
|
|
91
|
+
except (ValueError, SyntaxError, TypeError):
|
|
92
|
+
continue
|
|
93
|
+
if not isinstance(value, dict):
|
|
94
|
+
continue
|
|
95
|
+
error = value.get("error")
|
|
96
|
+
if isinstance(error, dict):
|
|
97
|
+
message = error.get("message")
|
|
98
|
+
if isinstance(message, str) and message.strip():
|
|
99
|
+
return message.strip()
|
|
101
100
|
return None
|
|
@@ -120,10 +120,10 @@ def _format_duration(seconds: float) -> str:
|
|
|
120
120
|
minutes = int(elapsed // 60)
|
|
121
121
|
remaining_seconds = int(elapsed % 60)
|
|
122
122
|
if minutes < 60:
|
|
123
|
-
return f"{minutes}m{remaining_seconds:02d}s"
|
|
123
|
+
return f"{minutes}m {remaining_seconds:02d}s"
|
|
124
124
|
hours = minutes // 60
|
|
125
125
|
remaining_minutes = minutes % 60
|
|
126
|
-
return f"{hours}h{remaining_minutes:02d}m"
|
|
126
|
+
return f"{hours}h {remaining_minutes:02d}m"
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
def _format_tokens(token_count: int) -> str:
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import contextlib
|
|
4
5
|
import logging
|
|
6
|
+
import os
|
|
5
7
|
import subprocess
|
|
6
8
|
import time
|
|
7
9
|
from pathlib import Path
|
|
@@ -22,11 +24,17 @@ class GitDiffStats(NamedTuple):
|
|
|
22
24
|
removed: int
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
class _GitCommandResult(NamedTuple):
|
|
28
|
+
returncode: int
|
|
29
|
+
stdout: str
|
|
30
|
+
|
|
31
|
+
|
|
25
32
|
class StatusBar:
|
|
26
33
|
_DEFAULT_TERMINAL_WIDTH: int = 100
|
|
27
34
|
_MIN_TERMINAL_WIDTH: int = 40
|
|
28
35
|
_GIT_DIFF_CACHE_SECONDS: float = 5.0
|
|
29
36
|
_GIT_CMD_TIMEOUT_SECONDS: float = 0.2
|
|
37
|
+
_GIT_FAILURE_BACKOFF_SECONDS: float = 30.0
|
|
30
38
|
_SEPARATOR: str = " \u00b7 " # " · "
|
|
31
39
|
|
|
32
40
|
# 分组颜色
|
|
@@ -46,6 +54,7 @@ class StatusBar:
|
|
|
46
54
|
self._context_left_pct: float = 100.0
|
|
47
55
|
self._git_diff_stats: GitDiffStats | None = None
|
|
48
56
|
self._git_diff_cache_time: float = 0.0
|
|
57
|
+
self._git_failure_backoff_until: float = 0.0
|
|
49
58
|
self._transient_message: str | None = None
|
|
50
59
|
self._transient_severity: str = "info"
|
|
51
60
|
self._transient_until: float | None = None
|
|
@@ -129,43 +138,76 @@ class StatusBar:
|
|
|
129
138
|
normalized = str(mode).strip().lower()
|
|
130
139
|
self._mode = normalized or "act"
|
|
131
140
|
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
def _git_backoff_active(self) -> bool:
|
|
142
|
+
return time.monotonic() < self._git_failure_backoff_until
|
|
143
|
+
|
|
144
|
+
def _record_git_failure_backoff(self, *, reason: str, args: tuple[str, ...]) -> None:
|
|
145
|
+
self._git_failure_backoff_until = (
|
|
146
|
+
time.monotonic() + self._GIT_FAILURE_BACKOFF_SECONDS
|
|
147
|
+
)
|
|
148
|
+
logger.debug(
|
|
149
|
+
"status bar git refresh disabled temporarily: reason=%s args=%s",
|
|
150
|
+
reason,
|
|
151
|
+
" ".join(args),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
async def _run_git_command(self, *args: str) -> _GitCommandResult | None:
|
|
155
|
+
if self._git_backoff_active():
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
env = os.environ.copy()
|
|
159
|
+
env.setdefault("GIT_TERMINAL_PROMPT", "0")
|
|
160
|
+
env.setdefault("GIT_OPTIONAL_LOCKS", "0")
|
|
161
|
+
env.setdefault("GCM_INTERACTIVE", "Never")
|
|
162
|
+
proc: asyncio.subprocess.Process | None = None
|
|
134
163
|
try:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
164
|
+
proc = await asyncio.create_subprocess_exec(
|
|
165
|
+
"git",
|
|
166
|
+
*args,
|
|
167
|
+
stdin=subprocess.DEVNULL,
|
|
168
|
+
stdout=asyncio.subprocess.PIPE,
|
|
169
|
+
stderr=subprocess.DEVNULL,
|
|
170
|
+
env=env,
|
|
142
171
|
)
|
|
143
|
-
|
|
144
|
-
|
|
172
|
+
stdout, _stderr = await asyncio.wait_for(
|
|
173
|
+
proc.communicate(),
|
|
174
|
+
timeout=self._GIT_CMD_TIMEOUT_SECONDS,
|
|
175
|
+
)
|
|
176
|
+
except asyncio.TimeoutError:
|
|
177
|
+
self._record_git_failure_backoff(reason="timeout", args=args)
|
|
178
|
+
if proc is not None and proc.returncode is None:
|
|
179
|
+
with contextlib.suppress(ProcessLookupError):
|
|
180
|
+
proc.kill()
|
|
181
|
+
with contextlib.suppress(Exception):
|
|
182
|
+
await asyncio.wait_for(proc.wait(), timeout=0.2)
|
|
183
|
+
return None
|
|
145
184
|
except Exception:
|
|
146
|
-
|
|
185
|
+
self._record_git_failure_backoff(reason="error", args=args)
|
|
186
|
+
if proc is not None and proc.returncode is None:
|
|
187
|
+
with contextlib.suppress(ProcessLookupError):
|
|
188
|
+
proc.kill()
|
|
189
|
+
with contextlib.suppress(Exception):
|
|
190
|
+
await asyncio.wait_for(proc.wait(), timeout=0.2)
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
return _GitCommandResult(
|
|
194
|
+
returncode=int(proc.returncode or 0),
|
|
195
|
+
stdout=stdout.decode("utf-8", errors="replace"),
|
|
196
|
+
)
|
|
147
197
|
|
|
198
|
+
async def _resolve_git_branch(self) -> str:
|
|
199
|
+
completed = await self._run_git_command("branch", "--show-current")
|
|
200
|
+
if completed is None:
|
|
201
|
+
return "N/A"
|
|
148
202
|
if completed.returncode != 0:
|
|
149
203
|
return "N/A"
|
|
150
204
|
branch = completed.stdout.strip()
|
|
151
205
|
return branch or "N/A"
|
|
152
206
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
completed = subprocess.run(
|
|
157
|
-
["git", "diff", "--shortstat", "--no-color"],
|
|
158
|
-
check=False,
|
|
159
|
-
capture_output=True,
|
|
160
|
-
text=True,
|
|
161
|
-
encoding="utf-8",
|
|
162
|
-
timeout=StatusBar._GIT_CMD_TIMEOUT_SECONDS,
|
|
163
|
-
)
|
|
164
|
-
except subprocess.TimeoutExpired:
|
|
165
|
-
return None
|
|
166
|
-
except Exception:
|
|
207
|
+
async def _resolve_git_diff_stats(self) -> GitDiffStats | None:
|
|
208
|
+
completed = await self._run_git_command("diff", "--shortstat", "--no-color")
|
|
209
|
+
if completed is None:
|
|
167
210
|
return None
|
|
168
|
-
|
|
169
211
|
if completed.returncode != 0:
|
|
170
212
|
return None
|
|
171
213
|
|
|
@@ -185,16 +227,6 @@ class StatusBar:
|
|
|
185
227
|
|
|
186
228
|
return GitDiffStats(added=added, removed=removed)
|
|
187
229
|
|
|
188
|
-
def _ensure_git_diff_stats(self) -> None:
|
|
189
|
-
now = time.monotonic()
|
|
190
|
-
if (
|
|
191
|
-
self._git_diff_stats is not None
|
|
192
|
-
and now - self._git_diff_cache_time < self._GIT_DIFF_CACHE_SECONDS
|
|
193
|
-
):
|
|
194
|
-
return
|
|
195
|
-
self._git_diff_stats = self._resolve_git_diff_stats()
|
|
196
|
-
self._git_diff_cache_time = now
|
|
197
|
-
|
|
198
230
|
def _resolve_llm_level(self) -> str:
|
|
199
231
|
agent = getattr(self._session, "_agent", None)
|
|
200
232
|
level = getattr(agent, "level", None)
|
|
@@ -294,23 +326,37 @@ class StatusBar:
|
|
|
294
326
|
except Exception:
|
|
295
327
|
pass
|
|
296
328
|
|
|
297
|
-
|
|
329
|
+
cfg = self._config
|
|
330
|
+
branch_task = (
|
|
331
|
+
asyncio.create_task(self._resolve_git_branch())
|
|
332
|
+
if cfg.show_git_branch
|
|
333
|
+
else None
|
|
334
|
+
)
|
|
298
335
|
now = time.monotonic()
|
|
299
336
|
diff_is_stale = (
|
|
300
|
-
|
|
301
|
-
|
|
337
|
+
cfg.show_git_diff
|
|
338
|
+
and (
|
|
339
|
+
self._git_diff_stats is None
|
|
340
|
+
or now - self._git_diff_cache_time >= self._GIT_DIFF_CACHE_SECONDS
|
|
341
|
+
)
|
|
302
342
|
)
|
|
303
|
-
|
|
304
|
-
|
|
343
|
+
diff_task = (
|
|
344
|
+
asyncio.create_task(self._resolve_git_diff_stats())
|
|
345
|
+
if diff_is_stale
|
|
346
|
+
else None
|
|
347
|
+
)
|
|
348
|
+
if branch_task is not None and diff_task is not None:
|
|
305
349
|
branch, diff_stats = await asyncio.gather(branch_task, diff_task)
|
|
350
|
+
self._git_branch = branch
|
|
306
351
|
self._git_diff_stats = diff_stats
|
|
307
352
|
self._git_diff_cache_time = time.monotonic()
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
353
|
+
elif branch_task is not None:
|
|
354
|
+
self._git_branch = await branch_task
|
|
355
|
+
elif diff_task is not None:
|
|
356
|
+
self._git_diff_stats = await diff_task
|
|
357
|
+
self._git_diff_cache_time = time.monotonic()
|
|
311
358
|
|
|
312
359
|
# Refresh token usage cache if any token-related items are enabled.
|
|
313
|
-
cfg = self._config
|
|
314
360
|
wants_token_counts = cfg.show_token_usage or cfg.show_token_input or cfg.show_token_output
|
|
315
361
|
wants_cost = cfg.show_session_cost
|
|
316
362
|
if wants_cost:
|
|
@@ -333,10 +333,10 @@ def _format_duration(seconds: float) -> str:
|
|
|
333
333
|
minutes = int(elapsed // 60)
|
|
334
334
|
remaining_seconds = int(elapsed % 60)
|
|
335
335
|
if minutes < 60:
|
|
336
|
-
return f"{minutes}m{remaining_seconds:02d}s"
|
|
336
|
+
return f"{minutes}m {remaining_seconds:02d}s"
|
|
337
337
|
hours = minutes // 60
|
|
338
338
|
remaining_minutes = minutes % 60
|
|
339
|
-
return f"{hours}h{remaining_minutes:02d}m"
|
|
339
|
+
return f"{hours}h {remaining_minutes:02d}m"
|
|
340
340
|
|
|
341
341
|
|
|
342
342
|
def _sweep_gradient_text(content: str, frame: int) -> Text:
|
|
@@ -186,6 +186,7 @@ class TerminalAgentTUI(
|
|
|
186
186
|
RenderPanelsMixin,
|
|
187
187
|
):
|
|
188
188
|
_NON_BLOCKING_UI_MODES = frozenset({UIMode.NORMAL})
|
|
189
|
+
_STATUS_REFRESH_TIMEOUT_SECONDS = 0.3
|
|
189
190
|
|
|
190
191
|
def __init__(
|
|
191
192
|
self,
|
|
@@ -206,6 +207,7 @@ class TerminalAgentTUI(
|
|
|
206
207
|
set_tool_result_registry(self._tool_result_registry)
|
|
207
208
|
self._task_poll_next_at = time.monotonic() + _TASK_POLL_INTERVAL_S
|
|
208
209
|
self._task_poll_last_list_id: str | None = None
|
|
210
|
+
self._status_refresh_timeout_s = self._STATUS_REFRESH_TIMEOUT_SECONDS
|
|
209
211
|
|
|
210
212
|
self._bind_team_message_callback(self._session)
|
|
211
213
|
|
|
@@ -1497,7 +1499,19 @@ class TerminalAgentTUI(
|
|
|
1497
1499
|
|
|
1498
1500
|
async def _refresh_status_bar_safely(self, *, reason: str) -> None:
|
|
1499
1501
|
try:
|
|
1500
|
-
|
|
1502
|
+
timeout_s = max(
|
|
1503
|
+
0.01,
|
|
1504
|
+
float(getattr(self, "_status_refresh_timeout_s", 0.3)),
|
|
1505
|
+
)
|
|
1506
|
+
await asyncio.wait_for(
|
|
1507
|
+
self._status_bar.refresh(),
|
|
1508
|
+
timeout=timeout_s,
|
|
1509
|
+
)
|
|
1510
|
+
except asyncio.TimeoutError:
|
|
1511
|
+
logger.debug(
|
|
1512
|
+
"TUI status bar refresh timed out during %s; continuing event pump",
|
|
1513
|
+
reason,
|
|
1514
|
+
)
|
|
1501
1515
|
except Exception:
|
|
1502
1516
|
logger.warning(
|
|
1503
1517
|
"TUI status bar refresh failed during %s; continuing event pump",
|
|
@@ -26,38 +26,86 @@ class TestFormatError(unittest.TestCase):
|
|
|
26
26
|
def test_provider_401(self):
|
|
27
27
|
from comate_cli.terminal_agent.error_display import format_error
|
|
28
28
|
|
|
29
|
-
exc = self._make_exc(
|
|
29
|
+
exc = self._make_exc(
|
|
30
|
+
"ModelProviderError",
|
|
31
|
+
"provider says the API key is expired",
|
|
32
|
+
status_code=401,
|
|
33
|
+
)
|
|
30
34
|
msg, transient, severity = format_error(exc)
|
|
31
35
|
assert severity == "error"
|
|
32
|
-
assert "API key
|
|
33
|
-
assert transient == "
|
|
36
|
+
assert msg == "Provider error (HTTP 401): provider says the API key is expired"
|
|
37
|
+
assert transient == "Provider error 401"
|
|
34
38
|
|
|
35
39
|
def test_provider_403(self):
|
|
36
40
|
from comate_cli.terminal_agent.error_display import format_error
|
|
37
41
|
|
|
38
|
-
exc = self._make_exc(
|
|
42
|
+
exc = self._make_exc(
|
|
43
|
+
"ModelProviderError",
|
|
44
|
+
"预扣费额度失败, 用户剩余额度: ¥0.000015, 需要预扣费额度: ¥0.000555",
|
|
45
|
+
status_code=403,
|
|
46
|
+
)
|
|
39
47
|
msg, transient, severity = format_error(exc)
|
|
40
48
|
assert severity == "error"
|
|
41
|
-
assert "
|
|
42
|
-
assert
|
|
49
|
+
assert "预扣费额度失败" in msg
|
|
50
|
+
assert "Access denied" not in msg
|
|
51
|
+
assert transient == "Provider error 403"
|
|
43
52
|
|
|
44
53
|
def test_provider_404(self):
|
|
45
54
|
from comate_cli.terminal_agent.error_display import format_error
|
|
46
55
|
|
|
47
|
-
exc = self._make_exc(
|
|
56
|
+
exc = self._make_exc(
|
|
57
|
+
"ModelProviderError",
|
|
58
|
+
"provider says the model route does not exist",
|
|
59
|
+
status_code=404,
|
|
60
|
+
)
|
|
48
61
|
msg, transient, severity = format_error(exc)
|
|
49
62
|
assert severity == "error"
|
|
50
|
-
assert "not
|
|
51
|
-
assert transient == "
|
|
63
|
+
assert "provider says the model route does not exist" in msg
|
|
64
|
+
assert transient == "Provider error 404"
|
|
52
65
|
|
|
53
66
|
def test_provider_5xx(self):
|
|
54
67
|
from comate_cli.terminal_agent.error_display import format_error
|
|
55
68
|
|
|
56
|
-
exc = self._make_exc(
|
|
69
|
+
exc = self._make_exc(
|
|
70
|
+
"ModelProviderError",
|
|
71
|
+
"No available channel for model MiniMax-M2.7 under group svip",
|
|
72
|
+
status_code=503,
|
|
73
|
+
)
|
|
57
74
|
msg, transient, severity = format_error(exc)
|
|
58
75
|
assert severity == "warning"
|
|
59
|
-
assert "
|
|
60
|
-
assert
|
|
76
|
+
assert "No available channel for model MiniMax-M2.7" in msg
|
|
77
|
+
assert "Server error" not in msg
|
|
78
|
+
assert transient == "Provider error 503"
|
|
79
|
+
|
|
80
|
+
def test_provider_openai_wrapped_error_message(self):
|
|
81
|
+
from comate_cli.terminal_agent.error_display import format_error
|
|
82
|
+
|
|
83
|
+
body = (
|
|
84
|
+
"Error code: 403 - {'error': {'message': '预扣费额度失败, "
|
|
85
|
+
"用户剩余额度: ¥0.000015', 'type': 'new_api_error', "
|
|
86
|
+
"'code': 'insufficient_user_quota'}}"
|
|
87
|
+
)
|
|
88
|
+
exc = self._make_exc("ModelProviderError", body, status_code=403)
|
|
89
|
+
msg, transient, severity = format_error(exc)
|
|
90
|
+
assert severity == "error"
|
|
91
|
+
assert msg == "Provider error (HTTP 403): 预扣费额度失败, 用户剩余额度: ¥0.000015"
|
|
92
|
+
assert transient == "Provider error 403"
|
|
93
|
+
|
|
94
|
+
def test_provider_json_error_message(self):
|
|
95
|
+
from comate_cli.terminal_agent.error_display import format_error
|
|
96
|
+
|
|
97
|
+
body = (
|
|
98
|
+
'{"error":{"type":"new_api_error","message":"预扣费额度失败, '
|
|
99
|
+
'用户剩余额度: ¥0.000015, 需要预扣费额度: ¥0.001664"},"type":"error"}'
|
|
100
|
+
)
|
|
101
|
+
exc = self._make_exc("ModelProviderError", body, status_code=403)
|
|
102
|
+
msg, transient, severity = format_error(exc)
|
|
103
|
+
assert severity == "error"
|
|
104
|
+
assert msg == (
|
|
105
|
+
"Provider error (HTTP 403): 预扣费额度失败, "
|
|
106
|
+
"用户剩余额度: ¥0.000015, 需要预扣费额度: ¥0.001664"
|
|
107
|
+
)
|
|
108
|
+
assert transient == "Provider error 403"
|
|
61
109
|
|
|
62
110
|
def test_provider_stream_protocol_error(self):
|
|
63
111
|
from comate_agent_sdk.llm.exceptions import ProviderStreamProtocolError
|
|
@@ -122,9 +170,11 @@ class TestFormatError(unittest.TestCase):
|
|
|
122
170
|
exc = self._make_exc("ModelProviderError", body, status_code=400)
|
|
123
171
|
msg, transient, severity = format_error(exc)
|
|
124
172
|
assert severity == "error"
|
|
125
|
-
assert
|
|
126
|
-
|
|
127
|
-
|
|
173
|
+
assert msg == (
|
|
174
|
+
"Provider error (HTTP 400): request (35099 tokens) exceeds the "
|
|
175
|
+
"available context size (32768 tokens)"
|
|
176
|
+
)
|
|
177
|
+
assert transient == "Provider error 400"
|
|
128
178
|
|
|
129
179
|
def test_context_size_type_without_parseable_numbers(self):
|
|
130
180
|
from comate_cli.terminal_agent.error_display import format_error
|
|
@@ -136,7 +186,8 @@ class TestFormatError(unittest.TestCase):
|
|
|
136
186
|
)
|
|
137
187
|
msg, transient, severity = format_error(exc)
|
|
138
188
|
assert severity == "error"
|
|
139
|
-
assert
|
|
189
|
+
assert "exceed_context_size_error without parseable numbers" in msg
|
|
190
|
+
assert transient == "Provider error 400"
|
|
140
191
|
|
|
141
192
|
def test_provider_400_generic(self):
|
|
142
193
|
from comate_cli.terminal_agent.error_display import format_error
|
|
@@ -144,8 +195,8 @@ class TestFormatError(unittest.TestCase):
|
|
|
144
195
|
exc = self._make_exc("ModelProviderError", "bad request", status_code=400)
|
|
145
196
|
msg, transient, severity = format_error(exc)
|
|
146
197
|
assert severity == "error"
|
|
147
|
-
assert "
|
|
148
|
-
assert transient == "
|
|
198
|
+
assert msg == "Provider error (HTTP 400): bad request"
|
|
199
|
+
assert transient == "Provider error 400"
|
|
149
200
|
|
|
150
201
|
def test_session_closed(self):
|
|
151
202
|
from comate_cli.terminal_agent.error_display import format_error
|