omnimancer-cli 0.3.5__tar.gz → 0.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/PKG-INFO +1 -1
  2. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/engine.py +40 -9
  3. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/env_loader.py +4 -2
  4. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/provider_initializer.py +31 -1
  5. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/bedrock.py +33 -12
  6. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer_cli.egg-info/PKG-INFO +1 -1
  7. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/pyproject.toml +1 -1
  8. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_bedrock_provider.py +103 -0
  9. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/LICENSE +0 -0
  10. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/README.md +0 -0
  11. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omn_fleet_hook.py +0 -0
  12. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/__init__.py +0 -0
  13. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/__main__.py +0 -0
  14. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/__init__.py +0 -0
  15. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/agent_loop.py +0 -0
  16. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/approval_formatter.py +0 -0
  17. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/approval_integration.py +0 -0
  18. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/approval_prompt.py +0 -0
  19. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/command_dispatch.py +0 -0
  20. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/commands.py +0 -0
  21. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/completion.py +0 -0
  22. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/display.py +0 -0
  23. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/dynamic_commands.py +0 -0
  24. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/file_mentions.py +0 -0
  25. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/h2l_command.py +0 -0
  26. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/h2l_headless.py +0 -0
  27. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/handlers/__init__.py +0 -0
  28. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/headless.py +0 -0
  29. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/headless_checkpoint.py +0 -0
  30. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/interface.py +0 -0
  31. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/prompt_input.py +0 -0
  32. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/pt_completion.py +0 -0
  33. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/session.py +0 -0
  34. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/subagent.py +0 -0
  35. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/system_prompts.py +0 -0
  36. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/tool_handler.py +0 -0
  37. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/turn_notify.py +0 -0
  38. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/cli/usage.py +0 -0
  39. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/__init__.py +0 -0
  40. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/__init__.py +0 -0
  41. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/approval_context.py +0 -0
  42. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/approval_interface.py +0 -0
  43. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/approval_manager.py +0 -0
  44. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/config.py +0 -0
  45. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/file_system_manager.py +0 -0
  46. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/program_executor.py +0 -0
  47. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/read_before_write_errors.py +0 -0
  48. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/status_core.py +0 -0
  49. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/status_manager.py +0 -0
  50. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/tool_definitions.py +0 -0
  51. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/types.py +0 -0
  52. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/web_client.py +0 -0
  53. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent/workflow_orchestrator.py +0 -0
  54. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent_engine.py +0 -0
  55. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent_managers.py +0 -0
  56. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/agent_mode_manager.py +0 -0
  57. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/chat_manager.py +0 -0
  58. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/config_manager.py +0 -0
  59. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/config_migration.py +0 -0
  60. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/config_validator.py +0 -0
  61. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/conversation_manager.py +0 -0
  62. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/fallback_manager.py +0 -0
  63. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/health_monitor.py +0 -0
  64. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/history_manager.py +0 -0
  65. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/hooks.py +0 -0
  66. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/mcp_integration_layer.py +0 -0
  67. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/models.py +0 -0
  68. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/prompt_enhancer.py +0 -0
  69. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/provider_capabilities.py +0 -0
  70. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/provider_registry.py +0 -0
  71. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/provider_resolver.py +0 -0
  72. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/rate_limit_fallback.py +0 -0
  73. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/__init__.py +0 -0
  74. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/approval_workflow.py +0 -0
  75. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/audit_logger.py +0 -0
  76. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/permission_controller.py +0 -0
  77. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/permission_rules.py +0 -0
  78. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/sandbox_manager.py +0 -0
  79. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/security/security_manager.py +0 -0
  80. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/core/signal_handler.py +0 -0
  81. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/events/__init__.py +0 -0
  82. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/events/emitter.py +0 -0
  83. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/events/jsonl_writer.py +0 -0
  84. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/events/schema.py +0 -0
  85. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/__init__.py +0 -0
  86. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/judge.py +0 -0
  87. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/loop.py +0 -0
  88. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/models.py +0 -0
  89. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/orchestrator.py +0 -0
  90. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/planner.py +0 -0
  91. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/prompts.py +0 -0
  92. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/refs.py +0 -0
  93. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/render.py +0 -0
  94. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/h2l/worker.py +0 -0
  95. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/mcp/__init__.py +0 -0
  96. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/mcp/client.py +0 -0
  97. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/mcp/manager.py +0 -0
  98. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/__init__.py +0 -0
  99. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/azure.py +0 -0
  100. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/base.py +0 -0
  101. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/cache_tokens.py +0 -0
  102. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/claude.py +0 -0
  103. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/claude_code.py +0 -0
  104. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/claude_credentials.py +0 -0
  105. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/cohere.py +0 -0
  106. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/digitalocean.py +0 -0
  107. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/factory.py +0 -0
  108. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/gemini.py +0 -0
  109. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/mistral.py +0 -0
  110. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/ollama.py +0 -0
  111. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/openai.py +0 -0
  112. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/openai_compatible.py +0 -0
  113. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/openrouter.py +0 -0
  114. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/perplexity.py +0 -0
  115. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/vertex.py +0 -0
  116. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/providers/xai.py +0 -0
  117. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/__init__.py +0 -0
  118. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/fleet/__init__.py +0 -0
  119. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/fleet/app.py +0 -0
  120. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/fleet/cli.py +0 -0
  121. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/fleet/models.py +0 -0
  122. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/fleet/sources.py +0 -0
  123. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/tui/fleet/widgets.py +0 -0
  124. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/ui/__init__.py +0 -0
  125. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/ui/cancellation_handler.py +0 -0
  126. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/ui/progress_indicator.py +0 -0
  127. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/ui/streaming_display.py +0 -0
  128. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/ui/turn_activity.py +0 -0
  129. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/utils/__init__.py +0 -0
  130. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/utils/error_handler.py +0 -0
  131. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/utils/errors.py +0 -0
  132. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer/utils/retry.py +0 -0
  133. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer_cli.egg-info/SOURCES.txt +0 -0
  134. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer_cli.egg-info/dependency_links.txt +0 -0
  135. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer_cli.egg-info/entry_points.txt +0 -0
  136. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer_cli.egg-info/requires.txt +0 -0
  137. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/omnimancer_cli.egg-info/top_level.txt +0 -0
  138. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/setup.cfg +0 -0
  139. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_approval_integration_compatibility.py +0 -0
  140. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_approval_preview_format.py +0 -0
  141. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_approval_remember.py +0 -0
  142. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_approval_system.py +0 -0
  143. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_azure_provider.py +0 -0
  144. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_claude_code_provider.py +0 -0
  145. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_claude_credentials.py +0 -0
  146. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_claude_prompt_cache.py +0 -0
  147. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_claude_provider.py +0 -0
  148. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_cohere_provider.py +0 -0
  149. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_command_approval_security.py +0 -0
  150. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_config_comprehensive.py +0 -0
  151. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_config_migration_passthrough.py +0 -0
  152. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_conversation_history.py +0 -0
  153. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_conversation_manager.py +0 -0
  154. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_core_engine.py +0 -0
  155. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_digitalocean_provider.py +0 -0
  156. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_enhanced_model_info.py +0 -0
  157. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_env_loader.py +0 -0
  158. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_error_handling_robustness.py +0 -0
  159. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_execution_timeout_errors.py +0 -0
  160. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_fallback_manager.py +0 -0
  161. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_fixtures.py +0 -0
  162. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_gemini_provider.py +0 -0
  163. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_hooks.py +0 -0
  164. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_httpx_compatibility.py +0 -0
  165. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_import_validation.py +0 -0
  166. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_interface_smoke_tests.py +0 -0
  167. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_mcp_integration.py +0 -0
  168. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_mcp_integration_layer.py +0 -0
  169. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_mcp_management_commands.py +0 -0
  170. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_mistral_provider.py +0 -0
  171. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_models.py +0 -0
  172. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_ollama_provider.py +0 -0
  173. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_openai_compatible_provider.py +0 -0
  174. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_openai_context_fit.py +0 -0
  175. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_openai_provider_tools.py +0 -0
  176. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_openai_request_timeout.py +0 -0
  177. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_openai_streaming.py +0 -0
  178. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_openrouter_provider.py +0 -0
  179. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_permission_rules.py +0 -0
  180. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_perplexity_provider.py +0 -0
  181. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_program_executor.py +0 -0
  182. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_aliases.py +0 -0
  183. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_base.py +0 -0
  184. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_cache_tokens.py +0 -0
  185. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_capability_consistency.py +0 -0
  186. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_discovery.py +0 -0
  187. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_integration.py +0 -0
  188. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_provider_registry.py +0 -0
  189. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_rate_limit_fallback.py +0 -0
  190. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_retry.py +0 -0
  191. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_security_validation.py +0 -0
  192. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_system_integration.py +0 -0
  193. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_system_prompts.py +0 -0
  194. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_tool_calling_integration.py +0 -0
  195. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_tool_handler.py +0 -0
  196. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_vertex_provider.py +0 -0
  197. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_web_request_markers.py +0 -0
  198. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/tests/test_workflows_comprehensive.py +0 -0
  199. {omnimancer_cli-0.3.5 → omnimancer_cli-0.3.6}/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.5
3
+ Version: 0.3.6
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
@@ -182,11 +182,12 @@ class CoreEngine:
182
182
  # DigitalOcean request). Leave it unset so the user gets a
183
183
  # clear error naming the provider.
184
184
  self._unavailable_default_provider = config.default_provider
185
+ # One accurate line instead of a guess; the full
186
+ # explanation is built on demand by _no_provider_error.
185
187
  logger.error(
186
- "Default provider '%s' is configured but failed to "
187
- "initialize (missing or invalid API key?). Not falling "
188
- "back to another provider.",
188
+ "Default provider '%s' is not available: %s",
189
189
  config.default_provider,
190
+ self._no_provider_error(),
190
191
  )
191
192
  elif self.providers:
192
193
  # No default configured — use first available provider.
@@ -306,14 +307,44 @@ class CoreEngine:
306
307
  missing, rather than a generic "no provider" message.
307
308
  """
308
309
  unavailable = getattr(self, "_unavailable_default_provider", None)
309
- if unavailable:
310
+ if not unavailable:
311
+ return "No provider available. Please configure a provider first."
312
+
313
+ env_prefix = "OMNIMANCER_" + unavailable.upper().replace("-", "_")
314
+ try:
315
+ from .provider_resolver import effective_providers
316
+
317
+ entry = effective_providers(self.config_manager).get(unavailable)
318
+ except Exception as exc: # diagnostics must not raise
319
+ logger.debug(f"Could not inspect entry for {unavailable}: {exc}")
320
+ entry = None
321
+
322
+ if entry is None:
310
323
  return (
311
- f"Provider '{unavailable}' is selected but failed to initialize "
312
- f"— most likely a missing or invalid API key. Configure it (e.g. "
313
- f"'/config set-provider {unavailable}' or the provider's API-key "
314
- f"environment variable) and try again."
324
+ f"Provider '{unavailable}' is selected as the default but is "
325
+ f"not configured: there is no entry for it and no "
326
+ f"{env_prefix}_API_KEY in the environment. Add it with "
327
+ f"'/config set-provider {unavailable}', or pick a different "
328
+ f"default with '/switch'."
315
329
  )
316
- return "No provider available. Please configure a provider first."
330
+
331
+ if not (getattr(entry, "model", "") or "").strip():
332
+ return (
333
+ f"Provider '{unavailable}' has no model configured, so it was "
334
+ f"not started. Set {env_prefix}_MODEL, or run '/model' to pick "
335
+ f"one."
336
+ )
337
+
338
+ from .provider_initializer import ProviderInitializer
339
+
340
+ reason = ProviderInitializer.get_last_init_errors().get(unavailable)
341
+ if reason:
342
+ return f"Provider '{unavailable}' did not start: {reason}"
343
+ return (
344
+ f"Provider '{unavailable}' is configured but did not start. Check "
345
+ f"its credentials ('/config set-provider {unavailable}' or "
346
+ f"{env_prefix}_API_KEY) and try again."
347
+ )
317
348
 
318
349
  # ------------------------------------------------------------------
319
350
  # Fallback wiring
@@ -230,7 +230,7 @@ def apply_env_overrides(config: "Config") -> "Config":
230
230
  model=overrides.get("model", ""),
231
231
  )
232
232
  effective.providers[name] = provider_config
233
- logger.info(f"Created provider '{name}' from environment overrides")
233
+ logger.debug(f"Created provider '{name}' from environment overrides")
234
234
 
235
235
  if "api_key" in overrides:
236
236
  provider_config.api_key = overrides["api_key"]
@@ -241,6 +241,8 @@ def apply_env_overrides(config: "Config") -> "Config":
241
241
 
242
242
  if default_override:
243
243
  effective.default_provider = default_override
244
- logger.info(f"Default provider overridden from environment: {default_override}")
244
+ logger.debug(
245
+ f"Default provider overridden from environment: {default_override}"
246
+ )
245
247
 
246
248
  return effective
@@ -44,6 +44,8 @@ class ProviderInitializer:
44
44
  # Configuration
45
45
  _lazy_loading_enabled: bool = True
46
46
  _cache_enabled: bool = True
47
+ # provider name -> why it failed, from the most recent initialization
48
+ _last_init_errors: Dict[str, str] = {}
47
49
 
48
50
  @classmethod
49
51
  def get_provider_class(cls, provider_name: str) -> Type[BaseProvider]:
@@ -447,14 +449,35 @@ class ProviderInitializer:
447
449
  provider_configs: Dictionary of provider configurations
448
450
  config_manager: Optional config manager for API key decryption
449
451
 
452
+ An entry with no model id is *incomplete*, not broken: environment
453
+ materialized entries (``OMNIMANCER_<PROVIDER>_API_KEY`` with no
454
+ ``_MODEL``) are legitimate when the model comes from somewhere else,
455
+ such as an H2L ``--high <entry>:<model>`` ref. Those are skipped
456
+ without an error; real failures are recorded so callers can report
457
+ the actual cause instead of guessing at one.
458
+
450
459
  Returns:
451
460
  Dictionary of initialized provider instances
452
461
  """
453
462
  from ..providers.factory import ProviderFactory
454
463
 
455
464
  providers = {}
465
+ errors: Dict[str, str] = {}
456
466
 
457
467
  for provider_name, config in provider_configs.items():
468
+ if not (getattr(config, "model", "") or "").strip():
469
+ env_var = (
470
+ "OMNIMANCER_" + provider_name.upper().replace("-", "_") + "_MODEL"
471
+ )
472
+ logger.info(
473
+ "Provider '%s' has no model configured; not started. "
474
+ "Set %s or '/config set-provider %s'. Model refs that "
475
+ "carry their own model (H2L tiers) still work.",
476
+ provider_name,
477
+ env_var,
478
+ provider_name,
479
+ )
480
+ continue
458
481
  try:
459
482
  # Use ProviderFactory instead of direct class loading
460
483
  provider = ProviderFactory.create_provider(
@@ -463,12 +486,19 @@ class ProviderInitializer:
463
486
  providers[provider_name] = provider
464
487
  logger.info(f"Initialized provider: {provider_name}")
465
488
  except Exception as e:
466
- logger.error(f"Failed to initialize provider {provider_name}: {e}")
489
+ errors[provider_name] = str(e)
490
+ logger.error(f"Provider '{provider_name}' did not start: {e}")
467
491
  # Continue with other providers instead of failing completely
468
492
  continue
469
493
 
494
+ cls._last_init_errors = errors
470
495
  return providers
471
496
 
497
+ @classmethod
498
+ def get_last_init_errors(cls) -> Dict[str, str]:
499
+ """Why each provider failed during the most recent initialization."""
500
+ return dict(getattr(cls, "_last_init_errors", {}) or {})
501
+
472
502
  @classmethod
473
503
  def preload_model_info(cls, provider_names: List[str]) -> None:
474
504
  """
@@ -70,6 +70,14 @@ class BedrockProvider(BaseProvider):
70
70
  # AWS-specific configuration
71
71
  self.aws_region = kwargs.get("aws_region", "us-east-1")
72
72
 
73
+ # Chat-completion timeout. ProviderConfig's field is `timeout`;
74
+ # `request_timeout` is accepted as an alias, then
75
+ # OMNIMANCER_REQUEST_TIMEOUT, then DEFAULT_REQUEST_TIMEOUT. A long
76
+ # agent turn on a large model does not fit in the old hardcoded 30s.
77
+ self.request_timeout = self._resolve_request_timeout(
78
+ kwargs.get("request_timeout") or kwargs.get("timeout")
79
+ )
80
+
73
81
  # Standard parameters. Bedrock fronts third-party models that reject
74
82
  # temperature/topP in inferenceConfig outright, and nothing in
75
83
  # Omnimancer lets a user pick these values, so an unset param stays
@@ -82,6 +90,21 @@ class BedrockProvider(BaseProvider):
82
90
  # Build base URL for Bedrock API key authentication
83
91
  self.base_url = f"https://bedrock-runtime.{self.aws_region}.amazonaws.com"
84
92
 
93
+ def _timeout_network_error(self) -> NetworkError:
94
+ """The NetworkError raised when a Converse request times out."""
95
+ return NetworkError(
96
+ f"Request to AWS Bedrock API timed out after "
97
+ f"{self.request_timeout:.0f}s. Raise it with this provider's "
98
+ f"timeout (providers.<name>.timeout in config.json) or the "
99
+ f"OMNIMANCER_REQUEST_TIMEOUT environment variable."
100
+ )
101
+
102
+ def _model_for_url(self) -> str:
103
+ """The path segment for this model: ARNs carry an unusable prefix."""
104
+ if self._is_arn(self.model):
105
+ return self._extract_model_id_from_arn(self.model)
106
+ return self.model
107
+
85
108
  def _is_arn(self, model_id: str) -> bool:
86
109
  """Check if the model ID is in ARN format."""
87
110
  return model_id.startswith("arn:aws:bedrock:")
@@ -144,7 +167,7 @@ class BedrockProvider(BaseProvider):
144
167
  request_body = self._prepare_bedrock_request(message, context)
145
168
 
146
169
  # Build URL for the specific model
147
- url = f"{self.base_url}/model/{self.model}/converse"
170
+ url = f"{self.base_url}/model/{self._model_for_url()}/converse"
148
171
 
149
172
  # Create headers with Bearer token authentication
150
173
  headers = {
@@ -155,14 +178,17 @@ class BedrockProvider(BaseProvider):
155
178
  # Make API request
156
179
  async with httpx.AsyncClient() as client:
157
180
  response = await client.post(
158
- url, headers=headers, content=request_body, timeout=30.0
181
+ url,
182
+ headers=headers,
183
+ content=request_body,
184
+ timeout=self.request_timeout,
159
185
  )
160
186
 
161
187
  # Handle response
162
188
  return self._handle_response(response)
163
189
 
164
190
  except httpx.TimeoutException:
165
- raise NetworkError("Request to AWS Bedrock API timed out")
191
+ raise self._timeout_network_error()
166
192
  except httpx.RequestError as e:
167
193
  raise NetworkError(f"Network error: {e}")
168
194
  except Exception as e:
@@ -191,13 +217,8 @@ class BedrockProvider(BaseProvider):
191
217
  message, context, available_tools
192
218
  )
193
219
 
194
- # Build URL - use appropriate endpoint based on model format
195
- if self._is_arn(self.model):
196
- # For ARN format, extract the actual model ID for the URL
197
- model_for_url = self._extract_model_id_from_arn(self.model)
198
- url = f"{self.base_url}/model" f"/{model_for_url}/converse"
199
- else:
200
- url = f"{self.base_url}/model" f"/{self.model}/converse"
220
+ # Build URL - an ARN model id is not usable as a path segment
221
+ url = f"{self.base_url}/model/{self._model_for_url()}/converse"
201
222
 
202
223
  # Create headers with Bearer token authentication
203
224
  headers = {
@@ -211,14 +232,14 @@ class BedrockProvider(BaseProvider):
211
232
  url,
212
233
  headers=headers,
213
234
  content=request_body,
214
- timeout=30.0,
235
+ timeout=self.request_timeout,
215
236
  )
216
237
 
217
238
  # Handle response with tool calls
218
239
  return self._handle_response_with_tools(response)
219
240
 
220
241
  except httpx.TimeoutException:
221
- raise NetworkError("Request to AWS Bedrock API timed out")
242
+ raise self._timeout_network_error()
222
243
  except httpx.RequestError as e:
223
244
  raise NetworkError(f"Network error: {e}")
224
245
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omnimancer-cli
3
- Version: 0.3.5
3
+ Version: 0.3.6
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.5"
7
+ version = "0.3.6"
8
8
  description = "A unified command-line interface for multiple AI language models"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -421,6 +421,86 @@ class TestBedrockProviderRequestPreparation:
421
421
  # ---------------------------------------------------------------------------
422
422
 
423
423
 
424
+ class TestBedrockProviderRequestTimeout:
425
+ """Bedrock hardcoded 30s while the base class already had the knob.
426
+
427
+ BaseProvider._resolve_request_timeout (explicit config, then
428
+ OMNIMANCER_REQUEST_TIMEOUT, then DEFAULT_REQUEST_TIMEOUT) existed for
429
+ exactly this reason: a long agent turn on a large model needs more than
430
+ 30 seconds, and only the OpenAI family honoured it.
431
+ """
432
+
433
+ @pytest.fixture(autouse=True)
434
+ def no_env_timeout(self, monkeypatch):
435
+ monkeypatch.delenv("OMNIMANCER_REQUEST_TIMEOUT", raising=False)
436
+ yield
437
+
438
+ def test_default_is_the_shared_default_not_thirty(self):
439
+ provider = BedrockProvider(api_key="k", model=_SONNET_MODEL)
440
+ assert provider.request_timeout == BedrockProvider.DEFAULT_REQUEST_TIMEOUT
441
+ assert provider.request_timeout > 30.0
442
+
443
+ def test_configured_timeout_wins(self):
444
+ provider = BedrockProvider(api_key="k", model=_SONNET_MODEL, timeout=600)
445
+ assert provider.request_timeout == 600.0
446
+
447
+ def test_env_timeout_is_honoured(self, monkeypatch):
448
+ monkeypatch.setenv("OMNIMANCER_REQUEST_TIMEOUT", "300")
449
+ provider = BedrockProvider(api_key="k", model=_SONNET_MODEL)
450
+ assert provider.request_timeout == 300.0
451
+
452
+ @pytest.mark.asyncio
453
+ async def test_send_message_uses_the_resolved_timeout(
454
+ self, sample_chat_context, mock_converse_response
455
+ ):
456
+ provider = BedrockProvider(api_key="k", model=_SONNET_MODEL, timeout=450)
457
+ mock_response = MagicMock()
458
+ mock_response.status_code = 200
459
+ mock_response.json.return_value = mock_converse_response
460
+
461
+ with patch("httpx.AsyncClient") as mock_client:
462
+ mock_post = AsyncMock(return_value=mock_response)
463
+ mock_client.return_value.__aenter__.return_value.post = mock_post
464
+ await provider.send_message("Hello", sample_chat_context)
465
+
466
+ assert mock_post.call_args.kwargs["timeout"] == 450.0
467
+
468
+ @pytest.mark.asyncio
469
+ async def test_send_message_with_tools_uses_the_resolved_timeout(
470
+ self, sample_chat_context, sample_tools, mock_tool_response
471
+ ):
472
+ provider = BedrockProvider(api_key="k", model=_SONNET_MODEL, timeout=450)
473
+ mock_response = MagicMock()
474
+ mock_response.status_code = 200
475
+ mock_response.json.return_value = mock_tool_response
476
+
477
+ with patch("httpx.AsyncClient") as mock_client:
478
+ mock_post = AsyncMock(return_value=mock_response)
479
+ mock_client.return_value.__aenter__.return_value.post = mock_post
480
+ await provider.send_message_with_tools(
481
+ "Hello", sample_chat_context, sample_tools
482
+ )
483
+
484
+ assert mock_post.call_args.kwargs["timeout"] == 450.0
485
+
486
+ @pytest.mark.asyncio
487
+ async def test_timeout_error_names_the_limit_and_the_knob(
488
+ self, sample_chat_context
489
+ ):
490
+ """A bare "timed out" leaves the user nothing to act on."""
491
+ provider = BedrockProvider(api_key="k", model=_SONNET_MODEL, timeout=45)
492
+ with patch("httpx.AsyncClient") as mock_client:
493
+ mock_client.return_value.__aenter__.return_value.post = AsyncMock(
494
+ side_effect=httpx.TimeoutException("too slow")
495
+ )
496
+ with pytest.raises(NetworkError) as exc:
497
+ await provider.send_message("Hello", sample_chat_context)
498
+
499
+ message = str(exc.value)
500
+ assert "45" in message
501
+ assert "OMNIMANCER_REQUEST_TIMEOUT" in message
502
+
503
+
424
504
  class TestBedrockProviderARNRoutingBehaviour:
425
505
  """Test that ARN-format models result in correct URL construction.
426
506
 
@@ -460,6 +540,29 @@ class TestBedrockProviderARNRoutingBehaviour:
460
540
  assert bare_model_id in call_url
461
541
  assert "arn:aws:bedrock:" not in call_url
462
542
 
543
+ @pytest.mark.asyncio
544
+ async def test_send_message_uses_extracted_id_in_url_for_arn(
545
+ self, bedrock_provider_arn, sample_chat_context, mock_converse_response
546
+ ):
547
+ """The no-tools path must extract the ARN too.
548
+
549
+ send_message_with_tools extracted the bare model id while
550
+ send_message interpolated the whole ARN, so the same configured
551
+ model worked in agent mode and built a malformed URL in plain chat.
552
+ """
553
+ mock_response = MagicMock()
554
+ mock_response.status_code = 200
555
+ mock_response.json.return_value = mock_converse_response
556
+
557
+ with patch("httpx.AsyncClient") as mock_client:
558
+ mock_post = AsyncMock(return_value=mock_response)
559
+ mock_client.return_value.__aenter__.return_value.post = mock_post
560
+ await bedrock_provider_arn.send_message("Hello", sample_chat_context)
561
+
562
+ call_url = mock_post.call_args[0][0]
563
+ assert _SONNET_ARN.split("/")[-1] in call_url
564
+ assert "arn:aws:bedrock:" not in call_url
565
+
463
566
  @pytest.mark.asyncio
464
567
  async def test_send_message_standard_model_uses_model_id_in_url(
465
568
  self, bedrock_provider, sample_chat_context
File without changes
File without changes
File without changes