@kendoo.agentdesk/agentdesk 0.18.2 → 0.18.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/cli/init.mjs +2 -1
- package/cli/session-isolation.mjs +44 -33
- package/cli/team.mjs +3 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,16 @@ All user-facing changes to AgentDesk. Each entry is tagged:
|
|
|
8
8
|
|
|
9
9
|
Internal refactors, infrastructure changes, and architectural notes are not listed here.
|
|
10
10
|
|
|
11
|
+
## [0.18.4] — 2026-04-18
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
- `[CLI]` Sandbox was too strict: the "deny all writes except project+scratch+tmp" rule blocked Claude and other tools from writing to their own state directories (`~/.claude`, `~/.npm`, etc.), causing sessions to crash in under 3 seconds with a misleading "hit rate/context limit" handoff. The sandbox now allows reads and writes by default and denies a specific list of sensitive paths (`~/.ssh`, `~/.aws`, `~/.gcloud`, `~/.config/gh`, `~/.docker`, `~/.kube`, `~/.agentdesk`, `~/.gitconfig`, `~/.netrc`, `~/.npmrc`, `~/.pypirc`, shell rc files). Identity-isolation goal preserved; compatibility restored.
|
|
15
|
+
|
|
16
|
+
## [0.18.3] — 2026-04-18
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- `[CLI]` Tracker credentials: if a project has credentials stored both locally (`.env`) and on the server, the local value now wins. Previously the server's copy overrode `.env`, which meant that rotating a Jira / Linear / GitHub token locally didn't take effect while the server still held the old one.
|
|
20
|
+
|
|
11
21
|
## [0.18.2] — 2026-04-18
|
|
12
22
|
|
|
13
23
|
### Fixed
|
package/cli/init.mjs
CHANGED
|
@@ -246,7 +246,8 @@ async function verifyAndPickTrackerProject({ rl, cwd, finalProjectKey, tracker,
|
|
|
246
246
|
const dotEnv = loadDotEnv(cwd);
|
|
247
247
|
const apiKey = loadApiKey(cwd);
|
|
248
248
|
const serverCreds = await fetchServerCreds(apiKey, finalProjectKey);
|
|
249
|
-
|
|
249
|
+
// .env wins over server credentials — fresh local rotation beats stale server state.
|
|
250
|
+
const credentials = resolveCredentialsFromEnv({ ...serverCreds, ...dotEnv });
|
|
250
251
|
const check = await checkTrackerPermissions({ tracker, config, credentials });
|
|
251
252
|
if (!check.ok) {
|
|
252
253
|
console.log(" ⚠ " + (check.errors[0] || "Verification failed"));
|
|
@@ -83,10 +83,10 @@ export function wrapIsolatedSpawn({ cmd, args, cwd, scratchHome, sessionId }) {
|
|
|
83
83
|
|
|
84
84
|
function macosProfile({ cwd, scratchHome }) {
|
|
85
85
|
const home = homedir();
|
|
86
|
-
// Known-sensitive
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
const
|
|
86
|
+
// Known-sensitive paths: reads AND writes denied, even though general
|
|
87
|
+
// access is allowed, so a confused agent can't slurp up other projects'
|
|
88
|
+
// tokens or clobber the user's SSH keys / shell config.
|
|
89
|
+
const denyPathsSubpath = [
|
|
90
90
|
join(home, ".ssh"),
|
|
91
91
|
join(home, ".aws"),
|
|
92
92
|
join(home, ".gcloud"),
|
|
@@ -96,36 +96,27 @@ function macosProfile({ cwd, scratchHome }) {
|
|
|
96
96
|
join(home, ".agentdesk"),
|
|
97
97
|
join(home, ".config", "agentdesk"),
|
|
98
98
|
];
|
|
99
|
-
const
|
|
99
|
+
const denyPathsLiteral = [
|
|
100
100
|
join(home, ".netrc"),
|
|
101
101
|
join(home, ".gitconfig"), // real gitconfig (scratch has its own)
|
|
102
102
|
join(home, ".npmrc"),
|
|
103
103
|
join(home, ".pypirc"),
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
scratchHome,
|
|
110
|
-
"/tmp",
|
|
111
|
-
"/private/tmp",
|
|
112
|
-
"/private/var/folders", // macOS TMPDIR lives here
|
|
104
|
+
join(home, ".zshrc"),
|
|
105
|
+
join(home, ".zprofile"),
|
|
106
|
+
join(home, ".bashrc"),
|
|
107
|
+
join(home, ".bash_profile"),
|
|
108
|
+
join(home, ".profile"),
|
|
113
109
|
];
|
|
114
110
|
|
|
115
111
|
const sb = [
|
|
116
112
|
`(version 1)`,
|
|
117
113
|
`(allow default)`,
|
|
118
114
|
``,
|
|
119
|
-
`; Deny
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
`(deny file-write*)`,
|
|
125
|
-
...writeSubpaths.map(p => `(allow file-write* (subpath ${sbString(p)}))`),
|
|
126
|
-
``,
|
|
127
|
-
`; Allow writes to devices so stdio, /dev/null, tty, ptys all work`,
|
|
128
|
-
`(allow file-write* (subpath "/dev"))`,
|
|
115
|
+
`; Deny access (read + write) to known credential and shell-config paths.`,
|
|
116
|
+
`; Agents can't read other projects' tokens, can't overwrite SSH keys or`,
|
|
117
|
+
`; rewrite the user's shell rc files.`,
|
|
118
|
+
...denyPathsSubpath.map(p => `(deny file-read* file-write* (subpath ${sbString(p)}))`),
|
|
119
|
+
...denyPathsLiteral.map(p => `(deny file-read* file-write* (literal ${sbString(p)}))`),
|
|
129
120
|
``,
|
|
130
121
|
].join("\n");
|
|
131
122
|
|
|
@@ -139,22 +130,42 @@ function sbString(s) {
|
|
|
139
130
|
// --- Linux bwrap args --------------------------------------------------------
|
|
140
131
|
|
|
141
132
|
function bwrapArgs({ cwd, scratchHome }) {
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
133
|
+
// Bind the whole filesystem normally so tools that need to write state
|
|
134
|
+
// (claude, npm, pip, yarn, etc.) keep working. Then cover the sensitive
|
|
135
|
+
// credential/config paths with tmpfs mounts so the agent can't read or
|
|
136
|
+
// write them — other projects' .env, SSH keys, shell rc files, etc.
|
|
145
137
|
const home = homedir();
|
|
146
|
-
|
|
147
|
-
|
|
138
|
+
const denyPaths = [
|
|
139
|
+
join(home, ".ssh"),
|
|
140
|
+
join(home, ".aws"),
|
|
141
|
+
join(home, ".gcloud"),
|
|
142
|
+
join(home, ".config", "gh"),
|
|
143
|
+
join(home, ".docker"),
|
|
144
|
+
join(home, ".kube"),
|
|
145
|
+
join(home, ".agentdesk"),
|
|
146
|
+
join(home, ".config", "agentdesk"),
|
|
147
|
+
join(home, ".netrc"),
|
|
148
|
+
join(home, ".gitconfig"),
|
|
149
|
+
join(home, ".npmrc"),
|
|
150
|
+
join(home, ".pypirc"),
|
|
151
|
+
join(home, ".zshrc"),
|
|
152
|
+
join(home, ".zprofile"),
|
|
153
|
+
join(home, ".bashrc"),
|
|
154
|
+
join(home, ".bash_profile"),
|
|
155
|
+
join(home, ".profile"),
|
|
156
|
+
];
|
|
157
|
+
const args = [
|
|
158
|
+
"--bind", "/", "/",
|
|
148
159
|
"--dev-bind", "/dev", "/dev",
|
|
149
160
|
"--proc", "/proc",
|
|
150
|
-
"--tmpfs", "/tmp",
|
|
151
|
-
"--tmpfs", home, // blank user home
|
|
152
|
-
"--bind", cwd, cwd, // project dir writable
|
|
153
|
-
"--bind", scratchHome, scratchHome, // scratch writable (HOME env var points here)
|
|
154
161
|
"--die-with-parent",
|
|
155
162
|
"--unshare-ipc",
|
|
156
163
|
"--unshare-uts",
|
|
157
164
|
"--unshare-pid",
|
|
158
165
|
// Network is NOT unshared — agents need network for tracker APIs and git.
|
|
159
166
|
];
|
|
167
|
+
for (const p of denyPaths) args.push("--tmpfs", p);
|
|
168
|
+
// scratch HOME + project dir stay writable (already bound via --bind / /
|
|
169
|
+
// above — tmpfs blanks only the specific deny paths, not the rest).
|
|
170
|
+
return args;
|
|
160
171
|
}
|
package/cli/team.mjs
CHANGED
|
@@ -112,7 +112,9 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
112
112
|
} catch {}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
// .env wins over server credentials — a freshly-rotated token in the
|
|
116
|
+
// user's project should beat a stale one left behind on the server.
|
|
117
|
+
const credentials = resolveCredentialsFromEnv({ ...serverCreds, ...projectEnvVars });
|
|
116
118
|
const check = await checkTrackerPermissions({ tracker, config, credentials });
|
|
117
119
|
|
|
118
120
|
if (!check.ok) {
|