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
@@ -38,7 +38,8 @@ class ToolDefinition:
38
38
  config_schema_provider: Callable[[], Optional['ParameterSchema']],
39
39
  tool_class: Optional[Type['BaseTool']] = None,
40
40
  custom_factory: Optional[Callable[['ToolConfig'], 'BaseTool']] = None,
41
- metadata: Optional[Dict[str, Any]] = None):
41
+ metadata: Optional[Dict[str, Any]] = None,
42
+ description_provider: Optional[Callable[[], str]] = None):
42
43
  """
43
44
  Initializes the ToolDefinition.
44
45
  """
@@ -69,6 +70,19 @@ class ToolDefinition:
69
70
  raise ValueError(f"ToolDefinition '{name}' with origin MCP must provide a 'mcp_server_id' in its metadata.")
70
71
 
71
72
  self._name = name
73
+ # Prefer an explicit description provider, otherwise derive one from the tool class when available.
74
+ if description_provider is not None and not callable(description_provider):
75
+ raise TypeError(f"ToolDefinition '{name}' requires a callable for 'description_provider' if provided.")
76
+
77
+ if description_provider:
78
+ self._description_provider = description_provider
79
+ elif tool_class is not None and hasattr(tool_class, "get_description") and callable(getattr(tool_class, "get_description")):
80
+ # Use the tool class' get_description as a dynamic provider by default.
81
+ self._description_provider = tool_class.get_description
82
+ else:
83
+ # Fall back to a static description provider.
84
+ self._description_provider = lambda: description
85
+
72
86
  self._description = description
73
87
  self._tool_class = tool_class
74
88
  self._custom_factory = custom_factory
@@ -136,15 +150,42 @@ class ToolDefinition:
136
150
  def reload_cached_schema(self) -> None:
137
151
  """
138
152
  Actively re-generates the schemas from their providers and updates the cache.
139
- This is an eager operation.
153
+ Also refreshes the description if a provider is available. This is an eager operation.
140
154
  """
141
155
  logger.info(f"Eagerly reloading schema cache for tool '{self.name}'.")
156
+ self._reload_description()
142
157
  self._cached_argument_schema = _CACHE_NOT_SET
143
158
  self._cached_config_schema = _CACHE_NOT_SET
144
159
  # The schemas will be regenerated on the next property access.
145
160
  # To make it fully eager, we can trigger the access here.
146
161
  _ = self.argument_schema
147
162
  _ = self.config_schema
163
+
164
+ def _reload_description(self) -> None:
165
+ """
166
+ Refreshes the cached description using the provider if available.
167
+ """
168
+ if not self._description_provider:
169
+ return
170
+ try:
171
+ new_description = self._description_provider()
172
+ if isinstance(new_description, str) and new_description:
173
+ if new_description != self._description:
174
+ logger.info(
175
+ f"Description for tool '{self.name}' updated during reload."
176
+ )
177
+ self._description = new_description
178
+ else:
179
+ logger.warning(
180
+ f"Description provider for tool '{self.name}' returned an invalid value. "
181
+ "Keeping existing description."
182
+ )
183
+ except Exception as exc:
184
+ logger.warning(
185
+ f"Failed to refresh description for tool '{self.name}' during reload: {exc}. "
186
+ "Keeping existing description.",
187
+ exc_info=True
188
+ )
148
189
 
149
190
  # --- Convenience Schema/Example Generation API (using default formatters) ---
150
191
  def get_usage_xml(self, provider: Optional[LLMProvider] = None) -> str:
@@ -0,0 +1,50 @@
1
+ import logging
2
+ from typing import TYPE_CHECKING
3
+ from autobyteus.tools import tool
4
+ from autobyteus.tools.tool_category import ToolCategory
5
+ from autobyteus.skills.registry import SkillRegistry
6
+
7
+ if TYPE_CHECKING:
8
+ from autobyteus.agent.context import AgentContext
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ @tool(name="load_skill", category=ToolCategory.GENERAL)
13
+ async def load_skill(context: 'AgentContext', skill_name: str) -> str:
14
+ """
15
+ Loads a skill's entry point (SKILL.md) and provides its root path context.
16
+ Use this to understand a specialized skill's capabilities and internal assets.
17
+
18
+ Args:
19
+ skill_name: The registered name of the skill (e.g., 'java_expert') or a path to a skill directory.
20
+
21
+ Returns:
22
+ A formatted context block containing the skill's map, its absolute root path, and path resolution guidance.
23
+ """
24
+ logger.debug(f"Tool 'load_skill' called for skill: {skill_name}")
25
+ registry = SkillRegistry()
26
+ skill = registry.get_skill(skill_name)
27
+
28
+ if not skill:
29
+ # Fallback: check if skill_name is actually a path
30
+ logger.debug(f"Skill '{skill_name}' not found in registry. Attempting to register from path.")
31
+ try:
32
+ skill = registry.register_skill_from_path(skill_name)
33
+ except Exception as e:
34
+ error_msg = f"Skill '{skill_name}' not found and is not a valid skill path: {e}"
35
+ logger.error(error_msg)
36
+ raise ValueError(error_msg)
37
+
38
+ logger.info(f"Skill '{skill.name}' successfully loaded.")
39
+ return f"""## Skill: {skill.name}
40
+ Root Path: {skill.root_path}
41
+
42
+ > **CRITICAL: Path Resolution When Using Tools**
43
+ >
44
+ > This skill uses relative paths. When using any tool that requires a file path,
45
+ > you MUST first construct the full absolute path by combining the Root Path above
46
+ > with the relative path from the skill instructions.
47
+ >
48
+ > **Example:** Root Path + `./scripts/format.sh` = `{skill.root_path}/scripts/format.sh`
49
+
50
+ {skill.content}"""
@@ -0,0 +1,45 @@
1
+ """
2
+ Terminal tools package.
3
+
4
+ Provides PTY-based terminal operations for agents with stateful
5
+ command execution and background process management.
6
+ """
7
+
8
+ import os
9
+
10
+ from autobyteus.tools.terminal.types import (
11
+ TerminalResult,
12
+ BackgroundProcessOutput,
13
+ ProcessInfo,
14
+ )
15
+ from autobyteus.tools.terminal.output_buffer import OutputBuffer
16
+ from autobyteus.tools.terminal.prompt_detector import PromptDetector
17
+ from autobyteus.tools.terminal.terminal_session_manager import TerminalSessionManager
18
+ from autobyteus.tools.terminal.background_process_manager import BackgroundProcessManager
19
+ from autobyteus.tools.terminal.session_factory import get_default_session_factory
20
+
21
+ PtySession = None
22
+ WslTmuxSession = None
23
+
24
+ if os.name != "nt":
25
+ from autobyteus.tools.terminal.pty_session import PtySession
26
+ else:
27
+ from autobyteus.tools.terminal.wsl_tmux_session import WslTmuxSession
28
+
29
+ __all__ = [
30
+ # Types
31
+ "TerminalResult",
32
+ "BackgroundProcessOutput",
33
+ "ProcessInfo",
34
+ # Components
35
+ "OutputBuffer",
36
+ "PromptDetector",
37
+ "TerminalSessionManager",
38
+ "BackgroundProcessManager",
39
+ "get_default_session_factory",
40
+ ]
41
+
42
+ if PtySession is not None:
43
+ __all__.append("PtySession")
44
+ if WslTmuxSession is not None:
45
+ __all__.append("WslTmuxSession")
@@ -0,0 +1,32 @@
1
+ """
2
+ ANSI escape sequence utilities for cleaning terminal output.
3
+ """
4
+
5
+ import re
6
+
7
+ # Regex pattern for ANSI escape sequences
8
+ # Matches: ESC[...m (colors), ESC[...H (cursor), etc.
9
+ _ANSI_ESCAPE_PATTERN = re.compile(r'\x1b\[[0-9;?]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[=>]')
10
+
11
+
12
+ def strip_ansi_codes(text: str) -> str:
13
+ """Remove ANSI escape sequences from text.
14
+
15
+ Strips color codes, cursor control, and other terminal escape
16
+ sequences, leaving only the plain text content.
17
+
18
+ Args:
19
+ text: String potentially containing ANSI escape codes.
20
+
21
+ Returns:
22
+ Clean string with ANSI codes removed.
23
+
24
+ Examples:
25
+ >>> strip_ansi_codes('\x1b[31mRed text\x1b[0m')
26
+ 'Red text'
27
+ >>> strip_ansi_codes('Hello\x1b[1;32m World\x1b[0m')
28
+ 'Hello World'
29
+ """
30
+ if not text:
31
+ return text
32
+ return _ANSI_ESCAPE_PATTERN.sub('', text)
@@ -0,0 +1,233 @@
1
+ """
2
+ Background Process Manager for long-running processes.
3
+
4
+ Manages multiple PTY sessions for background processes like servers,
5
+ with output buffering and lifecycle management.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import logging
12
+ import time
13
+ import uuid
14
+ from typing import Callable, Dict, Optional
15
+
16
+ from autobyteus.tools.terminal.output_buffer import OutputBuffer
17
+ from autobyteus.tools.terminal.session_factory import get_default_session_factory
18
+ from autobyteus.tools.terminal.types import BackgroundProcessOutput, ProcessInfo
19
+ from autobyteus.tools.terminal.ansi_utils import strip_ansi_codes
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+
24
+ class BackgroundProcess:
25
+ """Wrapper for a background process with its PTY and output buffer."""
26
+
27
+ def __init__(
28
+ self,
29
+ process_id: str,
30
+ command: str,
31
+ session: object,
32
+ output_buffer: OutputBuffer
33
+ ):
34
+ self.process_id = process_id
35
+ self.command = command
36
+ self.session = session
37
+ self.output_buffer = output_buffer
38
+ self.started_at = time.time()
39
+ self._reader_task: Optional[asyncio.Task] = None
40
+
41
+ @property
42
+ def is_running(self) -> bool:
43
+ return self.session.is_alive
44
+
45
+ def to_info(self) -> ProcessInfo:
46
+ return ProcessInfo(
47
+ process_id=self.process_id,
48
+ command=self.command,
49
+ started_at=self.started_at,
50
+ is_running=self.is_running
51
+ )
52
+
53
+
54
+ class BackgroundProcessManager:
55
+ """Manages background processes (servers, watchers, etc.).
56
+
57
+ Each background process runs in its own PTY session with
58
+ continuous output buffering.
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ session_factory: Callable[[str], object] = None,
64
+ max_output_bytes: int = 1_000_000
65
+ ):
66
+ """Initialize the background process manager.
67
+
68
+ Args:
69
+ session_factory: Factory function to create PtySession instances.
70
+ max_output_bytes: Maximum bytes to buffer per process.
71
+ """
72
+ self._session_factory = session_factory or get_default_session_factory()
73
+ self._max_output_bytes = max_output_bytes
74
+ self._processes: Dict[str, BackgroundProcess] = {}
75
+ self._counter = 0
76
+
77
+ def _generate_id(self) -> str:
78
+ """Generate a unique process ID."""
79
+ self._counter += 1
80
+ return f"bg_{self._counter:03d}"
81
+
82
+ async def start_process(self, command: str, cwd: str) -> str:
83
+ """Start a long-running process in the background.
84
+
85
+ Args:
86
+ command: The command to run.
87
+ cwd: Working directory for the process.
88
+
89
+ Returns:
90
+ Process ID that can be used to reference this process.
91
+ """
92
+ process_id = self._generate_id()
93
+ session_id = f"bg-{uuid.uuid4().hex[:8]}"
94
+
95
+ session = self._session_factory(session_id)
96
+ output_buffer = OutputBuffer(max_bytes=self._max_output_bytes)
97
+
98
+ # Start the session
99
+ await session.start(cwd)
100
+
101
+ # Create process wrapper
102
+ bg_process = BackgroundProcess(
103
+ process_id=process_id,
104
+ command=command,
105
+ session=session,
106
+ output_buffer=output_buffer
107
+ )
108
+
109
+ # Write the command
110
+ if not command.endswith('\n'):
111
+ command += '\n'
112
+ await session.write(command.encode('utf-8'))
113
+
114
+ # Start background reader task
115
+ bg_process._reader_task = asyncio.create_task(
116
+ self._read_loop(bg_process)
117
+ )
118
+
119
+ self._processes[process_id] = bg_process
120
+ logger.info(f"Started background process {process_id}: {command.strip()}")
121
+
122
+ return process_id
123
+
124
+ async def _read_loop(self, process: BackgroundProcess) -> None:
125
+ """Background task that continuously reads output from the PTY.
126
+
127
+ Args:
128
+ process: The background process to read from.
129
+ """
130
+ try:
131
+ while process.session.is_alive:
132
+ try:
133
+ data = await process.session.read(timeout=0.1)
134
+ if data:
135
+ process.output_buffer.append(data)
136
+ except Exception as e:
137
+ logger.debug(f"Read error for {process.process_id}: {e}")
138
+ break
139
+
140
+ await asyncio.sleep(0.05)
141
+ except asyncio.CancelledError:
142
+ pass
143
+ except Exception as e:
144
+ logger.error(f"Error in read loop for {process.process_id}: {e}")
145
+
146
+ def get_output(
147
+ self,
148
+ process_id: str,
149
+ lines: int = 100
150
+ ) -> BackgroundProcessOutput:
151
+ """Get recent output from a background process.
152
+
153
+ Args:
154
+ process_id: ID of the process.
155
+ lines: Number of recent lines to return.
156
+
157
+ Returns:
158
+ BackgroundProcessOutput with output and status.
159
+
160
+ Raises:
161
+ KeyError: If process_id not found.
162
+ """
163
+ if process_id not in self._processes:
164
+ raise KeyError(f"Process {process_id} not found")
165
+
166
+ process = self._processes[process_id]
167
+ raw_output = process.output_buffer.get_lines(lines)
168
+ clean_output = strip_ansi_codes(raw_output)
169
+ return BackgroundProcessOutput(
170
+ output=clean_output,
171
+ is_running=process.is_running,
172
+ process_id=process_id
173
+ )
174
+
175
+ async def stop_process(self, process_id: str) -> bool:
176
+ """Stop a background process.
177
+
178
+ Sends SIGTERM, then SIGKILL if necessary.
179
+
180
+ Args:
181
+ process_id: ID of the process to stop.
182
+
183
+ Returns:
184
+ True if process was found and stopped, False if not found.
185
+ """
186
+ if process_id not in self._processes:
187
+ return False
188
+
189
+ process = self._processes.pop(process_id)
190
+
191
+ # Cancel reader task
192
+ if process._reader_task:
193
+ process._reader_task.cancel()
194
+ try:
195
+ await process._reader_task
196
+ except asyncio.CancelledError:
197
+ pass
198
+
199
+ # Close the PTY session
200
+ await process.session.close()
201
+
202
+ logger.info(f"Stopped background process {process_id}")
203
+ return True
204
+
205
+ async def stop_all(self) -> int:
206
+ """Stop all background processes.
207
+
208
+ Returns:
209
+ Number of processes stopped.
210
+ """
211
+ count = len(self._processes)
212
+ process_ids = list(self._processes.keys())
213
+
214
+ for process_id in process_ids:
215
+ await self.stop_process(process_id)
216
+
217
+ return count
218
+
219
+ def list_processes(self) -> Dict[str, ProcessInfo]:
220
+ """List all background processes.
221
+
222
+ Returns:
223
+ Dict mapping process_id to ProcessInfo.
224
+ """
225
+ return {
226
+ pid: proc.to_info()
227
+ for pid, proc in self._processes.items()
228
+ }
229
+
230
+ @property
231
+ def process_count(self) -> int:
232
+ """Number of managed processes."""
233
+ return len(self._processes)
@@ -0,0 +1,105 @@
1
+ """
2
+ Ring buffer for capturing terminal output.
3
+
4
+ Provides bounded memory storage for command output with
5
+ support for retrieving recent lines.
6
+ """
7
+
8
+ import threading
9
+ from collections import deque
10
+ from typing import List
11
+
12
+
13
+ class OutputBuffer:
14
+ """Ring buffer that stores output with bounded memory.
15
+
16
+ Thread-safe buffer for capturing terminal output. Stores data
17
+ up to a maximum byte limit, discarding oldest data when full.
18
+
19
+ Attributes:
20
+ max_bytes: Maximum bytes to store before discarding old data.
21
+ """
22
+
23
+ def __init__(self, max_bytes: int = 1_000_000):
24
+ """Initialize the output buffer.
25
+
26
+ Args:
27
+ max_bytes: Maximum bytes to store (default 1MB).
28
+ """
29
+ self._max_bytes = max_bytes
30
+ self._buffer: deque = deque()
31
+ self._total_bytes = 0
32
+ self._lock = threading.Lock()
33
+
34
+ def append(self, data: bytes) -> None:
35
+ """Append data to the buffer.
36
+
37
+ If adding data would exceed max_bytes, oldest lines are
38
+ discarded to make room.
39
+
40
+ Args:
41
+ data: Bytes to append to the buffer.
42
+ """
43
+ if not data:
44
+ return
45
+
46
+ with self._lock:
47
+ # Decode and split by lines, keeping line endings
48
+ try:
49
+ text = data.decode('utf-8', errors='replace')
50
+ except Exception:
51
+ text = str(data)
52
+
53
+ # Split into lines but keep partial lines
54
+ lines = text.splitlines(keepends=True)
55
+
56
+ for line in lines:
57
+ line_bytes = len(line.encode('utf-8'))
58
+ self._buffer.append(line)
59
+ self._total_bytes += line_bytes
60
+
61
+ # Trim oldest lines if over limit
62
+ while self._total_bytes > self._max_bytes and self._buffer:
63
+ removed = self._buffer.popleft()
64
+ self._total_bytes -= len(removed.encode('utf-8'))
65
+
66
+ def get_lines(self, n: int = 100) -> str:
67
+ """Get the last n lines from the buffer.
68
+
69
+ Args:
70
+ n: Number of lines to retrieve.
71
+
72
+ Returns:
73
+ String containing the last n lines.
74
+ """
75
+ with self._lock:
76
+ if n >= len(self._buffer):
77
+ return ''.join(self._buffer)
78
+ return ''.join(list(self._buffer)[-n:])
79
+
80
+ def get_all(self) -> str:
81
+ """Get all content from the buffer.
82
+
83
+ Returns:
84
+ String containing all buffered content.
85
+ """
86
+ with self._lock:
87
+ return ''.join(self._buffer)
88
+
89
+ def clear(self) -> None:
90
+ """Clear all content from the buffer."""
91
+ with self._lock:
92
+ self._buffer.clear()
93
+ self._total_bytes = 0
94
+
95
+ @property
96
+ def size(self) -> int:
97
+ """Current size of buffer in bytes."""
98
+ with self._lock:
99
+ return self._total_bytes
100
+
101
+ @property
102
+ def line_count(self) -> int:
103
+ """Current number of lines in buffer."""
104
+ with self._lock:
105
+ return len(self._buffer)
@@ -0,0 +1,63 @@
1
+ """
2
+ Prompt detection for terminal command completion.
3
+
4
+ Detects when a shell prompt has returned, indicating command completion.
5
+ """
6
+
7
+ import re
8
+ from typing import Optional
9
+
10
+
11
+ class PromptDetector:
12
+ """Detects when shell prompt returns after command execution.
13
+
14
+ Uses regex pattern matching to identify when the shell has
15
+ returned to a prompt state, indicating command completion.
16
+ """
17
+
18
+ # Default pattern matches common prompts ending with $ or #
19
+ DEFAULT_PATTERN = r'[\$#]\s*$'
20
+
21
+ def __init__(self, prompt_pattern: Optional[str] = None):
22
+ """Initialize the prompt detector.
23
+
24
+ Args:
25
+ prompt_pattern: Regex pattern to match prompt.
26
+ Defaults to matching $ or # at end of line.
27
+ """
28
+ self._pattern = prompt_pattern or self.DEFAULT_PATTERN
29
+ self._compiled = re.compile(self._pattern, re.MULTILINE)
30
+
31
+ def check(self, output: str) -> bool:
32
+ """Check if output ends with a prompt.
33
+
34
+ Args:
35
+ output: Terminal output to check.
36
+
37
+ Returns:
38
+ True if output appears to end with a prompt.
39
+ """
40
+ if not output:
41
+ return False
42
+
43
+ # Check the last few lines for a prompt
44
+ lines = output.rstrip().split('\n')
45
+ if not lines:
46
+ return False
47
+
48
+ last_line = lines[-1]
49
+ return bool(self._compiled.search(last_line))
50
+
51
+ def set_pattern(self, pattern: str) -> None:
52
+ """Update the prompt pattern.
53
+
54
+ Args:
55
+ pattern: New regex pattern to use.
56
+ """
57
+ self._pattern = pattern
58
+ self._compiled = re.compile(self._pattern, re.MULTILINE)
59
+
60
+ @property
61
+ def pattern(self) -> str:
62
+ """Current prompt pattern."""
63
+ return self._pattern