langchain-agentx-python 0.1__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.
- langchain_agentx/__init__.py +46 -0
- langchain_agentx/command/__init__.py +28 -0
- langchain_agentx/command/builtin/__init__.py +25 -0
- langchain_agentx/command/builtin/clear.py +33 -0
- langchain_agentx/command/builtin/compact.py +33 -0
- langchain_agentx/command/builtin/memory.py +37 -0
- langchain_agentx/command/builtin/reload_plugins.py +42 -0
- langchain_agentx/command/context.py +30 -0
- langchain_agentx/command/dispatcher.py +183 -0
- langchain_agentx/command/registry.py +110 -0
- langchain_agentx/command/result.py +25 -0
- langchain_agentx/command/types.py +41 -0
- langchain_agentx/config/__init__.py +14 -0
- langchain_agentx/loop/__init__.py +47 -0
- langchain_agentx/loop/config/__init__.py +20 -0
- langchain_agentx/loop/config/agent_config.py +66 -0
- langchain_agentx/loop/config/agent_loop_config.py +72 -0
- langchain_agentx/loop/config/model_context_resolver.py +105 -0
- langchain_agentx/loop/config/runtime_settings.py +50 -0
- langchain_agentx/loop/config/token_estimator.py +133 -0
- langchain_agentx/loop/context/__init__.py +66 -0
- langchain_agentx/loop/context/blocking_guard.py +97 -0
- langchain_agentx/loop/context/compaction_service.py +60 -0
- langchain_agentx/loop/context/message_utils.py +56 -0
- langchain_agentx/loop/context/pipeline.py +127 -0
- langchain_agentx/loop/context/settings.py +103 -0
- langchain_agentx/loop/context/stages/__init__.py +29 -0
- langchain_agentx/loop/context/stages/autocompact.py +140 -0
- langchain_agentx/loop/context/stages/base.py +32 -0
- langchain_agentx/loop/context/stages/collapse.py +76 -0
- langchain_agentx/loop/context/stages/microcompact.py +76 -0
- langchain_agentx/loop/context/stages/noop.py +33 -0
- langchain_agentx/loop/context/stages/snip.py +71 -0
- langchain_agentx/loop/context/stages/tool_result_budget.py +69 -0
- langchain_agentx/loop/context/types.py +79 -0
- langchain_agentx/loop/exit/__init__.py +1 -0
- langchain_agentx/loop/exit/exit_logic.py +320 -0
- langchain_agentx/loop/exit/reason_codes.py +39 -0
- langchain_agentx/loop/graph/__init__.py +5 -0
- langchain_agentx/loop/graph/builtin_loop_control.py +197 -0
- langchain_agentx/loop/graph/factory.py +1409 -0
- langchain_agentx/loop/graph/graph_edges.py +820 -0
- langchain_agentx/loop/hook/__init__.py +48 -0
- langchain_agentx/loop/hook/async_hook_runner.py +62 -0
- langchain_agentx/loop/hook/config.py +280 -0
- langchain_agentx/loop/hook/engine.py +321 -0
- langchain_agentx/loop/hook/executors/__init__.py +9 -0
- langchain_agentx/loop/hook/executors/agent.py +107 -0
- langchain_agentx/loop/hook/executors/command.py +230 -0
- langchain_agentx/loop/hook/executors/http.py +114 -0
- langchain_agentx/loop/hook/executors/prompt.py +92 -0
- langchain_agentx/loop/hook/graph_wiring.py +134 -0
- langchain_agentx/loop/hook/registry.py +262 -0
- langchain_agentx/loop/hook/trust.py +43 -0
- langchain_agentx/loop/hook/types.py +110 -0
- langchain_agentx/loop/injection/__init__.py +13 -0
- langchain_agentx/loop/injection/dedup.py +74 -0
- langchain_agentx/loop/loop_abort.py +36 -0
- langchain_agentx/loop/model/__init__.py +1 -0
- langchain_agentx/loop/model/model_node.py +648 -0
- langchain_agentx/loop/model/model_nodes.py +661 -0
- langchain_agentx/loop/model/orphan_tool_results.py +38 -0
- langchain_agentx/loop/model/retrier.py +307 -0
- langchain_agentx/loop/model/retry_bridge.py +58 -0
- langchain_agentx/loop/model/retry_events.py +35 -0
- langchain_agentx/loop/model/retry_policy.py +56 -0
- langchain_agentx/loop/model/schema_and_format.py +153 -0
- langchain_agentx/loop/model/tool_and_model_binding.py +227 -0
- langchain_agentx/loop/model/tool_call_degradation_corrector.py +443 -0
- langchain_agentx/loop/model/tool_transcript_guard.py +225 -0
- langchain_agentx/loop/prompt/__init__.py +95 -0
- langchain_agentx/loop/prompt/builder.py +61 -0
- langchain_agentx/loop/prompt/builtin.py +218 -0
- langchain_agentx/loop/prompt/compact.py +408 -0
- langchain_agentx/loop/prompt/sections.py +120 -0
- langchain_agentx/loop/runtime/__init__.py +19 -0
- langchain_agentx/loop/runtime/context.py +34 -0
- langchain_agentx/loop/runtime/context_factory.py +107 -0
- langchain_agentx/loop/runtime/subagent_execution_paths.py +68 -0
- langchain_agentx/loop/subagent/__init__.py +53 -0
- langchain_agentx/loop/subagent/async_runner.py +215 -0
- langchain_agentx/loop/subagent/context.py +209 -0
- langchain_agentx/loop/subagent/fork_worktree_notice.py +25 -0
- langchain_agentx/loop/subagent/graph.py +72 -0
- langchain_agentx/loop/subagent/orchestrator.py +391 -0
- langchain_agentx/loop/subagent/progress.py +30 -0
- langchain_agentx/loop/subagent/prompt.py +52 -0
- langchain_agentx/loop/subagent/runner.py +504 -0
- langchain_agentx/loop/subagent/transcript.py +172 -0
- langchain_agentx/memory/__init__.py +2 -0
- langchain_agentx/memory/instruction/__init__.py +12 -0
- langchain_agentx/memory/instruction/loader.py +325 -0
- langchain_agentx/memory/instruction/resolver.py +24 -0
- langchain_agentx/memory/instruction/runtime.py +83 -0
- langchain_agentx/memory/instruction/sections.py +83 -0
- langchain_agentx/memory/instruction/types.py +59 -0
- langchain_agentx/memory/memdir/__init__.py +77 -0
- langchain_agentx/memory/memdir/age.py +36 -0
- langchain_agentx/memory/memdir/agent_memory.py +380 -0
- langchain_agentx/memory/memdir/extractor.py +309 -0
- langchain_agentx/memory/memdir/loader.py +187 -0
- langchain_agentx/memory/memdir/paths.py +63 -0
- langchain_agentx/memory/memdir/recall.py +45 -0
- langchain_agentx/memory/memdir/runtime.py +43 -0
- langchain_agentx/memory/memdir/scan.py +135 -0
- langchain_agentx/memory/memdir/types.py +104 -0
- langchain_agentx/memory/session/__init__.py +76 -0
- langchain_agentx/memory/session/compact_bridge.py +208 -0
- langchain_agentx/memory/session/prompts.py +172 -0
- langchain_agentx/memory/session/session_memory.py +282 -0
- langchain_agentx/observability/__init__.py +67 -0
- langchain_agentx/observability/evaluation/__init__.py +17 -0
- langchain_agentx/observability/evaluation/checkers/__init__.py +18 -0
- langchain_agentx/observability/evaluation/checkers/base.py +34 -0
- langchain_agentx/observability/evaluation/checkers/compaction.py +38 -0
- langchain_agentx/observability/evaluation/checkers/degradation.py +50 -0
- langchain_agentx/observability/evaluation/checkers/exit_quality.py +42 -0
- langchain_agentx/observability/evaluation/checkers/session_memory.py +45 -0
- langchain_agentx/observability/evaluation/checkers/tool_behavior.py +53 -0
- langchain_agentx/observability/evaluation/retention_scheduler.py +67 -0
- langchain_agentx/observability/evaluation/service.py +102 -0
- langchain_agentx/observability/evaluation/state.py +32 -0
- langchain_agentx/observability/evaluation/store.py +258 -0
- langchain_agentx/observability/events/__init__.py +15 -0
- langchain_agentx/observability/events/langchain_agentx_event_adapter.py +832 -0
- langchain_agentx/observability/logging/__init__.py +15 -0
- langchain_agentx/observability/logging/debug_burst.py +95 -0
- langchain_agentx/observability/logging/logging_config.py +178 -0
- langchain_agentx/observability/logging/logging_contract.py +65 -0
- langchain_agentx/observability/replay/__init__.py +35 -0
- langchain_agentx/observability/replay/cli.py +91 -0
- langchain_agentx/observability/replay/service.py +83 -0
- langchain_agentx/observability/replay/store.py +278 -0
- langchain_agentx/observability/replay/ui.py +47 -0
- langchain_agentx/observability/trace/__init__.py +25 -0
- langchain_agentx/observability/trace/collector.py +560 -0
- langchain_agentx/observability/trace/event_emitter.py +183 -0
- langchain_agentx/observability/trace/hook_event_emitter.py +49 -0
- langchain_agentx/observability/trace/models.py +144 -0
- langchain_agentx/observability/trace/sqlite_store.py +873 -0
- langchain_agentx/observability/trace/trace_callback.py +295 -0
- langchain_agentx/observability/trace/trace_lifecycle_collector.py +114 -0
- langchain_agentx/plugin/__init__.py +26 -0
- langchain_agentx/plugin/builtin.py +53 -0
- langchain_agentx/plugin/config.py +113 -0
- langchain_agentx/plugin/loader.py +386 -0
- langchain_agentx/plugin/manifest.py +154 -0
- langchain_agentx/plugin/registries.py +211 -0
- langchain_agentx/plugin/types.py +142 -0
- langchain_agentx/provider/__init__.py +27 -0
- langchain_agentx/provider/anthropic.py +121 -0
- langchain_agentx/provider/compatible_chat_openai.py +86 -0
- langchain_agentx/provider/env.py +45 -0
- langchain_agentx/provider/model_profile.py +156 -0
- langchain_agentx/provider/openai.py +89 -0
- langchain_agentx/session/__init__.py +17 -0
- langchain_agentx/session/agent_session.py +320 -0
- langchain_agentx/session/conversation_factory.py +87 -0
- langchain_agentx/session/conversation_recovery.py +156 -0
- langchain_agentx/session/conversation_session.py +198 -0
- langchain_agentx/session/factory.py +143 -0
- langchain_agentx/session/protocol.py +25 -0
- langchain_agentx/task_runtime/__init__.py +113 -0
- langchain_agentx/task_runtime/core/__init__.py +51 -0
- langchain_agentx/task_runtime/core/ids.py +33 -0
- langchain_agentx/task_runtime/core/interfaces.py +115 -0
- langchain_agentx/task_runtime/core/notification_priority.py +19 -0
- langchain_agentx/task_runtime/core/types.py +136 -0
- langchain_agentx/task_runtime/integrations/__init__.py +33 -0
- langchain_agentx/task_runtime/integrations/loop_adapter.py +91 -0
- langchain_agentx/task_runtime/integrations/loop_integration.py +61 -0
- langchain_agentx/task_runtime/integrations/prefetch_providers.py +108 -0
- langchain_agentx/task_runtime/integrations/provider_factory.py +103 -0
- langchain_agentx/task_runtime/integrations/queued_command_provider.py +184 -0
- langchain_agentx/task_runtime/integrations/sqlite_queued_command_provider.py +338 -0
- langchain_agentx/task_runtime/integrations/tool_use_summary_provider.py +254 -0
- langchain_agentx/task_runtime/orchestrator/__init__.py +5 -0
- langchain_agentx/task_runtime/orchestrator/runtime.py +386 -0
- langchain_agentx/task_runtime/output/__init__.py +5 -0
- langchain_agentx/task_runtime/output/sink.py +64 -0
- langchain_agentx/task_runtime/policy/__init__.py +11 -0
- langchain_agentx/task_runtime/policy/withhold_visibility.py +32 -0
- langchain_agentx/task_runtime/queue/__init__.py +5 -0
- langchain_agentx/task_runtime/queue/in_memory.py +55 -0
- langchain_agentx/task_runtime/skill_prefetch/__init__.py +4 -0
- langchain_agentx/task_runtime/skill_prefetch/attachments.py +46 -0
- langchain_agentx/task_runtime/skill_prefetch/models.py +37 -0
- langchain_agentx/task_runtime/skill_prefetch/provider.py +344 -0
- langchain_agentx/task_runtime/store/__init__.py +6 -0
- langchain_agentx/task_runtime/store/in_memory.py +81 -0
- langchain_agentx/task_runtime/store/sqlite_store.py +281 -0
- langchain_agentx/task_runtime/tasks/__init__.py +76 -0
- langchain_agentx/task_runtime/tasks/ai_analysis/__init__.py +15 -0
- langchain_agentx/task_runtime/tasks/ai_analysis/base.py +41 -0
- langchain_agentx/task_runtime/tasks/ai_analysis/evaluation.py +67 -0
- langchain_agentx/task_runtime/tasks/ai_analysis/registry.py +36 -0
- langchain_agentx/task_runtime/tasks/ai_analysis/scheduler.py +70 -0
- langchain_agentx/task_runtime/tasks/base/__init__.py +6 -0
- langchain_agentx/task_runtime/tasks/base/contracts.py +24 -0
- langchain_agentx/task_runtime/tasks/custom/__init__.py +7 -0
- langchain_agentx/task_runtime/tasks/custom/executor.py +60 -0
- langchain_agentx/task_runtime/tasks/custom/notification.py +7 -0
- langchain_agentx/task_runtime/tasks/custom/semantics.py +13 -0
- langchain_agentx/task_runtime/tasks/custom/spec.py +33 -0
- langchain_agentx/task_runtime/tasks/dream_task/__init__.py +15 -0
- langchain_agentx/task_runtime/tasks/dream_task/executor.py +61 -0
- langchain_agentx/task_runtime/tasks/dream_task/notification.py +19 -0
- langchain_agentx/task_runtime/tasks/dream_task/semantics.py +13 -0
- langchain_agentx/task_runtime/tasks/dream_task/spec.py +35 -0
- langchain_agentx/task_runtime/tasks/dream_task/state.py +17 -0
- langchain_agentx/task_runtime/tasks/in_process_teammate/__init__.py +12 -0
- langchain_agentx/task_runtime/tasks/in_process_teammate/executor.py +36 -0
- langchain_agentx/task_runtime/tasks/in_process_teammate/notification.py +25 -0
- langchain_agentx/task_runtime/tasks/in_process_teammate/semantics.py +13 -0
- langchain_agentx/task_runtime/tasks/in_process_teammate/spec.py +63 -0
- langchain_agentx/task_runtime/tasks/local_agent/__init__.py +14 -0
- langchain_agentx/task_runtime/tasks/local_agent/executor.py +33 -0
- langchain_agentx/task_runtime/tasks/local_agent/notification.py +21 -0
- langchain_agentx/task_runtime/tasks/local_agent/runner.py +43 -0
- langchain_agentx/task_runtime/tasks/local_agent/semantics.py +13 -0
- langchain_agentx/task_runtime/tasks/local_agent/spec.py +31 -0
- langchain_agentx/task_runtime/tasks/local_bash/__init__.py +13 -0
- langchain_agentx/task_runtime/tasks/local_bash/executor.py +95 -0
- langchain_agentx/task_runtime/tasks/local_bash/notification.py +22 -0
- langchain_agentx/task_runtime/tasks/local_bash/semantics.py +13 -0
- langchain_agentx/task_runtime/tasks/local_bash/spec.py +55 -0
- langchain_agentx/task_runtime/tasks/remote_agent/__init__.py +19 -0
- langchain_agentx/task_runtime/tasks/remote_agent/backend.py +76 -0
- langchain_agentx/task_runtime/tasks/remote_agent/executor.py +37 -0
- langchain_agentx/task_runtime/tasks/remote_agent/notification.py +22 -0
- langchain_agentx/task_runtime/tasks/remote_agent/semantics.py +13 -0
- langchain_agentx/task_runtime/tasks/remote_agent/spec.py +34 -0
- langchain_agentx/task_runtime/tasks/trace_cleanup/__init__.py +19 -0
- langchain_agentx/task_runtime/tasks/trace_cleanup/bootstrap.py +95 -0
- langchain_agentx/task_runtime/tasks/trace_cleanup/executor.py +66 -0
- langchain_agentx/task_runtime/tasks/trace_cleanup/scheduler.py +169 -0
- langchain_agentx/tool_runtime/__init__.py +90 -0
- langchain_agentx/tool_runtime/adapter.py +365 -0
- langchain_agentx/tool_runtime/base.py +319 -0
- langchain_agentx/tool_runtime/errors.py +190 -0
- langchain_agentx/tool_runtime/identical_call_cache.py +110 -0
- langchain_agentx/tool_runtime/loader.py +195 -0
- langchain_agentx/tool_runtime/models.py +260 -0
- langchain_agentx/tool_runtime/permission_context.py +78 -0
- langchain_agentx/tool_runtime/pipeline.py +621 -0
- langchain_agentx/tool_runtime/policy.py +447 -0
- langchain_agentx/tool_runtime/registry.py +81 -0
- langchain_agentx/tool_runtime/resolvers/__init__.py +27 -0
- langchain_agentx/tool_runtime/resolvers/agent_session.py +125 -0
- langchain_agentx/tool_runtime/resolvers/background.py +32 -0
- langchain_agentx/tool_runtime/resolvers/base.py +20 -0
- langchain_agentx/tool_runtime/resolvers/conversation.py +22 -0
- langchain_agentx/tool_runtime/resolvers/workflow.py +73 -0
- langchain_agentx/tool_runtime/session_store.py +132 -0
- langchain_agentx/tool_runtime/smoke_test_runtime.py +294 -0
- langchain_agentx/tool_runtime/state_bridge.py +164 -0
- langchain_agentx/tools/__init__.py +26 -0
- langchain_agentx/tools/agent/__init__.py +9 -0
- langchain_agentx/tools/agent/backend.py +53 -0
- langchain_agentx/tools/agent/built_in/__init__.py +19 -0
- langchain_agentx/tools/agent/built_in/agentx_guide.py +65 -0
- langchain_agentx/tools/agent/built_in/explore.py +80 -0
- langchain_agentx/tools/agent/built_in/general.py +57 -0
- langchain_agentx/tools/agent/built_in/plan.py +89 -0
- langchain_agentx/tools/agent/built_in/statusline_setup.py +64 -0
- langchain_agentx/tools/agent/built_in/verification.py +120 -0
- langchain_agentx/tools/agent/builtin_subagent_loader.py +89 -0
- langchain_agentx/tools/agent/cwd_resolution.py +119 -0
- langchain_agentx/tools/agent/limits.py +26 -0
- langchain_agentx/tools/agent/loader.py +270 -0
- langchain_agentx/tools/agent/models.py +85 -0
- langchain_agentx/tools/agent/prompt.py +120 -0
- langchain_agentx/tools/agent/registry/__init__.py +18 -0
- langchain_agentx/tools/agent/registry/config.py +29 -0
- langchain_agentx/tools/agent/registry/registry.py +47 -0
- langchain_agentx/tools/agent/scope.py +137 -0
- langchain_agentx/tools/agent/tool.py +256 -0
- langchain_agentx/tools/bash/__init__.py +9 -0
- langchain_agentx/tools/bash/ast_security.py +571 -0
- langchain_agentx/tools/bash/backend.py +1447 -0
- langchain_agentx/tools/bash/bash_hardening.py +734 -0
- langchain_agentx/tools/bash/bash_runtime_contract.py +41 -0
- langchain_agentx/tools/bash/cwd_reporter.py +95 -0
- langchain_agentx/tools/bash/limits.py +71 -0
- langchain_agentx/tools/bash/mode_validation.py +282 -0
- langchain_agentx/tools/bash/models.py +131 -0
- langchain_agentx/tools/bash/observability.py +148 -0
- langchain_agentx/tools/bash/output_utils.py +200 -0
- langchain_agentx/tools/bash/path_security.py +2429 -0
- langchain_agentx/tools/bash/prompt.py +68 -0
- langchain_agentx/tools/bash/read_only_validation.py +589 -0
- langchain_agentx/tools/bash/result_presenter.py +324 -0
- langchain_agentx/tools/bash/sandbox_decision.py +133 -0
- langchain_agentx/tools/bash/security.py +311 -0
- langchain_agentx/tools/bash/sed_edit_parser.py +243 -0
- langchain_agentx/tools/bash/sed_validation.py +163 -0
- langchain_agentx/tools/bash/semantics.py +111 -0
- langchain_agentx/tools/bash/session_manager.py +205 -0
- langchain_agentx/tools/bash/session_runtime.py +290 -0
- langchain_agentx/tools/bash/shell_locator.py +191 -0
- langchain_agentx/tools/bash/task_runtime.py +91 -0
- langchain_agentx/tools/bash/tool.py +939 -0
- langchain_agentx/tools/bash/windows_shell_quoting.py +45 -0
- langchain_agentx/tools/glob/__init__.py +9 -0
- langchain_agentx/tools/glob/models.py +57 -0
- langchain_agentx/tools/glob/pagination.py +30 -0
- langchain_agentx/tools/glob/prompt.py +24 -0
- langchain_agentx/tools/glob/rg_list_backend.py +139 -0
- langchain_agentx/tools/glob/rg_pattern.py +44 -0
- langchain_agentx/tools/glob/tool.py +327 -0
- langchain_agentx/tools/grep/__init__.py +7 -0
- langchain_agentx/tools/grep/backend.py +375 -0
- langchain_agentx/tools/grep/models.py +127 -0
- langchain_agentx/tools/grep/prompt.py +30 -0
- langchain_agentx/tools/grep/rg_subprocess_controller.py +114 -0
- langchain_agentx/tools/grep/tool.py +475 -0
- langchain_agentx/tools/read/__init__.py +9 -0
- langchain_agentx/tools/read/backend.py +415 -0
- langchain_agentx/tools/read/limits.py +67 -0
- langchain_agentx/tools/read/models.py +156 -0
- langchain_agentx/tools/read/prompt.py +73 -0
- langchain_agentx/tools/read/tool.py +494 -0
- langchain_agentx/tools/ripgrep_plugin_exclusions.py +137 -0
- langchain_agentx/tools/skill/__init__.py +4 -0
- langchain_agentx/tools/skill/argument_substitution.py +80 -0
- langchain_agentx/tools/skill/loader.py +196 -0
- langchain_agentx/tools/skill/models.py +88 -0
- langchain_agentx/tools/skill/policy.py +80 -0
- langchain_agentx/tools/skill/prompt.py +35 -0
- langchain_agentx/tools/skill/tool.py +222 -0
- langchain_agentx/utils/__init__.py +0 -0
- langchain_agentx/utils/cwd.py +124 -0
- langchain_agentx/utils/host_platform.py +112 -0
- langchain_agentx/utils/path_hierarchy.py +48 -0
- langchain_agentx/utils/path_user_input.py +66 -0
- langchain_agentx/utils/rg_executable.py +18 -0
- langchain_agentx/utils/subprocess_text.py +101 -0
- langchain_agentx/utils/temp_paths.py +77 -0
- langchain_agentx/utils/unc_path.py +25 -0
- langchain_agentx/utils/win_reserved_paths.py +51 -0
- langchain_agentx/workflow/__init__.py +7 -0
- langchain_agentx/workflow/base.py +97 -0
- langchain_agentx/workflow/batch.py +55 -0
- langchain_agentx/workflow/dag.py +54 -0
- langchain_agentx/workspace/__init__.py +13 -0
- langchain_agentx/workspace/config.py +140 -0
- langchain_agentx/workspace/path_key_normalizer.py +30 -0
- langchain_agentx/workspace/resolver.py +74 -0
- langchain_agentx/workspace/validators.py +41 -0
- langchain_agentx_python-0.1.dist-info/LICENSE +201 -0
- langchain_agentx_python-0.1.dist-info/METADATA +513 -0
- langchain_agentx_python-0.1.dist-info/RECORD +354 -0
- langchain_agentx_python-0.1.dist-info/WHEEL +5 -0
- langchain_agentx_python-0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/bash/sed_validation.py — BashRuntimeTool 模块2:sed 命令安全与工作流判定
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
将 `sed` 从普通 Bash 写命令里解耦出来,决定它应该走哪条执行路径:
|
|
6
|
+
1. 普通非原地 `sed`:继续走原有 Bash 权限链
|
|
7
|
+
2. 可稳定预览的 simple `sed -i`:进入 preview -> confirm -> apply
|
|
8
|
+
3. 含危险语义或无法稳定预览的 `sed -i`:保守 ask
|
|
9
|
+
|
|
10
|
+
核心判定点:
|
|
11
|
+
- 是否为 in-place edit(`-i` / `--in-place`)
|
|
12
|
+
- substitution flags 中是否包含 `e/E/w/W` 等危险能力
|
|
13
|
+
- 是否能够通过 `sed_edit_parser.py` 生成确定性 preview
|
|
14
|
+
|
|
15
|
+
与 `tool.py` 的关系:
|
|
16
|
+
`tool.py` 负责编排工作流和暂存 preview 结果;
|
|
17
|
+
本文件只回答“这个 sed 命令是否安全、是否可预览、是否必须人工审批”。
|
|
18
|
+
|
|
19
|
+
与 CC 对照:
|
|
20
|
+
对应 CC `sedValidation.ts` 的基础版能力,当前只覆盖模块 2 需要的高价值分支。
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import re
|
|
26
|
+
import shlex
|
|
27
|
+
from dataclasses import dataclass
|
|
28
|
+
|
|
29
|
+
from .sed_edit_parser import BashSedEditParser, SedEditInfo
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class BashSedCommandCheck:
|
|
34
|
+
is_sed: bool
|
|
35
|
+
is_in_place: bool
|
|
36
|
+
requires_manual_approval: bool
|
|
37
|
+
reason: str | None = None
|
|
38
|
+
edit_info: SedEditInfo | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BashSedCommandValidator:
|
|
42
|
+
"""判断 sed 是否能走 preview/apply,或必须人工审批。"""
|
|
43
|
+
|
|
44
|
+
_DANGEROUS_SUBST_FLAGS = frozenset({"e", "E", "w", "W"})
|
|
45
|
+
|
|
46
|
+
def __init__(self, parser: BashSedEditParser | None = None) -> None:
|
|
47
|
+
self._parser = parser or BashSedEditParser()
|
|
48
|
+
|
|
49
|
+
def evaluate(self, command: str) -> BashSedCommandCheck:
|
|
50
|
+
tokens = self._split(command)
|
|
51
|
+
if not tokens or tokens[0] != "sed":
|
|
52
|
+
return BashSedCommandCheck(False, False, False)
|
|
53
|
+
|
|
54
|
+
in_place = self._has_in_place_flag(tokens[1:])
|
|
55
|
+
expressions = self._collect_inline_expressions(tokens[1:])
|
|
56
|
+
if self._contains_dangerous_expression(expressions):
|
|
57
|
+
return BashSedCommandCheck(
|
|
58
|
+
is_sed=True,
|
|
59
|
+
is_in_place=in_place,
|
|
60
|
+
requires_manual_approval=True,
|
|
61
|
+
reason=(
|
|
62
|
+
"sed command requires explicit approval because it contains potentially "
|
|
63
|
+
"dangerous write/execute operations."
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if not in_place:
|
|
68
|
+
return BashSedCommandCheck(
|
|
69
|
+
is_sed=True,
|
|
70
|
+
is_in_place=False,
|
|
71
|
+
requires_manual_approval=False,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
edit_info = self._parser.parse(command)
|
|
75
|
+
if edit_info is None:
|
|
76
|
+
return BashSedCommandCheck(
|
|
77
|
+
is_sed=True,
|
|
78
|
+
is_in_place=True,
|
|
79
|
+
requires_manual_approval=True,
|
|
80
|
+
reason=(
|
|
81
|
+
"sed -i command requires explicit approval because a deterministic preview "
|
|
82
|
+
"cannot be generated safely."
|
|
83
|
+
),
|
|
84
|
+
)
|
|
85
|
+
return BashSedCommandCheck(
|
|
86
|
+
is_sed=True,
|
|
87
|
+
is_in_place=True,
|
|
88
|
+
requires_manual_approval=False,
|
|
89
|
+
edit_info=edit_info,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _split(command: str) -> list[str]:
|
|
94
|
+
try:
|
|
95
|
+
return shlex.split(command)
|
|
96
|
+
except ValueError:
|
|
97
|
+
return command.split()
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _has_in_place_flag(args: list[str]) -> bool:
|
|
101
|
+
return any(arg in {"-i", "--in-place"} or arg.startswith("-i") for arg in args)
|
|
102
|
+
|
|
103
|
+
def _collect_inline_expressions(self, args: list[str]) -> list[str]:
|
|
104
|
+
expressions: list[str] = []
|
|
105
|
+
i = 0
|
|
106
|
+
while i < len(args):
|
|
107
|
+
arg = args[i]
|
|
108
|
+
if arg in {"-e", "--expression"} and i + 1 < len(args):
|
|
109
|
+
expressions.append(args[i + 1])
|
|
110
|
+
i += 2
|
|
111
|
+
continue
|
|
112
|
+
if arg.startswith("--expression="):
|
|
113
|
+
expressions.append(arg.split("=", 1)[1])
|
|
114
|
+
i += 1
|
|
115
|
+
continue
|
|
116
|
+
if arg.startswith("-"):
|
|
117
|
+
i += 1
|
|
118
|
+
continue
|
|
119
|
+
if not expressions:
|
|
120
|
+
expressions.append(arg)
|
|
121
|
+
i += 1
|
|
122
|
+
return expressions
|
|
123
|
+
|
|
124
|
+
def _contains_dangerous_expression(self, expressions: list[str]) -> bool:
|
|
125
|
+
for expression in expressions:
|
|
126
|
+
parsed = self._parser.parse(f"sed -i {shlex.quote(expression)} dummy.txt")
|
|
127
|
+
if parsed is None:
|
|
128
|
+
dangerous_flags = self._extract_substitution_flags(expression)
|
|
129
|
+
if any(flag in self._DANGEROUS_SUBST_FLAGS for flag in dangerous_flags):
|
|
130
|
+
return True
|
|
131
|
+
if re.search(r"(^|[;\s])[weWE](?:[\s]|$)", expression):
|
|
132
|
+
return True
|
|
133
|
+
continue
|
|
134
|
+
if any(flag in self._DANGEROUS_SUBST_FLAGS for flag in parsed.flags):
|
|
135
|
+
return True
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
def _extract_substitution_flags(expression: str) -> str:
|
|
140
|
+
if not expression.startswith("s/"):
|
|
141
|
+
return ""
|
|
142
|
+
rest = expression[2:]
|
|
143
|
+
state = "pattern"
|
|
144
|
+
flags = ""
|
|
145
|
+
i = 0
|
|
146
|
+
while i < len(rest):
|
|
147
|
+
char = rest[i]
|
|
148
|
+
if char == "\\" and i + 1 < len(rest):
|
|
149
|
+
i += 2
|
|
150
|
+
continue
|
|
151
|
+
if char == "/":
|
|
152
|
+
if state == "pattern":
|
|
153
|
+
state = "replacement"
|
|
154
|
+
elif state == "replacement":
|
|
155
|
+
state = "flags"
|
|
156
|
+
else:
|
|
157
|
+
return flags
|
|
158
|
+
i += 1
|
|
159
|
+
continue
|
|
160
|
+
if state == "flags":
|
|
161
|
+
flags += char
|
|
162
|
+
i += 1
|
|
163
|
+
return flags if state == "flags" else ""
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/bash/semantics.py — 命令退出码语义解析
|
|
3
|
+
|
|
4
|
+
某些命令以非零退出码表达"正常信息"而非"错误",需要特殊解释,
|
|
5
|
+
否则工具框架会将其包装为 error envelope,导致模型误认为命令失败。
|
|
6
|
+
|
|
7
|
+
对应 CC commandSemantics.ts 的 interpretCommandResult()。
|
|
8
|
+
|
|
9
|
+
规则定义格式:
|
|
10
|
+
(命令匹配正则, {退出码: 语义描述})
|
|
11
|
+
语义描述为 None 表示该退出码视为成功(无 hint)
|
|
12
|
+
语义描述为字符串表示非错误但需要说明(作为 hint 展示)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
# 规则表(对应 CC commandSemantics.ts)
|
|
22
|
+
# ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
# (command_pattern, {exit_code: hint_message | None})
|
|
25
|
+
# hint_message=None → 视为成功,无需说明
|
|
26
|
+
# hint_message=str → 视为成功,present() 中以 hint 形式告知模型
|
|
27
|
+
_SEMANTIC_RULES: list[tuple[re.Pattern[str], dict[int, str | None]]] = [
|
|
28
|
+
# grep / rg / ag / ack:返回 1 = 无匹配(非错误)
|
|
29
|
+
(
|
|
30
|
+
re.compile(r"^(?:grep|rg|ag|ack|ugrep)\b"),
|
|
31
|
+
{1: "No matches found (exit code 1 is normal for grep when no lines match)"},
|
|
32
|
+
),
|
|
33
|
+
# diff:返回 1 = 文件存在差异(非错误,是正常信息)
|
|
34
|
+
(
|
|
35
|
+
re.compile(r"^diff\b"),
|
|
36
|
+
{1: "Files differ (exit code 1 is normal for diff when files are different)"},
|
|
37
|
+
),
|
|
38
|
+
# git diff / git log / git show:同 diff
|
|
39
|
+
(
|
|
40
|
+
re.compile(r"^git\s+(?:diff|log|show)\b"),
|
|
41
|
+
{1: "Files differ"},
|
|
42
|
+
),
|
|
43
|
+
# test / [ ] / [[ ]]:返回 1 = 条件为假(正常行为)
|
|
44
|
+
# 注意:[ 和 [[ 不是字母字符,\b 在其后不生效,需要匹配后面的空格或行尾
|
|
45
|
+
(
|
|
46
|
+
re.compile(r"^(?:test(?:\s|$)|\[{1,2}(?:\s|$))"),
|
|
47
|
+
{1: "Condition is false (exit code 1 is normal for test/[ ] when condition is false)"},
|
|
48
|
+
),
|
|
49
|
+
# cmp:返回 1 = 文件不同,返回 2 = 无法比较(错误)
|
|
50
|
+
(
|
|
51
|
+
re.compile(r"^cmp\b"),
|
|
52
|
+
{1: "Files are different (exit code 1 is normal for cmp when files differ)"},
|
|
53
|
+
),
|
|
54
|
+
# ls:返回 2 = 无访问权限/不存在(真正错误,不覆盖)
|
|
55
|
+
# find:返回 1 = 无匹配或权限问题(某些 find 实现)
|
|
56
|
+
(
|
|
57
|
+
re.compile(r"^find\b"),
|
|
58
|
+
{1: None}, # 视为成功(find 无结果时返回 0 或 1 取决于实现)
|
|
59
|
+
),
|
|
60
|
+
# git:某些子命令如 git fetch --dry-run 可能返回 1 表示有变更
|
|
61
|
+
(
|
|
62
|
+
re.compile(r"^git\s+fetch\b"),
|
|
63
|
+
{1: None},
|
|
64
|
+
),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class CommandResult:
|
|
70
|
+
"""
|
|
71
|
+
命令退出码语义解析结果。
|
|
72
|
+
|
|
73
|
+
is_error=True → 框架应将其视为错误(包装为 error envelope / 抛异常)
|
|
74
|
+
is_error=False → 视为成功,message 作为 hint 展示给模型
|
|
75
|
+
message → 语义说明(非空时注入 present() 的 hints)
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
is_error: bool
|
|
79
|
+
message: str | None = None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def interpret_command_result(
|
|
83
|
+
command: str,
|
|
84
|
+
exit_code: int,
|
|
85
|
+
stdout: str, # noqa: ARG001 保留参数供将来内容感知判断使用
|
|
86
|
+
) -> CommandResult:
|
|
87
|
+
"""
|
|
88
|
+
解析命令退出码,判断是否为真正错误。
|
|
89
|
+
|
|
90
|
+
返回:
|
|
91
|
+
CommandResult.is_error=False → 非错误,可能包含 hint 说明
|
|
92
|
+
CommandResult.is_error=True → 真正的执行错误
|
|
93
|
+
|
|
94
|
+
对应 CC commandSemantics.ts::interpretCommandResult()。
|
|
95
|
+
"""
|
|
96
|
+
if exit_code == 0:
|
|
97
|
+
return CommandResult(is_error=False)
|
|
98
|
+
|
|
99
|
+
# 提取命令基础部分(去掉参数)用于正则匹配
|
|
100
|
+
cmd_stripped = command.strip()
|
|
101
|
+
|
|
102
|
+
for pattern, code_map in _SEMANTIC_RULES:
|
|
103
|
+
if pattern.match(cmd_stripped):
|
|
104
|
+
if exit_code in code_map:
|
|
105
|
+
hint = code_map[exit_code]
|
|
106
|
+
return CommandResult(is_error=False, message=hint)
|
|
107
|
+
# 命令匹配但退出码不在规则表 → 视为错误
|
|
108
|
+
return CommandResult(is_error=True)
|
|
109
|
+
|
|
110
|
+
# 未命中任何规则 → 非零退出码视为错误
|
|
111
|
+
return CommandResult(is_error=True)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tools/bash/session_manager.py — BashRuntimeTool v3:会话管理器
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
管理多个 `BashSessionRuntime` 的创建、复用与销毁,保证:
|
|
6
|
+
1. 同一 session_key 复用同一长期 shell
|
|
7
|
+
2. 多会话之间状态隔离
|
|
8
|
+
3. 空闲会话按 TTL 清理
|
|
9
|
+
|
|
10
|
+
与 CC 对照:
|
|
11
|
+
对应 CC 长期 shell 任务管理模型的 Python 化基础设施。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
|
|
21
|
+
from .session_runtime import BashSessionRuntime
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class _SessionEntry:
|
|
26
|
+
runtime: BashSessionRuntime
|
|
27
|
+
last_used_ts: float
|
|
28
|
+
created_ts: float
|
|
29
|
+
command_count: int = 0
|
|
30
|
+
recycled_count: int = 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class BashSessionAuditRecord:
|
|
35
|
+
session_key: str
|
|
36
|
+
event: str
|
|
37
|
+
ts: float
|
|
38
|
+
details: dict[str, str]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BashSessionManager:
|
|
42
|
+
"""Bash 长期会话管理器。"""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
*,
|
|
47
|
+
idle_ttl_sec: int = 900,
|
|
48
|
+
max_sessions: int = 16,
|
|
49
|
+
max_commands_per_session: int = 200,
|
|
50
|
+
) -> None:
|
|
51
|
+
self._idle_ttl_sec = max(60, idle_ttl_sec)
|
|
52
|
+
self._max_sessions = max(1, max_sessions)
|
|
53
|
+
# 允许小阈值以便测试与细粒度回收(最小为 1)
|
|
54
|
+
self._max_commands_per_session = max(1, max_commands_per_session)
|
|
55
|
+
self._sessions: dict[str, _SessionEntry] = {}
|
|
56
|
+
self._audit_log: list[BashSessionAuditRecord] = []
|
|
57
|
+
self._lock = threading.Lock()
|
|
58
|
+
|
|
59
|
+
def get_or_create(
|
|
60
|
+
self,
|
|
61
|
+
*,
|
|
62
|
+
session_key: str,
|
|
63
|
+
cwd: str | None,
|
|
64
|
+
env: dict[str, str],
|
|
65
|
+
) -> BashSessionRuntime:
|
|
66
|
+
now = time.time()
|
|
67
|
+
with self._lock:
|
|
68
|
+
self._cleanup_expired_locked(now)
|
|
69
|
+
self._enforce_max_sessions_locked(now)
|
|
70
|
+
entry = self._sessions.get(session_key)
|
|
71
|
+
if entry is None:
|
|
72
|
+
runtime = BashSessionRuntime(
|
|
73
|
+
initial_cwd=cwd,
|
|
74
|
+
base_env=env,
|
|
75
|
+
)
|
|
76
|
+
entry = _SessionEntry(runtime=runtime, last_used_ts=now, created_ts=now)
|
|
77
|
+
self._sessions[session_key] = entry
|
|
78
|
+
self._append_audit_locked(
|
|
79
|
+
session_key=session_key,
|
|
80
|
+
event="created",
|
|
81
|
+
details={"cwd": cwd or "", "env_size": str(len(env))},
|
|
82
|
+
)
|
|
83
|
+
else:
|
|
84
|
+
entry.last_used_ts = now
|
|
85
|
+
if entry.command_count >= self._max_commands_per_session:
|
|
86
|
+
entry.runtime.close()
|
|
87
|
+
entry.runtime = BashSessionRuntime(initial_cwd=cwd, base_env=env)
|
|
88
|
+
entry.command_count = 0
|
|
89
|
+
entry.recycled_count += 1
|
|
90
|
+
self._append_audit_locked(
|
|
91
|
+
session_key=session_key,
|
|
92
|
+
event="recycled_command_limit",
|
|
93
|
+
details={"recycled_count": str(entry.recycled_count)},
|
|
94
|
+
)
|
|
95
|
+
entry.command_count += 1
|
|
96
|
+
return entry.runtime
|
|
97
|
+
|
|
98
|
+
def reset_session(self, session_key: str, *, cwd: str | None = None, env: dict[str, str] | None = None) -> None:
|
|
99
|
+
with self._lock:
|
|
100
|
+
old = self._sessions.pop(session_key, None)
|
|
101
|
+
if old is not None:
|
|
102
|
+
old.runtime.close()
|
|
103
|
+
runtime = BashSessionRuntime(initial_cwd=cwd, base_env=(env or os.environ.copy()))
|
|
104
|
+
now = time.time()
|
|
105
|
+
self._sessions[session_key] = _SessionEntry(
|
|
106
|
+
runtime=runtime,
|
|
107
|
+
last_used_ts=now,
|
|
108
|
+
created_ts=now,
|
|
109
|
+
command_count=0,
|
|
110
|
+
recycled_count=0,
|
|
111
|
+
)
|
|
112
|
+
self._append_audit_locked(
|
|
113
|
+
session_key=session_key,
|
|
114
|
+
event="reset",
|
|
115
|
+
details={"cwd": cwd or "", "env_size": str(len(env or {}))},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
def close_session(self, session_key: str) -> None:
|
|
119
|
+
with self._lock:
|
|
120
|
+
entry = self._sessions.pop(session_key, None)
|
|
121
|
+
if entry is not None:
|
|
122
|
+
entry.runtime.close()
|
|
123
|
+
with self._lock:
|
|
124
|
+
self._append_audit_locked(
|
|
125
|
+
session_key=session_key,
|
|
126
|
+
event="closed",
|
|
127
|
+
details={},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def close_all(self) -> None:
|
|
131
|
+
with self._lock:
|
|
132
|
+
entries = list(self._sessions.values())
|
|
133
|
+
self._sessions.clear()
|
|
134
|
+
for entry in entries:
|
|
135
|
+
entry.runtime.close()
|
|
136
|
+
|
|
137
|
+
def _cleanup_expired_locked(self, now_ts: float) -> None:
|
|
138
|
+
expired_keys = [
|
|
139
|
+
key
|
|
140
|
+
for key, entry in self._sessions.items()
|
|
141
|
+
if (now_ts - entry.last_used_ts) > self._idle_ttl_sec
|
|
142
|
+
]
|
|
143
|
+
for key in expired_keys:
|
|
144
|
+
entry = self._sessions.pop(key, None)
|
|
145
|
+
if entry is not None:
|
|
146
|
+
entry.runtime.close()
|
|
147
|
+
self._append_audit_locked(
|
|
148
|
+
session_key=key,
|
|
149
|
+
event="expired",
|
|
150
|
+
details={"idle_ttl_sec": str(self._idle_ttl_sec)},
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def _enforce_max_sessions_locked(self, now_ts: float) -> None:
|
|
154
|
+
if len(self._sessions) < self._max_sessions:
|
|
155
|
+
return
|
|
156
|
+
oldest_key = min(
|
|
157
|
+
self._sessions.keys(),
|
|
158
|
+
key=lambda k: self._sessions[k].last_used_ts,
|
|
159
|
+
)
|
|
160
|
+
oldest = self._sessions.pop(oldest_key, None)
|
|
161
|
+
if oldest is not None:
|
|
162
|
+
oldest.runtime.close()
|
|
163
|
+
self._append_audit_locked(
|
|
164
|
+
session_key=oldest_key,
|
|
165
|
+
event="evicted_max_sessions",
|
|
166
|
+
details={"max_sessions": str(self._max_sessions), "ts": str(now_ts)},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def get_audit_log(self, *, tail: int = 200) -> list[BashSessionAuditRecord]:
|
|
170
|
+
with self._lock:
|
|
171
|
+
if tail <= 0:
|
|
172
|
+
return []
|
|
173
|
+
return list(self._audit_log[-tail:])
|
|
174
|
+
|
|
175
|
+
def _append_audit_locked(self, *, session_key: str, event: str, details: dict[str, str]) -> None:
|
|
176
|
+
self._audit_log.append(
|
|
177
|
+
BashSessionAuditRecord(
|
|
178
|
+
session_key=session_key,
|
|
179
|
+
event=event,
|
|
180
|
+
ts=time.time(),
|
|
181
|
+
details=details,
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
if len(self._audit_log) > 2000:
|
|
185
|
+
self._audit_log = self._audit_log[-1000:]
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
_GLOBAL_SESSION_MANAGER: BashSessionManager | None = None
|
|
189
|
+
_GLOBAL_SESSION_MANAGER_LOCK = threading.Lock()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_global_bash_session_manager() -> BashSessionManager:
|
|
193
|
+
"""获取全局会话管理器(延迟初始化)。"""
|
|
194
|
+
global _GLOBAL_SESSION_MANAGER
|
|
195
|
+
with _GLOBAL_SESSION_MANAGER_LOCK:
|
|
196
|
+
if _GLOBAL_SESSION_MANAGER is None:
|
|
197
|
+
ttl = int(os.getenv("AGENTX_BASH_SESSION_IDLE_TTL_SEC", "900"))
|
|
198
|
+
max_sessions = int(os.getenv("AGENTX_BASH_MAX_SESSIONS", "16"))
|
|
199
|
+
max_commands = int(os.getenv("AGENTX_BASH_MAX_COMMANDS_PER_SESSION", "200"))
|
|
200
|
+
_GLOBAL_SESSION_MANAGER = BashSessionManager(
|
|
201
|
+
idle_ttl_sec=ttl,
|
|
202
|
+
max_sessions=max_sessions,
|
|
203
|
+
max_commands_per_session=max_commands,
|
|
204
|
+
)
|
|
205
|
+
return _GLOBAL_SESSION_MANAGER
|