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,77 @@
|
|
|
1
|
+
"""utils/temp_paths.py — 跨平台系统临时目录路径(测试与示例用)。
|
|
2
|
+
|
|
3
|
+
职责:
|
|
4
|
+
提供「与 OS 一致的可写临时区」下的 `Path` / `str`,避免在单测与演示中写死 POSIX ``/tmp``,
|
|
5
|
+
以便在 Windows(通常为 ``%TEMP%``)与 Linux/macOS 上同一套断言与逻辑路径。
|
|
6
|
+
核心逻辑在 SystemTempLocations;模块级函数为薄封装。
|
|
7
|
+
|
|
8
|
+
链路位置:
|
|
9
|
+
单测、examples 中仅需「绝对路径字符串」且不便注入 ``tmp_path`` 时使用。
|
|
10
|
+
|
|
11
|
+
当前裁剪范围:
|
|
12
|
+
不负责创建目录或清理文件;调用方按需 ``mkdir`` / 使用 ``tmp_path``。
|
|
13
|
+
生产业务路径仍应来自 ``AgentWorkspaceConfig`` 等配置层,而非本模块。
|
|
14
|
+
|
|
15
|
+
工程化约定(与 Claude Code 等 Node 工程对齐思路):
|
|
16
|
+
- **命名空间**:首段路径分量使用项目前缀(如 ``langchain_agentx_tool_runtime_test/...``),等价于
|
|
17
|
+
CC 中 ``join(tmpdir(), 'claude', ...)`` 在系统临时目录下再分一层,减少与其它软件冲突。
|
|
18
|
+
- **环境变量**:``tempfile.gettempdir()`` 会尊重常见宿主配置(如 Unix ``TMPDIR``、Windows ``TEMP``/``TMP``),
|
|
19
|
+
不在此模块重复实现;CI 若需固定沙箱目录,应通过设置上述环境变量而非改库代码。
|
|
20
|
+
- **非安全边界**:本模块**不做** CC ``memdir/paths`` 类对 UNC、盘符根、过短路径的拒绝策略;若路径来自
|
|
21
|
+
不可信输入,须在策略层校验,勿仅依赖本工具返回的路径。
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import tempfile
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SystemTempLocations:
|
|
31
|
+
"""封装 ``tempfile.gettempdir()`` 下的品牌子路径拼接。"""
|
|
32
|
+
|
|
33
|
+
def system_temp_dir(self) -> Path:
|
|
34
|
+
"""返回当前进程可见的系统临时目录(``tempfile.gettempdir()``)。"""
|
|
35
|
+
return Path(tempfile.gettempdir())
|
|
36
|
+
|
|
37
|
+
def under_system_temp(self, *relative_parts: str) -> Path:
|
|
38
|
+
"""``system_temp_dir()`` 下拼接相对路径(至少一段,避免误用根)。"""
|
|
39
|
+
if not relative_parts:
|
|
40
|
+
raise ValueError("under_system_temp requires at least one path component")
|
|
41
|
+
return self.system_temp_dir().joinpath(*relative_parts)
|
|
42
|
+
|
|
43
|
+
def under_system_temp_str(self, *relative_parts: str) -> str:
|
|
44
|
+
"""与 :meth:`under_system_temp` 相同,返回 ``str``(便于存入 session_store 等)。"""
|
|
45
|
+
return str(self.under_system_temp(*relative_parts))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_default_locations = SystemTempLocations()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def default_system_temp_locations() -> SystemTempLocations:
|
|
52
|
+
"""返回进程内共享的默认实例。"""
|
|
53
|
+
return _default_locations
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def system_temp_dir() -> Path:
|
|
57
|
+
"""返回当前进程可见的系统临时目录(``tempfile.gettempdir()``)。"""
|
|
58
|
+
return _default_locations.system_temp_dir()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def under_system_temp(*relative_parts: str) -> Path:
|
|
62
|
+
"""``system_temp_dir()`` 下拼接相对路径(至少一段,避免误用根)。"""
|
|
63
|
+
return _default_locations.under_system_temp(*relative_parts)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def under_system_temp_str(*relative_parts: str) -> str:
|
|
67
|
+
"""与 :func:`under_system_temp` 相同,返回 ``str``(便于存入 session_store 等)。"""
|
|
68
|
+
return _default_locations.under_system_temp_str(*relative_parts)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
__all__ = [
|
|
72
|
+
"SystemTempLocations",
|
|
73
|
+
"default_system_temp_locations",
|
|
74
|
+
"system_temp_dir",
|
|
75
|
+
"under_system_temp",
|
|
76
|
+
"under_system_temp_str",
|
|
77
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
utils/unc_path.py — UNC / 双斜线网径前缀检测
|
|
3
|
+
|
|
4
|
+
职责:为 Grep/Glob(及未来 Read 等)的 validate_input 提供与 CC 一致的 stat 前短路判断,
|
|
5
|
+
避免对 UNC 路径做本地 stat 触发不必要的远程/凭据面(CC GrepTool/GlobTool validateInput)。
|
|
6
|
+
|
|
7
|
+
CC 对照:src/tools/GrepTool/GrepTool.ts、src/tools/GlobTool/GlobTool.ts(absolutePath.startsWith('\\\\') || startsWith('//'))。
|
|
8
|
+
|
|
9
|
+
当前裁剪:仅前缀判定;不解析 \\\\?\\UNC\\ 等扩展设备语法(与 CC 当前检查范围一致)。
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_unc_path_skip_local_stat(path: str) -> bool:
|
|
16
|
+
"""
|
|
17
|
+
若路径在展开后仍表现为 UNC / SMB 风格根前缀,则不在本地做 exists/stat 校验。
|
|
18
|
+
|
|
19
|
+
- 反斜杠 UNC:`\\\\server\\share\\...`
|
|
20
|
+
- 正斜杠根:`//server/share/...`(含部分 POSIX 下 ``//`` 绝对路径形态)
|
|
21
|
+
"""
|
|
22
|
+
s = path.strip()
|
|
23
|
+
if not s:
|
|
24
|
+
return False
|
|
25
|
+
return s.startswith("\\\\") or s.startswith("//")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
utils/win_reserved_paths.py — Windows 保留设备名路径检测
|
|
3
|
+
|
|
4
|
+
职责:判断路径中是否出现 Windows 保留名(CON/PRN/AUX/NUL/COM1-9/LPT1-9),
|
|
5
|
+
供 Read 等工具在本地 I/O 前拒绝误读设备(与 Unix /dev 阻断互补)。
|
|
6
|
+
|
|
7
|
+
CC 对照:FileReadTool 仍以 Unix 设备表为主;本工程在 win32 上补一层路径段检测。
|
|
8
|
+
|
|
9
|
+
当前裁剪:仅按 Windows 规则拆分路径段并检查各段 stem(与 COM1.txt 等扩展名规则一致);
|
|
10
|
+
非 win32 恒为 False。拆分固定使用 PureWindowsPath,避免在 Linux CI 上 Path 仍为
|
|
11
|
+
PosixPath 时反斜杠路径被误解析(单段 + stem 误判)。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import PureWindowsPath
|
|
18
|
+
|
|
19
|
+
_WIN_RESERVED_STEMS: frozenset[str] = frozenset(
|
|
20
|
+
{"CON", "PRN", "AUX", "NUL", *(f"COM{i}" for i in range(1, 10)), *(f"LPT{i}" for i in range(1, 10))}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def path_uses_windows_reserved_name(path: str) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
若当前平台为 Windows,且路径任一段(按 Windows 路径规则拆分)的 stem 为保留设备名,则返回 True。
|
|
27
|
+
|
|
28
|
+
例如 ``C:\\temp\\NUL``、``C:\\share\\COM1.dat``、末段为 ``CON`` 的路径。
|
|
29
|
+
"""
|
|
30
|
+
if sys.platform != "win32":
|
|
31
|
+
return False
|
|
32
|
+
if not path or not path.strip():
|
|
33
|
+
return False
|
|
34
|
+
try:
|
|
35
|
+
parts = PureWindowsPath(path).parts
|
|
36
|
+
except (ValueError, OSError):
|
|
37
|
+
return False
|
|
38
|
+
for part in parts:
|
|
39
|
+
seg = part.rstrip("/\\")
|
|
40
|
+
if not seg:
|
|
41
|
+
continue
|
|
42
|
+
# 盘符段如 ``C:`` 无 stem 意义
|
|
43
|
+
if len(seg) == 2 and seg[1] == ":":
|
|
44
|
+
continue
|
|
45
|
+
stem = PureWindowsPath(seg).stem.upper()
|
|
46
|
+
if stem in _WIN_RESERVED_STEMS:
|
|
47
|
+
return True
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = ["path_uses_windows_reserved_name"]
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workflow/base.py — Workflow 编排基类。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
封装任务级容器生命周期(start/invoke/end)、并发控制与单次 agent invoke 入口。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
作为场景化 Workflow 的父类,复用 Agent Loop 并对齐 LoopContainer 生命周期协议。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅提供 BaseWorkflow 最小契约;Dag/Batch 骨架在独立文件占位。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
import inspect
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from typing import Any, Callable, Literal
|
|
20
|
+
from uuid import uuid4
|
|
21
|
+
|
|
22
|
+
from ..loop.hook.types import HookContext, HookEvent
|
|
23
|
+
from ..memory.memdir.agent_memory import drain_pending_agent_memory_extraction
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseWorkflow(ABC):
|
|
27
|
+
"""场景编排基类。"""
|
|
28
|
+
|
|
29
|
+
container_type: str = "workflow_task"
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
graph_factory: Callable[..., Any],
|
|
34
|
+
hook_engine: Any,
|
|
35
|
+
max_concurrency: int = 4,
|
|
36
|
+
error_policy: Literal["fail_fast", "best_effort"] = "best_effort",
|
|
37
|
+
) -> None:
|
|
38
|
+
self._graph_factory = graph_factory
|
|
39
|
+
self._hook_engine = hook_engine
|
|
40
|
+
self._semaphore = asyncio.Semaphore(max_concurrency)
|
|
41
|
+
self._error_policy = error_policy
|
|
42
|
+
self._workflow_id = uuid4().hex[:8]
|
|
43
|
+
|
|
44
|
+
async def __aenter__(self) -> "BaseWorkflow":
|
|
45
|
+
await self._on_workflow_start()
|
|
46
|
+
return self
|
|
47
|
+
|
|
48
|
+
async def __aexit__(self, *_) -> None:
|
|
49
|
+
await self._on_workflow_end()
|
|
50
|
+
|
|
51
|
+
async def run(self, **kwargs: Any) -> Any:
|
|
52
|
+
async with self:
|
|
53
|
+
return await self._execute(**kwargs)
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
57
|
+
"""子类实现具体编排逻辑。"""
|
|
58
|
+
|
|
59
|
+
async def _invoke_agent(
|
|
60
|
+
self,
|
|
61
|
+
messages: list[Any],
|
|
62
|
+
task_key: str,
|
|
63
|
+
parent_session_id: str | None = None,
|
|
64
|
+
) -> dict[str, Any]:
|
|
65
|
+
session_id = f"{self._workflow_id}-{task_key}"
|
|
66
|
+
async with self._semaphore:
|
|
67
|
+
graph = self._graph_factory(
|
|
68
|
+
session_id=session_id,
|
|
69
|
+
parent_conversation_session_id=parent_session_id,
|
|
70
|
+
is_subagent=parent_session_id is not None,
|
|
71
|
+
container_type=self.container_type,
|
|
72
|
+
)
|
|
73
|
+
result = await graph.ainvoke({"messages": messages})
|
|
74
|
+
return dict(result) if isinstance(result, dict) else {"result": result}
|
|
75
|
+
|
|
76
|
+
async def _on_workflow_start(self) -> None:
|
|
77
|
+
await self._execute_hook(HookEvent.SESSION_START)
|
|
78
|
+
|
|
79
|
+
async def _on_workflow_end(self) -> None:
|
|
80
|
+
await drain_pending_agent_memory_extraction(timeout_ms=60_000)
|
|
81
|
+
await self._execute_hook(HookEvent.SESSION_END)
|
|
82
|
+
|
|
83
|
+
async def _execute_hook(self, event: HookEvent) -> None:
|
|
84
|
+
ctx = HookContext(event=event, state={}, session_id=self._workflow_id)
|
|
85
|
+
execute_async = getattr(self._hook_engine, "execute_async", None)
|
|
86
|
+
if callable(execute_async):
|
|
87
|
+
async_out = execute_async(ctx)
|
|
88
|
+
if inspect.isawaitable(async_out):
|
|
89
|
+
await async_out
|
|
90
|
+
return
|
|
91
|
+
execute = getattr(self._hook_engine, "execute")
|
|
92
|
+
out = execute(ctx)
|
|
93
|
+
if inspect.isawaitable(out):
|
|
94
|
+
await out
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
__all__ = ["BaseWorkflow"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workflow/batch.py — Batch 编排骨架。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
为同构批处理任务提供标准执行模板(items -> process x N -> merge),
|
|
6
|
+
子类实现 _process_item() 和 merge_results() 即可。
|
|
7
|
+
|
|
8
|
+
链路位置:
|
|
9
|
+
继承 BaseWorkflow,为批处理型业务 Workflow 提供标准入口。
|
|
10
|
+
|
|
11
|
+
当前裁剪范围:
|
|
12
|
+
并发由 BaseWorkflow._semaphore 控制;错误策略由 error_policy 决定。
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
from abc import abstractmethod
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from .base import BaseWorkflow
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BatchWorkflow(BaseWorkflow):
|
|
25
|
+
"""批处理编排骨架。"""
|
|
26
|
+
|
|
27
|
+
async def process_all(self, items: list[Any]) -> Any:
|
|
28
|
+
"""
|
|
29
|
+
并发处理所有 items,结果交给 merge_results() 聚合。
|
|
30
|
+
|
|
31
|
+
best_effort:单个 item 失败记为 None,其余继续。
|
|
32
|
+
fail_fast:任意 item 失败立即抛异常。
|
|
33
|
+
"""
|
|
34
|
+
tasks = [self._process_item(item, idx) for idx, item in enumerate(items)]
|
|
35
|
+
if self._error_policy == "fail_fast":
|
|
36
|
+
results = await asyncio.gather(*tasks)
|
|
37
|
+
else:
|
|
38
|
+
raw_results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
39
|
+
results = [None if isinstance(item, BaseException) else item for item in raw_results]
|
|
40
|
+
return self.merge_results(results)
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
async def _process_item(self, item: Any, index: int) -> Any:
|
|
44
|
+
"""子类实现单个 item 的处理逻辑。"""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def merge_results(self, results: list[Any]) -> Any:
|
|
48
|
+
"""子类实现多个 item 结果的聚合逻辑。"""
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
52
|
+
"""子类实现入口,通常直接调用 process_all(items)。"""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
__all__ = ["BatchWorkflow"]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workflow/dag.py — DAG 编排骨架。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
为有向无环图编排场景提供步骤辅助入口(_run_step),
|
|
6
|
+
子类通过普通 Python 代码声明步骤依赖与并发关系。
|
|
7
|
+
|
|
8
|
+
链路位置:
|
|
9
|
+
继承 BaseWorkflow,为具体业务 Workflow(如 WikiWorkflow)提供结构化骨架。
|
|
10
|
+
|
|
11
|
+
当前裁剪范围:
|
|
12
|
+
不做自动 DAG 拓扑调度,步骤顺序与并发由子类 _execute() 控制。
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from abc import abstractmethod
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from .base import BaseWorkflow
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DagWorkflow(BaseWorkflow):
|
|
24
|
+
"""
|
|
25
|
+
DAG 编排骨架。
|
|
26
|
+
|
|
27
|
+
子类通过 _run_step() 与 Python 控制流表达依赖与并发。
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
async def _run_step(
|
|
31
|
+
self,
|
|
32
|
+
step_name: str,
|
|
33
|
+
messages: list[Any],
|
|
34
|
+
*,
|
|
35
|
+
parent_session_id: str | None = None,
|
|
36
|
+
) -> dict[str, Any]:
|
|
37
|
+
"""
|
|
38
|
+
执行单个 DAG 步骤。
|
|
39
|
+
|
|
40
|
+
step_name 作为 task_key,最终生成 session_id = f"{workflow_id}-{step_name}",
|
|
41
|
+
trace 可按步骤名检索执行链路。
|
|
42
|
+
"""
|
|
43
|
+
return await self._invoke_agent(
|
|
44
|
+
messages=messages,
|
|
45
|
+
task_key=step_name,
|
|
46
|
+
parent_session_id=parent_session_id,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
async def _execute(self, **kwargs: Any) -> Any:
|
|
51
|
+
"""子类实现具体步骤编排逻辑。"""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = ["DagWorkflow"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .config import AgentWorkspaceConfig
|
|
2
|
+
from .path_key_normalizer import PathKeyNormalizer, normalize_path_key
|
|
3
|
+
from .resolver import AgentWorkspaceResolver, resolve_agent_workspace_config
|
|
4
|
+
from .validators import AgentWorkspacePathValidator
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AgentWorkspaceConfig",
|
|
8
|
+
"AgentWorkspaceResolver",
|
|
9
|
+
"AgentWorkspacePathValidator",
|
|
10
|
+
"PathKeyNormalizer",
|
|
11
|
+
"normalize_path_key",
|
|
12
|
+
"resolve_agent_workspace_config",
|
|
13
|
+
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workspace/config.py — AgentWorkspace 路径配置模型
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
提供 `workspace_root + agent_home` 的派生目录视图(skills/worktrees/tasks/data/db),
|
|
6
|
+
作为跨模块目录语义的数据模型。
|
|
7
|
+
|
|
8
|
+
在整体链路中的位置:
|
|
9
|
+
AgentWorkspaceResolver.resolve()
|
|
10
|
+
-> AgentWorkspaceConfig
|
|
11
|
+
-> 各入口按属性读取目标路径。
|
|
12
|
+
|
|
13
|
+
当前裁剪范围:
|
|
14
|
+
仅路径派生,不做校验与副作用创建。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
import re
|
|
22
|
+
|
|
23
|
+
from .path_key_normalizer import normalize_path_key
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _sanitize_agent_type(agent_type: str) -> str:
|
|
27
|
+
"""安全化 agent_type,避免路径注入;键串先 NFC 与 session_id 同策略。"""
|
|
28
|
+
normalized = normalize_path_key(str(agent_type or "").strip())
|
|
29
|
+
normalized = normalized.replace(":", "-")
|
|
30
|
+
normalized = re.sub(r"[/\\]", "-", normalized)
|
|
31
|
+
normalized = normalized.replace("..", "-")
|
|
32
|
+
normalized = normalized.replace("\x00", "")
|
|
33
|
+
normalized = normalized.strip("-").strip()
|
|
34
|
+
if not normalized:
|
|
35
|
+
raise ValueError(f"agent_type {agent_type!r} sanitizes to empty string")
|
|
36
|
+
return normalized
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _normalize_memory_scope(scope: str) -> str:
|
|
40
|
+
"""单路径段 scope(project/user/local 等)做 NFC,并拒绝路径分隔与穿越片段。"""
|
|
41
|
+
s = normalize_path_key(str(scope or "").strip())
|
|
42
|
+
if not s:
|
|
43
|
+
raise ValueError("scope cannot be empty")
|
|
44
|
+
if re.search(r"[/\\]", s) or ".." in s:
|
|
45
|
+
raise ValueError(f"invalid scope for agent_memory_dir: {scope!r}")
|
|
46
|
+
return s
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class AgentWorkspaceConfig:
|
|
51
|
+
workspace_root: Path
|
|
52
|
+
agent_home: str = ".langchain_agentx"
|
|
53
|
+
plugins_enabled: bool = True
|
|
54
|
+
workspace_trust_accepted: bool = False
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def agent_home_dir(self) -> Path:
|
|
58
|
+
return (self.workspace_root / self.agent_home).resolve()
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def skills_dir(self) -> Path:
|
|
62
|
+
return self.agent_home_dir / "skills"
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def worktrees_dir(self) -> Path:
|
|
66
|
+
return self.agent_home_dir / "worktrees"
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def tasks_dir(self) -> Path:
|
|
70
|
+
return self.agent_home_dir / "tasks"
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def data_dir(self) -> Path:
|
|
74
|
+
return self.agent_home_dir / "data"
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def db_dir(self) -> Path:
|
|
78
|
+
return self.data_dir / "db"
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def traces_db_path(self) -> Path:
|
|
82
|
+
return self.db_dir / "traces.db"
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def task_runtime_db_path(self) -> Path:
|
|
86
|
+
return self.db_dir / "task_runtime.db"
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def evaluation_db_path(self) -> Path:
|
|
90
|
+
return self.db_dir / "evaluation.db"
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def memory_dir(self) -> Path:
|
|
94
|
+
return self.agent_home_dir / "memory"
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def memory_entrypoint(self) -> Path:
|
|
98
|
+
return self.memory_dir / "MEMORY.md"
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def sessions_dir(self) -> Path:
|
|
102
|
+
return self.agent_home_dir / "sessions"
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def transcripts_dir(self) -> Path:
|
|
106
|
+
return self.agent_home_dir / "transcripts"
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def workspace_config_path(self) -> Path:
|
|
110
|
+
return self.agent_home_dir / "workspace_config.json"
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def plugins_dir(self) -> Path:
|
|
114
|
+
return self.agent_home_dir / "plugins"
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def plugin_config_path(self) -> Path:
|
|
118
|
+
return self.agent_home_dir / "plugin_config.json"
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def plugin_cache_dir(self) -> Path:
|
|
122
|
+
return self.agent_home_dir / "plugin_cache"
|
|
123
|
+
|
|
124
|
+
def session_memory_path(self, session_id: str) -> Path:
|
|
125
|
+
sid = normalize_path_key(str(session_id).strip())
|
|
126
|
+
if not sid:
|
|
127
|
+
raise ValueError("session_id cannot be empty")
|
|
128
|
+
return self.sessions_dir / f"{sid}.md"
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def session_memory_config_dir(self) -> Path:
|
|
132
|
+
return self.agent_home_dir / "session-memory" / "config"
|
|
133
|
+
|
|
134
|
+
def agent_memory_dir(self, agent_type: str, scope: str = "project") -> Path:
|
|
135
|
+
sanitized = _sanitize_agent_type(agent_type)
|
|
136
|
+
sc = _normalize_memory_scope(scope)
|
|
137
|
+
return self.memory_dir / "agents" / sanitized / sc
|
|
138
|
+
|
|
139
|
+
def agent_memory_entrypoint(self, agent_type: str, scope: str = "project") -> Path:
|
|
140
|
+
return self.agent_memory_dir(agent_type, scope) / "MEMORY.md"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workspace/path_key_normalizer.py — 路径字符串键 NFC 稳定化
|
|
3
|
+
|
|
4
|
+
职责:对作为持久化键 / 文件名片段的路径相关字符串做 Unicode NFC,与 CC `normalize('NFC')` 同向。
|
|
5
|
+
在整体链路中的位置:AgentWorkspaceConfig 派生会话文件路径等边界;不在 expand_path 默认链上调用。
|
|
6
|
+
当前裁剪范围:仅 NFC;不 resolve、不替代 workspace 越界校验(见 validators)。调用方:``session_memory_path``、``_sanitize_agent_type``、``_normalize_memory_scope``(``agent_memory_dir``)。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import unicodedata
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True, slots=True)
|
|
16
|
+
class PathKeyNormalizer:
|
|
17
|
+
"""无状态键稳定化;多线程只读安全。"""
|
|
18
|
+
|
|
19
|
+
form: str = "NFC"
|
|
20
|
+
|
|
21
|
+
def normalize(self, path: str) -> str:
|
|
22
|
+
return unicodedata.normalize(self.form, path)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
_DEFAULT_NORMALIZER = PathKeyNormalizer()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def normalize_path_key(path: str) -> str:
|
|
29
|
+
"""模块级薄封装:会话 id、未来其它「键串」边界的默认 NFC。"""
|
|
30
|
+
return _DEFAULT_NORMALIZER.normalize(path)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workspace/resolver.py — AgentWorkspace 配置解析协作者
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
按统一优先级解析 workspace_root 与 agent_home,并产出
|
|
6
|
+
AgentWorkspaceConfig,作为全局目录语义单一来源。
|
|
7
|
+
|
|
8
|
+
在整体链路中的位置:
|
|
9
|
+
各入口(tool_runtime / task_runtime / loop / observability)
|
|
10
|
+
-> resolve_agent_workspace_config()
|
|
11
|
+
-> AgentWorkspaceResolver.resolve()
|
|
12
|
+
|
|
13
|
+
当前裁剪范围:
|
|
14
|
+
解析与校验;不直接做业务目录写入。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
from .config import AgentWorkspaceConfig
|
|
23
|
+
from .validators import AgentWorkspacePathValidator
|
|
24
|
+
|
|
25
|
+
DEFAULT_AGENT_HOME = ".langchain_agentx"
|
|
26
|
+
ENV_AGENT_HOME = "LANGCHAIN_AGENTX_AGENT_HOME"
|
|
27
|
+
ENV_WORKSPACE_ROOT = "LANGCHAIN_AGENTX_WORKSPACE_ROOT"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AgentWorkspaceResolver:
|
|
31
|
+
"""Agent workspace 配置解析器。"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
default_agent_home: str = DEFAULT_AGENT_HOME,
|
|
37
|
+
env_agent_home: str = ENV_AGENT_HOME,
|
|
38
|
+
env_workspace_root: str = ENV_WORKSPACE_ROOT,
|
|
39
|
+
path_validator: AgentWorkspacePathValidator | None = None,
|
|
40
|
+
) -> None:
|
|
41
|
+
self._default_agent_home = default_agent_home
|
|
42
|
+
self._env_agent_home = env_agent_home
|
|
43
|
+
self._env_workspace_root = env_workspace_root
|
|
44
|
+
self._path_validator = path_validator or AgentWorkspacePathValidator()
|
|
45
|
+
|
|
46
|
+
def resolve(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
workspace_root: str | Path | None = None,
|
|
50
|
+
agent_home: str | None = None,
|
|
51
|
+
) -> AgentWorkspaceConfig:
|
|
52
|
+
root_raw = workspace_root or os.getenv(self._env_workspace_root) or os.getcwd()
|
|
53
|
+
root = Path(root_raw).expanduser().resolve()
|
|
54
|
+
home_raw = (
|
|
55
|
+
agent_home
|
|
56
|
+
if agent_home is not None
|
|
57
|
+
else os.getenv(self._env_agent_home, self._default_agent_home)
|
|
58
|
+
)
|
|
59
|
+
home = self._path_validator.validate_agent_home(home_raw)
|
|
60
|
+
cfg = AgentWorkspaceConfig(workspace_root=root, agent_home=home)
|
|
61
|
+
self._path_validator.ensure_within_workspace(cfg.workspace_root, cfg.agent_home_dir)
|
|
62
|
+
return cfg
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
_DEFAULT_RESOLVER = AgentWorkspaceResolver()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def resolve_agent_workspace_config(
|
|
69
|
+
*,
|
|
70
|
+
workspace_root: str | Path | None = None,
|
|
71
|
+
agent_home: str | None = None,
|
|
72
|
+
) -> AgentWorkspaceConfig:
|
|
73
|
+
"""兼容入口:调用默认 AgentWorkspaceResolver。"""
|
|
74
|
+
return _DEFAULT_RESOLVER.resolve(workspace_root=workspace_root, agent_home=agent_home)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
workspace/validators.py — AgentWorkspace 路径校验协作者
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
统一封装 workspace 目录相关校验规则(agent_home 合法性、越界检查),
|
|
6
|
+
作为 resolver 的纯校验依赖,避免调用方散落 if/else 逻辑。
|
|
7
|
+
|
|
8
|
+
在整体链路中的位置:
|
|
9
|
+
AgentWorkspaceResolver.resolve()
|
|
10
|
+
-> AgentWorkspacePathValidator.validate_agent_home()
|
|
11
|
+
-> AgentWorkspacePathValidator.ensure_within_workspace()
|
|
12
|
+
|
|
13
|
+
当前裁剪范围:
|
|
14
|
+
仅覆盖路径规范与越界校验;不承载目录创建与业务策略。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentWorkspacePathValidator:
|
|
23
|
+
"""Agent workspace 路径规则校验器。"""
|
|
24
|
+
|
|
25
|
+
def validate_agent_home(self, agent_home: str) -> str:
|
|
26
|
+
text = (agent_home or "").strip()
|
|
27
|
+
if not text:
|
|
28
|
+
raise ValueError("agent_home cannot be empty")
|
|
29
|
+
path = Path(text)
|
|
30
|
+
if path.is_absolute():
|
|
31
|
+
raise ValueError("agent_home must be a relative path")
|
|
32
|
+
if ".." in path.parts:
|
|
33
|
+
raise ValueError("agent_home cannot contain '..'")
|
|
34
|
+
return text
|
|
35
|
+
|
|
36
|
+
def ensure_within_workspace(self, workspace_root: Path, candidate: Path) -> Path:
|
|
37
|
+
base = workspace_root.resolve()
|
|
38
|
+
resolved = candidate.resolve()
|
|
39
|
+
if not str(resolved).startswith(str(base)):
|
|
40
|
+
raise ValueError("Resolved path escapes workspace_root")
|
|
41
|
+
return resolved
|