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 openai

The 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

cookbook/91_tools/workspace_tools/basic_usage.py
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 listBehavior
allowedRuns silently.
confirmRequires user approval via HITL pause/resume.
neitherNot registered with the toolkit. The LLM never sees it.
bothRaises 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.

AliasRegistered toolWhat it does
readread_fileRead a file (line-numbered).
listlist_filesList a directory (recursive option).
searchsearch_contentRecursive content grep.
writewrite_fileCreate or overwrite a file (atomic).
editedit_fileReplace a substring (with replace_all).
movemove_fileMove or rename a file.
deletedelete_fileDelete a file.
shellrun_commandRun 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

ParameterTypeDefaultDescription
rootOptional[str|Path]cwdDirectory all operations are scoped to.
allowedOptional[List[str]]NoneAliases that run silently.
confirmOptional[List[str]]NoneAliases that require confirmation.
require_read_before_writeboolFalseBlock writes/edits/move/delete on existing files until read through this toolkit instance.
max_file_linesint100000Maximum lines read_file will load.
max_file_lengthint10000000Maximum file length (characters) read_file will load.
exclude_patternsOptional[List[str]]noise dirsPatterns 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