Workspace
Workspace gives an agent read/write/edit/search/shell access to a directory, with destructive operations gated behind human confirmation by default.
Workspace is a local-machine toolkit scoped to a single root directory. Reads (read, list, search) run silently; destructive operations (write, edit, move, delete, shell) require human confirmation by default through the run's HITL requirements.
Prerequisites
Create and activate a virtual environment, then install:
uv pip install agno openaiThe Agent model uses an OpenAI key, separately from any toolkit provider credentials.
Set OpenAI Key
Set your OPENAI_API_KEY as an environment variable. You can get one from OpenAI.
export OPENAI_API_KEY=sk-***Example
import tempfile
from pathlib import Path
from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.tools.workspace import Workspace
# Use a clean tmp directory so the demo doesn't touch real files.
workspace = Path(tempfile.mkdtemp(prefix="workspace_demo_"))
(workspace / "README.md").write_text(
"# Demo workspace\n\n"
"This file lives in a tmp directory.\n"
"The agent below will read it and produce a summary file.\n"
)
agent = Agent(
model=OpenAIResponses(id="gpt-5.4"),
tools=[
Workspace(
str(workspace),
allowed=Workspace.ALL_TOOLS,
confirm=[],
)
],
markdown=True,
)
if __name__ == "__main__":
agent.print_response(
"Read README.md, then write a 2-line summary to NOTES.md. "
"After that, list the files to confirm both exist."
)
print(f"\nWorkspace: {workspace}")This example passes confirm=[] to disable confirmation so it runs end-to-end without prompts. For production, leave the defaults on.
Permission Model
allowed and confirm are mutually exclusive partitions of short aliases:
| In list | Behavior |
|---|---|
allowed | Runs silently. |
confirm | Requires user approval via HITL pause/resume. |
| neither | Not registered with the toolkit. The LLM never sees it. |
| both | Raises ValueError. |
When both lists are None, reads auto-pass and writes require confirmation (the safe default). When only one is set, the other defaults to []. Use Workspace.ALL_TOOLS to register every tool.
| Alias | Registered tool | What it does |
|---|---|---|
read | read_file | Read a file (line-numbered). |
list | list_files | List a directory (recursive option). |
search | search_content | Recursive content grep. |
write | write_file | Create or overwrite a file (atomic). |
edit | edit_file | Replace a substring (with replace_all). |
move | move_file | Move or rename a file. |
delete | delete_file | Delete a file. |
shell | run_command | Run a shell command in root. |
This is a path-scoping boundary, not a process sandbox. Named file operations reject paths outside root. run_command executes a process with cwd=root and does not enforce those path or exclusion checks; it can access other files, environment variables and the network with ordinary process permissions. For untrusted code execution, run the agent inside a real sandbox (container, VM, or Daytona).
Toolkit Params
| Parameter | Type | Default | Description |
|---|---|---|---|
root | Optional[str|Path] | cwd | Directory all operations are scoped to. |
allowed | Optional[List[str]] | None | Aliases that run silently. |
confirm | Optional[List[str]] | None | Aliases that require confirmation. |
require_read_before_write | bool | False | Block writes/edits/move/delete on existing files until read through this toolkit instance. |
max_file_lines | int | 100000 | Maximum lines read_file will load. |
max_file_length | int | 10000000 | Maximum file length (characters) read_file will load. |
exclude_patterns | Optional[List[str]] | noise dirs | Patterns excluded from discovery and direct file operations. Pass [] to disable. |
allow_paths (Optional[List[str]], default None) accepts literal workspace-relative files or directories allowed through exclusions.
Exclusions apply to direct reads, writes, edits, moves and deletes as well as listing/search. Defaults cover noise directories and common credential paths. allow_paths=["build"] allows build/index.html, while an excluded child such as build/.env stays blocked. An explicitly allowed path beneath an excluded directory can be accessed by name even when recursive listing prunes its parent. Shell execution does not use these file exclusions.
Confirmation Flow
With aliases in confirm, the run pauses when the agent calls a gated tool. This separate example uses default confirmation and a disposable file. Inspect each operation, resolve its requirement, and resume using the response object; no session database is needed:
import tempfile
from pathlib import Path
from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.tools.workspace import Workspace
approval_root = Path(tempfile.mkdtemp(prefix="workspace_approval_"))
(approval_root / "old.log").write_text("Disposable tutorial log.\n")
approval_agent = Agent(
model=OpenAIResponses(id="gpt-5.4"),
tools=[Workspace(approval_root)],
)
run_response = approval_agent.run("Read old.log, then delete it.")
while run_response.is_paused:
for requirement in run_response.active_requirements:
if requirement.needs_confirmation:
print(requirement.tool_execution)
if input("Approve this operation? [y/N] ").strip().lower() == "y":
requirement.confirm()
else:
requirement.reject()
run_response = approval_agent.continue_run(
run_response=run_response,
requirements=run_response.requirements,
)See Workspace with confirmation for the full pause/resume example, and User Confirmation for the HITL pattern.
Developer Resources
- Tools source
- Basic usage
- With confirmation
- WorkspaceContextProvider wraps this toolkit read-only as a context provider