tunacode-cli 0.0.36__tar.gz → 0.0.38__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.

Potentially problematic release.


This version of tunacode-cli might be problematic. Click here for more details.

Files changed (210) hide show
  1. {tunacode_cli-0.0.36/src/tunacode_cli.egg-info → tunacode_cli-0.0.38}/PKG-INFO +17 -17
  2. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/README.md +16 -14
  3. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/pyproject.toml +24 -8
  4. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/setup.py +1 -1
  5. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/implementations/__init__.py +2 -1
  6. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/implementations/system.py +39 -0
  7. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/registry.py +8 -1
  8. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/repl.py +96 -30
  9. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/constants.py +1 -1
  10. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/agents/main.py +41 -1
  11. tunacode_cli-0.0.38/src/tunacode/core/agents/utils.py +304 -0
  12. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/config_setup.py +0 -1
  13. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/state.py +4 -0
  14. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/console.py +4 -0
  15. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/panels.py +74 -0
  16. tunacode_cli-0.0.38/src/tunacode/ui/utils.py +3 -0
  17. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38/src/tunacode_cli.egg-info}/PKG-INFO +17 -17
  18. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode_cli.egg-info/SOURCES.txt +4 -0
  19. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode_cli.egg-info/requires.txt +1 -3
  20. tunacode_cli-0.0.38/tests/characterization/agent/__init__.py +1 -0
  21. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/agent/conftest.py +8 -5
  22. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/agent/test_agent_creation.py +84 -66
  23. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/agent/test_json_tool_parsing.py +57 -59
  24. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/agent/test_process_node.py +115 -79
  25. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/agent/test_process_request.py +112 -104
  26. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/agent/test_tool_message_patching.py +59 -54
  27. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/background/test_background_edge_cases.py +18 -2
  28. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/background/test_cleanup.py +11 -2
  29. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/background/test_task_cancellation.py +9 -3
  30. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/background/test_task_creation.py +7 -2
  31. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/background/test_task_execution.py +7 -2
  32. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/code_index/test_cache_management.py +8 -2
  33. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/code_index/test_file_scanning.py +9 -2
  34. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/code_index/test_index_building.py +15 -4
  35. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/code_index/test_search_operations.py +11 -1
  36. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/code_index/test_symbol_extraction.py +11 -5
  37. tunacode_cli-0.0.38/tests/characterization/commands/__init__.py +1 -0
  38. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/commands/test_init_command.py +63 -67
  39. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/conftest.py +24 -4
  40. tunacode_cli-0.0.38/tests/characterization/context/__init__.py +1 -0
  41. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/context/test_context_acceptance.py +14 -16
  42. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/context/test_context_integration.py +15 -16
  43. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/context/test_context_loading.py +27 -28
  44. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/context/test_tunacode_logging.py +15 -16
  45. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/repl/test_command_parsing.py +27 -17
  46. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/repl/test_input_handling.py +24 -17
  47. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/repl/test_keyboard_interrupts.py +17 -12
  48. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/repl/test_multiline_input.py +17 -12
  49. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/repl/test_repl_initialization.py +14 -10
  50. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/repl/test_session_flow.py +34 -22
  51. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/services/test_error_recovery.py +13 -4
  52. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/services/test_llm_routing.py +13 -4
  53. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/services/test_mcp_integration.py +13 -4
  54. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/services/test_service_lifecycle.py +13 -4
  55. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/state/test_agent_tracking.py +5 -2
  56. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/state/test_message_history.py +6 -2
  57. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/state/test_permissions.py +5 -3
  58. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/state/test_session_management.py +5 -2
  59. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/state/test_state_initialization.py +7 -3
  60. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/state/test_user_config.py +5 -2
  61. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/test_characterization_commands.py +75 -68
  62. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/ui/test_async_ui.py +7 -2
  63. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/ui/test_console_output.py +7 -5
  64. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/ui/test_diff_display.py +4 -3
  65. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/ui/test_prompt_rendering.py +7 -2
  66. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/ui/test_tool_confirmations.py +6 -4
  67. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/utils/test_file_operations.py +11 -7
  68. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/utils/test_git_commands.py +32 -6
  69. tunacode_cli-0.0.38/tests/characterization/utils/test_token_counting.py +39 -0
  70. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/characterization/utils/test_utils_edge_cases.py +18 -7
  71. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/conftest.py +109 -52
  72. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/crud/test_core_file_operations.py +87 -71
  73. tunacode_cli-0.0.38/tests/fixtures/__init__.py +1 -0
  74. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/fixtures/file_operations.py +97 -78
  75. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/integration/test_error_recovery_flow.py +4 -2
  76. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/integration/test_full_session_flow.py +9 -9
  77. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/integration/test_mcp_tool_flow.py +13 -16
  78. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/integration/test_multi_tool_operations.py +4 -5
  79. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/integration/test_performance_scenarios.py +8 -4
  80. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_actual_parallelism.py +72 -65
  81. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_agent_initialization.py +53 -48
  82. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_background_manager.py +5 -4
  83. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_agent_main.py +15 -10
  84. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_bash.py +49 -49
  85. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_commands_system.py +32 -14
  86. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_glob.py +79 -75
  87. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_grep.py +83 -68
  88. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_grep_performance.py +75 -66
  89. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_iteration_limits.py +25 -29
  90. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_list_dir.py +62 -56
  91. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_read_file.py +48 -45
  92. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_repl_utils.py +15 -10
  93. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_run_command.py +50 -46
  94. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_setup_system.py +18 -11
  95. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_tool_ui_behavior.py +37 -29
  96. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_update_file.py +64 -101
  97. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_utilities.py +26 -20
  98. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_characterization_write_file.py +50 -46
  99. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_cli_command_flow.py +102 -119
  100. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_cli_file_operations_integration.py +73 -75
  101. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_config_directory_creation.py +15 -15
  102. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_config_setup_async.py +44 -33
  103. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_enhanced_visual_feedback.py +20 -15
  104. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_fallback_responses.py +13 -5
  105. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_fast_glob_search.py +55 -43
  106. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_file_operations_edge_cases.py +94 -86
  107. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_file_operations_stress.py +81 -77
  108. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_file_reference_context_tracking.py +27 -28
  109. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_file_reference_expansion.py +34 -32
  110. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_grep_fast_glob.py +63 -68
  111. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_grep_legacy_compat.py +6 -7
  112. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_grep_timeout.py +71 -69
  113. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_json_tool_parsing.py +57 -54
  114. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_list_dir.py +27 -26
  115. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_parallel_execution_demo.py +33 -31
  116. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_parallel_execution_freeze_fix.py +28 -27
  117. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_parallel_execution_integration.py +48 -40
  118. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_parallel_read_only_tools.py +90 -64
  119. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_parallel_tool_execution.py +31 -29
  120. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_read_only_confirmation.py +14 -14
  121. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_security.py +49 -45
  122. tunacode_cli-0.0.38/tests/test_streaming_panel_tool_confirmation.py +176 -0
  123. tunacode_cli-0.0.38/tests/test_streaming_spinner_conflict.py +129 -0
  124. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_tool_categorization.py +23 -24
  125. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_tool_combinations.py +143 -121
  126. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_tool_handler_ui_messages.py +43 -29
  127. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_update_command.py +6 -5
  128. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/tests/test_visual_parallel_feedback.py +50 -38
  129. tunacode_cli-0.0.36/tests/characterization/agent/__init__.py +0 -1
  130. tunacode_cli-0.0.36/tests/characterization/commands/__init__.py +0 -1
  131. tunacode_cli-0.0.36/tests/characterization/context/__init__.py +0 -1
  132. tunacode_cli-0.0.36/tests/characterization/utils/test_token_counting.py +0 -29
  133. tunacode_cli-0.0.36/tests/fixtures/__init__.py +0 -1
  134. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/CLAUDE.md +0 -0
  135. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/LICENSE +0 -0
  136. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/MANIFEST.in +0 -0
  137. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/TUNACODE.md +0 -0
  138. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/setup.cfg +0 -0
  139. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/__init__.py +0 -0
  140. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/__init__.py +0 -0
  141. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/__init__.py +0 -0
  142. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/base.py +0 -0
  143. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/implementations/conversation.py +0 -0
  144. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/implementations/debug.py +0 -0
  145. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/implementations/development.py +0 -0
  146. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/commands/implementations/model.py +0 -0
  147. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/main.py +0 -0
  148. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/textual_app.py +0 -0
  149. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/cli/textual_bridge.py +0 -0
  150. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/configuration/__init__.py +0 -0
  151. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/configuration/defaults.py +0 -0
  152. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/configuration/models.py +0 -0
  153. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/configuration/settings.py +0 -0
  154. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/context.py +0 -0
  155. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/__init__.py +0 -0
  156. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/agents/__init__.py +0 -0
  157. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/background/__init__.py +0 -0
  158. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/background/manager.py +0 -0
  159. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/code_index.py +0 -0
  160. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/llm/__init__.py +0 -0
  161. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/__init__.py +0 -0
  162. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/agent_setup.py +0 -0
  163. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/base.py +0 -0
  164. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/coordinator.py +0 -0
  165. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/environment_setup.py +0 -0
  166. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/setup/git_safety_setup.py +0 -0
  167. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/core/tool_handler.py +0 -0
  168. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/exceptions.py +0 -0
  169. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/prompts/system.md +0 -0
  170. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/py.typed +0 -0
  171. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/services/__init__.py +0 -0
  172. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/services/mcp.py +0 -0
  173. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/setup.py +0 -0
  174. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/__init__.py +0 -0
  175. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/base.py +0 -0
  176. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/bash.py +0 -0
  177. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/glob.py +0 -0
  178. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/grep.py +0 -0
  179. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/list_dir.py +0 -0
  180. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/read_file.py +0 -0
  181. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/read_file_async_poc.py +0 -0
  182. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/run_command.py +0 -0
  183. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/update_file.py +0 -0
  184. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/tools/write_file.py +0 -0
  185. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/types.py +0 -0
  186. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/__init__.py +0 -0
  187. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/completers.py +0 -0
  188. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/constants.py +0 -0
  189. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/decorators.py +0 -0
  190. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/input.py +0 -0
  191. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/keybindings.py +0 -0
  192. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/lexers.py +0 -0
  193. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/output.py +0 -0
  194. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/prompt_manager.py +0 -0
  195. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/tool_ui.py +0 -0
  196. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/ui/validators.py +0 -0
  197. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/__init__.py +0 -0
  198. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/bm25.py +0 -0
  199. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/diff_utils.py +0 -0
  200. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/file_utils.py +0 -0
  201. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/import_cache.py +0 -0
  202. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/ripgrep.py +0 -0
  203. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/security.py +0 -0
  204. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/system.py +0 -0
  205. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/text_utils.py +0 -0
  206. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/token_counter.py +0 -0
  207. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode/utils/user_configuration.py +0 -0
  208. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode_cli.egg-info/dependency_links.txt +0 -0
  209. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode_cli.egg-info/entry_points.txt +0 -0
  210. {tunacode_cli-0.0.36 → tunacode_cli-0.0.38}/src/tunacode_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tunacode-cli
3
- Version: 0.0.36
3
+ Version: 0.0.38
4
4
  Summary: Your agentic CLI developer.
5
5
  Author-email: larock22 <noreply@github.com>
6
6
  License-Expression: MIT
@@ -26,9 +26,7 @@ Requires-Dist: pygments==2.19.1
26
26
  Requires-Dist: rich==14.0.0
27
27
  Provides-Extra: dev
28
28
  Requires-Dist: build; extra == "dev"
29
- Requires-Dist: black; extra == "dev"
30
- Requires-Dist: flake8; extra == "dev"
31
- Requires-Dist: isort; extra == "dev"
29
+ Requires-Dist: ruff; extra == "dev"
32
30
  Requires-Dist: pytest; extra == "dev"
33
31
  Requires-Dist: pytest-cov; extra == "dev"
34
32
  Requires-Dist: pytest-asyncio; extra == "dev"
@@ -70,7 +68,7 @@ Choose your AI provider and set your API key:
70
68
  # OpenAI
71
69
  tunacode --model "openai:gpt-4o" --key "sk-your-openai-key"
72
70
 
73
- # Anthropic Claude
71
+ # Anthropic Claude
74
72
  tunacode --model "anthropic:claude-3.5-sonnet" --key "sk-ant-your-anthropic-key"
75
73
 
76
74
  # OpenRouter (100+ models)
@@ -82,13 +80,14 @@ Your config is saved to `~/.config/tunacode.json` (edit directly with `nvim ~/.c
82
80
  ### Recommended Models
83
81
 
84
82
  Based on extensive testing, these models provide the best performance:
83
+
85
84
  - `google/gemini-2.5-pro` - Excellent for complex reasoning
86
85
  - `openai/gpt-4.1` - Strong general-purpose model
87
86
  - `deepseek/deepseek-r1-0528` - Great for code generation
88
87
  - `openai/gpt-4.1-mini` - Fast and cost-effective
89
88
  - `anthropic/claude-4-sonnet-20250522` - Superior context handling
90
89
 
91
- *Note: Formal evaluations coming soon. Any model can work, but these have shown the best results in practice.*
90
+ _Note: Formal evaluations coming soon. Any model can work, but these have shown the best results in practice._
92
91
 
93
92
  ## Start Coding
94
93
 
@@ -98,16 +97,16 @@ tunacode
98
97
 
99
98
  ## Basic Commands
100
99
 
101
- | Command | Description |
102
- | ------- | ----------- |
103
- | `/help` | Show all commands |
104
- | `/model <provider:name>` | Switch model |
105
- | `/clear` | Clear message history |
106
- | `/compact` | Summarize conversation |
107
- | `/branch <name>` | Create Git branch |
108
- | `/yolo` | Skip confirmations |
109
- | `!<command>` | Run shell command |
110
- | `exit` | Exit TunaCode |
100
+ | Command | Description |
101
+ | ------------------------ | ---------------------- |
102
+ | `/help` | Show all commands |
103
+ | `/model <provider:name>` | Switch model |
104
+ | `/clear` | Clear message history |
105
+ | `/compact` | Summarize conversation |
106
+ | `/branch <name>` | Create Git branch |
107
+ | `/yolo` | Skip confirmations |
108
+ | `!<command>` | Run shell command |
109
+ | `exit` | Exit TunaCode |
111
110
 
112
111
  ## Performance
113
112
 
@@ -122,11 +121,12 @@ Multiple file reads, directory listings, and searches execute concurrently using
122
121
  - **Streaming UI**: Currently working on implementing streaming responses for better user experience
123
122
  - **Bug Fixes**: Actively addressing issues - please report any bugs you encounter!
124
123
 
125
- *Note: While the tool is fully functional, we're focusing on stability and core features before optimizing for speed.*
124
+ _Note: While the tool is fully functional, we're focusing on stability and core features before optimizing for speed._
126
125
 
127
126
  ## Safety First
128
127
 
129
128
  ⚠️ **Important**: TunaCode can modify your codebase. Always:
129
+
130
130
  - Use Git branches before making changes
131
131
  - Review file modifications before confirming
132
132
  - Keep backups of important work
@@ -33,7 +33,7 @@ Choose your AI provider and set your API key:
33
33
  # OpenAI
34
34
  tunacode --model "openai:gpt-4o" --key "sk-your-openai-key"
35
35
 
36
- # Anthropic Claude
36
+ # Anthropic Claude
37
37
  tunacode --model "anthropic:claude-3.5-sonnet" --key "sk-ant-your-anthropic-key"
38
38
 
39
39
  # OpenRouter (100+ models)
@@ -45,13 +45,14 @@ Your config is saved to `~/.config/tunacode.json` (edit directly with `nvim ~/.c
45
45
  ### Recommended Models
46
46
 
47
47
  Based on extensive testing, these models provide the best performance:
48
+
48
49
  - `google/gemini-2.5-pro` - Excellent for complex reasoning
49
50
  - `openai/gpt-4.1` - Strong general-purpose model
50
51
  - `deepseek/deepseek-r1-0528` - Great for code generation
51
52
  - `openai/gpt-4.1-mini` - Fast and cost-effective
52
53
  - `anthropic/claude-4-sonnet-20250522` - Superior context handling
53
54
 
54
- *Note: Formal evaluations coming soon. Any model can work, but these have shown the best results in practice.*
55
+ _Note: Formal evaluations coming soon. Any model can work, but these have shown the best results in practice._
55
56
 
56
57
  ## Start Coding
57
58
 
@@ -61,16 +62,16 @@ tunacode
61
62
 
62
63
  ## Basic Commands
63
64
 
64
- | Command | Description |
65
- | ------- | ----------- |
66
- | `/help` | Show all commands |
67
- | `/model <provider:name>` | Switch model |
68
- | `/clear` | Clear message history |
69
- | `/compact` | Summarize conversation |
70
- | `/branch <name>` | Create Git branch |
71
- | `/yolo` | Skip confirmations |
72
- | `!<command>` | Run shell command |
73
- | `exit` | Exit TunaCode |
65
+ | Command | Description |
66
+ | ------------------------ | ---------------------- |
67
+ | `/help` | Show all commands |
68
+ | `/model <provider:name>` | Switch model |
69
+ | `/clear` | Clear message history |
70
+ | `/compact` | Summarize conversation |
71
+ | `/branch <name>` | Create Git branch |
72
+ | `/yolo` | Skip confirmations |
73
+ | `!<command>` | Run shell command |
74
+ | `exit` | Exit TunaCode |
74
75
 
75
76
  ## Performance
76
77
 
@@ -85,11 +86,12 @@ Multiple file reads, directory listings, and searches execute concurrently using
85
86
  - **Streaming UI**: Currently working on implementing streaming responses for better user experience
86
87
  - **Bug Fixes**: Actively addressing issues - please report any bugs you encounter!
87
88
 
88
- *Note: While the tool is fully functional, we're focusing on stability and core features before optimizing for speed.*
89
+ _Note: While the tool is fully functional, we're focusing on stability and core features before optimizing for speed._
89
90
 
90
91
  ## Safety First
91
92
 
92
93
  ⚠️ **Important**: TunaCode can modify your codebase. Always:
94
+
93
95
  - Use Git branches before making changes
94
96
  - Review file modifications before confirming
95
97
  - Keep backups of important work
@@ -109,4 +111,4 @@ Multiple file reads, directory listings, and searches execute concurrently using
109
111
 
110
112
  ---
111
113
 
112
- MIT License - see [LICENSE](LICENSE) file
114
+ MIT License - see [LICENSE](LICENSE) file
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tunacode-cli"
7
- version = "0.0.36"
7
+ version = "0.0.38"
8
8
  description = "Your agentic CLI developer."
9
9
  keywords = ["cli", "agent", "development", "automation"]
10
10
  readme = "README.md"
@@ -38,9 +38,7 @@ tunacode = "tunacode.cli.main:app"
38
38
  [project.optional-dependencies]
39
39
  dev = [
40
40
  "build",
41
- "black",
42
- "flake8",
43
- "isort",
41
+ "ruff",
44
42
  "pytest",
45
43
  "pytest-cov",
46
44
  "pytest-asyncio",
@@ -51,9 +49,27 @@ dev = [
51
49
  Homepage = "https://github.com/larock22/tunacode"
52
50
  Repository = "https://github.com/larock22/tunacode"
53
51
 
54
- [tool.black]
52
+ [tool.ruff]
55
53
  line-length = 100
56
54
 
57
- [tool.isort]
58
- line_length = 100
59
- profile = "black"
55
+ [tool.ruff.lint]
56
+ extend-select = ["I"]
57
+ ignore = ["E203", "E501"]
58
+ exclude = [
59
+ ".bzr",
60
+ ".direnv",
61
+ ".eggs",
62
+ ".git",
63
+ ".hg",
64
+ ".mypy_cache",
65
+ ".nox",
66
+ ".pants.d",
67
+ ".ruff_cache",
68
+ ".svn",
69
+ ".tox",
70
+ ".venv",
71
+ "__pypackages__",
72
+ "build",
73
+ "dist",
74
+ "venv",
75
+ ]
@@ -1,4 +1,4 @@
1
- from setuptools import setup, find_namespace_packages
1
+ from setuptools import find_namespace_packages, setup
2
2
 
3
3
  setup(
4
4
  package_dir={"": "src"},
@@ -12,13 +12,14 @@ from .debug import (
12
12
  )
13
13
  from .development import BranchCommand, InitCommand
14
14
  from .model import ModelCommand
15
- from .system import ClearCommand, HelpCommand, RefreshConfigCommand, UpdateCommand
15
+ from .system import ClearCommand, HelpCommand, RefreshConfigCommand, StreamingCommand, UpdateCommand
16
16
 
17
17
  __all__ = [
18
18
  # System commands
19
19
  "HelpCommand",
20
20
  "ClearCommand",
21
21
  "RefreshConfigCommand",
22
+ "StreamingCommand",
22
23
  "UpdateCommand",
23
24
  # Debug commands
24
25
  "YoloCommand",
@@ -175,3 +175,42 @@ class UpdateCommand(SimpleCommand):
175
175
  await ui.error(f"Update failed: {e}")
176
176
  except FileNotFoundError:
177
177
  await ui.error(f"Could not find {installation_method} executable")
178
+
179
+
180
+ class StreamingCommand(SimpleCommand):
181
+ """Toggle streaming display on/off."""
182
+
183
+ spec = CommandSpec(
184
+ name="streaming",
185
+ aliases=["/streaming"],
186
+ description="Toggle streaming display on/off",
187
+ category=CommandCategory.SYSTEM,
188
+ )
189
+
190
+ async def execute(self, args: List[str], context: CommandContext) -> None:
191
+ current_setting = context.state_manager.session.user_config.get("settings", {}).get(
192
+ "enable_streaming", True
193
+ )
194
+
195
+ if args and args[0].lower() in ["on", "true", "1", "enable", "enabled"]:
196
+ new_setting = True
197
+ elif args and args[0].lower() in ["off", "false", "0", "disable", "disabled"]:
198
+ new_setting = False
199
+ else:
200
+ # Toggle current setting
201
+ new_setting = not current_setting
202
+
203
+ # Update the configuration
204
+ if "settings" not in context.state_manager.session.user_config:
205
+ context.state_manager.session.user_config["settings"] = {}
206
+ context.state_manager.session.user_config["settings"]["enable_streaming"] = new_setting
207
+
208
+ status = "enabled" if new_setting else "disabled"
209
+ await ui.success(f"Streaming display {status}")
210
+
211
+ if new_setting:
212
+ await ui.muted(
213
+ "Responses will be displayed progressively as they are generated (default)"
214
+ )
215
+ else:
216
+ await ui.muted("Responses will be displayed all at once after completion")
@@ -19,7 +19,13 @@ from .implementations.debug import (
19
19
  )
20
20
  from .implementations.development import BranchCommand, InitCommand
21
21
  from .implementations.model import ModelCommand
22
- from .implementations.system import ClearCommand, HelpCommand, RefreshConfigCommand, UpdateCommand
22
+ from .implementations.system import (
23
+ ClearCommand,
24
+ HelpCommand,
25
+ RefreshConfigCommand,
26
+ StreamingCommand,
27
+ UpdateCommand,
28
+ )
23
29
 
24
30
 
25
31
  @dataclass
@@ -106,6 +112,7 @@ class CommandRegistry:
106
112
  FixCommand,
107
113
  ParseToolsCommand,
108
114
  RefreshConfigCommand,
115
+ StreamingCommand,
109
116
  UpdateCommand,
110
117
  HelpCommand,
111
118
  BranchCommand,
@@ -70,8 +70,9 @@ async def _tool_confirm(tool_call, node, state_manager: StateManager):
70
70
  await _tool_ui.log_mcp(title, args)
71
71
  return
72
72
 
73
- # Stop spinner during user interaction
74
- state_manager.session.spinner.stop()
73
+ # Stop spinner during user interaction (only if not streaming)
74
+ if not state_manager.session.is_streaming_active and state_manager.session.spinner:
75
+ state_manager.session.spinner.stop()
75
76
 
76
77
  # Create confirmation request
77
78
  request = tool_handler.create_confirmation_request(tool_call.tool_name, args)
@@ -84,7 +85,10 @@ async def _tool_confirm(tool_call, node, state_manager: StateManager):
84
85
  raise UserAbortError("User aborted.")
85
86
 
86
87
  await ui.line() # Add line after user input
87
- state_manager.session.spinner.start()
88
+
89
+ # Restart spinner (only if not streaming)
90
+ if not state_manager.session.is_streaming_active and state_manager.session.spinner:
91
+ state_manager.session.spinner.start()
88
92
 
89
93
 
90
94
  async def _tool_handler(part, node, state_manager: StateManager):
@@ -96,7 +100,19 @@ async def _tool_handler(part, node, state_manager: StateManager):
96
100
  if tool_handler.should_confirm(part.tool_name):
97
101
  await ui.info(f"Tool({part.tool_name})")
98
102
 
99
- state_manager.session.spinner.stop()
103
+ # Stop spinner only if not streaming
104
+ if not state_manager.session.is_streaming_active and state_manager.session.spinner:
105
+ state_manager.session.spinner.stop()
106
+
107
+ # Track if we need to stop/restart streaming panel
108
+ streaming_panel = None
109
+ if state_manager.session.is_streaming_active and hasattr(
110
+ state_manager.session, "streaming_panel"
111
+ ):
112
+ streaming_panel = state_manager.session.streaming_panel
113
+ # Stop the streaming panel to prevent UI interference during confirmation
114
+ if streaming_panel and tool_handler.should_confirm(part.tool_name):
115
+ await streaming_panel.stop()
100
116
 
101
117
  try:
102
118
  args = _parse_args(part.args)
@@ -128,7 +144,13 @@ async def _tool_handler(part, node, state_manager: StateManager):
128
144
  patch_tool_messages("Operation aborted by user.", state_manager)
129
145
  raise
130
146
  finally:
131
- state_manager.session.spinner.start()
147
+ # Restart streaming panel if it was stopped
148
+ if streaming_panel and tool_handler.should_confirm(part.tool_name):
149
+ await streaming_panel.start()
150
+
151
+ # Restart spinner only if not streaming
152
+ if not state_manager.session.is_streaming_active and state_manager.session.spinner:
153
+ state_manager.session.spinner.start()
132
154
 
133
155
 
134
156
  # Initialize command registry
@@ -195,38 +217,82 @@ async def process_request(text: str, state_manager: StateManager, output: bool =
195
217
  await ui.error(str(e))
196
218
  return
197
219
 
198
- # Use normal agent processing
199
- res = await agent.process_request(
200
- state_manager.session.current_model,
201
- text,
202
- state_manager,
203
- tool_callback=tool_callback_with_state,
220
+ # Check if streaming is enabled (default: True for better UX)
221
+ enable_streaming = state_manager.session.user_config.get("settings", {}).get(
222
+ "enable_streaming", True
204
223
  )
224
+
225
+ if enable_streaming:
226
+ # Stop spinner before starting streaming display (Rich.Live conflict)
227
+ await ui.spinner(False, state_manager.session.spinner, state_manager)
228
+
229
+ # Mark that streaming is active to prevent spinner conflicts
230
+ state_manager.session.is_streaming_active = True
231
+
232
+ # Use streaming agent processing
233
+ streaming_panel = ui.StreamingAgentPanel()
234
+ await streaming_panel.start()
235
+
236
+ # Store streaming panel reference in session for tool handler access
237
+ state_manager.session.streaming_panel = streaming_panel
238
+
239
+ try:
240
+
241
+ async def streaming_callback(content: str):
242
+ await streaming_panel.update(content)
243
+
244
+ res = await agent.process_request(
245
+ state_manager.session.current_model,
246
+ text,
247
+ state_manager,
248
+ tool_callback=tool_callback_with_state,
249
+ streaming_callback=streaming_callback,
250
+ )
251
+ finally:
252
+ await streaming_panel.stop()
253
+ # Clear streaming panel reference
254
+ state_manager.session.streaming_panel = None
255
+ # Mark streaming as inactive
256
+ state_manager.session.is_streaming_active = False
257
+ # Don't restart spinner - it will be stopped in the outer finally block anyway
258
+ else:
259
+ # Use normal agent processing
260
+ res = await agent.process_request(
261
+ state_manager.session.current_model,
262
+ text,
263
+ state_manager,
264
+ tool_callback=tool_callback_with_state,
265
+ )
205
266
  if output:
206
267
  if state_manager.session.show_thoughts:
207
268
  new_msgs = state_manager.session.messages[start_idx:]
208
269
  for msg in new_msgs:
209
270
  if isinstance(msg, dict) and "thought" in msg:
210
271
  await ui.muted(f"THOUGHT: {msg['thought']}")
211
- # Check if result exists and has output
212
- if hasattr(res, "result") and res.result is not None and hasattr(res.result, "output"):
213
- await ui.agent(res.result.output)
214
- # Always show files in context after agent response
215
- if state_manager.session.files_in_context:
216
- # Extract just filenames from full paths for readability
217
- filenames = [
218
- Path(f).name for f in sorted(state_manager.session.files_in_context)
219
- ]
220
- await ui.muted(f"\nFiles in context: {', '.join(filenames)}")
221
- else:
222
- # Fallback: show that the request was processed
223
- await ui.muted("Request completed")
224
- # Show files in context even for empty responses
225
- if state_manager.session.files_in_context:
226
- filenames = [
227
- Path(f).name for f in sorted(state_manager.session.files_in_context)
228
- ]
229
- await ui.muted(f"Files in context: {', '.join(filenames)}")
272
+
273
+ # Only display result if not streaming (streaming already showed content)
274
+ if not enable_streaming:
275
+ # Check if result exists and has output
276
+ if (
277
+ hasattr(res, "result")
278
+ and res.result is not None
279
+ and hasattr(res.result, "output")
280
+ ):
281
+ output = res.result.output
282
+ # Filter out JSON responses with "thought" field
283
+ if isinstance(output, str) and not (
284
+ output.strip().startswith('{"thought"') or '"tool_uses"' in output
285
+ ):
286
+ await ui.agent(output)
287
+ else:
288
+ # Fallback: show that the request was processed
289
+ await ui.muted("Request completed")
290
+
291
+ # Always show files in context after agent response
292
+ if state_manager.session.files_in_context:
293
+ # Extract just filenames from full paths for readability
294
+ filenames = [Path(f).name for f in sorted(state_manager.session.files_in_context)]
295
+ await ui.muted(f"\nFiles in context: {', '.join(filenames)}")
230
296
  except CancelledError:
231
297
  await ui.muted("Request cancelled")
232
298
  except UserAbortError:
@@ -7,7 +7,7 @@ Centralizes all magic strings, UI text, error messages, and application constant
7
7
 
8
8
  # Application info
9
9
  APP_NAME = "TunaCode"
10
- APP_VERSION = "0.0.36"
10
+ APP_VERSION = "0.0.38"
11
11
 
12
12
  # File patterns
13
13
  GUIDE_FILE_PATTERN = "{name}.md"
@@ -12,6 +12,22 @@ from datetime import datetime, timezone
12
12
  from pathlib import Path
13
13
  from typing import Any, Iterator, List, Optional, Tuple
14
14
 
15
+ from pydantic_ai import Agent
16
+
17
+ # Import streaming types with fallback for older versions
18
+ try:
19
+ from pydantic_ai.messages import (
20
+ PartDeltaEvent,
21
+ TextPartDelta,
22
+ )
23
+
24
+ STREAMING_AVAILABLE = True
25
+ except ImportError:
26
+ # Fallback for older pydantic-ai versions
27
+ PartDeltaEvent = None
28
+ TextPartDelta = None
29
+ STREAMING_AVAILABLE = False
30
+
15
31
  from tunacode.constants import READ_ONLY_TOOLS
16
32
  from tunacode.core.state import StateManager
17
33
  from tunacode.services.mcp import get_mcp_servers
@@ -197,6 +213,7 @@ async def _process_node(
197
213
  tool_callback: Optional[ToolCallback],
198
214
  state_manager: StateManager,
199
215
  tool_buffer: Optional[ToolBuffer] = None,
216
+ streaming_callback: Optional[callable] = None,
200
217
  ):
201
218
  from tunacode.ui import console as ui
202
219
  from tunacode.utils.token_counter import estimate_tokens
@@ -216,6 +233,16 @@ async def _process_node(
216
233
  if hasattr(node, "model_response"):
217
234
  state_manager.session.messages.append(node.model_response)
218
235
 
236
+ # Stream content to callback if provided
237
+ # Use this as fallback when true token streaming is not available
238
+ if streaming_callback and not STREAMING_AVAILABLE:
239
+ for part in node.model_response.parts:
240
+ if hasattr(part, "content") and isinstance(part.content, str):
241
+ content = part.content.strip()
242
+ if content and not content.startswith('{"thought"'):
243
+ # Stream non-JSON content (actual response content)
244
+ await streaming_callback(content)
245
+
219
246
  # Enhanced display when thoughts are enabled
220
247
  if state_manager.session.show_thoughts:
221
248
  # Show raw API response data
@@ -683,6 +710,7 @@ async def process_request(
683
710
  message: str,
684
711
  state_manager: StateManager,
685
712
  tool_callback: Optional[ToolCallback] = None,
713
+ streaming_callback: Optional[callable] = None,
686
714
  ) -> AgentRun:
687
715
  agent = get_or_create_agent(model, state_manager)
688
716
  mh = state_manager.session.messages.copy()
@@ -723,7 +751,19 @@ async def process_request(
723
751
  i = 0
724
752
  async for node in agent_run:
725
753
  state_manager.session.current_iteration = i + 1
726
- await _process_node(node, tool_callback, state_manager, tool_buffer)
754
+
755
+ # Handle token-level streaming for model request nodes
756
+ if streaming_callback and STREAMING_AVAILABLE and Agent.is_model_request_node(node):
757
+ async with node.stream(agent_run.ctx) as request_stream:
758
+ async for event in request_stream:
759
+ if isinstance(event, PartDeltaEvent) and isinstance(
760
+ event.delta, TextPartDelta
761
+ ):
762
+ # Stream individual token deltas
763
+ if event.delta.content_delta:
764
+ await streaming_callback(event.delta.content_delta)
765
+
766
+ await _process_node(node, tool_callback, state_manager, tool_buffer, streaming_callback)
727
767
  if hasattr(node, "result") and node.result and hasattr(node.result, "output"):
728
768
  if node.result.output:
729
769
  response_state.has_user_response = True