tunacode-cli 0.2.1__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 (357) hide show
  1. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/AGENTS.md +3 -3
  2. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/CHANGELOG.md +8 -1
  3. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/PKG-INFO +2 -2
  4. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/core/core.md +5 -2
  5. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/pyproject.toml +2 -2
  6. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/constants.py +1 -1
  7. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_config.py +2 -0
  8. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_streaming.py +2 -12
  9. tunacode_cli-0.2.2/src/tunacode/core/agents/agent_components/agent_turn_control.py +37 -0
  10. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/main.py +3 -8
  11. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/exceptions.py +0 -1
  12. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/prompts/system_prompt.md +1 -0
  13. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/app.py +7 -31
  14. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/thoughts.py +2 -2
  15. tunacode_cli-0.2.2/src/tunacode/ui/thinking_state.py +138 -0
  16. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/chat.py +5 -1
  17. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_tool_concurrency_limit.py +18 -0
  18. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_request_threading.py +20 -9
  19. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_thinking_state.py +17 -16
  20. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/uv.lock +6 -6
  21. tunacode_cli-0.2.1/src/tunacode/ui/thinking_state.py +0 -123
  22. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.coderabbit.yaml +0 -0
  23. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.githooks/pre-commit +0 -0
  24. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.githooks/pre-push +0 -0
  25. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  26. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  27. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  28. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  29. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/dependabot.yml +0 -0
  30. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/pull_request_template.md +0 -0
  31. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/dependency-map.yml +0 -0
  32. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/empty-dir-check.yml +0 -0
  33. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/lint.yml +0 -0
  34. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/publish-release.yml +0 -0
  35. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/release.yml +0 -0
  36. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/tech-debt.yml +0 -0
  37. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.gitignore +0 -0
  38. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.pre-commit-config.yaml +0 -0
  39. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/HARNESS.md +0 -0
  40. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/LICENSE +0 -0
  41. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/MANIFEST.in +0 -0
  42. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/Makefile +0 -0
  43. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/README.md +0 -0
  44. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/assets/hashline-edit.png +0 -0
  45. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/assets/home.png +0 -0
  46. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/CODE-STANDARDS.md +0 -0
  47. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/README.md +0 -0
  48. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/dependencies/DEPENDENCY_LAYERS.dot +0 -0
  49. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/dependencies/DEPENDENCY_LAYERS.md +0 -0
  50. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/dependencies/DEPENDENCY_LAYERS.png +0 -0
  51. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/patterns/agent-message-serialization.md +0 -0
  52. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/codebase-map/structure/tree-structure.txt +0 -0
  53. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/deployment.md +0 -0
  54. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/git/changelog-update.md +0 -0
  55. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/git/practices.md +0 -0
  56. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-home-catpu.png +0 -0
  57. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-home.png +0 -0
  58. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-lsp.png +0 -0
  59. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-response.png +0 -0
  60. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/tool-calls.png +0 -0
  61. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/tunacode.png +0 -0
  62. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/configuration/configuration.md +0 -0
  63. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/configuration/models-registry.md +0 -0
  64. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/index.md +0 -0
  65. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/infrastructure/infrastructure.md +0 -0
  66. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/skills/skills.md +0 -0
  67. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/tools/hashline-subsystem.md +0 -0
  68. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/tools/tools-system.md +0 -0
  69. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/tools/tools.md +0 -0
  70. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/types/types.md +0 -0
  71. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/ui/commands.md +0 -0
  72. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/ui/ui.md +0 -0
  73. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/utils/utils.md +0 -0
  74. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/reviews/2026-03-24-input-latency-research-artifact.md +0 -0
  75. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/reviews/2026-03-25-provider-stream-stall-artifact.md +0 -0
  76. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/reviews/pr-429-coderabbit-review.md +0 -0
  77. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/skills/audit-harness/SKILL.md +0 -0
  78. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/ui/css-architecture.md +0 -0
  79. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/DEBUG.md +0 -0
  80. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/DOCS.md +0 -0
  81. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/DREAM.md +0 -0
  82. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/FEATURES.md +0 -0
  83. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/HARNESS.md +0 -0
  84. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/HOTFIX.md +0 -0
  85. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/QA.md +0 -0
  86. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/README.md +0 -0
  87. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/package-lock.json +0 -0
  88. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/pytest.ini +0 -0
  89. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/baseline/no-getattr-in-src.json +0 -0
  90. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/rule-tests/__snapshots__/no-getattr-in-src-snapshot.yml +0 -0
  91. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/rule-tests/no-getattr-in-src-test.yml +0 -0
  92. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/rules/no-getattr-in-src.yml +0 -0
  93. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/sgconfig.yml +0 -0
  94. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/benchmarks/input_latency.py +0 -0
  95. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/benchmarks/input_paint_latency.py +0 -0
  96. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/benchmarks/input_tmux_latency.py +0 -0
  97. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-defensive-slop.py +0 -0
  98. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-file-length.sh +0 -0
  99. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-naming-conventions.py +0 -0
  100. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-unused-constants.py +0 -0
  101. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_agents_freshness.py +0 -0
  102. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_ast_grep_baseline.py +0 -0
  103. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_markdown_frontmatter.py +0 -0
  104. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_stale_symbol_surfaces.py +0 -0
  105. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_unused_modules.py +0 -0
  106. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/dev-setup.sh +0 -0
  107. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/download_ripgrep.py +0 -0
  108. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/generate-release-notes.sh +0 -0
  109. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/generate_structure_tree.py +0 -0
  110. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/grimp_layers_report.py +0 -0
  111. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/install-git-hooks.sh +0 -0
  112. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/install_linux.sh +0 -0
  113. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/playwright_cache.py +0 -0
  114. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/preview_tool_panels.py +0 -0
  115. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run-dead-imports.sh +0 -0
  116. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run-pre-commit-hook.sh +0 -0
  117. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run_ast_grep_checks.sh +0 -0
  118. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run_gates.py +0 -0
  119. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/stale_symbol_surfaces_analysis.py +0 -0
  120. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/stale_symbol_surfaces_models.py +0 -0
  121. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/stale_symbol_surfaces_visitors.py +0 -0
  122. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/startup_timer.py +0 -0
  123. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/todo_baseline.json +0 -0
  124. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/todo_scanner.py +0 -0
  125. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/ui_import_timer.py +0 -0
  126. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/uninstall.sh +0 -0
  127. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/update_models_registry.sh +0 -0
  128. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/utils/check_empty_dirs.py +0 -0
  129. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/utils/vulture_whitelist.py +0 -0
  130. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/validate_ast_grep_rules.py +0 -0
  131. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/SKILL.md +0 -0
  132. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/references/common-issues.md +0 -0
  133. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/references/workflow-structure.md +0 -0
  134. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/scripts/bump_version.py +0 -0
  135. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/scripts/local_publish.py +0 -0
  136. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/scripts/release.py +0 -0
  137. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/__init__.py +0 -0
  138. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/__init__.py +0 -0
  139. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/defaults.py +0 -0
  140. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/ignore_patterns.py +0 -0
  141. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/limits.py +0 -0
  142. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/models.py +0 -0
  143. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/models_registry.json +0 -0
  144. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/paths.py +0 -0
  145. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/pricing.py +0 -0
  146. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/settings.py +0 -0
  147. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/user_config.py +0 -0
  148. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/__init__.py +0 -0
  149. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/__init__.py +0 -0
  150. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/__init__.py +0 -0
  151. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_helpers.py +0 -0
  152. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_session_config.py +0 -0
  153. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/helpers.py +0 -0
  154. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/resume/__init__.py +0 -0
  155. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/resume/sanitize.py +0 -0
  156. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/resume/sanitize_debug.py +0 -0
  157. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/__init__.py +0 -0
  158. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/controller.py +0 -0
  159. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/prompts.py +0 -0
  160. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/summarizer.py +0 -0
  161. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/types.py +0 -0
  162. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/debug/__init__.py +0 -0
  163. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/debug/usage_trace.py +0 -0
  164. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/__init__.py +0 -0
  165. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/handlers.py +0 -0
  166. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/levels.py +0 -0
  167. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/manager.py +0 -0
  168. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/records.py +0 -0
  169. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/session/__init__.py +0 -0
  170. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/session/state.py +0 -0
  171. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/__init__.py +0 -0
  172. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/agent_state.py +0 -0
  173. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/state.py +0 -0
  174. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/state_structures.py +0 -0
  175. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/ui_api/__init__.py +0 -0
  176. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/ui_api/file_filter.py +0 -0
  177. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/ui_api/formatting.py +0 -0
  178. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/__init__.py +0 -0
  179. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/__init__.py +0 -0
  180. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/__init__.py +0 -0
  181. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/agents.py +0 -0
  182. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/limits_settings.py +0 -0
  183. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/models_registry.py +0 -0
  184. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/skills.py +0 -0
  185. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/tunacode_context.py +0 -0
  186. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/manager.py +0 -0
  187. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/metadata.py +0 -0
  188. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/strategies.py +0 -0
  189. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/file_filter.py +0 -0
  190. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/py.typed +0 -0
  191. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/__init__.py +0 -0
  192. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/discovery.py +0 -0
  193. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/loader.py +0 -0
  194. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/models.py +0 -0
  195. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/prompting.py +0 -0
  196. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/registry.py +0 -0
  197. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/search.py +0 -0
  198. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/selection.py +0 -0
  199. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/__init__.py +0 -0
  200. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/bash.py +0 -0
  201. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/cache_accessors/__init__.py +0 -0
  202. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/cache_accessors/ignore_manager_cache.py +0 -0
  203. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/cache_accessors/ripgrep_cache.py +0 -0
  204. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/discover.py +0 -0
  205. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/hashline.py +0 -0
  206. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/hashline_edit.py +0 -0
  207. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/ignore.py +0 -0
  208. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/ignore_manager.py +0 -0
  209. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/line_cache.py +0 -0
  210. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/read_file.py +0 -0
  211. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/__init__.py +0 -0
  212. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/discover_pipeline.py +0 -0
  213. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/discover_terms.py +0 -0
  214. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/discover_types.py +0 -0
  215. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/file_errors.py +0 -0
  216. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/formatting.py +0 -0
  217. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/ripgrep.py +0 -0
  218. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/web_fetch.py +0 -0
  219. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/write_file.py +0 -0
  220. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/__init__.py +0 -0
  221. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/base.py +0 -0
  222. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/callbacks.py +0 -0
  223. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/dataclasses.py +0 -0
  224. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/models_registry.py +0 -0
  225. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/__init__.py +0 -0
  226. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/assets/logo.ansi +0 -0
  227. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/clipboard.py +0 -0
  228. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/command_registry.py +0 -0
  229. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/__init__.py +0 -0
  230. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/base.py +0 -0
  231. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/cancel.py +0 -0
  232. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/clear.py +0 -0
  233. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/compact.py +0 -0
  234. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/debug.py +0 -0
  235. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/exit.py +0 -0
  236. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/help.py +0 -0
  237. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/model.py +0 -0
  238. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/resume.py +0 -0
  239. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/skills.py +0 -0
  240. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/theme.py +0 -0
  241. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/update.py +0 -0
  242. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/context_panel.py +0 -0
  243. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/esc/__init__.py +0 -0
  244. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/esc/handler.py +0 -0
  245. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/esc/types.py +0 -0
  246. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/lifecycle.py +0 -0
  247. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/logo_assets.py +0 -0
  248. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/main.py +0 -0
  249. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/model_display.py +0 -0
  250. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/render_safety.py +0 -0
  251. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/__init__.py +0 -0
  252. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/agent_response.py +0 -0
  253. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/errors.py +0 -0
  254. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/panel_widths.py +0 -0
  255. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/panels.py +0 -0
  256. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/search.py +0 -0
  257. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/thinking.py +0 -0
  258. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/__init__.py +0 -0
  259. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/base.py +0 -0
  260. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/bash.py +0 -0
  261. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/discover.py +0 -0
  262. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/hashline_edit.py +0 -0
  263. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/read_file.py +0 -0
  264. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/syntax_utils.py +0 -0
  265. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/web_fetch.py +0 -0
  266. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/write_file.py +0 -0
  267. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/repl_support.py +0 -0
  268. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/request_bridge.py +0 -0
  269. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/request_debug.py +0 -0
  270. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/__init__.py +0 -0
  271. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/api_key_entry.py +0 -0
  272. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/model_picker.py +0 -0
  273. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/session_picker.py +0 -0
  274. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/setup.py +0 -0
  275. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/theme_picker.py +0 -0
  276. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/update_confirm.py +0 -0
  277. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/shell_runner.py +0 -0
  278. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/slopgotchi/__init__.py +0 -0
  279. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/slopgotchi/panel.py +0 -0
  280. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/streaming.py +0 -0
  281. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/layout.tcss +0 -0
  282. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/modals.tcss +0 -0
  283. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/panels.tcss +0 -0
  284. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/widgets.tcss +0 -0
  285. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles.py +0 -0
  286. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/welcome.py +0 -0
  287. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/__init__.py +0 -0
  288. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/autocomplete_positioning.py +0 -0
  289. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/command_autocomplete.py +0 -0
  290. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/editor.py +0 -0
  291. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/file_autocomplete.py +0 -0
  292. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/messages.py +0 -0
  293. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/resource_bar.py +0 -0
  294. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/skills_autocomplete.py +0 -0
  295. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/__init__.py +0 -0
  296. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/messaging/__init__.py +0 -0
  297. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/messaging/adapter.py +0 -0
  298. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/messaging/token_counter.py +0 -0
  299. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/system/__init__.py +0 -0
  300. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/system/gitignore.py +0 -0
  301. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tc-cvurren.png +0 -0
  302. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/__init__.py +0 -0
  303. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/__init__.py +0 -0
  304. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_import_order.py +0 -0
  305. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_imports_in_init.py +0 -0
  306. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_layer_dependencies.py +0 -0
  307. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_ui_api_surface.py +0 -0
  308. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/conftest.py +0 -0
  309. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/core/test_minimax_execution_path.py +0 -0
  310. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/core/test_mtime_caches_end_to_end.py +0 -0
  311. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/core/test_tinyagent_alchemy_usage_contract_live.py +0 -0
  312. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/ui/test_submit_loading_lifecycle.py +0 -0
  313. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/ui/test_theme_render_crash_regression.py +0 -0
  314. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_cli_default_command.py +0 -0
  315. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_repl_support.py +0 -0
  316. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_startup.py +0 -0
  317. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_tmux_tools.py +0 -0
  318. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/test_compaction.py +0 -0
  319. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/test_dependency_layers.py +0 -0
  320. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_default_config_minimax_keys.py +0 -0
  321. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_default_config_stream_agent_text.py +0 -0
  322. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_ignore_patterns.py +0 -0
  323. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_models_registry_loading_behavior.py +0 -0
  324. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_models_registry_minimax_contract.py +0 -0
  325. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_pricing_registry_loading.py +0 -0
  326. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_provider_api_key_env_fallback.py +0 -0
  327. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_user_config_loading.py +0 -0
  328. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_cache_abort.py +0 -0
  329. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_init_debug.py +0 -0
  330. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_skills_prompt_injection.py +0 -0
  331. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_stream_provider_debug.py +0 -0
  332. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_compaction_controller_outcomes.py +0 -0
  333. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_compaction_summarizer.py +0 -0
  334. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_context_overflow_detection.py +0 -0
  335. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_exceptions.py +0 -0
  336. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_logging.py +0 -0
  337. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_provider_error_surfacing.py +0 -0
  338. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_session_state_defaults.py +0 -0
  339. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_session_state_model_registry_loading.py +0 -0
  340. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_session_usage_schema.py +0 -0
  341. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_thinking_stream_routing.py +0 -0
  342. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_tinyagent_openrouter_model_config.py +0 -0
  343. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_cache_infrastructure.py +0 -0
  344. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_file_filter.py +0 -0
  345. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_migrated_lru_cache_replacements.py +0 -0
  346. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_typed_cache_accessors.py +0 -0
  347. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/skills/test_registry.py +0 -0
  348. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/skills/test_selection.py +0 -0
  349. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/test_stale_symbol_surfaces.py +0 -0
  350. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/tools/test_ignore_manager.py +0 -0
  351. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_app_loading_indicator.py +0 -0
  352. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_context_panel.py +0 -0
  353. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_render_safety.py +0 -0
  354. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_request_debug.py +0 -0
  355. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_theme_wrapping.py +0 -0
  356. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/utils/test_gitignore.py +0 -0
  357. {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/utils/test_shell_command_escape.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # AGENTS.md
2
- Last Updated: 2026-04-27
2
+ Last Updated: 2026-06-22
3
3
 
4
4
  ## Repository Orientation
5
5
  - This is `tunacode-cli`, a terminal AI coding agent with a Textual UI and tiny-agent tool loop.
@@ -61,7 +61,7 @@ Last Updated: 2026-04-27
61
61
  - Allowed import direction: `ui -> core -> tools`, with shared-layer imports from `utils`, `types`, `configuration`, `constants`, `exceptions`.
62
62
  - Import ordering test: `tests/architecture/test_import_order.py`.
63
63
  - Layer order constant includes shared modules (`configuration, constants, exceptions, prompts, skills, types, utils`) then layered modules (`tools, infrastructure, core, ui`).
64
- - Public-init constraint: `tests/architecture/test_init_bloat.py`.
64
+ - Public-init constraint: `tests/architecture/test_imports_in_init.py`.
65
65
  - Dependency map regeneration pipeline: `scripts/grimp_layers_report.py`, committed to `docs/architecture/dependencies/` by workflow.
66
66
 
67
67
  ## Documentation Sources to Trust
@@ -98,7 +98,7 @@ Last Updated: 2026-04-27
98
98
  - `uv run pytest`
99
99
  - `uv run pytest tests/test_dependency_layers.py -v`
100
100
  - `uv run pytest tests/architecture/test_import_order.py`
101
- - `uv run pytest tests/architecture/test_init_bloat.py`
101
+ - `uv run pytest tests/architecture/test_imports_in_init.py`
102
102
  - `uv run python scripts/run_gates.py`
103
103
  - `uv run python scripts/check_agents_freshness.py`
104
104
  - `uv run python scripts/generate_structure_tree.py`
@@ -4,7 +4,7 @@ summary: Release history for TunaCode versions and notable changes.
4
4
  when_to_read:
5
5
  - When preparing a release note
6
6
  - When reviewing past changes or version history
7
- last_updated: "2026-04-27"
7
+ last_updated: "2026-06-22"
8
8
  ---
9
9
 
10
10
  # Changelog
@@ -16,6 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
16
 
17
17
  ## [Unreleased]
18
18
 
19
+ ## [0.2.2] - 2026-06-22
20
+
21
+ ### Changed
22
+ - Moved thinking panel state management out of the app layer and addressed follow-up review feedback.
23
+ - Bumped `tiny-agent-os` to `1.2.28` and moved max-iteration enforcement to tinyagent's `should_stop_after_turn` hook so capped tool loops end through the normal turn-completion path.
24
+ - Updated the system prompt to tell the agent how to report progress when configured turn limits stop further tool calls.
25
+
19
26
  ## [0.2.1] - 2026-04-27
20
27
 
21
28
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tunacode-cli
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Your agentic CLI developer.
5
5
  Project-URL: Homepage, https://tunacode.xyz/
6
6
  Project-URL: Repository, https://github.com/alchemiststudiosDOTai/tunacode
@@ -32,7 +32,7 @@ Requires-Dist: rich<15.0.0,>=14.2.0
32
32
  Requires-Dist: ruff>=0.14.0
33
33
  Requires-Dist: textual-autocomplete>=4.0.6
34
34
  Requires-Dist: textual<5.0.0,>=4.0.0
35
- Requires-Dist: tiny-agent-os>=1.2.27
35
+ Requires-Dist: tiny-agent-os>=1.2.28
36
36
  Requires-Dist: typer>=0.15.0
37
37
  Provides-Extra: dev
38
38
  Requires-Dist: autoflake>=2.0.0; extra == 'dev'
@@ -8,7 +8,7 @@ when_to_read:
8
8
  - Debugging agent behavior
9
9
  - Modifying the request lifecycle
10
10
  - Changing how session state is persisted
11
- last_updated: "2026-04-04"
11
+ last_updated: "2026-06-21"
12
12
  ---
13
13
 
14
14
  # Core Layer
@@ -28,8 +28,9 @@ The engine. Takes a user message, routes it through a tinyagent `Agent`, handles
28
28
  | `main.py` | `RequestOrchestrator` -- the main request lifecycle. `process_request()` is the public entry point. Handles: history coercion, pre-request compaction, streaming event dispatch, abort cleanup, empty-response intervention, context-overflow retry. |
29
29
  | `helpers.py` | Pure helpers for `main.py`: history coercion/validation, usage parsing, context-overflow detection, tool-result display helpers, and `_TinyAgentStreamState` (per-stream mutable orchestration state). |
30
30
  | `agent_components/__init__.py` | Re-exports from sub-modules. |
31
- | `agent_components/agent_config.py` | `get_or_create_agent()` -- builds or retrieves a cached tinyagent `Agent`. Configures: system prompt, native tool definitions, model, stream function, API key resolver, compaction transform, and skill prompt injection. `invalidate_agent_cache()` clears both module and session caches after abort/timeout. `_build_tools()` constructs the tool list (bash, discover, read_file, hashline_edit, web_fetch, write_file). `_build_skills_prompt_state()` renders active and available skill blocks, and validation helpers include `_coerce_request_delay()`, `_coerce_global_request_timeout()`, `_compute_agent_version()`. |
31
+ | `agent_components/agent_config.py` | `get_or_create_agent()` -- builds or retrieves a cached tinyagent `Agent`. Configures: system prompt, native tool definitions, model, stream function, API key resolver, compaction transform, tinyagent turn-stop control, and skill prompt injection. `invalidate_agent_cache()` clears both module and session caches after abort/timeout. `_build_tools()` constructs the tool list (bash, discover, read_file, hashline_edit, web_fetch, write_file). `_build_skills_prompt_state()` renders active and available skill blocks, and validation helpers include `_coerce_request_delay()`, `_coerce_global_request_timeout()`, `_compute_agent_version()`. |
32
32
  | `agent_components/agent_helpers.py` | Human-readable tool descriptions for UI panels. `create_empty_response_message()` builds the intervention prompt when the model returns nothing. |
33
+ | `agent_components/agent_turn_control.py` | tinyagent host-side turn-control callbacks, including the `settings.max_iterations` `should_stop_after_turn` hook. |
33
34
  | `resume/sanitize.py` | Cleans persisted session messages for safe resume (removes dangling tool calls, fixes structural violations). |
34
35
  | `resume/sanitize_debug.py` | Debug instrumentation for sanitization. |
35
36
 
@@ -165,6 +166,8 @@ Selected skills are treated as active instructions, and their fingerprint is inc
165
166
 
166
167
  **Agent version hashing:** `_compute_agent_version()` generates a cache key from configuration that affects agent behavior: `max_retries`, `tool_strict_validation`, `request_delay`, `global_request_timeout`, `max_tokens`, and the computed skills prompt fingerprint.
167
168
 
169
+ **Turn limit control:** `agent_config.py` wires tinyagent's `should_stop_after_turn` host hook so `settings.max_iterations` ends the tool loop through the normal `TurnEndEvent` -> `AgentEndEvent` path. The stream event handler observes turn-end events but no longer calls `agent.abort()` for the iteration cap.
170
+
168
171
  ## Why
169
172
 
170
173
  The `RequestOrchestrator` class exists to keep the streaming event loop testable and the callback wiring explicit. Each event type has its own handler method -- no giant switch statement.
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
5
5
  [project]
6
6
  name = "tunacode-cli"
7
7
 
8
- version = "0.2.1"
8
+ version = "0.2.2"
9
9
  description = "Your agentic CLI developer."
10
10
  keywords = ["cli", "agent", "development", "automation"]
11
11
  readme = "README.md"
@@ -28,7 +28,7 @@ dependencies = [
28
28
  "typer>=0.15.0",
29
29
  "click>=8.1.0,<8.2.0",
30
30
  "prompt_toolkit>=3.0.52,<4.0.0",
31
- "tiny-agent-os>=1.2.27",
31
+ "tiny-agent-os>=1.2.28",
32
32
  "pygments>=2.19.2,<3.0.0",
33
33
  "rich>=14.2.0,<15.0.0",
34
34
  "textual>=4.0.0,<5.0.0",
@@ -18,7 +18,7 @@ KB = 1024
18
18
  MB = KB * 1024
19
19
 
20
20
  APP_NAME = "TunaCode"
21
- APP_VERSION = "0.2.1"
21
+ APP_VERSION = "0.2.2"
22
22
 
23
23
 
24
24
  AGENTS_MD = "AGENTS.md"
@@ -69,6 +69,7 @@ from .agent_session_config import (
69
69
  _compute_agent_version,
70
70
  _normalize_session_config,
71
71
  )
72
+ from .agent_turn_control import build_should_stop_after_turn as _build_should_stop_after_turn
72
73
 
73
74
  __all__ = [
74
75
  "get_or_create_agent",
@@ -507,6 +508,7 @@ def _build_agent_options(
507
508
  session_id=session.session_id,
508
509
  get_api_key=_build_api_key_resolver(config.env),
509
510
  transform_context=_build_transform_context(state_manager),
511
+ should_stop_after_turn=_build_should_stop_after_turn(session),
510
512
  )
511
513
 
512
514
 
@@ -148,16 +148,10 @@ class AgentStreamMixin:
148
148
  *,
149
149
  agent: Agent,
150
150
  state: _TinyAgentStreamState,
151
- max_iterations: int,
152
151
  baseline_message_count: int,
153
152
  ) -> bool:
154
- _ = (event_obj, baseline_message_count)
155
- state.runtime.iteration_count += 1
156
- state.runtime.current_iteration = state.runtime.iteration_count
157
- if state.runtime.iteration_count <= max_iterations:
158
- return False
159
- agent.abort()
160
- raise RuntimeError(f"Max iterations exceeded ({max_iterations}); aborted")
153
+ _ = (event_obj, agent, state, baseline_message_count)
154
+ return False
161
155
 
162
156
  async def _handle_stream_message_update(
163
157
  self,
@@ -281,7 +275,6 @@ class AgentStreamMixin:
281
275
  event: AgentEvent,
282
276
  agent: Agent,
283
277
  state: _TinyAgentStreamState,
284
- max_iterations: int,
285
278
  baseline_message_count: int,
286
279
  ) -> bool:
287
280
  if is_turn_end_event(event):
@@ -289,7 +282,6 @@ class AgentStreamMixin:
289
282
  event,
290
283
  agent=agent,
291
284
  state=state,
292
- max_iterations=max_iterations,
293
285
  baseline_message_count=baseline_message_count,
294
286
  )
295
287
  if isinstance(event, MessageUpdateEvent):
@@ -340,7 +332,6 @@ class AgentStreamMixin:
340
332
  self,
341
333
  *,
342
334
  agent: Agent,
343
- max_iterations: int,
344
335
  baseline_message_count: int,
345
336
  ) -> Agent:
346
337
  """Run ``agent.stream``; on success, persistence is via AgentEnd; on failure, via cleanup.
@@ -376,7 +367,6 @@ class AgentStreamMixin:
376
367
  event=event,
377
368
  agent=agent,
378
369
  state=state,
379
- max_iterations=max_iterations,
380
370
  baseline_message_count=baseline_message_count,
381
371
  )
382
372
  if should_stop:
@@ -0,0 +1,37 @@
1
+ """tinyagent host-side turn control callbacks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+
7
+ from tinyagent.agent_types import AgentContext, AgentMessage, AssistantMessage, ToolResultMessage
8
+
9
+ from tunacode.core.logging.manager import get_logger
10
+ from tunacode.core.types.state import SessionStateProtocol
11
+
12
+ from .agent_session_config import _coerce_max_iterations
13
+
14
+
15
+ def build_should_stop_after_turn(
16
+ session: SessionStateProtocol,
17
+ ) -> Callable[
18
+ [AssistantMessage, list[ToolResultMessage], AgentContext, list[AgentMessage]],
19
+ bool,
20
+ ]:
21
+ def _should_stop_after_turn(
22
+ message: AssistantMessage,
23
+ tool_results: list[ToolResultMessage],
24
+ context: AgentContext,
25
+ new_messages: list[AgentMessage],
26
+ ) -> bool:
27
+ _ = (message, tool_results, context, new_messages)
28
+ runtime = session.runtime
29
+ runtime.iteration_count += 1
30
+ runtime.current_iteration = runtime.iteration_count
31
+ max_iterations = _coerce_max_iterations(session)
32
+ if runtime.iteration_count <= max_iterations:
33
+ return False
34
+ get_logger().warning(f"Max iterations exceeded ({max_iterations}); ending agent loop")
35
+ return True
36
+
37
+ return _should_stop_after_turn
@@ -33,7 +33,7 @@ from tunacode.core.logging.manager import get_logger
33
33
  from tunacode.core.types.state import StateManagerProtocol
34
34
 
35
35
  from . import agent_components as ac
36
- from .agent_components.agent_config import _coerce_global_request_timeout, _coerce_max_iterations
36
+ from .agent_components.agent_config import _coerce_global_request_timeout
37
37
  from .agent_components.agent_streaming import AgentStreamMixin
38
38
  from .helpers import (
39
39
  CONTEXT_OVERFLOW_FAILURE_NOTICE,
@@ -91,7 +91,7 @@ class RequestOrchestrator(AgentStreamMixin):
91
91
  logger.lifecycle("Agent cache invalidated after timeout")
92
92
 
93
93
  async def _run_impl(self) -> Agent:
94
- max_iterations = self._initialize_request()
94
+ self._initialize_request()
95
95
  logger = get_logger()
96
96
  logger.info("Request started", request_id=self.state_manager.session.runtime.request_id)
97
97
 
@@ -139,17 +139,15 @@ class RequestOrchestrator(AgentStreamMixin):
139
139
 
140
140
  await self._run_stream(
141
141
  agent=agent,
142
- max_iterations=max_iterations,
143
142
  baseline_message_count=baseline_message_count,
144
143
  )
145
144
  await self._retry_after_context_overflow_if_needed(
146
145
  agent=agent,
147
- max_iterations=max_iterations,
148
146
  pre_request_history=pre_request_history,
149
147
  )
150
148
  return agent
151
149
 
152
- def _initialize_request(self) -> int:
150
+ def _initialize_request(self) -> None:
153
151
  request_id = str(uuid.uuid4())[:REQUEST_ID_LENGTH]
154
152
  session = self.state_manager.session
155
153
  runtime = session.runtime
@@ -160,7 +158,6 @@ class RequestOrchestrator(AgentStreamMixin):
160
158
  session.usage.last_call_usage = UsageMetrics()
161
159
  if not session.task.original_query:
162
160
  session.task.original_query = self.message
163
- return _coerce_max_iterations(session)
164
161
 
165
162
  def _maybe_emit_compaction_notice(self, outcome: CompactionOutcome) -> None:
166
163
  if self.notice_callback is None:
@@ -193,7 +190,6 @@ class RequestOrchestrator(AgentStreamMixin):
193
190
  self,
194
191
  *,
195
192
  agent: Agent,
196
- max_iterations: int,
197
193
  pre_request_history: list[AgentMessage],
198
194
  ) -> None:
199
195
  error_text = self._agent_error_text(agent)
@@ -212,7 +208,6 @@ class RequestOrchestrator(AgentStreamMixin):
212
208
  self.state_manager.session._debug_raw_stream_accum = ""
213
209
  await self._run_stream(
214
210
  agent=agent,
215
- max_iterations=max_iterations,
216
211
  baseline_message_count=len(conversation.messages),
217
212
  )
218
213
 
@@ -331,4 +331,3 @@ class ToolRetryError(TunaCodeError):
331
331
  def __init__(self, message: str, original_error: OriginalError = None):
332
332
  super().__init__(message)
333
333
  self.original_error = original_error
334
-
@@ -49,6 +49,7 @@ You MUST reuse paths exactly as returned by discover.
49
49
  - If more than 3 independent read-only calls are needed, queue them and launch the next call only after one finishes.
50
50
  - Do not batch hashline_edit or write_file calls. Execute file mutations one at a time in deterministic order.
51
51
  - Execute tool calls immediately. Do not narrate them.
52
+ - Respect configured turn limits. If the host stops further tool calls after a turn, report completed work, remaining work, and the next required step.
52
53
 
53
54
  ###Output###
54
55
  - No emojis.
@@ -65,6 +65,8 @@ from tunacode.ui.slopgotchi import (
65
65
  )
66
66
  from tunacode.ui.streaming import StreamingHandler
67
67
  from tunacode.ui.styles import STYLE_PRIMARY, STYLE_SUCCESS, STYLE_WARNING
68
+ from tunacode.ui.thinking_state import ThinkingState
69
+
68
70
  from tunacode.ui.widgets import (
69
71
  ChatContainer,
70
72
  CommandAutoComplete,
@@ -149,10 +151,9 @@ class TextualReplApp(App[None]):
149
151
  self._slopgotchi_timer: Timer | None = None
150
152
  self._request_bridge: RequestUiBridge | None = None
151
153
  self._delta_flush_timer: Timer | None = None
152
-
153
- self._current_thinking_text: str = ""
154
- self._last_thinking_update: float = 0.0
155
154
  self._last_editor_keypress_at: float = 0.0
155
+
156
+ self._thinking_state = ThinkingState(self)
156
157
  self._request_debug = RequestDebugTracer(self)
157
158
 
158
159
  def compose(self) -> ComposeResult:
@@ -298,7 +299,7 @@ class TextualReplApp(App[None]):
298
299
  thinking_callback_ms = 0.0
299
300
  if thinking_batch.has_data:
300
301
  thinking_started_at = time.monotonic()
301
- await self._thinking_callback(thinking_batch.text)
302
+ await self._thinking_state.callback(thinking_batch.text)
302
303
  thinking_callback_ms = (
303
304
  time.monotonic() - thinking_started_at
304
305
  ) * self.MILLISECONDS_PER_SECOND
@@ -326,7 +327,7 @@ class TextualReplApp(App[None]):
326
327
  if not self._loading_indicator_shown:
327
328
  self._request_debug.loading_shown(reason="request_start")
328
329
  self._show_loading_indicator()
329
- self._clear_thinking_state()
330
+ self._thinking_state.clear()
330
331
  bridge = RequestUiBridge(self)
331
332
  self._request_bridge = bridge
332
333
  self._start_delta_flush_timer()
@@ -398,7 +399,7 @@ class TextualReplApp(App[None]):
398
399
  self._hide_loading_indicator()
399
400
  self.query_one("#viewport").remove_class(RICHLOG_CLASS_STREAMING)
400
401
  self.streaming.reset()
401
- self._finalize_thinking_state_after_request()
402
+ self._thinking_state.finalize()
402
403
  self._update_compaction_status(False)
403
404
  response_panel_ms = 0.0
404
405
  response_char_count = 0
@@ -765,28 +766,3 @@ class TextualReplApp(App[None]):
765
766
  )
766
767
  if self._context_panel_visible:
767
768
  self._refresh_context_panel()
768
-
769
- def _hide_thinking_output(self) -> None:
770
- from tunacode.ui.thinking_state import hide_thinking_output
771
-
772
- hide_thinking_output(self)
773
-
774
- def _clear_thinking_state(self) -> None:
775
- from tunacode.ui.thinking_state import clear_thinking_state
776
-
777
- clear_thinking_state(self)
778
-
779
- def _finalize_thinking_state_after_request(self) -> None:
780
- from tunacode.ui.thinking_state import finalize_thinking_state_after_request
781
-
782
- finalize_thinking_state_after_request(self)
783
-
784
- def _refresh_thinking_output(self, force: bool = False) -> None:
785
- from tunacode.ui.thinking_state import refresh_thinking_output
786
-
787
- refresh_thinking_output(self, force)
788
-
789
- async def _thinking_callback(self, delta: str) -> None:
790
- from tunacode.ui.thinking_state import thinking_callback
791
-
792
- await thinking_callback(self, delta)
@@ -22,9 +22,9 @@ class ThoughtsCommand(Command):
22
22
  session.show_thoughts = not session.show_thoughts
23
23
 
24
24
  if session.show_thoughts:
25
- app._refresh_thinking_output(force=True)
25
+ app._thinking_state.refresh(force=True)
26
26
  app.notify("Thought panel: ON")
27
27
  return
28
28
 
29
- app._hide_thinking_output()
29
+ app._thinking_state.hide()
30
30
  app.notify("Thought panel: OFF")
@@ -0,0 +1,138 @@
1
+ """Thinking-panel state management for TunaCode TUI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from textual.widgets import Static
10
+
11
+ from tunacode.ui.app import TextualReplApp
12
+
13
+
14
+ THINKING_PANEL_HORIZONTAL_MARGIN = 2
15
+
16
+
17
+ class ThinkingState:
18
+ """Encapsulates live thinking-panel state and rendering logic."""
19
+
20
+ def __init__(self, app: TextualReplApp) -> None:
21
+ self._app = app
22
+ self._text: str = ""
23
+ self._last_update: float = 0.0
24
+
25
+ @property
26
+ def _widget(self) -> Static | None:
27
+ widget = self._app._thinking_panel_widget
28
+ if widget is None:
29
+ return None
30
+ return widget
31
+
32
+ def _editor_has_draft(self) -> bool:
33
+ return bool(self._app.editor.value.strip())
34
+
35
+ def _has_recent_editor_keypress(self) -> bool:
36
+ if not self._editor_has_draft():
37
+ return False
38
+ last_keypress_at = self._app._last_editor_keypress_at
39
+ if last_keypress_at <= 0.0:
40
+ return False
41
+ elapsed_ms = (time.monotonic() - last_keypress_at) * self._app.MILLISECONDS_PER_SECOND
42
+ return elapsed_ms < self._app.THINKING_DEFER_AFTER_KEYPRESS_MS
43
+
44
+ def _throttle_ms(self) -> float:
45
+ if self._editor_has_draft():
46
+ return self._app.THINKING_THROTTLE_WHILE_DRAFTING_MS
47
+ return self._app.THINKING_THROTTLE_MS
48
+
49
+ def hide(self) -> None:
50
+ """Hide the live thinking panel without removing the widget."""
51
+ widget = self._widget
52
+ if widget is None:
53
+ return
54
+ widget.update("")
55
+ widget.remove_class("active")
56
+
57
+ def clear(self) -> None:
58
+ """Reset thinking buffer and hide the widget."""
59
+ self._text = ""
60
+ self._last_update = 0.0
61
+ self.hide()
62
+
63
+ def refresh(self, *, force: bool = False) -> None:
64
+ """Throttled render of the live thinking panel."""
65
+ if not self._app.state_manager.session.show_thoughts:
66
+ return
67
+ if not self._text:
68
+ self.hide()
69
+ return
70
+
71
+ widget = self._widget
72
+ if widget is None:
73
+ return
74
+
75
+ if not force and self._has_recent_editor_keypress():
76
+ return
77
+
78
+ now = time.monotonic()
79
+ elapsed_ms = (now - self._last_update) * self._app.MILLISECONDS_PER_SECOND
80
+ if not force and elapsed_ms < self._throttle_ms():
81
+ return
82
+
83
+ from tunacode.ui.renderers.thinking import render_thinking_panel
84
+
85
+ self._last_update = now
86
+ content, meta = render_thinking_panel(
87
+ self._text,
88
+ max_lines=self._app.THINKING_MAX_RENDER_LINES,
89
+ max_chars=self._app.THINKING_MAX_RENDER_CHARS,
90
+ )
91
+ widget.border_title = meta.border_title
92
+ widget.border_subtitle = meta.border_subtitle
93
+ widget.update(content)
94
+ widget.add_class("active")
95
+
96
+ def _final_panel_width(self) -> int:
97
+ width_candidates = [
98
+ self._app.chat_container.content_region.width,
99
+ self._app.chat_container.size.width,
100
+ self._app.size.width,
101
+ ]
102
+ content_width = next((width for width in width_candidates if width > 0), 1)
103
+ return max(1, content_width - THINKING_PANEL_HORIZONTAL_MARGIN)
104
+
105
+ def finalize(self) -> None:
106
+ """After a request completes, persist a final thinking panel into chat history."""
107
+ if not self._app.state_manager.session.show_thoughts:
108
+ self.clear()
109
+ return
110
+ if not self._text:
111
+ self.hide()
112
+ return
113
+
114
+ from tunacode.ui.renderers.thinking import render_thinking_panel
115
+
116
+ content, meta = render_thinking_panel(
117
+ self._text,
118
+ max_lines=self._app.THINKING_MAX_RENDER_LINES,
119
+ max_chars=self._app.THINKING_MAX_RENDER_CHARS,
120
+ )
121
+ self._app.chat_container.write(
122
+ content,
123
+ width=self._final_panel_width(),
124
+ panel_meta=meta,
125
+ )
126
+ self.clear()
127
+
128
+ async def callback(self, delta: str) -> None:
129
+ """Accumulate thinking text and refresh the panel."""
130
+ self._text += delta
131
+ overflow = len(self._text) - self._app.THINKING_BUFFER_CHAR_LIMIT
132
+ if overflow > 0:
133
+ self._text = self._text[overflow:]
134
+
135
+ if not self._app.state_manager.session.show_thoughts:
136
+ return
137
+
138
+ self.refresh()
@@ -276,6 +276,7 @@ class ChatContainer(VerticalScroll):
276
276
  renderable: RenderableType | tuple[RenderableType, PanelMeta],
277
277
  *,
278
278
  expand: bool = False,
279
+ width: int | None = None,
279
280
  panel_meta: PanelMeta | None = None,
280
281
  ) -> CopyOnSelectStatic:
281
282
  """Append a renderable to the chat container.
@@ -285,6 +286,7 @@ class ChatContainer(VerticalScroll):
285
286
  Args:
286
287
  renderable: Rich renderable or ``(RenderableType, PanelMeta)`` tuple.
287
288
  expand: If True, widget expands to fill available width.
289
+ width: Optional explicit widget width in cells.
288
290
  panel_meta: Optional panel metadata for CSS-styled borders.
289
291
 
290
292
  Returns:
@@ -294,7 +296,9 @@ class ChatContainer(VerticalScroll):
294
296
 
295
297
  widget = CopyOnSelectStatic(panel_content)
296
298
  widget.add_class("chat-message")
297
- if expand:
299
+ if width is not None:
300
+ widget.styles.width = max(1, width)
301
+ elif expand:
298
302
  widget.add_class("expand")
299
303
 
300
304
  if panel_meta is not None:
@@ -4,14 +4,17 @@ import asyncio
4
4
 
5
5
  import pytest
6
6
  from tinyagent.agent_types import (
7
+ AgentContext,
7
8
  AgentTool,
8
9
  AgentToolResult,
9
10
  AgentToolUpdateCallback,
11
+ AssistantMessage,
10
12
  JsonObject,
11
13
  TextContent,
12
14
  )
13
15
 
14
16
  from tunacode.core.agents.agent_components import agent_config
17
+ from tunacode.core.session import StateManager
15
18
 
16
19
 
17
20
  async def _wait_for_entered_count(entered_order: list[str], expected: int) -> None:
@@ -70,3 +73,18 @@ async def test_apply_tool_concurrency_limit_caps_in_flight_and_preserves_queue_o
70
73
  def test_apply_tool_concurrency_limit_rejects_non_positive_max_parallel() -> None:
71
74
  with pytest.raises(ValueError, match="max_parallel_tool_calls must be >= 1"):
72
75
  agent_config._apply_tool_concurrency_limit([], max_parallel_tool_calls=0)
76
+
77
+
78
+ def test_should_stop_after_turn_updates_runtime_and_stops_after_limit() -> None:
79
+ state_manager = StateManager()
80
+ state_manager.session.user_config["settings"]["max_iterations"] = 1
81
+ should_stop = agent_config._build_should_stop_after_turn(state_manager.session)
82
+ message = AssistantMessage(content=[TextContent(text="done")])
83
+
84
+ assert should_stop(message, [], AgentContext(), []) is False
85
+ assert state_manager.session.runtime.iteration_count == 1
86
+ assert state_manager.session.runtime.current_iteration == 1
87
+
88
+ assert should_stop(message, [], AgentContext(), []) is True
89
+ assert state_manager.session.runtime.iteration_count == 2
90
+ assert state_manager.session.runtime.current_iteration == 2
@@ -52,6 +52,22 @@ class _FakeBridgeApp:
52
52
  return True
53
53
 
54
54
 
55
+ class _FakeThinkingState:
56
+ def __init__(self) -> None:
57
+ self.chunks: list[str] = []
58
+ self.cleared = False
59
+ self.finalized = False
60
+
61
+ async def callback(self, chunk: str) -> None:
62
+ self.chunks.append(chunk)
63
+
64
+ def clear(self) -> None:
65
+ self.cleared = True
66
+
67
+ def finalize(self) -> None:
68
+ self.finalized = True
69
+
70
+
55
71
  class _FakeToolCallbackApp:
56
72
  def __init__(self) -> None:
57
73
  self.messages: list[object] = []
@@ -177,12 +193,8 @@ async def test_flush_timer_applies_queued_deltas_to_streaming_handler() -> None:
177
193
  app = TextualReplApp(state_manager=StateManager())
178
194
  app._request_bridge = RequestUiBridge(_FakeBridgeApp())
179
195
  app.streaming = _FakeStreamingHandler() # type: ignore[assignment]
180
- thinking_chunks: list[str] = []
181
-
182
- async def _fake_thinking_callback(chunk: str) -> None:
183
- thinking_chunks.append(chunk)
184
-
185
- app._thinking_callback = _fake_thinking_callback # type: ignore[method-assign]
196
+ thinking_state = _FakeThinkingState()
197
+ app._thinking_state = thinking_state # type: ignore[assignment]
186
198
 
187
199
  await app._request_bridge.streaming_callback("hello")
188
200
  await app._request_bridge.streaming_callback(" world")
@@ -191,7 +203,7 @@ async def test_flush_timer_applies_queued_deltas_to_streaming_handler() -> None:
191
203
  await app._flush_request_deltas()
192
204
 
193
205
  assert app.streaming.chunks == ["hello world"]
194
- assert thinking_chunks == ["trace data"]
206
+ assert thinking_state.chunks == ["trace data"]
195
207
 
196
208
 
197
209
  def test_escape_handler_cancels_worker_handle() -> None:
@@ -226,8 +238,7 @@ async def test_process_request_runs_in_worker_and_sets_current_request_handle()
226
238
  app.set_interval = lambda *_args, **_kwargs: timer # type: ignore[method-assign]
227
239
  app._show_loading_indicator = lambda: None # type: ignore[method-assign]
228
240
  app._hide_loading_indicator = lambda: None # type: ignore[method-assign]
229
- app._clear_thinking_state = lambda: None # type: ignore[method-assign]
230
- app._finalize_thinking_state_after_request = lambda: None # type: ignore[method-assign]
241
+ app._thinking_state = _FakeThinkingState() # type: ignore[assignment]
231
242
  app._update_resource_bar = lambda: None # type: ignore[method-assign]
232
243
  app._get_latest_response_text = lambda: None # type: ignore[method-assign]
233
244
  app._update_compaction_status = compaction_updates.append # type: ignore[method-assign]