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,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LangChain-based agent utilities for CodeBaseX.
|
|
3
|
+
|
|
4
|
+
This package provides:
|
|
5
|
+
- LangChain-AgentX LangGraph agent factory (`loop`)
|
|
6
|
+
|
|
7
|
+
Typical usage:
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from langchain_agentx import create_loop_agent
|
|
11
|
+
```
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
from .loop import ( # noqa: F401
|
|
17
|
+
create_loop_agent,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .observability import ( # noqa: F401
|
|
21
|
+
EventAdapterState,
|
|
22
|
+
LangchainAgentEvent,
|
|
23
|
+
LangchainAgentEventType,
|
|
24
|
+
LangGraphToLangchainAgentEventAdapter,
|
|
25
|
+
TraceCollector,
|
|
26
|
+
TraceSession,
|
|
27
|
+
TraceSpan,
|
|
28
|
+
)
|
|
29
|
+
from .provider import CompatibleChatOpenAI # noqa: F401
|
|
30
|
+
from .session import AgentSession, create_agent_session # noqa: F401
|
|
31
|
+
from .workflow import BaseWorkflow # noqa: F401
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"create_loop_agent",
|
|
35
|
+
"create_agent_session",
|
|
36
|
+
"AgentSession",
|
|
37
|
+
"BaseWorkflow",
|
|
38
|
+
"EventAdapterState",
|
|
39
|
+
"LangchainAgentEvent",
|
|
40
|
+
"LangchainAgentEventType",
|
|
41
|
+
"LangGraphToLangchainAgentEventAdapter",
|
|
42
|
+
"TraceCollector",
|
|
43
|
+
"TraceSession",
|
|
44
|
+
"TraceSpan",
|
|
45
|
+
"CompatibleChatOpenAI",
|
|
46
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/__init__.py — 命令子系统公共导出。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
暴露 command 子系统核心类型、注册表与调度器。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 session 层与上层应用统一 import 使用。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅导出 P1 落地必需对象。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .context import CommandContext
|
|
15
|
+
from .dispatcher import CommandDispatcher
|
|
16
|
+
from .registry import CommandRegistry
|
|
17
|
+
from .result import CommandResult
|
|
18
|
+
from .types import CommandDefinition, CommandType
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"CommandContext",
|
|
22
|
+
"CommandDefinition",
|
|
23
|
+
"CommandDispatcher",
|
|
24
|
+
"CommandRegistry",
|
|
25
|
+
"CommandResult",
|
|
26
|
+
"CommandType",
|
|
27
|
+
]
|
|
28
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/builtin/__init__.py — 内置命令工厂导出。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
汇总 builtin 命令定义工厂,供 Session 初始化注册时调用。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
被 session/agent_session.py 与 session/conversation_session.py 依赖。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅导出 P1 阶段四个内置命令。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from .clear import make_clear_command
|
|
15
|
+
from .compact import make_compact_command
|
|
16
|
+
from .memory import make_memory_command
|
|
17
|
+
from .reload_plugins import make_reload_plugins_command
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"make_clear_command",
|
|
21
|
+
"make_compact_command",
|
|
22
|
+
"make_memory_command",
|
|
23
|
+
"make_reload_plugins_command",
|
|
24
|
+
]
|
|
25
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/builtin/clear.py — 内置 /clear 命令。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
触发 Session.clear_context(),清空当前会话上下文。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 CommandRegistry 注册,dispatcher 命中后执行。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅返回本地输出,不附带 UI 交互状态。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from ..context import CommandContext
|
|
17
|
+
from ..result import CommandResult
|
|
18
|
+
from ..types import CommandDefinition
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _handle_clear(ctx: CommandContext) -> CommandResult:
|
|
22
|
+
ctx.session.clear_context()
|
|
23
|
+
return CommandResult(output="会话上下文已清空")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def make_clear_command() -> CommandDefinition:
|
|
27
|
+
return CommandDefinition(
|
|
28
|
+
name="clear",
|
|
29
|
+
description="清空当前会话上下文",
|
|
30
|
+
command_type="local",
|
|
31
|
+
handler=_handle_clear,
|
|
32
|
+
)
|
|
33
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/builtin/compact.py — 内置 /compact 命令。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
调用 Session.compact_context() 并返回压缩前后统计。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 CommandRegistry 注册,作为 local 命令执行。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅输出 token 粗估统计,不暴露阶段级 compaction meta。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from ..context import CommandContext
|
|
17
|
+
from ..result import CommandResult
|
|
18
|
+
from ..types import CommandDefinition
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def _handle_compact(ctx: CommandContext) -> CommandResult:
|
|
22
|
+
before, after = await ctx.session.compact_context()
|
|
23
|
+
return CommandResult(output=f"上下文已压缩:{before} -> {after} tokens")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def make_compact_command() -> CommandDefinition:
|
|
27
|
+
return CommandDefinition(
|
|
28
|
+
name="compact",
|
|
29
|
+
description="压缩当前会话上下文",
|
|
30
|
+
command_type="local",
|
|
31
|
+
handler=_handle_compact,
|
|
32
|
+
)
|
|
33
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/builtin/memory.py — 内置 /memory 命令。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
读取 args 子命令并调用 Session.get_memory_summary()。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 CommandRegistry 注册,dispatcher 通过 context.extra 传入 args。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
支持 show(默认)/clear,输出文本由 Session API 统一生成。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from ..context import CommandContext
|
|
17
|
+
from ..result import CommandResult
|
|
18
|
+
from ..types import CommandDefinition
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _handle_memory(ctx: CommandContext) -> CommandResult:
|
|
22
|
+
args = str(ctx.extra.get("args", "")).strip().lower()
|
|
23
|
+
sub_cmd = args if args else "show"
|
|
24
|
+
if sub_cmd not in {"show", "clear"}:
|
|
25
|
+
return CommandResult(error=f"[command/execute] invalid /memory sub-command: {sub_cmd}")
|
|
26
|
+
output = ctx.session.get_memory_summary(sub_cmd=sub_cmd)
|
|
27
|
+
return CommandResult(output=output)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def make_memory_command() -> CommandDefinition:
|
|
31
|
+
return CommandDefinition(
|
|
32
|
+
name="memory",
|
|
33
|
+
description="查看或清理会话记忆摘要",
|
|
34
|
+
command_type="local",
|
|
35
|
+
handler=_handle_memory,
|
|
36
|
+
)
|
|
37
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/builtin/reload_plugins.py — 内置 /reload-plugins 命令。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
触发 Session.reload_plugins() 并返回加载摘要。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 CommandRegistry 注册,命中后走 local 异步执行。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
输出成功/失败计数,不展开逐条错误详情。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from ..context import CommandContext
|
|
17
|
+
from ..result import CommandResult
|
|
18
|
+
from ..types import CommandDefinition
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
async def _handle_reload_plugins(ctx: CommandContext) -> CommandResult:
|
|
22
|
+
if ctx.plugin_loader is None:
|
|
23
|
+
return CommandResult(output="Plugin 系统未启用")
|
|
24
|
+
result = await ctx.session.reload_plugins()
|
|
25
|
+
if result.errors:
|
|
26
|
+
return CommandResult(
|
|
27
|
+
output=(
|
|
28
|
+
f"插件重载完成({len(result.enabled)} 个成功,"
|
|
29
|
+
f"{len(result.errors)} 个错误)"
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
return CommandResult(output=f"插件重载完成:{len(result.enabled)} 个插件已加载")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def make_reload_plugins_command() -> CommandDefinition:
|
|
36
|
+
return CommandDefinition(
|
|
37
|
+
name="reload-plugins",
|
|
38
|
+
description="热重载插件并刷新命令/技能注册",
|
|
39
|
+
command_type="local",
|
|
40
|
+
handler=_handle_reload_plugins,
|
|
41
|
+
)
|
|
42
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/context.py — 命令执行上下文。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
为命令 handler 提供 Session API、workspace 与插件重载所需依赖。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 Session 在 dispatch 前构造,贯穿单次命令执行。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
以 session 注入为主,不暴露内部消息容器引用。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from ..workspace.config import AgentWorkspaceConfig
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class CommandContext:
|
|
24
|
+
session_id: str
|
|
25
|
+
session: Any
|
|
26
|
+
workspace_cfg: AgentWorkspaceConfig
|
|
27
|
+
hook_engine: Any | None = None
|
|
28
|
+
plugin_loader: Any | None = None
|
|
29
|
+
extra: dict[str, Any] = field(default_factory=dict)
|
|
30
|
+
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/dispatcher.py — 用户输入命令分流与执行器。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
解析 slash 输入、查找命令、执行 local/prompt 分派并返回 CommandResult。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
位于 Session 用户输入入口与 loop 调用之间,属于 loop 上游控制层。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
当前阶段只处理内置命令与普通 prompt,不处理 CLI local-tui 语义。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import inspect
|
|
17
|
+
import logging
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from .context import CommandContext
|
|
21
|
+
from .registry import CommandRegistry
|
|
22
|
+
from .result import CommandResult
|
|
23
|
+
from .types import CommandDefinition
|
|
24
|
+
from ..plugin.registries import CommandEntry, CommandRegistry as PluginCommandRegistry
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CommandDispatcher:
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
registry: CommandRegistry,
|
|
33
|
+
plugin_command_registry: PluginCommandRegistry | None = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
self._registry = registry
|
|
36
|
+
self._plugin_registry = plugin_command_registry
|
|
37
|
+
self._plugin_markdown_cache: dict[Path, tuple[int, tuple[str, str]]] = {}
|
|
38
|
+
|
|
39
|
+
def is_slash_command(self, user_input: str) -> bool:
|
|
40
|
+
return user_input.strip().startswith("/")
|
|
41
|
+
|
|
42
|
+
async def dispatch(self, user_input: str, context: CommandContext) -> CommandResult:
|
|
43
|
+
stripped = user_input.strip()
|
|
44
|
+
if not stripped.startswith("/"):
|
|
45
|
+
return CommandResult(should_query=True)
|
|
46
|
+
|
|
47
|
+
name, args = self._parse(stripped)
|
|
48
|
+
if not name:
|
|
49
|
+
return CommandResult(error=self._format_error("parse", "command must be `/name [args]`"))
|
|
50
|
+
command = self._registry.find(name)
|
|
51
|
+
if command is None:
|
|
52
|
+
command, lookup_error = self._find_plugin_command(name)
|
|
53
|
+
if lookup_error is not None:
|
|
54
|
+
return CommandResult(error=self._format_error("lookup", lookup_error))
|
|
55
|
+
if command is None:
|
|
56
|
+
return CommandResult(error=self._format_error("lookup", f"unknown command: /{name}"))
|
|
57
|
+
if not command.user_invocable:
|
|
58
|
+
return CommandResult(
|
|
59
|
+
error=self._format_error("permission", f"/{command.name} is not user-invocable")
|
|
60
|
+
)
|
|
61
|
+
if bool(context.extra.get("is_remote")) and not command.remote_safe:
|
|
62
|
+
return CommandResult(
|
|
63
|
+
error=self._format_error("permission", f"/{command.name} is not remote-safe")
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
context.extra["args"] = args
|
|
67
|
+
return await self._execute(command, args, context)
|
|
68
|
+
|
|
69
|
+
def _parse(self, slash_input: str) -> tuple[str, str]:
|
|
70
|
+
body = slash_input[1:]
|
|
71
|
+
parts = body.split(None, 1)
|
|
72
|
+
if not parts:
|
|
73
|
+
return "", ""
|
|
74
|
+
name = parts[0].lower()
|
|
75
|
+
args = parts[1].strip() if len(parts) > 1 else ""
|
|
76
|
+
return name, args
|
|
77
|
+
|
|
78
|
+
async def _execute(
|
|
79
|
+
self,
|
|
80
|
+
command: CommandDefinition,
|
|
81
|
+
args: str,
|
|
82
|
+
context: CommandContext,
|
|
83
|
+
) -> CommandResult:
|
|
84
|
+
if command.command_type == "prompt":
|
|
85
|
+
return self._execute_prompt(command, args)
|
|
86
|
+
return await self._execute_local(command, context)
|
|
87
|
+
|
|
88
|
+
async def _execute_local(
|
|
89
|
+
self,
|
|
90
|
+
command: CommandDefinition,
|
|
91
|
+
context: CommandContext,
|
|
92
|
+
) -> CommandResult:
|
|
93
|
+
if command.handler is None:
|
|
94
|
+
return CommandResult(error=self._format_error("execute", f"/{command.name}: handler not set"))
|
|
95
|
+
try:
|
|
96
|
+
result = command.handler(context)
|
|
97
|
+
if inspect.isawaitable(result):
|
|
98
|
+
result = await result
|
|
99
|
+
if not isinstance(result, CommandResult):
|
|
100
|
+
return CommandResult(
|
|
101
|
+
error=self._format_error(
|
|
102
|
+
"execute",
|
|
103
|
+
f"/{command.name}: handler returned unexpected type",
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
return result
|
|
107
|
+
except Exception as exc: # pragma: no cover - defensive path
|
|
108
|
+
logger.error("command /%s execution failed: %s", command.name, exc, exc_info=True)
|
|
109
|
+
return CommandResult(error=self._format_error("execute", f"/{command.name} failed: {exc}"))
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def _execute_prompt(command: CommandDefinition, args: str) -> CommandResult:
|
|
113
|
+
if command.prompt_template is None:
|
|
114
|
+
return CommandResult(
|
|
115
|
+
error=CommandDispatcher._format_error(
|
|
116
|
+
"execute",
|
|
117
|
+
f"/{command.name}: prompt_template not set",
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
rendered = command.prompt_template.replace("{args}", args)
|
|
121
|
+
return CommandResult(should_query=True, injected_prompt=rendered)
|
|
122
|
+
|
|
123
|
+
def _find_plugin_command(
|
|
124
|
+
self,
|
|
125
|
+
name: str,
|
|
126
|
+
) -> tuple[CommandDefinition | None, str | None]:
|
|
127
|
+
if self._plugin_registry is None or not name:
|
|
128
|
+
return None, None
|
|
129
|
+
all_entries = list(self._plugin_registry.list_all())
|
|
130
|
+
# 先匹配完整 key(/plugin@local:cmd)以规避短名冲突歧义。
|
|
131
|
+
exact_matches = [item for item in all_entries if item.key == name]
|
|
132
|
+
if len(exact_matches) == 1:
|
|
133
|
+
return self._entry_to_definition(exact_matches[0]), None
|
|
134
|
+
same_short_name = [item for item in all_entries if item.name == name]
|
|
135
|
+
if len(same_short_name) == 1:
|
|
136
|
+
return self._entry_to_definition(same_short_name[0]), None
|
|
137
|
+
if len(same_short_name) > 1:
|
|
138
|
+
candidates = ", ".join(f"/{item.key}" for item in same_short_name)
|
|
139
|
+
return None, f"unknown command: /{name} (ambiguous plugin command: {candidates})"
|
|
140
|
+
return None, None
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def _format_error(error_type: str, detail: str) -> str:
|
|
144
|
+
return f"[command/{error_type}] {detail}"
|
|
145
|
+
|
|
146
|
+
def _entry_to_definition(self, entry: CommandEntry) -> CommandDefinition:
|
|
147
|
+
prompt_template, description = self._load_md(entry.path)
|
|
148
|
+
return CommandDefinition(
|
|
149
|
+
name=entry.name,
|
|
150
|
+
description=description or f"Plugin command from {entry.plugin_source}",
|
|
151
|
+
command_type="prompt",
|
|
152
|
+
prompt_template=prompt_template,
|
|
153
|
+
source=entry.plugin_source,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def _parse_markdown(text: str) -> tuple[str, str]:
|
|
158
|
+
description = ""
|
|
159
|
+
body = text
|
|
160
|
+
if text.startswith("---"):
|
|
161
|
+
parts = text.split("---", 2)
|
|
162
|
+
if len(parts) == 3:
|
|
163
|
+
frontmatter = parts[1]
|
|
164
|
+
body = parts[2]
|
|
165
|
+
for line in frontmatter.splitlines():
|
|
166
|
+
stripped = line.strip()
|
|
167
|
+
if stripped.startswith("description:"):
|
|
168
|
+
description = stripped[len("description:") :].strip().strip("'\"")
|
|
169
|
+
break
|
|
170
|
+
return body.strip(), description
|
|
171
|
+
|
|
172
|
+
def _load_md(self, path: Path) -> tuple[str, str]:
|
|
173
|
+
stat = path.stat()
|
|
174
|
+
current_mtime_ns = stat.st_mtime_ns
|
|
175
|
+
cached = self._plugin_markdown_cache.get(path)
|
|
176
|
+
if cached is not None and cached[0] == current_mtime_ns:
|
|
177
|
+
return cached[1]
|
|
178
|
+
|
|
179
|
+
text = path.read_text(encoding="utf-8")
|
|
180
|
+
parsed = self._parse_markdown(text)
|
|
181
|
+
self._plugin_markdown_cache[path] = (current_mtime_ns, parsed)
|
|
182
|
+
return parsed
|
|
183
|
+
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/registry.py — 统一命令注册表。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
管理命令注册、冲突检测、别名查找与来源批量注销。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 Session 初始化并注入 dispatcher,作为命令发现入口。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
本阶段仅注册内置命令;register_plugin 接口已预留。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from collections.abc import Iterable
|
|
17
|
+
|
|
18
|
+
from .types import CommandDefinition
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CommandRegistry:
|
|
22
|
+
"""统一命令注册表。"""
|
|
23
|
+
|
|
24
|
+
def __init__(self) -> None:
|
|
25
|
+
self._commands: dict[str, CommandDefinition] = {}
|
|
26
|
+
self._alias_to_name: dict[str, str] = {}
|
|
27
|
+
self._by_source: dict[str, set[str]] = {}
|
|
28
|
+
|
|
29
|
+
def register(self, command: CommandDefinition) -> None:
|
|
30
|
+
normalized_name = self._normalize(command.name)
|
|
31
|
+
if normalized_name in self._commands:
|
|
32
|
+
raise ValueError(f"duplicate command name: {normalized_name}")
|
|
33
|
+
self._ensure_aliases_available(command.aliases)
|
|
34
|
+
self._commands[normalized_name] = command
|
|
35
|
+
self._by_source.setdefault(command.source, set()).add(normalized_name)
|
|
36
|
+
for alias in command.aliases:
|
|
37
|
+
self._alias_to_name[self._normalize(alias)] = normalized_name
|
|
38
|
+
|
|
39
|
+
def register_plugin(
|
|
40
|
+
self,
|
|
41
|
+
commands: Iterable[CommandDefinition],
|
|
42
|
+
*,
|
|
43
|
+
plugin_source: str,
|
|
44
|
+
) -> None:
|
|
45
|
+
for command in commands:
|
|
46
|
+
if command.source != plugin_source:
|
|
47
|
+
command = CommandDefinition(
|
|
48
|
+
name=command.name,
|
|
49
|
+
description=command.description,
|
|
50
|
+
command_type=command.command_type,
|
|
51
|
+
handler=command.handler,
|
|
52
|
+
prompt_template=command.prompt_template,
|
|
53
|
+
aliases=command.aliases,
|
|
54
|
+
user_invocable=command.user_invocable,
|
|
55
|
+
source=plugin_source,
|
|
56
|
+
)
|
|
57
|
+
self.register(command)
|
|
58
|
+
|
|
59
|
+
def unregister_plugin(self, plugin_source: str) -> None:
|
|
60
|
+
names = self._by_source.pop(plugin_source, set())
|
|
61
|
+
for name in names:
|
|
62
|
+
command = self._commands.pop(name, None)
|
|
63
|
+
if command is None:
|
|
64
|
+
continue
|
|
65
|
+
for alias in command.aliases:
|
|
66
|
+
self._alias_to_name.pop(self._normalize(alias), None)
|
|
67
|
+
|
|
68
|
+
def find(self, name: str) -> CommandDefinition | None:
|
|
69
|
+
normalized = self._normalize(name)
|
|
70
|
+
command = self._commands.get(normalized)
|
|
71
|
+
if command is not None:
|
|
72
|
+
return command
|
|
73
|
+
mapped_name = self._alias_to_name.get(normalized)
|
|
74
|
+
if mapped_name is None:
|
|
75
|
+
return None
|
|
76
|
+
return self._commands.get(mapped_name)
|
|
77
|
+
|
|
78
|
+
def list_all(self, *, include_non_user_invocable: bool = True) -> list[CommandDefinition]:
|
|
79
|
+
commands = list(self._commands.values())
|
|
80
|
+
if include_non_user_invocable:
|
|
81
|
+
return commands
|
|
82
|
+
return [cmd for cmd in commands if cmd.user_invocable]
|
|
83
|
+
|
|
84
|
+
def list_for_help(self) -> list[dict[str, object]]:
|
|
85
|
+
items: list[dict[str, object]] = []
|
|
86
|
+
for command in self.list_all(include_non_user_invocable=False):
|
|
87
|
+
items.append(
|
|
88
|
+
{
|
|
89
|
+
"name": command.name,
|
|
90
|
+
"description": command.description,
|
|
91
|
+
"aliases": command.aliases,
|
|
92
|
+
"source": command.source,
|
|
93
|
+
"command_type": command.command_type,
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
items.sort(key=lambda item: str(item["name"]))
|
|
97
|
+
return items
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _normalize(name: str) -> str:
|
|
101
|
+
return name.strip().lower()
|
|
102
|
+
|
|
103
|
+
def _ensure_aliases_available(self, aliases: tuple[str, ...]) -> None:
|
|
104
|
+
for alias in aliases:
|
|
105
|
+
normalized = self._normalize(alias)
|
|
106
|
+
if normalized in self._commands:
|
|
107
|
+
raise ValueError(f"alias conflicts with command name: {normalized}")
|
|
108
|
+
if normalized in self._alias_to_name:
|
|
109
|
+
raise ValueError(f"duplicate alias: {normalized}")
|
|
110
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/result.py — 命令分发执行结果模型。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
统一表达命令分发后的去向(是否进入 loop)与可见输出/错误。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 dispatcher 返回给 Session/调用方,用于决定后续控制流。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅保留 P0/P1 所需字段;不包含 UI 渲染细节。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class CommandResult:
|
|
21
|
+
should_query: bool = False
|
|
22
|
+
output: str | None = None
|
|
23
|
+
injected_prompt: str | None = None
|
|
24
|
+
error: str | None = None
|
|
25
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
command/types.py — 命令定义与类型约束。
|
|
3
|
+
|
|
4
|
+
职责:
|
|
5
|
+
定义命令类型枚举与 CommandDefinition,不承载调度逻辑。
|
|
6
|
+
|
|
7
|
+
链路位置:
|
|
8
|
+
由 registry/dispatcher/session 共同依赖,作为命令子系统的数据契约层。
|
|
9
|
+
|
|
10
|
+
当前裁剪范围:
|
|
11
|
+
仅覆盖 local/prompt 两类框架命令,不包含 CLI local-tui 扩展类型。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from collections.abc import Awaitable, Callable
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import TYPE_CHECKING, Literal
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from .context import CommandContext
|
|
22
|
+
from .result import CommandResult
|
|
23
|
+
|
|
24
|
+
CommandType = Literal["local", "prompt"]
|
|
25
|
+
CommandHandler = Callable[
|
|
26
|
+
["CommandContext"], "CommandResult | Awaitable[CommandResult]"
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class CommandDefinition:
|
|
32
|
+
name: str
|
|
33
|
+
description: str
|
|
34
|
+
command_type: CommandType
|
|
35
|
+
handler: CommandHandler | None = None
|
|
36
|
+
prompt_template: str | None = None
|
|
37
|
+
aliases: tuple[str, ...] = field(default_factory=tuple)
|
|
38
|
+
user_invocable: bool = True
|
|
39
|
+
remote_safe: bool = True
|
|
40
|
+
source: str = "builtin"
|
|
41
|
+
|