tau-coding-agent 0.1.0__py3-none-any.whl

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 (283) hide show
  1. tau/__init__.py +0 -0
  2. tau/agent/__init__.py +11 -0
  3. tau/agent/prompt/__init__.py +10 -0
  4. tau/agent/prompt/builder.py +302 -0
  5. tau/agent/prompt/types.py +33 -0
  6. tau/agent/service.py +369 -0
  7. tau/agent/types.py +61 -0
  8. tau/auth/manager.py +247 -0
  9. tau/auth/storage.py +82 -0
  10. tau/auth/types.py +41 -0
  11. tau/builtins/__init__.py +4 -0
  12. tau/builtins/__pycache__/__init__.cpython-313.pyc +0 -0
  13. tau/builtins/__pycache__/__init__.cpython-314.pyc +0 -0
  14. tau/builtins/commands/__init__.py +41 -0
  15. tau/builtins/commands/__pycache__/__init__.cpython-313.pyc +0 -0
  16. tau/builtins/commands/__pycache__/__init__.cpython-314.pyc +0 -0
  17. tau/builtins/commands/__pycache__/clear.cpython-313.pyc +0 -0
  18. tau/builtins/commands/__pycache__/clear.cpython-314.pyc +0 -0
  19. tau/builtins/commands/__pycache__/compact.cpython-313.pyc +0 -0
  20. tau/builtins/commands/__pycache__/compact.cpython-314.pyc +0 -0
  21. tau/builtins/commands/__pycache__/reload.cpython-313.pyc +0 -0
  22. tau/builtins/commands/__pycache__/reload.cpython-314.pyc +0 -0
  23. tau/builtins/commands/__pycache__/session.cpython-313.pyc +0 -0
  24. tau/builtins/commands/__pycache__/session.cpython-314.pyc +0 -0
  25. tau/builtins/commands/clear.py +16 -0
  26. tau/builtins/commands/compact.py +28 -0
  27. tau/builtins/commands/reload.py +27 -0
  28. tau/builtins/commands/session.py +19 -0
  29. tau/builtins/extensions/footer/__init__.py +76 -0
  30. tau/builtins/extensions/footer/__pycache__/__init__.cpython-313.pyc +0 -0
  31. tau/builtins/extensions/footer/__pycache__/git.cpython-313.pyc +0 -0
  32. tau/builtins/extensions/footer/__pycache__/model.cpython-313.pyc +0 -0
  33. tau/builtins/extensions/footer/__pycache__/utils.cpython-313.pyc +0 -0
  34. tau/builtins/extensions/footer/git.py +26 -0
  35. tau/builtins/extensions/footer/model.py +69 -0
  36. tau/builtins/extensions/footer/utils.py +44 -0
  37. tau/builtins/extensions/header/__init__.py +18 -0
  38. tau/builtins/extensions/header/__pycache__/__init__.cpython-313.pyc +0 -0
  39. tau/builtins/models/__init__.py +0 -0
  40. tau/builtins/models/__pycache__/__init__.cpython-313.pyc +0 -0
  41. tau/builtins/models/__pycache__/text.cpython-313.pyc +0 -0
  42. tau/builtins/models/audio.py +43 -0
  43. tau/builtins/models/image.py +43 -0
  44. tau/builtins/models/text.py +482 -0
  45. tau/builtins/models/video.py +40 -0
  46. tau/builtins/prompts/commit.md +7 -0
  47. tau/builtins/prompts/docs.md +7 -0
  48. tau/builtins/prompts/explain.md +7 -0
  49. tau/builtins/prompts/fix.md +7 -0
  50. tau/builtins/prompts/refactor.md +7 -0
  51. tau/builtins/prompts/review.md +7 -0
  52. tau/builtins/prompts/test.md +7 -0
  53. tau/builtins/providers/__init__.py +0 -0
  54. tau/builtins/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  55. tau/builtins/providers/__pycache__/text.cpython-313.pyc +0 -0
  56. tau/builtins/providers/audio.py +10 -0
  57. tau/builtins/providers/image.py +9 -0
  58. tau/builtins/providers/text.py +33 -0
  59. tau/builtins/providers/video.py +6 -0
  60. tau/builtins/skills/code-review/SKILL.md +4 -0
  61. tau/builtins/skills/debug/SKILL.md +4 -0
  62. tau/builtins/skills/git-commit/SKILL.md +4 -0
  63. tau/builtins/themes/dark.yaml +1 -0
  64. tau/builtins/themes/light.yaml +46 -0
  65. tau/builtins/tools/__init__.py +73 -0
  66. tau/builtins/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  67. tau/builtins/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  68. tau/builtins/tools/__pycache__/bash.cpython-313.pyc +0 -0
  69. tau/builtins/tools/__pycache__/bash.cpython-314.pyc +0 -0
  70. tau/builtins/tools/__pycache__/edit.cpython-313.pyc +0 -0
  71. tau/builtins/tools/__pycache__/edit.cpython-314.pyc +0 -0
  72. tau/builtins/tools/__pycache__/glob.cpython-313.pyc +0 -0
  73. tau/builtins/tools/__pycache__/glob.cpython-314.pyc +0 -0
  74. tau/builtins/tools/__pycache__/grep.cpython-313.pyc +0 -0
  75. tau/builtins/tools/__pycache__/grep.cpython-314.pyc +0 -0
  76. tau/builtins/tools/__pycache__/ls.cpython-313.pyc +0 -0
  77. tau/builtins/tools/__pycache__/ls.cpython-314.pyc +0 -0
  78. tau/builtins/tools/__pycache__/read.cpython-313.pyc +0 -0
  79. tau/builtins/tools/__pycache__/read.cpython-314.pyc +0 -0
  80. tau/builtins/tools/__pycache__/terminal.cpython-313.pyc +0 -0
  81. tau/builtins/tools/__pycache__/terminal.cpython-314.pyc +0 -0
  82. tau/builtins/tools/__pycache__/write.cpython-313.pyc +0 -0
  83. tau/builtins/tools/__pycache__/write.cpython-314.pyc +0 -0
  84. tau/builtins/tools/edit.py +215 -0
  85. tau/builtins/tools/glob.py +112 -0
  86. tau/builtins/tools/grep.py +146 -0
  87. tau/builtins/tools/ls.py +135 -0
  88. tau/builtins/tools/read.py +122 -0
  89. tau/builtins/tools/terminal.py +150 -0
  90. tau/builtins/tools/write.py +105 -0
  91. tau/commands/__init__.py +10 -0
  92. tau/commands/registry.py +71 -0
  93. tau/commands/types.py +33 -0
  94. tau/console/__init__.py +0 -0
  95. tau/console/cli.py +266 -0
  96. tau/console/commands/__init__.py +0 -0
  97. tau/console/commands/auth.py +193 -0
  98. tau/console/commands/packages.py +104 -0
  99. tau/console/commands/update.py +76 -0
  100. tau/core/__init__.py +0 -0
  101. tau/core/registry.py +102 -0
  102. tau/engine/__init__.py +47 -0
  103. tau/engine/service.py +768 -0
  104. tau/engine/types.py +163 -0
  105. tau/extensions/__init__.py +28 -0
  106. tau/extensions/api.py +928 -0
  107. tau/extensions/context.py +462 -0
  108. tau/extensions/events.py +70 -0
  109. tau/extensions/loader.py +386 -0
  110. tau/extensions/runtime.py +184 -0
  111. tau/extensions/settings.py +137 -0
  112. tau/hooks/__init__.py +112 -0
  113. tau/hooks/engine.py +237 -0
  114. tau/hooks/inference.py +21 -0
  115. tau/hooks/runtime.py +126 -0
  116. tau/hooks/service.py +121 -0
  117. tau/hooks/session.py +117 -0
  118. tau/hooks/tui.py +61 -0
  119. tau/hooks/types.py +72 -0
  120. tau/inference/__init__.py +180 -0
  121. tau/inference/api/__init__.py +0 -0
  122. tau/inference/api/audio/__init__.py +0 -0
  123. tau/inference/api/audio/base.py +29 -0
  124. tau/inference/api/audio/builtins.py +15 -0
  125. tau/inference/api/audio/elevenlabs_audio.py +183 -0
  126. tau/inference/api/audio/gemini_audio.py +95 -0
  127. tau/inference/api/audio/openai_audio.py +159 -0
  128. tau/inference/api/audio/registry.py +15 -0
  129. tau/inference/api/audio/sarvam_audio.py +163 -0
  130. tau/inference/api/audio/service.py +103 -0
  131. tau/inference/api/audio/utils.py +47 -0
  132. tau/inference/api/image/__init__.py +0 -0
  133. tau/inference/api/image/base.py +17 -0
  134. tau/inference/api/image/builtins.py +8 -0
  135. tau/inference/api/image/gemini_image.py +77 -0
  136. tau/inference/api/image/openai_image.py +103 -0
  137. tau/inference/api/image/openrouter.py +144 -0
  138. tau/inference/api/image/registry.py +15 -0
  139. tau/inference/api/image/service.py +71 -0
  140. tau/inference/api/registry.py +82 -0
  141. tau/inference/api/text/__init__.py +0 -0
  142. tau/inference/api/text/anthropic_claude_code.py +222 -0
  143. tau/inference/api/text/anthropic_messages.py +196 -0
  144. tau/inference/api/text/base.py +40 -0
  145. tau/inference/api/text/builtins.py +19 -0
  146. tau/inference/api/text/gemini_generate.py +234 -0
  147. tau/inference/api/text/github_copilot_chat.py +172 -0
  148. tau/inference/api/text/google_antigravity.py +522 -0
  149. tau/inference/api/text/mistral_chat.py +284 -0
  150. tau/inference/api/text/ollama_chat.py +200 -0
  151. tau/inference/api/text/openai_codex_responses.py +497 -0
  152. tau/inference/api/text/openai_completions.py +227 -0
  153. tau/inference/api/text/openai_responses.py +235 -0
  154. tau/inference/api/text/registry.py +50 -0
  155. tau/inference/api/text/service.py +297 -0
  156. tau/inference/api/text/types.py +7 -0
  157. tau/inference/api/text/utils.py +228 -0
  158. tau/inference/api/video/__init__.py +0 -0
  159. tau/inference/api/video/base.py +26 -0
  160. tau/inference/api/video/builtins.py +7 -0
  161. tau/inference/api/video/fal_video.py +119 -0
  162. tau/inference/api/video/openrouter_video.py +142 -0
  163. tau/inference/api/video/registry.py +15 -0
  164. tau/inference/api/video/service.py +72 -0
  165. tau/inference/model/__init__.py +0 -0
  166. tau/inference/model/registry.py +102 -0
  167. tau/inference/model/types.py +65 -0
  168. tau/inference/provider/__init__.py +0 -0
  169. tau/inference/provider/oauth/__init__.py +35 -0
  170. tau/inference/provider/oauth/anthropic_claude_code.py +286 -0
  171. tau/inference/provider/oauth/github_copilot.py +333 -0
  172. tau/inference/provider/oauth/google_antigravity.py +258 -0
  173. tau/inference/provider/oauth/openai_codex.py +309 -0
  174. tau/inference/provider/oauth/pkce.py +14 -0
  175. tau/inference/provider/oauth/types.py +46 -0
  176. tau/inference/provider/oauth/utils.py +154 -0
  177. tau/inference/provider/registry.py +141 -0
  178. tau/inference/provider/types.py +114 -0
  179. tau/inference/types.py +549 -0
  180. tau/inference/utils.py +219 -0
  181. tau/message/__init__.py +0 -0
  182. tau/message/types.py +482 -0
  183. tau/message/utils.py +178 -0
  184. tau/packages/__init__.py +11 -0
  185. tau/packages/manager.py +190 -0
  186. tau/packages/types.py +20 -0
  187. tau/packages/utils.py +67 -0
  188. tau/prompts/expand.py +58 -0
  189. tau/prompts/loader.py +69 -0
  190. tau/prompts/registry.py +45 -0
  191. tau/prompts/types.py +24 -0
  192. tau/rpc/__init__.py +8 -0
  193. tau/rpc/mode.py +783 -0
  194. tau/rpc/types.py +252 -0
  195. tau/runtime/service.py +759 -0
  196. tau/runtime/types.py +303 -0
  197. tau/session/branch_summarization.py +312 -0
  198. tau/session/compaction.py +646 -0
  199. tau/session/manager.py +652 -0
  200. tau/session/types.py +188 -0
  201. tau/session/utils.py +233 -0
  202. tau/settings/manager.py +1077 -0
  203. tau/settings/paths.py +150 -0
  204. tau/settings/storage.py +63 -0
  205. tau/settings/types.py +173 -0
  206. tau/settings/utils.py +25 -0
  207. tau/skills/loader.py +91 -0
  208. tau/skills/registry.py +70 -0
  209. tau/skills/types.py +25 -0
  210. tau/themes/loader.py +238 -0
  211. tau/themes/registry.py +108 -0
  212. tau/themes/types.py +19 -0
  213. tau/tool/__init__.py +3 -0
  214. tau/tool/registry.py +117 -0
  215. tau/tool/render.py +21 -0
  216. tau/tool/types.py +244 -0
  217. tau/trust/__init__.py +13 -0
  218. tau/trust/manager.py +80 -0
  219. tau/trust/types.py +14 -0
  220. tau/trust/utils.py +72 -0
  221. tau/tui/__init__.py +54 -0
  222. tau/tui/agent_hooks.py +346 -0
  223. tau/tui/ansi.py +330 -0
  224. tau/tui/app.py +540 -0
  225. tau/tui/autocomplete.py +33 -0
  226. tau/tui/capabilities.py +119 -0
  227. tau/tui/commands/__init__.py +3 -0
  228. tau/tui/commands/appearance.py +498 -0
  229. tau/tui/commands/auth.py +232 -0
  230. tau/tui/commands/context.py +38 -0
  231. tau/tui/commands/misc.py +82 -0
  232. tau/tui/commands/model.py +118 -0
  233. tau/tui/commands/session.py +464 -0
  234. tau/tui/component.py +268 -0
  235. tau/tui/components/__init__.py +0 -0
  236. tau/tui/components/autocomplete_manager.py +267 -0
  237. tau/tui/components/autocomplete_picker.py +143 -0
  238. tau/tui/components/box.py +90 -0
  239. tau/tui/components/command_palette.py +144 -0
  240. tau/tui/components/dynamic_border.py +19 -0
  241. tau/tui/components/file_picker.py +233 -0
  242. tau/tui/components/image.py +181 -0
  243. tau/tui/components/inline_selector.py +71 -0
  244. tau/tui/components/layout.py +1194 -0
  245. tau/tui/components/message_list.py +692 -0
  246. tau/tui/components/modal.py +97 -0
  247. tau/tui/components/model_palette.py +204 -0
  248. tau/tui/components/picker_overlay.py +174 -0
  249. tau/tui/components/prompt_overlay.py +236 -0
  250. tau/tui/components/resume_modal.py +372 -0
  251. tau/tui/components/select_list.py +222 -0
  252. tau/tui/components/settings_modal.py +274 -0
  253. tau/tui/components/settings_schema.py +203 -0
  254. tau/tui/components/spinner.py +119 -0
  255. tau/tui/components/text_input.py +396 -0
  256. tau/tui/components/text_prompt.py +82 -0
  257. tau/tui/components/tree_select_list.py +580 -0
  258. tau/tui/components/trust_screen.py +97 -0
  259. tau/tui/diff.py +114 -0
  260. tau/tui/fuzzy.py +99 -0
  261. tau/tui/input.py +496 -0
  262. tau/tui/input_handler.py +716 -0
  263. tau/tui/keybindings.py +87 -0
  264. tau/tui/markdown.py +286 -0
  265. tau/tui/message_renderers.py +31 -0
  266. tau/tui/overlay.py +326 -0
  267. tau/tui/renderer.py +378 -0
  268. tau/tui/terminal.py +499 -0
  269. tau/tui/theme.py +148 -0
  270. tau/tui/tui.py +544 -0
  271. tau/tui/ui_context.py +768 -0
  272. tau/tui/utils.py +20 -0
  273. tau/utils/__init__.py +0 -0
  274. tau/utils/http_proxy.py +221 -0
  275. tau/utils/image_processing.py +172 -0
  276. tau/utils/secrets.py +59 -0
  277. tau/utils/version_check.py +60 -0
  278. tau_coding_agent-0.1.0.dist-info/METADATA +177 -0
  279. tau_coding_agent-0.1.0.dist-info/RECORD +283 -0
  280. tau_coding_agent-0.1.0.dist-info/WHEEL +5 -0
  281. tau_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
  282. tau_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
  283. tau_coding_agent-0.1.0.dist-info/top_level.txt +1 -0
tau/tool/types.py ADDED
@@ -0,0 +1,244 @@
1
+ from __future__ import annotations
2
+ import asyncio
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass, field
5
+ from enum import Enum
6
+ from typing import TYPE_CHECKING, Any, Awaitable, Callable, Optional, Type
7
+ from pydantic import BaseModel
8
+ from pathlib import Path
9
+
10
+ if TYPE_CHECKING:
11
+ from tau.inference.api.text.service import TextLLM as LLM
12
+ from tau.settings.manager import SettingsManager
13
+
14
+ @dataclass
15
+ class ToolError:
16
+ """File-level tool load failure with optional stack trace."""
17
+ path: str
18
+ error: str
19
+ stack: str = ''
20
+
21
+
22
+ @dataclass
23
+ class LoadToolResults:
24
+ """Aggregate result of loading tools from one or more directories."""
25
+
26
+ tools: list[Tool] = field(default_factory=list)
27
+ errors: list[ToolError] = field(default_factory=list)
28
+
29
+
30
+ class ToolKind(str, Enum):
31
+ """Semantic category used by the engine to apply execution policy to a tool call."""
32
+
33
+ Read = "read"
34
+ Edit = "edit"
35
+ Write = "write"
36
+ Execute = "execute"
37
+ Web = "web"
38
+
39
+
40
+ class ToolExecutionMode(str, Enum):
41
+ """Controls how the engine schedules concurrent calls to the same tool."""
42
+
43
+ Sequential = "sequential"
44
+ Parallel = "parallel"
45
+ Batch = "batch"
46
+
47
+
48
+ @dataclass
49
+ class ToolInvocation:
50
+ """Complete tool call specification with resolved args and execution context."""
51
+ id: str
52
+ name: str
53
+ cwd: Path | None
54
+ params: dict[str, Any] = field(default_factory=dict)
55
+
56
+
57
+ @dataclass
58
+ class ToolResult:
59
+ """Tool execution outcome with optional error flag and early termination signal."""
60
+ id: str
61
+ content: str
62
+ is_error: bool = False
63
+ metadata: dict[str, Any] = field(default_factory=dict)
64
+ terminate: bool = False
65
+ terminate_message: str | None = None
66
+
67
+ @classmethod
68
+ def ok(
69
+ cls,
70
+ id: str,
71
+ content: str,
72
+ metadata: dict[str, Any] | None = None,
73
+ ) -> ToolResult:
74
+ """Construct a successful outcome.
75
+
76
+ Args:
77
+ id: The tool call ID this result corresponds to.
78
+ content: The result content (output of the tool).
79
+ metadata: Optional metadata dict (default empty).
80
+
81
+ Returns:
82
+ A ToolResult with is_error=False.
83
+ """
84
+ return cls(id=id, content=content, is_error=False, metadata=metadata or {})
85
+
86
+ @classmethod
87
+ def error(
88
+ cls,
89
+ id: str,
90
+ content: str,
91
+ metadata: dict[str, Any] | None = None,
92
+ ) -> ToolResult:
93
+ """Construct a failed outcome.
94
+
95
+ Args:
96
+ id: The tool call ID this result corresponds to.
97
+ content: The error message or description.
98
+ metadata: Optional metadata dict (default empty).
99
+
100
+ Returns:
101
+ A ToolResult with is_error=True.
102
+ """
103
+ return cls(id=id, content=content, is_error=True, metadata=metadata or {})
104
+
105
+ ToolExecutionUpdateCallback = Callable[[ToolResult], Awaitable[None]]
106
+
107
+ AbortSignal = asyncio.Event
108
+
109
+
110
+ @dataclass
111
+ class ToolContext:
112
+ """Runtime services available to tools during execution (LLM, agents, managers, etc)."""
113
+ llm: LLM | None = None
114
+ cwd: Path | None = None
115
+ settings: SettingsManager | None = None
116
+
117
+
118
+ @dataclass
119
+ class ToolRenderOptions:
120
+ """Render-time flags passed to render_result callbacks.
121
+
122
+ is_error: True when the tool call returned an error.
123
+ expanded: True when the user has toggled tool results open (Ctrl+O).
124
+ is_partial: True while the tool is still executing (streaming output).
125
+ metadata: Arbitrary data the tool stored in ToolResult.metadata.
126
+ """
127
+ is_error: bool = False
128
+ expanded: bool = False
129
+ is_partial: bool = False
130
+ metadata: dict[str, Any] = field(default_factory=dict)
131
+
132
+
133
+
134
+
135
+ class Tool(ABC):
136
+ """Abstract base for tools: executable, schema-validated components with metadata and policy."""
137
+
138
+ # Explicit class-level annotations so Pyright infers the correct attribute
139
+ # types at every call site without relying on __init__ parameter inference.
140
+ render_call: Optional[Callable[[dict, bool], list[str]]]
141
+ render_result: Optional[Callable[[str, "ToolRenderOptions"], list[str]]]
142
+ render_shell: str
143
+
144
+ def __init__(
145
+ self,
146
+ name: str,
147
+ description: str,
148
+ schema: Type[BaseModel],
149
+ kind: ToolKind,
150
+ execution_mode: ToolExecutionMode = ToolExecutionMode.Sequential,
151
+ *,
152
+ render_call: Optional[Callable[[dict, bool], list[str]]] = None,
153
+ render_result: Optional[Callable[[str, "ToolRenderOptions"], list[str]]] = None,
154
+ render_shell: str = "self",
155
+ prompt_snippet: Optional[str] = None,
156
+ prompt_guidelines: Optional[str] = None,
157
+ prepare_arguments: Optional[Callable[[dict], dict]] = None,
158
+ ) -> None:
159
+ """Initialize tool with name, description, schema, kind, and execution concurrency policy.
160
+
161
+ render_shell controls how the result block is framed in the TUI:
162
+ "self" (default) — renderer output is used as-is, no extra framing.
163
+ "default" — the standard ``└ first_line`` shell is applied to the
164
+ renderer output so it matches the built-in tool style.
165
+ """
166
+ self.name = name
167
+ self.description = description
168
+ self.schema = schema
169
+ self.kind = kind
170
+ self.execution_mode = execution_mode
171
+ self.render_call = render_call
172
+ self.render_result = render_result
173
+ self.render_shell = render_shell
174
+ self.prompt_snippet = prompt_snippet
175
+ self.prompt_guidelines = prompt_guidelines
176
+ self.prepare_arguments = prepare_arguments
177
+
178
+ def validate(self, params: dict[str, Any]) -> tuple[bool, list[str]]:
179
+ """Validate params against schema; return (success, error_list).
180
+
181
+ Args:
182
+ params: Tool call parameters to validate.
183
+
184
+ Returns:
185
+ A tuple of (success: bool, errors: list[str]).
186
+ """
187
+ try:
188
+ self.schema.model_validate(params)
189
+ return True, []
190
+ except Exception as e:
191
+ from pydantic import ValidationError
192
+ # Format Pydantic errors with field path for clarity
193
+ if isinstance(e, ValidationError):
194
+ errors = [
195
+ f"{' -> '.join(str(loc) for loc in err['loc'])}: {err['msg']}"
196
+ for err in e.errors()
197
+ ]
198
+ else:
199
+ errors = [str(e)]
200
+ return False, errors
201
+
202
+ def to_json(self) -> dict[str, Any]:
203
+ """Serialize to JSON schema with name, description, and input_schema.
204
+
205
+ Returns:
206
+ A dict with 'name', 'description', and 'input_schema' keys suitable for provider APIs.
207
+ """
208
+ return {
209
+ "name": self.name,
210
+ "description": self.description,
211
+ "input_schema": self.schema.model_json_schema(),
212
+ }
213
+
214
+ def _is_cancelled(self, signal: Optional[AbortSignal]) -> bool:
215
+ """Check if abort signal has been set.
216
+
217
+ Args:
218
+ signal: Optional asyncio.Event abort signal.
219
+
220
+ Returns:
221
+ True if the signal has been set, indicating cancellation requested.
222
+ """
223
+ return signal is not None and signal.is_set()
224
+
225
+ @abstractmethod
226
+ async def execute(
227
+ self,
228
+ invocation: ToolInvocation,
229
+ tool_execution_update_callback: Optional[ToolExecutionUpdateCallback] = None,
230
+ signal: Optional[AbortSignal] = None,
231
+ context: Optional[ToolContext] = None,
232
+ ) -> ToolResult:
233
+ """Execute the tool with params; subclasses must override.
234
+
235
+ Args:
236
+ invocation: Complete tool call specification with resolved parameters.
237
+ tool_execution_update_callback: Optional callback for streaming updates.
238
+ signal: Optional abort signal to check for user-initiated cancellation.
239
+ context: Optional ToolContext with runtime services available to the tool.
240
+
241
+ Returns:
242
+ A ToolResult with the outcome, content, and optional error details.
243
+ """
244
+ ...
tau/trust/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ from tau.trust.types import TrustOption
2
+ from tau.trust.utils import has_project_trust_inputs, get_trust_options, normalize, find_nearest
3
+ from tau.trust.manager import TrustStore, trust_store
4
+
5
+ __all__ = [
6
+ "TrustOption",
7
+ "TrustStore",
8
+ "trust_store",
9
+ "has_project_trust_inputs",
10
+ "get_trust_options",
11
+ "normalize",
12
+ "find_nearest",
13
+ ]
tau/trust/manager.py ADDED
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from tau.settings.paths import CONFIG_DIR_PATH
7
+ from tau.trust.types import TrustOption
8
+ from tau.trust.utils import normalize, find_nearest, has_project_trust_inputs, get_trust_options
9
+
10
+
11
+ class TrustStore:
12
+ """Persists per-directory trust decisions in ``~/.tau/trust.json``.
13
+
14
+ Trust walks up the directory tree — trusting a parent directory implicitly
15
+ trusts all child directories beneath it.
16
+ """
17
+
18
+ def __init__(self, config_dir: Path | None = None) -> None:
19
+ base = config_dir or CONFIG_DIR_PATH
20
+ self._path = base / "trust.json"
21
+
22
+ # ── Read ──────────────────────────────────────────────────────────────────
23
+
24
+ def _read(self) -> dict[str, bool | None]:
25
+ try:
26
+ return json.loads(self._path.read_text(encoding="utf-8"))
27
+ except (FileNotFoundError, json.JSONDecodeError):
28
+ return {}
29
+
30
+ def get(self, cwd: str | Path) -> bool | None:
31
+ """Return the stored trust decision, or ``None`` if no decision exists."""
32
+ data = self._read()
33
+ entry = find_nearest(data, normalize(cwd))
34
+ return entry[1] if entry is not None else None
35
+
36
+ def get_stored_path(self, cwd: str | Path) -> str | None:
37
+ """Return the directory path that holds the nearest trust decision, or ``None``."""
38
+ data = self._read()
39
+ entry = find_nearest(data, normalize(cwd))
40
+ return entry[0] if entry is not None else None
41
+
42
+ # ── Write ─────────────────────────────────────────────────────────────────
43
+
44
+ def _write(self, data: dict[str, bool | None]) -> None:
45
+ self._path.parent.mkdir(parents=True, exist_ok=True)
46
+ clean = {k: v for k, v in data.items() if v is not None}
47
+ self._path.write_text(json.dumps(clean, indent=2, sort_keys=True), encoding="utf-8")
48
+
49
+ def set(self, cwd: str | Path, decision: bool | None) -> None:
50
+ """Store a trust decision for *cwd*. Pass ``None`` to remove the entry."""
51
+ data = self._read()
52
+ key = normalize(cwd)
53
+ if decision is None:
54
+ data.pop(key, None)
55
+ else:
56
+ data[key] = decision
57
+ self._write(data)
58
+
59
+ def apply_option(self, option: TrustOption) -> None:
60
+ """Persist a :class:`TrustOption`. ``save_path=None`` means session-only — nothing is written."""
61
+ if option.save_path is None:
62
+ return
63
+ data = self._read()
64
+ data[normalize(option.save_path)] = option.trusted
65
+ if option.clear_child_path is not None:
66
+ data.pop(normalize(option.clear_child_path), None)
67
+ self._write(data)
68
+
69
+
70
+ # ── Module-level singleton ────────────────────────────────────────────────────
71
+
72
+ trust_store = TrustStore()
73
+
74
+ __all__ = [
75
+ "TrustStore",
76
+ "TrustOption",
77
+ "trust_store",
78
+ "has_project_trust_inputs",
79
+ "get_trust_options",
80
+ ]
tau/trust/types.py ADDED
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass
7
+ class TrustOption:
8
+ """A single choice presented to the user in the trust prompt."""
9
+ label: str
10
+ trusted: bool
11
+ # Absolute path to persist; None means session-only (no disk write)
12
+ save_path: str | None = None
13
+ # When saving a parent path, also remove this child path from the store
14
+ clear_child_path: str | None = None
tau/trust/utils.py ADDED
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from tau.settings.paths import CONFIG_DIR_NAME
6
+ from tau.trust.types import TrustOption
7
+
8
+
9
+ def normalize(cwd: str | Path) -> str:
10
+ """Resolve *cwd* to an absolute POSIX string."""
11
+ return str(Path(cwd).resolve())
12
+
13
+
14
+ def find_nearest(data: dict[str, bool | None], cwd: str) -> tuple[str, bool] | None:
15
+ """Walk up from *cwd* and return ``(path, decision)`` for the closest stored entry."""
16
+ current = normalize(cwd)
17
+ while True:
18
+ val = data.get(current)
19
+ if val is True:
20
+ return current, True
21
+ if val is False:
22
+ return current, False
23
+ parent = str(Path(current).parent)
24
+ if parent == current:
25
+ return None
26
+ current = parent
27
+
28
+
29
+ def has_project_trust_inputs(cwd: str | Path) -> bool:
30
+ """Return ``True`` if *cwd* (or any ancestor) contains files that require a trust decision.
31
+
32
+ Specifically looks for:
33
+ - A ``.tau/`` local config directory
34
+ - A ``.agents/skills/`` directory (shared-skill convention)
35
+ """
36
+ current = Path(normalize(cwd))
37
+ while True:
38
+ if (current / CONFIG_DIR_NAME).exists():
39
+ return True
40
+ if (current / ".agents" / "skills").exists():
41
+ return True
42
+ parent = current.parent
43
+ if parent == current:
44
+ return False
45
+ current = parent
46
+
47
+
48
+ def get_trust_options(cwd: str | Path, *, session_only: bool = True) -> list[TrustOption]:
49
+ """Build the ordered list of trust choices to present to the user.
50
+
51
+ Args:
52
+ cwd: Project working directory.
53
+ session_only: Include a "Trust (this session only)" option that does not
54
+ persist the decision to disk.
55
+ """
56
+ resolved = normalize(cwd)
57
+ parent = str(Path(resolved).parent)
58
+
59
+ options: list[TrustOption] = [
60
+ TrustOption(label="Trust", trusted=True, save_path=resolved),
61
+ ]
62
+ if parent != resolved:
63
+ options.append(TrustOption(
64
+ label=f"Trust parent folder ({parent})",
65
+ trusted=True,
66
+ save_path=parent,
67
+ clear_child_path=resolved,
68
+ ))
69
+ if session_only:
70
+ options.append(TrustOption(label="Trust (this session only)", trusted=True, save_path=None))
71
+ options.append(TrustOption(label="Do not trust", trusted=False, save_path=resolved))
72
+ return options
tau/tui/__init__.py ADDED
@@ -0,0 +1,54 @@
1
+ # Public API — import from here instead of individual submodules.
2
+ #
3
+ # Theme types: from tau.tui import LayoutTheme, SpinnerTheme, MessageTheme
4
+ # Keybindings: from tau.tui import configure_keybindings, get_keybindings
5
+ # Components: from tau.tui import SelectList, SelectItem, Box
6
+ #
7
+ # Themes: from tau.themes.registry import theme_registry
8
+
9
+ from tau.tui.theme import (
10
+ ColorFn,
11
+ LayoutTheme,
12
+ SpinnerTheme,
13
+ MarkdownTheme,
14
+ MessageTheme,
15
+ InputTheme,
16
+ SelectListTheme,
17
+ color,
18
+ rgb,
19
+ rgb_bold,
20
+ rgb_italic,
21
+ )
22
+ from tau.tui.keybindings import (
23
+ KeyMap,
24
+ KeybindingsManager,
25
+ get_keybindings,
26
+ configure_keybindings,
27
+ )
28
+ from tau.tui.components.select_list import SelectList, SelectItem
29
+ from tau.tui.components.box import Box
30
+
31
+ __all__ = [
32
+ # Theme types
33
+ "ColorFn",
34
+ "LayoutTheme",
35
+ "SpinnerTheme",
36
+ "MarkdownTheme",
37
+ "MessageTheme",
38
+ "InputTheme",
39
+ "SelectListTheme",
40
+ # Keybindings
41
+ "KeyMap",
42
+ "KeybindingsManager",
43
+ "get_keybindings",
44
+ "configure_keybindings",
45
+ # Color-function builders
46
+ "color",
47
+ "rgb",
48
+ "rgb_bold",
49
+ "rgb_italic",
50
+ # Components
51
+ "SelectList",
52
+ "SelectItem",
53
+ "Box",
54
+ ]