SuperPi.Env.Workspace (super_pi v0.1.0)

Copy Markdown View Source

The workspace of a conversation: where file and command tools act.

It is configuration, stored in the conversation's pi.agent document:

"workspace" => %{"kind" => "local"}                              # default
"workspace" => %{"kind" => "shared", "root" => "/mnt/shared/ws"}
"workspace" => %{"kind" => "scratch"}                            # also "base", "lifetime"
"workspace" => %{"kind" => "none"}
KindWhereSurvives node lossCapabilities
localThe disk of the node, as before (cwd and the permission policy decide).Nofs, exec, net
sharedA directory on storage that every node mounts. Paths are confined to root. Writes take a cross-node lock.Yesfs, exec, net
scratchA temporary directory on the node that runs the task, confined, removed when the task ends. "lifetime" => "conversation" keeps it until the sweep (scratch_ttl_hours, default 24). "base" or config :super_pi, scratch_dir sets the parent directory.Nofs, exec, net
noneNo filesystem or commands. The agent has the artifact tools, MCP tools and the web.n/anet

Network

net is on in every kind. "network" => false in the workspace map removes it, so a deployment can run an agent that reads a repository and cannot make outgoing calls: web, MCP and every other tool that declares a net_read or net_write effect is then not offered and is refused if called. This differs from Permissions (network: "deny"): the workspace decides whether the model is shown a network tool, the policy decides whether one call is allowed.

Capability of a tool

A tool states its effects (SuperPi.Tools.effects/2) and the effects give the capability: fs_write needs fs, exec needs exec, net_read and net_write need net, read and state_write need nothing. The built-in file and shell tools are matched by name, because read is also the effect of tools that read Super-Pi state. Artifact tools need artifacts. A tool that states no effects needs fs, exec and net, so an unknown tool is not offered where any of them is missing.

Artifact tools (SuperPi.Artifacts) are offered with shared, scratch and none, and with local when the workspace sets "artifacts" => true. A tool whose capability the workspace does not have is not offered and is refused if called. The tools never fall back to the local disk: a shared workspace without a root is an error.

Confinement of shared and scratch follows symlinks, so a link inside the workspace cannot lead out of it. It applies to file tools. A shell command runs with the root as its working directory but is not sandboxed by this module; use an OS-level sandbox for untrusted commands.

Summary

Functions

Resolves a workspace from its configuration map.

First capability a tool needs, or nil. Kept for callers that ask about one capability.

Removes the scratch directory of a finished task.

Absolute path for path inside a confined workspace. A relative path is joined to base (the root, or a cd inside it). Raises throw({:workspace_error, message}) for a path outside the root.

The tool list to offer a conversation: tools the workspace cannot serve are removed and the artifact tools are added when the workspace has them. Safe to call on every request.

Capabilities a tool needs, all of them: a list of :fs, :exec, :net or :artifacts. agent_cfg supplies the tool's declared effects (SuperPi.Tools.effects/2). See the module doc.

Resolves the workspace for a tool call. ctx has :conversation_id and, in a turn, :active_task_id.

True when the workspace can serve the tool.

Removes scratch directories that nothing needs: the directory of a task that is no longer running, and any directory idle longer than scratch_ttl_hours (default 24). Returns the number of directories removed.

Types

t()

@type t() :: %SuperPi.Env.Workspace{
  artifacts?: term(),
  capabilities: term(),
  confined?: term(),
  env: term(),
  kind: term(),
  notice: term(),
  root: term()
}

Functions

build(cfg, ctx \\ %{})

Resolves a workspace from its configuration map.

capability_for(tool, agent_cfg \\ nil)

First capability a tool needs, or nil. Kept for callers that ask about one capability.

cleanup_scratch(conversation_id, task_id)

Removes the scratch directory of a finished task.

contain!(workspace, path, base \\ nil)

Absolute path for path inside a confined workspace. A relative path is joined to base (the root, or a cd inside it). Raises throw({:workspace_error, message}) for a path outside the root.

offered_tools(tools, agent_cfg)

@spec offered_tools([map()], map()) :: [map()]

The tool list to offer a conversation: tools the workspace cannot serve are removed and the artifact tools are added when the workspace has them. Safe to call on every request.

required_capabilities(tool, agent_cfg \\ nil)

@spec required_capabilities(String.t(), map() | nil) :: [atom()]

Capabilities a tool needs, all of them: a list of :fs, :exec, :net or :artifacts. agent_cfg supplies the tool's declared effects (SuperPi.Tools.effects/2). See the module doc.

resolve(ctx)

@spec resolve(map()) :: {:ok, t()} | {:error, String.t()}

Resolves the workspace for a tool call. ctx has :conversation_id and, in a turn, :active_task_id.

supports?(ws, tool, agent_cfg \\ nil)

True when the workspace can serve the tool.

sweep(base \\ nil)

Removes scratch directories that nothing needs: the directory of a task that is no longer running, and any directory idle longer than scratch_ttl_hours (default 24). Returns the number of directories removed.