Skip to content

feat: add Agent SDK adapter for claude:agent-sdk connected mode#107

Merged
teng-lin merged 9 commits intomainfrom
feat/agent-sdk-adapter
Feb 21, 2026
Merged

feat: add Agent SDK adapter for claude:agent-sdk connected mode#107
teng-lin merged 9 commits intomainfrom
feat/agent-sdk-adapter

Conversation

@teng-lin
Copy link
Copy Markdown
Owner

Summary

  • Adds claude:agent-sdk as a new connected mode using @anthropic-ai/claude-agent-sdk (in-process, no child process)
  • Widens CliAdapterName union to include the new specifier; all downstream consumers (factory, resolver, validator) pick it up automatically
  • SDK is dynamically imported only when claude:agent-sdk is actually used — zero cost for other adapters

Key components

  • AgentSdkSession: BackendSession with AsyncMessageQueue, multi-turn input iterable, resume support via backend session ID capture from system:init
  • PermissionBridge: converts SDK's Promise-based canUseTool callback to BeamCode's message-based permission_request/permission_response flow (2-min timeout, cancelAll on close)
  • SDK message translator: thin wrapper — shared types (assistant, result, stream_event, etc.) delegated to existing Claude translator via cast; SDK-only types (hooks, tasks, compact_boundary) handled directly
  • AgentSdkAdapter: lightweight BackendAdapter, forward-connection (not InvertedConnectionAdapter)

Design decisions

  • Forward-connection adapter (direct-connection path in SessionCoordinator, like Codex/ACP)
  • Dynamic import() in async AgentSdkSession.create() factory — heavy SDK only loaded when used
  • Reuses existing Claude message translator for shared message types to avoid duplicating ~200 lines
  • sendRaw() throws (same pattern as CodexSession)

Test plan

  • npx tsc --noEmit — compiles cleanly
  • 57 new tests across 5 test files (adapter, session, translator, permission-bridge, compliance suite)
  • Full test suite: 2507/2507 pass, 0 regressions
  • E2E: beamcode --adapter claude:agent-sdk starts session, web UI works
  • E2E: permission request flows through to web UI
  • E2E: interrupt aborts SDK query
  • E2E: resume preserves context across sessions

Implements BackendAdapter using @anthropic-ai/claude-agent-sdk as an
alternative connected mode for Claude. The SDK is dynamically imported
only when the adapter is actually used, keeping the default bundle lean.

Key components:
- AgentSdkSession: BackendSession with AsyncMessageQueue, multi-turn
  input iterable, and resume support via backend session ID capture
- PermissionBridge: converts SDK's Promise-based canUseTool callback
  to BeamCode's message-based permission flow (2-min timeout)
- SDK message translator: thin wrapper delegating shared types to
  existing Claude translator, handling SDK-only types directly
- Widen CliAdapterName to include "claude:agent-sdk"

57 new tests across 5 test files including compliance suite.
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @teng-lin, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the system's ability to interact with the Claude AI by integrating the Anthropic Claude Agent SDK. This new integration method operates in-process, offering a more direct and potentially faster communication channel compared to traditional WebSocket-based connections. It includes robust session management, a mechanism for handling tool-use permissions, and efficient message translation, all designed to be dynamically loaded to minimize impact on other adapter functionalities.

Highlights

  • New Claude Agent SDK Adapter: Introduced a new claude:agent-sdk connected mode, allowing in-process communication with the Claude Agent SDK using @anthropic-ai/claude-agent-sdk.
  • Dynamic SDK Import: The heavy @anthropic-ai/claude-agent-sdk is now dynamically imported, ensuring zero overhead for users who do not utilize this specific adapter.
  • Core Components for Agent SDK Integration: Implemented AgentSdkSession for managing multi-turn conversations and resume support, PermissionBridge to translate SDK permission requests to BeamCode's message flow, and an SDK message translator for handling SDK-specific message types while reusing existing Claude translator logic for shared types.
  • Adapter Resolution and Capabilities: The CliAdapterName union was widened to include claude:agent-sdk, automatically integrating it into the adapter factory, resolver, and validator. The new adapter declares capabilities for streaming, permissions, and local availability.
Changelog
  • package.json
    • Added @anthropic-ai/claude-agent-sdk as a new dependency.
  • pnpm-lock.yaml
    • Updated lockfile to include @anthropic-ai/claude-agent-sdk and its transitive dependencies, including various @img/sharp platform-specific packages.
  • src/adapters/adapter-resolver.test.ts
    • Updated availableAdapters test to include claude:agent-sdk.
  • src/adapters/agent-sdk/agent-sdk-adapter.test.ts
    • Added new test file for AgentSdkAdapter to verify its name, capabilities, and connection/stop behavior.
  • src/adapters/agent-sdk/agent-sdk-adapter.ts
    • Added new file defining AgentSdkAdapter, a lightweight BackendAdapter for the Claude Agent SDK, handling forward-connections and dynamic SDK loading.
  • src/adapters/agent-sdk/agent-sdk-compliance.test.ts
    • Added new compliance test file for AgentSdkAdapter, mocking the Agent SDK's query function to echo user messages.
  • src/adapters/agent-sdk/agent-sdk-session.test.ts
    • Added new test file for AgentSdkSession, including tests for session creation, message translation, sendRaw behavior, send after close, session closing, and backend session ID capture.
  • src/adapters/agent-sdk/agent-sdk-session.ts
    • Added new file defining AgentSdkSession, an in-process BackendSession for the Claude Agent SDK, managing message translation, input/output queues, and permission handling.
  • src/adapters/agent-sdk/index.ts
    • Added new index file to export AgentSdkAdapter, AgentSdkSession, and PermissionBridge.
  • src/adapters/agent-sdk/permission-bridge.test.ts
    • Added new test file for PermissionBridge, covering tool request handling, resolution, cancellation, and timeout behavior.
  • src/adapters/agent-sdk/permission-bridge.ts
    • Added new file defining PermissionBridge, which translates Agent SDK's canUseTool callbacks into BeamCode's message-based permission flow with timeout support.
  • src/adapters/agent-sdk/sdk-message-translator.test.ts
    • Added new test file for SdkMessageTranslator, verifying translation of various SDK message types to UnifiedMessages and user message extraction.
  • src/adapters/agent-sdk/sdk-message-translator.ts
    • Added new file defining SdkMessageTranslator, responsible for translating Agent SDK messages to UnifiedMessages, delegating shared types to the existing Claude translator and handling SDK-specific types directly.
  • src/adapters/create-adapter.test.ts
    • Updated createAdapter test to include the new claude:agent-sdk adapter.
  • src/adapters/create-adapter.ts
    • Imported AgentSdkAdapter and added a case to createAdapter to instantiate AgentSdkAdapter for claude:agent-sdk.
  • src/core/interfaces/adapter-names.ts
    • Updated CliAdapterName type and CLI_ADAPTER_NAMES array to include claude:agent-sdk.
  • tsdown.config.ts
    • Updated build configuration to include the new adapters/agent-sdk entry point.
Activity
  • The pull request author has implemented a new claude:agent-sdk connected mode.
  • The author has widened the CliAdapterName union to include the new specifier, ensuring automatic integration.
  • The SDK is dynamically imported, optimizing for performance when not in use.
  • Key components like AgentSdkSession, PermissionBridge, and an SDK message translator have been developed.
  • The author has added 57 new tests across 5 test files, covering the adapter, session, translator, permission bridge, and compliance suite.
  • All existing tests (2507) passed with 0 regressions after the changes.
  • E2E tests are planned to verify session startup, web UI functionality, permission request flow, interrupt handling, and context preservation on resume.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new claude:agent-sdk adapter, which is a significant and well-implemented feature. The code is well-structured, with clear separation of concerns between the adapter, session, permission bridge, and message translator. The use of dynamic imports for the SDK is a great performance consideration. The test coverage is extensive, including unit tests, compliance tests, and integration tests, which provides high confidence in the changes. I have a couple of minor suggestions to improve test determinism and code readability, but overall this is an excellent contribution.

Comment on lines +223 to +224
// Wait a tick for the stream processing to capture the ID
await new Promise((r) => setTimeout(r, 10));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using setTimeout in tests can introduce flakiness. The backendSessionId is set within the consumeStream method just before the message is enqueued. Since await iter.next() waits for a message to be enqueued, the backendSessionId should be available immediately after, without needing to wait for another event loop tick. This setTimeout appears unnecessary. Removing it will make the test more robust and deterministic.

References
  1. To prevent flakiness, tests should avoid using setTimeout for synchronization and instead use deterministic helpers or wait for specific asynchronous events.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 690017f — removed the setTimeout. The backendSessionId is set before queue.enqueue(), so it's available immediately after iter.next() resolves.

const sessionId = (sdkMsg as Record<string, unknown>).session_id as string;
if (sessionId) {
// Store the backend session ID for resume support
(this as { backendSessionId?: string }).backendSessionId = sessionId;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For better code clarity and maintainability, class properties should be declared at the top of the class definition. The backendSessionId property is currently declared at the end of the class, and a type assertion is used here to assign to it. Moving the declaration to the top of the AgentSdkSession class with the other properties would make the code easier to read and remove the need for this cast.

Suggested change
(this as { backendSessionId?: string }).backendSessionId = sessionId;
this.backendSessionId = sessionId;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 690017f — moved backendSessionId declaration to the top of the class with other properties and removed the type assertion cast.

19 tests covering full session lifecycle: streaming, multi-turn,
permission flow (approve/deny/updatedInput), interrupt, error handling,
close, SDK-only system messages, backend session ID capture, and resume.
Add live backend E2E tests exercising the full Agent SDK session
lifecycle via SessionCoordinator.createSession({ adapterName: "claude:agent-sdk" }).

- 7 smoke tests (SDK package + Claude CLI, no API key needed):
  createSession, consumer session_init, multi-consumer, disconnect/reconnect,
  deleteSession, concurrent sessions, non-existent session delete
- 5 full tests (require CLI auth):
  user_message → assistant reply, streaming events, multi-turn,
  broadcast to two consumers, interrupt mid-turn
- Add getAgentSdkPrereqState() and isSdkPackageAvailable() to prereqs.ts
- Replace 12 hand-written tests with registerSharedSmokeTests +
  registerSharedFullTests, matching gemini/opencode pattern (22 tests)
- Remove ANTHROPIC_API_KEY fallback from agent-sdk prereqs — SDK
  uses CLI auth only, no separate API key needed
…Id declaration

- Remove unnecessary setTimeout in backendSessionId test — the ID is
  set before enqueue, so it's available after iter.next() resolves
- Move backendSessionId declaration to class top with other properties,
  remove type assertion cast
Add test:e2e:real:agent-sdk and test:e2e:real:agent-sdk:smoke
scripts matching the pattern of other adapters.
- Add pnpm test:e2e:real:agent-sdk to per-backend commands
- Add agent-sdk row to prerequisites table (CLI auth, no API key)
- Add Claude Agent SDK row to adapter comparison table
@teng-lin teng-lin merged commit 9b17b98 into main Feb 21, 2026
7 checks passed
@teng-lin teng-lin deleted the feat/agent-sdk-adapter branch February 22, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant