Skip to content

Commit 8df4ff3

Browse files
author
shahinyanm
committed
release: v0.7.3 — fix macOS sandbox blocking every agent command
On macOS the sandbox-exec profile denied all writes outside the worktree, but the agent's temp dir is /private/tmp/claude-<uid> and the resolved per-user TMPDIR under /private/var/folders — neither was writable, so EVERY command failed with "EPERM: operation not permitted, mkdir", the agent asked the user to run commands, and the task stalled in implementation. Allow those temp paths in the profile. (os.tmpdir() was already passed but didn't match due to the /var -> /private/var symlink.) Linux/bubblewrap already binds /tmp writable. Claude-Session: https://claude.ai/code/session_012GujqUM9Rzsu9dmhee8fQy
1 parent 1323637 commit 8df4ff3

4 files changed

Lines changed: 28 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

77
## [Unreleased]
88

9+
## [0.7.3] - 2026-06-26
10+
11+
### Fixed
12+
13+
- **Autopilot can run commands on macOS** — the OS sandbox (sandbox-exec) denied
14+
all writes outside the worktree, but the agent's temp dir lives elsewhere
15+
(`/private/tmp/claude-<uid>` and the resolved per-user `TMPDIR` under
16+
`/private/var/folders`). So on a Mac EVERY command died with "EPERM: operation
17+
not permitted, mkdir" — the agent couldn't run git/npm/tests, asked the user to
18+
run them, and the task stalled in implementation. The sandbox profile now keeps
19+
those temp paths writable (the passed `os.tmpdir()` didn't match because of the
20+
`/var``/private/var` symlink). Linux (bubblewrap) was unaffected — it already
21+
binds `/tmp` writable.
22+
923
## [0.7.2] - 2026-06-26
1024

1125
### Fixed
@@ -154,7 +168,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
154168
- **Task-journal availability** — every session reaches task-journal, so
155169
reasoning-chain events are recorded; confirmed working end-to-end.
156170

157-
[Unreleased]: https://github.com/Digital-Threads/loom/compare/v0.7.2...master
171+
[Unreleased]: https://github.com/Digital-Threads/loom/compare/v0.7.3...master
172+
[0.7.3]: https://github.com/Digital-Threads/loom/releases/tag/v0.7.3
158173
[0.7.2]: https://github.com/Digital-Threads/loom/releases/tag/v0.7.2
159174
[0.7.1]: https://github.com/Digital-Threads/loom/releases/tag/v0.7.1
160175
[0.7.0]: https://github.com/Digital-Threads/loom/releases/tag/v0.7.0

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@digital-threads/loom",
3-
"version": "0.7.2",
3+
"version": "0.7.3",
44
"type": "module",
55
"description": "Loom — local AI-dev orchestrator: give it a task and it runs the work through an analysis → spec → code → review → PR pipeline on a board, with cost, reasoning memory and multi-account support. Public beta.",
66
"license": "MIT",

src/core/layers/security/os-sandbox.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ export function wrapCommand(
7878
"(version 1)",
7979
"(allow default)",
8080
'(deny file-write* (subpath "/"))',
81+
// The agent (and the claude CLI / its Bun runtime) needs a writable temp
82+
// dir — e.g. /private/tmp/claude-<uid> and the per-user TMPDIR under
83+
// /private/var/folders. Without it EVERY command fails with "EPERM:
84+
// operation not permitted, mkdir" and the task stalls. Confinement is about
85+
// the user's repo/home, not temp (loom-sbtmp).
86+
'(allow file-write* (subpath "/private/tmp"))',
87+
'(allow file-write* (subpath "/private/var/folders"))',
8188
...writable.map((p) => `(allow file-write* (subpath ${JSON.stringify(p)}))`),
8289
].join(" ");
8390
return { cli: "sandbox-exec", args: ["-p", profile, cli, ...args] };

test/core/layers/security/os-sandbox.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ describe("os-sandbox (experimental, opt-in)", () => {
5050
expect(w.args[0]).toBe("-p");
5151
expect(w.args[1]).toContain('(deny file-write* (subpath "/"))');
5252
expect(w.args[1]).toContain("/wt");
53+
// …but the agent's temp dir stays writable — without it every command fails
54+
// with "EPERM: mkdir /private/tmp/claude-<uid>" and the task stalls (loom-sbtmp).
55+
expect(w.args[1]).toContain('(allow file-write* (subpath "/private/tmp"))');
56+
expect(w.args[1]).toContain('(subpath "/private/var/folders")');
5357
expect(w.args.slice(2)).toEqual(["claude", "-p"]);
5458
});
5559

0 commit comments

Comments
 (0)