autobyteus 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. autobyteus/agent/agent.py +15 -5
  2. autobyteus/agent/bootstrap_steps/__init__.py +3 -3
  3. autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +5 -59
  4. autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +1 -4
  5. autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +1 -3
  6. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +16 -13
  7. autobyteus/agent/bootstrap_steps/working_context_snapshot_restore_step.py +38 -0
  8. autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +2 -4
  9. autobyteus/agent/context/agent_config.py +47 -20
  10. autobyteus/agent/context/agent_context.py +23 -18
  11. autobyteus/agent/context/agent_runtime_state.py +21 -19
  12. autobyteus/agent/events/__init__.py +16 -1
  13. autobyteus/agent/events/agent_events.py +43 -3
  14. autobyteus/agent/events/agent_input_event_queue_manager.py +79 -26
  15. autobyteus/agent/events/event_store.py +57 -0
  16. autobyteus/agent/events/notifiers.py +69 -59
  17. autobyteus/agent/events/worker_event_dispatcher.py +21 -64
  18. autobyteus/agent/factory/agent_factory.py +83 -6
  19. autobyteus/agent/handlers/__init__.py +2 -0
  20. autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +51 -34
  21. autobyteus/agent/handlers/bootstrap_event_handler.py +155 -0
  22. autobyteus/agent/handlers/inter_agent_message_event_handler.py +10 -0
  23. autobyteus/agent/handlers/lifecycle_event_logger.py +19 -11
  24. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +10 -15
  25. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +188 -48
  26. autobyteus/agent/handlers/tool_execution_approval_event_handler.py +0 -10
  27. autobyteus/agent/handlers/tool_invocation_request_event_handler.py +53 -48
  28. autobyteus/agent/handlers/tool_result_event_handler.py +7 -8
  29. autobyteus/agent/handlers/user_input_message_event_handler.py +10 -3
  30. autobyteus/agent/input_processor/memory_ingest_input_processor.py +44 -0
  31. autobyteus/agent/lifecycle/__init__.py +12 -0
  32. autobyteus/agent/lifecycle/base_processor.py +109 -0
  33. autobyteus/agent/lifecycle/events.py +35 -0
  34. autobyteus/agent/lifecycle/processor_definition.py +36 -0
  35. autobyteus/agent/lifecycle/processor_registry.py +106 -0
  36. autobyteus/agent/llm_request_assembler.py +98 -0
  37. autobyteus/agent/llm_response_processor/__init__.py +1 -8
  38. autobyteus/agent/message/context_file_type.py +1 -1
  39. autobyteus/agent/runtime/agent_runtime.py +29 -21
  40. autobyteus/agent/runtime/agent_worker.py +98 -19
  41. autobyteus/agent/shutdown_steps/__init__.py +2 -0
  42. autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +2 -0
  43. autobyteus/agent/shutdown_steps/tool_cleanup_step.py +58 -0
  44. autobyteus/agent/status/__init__.py +14 -0
  45. autobyteus/agent/status/manager.py +93 -0
  46. autobyteus/agent/status/status_deriver.py +96 -0
  47. autobyteus/agent/{phases/phase_enum.py → status/status_enum.py} +16 -16
  48. autobyteus/agent/status/status_update_utils.py +73 -0
  49. autobyteus/agent/streaming/__init__.py +52 -5
  50. autobyteus/agent/streaming/adapters/__init__.py +18 -0
  51. autobyteus/agent/streaming/adapters/invocation_adapter.py +184 -0
  52. autobyteus/agent/streaming/adapters/tool_call_parsing.py +163 -0
  53. autobyteus/agent/streaming/adapters/tool_syntax_registry.py +67 -0
  54. autobyteus/agent/streaming/agent_event_stream.py +3 -183
  55. autobyteus/agent/streaming/api_tool_call/__init__.py +16 -0
  56. autobyteus/agent/streaming/api_tool_call/file_content_streamer.py +56 -0
  57. autobyteus/agent/streaming/api_tool_call/json_string_field_extractor.py +175 -0
  58. autobyteus/agent/streaming/api_tool_call_streaming_response_handler.py +4 -0
  59. autobyteus/agent/streaming/events/__init__.py +6 -0
  60. autobyteus/agent/streaming/events/stream_event_payloads.py +284 -0
  61. autobyteus/agent/streaming/events/stream_events.py +141 -0
  62. autobyteus/agent/streaming/handlers/__init__.py +15 -0
  63. autobyteus/agent/streaming/handlers/api_tool_call_streaming_response_handler.py +303 -0
  64. autobyteus/agent/streaming/handlers/parsing_streaming_response_handler.py +107 -0
  65. autobyteus/agent/streaming/handlers/pass_through_streaming_response_handler.py +107 -0
  66. autobyteus/agent/streaming/handlers/streaming_handler_factory.py +177 -0
  67. autobyteus/agent/streaming/handlers/streaming_response_handler.py +58 -0
  68. autobyteus/agent/streaming/parser/__init__.py +61 -0
  69. autobyteus/agent/streaming/parser/event_emitter.py +181 -0
  70. autobyteus/agent/streaming/parser/events.py +4 -0
  71. autobyteus/agent/streaming/parser/invocation_adapter.py +4 -0
  72. autobyteus/agent/streaming/parser/json_parsing_strategies/__init__.py +19 -0
  73. autobyteus/agent/streaming/parser/json_parsing_strategies/base.py +32 -0
  74. autobyteus/agent/streaming/parser/json_parsing_strategies/default.py +34 -0
  75. autobyteus/agent/streaming/parser/json_parsing_strategies/gemini.py +31 -0
  76. autobyteus/agent/streaming/parser/json_parsing_strategies/openai.py +64 -0
  77. autobyteus/agent/streaming/parser/json_parsing_strategies/registry.py +75 -0
  78. autobyteus/agent/streaming/parser/parser_context.py +227 -0
  79. autobyteus/agent/streaming/parser/parser_factory.py +132 -0
  80. autobyteus/agent/streaming/parser/sentinel_format.py +7 -0
  81. autobyteus/agent/streaming/parser/state_factory.py +62 -0
  82. autobyteus/agent/streaming/parser/states/__init__.py +1 -0
  83. autobyteus/agent/streaming/parser/states/base_state.py +60 -0
  84. autobyteus/agent/streaming/parser/states/custom_xml_tag_run_bash_parsing_state.py +38 -0
  85. autobyteus/agent/streaming/parser/states/custom_xml_tag_write_file_parsing_state.py +55 -0
  86. autobyteus/agent/streaming/parser/states/delimited_content_state.py +146 -0
  87. autobyteus/agent/streaming/parser/states/json_initialization_state.py +144 -0
  88. autobyteus/agent/streaming/parser/states/json_tool_parsing_state.py +137 -0
  89. autobyteus/agent/streaming/parser/states/sentinel_content_state.py +30 -0
  90. autobyteus/agent/streaming/parser/states/sentinel_initialization_state.py +117 -0
  91. autobyteus/agent/streaming/parser/states/text_state.py +78 -0
  92. autobyteus/agent/streaming/parser/states/xml_patch_file_tool_parsing_state.py +328 -0
  93. autobyteus/agent/streaming/parser/states/xml_run_bash_tool_parsing_state.py +129 -0
  94. autobyteus/agent/streaming/parser/states/xml_tag_initialization_state.py +151 -0
  95. autobyteus/agent/streaming/parser/states/xml_tool_parsing_state.py +63 -0
  96. autobyteus/agent/streaming/parser/states/xml_write_file_tool_parsing_state.py +343 -0
  97. autobyteus/agent/streaming/parser/strategies/__init__.py +17 -0
  98. autobyteus/agent/streaming/parser/strategies/base.py +24 -0
  99. autobyteus/agent/streaming/parser/strategies/json_tool_strategy.py +26 -0
  100. autobyteus/agent/streaming/parser/strategies/registry.py +28 -0
  101. autobyteus/agent/streaming/parser/strategies/sentinel_strategy.py +23 -0
  102. autobyteus/agent/streaming/parser/strategies/xml_tag_strategy.py +21 -0
  103. autobyteus/agent/streaming/parser/stream_scanner.py +167 -0
  104. autobyteus/agent/streaming/parser/streaming_parser.py +212 -0
  105. autobyteus/agent/streaming/parser/tool_call_parsing.py +4 -0
  106. autobyteus/agent/streaming/parser/tool_constants.py +7 -0
  107. autobyteus/agent/streaming/parser/tool_syntax_registry.py +4 -0
  108. autobyteus/agent/streaming/parser/xml_tool_parsing_state_registry.py +55 -0
  109. autobyteus/agent/streaming/parsing_streaming_response_handler.py +4 -0
  110. autobyteus/agent/streaming/pass_through_streaming_response_handler.py +4 -0
  111. autobyteus/agent/streaming/queue_streamer.py +3 -57
  112. autobyteus/agent/streaming/segments/__init__.py +5 -0
  113. autobyteus/agent/streaming/segments/segment_events.py +82 -0
  114. autobyteus/agent/streaming/stream_event_payloads.py +2 -223
  115. autobyteus/agent/streaming/stream_events.py +3 -140
  116. autobyteus/agent/streaming/streaming_handler_factory.py +4 -0
  117. autobyteus/agent/streaming/streaming_response_handler.py +4 -0
  118. autobyteus/agent/streaming/streams/__init__.py +5 -0
  119. autobyteus/agent/streaming/streams/agent_event_stream.py +197 -0
  120. autobyteus/agent/streaming/utils/__init__.py +5 -0
  121. autobyteus/agent/streaming/utils/queue_streamer.py +59 -0
  122. autobyteus/agent/system_prompt_processor/__init__.py +2 -0
  123. autobyteus/agent/system_prompt_processor/available_skills_processor.py +96 -0
  124. autobyteus/agent/system_prompt_processor/base_processor.py +1 -1
  125. autobyteus/agent/system_prompt_processor/processor_meta.py +15 -2
  126. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +39 -58
  127. autobyteus/agent/token_budget.py +56 -0
  128. autobyteus/agent/tool_execution_result_processor/memory_ingest_tool_result_processor.py +29 -0
  129. autobyteus/agent/tool_invocation.py +16 -40
  130. autobyteus/agent/tool_invocation_preprocessor/__init__.py +9 -0
  131. autobyteus/agent/tool_invocation_preprocessor/base_preprocessor.py +45 -0
  132. autobyteus/agent/tool_invocation_preprocessor/processor_definition.py +15 -0
  133. autobyteus/agent/tool_invocation_preprocessor/processor_meta.py +33 -0
  134. autobyteus/agent/tool_invocation_preprocessor/processor_registry.py +60 -0
  135. autobyteus/agent/utils/wait_for_idle.py +12 -14
  136. autobyteus/agent/workspace/base_workspace.py +6 -27
  137. autobyteus/agent_team/agent_team.py +3 -3
  138. autobyteus/agent_team/agent_team_builder.py +1 -41
  139. autobyteus/agent_team/bootstrap_steps/__init__.py +0 -4
  140. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +8 -18
  141. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +4 -16
  142. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +1 -2
  143. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +1 -2
  144. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +1 -2
  145. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +4 -4
  146. autobyteus/agent_team/context/agent_team_config.py +6 -3
  147. autobyteus/agent_team/context/agent_team_context.py +25 -3
  148. autobyteus/agent_team/context/agent_team_runtime_state.py +9 -6
  149. autobyteus/agent_team/events/__init__.py +11 -0
  150. autobyteus/agent_team/events/agent_team_event_dispatcher.py +22 -9
  151. autobyteus/agent_team/events/agent_team_events.py +16 -0
  152. autobyteus/agent_team/events/event_store.py +57 -0
  153. autobyteus/agent_team/factory/agent_team_factory.py +8 -0
  154. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +18 -2
  155. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +21 -5
  156. autobyteus/agent_team/handlers/process_user_message_event_handler.py +17 -8
  157. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +19 -4
  158. autobyteus/agent_team/runtime/agent_team_runtime.py +41 -10
  159. autobyteus/agent_team/runtime/agent_team_worker.py +69 -5
  160. autobyteus/agent_team/status/__init__.py +14 -0
  161. autobyteus/agent_team/status/agent_team_status.py +18 -0
  162. autobyteus/agent_team/status/agent_team_status_manager.py +33 -0
  163. autobyteus/agent_team/status/status_deriver.py +62 -0
  164. autobyteus/agent_team/status/status_update_utils.py +42 -0
  165. autobyteus/agent_team/streaming/__init__.py +2 -2
  166. autobyteus/agent_team/streaming/agent_team_event_notifier.py +6 -6
  167. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +4 -4
  168. autobyteus/agent_team/streaming/agent_team_stream_events.py +3 -3
  169. autobyteus/agent_team/system_prompt_processor/__init__.py +6 -0
  170. autobyteus/agent_team/system_prompt_processor/team_manifest_injector_processor.py +76 -0
  171. autobyteus/agent_team/task_notification/task_notification_mode.py +19 -0
  172. autobyteus/agent_team/utils/wait_for_idle.py +4 -4
  173. autobyteus/cli/agent_cli.py +18 -10
  174. autobyteus/cli/agent_team_tui/app.py +14 -11
  175. autobyteus/cli/agent_team_tui/state.py +13 -15
  176. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
  177. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +143 -36
  178. autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
  179. autobyteus/cli/agent_team_tui/widgets/shared.py +25 -25
  180. autobyteus/cli/cli_display.py +193 -44
  181. autobyteus/cli/workflow_tui/app.py +9 -10
  182. autobyteus/cli/workflow_tui/state.py +14 -16
  183. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +15 -15
  184. autobyteus/cli/workflow_tui/widgets/focus_pane.py +137 -35
  185. autobyteus/cli/workflow_tui/widgets/renderables.py +1 -1
  186. autobyteus/cli/workflow_tui/widgets/shared.py +25 -25
  187. autobyteus/clients/autobyteus_client.py +94 -1
  188. autobyteus/events/event_types.py +11 -18
  189. autobyteus/llm/api/autobyteus_llm.py +33 -29
  190. autobyteus/llm/api/claude_llm.py +142 -36
  191. autobyteus/llm/api/gemini_llm.py +163 -59
  192. autobyteus/llm/api/grok_llm.py +1 -1
  193. autobyteus/llm/api/minimax_llm.py +26 -0
  194. autobyteus/llm/api/mistral_llm.py +113 -87
  195. autobyteus/llm/api/ollama_llm.py +9 -42
  196. autobyteus/llm/api/openai_compatible_llm.py +127 -91
  197. autobyteus/llm/api/openai_llm.py +3 -3
  198. autobyteus/llm/api/openai_responses_llm.py +324 -0
  199. autobyteus/llm/api/zhipu_llm.py +21 -2
  200. autobyteus/llm/autobyteus_provider.py +70 -60
  201. autobyteus/llm/base_llm.py +85 -81
  202. autobyteus/llm/converters/__init__.py +14 -0
  203. autobyteus/llm/converters/anthropic_tool_call_converter.py +37 -0
  204. autobyteus/llm/converters/gemini_tool_call_converter.py +57 -0
  205. autobyteus/llm/converters/mistral_tool_call_converter.py +37 -0
  206. autobyteus/llm/converters/openai_tool_call_converter.py +38 -0
  207. autobyteus/llm/extensions/base_extension.py +6 -12
  208. autobyteus/llm/extensions/token_usage_tracking_extension.py +45 -18
  209. autobyteus/llm/llm_factory.py +282 -204
  210. autobyteus/llm/lmstudio_provider.py +60 -49
  211. autobyteus/llm/models.py +35 -2
  212. autobyteus/llm/ollama_provider.py +60 -49
  213. autobyteus/llm/ollama_provider_resolver.py +0 -1
  214. autobyteus/llm/prompt_renderers/__init__.py +19 -0
  215. autobyteus/llm/prompt_renderers/anthropic_prompt_renderer.py +104 -0
  216. autobyteus/llm/prompt_renderers/autobyteus_prompt_renderer.py +19 -0
  217. autobyteus/llm/prompt_renderers/base_prompt_renderer.py +10 -0
  218. autobyteus/llm/prompt_renderers/gemini_prompt_renderer.py +63 -0
  219. autobyteus/llm/prompt_renderers/mistral_prompt_renderer.py +87 -0
  220. autobyteus/llm/prompt_renderers/ollama_prompt_renderer.py +51 -0
  221. autobyteus/llm/prompt_renderers/openai_chat_renderer.py +97 -0
  222. autobyteus/llm/prompt_renderers/openai_responses_renderer.py +101 -0
  223. autobyteus/llm/providers.py +1 -3
  224. autobyteus/llm/token_counter/claude_token_counter.py +56 -25
  225. autobyteus/llm/token_counter/mistral_token_counter.py +12 -8
  226. autobyteus/llm/token_counter/openai_token_counter.py +24 -5
  227. autobyteus/llm/token_counter/token_counter_factory.py +12 -5
  228. autobyteus/llm/utils/llm_config.py +6 -12
  229. autobyteus/llm/utils/media_payload_formatter.py +27 -20
  230. autobyteus/llm/utils/messages.py +55 -3
  231. autobyteus/llm/utils/response_types.py +3 -0
  232. autobyteus/llm/utils/tool_call_delta.py +31 -0
  233. autobyteus/memory/__init__.py +35 -0
  234. autobyteus/memory/compaction/__init__.py +9 -0
  235. autobyteus/memory/compaction/compaction_result.py +8 -0
  236. autobyteus/memory/compaction/compactor.py +89 -0
  237. autobyteus/memory/compaction/summarizer.py +11 -0
  238. autobyteus/memory/compaction_snapshot_builder.py +84 -0
  239. autobyteus/memory/memory_manager.py +205 -0
  240. autobyteus/memory/models/__init__.py +14 -0
  241. autobyteus/memory/models/episodic_item.py +41 -0
  242. autobyteus/memory/models/memory_types.py +7 -0
  243. autobyteus/memory/models/raw_trace_item.py +79 -0
  244. autobyteus/memory/models/semantic_item.py +41 -0
  245. autobyteus/memory/models/tool_interaction.py +20 -0
  246. autobyteus/memory/path_resolver.py +27 -0
  247. autobyteus/memory/policies/__init__.py +5 -0
  248. autobyteus/memory/policies/compaction_policy.py +16 -0
  249. autobyteus/memory/restore/__init__.py +1 -0
  250. autobyteus/memory/restore/working_context_snapshot_bootstrapper.py +61 -0
  251. autobyteus/memory/retrieval/__init__.py +7 -0
  252. autobyteus/memory/retrieval/memory_bundle.py +11 -0
  253. autobyteus/memory/retrieval/retriever.py +13 -0
  254. autobyteus/memory/store/__init__.py +9 -0
  255. autobyteus/memory/store/base_store.py +14 -0
  256. autobyteus/memory/store/file_store.py +98 -0
  257. autobyteus/memory/store/working_context_snapshot_store.py +28 -0
  258. autobyteus/memory/tool_interaction_builder.py +46 -0
  259. autobyteus/memory/turn_tracker.py +9 -0
  260. autobyteus/memory/working_context_snapshot.py +69 -0
  261. autobyteus/memory/working_context_snapshot_serializer.py +135 -0
  262. autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
  263. autobyteus/multimedia/audio/api/gemini_audio_client.py +109 -16
  264. autobyteus/multimedia/audio/audio_client_factory.py +47 -9
  265. autobyteus/multimedia/audio/audio_model.py +2 -1
  266. autobyteus/multimedia/image/api/autobyteus_image_client.py +19 -5
  267. autobyteus/multimedia/image/api/gemini_image_client.py +39 -17
  268. autobyteus/multimedia/image/api/openai_image_client.py +125 -43
  269. autobyteus/multimedia/image/autobyteus_image_provider.py +2 -1
  270. autobyteus/multimedia/image/image_client_factory.py +47 -15
  271. autobyteus/multimedia/image/image_model.py +5 -2
  272. autobyteus/multimedia/providers.py +3 -2
  273. autobyteus/skills/loader.py +71 -0
  274. autobyteus/skills/model.py +11 -0
  275. autobyteus/skills/registry.py +70 -0
  276. autobyteus/task_management/tools/todo_tools/add_todo.py +2 -2
  277. autobyteus/task_management/tools/todo_tools/create_todo_list.py +2 -2
  278. autobyteus/task_management/tools/todo_tools/update_todo_status.py +2 -2
  279. autobyteus/tools/__init__.py +34 -47
  280. autobyteus/tools/base_tool.py +7 -0
  281. autobyteus/tools/file/__init__.py +2 -6
  282. autobyteus/tools/file/patch_file.py +149 -0
  283. autobyteus/tools/file/read_file.py +36 -5
  284. autobyteus/tools/file/write_file.py +4 -1
  285. autobyteus/tools/functional_tool.py +43 -6
  286. autobyteus/tools/mcp/__init__.py +2 -0
  287. autobyteus/tools/mcp/config_service.py +5 -1
  288. autobyteus/tools/mcp/server/__init__.py +2 -0
  289. autobyteus/tools/mcp/server/http_managed_mcp_server.py +1 -1
  290. autobyteus/tools/mcp/server/websocket_managed_mcp_server.py +141 -0
  291. autobyteus/tools/mcp/server_instance_manager.py +8 -1
  292. autobyteus/tools/mcp/types.py +61 -0
  293. autobyteus/tools/multimedia/audio_tools.py +70 -17
  294. autobyteus/tools/multimedia/download_media_tool.py +18 -4
  295. autobyteus/tools/multimedia/image_tools.py +246 -62
  296. autobyteus/tools/operation_executor/journal_manager.py +107 -0
  297. autobyteus/tools/operation_executor/operation_event_buffer.py +57 -0
  298. autobyteus/tools/operation_executor/operation_event_producer.py +29 -0
  299. autobyteus/tools/operation_executor/operation_executor.py +58 -0
  300. autobyteus/tools/registry/tool_definition.py +43 -2
  301. autobyteus/tools/skill/load_skill.py +50 -0
  302. autobyteus/tools/terminal/__init__.py +45 -0
  303. autobyteus/tools/terminal/ansi_utils.py +32 -0
  304. autobyteus/tools/terminal/background_process_manager.py +233 -0
  305. autobyteus/tools/terminal/output_buffer.py +105 -0
  306. autobyteus/tools/terminal/prompt_detector.py +63 -0
  307. autobyteus/tools/terminal/pty_session.py +241 -0
  308. autobyteus/tools/terminal/session_factory.py +20 -0
  309. autobyteus/tools/terminal/terminal_session_manager.py +226 -0
  310. autobyteus/tools/terminal/tools/__init__.py +13 -0
  311. autobyteus/tools/terminal/tools/get_process_output.py +81 -0
  312. autobyteus/tools/terminal/tools/run_bash.py +109 -0
  313. autobyteus/tools/terminal/tools/start_background_process.py +104 -0
  314. autobyteus/tools/terminal/tools/stop_background_process.py +67 -0
  315. autobyteus/tools/terminal/types.py +54 -0
  316. autobyteus/tools/terminal/wsl_tmux_session.py +221 -0
  317. autobyteus/tools/terminal/wsl_utils.py +156 -0
  318. autobyteus/tools/transaction_management/backup_handler.py +48 -0
  319. autobyteus/tools/transaction_management/operation_lifecycle_manager.py +62 -0
  320. autobyteus/tools/usage/__init__.py +1 -2
  321. autobyteus/tools/usage/formatters/__init__.py +17 -1
  322. autobyteus/tools/usage/formatters/base_formatter.py +8 -0
  323. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +2 -2
  324. autobyteus/tools/usage/formatters/mistral_json_schema_formatter.py +18 -0
  325. autobyteus/tools/usage/formatters/patch_file_xml_example_formatter.py +64 -0
  326. autobyteus/tools/usage/formatters/patch_file_xml_schema_formatter.py +31 -0
  327. autobyteus/tools/usage/formatters/run_bash_xml_example_formatter.py +32 -0
  328. autobyteus/tools/usage/formatters/run_bash_xml_schema_formatter.py +36 -0
  329. autobyteus/tools/usage/formatters/write_file_xml_example_formatter.py +53 -0
  330. autobyteus/tools/usage/formatters/write_file_xml_schema_formatter.py +31 -0
  331. autobyteus/tools/usage/providers/tool_manifest_provider.py +10 -10
  332. autobyteus/tools/usage/registries/__init__.py +1 -3
  333. autobyteus/tools/usage/registries/tool_formatting_registry.py +115 -8
  334. autobyteus/tools/usage/tool_schema_provider.py +51 -0
  335. autobyteus/tools/web/__init__.py +4 -0
  336. autobyteus/tools/web/read_url_tool.py +80 -0
  337. autobyteus/utils/diff_utils.py +271 -0
  338. autobyteus/utils/download_utils.py +109 -0
  339. autobyteus/utils/file_utils.py +57 -2
  340. autobyteus/utils/gemini_helper.py +64 -0
  341. autobyteus/utils/gemini_model_mapping.py +71 -0
  342. autobyteus/utils/llm_output_formatter.py +75 -0
  343. autobyteus/utils/tool_call_format.py +36 -0
  344. autobyteus/workflow/agentic_workflow.py +3 -3
  345. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +2 -2
  346. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +2 -2
  347. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +2 -2
  348. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +3 -9
  349. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +6 -6
  350. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +2 -2
  351. autobyteus/workflow/context/workflow_context.py +3 -3
  352. autobyteus/workflow/context/workflow_runtime_state.py +5 -5
  353. autobyteus/workflow/events/workflow_event_dispatcher.py +5 -5
  354. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +3 -3
  355. autobyteus/workflow/handlers/process_user_message_event_handler.py +5 -5
  356. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +2 -2
  357. autobyteus/workflow/runtime/workflow_runtime.py +8 -8
  358. autobyteus/workflow/runtime/workflow_worker.py +3 -3
  359. autobyteus/workflow/status/__init__.py +11 -0
  360. autobyteus/workflow/status/workflow_status.py +19 -0
  361. autobyteus/workflow/status/workflow_status_manager.py +48 -0
  362. autobyteus/workflow/streaming/__init__.py +2 -2
  363. autobyteus/workflow/streaming/workflow_event_notifier.py +7 -7
  364. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +4 -4
  365. autobyteus/workflow/streaming/workflow_stream_events.py +3 -3
  366. autobyteus/workflow/utils/wait_for_idle.py +4 -4
  367. autobyteus-1.3.0.dist-info/METADATA +293 -0
  368. autobyteus-1.3.0.dist-info/RECORD +606 -0
  369. {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/WHEEL +1 -1
  370. {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/top_level.txt +0 -1
  371. autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +0 -57
  372. autobyteus/agent/hooks/__init__.py +0 -16
  373. autobyteus/agent/hooks/base_phase_hook.py +0 -78
  374. autobyteus/agent/hooks/hook_definition.py +0 -36
  375. autobyteus/agent/hooks/hook_meta.py +0 -37
  376. autobyteus/agent/hooks/hook_registry.py +0 -106
  377. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +0 -103
  378. autobyteus/agent/phases/__init__.py +0 -18
  379. autobyteus/agent/phases/discover.py +0 -53
  380. autobyteus/agent/phases/manager.py +0 -265
  381. autobyteus/agent/phases/transition_decorator.py +0 -40
  382. autobyteus/agent/phases/transition_info.py +0 -33
  383. autobyteus/agent/remote_agent.py +0 -244
  384. autobyteus/agent/workspace/workspace_definition.py +0 -36
  385. autobyteus/agent/workspace/workspace_meta.py +0 -37
  386. autobyteus/agent/workspace/workspace_registry.py +0 -72
  387. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +0 -25
  388. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +0 -85
  389. autobyteus/agent_team/phases/__init__.py +0 -11
  390. autobyteus/agent_team/phases/agent_team_operational_phase.py +0 -19
  391. autobyteus/agent_team/phases/agent_team_phase_manager.py +0 -48
  392. autobyteus/llm/api/bedrock_llm.py +0 -92
  393. autobyteus/llm/api/groq_llm.py +0 -94
  394. autobyteus/llm/api/nvidia_llm.py +0 -108
  395. autobyteus/llm/utils/token_pricing_config.py +0 -87
  396. autobyteus/rpc/__init__.py +0 -73
  397. autobyteus/rpc/client/__init__.py +0 -17
  398. autobyteus/rpc/client/abstract_client_connection.py +0 -124
  399. autobyteus/rpc/client/client_connection_manager.py +0 -153
  400. autobyteus/rpc/client/sse_client_connection.py +0 -306
  401. autobyteus/rpc/client/stdio_client_connection.py +0 -280
  402. autobyteus/rpc/config/__init__.py +0 -13
  403. autobyteus/rpc/config/agent_server_config.py +0 -153
  404. autobyteus/rpc/config/agent_server_registry.py +0 -152
  405. autobyteus/rpc/hosting.py +0 -244
  406. autobyteus/rpc/protocol.py +0 -244
  407. autobyteus/rpc/server/__init__.py +0 -20
  408. autobyteus/rpc/server/agent_server_endpoint.py +0 -181
  409. autobyteus/rpc/server/base_method_handler.py +0 -40
  410. autobyteus/rpc/server/method_handlers.py +0 -259
  411. autobyteus/rpc/server/sse_server_handler.py +0 -182
  412. autobyteus/rpc/server/stdio_server_handler.py +0 -151
  413. autobyteus/rpc/server_main.py +0 -198
  414. autobyteus/rpc/transport_type.py +0 -13
  415. autobyteus/tools/bash/__init__.py +0 -2
  416. autobyteus/tools/bash/bash_executor.py +0 -100
  417. autobyteus/tools/browser/__init__.py +0 -2
  418. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +0 -75
  419. autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +0 -30
  420. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +0 -154
  421. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +0 -89
  422. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +0 -107
  423. autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +0 -14
  424. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +0 -26
  425. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +0 -14
  426. autobyteus/tools/browser/session_aware/shared_browser_session.py +0 -11
  427. autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +0 -25
  428. autobyteus/tools/browser/session_aware/web_element_action.py +0 -20
  429. autobyteus/tools/browser/standalone/__init__.py +0 -6
  430. autobyteus/tools/browser/standalone/factory/__init__.py +0 -0
  431. autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +0 -25
  432. autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +0 -14
  433. autobyteus/tools/browser/standalone/navigate_to.py +0 -84
  434. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -101
  435. autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -169
  436. autobyteus/tools/browser/standalone/webpage_reader.py +0 -105
  437. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -105
  438. autobyteus/tools/file/edit_file.py +0 -200
  439. autobyteus/tools/file/list_directory.py +0 -168
  440. autobyteus/tools/file/search_files.py +0 -188
  441. autobyteus/tools/timer.py +0 -175
  442. autobyteus/tools/usage/parsers/__init__.py +0 -22
  443. autobyteus/tools/usage/parsers/_json_extractor.py +0 -99
  444. autobyteus/tools/usage/parsers/_string_decoders.py +0 -18
  445. autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +0 -10
  446. autobyteus/tools/usage/parsers/base_parser.py +0 -41
  447. autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +0 -83
  448. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +0 -316
  449. autobyteus/tools/usage/parsers/exceptions.py +0 -13
  450. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +0 -77
  451. autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +0 -149
  452. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +0 -59
  453. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +0 -62
  454. autobyteus/workflow/phases/__init__.py +0 -11
  455. autobyteus/workflow/phases/workflow_operational_phase.py +0 -19
  456. autobyteus/workflow/phases/workflow_phase_manager.py +0 -48
  457. autobyteus-1.2.1.dist-info/METADATA +0 -205
  458. autobyteus-1.2.1.dist-info/RECORD +0 -511
  459. examples/__init__.py +0 -1
  460. examples/agent_team/__init__.py +0 -1
  461. examples/discover_phase_transitions.py +0 -104
  462. examples/run_agentic_software_engineer.py +0 -239
  463. examples/run_browser_agent.py +0 -262
  464. examples/run_google_slides_agent.py +0 -287
  465. examples/run_mcp_browser_client.py +0 -174
  466. examples/run_mcp_google_slides_client.py +0 -270
  467. examples/run_mcp_list_tools.py +0 -189
  468. examples/run_poem_writer.py +0 -284
  469. examples/run_sqlite_agent.py +0 -295
  470. /autobyteus/{tools/browser/session_aware → skills}/__init__.py +0 -0
  471. /autobyteus/tools/{browser/session_aware/factory → skill}/__init__.py +0 -0
  472. {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,51 @@
1
+ # file: autobyteus/autobyteus/tools/usage/tool_schema_provider.py
2
+ """
3
+ Provider-aware tool schema builder for API tool calls.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import Iterable, List, Dict, Optional
9
+
10
+ from autobyteus.llm.providers import LLMProvider
11
+ from autobyteus.tools.registry import default_tool_registry
12
+ from autobyteus.tools.usage.formatters.anthropic_json_schema_formatter import AnthropicJsonSchemaFormatter
13
+ from autobyteus.tools.usage.formatters.gemini_json_schema_formatter import GeminiJsonSchemaFormatter
14
+ from autobyteus.tools.usage.formatters.openai_json_schema_formatter import OpenAiJsonSchemaFormatter
15
+ from autobyteus.tools.registry.tool_definition import ToolDefinition
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class ToolSchemaProvider:
21
+ """Builds API tool schemas for a provider."""
22
+
23
+ def __init__(self, registry=default_tool_registry):
24
+ self._registry = registry
25
+
26
+ def build_schema(
27
+ self,
28
+ tool_names: Iterable[str],
29
+ provider: Optional[LLMProvider],
30
+ ) -> List[Dict]:
31
+ tool_definitions: List[ToolDefinition] = []
32
+ for name in tool_names:
33
+ tool_def = self._registry.get_tool_definition(name)
34
+ if tool_def:
35
+ tool_definitions.append(tool_def)
36
+ else:
37
+ logger.warning("Tool '%s' not found in registry.", name)
38
+
39
+ if not tool_definitions:
40
+ return []
41
+
42
+ formatter = self._select_formatter(provider)
43
+ return [formatter.provide(td) for td in tool_definitions]
44
+
45
+ @staticmethod
46
+ def _select_formatter(provider: Optional[LLMProvider]):
47
+ if provider == LLMProvider.ANTHROPIC:
48
+ return AnthropicJsonSchemaFormatter()
49
+ if provider == LLMProvider.GEMINI:
50
+ return GeminiJsonSchemaFormatter()
51
+ return OpenAiJsonSchemaFormatter()
@@ -0,0 +1,4 @@
1
+
2
+ from .read_url_tool import ReadUrl
3
+
4
+ __all__ = ["ReadUrl"]
@@ -0,0 +1,80 @@
1
+ import logging
2
+ import aiohttp
3
+ from typing import Optional, TYPE_CHECKING
4
+ from autobyteus.tools.base_tool import BaseTool
5
+ from autobyteus.tools.tool_category import ToolCategory
6
+ from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
7
+ from autobyteus.utils.html_cleaner import clean, CleaningMode
8
+
9
+ if TYPE_CHECKING:
10
+ from autobyteus.agent.context import AgentContext
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+ class ReadUrl(BaseTool):
15
+ """
16
+ Lightweight URL content reader that fetches web page content using aiohttp.
17
+ Optimized for fast, efficient reading of static content by extracting pure text.
18
+ """
19
+ CATEGORY = ToolCategory.WEB
20
+
21
+ @classmethod
22
+ def get_name(cls) -> str:
23
+ return "read_url"
24
+
25
+ @classmethod
26
+ def get_description(cls) -> str:
27
+ return (
28
+ "Reads the content of a specific URL using a lightweight HTTP client. "
29
+ "Faster and more efficient than using a browser tool for static pages. "
30
+ "Returns cleaned text content optimized for reading."
31
+ )
32
+
33
+ @classmethod
34
+ def get_argument_schema(cls) -> Optional[ParameterSchema]:
35
+ schema = ParameterSchema()
36
+ schema.add_parameter(ParameterDefinition(
37
+ name="url",
38
+ param_type=ParameterType.STRING,
39
+ description="The URL of the webpage to read.",
40
+ required=True
41
+ ))
42
+ schema.add_parameter(ParameterDefinition(
43
+ name="output_format",
44
+ param_type=ParameterType.STRING,
45
+ description="The desired output format: 'text' (default) or 'html'. 'text' returns cleaned text content, 'html' returns cleaned HTML.",
46
+ required=False,
47
+ default_value="text",
48
+ enum_values=["text", "html"]
49
+ ))
50
+ return schema
51
+
52
+ async def _execute(self, context: 'AgentContext', url: str, output_format: str = "text") -> str:
53
+ logger.info(f"Executing read_url for agent {context.agent_id} with URL: '{url}'")
54
+
55
+ try:
56
+ async with aiohttp.ClientSession() as session:
57
+ # aiohttp handles redirects by default (up to 10)
58
+ async with session.get(url, timeout=30) as response:
59
+ if response.status != 200:
60
+ error_msg = f"Failed to fetch content from {url}. Status code: {response.status}"
61
+ logger.error(error_msg)
62
+ return error_msg
63
+
64
+ html_content = await response.text()
65
+
66
+ # Use appropriate cleaning mode based on requested format
67
+ mode = CleaningMode.TEXT_CONTENT_FOCUSED if output_format == "text" else CleaningMode.THOROUGH
68
+ cleaned_content = clean(html_content, mode=mode)
69
+
70
+ if not cleaned_content.strip():
71
+ return f"Successfully fetched content from {url}, but the cleaned result was empty."
72
+
73
+ return cleaned_content
74
+
75
+ except aiohttp.ClientError as e:
76
+ logger.error(f"Network error reading URL '{url}': {e}", exc_info=True)
77
+ return f"Error reading URL '{url}': Network error ({str(e)})"
78
+ except Exception as e:
79
+ logger.error(f"Unexpected error reading URL '{url}': {e}", exc_info=True)
80
+ return f"Error reading URL '{url}': {str(e)}"
@@ -0,0 +1,271 @@
1
+ """
2
+ Unified diff utilities for applying patches to text content.
3
+ """
4
+ import re
5
+ import logging
6
+ from typing import List
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ _HUNK_HEADER_RE = re.compile(r"^@@ -(?P<old_start>\d+)(?:,(?P<old_count>\d+))? \+(?P<new_start>\d+)(?:,(?P<new_count>\d+))? @@")
11
+ _GIT_HEADER_PREFIXES = (
12
+ "diff --git ",
13
+ "index ",
14
+ "new file mode ",
15
+ "deleted file mode ",
16
+ "old mode ",
17
+ "new mode ",
18
+ "similarity index ",
19
+ "dissimilarity index ",
20
+ "rename from ",
21
+ "rename to ",
22
+ "copy from ",
23
+ "copy to ",
24
+ "binary files ",
25
+ )
26
+
27
+
28
+ class PatchApplicationError(ValueError):
29
+ """Raised when a unified diff patch cannot be applied to the target content."""
30
+
31
+
32
+ def apply_unified_diff(
33
+ original_lines: List[str],
34
+ patch: str,
35
+ fuzz_factor: int = 0,
36
+ ignore_whitespace: bool = False
37
+ ) -> List[str]:
38
+ """Applies a unified diff patch to the provided original lines and returns the patched lines.
39
+
40
+ Args:
41
+ original_lines: List of strings representing the original content lines (with line endings preserved).
42
+ patch: Unified diff patch string describing the edits to apply.
43
+ fuzz_factor: Number of lines to search up/down if exact line number match fails.
44
+ ignore_whitespace: If True, ignores leading/trailing whitespace when matching context.
45
+
46
+ Returns:
47
+ List of strings representing the patched content lines.
48
+
49
+ Raises:
50
+ PatchApplicationError: If the patch content cannot be applied cleanly.
51
+ """
52
+ if not patch or not patch.strip():
53
+ raise PatchApplicationError("Patch content is empty; nothing to apply.")
54
+
55
+ patched_lines: List[str] = []
56
+ orig_idx = 0
57
+ patch_lines = patch.splitlines(keepends=True)
58
+ line_idx = 0
59
+
60
+ def lines_match(l1: str, l2: str, *, allow_eof_newline_mismatch: bool = False) -> bool:
61
+ if ignore_whitespace:
62
+ return l1.strip() == l2.strip()
63
+ if l1 == l2:
64
+ return True
65
+ if allow_eof_newline_mismatch:
66
+ return l1.rstrip('\n') == l2.rstrip('\n')
67
+ return False
68
+
69
+ while line_idx < len(patch_lines):
70
+ line = patch_lines[line_idx]
71
+
72
+ if line.startswith('---') or line.startswith('+++'):
73
+ logger.debug("apply_unified_diff: skipping diff header line '%s'.", line.strip())
74
+ line_idx += 1
75
+ continue
76
+ stripped_line = line.lstrip().lower()
77
+ if any(stripped_line.startswith(prefix) for prefix in _GIT_HEADER_PREFIXES):
78
+ logger.debug("apply_unified_diff: skipping git diff header line '%s'.", line.strip())
79
+ line_idx += 1
80
+ continue
81
+
82
+ if not line.startswith('@@'):
83
+ stripped = line.strip()
84
+ if stripped == '':
85
+ # Handle empty lines between hunks or at start as noise, but
86
+ # legacy behavior might have been to append them?
87
+ # The previous loop skipped them if start of file (implicit in while loop start?),
88
+ # but inside the hunk loop it appended them.
89
+ # Here we are searching for hunk headers.
90
+ line_idx += 1
91
+ continue
92
+ raise PatchApplicationError(f"Unexpected content outside of hunk header: '{stripped}'.")
93
+
94
+ match = _HUNK_HEADER_RE.match(line)
95
+ if not match:
96
+ raise PatchApplicationError(f"Malformed hunk header: '{line.strip()}'.")
97
+
98
+ old_start = int(match.group('old_start'))
99
+ old_count = int(match.group('old_count') or '1')
100
+ new_start = int(match.group('new_start'))
101
+ new_count = int(match.group('new_count') or '1')
102
+ logger.debug("apply_unified_diff: processing hunk old_start=%s old_count=%s new_start=%s new_count=%s.",
103
+ old_start, old_count, new_start, new_count)
104
+
105
+ line_idx += 1 # Move past header
106
+
107
+ # Capture the hunk body
108
+ hunk_body = []
109
+ while line_idx < len(patch_lines):
110
+ h_line = patch_lines[line_idx]
111
+ if h_line.startswith('@@'):
112
+ break
113
+ hunk_body.append(h_line)
114
+ line_idx += 1
115
+
116
+ # Extract expected original content from hunk
117
+ # We process the hunk body to confirm what we expect to see in original_lines
118
+ refined_expected_orig = []
119
+ h_i = 0
120
+ while h_i < len(hunk_body):
121
+ h_line = hunk_body[h_i]
122
+ if h_line.startswith(' ') or h_line.startswith('-'):
123
+ content = h_line[1:]
124
+ # Check next line for '\ No newline...' to handle EOF correctly during match
125
+ if h_i + 1 < len(hunk_body) and hunk_body[h_i+1].startswith('\\ No newline'):
126
+ content = content.rstrip('\n')
127
+ elif h_i + 1 == len(hunk_body) and not content.endswith('\n'):
128
+ # Last line of patch might legitimately not have newline?
129
+ # Usually lines have newlines. If it's a file without newline at end, diff shows \ No newline.
130
+ pass
131
+ refined_expected_orig.append(content)
132
+ elif h_line.startswith('+'):
133
+ pass
134
+ elif h_line.startswith('\\'):
135
+ pass
136
+ elif h_line.strip() == '':
137
+ # Treat bare empty line as non-match content (noise/insert)?
138
+ # Previous code appended it to output but didn't check against input.
139
+ # So we do NOT add to refined_expected_orig.
140
+ pass
141
+ else:
142
+ raise PatchApplicationError(f"Unsupported patch line: '{h_line.strip()}'.")
143
+ h_i += 1
144
+
145
+ expected_count = len(refined_expected_orig)
146
+
147
+ # Fuzzy Search for Match
148
+ # Ideally target is old_start - 1 (converting 1-based old_start to 0-based index)
149
+ target_idx_base = old_start - 1 if old_start > 0 else 0
150
+
151
+ found_idx = -1
152
+
153
+ # Generate search offsets: 0, -1, 1, -2, 2, ...
154
+ offsets = [0]
155
+ for f in range(1, fuzz_factor + 1):
156
+ offsets.append(-f)
157
+ offsets.append(f)
158
+
159
+ for offset in offsets:
160
+ candidate_idx = target_idx_base + offset
161
+
162
+ # Constraints:
163
+ # 1. Must proceed forward from where we left off (orig_idx)
164
+ # 2. Must not go beyond EOF (handled by slicing)
165
+ if candidate_idx < orig_idx:
166
+ continue
167
+
168
+ # Check if this candidate location matches expected_orig
169
+ # We need to verify that original_lines[candidate_idx : candidate_idx + expected_count] matches refined_expected_orig
170
+
171
+ if candidate_idx + expected_count > len(original_lines):
172
+ # Ensure we don't go out of bounds (though partial match at EOF might be a thing? No, hunk must match fully)
173
+ continue
174
+
175
+ match_success = True
176
+ for k in range(expected_count):
177
+ # Note: lines_match handles whitespace if needed.
178
+ is_eof_line = (
179
+ candidate_idx + expected_count == len(original_lines)
180
+ and k == expected_count - 1
181
+ )
182
+ if not lines_match(
183
+ original_lines[candidate_idx + k],
184
+ refined_expected_orig[k],
185
+ allow_eof_newline_mismatch=is_eof_line,
186
+ ):
187
+ match_success = False
188
+ break
189
+
190
+ if match_success:
191
+ found_idx = candidate_idx
192
+ break
193
+
194
+ if found_idx == -1:
195
+ raise PatchApplicationError(f"Could not find context for hunk starting at {old_start} (fuzz={fuzz_factor}).")
196
+
197
+ # Apply changes
198
+
199
+ # 1. Copy lines from orig_idx to found_idx (these are the lines BEFORE the hunk that we skipped over/accepted)
200
+ patched_lines.extend(original_lines[orig_idx:found_idx])
201
+
202
+ # 2. Process hunk body to generate new lines
203
+ h_i = 0
204
+ while h_i < len(hunk_body):
205
+ h_line = hunk_body[h_i]
206
+ if h_line.startswith(' ') or h_line.startswith('-'):
207
+ # Valid context or removal - we skip them in output (they are either preserved via context or removed)
208
+ # If context (' '), we should actually append the ORIGINAL line to allow for fuzziness?
209
+ # Standard patch behavior: if fuzz matches, we output the *hunk's* version of the line?
210
+ # OR we output the *original* file's version?
211
+ # `patch` man page says: "patch takes the context from the diff file"
212
+ # Wait, if we fuzzy matched, likely we want to keep what was in the file unless the patch explicitly changes it.
213
+ # However, typically ' ' lines are just copied.
214
+ # If we use `patched_lines.append(original_lines[found_idx + handled_count])`, that preserves the file's indentation/whitespace.
215
+ # If we use `h_line[1:]`, we enforce the patch's indentation.
216
+ # Requirement: "Ignore Whitespace... Allow context comparisons to ignore...".
217
+ # If the patch "autocorrects" indentation in context lines, we probably want to KEEP the file's original indentation
218
+ # for context lines, so we don't accidentally "fix" them if we aren't touching them.
219
+ # Unified diff: ' ' means "unchanged". So we should emit the *original* line.
220
+
221
+ if h_line.startswith(' '):
222
+ # Find which original line this corresponds to.
223
+ # We are iterating h_i through hunk_body.
224
+ # We need to track our position in the matched original block.
225
+ # Let's track `current_match_offset`
226
+ pass
227
+ elif h_line.startswith('+'):
228
+ pass
229
+ h_i += 1
230
+
231
+ # Let's redo step 2 cleanly to handle the ' ' vs '-' vs '+' and the '\ No newline' logic.
232
+
233
+ matched_orig_offset = 0 # Offset into the matched block of original_lines [found_idx : found_idx+expected_count]
234
+
235
+ h_i = 0
236
+ while h_i < len(hunk_body):
237
+ h_line = hunk_body[h_i]
238
+
239
+ if h_line.startswith(' '):
240
+ # Context line: preserve original content from file
241
+ if found_idx + matched_orig_offset < len(original_lines):
242
+ patched_lines.append(original_lines[found_idx + matched_orig_offset])
243
+ matched_orig_offset += 1
244
+
245
+ elif h_line.startswith('-'):
246
+ # Removal: skip original content
247
+ matched_orig_offset += 1
248
+
249
+ elif h_line.startswith('+'):
250
+ # Addition: add content from patch
251
+ content = h_line[1:]
252
+ if h_i + 1 < len(hunk_body) and hunk_body[h_i+1].startswith('\\ No newline'):
253
+ content = content.rstrip('\n')
254
+ patched_lines.append(content)
255
+
256
+ elif h_line.strip() == '':
257
+ # "Empty" line in patch. Legacy behavior: append to output.
258
+ # Does not consume original.
259
+ patched_lines.append(h_line)
260
+
261
+ elif h_line.startswith('\\'):
262
+ # No newline marker, handled by lookahead in + case.
263
+ # For - and space, we used original lines so their newline status is preserved automatically!
264
+ pass
265
+
266
+ h_i += 1
267
+
268
+ orig_idx = found_idx + expected_count
269
+
270
+ patched_lines.extend(original_lines[orig_idx:])
271
+ return patched_lines
@@ -0,0 +1,109 @@
1
+ import logging
2
+ import aiohttp
3
+ import base64
4
+ import shutil
5
+ import os
6
+ import ssl
7
+ from pathlib import Path
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ SSL_VERIFY_ENV_VAR = "AUTOBYTEUS_DOWNLOAD_VERIFY_SSL"
12
+ SSL_CERT_FILE_ENV_VAR = "AUTOBYTEUS_DOWNLOAD_SSL_CERT_FILE"
13
+
14
+
15
+ def _resolve_ssl_param() -> bool | ssl.SSLContext:
16
+ """
17
+ Resolve SSL verification behavior for downloads.
18
+
19
+ - If AUTOBYTEUS_DOWNLOAD_SSL_CERT_FILE is set, use it as CA file.
20
+ - Else if AUTOBYTEUS_DOWNLOAD_VERIFY_SSL is truthy, verify using system CAs.
21
+ - Else (default), disable verification to allow self-signed certs.
22
+ """
23
+ cert_path = os.getenv(SSL_CERT_FILE_ENV_VAR)
24
+ if cert_path:
25
+ cert_file = Path(cert_path)
26
+ if not cert_file.is_file():
27
+ raise IOError(f"SSL cert file not found: {cert_file}")
28
+ return ssl.create_default_context(cafile=str(cert_file))
29
+
30
+ verify_env = os.getenv(SSL_VERIFY_ENV_VAR)
31
+ if verify_env and verify_env.strip().lower() in {"1", "true", "yes", "on"}:
32
+ return True
33
+ return False
34
+
35
+
36
+ async def download_file_from_url(url: str, file_path: Path) -> None:
37
+ """
38
+ Asynchronously downloads a file from a URL, decodes a data URI, or copies a local file
39
+ to a specified file path.
40
+
41
+ Features:
42
+ - Supports http/https URLs, data: URIs (base64), and local file paths.
43
+ - Creates parent directories if they don't exist.
44
+ - Uses streaming for HTTP URLs to handle large files efficiently.
45
+ - Guarantees cleanup of partial files on failure.
46
+
47
+ Args:
48
+ url: The source URL, data URI, or local path.
49
+ file_path: The specific local path (including filename) to save to.
50
+
51
+ Raises:
52
+ IOError: If download, decoding, or copying fails.
53
+ """
54
+ # Ensure parent directory exists
55
+ try:
56
+ file_path.parent.mkdir(parents=True, exist_ok=True)
57
+ except Exception as e:
58
+ logger.error(f"Failed to create directory structure for {file_path}: {e}")
59
+ raise IOError(f"Filesystem error: {e}")
60
+
61
+ try:
62
+ if url.startswith("data:"):
63
+ # Handle Data URI
64
+ try:
65
+ header, encoded = url.split(",", 1)
66
+ data = base64.b64decode(encoded)
67
+ with open(file_path, "wb") as f:
68
+ f.write(data)
69
+ logger.info(f"Successfully decoded and saved data URI to: {file_path}")
70
+ return
71
+ except Exception as e:
72
+ logger.error(f"Failed to decode data URI: {e}")
73
+ raise IOError(f"Invalid data URI format: {e}")
74
+
75
+ if os.path.exists(url) and os.path.isfile(url):
76
+ # Handle Local File Path (Copy)
77
+ try:
78
+ shutil.copy(url, file_path)
79
+ logger.info(f"Successfully copied local file from {url} to {file_path}")
80
+ return
81
+ except Exception as e:
82
+ logger.error(f"Failed to copy local file from {url} to {file_path}: {e}")
83
+ raise IOError(f"File copy error: {e}")
84
+
85
+ # Handle HTTP URL
86
+ ssl_param = _resolve_ssl_param()
87
+ async with aiohttp.ClientSession() as session:
88
+ async with session.get(url, ssl=ssl_param) as response:
89
+ if response.status != 200:
90
+ raise IOError(f"Failed to download from {url}: HTTP {response.status}")
91
+
92
+ # Open file for writing binary
93
+ with open(file_path, "wb") as f:
94
+ # Iterate over chunks to avoid loading large files into memory
95
+ async for chunk in response.content.iter_chunked(8192):
96
+ f.write(chunk)
97
+
98
+ logger.info(f"Successfully downloaded file to: {file_path}")
99
+
100
+ except Exception as e:
101
+ logger.error(f"Failed to process media from {url} to {file_path}: {e}")
102
+ # Cleanup: Delete the partial/empty file if it was created
103
+ if file_path.exists():
104
+ try:
105
+ file_path.unlink()
106
+ logger.debug(f"Cleaned up partial file: {file_path}")
107
+ except OSError as cleanup_error:
108
+ logger.warning(f"Failed to clean up partial file {file_path}: {cleanup_error}")
109
+ raise
@@ -2,8 +2,11 @@
2
2
 
3
3
  import os
4
4
  import platform
5
+ import tempfile
6
+ from pathlib import Path
7
+ from typing import Union
5
8
 
6
- def get_default_download_folder():
9
+ def get_default_download_folder() -> str:
7
10
  system = platform.system()
8
11
  if system == "Windows":
9
12
  return os.path.join(os.path.expanduser("~"), "Downloads")
@@ -12,4 +15,56 @@ def get_default_download_folder():
12
15
  elif system == "Linux":
13
16
  return os.path.join(os.path.expanduser("~"), "Downloads")
14
17
  else:
15
- return os.path.join(os.path.expanduser("~"), "Downloads") # Fallback
18
+ return os.path.join(os.path.expanduser("~"), "Downloads") # Fallback
19
+
20
+ def resolve_safe_path(user_path: str, workspace_root: Union[str, Path]) -> Path:
21
+ """
22
+ Resolves a file path and ensures it is contained within allowed safe directories.
23
+
24
+ Allowed directories:
25
+ 1. The Agent's Workspace (workspace_root)
26
+ 2. The User's Downloads directory
27
+ 3. The System Temporary directory
28
+
29
+ Args:
30
+ user_path: The relative or absolute path provided by the user/tool.
31
+ workspace_root: The root directory of the agent's workspace.
32
+
33
+ Returns:
34
+ The resolved absolute Path object.
35
+
36
+ Raises:
37
+ ValueError: If the path is outside the allowed directories.
38
+ """
39
+ workspace = Path(workspace_root).resolve()
40
+ downloads = Path(get_default_download_folder()).resolve()
41
+ temp_dir = Path(tempfile.gettempdir()).resolve()
42
+
43
+ path_obj = Path(user_path)
44
+
45
+ # If absolute, check directly. If relative, resolve against workspace.
46
+ if path_obj.is_absolute():
47
+ target = path_obj.resolve()
48
+ else:
49
+ target = (workspace / path_obj).resolve()
50
+
51
+ # Allowed roots list
52
+ allowed_roots = [workspace, downloads, temp_dir]
53
+
54
+ is_safe = False
55
+ for root in allowed_roots:
56
+ # Check if target is equal to or a subpath of root
57
+ try:
58
+ target.relative_to(root)
59
+ is_safe = True
60
+ break
61
+ except ValueError:
62
+ continue
63
+
64
+ if not is_safe:
65
+ raise ValueError(
66
+ f"Security Violation: Path '{user_path}' is not within allowed directories "
67
+ f"(Workspace: {workspace}, Downloads: {downloads}, or Temp: {temp_dir})."
68
+ )
69
+
70
+ return target
@@ -0,0 +1,64 @@
1
+ import os
2
+ import logging
3
+ from dataclasses import dataclass
4
+ from google import genai
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class GeminiRuntimeInfo:
11
+ runtime: str # "vertex" or "api_key"
12
+ project: str | None
13
+ location: str | None
14
+
15
+ def initialize_gemini_client_with_runtime() -> tuple[genai.Client, GeminiRuntimeInfo]:
16
+ """
17
+ Initializes the Google GenAI Client based on available environment variables.
18
+ Supports Vertex AI (GCP), Vertex AI Express (API Key), and AI Studio (API Key) modes.
19
+
20
+ Priority:
21
+ 1. Vertex AI Express (requires VERTEX_AI_API_KEY)
22
+ 2. Vertex AI (requires VERTEX_AI_PROJECT and VERTEX_AI_LOCATION)
23
+ 3. AI Studio (requires GEMINI_API_KEY)
24
+
25
+ Returns:
26
+ (client, runtime_info)
27
+
28
+ Raises:
29
+ ValueError: If neither configuration set is found.
30
+ """
31
+ # 1. Try Vertex AI Express Configuration (API Key)
32
+ vertex_api_key = os.environ.get("VERTEX_AI_API_KEY")
33
+ if vertex_api_key:
34
+ logger.info("Initializing Gemini Client in Vertex AI Express mode (API key).")
35
+ client = genai.Client(vertexai=True, api_key=vertex_api_key)
36
+ return client, GeminiRuntimeInfo(runtime="vertex", project=None, location=None)
37
+
38
+ # 2. Try Vertex AI Configuration (Project/Location + ADC)
39
+ project = os.environ.get("VERTEX_AI_PROJECT")
40
+ location = os.environ.get("VERTEX_AI_LOCATION")
41
+
42
+ if project and location:
43
+ logger.info(
44
+ f"Initializing Gemini Client in Vertex AI mode (Project: {project}, Location: {location})"
45
+ )
46
+ client = genai.Client(vertexai=True, project=project, location=location)
47
+ return client, GeminiRuntimeInfo(runtime="vertex", project=project, location=location)
48
+
49
+ # 3. Try AI Studio Configuration (API Key)
50
+ api_key = os.environ.get("GEMINI_API_KEY")
51
+ if api_key:
52
+ logger.info("Initializing Gemini Client in AI Studio mode.")
53
+ client = genai.Client(api_key=api_key)
54
+ return client, GeminiRuntimeInfo(runtime="api_key", project=None, location=None)
55
+
56
+ # 3. Fallback / Error
57
+ error_msg = (
58
+ "Failed to initialize Gemini Client: Missing configuration. "
59
+ "Please set 'VERTEX_AI_API_KEY' for Vertex AI Express mode, OR set both "
60
+ "'VERTEX_AI_PROJECT' and 'VERTEX_AI_LOCATION' for Vertex AI mode, OR set "
61
+ "'GEMINI_API_KEY' for AI Studio mode."
62
+ )
63
+ logger.error(error_msg)
64
+ raise ValueError(error_msg)