mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-11-10 11:21:06 +00:00
refactor!: export only lib.ts, with serveAcp and the authStatus wire names (#1566)
Stacked on #1564. The package's `exports` map listed `"./*": "./*"`, so every file under `dist/` was public, and any file move could break a deep import. It now lists only `.` (`dist/lib.js`) and `./package.json`. **`lib.ts` exports:** - `serveAcp`, which serves ACP v1 and v2 on stdio through the SDK's protocol router. This is what `index.ts` runs. - The `AcpProtocols` and `Logger` types. - `AUTH_STATUS_UPDATE_METHOD` and the `authStatus` payload types, which a client needs to read the extension's traffic. **No longer exported:** - `ClaudeAcpAgent` - `runAcp`: v1 only and unused, replaced by `serveAcp`, and now deleted. - `toAcpNotifications`, `streamEventToAcpNotifications`, `isLocalCommandMetadata`, `stripLocalCommandMetadata` - `toolInfoFromToolUse`, `toolUpdateFromToolResult`, `toDisplayPath`, `planEntries` - `SettingsManager` - `nodeToWebReadable`, `nodeToWebWritable`, `Pushable`, `unreachable` - The types `NewSessionMeta`, `SDKMessageFilter`, `ToolUpdateMeta` and `ClaudePlanEntry`. `src/acp-agent.ts`, which re-exported `src/v1/acp-agent.ts` for the `dist/acp-agent.js` path, is gone. So are the re-exports in `src/v1/acp-agent.ts` that kept moved names on that path. Tests now import those names from their own modules. The ESLint boundary rules and `AGENTS.md` drop their `acp-agent.ts` exemptions. `release-please-config.json` sets `bump-minor-pre-major`, so the `!` bumps the minor version, the same as a `feat:`. BREAKING CHANGE: deep imports of `dist/` files fail with `ERR_PACKAGE_PATH_NOT_EXPORTED`, and `lib.ts` exports only `serveAcp`, `AUTH_STATUS_UPDATE_METHOD` and their types. Test results: - `npx tsc --noEmit`, `npm run check`, `npm run build`: pass - `npx vitest run`: 3939 passed / 33 skipped. `module-exports.test.ts` pins the `lib.ts` names and the `exports` map. - `node scripts/smoke-binary.mjs -- node dist/index.js`: pass - Resolving the package from its root: `import("@agentclientprotocol/claude-agent-acp")` gives `serveAcp` and `AUTH_STATUS_UPDATE_METHOD`, `…/dist/acp-agent.js` throws `ERR_PACKAGE_PATH_NOT_EXPORTED`, and `…/package.json` resolves.
This commit is contained in:
@@ -3,10 +3,9 @@
|
||||
An ACP-compatible coding agent powered by the Claude Agent SDK. Source in `src/`,
|
||||
TypeScript, ESM, Node >= 22. `src/v1/` holds the v1 agent (`ClaudeAcpAgent` in
|
||||
`src/v1/acp-agent.ts`), `src/v2/` the v2 agent, and the rest of `src/` the modules
|
||||
both share. `src/acp-agent.ts` only re-exports `src/v1/acp-agent.ts` for the
|
||||
published `dist/acp-agent.js` path. ESLint rejects imports between `src/v1/` and
|
||||
both share. The package exports only `src/lib.ts`. ESLint rejects imports between `src/v1/` and
|
||||
`src/v2/`, and from shared modules into either agent. The entry points (`index.ts`, `lib.ts`,
|
||||
`serve.ts`, `binary-entry.ts`, `acp-agent.ts`) are exempt.
|
||||
`serve.ts`, `binary-entry.ts`) are exempt.
|
||||
|
||||
## Commands
|
||||
|
||||
|
||||
@@ -61,11 +61,11 @@ export default [
|
||||
curly: ["error", "all"],
|
||||
},
|
||||
},
|
||||
// `src/v2/` must not import the v1 agent (`src/v1/`, or `src/acp-agent.ts`, which re-exports it).
|
||||
// `src/v2/` must not import the v1 agent.
|
||||
{
|
||||
files: ["src/v2/**/*.ts"],
|
||||
rules: protocolBoundary(
|
||||
"^(\\.\\./)+(v1/|acp-agent\\.js$)",
|
||||
"^(\\.\\./)+v1/",
|
||||
"src/v2/ must not import the v1 agent. Move what both versions need into a shared module in src/.",
|
||||
),
|
||||
},
|
||||
@@ -88,11 +88,10 @@ export default [
|
||||
"src/lib.ts",
|
||||
"src/serve.ts",
|
||||
"src/binary-entry.ts",
|
||||
"src/acp-agent.ts",
|
||||
],
|
||||
rules: protocolBoundary(
|
||||
"^(\\./|(\\.\\./)+)(v1/|v2/|acp-agent\\.js$)",
|
||||
"Shared modules must not import src/v1/, src/v2/ or src/acp-agent.ts. Move the code into the shared module, or pass it in from the agent.",
|
||||
"^(\\./|(\\.\\./)+)(v1|v2)/",
|
||||
"Shared modules must not import src/v1/ or src/v2/. Move the code into the shared module, or pass it in from the agent.",
|
||||
),
|
||||
},
|
||||
prettierConfig,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"types": "./dist/lib.d.ts",
|
||||
"import": "./dist/lib.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
// The v1 agent lives in `v1/acp-agent.ts`. This module keeps the published
|
||||
// `dist/acp-agent.js` path that consumers deep-import.
|
||||
export * from "./v1/acp-agent.js";
|
||||
23
src/lib.ts
23
src/lib.ts
@@ -1,19 +1,7 @@
|
||||
// Export the main agent class and utilities for library usage
|
||||
export { ClaudeAcpAgent, runAcp } from "./v1/acp-agent.js";
|
||||
export { isLocalCommandMetadata, stripLocalCommandMetadata } from "./message-filters.js";
|
||||
export { toAcpNotifications, streamEventToAcpNotifications } from "./sdk-to-acp.js";
|
||||
export type { NewSessionMeta, SDKMessageFilter } from "./new-session-meta.js";
|
||||
export type { ToolUpdateMeta } from "./tool-calls/renderer.js";
|
||||
export { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./utils.js";
|
||||
export {
|
||||
toolInfoFromToolUse,
|
||||
toDisplayPath,
|
||||
planEntries,
|
||||
toolUpdateFromToolResult,
|
||||
} from "./tools.js";
|
||||
export { SettingsManager, type SettingsManagerOptions } from "./settings.js";
|
||||
// The `authStatus` extension's wire surface: the notification's method name and
|
||||
// the payload types needed to read the traffic. The mappers stay internal.
|
||||
// The package's public API: serve ACP (v1 and v2) on stdio, and the wire
|
||||
// names of the `authStatus` extension that a client needs to read its traffic.
|
||||
export { serveAcp, type AcpProtocols } from "./serve.js";
|
||||
export type { Logger } from "./logger.js";
|
||||
export {
|
||||
AUTH_STATUS_UPDATE_METHOD,
|
||||
type AuthStatus,
|
||||
@@ -21,6 +9,3 @@ export {
|
||||
type AuthStatusKind,
|
||||
type AuthStatusUpdateNotification,
|
||||
} from "./auth-status.js";
|
||||
|
||||
// Export types
|
||||
export type { ClaudePlanEntry } from "./tools.js";
|
||||
|
||||
@@ -37,12 +37,11 @@ import { ToolCallFieldTracker } from "../tool-calls/field-tracker.js";
|
||||
import {
|
||||
ClaudeAcpAgent,
|
||||
computeSessionFingerprint,
|
||||
buildConfigOptions,
|
||||
createFastModeConfigOption,
|
||||
discoverCustomAgents,
|
||||
runPromptWithCancellation,
|
||||
type SteerRequest,
|
||||
} from "../v1/acp-agent.js";
|
||||
import { buildConfigOptions, discoverCustomAgents } from "../session-config-options.js";
|
||||
import { createFastModeConfigOption } from "../session-fast-mode.js";
|
||||
import {
|
||||
toAcpNotifications,
|
||||
streamEventToAcpNotifications,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { randomUUID } from "crypto";
|
||||
import { query } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { Pushable } from "../utils.js";
|
||||
import { discoverCustomAgents, BUILTIN_AGENT_NAMES } from "../v1/acp-agent.js";
|
||||
import { discoverCustomAgents, BUILTIN_AGENT_NAMES } from "../session-config-options.js";
|
||||
|
||||
// `discoverCustomAgents` distinguishes user/plugin-configured agents from
|
||||
// Claude Code's built-in subagents by name (BUILTIN_AGENT_NAMES), since the
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import type { ClientCapabilities, SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { FastModeDisabledReason, ModelInfo } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { ClaudeAcpAgent } from "../v1/acp-agent.js";
|
||||
import {
|
||||
buildConfigOptions,
|
||||
clientSupportsBooleanConfigOptions,
|
||||
} from "../session-config-options.js";
|
||||
import {
|
||||
createFastModeConfigOption,
|
||||
fastModeStateEnabled,
|
||||
normalizeFastModeDisabledReason,
|
||||
@@ -11,8 +14,7 @@ import {
|
||||
FAST_MODE_CONFIG_ID,
|
||||
FAST_MODE_ON,
|
||||
FAST_MODE_OFF,
|
||||
ClaudeAcpAgent,
|
||||
} from "../v1/acp-agent.js";
|
||||
} from "../session-fast-mode.js";
|
||||
import type { AcpClient } from "../acp-client.js";
|
||||
|
||||
const MODES = {
|
||||
|
||||
@@ -1,74 +1,22 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import packageJson from "../../package.json" with { type: "json" };
|
||||
|
||||
describe("module runtime exports", () => {
|
||||
/**
|
||||
* The public runtime exports of lib.ts must change only deliberately; consumers import these names.
|
||||
* The public runtime exports of lib.ts, the package's only entry point, must change only deliberately; consumers import these names.
|
||||
*/
|
||||
it("lib.ts exports the expected names", async () => {
|
||||
expect(Object.keys(await import("../lib.js")).sort()).toEqual([
|
||||
"AUTH_STATUS_UPDATE_METHOD",
|
||||
"ClaudeAcpAgent",
|
||||
"Pushable",
|
||||
"SettingsManager",
|
||||
"isLocalCommandMetadata",
|
||||
"nodeToWebReadable",
|
||||
"nodeToWebWritable",
|
||||
"planEntries",
|
||||
"runAcp",
|
||||
"streamEventToAcpNotifications",
|
||||
"stripLocalCommandMetadata",
|
||||
"toAcpNotifications",
|
||||
"toDisplayPath",
|
||||
"toolInfoFromToolUse",
|
||||
"toolUpdateFromToolResult",
|
||||
"unreachable",
|
||||
"serveAcp",
|
||||
]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Moving code out of acp-agent.ts must keep every name re-exported, since consumers deep-import
|
||||
* dist/acp-agent.js; change this list only deliberately.
|
||||
*/
|
||||
it("acp-agent.ts exports the expected names", async () => {
|
||||
expect(Object.keys(await import("../acp-agent.js")).sort()).toEqual([
|
||||
"AGENT_CONFIG_ID",
|
||||
"AUTONOMOUS_RESULT_ORIGINS",
|
||||
"BUILTIN_AGENT_NAMES",
|
||||
"ClaudeAcpAgent",
|
||||
"DEFAULT_AGENT_ID",
|
||||
"EFFORT_CONFIG_ID",
|
||||
"FAST_MODE_CONFIG_ID",
|
||||
"FAST_MODE_OFF",
|
||||
"FAST_MODE_ON",
|
||||
"MODEL_CONFIG_ID",
|
||||
"MODE_CONFIG_ID",
|
||||
"applyAvailableModelsAllowlist",
|
||||
"buildConfigOptions",
|
||||
"clientSupportsBooleanConfigOptions",
|
||||
"clientSupportsRecommendedConfigValue",
|
||||
"computeSessionFingerprint",
|
||||
"createFastModeConfigOption",
|
||||
"discoverCustomAgents",
|
||||
"fastModeStateEnabled",
|
||||
"getAvailableSlashCommands",
|
||||
"isAbortedResult",
|
||||
"isLocalCommandMetadata",
|
||||
"isLoginRequiredText",
|
||||
"isReplayHiddenMetaMessage",
|
||||
"isSyntheticLoginMessage",
|
||||
"matchResumedModel",
|
||||
"messageIdForGrouping",
|
||||
"normalizeFastModeDisabledReason",
|
||||
"promptToClaude",
|
||||
"resolveFastModeEnabled",
|
||||
"resolveModelPreference",
|
||||
"runAcp",
|
||||
"runPromptWithCancellation",
|
||||
"settingsEffortForModel",
|
||||
"streamEventToAcpNotifications",
|
||||
"stripLocalCommandMetadata",
|
||||
"toAcpNotifications",
|
||||
"v1AgentApp",
|
||||
]);
|
||||
it("package.json exposes lib.ts as the only module entry point", () => {
|
||||
expect(Object.keys(packageJson.exports)).toEqual([".", "./package.json"]);
|
||||
expect(packageJson.exports["."]).toEqual({
|
||||
types: "./dist/lib.d.ts",
|
||||
import: "./dist/lib.js",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,8 @@ import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ModelInfo, Options } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { ClaudeAcpAgent, FAST_MODE_CONFIG_ID } from "../v1/acp-agent.js";
|
||||
import { ClaudeAcpAgent } from "../v1/acp-agent.js";
|
||||
import { FAST_MODE_CONFIG_ID } from "../session-fast-mode.js";
|
||||
import type { AcpClient } from "../acp-client.js";
|
||||
import { AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY } from "../air-extension.js";
|
||||
import { initializeClient, makeMockQuery } from "./helpers.js";
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { ClientCapabilities } from "@agentclientprotocol/sdk/unstable";
|
||||
import type { ModelInfo } from "@anthropic-ai/claude-agent-sdk";
|
||||
import { buildConfigOptions, clientSupportsRecommendedConfigValue } from "../v1/acp-agent.js";
|
||||
import {
|
||||
buildConfigOptions,
|
||||
clientSupportsRecommendedConfigValue,
|
||||
} from "../session-config-options.js";
|
||||
import { AIR_RECOMMENDED_CONFIG_VALUE_CAPABILITY } from "../air-extension.js";
|
||||
|
||||
const MODES = {
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
LoadSessionResponse,
|
||||
LogoutRequest,
|
||||
methods,
|
||||
ndJsonStream,
|
||||
NewSessionRequest,
|
||||
NewSessionResponse,
|
||||
PromptRequest,
|
||||
@@ -312,14 +311,7 @@ import {
|
||||
isFinalToolCallStatus,
|
||||
type ToolCallUpdate,
|
||||
} from "../tool-calls/renderer.js";
|
||||
import {
|
||||
messageLoopYielder,
|
||||
nodeToWebReadable,
|
||||
nodeToWebWritable,
|
||||
Pushable,
|
||||
raceTimeoutAndAbort,
|
||||
unreachable,
|
||||
} from "../utils.js";
|
||||
import { messageLoopYielder, Pushable, raceTimeoutAndAbort, unreachable } from "../utils.js";
|
||||
import {
|
||||
acceptedPlanToolResult,
|
||||
ExitPlanCoordinator,
|
||||
@@ -330,7 +322,6 @@ import { AGENT_CONFIG_ID, DEFAULT_AGENT_ID } from "../session-config-ids.js";
|
||||
import { type PersistedTaskNotification, taskNotificationsOf } from "../task-notifications.js";
|
||||
import { formatUsageResponse, isUsageCommandText, parseUsageResponse } from "../usage-markdown.js";
|
||||
|
||||
export { DEFAULT_AGENT_ID } from "../session-config-ids.js";
|
||||
import { parseMcpCommand, runMcpCommand } from "../mcp-command.js";
|
||||
import { configuredSettings, resolveThinkingConfig } from "../session-env.js";
|
||||
import {
|
||||
@@ -408,33 +399,6 @@ import { STARTUP_FAILURE_RESULTS_ENV, startupFailureError } from "../startup-fai
|
||||
import { recordStreamedDelta, unstreamedRemainder, type StreamedBlock } from "../streamed-text.js";
|
||||
import { subagentHookOptions } from "../subagent-configuration.js";
|
||||
|
||||
export {
|
||||
AGENT_CONFIG_ID,
|
||||
BUILTIN_AGENT_NAMES,
|
||||
buildConfigOptions,
|
||||
clientSupportsBooleanConfigOptions,
|
||||
clientSupportsRecommendedConfigValue,
|
||||
type ConfigOptionPresentation,
|
||||
discoverCustomAgents,
|
||||
MODE_CONFIG_ID,
|
||||
} from "../session-config-options.js";
|
||||
export {
|
||||
createFastModeConfigOption,
|
||||
FAST_MODE_CONFIG_ID,
|
||||
FAST_MODE_OFF,
|
||||
FAST_MODE_ON,
|
||||
fastModeStateEnabled,
|
||||
type FastModeOptionState,
|
||||
normalizeFastModeDisabledReason,
|
||||
resolveFastModeEnabled,
|
||||
} from "../session-fast-mode.js";
|
||||
export { EFFORT_CONFIG_ID, settingsEffortForModel } from "../session-effort.js";
|
||||
export {
|
||||
applyAvailableModelsAllowlist,
|
||||
matchResumedModel,
|
||||
MODEL_CONFIG_ID,
|
||||
resolveModelPreference,
|
||||
} from "../session-model.js";
|
||||
import type { AcpClient } from "../acp-client.js";
|
||||
import type { Logger } from "../logger.js";
|
||||
import {
|
||||
@@ -456,28 +420,6 @@ import {
|
||||
} from "../sdk-to-acp.js";
|
||||
import { getAvailableSlashCommands } from "../slash-commands.js";
|
||||
|
||||
// Moved to their own modules; kept as exports for consumers that deep-import dist/acp-agent.js
|
||||
// (re-exported by `../acp-agent.ts`).
|
||||
export { AUTONOMOUS_RESULT_ORIGINS, isAbortedResult, isLoginRequiredText } from "../result-end.js";
|
||||
export type { AcpClient } from "../acp-client.js";
|
||||
export type { Logger } from "../logger.js";
|
||||
export {
|
||||
isLocalCommandMetadata,
|
||||
isReplayHiddenMetaMessage,
|
||||
isSyntheticLoginMessage,
|
||||
stripLocalCommandMetadata,
|
||||
} from "../message-filters.js";
|
||||
export type { NewSessionMeta, SDKMessageFilter } from "../new-session-meta.js";
|
||||
export { promptToClaude } from "../prompt-to-claude.js";
|
||||
export {
|
||||
messageIdForGrouping,
|
||||
streamEventToAcpNotifications,
|
||||
toAcpNotifications,
|
||||
type StreamedToolInputCache,
|
||||
type ToolUseCache,
|
||||
} from "../sdk-to-acp.js";
|
||||
export { getAvailableSlashCommands } from "../slash-commands.js";
|
||||
|
||||
/** Claude CLI emits this synthetic result when an interrupted cycle ends on
|
||||
* queued user input before producing any assistant content. It is a hand-off
|
||||
* marker, not the outcome of the replacement prompt that may already be
|
||||
@@ -1746,8 +1688,6 @@ export function computeSessionFingerprint(params: {
|
||||
* cannot drift from the errors this agent throws. */
|
||||
const AUTH_REQUIRED_CODE = RequestError.authRequired().code;
|
||||
|
||||
export type { ToolUpdateMeta } from "../tool-calls/renderer.js";
|
||||
|
||||
const SUBAGENT_TRANSCRIPT_CAPABILITY = "subagent-transcript";
|
||||
|
||||
function supportsSubagentTranscript(capabilities?: ClientCapabilities | null): boolean {
|
||||
@@ -11319,15 +11259,6 @@ export function v1AgentApp(
|
||||
return sessionIndex.onSessionIndexRequests(app, () => agent);
|
||||
}
|
||||
|
||||
/** Serves ACP v1 on stdio. */
|
||||
export function runAcp(logger?: Logger) {
|
||||
const stream = ndJsonStream(nodeToWebWritable(process.stdout), nodeToWebReadable(process.stdin));
|
||||
let agent!: ClaudeAcpAgent;
|
||||
// `connect` runs the connect handlers before it returns, so `agent` is set.
|
||||
const connection = v1AgentApp(logger, (created) => (agent = created)).connect(stream);
|
||||
return { connection, agent };
|
||||
}
|
||||
|
||||
/**
|
||||
* What a `command_lifecycle` "started" frame says started the command: a
|
||||
* ScheduleWakeup (dynamic `/loop`) wakeup, with the time it was set for in
|
||||
|
||||
Reference in New Issue
Block a user