Workspace tools
GET/PUT/DELETE /admin/workspace-tools/{name} — workspaces register their own tool definitions on top of the four built-in agent-runtime tools (python_exec, file_read, file_write, shell_exec).
User-uploaded tools merge into the same ToolRegistry the built-ins use, so workspace-published tools dispatch through the same Sandbox adapter the operator configured (subprocess on personal-tier, Docker sibling-container on SME/enterprise). Resource caps, fs fence, and audit hooks all apply automatically — there's no escape hatch where workspace-defined tools bypass the harness.
Endpoints
| Method | Path | Auth | Summary |
|---|---|---|---|
GET | /admin/workspace-tools | Bearer + workspace | List the caller workspace's tool definitions. |
PUT | /admin/workspace-tools/{name} | Bearer + workspace | Upsert one tool definition. |
DELETE | /admin/workspace-tools/{name} | Bearer + workspace | Remove one tool definition. |
Spec wire shape: WorkspaceToolSpec (packages/agent_runtime/workspace_tools.py:54):
class WorkspaceToolSpec(BaseModel):
name: str
description: str = ""
parameters: dict[str, Any] = {} # JSON-schema for tool args
handler_kind: HandlerKind # "shell_template" | "python_template"
handler_payload: dict[str, Any] = {}Two handler kinds ship today:
shell_template—handler_payload.commandis a string with{arg_name}placeholders. At dispatch time the agent's tool-call arguments arestr.format_map'd into the command and the result runs asbash -c "<rendered>"inside the sandbox.python_template— same shape but the rendered string runs aspython3 -c "<rendered>".
Both run through the sandbox; both honour the per-workspace SandboxPolicy (memory / cpu / pids / wall-clock / fs allowlist).
GET /admin/workspace-tools
Lists every workspace tool registered in the caller's workspace, ordered alphabetically by name. Cross-tenant tools are never enumerated.
curl https://your-host/admin/workspace-tools \
-H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN"PUT /admin/workspace-tools/{name}
Upsert by name. The body matches WorkspaceToolSpec. The path parameter overrides the body's name field (the route hard-codes the workspace fence so cross-tenant writes are impossible).
curl -X PUT https://your-host/admin/workspace-tools/grep_repo \
-H "Authorization: Bearer $BIGWAVE_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "grep_repo",
"description": "Search the repo for a regex pattern.",
"parameters": {
"type": "object",
"properties": {
"pattern": { "type": "string" }
},
"required": ["pattern"]
},
"handler_kind": "shell_template",
"handler_payload": {
"command": "grep -rn {pattern} /workspace"
}
}'Response: 200 OK with the persisted WorkspaceToolSpec.
DELETE /admin/workspace-tools/{name}
Removes the tool. Returns 204 No Content. 404 on unknown name (also for cross-tenant access — workspace fence).
Operator notes
- Tools dispatch through the configured Sandbox. A workspace tool cannot escape the harness — every dispatch is a
sandbox.run(handle, request)call, so resource caps + fs fence- audit hooks apply.
- Argument shell-quoting. For
shell_templatehandlers each argument value is automatically passed throughshlex.quotebefore being substituted into the command string. This prevents a model-supplied value such as"; rm -rf /"from breaking out of its intended position in the rendered shell command. Operators should still constrain theparametersJSON schema (e.g.enum,pattern,maxLength) to limit what values the model may supply. Note thatpython_templatehandlers are not shell-quoted — the rendered source is passed directly as the argument topython3 -c, which is a single argv element and is never interpreted by a shell. - Persistence. Tool definitions live in the
workspace_toolstable (migration 0018). Restarting the API process preserves the registrations. - Merge order. On every
POST /v1/agent-sessions/{id}/run, the runtime merges workspace tools on top of the four built-ins. A workspace tool with the same name as a built-in overrides the built-in for that session. Use the same name only when the override is intentional. - Errors:
400 Bad Request— malformed body, unknownhandler_kind.401 Unauthorized— missing bearer.404 Not Found— unknown name (delete) or cross-tenant.503 Service Unavailable—app.state.workspace_tool_repoisn't wired (lifespan-stripped tests; not a real-prod path).
Related
/how-to/11-tool-calls— operator runbook covering tool-call observability./admin/sandbox— per-workspaceSandboxPolicythat constrains how workspace tools execute.docs/runbooks/agent-harness— end-to-end agent-session runbook including workspace tools.- migration 0018 — the
workspace_toolstable schema.