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.
- autobyteus/agent/agent.py +15 -5
- autobyteus/agent/bootstrap_steps/__init__.py +1 -3
- autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +3 -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/workspace_context_initialization_step.py +2 -4
- autobyteus/agent/context/agent_config.py +43 -20
- autobyteus/agent/context/agent_context.py +23 -18
- autobyteus/agent/context/agent_runtime_state.py +19 -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 +52 -0
- 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 +40 -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 +81 -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 +32 -0
- autobyteus/memory/active_transcript.py +69 -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 +183 -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/policies/__init__.py +5 -0
- autobyteus/memory/policies/compaction_policy.py +16 -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 +7 -0
- autobyteus/memory/store/base_store.py +14 -0
- autobyteus/memory/store/file_store.py +98 -0
- autobyteus/memory/tool_interaction_builder.py +46 -0
- autobyteus/memory/turn_tracker.py +9 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
- autobyteus/multimedia/audio/api/gemini_audio_client.py +108 -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 +38 -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 +56 -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.2.3.dist-info/METADATA +293 -0
- autobyteus-1.2.3.dist-info/RECORD +600 -0
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/WHEEL +1 -1
- {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.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.2.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import re
|
|
3
|
-
import logging
|
|
4
|
-
from typing import TYPE_CHECKING, List
|
|
5
|
-
|
|
6
|
-
from autobyteus.tools.functional_tool import tool
|
|
7
|
-
from autobyteus.tools.tool_category import ToolCategory
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from autobyteus.agent.context import AgentContext
|
|
11
|
-
|
|
12
|
-
logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
_HUNK_HEADER_RE = re.compile(r"^@@ -(?P<old_start>\d+)(?:,(?P<old_count>\d+))? \+(?P<new_start>\d+)(?:,(?P<new_count>\d+))? @@")
|
|
15
|
-
|
|
16
|
-
class PatchApplicationError(ValueError):
|
|
17
|
-
"""Raised when a unified diff patch cannot be applied to the target file."""
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _resolve_file_path(context: 'AgentContext', path: str) -> str:
|
|
21
|
-
"""Resolves an absolute path for the given input, using the agent workspace when needed."""
|
|
22
|
-
if os.path.isabs(path):
|
|
23
|
-
final_path = path
|
|
24
|
-
logger.debug("edit_file: provided path '%s' is absolute.", path)
|
|
25
|
-
else:
|
|
26
|
-
if not context.workspace:
|
|
27
|
-
error_msg = ("Relative path '%s' provided, but no workspace is configured for agent '%s'. "
|
|
28
|
-
"A workspace is required to resolve relative paths.")
|
|
29
|
-
logger.error(error_msg, path, context.agent_id)
|
|
30
|
-
raise ValueError(error_msg % (path, context.agent_id))
|
|
31
|
-
base_path = context.workspace.get_base_path()
|
|
32
|
-
if not base_path or not isinstance(base_path, str):
|
|
33
|
-
error_msg = ("Agent '%s' has a configured workspace, but it provided an invalid base path ('%s'). "
|
|
34
|
-
"Cannot resolve relative path '%s'.")
|
|
35
|
-
logger.error(error_msg, context.agent_id, base_path, path)
|
|
36
|
-
raise ValueError(error_msg % (context.agent_id, base_path, path))
|
|
37
|
-
final_path = os.path.join(base_path, path)
|
|
38
|
-
logger.debug("edit_file: resolved relative path '%s' against workspace base '%s' to '%s'.", path, base_path, final_path)
|
|
39
|
-
|
|
40
|
-
normalized_path = os.path.normpath(final_path)
|
|
41
|
-
logger.debug("edit_file: normalized path to '%s'.", normalized_path)
|
|
42
|
-
return normalized_path
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def _apply_unified_diff(original_lines: List[str], patch: str) -> List[str]:
|
|
46
|
-
"""Applies a unified diff patch to the provided original lines and returns the patched lines."""
|
|
47
|
-
if not patch or not patch.strip():
|
|
48
|
-
raise PatchApplicationError("Patch content is empty; nothing to apply.")
|
|
49
|
-
|
|
50
|
-
patched_lines: List[str] = []
|
|
51
|
-
orig_idx = 0
|
|
52
|
-
patch_lines = patch.splitlines(keepends=True)
|
|
53
|
-
line_idx = 0
|
|
54
|
-
|
|
55
|
-
while line_idx < len(patch_lines):
|
|
56
|
-
line = patch_lines[line_idx]
|
|
57
|
-
|
|
58
|
-
if line.startswith('---') or line.startswith('+++'):
|
|
59
|
-
logger.debug("edit_file: skipping diff header line '%s'.", line.strip())
|
|
60
|
-
line_idx += 1
|
|
61
|
-
continue
|
|
62
|
-
|
|
63
|
-
if not line.startswith('@@'):
|
|
64
|
-
stripped = line.strip()
|
|
65
|
-
if stripped == '':
|
|
66
|
-
line_idx += 1
|
|
67
|
-
continue
|
|
68
|
-
raise PatchApplicationError(f"Unexpected content outside of hunk header: '{stripped}'.")
|
|
69
|
-
|
|
70
|
-
match = _HUNK_HEADER_RE.match(line)
|
|
71
|
-
if not match:
|
|
72
|
-
raise PatchApplicationError(f"Malformed hunk header: '{line.strip()}'.")
|
|
73
|
-
|
|
74
|
-
old_start = int(match.group('old_start'))
|
|
75
|
-
old_count = int(match.group('old_count') or '1')
|
|
76
|
-
new_start = int(match.group('new_start'))
|
|
77
|
-
new_count = int(match.group('new_count') or '1')
|
|
78
|
-
logger.debug("edit_file: processing hunk old_start=%s old_count=%s new_start=%s new_count=%s.",
|
|
79
|
-
old_start, old_count, new_start, new_count)
|
|
80
|
-
|
|
81
|
-
target_idx = old_start - 1 if old_start > 0 else 0
|
|
82
|
-
if target_idx > len(original_lines):
|
|
83
|
-
raise PatchApplicationError("Patch hunk starts beyond end of file.")
|
|
84
|
-
if target_idx < orig_idx:
|
|
85
|
-
raise PatchApplicationError("Patch hunks overlap or are out of order.")
|
|
86
|
-
|
|
87
|
-
patched_lines.extend(original_lines[orig_idx:target_idx])
|
|
88
|
-
orig_idx = target_idx
|
|
89
|
-
|
|
90
|
-
line_idx += 1
|
|
91
|
-
hunk_consumed = 0
|
|
92
|
-
removed = 0
|
|
93
|
-
added = 0
|
|
94
|
-
|
|
95
|
-
while line_idx < len(patch_lines):
|
|
96
|
-
hunk_line = patch_lines[line_idx]
|
|
97
|
-
if hunk_line.startswith('@@'):
|
|
98
|
-
break
|
|
99
|
-
|
|
100
|
-
if hunk_line.startswith('-'):
|
|
101
|
-
if orig_idx >= len(original_lines):
|
|
102
|
-
raise PatchApplicationError("Patch attempts to remove lines beyond file length.")
|
|
103
|
-
if original_lines[orig_idx] != hunk_line[1:]:
|
|
104
|
-
raise PatchApplicationError("Patch removal does not match file content.")
|
|
105
|
-
orig_idx += 1
|
|
106
|
-
hunk_consumed += 1
|
|
107
|
-
removed += 1
|
|
108
|
-
elif hunk_line.startswith('+'):
|
|
109
|
-
patched_lines.append(hunk_line[1:])
|
|
110
|
-
added += 1
|
|
111
|
-
elif hunk_line.startswith(' '):
|
|
112
|
-
if orig_idx >= len(original_lines):
|
|
113
|
-
raise PatchApplicationError("Patch context exceeds file length.")
|
|
114
|
-
if original_lines[orig_idx] != hunk_line[1:]:
|
|
115
|
-
raise PatchApplicationError("Patch context does not match file content.")
|
|
116
|
-
patched_lines.append(original_lines[orig_idx])
|
|
117
|
-
orig_idx += 1
|
|
118
|
-
hunk_consumed += 1
|
|
119
|
-
elif hunk_line.startswith('\\'):
|
|
120
|
-
if hunk_line.strip() == '\':
|
|
121
|
-
if patched_lines:
|
|
122
|
-
patched_lines[-1] = patched_lines[-1].rstrip('\n')
|
|
123
|
-
else:
|
|
124
|
-
raise PatchApplicationError(f"Unsupported patch directive: '{hunk_line.strip()}'.")
|
|
125
|
-
elif hunk_line.strip() == '':
|
|
126
|
-
patched_lines.append(hunk_line)
|
|
127
|
-
else:
|
|
128
|
-
raise PatchApplicationError(f"Unsupported patch line: '{hunk_line.strip()}'.")
|
|
129
|
-
|
|
130
|
-
line_idx += 1
|
|
131
|
-
|
|
132
|
-
consumed_total = hunk_consumed
|
|
133
|
-
if old_count == 0:
|
|
134
|
-
if consumed_total != 0:
|
|
135
|
-
raise PatchApplicationError("Patch expects zero original lines but consumed some context.")
|
|
136
|
-
else:
|
|
137
|
-
if consumed_total != old_count:
|
|
138
|
-
raise PatchApplicationError(
|
|
139
|
-
f"Patch expected to consume {old_count} original lines but consumed {consumed_total}.")
|
|
140
|
-
|
|
141
|
-
context_lines = consumed_total - removed
|
|
142
|
-
expected_new_lines = context_lines + added
|
|
143
|
-
if new_count == 0:
|
|
144
|
-
if expected_new_lines != 0:
|
|
145
|
-
raise PatchApplicationError("Patch declares zero new lines but produced changes.")
|
|
146
|
-
else:
|
|
147
|
-
if expected_new_lines != new_count:
|
|
148
|
-
raise PatchApplicationError(
|
|
149
|
-
f"Patch expected to produce {new_count} new lines but produced {expected_new_lines}.")
|
|
150
|
-
|
|
151
|
-
patched_lines.extend(original_lines[orig_idx:])
|
|
152
|
-
return patched_lines
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
@tool(name="edit_file", category=ToolCategory.FILE_SYSTEM)
|
|
156
|
-
async def edit_file(context: 'AgentContext', path: str, patch: str, create_if_missing: bool = False) -> str:
|
|
157
|
-
"""Applies a unified diff patch to update a text file without overwriting unrelated content.
|
|
158
|
-
|
|
159
|
-
Args:
|
|
160
|
-
path: Path to the target file. Relative paths are resolved against the agent workspace when available.
|
|
161
|
-
patch: Unified diff patch describing the edits to apply.
|
|
162
|
-
create_if_missing: When True, allows applying a patch that introduces content to a non-existent file.
|
|
163
|
-
|
|
164
|
-
Raises:
|
|
165
|
-
FileNotFoundError: If the file does not exist and create_if_missing is False.
|
|
166
|
-
PatchApplicationError: If the patch content cannot be applied cleanly.
|
|
167
|
-
IOError: If file reading or writing fails.
|
|
168
|
-
"""
|
|
169
|
-
logger.debug("edit_file: requested edit for agent '%s' on path '%s'.", context.agent_id, path)
|
|
170
|
-
final_path = _resolve_file_path(context, path)
|
|
171
|
-
|
|
172
|
-
dir_path = os.path.dirname(final_path)
|
|
173
|
-
if dir_path and not os.path.exists(dir_path) and create_if_missing:
|
|
174
|
-
os.makedirs(dir_path, exist_ok=True)
|
|
175
|
-
|
|
176
|
-
file_exists = os.path.exists(final_path)
|
|
177
|
-
if not file_exists and not create_if_missing:
|
|
178
|
-
raise FileNotFoundError(f"The file at resolved path {final_path} does not exist.")
|
|
179
|
-
|
|
180
|
-
try:
|
|
181
|
-
original_lines: List[str]
|
|
182
|
-
if file_exists:
|
|
183
|
-
with open(final_path, 'r', encoding='utf-8') as source:
|
|
184
|
-
original_lines = source.read().splitlines(keepends=True)
|
|
185
|
-
else:
|
|
186
|
-
original_lines = []
|
|
187
|
-
|
|
188
|
-
patched_lines = _apply_unified_diff(original_lines, patch)
|
|
189
|
-
|
|
190
|
-
with open(final_path, 'w', encoding='utf-8') as destination:
|
|
191
|
-
destination.writelines(patched_lines)
|
|
192
|
-
|
|
193
|
-
logger.info("edit_file: successfully applied patch to '%s'.", final_path)
|
|
194
|
-
return f"File edited successfully at {final_path}"
|
|
195
|
-
except PatchApplicationError as patch_err:
|
|
196
|
-
logger.error("edit_file: failed to apply patch to '%s': %s", final_path, patch_err, exc_info=True)
|
|
197
|
-
raise patch_err
|
|
198
|
-
except Exception as exc: # pragma: no cover - general safeguard
|
|
199
|
-
logger.error("edit_file: unexpected error while editing '%s': %s", final_path, exc, exc_info=True)
|
|
200
|
-
raise IOError(f"Could not edit file at '{final_path}': {exc}")
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/tools/file/list_directory.py
|
|
2
|
-
"""
|
|
3
|
-
This module provides a tool for listing directory contents in a structured,
|
|
4
|
-
tree-like format, mirroring the behavior of the Codex Rust implementation.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import asyncio
|
|
8
|
-
import logging
|
|
9
|
-
import os
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from collections import deque
|
|
12
|
-
from dataclasses import dataclass
|
|
13
|
-
from typing import List, Deque, Tuple, Optional, TYPE_CHECKING
|
|
14
|
-
|
|
15
|
-
from autobyteus.tools.functional_tool import tool
|
|
16
|
-
from autobyteus.tools.tool_category import ToolCategory
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from autobyteus.agent.context import AgentContext
|
|
20
|
-
|
|
21
|
-
logger = logging.getLogger(__name__)
|
|
22
|
-
|
|
23
|
-
# Constants from the design document
|
|
24
|
-
INDENTATION_SPACES = 2
|
|
25
|
-
MAX_ENTRY_LENGTH = 500
|
|
26
|
-
|
|
27
|
-
@dataclass
|
|
28
|
-
class DirEntry:
|
|
29
|
-
"""Represents a collected directory entry for sorting and formatting."""
|
|
30
|
-
name: str
|
|
31
|
-
kind: str
|
|
32
|
-
depth: int
|
|
33
|
-
|
|
34
|
-
@tool(name="list_directory", category=ToolCategory.FILE_SYSTEM)
|
|
35
|
-
async def list_directory(
|
|
36
|
-
context: 'AgentContext',
|
|
37
|
-
path: str,
|
|
38
|
-
depth: int = 2,
|
|
39
|
-
limit: int = 25,
|
|
40
|
-
offset: int = 1
|
|
41
|
-
) -> str:
|
|
42
|
-
"""
|
|
43
|
-
Lists the contents of a directory in a structured, tree-like format.
|
|
44
|
-
|
|
45
|
-
This tool performs a breadth-first traversal of the specified directory up to a
|
|
46
|
-
given depth. It returns a deterministic, lexicographically sorted list of entries,
|
|
47
|
-
formatted with indentation and tree glyphs to represent the hierarchy.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
path: The path to the directory to list. Relative paths are resolved against the agent's workspace.
|
|
51
|
-
depth: The maximum directory depth to traverse. Must be > 0.
|
|
52
|
-
limit: The maximum number of entries to return in the output. Must be > 0.
|
|
53
|
-
offset: The 1-indexed entry number to start from, for pagination. Must be > 0.
|
|
54
|
-
"""
|
|
55
|
-
# --- 1. Argument Validation ---
|
|
56
|
-
logger.debug(f"list_directory for agent {context.agent_id}, initial path: {path}")
|
|
57
|
-
|
|
58
|
-
final_path: str
|
|
59
|
-
if os.path.isabs(path):
|
|
60
|
-
final_path = path
|
|
61
|
-
logger.debug(f"Path '{path}' is absolute. Using it directly.")
|
|
62
|
-
else:
|
|
63
|
-
if not context.workspace:
|
|
64
|
-
error_msg = f"Relative path '{path}' provided, but no workspace is configured for agent '{context.agent_id}'. A workspace is required to resolve relative paths."
|
|
65
|
-
logger.error(error_msg)
|
|
66
|
-
raise ValueError(error_msg)
|
|
67
|
-
|
|
68
|
-
base_path = context.workspace.get_base_path()
|
|
69
|
-
if not base_path or not isinstance(base_path, str):
|
|
70
|
-
error_msg = f"Agent '{context.agent_id}' has a configured workspace, but it provided an invalid base path ('{base_path}'). Cannot resolve relative path '{path}'."
|
|
71
|
-
logger.error(error_msg)
|
|
72
|
-
raise ValueError(error_msg)
|
|
73
|
-
|
|
74
|
-
final_path = os.path.join(base_path, path)
|
|
75
|
-
logger.debug(f"Path '{path}' is relative. Resolved to '{final_path}' using workspace base path '{base_path}'.")
|
|
76
|
-
|
|
77
|
-
final_path = os.path.normpath(final_path)
|
|
78
|
-
|
|
79
|
-
if not Path(final_path).is_dir():
|
|
80
|
-
raise FileNotFoundError(f"Directory not found at path: {final_path}")
|
|
81
|
-
if depth <= 0 or limit <= 0 or offset <= 0:
|
|
82
|
-
raise ValueError("depth, limit, and offset must all be greater than zero.")
|
|
83
|
-
|
|
84
|
-
# --- 2. Asynchronous Traversal ---
|
|
85
|
-
loop = asyncio.get_running_loop()
|
|
86
|
-
all_entries = await loop.run_in_executor(
|
|
87
|
-
None, _traverse_directory_bfs, Path(final_path), depth
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
# --- 3. Slicing ---
|
|
91
|
-
total_found = len(all_entries)
|
|
92
|
-
start_index = offset - 1
|
|
93
|
-
end_index = start_index + limit
|
|
94
|
-
sliced_entries = all_entries[start_index:end_index]
|
|
95
|
-
|
|
96
|
-
# --- 4. Formatting ---
|
|
97
|
-
output_lines = [f"Absolute path: {final_path}"]
|
|
98
|
-
|
|
99
|
-
# To correctly apply tree glyphs, we need to know which entry is the last in its directory
|
|
100
|
-
# This is complex with BFS. A simpler, visually acceptable approach is taken here.
|
|
101
|
-
# For a more accurate glyph representation like the Rust version, we would need to
|
|
102
|
-
# process entries directory by directory after collection.
|
|
103
|
-
for i, entry in enumerate(sliced_entries):
|
|
104
|
-
# A simplified glyph logic: last item in the slice gets the closing glyph
|
|
105
|
-
is_last = (i == len(sliced_entries) - 1)
|
|
106
|
-
output_lines.append(_format_entry_line(entry, is_last))
|
|
107
|
-
|
|
108
|
-
if total_found > end_index:
|
|
109
|
-
output_lines.append(f"More than {limit} entries found.")
|
|
110
|
-
|
|
111
|
-
return "\n".join(output_lines)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def _traverse_directory_bfs(start_path: Path, max_depth: int) -> List[DirEntry]:
|
|
115
|
-
"""
|
|
116
|
-
Performs a breadth-first traversal of a directory. This is a synchronous function
|
|
117
|
-
designed to be run in a thread pool executor.
|
|
118
|
-
"""
|
|
119
|
-
collected: List[DirEntry] = []
|
|
120
|
-
queue: Deque[Tuple[Path, int]] = deque([(start_path, 0)])
|
|
121
|
-
|
|
122
|
-
while queue:
|
|
123
|
-
current_path, current_depth = queue.popleft()
|
|
124
|
-
|
|
125
|
-
if current_depth >= max_depth:
|
|
126
|
-
continue
|
|
127
|
-
|
|
128
|
-
try:
|
|
129
|
-
# Use os.scandir for efficiency as it fetches file type info
|
|
130
|
-
entries_at_level = []
|
|
131
|
-
for entry in os.scandir(current_path):
|
|
132
|
-
kind = "[unknown]"
|
|
133
|
-
if entry.is_dir():
|
|
134
|
-
kind = "[dir]"
|
|
135
|
-
queue.append((Path(entry.path), current_depth + 1))
|
|
136
|
-
elif entry.is_file():
|
|
137
|
-
kind = "[file]"
|
|
138
|
-
elif entry.is_symlink():
|
|
139
|
-
kind = "[link]"
|
|
140
|
-
|
|
141
|
-
# Truncate long filenames
|
|
142
|
-
display_name = entry.name
|
|
143
|
-
if len(display_name) > MAX_ENTRY_LENGTH:
|
|
144
|
-
display_name = display_name[:MAX_ENTRY_LENGTH] + "..."
|
|
145
|
-
|
|
146
|
-
entries_at_level.append(DirEntry(name=display_name, kind=kind, depth=current_depth + 1))
|
|
147
|
-
|
|
148
|
-
# Sort entries at the current level before adding to the main list
|
|
149
|
-
entries_at_level.sort(key=lambda e: e.name)
|
|
150
|
-
collected.extend(entries_at_level)
|
|
151
|
-
|
|
152
|
-
except (PermissionError, OSError) as e:
|
|
153
|
-
logger.warning(f"Could not read directory '{current_path}': {e}")
|
|
154
|
-
continue
|
|
155
|
-
|
|
156
|
-
return collected
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _format_entry_line(entry: DirEntry, is_last_in_slice: bool) -> str:
|
|
160
|
-
"""Formats a single directory entry into its final string representation."""
|
|
161
|
-
# This simplified glyph logic doesn't know about siblings, just the slice.
|
|
162
|
-
# A full implementation would require grouping by parent path after collection.
|
|
163
|
-
prefix = "└─ " if is_last_in_slice else "├─ "
|
|
164
|
-
|
|
165
|
-
# Indentation is based on depth from the root search path
|
|
166
|
-
indentation = " " * INDENTATION_SPACES * (entry.depth -1)
|
|
167
|
-
|
|
168
|
-
return f"{indentation}{prefix}{entry.kind} {entry.name}"
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# file: autobyteus/autobyteus/tools/file/search_files.py
|
|
2
|
-
"""
|
|
3
|
-
This module provides a high-performance fuzzy file search tool.
|
|
4
|
-
It uses 'git ls-files' for speed in Git repositories and falls back
|
|
5
|
-
to a filesystem walk for other directories, respecting .gitignore.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import asyncio
|
|
9
|
-
import json
|
|
10
|
-
import logging
|
|
11
|
-
import os
|
|
12
|
-
import subprocess
|
|
13
|
-
from pathlib import Path
|
|
14
|
-
from typing import List, Dict, Optional, Tuple, TYPE_CHECKING
|
|
15
|
-
|
|
16
|
-
from rapidfuzz import process, fuzz
|
|
17
|
-
from pathspec import PathSpec
|
|
18
|
-
from pathspec.patterns import GitWildMatchPattern
|
|
19
|
-
|
|
20
|
-
from autobyteus.tools.functional_tool import tool
|
|
21
|
-
from autobyteus.tools.tool_category import ToolCategory
|
|
22
|
-
|
|
23
|
-
if TYPE_CHECKING:
|
|
24
|
-
from autobyteus.agent.context import AgentContext
|
|
25
|
-
|
|
26
|
-
logger = logging.getLogger(__name__)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@tool(name="search_files", category=ToolCategory.FILE_SYSTEM)
|
|
30
|
-
async def search_files(
|
|
31
|
-
context: 'AgentContext',
|
|
32
|
-
query: Optional[str] = None,
|
|
33
|
-
path: str = '.',
|
|
34
|
-
limit: int = 64,
|
|
35
|
-
exclude_patterns: Optional[List[str]] = None
|
|
36
|
-
) -> str:
|
|
37
|
-
"""
|
|
38
|
-
Performs a high-performance fuzzy search for files in a directory.
|
|
39
|
-
|
|
40
|
-
This tool intelligently discovers files. If the search directory is a Git repository,
|
|
41
|
-
it uses the highly efficient 'git ls-files' command. Otherwise, it performs a
|
|
42
|
-
standard filesystem walk. In both cases, it respects .gitignore rules and any
|
|
43
|
-
additional exclusion patterns provided. The search results are returned as a
|
|
44
|
-
JSON string, with each result including the file path and a relevance score.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
query: The fuzzy search pattern. If omitted, the tool lists all discoverable files up to the limit.
|
|
48
|
-
path: The directory to search in. Relative paths are resolved against the agent's workspace. Defaults to the workspace root.
|
|
49
|
-
limit: The maximum number of results to return.
|
|
50
|
-
exclude_patterns: A list of glob patterns to exclude from the search, in addition to .gitignore rules.
|
|
51
|
-
"""
|
|
52
|
-
final_path = _resolve_search_path(context, path)
|
|
53
|
-
if not final_path.is_dir():
|
|
54
|
-
raise FileNotFoundError(f"The specified search path does not exist or is not a directory: {final_path}")
|
|
55
|
-
|
|
56
|
-
exclude = exclude_patterns or []
|
|
57
|
-
files, discovery_method = await _discover_files(final_path, exclude)
|
|
58
|
-
|
|
59
|
-
if not query:
|
|
60
|
-
# If no query, just return the first 'limit' files found
|
|
61
|
-
matches = [{"path": f, "score": 100} for f in files[:limit]]
|
|
62
|
-
result_summary = {
|
|
63
|
-
"discovery_method": discovery_method,
|
|
64
|
-
"total_files_scanned": len(files),
|
|
65
|
-
"matches_found": len(matches),
|
|
66
|
-
"results": matches
|
|
67
|
-
}
|
|
68
|
-
return json.dumps(result_summary, indent=2)
|
|
69
|
-
|
|
70
|
-
# Use rapidfuzz to find the best matches
|
|
71
|
-
results = process.extract(
|
|
72
|
-
query,
|
|
73
|
-
files,
|
|
74
|
-
scorer=fuzz.WRatio,
|
|
75
|
-
limit=limit,
|
|
76
|
-
score_cutoff=50
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
file_matches = [{"path": path, "score": round(score)} for path, score, _ in results]
|
|
80
|
-
|
|
81
|
-
result_summary = {
|
|
82
|
-
"discovery_method": discovery_method,
|
|
83
|
-
"total_files_scanned": len(files),
|
|
84
|
-
"matches_found": len(file_matches),
|
|
85
|
-
"results": file_matches
|
|
86
|
-
}
|
|
87
|
-
return json.dumps(result_summary, indent=2)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _resolve_search_path(context: 'AgentContext', path: str) -> Path:
|
|
91
|
-
"""Resolves the search path against the agent's workspace if relative."""
|
|
92
|
-
if os.path.isabs(path):
|
|
93
|
-
return Path(path)
|
|
94
|
-
|
|
95
|
-
if not context.workspace:
|
|
96
|
-
raise ValueError(f"Relative path '{path}' provided, but no workspace is configured for agent '{context.agent_id}'.")
|
|
97
|
-
|
|
98
|
-
base_path = context.workspace.get_base_path()
|
|
99
|
-
if not base_path:
|
|
100
|
-
raise ValueError(f"Agent '{context.agent_id}' has a workspace, but it provided an invalid base path.")
|
|
101
|
-
|
|
102
|
-
return Path(os.path.normpath(os.path.join(base_path, path)))
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
async def _is_git_repository_async(path: Path) -> bool:
|
|
106
|
-
"""Asynchronously checks if a given path is within a Git repository."""
|
|
107
|
-
process = await asyncio.create_subprocess_exec(
|
|
108
|
-
"git", "rev-parse", "--is-inside-work-tree",
|
|
109
|
-
cwd=str(path),
|
|
110
|
-
stdout=asyncio.subprocess.PIPE,
|
|
111
|
-
stderr=asyncio.subprocess.PIPE,
|
|
112
|
-
)
|
|
113
|
-
stdout, _ = await process.communicate()
|
|
114
|
-
return stdout.decode().strip() == "true"
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
async def _get_files_from_git_async(path: Path) -> List[str]:
|
|
118
|
-
"""Uses 'git ls-files' to get a list of all tracked and untracked files."""
|
|
119
|
-
try:
|
|
120
|
-
process = await asyncio.create_subprocess_exec(
|
|
121
|
-
"git", "ls-files", "-co", "--exclude-standard",
|
|
122
|
-
cwd=str(path),
|
|
123
|
-
stdout=asyncio.subprocess.PIPE,
|
|
124
|
-
stderr=asyncio.subprocess.PIPE,
|
|
125
|
-
)
|
|
126
|
-
stdout_bytes, stderr_bytes = await process.communicate()
|
|
127
|
-
if process.returncode != 0:
|
|
128
|
-
stderr = stderr_bytes.decode().strip()
|
|
129
|
-
logger.error(f"Failed to run 'git ls-files' in '{path}': {stderr}")
|
|
130
|
-
return []
|
|
131
|
-
|
|
132
|
-
stdout = stdout_bytes.decode().strip()
|
|
133
|
-
return stdout.strip().split("\n") if stdout.strip() else []
|
|
134
|
-
except (subprocess.CalledProcessError, FileNotFoundError) as e:
|
|
135
|
-
logger.error(f"Failed to run 'git ls-files': {e}")
|
|
136
|
-
return []
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def _get_files_with_walk_sync(path: Path, exclude_patterns: List[str]) -> List[str]:
|
|
140
|
-
"""Synchronously walks the filesystem to find files, respecting ignore patterns."""
|
|
141
|
-
files: List[str] = []
|
|
142
|
-
|
|
143
|
-
all_exclude_patterns = exclude_patterns[:]
|
|
144
|
-
gitignore_path = path / ".gitignore"
|
|
145
|
-
if gitignore_path.is_file():
|
|
146
|
-
try:
|
|
147
|
-
with open(gitignore_path, "r", encoding='utf-8') as f:
|
|
148
|
-
all_exclude_patterns.extend(f.read().splitlines())
|
|
149
|
-
except Exception as e:
|
|
150
|
-
logger.warning(f"Could not read .gitignore file at '{gitignore_path}': {e}")
|
|
151
|
-
|
|
152
|
-
spec = PathSpec.from_lines(GitWildMatchPattern, all_exclude_patterns)
|
|
153
|
-
|
|
154
|
-
for root, _, filenames in os.walk(path, topdown=True):
|
|
155
|
-
root_path = Path(root)
|
|
156
|
-
for filename in filenames:
|
|
157
|
-
full_path = root_path / filename
|
|
158
|
-
try:
|
|
159
|
-
relative_path = full_path.relative_to(path)
|
|
160
|
-
if not spec.match_file(str(relative_path)):
|
|
161
|
-
files.append(str(relative_path))
|
|
162
|
-
except (ValueError, IsADirectoryError):
|
|
163
|
-
# Handles cases like broken symlinks
|
|
164
|
-
continue
|
|
165
|
-
return files
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
async def _get_files_with_walk_async(path: Path, exclude_patterns: List[str]) -> List[str]:
|
|
169
|
-
"""Runs the synchronous walk in a thread pool."""
|
|
170
|
-
loop = asyncio.get_running_loop()
|
|
171
|
-
return await loop.run_in_executor(
|
|
172
|
-
None, _get_files_with_walk_sync, path, exclude_patterns
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
async def _discover_files(cwd: Path, exclude: List[str]) -> Tuple[List[str], str]:
|
|
177
|
-
"""Orchestrates the file discovery, choosing between Git and os.walk."""
|
|
178
|
-
if await _is_git_repository_async(cwd):
|
|
179
|
-
logger.info(f"Using 'git ls-files' for fast file discovery in '{cwd}'.")
|
|
180
|
-
files = await _get_files_from_git_async(cwd)
|
|
181
|
-
# Git ls-files already handles gitignore, but we may have extra excludes
|
|
182
|
-
if exclude:
|
|
183
|
-
spec = PathSpec.from_lines(GitWildMatchPattern, exclude)
|
|
184
|
-
files = [f for f in files if not spec.match_file(f)]
|
|
185
|
-
return files, "git"
|
|
186
|
-
else:
|
|
187
|
-
logger.info(f"Using 'os.walk' to scan directory '{cwd}'.")
|
|
188
|
-
return await _get_files_with_walk_async(cwd, exclude), "os_walk"
|