def delete_file(path: str, ctx: Context | None = None) -> str:
context_tokens = activate_runtime_context(ctx)
path = str(pathlib.Path(WORKSPACE_ROOT) / path) if not os.path.isabs(path) else path
try:
path_check = check_path_policy(path, tool="delete_file")
if path_check:
result = PolicyResult(allowed=False, reason=path_check[0], decision_tier="blocked", matched_rule=path_check[1])
else:
result = PolicyResult(allowed=True, reason="allowed", decision_tier="allowed", matched_rule=None)
if result.allowed:
if not os.path.exists(path):
append_log_entry(build_log_entry("delete_file", result, path=path, error="file not found"))
return f"Error: file not found: {path}"
if os.path.isdir(path):
result = PolicyResult(
allowed=False,
reason=f"'{path}' is a directory - delete_file only removes individual files. Use execute_command for directory operations (note: bulk/recursive deletions are also subject to policy).",
decision_tier="blocked",
matched_rule=None,
)
budget_fields: dict = {}
if result.allowed:
bytes_est = 0
try:
if os.path.isfile(path):
bytes_est = int(os.path.getsize(path))
except OSError:
bytes_est = 0
budget_allowed, budget_reason, budget_rule, budget_fields = check_and_record_cumulative_budget(
tool="delete_file",
command=None,
affected_paths=[path],
operation_count=1,
bytes_estimate=bytes_est,
)
if not budget_allowed:
result = PolicyResult(
allowed=False,
reason=budget_reason or "Cumulative blast-radius budget exceeded for current scope.",
decision_tier="blocked",
matched_rule=budget_rule or "requires_simulation.cumulative_budget_exceeded",
)
log_entry = build_log_entry("delete_file", result, path=path, **budget_fields)
if not result.allowed:
append_log_entry(log_entry)
return f"[POLICY BLOCK] {result.reason}"
backup_enabled = bool(POLICY.get("audit", {}).get("backup_enabled", True))
backup_location = backup_paths([path]) if backup_enabled else ""
if backup_location:
log_entry["backup_location"] = backup_location
append_log_entry(log_entry)
try:
os.remove(path)
except OSError as e:
return f"Error deleting file: {e}"
return f"Successfully deleted {path}. " + (
f"Backup saved to {backup_location} - the file can be recovered from there."
if backup_location
else "No content-change backup was needed."
)
finally:
reset_runtime_context(context_tokens)