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.
- tau/__init__.py +0 -0
- tau/agent/__init__.py +11 -0
- tau/agent/prompt/__init__.py +10 -0
- tau/agent/prompt/builder.py +302 -0
- tau/agent/prompt/types.py +33 -0
- tau/agent/service.py +369 -0
- tau/agent/types.py +61 -0
- tau/auth/manager.py +247 -0
- tau/auth/storage.py +82 -0
- tau/auth/types.py +41 -0
- tau/builtins/__init__.py +4 -0
- tau/builtins/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/__pycache__/__init__.cpython-314.pyc +0 -0
- tau/builtins/commands/__init__.py +41 -0
- tau/builtins/commands/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/__init__.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/clear.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/clear.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/compact.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/compact.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/reload.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/reload.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/session.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/session.cpython-314.pyc +0 -0
- tau/builtins/commands/clear.py +16 -0
- tau/builtins/commands/compact.py +28 -0
- tau/builtins/commands/reload.py +27 -0
- tau/builtins/commands/session.py +19 -0
- tau/builtins/extensions/footer/__init__.py +76 -0
- tau/builtins/extensions/footer/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/__pycache__/git.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/__pycache__/model.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/__pycache__/utils.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/git.py +26 -0
- tau/builtins/extensions/footer/model.py +69 -0
- tau/builtins/extensions/footer/utils.py +44 -0
- tau/builtins/extensions/header/__init__.py +18 -0
- tau/builtins/extensions/header/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/models/__init__.py +0 -0
- tau/builtins/models/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/models/__pycache__/text.cpython-313.pyc +0 -0
- tau/builtins/models/audio.py +43 -0
- tau/builtins/models/image.py +43 -0
- tau/builtins/models/text.py +482 -0
- tau/builtins/models/video.py +40 -0
- tau/builtins/prompts/commit.md +7 -0
- tau/builtins/prompts/docs.md +7 -0
- tau/builtins/prompts/explain.md +7 -0
- tau/builtins/prompts/fix.md +7 -0
- tau/builtins/prompts/refactor.md +7 -0
- tau/builtins/prompts/review.md +7 -0
- tau/builtins/prompts/test.md +7 -0
- tau/builtins/providers/__init__.py +0 -0
- tau/builtins/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/providers/__pycache__/text.cpython-313.pyc +0 -0
- tau/builtins/providers/audio.py +10 -0
- tau/builtins/providers/image.py +9 -0
- tau/builtins/providers/text.py +33 -0
- tau/builtins/providers/video.py +6 -0
- tau/builtins/skills/code-review/SKILL.md +4 -0
- tau/builtins/skills/debug/SKILL.md +4 -0
- tau/builtins/skills/git-commit/SKILL.md +4 -0
- tau/builtins/themes/dark.yaml +1 -0
- tau/builtins/themes/light.yaml +46 -0
- tau/builtins/tools/__init__.py +73 -0
- tau/builtins/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/bash.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/bash.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/edit.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/edit.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/glob.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/glob.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/grep.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/grep.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/ls.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/ls.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/read.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/read.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/terminal.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/terminal.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/write.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/write.cpython-314.pyc +0 -0
- tau/builtins/tools/edit.py +215 -0
- tau/builtins/tools/glob.py +112 -0
- tau/builtins/tools/grep.py +146 -0
- tau/builtins/tools/ls.py +135 -0
- tau/builtins/tools/read.py +122 -0
- tau/builtins/tools/terminal.py +150 -0
- tau/builtins/tools/write.py +105 -0
- tau/commands/__init__.py +10 -0
- tau/commands/registry.py +71 -0
- tau/commands/types.py +33 -0
- tau/console/__init__.py +0 -0
- tau/console/cli.py +266 -0
- tau/console/commands/__init__.py +0 -0
- tau/console/commands/auth.py +193 -0
- tau/console/commands/packages.py +104 -0
- tau/console/commands/update.py +76 -0
- tau/core/__init__.py +0 -0
- tau/core/registry.py +102 -0
- tau/engine/__init__.py +47 -0
- tau/engine/service.py +768 -0
- tau/engine/types.py +163 -0
- tau/extensions/__init__.py +28 -0
- tau/extensions/api.py +928 -0
- tau/extensions/context.py +462 -0
- tau/extensions/events.py +70 -0
- tau/extensions/loader.py +386 -0
- tau/extensions/runtime.py +184 -0
- tau/extensions/settings.py +137 -0
- tau/hooks/__init__.py +112 -0
- tau/hooks/engine.py +237 -0
- tau/hooks/inference.py +21 -0
- tau/hooks/runtime.py +126 -0
- tau/hooks/service.py +121 -0
- tau/hooks/session.py +117 -0
- tau/hooks/tui.py +61 -0
- tau/hooks/types.py +72 -0
- tau/inference/__init__.py +180 -0
- tau/inference/api/__init__.py +0 -0
- tau/inference/api/audio/__init__.py +0 -0
- tau/inference/api/audio/base.py +29 -0
- tau/inference/api/audio/builtins.py +15 -0
- tau/inference/api/audio/elevenlabs_audio.py +183 -0
- tau/inference/api/audio/gemini_audio.py +95 -0
- tau/inference/api/audio/openai_audio.py +159 -0
- tau/inference/api/audio/registry.py +15 -0
- tau/inference/api/audio/sarvam_audio.py +163 -0
- tau/inference/api/audio/service.py +103 -0
- tau/inference/api/audio/utils.py +47 -0
- tau/inference/api/image/__init__.py +0 -0
- tau/inference/api/image/base.py +17 -0
- tau/inference/api/image/builtins.py +8 -0
- tau/inference/api/image/gemini_image.py +77 -0
- tau/inference/api/image/openai_image.py +103 -0
- tau/inference/api/image/openrouter.py +144 -0
- tau/inference/api/image/registry.py +15 -0
- tau/inference/api/image/service.py +71 -0
- tau/inference/api/registry.py +82 -0
- tau/inference/api/text/__init__.py +0 -0
- tau/inference/api/text/anthropic_claude_code.py +222 -0
- tau/inference/api/text/anthropic_messages.py +196 -0
- tau/inference/api/text/base.py +40 -0
- tau/inference/api/text/builtins.py +19 -0
- tau/inference/api/text/gemini_generate.py +234 -0
- tau/inference/api/text/github_copilot_chat.py +172 -0
- tau/inference/api/text/google_antigravity.py +522 -0
- tau/inference/api/text/mistral_chat.py +284 -0
- tau/inference/api/text/ollama_chat.py +200 -0
- tau/inference/api/text/openai_codex_responses.py +497 -0
- tau/inference/api/text/openai_completions.py +227 -0
- tau/inference/api/text/openai_responses.py +235 -0
- tau/inference/api/text/registry.py +50 -0
- tau/inference/api/text/service.py +297 -0
- tau/inference/api/text/types.py +7 -0
- tau/inference/api/text/utils.py +228 -0
- tau/inference/api/video/__init__.py +0 -0
- tau/inference/api/video/base.py +26 -0
- tau/inference/api/video/builtins.py +7 -0
- tau/inference/api/video/fal_video.py +119 -0
- tau/inference/api/video/openrouter_video.py +142 -0
- tau/inference/api/video/registry.py +15 -0
- tau/inference/api/video/service.py +72 -0
- tau/inference/model/__init__.py +0 -0
- tau/inference/model/registry.py +102 -0
- tau/inference/model/types.py +65 -0
- tau/inference/provider/__init__.py +0 -0
- tau/inference/provider/oauth/__init__.py +35 -0
- tau/inference/provider/oauth/anthropic_claude_code.py +286 -0
- tau/inference/provider/oauth/github_copilot.py +333 -0
- tau/inference/provider/oauth/google_antigravity.py +258 -0
- tau/inference/provider/oauth/openai_codex.py +309 -0
- tau/inference/provider/oauth/pkce.py +14 -0
- tau/inference/provider/oauth/types.py +46 -0
- tau/inference/provider/oauth/utils.py +154 -0
- tau/inference/provider/registry.py +141 -0
- tau/inference/provider/types.py +114 -0
- tau/inference/types.py +549 -0
- tau/inference/utils.py +219 -0
- tau/message/__init__.py +0 -0
- tau/message/types.py +482 -0
- tau/message/utils.py +178 -0
- tau/packages/__init__.py +11 -0
- tau/packages/manager.py +190 -0
- tau/packages/types.py +20 -0
- tau/packages/utils.py +67 -0
- tau/prompts/expand.py +58 -0
- tau/prompts/loader.py +69 -0
- tau/prompts/registry.py +45 -0
- tau/prompts/types.py +24 -0
- tau/rpc/__init__.py +8 -0
- tau/rpc/mode.py +783 -0
- tau/rpc/types.py +252 -0
- tau/runtime/service.py +759 -0
- tau/runtime/types.py +303 -0
- tau/session/branch_summarization.py +312 -0
- tau/session/compaction.py +646 -0
- tau/session/manager.py +652 -0
- tau/session/types.py +188 -0
- tau/session/utils.py +233 -0
- tau/settings/manager.py +1077 -0
- tau/settings/paths.py +150 -0
- tau/settings/storage.py +63 -0
- tau/settings/types.py +173 -0
- tau/settings/utils.py +25 -0
- tau/skills/loader.py +91 -0
- tau/skills/registry.py +70 -0
- tau/skills/types.py +25 -0
- tau/themes/loader.py +238 -0
- tau/themes/registry.py +108 -0
- tau/themes/types.py +19 -0
- tau/tool/__init__.py +3 -0
- tau/tool/registry.py +117 -0
- tau/tool/render.py +21 -0
- tau/tool/types.py +244 -0
- tau/trust/__init__.py +13 -0
- tau/trust/manager.py +80 -0
- tau/trust/types.py +14 -0
- tau/trust/utils.py +72 -0
- tau/tui/__init__.py +54 -0
- tau/tui/agent_hooks.py +346 -0
- tau/tui/ansi.py +330 -0
- tau/tui/app.py +540 -0
- tau/tui/autocomplete.py +33 -0
- tau/tui/capabilities.py +119 -0
- tau/tui/commands/__init__.py +3 -0
- tau/tui/commands/appearance.py +498 -0
- tau/tui/commands/auth.py +232 -0
- tau/tui/commands/context.py +38 -0
- tau/tui/commands/misc.py +82 -0
- tau/tui/commands/model.py +118 -0
- tau/tui/commands/session.py +464 -0
- tau/tui/component.py +268 -0
- tau/tui/components/__init__.py +0 -0
- tau/tui/components/autocomplete_manager.py +267 -0
- tau/tui/components/autocomplete_picker.py +143 -0
- tau/tui/components/box.py +90 -0
- tau/tui/components/command_palette.py +144 -0
- tau/tui/components/dynamic_border.py +19 -0
- tau/tui/components/file_picker.py +233 -0
- tau/tui/components/image.py +181 -0
- tau/tui/components/inline_selector.py +71 -0
- tau/tui/components/layout.py +1194 -0
- tau/tui/components/message_list.py +692 -0
- tau/tui/components/modal.py +97 -0
- tau/tui/components/model_palette.py +204 -0
- tau/tui/components/picker_overlay.py +174 -0
- tau/tui/components/prompt_overlay.py +236 -0
- tau/tui/components/resume_modal.py +372 -0
- tau/tui/components/select_list.py +222 -0
- tau/tui/components/settings_modal.py +274 -0
- tau/tui/components/settings_schema.py +203 -0
- tau/tui/components/spinner.py +119 -0
- tau/tui/components/text_input.py +396 -0
- tau/tui/components/text_prompt.py +82 -0
- tau/tui/components/tree_select_list.py +580 -0
- tau/tui/components/trust_screen.py +97 -0
- tau/tui/diff.py +114 -0
- tau/tui/fuzzy.py +99 -0
- tau/tui/input.py +496 -0
- tau/tui/input_handler.py +716 -0
- tau/tui/keybindings.py +87 -0
- tau/tui/markdown.py +286 -0
- tau/tui/message_renderers.py +31 -0
- tau/tui/overlay.py +326 -0
- tau/tui/renderer.py +378 -0
- tau/tui/terminal.py +499 -0
- tau/tui/theme.py +148 -0
- tau/tui/tui.py +544 -0
- tau/tui/ui_context.py +768 -0
- tau/tui/utils.py +20 -0
- tau/utils/__init__.py +0 -0
- tau/utils/http_proxy.py +221 -0
- tau/utils/image_processing.py +172 -0
- tau/utils/secrets.py +59 -0
- tau/utils/version_check.py +60 -0
- tau_coding_agent-0.1.0.dist-info/METADATA +177 -0
- tau_coding_agent-0.1.0.dist-info/RECORD +283 -0
- tau_coding_agent-0.1.0.dist-info/WHEEL +5 -0
- tau_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
- tau_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|
+
]
|