langchain-agentx-cli 0.2.0__tar.gz → 0.2.2__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 (138) hide show
  1. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/PKG-INFO +2 -2
  2. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/__init__.py +1 -1
  3. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/app.py +30 -0
  4. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/bridge/session_bridge.py +36 -6
  5. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/cli.py +15 -3
  6. langchain_agentx_cli-0.2.2/langchain_agentx_cli/observation/__init__.py +44 -0
  7. langchain_agentx_cli-0.2.2/langchain_agentx_cli/observation/config.py +35 -0
  8. langchain_agentx_cli-0.2.2/langchain_agentx_cli/observation/observer.py +173 -0
  9. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/screens/repl.py +0 -3
  10. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/clipboard.py +82 -24
  11. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/consumer.py +4 -2
  12. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/message_list.py +116 -43
  13. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/messages/assistant_message.py +16 -0
  14. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/PKG-INFO +2 -2
  15. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/SOURCES.txt +4 -0
  16. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/requires.txt +1 -1
  17. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/pyproject.toml +2 -2
  18. langchain_agentx_cli-0.2.2/tests/test_llm_config_chain.py +182 -0
  19. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/LICENSE +0 -0
  20. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/README.md +0 -0
  21. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/__main__.py +0 -0
  22. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/bootstrap.py +0 -0
  23. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/bridge/__init__.py +0 -0
  24. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/__init__.py +0 -0
  25. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/__init__.py +0 -0
  26. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/clear.py +0 -0
  27. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/compact.py +0 -0
  28. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/help.py +0 -0
  29. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/model.py +0 -0
  30. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/quit.py +0 -0
  31. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/builtin/theme.py +0 -0
  32. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/parser.py +0 -0
  33. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/providers/__init__.py +0 -0
  34. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/providers/sdk_commands.py +0 -0
  35. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/commands/registry.py +0 -0
  36. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/completion/__init__.py +0 -0
  37. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/completion/base.py +0 -0
  38. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/completion/command_source.py +0 -0
  39. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/completion/history_source.py +0 -0
  40. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/config.py +0 -0
  41. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/history/__init__.py +0 -0
  42. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/history/store.py +0 -0
  43. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/keybindings/__init__.py +0 -0
  44. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/keybindings/bindings.py +0 -0
  45. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/llm_config.py +0 -0
  46. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/permissions/__init__.py +0 -0
  47. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/permissions/factory.py +0 -0
  48. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/permissions/policy.py +0 -0
  49. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/prompts/__init__.py +0 -0
  50. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/prompts/assembler.py +0 -0
  51. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/prompts/sections.py +0 -0
  52. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/prompts/system_prompt.py +0 -0
  53. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/screens/__init__.py +0 -0
  54. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/session_factory.py +0 -0
  55. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/session_options.py +0 -0
  56. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/state/__init__.py +0 -0
  57. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/state/task_store.py +0 -0
  58. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/__init__.py +0 -0
  59. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/colors.py +0 -0
  60. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/detection.py +0 -0
  61. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/manager.py +0 -0
  62. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/settings.py +0 -0
  63. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/system_theme.py +0 -0
  64. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/themes.py +0 -0
  65. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/theme/watcher.py +0 -0
  66. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tools/__init__.py +0 -0
  67. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tools/registry.py +0 -0
  68. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/__init__.py +0 -0
  69. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/message_selection.py +0 -0
  70. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/__init__.py +0 -0
  71. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/cache.py +0 -0
  72. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/component_manager.py +0 -0
  73. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/config.py +0 -0
  74. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/dialog.py +0 -0
  75. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/inject.py +0 -0
  76. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/models.py +0 -0
  77. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/presenters/__init__.py +0 -0
  78. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/presenters/base.py +0 -0
  79. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/presenters/bash.py +0 -0
  80. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/presenters/fallback.py +0 -0
  81. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/presenters/file.py +0 -0
  82. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/permissions/queue.py +0 -0
  83. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/tui/safe_screen.py +0 -0
  84. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/welcome.py +0 -0
  85. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/__init__.py +0 -0
  86. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/compact_progress.py +0 -0
  87. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/completion_overlay.py +0 -0
  88. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/input_area.py +0 -0
  89. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/message_events.py +0 -0
  90. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/messages/__init__.py +0 -0
  91. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/messages/error_message.py +0 -0
  92. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/messages/rendering.py +0 -0
  93. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/messages/thinking_message.py +0 -0
  94. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/pending_permission.py +0 -0
  95. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/permission_inline.py +0 -0
  96. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/permission_keybindings.py +0 -0
  97. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/spinner.py +0 -0
  98. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/task_panel.py +0 -0
  99. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/__init__.py +0 -0
  100. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/agent/__init__.py +0 -0
  101. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/agent/widget.py +0 -0
  102. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/ask_user_question/__init__.py +0 -0
  103. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/ask_user_question/widget.py +0 -0
  104. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/base.py +0 -0
  105. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/bash/__init__.py +0 -0
  106. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/bash/widget.py +0 -0
  107. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/batch_edit/__init__.py +0 -0
  108. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/batch_edit/widget.py +0 -0
  109. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/edit/__init__.py +0 -0
  110. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/edit/widget.py +0 -0
  111. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/glob/__init__.py +0 -0
  112. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/glob/widget.py +0 -0
  113. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/grep/__init__.py +0 -0
  114. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/grep/widget.py +0 -0
  115. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/helpers.py +0 -0
  116. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/read/__init__.py +0 -0
  117. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/read/widget.py +0 -0
  118. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/skill/__init__.py +0 -0
  119. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/skill/widget.py +0 -0
  120. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/user_message/__init__.py +0 -0
  121. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/user_message/widget.py +0 -0
  122. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/webfetch/__init__.py +0 -0
  123. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/webfetch/widget.py +0 -0
  124. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/websearch/__init__.py +0 -0
  125. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/websearch/widget.py +0 -0
  126. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/write/__init__.py +0 -0
  127. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli/widgets/tools/write/widget.py +0 -0
  128. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/dependency_links.txt +0 -0
  129. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/entry_points.txt +0 -0
  130. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/not-zip-safe +0 -0
  131. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/langchain_agentx_cli.egg-info/top_level.txt +0 -0
  132. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/setup.cfg +0 -0
  133. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/tests/test_app_interrupt.py +0 -0
  134. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/tests/test_repl_commands.py +0 -0
  135. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/tests/test_repl_submit_flow.py +0 -0
  136. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/tests/test_repl_ui.py +0 -0
  137. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/tests/test_smoke.py +0 -0
  138. {langchain_agentx_cli-0.2.0 → langchain_agentx_cli-0.2.2}/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.2.0
3
+ Version: 0.2.2
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.4.0,>=0.3.4
25
+ Requires-Dist: langchain-agentx-python<0.4.0,>=0.3.7
26
26
  Requires-Dist: click>=8.1
27
27
  Requires-Dist: textual>=0.79.0
28
28
  Provides-Extra: dev
@@ -1,3 +1,3 @@
1
1
  """langchain_agentx_cli:依托 langchain_agentx_python SDK 的终端 CLI/TUI(CC Ink 迁移)。"""
2
2
 
3
- __version__ = "0.2.0"
3
+ __version__ = "0.2.2"
@@ -10,6 +10,7 @@ app.py — Textual ReplApp 容器。
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ from pathlib import Path
13
14
  from typing import TYPE_CHECKING
14
15
 
15
16
  from textual import on
@@ -55,12 +56,15 @@ class ReplApp(App[None]):
55
56
  show=False,
56
57
  priority=True,
57
58
  ),
59
+ # 对齐 CC: Ctrl+O 切换 Transcript 模式
60
+ Binding("ctrl+o", "toggle_transcript", "Transcript", show=False, priority=True),
58
61
  ]
59
62
 
60
63
  def __init__(
61
64
  self,
62
65
  launch_config: ReplLaunchConfig,
63
66
  session: AgentSession | None = None,
67
+ debug: bool = False,
64
68
  ) -> None:
65
69
  super().__init__()
66
70
  self._launch_config = launch_config
@@ -74,6 +78,7 @@ class ReplApp(App[None]):
74
78
  self._screen_messenger = ReplScreenMessenger(self)
75
79
  # Phase 3: 加载用户偏好(供 MessageListWidget 和工具 Widget 使用)
76
80
  self.preferences: AppUserPreferences = AppPreferencesStore().load()
81
+ self._debug = debug
77
82
 
78
83
  @property
79
84
  def screen_messenger(self) -> ReplScreenMessenger:
@@ -92,6 +97,13 @@ class ReplApp(App[None]):
92
97
  return self._bridge
93
98
 
94
99
  def on_mount(self) -> None:
100
+ # DEBUG: 启用观测日志
101
+ if self._debug:
102
+ from langchain_agentx_cli.observation import observer
103
+
104
+ observer.enable(self.workspace_root)
105
+ observer.info("[APP] ReplApp mounted, workspace=%s", self.workspace_root)
106
+
95
107
  prefs = AppPreferencesStore().load()
96
108
  initial_setting = self._launch_config.theme or prefs.theme
97
109
  self.theme_manager = ThemeManager(self)
@@ -149,3 +161,21 @@ class ReplApp(App[None]):
149
161
  if self._bridge is not None:
150
162
  self._bridge.cancel_stream()
151
163
  self.exit(0)
164
+
165
+ def action_toggle_transcript(self) -> None:
166
+ """切换 Transcript 模式(对齐 CC Ctrl+O)。"""
167
+ screen = self.screen
168
+ if isinstance(screen, ReplScreen):
169
+ message_list = screen._message_list()
170
+ if message_list is not None:
171
+ from langchain_agentx_cli.observation import info
172
+ old_mode = message_list.display_mode
173
+ message_list.toggle_transcript_mode()
174
+ new_mode = message_list.display_mode
175
+ info("[APP] transcript toggled: %s → %s", old_mode, new_mode)
176
+
177
+ # 显示通知
178
+ if new_mode.value == "transcript":
179
+ self.notify("Transcript 模式已启用(显示所有内容)", severity="information", timeout=3)
180
+ else:
181
+ self.notify("已返回 Normal 模式", severity="information", timeout=2)
@@ -18,12 +18,14 @@ from __future__ import annotations
18
18
 
19
19
  import asyncio
20
20
  from collections.abc import Callable
21
+ from dataclasses import dataclass, field
21
22
  from typing import TYPE_CHECKING, Any
22
23
 
23
24
  from langchain_agentx.tool_runtime.resolvers import (
24
25
  AgentSessionPermissionResolver,
25
26
  PermissionRequest,
26
27
  )
28
+ from langchain_agentx_cli.observation import debug, info
27
29
  from langchain_agentx_cli.tui.permissions import (
28
30
  PermissionQueueConsumer,
29
31
  SessionPermissionCache,
@@ -70,6 +72,16 @@ MVP_ADAPTER_CONFIG: dict[str, bool] = {
70
72
  _LLM_FAILED_PREFIX = "[LLM request failed]"
71
73
 
72
74
 
75
+ @dataclass
76
+ class _TurnSegmentState:
77
+ """一个 compaction 段内的流式状态;在 turn 开始和 COMPACT_END 时整体重置。"""
78
+
79
+ streamed_text: bool = False
80
+
81
+ def reset(self) -> None:
82
+ self.streamed_text = False
83
+
84
+
73
85
  class SessionBridge:
74
86
  """TUI 与 SDK 之间的桥接层(消费 LangchainAgentEvent)。"""
75
87
 
@@ -79,7 +91,7 @@ class SessionBridge:
79
91
  self._session = session
80
92
  self._adapter = LangGraphToLangchainAgentEventAdapter(config=dict(MVP_ADAPTER_CONFIG))
81
93
  self._current_worker: Any = None
82
- self._streamed_text = False
94
+ self._seg = _TurnSegmentState()
83
95
  # 对齐 CC: 集中式权限队列状态管理
84
96
  self._permission_queue: asyncio.Queue[PermissionRequest] = asyncio.Queue()
85
97
  self._permission_cache = SessionPermissionCache()
@@ -111,12 +123,15 @@ class SessionBridge:
111
123
 
112
124
  async def _stream_turn(self, user_input: str) -> None:
113
125
  self._adapter._reset_state()
114
- self._streamed_text = False
126
+ self._seg.reset()
115
127
  raw_events = self._session.stream_loop_events(user_input)
116
128
  try:
117
129
  async for event in self._adapter.adapt(raw_events):
118
130
  self._dispatch_event(event)
131
+ except (KeyboardInterrupt, SystemExit):
132
+ raise
119
133
  except Exception as exc:
134
+ debug("stream_turn error", exc_info=True)
120
135
  self._post_ui(ErrorMessageOccurred(error=str(exc)))
121
136
  finally:
122
137
  self._post_ui(TurnInProgress(in_progress=False))
@@ -124,6 +139,12 @@ class SessionBridge:
124
139
 
125
140
  def _dispatch_event(self, event: LangchainAgentEvent) -> None:
126
141
  data = event.data or {}
142
+ # DEBUG: 追踪所有事件
143
+ info(
144
+ "[BRIDGE] event=%s data_keys=%s",
145
+ event.event_type,
146
+ list(data.keys()) if data else [],
147
+ )
127
148
  match event.event_type:
128
149
  case LangchainAgentEventType.TEXT_START:
129
150
  self._post_ui(
@@ -131,8 +152,9 @@ class SessionBridge:
131
152
  )
132
153
  case LangchainAgentEventType.TEXT_DELTA:
133
154
  delta = str(data.get("text", ""))
155
+ info("[BRIDGE] TEXT_DELTA len=%d first50=%r", len(delta), delta[:50])
134
156
  if delta:
135
- self._streamed_text = True
157
+ self._seg.streamed_text = True
136
158
  self._post_ui(
137
159
  AssistantMessageChunk(
138
160
  delta=delta,
@@ -140,6 +162,7 @@ class SessionBridge:
140
162
  )
141
163
  )
142
164
  case LangchainAgentEventType.TEXT_END:
165
+ info("[BRIDGE] TEXT_END — finalizing assistant")
143
166
  self._post_ui(
144
167
  AssistantMessageChunk(delta="", is_complete=True)
145
168
  )
@@ -191,9 +214,16 @@ class SessionBridge:
191
214
  self._emit_finish_answer(str(data.get("answer", "")))
192
215
  self._post_ui(TurnInProgress(in_progress=False))
193
216
  case LangchainAgentEventType.COMPACT_END:
194
- self._post_ui(ContextCompactedMessage())
217
+ self._seg.reset()
218
+ compact_type = data.get("compact_type", "autocompact") # 向后兼容
219
+ tokens_freed = data.get("tokens_freed") or 0
220
+ info("[BRIDGE] COMPACT_END type=%s tokens=%s", compact_type, tokens_freed)
221
+ # 只显示 autocompact(LLM 摘要),忽略 microcompact(工具清理)
222
+ if compact_type == "autocompact":
223
+ self._post_ui(ContextCompactedMessage())
195
224
  case LangchainAgentEventType.COMPACT_START:
196
- self._post_ui(ContextCompactingMessage())
225
+ info("[BRIDGE] COMPACT_START — compacting...")
226
+ pass
197
227
  case _:
198
228
  pass
199
229
 
@@ -205,7 +235,7 @@ class SessionBridge:
205
235
  if text.startswith(_LLM_FAILED_PREFIX):
206
236
  self._post_ui(ErrorMessageOccurred(error=text))
207
237
  return
208
- if not self._streamed_text:
238
+ if not self._seg.streamed_text:
209
239
  self._post_ui(
210
240
  AssistantMessageChunk(delta=text, is_complete=True)
211
241
  )
@@ -50,6 +50,7 @@ class CliEntry:
50
50
  show_config: bool,
51
51
  enable_git_snapshot: bool | None,
52
52
  skip_permissions: bool = False,
53
+ debug: bool = False,
53
54
  ) -> int:
54
55
  # Mode 到 agent_home 的映射(优先级高于 --agent-home)
55
56
  if mode:
@@ -88,7 +89,7 @@ class CliEntry:
88
89
  return 2
89
90
 
90
91
  try:
91
- return self._run_tui(launch_config, session)
92
+ return self._run_tui(launch_config, session, debug=debug)
92
93
  finally:
93
94
  asyncio.run(close_agent_session(session))
94
95
 
@@ -138,8 +139,10 @@ class CliEntry:
138
139
  }
139
140
  return mode_map.get(mode.lower(), ".langchain_agentx")
140
141
 
141
- def _run_tui(self, launch_config: ReplLaunchConfig, session) -> int: # noqa: ANN001
142
- app = ReplApp(launch_config=launch_config, session=session)
142
+ def _run_tui(
143
+ self, launch_config: ReplLaunchConfig, session, debug: bool = False # noqa: ANN001
144
+ ) -> int:
145
+ app = ReplApp(launch_config=launch_config, session=session, debug=debug)
143
146
  app.run()
144
147
  return 0
145
148
 
@@ -203,6 +206,13 @@ _CONFIG_PATH = user_config_path()
203
206
  default=False,
204
207
  help="跳过工具权限确认(对齐 CC --dangerously-skip-permissions)",
205
208
  )
209
+ @click.option(
210
+ "--debug",
211
+ "-d",
212
+ is_flag=True,
213
+ default=False,
214
+ help="启用观察日志(写入 workspace/log/agentx-{pid}.log)",
215
+ )
206
216
  def main(
207
217
  workspace_root: Path | None,
208
218
  mode: str | None,
@@ -212,6 +222,7 @@ def main(
212
222
  show_config: bool,
213
223
  enable_git_snapshot: bool | None,
214
224
  skip_permissions: bool,
225
+ debug: bool,
215
226
  ) -> None:
216
227
  """langchain-agentx 终端 REPL(MVP)。"""
217
228
  code = CliEntry().run(
@@ -223,5 +234,6 @@ def main(
223
234
  show_config=show_config,
224
235
  enable_git_snapshot=enable_git_snapshot,
225
236
  skip_permissions=skip_permissions,
237
+ debug=debug,
226
238
  )
227
239
  raise SystemExit(code)
@@ -0,0 +1,44 @@
1
+ """
2
+ observation — 统一日志观测模块。
3
+
4
+ 提供启用/禁用控制的日志接口,默认禁用(零开销)。
5
+
6
+ 使用方式:
7
+ from langchain_agentx_cli.observation import observer, info
8
+
9
+ # 模块直接调用
10
+ info("[MODULE] some_event value=%s", value)
11
+
12
+ # 或使用 observer 实例
13
+ observer.info("[MODULE] some_event value=%s", value)
14
+ """
15
+
16
+ from langchain_agentx_cli.observation.observer import (
17
+ Observer,
18
+ critical,
19
+ debug,
20
+ disable,
21
+ enable,
22
+ error,
23
+ get_observer,
24
+ info,
25
+ is_enabled,
26
+ warning,
27
+ )
28
+
29
+ # 全局 observer 实例(常用别名)
30
+ observer = get_observer()
31
+
32
+ __all__ = [
33
+ "Observer",
34
+ "observer",
35
+ "get_observer",
36
+ "enable",
37
+ "disable",
38
+ "is_enabled",
39
+ "debug",
40
+ "info",
41
+ "warning",
42
+ "error",
43
+ "critical",
44
+ ]
@@ -0,0 +1,35 @@
1
+ """
2
+ observation/config.py — 日志配置常量。
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import os
8
+ from pathlib import Path
9
+
10
+ # 日志文件名模板
11
+ LOG_FILENAME_TEMPLATE = "agentx-{pid}.log"
12
+
13
+ # 默认日志格式
14
+ LOG_FORMAT = "%(asctime)s %(name)s %(levelname)s %(message)s"
15
+ LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
16
+
17
+ # 默认日志级别
18
+ DEFAULT_LOG_LEVEL = "INFO"
19
+
20
+ # 日志目录名(相对 workspace root)
21
+ LOG_DIR_NAME = "log"
22
+
23
+
24
+ def get_log_dir(workspace_root: Path | None) -> Path:
25
+ """获取日志目录路径。"""
26
+ if workspace_root:
27
+ return workspace_root / LOG_DIR_NAME
28
+ # 回退到临时目录(无 workspace 时)
29
+ return Path(os.getenv("TMPDIR", "/tmp")) / "agentx-logs"
30
+
31
+
32
+ def get_log_path(workspace_root: Path | None) -> Path:
33
+ """获取日志文件完整路径。"""
34
+ log_dir = get_log_dir(workspace_root)
35
+ return log_dir / LOG_FILENAME_TEMPLATE.format(pid=os.getpid())
@@ -0,0 +1,173 @@
1
+ """
2
+ observation/observer.py — 观测器(单例)。
3
+
4
+ 提供统一的日志接口,支持启用/禁用切换。禁用时所有操作为 no-op。
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from pathlib import Path
11
+ from typing import Final
12
+
13
+ from langchain_agentx_cli.observation.config import (
14
+ DEFAULT_LOG_LEVEL,
15
+ LOG_DATE_FORMAT,
16
+ LOG_FORMAT,
17
+ get_log_path,
18
+ )
19
+
20
+
21
+ class _NullHandler(logging.Handler):
22
+ """零开销的空 Handler,用于禁用状态。"""
23
+
24
+ def emit(self, record: logging.LogRecord) -> None:
25
+ pass
26
+
27
+
28
+ class Observer:
29
+ """
30
+ 观测器:统一的日志入口。
31
+
32
+ 单例模式,通过 get_instance() 获取实例。
33
+
34
+ 禁用状态:所有 log 方法为 no-op(零开销)。
35
+ 启用状态:日志写入 workspace/log/agentx-{pid}.log
36
+ """
37
+
38
+ _instance: Final[Observer] = None # type: ignore[assignment]
39
+
40
+ def __new__(cls) -> "Observer":
41
+ if cls._instance is None:
42
+ cls._instance = super().__new__(cls)
43
+ cls._instance._initialized = False
44
+ return cls._instance
45
+
46
+ def __init__(self) -> None:
47
+ if self._initialized:
48
+ return
49
+ self._initialized = True
50
+
51
+ self._enabled: bool = False
52
+ self._logger: logging.Logger | None = None
53
+ self._null_handler = _NullHandler()
54
+
55
+ def enable(self, workspace_root: Path, level: str = DEFAULT_LOG_LEVEL) -> None:
56
+ """
57
+ 启用观测日志。
58
+
59
+ Args:
60
+ workspace_root: 工作区根目录,日志写入 workspace_root/log/
61
+ level: 日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)
62
+ """
63
+ if self._enabled:
64
+ return
65
+
66
+ log_path = get_log_path(workspace_root)
67
+ log_path.parent.mkdir(parents=True, exist_ok=True)
68
+
69
+ self._logger = logging.getLogger("agentx")
70
+ self._logger.setLevel(getattr(logging, level.upper(), logging.INFO))
71
+ self._logger.handlers.clear()
72
+
73
+ handler = logging.FileHandler(log_path, encoding="utf-8")
74
+ handler.setFormatter(logging.Formatter(LOG_FORMAT, LOG_DATE_FORMAT))
75
+ self._logger.addHandler(handler)
76
+
77
+ self._enabled = True
78
+ self.info("[OBSERVER] enabled log_file=%s", log_path)
79
+
80
+ def disable(self) -> None:
81
+ """关闭观测日志,后续调用变为 no-op。"""
82
+ if not self._enabled:
83
+ return
84
+
85
+ self.info("[OBSERVER] disabling")
86
+ self._enabled = False
87
+
88
+ if self._logger:
89
+ for handler in self._logger.handlers[:]:
90
+ handler.close()
91
+ self._logger.removeHandler(handler)
92
+ self._logger = None
93
+
94
+ @property
95
+ def is_enabled(self) -> bool:
96
+ """是否已启用。"""
97
+ return self._enabled
98
+
99
+ def _log(self, level: int, msg: str, *args, **kwargs) -> None:
100
+ """内部日志方法。"""
101
+ if not self._enabled or self._logger is None:
102
+ return
103
+ self._logger.log(level, msg, *args, **kwargs)
104
+
105
+ def debug(self, msg: str, *args, **kwargs) -> None:
106
+ """记录 DEBUG 级别日志。"""
107
+ self._log(logging.DEBUG, msg, *args, **kwargs)
108
+
109
+ def info(self, msg: str, *args, **kwargs) -> None:
110
+ """记录 INFO 级别日志。"""
111
+ self._log(logging.INFO, msg, *args, **kwargs)
112
+
113
+ def warning(self, msg: str, *args, **kwargs) -> None:
114
+ """记录 WARNING 级别日志。"""
115
+ self._log(logging.WARNING, msg, *args, **kwargs)
116
+
117
+ def error(self, msg: str, *args, **kwargs) -> None:
118
+ """记录 ERROR 级别日志。"""
119
+ self._log(logging.ERROR, msg, *args, **kwargs)
120
+
121
+ def critical(self, msg: str, *args, **kwargs) -> None:
122
+ """记录 CRITICAL 级别日志。"""
123
+ self._log(logging.CRITICAL, msg, *args, **kwargs)
124
+
125
+
126
+ # 全局单例实例
127
+ _observer: Observer = Observer()
128
+
129
+
130
+ def get_observer() -> Observer:
131
+ """获取观测器单例实例。"""
132
+ return _observer
133
+
134
+
135
+ # 模块级便捷函数,直接调用单例方法
136
+ def enable(workspace_root: Path, level: str = DEFAULT_LOG_LEVEL) -> None:
137
+ """启用观测日志。"""
138
+ _observer.enable(workspace_root, level)
139
+
140
+
141
+ def disable() -> None:
142
+ """关闭观测日志。"""
143
+ _observer.disable()
144
+
145
+
146
+ def is_enabled() -> bool:
147
+ """是否已启用。"""
148
+ return _observer.is_enabled
149
+
150
+
151
+ def debug(msg: str, *args, **kwargs) -> None:
152
+ """记录 DEBUG 级别日志。"""
153
+ _observer.debug(msg, *args, **kwargs)
154
+
155
+
156
+ def info(msg: str, *args, **kwargs) -> None:
157
+ """记录 INFO 级别日志。"""
158
+ _observer.info(msg, *args, **kwargs)
159
+
160
+
161
+ def warning(msg: str, *args, **kwargs) -> None:
162
+ """记录 WARNING 级别日志。"""
163
+ _observer.warning(msg, *args, **kwargs)
164
+
165
+
166
+ def error(msg: str, *args, **kwargs) -> None:
167
+ """记录 ERROR 级别日志。"""
168
+ _observer.error(msg, *args, **kwargs)
169
+
170
+
171
+ def critical(msg: str, *args, **kwargs) -> None:
172
+ """记录 CRITICAL 级别日志。"""
173
+ _observer.critical(msg, *args, **kwargs)
@@ -88,9 +88,6 @@ class ReplScreen(Screen):
88
88
  self._message_list().show_welcome(launch_config)
89
89
  # Phase 3: 传递用户偏好配置给 MessageListWidget
90
90
  if hasattr(app, "preferences"):
91
- self._message_list().set_show_suppressed_text(
92
- app.preferences.show_suppressed_text
93
- )
94
91
  # 对齐 CC:传递 verbose 模式(控制 thinking 内容显示)
95
92
  self._message_list().set_verbose(app.preferences.verbose)
96
93
  input_area = self.query_one("#input-area", InputAreaWidget)