Skip to content

Claude host PR 2: Claude Agent SDK HarnessAdapter (#2438 Phase 1) #2441

Open

Description

@bradygaster

Part of #2438 (PR 2 of the epic's PR plan, Wave 2). Starts after PR 1 merges. Can be developed in parallel with PR 3; they don't touch the same files.

Goal

Ship the first real HarnessAdapter, built on the Claude Agent SDK (TypeScript), so squad_backend_send_message admits turns instead of returning unavailable. This is the critical path for promoting the v0.2 profiles: client-harness-adapters-v0.2 §15.1 needs contract tests against Copilot plus one non-Copilot host, and client-backend-decoupling-v0.2 §14.4 needs at least one real execution harness.

Kept as its own PR so spec reviewers can read it against harness-adapters §15 and decoupling §14 without unrelated changes.

Settle before implementing

Record the answer in #2438 or the PR description:

  • Open question 2: does the harness run subagents through the Agent SDK's agents option (one query() per conversation), or one query() per member run? This decides how task events map to broker run identity (harness-adapters §7.2).

Scope

Interface: HarnessAdapter in packages/squad-sdk/src/backend/types.ts (~L382). Registry: packages/squad-sdk/src/backend/harness-registry.ts.

  • Adapter with id claude-agent-sdk:
    • submit → query(). Stream assistant and tool messages as message events, and subagent work as task events. Deduplicate by (conversationId, runId, seq) or operationId across retries and restarts.
    • approval-requested → the canUseTool callback. The harness supplies the opaque hash (decoupling §8): SHA-256 of the canonical tool name and input.
    • respondApproval resolves the pending canUseTool promise, idempotently by operationId.
    • cancel(runId) aborts the query, idempotently by operationId.
    • Stamp every outbound event with the submission epoch (decoupling §9).
    • Declare extensionNamespaces: ['x-claude'].
  • Add the Claude Agent SDK as an optional peer dependency, loaded lazily. Without it installed, nothing changes. @github/copilot-sdk stays a hard dependency (making it optional is a separate change).
  • Register the harness with squad state-mcp --experimental-backend when the SDK is available (packages/squad-cli/src/cli/core/backend-mcp.ts), so executionAvailable becomes true. Without a harness the bridge keeps returning unavailable.
  • Auth: the harness takes ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, or Bedrock/Vertex settings, not a personal claude.ai login. Check Anthropic's current terms for products built on the Agent SDK.

Tests

  • Unit tests use a fake SDK transport; no network.
  • Contract tests: the same HarnessAdapter assertions run against Copilot and Claude (harness-adapters §15.1, §15.5).
  • Golden trace: test-fixtures/spec/suite-v0.2/golden-trace.json (G1–G9) against the real Claude harness (decoupling §14.4).
  • Approval traces: approve, deny, hash mismatch and replay round-trip through canUseTool (harness-adapters §15.3, decoupling §14.3).
  • Live tests run only when SQUAD_REAL_CLI_TESTS=1, as test/clients-real-cli.test.ts already does.

Evidence: run the live contract and golden-trace tests against a direct Anthropic API, Bedrock or Vertex endpoint. A Copilot bridge serves the model from Copilot's backend and translates the protocol, so it isn't independent non-Copilot evidence. If a bridge is used anyway, say so in the evidence string.

Handoff note: the live tests need an Anthropic API key (or Bedrock/Vertex credentials), billed separately from any Claude subscription. If whoever implements this can't run them, land the unit and contract tests with the fake transport, and leave the live runs to someone with credentials or a CI secret.

Done when

  • The adapter, optional dependency and registration are in.
  • The unit tests and fake-transport contract tests pass.
  • The live contract and golden-trace runs pass with SQUAD_REAL_CLI_TESTS=1, with the endpoint recorded.
  • The regression guards from PR 1 stay green.

Activity

  1. added
    squadSquad triage inbox — Lead will assign to a member
    squad:agent-orchestration-devAssigned to agent-orchestration-dev (Agent Orchestration Engineer)
    squad:sdk-runtime-devAssigned to sdk-runtime-dev (SDK Runtime Engineer)
    on Nov 9, 2026
  2. github-actions commented on Nov 9, 2026

    @github-actions
    Contributor

    📋 Assigned to sdk-runtime-dev (SDK Runtime Engineer)

    Issue: #2441 — Claude host PR 2: Claude Agent SDK HarnessAdapter (#2438 Phase 1)

    sdk-runtime-dev will pick this up in the next Copilot session.

    For Copilot coding agent: If enabled, this issue will be worked automatically.
    Otherwise, start a Copilot session and say:
    sdk-runtime-dev, work on issue #2441

  3. bradygaster commented on Nov 10, 2026

    @bradygaster
    CollaboratorAuthor

    Kickoff notes (from the #2442 session)

    Context for whoever picks this up. #2442 (Phases 2, 3, 4 and 6, Claude projection v2) is open as #2450.

    Testing approach (proposed): live-first, with only a thin fake.

    • The main path runs against the real Claude Agent SDK behind SQUAD_REAL_CLI_TESTS=1: submit and subscribe, the golden trace G1–G9, and approve or deny through canUseTool. These runs are the evidence anyway, and they can't drift from the real SDK the way a full fake transport can.
    • A thin fake replays one recorded SDK exchange, only for the deterministic protocol checks that must run on every PR, including PRs without credentials: approval hash mismatch, replay, and stale-epoch fencing. It doesn't simulate the SDK.
    • This replaces "Unit tests use a fake SDK transport; no network" if maintainers agree.

    Credentials: an Anthropic API key, or Bedrock or Vertex settings, set in the environment before the session starts. Live runs record the endpoint; a Copilot bridge doesn't count as independent evidence.

    Estimate: about 9–13 hours of implementation. That's lower than first thought, because @github/copilot-sdk stays a hard dependency, as this issue says. The biggest unknown is how cleanly canUseTool maps onto the harness approval contract (hash, scope, replay); if it doesn't, add roughly 4–6 hours.

    Can run in parallel with #2443 (plugin), which waits on #2450 merging. The two don't touch the same files.

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    squadSquad triage inbox — Lead will assign to a membersquad:agent-orchestration-devAssigned to agent-orchestration-dev (Agent Orchestration Engineer)squad:sdk-runtime-devAssigned to sdk-runtime-dev (SDK Runtime Engineer)type:featureNew capability

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions