jeevesagent.tools¶
Tool registry and decorators + built-in filesystem / shell tools.
Users typically construct tools via tool() (decorator) and pass
the resulting Tool objects to Agent. The agent wraps
them in an InProcessToolHost.
For the canonical “Claude-Code-shaped” tool set (read / write / edit
/ bash), import the four factory functions from
jeevesagent.tools.builtin (also re-exported at the top level).
Submodules¶
Exceptions¶
Raised when a tool argument resolves outside its workdir. |
Classes¶
A dict-backed |
|
A registered tool: definition plus the callable that executes it. |
Functions¶
|
Build a |
|
Return the framework's default workdir for built-in tools, |
|
Build a |
|
Return all three filesystem tools (read + write + edit) |
|
Build a |
|
Promote a callable to a |
|
Build a |
Package Contents¶
- exception jeevesagent.tools.PathEscapeError[source]¶
Bases:
ValueErrorRaised when a tool argument resolves outside its workdir.
Initialize self. See help(type(self)) for accurate signature.
- class jeevesagent.tools.InProcessToolHost(tools: list[Tool | collections.abc.Callable[Ellipsis, Any]] | None = None)[source]¶
A dict-backed
ToolHost.- async call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') jeevesagent.core.types.ToolResult[source]¶
- register(item: Tool | collections.abc.Callable[Ellipsis, Any]) Tool[source]¶
- async watch() collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent][source]¶
In-process registry is static; the generator yields nothing.
Iterating over an empty tuple keeps this an async generator (so the return type is
AsyncIterator) without ever producing an event at runtime.
- class jeevesagent.tools.Tool[source]¶
A registered tool: definition plus the callable that executes it.
- async execute(args: collections.abc.Mapping[str, Any]) Any[source]¶
Invoke the underlying callable.
Async functions are awaited; sync functions are dispatched to a worker thread via
anyio.to_thread.run_sync()so they don’t block the event loop.
- fn: collections.abc.Callable[Ellipsis, Any]¶
- jeevesagent.tools.bash_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'bash', timeout: float = 30.0, allow_pattern: collections.abc.Callable[[str], bool] | None = None, extra_env: dict[str, str] | None = None) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat runs a shell command with the workdir as the current working directory.Default safety:
Commands matching the built-in destructive patterns (
rm -rf /,sudo,mkfs, fork bombs, …) are rejected before being executed.Commands run with a default
timeoutof 30 seconds; the subprocess is killed on timeout.The shell is invoked via
/bin/sh -c <command>, so pipelines + redirections work the way you’d expect.
Knobs:
allow_pattern— a callable that takes the command string and returns True if the command should run. When provided, it OVERRIDES the default deny list — you take full responsibility.extra_env— extra environment variables merged into the subprocess env.timeout— seconds before the command is killed.
workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).
- jeevesagent.tools.default_workdir() pathlib.Path[source]¶
Return the framework’s default workdir for built-in tools, creating it lazily on first call.
The directory is a fresh tempdir under
$TMPDIR/jeeves_agent_*, created once per process. All built-in tool factories share it when called without an explicitworkdirargument, so an Agent that registersread_tool()andwrite_tool()(no args) sees the same place.The directory is NOT auto-cleaned at process exit — leave that to the OS’s tempdir cleanup so debug data survives a crash.
- jeevesagent.tools.edit_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'edit') jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat does find-and-replace inside an existing file underworkdir.- The tool’s signature seen by the model:
- ``edit(path: str, old_string: str, new_string: str,
replace_all: bool = False)``
Behaviour matches Claude Code’s Edit tool:
old_stringmust be EXACTLY present in the file. Mismatch (whitespace, indentation, line breaks) → error.old_stringmust appear EXACTLY once in the file unlessreplace_all=Trueis passed — forces the model to give enough surrounding context for unambiguous matches.new_stringreplacesold_string(or every occurrence ifreplace_all=True).
workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).
- jeevesagent.tools.filesystem_tools(workdir: pathlib.Path | str | None = None) list[jeevesagent.tools.registry.Tool][source]¶
Return all three filesystem tools (read + write + edit) bound to a single workdir.
bash_toolis excluded — pair them only when you want shell access too.workdiris optional;Noneuses the framework’s default tempdir (shared with bash_tool() called the same way).
- jeevesagent.tools.read_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'read', line_limit: int = _DEFAULT_READ_LINE_LIMIT) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat reads a text file underworkdir.- The tool’s signature seen by the model:
read(path: str, offset: int = 0, limit: int | None = None)
Returns the file’s text with line numbers prefixed (one line per output line), in the same format Claude Code’s Read tool uses — that lets the
edittool work without ambiguity later. Long files are truncated toline_limitlines per call; passoffset/limitto read further chunks.Errors (file-not-found, path-escape) are returned as a string starting with
"ERROR: "rather than raising — the model sees them as a tool result and can adjust.workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools called without a workdir).
- jeevesagent.tools.tool(fn: collections.abc.Callable[Ellipsis, Any]) Tool[source]¶
- jeevesagent.tools.tool(*, name: str | None = None, description: str | None = None, destructive: bool = False) collections.abc.Callable[[collections.abc.Callable[Ellipsis, Any]], Tool]
Promote a callable to a
Tool.Use as
@tool(bare) or@tool(name=..., description=..., destructive=...). The schema is derived from parameter annotations; primitive types map to their JSON-Schema equivalents, anything else falls back tostring.
- jeevesagent.tools.write_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'write', create_parents: bool = True) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat writes / overwrites a text file underworkdir.- The tool’s signature seen by the model:
write(path: str, content: str)
Overwrites existing files. With
create_parents=True(the default), missing parent directories are created automatically.Returns a confirmation string with the byte count, or an
"ERROR: "-prefixed message on failure.workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).