fix: inherit process.env when spawning agent subprocess (#374)

In 0.20.0, the env option was introduced with an explicit object spread,
but without including process.env. This caused the subprocess to receive
an empty environment, stripping HOME, PATH and other inherited variables
when spawned from an external editor such as Zed.

Add ...process.env as the base so user-provided and gateway env vars
still override it, while preserving the parent process environment.

Add tests to verify HOME/PATH are inherited and that user-provided vars
are merged on top correctly.

Fixes #373

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
This commit is contained in:
José O R Ferreira
2026-03-04 02:05:49 -04:00
committed by GitHub
parent edf6486c44
commit 52d5b74aa7
7 changed files with 59 additions and 6 deletions

View File

@@ -1,5 +1,9 @@
# Changelog
## 0.20.1
- fix: inherit process.env when spawning agent subprocess
## 0.20.0
- Update to @anthropic-ai/claude-agent-sdk@0.2.63

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@zed-industries/claude-agent-acp",
"version": "0.20.0",
"version": "0.20.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@zed-industries/claude-agent-acp",
"version": "0.20.0",
"version": "0.20.1",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "0.14.1",

View File

@@ -3,7 +3,7 @@
"publishConfig": {
"access": "public"
},
"version": "0.20.0",
"version": "0.20.1",
"description": "An ACP-compatible coding agent powered by the Claude Agent SDK (TypeScript)",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",

View File

@@ -1240,6 +1240,7 @@ export class ClaudeAcpAgent implements Agent {
...(maxThinkingTokens !== undefined && { maxThinkingTokens }),
...userProvidedOptions,
env: {
...process.env,
...userProvidedOptions?.env,
...createEnvForGateway(this.gatewayAuthMeta),
},

View File

@@ -227,7 +227,7 @@ describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration"
sessionId: newSessionResponse.sessionId,
});
expect(client.takeReceivedText()).toBe("");
expect(client.takeReceivedText()).toBe("Error: No messages to compact");
// Send something
await connection.prompt({

View File

@@ -101,12 +101,12 @@ describe("authorization", () => {
expect(mockQuery).toHaveBeenCalledWith(
expect.objectContaining({
options: expect.objectContaining({
env: {
env: expect.objectContaining({
ANTHROPIC_AUTH_TOKEN: "",
ANTHROPIC_BASE_URL: "https://gateway.example",
ANTHROPIC_CUSTOM_HEADERS: "x-api-key: test",
userEnv: "userEnv",
},
}),
}),
}),
);

View File

@@ -151,6 +151,54 @@ describe("createSession options merging", () => {
expect(capturedOptions!.hooks?.PostToolUse).toHaveLength(2);
});
it("inherits HOME and PATH from process.env when no env is provided", async () => {
await agent.newSession({
cwd: "/test",
mcpServers: [],
});
expect(capturedOptions?.env?.HOME).toBe(process.env.HOME);
expect(capturedOptions?.env?.PATH).toBe(process.env.PATH);
});
it("merges user-provided env vars on top of process.env", async () => {
await agent.newSession({
cwd: "/test",
mcpServers: [],
_meta: {
claudeCode: {
options: {
env: {
CUSTOM_VAR: "custom-value",
},
},
},
},
});
expect(capturedOptions?.env?.HOME).toBe(process.env.HOME);
expect(capturedOptions?.env?.PATH).toBe(process.env.PATH);
expect(capturedOptions?.env?.CUSTOM_VAR).toBe("custom-value");
});
it("allows user-provided env vars to override process.env entries", async () => {
await agent.newSession({
cwd: "/test",
mcpServers: [],
_meta: {
claudeCode: {
options: {
env: {
HOME: "/custom/home",
},
},
},
},
});
expect(capturedOptions?.env?.HOME).toBe("/custom/home");
});
it("merges user-provided mcpServers with ACP mcpServers", async () => {
await agent.newSession({
cwd: "/test",