core/tools: point a refused write at a temporary workspace

A path outside the workspace is usually rejected while the model only wants a
scratch file: the refusal named the grant tool, so the model had to ask a human
for access it never needed. A write now also names `new_temporary_workspace`,
whose path needs no grant. Reads and commands keep the grant hint alone.
This commit is contained in:
2026-10-26 00:52:28 +08:00
parent a79039f06f
commit fb77fd676b
+8
View File
@@ -445,9 +445,17 @@ def resolve_path(path: str | Path, *, required: AccessMode = AccessMode.WRITE) -
candidate = root / candidate
resolved = candidate.expanduser().resolve()
if not _under_any_root(resolved, instance, policy) and not _path_grant_covers(resolved, required):
# A refused *write* is often a scratch file the model meant to keep in
# the workspace: name that way out too, next to asking the human.
scratch = (
"; if the file is only scratch, call new_temporary_workspace and write under the absolute path it returns"
if required is AccessMode.WRITE
else ""
)
msg = (
f"path escapes workspace root ({root}): {path}; "
f"call request_directory_access with mode={required.name.lower()!r} to request access outside the workspace"
f"{scratch}"
)
raise WorkspaceError(msg)
# Normalize separators so denylist entries like ``/secrets/`` match on Windows.