autobyteus 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/agent.py +15 -5
- autobyteus/agent/bootstrap_steps/__init__.py +3 -3
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +5 -59
- autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +1 -4
- autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +1 -3
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +16 -13
- autobyteus/agent/bootstrap_steps/working_context_snapshot_restore_step.py +38 -0
- autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +2 -4
- autobyteus/agent/context/agent_config.py +47 -20
- autobyteus/agent/context/agent_context.py +23 -18
- autobyteus/agent/context/agent_runtime_state.py +21 -19
- autobyteus/agent/events/__init__.py +16 -1
- autobyteus/agent/events/agent_events.py +43 -3
- autobyteus/agent/events/agent_input_event_queue_manager.py +79 -26
- autobyteus/agent/events/event_store.py +57 -0
- autobyteus/agent/events/notifiers.py +69 -59
- autobyteus/agent/events/worker_event_dispatcher.py +21 -64
- autobyteus/agent/factory/agent_factory.py +83 -6
- autobyteus/agent/handlers/__init__.py +2 -0
- autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +51 -34
- autobyteus/agent/handlers/bootstrap_event_handler.py +155 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +10 -0
- autobyteus/agent/handlers/lifecycle_event_logger.py +19 -11
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +10 -15
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +188 -48
- autobyteus/agent/handlers/tool_execution_approval_event_handler.py +0 -10
- autobyteus/agent/handlers/tool_invocation_request_event_handler.py +53 -48
- autobyteus/agent/handlers/tool_result_event_handler.py +7 -8
- autobyteus/agent/handlers/user_input_message_event_handler.py +10 -3
- autobyteus/agent/input_processor/memory_ingest_input_processor.py +44 -0
- autobyteus/agent/lifecycle/__init__.py +12 -0
- autobyteus/agent/lifecycle/base_processor.py +109 -0
- autobyteus/agent/lifecycle/events.py +35 -0
- autobyteus/agent/lifecycle/processor_definition.py +36 -0
- autobyteus/agent/lifecycle/processor_registry.py +106 -0
- autobyteus/agent/llm_request_assembler.py +98 -0
- autobyteus/agent/llm_response_processor/__init__.py +1 -8
- autobyteus/agent/message/context_file_type.py +1 -1
- autobyteus/agent/runtime/agent_runtime.py +29 -21
- autobyteus/agent/runtime/agent_worker.py +98 -19
- autobyteus/agent/shutdown_steps/__init__.py +2 -0
- autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +2 -0
- autobyteus/agent/shutdown_steps/tool_cleanup_step.py +58 -0
- autobyteus/agent/status/__init__.py +14 -0
- autobyteus/agent/status/manager.py +93 -0
- autobyteus/agent/status/status_deriver.py +96 -0
- autobyteus/agent/{phases/phase_enum.py → status/status_enum.py} +16 -16
- autobyteus/agent/status/status_update_utils.py +73 -0
- autobyteus/agent/streaming/__init__.py +52 -5
- autobyteus/agent/streaming/adapters/__init__.py +18 -0
- autobyteus/agent/streaming/adapters/invocation_adapter.py +184 -0
- autobyteus/agent/streaming/adapters/tool_call_parsing.py +163 -0
- autobyteus/agent/streaming/adapters/tool_syntax_registry.py +67 -0
- autobyteus/agent/streaming/agent_event_stream.py +3 -183
- autobyteus/agent/streaming/api_tool_call/__init__.py +16 -0
- autobyteus/agent/streaming/api_tool_call/file_content_streamer.py +56 -0
- autobyteus/agent/streaming/api_tool_call/json_string_field_extractor.py +175 -0
- autobyteus/agent/streaming/api_tool_call_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/events/__init__.py +6 -0
- autobyteus/agent/streaming/events/stream_event_payloads.py +284 -0
- autobyteus/agent/streaming/events/stream_events.py +141 -0
- autobyteus/agent/streaming/handlers/__init__.py +15 -0
- autobyteus/agent/streaming/handlers/api_tool_call_streaming_response_handler.py +303 -0
- autobyteus/agent/streaming/handlers/parsing_streaming_response_handler.py +107 -0
- autobyteus/agent/streaming/handlers/pass_through_streaming_response_handler.py +107 -0
- autobyteus/agent/streaming/handlers/streaming_handler_factory.py +177 -0
- autobyteus/agent/streaming/handlers/streaming_response_handler.py +58 -0
- autobyteus/agent/streaming/parser/__init__.py +61 -0
- autobyteus/agent/streaming/parser/event_emitter.py +181 -0
- autobyteus/agent/streaming/parser/events.py +4 -0
- autobyteus/agent/streaming/parser/invocation_adapter.py +4 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/__init__.py +19 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/base.py +32 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/default.py +34 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/gemini.py +31 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/openai.py +64 -0
- autobyteus/agent/streaming/parser/json_parsing_strategies/registry.py +75 -0
- autobyteus/agent/streaming/parser/parser_context.py +227 -0
- autobyteus/agent/streaming/parser/parser_factory.py +132 -0
- autobyteus/agent/streaming/parser/sentinel_format.py +7 -0
- autobyteus/agent/streaming/parser/state_factory.py +62 -0
- autobyteus/agent/streaming/parser/states/__init__.py +1 -0
- autobyteus/agent/streaming/parser/states/base_state.py +60 -0
- autobyteus/agent/streaming/parser/states/custom_xml_tag_run_bash_parsing_state.py +38 -0
- autobyteus/agent/streaming/parser/states/custom_xml_tag_write_file_parsing_state.py +55 -0
- autobyteus/agent/streaming/parser/states/delimited_content_state.py +146 -0
- autobyteus/agent/streaming/parser/states/json_initialization_state.py +144 -0
- autobyteus/agent/streaming/parser/states/json_tool_parsing_state.py +137 -0
- autobyteus/agent/streaming/parser/states/sentinel_content_state.py +30 -0
- autobyteus/agent/streaming/parser/states/sentinel_initialization_state.py +117 -0
- autobyteus/agent/streaming/parser/states/text_state.py +78 -0
- autobyteus/agent/streaming/parser/states/xml_patch_file_tool_parsing_state.py +328 -0
- autobyteus/agent/streaming/parser/states/xml_run_bash_tool_parsing_state.py +129 -0
- autobyteus/agent/streaming/parser/states/xml_tag_initialization_state.py +151 -0
- autobyteus/agent/streaming/parser/states/xml_tool_parsing_state.py +63 -0
- autobyteus/agent/streaming/parser/states/xml_write_file_tool_parsing_state.py +343 -0
- autobyteus/agent/streaming/parser/strategies/__init__.py +17 -0
- autobyteus/agent/streaming/parser/strategies/base.py +24 -0
- autobyteus/agent/streaming/parser/strategies/json_tool_strategy.py +26 -0
- autobyteus/agent/streaming/parser/strategies/registry.py +28 -0
- autobyteus/agent/streaming/parser/strategies/sentinel_strategy.py +23 -0
- autobyteus/agent/streaming/parser/strategies/xml_tag_strategy.py +21 -0
- autobyteus/agent/streaming/parser/stream_scanner.py +167 -0
- autobyteus/agent/streaming/parser/streaming_parser.py +212 -0
- autobyteus/agent/streaming/parser/tool_call_parsing.py +4 -0
- autobyteus/agent/streaming/parser/tool_constants.py +7 -0
- autobyteus/agent/streaming/parser/tool_syntax_registry.py +4 -0
- autobyteus/agent/streaming/parser/xml_tool_parsing_state_registry.py +55 -0
- autobyteus/agent/streaming/parsing_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/pass_through_streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/queue_streamer.py +3 -57
- autobyteus/agent/streaming/segments/__init__.py +5 -0
- autobyteus/agent/streaming/segments/segment_events.py +82 -0
- autobyteus/agent/streaming/stream_event_payloads.py +2 -223
- autobyteus/agent/streaming/stream_events.py +3 -140
- autobyteus/agent/streaming/streaming_handler_factory.py +4 -0
- autobyteus/agent/streaming/streaming_response_handler.py +4 -0
- autobyteus/agent/streaming/streams/__init__.py +5 -0
- autobyteus/agent/streaming/streams/agent_event_stream.py +197 -0
- autobyteus/agent/streaming/utils/__init__.py +5 -0
- autobyteus/agent/streaming/utils/queue_streamer.py +59 -0
- autobyteus/agent/system_prompt_processor/__init__.py +2 -0
- autobyteus/agent/system_prompt_processor/available_skills_processor.py +96 -0
- autobyteus/agent/system_prompt_processor/base_processor.py +1 -1
- autobyteus/agent/system_prompt_processor/processor_meta.py +15 -2
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +39 -58
- autobyteus/agent/token_budget.py +56 -0
- autobyteus/agent/tool_execution_result_processor/memory_ingest_tool_result_processor.py +29 -0
- autobyteus/agent/tool_invocation.py +16 -40
- autobyteus/agent/tool_invocation_preprocessor/__init__.py +9 -0
- autobyteus/agent/tool_invocation_preprocessor/base_preprocessor.py +45 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_definition.py +15 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_meta.py +33 -0
- autobyteus/agent/tool_invocation_preprocessor/processor_registry.py +60 -0
- autobyteus/agent/utils/wait_for_idle.py +12 -14
- autobyteus/agent/workspace/base_workspace.py +6 -27
- autobyteus/agent_team/agent_team.py +3 -3
- autobyteus/agent_team/agent_team_builder.py +1 -41
- autobyteus/agent_team/bootstrap_steps/__init__.py +0 -4
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +8 -18
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +4 -16
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +1 -2
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +4 -4
- autobyteus/agent_team/context/agent_team_config.py +6 -3
- autobyteus/agent_team/context/agent_team_context.py +25 -3
- autobyteus/agent_team/context/agent_team_runtime_state.py +9 -6
- autobyteus/agent_team/events/__init__.py +11 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +22 -9
- autobyteus/agent_team/events/agent_team_events.py +16 -0
- autobyteus/agent_team/events/event_store.py +57 -0
- autobyteus/agent_team/factory/agent_team_factory.py +8 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +18 -2
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +21 -5
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +17 -8
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +19 -4
- autobyteus/agent_team/runtime/agent_team_runtime.py +41 -10
- autobyteus/agent_team/runtime/agent_team_worker.py +69 -5
- autobyteus/agent_team/status/__init__.py +14 -0
- autobyteus/agent_team/status/agent_team_status.py +18 -0
- autobyteus/agent_team/status/agent_team_status_manager.py +33 -0
- autobyteus/agent_team/status/status_deriver.py +62 -0
- autobyteus/agent_team/status/status_update_utils.py +42 -0
- autobyteus/agent_team/streaming/__init__.py +2 -2
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +6 -6
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +4 -4
- autobyteus/agent_team/streaming/agent_team_stream_events.py +3 -3
- autobyteus/agent_team/system_prompt_processor/__init__.py +6 -0
- autobyteus/agent_team/system_prompt_processor/team_manifest_injector_processor.py +76 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +19 -0
- autobyteus/agent_team/utils/wait_for_idle.py +4 -4
- autobyteus/cli/agent_cli.py +18 -10
- autobyteus/cli/agent_team_tui/app.py +14 -11
- autobyteus/cli/agent_team_tui/state.py +13 -15
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +143 -36
- autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
- autobyteus/cli/agent_team_tui/widgets/shared.py +25 -25
- autobyteus/cli/cli_display.py +193 -44
- autobyteus/cli/workflow_tui/app.py +9 -10
- autobyteus/cli/workflow_tui/state.py +14 -16
- autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +15 -15
- autobyteus/cli/workflow_tui/widgets/focus_pane.py +137 -35
- autobyteus/cli/workflow_tui/widgets/renderables.py +1 -1
- autobyteus/cli/workflow_tui/widgets/shared.py +25 -25
- autobyteus/clients/autobyteus_client.py +94 -1
- autobyteus/events/event_types.py +11 -18
- autobyteus/llm/api/autobyteus_llm.py +33 -29
- autobyteus/llm/api/claude_llm.py +142 -36
- autobyteus/llm/api/gemini_llm.py +163 -59
- autobyteus/llm/api/grok_llm.py +1 -1
- autobyteus/llm/api/minimax_llm.py +26 -0
- autobyteus/llm/api/mistral_llm.py +113 -87
- autobyteus/llm/api/ollama_llm.py +9 -42
- autobyteus/llm/api/openai_compatible_llm.py +127 -91
- autobyteus/llm/api/openai_llm.py +3 -3
- autobyteus/llm/api/openai_responses_llm.py +324 -0
- autobyteus/llm/api/zhipu_llm.py +21 -2
- autobyteus/llm/autobyteus_provider.py +70 -60
- autobyteus/llm/base_llm.py +85 -81
- autobyteus/llm/converters/__init__.py +14 -0
- autobyteus/llm/converters/anthropic_tool_call_converter.py +37 -0
- autobyteus/llm/converters/gemini_tool_call_converter.py +57 -0
- autobyteus/llm/converters/mistral_tool_call_converter.py +37 -0
- autobyteus/llm/converters/openai_tool_call_converter.py +38 -0
- autobyteus/llm/extensions/base_extension.py +6 -12
- autobyteus/llm/extensions/token_usage_tracking_extension.py +45 -18
- autobyteus/llm/llm_factory.py +282 -204
- autobyteus/llm/lmstudio_provider.py +60 -49
- autobyteus/llm/models.py +35 -2
- autobyteus/llm/ollama_provider.py +60 -49
- autobyteus/llm/ollama_provider_resolver.py +0 -1
- autobyteus/llm/prompt_renderers/__init__.py +19 -0
- autobyteus/llm/prompt_renderers/anthropic_prompt_renderer.py +104 -0
- autobyteus/llm/prompt_renderers/autobyteus_prompt_renderer.py +19 -0
- autobyteus/llm/prompt_renderers/base_prompt_renderer.py +10 -0
- autobyteus/llm/prompt_renderers/gemini_prompt_renderer.py +63 -0
- autobyteus/llm/prompt_renderers/mistral_prompt_renderer.py +87 -0
- autobyteus/llm/prompt_renderers/ollama_prompt_renderer.py +51 -0
- autobyteus/llm/prompt_renderers/openai_chat_renderer.py +97 -0
- autobyteus/llm/prompt_renderers/openai_responses_renderer.py +101 -0
- autobyteus/llm/providers.py +1 -3
- autobyteus/llm/token_counter/claude_token_counter.py +56 -25
- autobyteus/llm/token_counter/mistral_token_counter.py +12 -8
- autobyteus/llm/token_counter/openai_token_counter.py +24 -5
- autobyteus/llm/token_counter/token_counter_factory.py +12 -5
- autobyteus/llm/utils/llm_config.py +6 -12
- autobyteus/llm/utils/media_payload_formatter.py +27 -20
- autobyteus/llm/utils/messages.py +55 -3
- autobyteus/llm/utils/response_types.py +3 -0
- autobyteus/llm/utils/tool_call_delta.py +31 -0
- autobyteus/memory/__init__.py +35 -0
- autobyteus/memory/compaction/__init__.py +9 -0
- autobyteus/memory/compaction/compaction_result.py +8 -0
- autobyteus/memory/compaction/compactor.py +89 -0
- autobyteus/memory/compaction/summarizer.py +11 -0
- autobyteus/memory/compaction_snapshot_builder.py +84 -0
- autobyteus/memory/memory_manager.py +205 -0
- autobyteus/memory/models/__init__.py +14 -0
- autobyteus/memory/models/episodic_item.py +41 -0
- autobyteus/memory/models/memory_types.py +7 -0
- autobyteus/memory/models/raw_trace_item.py +79 -0
- autobyteus/memory/models/semantic_item.py +41 -0
- autobyteus/memory/models/tool_interaction.py +20 -0
- autobyteus/memory/path_resolver.py +27 -0
- autobyteus/memory/policies/__init__.py +5 -0
- autobyteus/memory/policies/compaction_policy.py +16 -0
- autobyteus/memory/restore/__init__.py +1 -0
- autobyteus/memory/restore/working_context_snapshot_bootstrapper.py +61 -0
- autobyteus/memory/retrieval/__init__.py +7 -0
- autobyteus/memory/retrieval/memory_bundle.py +11 -0
- autobyteus/memory/retrieval/retriever.py +13 -0
- autobyteus/memory/store/__init__.py +9 -0
- autobyteus/memory/store/base_store.py +14 -0
- autobyteus/memory/store/file_store.py +98 -0
- autobyteus/memory/store/working_context_snapshot_store.py +28 -0
- autobyteus/memory/tool_interaction_builder.py +46 -0
- autobyteus/memory/turn_tracker.py +9 -0
- autobyteus/memory/working_context_snapshot.py +69 -0
- autobyteus/memory/working_context_snapshot_serializer.py +135 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
- autobyteus/multimedia/audio/api/gemini_audio_client.py +109 -16
- autobyteus/multimedia/audio/audio_client_factory.py +47 -9
- autobyteus/multimedia/audio/audio_model.py +2 -1
- autobyteus/multimedia/image/api/autobyteus_image_client.py +19 -5
- autobyteus/multimedia/image/api/gemini_image_client.py +39 -17
- autobyteus/multimedia/image/api/openai_image_client.py +125 -43
- autobyteus/multimedia/image/autobyteus_image_provider.py +2 -1
- autobyteus/multimedia/image/image_client_factory.py +47 -15
- autobyteus/multimedia/image/image_model.py +5 -2
- autobyteus/multimedia/providers.py +3 -2
- autobyteus/skills/loader.py +71 -0
- autobyteus/skills/model.py +11 -0
- autobyteus/skills/registry.py +70 -0
- autobyteus/task_management/tools/todo_tools/add_todo.py +2 -2
- autobyteus/task_management/tools/todo_tools/create_todo_list.py +2 -2
- autobyteus/task_management/tools/todo_tools/update_todo_status.py +2 -2
- autobyteus/tools/__init__.py +34 -47
- autobyteus/tools/base_tool.py +7 -0
- autobyteus/tools/file/__init__.py +2 -6
- autobyteus/tools/file/patch_file.py +149 -0
- autobyteus/tools/file/read_file.py +36 -5
- autobyteus/tools/file/write_file.py +4 -1
- autobyteus/tools/functional_tool.py +43 -6
- autobyteus/tools/mcp/__init__.py +2 -0
- autobyteus/tools/mcp/config_service.py +5 -1
- autobyteus/tools/mcp/server/__init__.py +2 -0
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +1 -1
- autobyteus/tools/mcp/server/websocket_managed_mcp_server.py +141 -0
- autobyteus/tools/mcp/server_instance_manager.py +8 -1
- autobyteus/tools/mcp/types.py +61 -0
- autobyteus/tools/multimedia/audio_tools.py +70 -17
- autobyteus/tools/multimedia/download_media_tool.py +18 -4
- autobyteus/tools/multimedia/image_tools.py +246 -62
- autobyteus/tools/operation_executor/journal_manager.py +107 -0
- autobyteus/tools/operation_executor/operation_event_buffer.py +57 -0
- autobyteus/tools/operation_executor/operation_event_producer.py +29 -0
- autobyteus/tools/operation_executor/operation_executor.py +58 -0
- autobyteus/tools/registry/tool_definition.py +43 -2
- autobyteus/tools/skill/load_skill.py +50 -0
- autobyteus/tools/terminal/__init__.py +45 -0
- autobyteus/tools/terminal/ansi_utils.py +32 -0
- autobyteus/tools/terminal/background_process_manager.py +233 -0
- autobyteus/tools/terminal/output_buffer.py +105 -0
- autobyteus/tools/terminal/prompt_detector.py +63 -0
- autobyteus/tools/terminal/pty_session.py +241 -0
- autobyteus/tools/terminal/session_factory.py +20 -0
- autobyteus/tools/terminal/terminal_session_manager.py +226 -0
- autobyteus/tools/terminal/tools/__init__.py +13 -0
- autobyteus/tools/terminal/tools/get_process_output.py +81 -0
- autobyteus/tools/terminal/tools/run_bash.py +109 -0
- autobyteus/tools/terminal/tools/start_background_process.py +104 -0
- autobyteus/tools/terminal/tools/stop_background_process.py +67 -0
- autobyteus/tools/terminal/types.py +54 -0
- autobyteus/tools/terminal/wsl_tmux_session.py +221 -0
- autobyteus/tools/terminal/wsl_utils.py +156 -0
- autobyteus/tools/transaction_management/backup_handler.py +48 -0
- autobyteus/tools/transaction_management/operation_lifecycle_manager.py +62 -0
- autobyteus/tools/usage/__init__.py +1 -2
- autobyteus/tools/usage/formatters/__init__.py +17 -1
- autobyteus/tools/usage/formatters/base_formatter.py +8 -0
- autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +2 -2
- autobyteus/tools/usage/formatters/mistral_json_schema_formatter.py +18 -0
- autobyteus/tools/usage/formatters/patch_file_xml_example_formatter.py +64 -0
- autobyteus/tools/usage/formatters/patch_file_xml_schema_formatter.py +31 -0
- autobyteus/tools/usage/formatters/run_bash_xml_example_formatter.py +32 -0
- autobyteus/tools/usage/formatters/run_bash_xml_schema_formatter.py +36 -0
- autobyteus/tools/usage/formatters/write_file_xml_example_formatter.py +53 -0
- autobyteus/tools/usage/formatters/write_file_xml_schema_formatter.py +31 -0
- autobyteus/tools/usage/providers/tool_manifest_provider.py +10 -10
- autobyteus/tools/usage/registries/__init__.py +1 -3
- autobyteus/tools/usage/registries/tool_formatting_registry.py +115 -8
- autobyteus/tools/usage/tool_schema_provider.py +51 -0
- autobyteus/tools/web/__init__.py +4 -0
- autobyteus/tools/web/read_url_tool.py +80 -0
- autobyteus/utils/diff_utils.py +271 -0
- autobyteus/utils/download_utils.py +109 -0
- autobyteus/utils/file_utils.py +57 -2
- autobyteus/utils/gemini_helper.py +64 -0
- autobyteus/utils/gemini_model_mapping.py +71 -0
- autobyteus/utils/llm_output_formatter.py +75 -0
- autobyteus/utils/tool_call_format.py +36 -0
- autobyteus/workflow/agentic_workflow.py +3 -3
- autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +2 -2
- autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +3 -9
- autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +6 -6
- autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +2 -2
- autobyteus/workflow/context/workflow_context.py +3 -3
- autobyteus/workflow/context/workflow_runtime_state.py +5 -5
- autobyteus/workflow/events/workflow_event_dispatcher.py +5 -5
- autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +3 -3
- autobyteus/workflow/handlers/process_user_message_event_handler.py +5 -5
- autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +2 -2
- autobyteus/workflow/runtime/workflow_runtime.py +8 -8
- autobyteus/workflow/runtime/workflow_worker.py +3 -3
- autobyteus/workflow/status/__init__.py +11 -0
- autobyteus/workflow/status/workflow_status.py +19 -0
- autobyteus/workflow/status/workflow_status_manager.py +48 -0
- autobyteus/workflow/streaming/__init__.py +2 -2
- autobyteus/workflow/streaming/workflow_event_notifier.py +7 -7
- autobyteus/workflow/streaming/workflow_stream_event_payloads.py +4 -4
- autobyteus/workflow/streaming/workflow_stream_events.py +3 -3
- autobyteus/workflow/utils/wait_for_idle.py +4 -4
- autobyteus-1.3.0.dist-info/METADATA +293 -0
- autobyteus-1.3.0.dist-info/RECORD +606 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/WHEEL +1 -1
- {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/top_level.txt +0 -1
- autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +0 -57
- autobyteus/agent/hooks/__init__.py +0 -16
- autobyteus/agent/hooks/base_phase_hook.py +0 -78
- autobyteus/agent/hooks/hook_definition.py +0 -36
- autobyteus/agent/hooks/hook_meta.py +0 -37
- autobyteus/agent/hooks/hook_registry.py +0 -106
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +0 -103
- autobyteus/agent/phases/__init__.py +0 -18
- autobyteus/agent/phases/discover.py +0 -53
- autobyteus/agent/phases/manager.py +0 -265
- autobyteus/agent/phases/transition_decorator.py +0 -40
- autobyteus/agent/phases/transition_info.py +0 -33
- autobyteus/agent/remote_agent.py +0 -244
- autobyteus/agent/workspace/workspace_definition.py +0 -36
- autobyteus/agent/workspace/workspace_meta.py +0 -37
- autobyteus/agent/workspace/workspace_registry.py +0 -72
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +0 -25
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +0 -85
- autobyteus/agent_team/phases/__init__.py +0 -11
- autobyteus/agent_team/phases/agent_team_operational_phase.py +0 -19
- autobyteus/agent_team/phases/agent_team_phase_manager.py +0 -48
- autobyteus/llm/api/bedrock_llm.py +0 -92
- autobyteus/llm/api/groq_llm.py +0 -94
- autobyteus/llm/api/nvidia_llm.py +0 -108
- autobyteus/llm/utils/token_pricing_config.py +0 -87
- autobyteus/rpc/__init__.py +0 -73
- autobyteus/rpc/client/__init__.py +0 -17
- autobyteus/rpc/client/abstract_client_connection.py +0 -124
- autobyteus/rpc/client/client_connection_manager.py +0 -153
- autobyteus/rpc/client/sse_client_connection.py +0 -306
- autobyteus/rpc/client/stdio_client_connection.py +0 -280
- autobyteus/rpc/config/__init__.py +0 -13
- autobyteus/rpc/config/agent_server_config.py +0 -153
- autobyteus/rpc/config/agent_server_registry.py +0 -152
- autobyteus/rpc/hosting.py +0 -244
- autobyteus/rpc/protocol.py +0 -244
- autobyteus/rpc/server/__init__.py +0 -20
- autobyteus/rpc/server/agent_server_endpoint.py +0 -181
- autobyteus/rpc/server/base_method_handler.py +0 -40
- autobyteus/rpc/server/method_handlers.py +0 -259
- autobyteus/rpc/server/sse_server_handler.py +0 -182
- autobyteus/rpc/server/stdio_server_handler.py +0 -151
- autobyteus/rpc/server_main.py +0 -198
- autobyteus/rpc/transport_type.py +0 -13
- autobyteus/tools/bash/__init__.py +0 -2
- autobyteus/tools/bash/bash_executor.py +0 -100
- autobyteus/tools/browser/__init__.py +0 -2
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +0 -75
- autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +0 -30
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +0 -154
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +0 -89
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +0 -107
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +0 -14
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +0 -26
- autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +0 -14
- autobyteus/tools/browser/session_aware/shared_browser_session.py +0 -11
- autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +0 -25
- autobyteus/tools/browser/session_aware/web_element_action.py +0 -20
- autobyteus/tools/browser/standalone/__init__.py +0 -6
- autobyteus/tools/browser/standalone/factory/__init__.py +0 -0
- autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +0 -25
- autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +0 -14
- autobyteus/tools/browser/standalone/navigate_to.py +0 -84
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -101
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -169
- autobyteus/tools/browser/standalone/webpage_reader.py +0 -105
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -105
- autobyteus/tools/file/edit_file.py +0 -200
- autobyteus/tools/file/list_directory.py +0 -168
- autobyteus/tools/file/search_files.py +0 -188
- autobyteus/tools/timer.py +0 -175
- autobyteus/tools/usage/parsers/__init__.py +0 -22
- autobyteus/tools/usage/parsers/_json_extractor.py +0 -99
- autobyteus/tools/usage/parsers/_string_decoders.py +0 -18
- autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +0 -10
- autobyteus/tools/usage/parsers/base_parser.py +0 -41
- autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +0 -83
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +0 -316
- autobyteus/tools/usage/parsers/exceptions.py +0 -13
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +0 -77
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +0 -149
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +0 -59
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +0 -62
- autobyteus/workflow/phases/__init__.py +0 -11
- autobyteus/workflow/phases/workflow_operational_phase.py +0 -19
- autobyteus/workflow/phases/workflow_phase_manager.py +0 -48
- autobyteus-1.2.1.dist-info/METADATA +0 -205
- autobyteus-1.2.1.dist-info/RECORD +0 -511
- examples/__init__.py +0 -1
- examples/agent_team/__init__.py +0 -1
- examples/discover_phase_transitions.py +0 -104
- examples/run_agentic_software_engineer.py +0 -239
- examples/run_browser_agent.py +0 -262
- examples/run_google_slides_agent.py +0 -287
- examples/run_mcp_browser_client.py +0 -174
- examples/run_mcp_google_slides_client.py +0 -270
- examples/run_mcp_list_tools.py +0 -189
- examples/run_poem_writer.py +0 -284
- examples/run_sqlite_agent.py +0 -295
- /autobyteus/{tools/browser/session_aware → skills}/__init__.py +0 -0
- /autobyteus/tools/{browser/session_aware/factory → skill}/__init__.py +0 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PTY Session wrapper for stateful terminal sessions.
|
|
3
|
+
|
|
4
|
+
This module provides a pseudo-terminal (PTY) abstraction that spawns
|
|
5
|
+
a persistent bash shell, enabling stateful terminal operations where
|
|
6
|
+
directory changes, environment variables, and shell state persist
|
|
7
|
+
across commands.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import fcntl
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import pty
|
|
15
|
+
import select
|
|
16
|
+
import signal
|
|
17
|
+
import struct
|
|
18
|
+
import termios
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PtySession:
|
|
25
|
+
"""PTY session implementation using the pty module.
|
|
26
|
+
|
|
27
|
+
Spawns a persistent bash shell that maintains state across commands.
|
|
28
|
+
|
|
29
|
+
For testing, create a MockPtySession with the same interface (duck typing).
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
session_id: Unique identifier for this session.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, session_id: str):
|
|
36
|
+
"""Initialize a PTY session.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
session_id: Unique identifier for this session.
|
|
40
|
+
"""
|
|
41
|
+
self._session_id = session_id
|
|
42
|
+
self._master_fd: Optional[int] = None
|
|
43
|
+
self._pid: Optional[int] = None
|
|
44
|
+
self._closed = False
|
|
45
|
+
self._cwd: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def session_id(self) -> str:
|
|
49
|
+
"""Unique session identifier."""
|
|
50
|
+
return self._session_id
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def is_alive(self) -> bool:
|
|
54
|
+
"""Check if the shell process is still running."""
|
|
55
|
+
if self._pid is None or self._closed:
|
|
56
|
+
return False
|
|
57
|
+
try:
|
|
58
|
+
# Check if process exists without waiting
|
|
59
|
+
pid, status = os.waitpid(self._pid, os.WNOHANG)
|
|
60
|
+
if pid == 0:
|
|
61
|
+
return True # Process still running
|
|
62
|
+
return False # Process exited
|
|
63
|
+
except ChildProcessError:
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
async def start(self, cwd: str) -> None:
|
|
67
|
+
"""Start a bash shell in a PTY.
|
|
68
|
+
|
|
69
|
+
Uses fork/exec to create a child process with a pseudo-terminal.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
cwd: Working directory for the shell.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
RuntimeError: If session is already started.
|
|
76
|
+
OSError: If fork or PTY creation fails.
|
|
77
|
+
"""
|
|
78
|
+
if self._master_fd is not None:
|
|
79
|
+
raise RuntimeError("Session already started")
|
|
80
|
+
|
|
81
|
+
self._cwd = cwd
|
|
82
|
+
|
|
83
|
+
# Create pseudo-terminal
|
|
84
|
+
master_fd, slave_fd = pty.openpty()
|
|
85
|
+
|
|
86
|
+
pid = os.fork()
|
|
87
|
+
|
|
88
|
+
if pid == 0:
|
|
89
|
+
# Child process
|
|
90
|
+
try:
|
|
91
|
+
os.close(master_fd)
|
|
92
|
+
os.setsid()
|
|
93
|
+
|
|
94
|
+
# Make slave the controlling terminal
|
|
95
|
+
fcntl.ioctl(slave_fd, termios.TIOCSCTTY, 0)
|
|
96
|
+
|
|
97
|
+
# Redirect stdin/stdout/stderr to slave
|
|
98
|
+
os.dup2(slave_fd, 0)
|
|
99
|
+
os.dup2(slave_fd, 1)
|
|
100
|
+
os.dup2(slave_fd, 2)
|
|
101
|
+
|
|
102
|
+
if slave_fd > 2:
|
|
103
|
+
os.close(slave_fd)
|
|
104
|
+
|
|
105
|
+
# Set environment variables for better UX
|
|
106
|
+
os.environ['TERM'] = 'xterm-256color'
|
|
107
|
+
# Simple prompt for easier detection
|
|
108
|
+
os.environ['PS1'] = r'\w $ '
|
|
109
|
+
|
|
110
|
+
# Change to working directory
|
|
111
|
+
os.chdir(cwd)
|
|
112
|
+
|
|
113
|
+
# Execute bash with minimal startup
|
|
114
|
+
os.execlp('bash', 'bash', '--norc', '--noprofile', '-i')
|
|
115
|
+
except Exception as e:
|
|
116
|
+
logger.error(f"Child process error: {e}")
|
|
117
|
+
os._exit(1)
|
|
118
|
+
else:
|
|
119
|
+
# Parent process
|
|
120
|
+
os.close(slave_fd)
|
|
121
|
+
self._master_fd = master_fd
|
|
122
|
+
self._pid = pid
|
|
123
|
+
|
|
124
|
+
# Set non-blocking mode
|
|
125
|
+
flags = fcntl.fcntl(master_fd, fcntl.F_GETFL)
|
|
126
|
+
fcntl.fcntl(master_fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
|
|
127
|
+
|
|
128
|
+
# Give bash a moment to start
|
|
129
|
+
await asyncio.sleep(0.1)
|
|
130
|
+
|
|
131
|
+
logger.info(f"Started PTY session {self._session_id} (pid={pid}) in {cwd}")
|
|
132
|
+
|
|
133
|
+
async def write(self, data: bytes) -> None:
|
|
134
|
+
"""Write data to the PTY master.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
data: Bytes to write to the terminal.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
RuntimeError: If session is closed or not started.
|
|
141
|
+
"""
|
|
142
|
+
if self._closed:
|
|
143
|
+
raise RuntimeError("Session is closed")
|
|
144
|
+
if self._master_fd is None:
|
|
145
|
+
raise RuntimeError("Session not started")
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
os.write(self._master_fd, data)
|
|
149
|
+
except OSError as e:
|
|
150
|
+
logger.error(f"Error writing to PTY: {e}")
|
|
151
|
+
raise
|
|
152
|
+
|
|
153
|
+
async def read(self, timeout: float = 0.1) -> Optional[bytes]:
|
|
154
|
+
"""Read available data from the PTY.
|
|
155
|
+
|
|
156
|
+
Uses select for non-blocking read with timeout.
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
timeout: Maximum time to wait for data in seconds.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Bytes read from PTY, or None if no data available.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
RuntimeError: If session not started.
|
|
166
|
+
"""
|
|
167
|
+
if self._closed:
|
|
168
|
+
return None
|
|
169
|
+
if self._master_fd is None:
|
|
170
|
+
raise RuntimeError("Session not started")
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
# Wait for data with timeout
|
|
174
|
+
readable, _, _ = select.select([self._master_fd], [], [], timeout)
|
|
175
|
+
|
|
176
|
+
if not readable:
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
# Read available data
|
|
180
|
+
data = os.read(self._master_fd, 4096)
|
|
181
|
+
return data if data else None
|
|
182
|
+
|
|
183
|
+
except OSError as e:
|
|
184
|
+
if e.errno == 5: # EIO - terminal closed
|
|
185
|
+
return None
|
|
186
|
+
logger.error(f"Error reading from PTY: {e}")
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
def resize(self, rows: int, cols: int) -> None:
|
|
190
|
+
"""Resize the PTY terminal.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
rows: Number of rows.
|
|
194
|
+
cols: Number of columns.
|
|
195
|
+
"""
|
|
196
|
+
if self._master_fd is None:
|
|
197
|
+
raise RuntimeError("Session not started")
|
|
198
|
+
if self._closed:
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
winsize = struct.pack('HHHH', rows, cols, 0, 0)
|
|
203
|
+
fcntl.ioctl(self._master_fd, termios.TIOCSWINSZ, winsize)
|
|
204
|
+
logger.debug(f"Resized PTY {self._session_id} to {rows}x{cols}")
|
|
205
|
+
except OSError as e:
|
|
206
|
+
logger.error(f"Error resizing PTY: {e}")
|
|
207
|
+
|
|
208
|
+
async def close(self) -> None:
|
|
209
|
+
"""Close the PTY and terminate the shell.
|
|
210
|
+
|
|
211
|
+
Sends SIGTERM first, then SIGKILL if necessary.
|
|
212
|
+
"""
|
|
213
|
+
if self._closed:
|
|
214
|
+
return
|
|
215
|
+
|
|
216
|
+
self._closed = True
|
|
217
|
+
|
|
218
|
+
if self._master_fd is not None:
|
|
219
|
+
try:
|
|
220
|
+
os.close(self._master_fd)
|
|
221
|
+
except OSError:
|
|
222
|
+
pass
|
|
223
|
+
self._master_fd = None
|
|
224
|
+
|
|
225
|
+
if self._pid is not None:
|
|
226
|
+
try:
|
|
227
|
+
# Try graceful termination first
|
|
228
|
+
os.kill(self._pid, signal.SIGTERM)
|
|
229
|
+
# Wait briefly
|
|
230
|
+
await asyncio.sleep(0.1)
|
|
231
|
+
# Force kill if still running
|
|
232
|
+
try:
|
|
233
|
+
os.kill(self._pid, signal.SIGKILL)
|
|
234
|
+
except ProcessLookupError:
|
|
235
|
+
pass # Already dead
|
|
236
|
+
os.waitpid(self._pid, 0)
|
|
237
|
+
except (ProcessLookupError, ChildProcessError):
|
|
238
|
+
pass
|
|
239
|
+
self._pid = None
|
|
240
|
+
|
|
241
|
+
logger.info(f"Closed PTY session {self._session_id}")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session factory selection for terminal backends.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _is_windows() -> bool:
|
|
9
|
+
"""Return True if running on Windows."""
|
|
10
|
+
return os.name == "nt"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_default_session_factory():
|
|
14
|
+
"""Return the default PTY session class for the current platform."""
|
|
15
|
+
if _is_windows():
|
|
16
|
+
from autobyteus.tools.terminal.wsl_tmux_session import WslTmuxSession
|
|
17
|
+
return WslTmuxSession
|
|
18
|
+
|
|
19
|
+
from autobyteus.tools.terminal.pty_session import PtySession
|
|
20
|
+
return PtySession
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Terminal Session Manager for executing commands in a stateful PTY.
|
|
3
|
+
|
|
4
|
+
Provides high-level command execution with prompt detection and
|
|
5
|
+
timeout handling.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import logging
|
|
12
|
+
import uuid
|
|
13
|
+
from typing import Callable, Optional
|
|
14
|
+
|
|
15
|
+
from autobyteus.tools.terminal.output_buffer import OutputBuffer
|
|
16
|
+
from autobyteus.tools.terminal.prompt_detector import PromptDetector
|
|
17
|
+
from autobyteus.tools.terminal.session_factory import get_default_session_factory
|
|
18
|
+
from autobyteus.tools.terminal.types import TerminalResult
|
|
19
|
+
from autobyteus.tools.terminal.ansi_utils import strip_ansi_codes
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TerminalSessionManager:
|
|
25
|
+
"""Manages the main stateful terminal session for an agent.
|
|
26
|
+
|
|
27
|
+
Provides command execution with automatic prompt detection and
|
|
28
|
+
timeout handling. The underlying PTY maintains state between
|
|
29
|
+
commands (cd, environment variables, etc).
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
current_session: The active PTY session if started.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
session_factory: Callable[[str], object] = None,
|
|
38
|
+
prompt_detector: PromptDetector = None
|
|
39
|
+
):
|
|
40
|
+
"""Initialize the terminal session manager.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
session_factory: Factory function to create PtySession instances.
|
|
44
|
+
Defaults to PtySession constructor.
|
|
45
|
+
prompt_detector: PromptDetector instance for command completion.
|
|
46
|
+
Defaults to standard PromptDetector.
|
|
47
|
+
"""
|
|
48
|
+
self._session_factory = session_factory or get_default_session_factory()
|
|
49
|
+
self._prompt_detector = prompt_detector or PromptDetector()
|
|
50
|
+
self._session: Optional[object] = None
|
|
51
|
+
self._output_buffer = OutputBuffer()
|
|
52
|
+
self._cwd: Optional[str] = None
|
|
53
|
+
self._started = False
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def current_session(self) -> Optional[object]:
|
|
57
|
+
"""The active PTY session if started."""
|
|
58
|
+
return self._session
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def is_started(self) -> bool:
|
|
62
|
+
"""True if session has been started."""
|
|
63
|
+
return self._started and self._session is not None
|
|
64
|
+
|
|
65
|
+
async def ensure_started(self, cwd: str) -> None:
|
|
66
|
+
"""Ensure the session is started.
|
|
67
|
+
|
|
68
|
+
Creates a new session if not already started.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
cwd: Working directory for the shell.
|
|
72
|
+
"""
|
|
73
|
+
if self._session is not None and self._session.is_alive:
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
# Clean up dead session if any
|
|
77
|
+
if self._session is not None:
|
|
78
|
+
await self._session.close()
|
|
79
|
+
|
|
80
|
+
session_id = f"term-{uuid.uuid4().hex[:8]}"
|
|
81
|
+
self._session = self._session_factory(session_id)
|
|
82
|
+
await self._session.start(cwd)
|
|
83
|
+
self._cwd = cwd
|
|
84
|
+
self._started = True
|
|
85
|
+
|
|
86
|
+
# Drain initial prompt output
|
|
87
|
+
await self._drain_output(timeout=0.5)
|
|
88
|
+
self._output_buffer.clear()
|
|
89
|
+
|
|
90
|
+
logger.info(f"Terminal session started in {cwd}")
|
|
91
|
+
|
|
92
|
+
async def execute_command(
|
|
93
|
+
self,
|
|
94
|
+
command: str,
|
|
95
|
+
timeout_seconds: int = 30
|
|
96
|
+
) -> TerminalResult:
|
|
97
|
+
"""Execute a command and wait for completion.
|
|
98
|
+
|
|
99
|
+
Writes the command to the PTY and waits for the prompt to
|
|
100
|
+
return, indicating command completion.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
command: The bash command to execute.
|
|
104
|
+
timeout_seconds: Maximum time to wait for completion.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
TerminalResult with captured output.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
RuntimeError: If session is not started.
|
|
111
|
+
"""
|
|
112
|
+
if self._session is None:
|
|
113
|
+
raise RuntimeError("Session not started. Call ensure_started first.")
|
|
114
|
+
|
|
115
|
+
# Clear buffer for this command
|
|
116
|
+
self._output_buffer.clear()
|
|
117
|
+
|
|
118
|
+
# Ensure command ends with newline
|
|
119
|
+
if not command.endswith('\n'):
|
|
120
|
+
command += '\n'
|
|
121
|
+
|
|
122
|
+
# Write command
|
|
123
|
+
await self._session.write(command.encode('utf-8'))
|
|
124
|
+
|
|
125
|
+
# Wait for prompt with timeout
|
|
126
|
+
timed_out = False
|
|
127
|
+
start_time = asyncio.get_event_loop().time()
|
|
128
|
+
|
|
129
|
+
while True:
|
|
130
|
+
elapsed = asyncio.get_event_loop().time() - start_time
|
|
131
|
+
if elapsed >= timeout_seconds:
|
|
132
|
+
timed_out = True
|
|
133
|
+
logger.warning(f"Command timed out after {timeout_seconds}s: {command.strip()}")
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
# Read available output
|
|
137
|
+
try:
|
|
138
|
+
data = await self._session.read(timeout=0.1)
|
|
139
|
+
if data:
|
|
140
|
+
self._output_buffer.append(data)
|
|
141
|
+
|
|
142
|
+
# Check if prompt returned
|
|
143
|
+
current_output = self._output_buffer.get_all()
|
|
144
|
+
if self._prompt_detector.check(current_output):
|
|
145
|
+
break
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.error(f"Error reading from PTY: {e}")
|
|
148
|
+
break
|
|
149
|
+
|
|
150
|
+
# Get captured output and strip ANSI escape codes
|
|
151
|
+
output = self._output_buffer.get_all()
|
|
152
|
+
clean_output = strip_ansi_codes(output)
|
|
153
|
+
|
|
154
|
+
# Try to extract exit code if not timed out
|
|
155
|
+
exit_code = None
|
|
156
|
+
if not timed_out:
|
|
157
|
+
exit_code = await self._get_exit_code()
|
|
158
|
+
|
|
159
|
+
return TerminalResult(
|
|
160
|
+
stdout=clean_output,
|
|
161
|
+
stderr="", # PTY mixes stdout/stderr
|
|
162
|
+
exit_code=exit_code,
|
|
163
|
+
timed_out=timed_out
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
async def _get_exit_code(self) -> Optional[int]:
|
|
167
|
+
"""Try to get the exit code of the last command.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Exit code if successfully retrieved, None otherwise.
|
|
171
|
+
"""
|
|
172
|
+
try:
|
|
173
|
+
# Clear buffer
|
|
174
|
+
self._output_buffer.clear()
|
|
175
|
+
|
|
176
|
+
# Echo the exit code
|
|
177
|
+
await self._session.write(b"echo $?\n")
|
|
178
|
+
|
|
179
|
+
# Wait for output
|
|
180
|
+
await asyncio.sleep(0.2)
|
|
181
|
+
await self._drain_output(timeout=0.3)
|
|
182
|
+
|
|
183
|
+
output = self._output_buffer.get_all()
|
|
184
|
+
output = strip_ansi_codes(output)
|
|
185
|
+
|
|
186
|
+
# Parse the exit code from output
|
|
187
|
+
lines = output.strip().split('\n')
|
|
188
|
+
for line in lines:
|
|
189
|
+
line = line.strip()
|
|
190
|
+
if line.isdigit():
|
|
191
|
+
return int(line)
|
|
192
|
+
|
|
193
|
+
return None
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.debug(f"Failed to get exit code: {e}")
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
async def _drain_output(self, timeout: float = 0.5) -> None:
|
|
199
|
+
"""Read and buffer all available output.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
timeout: Maximum time to wait for more output.
|
|
203
|
+
"""
|
|
204
|
+
if self._session is None:
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
start = asyncio.get_event_loop().time()
|
|
208
|
+
while asyncio.get_event_loop().time() - start < timeout:
|
|
209
|
+
try:
|
|
210
|
+
data = await self._session.read(timeout=0.05)
|
|
211
|
+
if data:
|
|
212
|
+
self._output_buffer.append(data)
|
|
213
|
+
else:
|
|
214
|
+
# No more data immediately available
|
|
215
|
+
await asyncio.sleep(0.05)
|
|
216
|
+
except Exception:
|
|
217
|
+
break
|
|
218
|
+
|
|
219
|
+
async def close(self) -> None:
|
|
220
|
+
"""Close the terminal session."""
|
|
221
|
+
if self._session is not None:
|
|
222
|
+
await self._session.close()
|
|
223
|
+
self._session = None
|
|
224
|
+
self._started = False
|
|
225
|
+
self._output_buffer.clear()
|
|
226
|
+
logger.info("Terminal session closed")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Terminal tools package - LLM-facing tool functions."""
|
|
2
|
+
|
|
3
|
+
from autobyteus.tools.terminal.tools.run_bash import run_bash
|
|
4
|
+
from autobyteus.tools.terminal.tools.start_background_process import start_background_process
|
|
5
|
+
from autobyteus.tools.terminal.tools.get_process_output import get_process_output
|
|
6
|
+
from autobyteus.tools.terminal.tools.stop_background_process import stop_background_process
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"run_bash",
|
|
10
|
+
"start_background_process",
|
|
11
|
+
"get_process_output",
|
|
12
|
+
"stop_background_process",
|
|
13
|
+
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
get_process_output tool - Read output from background processes.
|
|
3
|
+
|
|
4
|
+
Returns recent output from a background process started with
|
|
5
|
+
start_background_process.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import TYPE_CHECKING, Optional
|
|
10
|
+
|
|
11
|
+
from autobyteus.tools import tool
|
|
12
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from autobyteus.agent.context import AgentContext
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _get_background_manager(context: Optional['AgentContext']):
|
|
21
|
+
"""Get the background process manager for this agent."""
|
|
22
|
+
from autobyteus.tools.terminal.background_process_manager import BackgroundProcessManager
|
|
23
|
+
|
|
24
|
+
if context is None:
|
|
25
|
+
if not hasattr(_get_background_manager, '_default_manager'):
|
|
26
|
+
_get_background_manager._default_manager = BackgroundProcessManager()
|
|
27
|
+
return _get_background_manager._default_manager
|
|
28
|
+
|
|
29
|
+
if not hasattr(context, '_background_process_manager'):
|
|
30
|
+
context._background_process_manager = BackgroundProcessManager()
|
|
31
|
+
|
|
32
|
+
return context._background_process_manager
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@tool(name="get_process_output", category=ToolCategory.SYSTEM)
|
|
36
|
+
async def get_process_output(
|
|
37
|
+
context: Optional['AgentContext'],
|
|
38
|
+
process_id: str,
|
|
39
|
+
lines: int = 100
|
|
40
|
+
) -> dict:
|
|
41
|
+
"""
|
|
42
|
+
Get recent output from a background process.
|
|
43
|
+
|
|
44
|
+
Use this to check what a background process (started with
|
|
45
|
+
start_background_process) is outputting. This is useful to:
|
|
46
|
+
- Verify a server started successfully
|
|
47
|
+
- Check for errors in the output
|
|
48
|
+
- Monitor progress of a long-running task
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
process_id: ID returned by start_background_process.
|
|
52
|
+
lines: Number of recent lines to return (default 100).
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
dict with:
|
|
56
|
+
- output: Recent output lines from the process
|
|
57
|
+
- is_running: True if process is still running
|
|
58
|
+
- process_id: The process identifier
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
- After starting "yarn dev", check output to verify server is ready
|
|
62
|
+
- Check for compilation errors in build output
|
|
63
|
+
- Monitor test runner output
|
|
64
|
+
"""
|
|
65
|
+
manager = _get_background_manager(context)
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
result = manager.get_output(process_id, lines)
|
|
69
|
+
return {
|
|
70
|
+
"output": result.output,
|
|
71
|
+
"is_running": result.is_running,
|
|
72
|
+
"process_id": result.process_id
|
|
73
|
+
}
|
|
74
|
+
except KeyError:
|
|
75
|
+
logger.warning(f"Process not found: {process_id}")
|
|
76
|
+
return {
|
|
77
|
+
"output": "",
|
|
78
|
+
"is_running": False,
|
|
79
|
+
"process_id": process_id,
|
|
80
|
+
"error": f"Process '{process_id}' not found. It may have already stopped or never existed."
|
|
81
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
run_bash tool - Execute commands in a stateful terminal.
|
|
3
|
+
|
|
4
|
+
This tool replaces the stateless bash_executor with a PTY-based
|
|
5
|
+
implementation that maintains shell state (cd, environment variables).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import TYPE_CHECKING, Optional
|
|
10
|
+
|
|
11
|
+
from autobyteus.tools import tool
|
|
12
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
13
|
+
from autobyteus.tools.terminal.types import TerminalResult
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from autobyteus.agent.context import AgentContext
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_terminal_manager(context: Optional['AgentContext']):
|
|
22
|
+
"""Get or create the terminal session manager for this agent.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
context: The agent context.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
TerminalSessionManager instance.
|
|
29
|
+
"""
|
|
30
|
+
from autobyteus.tools.terminal.terminal_session_manager import TerminalSessionManager
|
|
31
|
+
|
|
32
|
+
if context is None:
|
|
33
|
+
# Fallback for non-agent use
|
|
34
|
+
if not hasattr(_get_terminal_manager, '_default_manager'):
|
|
35
|
+
_get_terminal_manager._default_manager = TerminalSessionManager()
|
|
36
|
+
return _get_terminal_manager._default_manager
|
|
37
|
+
|
|
38
|
+
# Store manager in context for per-agent isolation
|
|
39
|
+
if not hasattr(context, '_terminal_session_manager'):
|
|
40
|
+
context._terminal_session_manager = TerminalSessionManager()
|
|
41
|
+
|
|
42
|
+
return context._terminal_session_manager
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_cwd(context: Optional['AgentContext']) -> str:
|
|
46
|
+
"""Get the working directory for the terminal.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
context: The agent context.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Working directory path.
|
|
53
|
+
"""
|
|
54
|
+
import tempfile
|
|
55
|
+
|
|
56
|
+
if context and hasattr(context, 'workspace') and context.workspace:
|
|
57
|
+
try:
|
|
58
|
+
base_path = context.workspace.get_base_path()
|
|
59
|
+
if base_path and isinstance(base_path, str):
|
|
60
|
+
return base_path
|
|
61
|
+
except Exception:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
return tempfile.gettempdir()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@tool(name="run_bash", category=ToolCategory.SYSTEM)
|
|
68
|
+
async def run_bash(
|
|
69
|
+
context: Optional['AgentContext'],
|
|
70
|
+
command: str,
|
|
71
|
+
timeout_seconds: int = 30
|
|
72
|
+
) -> TerminalResult:
|
|
73
|
+
"""
|
|
74
|
+
Execute a command in the terminal and wait for completion.
|
|
75
|
+
|
|
76
|
+
This tool is STATELESS. Each command runs in a fresh shell session.
|
|
77
|
+
Directory changes (cd) and environment variables (export) DO NOT persist
|
|
78
|
+
between calls. You must chain commands if state dependence is needed.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
command: The bash command to execute.
|
|
82
|
+
timeout_seconds: Maximum time to wait for completion (default 30s).
|
|
83
|
+
Command is killed if timeout is exceeded.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
TerminalResult with:
|
|
87
|
+
- stdout: Output from the command
|
|
88
|
+
- stderr: Error output (may be mixed with stdout in PTY)
|
|
89
|
+
- exit_code: Exit code if available
|
|
90
|
+
- timed_out: True if command exceeded timeout
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
- run_bash("ls -la")
|
|
94
|
+
- run_bash("cd src && npm install", timeout_seconds=120) # Correct way to run in subdir
|
|
95
|
+
- run_bash("cd src; ls") # Correct way to list subdir
|
|
96
|
+
"""
|
|
97
|
+
manager = _get_terminal_manager(context)
|
|
98
|
+
cwd = _get_cwd(context)
|
|
99
|
+
|
|
100
|
+
# Ensure session is started
|
|
101
|
+
await manager.ensure_started(cwd)
|
|
102
|
+
|
|
103
|
+
logger.debug(f"Executing terminal command: {command}")
|
|
104
|
+
result = await manager.execute_command(command, timeout_seconds)
|
|
105
|
+
|
|
106
|
+
if result.timed_out:
|
|
107
|
+
logger.warning(f"Command timed out: {command}")
|
|
108
|
+
|
|
109
|
+
return result
|