Skip to content

feat(backend): claude-agent-sdk and copilot-sdk execution harnesses (#2441) - #2451

Merged
bradygaster merged 13 commits into
devfrom
squad/2441-claude-agent-sdk-harness
Nov 10, 2026
Merged

bradygaster merged 13 commits into
devfrom
squad/2441-claude-agent-sdk-harness

Conversation

@bradygaster

@bradygaster bradygaster commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

What

Adds the first two HarnessAdapter implementations to the experimental @bradygaster/squad-sdk/backend subpath, plus a shared contract suite that runs against both. Phase 1 of epic #2438.

  • claude-agent-sdk (ClaudeAgentSdkHarness): runs admitted turns on the Claude Agent SDK. @anthropic-ai/claude-agent-sdk is an optional peer dependency that's loaded lazily, so package-lock.json doesn't change and installs without it are unchanged.
  • copilot-sdk (CopilotSdkHarness): runs turns on @github/copilot-sdk, which is already a dependency.
  • TurnApprovals: one approval queue that both harnesses share.
  • squad state-mcp --experimental-backend registers claude-agent-sdk when its SDK is installed. copilot-sdk is opt-in (SQUAD_BACKEND_HARNESSES=copilot-sdk), because its SDK is always installed. SQUAD_BACKEND_HARNESSES=none turns both off.

Why

Refs #2441. Until now the broker had the HarnessAdapter interface and a registry, but no implementation: squad_backend_send_message could only refuse. §15 of the harness-adapters spec asks for the same contract to run against Copilot and one non-Copilot host.

How

Approvals. Both harnesses use TurnApprovals the same way:

  • Each request is bound to a sha256 of its canonical action. A response with a different hash never resolves it.
  • Parallel tool calls are offered one at a time, because the broker holds one pending approval per turn and drops a second.
  • A deny ends the turn, matching the broker, which marks it cancelled. Queued calls are denied and the runtime is stopped.
  • A redelivered decision is a no-op.

Claude.

  • One query() per turn, in order per conversation, resuming the conversation's session.
  • canUseTool becomes approval-requested, with approvalId = toolUseID. Approve runs the original input unchanged.
  • It runs with permissionMode: 'default' and settingSources: [], so no repository or user allow rule can approve a call the broker never sees.

Copilot.

  • One session per conversation (resumed by id). A turn is send(), then session.idle (completed) or session.error (failed).
  • Only main-agent assistant.message content enters the transcript.
  • onPermissionRequest becomes approvals: approve-once on approve, reject plus abort on deny.

Known limitation. In both harnesses a pending approval lives in process memory and is lost if the harness restarts. The specs say so: the broker's record must then be cancelled and the session resumed.

Tests

  • Shared contract (test/harness-contract.ts): 6 checks per harness through the real LocalSquadBackend, passing for both (12/12). It covers self-description, event identity and epoch, redelivery, hash-bound approvals with replay, one-at-a-time approvals with deny ending the turn, and idempotent cancel.
  • Harness-specific: test/claude-agent-sdk-harness.test.ts (15) and test/copilot-sdk-harness.test.ts (8). The deterministic tests drive each real adapter with a scripted SDK; there is no network.
  • Live, both harnesses:
    • Copilot (2026-11-09, @github/copilot-sdk 1.0.19, signed-in user): one turn through the broker, 4.4 s.
    • Claude (2026-11-10, @anthropic-ai/claude-agent-sdk 0.3.317, Anthropic API key): a plain turn, 6.7 s; and a Bash touch that reached the broker as an approval, was denied, ended the turn cancelled and did not run, 6.1 s.
    • Both are recorded in the host feature matrix (experimental-backend.* → validated-live, with liveRuns entries). Claude Code runs some read-only commands (such as echo) without calling canUseTool, so those never reach the broker; spec §5 says so.
  • Broad run: 1,367 tests pass locally. The 4 failures (export-import, upstream-hardening) fail the same way on clean dev on macOS.

Open questions for reviewers

  • Restart behaviour: a pending approval lives in harness memory. On restart, the broker's pending approval should be cancelled and the session resumed. Does that meet "durable approval versions" (§15 item 6)?
  • Scope: Claude host PR 2: Claude Agent SDK HarnessAdapter (#2438 Phase 1) #2441 asked for the Claude harness. The Copilot harness and the shared TurnHarness lifecycle were added so the shared contract runs on Copilot plus one non-Copilot host (§15 item 1).

Review rounds

Three rounds are addressed and resolved:

  • Turn lifecycle: cancellation during startup; abort ordering; sub-agent events; conversation-scoped approvals; atomic dedupe.
  • Approval-record disclosure: approval records now carry no tool input, only a redacted summary and the hash.
  • Hardening: Copilot configDirectory isolation, and bounded memory.

⚠️ Quick Check

  • Changeset added (.changeset/claude-agent-sdk-harness.md, SDK and CLI, minor)

PR Readiness Checklist

Branch & Commit

  • Branch created from dev, up to date
  • Diff contains only intended changes (21 files)
  • Not in draft
  • Commit history: four focused commits

Build & Test

  • npm run build (SDK and CLI with tsc)
  • npm test: 4 local failures that also fail on clean dev (see above)
  • npm run lint
  • npm run lint:docs

Docs

  • Specs: harness-adapters §5 and the §14 layout row; client-backend decoupling (3 places)
  • Architecture doc, SDK reference, state-mcp help texts
  • Host feature matrix (live run logged, regenerated, --check clean)

Exports

  • Exported from the existing experimental ./backend subpath; no new subpath

Breaking Changes

None. The harnesses are new and experimental. copilot-sdk is opt-in on the bridge, and claude-agent-sdk registers only where its optional SDK is installed.

Waivers

None.

🤖 Generated with Claude Code

bradygaster and others added 4 commits November 9, 2026 17:38
Adds the `claude-agent-sdk` harness (experimental `./backend` subpath),
running admitted turns on @anthropic-ai/claude-agent-sdk, an optional peer
dependency loaded lazily (no lockfile change; @github/copilot-sdk stays a
hard dependency).

- One query() per turn, in order per conversation, resuming the
  conversation's Claude session; submissions deduplicated by operationId
  or (conversationId, runId, seq).
- canUseTool -> approval-requested with approvalId = toolUseID and a
  sha256 hash of the canonical tool name and input; a response with a
  different hash never resolves the call; approve runs the original input.
- The broker holds one pending approval per turn, so parallel tool calls
  are queued and offered one at a time; a deny ends the turn (as the
  broker marks it cancelled), denying queued calls and aborting.
- permissionMode 'default' and settingSources [] so no repository or user
  allow rule approves a call the broker never sees.

Tests: 13 deterministic protocol tests replay scripted SDK exchanges
through the real adapter and LocalSquadBackend; 2 live tests run the real
SDK behind SQUAD_REAL_CLI_TESTS=1 and credentials (not yet run).

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… contract

- `squad state-mcp --experimental-backend` registers the claude-agent-sdk
  harness when the optional @anthropic-ai/claude-agent-sdk peer resolves
  (isClaudeAgentSdkAvailable), running in the repository root;
  SQUAD_BACKEND_HARNESSES=none turns it off. Without the SDK nothing changes.
- test/harness-contract.ts: a reusable HarnessAdapter contract (describe,
  event identity and epoch, redelivery, hash-bound approvals with replay,
  one approval at a time with deny ending the turn, idempotent cancel)
  through the real LocalSquadBackend, run for claude-agent-sdk. No Copilot
  harness exists yet, so it runs against one harness for now.
- Specs, docs and help no longer say no harness ships: harness-adapters §5
  and a §14 layout row, the decoupling spec, the architecture doc, the SDK
  reference and both state-mcp help texts. Changeset added.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tract on both

- copilot-sdk (CopilotSdkHarness) on @github/copilot-sdk: one Copilot
  session per conversation (created, then resumed by id), send() then
  session.idle / session.error; only main-agent assistant.message content
  enters the transcript. onPermissionRequest goes to broker approvals:
  approve-once on approve, reject plus abort on deny; toolCallId is the
  approvalId when present. Cancel aborts without waiting for idle.
- TurnApprovals (approval-queue.ts): the hash-bound, one-at-a-time,
  deny-ends-the-turn queue both harnesses now share; claude-agent-sdk moved
  onto it with its hash format unchanged.
- The shared HarnessAdapter contract runs against copilot-sdk and
  claude-agent-sdk (harness-adapters §15 item 1), plus Copilot-specific
  tests and one live test behind SQUAD_REAL_CLI_TESTS=1.
- state-mcp registers copilot-sdk only when SQUAD_BACKEND_HARNESSES lists
  it, since its SDK is always installed.
- Specs, docs, help and the changeset describe both harnesses.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- liveRuns: copilot / copilot-sdk-harness / 2026-11-09 / pass. One
  plain-text turn through LocalSquadBackend on the real Copilot backend
  (@github/copilot-sdk 1.0.19, signed-in user, 4.4 s); no tool approval.
- experimental-backend.copilot -> validated-live with the lab entry, the
  live test and the shared harness contract; experimental-backend.claude
  cites the contract and harness tests (live runs need an API key).
- The matrix test allows validated-live for copilot now that it has a
  recorded passing run; the limitations note says so.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #2451

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 25
Files added 11
Files modified 14
Files deleted 0
Modules touched 6
Critical files 2

🎯 Risk Factors

  • 25 files changed (21-50 → HIGH)
  • 6 modules touched (5-8 → HIGH)
  • Critical files touched: packages/squad-sdk/package.json, packages/squad-sdk/src/backend/index.ts

📦 Modules Affected

docs (5 files)
  • docs/specification/client-backend-decoupling-v0.2.md
  • docs/specification/client-harness-adapters-v0.2.md
  • docs/src/content/docs/concepts/squad-2-0-architecture.md
  • docs/src/content/docs/reference/host-feature-matrix.md
  • docs/src/content/docs/reference/sdk.md
root (2 files)
  • .changeset/claude-agent-sdk-harness.md
  • test-fixtures/spec/suite-v0.2/host-feature-matrix.json
scripts (1 file)
  • scripts/release/test-reconciliation.mjs
squad-cli (3 files)
  • packages/squad-cli/src/cli/commands/state-mcp.ts
  • packages/squad-cli/src/cli/core/backend-mcp.ts
  • packages/squad-cli/src/cli/core/command-help.ts
squad-sdk (7 files)
  • packages/squad-sdk/package.json
  • packages/squad-sdk/src/backend/harnesses/approval-queue.ts
  • packages/squad-sdk/src/backend/harnesses/claude-agent-sdk.ts
  • packages/squad-sdk/src/backend/harnesses/copilot-sdk.ts
  • packages/squad-sdk/src/backend/harnesses/harness-state.ts
  • packages/squad-sdk/src/backend/harnesses/turn-harness.ts
  • packages/squad-sdk/src/backend/index.ts
tests (7 files)
  • test/claude-agent-sdk-harness.test.ts
  • test/copilot-sdk-harness.test.ts
  • test/harness-contract.test.ts
  • test/harness-contract.ts
  • test/harness-review-round4.test.ts
  • test/squad-20-host-feature-matrix.test.ts
  • test/squad-20-real-clients-stdio.e2e.test.ts

⚠️ Critical Files

  • packages/squad-sdk/package.json
  • packages/squad-sdk/src/backend/index.ts

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

🏗️ Architectural Review

Authoritative evidence: scanners from the PR base commit, posted by the default-branch workflow_run reporter.
Results for head commit 6cdea5dde5c56435dd71d524be9b2c3ea3602bb4.

⚠️ Architectural review: 2 warning(s).

Severity Category Finding Files
🟡 warning bootstrap-area 2 file(s) in the bootstrap area (packages/squad-cli/src/cli/core/) were modified. These files must maintain zero external dependencies. Review carefully. packages/squad-cli/src/cli/core/backend-mcp.ts, packages/squad-cli/src/cli/core/command-help.ts
🟡 warning sweeping-refactor This PR touches 25 files (25 modified/added, 0 deleted). Large PRs are harder to review — consider splitting if possible. —

Automated architectural review — informational only.

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit 6cdea5d

PR Scope: 📦🔧 Mixed (product + infrastructure)

⚠️ 3 item(s) to address before review

Status Check Details
❌ Single commit 13 commits — consider squashing before review
✅ Not in draft Ready for review
✅ Branch up to date Up to date with dev
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present Changeset file found
✅ Scope clean No .squad/ or docs/proposals/ files
✅ No merge conflicts No merge conflicts
✅ Copilot threads resolved 3 active Copilot thread(s) resolved (11 outdated skipped)
❌ CI passing 1 check(s) still running

Files Changed (25 files, +2576 −40)

File +/−
.changeset/claude-agent-sdk-harness.md +6 −0
docs/specification/client-backend-decoupling-v0.2.md +15 −5
docs/specification/client-harness-adapters-v0.2.md +70 −4
docs/src/content/docs/concepts/squad-2-0-architecture.md +1 −1
docs/src/content/docs/reference/host-feature-matrix.md +9 −6
docs/src/content/docs/reference/sdk.md +3 −1
packages/squad-cli/src/cli/commands/state-mcp.ts +5 −2
packages/squad-cli/src/cli/core/backend-mcp.ts +75 −7
packages/squad-cli/src/cli/core/command-help.ts +3 −2
packages/squad-sdk/package.json +8 −0
packages/squad-sdk/src/backend/harnesses/approval-queue.ts +215 −0
packages/squad-sdk/src/backend/harnesses/claude-agent-sdk.ts +245 −0
packages/squad-sdk/src/backend/harnesses/copilot-sdk.ts +251 −0
packages/squad-sdk/src/backend/harnesses/harness-state.ts +78 −0
packages/squad-sdk/src/backend/harnesses/turn-harness.ts +233 −0
packages/squad-sdk/src/backend/index.ts +9 −0
scripts/release/test-reconciliation.mjs +1 −0
test-fixtures/spec/suite-v0.2/host-feature-matrix.json +45 −9
test/claude-agent-sdk-harness.test.ts +328 −0
test/copilot-sdk-harness.test.ts +301 −0
test/harness-contract.test.ts +153 −0
test/harness-contract.ts +211 −0
test/harness-review-round4.test.ts +306 −0
test/squad-20-host-feature-matrix.test.ts +2 −2
test/squad-20-real-clients-stdio.e2e.test.ts +3 −1

Total: +2576 −40


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

…agent-sdk-harness

The only conflict was the generated host-feature-matrix.md; it is
regenerated from the auto-merged matrix JSON, which keeps both sides
(experimental-backend.copilot validated-live with its live run, and
ralph-cli-triage-watch.claude supported from #2450).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@bradygaster bradygaster left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

SDK runtime review (sdk-runtime-dev), against head 2fb5cbcd6832630ad200c01dd908ff3f9e463928 and freshly fetched dev/merge-base 344705b0ae5e392ff03d39278728c038a85d88c9. Five actionable, nonduplicate correctness findings are inline. This is a COMMENT review, not an approval or a formal blocking verdict.

I traced admission/subscription, SDK execution, approval delivery, cancellation, conversation serialization, epoch filtering, optional loading/registration and public exports. All reproductions use the actual adapters and LocalSquadBackend with deterministic in-process SDK doubles, not live API calls. A dependency-free Node 24 runner produced 10 failing invariant cases across these five findings, with 4 passing normal-flow controls (completion/resume/epoch/sequential retry, and hash binding/replay/serialized approvals/deny, for both adapters). Source was not patched.

Validation limitations: npm test -- test/claude-agent-sdk-harness.test.ts test/copilot-sdk-harness.test.ts test/harness-contract.test.ts test/backend.test.ts test/squad-20-host-feature-matrix.test.ts could not start (vitest: command not found). npm run lint, npm run build --workspace packages/squad-sdk, and npm run build --workspace packages/squad-cli could not start (tsc: command not found). Locked restore attempts failed: official npm transport/npm exit-handler failure; offline cache missing zod-to-json-schema; configured mirror returned 404 for locked vite-8.3.6.tgz. No dependency versions or locks were weakened. Consequently I have not independently verified the claimed four broad failures against dev.

No live/charged execution was attempted. Claude live/G1–G9 evidence and restart durability remain explicitly acknowledged draft/promotion work, not new findings here. No later projection/plugin roadmap is being requested. Review was isolated; author branches/worktrees and PR body/draft state were left untouched.

Review of #2451. Both harnesses now share TurnHarness, which owns the
broker-facing lifecycle:
- Atomic acceptance: concurrent deliveries of one operation share one
  acceptance; a failed start forgets it so the broker's retry runs.
- Cancellation survives startup: the run is registered before the runtime
  starts, and every setup await is followed by a checkpoint, so a cancel
  during SDK load, client start or session creation never reaches
  query()/send().
- Ordering: the conversation's next turn starts only after an interrupt
  has finished (Copilot awaits session.abort()), so late events from an
  aborted turn can't be attributed to the next one. cancel() acknowledges
  once the stop is requested, since the broker awaits it.
- Approvals are scoped to their conversation (tool-call ids repeat).

Copilot: events from sub-agents (agentId) no longer complete or fail the
main turn; only sessions that finished creating are aborted.

Approval records: TurnApprovals stores a short summary and a redacted
payload (secret-looking keys and inline credentials masked, long strings
truncated); the hash still covers the full action.

Tests: five new shared contract checks for both harnesses (startup
cancel, concurrent and retried delivery, cross-conversation approval ids,
redaction) and four Copilot-specific ones (cancel during session
creation, slow abort with a queued turn and late events, sub-agent idle
and error).

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cords

Code scanning review on #2451. Inline credential names now allow prefix
segments (X-, GITHUB_, AWS_SECRET_) and cover cookie, access-key and
auth-token names, so Cookie headers and AWS_SECRET_ACCESS_KEY are masked
too (X-API-Key and X-Auth-Token already were). Values stop at quotes and
&, so the surrounding command or URL stays intact. Look-alikes such as
--author=, tokenizer.py and mkdir -p are left alone. Tests cover both.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code scanning review on #2451. redactText now also masks secrets with no
name in front of them: well-known vendor token formats (GitHub, OpenAI and
Anthropic, Slack, AWS, Google, GitLab, npm), JWTs, long opaque
mixed-case base64-like tokens, and the password in scheme://user:pass@host.
Plain hex (git SHAs, checksums), UUIDs, paths and package specs stay
readable on purpose, since the approver has to see what they're approving.
Tests cover both sides.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bradygaster
bradygaster marked this pull request as ready for review November 10, 2026 03:53
Copilot AI auto review requested due to automatic review settings November 10, 2026 03:53
…emory

- Approval records carry only the id, a redacted summary and the hash; the
  tool input is never sent as `payload` (nothing read it), so secrets in
  tool arguments stay out of backend state regardless of redaction
  coverage. The hash still binds the decision to the full input.
- copilot-sdk: optional `configDirectory` for harness sessions. The
  Copilot runtime can keep "approve permanently" decisions (URL prompts)
  in its config directory, so a session sharing the user's default could
  skip the broker for a URL approved in their own CLI. Default unchanged.
- TurnHarness drops finished turns and idle conversation tails, and keeps
  the most recent 10,000 accepted delivery ids, so long-running bridges
  don't grow without bound; late redelivery of a finished turn stays a
  no-op.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

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.

🟡 Changes recommended

Unresolved critical defects can expose credentials, conceal executable code from approvers, bypass deny rules, and repeat tool side effects after a restart.

14 open issues
What changed in this PR

Adds the first execution harnesses to the experimental SDK backend, allowing the conversation broker to dispatch turns to Claude Agent SDK or GitHub Copilot SDK.

Changes:

  • Introduces both adapters with shared turn lifecycle and hash-bound approval handling.
  • Adds configurable harness registration to the opt-in state-mcp backend.
  • Adds contract, regression and live tests; updates specifications and records Copilot live evidence.
File Description
test/​squad-20-real-clients-stdio.e2e.test.ts Updates the no-harness explanatory comment only.
test/​squad-20-host-feature-matrix.test.ts Allows recorded Copilot live validation.
test/​harness-contract.ts Defines shared adapter contract checks.
test/​harness-contract.test.ts Supplies scripted SDK drivers and redaction checks.
test/​copilot-sdk-harness.test.ts Tests Copilot execution, approvals and cancellation.
test/​claude-agent-sdk-harness.test.ts Tests Claude execution, permissions and registration.
test-fixtures/​spec/​suite-v0.2/​host-feature-matrix.json Records harness capabilities and live evidence.
packages/​squad-sdk/​src/​backend/​index.ts Exports adapters and shared lifecycle utilities.
packages/​squad-sdk/​src/​backend/​harnesses/​turn-harness.ts Implements shared turn scheduling and cancellation.
packages/​squad-sdk/​src/​backend/​harnesses/​copilot-sdk.ts Implements Copilot session execution.
packages/​squad-sdk/​src/​backend/​harnesses/​claude-agent-sdk.ts Implements lazily loaded Claude execution.
packages/​squad-sdk/​src/​backend/​harnesses/​approval-queue.ts Implements approval queuing, hashing and redaction.
packages/​squad-sdk/​package.json Declares the optional Claude SDK peer.
packages/​squad-cli/​src/​cli/​core/​command-help.ts Updates help text for Claude registration.
packages/​squad-cli/​src/​cli/​core/​backend-mcp.ts Wires configurable harnesses into the local backend.
packages/​squad-cli/​src/​cli/​commands/​state-mcp.ts Documents harness selection in command help.
docs/​src/​content/​docs/​reference/​sdk.md Documents adapter exports and bridge configuration.
docs/​src/​content/​docs/​reference/​host-feature-matrix.md Updates the generated harness validation matrix.
docs/​src/​content/​docs/​concepts/​squad-2-0-architecture.md Describes optional execution through the bridge.
docs/​specification/​client-harness-adapters-v0.2.md Documents shipped adapters and shared behavior.
docs/​specification/​client-backend-decoupling-v0.2.md Updates implementation status and restart limitations.
.changeset/​claude-agent-sdk-harness.md Adds SDK and CLI minor-release notes.

Review effort: Max (auto)

Comment thread test/claude-agent-sdk-harness.test.ts Resolved
bradygaster and others added 2 commits November 9, 2026 20:19
- The live approval test asks for a write (`touch` in a temp directory),
  since Claude Code runs some read-only commands such as `echo` without
  calling canUseTool. It expects the broker deny to end the turn
  cancelled, checks the file was not created, and prints the turn,
  approvals and harness events on failure.
- Both Claude live tests passed 2026-11-10 (Agent SDK 0.3.317, Anthropic
  API key): a plain turn through the broker, and a write that reached the
  broker, was denied and did not run.
- Matrix: liveRuns claude / claude-agent-sdk-harness, and
  experimental-backend.claude -> validated-live. Spec §5 notes that
  read-only commands can bypass canUseTool.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Copilot correlation

Copilot review of #2451 (round 4).

Durability (HarnessState, `stateDir`): accepted delivery ids and each
conversation's runtime session id survive restarts, so a redelivered turn
never runs twice and the next turn resumes its session. state-mcp keeps
this beside the local backend, or in a per-user cache for configured
backends.

Lifecycle: a failed interrupt stays retryable and tells the harness to stop
reusing the session; dropping an approval the broker already holds ends the
turn instead of offering the next one into the occupied slot.

Claude: the real SDK starts only with an API credential (API key, auth
token, Bedrock, Vertex or Foundry), never a saved claude.ai login, and
state-mcp registers it only then. Deny rules from user, project and local
settings are carried over as disallowedTools, since settingSources: [] would
otherwise drop them.

Copilot: a turn reacts only to events after its own user.message (matched to
send()'s id), so late events from an aborted turn are ignored; a failed send,
failed interrupt or timeout (turnTimeoutMs, default 30 min) drops the cached
session; each permission request of one tool call gets its own approval id;
state-mcp gives the harness its own Copilot config directory.

Redaction: JSON keys, Authorization schemes, credential flags and -u
user:password are masked; executable syntax is never masked.

Bridge: turns run in the worktree-aware repoRoot. The stdio e2e test pins
SQUAD_BACKEND_HARNESSES=none.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
const dir = (id: string) => (options.stateDir ? path.join(options.stateDir, id) : undefined);
const claudeAvailable = options.claudeSdkAvailable ?? isClaudeAgentSdkAvailable;
if (wanted.includes('claude-agent-sdk') && claudeAvailable() && claudeApiCredentialsConfigured(env)) {
registry.register(new ClaudeAgentSdkHarness({ cwd: repoRoot, env, ...(dir('claude-agent-sdk') ? { stateDir: dir('claude-agent-sdk')! } : {}) }));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not changed; leaving this open for a human reviewer. The harness gives env to the Agent SDK as Claude Code's environment, which is the same exposure as before: with no env, the SDK uses the full process environment, as any Claude Code session the user starts does. An allowlist would break the work bridge turns exist for. Approved tool calls need GITHUB_TOKEN/gh auth to open PRs, npm proxy and registry settings, PATH additions and toolchain variables. Those calls are already gated by broker approval, and settings deny rules apply as disallowedTools. Anyone embedding the harness who wants a narrower environment can pass env to ClaudeAgentSdkHarness directly.

🤖 Addressed by Claude Code

bradygaster and others added 2 commits November 9, 2026 20:39
…liation

"Verify exact SHA and build evidence" failed on #2451: the three live
harness tests (claude-agent-sdk x2, copilot-sdk x1) skip without
SQUAD_REAL_CLI_TESTS=1 and credentials, and had no reviewed rule, so they
were unexplained. Add an env-gated rule scoped to the two harness test
files and their "live (real ... SDK)" describe blocks only.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ted config dir

Two live Copilot runs on 2026-11-10 (@github/copilot-sdk 1.0.19, signed-in
user), both passing: a plain turn through the broker (5.7 s), confirming
the new correlation to the turn's own user.message, and the same turn with
a harness-only configDirectory in an empty temp dir (3.2 s), confirming
sign-in still works there. Live turns now use a 2-minute timeout so a
correlation miss fails fast. Recorded in the host feature matrix.

Refs #2441

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bradygaster
bradygaster merged commit 675c9e9 into dev Nov 10, 2026
31 checks passed
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.

3 participants