@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 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
- const credentials = resolveCredentialsFromEnv({ ...dotEnv, ...serverCreds });
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 user paths where credentials typically live. Reads denied
87
- // even though general reads are allowed, so a confused agent can't slurp
88
- // up other projects' tokens or the user's SSH keys.
89
- const denyReadPaths = [
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 denyReadLiterals = [
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
- // Writes allowed only here. Everywhere else: read-only by default.
107
- const writeSubpaths = [
108
- cwd,
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 reads to known credential locations`,
120
- ...denyReadPaths.map(p => `(deny file-read* (subpath ${sbString(p)}))`),
121
- ...denyReadLiterals.map(p => `(deny file-read* (literal ${sbString(p)}))`),
122
- ``,
123
- `; Writes: deny everywhere, then allow the project dir + scratch + tmp`,
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
- // Everything read-only by default via ro-bind of /. Writable exceptions:
143
- // the project dir, the scratch HOME, and a private /tmp. /home/<user> is
144
- // replaced with a tmpfs so the agent can't browse the real home dir.
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
- return [
147
- "--ro-bind", "/", "/",
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
- const credentials = resolveCredentialsFromEnv({ ...projectEnvVars, ...serverCreds });
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.18.2",
3
+ "version": "0.18.4",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {