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.
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/AGENTS.md +3 -3
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/CHANGELOG.md +8 -1
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/PKG-INFO +2 -2
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/core/core.md +5 -2
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/pyproject.toml +2 -2
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/constants.py +1 -1
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_config.py +2 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_streaming.py +2 -12
- tunacode_cli-0.2.2/src/tunacode/core/agents/agent_components/agent_turn_control.py +37 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/main.py +3 -8
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/exceptions.py +0 -1
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/prompts/system_prompt.md +1 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/app.py +7 -31
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/thoughts.py +2 -2
- tunacode_cli-0.2.2/src/tunacode/ui/thinking_state.py +138 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/chat.py +5 -1
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_tool_concurrency_limit.py +18 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_request_threading.py +20 -9
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_thinking_state.py +17 -16
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/uv.lock +6 -6
- tunacode_cli-0.2.1/src/tunacode/ui/thinking_state.py +0 -123
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.coderabbit.yaml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.githooks/pre-commit +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.githooks/pre-push +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/ISSUE_TEMPLATE/question.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/dependabot.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/pull_request_template.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/dependency-map.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/empty-dir-check.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/lint.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/publish-release.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/release.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.github/workflows/tech-debt.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.gitignore +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/.pre-commit-config.yaml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/HARNESS.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/LICENSE +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/MANIFEST.in +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/Makefile +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/README.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/assets/hashline-edit.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/assets/home.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/CODE-STANDARDS.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/README.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/dependencies/DEPENDENCY_LAYERS.dot +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/dependencies/DEPENDENCY_LAYERS.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/dependencies/DEPENDENCY_LAYERS.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/architecture/patterns/agent-message-serialization.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/codebase-map/structure/tree-structure.txt +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/deployment.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/git/changelog-update.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/git/practices.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-home-catpu.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-home.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-lsp.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/dream-ui/tunacode-cli-response.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/tool-calls.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/images/tunacode.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/configuration/configuration.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/configuration/models-registry.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/index.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/infrastructure/infrastructure.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/skills/skills.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/tools/hashline-subsystem.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/tools/tools-system.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/tools/tools.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/types/types.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/ui/commands.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/ui/ui.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/modules/utils/utils.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/reviews/2026-03-24-input-latency-research-artifact.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/reviews/2026-03-25-provider-stream-stall-artifact.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/reviews/pr-429-coderabbit-review.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/skills/audit-harness/SKILL.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/ui/css-architecture.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/DEBUG.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/DOCS.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/DREAM.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/FEATURES.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/HARNESS.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/HOTFIX.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/QA.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/docs/workflows/README.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/package-lock.json +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/pytest.ini +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/baseline/no-getattr-in-src.json +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/rule-tests/__snapshots__/no-getattr-in-src-snapshot.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/rule-tests/no-getattr-in-src-test.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/rules/no-getattr-in-src.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/rules/ast-grep/sgconfig.yml +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/benchmarks/input_latency.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/benchmarks/input_paint_latency.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/benchmarks/input_tmux_latency.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-defensive-slop.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-file-length.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-naming-conventions.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check-unused-constants.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_agents_freshness.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_ast_grep_baseline.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_markdown_frontmatter.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_stale_symbol_surfaces.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/check_unused_modules.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/dev-setup.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/download_ripgrep.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/generate-release-notes.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/generate_structure_tree.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/grimp_layers_report.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/install-git-hooks.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/install_linux.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/playwright_cache.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/preview_tool_panels.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run-dead-imports.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run-pre-commit-hook.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run_ast_grep_checks.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/run_gates.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/stale_symbol_surfaces_analysis.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/stale_symbol_surfaces_models.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/stale_symbol_surfaces_visitors.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/startup_timer.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/todo_baseline.json +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/todo_scanner.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/ui_import_timer.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/uninstall.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/update_models_registry.sh +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/utils/check_empty_dirs.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/utils/vulture_whitelist.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/scripts/validate_ast_grep_rules.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/SKILL.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/references/common-issues.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/references/workflow-structure.md +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/scripts/bump_version.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/scripts/local_publish.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/skills/pypi-release/scripts/release.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/defaults.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/ignore_patterns.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/limits.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/models.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/models_registry.json +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/paths.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/pricing.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/settings.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/configuration/user_config.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_helpers.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_session_config.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/helpers.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/resume/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/resume/sanitize.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/resume/sanitize_debug.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/controller.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/prompts.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/summarizer.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/compaction/types.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/debug/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/debug/usage_trace.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/handlers.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/levels.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/manager.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/logging/records.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/session/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/session/state.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/agent_state.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/state.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/types/state_structures.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/ui_api/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/ui_api/file_filter.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/ui_api/formatting.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/agents.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/limits_settings.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/models_registry.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/skills.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/caches/tunacode_context.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/manager.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/metadata.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/cache/strategies.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/infrastructure/file_filter.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/py.typed +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/discovery.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/loader.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/models.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/prompting.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/registry.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/search.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/skills/selection.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/bash.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/cache_accessors/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/cache_accessors/ignore_manager_cache.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/cache_accessors/ripgrep_cache.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/discover.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/hashline.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/hashline_edit.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/ignore.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/ignore_manager.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/line_cache.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/read_file.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/discover_pipeline.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/discover_terms.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/discover_types.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/file_errors.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/formatting.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/utils/ripgrep.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/web_fetch.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/tools/write_file.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/base.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/callbacks.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/dataclasses.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/types/models_registry.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/assets/logo.ansi +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/clipboard.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/command_registry.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/base.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/cancel.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/clear.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/compact.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/debug.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/exit.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/help.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/model.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/resume.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/skills.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/theme.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/commands/update.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/context_panel.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/esc/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/esc/handler.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/esc/types.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/lifecycle.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/logo_assets.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/main.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/model_display.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/render_safety.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/agent_response.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/errors.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/panel_widths.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/panels.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/search.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/thinking.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/base.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/bash.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/discover.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/hashline_edit.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/read_file.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/syntax_utils.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/web_fetch.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/renderers/tools/write_file.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/repl_support.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/request_bridge.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/request_debug.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/api_key_entry.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/model_picker.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/session_picker.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/setup.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/theme_picker.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/screens/update_confirm.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/shell_runner.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/slopgotchi/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/slopgotchi/panel.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/streaming.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/layout.tcss +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/modals.tcss +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/panels.tcss +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles/widgets.tcss +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/styles.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/welcome.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/autocomplete_positioning.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/command_autocomplete.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/editor.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/file_autocomplete.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/messages.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/resource_bar.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/ui/widgets/skills_autocomplete.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/messaging/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/messaging/adapter.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/messaging/token_counter.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/system/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/utils/system/gitignore.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tc-cvurren.png +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/__init__.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_import_order.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_imports_in_init.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_layer_dependencies.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/architecture/test_ui_api_surface.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/conftest.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/core/test_minimax_execution_path.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/core/test_mtime_caches_end_to_end.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/core/test_tinyagent_alchemy_usage_contract_live.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/ui/test_submit_loading_lifecycle.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/integration/ui/test_theme_render_crash_regression.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_cli_default_command.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_repl_support.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_startup.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/system/cli/test_tmux_tools.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/test_compaction.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/test_dependency_layers.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_default_config_minimax_keys.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_default_config_stream_agent_text.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_ignore_patterns.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_models_registry_loading_behavior.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_models_registry_minimax_contract.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_pricing_registry_loading.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_provider_api_key_env_fallback.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/configuration/test_user_config_loading.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_cache_abort.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_init_debug.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_skills_prompt_injection.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_agent_stream_provider_debug.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_compaction_controller_outcomes.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_compaction_summarizer.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_context_overflow_detection.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_exceptions.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_logging.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_provider_error_surfacing.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_session_state_defaults.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_session_state_model_registry_loading.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_session_usage_schema.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_thinking_stream_routing.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/core/test_tinyagent_openrouter_model_config.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_cache_infrastructure.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_file_filter.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_migrated_lru_cache_replacements.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/infrastructure/test_typed_cache_accessors.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/skills/test_registry.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/skills/test_selection.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/test_stale_symbol_surfaces.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/tools/test_ignore_manager.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_app_loading_indicator.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_context_panel.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_render_safety.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_request_debug.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/ui/test_theme_wrapping.py +0 -0
- {tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/tests/unit/utils/test_gitignore.py +0 -0
- {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-
|
|
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/
|
|
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/
|
|
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-
|
|
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.
|
|
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.
|
|
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-
|
|
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.
|
|
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.
|
|
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",
|
{tunacode_cli-0.2.1 → tunacode_cli-0.2.2}/src/tunacode/core/agents/agent_components/agent_config.py
RENAMED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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) ->
|
|
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
|
|
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
25
|
+
app._thinking_state.refresh(force=True)
|
|
26
26
|
app.notify("Thought panel: ON")
|
|
27
27
|
return
|
|
28
28
|
|
|
29
|
-
app.
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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]
|