langchain-agentx-cli 0.1.4__tar.gz → 0.2.0__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.
Files changed (134) hide show
  1. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/PKG-INFO +1 -1
  2. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/__init__.py +1 -1
  3. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/app.py +3 -1
  4. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/bridge/session_bridge.py +14 -7
  5. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/cli.py +2 -2
  6. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/screens/repl.py +23 -11
  7. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/themes.py +5 -0
  8. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/clipboard.py +47 -13
  9. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/message_selection.py +8 -7
  10. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/__init__.py +4 -0
  11. langchain_agentx_cli-0.2.0/langchain_agentx_cli/tui/permissions/component_manager.py +148 -0
  12. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/consumer.py +34 -6
  13. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/dialog.py +7 -2
  14. langchain_agentx_cli-0.2.0/langchain_agentx_cli/tui/permissions/queue.py +170 -0
  15. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/message_events.py +9 -4
  16. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/message_list.py +22 -39
  17. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/messages/__init__.py +2 -0
  18. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/messages/thinking_message.py +7 -8
  19. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/pending_permission.py +3 -3
  20. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/permission_inline.py +29 -23
  21. langchain_agentx_cli-0.2.0/langchain_agentx_cli/widgets/permission_keybindings.py +174 -0
  22. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/spinner.py +9 -1
  23. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/base.py +11 -8
  24. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/PKG-INFO +1 -1
  25. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/SOURCES.txt +3 -0
  26. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/pyproject.toml +1 -1
  27. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/LICENSE +0 -0
  28. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/README.md +0 -0
  29. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/__main__.py +0 -0
  30. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/bootstrap.py +0 -0
  31. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/bridge/__init__.py +0 -0
  32. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/__init__.py +0 -0
  33. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/__init__.py +0 -0
  34. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/clear.py +0 -0
  35. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/compact.py +0 -0
  36. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/help.py +0 -0
  37. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/model.py +0 -0
  38. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/quit.py +0 -0
  39. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/builtin/theme.py +0 -0
  40. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/parser.py +0 -0
  41. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/providers/__init__.py +0 -0
  42. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/providers/sdk_commands.py +0 -0
  43. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/commands/registry.py +0 -0
  44. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/completion/__init__.py +0 -0
  45. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/completion/base.py +0 -0
  46. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/completion/command_source.py +0 -0
  47. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/completion/history_source.py +0 -0
  48. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/config.py +0 -0
  49. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/history/__init__.py +0 -0
  50. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/history/store.py +0 -0
  51. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/keybindings/__init__.py +0 -0
  52. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/keybindings/bindings.py +0 -0
  53. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/llm_config.py +0 -0
  54. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/permissions/__init__.py +0 -0
  55. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/permissions/factory.py +0 -0
  56. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/permissions/policy.py +0 -0
  57. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/prompts/__init__.py +0 -0
  58. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/prompts/assembler.py +0 -0
  59. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/prompts/sections.py +0 -0
  60. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/prompts/system_prompt.py +0 -0
  61. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/screens/__init__.py +0 -0
  62. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/session_factory.py +0 -0
  63. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/session_options.py +0 -0
  64. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/state/__init__.py +0 -0
  65. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/state/task_store.py +0 -0
  66. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/__init__.py +0 -0
  67. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/colors.py +0 -0
  68. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/detection.py +0 -0
  69. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/manager.py +0 -0
  70. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/settings.py +0 -0
  71. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/system_theme.py +0 -0
  72. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/theme/watcher.py +0 -0
  73. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tools/__init__.py +0 -0
  74. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tools/registry.py +0 -0
  75. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/__init__.py +0 -0
  76. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/cache.py +0 -0
  77. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/config.py +0 -0
  78. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/inject.py +0 -0
  79. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/models.py +0 -0
  80. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/presenters/__init__.py +0 -0
  81. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/presenters/base.py +0 -0
  82. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/presenters/bash.py +0 -0
  83. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/presenters/fallback.py +0 -0
  84. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/permissions/presenters/file.py +0 -0
  85. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/tui/safe_screen.py +0 -0
  86. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/welcome.py +0 -0
  87. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/__init__.py +0 -0
  88. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/compact_progress.py +0 -0
  89. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/completion_overlay.py +0 -0
  90. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/input_area.py +0 -0
  91. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/messages/assistant_message.py +0 -0
  92. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/messages/error_message.py +0 -0
  93. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/messages/rendering.py +0 -0
  94. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/task_panel.py +0 -0
  95. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/__init__.py +0 -0
  96. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/agent/__init__.py +0 -0
  97. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/agent/widget.py +0 -0
  98. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/ask_user_question/__init__.py +0 -0
  99. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/ask_user_question/widget.py +0 -0
  100. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/bash/__init__.py +0 -0
  101. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/bash/widget.py +0 -0
  102. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/batch_edit/__init__.py +0 -0
  103. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/batch_edit/widget.py +0 -0
  104. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/edit/__init__.py +0 -0
  105. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/edit/widget.py +0 -0
  106. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/glob/__init__.py +0 -0
  107. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/glob/widget.py +0 -0
  108. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/grep/__init__.py +0 -0
  109. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/grep/widget.py +0 -0
  110. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/helpers.py +0 -0
  111. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/read/__init__.py +0 -0
  112. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/read/widget.py +0 -0
  113. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/skill/__init__.py +0 -0
  114. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/skill/widget.py +0 -0
  115. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/user_message/__init__.py +0 -0
  116. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/user_message/widget.py +0 -0
  117. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/webfetch/__init__.py +0 -0
  118. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/webfetch/widget.py +0 -0
  119. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/websearch/__init__.py +0 -0
  120. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/websearch/widget.py +0 -0
  121. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/write/__init__.py +0 -0
  122. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli/widgets/tools/write/widget.py +0 -0
  123. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/dependency_links.txt +0 -0
  124. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/entry_points.txt +0 -0
  125. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/not-zip-safe +0 -0
  126. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/requires.txt +0 -0
  127. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/langchain_agentx_cli.egg-info/top_level.txt +0 -0
  128. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/setup.cfg +0 -0
  129. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/tests/test_app_interrupt.py +0 -0
  130. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/tests/test_repl_commands.py +0 -0
  131. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/tests/test_repl_submit_flow.py +0 -0
  132. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/tests/test_repl_ui.py +0 -0
  133. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/tests/test_smoke.py +0 -0
  134. {langchain_agentx_cli-0.1.4 → langchain_agentx_cli-0.2.0}/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.1.4
3
+ Version: 0.2.0
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
@@ -1,3 +1,3 @@
1
1
  """langchain_agentx_cli:依托 langchain_agentx_python SDK 的终端 CLI/TUI(CC Ink 迁移)。"""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
@@ -123,7 +123,9 @@ class ReplApp(App[None]):
123
123
  self._exit_armed = False
124
124
 
125
125
  def action_cancel_stream(self) -> None:
126
- if try_copy_message_selection(self) is not None:
126
+ result = try_copy_message_selection(self)
127
+ if result is not None:
128
+ # 有选区被复制(无论成功与否),不执行后续操作
127
129
  return
128
130
  # Dismiss inline permission widget if active
129
131
  try:
@@ -8,7 +8,7 @@ bridge/session_bridge.py — SessionBridge(SDK 事件 → Textual Message)
8
8
  链路位置:
9
9
  ReplApp → SessionBridge → AgentSession.stream_loop_events
10
10
  → LangGraphToLangchainAgentEventAdapter.adapt();
11
- 权限:AgentSessionPermissionResolver ← PermissionQueueConsumer。
11
+ 权限:AgentSessionPermissionResolver ← PermissionQueueConsumer ← ConfirmQueue
12
12
 
13
13
  当前裁剪范围:
14
14
  不解析 LangGraph 原始事件名;Resolver 超时 300s(见 tui/permissions/config.py)。
@@ -31,6 +31,7 @@ from langchain_agentx_cli.tui.permissions import (
31
31
  )
32
32
  from langchain_agentx_cli.tui.permissions.config import DEFAULT_PERMISSION_TIMEOUT_SECONDS
33
33
  from langchain_agentx_cli.tui.permissions.dialog import DialogPermissionDecisionPort
34
+ from langchain_agentx_cli.tui.permissions.queue import ConfirmQueue
34
35
 
35
36
  from langchain_agentx import (
36
37
  LangGraphToLangchainAgentEventAdapter,
@@ -41,6 +42,7 @@ from langchain_agentx import (
41
42
  from langchain_agentx_cli.widgets.messages import (
42
43
  AssistantMessageChunk,
43
44
  ContextCompactedMessage,
45
+ ContextCompactingMessage,
44
46
  ErrorMessageOccurred,
45
47
  ThinkingContentDelta,
46
48
  ThinkingEnded,
@@ -78,8 +80,10 @@ class SessionBridge:
78
80
  self._adapter = LangGraphToLangchainAgentEventAdapter(config=dict(MVP_ADAPTER_CONFIG))
79
81
  self._current_worker: Any = None
80
82
  self._streamed_text = False
83
+ # 对齐 CC: 集中式权限队列状态管理
81
84
  self._permission_queue: asyncio.Queue[PermissionRequest] = asyncio.Queue()
82
85
  self._permission_cache = SessionPermissionCache()
86
+ self._confirm_queue = ConfirmQueue(max_size=10) # 对齐 CC confirmQueue
83
87
  self._permission_resolver = AgentSessionPermissionResolver(
84
88
  request_queue=self._permission_queue,
85
89
  timeout=DEFAULT_PERMISSION_TIMEOUT_SECONDS,
@@ -88,6 +92,7 @@ class SessionBridge:
88
92
  self._permission_consumer = PermissionQueueConsumer(
89
93
  queue=self._permission_queue,
90
94
  cache=self._permission_cache,
95
+ confirm_queue=self._confirm_queue, # 传入 confirm_queue
91
96
  app=app,
92
97
  decision_port=DialogPermissionDecisionPort(app),
93
98
  )
@@ -186,12 +191,9 @@ class SessionBridge:
186
191
  self._emit_finish_answer(str(data.get("answer", "")))
187
192
  self._post_ui(TurnInProgress(in_progress=False))
188
193
  case LangchainAgentEventType.COMPACT_END:
189
- self._post_ui(
190
- ContextCompactedMessage(
191
- summary=str(data.get("summary", "")),
192
- tokens_freed=int(data.get("tokens_freed", 0)),
193
- )
194
- )
194
+ self._post_ui(ContextCompactedMessage())
195
+ case LangchainAgentEventType.COMPACT_START:
196
+ self._post_ui(ContextCompactingMessage())
195
197
  case _:
196
198
  pass
197
199
 
@@ -226,6 +228,11 @@ class SessionBridge:
226
228
  def permission_cache(self) -> SessionPermissionCache:
227
229
  return self._permission_cache
228
230
 
231
+ @property
232
+ def confirm_queue(self) -> ConfirmQueue:
233
+ """对齐 CC: confirmQueue 状态访问。"""
234
+ return self._confirm_queue
235
+
229
236
  @property
230
237
  def permission_resolver(self) -> AgentSessionPermissionResolver:
231
238
  return self._permission_resolver
@@ -17,8 +17,8 @@ from pathlib import Path
17
17
 
18
18
  import click
19
19
 
20
- # 过滤 LangGraph 的 PendingDeprecationWarning(避免污染启动画面)
21
- warnings.filterwarnings("ignore", category=DeprecationWarning, module="langgraph")
20
+ # 过滤 LangGraph 的 allowed_objects 警告(避免污染启动画面)
21
+ warnings.filterwarnings("ignore", message=".*allowed_objects.*")
22
22
 
23
23
  from langchain_agentx_cli.app import ReplApp
24
24
  from langchain_agentx_cli.bootstrap import CliEnvironmentError, EnvironmentChecker
@@ -29,6 +29,7 @@ from langchain_agentx_cli.widgets.message_list import MessageListWidget
29
29
  from langchain_agentx_cli.widgets.messages import (
30
30
  AssistantMessageChunk,
31
31
  ContextCompactedMessage,
32
+ ContextCompactingMessage,
32
33
  ErrorMessageOccurred,
33
34
  InfoMessage,
34
35
  PermissionPendingHidden,
@@ -130,13 +131,17 @@ class ReplScreen(Screen):
130
131
  self._message_list().append_info(content)
131
132
 
132
133
  def _copy_message_selection_if_any(self) -> bool:
133
- copied = try_copy_message_selection(self.app)
134
- if copied is None:
134
+ result = try_copy_message_selection(self.app)
135
+ if result is None:
135
136
  return False
137
+ copied, failure_reason = result
138
+ # 根据是否失败选择不同的 severity 和 timeout
139
+ severity = "warning" if failure_reason else "information"
140
+ timeout = 5 if failure_reason else 2
136
141
  self.notify(
137
- format_copied_toast(len(copied)),
138
- severity="information",
139
- timeout=2,
142
+ format_copied_toast(len(copied), failure_reason),
143
+ severity=severity,
144
+ timeout=timeout,
140
145
  )
141
146
  return True
142
147
 
@@ -236,12 +241,13 @@ class ReplScreen(Screen):
236
241
  def on_spinner_completion(self, event: SpinnerCompletion) -> None:
237
242
  self._message_list().append_spinner_status(event.content)
238
243
 
244
+ @on(ContextCompactingMessage)
245
+ def on_context_compacting(self, _event: ContextCompactingMessage) -> None:
246
+ self._message_list().spinner_enter_compacting()
247
+
239
248
  @on(ContextCompactedMessage)
240
- def on_context_compacted(self, event: ContextCompactedMessage) -> None:
241
- self._message_list().append_context_compacted(
242
- summary=event.summary,
243
- tokens_freed=event.tokens_freed,
244
- )
249
+ def on_context_compacted(self, _event: ContextCompactedMessage) -> None:
250
+ self._message_list().append_context_compacted()
245
251
 
246
252
  @on(ThinkingStarted)
247
253
  def on_thinking_started(self, _event: ThinkingStarted) -> None:
@@ -265,10 +271,16 @@ class ReplScreen(Screen):
265
271
 
266
272
  @on(PermissionPendingHidden)
267
273
  def on_permission_pending_hidden(self, _event: PermissionPendingHidden) -> None:
274
+ """权限确认隐藏后的清理。
275
+
276
+ 修复:不立即抢回焦点,让用户自然操作或由下一个UI元素控制焦点。
277
+ 如果输入区当前无焦点且无其他元素有焦点,再考虑恢复输入区焦点。
278
+ """
268
279
  self._message_list().hide_permission_pending()
269
280
  input_area = self.query_one("#input-area", InputAreaWidget)
270
281
  input_area.set_permission_pending(False)
271
- input_area.focus()
282
+ # 不立即抢回焦点,避免打断用户交互流
283
+ # 焦点会在自然状态下返回到输入区(用户按键时)
272
284
 
273
285
  # D10: Compact 进度条事件处理
274
286
 
@@ -39,6 +39,10 @@ def _build_rgb_theme(
39
39
  palette: dict[str, str],
40
40
  dark: bool,
41
41
  ) -> Theme:
42
+ variables = {
43
+ "screen-selection-background": "#264F78" if dark else "#B4D5FF",
44
+ "screen-selection-foreground": rgb_to_hex(palette["text"]),
45
+ }
42
46
  return Theme(
43
47
  name=name,
44
48
  primary=rgb_to_hex(palette["claude"]),
@@ -52,6 +56,7 @@ def _build_rgb_theme(
52
56
  surface=rgb_to_hex(palette["userMessageBackground"]),
53
57
  panel=rgb_to_hex(palette["panel"]),
54
58
  dark=dark,
59
+ variables=variables,
55
60
  )
56
61
 
57
62
 
@@ -51,10 +51,11 @@ def _wrap_for_multiplexer(sequence: str) -> str:
51
51
 
52
52
 
53
53
  def _write_osc52_to_tty(sequence: str) -> bool:
54
- """直接写入 TTY fd,绕过 Textual stdout 接管。
54
+ """直接写入终端 fd,对齐 CC stdout 的行为。
55
55
 
56
- Textual alternate screen 模式下接管了 stdout,OSC 52 序列会被吞掉。
57
- /dev/tty 始终指向控制终端,不经过 stdout 重定向。
56
+ CC (Ink) 通过 process.stdout.write(raw) 发送 OSC 52
57
+ Textual 接管了 sys.stdout 但底层 fd 1 仍连接终端。
58
+ 直接用 os.write(1, ...) 绕过 Python IO 层。
58
59
 
59
60
  Args:
60
61
  sequence: OSC 52 序列(已包装 tmux/screen)
@@ -62,23 +63,31 @@ def _write_osc52_to_tty(sequence: str) -> bool:
62
63
  Returns:
63
64
  是否成功写入
64
65
  """
65
- # 优先尝试打开 /dev/tty(始终指向控制终端)
66
+ encoded = sequence.encode("utf-8")
67
+
68
+ # 对齐 CC:写 stdout fd(网页终端通常只监听 stdout)
69
+ try:
70
+ os.write(1, encoded)
71
+ return True
72
+ except OSError:
73
+ pass
74
+
75
+ # 回退:/dev/tty
66
76
  try:
67
77
  fd = os.open("/dev/tty", os.O_WRONLY)
68
78
  try:
69
- os.write(fd, sequence.encode("utf-8"))
79
+ os.write(fd, encoded)
70
80
  return True
71
81
  finally:
72
82
  os.close(fd)
73
83
  except OSError:
74
84
  pass
75
85
 
76
- # 回退:尝试 stderr(Textual 通常不接管 stderr)
86
+ # 最后回退:stderr
77
87
  try:
78
- sys.stderr.write(sequence)
79
- sys.stderr.flush()
88
+ os.write(2, encoded)
80
89
  return True
81
- except Exception:
90
+ except OSError:
82
91
  return False
83
92
 
84
93
 
@@ -172,7 +181,7 @@ def _copy_native(text: str) -> bool:
172
181
  return False
173
182
 
174
183
 
175
- def copy_text(text: str) -> bool:
184
+ def copy_text(text: str) -> tuple[bool, str | None]:
176
185
  """
177
186
  复制文本到剪贴板(对齐 CC setClipboard)。
178
187
 
@@ -184,10 +193,10 @@ def copy_text(text: str) -> bool:
184
193
  text: 要复制的文本
185
194
 
186
195
  Returns:
187
- 是否成功执行了复制操作(注意:OSC 52 无法验证是否成功)
196
+ (是否成功, 失败原因描述) - 成功时第二个值为 None
188
197
  """
189
198
  if not text:
190
- return False
199
+ return False, "无文本内容"
191
200
 
192
201
  # 1. 优先尝试 native copy(fire-and-forget)
193
202
  native_ok = _copy_native(text)
@@ -199,4 +208,29 @@ def copy_text(text: str) -> bool:
199
208
 
200
209
  # 直接写入 TTY,绕过 Textual 的 stdout 接管
201
210
  osc52_ok = _write_osc52_to_tty(wrapped_seq)
202
- return osc52_ok or native_ok
211
+
212
+ if osc52_ok or native_ok:
213
+ return True, None
214
+
215
+ # 诊断失败原因
216
+ reasons = []
217
+ if platform.system().lower() == "linux":
218
+ if os.environ.get("SSH_CONNECTION"):
219
+ reasons.append("SSH 环境无法使用 xclip/xsel")
220
+ elif not (os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")):
221
+ reasons.append("无 DISPLAY/WAYLAND_DISPLAY 环境变量")
222
+ else:
223
+ cmd = _detect_linux_copy_command()
224
+ if cmd:
225
+ reasons.append(f"{cmd} 存在但调用失败")
226
+ else:
227
+ reasons.append("未安装 xclip/wl-copy/xsel(sudo yum install xclip)")
228
+
229
+ if os.environ.get("TMUX"):
230
+ reasons.append("tmux 环境可能不支持 OSC 52")
231
+ elif os.environ.get("STY"):
232
+ reasons.append("screen 环境可能不支持 OSC 52")
233
+ else:
234
+ reasons.append("终端可能不支持 OSC 52")
235
+
236
+ return False, ",".join(reasons)
@@ -46,8 +46,8 @@ def message_list_has_selection(screen: Screen) -> bool:
46
46
  return any(selection_in_message_list(w) for w in selections if w.is_attached)
47
47
 
48
48
 
49
- def try_copy_message_selection(app: App) -> str | None:
50
- """若消息区有选区则复制到剪贴板,返回已复制文本。"""
49
+ def try_copy_message_selection(app: App) -> tuple[str, str | None] | None:
50
+ """若消息区有选区则复制到剪贴板,返回 (已复制文本, 失败原因)。"""
51
51
  try:
52
52
  screen = app.screen
53
53
  except ScreenStackError:
@@ -58,11 +58,12 @@ def try_copy_message_selection(app: App) -> str | None:
58
58
  if not selected or not selected.strip():
59
59
  return None
60
60
 
61
- # 使用 CC 策略:native copy + OSC 52(直接写入 stdout)
62
- copy_text(selected)
61
+ # 对齐 CC:OSC 52 stdout + native copy 双重保险
62
+ ok, reason = copy_text(selected)
63
+ return (selected, reason)
63
64
 
64
- return selected
65
65
 
66
-
67
- def format_copied_toast(char_count: int) -> str:
66
+ def format_copied_toast(char_count: int, failure_reason: str | None = None) -> str:
67
+ if failure_reason:
68
+ return f"复制失败:{failure_reason}({char_count} 个字符)"
68
69
  return f"已复制 {char_count} 个字符到剪贴板"
@@ -3,6 +3,7 @@ tui/permissions — 权限确认子系统(Queue + Future)。
3
3
 
4
4
  职责:
5
5
  消费 SDK AgentSessionPermissionResolver 队列,会话缓存,驱动用户确认(内联 Widget)。
6
+ 集成 ConfirmQueue 状态管理(对齐 CC)。
6
7
 
7
8
  链路位置:
8
9
  SessionBridge 创建 Queue/Resolver → PermissionQueueConsumer → SDK Future。
@@ -24,15 +25,18 @@ from langchain_agentx_cli.tui.permissions.presenters import (
24
25
  PermissionPresentation,
25
26
  permission_presenter_for_tool,
26
27
  )
28
+ from langchain_agentx_cli.tui.permissions.queue import ConfirmQueue, QueuedPermission
27
29
 
28
30
  __all__ = [
29
31
  "DEFAULT_PERMISSION_TIMEOUT_SECONDS",
32
+ "ConfirmQueue",
30
33
  "DialogPermissionDecisionPort",
31
34
  "NoDialogPermissionDecisionPort",
32
35
  "PermissionDecision",
33
36
  "PermissionDecisionPort",
34
37
  "PermissionPresentation",
35
38
  "PermissionQueueConsumer",
39
+ "QueuedPermission",
36
40
  "SessionPermissionCache",
37
41
  "inject_permission_resolver",
38
42
  "permission_presenter_for_tool",
@@ -0,0 +1,148 @@
1
+ """
2
+ tui/permissions/component_manager.py — 权限组件生命周期管理(对齐 CC)。
3
+
4
+ 职责:
5
+ 管理权限 Widget 的挂载、卸载和焦点转移。
6
+ 确保组件完全准备好后再显示,对齐 CC 的 Portal 和 autoFocus 行为。
7
+
8
+ 链路位置:
9
+ ConfirmQueue → PermissionComponentManager → MessageListWidget。
10
+
11
+ 对齐 CC:
12
+ - useEffect 自动处理焦点
13
+ - Portal 组件的挂载时机
14
+ - 组件卸载时的清理
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import asyncio
20
+ import logging
21
+ from typing import TYPE_CHECKING, Callable
22
+
23
+ from textual.timer import Timer
24
+
25
+ if TYPE_CHECKING:
26
+ from langchain_agentx_cli.tui.permissions.queue import ConfirmQueue, QueuedPermission
27
+ from langchain_agentx_cli.widgets.message_list import MessageListWidget
28
+ from langchain_agentx_cli.widgets.permission_inline import InlinePermissionWidget
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class PermissionComponentManager:
34
+ """权限组件生命周期管理器(对齐 CC 组件管理)。
35
+
36
+ CC 对照:
37
+ useEffect(() => {
38
+ if (confirm && ref.current) {
39
+ ref.current.focus();
40
+ }
41
+ }, [confirm]);
42
+
43
+ 提供类似功能:
44
+ - mount_widget() - 挂载并聚焦
45
+ - unmount_widget() - 卸载并清理
46
+ - ensure_focused() - 确保获得焦点
47
+ """
48
+
49
+ def __init__(self, queue: ConfirmQueue, message_list: MessageListWidget) -> None:
50
+ self._queue = queue
51
+ self._message_list = message_list
52
+ self._focus_retry_timer: Timer | None = None
53
+ self._max_focus_retries = 5
54
+
55
+ async def mount_and_focus(
56
+ self,
57
+ queued: QueuedPermission,
58
+ widget: InlinePermissionWidget,
59
+ ) -> bool:
60
+ """挂载 Widget 并确保获得焦点(对齐 CC autoFocus)。
61
+
62
+ 返回是否成功挂载并获得焦点。
63
+ """
64
+ # 先挂载 widget
65
+ self._message_list.mount(widget)
66
+ queued.widget = widget
67
+ self._queue.set_active(queued.queue_id, widget)
68
+
69
+ # 滚动到底部
70
+ self._message_list._scroll_to_end(force=True)
71
+
72
+ # 尝试获取焦点
73
+ return await self._ensure_widget_focused(widget, retry=True)
74
+
75
+ async def _ensure_widget_focused(
76
+ self,
77
+ widget: InlinePermissionWidget,
78
+ retry: bool = False,
79
+ ) -> bool:
80
+ """确保 Widget 获得焦点(支持重试)。"""
81
+ retries = 0
82
+ max_retries = self._max_focus_retries if retry else 1
83
+
84
+ while retries < max_retries:
85
+ # 检查 widget 是否仍然 attached
86
+ if not widget.is_attached:
87
+ logger.warning("Widget detached, cannot focus")
88
+ return False
89
+
90
+ # 尝试获取焦点
91
+ widget.focus()
92
+
93
+ # 等待一帧让焦点状态更新
94
+ await asyncio.sleep(0.01)
95
+
96
+ # 验证是否成功获得焦点
97
+ if widget.has_focus:
98
+ logger.debug(f"Widget {widget.id} gained focus (attempt {retries + 1})")
99
+ return True
100
+
101
+ retries += 1
102
+ if retries < max_retries:
103
+ await asyncio.sleep(0.05) # 短暂延迟后重试
104
+
105
+ logger.warning(f"Widget {widget.id} failed to gain focus after {max_retries} attempts")
106
+ return False
107
+
108
+ def unmount(self, queue_id: str) -> None:
109
+ """卸载 Widget 并清理。"""
110
+ queued = self._queue._queue.get(queue_id)
111
+ if queued is None:
112
+ return
113
+
114
+ if queued.widget is not None:
115
+ if queued.widget.is_attached:
116
+ # 先 dismiss future
117
+ queued.widget.dismiss_externally()
118
+ # 再移除 widget
119
+ queued.widget.remove()
120
+ queued.widget = None
121
+
122
+ self._queue.remove(queue_id)
123
+
124
+ def unmount_all(self) -> None:
125
+ """卸载所有 Widget。"""
126
+ for queue_id in list(self._queue._queue.keys()):
127
+ self.unmount(queue_id)
128
+
129
+ def get_focused_widget(self) -> InlinePermissionWidget | None:
130
+ """获取当前有焦点的权限 Widget。"""
131
+ active = self._queue.get_active()
132
+ if active and active.widget and active.widget.has_focus:
133
+ return active.widget
134
+ return None
135
+
136
+ def cleanup(self) -> None:
137
+ """清理所有资源。"""
138
+ self.unmount_all()
139
+ if self._focus_retry_timer:
140
+ self._focus_retry_timer.stop()
141
+
142
+
143
+ def create_component_manager(
144
+ queue: ConfirmQueue,
145
+ message_list: MessageListWidget,
146
+ ) -> PermissionComponentManager:
147
+ """创建组件管理器(工厂函数)。"""
148
+ return PermissionComponentManager(queue, message_list)
@@ -3,6 +3,7 @@ tui/permissions/consumer.py — SDK 权限队列消费者。
3
3
 
4
4
  职责:
5
5
  后台消费 PermissionRequest 队列,查缓存、委托决策端口、resolve Future。
6
+ 集成 ConfirmQueue 状态管理(对齐 CC)。
6
7
 
7
8
  链路位置:
8
9
  ReplApp.on_mount 启动 → PermissionQueueConsumer → SDK AgentSessionPermissionResolver。
@@ -22,6 +23,7 @@ from langchain_agentx.tool_runtime.resolvers import PermissionRequest
22
23
  from langchain_agentx_cli.tui.permissions.cache import SessionPermissionCache
23
24
  from langchain_agentx_cli.tui.permissions.models import PermissionDecision
24
25
  from langchain_agentx_cli.tui.permissions.presenters import permission_presenter_for_tool
26
+ from langchain_agentx_cli.tui.permissions.queue import ConfirmQueue
25
27
 
26
28
  if TYPE_CHECKING:
27
29
  from langchain_agentx_cli.app import ReplApp
@@ -54,7 +56,10 @@ class NoDialogPermissionDecisionPort:
54
56
 
55
57
 
56
58
  class PermissionQueueConsumer:
57
- """消费 SDK 权限队列,resolve PermissionRequest.future。"""
59
+ """消费 SDK 权限队列,resolve PermissionRequest.future。
60
+
61
+ 对齐 CC: 集成 ConfirmQueue 状态管理。
62
+ """
58
63
 
59
64
  def __init__(
60
65
  self,
@@ -63,11 +68,13 @@ class PermissionQueueConsumer:
63
68
  app: ReplApp,
64
69
  *,
65
70
  decision_port: PermissionDecisionPort | None = None,
71
+ confirm_queue: ConfirmQueue | None = None,
66
72
  ) -> None:
67
73
  self._queue = queue
68
74
  self._cache = cache
69
75
  self._app = app
70
76
  self._decision_port = decision_port or NoDialogPermissionDecisionPort()
77
+ self._confirm_queue = confirm_queue # 对齐 CC confirmQueue
71
78
  self._worker: Any = None
72
79
 
73
80
  def start(self) -> None:
@@ -116,12 +123,29 @@ class PermissionQueueConsumer:
116
123
  return self._cache.check(request.tool_name)
117
124
 
118
125
  async def _handle_request(self, request: PermissionRequest) -> None:
126
+ """处理权限请求(对齐 CC confirmQueue 流程)。"""
127
+ queued = None
119
128
  try:
129
+ # 检查缓存
120
130
  if self._is_cached(request):
121
131
  self._resolve_future(request, True)
122
132
  return
123
133
 
124
- decision = await self._decision_port.resolve(request, self._cache)
134
+ # 添加到 confirm_queue(对齐 CC addConfirm)
135
+ if self._confirm_queue is not None:
136
+ queued = await self._confirm_queue.add(request)
137
+ logger.debug(
138
+ f"Added to confirm_queue: {request.tool_name} (id={queued.queue_id})"
139
+ )
140
+
141
+ # 委托给决策端口
142
+ decision = await self._decision_port.resolve(
143
+ request,
144
+ self._cache,
145
+ queued, # 传递 queued 信息
146
+ )
147
+
148
+ # 处理决策
125
149
  if decision is PermissionDecision.ALLOW_SESSION:
126
150
  presenter = permission_presenter_for_tool(
127
151
  request.tool_name,
@@ -129,11 +153,11 @@ class PermissionQueueConsumer:
129
153
  )
130
154
  self._cache.add(request.tool_name, presenter.extract_pattern())
131
155
  self._resolve_future(request, True)
132
- return
133
- if decision is PermissionDecision.ALLOW_ONCE:
156
+ elif decision is PermissionDecision.ALLOW_ONCE:
134
157
  self._resolve_future(request, True)
135
- return
136
- self._resolve_future(request, False)
158
+ else:
159
+ self._resolve_future(request, False)
160
+
137
161
  except asyncio.CancelledError:
138
162
  self._resolve_future(request, False)
139
163
  raise
@@ -143,6 +167,10 @@ class PermissionQueueConsumer:
143
167
  request.tool_name,
144
168
  )
145
169
  self._resolve_future(request, False)
170
+ finally:
171
+ # 从 confirm_queue 移除(对齐 CC removeConfirm)
172
+ if self._confirm_queue is not None and queued is not None:
173
+ self._confirm_queue.remove(queued.queue_id)
146
174
 
147
175
  @staticmethod
148
176
  def _resolve_future(request: PermissionRequest, allowed: bool) -> None:
@@ -3,6 +3,7 @@ tui/permissions/dialog.py — 内联权限确认决策端口。
3
3
 
4
4
  职责:
5
5
  在消息流中 mount InlinePermissionWidget,收集用户决策。
6
+ 集成 ConfirmQueue 状态管理(对齐 CC)。
6
7
 
7
8
  链路位置:
8
9
  DialogPermissionDecisionPort → InlinePermissionWidget → Consumer resolve Future。
@@ -20,6 +21,7 @@ from langchain_agentx_cli.tui.permissions.cache import SessionPermissionCache
20
21
  from langchain_agentx_cli.tui.permissions.models import PermissionDecision
21
22
  from langchain_agentx_cli.tui.permissions.presenters import permission_presenter_for_tool
22
23
  from langchain_agentx_cli.tui.permissions.presenters.base import PermissionPresentation
24
+ from langchain_agentx_cli.tui.permissions.queue import QueuedPermission
23
25
  from langchain_agentx_cli.widgets.messages import PermissionPendingHidden, PermissionPendingShown
24
26
 
25
27
  if TYPE_CHECKING:
@@ -46,6 +48,7 @@ class DialogPermissionDecisionPort:
46
48
  self,
47
49
  request: PermissionRequest,
48
50
  cache: SessionPermissionCache,
51
+ queued: QueuedPermission | None = None, # 新增:对齐 CC confirmQueue
49
52
  ) -> PermissionDecision:
50
53
  del cache
51
54
  presentation = permission_presenter_for_tool(
@@ -53,9 +56,9 @@ class DialogPermissionDecisionPort:
53
56
  request.ask_prompt,
54
57
  ).present()
55
58
 
56
- self._post_pending(request, presentation)
59
+ self._post_pending(request, presentation, queued)
57
60
  try:
58
- return await self._resolve_inline(request, presentation)
61
+ return await self._resolve_inline(request, presentation, queued)
59
62
  except asyncio.CancelledError:
60
63
  return PermissionDecision.DENY
61
64
  except Exception:
@@ -68,6 +71,7 @@ class DialogPermissionDecisionPort:
68
71
  self,
69
72
  request: PermissionRequest,
70
73
  presentation: PermissionPresentation,
74
+ queued: QueuedPermission | None = None,
71
75
  ) -> PermissionDecision:
72
76
  """Mount inline widget,与 SDK Future 竞态。"""
73
77
  from langchain_agentx_cli.widgets.permission_inline import InlinePermissionWidget
@@ -120,6 +124,7 @@ class DialogPermissionDecisionPort:
120
124
  self,
121
125
  request: PermissionRequest,
122
126
  presentation: PermissionPresentation,
127
+ queued: QueuedPermission | None = None,
123
128
  ) -> None:
124
129
  pass # Pending shown is posted with widget in _resolve_inline
125
130