omnimancer-cli 0.3.6__tar.gz → 0.3.7__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.
Files changed (199) hide show
  1. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/PKG-INFO +1 -1
  2. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/h2l_command.py +6 -0
  3. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/h2l_headless.py +10 -0
  4. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/orchestrator.py +89 -18
  5. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/worker.py +50 -0
  6. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer_cli.egg-info/PKG-INFO +1 -1
  7. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/pyproject.toml +1 -1
  8. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/LICENSE +0 -0
  9. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/README.md +0 -0
  10. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omn_fleet_hook.py +0 -0
  11. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/__init__.py +0 -0
  12. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/__main__.py +0 -0
  13. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/__init__.py +0 -0
  14. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/agent_loop.py +0 -0
  15. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/approval_formatter.py +0 -0
  16. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/approval_integration.py +0 -0
  17. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/approval_prompt.py +0 -0
  18. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/command_dispatch.py +0 -0
  19. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/commands.py +0 -0
  20. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/completion.py +0 -0
  21. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/display.py +0 -0
  22. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/dynamic_commands.py +0 -0
  23. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/file_mentions.py +0 -0
  24. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/handlers/__init__.py +0 -0
  25. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/headless.py +0 -0
  26. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/headless_checkpoint.py +0 -0
  27. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/interface.py +0 -0
  28. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/prompt_input.py +0 -0
  29. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/pt_completion.py +0 -0
  30. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/session.py +0 -0
  31. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/subagent.py +0 -0
  32. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/system_prompts.py +0 -0
  33. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/tool_handler.py +0 -0
  34. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/turn_notify.py +0 -0
  35. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/cli/usage.py +0 -0
  36. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/__init__.py +0 -0
  37. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/__init__.py +0 -0
  38. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/approval_context.py +0 -0
  39. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/approval_interface.py +0 -0
  40. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/approval_manager.py +0 -0
  41. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/config.py +0 -0
  42. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/file_system_manager.py +0 -0
  43. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/program_executor.py +0 -0
  44. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/read_before_write_errors.py +0 -0
  45. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/status_core.py +0 -0
  46. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/status_manager.py +0 -0
  47. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/tool_definitions.py +0 -0
  48. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/types.py +0 -0
  49. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/web_client.py +0 -0
  50. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent/workflow_orchestrator.py +0 -0
  51. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent_engine.py +0 -0
  52. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent_managers.py +0 -0
  53. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/agent_mode_manager.py +0 -0
  54. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/chat_manager.py +0 -0
  55. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/config_manager.py +0 -0
  56. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/config_migration.py +0 -0
  57. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/config_validator.py +0 -0
  58. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/conversation_manager.py +0 -0
  59. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/engine.py +0 -0
  60. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/env_loader.py +0 -0
  61. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/fallback_manager.py +0 -0
  62. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/health_monitor.py +0 -0
  63. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/history_manager.py +0 -0
  64. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/hooks.py +0 -0
  65. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/mcp_integration_layer.py +0 -0
  66. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/models.py +0 -0
  67. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/prompt_enhancer.py +0 -0
  68. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/provider_capabilities.py +0 -0
  69. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/provider_initializer.py +0 -0
  70. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/provider_registry.py +0 -0
  71. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/provider_resolver.py +0 -0
  72. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/rate_limit_fallback.py +0 -0
  73. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/__init__.py +0 -0
  74. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/approval_workflow.py +0 -0
  75. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/audit_logger.py +0 -0
  76. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/permission_controller.py +0 -0
  77. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/permission_rules.py +0 -0
  78. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/sandbox_manager.py +0 -0
  79. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/security/security_manager.py +0 -0
  80. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/core/signal_handler.py +0 -0
  81. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/events/__init__.py +0 -0
  82. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/events/emitter.py +0 -0
  83. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/events/jsonl_writer.py +0 -0
  84. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/events/schema.py +0 -0
  85. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/__init__.py +0 -0
  86. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/judge.py +0 -0
  87. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/loop.py +0 -0
  88. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/models.py +0 -0
  89. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/planner.py +0 -0
  90. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/prompts.py +0 -0
  91. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/refs.py +0 -0
  92. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/h2l/render.py +0 -0
  93. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/mcp/__init__.py +0 -0
  94. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/mcp/client.py +0 -0
  95. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/mcp/manager.py +0 -0
  96. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/__init__.py +0 -0
  97. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/azure.py +0 -0
  98. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/base.py +0 -0
  99. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/bedrock.py +0 -0
  100. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/cache_tokens.py +0 -0
  101. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/claude.py +0 -0
  102. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/claude_code.py +0 -0
  103. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/claude_credentials.py +0 -0
  104. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/cohere.py +0 -0
  105. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/digitalocean.py +0 -0
  106. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/factory.py +0 -0
  107. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/gemini.py +0 -0
  108. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/mistral.py +0 -0
  109. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/ollama.py +0 -0
  110. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/openai.py +0 -0
  111. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/openai_compatible.py +0 -0
  112. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/openrouter.py +0 -0
  113. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/perplexity.py +0 -0
  114. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/vertex.py +0 -0
  115. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/providers/xai.py +0 -0
  116. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/__init__.py +0 -0
  117. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/fleet/__init__.py +0 -0
  118. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/fleet/app.py +0 -0
  119. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/fleet/cli.py +0 -0
  120. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/fleet/models.py +0 -0
  121. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/fleet/sources.py +0 -0
  122. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/tui/fleet/widgets.py +0 -0
  123. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/ui/__init__.py +0 -0
  124. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/ui/cancellation_handler.py +0 -0
  125. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/ui/progress_indicator.py +0 -0
  126. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/ui/streaming_display.py +0 -0
  127. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/ui/turn_activity.py +0 -0
  128. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/utils/__init__.py +0 -0
  129. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/utils/error_handler.py +0 -0
  130. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/utils/errors.py +0 -0
  131. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer/utils/retry.py +0 -0
  132. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer_cli.egg-info/SOURCES.txt +0 -0
  133. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer_cli.egg-info/dependency_links.txt +0 -0
  134. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer_cli.egg-info/entry_points.txt +0 -0
  135. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer_cli.egg-info/requires.txt +0 -0
  136. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/omnimancer_cli.egg-info/top_level.txt +0 -0
  137. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/setup.cfg +0 -0
  138. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_approval_integration_compatibility.py +0 -0
  139. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_approval_preview_format.py +0 -0
  140. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_approval_remember.py +0 -0
  141. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_approval_system.py +0 -0
  142. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_azure_provider.py +0 -0
  143. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_bedrock_provider.py +0 -0
  144. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_claude_code_provider.py +0 -0
  145. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_claude_credentials.py +0 -0
  146. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_claude_prompt_cache.py +0 -0
  147. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_claude_provider.py +0 -0
  148. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_cohere_provider.py +0 -0
  149. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_command_approval_security.py +0 -0
  150. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_config_comprehensive.py +0 -0
  151. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_config_migration_passthrough.py +0 -0
  152. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_conversation_history.py +0 -0
  153. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_conversation_manager.py +0 -0
  154. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_core_engine.py +0 -0
  155. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_digitalocean_provider.py +0 -0
  156. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_enhanced_model_info.py +0 -0
  157. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_env_loader.py +0 -0
  158. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_error_handling_robustness.py +0 -0
  159. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_execution_timeout_errors.py +0 -0
  160. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_fallback_manager.py +0 -0
  161. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_fixtures.py +0 -0
  162. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_gemini_provider.py +0 -0
  163. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_hooks.py +0 -0
  164. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_httpx_compatibility.py +0 -0
  165. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_import_validation.py +0 -0
  166. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_interface_smoke_tests.py +0 -0
  167. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_mcp_integration.py +0 -0
  168. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_mcp_integration_layer.py +0 -0
  169. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_mcp_management_commands.py +0 -0
  170. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_mistral_provider.py +0 -0
  171. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_models.py +0 -0
  172. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_ollama_provider.py +0 -0
  173. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_openai_compatible_provider.py +0 -0
  174. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_openai_context_fit.py +0 -0
  175. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_openai_provider_tools.py +0 -0
  176. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_openai_request_timeout.py +0 -0
  177. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_openai_streaming.py +0 -0
  178. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_openrouter_provider.py +0 -0
  179. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_permission_rules.py +0 -0
  180. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_perplexity_provider.py +0 -0
  181. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_program_executor.py +0 -0
  182. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_aliases.py +0 -0
  183. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_base.py +0 -0
  184. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_cache_tokens.py +0 -0
  185. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_capability_consistency.py +0 -0
  186. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_discovery.py +0 -0
  187. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_integration.py +0 -0
  188. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_provider_registry.py +0 -0
  189. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_rate_limit_fallback.py +0 -0
  190. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_retry.py +0 -0
  191. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_security_validation.py +0 -0
  192. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_system_integration.py +0 -0
  193. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_system_prompts.py +0 -0
  194. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_tool_calling_integration.py +0 -0
  195. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_tool_handler.py +0 -0
  196. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_vertex_provider.py +0 -0
  197. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_web_request_markers.py +0 -0
  198. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_workflows_comprehensive.py +0 -0
  199. {omnimancer_cli-0.3.6 → omnimancer_cli-0.3.7}/tests/test_xai_provider.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omnimancer-cli
3
- Version: 0.3.6
3
+ Version: 0.3.7
4
4
  Summary: A unified command-line interface for multiple AI language models
5
5
  Author-email: Kellan Strong <kellan@jite.ai>
6
6
  License: MIT
@@ -251,6 +251,12 @@ class TUIPresenter:
251
251
  f"{verdict.judge}): {failures}"
252
252
  )
253
253
 
254
+ async def on_model_dropped(self, ref: H2LModelRef, error: str) -> None:
255
+ self.console.print(
256
+ f" [yellow]Dropped {ref.label}: it cannot call tools, so the "
257
+ f"remaining low models take its stories[/yellow] [dim]({error})[/dim]"
258
+ )
259
+
254
260
  async def on_escalation(self, story: Story, outcome: StoryOutcome) -> str:
255
261
  while True:
256
262
  answer = (
@@ -232,6 +232,16 @@ class HeadlessPresenter:
232
232
  + (f" ({'; '.join(verdict.failures)})" if verdict.failures else "")
233
233
  )
234
234
 
235
+ async def on_model_dropped(self, ref: H2LModelRef, error: str) -> None:
236
+ self.emitter.emit_h2l(
237
+ "model_dropped",
238
+ {"provider": ref.provider, "model": ref.model, "error": error},
239
+ )
240
+ self._progress(
241
+ f"dropped {ref.label}: it cannot call tools, so the remaining low "
242
+ f"models take its stories ({error})"
243
+ )
244
+
235
245
  async def on_escalation(self, story: Story, outcome: StoryOutcome) -> str:
236
246
  return "high"
237
247
 
@@ -18,7 +18,17 @@ import logging
18
18
  import uuid
19
19
  from dataclasses import dataclass
20
20
  from pathlib import Path
21
- from typing import Any, Callable, Dict, Iterator, List, Optional, Protocol, Tuple
21
+ from typing import (
22
+ Any,
23
+ Callable,
24
+ Dict,
25
+ Iterator,
26
+ List,
27
+ Optional,
28
+ Protocol,
29
+ Set,
30
+ Tuple,
31
+ )
22
32
 
23
33
  from ..core.agent.tool_definitions import CODING_AGENT_TOOLS
24
34
  from ..core.rate_limit_fallback import matches_rate_limit
@@ -50,6 +60,32 @@ _current_story: contextvars.ContextVar[str] = contextvars.ContextVar(
50
60
  _TERMINAL_BAD = (StoryState.BLOCKED, StoryState.SKIPPED, StoryState.CANCELLED)
51
61
 
52
62
 
63
+ # Vendor wording for "this model cannot call tools" (Bedrock, Ollama,
64
+ # OpenRouter, OpenAI-compatible gateways).
65
+ _NO_TOOL_PATTERNS: Tuple[str, ...] = (
66
+ "support tool use",
67
+ "does not support tools",
68
+ "tool calling is not supported",
69
+ "tool use is not supported",
70
+ "does not support function calling",
71
+ "function calling is not supported",
72
+ )
73
+
74
+
75
+ def matches_no_tool_support(error: str) -> bool:
76
+ """True when *error* says the model itself cannot call tools."""
77
+ lowered = error.lower()
78
+ return any(p in lowered for p in _NO_TOOL_PATTERNS)
79
+
80
+
81
+ # Returned by ``_attempt`` when the model, not the story, failed: the attempt
82
+ # is retried on another model without spending a retry or consulting the judge.
83
+ _MODEL_DROPPED = Verdict(
84
+ story_id="", passed=False, failures=["model cannot call tools"]
85
+ )
86
+ _NO_LOW_MODEL = "no_tool_capable_low_model"
87
+
88
+
53
89
  @dataclass
54
90
  class PlanDecision:
55
91
  accepted: bool
@@ -100,6 +136,8 @@ class H2LRun:
100
136
  self._stop_cause = "done"
101
137
  self._stopped = False
102
138
  self._pool_index = 0
139
+ # Labels of low models that reported they cannot call tools.
140
+ self._dropped: Set[str] = set()
103
141
  # Surfaces turn this off when Bash would only be denied (headless
104
142
  # without --dangerously-skip-permissions).
105
143
  self.planner_bash = True
@@ -299,30 +337,39 @@ class H2LRun:
299
337
  feedback: Optional[str] = None
300
338
  attempt = 0
301
339
  while True:
302
- attempt += 1
303
- ref, provider = self._next_ref()
304
- verdict = await self._attempt(story, ref, provider, attempt, feedback)
305
- if verdict is None:
306
- return # cancelled or the run stopped
307
- if verdict.passed:
308
- outcome.state = StoryState.PASSED
309
- outcome.score = verdict.score
310
- outcome.model = ref.label
311
- return
312
- feedback = verdict.feedback
313
- if attempt < max_attempts:
314
- continue
340
+ # None once every low model has been dropped for lacking tools;
341
+ # only the escalation policy is left then.
342
+ picked = self._next_ref()
343
+ if picked is not None:
344
+ attempt += 1
345
+ ref, provider = picked
346
+ verdict = await self._attempt(story, ref, provider, attempt, feedback)
347
+ if verdict is None:
348
+ return # cancelled or the run stopped
349
+ if verdict is _MODEL_DROPPED:
350
+ attempt -= 1 # the model failed, not the story
351
+ continue
352
+ if verdict.passed:
353
+ outcome.state = StoryState.PASSED
354
+ outcome.score = verdict.score
355
+ outcome.model = ref.label
356
+ return
357
+ feedback = verdict.feedback
358
+ if attempt < max_attempts:
359
+ continue
315
360
  policy: str = self.config.escalation
316
361
  if policy == "ask":
317
362
  policy = await self.presenter.on_escalation(story, outcome)
318
- if policy == "retry":
363
+ if policy == "retry" and picked is not None:
319
364
  max_attempts += 1
320
365
  continue
321
366
  if policy == "high":
322
367
  await self._escalate_high(story, outcome, attempt + 1, feedback)
323
368
  return
324
369
  outcome.state = StoryState.BLOCKED
325
- outcome.reason = "retries_exhausted"
370
+ outcome.reason = (
371
+ "retries_exhausted" if picked is not None else _NO_LOW_MODEL
372
+ )
326
373
  return
327
374
 
328
375
  async def _attempt(
@@ -372,6 +419,10 @@ class H2LRun:
372
419
  outcome.state = StoryState.PENDING
373
420
  outcome.attempts = attempt - 1
374
421
  return None
422
+ if not report.success and matches_no_tool_support(report.error or ""):
423
+ outcome.attempts = attempt - 1
424
+ await self._drop_model(ref, report.error or "")
425
+ return _MODEL_DROPPED
375
426
 
376
427
  with self._high_context():
377
428
  verdict: Verdict = await judge.check(story, report)
@@ -451,13 +502,33 @@ class H2LRun:
451
502
 
452
503
  return observe
453
504
 
454
- def _next_ref(self) -> Tuple[H2LModelRef, Any]:
505
+ def _next_ref(self) -> Optional[Tuple[H2LModelRef, Any]]:
455
506
  tiers = self._ensure_tiers()
456
- pool = list(zip(self.config.low, tiers.low))
507
+ pool = [
508
+ (ref, provider)
509
+ for ref, provider in zip(self.config.low, tiers.low)
510
+ if ref.label not in self._dropped
511
+ ]
512
+ if not pool:
513
+ return None
457
514
  ref, provider = pool[self._pool_index % len(pool)]
458
515
  self._pool_index += 1
459
516
  return ref, provider
460
517
 
518
+ async def _drop_model(self, ref: H2LModelRef, error: str) -> None:
519
+ """Take a tool-less low model out of rotation; tell the surface once."""
520
+ if ref.label in self._dropped:
521
+ return
522
+ self._dropped.add(ref.label)
523
+ logger.info("H2L dropped %s, which cannot call tools: %s", ref.label, error)
524
+ hook = getattr(self.presenter, "on_model_dropped", None)
525
+ if hook is None:
526
+ return
527
+ try:
528
+ await hook(ref, error)
529
+ except Exception as exc: # presentation must never break the run
530
+ logger.debug("H2L on_model_dropped presenter hook failed: %s", exc)
531
+
461
532
  def _add_usage(self, tier: str, usage: UsageTotals) -> None:
462
533
  self.usage_by_tier[tier] = self.usage_by_tier[tier].add(usage)
463
534
 
@@ -9,6 +9,7 @@ carries the diff, verify output, and out-of-scope files the judge needs.
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ import difflib
12
13
  import logging
13
14
  import re
14
15
  import subprocess
@@ -41,6 +42,8 @@ class WorkerRunner:
41
42
  self.cwd = Path(cwd)
42
43
  # Overridable for tests; the real handler routes through the gate.
43
44
  self._tool_handler_factory: Callable[[Any], Any] = ToolHandler
45
+ # Story id -> {path: content or None}, taken before its first attempt.
46
+ self._baselines: Dict[str, Dict[str, Optional[str]]] = {}
44
47
 
45
48
  async def run(
46
49
  self,
@@ -102,6 +105,8 @@ class WorkerRunner:
102
105
  )
103
106
 
104
107
  agent_id = agent_id or f"h2l-{story.id}-{uuid.uuid4().hex[:8]}"
108
+ if story.id not in self._baselines:
109
+ self._baselines[story.id] = {p: _read_text(p) for p in story.files}
105
110
  before = self._dirty_files()
106
111
  status = 1
107
112
  with fleet_events.agent_context(agent_id, run_id):
@@ -247,6 +252,9 @@ class WorkerRunner:
247
252
  def _capture_changes(
248
253
  self, report: WorkerReport, story: Story, before: Dict[str, str]
249
254
  ) -> None:
255
+ if self._repo_root() is None:
256
+ self._capture_from_baseline(report, story)
257
+ return
250
258
  after = self._dirty_files()
251
259
  scope = set(story.files)
252
260
  new_or_changed = {p for p in after if p not in before or p in scope}
@@ -268,6 +276,48 @@ class WorkerRunner:
268
276
  return f"[git diff unavailable for {path}]"
269
277
  return proc.stdout
270
278
 
279
+ def _capture_from_baseline(self, report: WorkerReport, story: Story) -> None:
280
+ """Without git, diff the story's files against its first-attempt snapshot.
281
+
282
+ The snapshot stands in for HEAD, so retries accumulate as they do under
283
+ git. Out-of-scope writes cannot be seen this way; the tool-level scope
284
+ check is the guard there.
285
+ """
286
+ baseline = self._baselines.get(story.id, {})
287
+ changed: List[str] = []
288
+ chunks: List[str] = []
289
+ for path in story.files:
290
+ old, new = baseline.get(path), _read_text(path)
291
+ if old != new:
292
+ changed.append(path)
293
+ chunks.append(_unified_diff(path, old, new))
294
+ report.files_changed = changed
295
+ report.diff = "\n".join(chunks)
296
+
297
+
298
+ def _read_text(path: str) -> Optional[str]:
299
+ try:
300
+ return Path(path).read_text(encoding="utf-8", errors="replace")
301
+ except OSError:
302
+ return None
303
+
304
+
305
+ def _unified_diff(path: str, old: Optional[str], new: Optional[str]) -> str:
306
+ fromfile = path if old is not None else "/dev/null"
307
+ tofile = path if new is not None else "/dev/null"
308
+ lines = list(
309
+ difflib.unified_diff(
310
+ (old or "").splitlines(),
311
+ (new or "").splitlines(),
312
+ fromfile=fromfile,
313
+ tofile=tofile,
314
+ lineterm="",
315
+ )
316
+ )
317
+ # An empty file created or removed has no hunks; keep the header so the
318
+ # change is still visible.
319
+ return "\n".join(lines or [f"--- {fromfile}", f"+++ {tofile}"])
320
+
271
321
 
272
322
  def worker_tool_names(tools: List[ToolDefinition]) -> List[str]:
273
323
  return [t.name for t in tools]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omnimancer-cli
3
- Version: 0.3.6
3
+ Version: 0.3.7
4
4
  Summary: A unified command-line interface for multiple AI language models
5
5
  Author-email: Kellan Strong <kellan@jite.ai>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "omnimancer-cli"
7
- version = "0.3.6"
7
+ version = "0.3.7"
8
8
  description = "A unified command-line interface for multiple AI language models"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes