Compare commits

...

4 Commits

Author SHA1 Message Date
Ben Brandt
521d1f766d Prep 0.22.2 release (#440) 2026-03-18 14:26:49 +00:00
Egor Baranov
f11435df13 Support additional skill directories via additionalRoots meta parameter (#406)
Add support for `_meta.additionalRoots` in `claude-agent-acp`.

This change introduces a top-level `additionalRoots` field in session
metadata and forwards it to Claude by merging it into
`additionalDirectories` when creating a session.

Behavior:
- reads `_meta.additionalRoots` from the ACP session request
- treats it as a `string[]`
- merges it with any user-provided
`claudeCode.options.additionalDirectories`
- passes the combined list through to Claude unchanged

This keeps `additionalRoots` as an ACP-level extension rather than a
Claude-specific option, while mapping it onto Claude's existing
`additionalDirectories` support.
2026-03-18 14:20:30 +00:00
Ben Brandt
e8545266fe Prep 0.22.1 release (#438) 2026-03-17 21:58:33 +00:00
Aleksandr Suhinin
ff3b97aece fix: suppress account assertion if gateway authentication is used (#436) 2026-03-17 17:51:02 +01:00
5 changed files with 95 additions and 5 deletions

View File

@@ -1,5 +1,13 @@
# Changelog
## 0.22.2
- Add experimental meta param for testing additional directories
## 0.22.1
- Fix: invalid auth required state in gateway mode
## 0.22.0
- Use stable list sessions method (#429)

4
package-lock.json generated
View File

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

View File

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

View File

@@ -156,6 +156,7 @@ export type NewSessionMeta = {
*/
options?: Options;
};
additionalRoots?: string[];
};
/**
@@ -1299,7 +1300,8 @@ export class ClaudeAcpAgent implements Agent {
);
// Extract options from _meta if provided
const userProvidedOptions = (params._meta as NewSessionMeta | undefined)?.claudeCode?.options;
const sessionMeta = params._meta as NewSessionMeta | undefined;
const userProvidedOptions = sessionMeta?.claudeCode?.options;
// Configure thinking tokens from environment variable
const maxThinkingTokens = process.env.MAX_THINKING_TOKENS
@@ -1378,6 +1380,11 @@ export class ClaudeAcpAgent implements Agent {
abortController,
};
options.additionalDirectories = [
...(userProvidedOptions?.additionalDirectories ?? []),
...(sessionMeta?.additionalRoots ?? []),
];
if (creationOpts?.resume === undefined || creationOpts?.forkSession) {
// Set our own session id if not resuming an existing session.
options.sessionId = sessionId;
@@ -1407,7 +1414,11 @@ export class ClaudeAcpAgent implements Agent {
throw error;
}
if (shouldHideClaudeAuth() && initializationResult.account.subscriptionType) {
if (
shouldHideClaudeAuth() &&
initializationResult.account.subscriptionType &&
this.gatewayAuthMeta === null
) {
throw RequestError.authRequired(
undefined,
"This integration does not support using claude.ai subscriptions.",

View File

@@ -0,0 +1,71 @@
import { mkdtemp, rm } from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { AgentSideConnection, SessionNotification } from "@agentclientprotocol/sdk";
import type { Options } from "@anthropic-ai/claude-agent-sdk";
import type { ClaudeAcpAgent as ClaudeAcpAgentType } from "../acp-agent.js";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
let capturedOptions: Options | undefined;
vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
...(await vi.importActual<typeof import("@anthropic-ai/claude-agent-sdk")>(
"@anthropic-ai/claude-agent-sdk",
)),
query: ({ options }: { options: Options }) => {
capturedOptions = options;
return {
initializationResult: async () => ({
models: [{ value: "claude-sonnet-4-5", displayName: "Claude Sonnet", description: "Fast" }],
}),
setModel: async () => {},
supportedCommands: async () => [],
[Symbol.asyncIterator]: async function* () {},
};
},
}));
vi.mock("../tools.js", async () => ({
...(await vi.importActual<typeof import("../tools.js")>("../tools.js")),
registerHookCallback: vi.fn(),
}));
describe("additionalRoots", () => {
let agent: ClaudeAcpAgentType;
const tempDirs: string[] = [];
const newSession = (meta: Record<string, unknown>, cwd = "/test") =>
agent.newSession({ cwd, mcpServers: [], _meta: meta });
beforeEach(async () => {
capturedOptions = undefined;
tempDirs.length = 0;
vi.resetModules();
const { ClaudeAcpAgent } = await import("../acp-agent.js");
agent = new ClaudeAcpAgent({
sessionUpdate: async (_notification: SessionNotification) => {},
requestPermission: async () => ({ outcome: { outcome: "cancelled" } }),
readTextFile: async () => ({ content: "" }),
writeTextFile: async () => ({}),
} as unknown as AgentSideConnection);
});
afterEach(
async () =>
void (await Promise.all(tempDirs.map((dir) => rm(dir, { recursive: true, force: true })))),
);
it("passes through relative roots as provided", async () => {
const projectRoot = await mkdtemp(path.join(os.tmpdir(), "claude-project-"));
tempDirs.push(projectRoot);
await newSession({ additionalRoots: ["."] }, projectRoot);
expect(capturedOptions!.additionalDirectories).toEqual(["."]);
});
it("merges additionalRoots with user additionalDirectories without normalization", async () => {
const root = await mkdtemp(path.join(os.tmpdir(), "claude-root-"));
tempDirs.push(root);
await newSession({
additionalRoots: ["", root],
claudeCode: { options: { additionalDirectories: ["/workspace/shared"] } },
});
expect(capturedOptions!.additionalDirectories).toEqual(["/workspace/shared", "", root]);
});
});