langchain-agentx-cli 0.2.2__tar.gz → 0.3.1__tar.gz
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_cli-0.2.2 → langchain_agentx_cli-0.3.1}/PKG-INFO +2 -2
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/__init__.py +1 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/app.py +18 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/bridge/session_bridge.py +177 -15
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/cli.py +21 -4
- langchain_agentx_cli-0.3.1/langchain_agentx_cli/commands/builtin/compact.py +74 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/session_factory.py +32 -10
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/state/task_store.py +63 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/__init__.py +1 -3
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/presenters/base.py +2 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/presenters/bash.py +35 -2
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/message_events.py +122 -4
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/message_list.py +257 -11
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/messages/__init__.py +12 -0
- langchain_agentx_cli-0.3.1/langchain_agentx_cli/widgets/messages/hook_message.py +72 -0
- langchain_agentx_cli-0.3.1/langchain_agentx_cli/widgets/messages/stop_hook_summary.py +70 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/permission_inline.py +25 -27
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/permission_keybindings.py +6 -16
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/__init__.py +15 -8
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/agent/widget.py +34 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/ask_user_question/widget.py +34 -2
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/base.py +97 -15
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/bash/widget.py +41 -5
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/batch_edit/widget.py +30 -15
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/edit/widget.py +10 -5
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/glob/widget.py +10 -5
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/grep/widget.py +10 -5
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/helpers.py +56 -3
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/read/widget.py +46 -6
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/skill/widget.py +13 -6
- langchain_agentx_cli-0.3.1/langchain_agentx_cli/widgets/tools/user_message/widget.py +83 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/webfetch/widget.py +20 -5
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/websearch/widget.py +15 -5
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/write/widget.py +13 -6
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/PKG-INFO +2 -2
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/SOURCES.txt +2 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/requires.txt +1 -1
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/pyproject.toml +2 -2
- langchain_agentx_cli-0.2.2/langchain_agentx_cli/commands/builtin/compact.py +0 -44
- langchain_agentx_cli-0.2.2/langchain_agentx_cli/tui/permissions/inject.py +0 -57
- langchain_agentx_cli-0.2.2/langchain_agentx_cli/widgets/tools/user_message/widget.py +0 -88
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/LICENSE +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/README.md +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/__main__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/bootstrap.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/bridge/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/builtin/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/builtin/clear.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/builtin/help.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/builtin/model.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/builtin/quit.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/builtin/theme.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/parser.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/providers/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/providers/sdk_commands.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/commands/registry.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/completion/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/completion/base.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/completion/command_source.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/completion/history_source.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/config.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/history/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/history/store.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/keybindings/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/keybindings/bindings.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/llm_config.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/observation/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/observation/config.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/observation/observer.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/permissions/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/permissions/factory.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/permissions/policy.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/prompts/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/prompts/assembler.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/prompts/sections.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/prompts/system_prompt.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/screens/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/screens/repl.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/session_options.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/state/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/colors.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/detection.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/manager.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/settings.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/system_theme.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/themes.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/theme/watcher.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tools/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tools/registry.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/clipboard.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/message_selection.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/cache.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/component_manager.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/config.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/consumer.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/dialog.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/models.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/presenters/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/presenters/fallback.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/presenters/file.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/permissions/queue.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/tui/safe_screen.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/welcome.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/compact_progress.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/completion_overlay.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/input_area.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/messages/assistant_message.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/messages/error_message.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/messages/rendering.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/messages/thinking_message.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/pending_permission.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/spinner.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/task_panel.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/agent/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/ask_user_question/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/bash/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/batch_edit/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/edit/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/glob/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/grep/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/read/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/skill/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/user_message/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/webfetch/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/websearch/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/widgets/tools/write/__init__.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/dependency_links.txt +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/entry_points.txt +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/not-zip-safe +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli.egg-info/top_level.txt +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/setup.cfg +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_app_interrupt.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_llm_config_chain.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_repl_commands.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_repl_submit_flow.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_repl_ui.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_smoke.py +0 -0
- {langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/tests/test_welcome.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain-agentx-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Terminal CLI/TUI for AgentX: migrate Claude Code Ink UI, backed by langchain-agentx-python SDK.
|
|
5
5
|
Author: GoodMood2008
|
|
6
6
|
License: Apache-2.0
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Software Development
|
|
|
22
22
|
Requires-Python: >=3.11
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: langchain-agentx-python<0.
|
|
25
|
+
Requires-Dist: langchain-agentx-python<0.6.0,>=0.4.5
|
|
26
26
|
Requires-Dist: click>=8.1
|
|
27
27
|
Requires-Dist: textual>=0.79.0
|
|
28
28
|
Provides-Extra: dev
|
|
@@ -10,6 +10,7 @@ app.py — Textual ReplApp 容器。
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
+
import asyncio
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
from typing import TYPE_CHECKING
|
|
15
16
|
|
|
@@ -36,6 +37,10 @@ from langchain_agentx_cli.widgets.messages import UserMessage
|
|
|
36
37
|
|
|
37
38
|
if TYPE_CHECKING:
|
|
38
39
|
from langchain_agentx import AgentSession
|
|
40
|
+
from langchain_agentx.tool_runtime.resolvers import (
|
|
41
|
+
AgentSessionPermissionResolver,
|
|
42
|
+
PermissionRequest,
|
|
43
|
+
)
|
|
39
44
|
|
|
40
45
|
_CANCEL_HINT = "已取消当前生成。再次按 Ctrl+C 退出。"
|
|
41
46
|
_EXIT_HINT = "再次按 Ctrl+C 退出。"
|
|
@@ -64,11 +69,15 @@ class ReplApp(App[None]):
|
|
|
64
69
|
self,
|
|
65
70
|
launch_config: ReplLaunchConfig,
|
|
66
71
|
session: AgentSession | None = None,
|
|
72
|
+
permission_queue: asyncio.Queue[PermissionRequest] | None = None,
|
|
73
|
+
permission_resolver: AgentSessionPermissionResolver | None = None,
|
|
67
74
|
debug: bool = False,
|
|
68
75
|
) -> None:
|
|
69
76
|
super().__init__()
|
|
70
77
|
self._launch_config = launch_config
|
|
71
78
|
self._session = session
|
|
79
|
+
self._permission_queue = permission_queue
|
|
80
|
+
self._permission_resolver = permission_resolver
|
|
72
81
|
self._bridge: SessionBridge | None = None
|
|
73
82
|
self._exit_armed = False
|
|
74
83
|
self.command_registry = CommandRegistry()
|
|
@@ -109,7 +118,15 @@ class ReplApp(App[None]):
|
|
|
109
118
|
self.theme_manager = ThemeManager(self)
|
|
110
119
|
self.theme_manager.setup(initial=initial_setting)
|
|
111
120
|
if self._session is not None:
|
|
112
|
-
|
|
121
|
+
# 方案 A: 传入预先创建的 permission_queue 和 permission_resolver
|
|
122
|
+
if self._permission_queue is None or self._permission_resolver is None:
|
|
123
|
+
raise RuntimeError("permission_queue 和 permission_resolver 必须由 open_agent_session 提供")
|
|
124
|
+
self._bridge = SessionBridge(
|
|
125
|
+
self,
|
|
126
|
+
self._session,
|
|
127
|
+
permission_queue=self._permission_queue,
|
|
128
|
+
permission_resolver=self._permission_resolver,
|
|
129
|
+
)
|
|
113
130
|
register_sdk_commands(self.command_registry, self._session)
|
|
114
131
|
self._bridge.permission_consumer.start()
|
|
115
132
|
self.push_screen(ReplScreen())
|
|
@@ -29,7 +29,6 @@ from langchain_agentx_cli.observation import debug, info
|
|
|
29
29
|
from langchain_agentx_cli.tui.permissions import (
|
|
30
30
|
PermissionQueueConsumer,
|
|
31
31
|
SessionPermissionCache,
|
|
32
|
-
inject_permission_resolver,
|
|
33
32
|
)
|
|
34
33
|
from langchain_agentx_cli.tui.permissions.config import DEFAULT_PERMISSION_TIMEOUT_SECONDS
|
|
35
34
|
from langchain_agentx_cli.tui.permissions.dialog import DialogPermissionDecisionPort
|
|
@@ -46,6 +45,10 @@ from langchain_agentx_cli.widgets.messages import (
|
|
|
46
45
|
ContextCompactedMessage,
|
|
47
46
|
ContextCompactingMessage,
|
|
48
47
|
ErrorMessageOccurred,
|
|
48
|
+
HookFinished,
|
|
49
|
+
HookStarted,
|
|
50
|
+
TaskProgress,
|
|
51
|
+
TaskStarted,
|
|
49
52
|
ThinkingContentDelta,
|
|
50
53
|
ThinkingEnded,
|
|
51
54
|
ThinkingStarted,
|
|
@@ -63,9 +66,9 @@ if TYPE_CHECKING:
|
|
|
63
66
|
MVP_ADAPTER_CONFIG: dict[str, bool] = {
|
|
64
67
|
"enable_reasoning_events": True,
|
|
65
68
|
"enable_step_events": False,
|
|
66
|
-
"enable_hook_events":
|
|
69
|
+
"enable_hook_events": True, # 启用 Hook 事件
|
|
67
70
|
"enable_compact_events": True,
|
|
68
|
-
"enable_subagent_events":
|
|
71
|
+
"enable_subagent_events": True, # Phase 3: 启用子 agent 事件
|
|
69
72
|
"synthesize_tool_call": True,
|
|
70
73
|
}
|
|
71
74
|
|
|
@@ -85,22 +88,26 @@ class _TurnSegmentState:
|
|
|
85
88
|
class SessionBridge:
|
|
86
89
|
"""TUI 与 SDK 之间的桥接层(消费 LangchainAgentEvent)。"""
|
|
87
90
|
|
|
88
|
-
def __init__(
|
|
91
|
+
def __init__(
|
|
92
|
+
self,
|
|
93
|
+
app: ReplApp,
|
|
94
|
+
session: AgentSession,
|
|
95
|
+
*,
|
|
96
|
+
permission_queue: asyncio.Queue[PermissionRequest],
|
|
97
|
+
permission_resolver: AgentSessionPermissionResolver,
|
|
98
|
+
) -> None:
|
|
89
99
|
self._app = app
|
|
90
100
|
self._screen_messenger = app.screen_messenger
|
|
91
101
|
self._session = session
|
|
92
102
|
self._adapter = LangGraphToLangchainAgentEventAdapter(config=dict(MVP_ADAPTER_CONFIG))
|
|
93
103
|
self._current_worker: Any = None
|
|
94
104
|
self._seg = _TurnSegmentState()
|
|
95
|
-
#
|
|
96
|
-
self._permission_queue
|
|
105
|
+
# 方案 A: 接收预先创建的 queue 和 resolver(由 open_agent_session 传入)
|
|
106
|
+
self._permission_queue = permission_queue
|
|
97
107
|
self._permission_cache = SessionPermissionCache()
|
|
98
108
|
self._confirm_queue = ConfirmQueue(max_size=10) # 对齐 CC confirmQueue
|
|
99
|
-
self._permission_resolver =
|
|
100
|
-
|
|
101
|
-
timeout=DEFAULT_PERMISSION_TIMEOUT_SECONDS,
|
|
102
|
-
)
|
|
103
|
-
inject_permission_resolver(session, self._permission_resolver)
|
|
109
|
+
self._permission_resolver = permission_resolver
|
|
110
|
+
# 不再需要 inject_permission_resolver,SDK 在编译时已使用传入的 resolver
|
|
104
111
|
self._permission_consumer = PermissionQueueConsumer(
|
|
105
112
|
queue=self._permission_queue,
|
|
106
113
|
cache=self._permission_cache,
|
|
@@ -112,6 +119,27 @@ class SessionBridge:
|
|
|
112
119
|
def _post_ui(self, message: object) -> None:
|
|
113
120
|
self._screen_messenger.post(message)
|
|
114
121
|
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _build_fallback_text(data: dict[str, Any]) -> str:
|
|
124
|
+
"""为未升级的 widget 提供兜底渲染文本:summary + payload。
|
|
125
|
+
|
|
126
|
+
与 SDK envelope_to_tool_output 的语义一致(summary\\npayload),
|
|
127
|
+
让仍按 event.output 字符串渲染的 widget 不需要改动即可工作。
|
|
128
|
+
"""
|
|
129
|
+
summary = str(data.get("summary", ""))
|
|
130
|
+
payload = data.get("payload")
|
|
131
|
+
if payload is None or payload == "":
|
|
132
|
+
return summary
|
|
133
|
+
if isinstance(payload, str):
|
|
134
|
+
payload_str = payload
|
|
135
|
+
else:
|
|
136
|
+
import json
|
|
137
|
+
try:
|
|
138
|
+
payload_str = json.dumps(payload, ensure_ascii=False)
|
|
139
|
+
except (TypeError, ValueError):
|
|
140
|
+
payload_str = str(payload)
|
|
141
|
+
return f"{summary}\n{payload_str}" if summary else payload_str
|
|
142
|
+
|
|
115
143
|
def submit_input(self, text: str) -> None:
|
|
116
144
|
self._post_ui(UserMessage(content=text))
|
|
117
145
|
self._post_ui(TurnInProgress(in_progress=True))
|
|
@@ -168,30 +196,56 @@ class SessionBridge:
|
|
|
168
196
|
)
|
|
169
197
|
case LangchainAgentEventType.TOOL_INPUT:
|
|
170
198
|
tool_input = data.get("input")
|
|
199
|
+
tool_name = str(data.get("tool_name", ""))
|
|
171
200
|
if not isinstance(tool_input, dict):
|
|
172
201
|
tool_input = data.get("params") if isinstance(data.get("params"), dict) else {}
|
|
202
|
+
info("[BRIDGE] TOOL_INPUT tool_name=%s", tool_name)
|
|
173
203
|
self._post_ui(
|
|
174
204
|
ToolUseStarted(
|
|
175
|
-
tool_name=
|
|
205
|
+
tool_name=tool_name,
|
|
176
206
|
tool_input=tool_input,
|
|
177
207
|
)
|
|
178
208
|
)
|
|
179
209
|
case LangchainAgentEventType.TOOL_CALL:
|
|
210
|
+
tool_name = str(data.get("tool_name", ""))
|
|
211
|
+
info("[BRIDGE] TOOL_CALL tool_name=%s", tool_name)
|
|
180
212
|
pass
|
|
181
213
|
case LangchainAgentEventType.TOOL_RESULT:
|
|
214
|
+
tool_name = str(data.get("tool_name", ""))
|
|
215
|
+
fallback_output = self._build_fallback_text(data)
|
|
216
|
+
info(
|
|
217
|
+
"[BRIDGE] TOOL_RESULT tool_name=%s summary_len=%d payload_type=%s has_display=%s",
|
|
218
|
+
tool_name,
|
|
219
|
+
len(str(data.get("summary", ""))),
|
|
220
|
+
type(data.get("payload")).__name__,
|
|
221
|
+
bool(data.get("display")),
|
|
222
|
+
)
|
|
182
223
|
self._post_ui(
|
|
183
224
|
ToolUseCompleted(
|
|
184
|
-
tool_name=
|
|
185
|
-
output=
|
|
225
|
+
tool_name=tool_name,
|
|
226
|
+
output=fallback_output,
|
|
227
|
+
summary=str(data.get("summary", "")),
|
|
228
|
+
payload=data.get("payload"),
|
|
229
|
+
status=str(data.get("status", "ok")),
|
|
230
|
+
meta=data.get("meta") if isinstance(data.get("meta"), dict) else None,
|
|
231
|
+
display=data.get("display") if isinstance(data.get("display"), dict) else None,
|
|
232
|
+
truncated=bool(data.get("truncated", False)),
|
|
233
|
+
overflow_file=data.get("overflow_file"),
|
|
186
234
|
)
|
|
187
235
|
)
|
|
188
236
|
case LangchainAgentEventType.TOOL_ERROR:
|
|
237
|
+
tool_name = str(data.get("tool_name", ""))
|
|
189
238
|
error_text = str(data.get("error", ""))
|
|
190
239
|
self._post_ui(
|
|
191
240
|
ToolUseCompleted(
|
|
192
|
-
tool_name=
|
|
241
|
+
tool_name=tool_name,
|
|
193
242
|
output=f"Error: {error_text}",
|
|
194
243
|
error=error_text or None,
|
|
244
|
+
summary=str(data.get("summary", "")),
|
|
245
|
+
payload=data.get("payload"),
|
|
246
|
+
status=str(data.get("status", "error")),
|
|
247
|
+
meta=data.get("meta") if isinstance(data.get("meta"), dict) else None,
|
|
248
|
+
display=data.get("display") if isinstance(data.get("display"), dict) else None,
|
|
195
249
|
)
|
|
196
250
|
)
|
|
197
251
|
case LangchainAgentEventType.ERROR:
|
|
@@ -224,6 +278,114 @@ class SessionBridge:
|
|
|
224
278
|
case LangchainAgentEventType.COMPACT_START:
|
|
225
279
|
info("[BRIDGE] COMPACT_START — compacting...")
|
|
226
280
|
pass
|
|
281
|
+
# Phase 3: 子 agent 工具事件(通过 adispatch_custom_event 冒泡)
|
|
282
|
+
case LangchainAgentEventType.SUBAGENT_TOOL_CALL:
|
|
283
|
+
tool_name = str(data.get("tool_name", ""))
|
|
284
|
+
tool_input = data.get("tool_input")
|
|
285
|
+
parent_tool_use_id = data.get("parent_tool_use_id")
|
|
286
|
+
info(
|
|
287
|
+
"[BRIDGE] SUBAGENT_TOOL_CALL tool=%s parent=%s",
|
|
288
|
+
tool_name,
|
|
289
|
+
parent_tool_use_id,
|
|
290
|
+
)
|
|
291
|
+
self._post_ui(
|
|
292
|
+
ToolUseStarted(
|
|
293
|
+
tool_name=tool_name,
|
|
294
|
+
tool_input=tool_input if isinstance(tool_input, dict) else {},
|
|
295
|
+
parent_tool_use_id=parent_tool_use_id,
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
case LangchainAgentEventType.SUBAGENT_TOOL_RESULT:
|
|
299
|
+
tool_name = str(data.get("tool_name", ""))
|
|
300
|
+
parent_tool_use_id = data.get("parent_tool_use_id")
|
|
301
|
+
fallback_output = self._build_fallback_text(data)
|
|
302
|
+
info(
|
|
303
|
+
"[BRIDGE] SUBAGENT_TOOL_RESULT tool=%s parent=%s summary_len=%d",
|
|
304
|
+
tool_name,
|
|
305
|
+
parent_tool_use_id,
|
|
306
|
+
len(str(data.get("summary", ""))),
|
|
307
|
+
)
|
|
308
|
+
self._post_ui(
|
|
309
|
+
ToolUseCompleted(
|
|
310
|
+
tool_name=tool_name,
|
|
311
|
+
output=fallback_output,
|
|
312
|
+
summary=str(data.get("summary", "")),
|
|
313
|
+
payload=data.get("payload"),
|
|
314
|
+
status=str(data.get("status", "ok")),
|
|
315
|
+
meta=data.get("meta") if isinstance(data.get("meta"), dict) else None,
|
|
316
|
+
display=data.get("display") if isinstance(data.get("display"), dict) else None,
|
|
317
|
+
truncated=bool(data.get("truncated", False)),
|
|
318
|
+
overflow_file=data.get("overflow_file"),
|
|
319
|
+
parent_tool_use_id=parent_tool_use_id,
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
case LangchainAgentEventType.HOOK_STARTED:
|
|
323
|
+
hook_event = str(data.get("hook_event", ""))
|
|
324
|
+
hook_name = str(data.get("hook_name", ""))
|
|
325
|
+
info("[BRIDGE] HOOK_STARTED event=%s name=%s", hook_event, hook_name)
|
|
326
|
+
self._post_ui(
|
|
327
|
+
HookStarted(
|
|
328
|
+
hook_event=hook_event,
|
|
329
|
+
hook_name=hook_name,
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
case LangchainAgentEventType.HOOK_FINISHED:
|
|
333
|
+
hook_event = str(data.get("hook_event", ""))
|
|
334
|
+
hook_name = str(data.get("hook_name", ""))
|
|
335
|
+
outcome = str(data.get("outcome", ""))
|
|
336
|
+
info(
|
|
337
|
+
"[BRIDGE] HOOK_FINISHED event=%s name=%s outcome=%s stdout_len=%d stderr_len=%d",
|
|
338
|
+
hook_event,
|
|
339
|
+
hook_name,
|
|
340
|
+
outcome,
|
|
341
|
+
len(str(data.get("combined_stdout", ""))),
|
|
342
|
+
len(str(data.get("combined_stderr", ""))),
|
|
343
|
+
)
|
|
344
|
+
self._post_ui(
|
|
345
|
+
HookFinished(
|
|
346
|
+
hook_event=hook_event,
|
|
347
|
+
hook_name=hook_name,
|
|
348
|
+
outcome=outcome,
|
|
349
|
+
stdout=data.get("combined_stdout"),
|
|
350
|
+
stderr=data.get("combined_stderr"),
|
|
351
|
+
exit_codes=data.get("exit_codes"),
|
|
352
|
+
combined_stdout=data.get("combined_stdout"),
|
|
353
|
+
combined_stderr=data.get("combined_stderr"),
|
|
354
|
+
)
|
|
355
|
+
)
|
|
356
|
+
case LangchainAgentEventType.TASK_STARTED:
|
|
357
|
+
task_id = str(data.get("task_id", ""))
|
|
358
|
+
task_type = str(data.get("task_type", ""))
|
|
359
|
+
description = str(data.get("description", ""))
|
|
360
|
+
tool_use_id = data.get("tool_use_id")
|
|
361
|
+
info("[BRIDGE] TASK_STARTED id=%s type=%s", task_id, task_type)
|
|
362
|
+
self._post_ui(
|
|
363
|
+
TaskStarted(
|
|
364
|
+
task_id=task_id,
|
|
365
|
+
task_type=task_type,
|
|
366
|
+
description=description,
|
|
367
|
+
tool_use_id=tool_use_id,
|
|
368
|
+
prompt=data.get("prompt"),
|
|
369
|
+
workflow_name=data.get("workflow_name"),
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
case LangchainAgentEventType.TASK_PROGRESS:
|
|
373
|
+
task_id = str(data.get("task_id", ""))
|
|
374
|
+
description = str(data.get("description", ""))
|
|
375
|
+
usage = data.get("usage")
|
|
376
|
+
last_tool_name = data.get("last_tool_name")
|
|
377
|
+
summary = data.get("summary")
|
|
378
|
+
info("[BRIDGE] TASK_PROGRESS id=%s summary=%s", task_id, summary)
|
|
379
|
+
self._post_ui(
|
|
380
|
+
TaskProgress(
|
|
381
|
+
task_id=task_id,
|
|
382
|
+
description=description,
|
|
383
|
+
usage=usage if isinstance(usage, dict) else None,
|
|
384
|
+
last_tool_name=last_tool_name,
|
|
385
|
+
summary=summary,
|
|
386
|
+
tool_use_id=data.get("tool_use_id"),
|
|
387
|
+
)
|
|
388
|
+
)
|
|
227
389
|
case _:
|
|
228
390
|
pass
|
|
229
391
|
|
|
@@ -76,7 +76,7 @@ class CliEntry:
|
|
|
76
76
|
return 0
|
|
77
77
|
|
|
78
78
|
try:
|
|
79
|
-
session = asyncio.run(
|
|
79
|
+
session, permission_queue, permission_resolver = asyncio.run(
|
|
80
80
|
open_agent_session(
|
|
81
81
|
launch_config,
|
|
82
82
|
cli_provider=provider,
|
|
@@ -89,7 +89,13 @@ class CliEntry:
|
|
|
89
89
|
return 2
|
|
90
90
|
|
|
91
91
|
try:
|
|
92
|
-
return self._run_tui(
|
|
92
|
+
return self._run_tui(
|
|
93
|
+
launch_config,
|
|
94
|
+
session,
|
|
95
|
+
permission_queue=permission_queue,
|
|
96
|
+
permission_resolver=permission_resolver,
|
|
97
|
+
debug=debug,
|
|
98
|
+
)
|
|
93
99
|
finally:
|
|
94
100
|
asyncio.run(close_agent_session(session))
|
|
95
101
|
|
|
@@ -140,9 +146,20 @@ class CliEntry:
|
|
|
140
146
|
return mode_map.get(mode.lower(), ".langchain_agentx")
|
|
141
147
|
|
|
142
148
|
def _run_tui(
|
|
143
|
-
self,
|
|
149
|
+
self,
|
|
150
|
+
launch_config: ReplLaunchConfig,
|
|
151
|
+
session, # noqa: ANN001
|
|
152
|
+
permission_queue, # noqa: ANN001
|
|
153
|
+
permission_resolver, # noqa: ANN001
|
|
154
|
+
debug: bool = False,
|
|
144
155
|
) -> int:
|
|
145
|
-
app = ReplApp(
|
|
156
|
+
app = ReplApp(
|
|
157
|
+
launch_config=launch_config,
|
|
158
|
+
session=session,
|
|
159
|
+
permission_queue=permission_queue,
|
|
160
|
+
permission_resolver=permission_resolver,
|
|
161
|
+
debug=debug,
|
|
162
|
+
)
|
|
146
163
|
app.run()
|
|
147
164
|
return 0
|
|
148
165
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""builtin/compact.py — /compact 命令(调用 SDK compact_context)。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from langchain_agentx_cli.commands.registry import Command, CommandContext
|
|
6
|
+
from langchain_agentx_cli.widgets.message_events import (
|
|
7
|
+
CompactFinished,
|
|
8
|
+
CompactProgressUpdate,
|
|
9
|
+
CompactStarted,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
_SESSION_REQUIRED = "错误:会话未建立,无法压缩上下文"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def handle_compact(ctx: CommandContext) -> str:
|
|
16
|
+
if ctx.bridge is None or not ctx.bridge.is_session_ready():
|
|
17
|
+
return _SESSION_REQUIRED
|
|
18
|
+
|
|
19
|
+
# 对齐 CC:支持可选的自定义总结指令参数
|
|
20
|
+
custom_instructions = ctx.args.strip() if ctx.args else None
|
|
21
|
+
|
|
22
|
+
# 通知 TUI 开始 compact(mount 进度条 widget)
|
|
23
|
+
ctx.app.post_message(CompactStarted())
|
|
24
|
+
|
|
25
|
+
def on_progress(p) -> None: # noqa: ANN001
|
|
26
|
+
"""SDK 回调:向 TUI 发送进度更新。"""
|
|
27
|
+
ctx.app.post_message(
|
|
28
|
+
CompactProgressUpdate(percent=p.percent, detail=p.detail)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# 如果有自定义指令,设置到 CompactionSettings
|
|
32
|
+
if custom_instructions:
|
|
33
|
+
from langchain_agentx.loop.context.settings import (
|
|
34
|
+
CompactionSettings,
|
|
35
|
+
set_active_compaction_settings,
|
|
36
|
+
get_active_compaction_settings,
|
|
37
|
+
)
|
|
38
|
+
current = get_active_compaction_settings()
|
|
39
|
+
# 创建新的 settings,只替换 compact_custom_instructions
|
|
40
|
+
updated = CompactionSettings(
|
|
41
|
+
**{**current.__dict__, 'compact_custom_instructions': custom_instructions}
|
|
42
|
+
)
|
|
43
|
+
set_active_compaction_settings(updated)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
before, after = await ctx.bridge.session.compact_context(
|
|
47
|
+
progress_callback=on_progress
|
|
48
|
+
)
|
|
49
|
+
finally:
|
|
50
|
+
# 恢复原始设置(即使出错也要恢复)
|
|
51
|
+
if custom_instructions:
|
|
52
|
+
from langchain_agentx.loop.context.settings import set_active_compaction_settings, get_active_compaction_settings
|
|
53
|
+
current = get_active_compaction_settings()
|
|
54
|
+
updated = CompactionSettings(
|
|
55
|
+
**{**current.__dict__, 'compact_custom_instructions': None}
|
|
56
|
+
)
|
|
57
|
+
set_active_compaction_settings(updated)
|
|
58
|
+
|
|
59
|
+
# 通知 TUI 结束(移除进度条,显示结果)
|
|
60
|
+
ctx.app.post_message(CompactFinished(before=before, after=after))
|
|
61
|
+
|
|
62
|
+
pct = int(round(after / before * 100)) if before > 0 else 0
|
|
63
|
+
result = f"[信息] 已压缩:{before} tokens → {after} tokens ({pct}%)"
|
|
64
|
+
if custom_instructions:
|
|
65
|
+
result += f"\n[使用自定义指令: {custom_instructions}]"
|
|
66
|
+
return result
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def make_compact_command() -> Command:
|
|
70
|
+
return Command(
|
|
71
|
+
name="compact",
|
|
72
|
+
description="压缩当前会话上下文。可选: /compact [自定义总结指令]",
|
|
73
|
+
handler=handle_compact,
|
|
74
|
+
)
|
{langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/session_factory.py
RENAMED
|
@@ -13,6 +13,7 @@ session_factory.py — AgentSession 创建与生命周期。
|
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import asyncio
|
|
16
17
|
from typing import TYPE_CHECKING
|
|
17
18
|
|
|
18
19
|
from langchain_agentx_cli.bootstrap import CliEnvironmentError
|
|
@@ -27,6 +28,10 @@ if TYPE_CHECKING:
|
|
|
27
28
|
from langchain_core.language_models.chat_models import BaseChatModel
|
|
28
29
|
|
|
29
30
|
from langchain_agentx import AgentSession
|
|
31
|
+
from langchain_agentx.tool_runtime.resolvers import (
|
|
32
|
+
AgentSessionPermissionResolver,
|
|
33
|
+
PermissionRequest,
|
|
34
|
+
)
|
|
30
35
|
|
|
31
36
|
from langchain_agentx_cli.config import ReplLaunchConfig
|
|
32
37
|
|
|
@@ -64,9 +69,15 @@ async def open_agent_session(
|
|
|
64
69
|
cli_model: str | None = None,
|
|
65
70
|
enable_git_snapshot: bool | None = None,
|
|
66
71
|
include_default_session_context: bool = True,
|
|
67
|
-
) -> AgentSession:
|
|
68
|
-
"""创建并进入 AgentSession(构建 graph、SESSION_START hook)。
|
|
72
|
+
) -> tuple[AgentSession, asyncio.Queue[PermissionRequest], AgentSessionPermissionResolver]:
|
|
73
|
+
"""创建并进入 AgentSession(构建 graph、SESSION_START hook)。
|
|
74
|
+
|
|
75
|
+
返回: (session, permission_queue, permission_resolver)
|
|
76
|
+
permission_queue 和 permission_resolver 由调用方传递给 SessionBridge。
|
|
77
|
+
"""
|
|
69
78
|
from langchain_agentx import create_agent_session
|
|
79
|
+
from langchain_agentx.tool_runtime.resolvers import AgentSessionPermissionResolver
|
|
80
|
+
from langchain_agentx_cli.tui.permissions.config import DEFAULT_PERMISSION_TIMEOUT_SECONDS
|
|
70
81
|
|
|
71
82
|
cfg = resolve_llm_config(cli_provider=cli_provider, cli_model=cli_model)
|
|
72
83
|
llm = build_chat_model(cfg)
|
|
@@ -82,18 +93,29 @@ async def open_agent_session(
|
|
|
82
93
|
skip_permissions=launch_config.skip_permissions,
|
|
83
94
|
)
|
|
84
95
|
|
|
96
|
+
# 方案 A: 在 session 创建前创建 permission_resolver,通过 loop_kwargs 传入
|
|
97
|
+
permission_queue: asyncio.Queue[PermissionRequest] = asyncio.Queue()
|
|
98
|
+
permission_resolver = AgentSessionPermissionResolver(
|
|
99
|
+
request_queue=permission_queue,
|
|
100
|
+
timeout=DEFAULT_PERMISSION_TIMEOUT_SECONDS,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
loop_kwargs = build_session_loop_kwargs(
|
|
104
|
+
workspace_root=launch_config.workspace_root,
|
|
105
|
+
model=cfg.model,
|
|
106
|
+
provider=cfg.provider,
|
|
107
|
+
enable_git_snapshot=enable_git_snapshot,
|
|
108
|
+
include_default_session_context=include_default_session_context,
|
|
109
|
+
)
|
|
110
|
+
# 注入 permission_resolver 到 loop_kwargs
|
|
111
|
+
loop_kwargs["permission_resolver"] = permission_resolver
|
|
112
|
+
|
|
85
113
|
session = create_agent_session(
|
|
86
114
|
llm,
|
|
87
115
|
workspace_root=launch_config.workspace_root,
|
|
88
116
|
agent_home=launch_config.agent_home,
|
|
89
117
|
loader=loader,
|
|
90
|
-
**
|
|
91
|
-
workspace_root=launch_config.workspace_root,
|
|
92
|
-
model=cfg.model,
|
|
93
|
-
provider=cfg.provider,
|
|
94
|
-
enable_git_snapshot=enable_git_snapshot,
|
|
95
|
-
include_default_session_context=include_default_session_context,
|
|
96
|
-
),
|
|
118
|
+
**loop_kwargs,
|
|
97
119
|
)
|
|
98
120
|
try:
|
|
99
121
|
await session.__aenter__()
|
|
@@ -103,7 +125,7 @@ async def open_agent_session(
|
|
|
103
125
|
raise CliEnvironmentError(
|
|
104
126
|
f"无法创建 AgentSession(provider={cfg.provider!r}, model={cfg.model!r}):{exc}"
|
|
105
127
|
) from exc
|
|
106
|
-
return session
|
|
128
|
+
return session, permission_queue, permission_resolver
|
|
107
129
|
|
|
108
130
|
|
|
109
131
|
async def close_agent_session(session: AgentSession) -> None:
|
{langchain_agentx_cli-0.2.2 → langchain_agentx_cli-0.3.1}/langchain_agentx_cli/state/task_store.py
RENAMED
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
import time
|
|
20
20
|
from dataclasses import dataclass, field
|
|
21
21
|
from enum import Enum
|
|
22
|
-
from typing import Callable
|
|
22
|
+
from typing import Any, Callable
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class TaskStatus(Enum):
|
|
@@ -165,6 +165,68 @@ class TaskStore:
|
|
|
165
165
|
del self._tasks[task_id]
|
|
166
166
|
self._notify()
|
|
167
167
|
|
|
168
|
+
def update_from_tool_result(
|
|
169
|
+
self,
|
|
170
|
+
tool_name: str,
|
|
171
|
+
tool_input: dict,
|
|
172
|
+
*,
|
|
173
|
+
display: dict[str, Any] | None = None,
|
|
174
|
+
output: str = "",
|
|
175
|
+
) -> None:
|
|
176
|
+
"""从 TOOL_RESULT 的 display 字段同步 Task 状态(D09 + SDK 双流)。"""
|
|
177
|
+
d = display or {}
|
|
178
|
+
now = time.time()
|
|
179
|
+
|
|
180
|
+
if tool_name == "TaskCreate":
|
|
181
|
+
if not d.get("success"):
|
|
182
|
+
return
|
|
183
|
+
task_id = str(d.get("task_id") or "").strip()
|
|
184
|
+
if not task_id:
|
|
185
|
+
return
|
|
186
|
+
subject = str(d.get("subject") or tool_input.get("subject", ""))
|
|
187
|
+
description = str(tool_input.get("description", ""))
|
|
188
|
+
if task_id in self._tasks:
|
|
189
|
+
self._tasks[task_id].subject = subject or self._tasks[task_id].subject
|
|
190
|
+
else:
|
|
191
|
+
self._tasks[task_id] = TaskEntry(
|
|
192
|
+
id=task_id,
|
|
193
|
+
subject=subject,
|
|
194
|
+
description=description,
|
|
195
|
+
status=TaskStatus.PENDING.value,
|
|
196
|
+
active_form=tool_input.get("activeForm"),
|
|
197
|
+
metadata=tool_input.get("metadata"),
|
|
198
|
+
)
|
|
199
|
+
self._notify()
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
if tool_name == "TaskUpdate":
|
|
203
|
+
task_id = str(d.get("task_id") or tool_input.get("taskId", "")).strip()
|
|
204
|
+
if not task_id or task_id not in self._tasks:
|
|
205
|
+
return
|
|
206
|
+
entry = self._tasks[task_id]
|
|
207
|
+
new_status = d.get("new_status")
|
|
208
|
+
if new_status:
|
|
209
|
+
if (
|
|
210
|
+
new_status == TaskStatus.COMPLETED.value
|
|
211
|
+
and entry.status != new_status
|
|
212
|
+
):
|
|
213
|
+
entry.completed_at = now
|
|
214
|
+
entry.status = str(new_status)
|
|
215
|
+
self._notify()
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
if tool_name == "TaskGet" and d.get("found") and d.get("task_id"):
|
|
219
|
+
task_id = str(d["task_id"])
|
|
220
|
+
if task_id in self._tasks and d.get("status"):
|
|
221
|
+
self._tasks[task_id].status = str(d["status"])
|
|
222
|
+
self._notify()
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
if tool_name == "TaskList":
|
|
226
|
+
# 列表工具不改变单条 Task;TaskPanel 仍由 started 事件驱动
|
|
227
|
+
_ = output
|
|
228
|
+
return
|
|
229
|
+
|
|
168
230
|
def get_sorted_tasks(self) -> list[TaskEntry]:
|
|
169
231
|
"""获取排序后的 Task 列表(对齐 CC 优先级)。
|
|
170
232
|
|
|
@@ -6,7 +6,7 @@ tui/permissions — 权限确认子系统(Queue + Future)。
|
|
|
6
6
|
集成 ConfirmQueue 状态管理(对齐 CC)。
|
|
7
7
|
|
|
8
8
|
链路位置:
|
|
9
|
-
|
|
9
|
+
open_agent_session 创建 Queue/Resolver → SessionBridge → PermissionQueueConsumer → SDK Future。
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
from langchain_agentx_cli.tui.permissions.cache import SessionPermissionCache
|
|
@@ -19,7 +19,6 @@ from langchain_agentx_cli.tui.permissions.consumer import (
|
|
|
19
19
|
from langchain_agentx_cli.tui.permissions.dialog import (
|
|
20
20
|
DialogPermissionDecisionPort,
|
|
21
21
|
)
|
|
22
|
-
from langchain_agentx_cli.tui.permissions.inject import inject_permission_resolver
|
|
23
22
|
from langchain_agentx_cli.tui.permissions.models import PermissionDecision
|
|
24
23
|
from langchain_agentx_cli.tui.permissions.presenters import (
|
|
25
24
|
PermissionPresentation,
|
|
@@ -38,6 +37,5 @@ __all__ = [
|
|
|
38
37
|
"PermissionQueueConsumer",
|
|
39
38
|
"QueuedPermission",
|
|
40
39
|
"SessionPermissionCache",
|
|
41
|
-
"inject_permission_resolver",
|
|
42
40
|
"permission_presenter_for_tool",
|
|
43
41
|
]
|