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,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
loop/runtime/subagent_execution_paths.py — 子 Agent 工作目录与 worktree 单点准备
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
计算子会话的 ``effective_cwd``(供 ``run_with_cwd_override`` / ``AgentLoopConfig.cwd`` /
|
|
6
|
+
``SubagentContext.cwd`` 同源),并在 ``isolation=worktree`` 时创建唯一临时目录。
|
|
7
|
+
链路位置:
|
|
8
|
+
``SubagentOrchestrator._derive_runtime_context`` → ``prepare`` → ``RuntimeContextFactory.derive``;
|
|
9
|
+
``create_subagent_context(..., path_prep=...)`` 消费本结构,避免重复 ``mkdir``。
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅 ``none`` / ``worktree``;不处理 CC ``remote`` isolation。路径展开 / 权限校验留在 Phase 2。
|
|
12
|
+
CC 对照:
|
|
13
|
+
``AgentTool.tsx`` 中 ``cwdOverridePath = cwd ?? worktreeInfo?.worktreePath``。
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import uuid
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from langchain_agentx.loop.runtime.context import SubagentContextOverrides
|
|
24
|
+
from langchain_agentx.workspace import resolve_agent_workspace_config
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(frozen=True)
|
|
28
|
+
class PreparedSubagentPaths:
|
|
29
|
+
"""``SubagentExecutionPaths.prepare`` 的输出:最终 cwd 与可选 worktree 目录。"""
|
|
30
|
+
|
|
31
|
+
effective_cwd: str
|
|
32
|
+
worktree_dir: str | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SubagentExecutionPaths:
|
|
36
|
+
"""子 Agent 路径准备(OOP 单点,禁止在 orchestrator 内堆叠 mkdir 逻辑)。"""
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def prepare(
|
|
40
|
+
*,
|
|
41
|
+
parent_ctx: Any,
|
|
42
|
+
overrides: "SubagentContextOverrides",
|
|
43
|
+
) -> PreparedSubagentPaths:
|
|
44
|
+
"""解析 ``effective_cwd``,并在 worktree 模式下创建临时目录。
|
|
45
|
+
|
|
46
|
+
顺序对齐 CC:**显式 ``overrides.cwd`` 优先于 worktree 目录**(``cwd ?? worktreePath``)。
|
|
47
|
+
"""
|
|
48
|
+
parent_cwd = getattr(parent_ctx, "cwd", None)
|
|
49
|
+
parent_workspace_root = getattr(parent_ctx, "workspace_root", None) or os.getcwd()
|
|
50
|
+
parent_agent_home = getattr(parent_ctx, "agent_home", ".langchain_agentx")
|
|
51
|
+
raw = overrides.cwd
|
|
52
|
+
override_cwd = raw.strip() if isinstance(raw, str) and raw.strip() else None
|
|
53
|
+
|
|
54
|
+
if overrides.isolation == "worktree":
|
|
55
|
+
workspace_cfg = resolve_agent_workspace_config(
|
|
56
|
+
workspace_root=parent_workspace_root,
|
|
57
|
+
agent_home=parent_agent_home,
|
|
58
|
+
)
|
|
59
|
+
workspace_cfg.worktrees_dir.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
worktree_dir = str(
|
|
61
|
+
workspace_cfg.worktrees_dir / f"agentx-worktree-{uuid.uuid4().hex[:12]}"
|
|
62
|
+
)
|
|
63
|
+
os.makedirs(worktree_dir, exist_ok=False)
|
|
64
|
+
effective = override_cwd or worktree_dir
|
|
65
|
+
return PreparedSubagentPaths(effective_cwd=effective, worktree_dir=worktree_dir)
|
|
66
|
+
|
|
67
|
+
base = override_cwd or parent_cwd or os.getcwd()
|
|
68
|
+
return PreparedSubagentPaths(effective_cwd=base, worktree_dir=None)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Loop Subagent 框架
|
|
2
|
+
|
|
3
|
+
职责:子 Agent 执行框架的公共接口导出
|
|
4
|
+
在整体链路中的位置:被 tools/agent/tool.py 调用
|
|
5
|
+
CC 对照:src/agent/subagent/ 目录
|
|
6
|
+
当前裁剪范围:v1 同步执行路径,stub runner
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from langchain_agentx.loop.subagent.context import (
|
|
10
|
+
SubagentContext,
|
|
11
|
+
SubagentResult,
|
|
12
|
+
cleanup_subagent_context,
|
|
13
|
+
create_subagent_context,
|
|
14
|
+
)
|
|
15
|
+
from langchain_agentx.loop.subagent.async_runner import (
|
|
16
|
+
BackgroundAgentHandle,
|
|
17
|
+
get_background_agent_handle,
|
|
18
|
+
register_background_agent,
|
|
19
|
+
run_async_agent_lifecycle,
|
|
20
|
+
)
|
|
21
|
+
from langchain_agentx.loop.subagent.progress import SubagentProgressEvent
|
|
22
|
+
from langchain_agentx.loop.subagent.runner import (
|
|
23
|
+
BackgroundLaunchedResult,
|
|
24
|
+
_run_coroutine_sync,
|
|
25
|
+
run_subagent,
|
|
26
|
+
run_subagent_with_background_signal,
|
|
27
|
+
)
|
|
28
|
+
from langchain_agentx.loop.subagent.transcript import (
|
|
29
|
+
JsonlTranscriptStore,
|
|
30
|
+
SubagentTranscriptManager,
|
|
31
|
+
SubagentTranscriptStore,
|
|
32
|
+
)
|
|
33
|
+
from langchain_agentx.loop.subagent.graph import create_subagent_graph
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"SubagentContext",
|
|
37
|
+
"SubagentResult",
|
|
38
|
+
"create_subagent_context",
|
|
39
|
+
"cleanup_subagent_context",
|
|
40
|
+
"SubagentProgressEvent",
|
|
41
|
+
"run_subagent",
|
|
42
|
+
"run_subagent_with_background_signal",
|
|
43
|
+
"BackgroundLaunchedResult",
|
|
44
|
+
"_run_coroutine_sync",
|
|
45
|
+
"register_background_agent",
|
|
46
|
+
"get_background_agent_handle",
|
|
47
|
+
"BackgroundAgentHandle",
|
|
48
|
+
"run_async_agent_lifecycle",
|
|
49
|
+
"JsonlTranscriptStore",
|
|
50
|
+
"SubagentTranscriptManager",
|
|
51
|
+
"SubagentTranscriptStore",
|
|
52
|
+
"create_subagent_graph",
|
|
53
|
+
]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
loop/subagent/async_runner.py — 子 Agent 异步生命周期管理
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
- register_background_agent():threading 后台启动子 Agent(旧接口,供 v1 兼容)
|
|
6
|
+
- run_async_agent_lifecycle():coordinate 模式纯 async 生命周期(计划接口,供 AgentRuntimeTool 调用)
|
|
7
|
+
在整体链路中的位置:AgentRuntimeTool.invoke(is_async=True) → run_async_agent_lifecycle()
|
|
8
|
+
CC 对照:agentToolUtils.ts runAsyncAgentLifecycle();LocalAgentTask enqueueAgentNotification()
|
|
9
|
+
当前裁剪范围:v1 基础版;完整 worktree 清理 / output_file 管理为 v2
|
|
10
|
+
|
|
11
|
+
执行流程(run_async_agent_lifecycle):
|
|
12
|
+
1. 构造 output_file 路径,确保父目录存在
|
|
13
|
+
2. task_runtime.update(task_id, {status: running})
|
|
14
|
+
3. 临时置 ctx.is_async=False,调用 run_subagent() 执行子图
|
|
15
|
+
4. 写 output_file(result.content)
|
|
16
|
+
5. task_runtime.mark_terminal(completed/failed)
|
|
17
|
+
异常分支:
|
|
18
|
+
CancelledError → 写 "cancelled",mark_terminal(failed, terminal_reason="cancelled"),re-raise
|
|
19
|
+
其他 Exception → 写错误信息,mark_terminal(failed)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import asyncio
|
|
25
|
+
import os
|
|
26
|
+
import threading
|
|
27
|
+
import uuid
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from typing import Any, Callable
|
|
30
|
+
|
|
31
|
+
from langchain_agentx.task_runtime.core.types import TaskScope, TaskSpec, TaskStatus, TaskType
|
|
32
|
+
|
|
33
|
+
from .context import SubagentContext
|
|
34
|
+
from .runner import run_subagent
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class BackgroundAgentHandle:
|
|
39
|
+
task_id: str
|
|
40
|
+
agent_id: str
|
|
41
|
+
output_file: str
|
|
42
|
+
thread: threading.Thread
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
_BACKGROUND_HANDLES: dict[str, BackgroundAgentHandle] = {}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _ensure_parent(path: str) -> None:
|
|
49
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _resolve_task_runtime(ctx: Any) -> Any | None:
|
|
53
|
+
runtime = getattr(ctx, "task_runtime", None)
|
|
54
|
+
if runtime is not None:
|
|
55
|
+
return runtime
|
|
56
|
+
state = getattr(ctx, "state", {})
|
|
57
|
+
if isinstance(state, dict):
|
|
58
|
+
return state.get("task_runtime")
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _spawn_task_record(task_runtime: Any, ctx: SubagentContext, description: str) -> str:
|
|
63
|
+
if task_runtime is None:
|
|
64
|
+
return str(uuid.uuid4())
|
|
65
|
+
spec = TaskSpec(
|
|
66
|
+
task_type=TaskType.LOCAL_AGENT,
|
|
67
|
+
description=description,
|
|
68
|
+
input={"prompt": description, "agent_label": ctx.subagent_type},
|
|
69
|
+
scope=TaskScope(agent_id=ctx.parent_agent_id),
|
|
70
|
+
)
|
|
71
|
+
return task_runtime.spawn(spec)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def register_background_agent(
|
|
75
|
+
*,
|
|
76
|
+
ctx: SubagentContext,
|
|
77
|
+
loader: Any,
|
|
78
|
+
model: Any | None,
|
|
79
|
+
output_dir: str,
|
|
80
|
+
description: str,
|
|
81
|
+
on_progress: Callable[[Any], None] | None = None,
|
|
82
|
+
parent_ctx: Any | None = None,
|
|
83
|
+
) -> BackgroundAgentHandle:
|
|
84
|
+
"""注册并启动后台子 Agent。"""
|
|
85
|
+
task_runtime = _resolve_task_runtime(parent_ctx)
|
|
86
|
+
task_id = _spawn_task_record(task_runtime, ctx, description)
|
|
87
|
+
output_file = os.path.join(output_dir, task_id, "output.txt")
|
|
88
|
+
_ensure_parent(output_file)
|
|
89
|
+
|
|
90
|
+
def _run() -> None:
|
|
91
|
+
async def _lifecycle() -> None:
|
|
92
|
+
try:
|
|
93
|
+
result = await run_subagent(
|
|
94
|
+
ctx=ctx,
|
|
95
|
+
loader=loader,
|
|
96
|
+
model=model,
|
|
97
|
+
on_progress=on_progress,
|
|
98
|
+
)
|
|
99
|
+
with open(output_file, "w", encoding="utf-8") as fp:
|
|
100
|
+
fp.write(result.content)
|
|
101
|
+
if task_runtime is not None:
|
|
102
|
+
status = TaskStatus.COMPLETED if result.status == "completed" else TaskStatus.FAILED
|
|
103
|
+
task_runtime.mark_terminal(
|
|
104
|
+
task_id,
|
|
105
|
+
status,
|
|
106
|
+
result.content[:500] if result.content else (result.error_message or "subagent completed"),
|
|
107
|
+
output_file=output_file,
|
|
108
|
+
terminal_reason=result.terminal_reason or None,
|
|
109
|
+
)
|
|
110
|
+
except Exception as exc:
|
|
111
|
+
with open(output_file, "w", encoding="utf-8") as fp:
|
|
112
|
+
fp.write(str(exc))
|
|
113
|
+
if task_runtime is not None:
|
|
114
|
+
task_runtime.mark_terminal(
|
|
115
|
+
task_id,
|
|
116
|
+
TaskStatus.FAILED,
|
|
117
|
+
f"Subagent background lifecycle failed: {exc}",
|
|
118
|
+
output_file=output_file,
|
|
119
|
+
terminal_reason="error",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
asyncio.run(_lifecycle())
|
|
123
|
+
|
|
124
|
+
thread = threading.Thread(target=_run, daemon=True, name=f"agentx-subagent-{task_id}")
|
|
125
|
+
thread.start()
|
|
126
|
+
handle = BackgroundAgentHandle(
|
|
127
|
+
task_id=task_id,
|
|
128
|
+
agent_id=ctx.agent_id,
|
|
129
|
+
output_file=output_file,
|
|
130
|
+
thread=thread,
|
|
131
|
+
)
|
|
132
|
+
_BACKGROUND_HANDLES[task_id] = handle
|
|
133
|
+
return handle
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_background_agent_handle(task_id: str) -> BackgroundAgentHandle | None:
|
|
137
|
+
return _BACKGROUND_HANDLES.get(task_id)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
async def run_async_agent_lifecycle(
|
|
141
|
+
task_id: str,
|
|
142
|
+
ctx: SubagentContext,
|
|
143
|
+
loader: Any,
|
|
144
|
+
task_runtime: Any,
|
|
145
|
+
output_dir: str,
|
|
146
|
+
) -> None:
|
|
147
|
+
"""coordinate 模式异步生命周期:后台执行子 Agent,完成后通过 task_runtime 写通知。
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
task_id: 已由调用方在 task_runtime 中注册的任务 ID
|
|
151
|
+
ctx: 子 Agent 执行上下文(is_async 应为 True)
|
|
152
|
+
tools: 工具列表
|
|
153
|
+
task_runtime: TaskRuntime 实例,用于写终态通知
|
|
154
|
+
output_dir: 输出文件根目录
|
|
155
|
+
|
|
156
|
+
执行流程:
|
|
157
|
+
1. mark_terminal(running)
|
|
158
|
+
2. run_subagent()
|
|
159
|
+
3. 写 output_file
|
|
160
|
+
4. mark_terminal(completed/failed)
|
|
161
|
+
|
|
162
|
+
CancelledError 单独捕获,写 cancelled 状态后 re-raise。
|
|
163
|
+
"""
|
|
164
|
+
output_file = os.path.join(output_dir, task_id, "output.txt")
|
|
165
|
+
_ensure_parent(output_file)
|
|
166
|
+
|
|
167
|
+
if task_runtime is not None:
|
|
168
|
+
try:
|
|
169
|
+
task_runtime.update(task_id, {"status": TaskStatus.RUNNING.value})
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
# 子会话 is_async=True 时 run_subagent 会抛 RuntimeError,
|
|
175
|
+
# 此处临时置 False 走真实执行路径(lifecycle 本身已是异步上下文)
|
|
176
|
+
ctx.is_async = False
|
|
177
|
+
result = await run_subagent(ctx=ctx, loader=loader, model=None)
|
|
178
|
+
|
|
179
|
+
with open(output_file, "w", encoding="utf-8") as fp:
|
|
180
|
+
fp.write(result.content)
|
|
181
|
+
|
|
182
|
+
if task_runtime is not None:
|
|
183
|
+
status = TaskStatus.COMPLETED if result.status == "completed" else TaskStatus.FAILED
|
|
184
|
+
task_runtime.mark_terminal(
|
|
185
|
+
task_id,
|
|
186
|
+
status,
|
|
187
|
+
result.content[:500] if result.content else (result.error_message or "done"),
|
|
188
|
+
output_file=output_file,
|
|
189
|
+
terminal_reason=result.terminal_reason or None,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
except asyncio.CancelledError:
|
|
193
|
+
with open(output_file, "w", encoding="utf-8") as fp:
|
|
194
|
+
fp.write("cancelled")
|
|
195
|
+
if task_runtime is not None:
|
|
196
|
+
try:
|
|
197
|
+
task_runtime.mark_terminal(
|
|
198
|
+
task_id, TaskStatus.FAILED, "subagent cancelled",
|
|
199
|
+
output_file=output_file, terminal_reason="cancelled",
|
|
200
|
+
)
|
|
201
|
+
except Exception:
|
|
202
|
+
pass
|
|
203
|
+
raise
|
|
204
|
+
|
|
205
|
+
except Exception as exc:
|
|
206
|
+
with open(output_file, "w", encoding="utf-8") as fp:
|
|
207
|
+
fp.write(str(exc))
|
|
208
|
+
if task_runtime is not None:
|
|
209
|
+
task_runtime.mark_terminal(
|
|
210
|
+
task_id,
|
|
211
|
+
TaskStatus.FAILED,
|
|
212
|
+
f"subagent failed: {exc}",
|
|
213
|
+
output_file=output_file,
|
|
214
|
+
terminal_reason="error",
|
|
215
|
+
)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""Subagent Context 与 Result 数据结构
|
|
2
|
+
|
|
3
|
+
职责:封装子会话的独立状态与继承自父会话的共享引用
|
|
4
|
+
在整体链路中的位置:create_subagent_context() 构造 → run_subagent() 消费
|
|
5
|
+
CC 对照:src/agent/subagent/context.ts SubagentContext 接口
|
|
6
|
+
当前裁剪范围:v1 全量;system_prompt / is_async 字段驱动执行路径选择;
|
|
7
|
+
``path_prep`` 与遗留 ``cwd``/worktree 自建二选一(编排层须传 ``path_prep``);
|
|
8
|
+
fork + worktree 时可选注入 CC 等价 ``buildWorktreeNotice`` user 消息(见 ``fork_worktree_notice``)。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import shutil
|
|
13
|
+
import uuid
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
from langchain_agentx.loop.runtime.subagent_execution_paths import PreparedSubagentPaths
|
|
18
|
+
from langchain_agentx.loop.subagent.fork_worktree_notice import build_worktree_notice
|
|
19
|
+
from langchain_agentx.workspace import resolve_agent_workspace_config
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class SubagentContext:
|
|
24
|
+
"""子 Agent 执行上下文
|
|
25
|
+
|
|
26
|
+
独立字段:agent_id / initial_messages / max_steps / subagent_type / agent_depth
|
|
27
|
+
system_prompt / is_async / fork_from_parent
|
|
28
|
+
共享字段:session_store / tool_registry(从父会话继承)
|
|
29
|
+
执行路径:fork_from_parent 决定消息构造;is_async 决定同步/异步路径路由
|
|
30
|
+
"""
|
|
31
|
+
# 独立字段
|
|
32
|
+
agent_id: str
|
|
33
|
+
initial_messages: list[dict[str, Any]]
|
|
34
|
+
max_steps: int | None
|
|
35
|
+
subagent_type: str
|
|
36
|
+
model: Any | None = None
|
|
37
|
+
agent_depth: int = 0
|
|
38
|
+
|
|
39
|
+
# 执行路径字段(由外部注入,驱动 runner 路由决策)
|
|
40
|
+
system_prompt: str = "" # 由 SubagentConfig.system_prompt_factory() 注入
|
|
41
|
+
fork_from_parent: bool = False # True = fork 模式,initial_messages 含父会话历史
|
|
42
|
+
is_async: bool = False # True = 走异步路径(coordinate/fork-async)
|
|
43
|
+
|
|
44
|
+
# 共享字段(从父会话继承)
|
|
45
|
+
session_store: Any = None # SessionStore
|
|
46
|
+
tool_registry: Any = None # ToolRegistry
|
|
47
|
+
tool_loader: Any = None # ToolRuntimeLoader
|
|
48
|
+
|
|
49
|
+
# 可选字段
|
|
50
|
+
parent_agent_id: str | None = None
|
|
51
|
+
parent_run_id: str | None = None # 父 Agent 的 trace run_id,供 TraceLifecycleCollector/Collector 建立父子链接
|
|
52
|
+
parent_tool_call_id: str | None = None # 触发本子 Agent 的 tool_call_id
|
|
53
|
+
parent_conversation_session_id: str | None = None
|
|
54
|
+
enable_trace: bool = True
|
|
55
|
+
isolation: str = "none"
|
|
56
|
+
worktree_dir: str | None = None
|
|
57
|
+
workspace_root: str = ""
|
|
58
|
+
agent_home: str = ".langchain_agentx"
|
|
59
|
+
cwd: str | None = None
|
|
60
|
+
_file_read_state: dict[str, Any] = field(default_factory=dict)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class SubagentResult:
|
|
65
|
+
"""子 Agent 执行结果"""
|
|
66
|
+
agent_id: str
|
|
67
|
+
content: str
|
|
68
|
+
status: str # "completed" | "error" | "interrupted"
|
|
69
|
+
terminal_reason: str = ""
|
|
70
|
+
total_steps: int = 0
|
|
71
|
+
error_message: str = ""
|
|
72
|
+
output_file: str | None = None
|
|
73
|
+
child_run_id: str | None = None # 子会话的 trace session_id,供父 run 追溯
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def create_subagent_context(
|
|
77
|
+
parent_ctx: Any,
|
|
78
|
+
task: str,
|
|
79
|
+
subagent_type: str,
|
|
80
|
+
system_prompt: str = "",
|
|
81
|
+
max_steps: int | None = None,
|
|
82
|
+
model: Any | None = None,
|
|
83
|
+
agent_depth: int = 0,
|
|
84
|
+
isolation: str = "none",
|
|
85
|
+
cwd: str | None = None,
|
|
86
|
+
fork_from_parent: bool = False,
|
|
87
|
+
is_async: bool = False,
|
|
88
|
+
*,
|
|
89
|
+
path_prep: PreparedSubagentPaths | None = None,
|
|
90
|
+
inject_fork_worktree_notice: bool = False,
|
|
91
|
+
fork_worktree_parent_cwd: str | None = None,
|
|
92
|
+
fork_worktree_path: str | None = None,
|
|
93
|
+
) -> SubagentContext:
|
|
94
|
+
"""工厂函数:从父上下文创建子 Agent 上下文
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
parent_ctx: 父会话上下文(需有 session_store / tool_registry)
|
|
98
|
+
task: 子任务描述
|
|
99
|
+
subagent_type: 子 Agent 类型("general-purpose" / "explore" / "plan")
|
|
100
|
+
system_prompt: 系统提示词(由 SubagentConfig.system_prompt_factory() 提供)
|
|
101
|
+
max_steps: 最大步数
|
|
102
|
+
model: 子 Agent 使用模型(None 时由调用方在 runner 阶段继续继承)
|
|
103
|
+
agent_depth: Agent 嵌套深度
|
|
104
|
+
isolation: 隔离模式("none" / "worktree")
|
|
105
|
+
cwd: 工作目录覆盖(与 ``path_prep`` 二选一;``path_prep`` 非空时忽略本参数的路径语义)
|
|
106
|
+
fork_from_parent: True = fork 模式,initial_messages 继承父会话历史
|
|
107
|
+
is_async: True = 走异步执行路径(coordinate/fork-async)
|
|
108
|
+
path_prep: 由 ``SubagentExecutionPaths.prepare`` 提供时,使用其 ``effective_cwd`` /
|
|
109
|
+
``worktree_dir``,且不再在本函数内创建 worktree 目录。
|
|
110
|
+
inject_fork_worktree_notice: 为 True 且 ``fork_from_parent`` 且 ``fork_worktree_path`` 非空时,
|
|
111
|
+
在最终任务 user 消息前插入 CC 等价 worktree 提示(父 cwd 由 ``fork_worktree_parent_cwd`` 提供)。
|
|
112
|
+
fork_worktree_parent_cwd: 父 Agent 工作目录(须在 ``run_with_cwd_override`` 子 cwd 生效前由编排层捕获)。
|
|
113
|
+
fork_worktree_path: 子 Agent 的 worktree 根路径(通常即 ``path_prep.worktree_dir``)。
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
SubagentContext 实例
|
|
117
|
+
"""
|
|
118
|
+
agent_id = str(uuid.uuid4())
|
|
119
|
+
parent_run_id_raw = getattr(parent_ctx, "run_id", None)
|
|
120
|
+
if not isinstance(parent_run_id_raw, str) or not parent_run_id_raw:
|
|
121
|
+
parent_run_id_raw = getattr(parent_ctx, "session_id", None)
|
|
122
|
+
if not isinstance(parent_run_id_raw, str) or not parent_run_id_raw:
|
|
123
|
+
parent_run_id_raw = None
|
|
124
|
+
parent_conversation_id = getattr(parent_ctx, "conversation_session_id", None)
|
|
125
|
+
if not isinstance(parent_conversation_id, str) or not parent_conversation_id:
|
|
126
|
+
parent_conversation_id = parent_run_id_raw
|
|
127
|
+
parent_tool_call_id = getattr(parent_ctx, "tool_call_id", None)
|
|
128
|
+
if not isinstance(parent_tool_call_id, str) or not parent_tool_call_id:
|
|
129
|
+
anchor = parent_run_id_raw or "unknown-run"
|
|
130
|
+
parent_tool_call_id = f"synthetic-agent-call:{anchor}:{agent_id[:8]}"
|
|
131
|
+
|
|
132
|
+
parent_cwd = getattr(parent_ctx, "cwd", None)
|
|
133
|
+
|
|
134
|
+
initial_messages = [{"role": "user", "content": task}]
|
|
135
|
+
if fork_from_parent:
|
|
136
|
+
parent_messages = getattr(parent_ctx, "state", {}).get("messages", [])
|
|
137
|
+
if isinstance(parent_messages, list):
|
|
138
|
+
initial_messages = []
|
|
139
|
+
for msg in parent_messages:
|
|
140
|
+
if isinstance(msg, dict):
|
|
141
|
+
initial_messages.append(dict(msg))
|
|
142
|
+
notice_parent = fork_worktree_parent_cwd or parent_cwd or os.getcwd()
|
|
143
|
+
if (
|
|
144
|
+
inject_fork_worktree_notice
|
|
145
|
+
and fork_worktree_path
|
|
146
|
+
and isinstance(notice_parent, str)
|
|
147
|
+
and notice_parent.strip()
|
|
148
|
+
):
|
|
149
|
+
initial_messages.append(
|
|
150
|
+
{
|
|
151
|
+
"role": "user",
|
|
152
|
+
"content": build_worktree_notice(
|
|
153
|
+
notice_parent.strip(),
|
|
154
|
+
fork_worktree_path,
|
|
155
|
+
),
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
initial_messages.append({"role": "user", "content": task})
|
|
159
|
+
|
|
160
|
+
parent_workspace_root = getattr(parent_ctx, "workspace_root", None) or os.getcwd()
|
|
161
|
+
parent_agent_home = getattr(parent_ctx, "agent_home", ".langchain_agentx")
|
|
162
|
+
worktree_dir: str | None = None
|
|
163
|
+
if path_prep is not None:
|
|
164
|
+
effective_cwd = path_prep.effective_cwd
|
|
165
|
+
worktree_dir = path_prep.worktree_dir
|
|
166
|
+
else:
|
|
167
|
+
effective_cwd = cwd or parent_cwd or os.getcwd()
|
|
168
|
+
if isolation == "worktree":
|
|
169
|
+
workspace_cfg = resolve_agent_workspace_config(
|
|
170
|
+
workspace_root=parent_workspace_root,
|
|
171
|
+
agent_home=parent_agent_home,
|
|
172
|
+
)
|
|
173
|
+
workspace_cfg.worktrees_dir.mkdir(parents=True, exist_ok=True)
|
|
174
|
+
worktree_dir = str(
|
|
175
|
+
workspace_cfg.worktrees_dir / f"agentx-worktree-{uuid.uuid4().hex[:12]}"
|
|
176
|
+
)
|
|
177
|
+
os.makedirs(worktree_dir, exist_ok=False)
|
|
178
|
+
effective_cwd = (cwd.strip() if isinstance(cwd, str) and cwd.strip() else None) or worktree_dir
|
|
179
|
+
|
|
180
|
+
return SubagentContext(
|
|
181
|
+
agent_id=agent_id,
|
|
182
|
+
initial_messages=initial_messages,
|
|
183
|
+
max_steps=max_steps,
|
|
184
|
+
model=model,
|
|
185
|
+
subagent_type=subagent_type,
|
|
186
|
+
agent_depth=agent_depth,
|
|
187
|
+
system_prompt=system_prompt,
|
|
188
|
+
fork_from_parent=fork_from_parent,
|
|
189
|
+
is_async=is_async,
|
|
190
|
+
session_store=getattr(parent_ctx, "session_store", None),
|
|
191
|
+
tool_registry=getattr(parent_ctx, "tool_registry", None),
|
|
192
|
+
tool_loader=getattr(parent_ctx, "tool_loader", None),
|
|
193
|
+
parent_agent_id=getattr(parent_ctx, "agent_id", None),
|
|
194
|
+
parent_run_id=parent_run_id_raw,
|
|
195
|
+
parent_tool_call_id=parent_tool_call_id,
|
|
196
|
+
parent_conversation_session_id=parent_conversation_id,
|
|
197
|
+
enable_trace=bool(getattr(parent_ctx, "trace_enabled", True)),
|
|
198
|
+
isolation=isolation,
|
|
199
|
+
worktree_dir=worktree_dir,
|
|
200
|
+
workspace_root=str(parent_workspace_root),
|
|
201
|
+
agent_home=str(parent_agent_home),
|
|
202
|
+
cwd=effective_cwd,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def cleanup_subagent_context(ctx: SubagentContext) -> None:
|
|
207
|
+
"""清理 subagent 临时资源(如 worktree 目录)。"""
|
|
208
|
+
if ctx.isolation == "worktree" and ctx.worktree_dir:
|
|
209
|
+
shutil.rmtree(ctx.worktree_dir, ignore_errors=True)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
loop/subagent/fork_worktree_notice.py — Fork 子 Agent 的 worktree 路径提示文案
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
生成与 Claude Code ``buildWorktreeNotice`` 等价的 user 消息正文,提示子 Agent
|
|
6
|
+
将继承消息中的路径从父工作目录映射到隔离 worktree。
|
|
7
|
+
链路位置:
|
|
8
|
+
``create_subagent_context`` 在 ``fork_from_parent`` 且存在 worktree 目录时追加一条 user 消息。
|
|
9
|
+
当前裁剪范围:
|
|
10
|
+
仅英文固定模板(与 CC 原文一致);不承载 i18n。
|
|
11
|
+
CC 对照:
|
|
12
|
+
``src/tools/AgentTool/forkSubagent.ts`` ``buildWorktreeNotice(parentCwd, worktreeCwd)``。
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_worktree_notice(parent_cwd: str, worktree_path: str) -> str:
|
|
17
|
+
"""与 CC ``buildWorktreeNotice`` 原文一致(参数为父 cwd 与 worktree 根路径)。"""
|
|
18
|
+
return (
|
|
19
|
+
f"You've inherited the conversation context above from a parent agent working in {parent_cwd}. "
|
|
20
|
+
f"You are operating in an isolated git worktree at {worktree_path} — same repository, "
|
|
21
|
+
"same relative file structure, separate working copy. Paths in the inherited context refer "
|
|
22
|
+
"to the parent's working directory; translate them to your worktree root. Re-read files before "
|
|
23
|
+
"editing if the parent may have modified them since they appear in the context. Your changes stay "
|
|
24
|
+
"in this worktree and will not affect the parent's files."
|
|
25
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
loop/subagent/graph.py — 子 Agent 图创建
|
|
3
|
+
|
|
4
|
+
职责:create_subagent_graph(),基于 SubagentContext 创建独立子 Loop 图
|
|
5
|
+
在整体链路中的位置:run_subagent() → create_subagent_graph() → subagent_graph.astream_events()
|
|
6
|
+
CC 对照:runAgent.ts 中 queryLoop() 调用(子会话复用主引擎)
|
|
7
|
+
当前裁剪范围:v1 复用 create_loop_agent;通过 tools 参数注入子代理工具,并传入
|
|
8
|
+
parent_run_id/parent_tool_call_id 与父 run 关联。
|
|
9
|
+
|
|
10
|
+
执行流程(create_subagent_graph):
|
|
11
|
+
1. 从 ctx 读取 system_prompt / session_store / max_steps
|
|
12
|
+
2. 调用 create_loop_agent(is_subagent=True, loader=..., parent_run_id=..., parent_tool_call_id=...)
|
|
13
|
+
创建子图
|
|
14
|
+
3. 返回编译后的 CompiledStateGraph,由 run_subagent() 调用 astream_events()
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from langchain_core.language_models.chat_models import BaseChatModel
|
|
23
|
+
from langchain_core.tools import BaseTool
|
|
24
|
+
from langgraph.graph.state import CompiledStateGraph
|
|
25
|
+
|
|
26
|
+
from .context import SubagentContext
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def create_subagent_graph(
|
|
30
|
+
ctx: SubagentContext,
|
|
31
|
+
loader: Any,
|
|
32
|
+
model: str | BaseChatModel | None = None,
|
|
33
|
+
) -> CompiledStateGraph:
|
|
34
|
+
"""创建子 Agent 的 LangGraph 图。
|
|
35
|
+
|
|
36
|
+
复用 create_loop_agent(..., is_subagent=True)。
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
ctx: 子 Agent 执行上下文(SubagentContext)
|
|
40
|
+
loader: 子会话 ToolRuntimeLoader(已完成工具过滤与运行时装配)
|
|
41
|
+
model: 语言模型(str 或 BaseChatModel),None 时由调用方保证已在 ctx 外部传入
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
编译后的 CompiledStateGraph,可直接调用 astream_events()
|
|
45
|
+
"""
|
|
46
|
+
from langchain_agentx.loop.graph.factory import create_loop_agent
|
|
47
|
+
|
|
48
|
+
if model is None:
|
|
49
|
+
raise ValueError("model is required for create_subagent_graph")
|
|
50
|
+
|
|
51
|
+
# 显式 session_id:避免子图构图时生成 ephemeral id 刷屏日志,并与 ctx.agent_id 对齐便于追溯
|
|
52
|
+
session_id = f"subagent:{ctx.subagent_type}:{ctx.agent_id}"
|
|
53
|
+
|
|
54
|
+
return create_loop_agent(
|
|
55
|
+
model=model,
|
|
56
|
+
loader=loader,
|
|
57
|
+
system_prompt=ctx.system_prompt or None,
|
|
58
|
+
session_id=session_id,
|
|
59
|
+
session_store=ctx.session_store,
|
|
60
|
+
max_steps=ctx.max_steps,
|
|
61
|
+
is_subagent=True,
|
|
62
|
+
name=f"subagent:{ctx.subagent_type}:{ctx.agent_id[:8]}",
|
|
63
|
+
parent_run_id=ctx.parent_run_id,
|
|
64
|
+
parent_tool_call_id=ctx.parent_tool_call_id,
|
|
65
|
+
parent_conversation_session_id=ctx.parent_conversation_session_id,
|
|
66
|
+
enable_trace=ctx.enable_trace,
|
|
67
|
+
# 背景子代理默认隐藏;前台子代理沿用默认可见性。
|
|
68
|
+
trace_visibility="hidden" if ctx.is_async else "default",
|
|
69
|
+
workspace_root=ctx.workspace_root or None,
|
|
70
|
+
agent_home=ctx.agent_home,
|
|
71
|
+
subagent_type=ctx.subagent_type,
|
|
72
|
+
)
|