voidx 3.7.0__tar.gz → 3.7.2__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.
- {voidx-3.7.0/src/voidx.egg-info → voidx-3.7.2}/PKG-INFO +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_call_llm_compaction.py +20 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_graph_authorization.py +190 -4
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_subagent_runner.py +14 -2
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_tool_execution_auth.py +1 -1
- voidx-3.7.2/src/tests/test_agent/graph/test_turn_stop_without_pending.py +236 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_workflow_done.py +10 -5
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_workflow_transactions_barrier.py +3 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_model.py +98 -13
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_model_advanced.py +7 -11
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_tavily.py +0 -4
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission.py +30 -48
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission_phase2.py +10 -9
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission_phase4.py +12 -12
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission_phase5.py +5 -6
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission_phase6.py +37 -4
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_tool_filters_gemini.py +111 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_config/test_config.py +0 -4
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_config/test_config_advanced.py +25 -37
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_gemini_provider.py +21 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_llm_catalog_fetchers.py +110 -2
- voidx-3.7.2/src/tests/test_permission/test_risk.py +102 -0
- voidx-3.7.2/src/tests/test_release_npm_wheel_sync.py +97 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_skills/test_workflow_advance.py +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_git_tool_phase5.py +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_git_tool_raw_permissions.py +61 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_powershell_tool_phase6.py +49 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_shell_tool_phase6.py +54 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_adapter.py +46 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_gateway_v2_dispatch.py +2 -6
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_events_subagent.py +22 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_tool_display.py +32 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_tree_smoke.py +18 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_wheel_verify_cwd.py +5 -5
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/__init__.py +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/contracts.py +4 -3
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/core/helpers.py +58 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/core/llm.py +43 -12
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/permissions.py +115 -28
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/subagent.py +2 -2
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/executor.py +18 -13
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/workflow.py +1 -30
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/turn_control.py +6 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/wiring.py +1 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/handler.py +30 -53
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/model.py +45 -36
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/tool_filters.py +19 -8
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/__init__.py +2 -6
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/enums.py +9 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/models.py +2 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings.py +6 -15
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_permissions.py +13 -23
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/permission.md +5 -7
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/reference.md +2 -3
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/catalog.py +134 -25
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/provider.py +18 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/__init__.py +9 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/context.py +7 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/engine.py +70 -41
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/git_policy.py +5 -0
- voidx-3.7.2/src/voidx/permission/presets.py +66 -0
- voidx-3.7.2/src/voidx/permission/risk.py +94 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/rules.py +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/schema.py +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/service.py +30 -96
- voidx-3.7.2/src/voidx/permission/shell_policy.py +243 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/base.py +18 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/tool.py +14 -12
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/tool.py +14 -12
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/service.py +2 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/command_catalog.py +3 -5
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/commands.py +5 -12
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/method/settings.py +8 -16
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/capture.py +4 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/nodes.py +4 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/events/consumers.py +4 -2
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/events/schema.py +3 -0
- voidx-3.7.2/src/voidx/ui/output/tool_display.py +136 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/__init__.py +0 -2
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/nodes.py +0 -73
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/policy.py +0 -4
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/schema.py +0 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/service.py +0 -1
- {voidx-3.7.0 → voidx-3.7.2/src/voidx.egg-info}/PKG-INFO +1 -1
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx.egg-info/SOURCES.txt +5 -1
- voidx-3.7.0/src/voidx/config/permissions.py +0 -24
- voidx-3.7.0/src/voidx/permission/shell_policy.py +0 -138
- voidx-3.7.0/src/voidx/ui/output/tool_display.py +0 -62
- {voidx-3.7.0 → voidx-3.7.2}/LICENSE +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/README.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/pyproject.toml +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/setup.cfg +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/conftest.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/conftest.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/conftest.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/run_loop_helpers.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/stream_llm_helpers.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_call_llm_compaction_advanced.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_call_llm_tools.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_compaction_flow.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_compaction_flow_run_once.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_convergence.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_execute_tools_guard.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_execute_tools_todo.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_graph_setup_prompts.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_guards_tool_op.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_parallel_subagents.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_parallel_subagents_dedup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_prepare_workflow.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_run_loop_startup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_run_loop_title_lsp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_run_loop_title_misc.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_run_loop_workflow.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_run_loop_workflow_advanced.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_runtime_guards.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_context_frames.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_crud.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_messages.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_persistence.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_run_once.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_runtime_state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_session_transcript.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_stream_llm_sanitization.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_subagent_llm_retry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_subagent_persistence.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_subagent_step_budget.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_subagent_step_budget_convergence.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_subagent_step_budget_final.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_todo_events.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_tool_result_preview.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_turn_control_e2e.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_turn_control_integration.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_turn_runner_guidance_discard.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_turn_runner_idle_event.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_workflow_review.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_workflow_transactions.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/loop/test_manager.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/loop/test_prompt_source.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_init.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_loop.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_mcp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_session.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_skills.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/slash/test_slash_upgrade.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_attachments.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_file_rwlock.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_goal_resolver.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_goal_resolver_advanced.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_guard_guidance.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_guide_command.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_message_trimming_parsers.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_message_trimming_rules.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_module_boundaries.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission_append.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_permission_phase3.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_prompts.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_runtime_context_builder.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_runtime_context_prompts.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_runtime_context_skill_stripping.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_task_state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_task_state_context_sync.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_task_state_rendering.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_todo_replay_sanitization.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_tool_exchange_sanitizer.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_tool_messages.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_tool_result_storage.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_turn_control.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_turn_control_prompt.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_turn_metrics.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/test_turn_start.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_archive_script.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_config/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_config/test_retry_config.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_install_sh.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/conftest.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_compaction_retry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_fallback_summary.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_goal_resolver_retry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_instruction_cache.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_llm_catalog.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_llm_provider.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_llm_provider_advanced.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_llm_usage.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_overflow_threshold.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_prune_args.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_request_log.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_streaming_sanitize.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_llm/test_token_counting.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_logging/test_external.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_logging/test_internal_error.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_logging/test_tool_log.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_lsp/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_lsp/test_lsp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_lsp/test_lsp_advanced.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_mcp/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_mcp/test_mcp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_memory/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_memory/test_main.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_memory/test_main_startup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_memory/test_schema_migration.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_npm_package.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_permission/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_runtime/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_runtime/test_goal_resolution_refactor.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_runtime/test_processes.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_runtime/test_runtime_ui.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_selfupdate/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_selfupdate/test_selfupdate.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_skills/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_skills/conftest.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_skills/test_create_skill.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_skills/test_skill_parsing.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_skills/test_skill_references.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_test_runner.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/bash/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/bash/test_router_git.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/bash/test_router_safety.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/bash/test_router_sed_grep.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/bash/test_tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_coverage_fingerprint.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_anchors.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_bounds_input.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_coverage.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_dedup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_drift_fallback.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_drift_fallback_e2e.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_errors.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_line_insert.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_llm_messages.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_replace.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_edit_trailing_newline.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_read.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_read_write.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_render_numbered_diff.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/file/test_write_file.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_clarify_tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_diffing.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_file_tools_redesign.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_git_tool_destructive.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_git_tool_schema_errors.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_git_tool_structured.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_infer_state_patch.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_interactive_tools.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_interactive_tools_write.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_load_doc_template.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_loop_integration.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_loop_registry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_make_interact_callback.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_plan_checkpoint.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_powershell_tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_replace_failure_logging.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_resolve_safe.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_retry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_schedule_wakeup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_search.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_skills_tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_state_update_from_executed_tools.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_task_tracker.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_todo_tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_tool_error_handling.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_tool_registry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_tool_schemas.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_tool_state_patch.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_user_interaction_models.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_web_mcp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_webfetch.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_tools/test_workflow_tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/conftest.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/helpers.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_diff_review.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_gateway_headless_frontend.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_gateway_v2_crud_diff.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_gateway_v2_routing.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_gateway_v2_server.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_gateway_v2_session.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_guidance_fallback.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_run_manager.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_terminal.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_events_dock_bus.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_events_dock_prompts.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_events_dock_status.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_events_streaming.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_events_todo.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/gateway/test_ui_gateway.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_console_app.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_dock_formatting.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_fmt_args.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_output_browse.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_scrollback_flush.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_ui_diff.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/output/test_ui_session_changes.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/protocol/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/protocol/test_dto.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/protocol/test_v2_envelope.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/protocol/test_v2_methods.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/protocol/test_v2_snapshot.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/protocol/test_v2_threads.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/test_display_policy.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/test_file_picker.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/tools/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/tools/test_clipboard_image.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/tools/test_clipboard_image_windows.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/tools/test_clipboard_text.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/tools/test_clipboard_text_windows.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_ui/tools/test_code_ide.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_voidx_entrypoint.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_wheel_build.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_workflow/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_workflow/test_auto_advance.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/tests/test_workflow/test_workflow_reconcile.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/agents.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/attachments.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/goal_resolver.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/compaction.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/compaction_coordinator.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/convergence.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/core/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/core/voidx_graph.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/run_loop.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/runtime.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/runtime_guards.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/session_mixin.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/session_runtime.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/streaming.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/thread_context.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/title_mixin.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/todo_events.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_execution.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/guards.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/helpers.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/types.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/tool_executor/ui.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/topology.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/transcript_mixin.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/turn_metrics.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/turn_mixin.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/turn_runner.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/graph/workflow_utils.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/loop/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/loop/manager.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/loop/prompt_source.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/loop/slash.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/message_rows.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/message_trimming.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/prompts.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/runtime_context.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/code_ide.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/guide.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/host.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/init.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/lsp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/mcp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/profile.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/runtime.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/session.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/skills.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/slash/upgrade.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/task_state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/todo_state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/tool_call_ids.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/tool_exchange_sanitizer.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/tool_messages.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/agent/tool_result_storage.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/defaults.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_agent.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_api_keys.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_code_ide.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_custom.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_mcp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_retry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_skills.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_update.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_utils.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/config/settings_web.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/README.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/api-doc.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/capability-spec.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/implementation-spec.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/prd.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/readme.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/rfc.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/tasks.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/templates/tech-design.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/README.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/context.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/debug.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/extension.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/mode.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/model.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/preferences.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/quickstart.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/session.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/upgrade.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/web.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/data/documents/voidx-guide/workflow.md +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/diffing.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/compaction.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/context.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/instruction.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/message_markers.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/message_status.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/service.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/llm/usage.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/logging/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/logging/external.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/logging/internal_error.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/logging/request_log.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/logging/tool_log.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/client.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/config.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/detector.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/detector_data.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/errors.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/manager.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/schema.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/lsp/service.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/main.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/client/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/client/base.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/client/errors.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/client/http_transport.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/client/sse_transport.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/client/stdio_transport.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/manager.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/schema.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp/tool.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp_servers/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/mcp_servers/web.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/cleanup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/context_frames.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/jsonl_store.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/model_profiles.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/runtime_state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/service.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/session.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/store.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/subagents.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/memory/transcript.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/paths.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/evaluate.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/grants.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/process_sandbox.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/sandbox.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/permission/wildcard.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/_win32_jobs.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/attachments.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/intent.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/processes.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/reference_tokens.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/task_state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/todo.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/ui.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/runtime/ui_port.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/selfupdate.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/skills/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/skills/context.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/skills/references.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/skills/registry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/skills/schema.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/skills/service.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/agent.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/core.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/hint/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/hint/file.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/hint/git.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/hint/search.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/router.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/bash/safety.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/checkpoint.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/clarify.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/compact.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/document.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/manage.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/read.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/replace.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/replace_resolve.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/safe_path.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/types.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/file/write.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/git.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/lsp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/core.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/hint/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/hint/file.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/hint/search.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/router.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/safety.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/powershell/sandbox.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/registry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/retry.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/schedule_wakeup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/search.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/shell/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/shell/common.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/shell/hint/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/shell/hint/git.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/skills.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/task_status.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/task_tracker.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/todo.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/web/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/web/content.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/web/fetch.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/web/mcp.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/web/search.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/tools/workflow.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/frontend.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/adapter.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/bootstrap.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/diff_review.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/frontend.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/run_manager.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/server.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/consumer.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/core.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/method/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/method/diff.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/method/integrations.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/method/sessions.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/session/method/terminal.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/gateway/terminal.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/agent_display.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/ansi_marker.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/browse.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/console/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/console/app.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/console/formatting.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/console/streaming.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/diff.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/display_policy.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/agent_placeholder.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/app.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/formatting.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/nodes_checkpoint.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/nodes_clarify.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/nodes_permission.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/nodes_startup.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/nodes_status.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/state.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/status.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/stream.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/dock/todo.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/events/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/events/bus.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/manage_display.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/tree.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/output/types.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/commands.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/requests.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/schema.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/transcript.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/v2/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/v2/envelope.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/v2/methods.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/v2/snapshot.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/protocol/v2/threads.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/session.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/__init__.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/attachment_tokens.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/clipboard_image.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/clipboard_text.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/code_ide.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/file_picker.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/tools/skill_picker.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/ui/transcript.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/auto_advance.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/context.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/dag.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/reconcile.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/render.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/route.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/runtime.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx/workflow/types.py +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx.egg-info/dependency_links.txt +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx.egg-info/entry_points.txt +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx.egg-info/requires.txt +0 -0
- {voidx-3.7.0 → voidx-3.7.2}/src/voidx.egg-info/top_level.txt +0 -0
|
@@ -792,3 +792,23 @@ def test_llm_retry_delay_exponential_cap():
|
|
|
792
792
|
assert _llm_retry_delay(8) == 60.0
|
|
793
793
|
assert _llm_retry_delay(9) == 60.0
|
|
794
794
|
assert _llm_retry_delay(10) == 60.0
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
def test_clean_error_message():
|
|
798
|
+
from voidx.agent.graph.core.helpers import _clean_error_message
|
|
799
|
+
|
|
800
|
+
# Test case 1: OpenAI rate limit error with dict representation
|
|
801
|
+
exc1 = Exception("Error code: 402 - {'error': {'type': 'rate_limit_error', 'message': '每日额度超限: 当前 $50.813...'}}")
|
|
802
|
+
assert _clean_error_message(exc1) == "Error code: 402 - 每日额度超限: 当前 $50.813..."
|
|
803
|
+
|
|
804
|
+
# Test case 2: OpenAI error with json representation
|
|
805
|
+
exc2 = Exception('Error code: 400 - {"error": {"message": "Invalid prompt", "type": "invalid_request_error"}}')
|
|
806
|
+
assert _clean_error_message(exc2) == "Error code: 400 - Invalid prompt"
|
|
807
|
+
|
|
808
|
+
# Test case 3: Standard exception with no JSON/dict
|
|
809
|
+
exc3 = Exception("Connection refused")
|
|
810
|
+
assert _clean_error_message(exc3) == "Connection refused"
|
|
811
|
+
|
|
812
|
+
# Test case 4: Dict representation without error key
|
|
813
|
+
exc4 = Exception("Error: {'message': 'Something went wrong'}")
|
|
814
|
+
assert _clean_error_message(exc4) == "Error - Something went wrong"
|
|
@@ -72,6 +72,10 @@ def _result_task_state(result: dict) -> TaskState:
|
|
|
72
72
|
return TaskState.model_validate(result["task_state"])
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _asked_tool_calls(batch):
|
|
76
|
+
return [getattr(item, "tool_call", item) for item in batch]
|
|
77
|
+
|
|
78
|
+
|
|
75
79
|
def _child_goal_resolution(
|
|
76
80
|
goal_type: str = "feature",
|
|
77
81
|
*,
|
|
@@ -191,7 +195,7 @@ async def test_graph_authorization_prompts_for_implement_agent(tmp_path):
|
|
|
191
195
|
|
|
192
196
|
assert [tc["name"] for tc in approved] == ["agent"]
|
|
193
197
|
assert denied == []
|
|
194
|
-
assert [[tc["args"]["goal_resolution"]["plan"]["join"] for tc in batch] for batch in asked] == [["tdd"]]
|
|
198
|
+
assert [[tc["args"]["goal_resolution"]["plan"]["join"] for tc in _asked_tool_calls(batch)] for batch in asked] == [["tdd"]]
|
|
195
199
|
|
|
196
200
|
|
|
197
201
|
@pytest.mark.asyncio
|
|
@@ -210,6 +214,34 @@ async def test_graph_authorization_respects_session_deny_for_safe_bash(tmp_path)
|
|
|
210
214
|
assert "Permission denied" in denied[0][1]
|
|
211
215
|
|
|
212
216
|
|
|
217
|
+
@pytest.mark.asyncio
|
|
218
|
+
async def test_graph_authorization_never_approves_mixed_blocked_batch(tmp_path):
|
|
219
|
+
graph = _graph(tmp_path)
|
|
220
|
+
graph._permission.permission_preset = "safe"
|
|
221
|
+
asked: list[list[object]] = []
|
|
222
|
+
|
|
223
|
+
async def approve(tool_calls):
|
|
224
|
+
asked.append(tool_calls)
|
|
225
|
+
return "y"
|
|
226
|
+
|
|
227
|
+
graph._ask_tool_permission = approve
|
|
228
|
+
|
|
229
|
+
approved, denied = await graph._authorize_tool_calls(
|
|
230
|
+
[
|
|
231
|
+
{"name": "bash", "args": {"command": "rm -rf /"}, "id": "blocked"},
|
|
232
|
+
{"name": "bash", "args": {"command": "echo $(date)"}, "id": "ask"},
|
|
233
|
+
],
|
|
234
|
+
plan_mode=False,
|
|
235
|
+
session_id="test",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
assert [call["id"] for call in approved] == ["ask"]
|
|
239
|
+
assert [call["id"] for call, _ in denied] == ["blocked"]
|
|
240
|
+
assert len(asked) == 2
|
|
241
|
+
assert [item.tool_call["id"] for item in asked[0]] == ["blocked"]
|
|
242
|
+
assert [item.tool_call["id"] for item in asked[1]] == ["ask"]
|
|
243
|
+
|
|
244
|
+
|
|
213
245
|
@pytest.mark.asyncio
|
|
214
246
|
async def test_graph_authorization_asks_for_write_by_active_workflow_gate(tmp_path):
|
|
215
247
|
graph = _graph(tmp_path)
|
|
@@ -231,7 +263,7 @@ async def test_graph_authorization_asks_for_write_by_active_workflow_gate(tmp_pa
|
|
|
231
263
|
],
|
|
232
264
|
)
|
|
233
265
|
|
|
234
|
-
assert [[call["id"] for call in batch] for batch in asked] == [["call_1"]]
|
|
266
|
+
assert [[call["id"] for call in _asked_tool_calls(batch)] for batch in asked] == [["call_1"]]
|
|
235
267
|
assert [call["id"] for call in approved] == ["call_1"]
|
|
236
268
|
assert denied == []
|
|
237
269
|
|
|
@@ -294,7 +326,7 @@ async def test_graph_authorization_allows_plan_gate_doc_paths_only(tmp_path):
|
|
|
294
326
|
)
|
|
295
327
|
|
|
296
328
|
assert [call["id"] for call in approved] == ["call_docs"]
|
|
297
|
-
assert [[call["id"] for call in batch] for batch in asked] == [["call_src"]]
|
|
329
|
+
assert [[call["id"] for call in _asked_tool_calls(batch)] for batch in asked] == [["call_src"]]
|
|
298
330
|
assert [call["id"] for call, _reason in denied] == ["call_src"]
|
|
299
331
|
assert denied[0][1] == "User denied: replace"
|
|
300
332
|
|
|
@@ -371,7 +403,7 @@ async def test_graph_authorization_asks_for_workflow_gate_tools_instead_of_denyi
|
|
|
371
403
|
],
|
|
372
404
|
)
|
|
373
405
|
|
|
374
|
-
assert [[call["id"] for call in batch] for batch in asked] == [["call_src"]]
|
|
406
|
+
assert [[call["id"] for call in _asked_tool_calls(batch)] for batch in asked] == [["call_src"]]
|
|
375
407
|
assert [call["id"] for call in approved] == ["call_src"]
|
|
376
408
|
assert denied == []
|
|
377
409
|
|
|
@@ -470,6 +502,9 @@ async def test_permission_prompt_uses_dock_details_when_events_are_active(tmp_pa
|
|
|
470
502
|
"name": "bash",
|
|
471
503
|
"pattern": "npm install lodash",
|
|
472
504
|
"args": {"command": "npm install lodash"},
|
|
505
|
+
"risk": None,
|
|
506
|
+
"allowed_scopes": [],
|
|
507
|
+
"default_scope": None,
|
|
473
508
|
}]
|
|
474
509
|
record = test_dock.status_record("permission:request")
|
|
475
510
|
assert record is not None
|
|
@@ -480,3 +515,154 @@ async def test_permission_prompt_uses_dock_details_when_events_are_active(tmp_pa
|
|
|
480
515
|
await graph._ui.events.stop()
|
|
481
516
|
test_dock.deactivate()
|
|
482
517
|
set_dock(None)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
@pytest.mark.asyncio
|
|
521
|
+
async def test_read_only_permission_prompt_limits_choices_to_once(tmp_path):
|
|
522
|
+
graph = _graph(tmp_path)
|
|
523
|
+
graph._permission.sandbox_mode = "read-only"
|
|
524
|
+
graph._permission.permission_mode = "read-only"
|
|
525
|
+
graph._permission.approval_policy = "untrusted"
|
|
526
|
+
captured_choices = None
|
|
527
|
+
|
|
528
|
+
class FakeApp:
|
|
529
|
+
async def ask_choice(self, _prompt, choices, details=None):
|
|
530
|
+
nonlocal captured_choices
|
|
531
|
+
captured_choices = choices
|
|
532
|
+
return "y"
|
|
533
|
+
|
|
534
|
+
graph._app = FakeApp()
|
|
535
|
+
|
|
536
|
+
approved, denied = await graph._authorize_tool_calls(
|
|
537
|
+
[{"name": "bash", "args": {"command": "cat input.txt > output.txt"}, "id": "call_1"}],
|
|
538
|
+
plan_mode=False,
|
|
539
|
+
session_id="s",
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
assert [tc["name"] for tc in approved] == ["bash"]
|
|
543
|
+
assert denied == []
|
|
544
|
+
assert captured_choices == [
|
|
545
|
+
("Yes", "y", "Allow this tool use once"),
|
|
546
|
+
("No", "n", "Deny these tools"),
|
|
547
|
+
]
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
@pytest.mark.asyncio
|
|
551
|
+
async def test_permission_prompt_details_include_risk_and_scopes(tmp_path):
|
|
552
|
+
graph = _graph(tmp_path)
|
|
553
|
+
graph._permission.permission_mode = "default"
|
|
554
|
+
graph._permission.approval_policy = "untrusted"
|
|
555
|
+
received_details = None
|
|
556
|
+
|
|
557
|
+
class FakeApp:
|
|
558
|
+
async def ask_choice(self, _prompt, _choices, details=None):
|
|
559
|
+
nonlocal received_details
|
|
560
|
+
received_details = details
|
|
561
|
+
return "y"
|
|
562
|
+
|
|
563
|
+
graph._app = FakeApp()
|
|
564
|
+
|
|
565
|
+
approved, denied = await graph._authorize_tool_calls(
|
|
566
|
+
[{"name": "bash", "args": {"command": "npm install lodash"}, "id": "call_1"}],
|
|
567
|
+
plan_mode=False,
|
|
568
|
+
session_id="s",
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
assert [tc["name"] for tc in approved] == ["bash"]
|
|
572
|
+
assert denied == []
|
|
573
|
+
assert received_details == [{
|
|
574
|
+
"name": "bash",
|
|
575
|
+
"pattern": "npm install lodash",
|
|
576
|
+
"args": {"command": "npm install lodash"},
|
|
577
|
+
"risk": {
|
|
578
|
+
"level": "extreme",
|
|
579
|
+
"tags": ["dependency_install"],
|
|
580
|
+
"reason": "shell policy deferred: dependency install command",
|
|
581
|
+
"tool_name": "bash",
|
|
582
|
+
"pattern": "npm install lodash",
|
|
583
|
+
},
|
|
584
|
+
"allowed_scopes": ["once"],
|
|
585
|
+
"default_scope": "once",
|
|
586
|
+
}]
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
@pytest.mark.asyncio
|
|
590
|
+
async def test_graph_authorization_attaches_approved_risk_token_to_approved_shell_call(tmp_path):
|
|
591
|
+
graph = _graph(tmp_path)
|
|
592
|
+
graph._permission.sandbox_mode = "read-only"
|
|
593
|
+
graph._permission.permission_mode = "read-only"
|
|
594
|
+
graph._permission.approval_policy = "untrusted"
|
|
595
|
+
|
|
596
|
+
async def approve(_tool_calls):
|
|
597
|
+
return "y"
|
|
598
|
+
|
|
599
|
+
graph._ask_tool_permission = approve
|
|
600
|
+
command = "cat input.txt > output.txt"
|
|
601
|
+
|
|
602
|
+
approved, denied = await graph._authorize_tool_calls(
|
|
603
|
+
[{"name": "bash", "args": {"command": command}, "id": "call_1"}],
|
|
604
|
+
plan_mode=False,
|
|
605
|
+
session_id="s",
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
assert denied == []
|
|
609
|
+
assert approved[0]["metadata"]["approved_risk"] == {
|
|
610
|
+
"tool_name": "bash",
|
|
611
|
+
"pattern": command,
|
|
612
|
+
"risk_level": "extreme",
|
|
613
|
+
"tags": ["dynamic_shell"],
|
|
614
|
+
"reason": "shell policy deferred: compound shell syntax, compound shell operator",
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
@pytest.mark.asyncio
|
|
619
|
+
async def test_blocked_permission_prompt_only_acknowledges_and_denies_execution(tmp_path):
|
|
620
|
+
graph = _graph(tmp_path)
|
|
621
|
+
graph._permission.permission_mode = "default"
|
|
622
|
+
graph._permission.approval_policy = "untrusted"
|
|
623
|
+
captured_choices = None
|
|
624
|
+
captured_details = None
|
|
625
|
+
|
|
626
|
+
class FakeApp:
|
|
627
|
+
async def ask_choice(self, _prompt, choices, details=None):
|
|
628
|
+
nonlocal captured_choices, captured_details
|
|
629
|
+
captured_choices = choices
|
|
630
|
+
captured_details = details
|
|
631
|
+
return "n"
|
|
632
|
+
|
|
633
|
+
graph._app = FakeApp()
|
|
634
|
+
|
|
635
|
+
approved, denied = await graph._authorize_tool_calls(
|
|
636
|
+
[{"name": "bash", "args": {"command": "sudo true"}, "id": "call_blocked"}],
|
|
637
|
+
plan_mode=False,
|
|
638
|
+
session_id="s",
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
assert approved == []
|
|
642
|
+
assert denied == [({"name": "bash", "args": {"command": "sudo true"}, "id": "call_blocked"}, "Blocked: sudo is blocked — privilege escalation")]
|
|
643
|
+
assert captured_choices == [("Do not run", "n", "This command is blocked")]
|
|
644
|
+
assert captured_details[0]["risk"]["level"] == "blocked"
|
|
645
|
+
assert captured_details[0]["allowed_scopes"] == []
|
|
646
|
+
assert captured_details[0]["default_scope"] is None
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
@pytest.mark.asyncio
|
|
650
|
+
async def test_blocked_permission_prompt_cannot_be_approved_with_yes(tmp_path):
|
|
651
|
+
graph = _graph(tmp_path)
|
|
652
|
+
graph._permission.permission_mode = "default"
|
|
653
|
+
graph._permission.approval_policy = "untrusted"
|
|
654
|
+
|
|
655
|
+
class FakeApp:
|
|
656
|
+
async def ask_choice(self, _prompt, _choices, details=None):
|
|
657
|
+
return "y"
|
|
658
|
+
|
|
659
|
+
graph._app = FakeApp()
|
|
660
|
+
|
|
661
|
+
approved, denied = await graph._authorize_tool_calls(
|
|
662
|
+
[{"name": "bash", "args": {"command": "sudo true"}, "id": "call_blocked"}],
|
|
663
|
+
plan_mode=False,
|
|
664
|
+
session_id="s",
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
assert approved == []
|
|
668
|
+
assert denied == [({"name": "bash", "args": {"command": "sudo true"}, "id": "call_blocked"}, "Blocked: sudo is blocked — privilege escalation")]
|
|
@@ -72,6 +72,10 @@ def _result_task_state(result: dict) -> TaskState:
|
|
|
72
72
|
return TaskState.model_validate(result["task_state"])
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _asked_tool_calls(batch):
|
|
76
|
+
return [getattr(item, "tool_call", item) for item in batch]
|
|
77
|
+
|
|
78
|
+
|
|
75
79
|
def _child_goal_resolution(
|
|
76
80
|
goal_type: str = "feature",
|
|
77
81
|
*,
|
|
@@ -320,6 +324,13 @@ async def test_subagent_runner_authorizes_with_child_interaction_mode(tmp_path,
|
|
|
320
324
|
async def test_graph_authorization_does_not_treat_goal_as_read_only_mode(tmp_path):
|
|
321
325
|
graph = _graph(tmp_path)
|
|
322
326
|
graph._permission.approval_policy = "on-request"
|
|
327
|
+
asked: list[list[dict]] = []
|
|
328
|
+
|
|
329
|
+
async def approve(tool_calls):
|
|
330
|
+
asked.append(tool_calls)
|
|
331
|
+
return "y"
|
|
332
|
+
|
|
333
|
+
graph._ask_tool_permission = approve
|
|
323
334
|
|
|
324
335
|
approved, denied = await graph._authorize_tool_calls(
|
|
325
336
|
[{"name": "edit", "args": {"file_path": "src/app.py"}, "id": "call_1"}],
|
|
@@ -331,6 +342,7 @@ async def test_graph_authorization_does_not_treat_goal_as_read_only_mode(tmp_pat
|
|
|
331
342
|
|
|
332
343
|
assert [tc["name"] for tc in approved] == ["replace"]
|
|
333
344
|
assert denied == []
|
|
345
|
+
assert [[tc["name"] for tc in _asked_tool_calls(batch)] for batch in asked] == [["replace"]]
|
|
334
346
|
|
|
335
347
|
|
|
336
348
|
@pytest.mark.asyncio
|
|
@@ -369,7 +381,7 @@ async def test_graph_authorization_prompts_for_edit(tmp_path):
|
|
|
369
381
|
|
|
370
382
|
assert [tc["name"] for tc in approved] == ["replace"]
|
|
371
383
|
assert denied == []
|
|
372
|
-
assert [[tc["name"] for tc in batch] for batch in asked] == [["replace"]]
|
|
384
|
+
assert [[tc["name"] for tc in _asked_tool_calls(batch)] for batch in asked] == [["replace"]]
|
|
373
385
|
|
|
374
386
|
|
|
375
387
|
@pytest.mark.asyncio
|
|
@@ -413,4 +425,4 @@ async def test_graph_authorization_prompts_for_unsafe_bash(tmp_path):
|
|
|
413
425
|
|
|
414
426
|
assert [tc["name"] for tc in approved] == ["bash"]
|
|
415
427
|
assert denied == []
|
|
416
|
-
assert [[tc["name"] for tc in batch] for batch in asked] == [["bash"]]
|
|
428
|
+
assert [[tc["name"] for tc in _asked_tool_calls(batch)] for batch in asked] == [["bash"]]
|
|
@@ -135,7 +135,7 @@ def _tree_nodes(root):
|
|
|
135
135
|
@pytest.mark.asyncio
|
|
136
136
|
async def test_graph_on_request_auto_approves_need_ask_tools(tmp_path):
|
|
137
137
|
graph = _graph(tmp_path)
|
|
138
|
-
graph._permission.
|
|
138
|
+
graph._permission.permission_preset = "full_access"
|
|
139
139
|
|
|
140
140
|
approved, denied = await graph._authorize_tool_calls(
|
|
141
141
|
[{"name": "write", "args": {"file_path": "app.py", "op": "append", "new_string": "x"}, "id": "call_1"}],
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Test: turn stop without pending provisional text in running state.
|
|
2
|
+
|
|
3
|
+
When the main agent calls a child agent (e.g. review) and the child returns,
|
|
4
|
+
the main agent may call turn operation='stop' directly without first emitting
|
|
5
|
+
plain text. In that case pending_provisional is None and validate_turn_call
|
|
6
|
+
rejects the stop. The INVALID_TURN_PROMPT then tells the LLM to "not output
|
|
7
|
+
text, call turn stop" — which is contradictory because there is no pending
|
|
8
|
+
text to commit. This creates a dead loop that ends with a failure message
|
|
9
|
+
and should_continue=False, producing no user-facing text.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage, ToolMessage
|
|
14
|
+
|
|
15
|
+
from voidx.agent.graph import VoidXGraph
|
|
16
|
+
from voidx.config import Config, ModelConfig
|
|
17
|
+
from voidx.ui.output.events import AssistantStreamCommitted, AssistantStreamUpdated
|
|
18
|
+
from tests.test_agent.graph.stream_llm_helpers import FakeRenderer
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ScriptedStreamingModel:
|
|
22
|
+
def __init__(self, scripts: list[list[AIMessageChunk]]) -> None:
|
|
23
|
+
self.scripts = list(scripts)
|
|
24
|
+
self.call_index = 0
|
|
25
|
+
self.bound_tools = None
|
|
26
|
+
|
|
27
|
+
def bind_tools(self, tool_defs):
|
|
28
|
+
self.bound_tools = tool_defs
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
async def astream(self, messages):
|
|
32
|
+
idx = self.call_index
|
|
33
|
+
self.call_index += 1
|
|
34
|
+
if idx >= len(self.scripts):
|
|
35
|
+
pytest.fail(
|
|
36
|
+
f"Unexpected LLM call {idx + 1}; "
|
|
37
|
+
f"only {len(self.scripts)} scripted responses were provided"
|
|
38
|
+
)
|
|
39
|
+
for chunk in self.scripts[idx]:
|
|
40
|
+
yield chunk
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _turn_stop_chunk() -> AIMessageChunk:
|
|
44
|
+
return AIMessageChunk(
|
|
45
|
+
content="",
|
|
46
|
+
tool_calls=[{
|
|
47
|
+
"name": "turn",
|
|
48
|
+
"args": {"operation": "stop", "intent": "", "goal": ""},
|
|
49
|
+
"id": "tc1",
|
|
50
|
+
"type": "tool_call",
|
|
51
|
+
}],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _text_chunk(text: str) -> AIMessageChunk:
|
|
56
|
+
return AIMessageChunk(content=text)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _text_and_turn_stop_chunk(text: str) -> AIMessageChunk:
|
|
60
|
+
return AIMessageChunk(
|
|
61
|
+
content=text,
|
|
62
|
+
tool_calls=[{
|
|
63
|
+
"name": "turn",
|
|
64
|
+
"args": {"operation": "stop", "intent": "", "goal": ""},
|
|
65
|
+
"id": "tc1",
|
|
66
|
+
"type": "tool_call",
|
|
67
|
+
}],
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _make_graph(tmp_path, model, monkeypatch, provider="openai"):
|
|
72
|
+
import voidx.agent.graph.core.llm as graph_module
|
|
73
|
+
|
|
74
|
+
async def fail_on_retry(delay):
|
|
75
|
+
pytest.fail(f"Unexpected LLM retry with delay {delay}s")
|
|
76
|
+
|
|
77
|
+
monkeypatch.setattr(graph_module, "StreamingRenderer", FakeRenderer)
|
|
78
|
+
monkeypatch.setattr(graph_module.asyncio, "sleep", fail_on_retry)
|
|
79
|
+
|
|
80
|
+
graph = VoidXGraph(
|
|
81
|
+
Config(
|
|
82
|
+
model=ModelConfig(provider=provider, model="test-model"),
|
|
83
|
+
workspace=str(tmp_path),
|
|
84
|
+
),
|
|
85
|
+
api_key=None,
|
|
86
|
+
)
|
|
87
|
+
graph.model = model
|
|
88
|
+
return graph
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.asyncio
|
|
92
|
+
async def test_turn_stop_in_running_state_without_pending_emits_text(tmp_path, monkeypatch):
|
|
93
|
+
"""When turn_state='running' and LLM calls turn stop without prior text,
|
|
94
|
+
the agent should recover by prompting the LLM to produce text, not enter
|
|
95
|
+
a dead loop that ends with a bare failure message.
|
|
96
|
+
"""
|
|
97
|
+
model = ScriptedStreamingModel([
|
|
98
|
+
# 1) LLM calls turn stop directly (no text, no pending_provisional)
|
|
99
|
+
[_turn_stop_chunk()],
|
|
100
|
+
# 2) After MISSING_PENDING_PROMPT, LLM outputs plain text summary
|
|
101
|
+
[_text_chunk("Review completed: PASS")],
|
|
102
|
+
# 3) After TURN_STOP_PROMPT, LLM calls turn stop to commit
|
|
103
|
+
[_turn_stop_chunk()],
|
|
104
|
+
])
|
|
105
|
+
graph = _make_graph(tmp_path, model, monkeypatch)
|
|
106
|
+
|
|
107
|
+
result = await graph._call_llm({
|
|
108
|
+
"messages": [HumanMessage(content="Run review")],
|
|
109
|
+
"step_count": 1,
|
|
110
|
+
"persona": "coordinate",
|
|
111
|
+
"turn_state": "running",
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
# The agent should produce user-facing text, not a bare failure message.
|
|
115
|
+
msg = result["messages"][0]
|
|
116
|
+
assert isinstance(msg, AIMessage)
|
|
117
|
+
assert not msg.tool_calls
|
|
118
|
+
text = (msg.content or "").strip() if isinstance(msg.content, str) else ""
|
|
119
|
+
assert "Review completed" in text
|
|
120
|
+
assert "LLM call failed" not in text
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@pytest.mark.parametrize("repair_shape", ["text_then_stop", "text_and_stop"])
|
|
124
|
+
@pytest.mark.asyncio
|
|
125
|
+
async def test_headless_repair_commit_emits_user_visible_stream(tmp_path, monkeypatch, repair_shape):
|
|
126
|
+
"""Gemini often repairs missing pending text by returning text + turn stop
|
|
127
|
+
in one headless repair call. The committed answer must still be emitted to
|
|
128
|
+
the UI; checking only result["messages"] misses the user-visible blank turn.
|
|
129
|
+
"""
|
|
130
|
+
import voidx.agent.graph.core.llm as graph_module
|
|
131
|
+
|
|
132
|
+
emitted = []
|
|
133
|
+
|
|
134
|
+
class TrackingEvents:
|
|
135
|
+
async def emit(self, event):
|
|
136
|
+
emitted.append(event)
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
def emit_direct(self, event):
|
|
140
|
+
emitted.append(event)
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
async def drain(self):
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def is_running(self):
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
class TrackingUi:
|
|
151
|
+
class Output:
|
|
152
|
+
def print(self, *args, **kwargs):
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
def error(self, *args, **kwargs):
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
def __init__(self):
|
|
159
|
+
self.events = TrackingEvents()
|
|
160
|
+
self.console = None
|
|
161
|
+
self.ui = self.Output()
|
|
162
|
+
|
|
163
|
+
def via_events(self):
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
class HeadlessAwareRenderer(FakeRenderer):
|
|
167
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
168
|
+
super().__init__(*args, **kwargs)
|
|
169
|
+
self.headless = bool(kwargs.get("headless", False))
|
|
170
|
+
|
|
171
|
+
def feed_text(self, text: str) -> None:
|
|
172
|
+
if not self.headless:
|
|
173
|
+
super().feed_text(text)
|
|
174
|
+
|
|
175
|
+
async def fail_on_retry(delay):
|
|
176
|
+
pytest.fail(f"Unexpected LLM retry with delay {delay}s")
|
|
177
|
+
|
|
178
|
+
monkeypatch.setattr(graph_module, "StreamingRenderer", HeadlessAwareRenderer)
|
|
179
|
+
monkeypatch.setattr(graph_module.asyncio, "sleep", fail_on_retry)
|
|
180
|
+
|
|
181
|
+
scripts = [[_turn_stop_chunk()]]
|
|
182
|
+
if repair_shape == "text_then_stop":
|
|
183
|
+
scripts.extend([[_text_chunk("Review completed: PASS")], [_turn_stop_chunk()]])
|
|
184
|
+
else:
|
|
185
|
+
scripts.append([_text_and_turn_stop_chunk("Review completed: PASS")])
|
|
186
|
+
model = ScriptedStreamingModel(scripts)
|
|
187
|
+
graph = VoidXGraph(
|
|
188
|
+
Config(
|
|
189
|
+
model=ModelConfig(provider="gemini", model="gemini-test"),
|
|
190
|
+
workspace=str(tmp_path),
|
|
191
|
+
),
|
|
192
|
+
api_key=None,
|
|
193
|
+
)
|
|
194
|
+
graph.model = model
|
|
195
|
+
graph._ui = TrackingUi()
|
|
196
|
+
|
|
197
|
+
result = await graph._call_llm({
|
|
198
|
+
"messages": [HumanMessage(content="Run review")],
|
|
199
|
+
"step_count": 1,
|
|
200
|
+
"persona": "coordinate",
|
|
201
|
+
"turn_state": "running",
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
assert result["messages"][0].content == "Review completed: PASS"
|
|
205
|
+
stream_updates = [event for event in emitted if isinstance(event, AssistantStreamUpdated)]
|
|
206
|
+
assert any("Review completed: PASS" in event.text for event in stream_updates)
|
|
207
|
+
assert any(isinstance(event, AssistantStreamCommitted) for event in emitted)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@pytest.mark.asyncio
|
|
211
|
+
async def test_turn_stop_with_text_after_missing_pending(tmp_path, monkeypatch):
|
|
212
|
+
"""When MISSING_PENDING_PROMPT is injected and LLM responds with text +
|
|
213
|
+
turn stop in the same message, the agent should accept the text as
|
|
214
|
+
pending and commit it, not treat it as INVALID_TURN.
|
|
215
|
+
"""
|
|
216
|
+
model = ScriptedStreamingModel([
|
|
217
|
+
# 1) LLM calls turn stop directly (no text, no pending_provisional)
|
|
218
|
+
[_turn_stop_chunk()],
|
|
219
|
+
# 2) After MISSING_PENDING_PROMPT, LLM outputs text + turn stop together
|
|
220
|
+
[_text_chunk("Review completed: PASS"), _turn_stop_chunk()],
|
|
221
|
+
])
|
|
222
|
+
graph = _make_graph(tmp_path, model, monkeypatch)
|
|
223
|
+
|
|
224
|
+
result = await graph._call_llm({
|
|
225
|
+
"messages": [HumanMessage(content="Run review")],
|
|
226
|
+
"step_count": 1,
|
|
227
|
+
"persona": "coordinate",
|
|
228
|
+
"turn_state": "running",
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
msg = result["messages"][0]
|
|
232
|
+
assert isinstance(msg, AIMessage)
|
|
233
|
+
assert not msg.tool_calls
|
|
234
|
+
text = (msg.content or "").strip() if isinstance(msg.content, str) else ""
|
|
235
|
+
assert "Review completed" in text
|
|
236
|
+
assert "LLM call failed" not in text
|
|
@@ -132,7 +132,9 @@ def _tree_nodes(root):
|
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
@pytest.mark.asyncio
|
|
135
|
-
async def
|
|
135
|
+
async def test_workflow_done_returns_to_llm_when_workflow_complete(tmp_path):
|
|
136
|
+
from voidx.agent.graph.topology import route_after_execute_tools
|
|
137
|
+
|
|
136
138
|
graph = _graph(tmp_path)
|
|
137
139
|
parent = AIMessage(
|
|
138
140
|
content="",
|
|
@@ -164,7 +166,8 @@ async def test_workflow_done_stops_before_followup_llm_when_workflow_complete(tm
|
|
|
164
166
|
),
|
|
165
167
|
})
|
|
166
168
|
|
|
167
|
-
assert result
|
|
169
|
+
assert result.get("should_continue") is not False
|
|
170
|
+
assert route_after_execute_tools(result) == "call_llm"
|
|
168
171
|
by_name = {run.name: run for run in _result_task_state(result).workflow_runs.values()}
|
|
169
172
|
assert by_name["verify"].status == WorkflowRunStatus.SATISFIED
|
|
170
173
|
|
|
@@ -256,7 +259,9 @@ async def test_workflow_route_end_satisfies_non_review_without_successor(tmp_pat
|
|
|
256
259
|
|
|
257
260
|
|
|
258
261
|
@pytest.mark.asyncio
|
|
259
|
-
async def
|
|
262
|
+
async def test_multiple_workflow_done_calls_finish_batch_before_returning_to_llm(tmp_path):
|
|
263
|
+
from voidx.agent.graph.topology import route_after_execute_tools
|
|
264
|
+
|
|
260
265
|
graph = _graph(tmp_path)
|
|
261
266
|
parent = AIMessage(
|
|
262
267
|
content="",
|
|
@@ -303,9 +308,9 @@ async def test_multiple_workflow_done_calls_finish_batch_before_stopping(tmp_pat
|
|
|
303
308
|
"call_design_done",
|
|
304
309
|
"call_verify_done",
|
|
305
310
|
]
|
|
306
|
-
assert result
|
|
311
|
+
assert result.get("should_continue") is not False
|
|
312
|
+
assert route_after_execute_tools(result) == "call_llm"
|
|
307
313
|
by_name = {run.name: run for run in _result_task_state(result).workflow_runs.values()}
|
|
308
314
|
assert by_name["design"].status == WorkflowRunStatus.SATISFIED
|
|
309
315
|
assert by_name["verify"].status == WorkflowRunStatus.SATISFIED
|
|
310
316
|
|
|
311
|
-
|
{voidx-3.7.0 → voidx-3.7.2}/src/tests/test_agent/graph/test_workflow_transactions_barrier.py
RENAMED
|
@@ -301,6 +301,9 @@ async def test_workflow_transaction_reauthorizes_following_write(tmp_path):
|
|
|
301
301
|
nonlocal invalidations
|
|
302
302
|
invalidations += 1
|
|
303
303
|
|
|
304
|
+
async def ask_choice(self, _prompt, _choices, details=None):
|
|
305
|
+
return "y"
|
|
306
|
+
|
|
304
307
|
graph._app = FakeApp()
|
|
305
308
|
parent = AIMessage(
|
|
306
309
|
content="",
|