tau-coding-agent 0.2.0__tar.gz → 0.3.1__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 (315) hide show
  1. {tau_coding_agent-0.2.0/tau_coding_agent.egg-info → tau_coding_agent-0.3.1}/PKG-INFO +3 -2
  2. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/architecture.md +1 -1
  3. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/extensions.md +3 -2
  4. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/sessions.md +16 -4
  5. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/themes.md +32 -1
  6. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/pyproject.toml +4 -3
  7. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/agent/service.py +143 -42
  8. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/auth/manager.py +82 -0
  9. tau_coding_agent-0.3.1/tau/builtins/themes/dark.yaml +50 -0
  10. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/themes/light.yaml +3 -0
  11. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/console/cli.py +3 -5
  12. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/engine/service.py +2 -1
  13. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/context.py +13 -0
  14. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/service.py +55 -8
  15. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/types.py +3 -0
  16. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/message/types.py +38 -4
  17. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/session/manager.py +8 -0
  18. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/themes/loader.py +78 -8
  19. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/agent_hooks.py +18 -5
  20. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/app.py +1 -0
  21. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/autocomplete_picker.py +3 -4
  22. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/command_palette.py +3 -4
  23. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/file_picker.py +3 -4
  24. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/layout.py +26 -6
  25. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/message_list.py +15 -9
  26. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/spinner.py +47 -11
  27. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/text_input.py +209 -52
  28. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/input.py +164 -25
  29. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/input_handler.py +138 -23
  30. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/keybindings.py +6 -3
  31. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/markdown.py +59 -4
  32. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/overlay.py +1 -0
  33. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/theme.py +3 -0
  34. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/tui.py +6 -10
  35. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1/tau_coding_agent.egg-info}/PKG-INFO +3 -2
  36. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau_coding_agent.egg-info/requires.txt +1 -0
  37. tau_coding_agent-0.2.0/tau/builtins/themes/dark.yaml +0 -1
  38. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/LICENSE +0 -0
  39. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/MANIFEST.in +0 -0
  40. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/README.md +0 -0
  41. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/IMPLEMENTATION_SUMMARY.md +0 -0
  42. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/auth.md +0 -0
  43. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/cli-reference.md +0 -0
  44. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/development.md +0 -0
  45. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/docs.json +0 -0
  46. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/extension-settings.md +0 -0
  47. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/http-proxy.md +0 -0
  48. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/index.md +0 -0
  49. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/inference-providers.md +0 -0
  50. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/installation.md +0 -0
  51. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/keybindings.md +0 -0
  52. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/messages.md +0 -0
  53. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/project-context.md +0 -0
  54. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/project-structure.md +0 -0
  55. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/prompts.md +0 -0
  56. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/python-api.md +0 -0
  57. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/quickstart.md +0 -0
  58. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/settings.md +0 -0
  59. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/skills.md +0 -0
  60. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/tools.md +0 -0
  61. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/docs/usage.md +0 -0
  62. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/setup.cfg +0 -0
  63. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/__init__.py +0 -0
  64. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/agent/__init__.py +0 -0
  65. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/agent/prompt/__init__.py +0 -0
  66. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/agent/prompt/builder.py +0 -0
  67. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/agent/prompt/types.py +0 -0
  68. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/agent/types.py +0 -0
  69. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/auth/storage.py +0 -0
  70. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/auth/types.py +0 -0
  71. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/__init__.py +0 -0
  72. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/__pycache__/__init__.cpython-313.pyc +0 -0
  73. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/__pycache__/__init__.cpython-314.pyc +0 -0
  74. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__init__.py +0 -0
  75. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/__init__.cpython-313.pyc +0 -0
  76. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/__init__.cpython-314.pyc +0 -0
  77. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/clear.cpython-313.pyc +0 -0
  78. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/clear.cpython-314.pyc +0 -0
  79. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/compact.cpython-313.pyc +0 -0
  80. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/compact.cpython-314.pyc +0 -0
  81. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/reload.cpython-313.pyc +0 -0
  82. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/reload.cpython-314.pyc +0 -0
  83. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/session.cpython-313.pyc +0 -0
  84. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/__pycache__/session.cpython-314.pyc +0 -0
  85. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/clear.py +0 -0
  86. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/compact.py +0 -0
  87. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/reload.py +0 -0
  88. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/commands/session.py +0 -0
  89. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/__init__.py +0 -0
  90. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/__pycache__/__init__.cpython-313.pyc +0 -0
  91. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/__pycache__/git.cpython-313.pyc +0 -0
  92. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/__pycache__/model.cpython-313.pyc +0 -0
  93. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/__pycache__/utils.cpython-313.pyc +0 -0
  94. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/git.py +0 -0
  95. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/model.py +0 -0
  96. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/footer/utils.py +0 -0
  97. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/header/__init__.py +0 -0
  98. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/extensions/header/__pycache__/__init__.cpython-313.pyc +0 -0
  99. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/__init__.py +0 -0
  100. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/__pycache__/__init__.cpython-313.pyc +0 -0
  101. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/__pycache__/text.cpython-313.pyc +0 -0
  102. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/audio.py +0 -0
  103. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/image.py +0 -0
  104. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/text.py +0 -0
  105. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/models/video.py +0 -0
  106. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/commit.md +0 -0
  107. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/docs.md +0 -0
  108. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/explain.md +0 -0
  109. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/fix.md +0 -0
  110. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/refactor.md +0 -0
  111. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/review.md +0 -0
  112. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/prompts/test.md +0 -0
  113. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/__init__.py +0 -0
  114. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  115. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/__pycache__/text.cpython-313.pyc +0 -0
  116. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/audio.py +0 -0
  117. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/image.py +0 -0
  118. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/text.py +0 -0
  119. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/providers/video.py +0 -0
  120. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/skills/code-review/SKILL.md +0 -0
  121. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/skills/debug/SKILL.md +0 -0
  122. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/skills/git-commit/SKILL.md +0 -0
  123. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__init__.py +0 -0
  124. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  125. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  126. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/bash.cpython-313.pyc +0 -0
  127. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/bash.cpython-314.pyc +0 -0
  128. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/edit.cpython-313.pyc +0 -0
  129. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/edit.cpython-314.pyc +0 -0
  130. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/glob.cpython-313.pyc +0 -0
  131. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/glob.cpython-314.pyc +0 -0
  132. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/grep.cpython-313.pyc +0 -0
  133. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/grep.cpython-314.pyc +0 -0
  134. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/ls.cpython-313.pyc +0 -0
  135. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/ls.cpython-314.pyc +0 -0
  136. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/read.cpython-313.pyc +0 -0
  137. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/read.cpython-314.pyc +0 -0
  138. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/terminal.cpython-313.pyc +0 -0
  139. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/terminal.cpython-314.pyc +0 -0
  140. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/write.cpython-313.pyc +0 -0
  141. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/__pycache__/write.cpython-314.pyc +0 -0
  142. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/edit.py +0 -0
  143. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/glob.py +0 -0
  144. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/grep.py +0 -0
  145. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/ls.py +0 -0
  146. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/read.py +0 -0
  147. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/terminal.py +0 -0
  148. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/builtins/tools/write.py +0 -0
  149. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/commands/__init__.py +0 -0
  150. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/commands/registry.py +0 -0
  151. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/commands/types.py +0 -0
  152. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/console/__init__.py +0 -0
  153. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/console/commands/__init__.py +0 -0
  154. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/console/commands/auth.py +0 -0
  155. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/console/commands/packages.py +0 -0
  156. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/console/commands/update.py +0 -0
  157. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/core/__init__.py +0 -0
  158. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/core/registry.py +0 -0
  159. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/engine/__init__.py +0 -0
  160. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/engine/types.py +0 -0
  161. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/__init__.py +0 -0
  162. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/api.py +0 -0
  163. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/events.py +0 -0
  164. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/loader.py +0 -0
  165. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/runtime.py +0 -0
  166. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/extensions/settings.py +0 -0
  167. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/__init__.py +0 -0
  168. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/engine.py +0 -0
  169. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/inference.py +0 -0
  170. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/runtime.py +0 -0
  171. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/service.py +0 -0
  172. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/session.py +0 -0
  173. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/tui.py +0 -0
  174. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/hooks/types.py +0 -0
  175. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/__init__.py +0 -0
  176. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/__init__.py +0 -0
  177. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/__init__.py +0 -0
  178. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/base.py +0 -0
  179. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/builtins.py +0 -0
  180. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/elevenlabs_audio.py +0 -0
  181. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/gemini_audio.py +0 -0
  182. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/openai_audio.py +0 -0
  183. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/registry.py +0 -0
  184. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/sarvam_audio.py +0 -0
  185. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/service.py +0 -0
  186. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/audio/utils.py +0 -0
  187. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/__init__.py +0 -0
  188. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/base.py +0 -0
  189. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/builtins.py +0 -0
  190. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/gemini_image.py +0 -0
  191. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/openai_image.py +0 -0
  192. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/openrouter.py +0 -0
  193. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/registry.py +0 -0
  194. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/image/service.py +0 -0
  195. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/registry.py +0 -0
  196. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/__init__.py +0 -0
  197. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/anthropic_claude_code.py +0 -0
  198. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/anthropic_messages.py +0 -0
  199. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/base.py +0 -0
  200. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/builtins.py +0 -0
  201. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/gemini_generate.py +0 -0
  202. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/github_copilot_chat.py +0 -0
  203. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/google_antigravity.py +0 -0
  204. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/mistral_chat.py +0 -0
  205. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/ollama_chat.py +0 -0
  206. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/openai_codex_responses.py +0 -0
  207. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/openai_completions.py +0 -0
  208. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/openai_responses.py +0 -0
  209. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/registry.py +0 -0
  210. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/types.py +0 -0
  211. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/text/utils.py +0 -0
  212. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/__init__.py +0 -0
  213. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/base.py +0 -0
  214. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/builtins.py +0 -0
  215. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/fal_video.py +0 -0
  216. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/openrouter_video.py +0 -0
  217. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/registry.py +0 -0
  218. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/api/video/service.py +0 -0
  219. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/model/__init__.py +0 -0
  220. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/model/registry.py +0 -0
  221. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/model/types.py +0 -0
  222. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/__init__.py +0 -0
  223. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/__init__.py +0 -0
  224. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/anthropic_claude_code.py +0 -0
  225. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/github_copilot.py +0 -0
  226. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/google_antigravity.py +0 -0
  227. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/openai_codex.py +0 -0
  228. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/pkce.py +0 -0
  229. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/types.py +0 -0
  230. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/oauth/utils.py +0 -0
  231. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/registry.py +0 -0
  232. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/provider/types.py +0 -0
  233. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/inference/utils.py +0 -0
  234. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/message/__init__.py +0 -0
  235. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/message/utils.py +0 -0
  236. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/packages/__init__.py +0 -0
  237. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/packages/manager.py +0 -0
  238. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/packages/types.py +0 -0
  239. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/packages/utils.py +0 -0
  240. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/prompts/expand.py +0 -0
  241. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/prompts/loader.py +0 -0
  242. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/prompts/registry.py +0 -0
  243. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/prompts/types.py +0 -0
  244. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/rpc/__init__.py +0 -0
  245. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/rpc/mode.py +0 -0
  246. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/rpc/types.py +0 -0
  247. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/runtime/service.py +0 -0
  248. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/runtime/types.py +0 -0
  249. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/session/branch_summarization.py +0 -0
  250. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/session/compaction.py +0 -0
  251. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/session/types.py +0 -0
  252. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/session/utils.py +0 -0
  253. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/settings/manager.py +0 -0
  254. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/settings/paths.py +0 -0
  255. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/settings/storage.py +0 -0
  256. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/settings/types.py +0 -0
  257. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/settings/utils.py +0 -0
  258. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/skills/loader.py +0 -0
  259. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/skills/registry.py +0 -0
  260. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/skills/types.py +0 -0
  261. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/themes/registry.py +0 -0
  262. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/themes/types.py +0 -0
  263. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tool/__init__.py +0 -0
  264. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tool/registry.py +0 -0
  265. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tool/render.py +0 -0
  266. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tool/types.py +0 -0
  267. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/trust/__init__.py +0 -0
  268. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/trust/manager.py +0 -0
  269. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/trust/types.py +0 -0
  270. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/trust/utils.py +0 -0
  271. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/__init__.py +0 -0
  272. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/ansi.py +0 -0
  273. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/autocomplete.py +0 -0
  274. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/capabilities.py +0 -0
  275. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/__init__.py +0 -0
  276. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/appearance.py +0 -0
  277. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/auth.py +0 -0
  278. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/context.py +0 -0
  279. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/misc.py +0 -0
  280. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/model.py +0 -0
  281. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/commands/session.py +0 -0
  282. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/component.py +0 -0
  283. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/__init__.py +0 -0
  284. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/autocomplete_manager.py +0 -0
  285. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/box.py +0 -0
  286. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/dynamic_border.py +0 -0
  287. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/image.py +0 -0
  288. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/inline_selector.py +0 -0
  289. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/modal.py +0 -0
  290. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/model_palette.py +0 -0
  291. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/picker_overlay.py +0 -0
  292. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/prompt_overlay.py +0 -0
  293. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/resume_modal.py +0 -0
  294. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/select_list.py +0 -0
  295. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/settings_modal.py +0 -0
  296. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/settings_schema.py +0 -0
  297. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/text_prompt.py +0 -0
  298. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/tree_select_list.py +0 -0
  299. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/components/trust_screen.py +0 -0
  300. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/diff.py +0 -0
  301. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/fuzzy.py +0 -0
  302. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/message_renderers.py +0 -0
  303. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/renderer.py +0 -0
  304. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/terminal.py +0 -0
  305. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/ui_context.py +0 -0
  306. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/tui/utils.py +0 -0
  307. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/utils/__init__.py +0 -0
  308. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/utils/http_proxy.py +0 -0
  309. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/utils/image_processing.py +0 -0
  310. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/utils/secrets.py +0 -0
  311. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau/utils/version_check.py +0 -0
  312. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau_coding_agent.egg-info/SOURCES.txt +0 -0
  313. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau_coding_agent.egg-info/dependency_links.txt +0 -0
  314. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau_coding_agent.egg-info/entry_points.txt +0 -0
  315. {tau_coding_agent-0.2.0 → tau_coding_agent-0.3.1}/tau_coding_agent.egg-info/top_level.txt +0 -0
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tau-coding-agent
3
- Version: 0.2.0
3
+ Version: 0.3.1
4
4
  Summary: A self-extensible agent CLI with terminal UI, multi-provider LLM support, session management with branching, and a plugin system for tools and commands
5
- Requires-Python: ==3.13.*
5
+ Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  Requires-Dist: anthropic==0.109.1
@@ -25,6 +25,7 @@ Requires-Dist: pyyaml==6.0.2
25
25
  Requires-Dist: pygments==2.20.0
26
26
  Requires-Dist: pathspec==1.1.1
27
27
  Requires-Dist: gitpython==3.1.50
28
+ Requires-Dist: grapheme==0.6.0
28
29
  Dynamic: license-file
29
30
 
30
31
  # Tau (τ)
@@ -161,7 +161,7 @@ Before each inference call, the runtime builds the context:
161
161
  8. Count tokens
162
162
  ```
163
163
 
164
- If tokens exceed the model's context window, automatic compaction summarizes older messages.
164
+ Automatic compaction summarizes older messages when context approaches the limit. It is checked before sending a turn (pre-flight) and after each turn, and as a reactive backstop — a request that still fails with a provider context-overflow error triggers a compact-and-retry (bounded to one attempt). See [Context Compaction](sessions.md#context-compaction).
165
165
 
166
166
  ## Hook & Event System
167
167
 
@@ -200,6 +200,7 @@ def register(tau):
200
200
  | `ctx.cwd` | `Path` | Working directory |
201
201
  | `ctx.model_id` | `str` | Active model, e.g. `"claude-sonnet-4-6"` |
202
202
  | `ctx.provider_id` | `str` | Active provider, e.g. `"anthropic"` |
203
+ | `ctx.llm` | `TextLLM \| None` | Active text LLM for running your own model calls (`await ctx.llm.invoke(LLMContext(...))`); `None` outside a live session |
203
204
  | `ctx.settings` | `SettingsManager \| None` | Full settings access |
204
205
  | `ctx.mode` | `str` | `"tui"` when running in the interactive terminal, `"headless"` otherwise |
205
206
  | `ctx.has_ui` | `bool` | True when dialog-capable UI is available — guards TUI-only calls |
@@ -459,7 +460,7 @@ the default algorithm runs.
459
460
  ### Example — use a different model for summarisation
460
461
 
461
462
  ```python
462
- from tau.hooks.types import BeforeCompactionResult
463
+ from tau.hooks import BeforeCompactionResult
463
464
  from tau.session.compaction import CompactionResult
464
465
 
465
466
  def register(tau):
@@ -489,7 +490,7 @@ def register(tau):
489
490
  ### Example — block automatic compaction
490
491
 
491
492
  ```python
492
- from tau.hooks.types import BeforeCompactionResult
493
+ from tau.hooks import BeforeCompactionResult
493
494
 
494
495
  def register(tau):
495
496
  @tau.on("before_compaction")
@@ -246,10 +246,22 @@ Long sessions eventually fill the model's context window. Tau handles this autom
246
246
 
247
247
  ### How It Works
248
248
 
249
- 1. **Auto-trigger** — after each agent turn, Tau estimates the current token usage. If `context_tokens > context_window - reserve_tokens`, compaction runs automatically.
250
- 2. **Cut-point detection** — Tau walks backwards from the most recent message, accumulating token estimates, until it has identified the `keep_recent_tokens` worth of messages to preserve. It never cuts in the middle of a tool call/result pair.
251
- 3. **Summarisation** — the messages before the cut point are sent to the model with a summarisation prompt. The resulting summary is stored in the session file as a `compaction` entry and prepended to the context on the next turn.
252
- 4. **Iterative merging** — if compaction has happened before, the new summary prompt includes the previous summary so history is never lost.
249
+ 1. **Cut-point detection** — Tau walks backwards from the most recent message, accumulating token estimates, until it has identified the `keep_recent_tokens` worth of messages to preserve. It never cuts in the middle of a tool call/result pair; if the cut would land inside a turn, the turn's prefix is summarised separately.
250
+ 2. **Summarisation** — the messages before the cut point are sent to the model with a structured summarisation prompt (Goal / Progress / Decisions / Next Steps / Critical Context). The resulting summary is stored in the session file as a `compaction` entry and prepended to the context (wrapped in `<context-summary>` tags) on the next turn.
251
+ 3. **Iterative merging** — if compaction has happened before, the new summary prompt includes the previous summary so history is never lost.
252
+
253
+ ### When Compaction Runs
254
+
255
+ Token usage is estimated from the **last successful response's reported usage** plus a chars/4 estimate of anything after it, so the trigger reflects real provider accounting rather than a pure guess. The threshold is `context_tokens > context_window - reserve_tokens`. Tau checks this at three points so context is trimmed before it can overflow:
256
+
257
+ 1. **Pre-flight** — before sending a new turn. Catches resumed or already-oversized sessions that would otherwise fail on the very first request.
258
+ 2. **Post-turn** — after each agent turn completes, the normal proactive path.
259
+ 3. **Overflow recovery (reactive backstop)** — if a request still fails with a provider *context-overflow* error, Tau catches it, drops the failed response, compacts, and **retries the turn once** automatically. This is bounded to a single attempt per turn — if it overflows again, Tau surfaces a message asking you to reduce context or switch to a larger-context model rather than looping.
260
+
261
+ Two guards keep this stable:
262
+
263
+ - **Stale-anchor guard** — immediately after a compaction, the kept messages still carry their pre-compaction (large) usage numbers. Tau skips re-triggering when the usage anchor predates the latest compaction, so it won't compact on every subsequent turn.
264
+ - **Circuit breaker** — if automatic compaction fails repeatedly (3×), Tau stops auto-compacting for the session and surfaces a notice instead of failing silently. You can still run `/compact` manually.
253
265
 
254
266
  ### Manual Compaction
255
267
 
@@ -73,6 +73,34 @@ The `name` field in the file determines the theme name used in settings and `--t
73
73
 
74
74
  > **Formats**: `.yaml` and `.yml` are recommended. `.json` is also accepted for backwards compatibility.
75
75
 
76
+ ### Color values
77
+
78
+ Every color token accepts three forms, and you can mix them freely in one theme:
79
+
80
+ | Form | Example | Renders as |
81
+ |------|---------|------------|
82
+ | **Hex** (24-bit truecolor) | `"#a78bfa"` | exact RGB, identical on every terminal |
83
+ | **Named ANSI** | `bright_cyan` | the terminal's own palette color (adapts to the user's terminal theme) |
84
+ | **With attributes** | `{ color: "#a78bfa", bold: true, italic: true, dim: true }` | either hex or named, plus styling |
85
+
86
+ Valid names: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`, `default`, and their `bright_` variants (e.g. `bright_black`).
87
+
88
+ **Which to use?**
89
+ - **Named ANSI** for a theme that should *blend into the user's terminal* (the built-in `dark` theme uses these, so its colors match whatever palette the user has set). Best for a default/adaptive theme.
90
+ - **Hex** for a theme with an *exact, branded look* that should appear the same on every machine (the built-in `light` theme uses hex). Best for a distinctive, designed theme.
91
+
92
+ An unrecognized value (a typo, or malformed hex like `#fff`) silently falls back to that token's built-in default rather than erroring. Hex must be full `#rrggbb`.
93
+
94
+ ### Syntax highlighting
95
+
96
+ Fenced code blocks are syntax-highlighted with [Pygments](https://pygments.org/styles/). Choose the style with a top-level `code_syntax_style` key:
97
+
98
+ ```yaml
99
+ code_syntax_style: monokai # any Pygments style name; "" disables highlighting
100
+ ```
101
+
102
+ Pick a dark style (`monokai`, `dracula`, `nord`, `gruvbox-dark`, …) for dark themes and a light style (`friendly`, `default`, `xcode`, `solarized-light`, …) for light backgrounds. Setting it to `""` falls back to the flat `code_block` color.
103
+
76
104
  ### Theme YAML format
77
105
 
78
106
  ```yaml
@@ -82,8 +110,11 @@ name: my_theme
82
110
  show_thinking: true # show/hide the model's thinking blocks
83
111
  show_tool_calls: true # show/hide tool call output
84
112
 
113
+ # Syntax highlighting for fenced code blocks (Pygments style; "" to disable)
114
+ code_syntax_style: monokai
115
+
85
116
  colors:
86
- # UI chrome
117
+ # UI chrome — hex for exact colors, or names like `bright_black` to use the terminal palette
87
118
  divider: "#374151"
88
119
  spinner_frame: "#0ea5e9"
89
120
  spinner_label: "#6b7280"
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "tau-coding-agent"
7
- version = "0.2.0"
7
+ version = "0.3.1"
8
8
  description = "A self-extensible agent CLI with terminal UI, multi-provider LLM support, session management with branching, and a plugin system for tools and commands"
9
9
  readme = "README.md"
10
- requires-python = "==3.13.*"
10
+ requires-python = ">=3.12"
11
11
  dependencies = [
12
12
  "anthropic==0.109.1",
13
13
  "certifi==2026.5.20",
@@ -29,6 +29,7 @@ dependencies = [
29
29
  "pygments==2.20.0",
30
30
  "pathspec==1.1.1",
31
31
  "gitpython==3.1.50",
32
+ "grapheme==0.6.0",
32
33
  ]
33
34
 
34
35
  [project.scripts]
@@ -40,7 +41,7 @@ tau = "tau.console.cli:main"
40
41
  resolution = "highest"
41
42
 
42
43
  [tool.ruff]
43
- target-version = "py313"
44
+ target-version = "py312"
44
45
  line-length = 100
45
46
 
46
47
  [tool.ruff.lint]
@@ -77,6 +77,7 @@ class Agent:
77
77
  self._phase: AgentPhase = AgentPhase.IDLE
78
78
  self._signal: asyncio.Event = asyncio.Event()
79
79
  self._compaction_failures: int = 0
80
+ self._overflow_recovery_attempted: bool = False
80
81
  self._engine.options.before_tool_call = self._before_tool_call
81
82
  self._engine.options.after_tool_call = self._after_tool_call
82
83
 
@@ -208,12 +209,25 @@ class Agent:
208
209
  async def compact(self, custom_instructions: str | None = None) -> bool:
209
210
  """Manually trigger context compaction. Returns True if compaction ran."""
210
211
  from tau.session.compaction import prepare_compaction
211
- from tau.hooks.engine import CompactionEndEvent
212
212
  entries = self._session_manager.get_branch()
213
213
  preparation = prepare_compaction(entries, self._config.compaction)
214
214
  if preparation is None:
215
215
  return False
216
- result, from_extension = await self._run_compaction(preparation, entries, manual=True, custom_instructions=custom_instructions)
216
+ await self._apply_compaction(preparation, entries, manual=True, custom_instructions=custom_instructions)
217
+ return True
218
+
219
+ async def _apply_compaction(
220
+ self,
221
+ preparation: "CompactionPreparation",
222
+ entries: list,
223
+ manual: bool,
224
+ custom_instructions: str | None = None,
225
+ ) -> None:
226
+ """Run a prepared compaction, persist the summary, and emit the end event."""
227
+ from tau.hooks.engine import CompactionEndEvent
228
+ result, from_extension = await self._run_compaction(
229
+ preparation, entries, manual=manual, custom_instructions=custom_instructions
230
+ )
217
231
  self._session_manager.append_compaction(
218
232
  summary=result.summary,
219
233
  first_kept_entry_id=result.first_kept_entry_id,
@@ -221,12 +235,19 @@ class Agent:
221
235
  )
222
236
  self._compaction_failures = 0
223
237
  await self.hooks.emit(CompactionEndEvent(
224
- manual=True,
238
+ manual=manual,
225
239
  tokens_before=result.tokens_before,
226
240
  summary_length=len(result.summary),
227
241
  from_extension=from_extension,
228
242
  ))
229
- return True
243
+
244
+ def _latest_compaction_timestamp(self) -> float | None:
245
+ """Timestamp of the most recent compaction in the active branch, if any."""
246
+ from tau.session.types import CompactionEntry
247
+ for entry in reversed(self._session_manager.get_branch()):
248
+ if isinstance(entry, CompactionEntry):
249
+ return entry.timestamp
250
+ return None
230
251
 
231
252
  async def _check_compaction(self) -> None:
232
253
  """Auto-compact if context usage exceeds the threshold. Circuit-breaks after 3 failures."""
@@ -234,7 +255,6 @@ class Agent:
234
255
  estimate_context_tokens, should_compact,
235
256
  prepare_compaction,
236
257
  )
237
- from tau.hooks.engine import CompactionEndEvent
238
258
 
239
259
  if self._compaction_failures >= 3:
240
260
  return
@@ -248,29 +268,99 @@ class Agent:
248
268
  llm_messages = _to_llm_messages(session_ctx.messages)
249
269
 
250
270
  usage = estimate_context_tokens(llm_messages)
251
- if not should_compact(usage.tokens, self._context_window, settings):
252
- return
271
+
272
+ # "Silent" overflow: some providers accept an over-limit prompt and return a
273
+ # successful response (z.ai) or truncate the input and stop with no output
274
+ # (Xiaomi MiMo) instead of erroring. The threshold check can miss these, so
275
+ # force compaction when the last response shows the symptom.
276
+ last = self._session_manager.find_last_assistant_message()
277
+ forced = last is not None and self._is_silent_overflow(last)
278
+
279
+ if not forced:
280
+ if not should_compact(usage.tokens, self._context_window, settings):
281
+ return
282
+ # Stale-anchor guard: right after a compaction the kept messages still carry
283
+ # pre-compaction usage on their anchor, which would re-trigger compaction every
284
+ # turn. Skip if the usage anchor predates the latest compaction boundary.
285
+ if usage.last_usage_index is not None:
286
+ anchor = llm_messages[usage.last_usage_index]
287
+ comp_ts = self._latest_compaction_timestamp()
288
+ if comp_ts is not None and getattr(anchor, "timestamp", 0.0) <= comp_ts:
289
+ return
253
290
 
254
291
  preparation = prepare_compaction(entries, settings)
255
292
  if preparation is None:
256
293
  return
257
294
 
258
295
  try:
259
- result, from_extension = await self._run_compaction(preparation, entries, manual=False)
260
- self._session_manager.append_compaction(
261
- summary=result.summary,
262
- first_kept_entry_id=result.first_kept_entry_id,
263
- tokens_before=result.tokens_before,
296
+ await self._apply_compaction(preparation, entries, manual=False)
297
+ except Exception:
298
+ self._compaction_failures += 1
299
+
300
+ def _is_silent_overflow(self, message: AssistantMessage) -> bool:
301
+ """Detect overflow on a *successful* response (no error was raised).
302
+
303
+ - Silent (z.ai): a normal stop whose input tokens exceed the window.
304
+ - Length-stop (Xiaomi MiMo): server truncated the input to fill the window,
305
+ leaving no room to generate, so it stops with zero output.
306
+ """
307
+ from tau.inference.types import StopReason
308
+ cw = self._context_window
309
+ if cw <= 0:
310
+ return False
311
+ u = message.usage
312
+ inp = u.input_tokens + u.cache_read_tokens
313
+ if message.stop_reason == StopReason.Stop and inp > cw:
314
+ return True
315
+ if message.stop_reason == StopReason.Length and u.output_tokens == 0 and inp >= cw * 0.99:
316
+ return True
317
+ return False
318
+
319
+ async def _try_overflow_recovery(self) -> bool:
320
+ """If the last turn died with a context-overflow error, compact once and signal a retry.
321
+
322
+ Drops the error message so it isn't kept or used as a stale anchor, compacts the
323
+ history, and lets the caller re-run the turn. Bounded to one attempt per turn so a
324
+ session that overflows even after compaction fails cleanly.
325
+ """
326
+ from tau.inference.utils import ErrorKind
327
+ from tau.session.compaction import prepare_compaction
328
+
329
+ last = self._session_manager.find_last_assistant_message()
330
+ if last is None or last.error_kind != ErrorKind.CONTEXT_OVERFLOW:
331
+ return False
332
+
333
+ if self._overflow_recovery_attempted:
334
+ self._notify(
335
+ "Context overflow recovery failed after compaction. "
336
+ "Reduce context or switch to a larger-context model."
264
337
  )
265
- self._compaction_failures = 0
266
- await self.hooks.emit(CompactionEndEvent(
267
- manual=False,
268
- tokens_before=result.tokens_before,
269
- summary_length=len(result.summary),
270
- from_extension=from_extension,
271
- ))
338
+ return False
339
+ self._overflow_recovery_attempted = True
340
+
341
+ # Drop the error assistant message — it has no usable content and would otherwise
342
+ # anchor stale usage / be re-sent on retry.
343
+ self._session_manager.remove_last_message()
344
+
345
+ entries = self._session_manager.get_branch()
346
+ preparation = prepare_compaction(entries, self._config.compaction)
347
+ if preparation is None:
348
+ return False
349
+ try:
350
+ await self._apply_compaction(preparation, entries, manual=False)
272
351
  except Exception:
273
352
  self._compaction_failures += 1
353
+ return False
354
+ return True
355
+
356
+ def _notify(self, message: str) -> None:
357
+ """Surface a message to the UI if a runtime/UI is wired up."""
358
+ if self._runtime is None:
359
+ return
360
+ from tau.extensions.context import ExtensionContext
361
+ ctx = ExtensionContext.from_runtime(self._runtime)
362
+ if ctx.ui is not None:
363
+ ctx.ui.notify(message)
274
364
 
275
365
  async def _run_compaction(self, preparation: "CompactionPreparation", entries: list, manual: bool, custom_instructions: str | None = None) -> tuple:
276
366
  """Emit before_compaction (allowing interception), then run the default algorithm.
@@ -281,7 +371,7 @@ class Agent:
281
371
  consistent with error-fallthrough behaviour.
282
372
  """
283
373
  from tau.session.compaction import compact as _compact
284
- from tau.hooks.types import BeforeCompactionEvent, BeforeCompactionResult, CompactionStartEvent
374
+ from tau.hooks.engine import BeforeCompactionEvent, BeforeCompactionResult, CompactionStartEvent
285
375
 
286
376
  before_results = await self.hooks.emit(BeforeCompactionEvent(
287
377
  preparation=preparation,
@@ -312,33 +402,33 @@ class Agent:
312
402
 
313
403
  opts = options or PromptOptions()
314
404
 
315
- session_ctx = self._session_manager.build_session_context()
316
- llm_messages = _to_llm_messages(session_ctx.messages)
317
- llm_messages = strip_unusable_trailing_assistant(llm_messages, self._session_manager)
318
-
319
- if opts.images:
320
- user_message = UserMessage.with_images(text, list(opts.images))
321
- elif opts.audio:
322
- user_message = UserMessage.with_audio(text, list(opts.audio))
323
- elif opts.video:
324
- user_message = UserMessage.with_video(text, list(opts.video))
325
- else:
326
- user_message = UserMessage.from_text(text)
327
- self._session_manager.append_message(user_message, meta=opts.meta)
328
- llm_messages.append(user_message)
405
+ # Pre-flight: a resumed or already-oversized session can exceed the window on the
406
+ # very first send. Compact before appending and shipping the new turn.
407
+ await self._check_compaction()
329
408
 
330
- ctx = AgentContext(
331
- system_prompt=self._system_prompt,
332
- messages=llm_messages,
333
- tools=self._engine.tools,
409
+ user_message = UserMessage.with_media(
410
+ text,
411
+ list(opts.images) if opts.images else None,
412
+ list(opts.audio) if opts.audio else None,
413
+ list(opts.video) if opts.video else None,
334
414
  )
415
+ self._session_manager.append_message(user_message, meta=opts.meta)
335
416
 
336
- self._signal = asyncio.Event()
337
- self._engine.llm.api.options.signal = self._signal
338
-
417
+ self._overflow_recovery_attempted = False
339
418
  self._phase = AgentPhase.TURN
340
419
  try:
341
- await self._run(ctx)
420
+ while True:
421
+ ctx = self._build_turn_context()
422
+ self._signal = asyncio.Event()
423
+ self._engine.llm.api.options.signal = self._signal
424
+ try:
425
+ await self._run(ctx)
426
+ break
427
+ except RuntimeError:
428
+ # On a context-overflow error, compact and retry the turn once.
429
+ if await self._try_overflow_recovery():
430
+ continue
431
+ raise
342
432
  finally:
343
433
  self._phase = AgentPhase.IDLE
344
434
 
@@ -349,6 +439,17 @@ class Agent:
349
439
  if not self._engine.has_pending_messages():
350
440
  await self.hooks.emit(SettledEvent())
351
441
 
442
+ def _build_turn_context(self) -> AgentContext:
443
+ """Build the LLM context for a turn from the current (possibly compacted) session."""
444
+ session_ctx = self._session_manager.build_session_context()
445
+ llm_messages = _to_llm_messages(session_ctx.messages)
446
+ llm_messages = strip_unusable_trailing_assistant(llm_messages, self._session_manager)
447
+ return AgentContext(
448
+ system_prompt=self._system_prompt,
449
+ messages=llm_messages,
450
+ tools=self._engine.tools,
451
+ )
452
+
352
453
  async def _run(self, ctx: AgentContext) -> None:
353
454
  unsubscribe = self.hooks.register(
354
455
  'message_end',
@@ -16,6 +16,28 @@ def _get_env_api_key(provider: str) -> str | None:
16
16
  return os.environ.get(f"{provider.upper()}_API_KEY")
17
17
 
18
18
 
19
+ def _is_unrecoverable_refresh_error(error: Exception) -> bool:
20
+ """Return True when a failed token refresh means the refresh token itself is
21
+ dead (so the user must log in again), rather than a transient network/5xx blip.
22
+
23
+ Defers to the shared inference error classifier for the cases it understands
24
+ (auth → unrecoverable; rate-limit/overloaded/server/timeout → transient). OAuth
25
+ token endpoints, however, report a dead refresh token as ``invalid_grant`` /
26
+ HTTP 400 — which the inference-oriented classifier leaves as UNKNOWN — and the
27
+ provider wrappers raise these as ``RuntimeError("Request failed (<code>): ...")``
28
+ without a status_code attribute, so we match those markers explicitly.
29
+ """
30
+ from tau.inference.utils import classify_error, ErrorKind
31
+
32
+ kind = classify_error(error).kind
33
+ if kind in (ErrorKind.AUTH, ErrorKind.AUTH_PERMANENT):
34
+ return True
35
+ if kind in (ErrorKind.RATE_LIMIT, ErrorKind.OVERLOADED, ErrorKind.SERVER_ERROR, ErrorKind.TIMEOUT):
36
+ return False
37
+ text = str(error).lower()
38
+ return any(m in text for m in ("invalid_grant", "invalid_request", "invalid_token", "(400)", "(401)", "(403)"))
39
+
40
+
19
41
  class AuthManager:
20
42
  """Credential storage with pluggable backends."""
21
43
 
@@ -231,6 +253,66 @@ class AuthManager:
231
253
  result = await self.storage.with_lock_async(refresh_fn)
232
254
  return result.result
233
255
 
256
+ def is_oauth(self, provider: str) -> bool:
257
+ """Return True if the stored credential for a provider is an OAuth credential."""
258
+ return isinstance(self.get(provider), OAuthCredential)
259
+
260
+ async def force_refresh(self, provider: str, stale_access: str | None = None) -> OAuthCredential | None:
261
+ """Force-refresh an OAuth credential whose access token was rejected (e.g. a
262
+ mid-request 401) even though it is not yet time-expired.
263
+
264
+ Returns the new credential on success. Returns None if the provider isn't
265
+ OAuth or the refresh failed; on an *unrecoverable* failure (dead refresh
266
+ token) the stored credential is removed so the caller can prompt re-login,
267
+ whereas a transient failure leaves it untouched.
268
+
269
+ ``stale_access`` is the access token that was just rejected: if another
270
+ instance has already refreshed it under the lock, we adopt that result
271
+ instead of rotating the refresh token a second time.
272
+ """
273
+ oauth_provider = self.registry.text.get_oauth_provider(provider=provider)
274
+ if not oauth_provider:
275
+ return None
276
+
277
+ async def refresh_fn(current: str | None) -> LockResult:
278
+ """Refresh (unconditionally) the OAuth token in storage under the lock."""
279
+ current_data = self._parse_storage_data(current)
280
+ credential = current_data.get(provider)
281
+
282
+ if not isinstance(credential, OAuthCredential):
283
+ return LockResult(result=None)
284
+
285
+ # Another instance already refreshed after our token was rejected.
286
+ if stale_access is not None and credential.access != stale_access:
287
+ self.data = current_data
288
+ return LockResult(result=credential)
289
+
290
+ try:
291
+ refreshed_credential = await oauth_provider.refresh_token(credential=credential)
292
+ if credential.extra:
293
+ merged_extra = dict(credential.extra)
294
+ merged_extra.update(refreshed_credential.extra)
295
+ refreshed_credential.extra = merged_extra
296
+ current_data[provider] = refreshed_credential
297
+ self.data = current_data
298
+ serialized = {k: self._serialize_credential(v) for k, v in current_data.items()}
299
+ return LockResult(result=refreshed_credential, next=json.dumps(serialized, indent=2))
300
+ except Exception as e:
301
+ self._record_error(e)
302
+ if _is_unrecoverable_refresh_error(e):
303
+ # Refresh token is dead — drop the credential so the user is
304
+ # prompted to log in again rather than retrying a broken token.
305
+ current_data.pop(provider, None)
306
+ self.data = current_data
307
+ serialized = {k: self._serialize_credential(v) for k, v in current_data.items()}
308
+ return LockResult(result=None, next=json.dumps(serialized, indent=2))
309
+ # Transient failure — keep the credential for a later retry.
310
+ self.data = current_data
311
+ return LockResult(result=None)
312
+
313
+ result = await self.storage.with_lock_async(refresh_fn)
314
+ return result.result
315
+
234
316
  async def login(self, provider: str, callbacks: OAuthLoginCallbacks):
235
317
  """Perform OAuth login flow for a provider and store the resulting credential."""
236
318
  if oauth_provider := self.registry.text.get_oauth_provider(provider):
@@ -0,0 +1,50 @@
1
+ name: dark
2
+
3
+ # The default theme. Colours use named ANSI values (not hex), so they map to the
4
+ # terminal's own 16-colour palette and adapt to the user's terminal theme.
5
+ # Per-token bold/italic is applied by the loader, so only the colour is listed here.
6
+ # Purely structural tokens (bold/italic/strikethrough/dim/reverse, the plain
7
+ # "tool result", spinner label) carry no colour and inherit the code defaults.
8
+
9
+ # Pygments style for fenced code blocks (dark background).
10
+ code_syntax_style: monokai
11
+
12
+ colors:
13
+ # Chrome
14
+ divider: bright_black
15
+
16
+ # Spinner
17
+ spinner_frame: bright_cyan
18
+
19
+ # Markdown
20
+ heading: bright_cyan
21
+ code_inline: bright_yellow
22
+ code_block: bright_green
23
+ code_block_border: bright_black
24
+ quote_border: bright_black
25
+ hr: bright_black
26
+ list_bullet: bright_cyan
27
+ link_text: bright_cyan
28
+ link_url: bright_black
29
+
30
+ # Messages
31
+ you_label: bright_cyan
32
+ assistant_label: bright_green
33
+ tool_arrow: bright_yellow
34
+ tool_result_err: bright_red
35
+ error_label: bright_red
36
+ stream_cursor: bright_white
37
+
38
+ # Diffs
39
+ diff_added: bright_green
40
+ diff_removed: bright_red
41
+ diff_context: bright_black
42
+ diff_hunk: bright_yellow
43
+
44
+ # Select list / palette
45
+ selected_label: bright_white
46
+ selected_desc: bright_black
47
+ normal_label: bright_black
48
+ normal_desc: bright_black
49
+ indicator: bright_black
50
+ empty: bright_black
@@ -1,5 +1,8 @@
1
1
  name: light
2
2
 
3
+ # Light-background syntax style for fenced code blocks (pygments style name).
4
+ code_syntax_style: friendly
5
+
3
6
  colors:
4
7
  # UI chrome
5
8
  divider: "#c8c8c8"
@@ -36,7 +36,6 @@ def resolve_model(model: str | None, provider: str | None) -> tuple[str | None,
36
36
 
37
37
 
38
38
  @click.group(invoke_without_command=True, context_settings={"help_option_names": ["-h", "--help"]})
39
- @click.argument("message", required=False, default=None)
40
39
  @click.option("--version", "-v", is_flag=True, default=False, help="Print version and exit.")
41
40
  @click.option("--debug", "-d", is_flag=True, default=False, help="Enable debug logging.")
42
41
  @click.option("--cwd", "-c", default=None, metavar="PATH", help="Set the working directory.")
@@ -71,7 +70,6 @@ def resolve_model(model: str | None, provider: str | None) -> tuple[str | None,
71
70
  @click.pass_context
72
71
  def cli(
73
72
  ctx: click.Context,
74
- message: str | None,
75
73
  version: bool,
76
74
  debug: bool,
77
75
  cwd: str | None,
@@ -103,7 +101,7 @@ def cli(
103
101
  os.chdir(cwd)
104
102
 
105
103
  ctx.ensure_object(dict)
106
- ctx.obj["message"] = prompt or message
104
+ ctx.obj["prompt"] = prompt
107
105
  ctx.obj["provider"] = provider
108
106
  ctx.obj["model"] = model
109
107
  ctx.obj["theme"] = theme
@@ -156,9 +154,9 @@ async def _start(opts: dict) -> None:
156
154
  case "interactive":
157
155
  await _run_interactive(runtime, opts["theme"])
158
156
  case "print":
159
- await _run_print(runtime, opts["message"], quiet=opts.get("quiet", False))
157
+ await _run_print(runtime, opts["prompt"], quiet=opts.get("quiet", False))
160
158
  case "json":
161
- await _run_json(runtime, opts["message"], quiet=opts.get("quiet", False))
159
+ await _run_json(runtime, opts["prompt"], quiet=opts.get("quiet", False))
162
160
  case "rpc":
163
161
  from tau.rpc.mode import run_rpc_mode
164
162
  await run_rpc_mode(runtime)
@@ -564,9 +564,10 @@ class Engine:
564
564
  _streaming_thinking = None
565
565
  else:
566
566
  message.contents.append(thinking)
567
- case ErrorEvent(reason=reason, error=error):
567
+ case ErrorEvent(reason=reason, error=error, kind=kind):
568
568
  message.stop_reason = reason
569
569
  message.error = error
570
+ message.error_kind = kind
570
571
  case EndEvent() as ev:
571
572
  message.stop_reason = ev.reason
572
573
  message.usage = Usage(