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
@@ -0,0 +1,343 @@
1
+ """
2
+ XmlWriteFileToolParsingState: Streams <tool name="write_file"> blocks.
3
+
4
+ This state specializes the generic XmlToolParsingState to stream file content
5
+ and capture the path for display. Argument parsing is handled later by the
6
+ ToolInvocationAdapter.
7
+ """
8
+ from typing import TYPE_CHECKING, Optional
9
+
10
+ from .xml_tool_parsing_state import XmlToolParsingState
11
+ from ..events import SegmentType
12
+
13
+ if TYPE_CHECKING:
14
+ from ..parser_context import ParserContext
15
+
16
+
17
+ class XmlWriteFileToolParsingState(XmlToolParsingState):
18
+ """
19
+ Streams <tool name="write_file"> tool calls.
20
+
21
+ This state operates identically to XmlToolParsingState but provides
22
+ a distinct type (WRITE_FILE) and specialized metadata handling if needed.
23
+ """
24
+
25
+ SEGMENT_TYPE = SegmentType.WRITE_FILE
26
+ START_CONTENT_MARKER = "__START_CONTENT__"
27
+ END_CONTENT_MARKER = "__END_CONTENT__"
28
+ CONTENT_ARG_CLOSE_TAG = "</arg>"
29
+
30
+ def __init__(self, context: "ParserContext", opening_tag: str):
31
+ super().__init__(context, opening_tag)
32
+ if self._tool_name != "write_file":
33
+ pass
34
+
35
+ # Internal state for streaming
36
+ self._found_content_start = False
37
+ self._content_buffering = ""
38
+ self._captured_path: Optional[str] = None
39
+ self._defer_start = True # New flag to defer emission
40
+ self._swallowing_remaining = False # New flag to swallow closing tags
41
+ self._content_mode = "seek_marker"
42
+ self._content_seek_buffer = ""
43
+ self._marker_tail = ""
44
+
45
+ def run(self) -> None:
46
+ """
47
+ Custom run loop to stream ONLY the content argument.
48
+ """
49
+ from .text_state import TextState
50
+
51
+ if self._swallowing_remaining:
52
+ self._handle_swallowing()
53
+ return
54
+
55
+ # Note: We do NOT emit start immediately anymore.
56
+
57
+ if not self.context.has_more_chars():
58
+ return
59
+
60
+ chunk = self.context.consume_remaining()
61
+
62
+ if not self._found_content_start:
63
+ self._content_buffering += chunk
64
+
65
+ import re
66
+
67
+ # 1. Try to find path if missing
68
+ if not self._captured_path:
69
+ path_match = re.search(r'<arg\s+name=["\']path["\']>([^<]+)</arg>', self._content_buffering, re.IGNORECASE)
70
+ if path_match:
71
+ self._captured_path = path_match.group(1).strip()
72
+ # Now we have path, we can emit start if we were waiting for it
73
+ if self._defer_start and not self._segment_started:
74
+ # Construct metadata with path
75
+ meta = self._get_start_metadata()
76
+ meta["path"] = self._captured_path
77
+ self.context.emit_segment_start(self.SEGMENT_TYPE, **meta)
78
+ self._segment_started = True
79
+ self._defer_start = False
80
+
81
+ # 2. Look for content start
82
+ match = re.search(r'<arg\s+name=["\']content["\']>', self._content_buffering, re.IGNORECASE)
83
+
84
+ if match:
85
+ self._found_content_start = True
86
+ end_of_tag = match.end()
87
+
88
+ # If we still haven't emitted start (e.g. no path found but content started), emit now without path
89
+ if not self._segment_started:
90
+ self.context.emit_segment_start(self.SEGMENT_TYPE, **self._get_start_metadata())
91
+ self._segment_started = True
92
+
93
+ # Update path in metadata if we found it late (redundant but safe)
94
+ if self._captured_path:
95
+ self.context.update_current_segment_metadata(path=self._captured_path)
96
+
97
+ real_content = self._content_buffering[end_of_tag:]
98
+ self._content_buffering = ""
99
+ self._content_mode = "seek_marker"
100
+ self._content_seek_buffer = ""
101
+ self._marker_tail = ""
102
+ self._tail = ""
103
+ self._process_content_chunk(real_content)
104
+ else:
105
+ # If closing tool and still no content
106
+ if "</tool>" in self._content_buffering:
107
+ # If start never happened, force it
108
+ if not self._segment_started:
109
+ self.context.emit_segment_start(self.SEGMENT_TYPE, **self._get_start_metadata())
110
+ self._segment_started = True
111
+
112
+ self._on_segment_complete()
113
+ self.context.emit_segment_end()
114
+ self.context.transition_to(TextState(self.context))
115
+ else:
116
+ self._process_content_chunk(chunk)
117
+
118
+ def _process_content_chunk(self, chunk: str) -> None:
119
+ """Process content chunk, supporting optional content markers."""
120
+ if not chunk:
121
+ return
122
+
123
+ if self._content_mode == "marker":
124
+ self._process_marker_content(chunk)
125
+ return
126
+
127
+ if self._content_mode == "default":
128
+ self._process_default_content(chunk)
129
+ return
130
+
131
+ self._process_seek_marker_content(chunk)
132
+
133
+ def _process_seek_marker_content(self, chunk: str) -> None:
134
+ """Seek __START_CONTENT__ before committing to default parsing."""
135
+ self._content_seek_buffer += chunk
136
+
137
+ start_idx = self._content_seek_buffer.find(self.START_CONTENT_MARKER)
138
+ if start_idx != -1:
139
+ after_start = self._content_seek_buffer[start_idx + len(self.START_CONTENT_MARKER):]
140
+ # Strip leading newline after marker to avoid empty first line
141
+ if after_start.startswith("\n"):
142
+ after_start = after_start[1:]
143
+ self._content_seek_buffer = ""
144
+ self._content_mode = "marker"
145
+ self._marker_tail = ""
146
+ self._tail = ""
147
+ if after_start:
148
+ self._process_marker_content(after_start)
149
+ return
150
+
151
+ closing_idx = self._content_seek_buffer.find(self.CONTENT_ARG_CLOSE_TAG)
152
+ if closing_idx != -1:
153
+ buffered = self._content_seek_buffer
154
+ self._content_seek_buffer = ""
155
+ self._content_mode = "default"
156
+ self._tail = ""
157
+ self._process_default_content(buffered)
158
+ return
159
+
160
+ stripped = self._content_seek_buffer.lstrip()
161
+ if stripped and not self.START_CONTENT_MARKER.startswith(stripped):
162
+ buffered = self._content_seek_buffer
163
+ self._content_seek_buffer = ""
164
+ self._content_mode = "default"
165
+ self._tail = ""
166
+ self._process_default_content(buffered)
167
+
168
+ def _process_default_content(self, chunk: str) -> None:
169
+ """Process content chunk, stripping closing tags."""
170
+ closing_tag = self.CONTENT_ARG_CLOSE_TAG
171
+ combined = self._tail + chunk
172
+
173
+ idx = combined.find(closing_tag)
174
+
175
+ if idx != -1:
176
+ actual_content = combined[:idx]
177
+ if actual_content:
178
+ self.context.emit_segment_content(actual_content)
179
+
180
+ # We found the end of the content argument.
181
+ # Instead of stopping, we switch to swallowing mode to eat </arguments></tool>
182
+ self._tail = ""
183
+ remainder = combined[idx + len(closing_tag):]
184
+ self._content_buffering = remainder
185
+ self._swallowing_remaining = True
186
+
187
+ # Immediately try to finish if we have the closing tags
188
+ self._handle_swallowing()
189
+ return
190
+
191
+ holdback_len = len(closing_tag) - 1
192
+ if len(combined) > holdback_len:
193
+ safe = combined[:-holdback_len]
194
+ if safe:
195
+ self.context.emit_segment_content(safe)
196
+ self._tail = combined[-holdback_len:]
197
+ else:
198
+ self._tail = combined
199
+
200
+ def _process_marker_content(self, chunk: str) -> None:
201
+ """Process content chunk when inside __START_CONTENT__/__END_CONTENT__ markers.
202
+
203
+ The __END_CONTENT__ sentinel is only valid if followed by optional whitespace
204
+ and then </arg>. This prevents false positives when file content contains
205
+ the literal __END_CONTENT__ string.
206
+ """
207
+ import re
208
+
209
+ combined = self._marker_tail + chunk
210
+ end_marker = self.END_CONTENT_MARKER
211
+ closing_tag = self.CONTENT_ARG_CLOSE_TAG
212
+
213
+ # Priority 1: Check for the explicit end marker WITH lookahead validation
214
+ # We need to find __END_CONTENT__ that is followed by whitespace* + </arg>
215
+ search_start = 0
216
+ while True:
217
+ idx = combined.find(end_marker, search_start)
218
+ if idx == -1:
219
+ break
220
+
221
+ remainder_after_marker = combined[idx + len(end_marker):]
222
+
223
+ # Validate: must be followed by whitespace* + </arg>
224
+ if re.match(r'^\s*</arg>', remainder_after_marker):
225
+ # Valid sentinel - emit content and transition
226
+ actual_content = combined[:idx]
227
+ if actual_content:
228
+ self.context.emit_segment_content(actual_content)
229
+
230
+ self._marker_tail = ""
231
+ remainder = combined[idx + len(end_marker):]
232
+ self._content_buffering = remainder
233
+ self._swallowing_remaining = True
234
+ self._handle_swallowing()
235
+ return
236
+ elif remainder_after_marker.strip() == "":
237
+ # Indeterminate - need more data to decide if this is valid
238
+ # Hold back from idx onwards
239
+ if idx > 0:
240
+ # Emit safe content before the potential marker
241
+ safe_content = combined[:idx]
242
+ if safe_content:
243
+ self.context.emit_segment_content(safe_content)
244
+ self._marker_tail = combined[idx:]
245
+ else:
246
+ self._marker_tail = combined
247
+ return
248
+ else:
249
+ # False positive - this __END_CONTENT__ is part of the file content
250
+ # Continue searching for the next occurrence
251
+ search_start = idx + len(end_marker)
252
+
253
+ # Priority 2: Check for closing arg tag as fallback (missing sentinel case)
254
+ # Only treat </arg> as terminator if it looks like the actual XML structure end
255
+ # (i.e. followed by </arguments> or </tool>)
256
+ idx_close = combined.find(closing_tag)
257
+ if idx_close != -1:
258
+ remainder_after_close = combined[idx_close + len(closing_tag):]
259
+
260
+ # Check if followed by standard XML closure (ignoring whitespace)
261
+ # We match if we see the start of the next tag, OR if we have only whitespace (ambiguous - wait)
262
+
263
+ is_valid_closure = False
264
+ # If we see the next tag immediately start
265
+ if re.match(r'^\s*(?:</arguments>|</tool>)', remainder_after_close):
266
+ is_valid_closure = True
267
+
268
+ # If we have indeterminate whitespace, we must hold back to be sure
269
+ elif remainder_after_close.strip() == "":
270
+ # We can't decide yet. Hold back everything from existing tag start.
271
+ # But we can allow partial emit of previous content if we separate it.
272
+ # Let's just hold back the whole combined tail to be safe/simple.
273
+ self._marker_tail = combined
274
+ return
275
+
276
+ if is_valid_closure:
277
+ actual_content = combined[:idx_close]
278
+
279
+ # User request: remove the last \n for the file content
280
+ # This specifically handles the indented </arg> case logic
281
+ if re.search(r'\n\s*$', actual_content):
282
+ actual_content = re.sub(r'\n\s*$', '', actual_content)
283
+
284
+ if actual_content:
285
+ self.context.emit_segment_content(actual_content)
286
+
287
+ self._marker_tail = ""
288
+ remainder = combined[idx_close + len(closing_tag):]
289
+ self._content_buffering = remainder
290
+ self._swallowing_remaining = True
291
+ self._handle_swallowing()
292
+ return
293
+
294
+ # Holdback logic
295
+ # We need to hold back enough to detect EITHER marker OR closing_tag + context
296
+ # </arg> (6) + \s + </tool> (7) ~= 15-20 chars holdback
297
+ # But to be safe against splitting </arguments>, let's hold back ~20 chars.
298
+ # Also need to account for __END_CONTENT__ (16 chars) + whitespace + </arg> (6) = ~25 chars
299
+
300
+ max_holdback = 35 # Safe buffer for regex lookahead
301
+
302
+ if len(combined) > max_holdback:
303
+ safe = combined[:-max_holdback]
304
+ if safe:
305
+ self.context.emit_segment_content(safe)
306
+ self._marker_tail = combined[-max_holdback:]
307
+ else:
308
+ self._marker_tail = combined
309
+
310
+ def _handle_swallowing(self) -> None:
311
+ """Consume stream until </tool> is found."""
312
+ from .text_state import TextState
313
+
314
+ # Add any new data to buffer
315
+ self._content_buffering += self.context.consume_remaining()
316
+
317
+ closing_tag = "</tool>"
318
+ idx = self._content_buffering.find(closing_tag)
319
+
320
+ if idx != -1:
321
+ # We found the end!
322
+ # We are done with this tool.
323
+
324
+ # Anything after </tool> belongs to the next state (TextState)
325
+ remainder = self._content_buffering[idx + len(closing_tag):]
326
+
327
+ self._on_segment_complete()
328
+ self.context.emit_segment_end()
329
+ if remainder:
330
+ # Rewind so the next state can parse the remainder (e.g., another tool tag).
331
+ self.context.rewind_by(len(remainder))
332
+ self.context.transition_to(TextState(self.context))
333
+ else:
334
+ # Nothing yet, keep swallowing (clearing buffer to avoid memory issues if valid)
335
+ # But we need to keep a holdback in case </tool> is split?
336
+ # </tool> is 7 chars.
337
+ holdback_len = len(closing_tag) - 1
338
+ if len(self._content_buffering) > holdback_len:
339
+ # Discard safe prefix
340
+ self._content_buffering = self._content_buffering[-holdback_len:]
341
+
342
+ def _on_segment_complete(self) -> None:
343
+ return None
@@ -0,0 +1,17 @@
1
+ """
2
+ Detection strategies for streaming parser.
3
+ """
4
+
5
+ from .base import DetectionStrategy
6
+ from .json_tool_strategy import JsonToolStrategy
7
+ from .sentinel_strategy import SentinelStrategy
8
+ from .xml_tag_strategy import XmlTagStrategy
9
+ from .registry import create_detection_strategies
10
+
11
+ __all__ = [
12
+ "DetectionStrategy",
13
+ "JsonToolStrategy",
14
+ "SentinelStrategy",
15
+ "XmlTagStrategy",
16
+ "create_detection_strategies",
17
+ ]
@@ -0,0 +1,24 @@
1
+ """
2
+ Detection strategy interfaces for streaming parser.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import Protocol, TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ..parser_context import ParserContext
10
+ from ..states.base_state import BaseState
11
+
12
+
13
+ class DetectionStrategy(Protocol):
14
+ """Protocol for detection strategies."""
15
+
16
+ name: str
17
+
18
+ def next_marker(self, context: "ParserContext", start_pos: int) -> int:
19
+ """Return next marker index or -1 if not applicable."""
20
+ ...
21
+
22
+ def create_state(self, context: "ParserContext") -> "BaseState":
23
+ """Create the state to handle parsing after this marker."""
24
+ ...
@@ -0,0 +1,26 @@
1
+ """
2
+ Detection strategy for JSON tool calls.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ..parser_context import ParserContext
10
+ from ..states.base_state import BaseState
11
+
12
+
13
+ class JsonToolStrategy:
14
+ name = "json_tool"
15
+
16
+ def next_marker(self, context: "ParserContext", start_pos: int) -> int:
17
+ if not context.parse_tool_calls:
18
+ return -1
19
+ next_curly = context.find("{", start_pos)
20
+ next_bracket = context.find("[", start_pos)
21
+ candidates = [idx for idx in (next_curly, next_bracket) if idx != -1]
22
+ return min(candidates) if candidates else -1
23
+
24
+ def create_state(self, context: "ParserContext") -> "BaseState":
25
+ from ..states.json_initialization_state import JsonInitializationState
26
+ return JsonInitializationState(context)
@@ -0,0 +1,28 @@
1
+ """
2
+ Registry for detection strategies.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import Dict, List
7
+
8
+ from .base import DetectionStrategy
9
+ from .json_tool_strategy import JsonToolStrategy
10
+ from .sentinel_strategy import SentinelStrategy
11
+ from .xml_tag_strategy import XmlTagStrategy
12
+
13
+
14
+ _STRATEGY_REGISTRY: Dict[str, DetectionStrategy] = {
15
+ "sentinel": SentinelStrategy(),
16
+ "xml_tag": XmlTagStrategy(),
17
+ "json_tool": JsonToolStrategy(),
18
+ }
19
+
20
+
21
+ def create_detection_strategies(strategy_order: List[str]) -> List[DetectionStrategy]:
22
+ """Create ordered detection strategies."""
23
+ strategies: List[DetectionStrategy] = []
24
+ for name in strategy_order:
25
+ strategy = _STRATEGY_REGISTRY.get(name)
26
+ if strategy:
27
+ strategies.append(strategy)
28
+ return strategies
@@ -0,0 +1,23 @@
1
+ """
2
+ Detection strategy for sentinel-formatted segments.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ from ..sentinel_format import START_MARKER
9
+
10
+ if TYPE_CHECKING:
11
+ from ..parser_context import ParserContext
12
+ from ..states.base_state import BaseState
13
+
14
+
15
+ class SentinelStrategy:
16
+ name = "sentinel"
17
+
18
+ def next_marker(self, context: "ParserContext", start_pos: int) -> int:
19
+ return context.find(START_MARKER, start_pos)
20
+
21
+ def create_state(self, context: "ParserContext") -> "BaseState":
22
+ from ..states.sentinel_initialization_state import SentinelInitializationState
23
+ return SentinelInitializationState(context)
@@ -0,0 +1,21 @@
1
+ """
2
+ Detection strategy for XML-like tags such as <file>, <bash>, <tool>, <!doctype>.
3
+ """
4
+ from __future__ import annotations
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ..parser_context import ParserContext
10
+ from ..states.base_state import BaseState
11
+
12
+
13
+ class XmlTagStrategy:
14
+ name = "xml_tag"
15
+
16
+ def next_marker(self, context: "ParserContext", start_pos: int) -> int:
17
+ return context.find("<", start_pos)
18
+
19
+ def create_state(self, context: "ParserContext") -> "BaseState":
20
+ from ..states.xml_tag_initialization_state import XmlTagInitializationState
21
+ return XmlTagInitializationState(context)
@@ -0,0 +1,167 @@
1
+ """
2
+ StreamScanner: A class to manage a string buffer and cursor position for sequential reading.
3
+
4
+ This encapsulates the logic of navigating a stream of text, preventing direct
5
+ manipulation of the cursor and buffer from multiple state classes.
6
+ """
7
+ from typing import Optional
8
+
9
+
10
+ class StreamScanner:
11
+ """
12
+ Manages a string buffer and a cursor position for sequential reading.
13
+
14
+ This class encapsulates navigation logic for streaming text, providing
15
+ a clean interface for state machine states to read characters without
16
+ directly manipulating the buffer.
17
+ """
18
+
19
+ def __init__(self, initial_buffer: str = ""):
20
+ """Initialize the scanner with an optional initial buffer."""
21
+ self._buffer: str = initial_buffer
22
+ self._pos: int = 0
23
+
24
+ def append(self, text: str) -> None:
25
+ """Append more text to the end of the buffer."""
26
+ self._buffer += text
27
+
28
+ def peek(self) -> Optional[str]:
29
+ """
30
+ Look at the character at the current cursor position without advancing.
31
+
32
+ Returns:
33
+ The character at the cursor, or None if at the end.
34
+ """
35
+ if self._pos < len(self._buffer):
36
+ return self._buffer[self._pos]
37
+ return None
38
+
39
+ def advance(self) -> None:
40
+ """Move the cursor forward by one position."""
41
+ if self.has_more_chars():
42
+ self._pos += 1
43
+
44
+ def advance_by(self, count: int) -> None:
45
+ """
46
+ Move the cursor forward by a specified number of positions.
47
+
48
+ Args:
49
+ count: The number of characters to advance.
50
+ """
51
+ self._pos = min(len(self._buffer), self._pos + count)
52
+
53
+ def has_more_chars(self) -> bool:
54
+ """
55
+ Check if there are more characters to read from the buffer.
56
+
57
+ Returns:
58
+ True if the cursor is not at the end of the buffer.
59
+ """
60
+ return self._pos < len(self._buffer)
61
+
62
+ def substring(self, start: int, end: Optional[int] = None) -> str:
63
+ """
64
+ Extract a substring from the buffer.
65
+
66
+ Args:
67
+ start: The starting index.
68
+ end: The ending index (exclusive). If None, reads to end of buffer.
69
+
70
+ Returns:
71
+ The extracted substring.
72
+ """
73
+ if end is None:
74
+ return self._buffer[start:]
75
+ return self._buffer[start:end]
76
+
77
+ def find(self, sub: str, start: Optional[int] = None) -> int:
78
+ """
79
+ Find a substring in the buffer starting from a position.
80
+
81
+ Args:
82
+ sub: The substring to search for.
83
+ start: Optional start index. Defaults to current position.
84
+
85
+ Returns:
86
+ The index of the substring, or -1 if not found.
87
+ """
88
+ if start is None:
89
+ start = self._pos
90
+ return self._buffer.find(sub, start)
91
+
92
+ def consume(self, count: int) -> str:
93
+ """
94
+ Consume a number of characters from the current position.
95
+
96
+ Args:
97
+ count: Number of characters to consume.
98
+
99
+ Returns:
100
+ The consumed substring.
101
+ """
102
+ if count <= 0:
103
+ return ""
104
+ end = min(len(self._buffer), self._pos + count)
105
+ result = self._buffer[self._pos:end]
106
+ self._pos = end
107
+ return result
108
+
109
+ def consume_remaining(self) -> str:
110
+ """
111
+ Consume all remaining characters in the buffer.
112
+
113
+ Returns:
114
+ The remaining substring from current position to end.
115
+ """
116
+ if self._pos >= len(self._buffer):
117
+ return ""
118
+ result = self._buffer[self._pos:]
119
+ self._pos = len(self._buffer)
120
+ return result
121
+
122
+ def get_position(self) -> int:
123
+ """
124
+ Return the current zero-based position of the cursor.
125
+
126
+ Returns:
127
+ The current cursor position.
128
+ """
129
+ return self._pos
130
+
131
+ def get_buffer_length(self) -> int:
132
+ """
133
+ Return the total length of the internal buffer.
134
+
135
+ Returns:
136
+ The length of the buffer.
137
+ """
138
+ return len(self._buffer)
139
+
140
+ def set_position(self, position: int) -> None:
141
+ """
142
+ Set the cursor to a specific position.
143
+
144
+ Args:
145
+ position: The new cursor position (clamped to valid range).
146
+ """
147
+ self._pos = max(0, min(len(self._buffer), position))
148
+
149
+ def compact(self, min_prefix: int = 65536) -> None:
150
+ """
151
+ Compact the buffer by dropping consumed prefix data.
152
+
153
+ If all data is consumed, clears the buffer entirely.
154
+ Otherwise, drops the consumed prefix when it exceeds min_prefix.
155
+
156
+ Args:
157
+ min_prefix: Minimum consumed prefix length to trigger compaction.
158
+ """
159
+ if self._pos == 0:
160
+ return
161
+ if self._pos >= len(self._buffer):
162
+ self._buffer = ""
163
+ self._pos = 0
164
+ return
165
+ if self._pos >= min_prefix:
166
+ self._buffer = self._buffer[self._pos:]
167
+ self._pos = 0