stcode 0.0.1a0__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.
- stcode/__init__.py +10 -0
- stcode/cli/__init__.py +9 -0
- stcode/cli/bootstrap.py +70 -0
- stcode/cli/labels.py +621 -0
- stcode/cli/logic/__init__.py +0 -0
- stcode/cli/logic/commands.py +36 -0
- stcode/cli/logic/completion.py +43 -0
- stcode/cli/logic/connection.py +46 -0
- stcode/cli/logic/requests.py +18 -0
- stcode/cli/logic/sessions.py +67 -0
- stcode/cli/logic/settings.py +35 -0
- stcode/cli/logic/state.py +43 -0
- stcode/cli/logic/workspace.py +17 -0
- stcode/cli/main.py +290 -0
- stcode/cli/models.py +61 -0
- stcode/cli/services/__init__.py +0 -0
- stcode/cli/services/client.py +393 -0
- stcode/cli/services/launcher.py +159 -0
- stcode/cli/services/paths.py +18 -0
- stcode/cli/services/preferences.py +107 -0
- stcode/cli/ui/__init__.py +0 -0
- stcode/cli/ui/app.py +136 -0
- stcode/cli/ui/app.tcss +3 -0
- stcode/cli/ui/components/__init__.py +0 -0
- stcode/cli/ui/components/banner.py +78 -0
- stcode/cli/ui/components/cards.py +229 -0
- stcode/cli/ui/components/prompt.py +225 -0
- stcode/cli/ui/components/status.py +95 -0
- stcode/cli/ui/components/transcript/__init__.py +51 -0
- stcode/cli/ui/components/transcript/entries.py +232 -0
- stcode/cli/ui/components/transcript/formatting.py +188 -0
- stcode/cli/ui/components/transcript/view.py +101 -0
- stcode/cli/ui/screens/__init__.py +0 -0
- stcode/cli/ui/screens/chat/__init__.py +0 -0
- stcode/cli/ui/screens/chat/chat.tcss +12 -0
- stcode/cli/ui/screens/chat/presenter.py +187 -0
- stcode/cli/ui/screens/chat/screen.py +682 -0
- stcode/cli/ui/screens/connect.py +126 -0
- stcode/cli/ui/screens/connect.tcss +49 -0
- stcode/cli/ui/screens/dialog.tcss +40 -0
- stcode/cli/ui/screens/sessions.py +105 -0
- stcode/cli/ui/screens/settings.py +137 -0
- stcode/cli/ui/screens/settings.tcss +69 -0
- stcode/cli/ui/screens/trust.py +50 -0
- stcode/cli/ui/theme.py +221 -0
- stcode/core/__main__.py +7 -0
- stcode/core/agent/__init__.py +41 -0
- stcode/core/agent/agent.py +651 -0
- stcode/core/agent/events.py +97 -0
- stcode/core/agent/supervisor.py +226 -0
- stcode/core/agent/task.py +120 -0
- stcode/core/common/__init__.py +21 -0
- stcode/core/common/compat.py +27 -0
- stcode/core/common/ids.py +43 -0
- stcode/core/common/tools.py +50 -0
- stcode/core/common/trace.py +406 -0
- stcode/core/common/truncate.py +76 -0
- stcode/core/configs.py +490 -0
- stcode/core/daemon/__init__.py +32 -0
- stcode/core/daemon/autonomy.py +71 -0
- stcode/core/daemon/main.py +126 -0
- stcode/core/daemon/protocol.py +499 -0
- stcode/core/daemon/runner.py +359 -0
- stcode/core/daemon/server.py +682 -0
- stcode/core/daemon/workspace.py +88 -0
- stcode/core/harness/__init__.py +81 -0
- stcode/core/harness/approvals.py +85 -0
- stcode/core/harness/context.py +260 -0
- stcode/core/harness/errors.py +40 -0
- stcode/core/harness/harness.py +417 -0
- stcode/core/harness/mcp.py +530 -0
- stcode/core/harness/outputs.py +99 -0
- stcode/core/harness/prompts/__init__.py +138 -0
- stcode/core/harness/prompts/execute.py +51 -0
- stcode/core/harness/prompts/plan.py +57 -0
- stcode/core/harness/prompts/sections.py +238 -0
- stcode/core/harness/skills/__init__.py +30 -0
- stcode/core/harness/skills/loader.py +247 -0
- stcode/core/harness/tools/__init__.py +111 -0
- stcode/core/harness/tools/base.py +640 -0
- stcode/core/harness/tools/files.py +276 -0
- stcode/core/harness/tools/interact.py +72 -0
- stcode/core/harness/tools/registry.py +113 -0
- stcode/core/harness/tools/repl.py +97 -0
- stcode/core/harness/tools/schema.py +186 -0
- stcode/core/harness/tools/search.py +356 -0
- stcode/core/harness/tools/shell.py +372 -0
- stcode/core/harness/tools/skill.py +66 -0
- stcode/core/harness/tools/todo.py +62 -0
- stcode/core/harness/tools/web.py +163 -0
- stcode/core/providers/__init__.py +31 -0
- stcode/core/providers/anthropic_claude.py +243 -0
- stcode/core/providers/base.py +73 -0
- stcode/core/providers/gateway.py +267 -0
- stcode/core/providers/google_gemini.py +225 -0
- stcode/core/providers/openai_gpt.py +250 -0
- stcode/core/providers/registry.py +46 -0
- stcode/core/providers/types.py +148 -0
- stcode/core/repl/__init__.py +19 -0
- stcode/core/repl/_worker.py +179 -0
- stcode/core/repl/client.py +374 -0
- stcode/core/session/__init__.py +39 -0
- stcode/core/session/session.py +493 -0
- stcode/core/team/__init__.py +38 -0
- stcode/core/team/mailbox.py +249 -0
- stcode/core/team/tools.py +145 -0
- stcode-0.0.1a0.dist-info/METADATA +229 -0
- stcode-0.0.1a0.dist-info/RECORD +111 -0
- stcode-0.0.1a0.dist-info/WHEEL +4 -0
- stcode-0.0.1a0.dist-info/entry_points.txt +3 -0
- stcode-0.0.1a0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Skill loading — `SKILL.md` files the agent pulls in when it needs them.
|
|
3
|
+
|
|
4
|
+
A skill is a folder with a `SKILL.md`: YAML frontmatter naming it and saying when it
|
|
5
|
+
applies, then markdown instructions, then whatever they reference.
|
|
6
|
+
|
|
7
|
+
~/.agents/skills/pdf/
|
|
8
|
+
SKILL.md <- frontmatter + instructions
|
|
9
|
+
reference.md <- read on demand, from inside the instructions
|
|
10
|
+
|
|
11
|
+
**Progressive disclosure is the whole design.** Discovery reads only the frontmatter;
|
|
12
|
+
the body loads on `skill(name)`, its references only if the agent follows them. Nine
|
|
13
|
+
skills cost ~400 tokens of catalogue instead of ~90,000 of instructions.
|
|
14
|
+
|
|
15
|
+
The format and default path (`~/.agents/skills`) are the cross-agent convention.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Iterator
|
|
24
|
+
|
|
25
|
+
SKILL_FILENAMES = ("SKILL.md", "skill.md")
|
|
26
|
+
USER_SKILLS_DIR = Path.home() / ".agents" / "skills"
|
|
27
|
+
PROJECT_SKILLS_DIRNAME = ".agents/skills"
|
|
28
|
+
SKILLS_PATH_ENV = "STCODE_SKILLS_PATH"
|
|
29
|
+
"""Colon-separated extra directories, searched before the conventional ones."""
|
|
30
|
+
|
|
31
|
+
MAX_SKILL_BYTES = 256 * 1024
|
|
32
|
+
"""A SKILL.md larger than this is a mistake — long material belongs in a file the
|
|
33
|
+
instructions point at, so it is read only when it is actually needed."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class Skill:
|
|
38
|
+
"""One discovered skill. `body` is None until someone asks for it."""
|
|
39
|
+
|
|
40
|
+
name: str
|
|
41
|
+
description: str
|
|
42
|
+
path: Path
|
|
43
|
+
"""The `SKILL.md` itself. `path.parent` is the skill's root, which is what relative
|
|
44
|
+
references inside the body resolve against."""
|
|
45
|
+
|
|
46
|
+
source: str = "user"
|
|
47
|
+
metadata: dict[str, str | list[str]] = field(default_factory=dict)
|
|
48
|
+
body: str | None = None
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def root(self) -> Path:
|
|
52
|
+
return self.path.parent
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def allowed_tools(self) -> list[str]:
|
|
56
|
+
"""Tools the skill declares it needs, if it declares any.
|
|
57
|
+
|
|
58
|
+
Advisory, not a sandbox: it narrows what a sub-agent is *given* when a skill is
|
|
59
|
+
the reason it was spawned. A skill cannot grant itself a tool the session does
|
|
60
|
+
not already have.
|
|
61
|
+
"""
|
|
62
|
+
raw = self.metadata.get("allowed-tools") or self.metadata.get("allowed_tools") or []
|
|
63
|
+
return raw if isinstance(raw, list) else [item.strip() for item in raw.split(",") if item.strip()]
|
|
64
|
+
|
|
65
|
+
def load(self) -> str:
|
|
66
|
+
"""Read and cache the instructions."""
|
|
67
|
+
if self.body is None:
|
|
68
|
+
_, self.body = parse_skill_file(self.path)
|
|
69
|
+
return self.body
|
|
70
|
+
|
|
71
|
+
def catalogue_line(self) -> str:
|
|
72
|
+
return f"- **{self.name}** — {self.description}"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def parse_frontmatter(text: str) -> tuple[dict[str, str | list[str]], str]:
|
|
76
|
+
"""Split `---`-delimited frontmatter from the body.
|
|
77
|
+
|
|
78
|
+
A deliberately small YAML subset — scalars, quoted scalars, block lists, inline
|
|
79
|
+
lists, and indented continuations — rather than a YAML dependency. Skill
|
|
80
|
+
frontmatter is two or three keys in practice, and the failure mode of a partial
|
|
81
|
+
parser here is a skill with a missing field, which `discover` reports and skips.
|
|
82
|
+
"""
|
|
83
|
+
metadata: dict[str, str | list[str]] = {}
|
|
84
|
+
if not text.startswith("---"):
|
|
85
|
+
return metadata, text
|
|
86
|
+
|
|
87
|
+
lines = text.splitlines()
|
|
88
|
+
try:
|
|
89
|
+
end = next(i for i in range(1, len(lines)) if lines[i].strip() == "---")
|
|
90
|
+
except StopIteration:
|
|
91
|
+
return metadata, text # unterminated block: it is body text, not frontmatter
|
|
92
|
+
|
|
93
|
+
key: str | None = None
|
|
94
|
+
for line in lines[1:end]:
|
|
95
|
+
if not line.strip() or line.lstrip().startswith("#"):
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
if line.lstrip().startswith("- ") and key:
|
|
99
|
+
existing = metadata.get(key)
|
|
100
|
+
item = _unquote(line.lstrip()[2:].strip())
|
|
101
|
+
metadata[key] = [*existing, item] if isinstance(existing, list) else [item]
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
if line[0].isspace() and key:
|
|
105
|
+
# A wrapped scalar. Join with a space; the newline was formatting.
|
|
106
|
+
previous = metadata.get(key)
|
|
107
|
+
if isinstance(previous, str):
|
|
108
|
+
metadata[key] = f"{previous} {line.strip()}"
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
name, separator, value = line.partition(":")
|
|
112
|
+
if not separator:
|
|
113
|
+
continue
|
|
114
|
+
key = name.strip()
|
|
115
|
+
value = value.strip()
|
|
116
|
+
if value.startswith("[") and value.endswith("]"):
|
|
117
|
+
metadata[key] = [_unquote(part.strip()) for part in value[1:-1].split(",") if part.strip()]
|
|
118
|
+
else:
|
|
119
|
+
metadata[key] = _unquote(value)
|
|
120
|
+
|
|
121
|
+
return metadata, "\n".join(lines[end + 1 :]).lstrip("\n")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _unquote(value: str) -> str:
|
|
125
|
+
if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'":
|
|
126
|
+
return value[1:-1]
|
|
127
|
+
return value
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def parse_skill_file(path: Path) -> tuple[dict[str, str | list[str]], str]:
|
|
131
|
+
text = path.read_text(encoding="utf-8", errors="replace")
|
|
132
|
+
if len(text.encode("utf-8")) > MAX_SKILL_BYTES:
|
|
133
|
+
text = text[:MAX_SKILL_BYTES]
|
|
134
|
+
return parse_frontmatter(text)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def skill_search_paths(cwd: Path | None = None) -> list[Path]:
|
|
138
|
+
"""Where skills are looked for, most specific first.
|
|
139
|
+
|
|
140
|
+
A project's own `.agents/skills` beats the user's, so a repository can ship a skill
|
|
141
|
+
that overrides a personal one of the same name — the repository is the more
|
|
142
|
+
specific statement about how work in it should be done.
|
|
143
|
+
"""
|
|
144
|
+
paths: list[Path] = []
|
|
145
|
+
for entry in os.environ.get(SKILLS_PATH_ENV, "").split(os.pathsep):
|
|
146
|
+
if entry.strip():
|
|
147
|
+
paths.append(Path(entry.strip()).expanduser())
|
|
148
|
+
paths.append((cwd or Path.cwd()) / PROJECT_SKILLS_DIRNAME)
|
|
149
|
+
paths.append(USER_SKILLS_DIR)
|
|
150
|
+
return paths
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _skill_files(directory: Path) -> Iterator[Path]:
|
|
154
|
+
if not directory.is_dir():
|
|
155
|
+
return
|
|
156
|
+
for entry in sorted(directory.iterdir()):
|
|
157
|
+
if entry.is_dir():
|
|
158
|
+
for filename in SKILL_FILENAMES:
|
|
159
|
+
candidate = entry / filename
|
|
160
|
+
if candidate.is_file():
|
|
161
|
+
yield candidate
|
|
162
|
+
break
|
|
163
|
+
elif entry.suffix == ".md" and entry.name not in SKILL_FILENAMES:
|
|
164
|
+
# A bare `<name>.md` is a skill with no bundled files. Common enough for
|
|
165
|
+
# short, self-contained instructions that rejecting it would be pedantry.
|
|
166
|
+
yield entry
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class SkillRegistry:
|
|
170
|
+
"""The skills available this session, indexed by name.
|
|
171
|
+
|
|
172
|
+
Discovery is frontmatter-only and happens once at startup: it stats a handful of
|
|
173
|
+
directories and reads a few hundred bytes from each `SKILL.md`. Bodies are read
|
|
174
|
+
lazily, so the cost of having fifty skills installed and using none is negligible.
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
def __init__(self, skills: dict[str, Skill] | None = None) -> None:
|
|
178
|
+
self._skills: dict[str, Skill] = skills or {}
|
|
179
|
+
self.problems: list[str] = []
|
|
180
|
+
"""Skills that were found but could not be read. Surfaced rather than swallowed —
|
|
181
|
+
a skill that silently fails to load looks identical to one that was never
|
|
182
|
+
written, and the user has no way to tell which."""
|
|
183
|
+
|
|
184
|
+
def __len__(self) -> int:
|
|
185
|
+
return len(self._skills)
|
|
186
|
+
|
|
187
|
+
def __contains__(self, name: str) -> bool:
|
|
188
|
+
return name in self._skills
|
|
189
|
+
|
|
190
|
+
def __iter__(self) -> Iterator[Skill]:
|
|
191
|
+
return iter(self._skills.values())
|
|
192
|
+
|
|
193
|
+
def get(self, name: str) -> Skill | None:
|
|
194
|
+
return self._skills.get(name)
|
|
195
|
+
|
|
196
|
+
def names(self) -> list[str]:
|
|
197
|
+
return sorted(self._skills)
|
|
198
|
+
|
|
199
|
+
def add(self, skill: Skill) -> None:
|
|
200
|
+
# First writer wins: `skill_search_paths` is ordered most-specific-first, so a
|
|
201
|
+
# project skill must not be replaced by the user-global one behind it.
|
|
202
|
+
self._skills.setdefault(skill.name, skill)
|
|
203
|
+
|
|
204
|
+
def catalogue(self) -> str:
|
|
205
|
+
"""The listing that goes in the system prompt — names and descriptions only."""
|
|
206
|
+
if not self._skills:
|
|
207
|
+
return ""
|
|
208
|
+
return "\n".join(skill.catalogue_line() for skill in sorted(self._skills.values(), key=lambda s: s.name))
|
|
209
|
+
|
|
210
|
+
@classmethod
|
|
211
|
+
def discover(cls, cwd: Path | None = None, paths: list[Path] | None = None) -> "SkillRegistry":
|
|
212
|
+
registry = cls()
|
|
213
|
+
project_dir = (cwd or Path.cwd()) / PROJECT_SKILLS_DIRNAME
|
|
214
|
+
for directory in paths or skill_search_paths(cwd):
|
|
215
|
+
# Compared as a path, not a substring: `~/.agents/skills` also *contains*
|
|
216
|
+
# ".agents/skills", so a substring test labels every user skill "project".
|
|
217
|
+
source = "project" if directory == project_dir else "user"
|
|
218
|
+
for path in _skill_files(directory):
|
|
219
|
+
try:
|
|
220
|
+
metadata, _ = parse_skill_file(path)
|
|
221
|
+
except OSError as exc:
|
|
222
|
+
registry.problems.append(f"{path}: {exc.strerror}")
|
|
223
|
+
continue
|
|
224
|
+
|
|
225
|
+
name = str(metadata.get("name") or path.parent.name if path.name in SKILL_FILENAMES else path.stem)
|
|
226
|
+
description = str(metadata.get("description") or "")
|
|
227
|
+
if not description:
|
|
228
|
+
registry.problems.append(f"{path}: no `description` in its frontmatter, skipped")
|
|
229
|
+
continue
|
|
230
|
+
registry.add(
|
|
231
|
+
Skill(name=name, description=description, path=path, source=source, metadata=metadata)
|
|
232
|
+
)
|
|
233
|
+
return registry
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
__all__ = [
|
|
237
|
+
"MAX_SKILL_BYTES",
|
|
238
|
+
"PROJECT_SKILLS_DIRNAME",
|
|
239
|
+
"SKILLS_PATH_ENV",
|
|
240
|
+
"SKILL_FILENAMES",
|
|
241
|
+
"USER_SKILLS_DIR",
|
|
242
|
+
"Skill",
|
|
243
|
+
"SkillRegistry",
|
|
244
|
+
"parse_frontmatter",
|
|
245
|
+
"parse_skill_file",
|
|
246
|
+
"skill_search_paths",
|
|
247
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Built-in tools, and the named sets an agent is spawned with.
|
|
3
|
+
|
|
4
|
+
The sets matter as much as the tools:
|
|
5
|
+
|
|
6
|
+
* `MAIN_TOOLS` — a top-level agent's allowance.
|
|
7
|
+
* `WORKER_TOOLS` — what `task` hands a sub-agent. No `task` and no `repl`: one that can
|
|
8
|
+
spawn is one for which `max_depth` bounds nothing, and one holding a persistent
|
|
9
|
+
namespace is doing the parent's job without the parent's oversight.
|
|
10
|
+
* `READ_ONLY_TOOLS` — derived from declared permissions, so it cannot drift.
|
|
11
|
+
|
|
12
|
+
`web_search` is in both. Without `TAVILY_API_KEY` it says so on the first call rather
|
|
13
|
+
than being missing with no explanation.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from stcode.core.harness.tools.base import (
|
|
17
|
+
DEFAULT_MAX_OUTPUT,
|
|
18
|
+
ApprovalRequest,
|
|
19
|
+
Question,
|
|
20
|
+
Runtime,
|
|
21
|
+
Tool,
|
|
22
|
+
ToolCancelled,
|
|
23
|
+
ToolDenied,
|
|
24
|
+
ToolError,
|
|
25
|
+
ToolForbidden,
|
|
26
|
+
ToolPermission,
|
|
27
|
+
ToolResult,
|
|
28
|
+
ToolTimeout,
|
|
29
|
+
current_runtime,
|
|
30
|
+
to_tool_definition,
|
|
31
|
+
tool,
|
|
32
|
+
)
|
|
33
|
+
from stcode.core.harness.tools.files import edit, read, write
|
|
34
|
+
from stcode.core.harness.tools.interact import ask_user_question
|
|
35
|
+
from stcode.core.harness.tools.registry import ToolRegistry
|
|
36
|
+
from stcode.core.harness.tools.repl import repl
|
|
37
|
+
from stcode.core.harness.tools.schema import schema_from_signature
|
|
38
|
+
from stcode.core.harness.tools.search import glob, grep, ls
|
|
39
|
+
from stcode.core.harness.tools.shell import bash, bash_output
|
|
40
|
+
from stcode.core.harness.tools.skill import skill
|
|
41
|
+
from stcode.core.harness.tools.todo import todo_write
|
|
42
|
+
from stcode.core.harness.tools.web import web_search
|
|
43
|
+
|
|
44
|
+
BUILTIN_TOOLS: tuple[Tool[object], ...] = (
|
|
45
|
+
read,
|
|
46
|
+
write,
|
|
47
|
+
edit,
|
|
48
|
+
bash,
|
|
49
|
+
bash_output,
|
|
50
|
+
glob,
|
|
51
|
+
grep,
|
|
52
|
+
ls,
|
|
53
|
+
todo_write,
|
|
54
|
+
web_search,
|
|
55
|
+
ask_user_question,
|
|
56
|
+
skill,
|
|
57
|
+
repl,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
WORKER_TOOLS: tuple[str, ...] = (
|
|
61
|
+
"read", "write", "edit", "bash", "bash_output", "glob", "grep", "ls",
|
|
62
|
+
"todo_write", "ask_user_question", "skill", "web_search",
|
|
63
|
+
)
|
|
64
|
+
"""What a sub-agent gets. No `task`, no `repl` — see the module docstring."""
|
|
65
|
+
|
|
66
|
+
MAIN_TOOLS: tuple[str, ...] = (*WORKER_TOOLS, "repl")
|
|
67
|
+
"""What a top-level agent gets. `task` is added on top by `core/agent/` at construction:
|
|
68
|
+
it cannot live here, because a tool that spawns an `Agent` would point `core/harness` at
|
|
69
|
+
its own caller."""
|
|
70
|
+
|
|
71
|
+
READ_ONLY_TOOLS: tuple[str, ...] = tuple(
|
|
72
|
+
builtin.name for builtin in BUILTIN_TOOLS if builtin.permission is ToolPermission.READ
|
|
73
|
+
)
|
|
74
|
+
"""Derived from the declared permissions rather than hand-listed, so it cannot drift."""
|
|
75
|
+
|
|
76
|
+
__all__ = [
|
|
77
|
+
"BUILTIN_TOOLS",
|
|
78
|
+
"DEFAULT_MAX_OUTPUT",
|
|
79
|
+
"MAIN_TOOLS",
|
|
80
|
+
"READ_ONLY_TOOLS",
|
|
81
|
+
"WORKER_TOOLS",
|
|
82
|
+
"ApprovalRequest",
|
|
83
|
+
"Question",
|
|
84
|
+
"Runtime",
|
|
85
|
+
"Tool",
|
|
86
|
+
"ToolCancelled",
|
|
87
|
+
"ToolDenied",
|
|
88
|
+
"ToolError",
|
|
89
|
+
"ToolForbidden",
|
|
90
|
+
"ToolPermission",
|
|
91
|
+
"ToolRegistry",
|
|
92
|
+
"ToolResult",
|
|
93
|
+
"ToolTimeout",
|
|
94
|
+
"ask_user_question",
|
|
95
|
+
"bash",
|
|
96
|
+
"bash_output",
|
|
97
|
+
"current_runtime",
|
|
98
|
+
"edit",
|
|
99
|
+
"glob",
|
|
100
|
+
"grep",
|
|
101
|
+
"ls",
|
|
102
|
+
"read",
|
|
103
|
+
"repl",
|
|
104
|
+
"schema_from_signature",
|
|
105
|
+
"skill",
|
|
106
|
+
"to_tool_definition",
|
|
107
|
+
"todo_write",
|
|
108
|
+
"tool",
|
|
109
|
+
"web_search",
|
|
110
|
+
"write",
|
|
111
|
+
]
|