autobyteus 1.2.1__py3-none-any.whl → 1.2.3__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 (466) hide show
  1. autobyteus/agent/agent.py +15 -5
  2. autobyteus/agent/bootstrap_steps/__init__.py +1 -3
  3. autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +3 -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/workspace_context_initialization_step.py +2 -4
  8. autobyteus/agent/context/agent_config.py +43 -20
  9. autobyteus/agent/context/agent_context.py +23 -18
  10. autobyteus/agent/context/agent_runtime_state.py +19 -19
  11. autobyteus/agent/events/__init__.py +16 -1
  12. autobyteus/agent/events/agent_events.py +43 -3
  13. autobyteus/agent/events/agent_input_event_queue_manager.py +79 -26
  14. autobyteus/agent/events/event_store.py +57 -0
  15. autobyteus/agent/events/notifiers.py +69 -59
  16. autobyteus/agent/events/worker_event_dispatcher.py +21 -64
  17. autobyteus/agent/factory/agent_factory.py +52 -0
  18. autobyteus/agent/handlers/__init__.py +2 -0
  19. autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +51 -34
  20. autobyteus/agent/handlers/bootstrap_event_handler.py +155 -0
  21. autobyteus/agent/handlers/inter_agent_message_event_handler.py +10 -0
  22. autobyteus/agent/handlers/lifecycle_event_logger.py +19 -11
  23. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +10 -15
  24. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +188 -48
  25. autobyteus/agent/handlers/tool_execution_approval_event_handler.py +0 -10
  26. autobyteus/agent/handlers/tool_invocation_request_event_handler.py +53 -48
  27. autobyteus/agent/handlers/tool_result_event_handler.py +7 -8
  28. autobyteus/agent/handlers/user_input_message_event_handler.py +10 -3
  29. autobyteus/agent/input_processor/memory_ingest_input_processor.py +40 -0
  30. autobyteus/agent/lifecycle/__init__.py +12 -0
  31. autobyteus/agent/lifecycle/base_processor.py +109 -0
  32. autobyteus/agent/lifecycle/events.py +35 -0
  33. autobyteus/agent/lifecycle/processor_definition.py +36 -0
  34. autobyteus/agent/lifecycle/processor_registry.py +106 -0
  35. autobyteus/agent/llm_request_assembler.py +98 -0
  36. autobyteus/agent/llm_response_processor/__init__.py +1 -8
  37. autobyteus/agent/message/context_file_type.py +1 -1
  38. autobyteus/agent/runtime/agent_runtime.py +29 -21
  39. autobyteus/agent/runtime/agent_worker.py +98 -19
  40. autobyteus/agent/shutdown_steps/__init__.py +2 -0
  41. autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +2 -0
  42. autobyteus/agent/shutdown_steps/tool_cleanup_step.py +58 -0
  43. autobyteus/agent/status/__init__.py +14 -0
  44. autobyteus/agent/status/manager.py +93 -0
  45. autobyteus/agent/status/status_deriver.py +96 -0
  46. autobyteus/agent/{phases/phase_enum.py → status/status_enum.py} +16 -16
  47. autobyteus/agent/status/status_update_utils.py +73 -0
  48. autobyteus/agent/streaming/__init__.py +52 -5
  49. autobyteus/agent/streaming/adapters/__init__.py +18 -0
  50. autobyteus/agent/streaming/adapters/invocation_adapter.py +184 -0
  51. autobyteus/agent/streaming/adapters/tool_call_parsing.py +163 -0
  52. autobyteus/agent/streaming/adapters/tool_syntax_registry.py +67 -0
  53. autobyteus/agent/streaming/agent_event_stream.py +3 -183
  54. autobyteus/agent/streaming/api_tool_call/__init__.py +16 -0
  55. autobyteus/agent/streaming/api_tool_call/file_content_streamer.py +56 -0
  56. autobyteus/agent/streaming/api_tool_call/json_string_field_extractor.py +175 -0
  57. autobyteus/agent/streaming/api_tool_call_streaming_response_handler.py +4 -0
  58. autobyteus/agent/streaming/events/__init__.py +6 -0
  59. autobyteus/agent/streaming/events/stream_event_payloads.py +284 -0
  60. autobyteus/agent/streaming/events/stream_events.py +141 -0
  61. autobyteus/agent/streaming/handlers/__init__.py +15 -0
  62. autobyteus/agent/streaming/handlers/api_tool_call_streaming_response_handler.py +303 -0
  63. autobyteus/agent/streaming/handlers/parsing_streaming_response_handler.py +107 -0
  64. autobyteus/agent/streaming/handlers/pass_through_streaming_response_handler.py +107 -0
  65. autobyteus/agent/streaming/handlers/streaming_handler_factory.py +177 -0
  66. autobyteus/agent/streaming/handlers/streaming_response_handler.py +58 -0
  67. autobyteus/agent/streaming/parser/__init__.py +61 -0
  68. autobyteus/agent/streaming/parser/event_emitter.py +181 -0
  69. autobyteus/agent/streaming/parser/events.py +4 -0
  70. autobyteus/agent/streaming/parser/invocation_adapter.py +4 -0
  71. autobyteus/agent/streaming/parser/json_parsing_strategies/__init__.py +19 -0
  72. autobyteus/agent/streaming/parser/json_parsing_strategies/base.py +32 -0
  73. autobyteus/agent/streaming/parser/json_parsing_strategies/default.py +34 -0
  74. autobyteus/agent/streaming/parser/json_parsing_strategies/gemini.py +31 -0
  75. autobyteus/agent/streaming/parser/json_parsing_strategies/openai.py +64 -0
  76. autobyteus/agent/streaming/parser/json_parsing_strategies/registry.py +75 -0
  77. autobyteus/agent/streaming/parser/parser_context.py +227 -0
  78. autobyteus/agent/streaming/parser/parser_factory.py +132 -0
  79. autobyteus/agent/streaming/parser/sentinel_format.py +7 -0
  80. autobyteus/agent/streaming/parser/state_factory.py +62 -0
  81. autobyteus/agent/streaming/parser/states/__init__.py +1 -0
  82. autobyteus/agent/streaming/parser/states/base_state.py +60 -0
  83. autobyteus/agent/streaming/parser/states/custom_xml_tag_run_bash_parsing_state.py +38 -0
  84. autobyteus/agent/streaming/parser/states/custom_xml_tag_write_file_parsing_state.py +55 -0
  85. autobyteus/agent/streaming/parser/states/delimited_content_state.py +146 -0
  86. autobyteus/agent/streaming/parser/states/json_initialization_state.py +144 -0
  87. autobyteus/agent/streaming/parser/states/json_tool_parsing_state.py +137 -0
  88. autobyteus/agent/streaming/parser/states/sentinel_content_state.py +30 -0
  89. autobyteus/agent/streaming/parser/states/sentinel_initialization_state.py +117 -0
  90. autobyteus/agent/streaming/parser/states/text_state.py +78 -0
  91. autobyteus/agent/streaming/parser/states/xml_patch_file_tool_parsing_state.py +328 -0
  92. autobyteus/agent/streaming/parser/states/xml_run_bash_tool_parsing_state.py +129 -0
  93. autobyteus/agent/streaming/parser/states/xml_tag_initialization_state.py +151 -0
  94. autobyteus/agent/streaming/parser/states/xml_tool_parsing_state.py +63 -0
  95. autobyteus/agent/streaming/parser/states/xml_write_file_tool_parsing_state.py +343 -0
  96. autobyteus/agent/streaming/parser/strategies/__init__.py +17 -0
  97. autobyteus/agent/streaming/parser/strategies/base.py +24 -0
  98. autobyteus/agent/streaming/parser/strategies/json_tool_strategy.py +26 -0
  99. autobyteus/agent/streaming/parser/strategies/registry.py +28 -0
  100. autobyteus/agent/streaming/parser/strategies/sentinel_strategy.py +23 -0
  101. autobyteus/agent/streaming/parser/strategies/xml_tag_strategy.py +21 -0
  102. autobyteus/agent/streaming/parser/stream_scanner.py +167 -0
  103. autobyteus/agent/streaming/parser/streaming_parser.py +212 -0
  104. autobyteus/agent/streaming/parser/tool_call_parsing.py +4 -0
  105. autobyteus/agent/streaming/parser/tool_constants.py +7 -0
  106. autobyteus/agent/streaming/parser/tool_syntax_registry.py +4 -0
  107. autobyteus/agent/streaming/parser/xml_tool_parsing_state_registry.py +55 -0
  108. autobyteus/agent/streaming/parsing_streaming_response_handler.py +4 -0
  109. autobyteus/agent/streaming/pass_through_streaming_response_handler.py +4 -0
  110. autobyteus/agent/streaming/queue_streamer.py +3 -57
  111. autobyteus/agent/streaming/segments/__init__.py +5 -0
  112. autobyteus/agent/streaming/segments/segment_events.py +81 -0
  113. autobyteus/agent/streaming/stream_event_payloads.py +2 -223
  114. autobyteus/agent/streaming/stream_events.py +3 -140
  115. autobyteus/agent/streaming/streaming_handler_factory.py +4 -0
  116. autobyteus/agent/streaming/streaming_response_handler.py +4 -0
  117. autobyteus/agent/streaming/streams/__init__.py +5 -0
  118. autobyteus/agent/streaming/streams/agent_event_stream.py +197 -0
  119. autobyteus/agent/streaming/utils/__init__.py +5 -0
  120. autobyteus/agent/streaming/utils/queue_streamer.py +59 -0
  121. autobyteus/agent/system_prompt_processor/__init__.py +2 -0
  122. autobyteus/agent/system_prompt_processor/available_skills_processor.py +96 -0
  123. autobyteus/agent/system_prompt_processor/base_processor.py +1 -1
  124. autobyteus/agent/system_prompt_processor/processor_meta.py +15 -2
  125. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +39 -58
  126. autobyteus/agent/token_budget.py +56 -0
  127. autobyteus/agent/tool_execution_result_processor/memory_ingest_tool_result_processor.py +29 -0
  128. autobyteus/agent/tool_invocation.py +16 -40
  129. autobyteus/agent/tool_invocation_preprocessor/__init__.py +9 -0
  130. autobyteus/agent/tool_invocation_preprocessor/base_preprocessor.py +45 -0
  131. autobyteus/agent/tool_invocation_preprocessor/processor_definition.py +15 -0
  132. autobyteus/agent/tool_invocation_preprocessor/processor_meta.py +33 -0
  133. autobyteus/agent/tool_invocation_preprocessor/processor_registry.py +60 -0
  134. autobyteus/agent/utils/wait_for_idle.py +12 -14
  135. autobyteus/agent/workspace/base_workspace.py +6 -27
  136. autobyteus/agent_team/agent_team.py +3 -3
  137. autobyteus/agent_team/agent_team_builder.py +1 -41
  138. autobyteus/agent_team/bootstrap_steps/__init__.py +0 -4
  139. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +8 -18
  140. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +4 -16
  141. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +1 -2
  142. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +1 -2
  143. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +1 -2
  144. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +4 -4
  145. autobyteus/agent_team/context/agent_team_config.py +6 -3
  146. autobyteus/agent_team/context/agent_team_context.py +25 -3
  147. autobyteus/agent_team/context/agent_team_runtime_state.py +9 -6
  148. autobyteus/agent_team/events/__init__.py +11 -0
  149. autobyteus/agent_team/events/agent_team_event_dispatcher.py +22 -9
  150. autobyteus/agent_team/events/agent_team_events.py +16 -0
  151. autobyteus/agent_team/events/event_store.py +57 -0
  152. autobyteus/agent_team/factory/agent_team_factory.py +8 -0
  153. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +18 -2
  154. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +21 -5
  155. autobyteus/agent_team/handlers/process_user_message_event_handler.py +17 -8
  156. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +19 -4
  157. autobyteus/agent_team/runtime/agent_team_runtime.py +41 -10
  158. autobyteus/agent_team/runtime/agent_team_worker.py +69 -5
  159. autobyteus/agent_team/status/__init__.py +14 -0
  160. autobyteus/agent_team/status/agent_team_status.py +18 -0
  161. autobyteus/agent_team/status/agent_team_status_manager.py +33 -0
  162. autobyteus/agent_team/status/status_deriver.py +62 -0
  163. autobyteus/agent_team/status/status_update_utils.py +42 -0
  164. autobyteus/agent_team/streaming/__init__.py +2 -2
  165. autobyteus/agent_team/streaming/agent_team_event_notifier.py +6 -6
  166. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +4 -4
  167. autobyteus/agent_team/streaming/agent_team_stream_events.py +3 -3
  168. autobyteus/agent_team/system_prompt_processor/__init__.py +6 -0
  169. autobyteus/agent_team/system_prompt_processor/team_manifest_injector_processor.py +76 -0
  170. autobyteus/agent_team/task_notification/task_notification_mode.py +19 -0
  171. autobyteus/agent_team/utils/wait_for_idle.py +4 -4
  172. autobyteus/cli/agent_cli.py +18 -10
  173. autobyteus/cli/agent_team_tui/app.py +14 -11
  174. autobyteus/cli/agent_team_tui/state.py +13 -15
  175. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
  176. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +143 -36
  177. autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
  178. autobyteus/cli/agent_team_tui/widgets/shared.py +25 -25
  179. autobyteus/cli/cli_display.py +193 -44
  180. autobyteus/cli/workflow_tui/app.py +9 -10
  181. autobyteus/cli/workflow_tui/state.py +14 -16
  182. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +15 -15
  183. autobyteus/cli/workflow_tui/widgets/focus_pane.py +137 -35
  184. autobyteus/cli/workflow_tui/widgets/renderables.py +1 -1
  185. autobyteus/cli/workflow_tui/widgets/shared.py +25 -25
  186. autobyteus/clients/autobyteus_client.py +94 -1
  187. autobyteus/events/event_types.py +11 -18
  188. autobyteus/llm/api/autobyteus_llm.py +33 -29
  189. autobyteus/llm/api/claude_llm.py +142 -36
  190. autobyteus/llm/api/gemini_llm.py +163 -59
  191. autobyteus/llm/api/grok_llm.py +1 -1
  192. autobyteus/llm/api/minimax_llm.py +26 -0
  193. autobyteus/llm/api/mistral_llm.py +113 -87
  194. autobyteus/llm/api/ollama_llm.py +9 -42
  195. autobyteus/llm/api/openai_compatible_llm.py +127 -91
  196. autobyteus/llm/api/openai_llm.py +3 -3
  197. autobyteus/llm/api/openai_responses_llm.py +324 -0
  198. autobyteus/llm/api/zhipu_llm.py +21 -2
  199. autobyteus/llm/autobyteus_provider.py +70 -60
  200. autobyteus/llm/base_llm.py +85 -81
  201. autobyteus/llm/converters/__init__.py +14 -0
  202. autobyteus/llm/converters/anthropic_tool_call_converter.py +37 -0
  203. autobyteus/llm/converters/gemini_tool_call_converter.py +57 -0
  204. autobyteus/llm/converters/mistral_tool_call_converter.py +37 -0
  205. autobyteus/llm/converters/openai_tool_call_converter.py +38 -0
  206. autobyteus/llm/extensions/base_extension.py +6 -12
  207. autobyteus/llm/extensions/token_usage_tracking_extension.py +45 -18
  208. autobyteus/llm/llm_factory.py +282 -204
  209. autobyteus/llm/lmstudio_provider.py +60 -49
  210. autobyteus/llm/models.py +35 -2
  211. autobyteus/llm/ollama_provider.py +60 -49
  212. autobyteus/llm/ollama_provider_resolver.py +0 -1
  213. autobyteus/llm/prompt_renderers/__init__.py +19 -0
  214. autobyteus/llm/prompt_renderers/anthropic_prompt_renderer.py +104 -0
  215. autobyteus/llm/prompt_renderers/autobyteus_prompt_renderer.py +19 -0
  216. autobyteus/llm/prompt_renderers/base_prompt_renderer.py +10 -0
  217. autobyteus/llm/prompt_renderers/gemini_prompt_renderer.py +63 -0
  218. autobyteus/llm/prompt_renderers/mistral_prompt_renderer.py +87 -0
  219. autobyteus/llm/prompt_renderers/ollama_prompt_renderer.py +51 -0
  220. autobyteus/llm/prompt_renderers/openai_chat_renderer.py +97 -0
  221. autobyteus/llm/prompt_renderers/openai_responses_renderer.py +101 -0
  222. autobyteus/llm/providers.py +1 -3
  223. autobyteus/llm/token_counter/claude_token_counter.py +56 -25
  224. autobyteus/llm/token_counter/mistral_token_counter.py +12 -8
  225. autobyteus/llm/token_counter/openai_token_counter.py +24 -5
  226. autobyteus/llm/token_counter/token_counter_factory.py +12 -5
  227. autobyteus/llm/utils/llm_config.py +6 -12
  228. autobyteus/llm/utils/media_payload_formatter.py +27 -20
  229. autobyteus/llm/utils/messages.py +55 -3
  230. autobyteus/llm/utils/response_types.py +3 -0
  231. autobyteus/llm/utils/tool_call_delta.py +31 -0
  232. autobyteus/memory/__init__.py +32 -0
  233. autobyteus/memory/active_transcript.py +69 -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 +183 -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/policies/__init__.py +5 -0
  247. autobyteus/memory/policies/compaction_policy.py +16 -0
  248. autobyteus/memory/retrieval/__init__.py +7 -0
  249. autobyteus/memory/retrieval/memory_bundle.py +11 -0
  250. autobyteus/memory/retrieval/retriever.py +13 -0
  251. autobyteus/memory/store/__init__.py +7 -0
  252. autobyteus/memory/store/base_store.py +14 -0
  253. autobyteus/memory/store/file_store.py +98 -0
  254. autobyteus/memory/tool_interaction_builder.py +46 -0
  255. autobyteus/memory/turn_tracker.py +9 -0
  256. autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
  257. autobyteus/multimedia/audio/api/gemini_audio_client.py +108 -16
  258. autobyteus/multimedia/audio/audio_client_factory.py +47 -9
  259. autobyteus/multimedia/audio/audio_model.py +2 -1
  260. autobyteus/multimedia/image/api/autobyteus_image_client.py +19 -5
  261. autobyteus/multimedia/image/api/gemini_image_client.py +38 -17
  262. autobyteus/multimedia/image/api/openai_image_client.py +125 -43
  263. autobyteus/multimedia/image/autobyteus_image_provider.py +2 -1
  264. autobyteus/multimedia/image/image_client_factory.py +47 -15
  265. autobyteus/multimedia/image/image_model.py +5 -2
  266. autobyteus/multimedia/providers.py +3 -2
  267. autobyteus/skills/loader.py +71 -0
  268. autobyteus/skills/model.py +11 -0
  269. autobyteus/skills/registry.py +70 -0
  270. autobyteus/task_management/tools/todo_tools/add_todo.py +2 -2
  271. autobyteus/task_management/tools/todo_tools/create_todo_list.py +2 -2
  272. autobyteus/task_management/tools/todo_tools/update_todo_status.py +2 -2
  273. autobyteus/tools/__init__.py +34 -47
  274. autobyteus/tools/base_tool.py +7 -0
  275. autobyteus/tools/file/__init__.py +2 -6
  276. autobyteus/tools/file/patch_file.py +149 -0
  277. autobyteus/tools/file/read_file.py +36 -5
  278. autobyteus/tools/file/write_file.py +4 -1
  279. autobyteus/tools/functional_tool.py +43 -6
  280. autobyteus/tools/mcp/__init__.py +2 -0
  281. autobyteus/tools/mcp/config_service.py +5 -1
  282. autobyteus/tools/mcp/server/__init__.py +2 -0
  283. autobyteus/tools/mcp/server/http_managed_mcp_server.py +1 -1
  284. autobyteus/tools/mcp/server/websocket_managed_mcp_server.py +141 -0
  285. autobyteus/tools/mcp/server_instance_manager.py +8 -1
  286. autobyteus/tools/mcp/types.py +61 -0
  287. autobyteus/tools/multimedia/audio_tools.py +70 -17
  288. autobyteus/tools/multimedia/download_media_tool.py +18 -4
  289. autobyteus/tools/multimedia/image_tools.py +246 -62
  290. autobyteus/tools/operation_executor/journal_manager.py +107 -0
  291. autobyteus/tools/operation_executor/operation_event_buffer.py +57 -0
  292. autobyteus/tools/operation_executor/operation_event_producer.py +29 -0
  293. autobyteus/tools/operation_executor/operation_executor.py +58 -0
  294. autobyteus/tools/registry/tool_definition.py +43 -2
  295. autobyteus/tools/skill/load_skill.py +50 -0
  296. autobyteus/tools/terminal/__init__.py +45 -0
  297. autobyteus/tools/terminal/ansi_utils.py +32 -0
  298. autobyteus/tools/terminal/background_process_manager.py +233 -0
  299. autobyteus/tools/terminal/output_buffer.py +105 -0
  300. autobyteus/tools/terminal/prompt_detector.py +63 -0
  301. autobyteus/tools/terminal/pty_session.py +241 -0
  302. autobyteus/tools/terminal/session_factory.py +20 -0
  303. autobyteus/tools/terminal/terminal_session_manager.py +226 -0
  304. autobyteus/tools/terminal/tools/__init__.py +13 -0
  305. autobyteus/tools/terminal/tools/get_process_output.py +81 -0
  306. autobyteus/tools/terminal/tools/run_bash.py +109 -0
  307. autobyteus/tools/terminal/tools/start_background_process.py +104 -0
  308. autobyteus/tools/terminal/tools/stop_background_process.py +67 -0
  309. autobyteus/tools/terminal/types.py +54 -0
  310. autobyteus/tools/terminal/wsl_tmux_session.py +221 -0
  311. autobyteus/tools/terminal/wsl_utils.py +156 -0
  312. autobyteus/tools/transaction_management/backup_handler.py +48 -0
  313. autobyteus/tools/transaction_management/operation_lifecycle_manager.py +62 -0
  314. autobyteus/tools/usage/__init__.py +1 -2
  315. autobyteus/tools/usage/formatters/__init__.py +17 -1
  316. autobyteus/tools/usage/formatters/base_formatter.py +8 -0
  317. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +2 -2
  318. autobyteus/tools/usage/formatters/mistral_json_schema_formatter.py +18 -0
  319. autobyteus/tools/usage/formatters/patch_file_xml_example_formatter.py +64 -0
  320. autobyteus/tools/usage/formatters/patch_file_xml_schema_formatter.py +31 -0
  321. autobyteus/tools/usage/formatters/run_bash_xml_example_formatter.py +32 -0
  322. autobyteus/tools/usage/formatters/run_bash_xml_schema_formatter.py +36 -0
  323. autobyteus/tools/usage/formatters/write_file_xml_example_formatter.py +53 -0
  324. autobyteus/tools/usage/formatters/write_file_xml_schema_formatter.py +31 -0
  325. autobyteus/tools/usage/providers/tool_manifest_provider.py +10 -10
  326. autobyteus/tools/usage/registries/__init__.py +1 -3
  327. autobyteus/tools/usage/registries/tool_formatting_registry.py +115 -8
  328. autobyteus/tools/usage/tool_schema_provider.py +51 -0
  329. autobyteus/tools/web/__init__.py +4 -0
  330. autobyteus/tools/web/read_url_tool.py +80 -0
  331. autobyteus/utils/diff_utils.py +271 -0
  332. autobyteus/utils/download_utils.py +109 -0
  333. autobyteus/utils/file_utils.py +57 -2
  334. autobyteus/utils/gemini_helper.py +56 -0
  335. autobyteus/utils/gemini_model_mapping.py +71 -0
  336. autobyteus/utils/llm_output_formatter.py +75 -0
  337. autobyteus/utils/tool_call_format.py +36 -0
  338. autobyteus/workflow/agentic_workflow.py +3 -3
  339. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +2 -2
  340. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +2 -2
  341. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +2 -2
  342. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +3 -9
  343. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +6 -6
  344. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +2 -2
  345. autobyteus/workflow/context/workflow_context.py +3 -3
  346. autobyteus/workflow/context/workflow_runtime_state.py +5 -5
  347. autobyteus/workflow/events/workflow_event_dispatcher.py +5 -5
  348. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +3 -3
  349. autobyteus/workflow/handlers/process_user_message_event_handler.py +5 -5
  350. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +2 -2
  351. autobyteus/workflow/runtime/workflow_runtime.py +8 -8
  352. autobyteus/workflow/runtime/workflow_worker.py +3 -3
  353. autobyteus/workflow/status/__init__.py +11 -0
  354. autobyteus/workflow/status/workflow_status.py +19 -0
  355. autobyteus/workflow/status/workflow_status_manager.py +48 -0
  356. autobyteus/workflow/streaming/__init__.py +2 -2
  357. autobyteus/workflow/streaming/workflow_event_notifier.py +7 -7
  358. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +4 -4
  359. autobyteus/workflow/streaming/workflow_stream_events.py +3 -3
  360. autobyteus/workflow/utils/wait_for_idle.py +4 -4
  361. autobyteus-1.2.3.dist-info/METADATA +293 -0
  362. autobyteus-1.2.3.dist-info/RECORD +600 -0
  363. {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/WHEEL +1 -1
  364. {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/top_level.txt +0 -1
  365. autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +0 -57
  366. autobyteus/agent/hooks/__init__.py +0 -16
  367. autobyteus/agent/hooks/base_phase_hook.py +0 -78
  368. autobyteus/agent/hooks/hook_definition.py +0 -36
  369. autobyteus/agent/hooks/hook_meta.py +0 -37
  370. autobyteus/agent/hooks/hook_registry.py +0 -106
  371. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +0 -103
  372. autobyteus/agent/phases/__init__.py +0 -18
  373. autobyteus/agent/phases/discover.py +0 -53
  374. autobyteus/agent/phases/manager.py +0 -265
  375. autobyteus/agent/phases/transition_decorator.py +0 -40
  376. autobyteus/agent/phases/transition_info.py +0 -33
  377. autobyteus/agent/remote_agent.py +0 -244
  378. autobyteus/agent/workspace/workspace_definition.py +0 -36
  379. autobyteus/agent/workspace/workspace_meta.py +0 -37
  380. autobyteus/agent/workspace/workspace_registry.py +0 -72
  381. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +0 -25
  382. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +0 -85
  383. autobyteus/agent_team/phases/__init__.py +0 -11
  384. autobyteus/agent_team/phases/agent_team_operational_phase.py +0 -19
  385. autobyteus/agent_team/phases/agent_team_phase_manager.py +0 -48
  386. autobyteus/llm/api/bedrock_llm.py +0 -92
  387. autobyteus/llm/api/groq_llm.py +0 -94
  388. autobyteus/llm/api/nvidia_llm.py +0 -108
  389. autobyteus/llm/utils/token_pricing_config.py +0 -87
  390. autobyteus/rpc/__init__.py +0 -73
  391. autobyteus/rpc/client/__init__.py +0 -17
  392. autobyteus/rpc/client/abstract_client_connection.py +0 -124
  393. autobyteus/rpc/client/client_connection_manager.py +0 -153
  394. autobyteus/rpc/client/sse_client_connection.py +0 -306
  395. autobyteus/rpc/client/stdio_client_connection.py +0 -280
  396. autobyteus/rpc/config/__init__.py +0 -13
  397. autobyteus/rpc/config/agent_server_config.py +0 -153
  398. autobyteus/rpc/config/agent_server_registry.py +0 -152
  399. autobyteus/rpc/hosting.py +0 -244
  400. autobyteus/rpc/protocol.py +0 -244
  401. autobyteus/rpc/server/__init__.py +0 -20
  402. autobyteus/rpc/server/agent_server_endpoint.py +0 -181
  403. autobyteus/rpc/server/base_method_handler.py +0 -40
  404. autobyteus/rpc/server/method_handlers.py +0 -259
  405. autobyteus/rpc/server/sse_server_handler.py +0 -182
  406. autobyteus/rpc/server/stdio_server_handler.py +0 -151
  407. autobyteus/rpc/server_main.py +0 -198
  408. autobyteus/rpc/transport_type.py +0 -13
  409. autobyteus/tools/bash/__init__.py +0 -2
  410. autobyteus/tools/bash/bash_executor.py +0 -100
  411. autobyteus/tools/browser/__init__.py +0 -2
  412. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +0 -75
  413. autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +0 -30
  414. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +0 -154
  415. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +0 -89
  416. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +0 -107
  417. autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +0 -14
  418. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +0 -26
  419. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +0 -14
  420. autobyteus/tools/browser/session_aware/shared_browser_session.py +0 -11
  421. autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +0 -25
  422. autobyteus/tools/browser/session_aware/web_element_action.py +0 -20
  423. autobyteus/tools/browser/standalone/__init__.py +0 -6
  424. autobyteus/tools/browser/standalone/factory/__init__.py +0 -0
  425. autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +0 -25
  426. autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +0 -14
  427. autobyteus/tools/browser/standalone/navigate_to.py +0 -84
  428. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -101
  429. autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -169
  430. autobyteus/tools/browser/standalone/webpage_reader.py +0 -105
  431. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -105
  432. autobyteus/tools/file/edit_file.py +0 -200
  433. autobyteus/tools/file/list_directory.py +0 -168
  434. autobyteus/tools/file/search_files.py +0 -188
  435. autobyteus/tools/timer.py +0 -175
  436. autobyteus/tools/usage/parsers/__init__.py +0 -22
  437. autobyteus/tools/usage/parsers/_json_extractor.py +0 -99
  438. autobyteus/tools/usage/parsers/_string_decoders.py +0 -18
  439. autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +0 -10
  440. autobyteus/tools/usage/parsers/base_parser.py +0 -41
  441. autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +0 -83
  442. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +0 -316
  443. autobyteus/tools/usage/parsers/exceptions.py +0 -13
  444. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +0 -77
  445. autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +0 -149
  446. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +0 -59
  447. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +0 -62
  448. autobyteus/workflow/phases/__init__.py +0 -11
  449. autobyteus/workflow/phases/workflow_operational_phase.py +0 -19
  450. autobyteus/workflow/phases/workflow_phase_manager.py +0 -48
  451. autobyteus-1.2.1.dist-info/METADATA +0 -205
  452. autobyteus-1.2.1.dist-info/RECORD +0 -511
  453. examples/__init__.py +0 -1
  454. examples/agent_team/__init__.py +0 -1
  455. examples/discover_phase_transitions.py +0 -104
  456. examples/run_agentic_software_engineer.py +0 -239
  457. examples/run_browser_agent.py +0 -262
  458. examples/run_google_slides_agent.py +0 -287
  459. examples/run_mcp_browser_client.py +0 -174
  460. examples/run_mcp_google_slides_client.py +0 -270
  461. examples/run_mcp_list_tools.py +0 -189
  462. examples/run_poem_writer.py +0 -284
  463. examples/run_sqlite_agent.py +0 -295
  464. /autobyteus/{tools/browser/session_aware → skills}/__init__.py +0 -0
  465. /autobyteus/tools/{browser/session_aware/factory → skill}/__init__.py +0 -0
  466. {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -155,6 +155,12 @@ def _get_parameter_type_from_hint(py_type: Any, param_name: str) -> Tuple[Parame
155
155
  logger.warning(f"Unmapped type hint {py_type} (actual_type: {actual_type}) for param '{param_name}'. Defaulting to ParameterType.STRING.")
156
156
  return ParameterType.STRING, None
157
157
 
158
+
159
+ try:
160
+ from pydantic.fields import FieldInfo
161
+ except ImportError:
162
+ FieldInfo = None # type: ignore
163
+
158
164
  def _parse_signature(sig: inspect.Signature, tool_name: str) -> Tuple[TypingList[str], bool, bool, ParameterSchema]:
159
165
  func_param_names = []
160
166
  expects_context = False
@@ -175,26 +181,51 @@ def _parse_signature(sig: inspect.Signature, tool_name: str) -> Tuple[TypingList
175
181
  param_type_enum, item_schema = _get_parameter_type_from_hint(param_type_hint, param_name)
176
182
 
177
183
  is_required = param_obj.default == inspect.Parameter.empty
178
- if get_origin(param_type_hint) is Union and type(None) in get_args(param_type_hint):
179
- is_required = False
180
-
184
+ default_val = param_obj.default if param_obj.default != inspect.Parameter.empty else None
185
+
186
+ # --- Pydantic Field Extraction Logic ---
181
187
  param_desc = f"Parameter '{param_name}' for tool '{tool_name}'."
182
188
  param_name_lower = param_name.lower()
183
189
  if "path" in param_name_lower or "file" in param_name_lower or "dir" in param_name_lower or "folder" in param_name_lower:
184
- param_desc += " This is expected to be a path."
190
+ param_desc += " This is expected to be a path."
191
+
192
+ if FieldInfo and isinstance(param_obj.default, FieldInfo):
193
+ field_info = param_obj.default
194
+
195
+ # 1. Description
196
+ if field_info.description:
197
+ param_desc = field_info.description
185
198
 
199
+ # 2. Default Value & Requiredness
200
+ # If PydanticUndefined (or similar sentinel), it means required.
201
+ # Otherwise, use the default value from Field.
202
+ # Note: Pydantic v1 uses Undefined, v2 uses PydanticUndefined.
203
+ # We check if it is the special undefined value via representation or direct check.
204
+
205
+ # Simple heuristic for "Undefined" without importing the specific sentinel
206
+ if str(field_info.default) == "PydanticUndefined" or field_info.default == ...:
207
+ is_required = True
208
+ default_val = None
209
+ else:
210
+ is_required = False
211
+ default_val = field_info.default
212
+
213
+ if get_origin(param_type_hint) is Union and type(None) in get_args(param_type_hint):
214
+ is_required = False
215
+
186
216
  schema_param = ParameterDefinition(
187
217
  name=param_name,
188
218
  param_type=param_type_enum,
189
219
  description=param_desc,
190
220
  required=is_required,
191
- default_value=param_obj.default if param_obj.default != inspect.Parameter.empty else None,
221
+ default_value=default_val,
192
222
  array_item_schema=item_schema
193
223
  )
194
224
  generated_arg_schema.add_parameter(schema_param)
195
225
 
196
226
  return func_param_names, expects_context, expects_tool_state, generated_arg_schema
197
227
 
228
+
198
229
  # --- The refactored @tool decorator ---
199
230
 
200
231
  def tool(
@@ -217,6 +248,11 @@ def tool(
217
248
 
218
249
  final_arg_schema = argument_schema if argument_schema is not None else gen_arg_schema
219
250
 
251
+ def _current_description() -> str:
252
+ """Recompute the description from the latest docstring/override."""
253
+ latest_doc = inspect.getdoc(func)
254
+ return description or (latest_doc.split('\n\n')[0] if latest_doc else f"Functional tool: {tool_name}")
255
+
220
256
  def factory(inst_config: Optional[ToolConfig] = None) -> FunctionalTool:
221
257
  return FunctionalTool(
222
258
  original_func=func,
@@ -239,7 +275,8 @@ def tool(
239
275
  custom_factory=factory,
240
276
  tool_class=None,
241
277
  origin=ToolOrigin.LOCAL,
242
- category=category
278
+ category=category,
279
+ description_provider=_current_description
243
280
  )
244
281
  default_tool_registry.register_tool(tool_def)
245
282
 
@@ -19,6 +19,7 @@ from .types import (
19
19
  BaseMcpConfig,
20
20
  StdioMcpServerConfig,
21
21
  StreamableHttpMcpServerConfig,
22
+ WebsocketMcpServerConfig,
22
23
  McpTransportType,
23
24
  McpServerInstanceKey,
24
25
  )
@@ -37,6 +38,7 @@ __all__ = [
37
38
  "BaseMcpConfig",
38
39
  "StdioMcpServerConfig",
39
40
  "StreamableHttpMcpServerConfig",
41
+ "WebsocketMcpServerConfig",
40
42
  "McpTransportType",
41
43
  "McpServerInstanceKey",
42
44
  # Services and Managers
@@ -9,6 +9,7 @@ from .types import (
9
9
  BaseMcpConfig,
10
10
  StdioMcpServerConfig,
11
11
  StreamableHttpMcpServerConfig,
12
+ WebsocketMcpServerConfig,
12
13
  McpTransportType
13
14
  )
14
15
  from autobyteus.utils.singleton import SingletonMeta
@@ -49,7 +50,8 @@ class McpConfigService(metaclass=SingletonMeta):
49
50
 
50
51
  transport_specific_params_key_map = {
51
52
  McpTransportType.STDIO: "stdio_params",
52
- McpTransportType.STREAMABLE_HTTP: "streamable_http_params"
53
+ McpTransportType.STREAMABLE_HTTP: "streamable_http_params",
54
+ McpTransportType.WEBSOCKET: "websocket_params",
53
55
  }
54
56
 
55
57
  if transport_type in transport_specific_params_key_map:
@@ -74,6 +76,8 @@ class McpConfigService(metaclass=SingletonMeta):
74
76
  return StdioMcpServerConfig(**constructor_params)
75
77
  elif transport_type == McpTransportType.STREAMABLE_HTTP:
76
78
  return StreamableHttpMcpServerConfig(**constructor_params)
79
+ elif transport_type == McpTransportType.WEBSOCKET:
80
+ return WebsocketMcpServerConfig(**constructor_params)
77
81
  else:
78
82
  raise ValueError(f"Unsupported McpTransportType '{transport_type}' for server '{server_id}'.")
79
83
  except TypeError as e:
@@ -5,6 +5,7 @@ This package contains the core abstractions for managing connections to remote M
5
5
  from .base_managed_mcp_server import BaseManagedMcpServer, ServerState
6
6
  from .stdio_managed_mcp_server import StdioManagedMcpServer
7
7
  from .http_managed_mcp_server import HttpManagedMcpServer
8
+ from .websocket_managed_mcp_server import WebsocketManagedMcpServer
8
9
  from .proxy import McpServerProxy
9
10
 
10
11
  __all__ = [
@@ -12,5 +13,6 @@ __all__ = [
12
13
  "ServerState",
13
14
  "StdioManagedMcpServer",
14
15
  "HttpManagedMcpServer",
16
+ "WebsocketManagedMcpServer",
15
17
  "McpServerProxy",
16
18
  ]
@@ -23,7 +23,7 @@ class HttpManagedMcpServer(BaseManagedMcpServer):
23
23
  config = cast(StreamableHttpMcpServerConfig, self._config)
24
24
 
25
25
  logger.debug(f"Establishing HTTP connection for server '{self.server_id}' to URL: {config.url}")
26
- read_stream, write_stream = await self._exit_stack.enter_async_context(
26
+ read_stream, write_stream, _ = await self._exit_stack.enter_async_context(
27
27
  streamablehttp_client(config.url, headers=config.headers)
28
28
  )
29
29
  session = await self._exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import logging
5
+ import ssl
6
+ from collections.abc import AsyncGenerator
7
+ from contextlib import asynccontextmanager
8
+ from typing import cast
9
+
10
+ import anyio
11
+ from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
12
+ from mcp import ClientSession, types as mcp_types
13
+ from mcp.shared.message import SessionMessage
14
+ from pydantic import ValidationError
15
+ from websockets.asyncio.client import connect as ws_connect
16
+ from websockets.typing import Subprotocol
17
+
18
+ from .base_managed_mcp_server import BaseManagedMcpServer
19
+ from ..types import WebsocketMcpServerConfig
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ INITIALIZE_TIMEOUT = 10 # seconds
24
+
25
+
26
+ def _build_ssl_context(config: WebsocketMcpServerConfig) -> ssl.SSLContext | None:
27
+ """Builds an SSL context when the target URL uses wss://."""
28
+ if not config.url or not config.url.lower().startswith("wss://"):
29
+ return None
30
+
31
+ if config.verify_tls:
32
+ context = ssl.create_default_context()
33
+ if config.ca_file:
34
+ context.load_verify_locations(cafile=config.ca_file)
35
+ else:
36
+ context = ssl._create_unverified_context()
37
+
38
+ if config.client_cert:
39
+ context.load_cert_chain(certfile=config.client_cert, keyfile=config.client_key)
40
+
41
+ return context
42
+
43
+
44
+ def _normalize_subprotocols(config: WebsocketMcpServerConfig) -> list[Subprotocol]:
45
+ """Ensures the MCP subprotocol is always negotiated."""
46
+ provided = [proto for proto in config.subprotocols if proto]
47
+ lowered = {proto.lower() for proto in provided}
48
+ if "mcp" not in lowered:
49
+ provided.append("mcp")
50
+ return [Subprotocol(proto) for proto in provided]
51
+
52
+
53
+ @asynccontextmanager
54
+ async def _websocket_transport(
55
+ config: WebsocketMcpServerConfig,
56
+ ) -> AsyncGenerator[
57
+ tuple[MemoryObjectReceiveStream[SessionMessage | Exception], MemoryObjectSendStream[SessionMessage]],
58
+ None,
59
+ ]:
60
+ read_stream_writer, read_stream = anyio.create_memory_object_stream[SessionMessage | Exception](0)
61
+ write_stream, write_stream_reader = anyio.create_memory_object_stream[SessionMessage](0)
62
+
63
+ headers = dict(config.headers)
64
+ if not headers:
65
+ headers = None
66
+
67
+ connect_kwargs = {
68
+ "origin": config.origin,
69
+ "subprotocols": _normalize_subprotocols(config),
70
+ "additional_headers": headers,
71
+ "open_timeout": config.open_timeout,
72
+ "ping_interval": config.ping_interval,
73
+ "ping_timeout": config.ping_timeout,
74
+ "ssl": _build_ssl_context(config),
75
+ }
76
+ # Remove None values so websockets uses library defaults
77
+ connect_kwargs = {key: value for key, value in connect_kwargs.items() if value is not None}
78
+
79
+ negotiated_protocols = [str(proto) for proto in connect_kwargs.get("subprotocols", [])]
80
+
81
+ logger.debug(
82
+ "Connecting to MCP WebSocket %s (subprotocols=%s, origin=%s)",
83
+ config.url,
84
+ negotiated_protocols,
85
+ config.origin,
86
+ )
87
+
88
+ async with ws_connect(config.url, **connect_kwargs) as websocket:
89
+
90
+ async def ws_reader():
91
+ async with read_stream_writer:
92
+ async for raw_message in websocket:
93
+ payload = raw_message.decode("utf-8") if isinstance(raw_message, bytes) else raw_message
94
+ try:
95
+ message = mcp_types.JSONRPCMessage.model_validate_json(payload)
96
+ await read_stream_writer.send(SessionMessage(message))
97
+ except ValidationError as exc:
98
+ await read_stream_writer.send(exc)
99
+
100
+ async def ws_writer():
101
+ async with write_stream_reader:
102
+ async for session_message in write_stream_reader:
103
+ payload = session_message.message.model_dump_json(by_alias=True, exclude_none=True)
104
+ await websocket.send(payload)
105
+
106
+ async with anyio.create_task_group() as tg:
107
+ tg.start_soon(ws_reader)
108
+ tg.start_soon(ws_writer)
109
+ try:
110
+ yield read_stream, write_stream
111
+ finally:
112
+ tg.cancel_scope.cancel()
113
+
114
+
115
+ class WebsocketManagedMcpServer(BaseManagedMcpServer):
116
+ """Manages the lifecycle of a WebSocket-based MCP server connection."""
117
+
118
+ def __init__(self, config: WebsocketMcpServerConfig):
119
+ super().__init__(config)
120
+
121
+ async def _create_client_session(self) -> ClientSession:
122
+ config = cast(WebsocketMcpServerConfig, self._config)
123
+
124
+ read_stream, write_stream = await self._exit_stack.enter_async_context(
125
+ _websocket_transport(config)
126
+ )
127
+ session = await self._exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
128
+
129
+ try:
130
+ await asyncio.wait_for(session.initialize(), timeout=INITIALIZE_TIMEOUT)
131
+ except asyncio.TimeoutError:
132
+ logger.error(
133
+ "Timeout occurred while initializing WebSocket session for server '%s'.",
134
+ self.server_id,
135
+ )
136
+ raise ConnectionError(
137
+ f"Server '{self.server_id}' failed to initialize within the timeout period."
138
+ ) from None
139
+
140
+ logger.debug("ClientSession established and initialized for WebSocket server '%s'.", self.server_id)
141
+ return session
@@ -7,7 +7,12 @@ from contextlib import asynccontextmanager
7
7
  from autobyteus.utils.singleton import SingletonMeta
8
8
  from autobyteus.agent.context import AgentContextRegistry
9
9
  from .config_service import McpConfigService
10
- from .server import BaseManagedMcpServer, StdioManagedMcpServer, HttpManagedMcpServer
10
+ from .server import (
11
+ BaseManagedMcpServer,
12
+ StdioManagedMcpServer,
13
+ HttpManagedMcpServer,
14
+ WebsocketManagedMcpServer,
15
+ )
11
16
  from .types import McpTransportType, McpServerInstanceKey, BaseMcpConfig, StdioMcpServerConfig
12
17
 
13
18
  logger = logging.getLogger(__name__)
@@ -29,6 +34,8 @@ class McpServerInstanceManager(metaclass=SingletonMeta):
29
34
  return StdioManagedMcpServer(server_config)
30
35
  elif server_config.transport_type == McpTransportType.STREAMABLE_HTTP:
31
36
  return HttpManagedMcpServer(server_config)
37
+ elif server_config.transport_type == McpTransportType.WEBSOCKET:
38
+ return WebsocketManagedMcpServer(server_config)
32
39
  else:
33
40
  raise NotImplementedError(f"No ManagedMcpServer implementation for transport type '{server_config.transport_type}'.")
34
41
 
@@ -10,6 +10,7 @@ class McpTransportType(str, Enum):
10
10
  """Enumeration of supported MCP transport types."""
11
11
  STDIO = "stdio"
12
12
  STREAMABLE_HTTP = "streamable_http"
13
+ WEBSOCKET = "websocket"
13
14
 
14
15
  @dataclass(frozen=True)
15
16
  class McpServerInstanceKey:
@@ -85,3 +86,63 @@ class StreamableHttpMcpServerConfig(BaseMcpConfig):
85
86
  raise ValueError(f"StreamableHttpMcpServerConfig '{self.server_id}' 'token' must be a string if provided.")
86
87
  if not isinstance(self.headers, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in self.headers.items()):
87
88
  raise ValueError(f"StreamableHttpMcpServerConfig '{self.server_id}' 'headers' must be a Dict[str, str].")
89
+
90
+ @dataclass
91
+ class WebsocketMcpServerConfig(BaseMcpConfig):
92
+ """Configuration parameters for an MCP server using a WebSocket transport."""
93
+
94
+ url: Optional[str] = None
95
+ headers: Dict[str, str] = field(default_factory=dict)
96
+ subprotocols: List[str] = field(default_factory=list)
97
+ origin: Optional[str] = None
98
+ open_timeout: Optional[float] = 10.0
99
+ ping_interval: Optional[float] = None
100
+ ping_timeout: Optional[float] = None
101
+ verify_tls: bool = True
102
+ ca_file: Optional[str] = None
103
+ client_cert: Optional[str] = None
104
+ client_key: Optional[str] = None
105
+
106
+ def __post_init__(self):
107
+ super().__post_init__()
108
+ self.transport_type = McpTransportType.WEBSOCKET
109
+
110
+ if self.url is None or not isinstance(self.url, str) or not self.url.strip():
111
+ raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'url' must be a non-empty string.")
112
+
113
+ normalized_url = self.url.strip().lower()
114
+ if not (normalized_url.startswith("ws://") or normalized_url.startswith("wss://")):
115
+ raise ValueError(
116
+ f"WebsocketMcpServerConfig '{self.server_id}' 'url' must start with ws:// or wss://."
117
+ )
118
+
119
+ if not isinstance(self.headers, dict) or not all(isinstance(k, str) and isinstance(v, str) for k, v in self.headers.items()):
120
+ raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'headers' must be a Dict[str, str].")
121
+
122
+ if not isinstance(self.subprotocols, list) or not all(isinstance(item, str) for item in self.subprotocols):
123
+ raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'subprotocols' must be a list of strings.")
124
+
125
+ if self.origin is not None and not isinstance(self.origin, str):
126
+ raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'origin' must be a string if provided.")
127
+
128
+ for field_name in ("open_timeout", "ping_interval", "ping_timeout"):
129
+ value = getattr(self, field_name)
130
+ if value is not None and (not isinstance(value, (int, float)) or value <= 0):
131
+ raise ValueError(
132
+ f"WebsocketMcpServerConfig '{self.server_id}' '{field_name}' must be a positive number when provided."
133
+ )
134
+
135
+ if not isinstance(self.verify_tls, bool):
136
+ raise ValueError(f"WebsocketMcpServerConfig '{self.server_id}' 'verify_tls' must be a boolean.")
137
+
138
+ for path_field in ("ca_file", "client_cert", "client_key"):
139
+ path_value = getattr(self, path_field)
140
+ if path_value is not None and not isinstance(path_value, str):
141
+ raise ValueError(
142
+ f"WebsocketMcpServerConfig '{self.server_id}' '{path_field}' must be a string path when provided."
143
+ )
144
+
145
+ if self.client_key and not self.client_cert:
146
+ raise ValueError(
147
+ f"WebsocketMcpServerConfig '{self.server_id}' requires 'client_cert' when 'client_key' is provided."
148
+ )
@@ -1,14 +1,38 @@
1
1
  import os
2
2
  import logging
3
- from typing import Optional, List
3
+ from typing import Optional, List, Any
4
+ from pathlib import Path
4
5
 
5
6
  from autobyteus.tools.base_tool import BaseTool
6
7
  from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
7
8
  from autobyteus.tools.tool_category import ToolCategory
8
9
  from autobyteus.multimedia.audio import audio_client_factory, AudioModel, AudioClientFactory
10
+ from autobyteus.multimedia.audio.base_audio_client import BaseAudioClient
11
+ from autobyteus.utils.download_utils import download_file_from_url
12
+ from autobyteus.utils.file_utils import resolve_safe_path
9
13
 
10
14
  logger = logging.getLogger(__name__)
11
15
 
16
+ def _get_workspace_root(context) -> str:
17
+ if not context.workspace:
18
+ error_msg = (
19
+ f"Relative path provided, but no workspace is configured for agent '{context.agent_id}'. "
20
+ "A workspace is required to resolve relative paths."
21
+ )
22
+ logger.error(error_msg)
23
+ raise ValueError(error_msg)
24
+
25
+ base_path = context.workspace.get_base_path()
26
+ if not base_path or not isinstance(base_path, str):
27
+ error_msg = (
28
+ f"Agent '{context.agent_id}' has a configured workspace, but it provided an invalid base path "
29
+ f"('{base_path}'). Cannot resolve relative paths."
30
+ )
31
+ logger.error(error_msg)
32
+ raise ValueError(error_msg)
33
+
34
+ return base_path
35
+
12
36
 
13
37
  def _get_configured_model_identifier(env_var: str, default_model: Optional[str] = None) -> str:
14
38
  """
@@ -54,13 +78,16 @@ def _build_dynamic_audio_schema(base_params: List[ParameterDefinition], model_en
54
78
 
55
79
  class GenerateSpeechTool(BaseTool):
56
80
  """
57
-
58
81
  An agent tool for generating speech from text using a Text-to-Speech (TTS) model.
59
82
  """
60
83
  CATEGORY = ToolCategory.MULTIMEDIA
61
84
  MODEL_ENV_VAR = "DEFAULT_SPEECH_GENERATION_MODEL"
62
85
  DEFAULT_MODEL = "gemini-2.5-flash-tts"
63
86
 
87
+ def __init__(self, config=None):
88
+ super().__init__(config)
89
+ self._client: Optional[BaseAudioClient] = None
90
+
64
91
  @classmethod
65
92
  def get_name(cls) -> str:
66
93
  return "generate_speech"
@@ -69,7 +96,7 @@ class GenerateSpeechTool(BaseTool):
69
96
  def get_description(cls) -> str:
70
97
  return (
71
98
  "Generates spoken audio from text using the system's default Text-to-Speech (TTS) model. "
72
- "Returns a list of local file paths to the generated audio files (.wav) upon success."
99
+ "Saves the generated audio file (.wav or .mp3) to the specified local file path and returns the path."
73
100
  )
74
101
 
75
102
  @classmethod
@@ -82,25 +109,51 @@ class GenerateSpeechTool(BaseTool):
82
109
  "The text to be converted into spoken audio. For multi-speaker mode, you must format the prompt "
83
110
  "with speaker labels that match the speakers defined in 'speaker_mapping'. "
84
111
  "CRITICAL: Each speaker's dialogue MUST be on a new line. "
85
- "Example: 'Joe: Hello Jane.\\nJane: Hi Joe, how are you?'"
112
+ "Example: 'Joe: Hello Jane.\nJane: Hi Joe, how are you?'"
113
+ ),
114
+ required=True
115
+ ),
116
+ ParameterDefinition(
117
+ name="output_file_path",
118
+ param_type=ParameterType.STRING,
119
+ description=(
120
+ "Required. The local file path (relative to workspace) where the generated audio should be saved. "
121
+ "Example: 'assets/audio/speech.wav'"
86
122
  ),
87
123
  required=True
88
124
  )
89
125
  ]
90
126
  return _build_dynamic_audio_schema(base_params, cls.MODEL_ENV_VAR, cls.DEFAULT_MODEL)
91
127
 
92
- async def _execute(self, context, prompt: str, generation_config: Optional[dict] = None) -> List[str]:
128
+ async def _execute(
129
+ self,
130
+ context,
131
+ prompt: str,
132
+ output_file_path: str,
133
+ generation_config: Optional[dict] = None,
134
+ ) -> Any:
93
135
  model_identifier = _get_configured_model_identifier(self.MODEL_ENV_VAR, self.DEFAULT_MODEL)
94
136
  logger.info(f"generate_speech executing with configured model '{model_identifier}'.")
95
- client = None
96
- try:
97
- client = audio_client_factory.create_audio_client(model_identifier=model_identifier)
98
- response = await client.generate_speech(prompt=prompt, generation_config=generation_config)
99
-
100
- if not response.audio_urls:
101
- raise ValueError("Speech generation failed to return any audio file paths.")
102
-
103
- return response.audio_urls
104
- finally:
105
- if client:
106
- await client.cleanup()
137
+ if self._client is None:
138
+ self._client = audio_client_factory.create_audio_client(model_identifier=model_identifier)
139
+
140
+ response = await self._client.generate_speech(prompt=prompt, generation_config=generation_config)
141
+
142
+ if not response.audio_urls:
143
+ raise ValueError("Speech generation failed to return any audio file paths.")
144
+
145
+ first_url = response.audio_urls[0]
146
+
147
+ if not output_file_path:
148
+ raise ValueError("output_file_path is required but was not provided.")
149
+
150
+ # Save to File
151
+ resolved_path = resolve_safe_path(output_file_path, _get_workspace_root(context))
152
+ await download_file_from_url(first_url, resolved_path)
153
+
154
+ return {"file_path": str(resolved_path)}
155
+
156
+ async def cleanup(self) -> None:
157
+ if self._client:
158
+ await self._client.cleanup()
159
+ self._client = None
@@ -28,9 +28,10 @@ class DownloadMediaTool(BaseTool):
28
28
  @classmethod
29
29
  def get_description(cls) -> str:
30
30
  return (
31
- "Downloads various media files (e.g., images like PNG/JPG, documents like PDF, audio like MP3/WAV) "
32
- "from a direct URL and saves them locally. It intelligently determines the correct file extension "
33
- "based on the content type. Returns the absolute path to the downloaded file."
31
+ "Download a media file (image/PDF/audio/etc.) from a direct URL and save it locally. "
32
+ "The tool picks the correct file extension from the HTTP Content-Type header (or falls back to the URL). "
33
+ "Files are saved to the agent workspace if you give a relative folder (preferred), or to your default "
34
+ "Downloads directory when no folder is provided. Returns the absolute path of the saved file."
34
35
  )
35
36
 
36
37
  @classmethod
@@ -63,7 +64,20 @@ class DownloadMediaTool(BaseTool):
63
64
  # Security: prevent path traversal attacks.
64
65
  if ".." in folder:
65
66
  raise ValueError("Security error: 'folder' path cannot contain '..'.")
66
- destination_dir = os.path.abspath(folder)
67
+ if not os.path.isabs(folder):
68
+ workspace = context.workspace
69
+ # Prefer workspace base path when available to keep downloads inside the agent's sandbox.
70
+ if workspace and hasattr(workspace, "get_base_path") and callable(getattr(workspace, "get_base_path")):
71
+ base_path = os.path.abspath(workspace.get_base_path())
72
+ destination_dir = os.path.abspath(os.path.join(base_path, folder))
73
+ # Ensure resolved path stays within workspace
74
+ if os.path.commonpath([base_path]) != os.path.commonpath([base_path, destination_dir]):
75
+ raise ValueError(f"Security error: 'folder' resolves outside workspace: {destination_dir}")
76
+ else:
77
+ # Fallback: resolve relative folder under the default download directory
78
+ destination_dir = os.path.abspath(os.path.join(get_default_download_folder(), folder))
79
+ else:
80
+ destination_dir = os.path.abspath(folder)
67
81
  else:
68
82
  destination_dir = get_default_download_folder()
69
83