Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6fc8df5
feat(sdk): Squad 2.0 decoupled backend, harness SPI and MCP bridge
Oct 17, 2026
56a1fa8
test(sdk): Squad 2.0 security & conformance contracts
Oct 17, 2026
1c89b26
feat(clients): multi-client host adapters and --client flag
Oct 17, 2026
25bf9b4
docs: freeze Squad 2.0 protocol baseline
Oct 17, 2026
0352e0e
Merge branch 'squad/2.0-backend-and-durable' into squad/2.0
Oct 17, 2026
e64fbdc
Merge branch 'squad/2.0-client-adapters' into squad/2.0
Oct 17, 2026
22438bf
Merge branch 'squad/2.0-e2e-and-security' into squad/2.0
Oct 17, 2026
beaff5d
fix(2.0): align backend capability vocabulary with spec matrix and re…
Oct 17, 2026
675d4f9
test(2.0): end-to-end all-scenarios suite across merged Squad 2.0 cod…
Oct 17, 2026
e50955a
docs: add Squad 2.0 architecture guides
Oct 17, 2026
f247d0f
Merge branch 'squad/2.0-client-adapters' into squad/2.0
Oct 17, 2026
ef49983
chore(test): resolve Squad SDK entrypoints from source in vitest
Oct 17, 2026
d9961cf
test(2.0): RC1-to-stable exit criteria (upgrade/rollback, fault injec…
Oct 18, 2026
c9ea24b
test(2.0): real child-process stdio MCP and live client binary tests
Oct 18, 2026
41c8a78
fix(clients): Kiro resource paths and coordinator roster, add real-CL…
Oct 18, 2026
ad7246b
fix(clients): Kiro resource paths and coordinator roster, add real-CL…
Oct 18, 2026
af93bca
test(2.0): locate per-user kiro-cli install when not on PATH
Oct 18, 2026
5c82683
feat(clients): add OpenAI Codex and Google Antigravity adapters
Oct 18, 2026
bcde187
fix(clients): guard coordinator Init Mode and clarify static vs mutab…
Oct 18, 2026
f31d4d1
Merge branch 'squad/2.0-client-adapters' into squad/2.0
Oct 18, 2026
617066b
fix(clients): roll back codex TOML and id-scoped blocks; add codex/an…
Oct 18, 2026
588ae6f
feat(clients): per-member subagents with per-client models, Ralph mod…
Oct 18, 2026
26542fc
feat(clients): full coordinator behavioral contract in every non-Copi…
Oct 18, 2026
87a8f7a
docs: add live client demo screenshots (claude, opencode, codex)
Oct 18, 2026
d98a87f
fix(clients): ignore .claude/worktrees and forbid killing processes b…
Oct 18, 2026
54c2ea8
feat(clients,cli): squad cast --sync, Antigravity tools, ascii slugs
Oct 18, 2026
7e27f8f
docs/fix: per-vendor demo folders, interactive Claude (cli-tunnel) ev…
Oct 18, 2026
cd70cba
fix(clients): host-prefixed tool names, dir vs file, builtin subagent…
Oct 18, 2026
ccc5660
docs: refresh kiro and antigravity demo screenshots (antigravity fini…
Oct 18, 2026
098631b
feat(clients): make per-member subagent files opt-in
Oct 18, 2026
01a0773
refactor(clients): drop per-member subagent files; keep accented names
Oct 18, 2026
9c3e265
refactor(cli): remove squad cast --sync; add fresh interactive Claude…
Oct 18, 2026
5f95bd8
docs(demos): interactive cli-tunnel evidence for opencode, Codex, Ant…
Oct 18, 2026
d780eca
docs(demos): captions, controlled-demo label, fresh-session Scribe fr…
Oct 18, 2026
8db8d6a
docs(demos): Claude interactive start and fresh-session Scribe frames
Oct 18, 2026
9f00181
docs(demos): one continuous interactive Claude session (cast, build, …
Oct 18, 2026
ae3195b
test(clients): real cursor-agent loads the generated .cursor/mcp.json
Oct 18, 2026
9eef3f7
refactor(sdk): HostProjection + spec ClientAdapter facade, src/client…
Oct 18, 2026
06682ae
docs(demos): interactive Cursor Agent session (cast, build, issues; R…
Oct 18, 2026
6bddfea
chore: exclude local demo screenshots from the upstream PR (kept on t…
tamirdresher Oct 18, 2026
51a5a96
chore: sync PR branch with squad/2.0 (backend-mcp moved to core/, spe…
Oct 18, 2026
6d095b9
fix(ci): lint/changeset/spellcheck + docs for multi-host clients
Oct 18, 2026
20689bf
chore(pr): drop local worktree paths, internal names and personal-nam…
Oct 18, 2026
cb86aef
chore(release): bump squad-sdk and squad-cli to the 2.0 major via cha…
Oct 18, 2026
699da4d
fix(clients): address Copilot review on client adapters
Oct 18, 2026
0b715e2
fix(backend): address Copilot review on local/durable-http backends (…
Oct 18, 2026
0377874
test(backend): race/fault matrix for PR #2238 review (orderings, rest…
Oct 18, 2026
6451747
fix(backend): lock crash recovery, monotonic approval versions, epoch…
Oct 18, 2026
89a10b1
fix(squad-2.0): Brady re-review round 3 (13 defects + 40 CI failures)
Oct 18, 2026
47bd105
fix(squad-2.0): recover abandoned .recover mutexes, write legacy outb…
Oct 18, 2026
24d6670
fix(upgrade): repair handwritten casting registries before preflight …
Oct 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/squad-2-0-multi-host-clients.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@bradygaster/squad-sdk": major
"@bradygaster/squad-cli": major
---

Squad 2.0: decoupled backend and multi-host client adapters. Add `squad init --client` and `squad upgrade --client` (copilot, claude, kiro, opencode, cursor, codex, antigravity) with additive, non-clobbering config merges, persisted in `.squad/config.json` `clients[]`; add the `HostProjection` / spec `ClientAdapter` SDK surface with canonical capability tokens under `@bradygaster/squad-sdk/client` (and the deprecated `/clients` alias), and a host-agnostic coordinator prompt with per-host dispatch.
11 changes: 11 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@ npm run test:watch
npm run lint
```

### Adding or changing a client host adapter

Host adapters live in `packages/squad-sdk/src/client/adapters/` (the `squad-sdk/clients` export is a deprecated alias for the same symbols).

1. Add the host id to `CLIENT_IDS` and a `HostProjection` in `adapters.ts`: it returns declarative `ProjectionFile`s (`managed`, `json`, `block`, `text`) and never owns routing, durable state or authorization.
2. Declare its capability tokens in `capabilities.ts` (tokens are authoritative; boolean flags are derived) and add its anchor/MCP paths to `client/adapter-contract.ts`.
3. Add its dispatch section in `coordinator.ts` (how the host spawns a specialist) and keep the shared prompt host-agnostic.
4. Merge shared config (JSON/TOML) additively: only Squad's own keys, preserve everything else, refuse malformed input.
5. Add unit tests in `test/clients.test.ts` and, if the host has a real CLI, a skipped-when-absent check in `test/clients-real-cli.test.ts`.
6. Update `docs/src/content/docs/scenarios/using-squad-across-clients.md`, `docs/tutorials/multi-client-quickstart.md`, the CLI reference and add a changeset.

### 5. Keeping Your Fork in Sync

Before opening or updating a PR, rebase your branch on the latest upstream dev:
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ squad upgrade

Use `--force` to re-apply updates even when your installed version already matches the latest.

### Use Squad in other AI hosts

Squad is not tied to one client. Add `--client` to project the same team and the `squad_state` backend into Claude Code, Kiro, OpenCode, Cursor, OpenAI Codex or Google Antigravity (alongside Copilot):

```bash
squad init --client claude,opencode # or: --client all
squad upgrade --client codex # add a host later; refreshes every configured host
```

Each host gets a native coordinator file plus MCP wiring; your own settings in `.mcp.json`, `opencode.json` and the like are never clobbered. See [Using Squad across clients](docs/src/content/docs/scenarios/using-squad-across-clients.md) and the [multi-client quickstart](docs/tutorials/multi-client-quickstart.md).

---

## Local Development Installation
Expand Down Expand Up @@ -163,8 +174,8 @@ Say **"squad commands"** in chat to see a categorized menu of common operations

| Command | What it does |
|---------|-------------|
| `squad init` | **Init** — scaffold Squad in the current directory (idempotent — safe to run multiple times); alias: `cast`; use `--global` to init in personal squad directory, `--mode remote <path>` for dual-root mode |
| `squad upgrade` | Update Squad-owned files to latest; never touches your team state; use `--global` to upgrade personal squad, `--migrate-directory` to rename `.ai-team/` → `.squad/` |
| `squad init` | **Init** — scaffold Squad in the current directory (idempotent — safe to run multiple times); alias: `cast`; `--client <list>` also projects Claude Code, Kiro, OpenCode, Cursor, Codex and Antigravity; use `--global` to init in personal squad directory, `--mode remote <path>` for dual-root mode |
| `squad upgrade` | Update Squad-owned files to latest; never touches your team state; `--client <list>` adds hosts and refreshes every configured projection; use `--global` to upgrade personal squad, `--migrate-directory` to rename `.ai-team/` → `.squad/` |
| `squad upgrade --self` | Update the Squad CLI package itself; add `--insider` for dev-channel prerelease builds |
| `squad update-check` | Report cached CLI update status for tooling/CI; use `--json` for structured output, `--refresh` to bypass the cache |
| `squad status` | Show which squad is active and why |
Expand Down
3 changes: 3 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
"aadnesd",
"ADO",
"AKIA",
"affordances",
"kiro",
"resumability",
"amolchanov",
"anthropics",
"Artsdalen",
Expand Down
333 changes: 333 additions & 0 deletions docs/SQUAD-2.0-OVERVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
# Squad 2.0 Overview

Squad 2.0 is the architectural split that stops treating "the editor" and "the backend" as the same thing.

In Squad 1.x, a lot of the conversation model, routing assumptions, and execution behavior were effectively bundled with the host you happened to be using. That worked fine as long as your world was mostly one client, one runtime, one execution story.

Squad 2.0 is the version where we deliberately break that coupling.

The goal is simple:

- keep the **team experience** consistent;
- let the **host client** be native to Copilot, Claude Code, Kiro, OpenCode, or Cursor;
- let the **backend** stay local or move to **DurableSquad** over HTTP;
- let the **execution harness** evolve independently without rewriting every client projection.

That is what makes the same Squad team portable across hosts without turning the protocol into a pile of editor-specific hacks.

---

## Why Squad 2.0 exists

Squad 2.0 solves three practical problems.

### 1. The client should not own the conversation contract

A host can be fantastic at UI, subagents, or local tooling and still be a terrible place to define durable turn ordering, replay semantics, approvals, or cancellation. Those are backend concerns.

### 2. MCP is useful, but MCP is not the whole protocol

MCP is a great transport for exposing tools locally. It is not, by itself, the canonical wire contract for long-lived conversations, resumable event logs, versioned approvals, or `runId`-scoped cancellation.

### 3. Execution fabric changes faster than the team model

Sometimes you want local in-process execution. Sometimes you want a remote durable broker. Sometimes you want a custom harness. The team should not care. The protocol should.

---

## The 3-plane architecture

Squad 2.0 is intentionally split into three planes.

```mermaid
flowchart LR
user[Developer] --> client

subgraph CP[Client Plane\n@bradygaster/squad-sdk/clients]
client[Host-native adapter\nCopilot / Claude / Kiro / OpenCode / Cursor]
projection[Workspace projection\nagent files + MCP config + rules]
caps[ClientCapabilities\ndispatch + transport modes]
client --> projection
client --> caps
end

subgraph BP[Control / Broker Plane\n@bradygaster/squad-sdk/backend]
backend[SquadBackend / IConversationBackend]
log[Durable ordered event log\nBackendEnvelope + monotonic seq]
auth[Capability negotiation\nclient ∩ backend ∩ policy]
approvals[Versioned approvals\nv1 requested -> v2 decided]
resume[Opaque resume cursor\nlastAcceptedSeq + resumable]
backend --> log
backend --> auth
backend --> approvals
backend --> resume
end

subgraph EP[Execution Plane\nHarnessAdapter SPI]
harness[HarnessAdapter]
exec[Agent runs, tools, streaming, tasks]
fence[Lease / epoch fencing\n(runId, seq) dedupe]
harness --> exec
harness --> fence
end

client --> backend
backend --> harness
```

### Plane 1: Client Plane (`@bradygaster/squad-sdk/clients`)

The client plane is where Squad becomes native to the host you are actually using.

| Concern | What lives here | Examples |
|---|---|---|
| Workspace projection | Host-native files and config | `.mcp.json`, `.claude/agents/squad.md`, `.kiro/settings/mcp.json`, `.cursor/rules/squad.mdc` |
| Capability declaration | Host features the adapter can honestly claim | `ClientCapabilities`, `transportModes`, steering support, subagent support |
| Coordinator dispatch | How the coordinator asks the host to run specialists | Copilot `task` / `create_session` / `runSubagent`, Claude `Task`, OpenCode `@agent`, Cursor subagents |
| Workspace description | The normalized host view of root, working dir, agent anchor, and MCP config | the `projectWorkspace()` concept from the adapter profile |

The important rule is that the client plane owns **bootstrap and translation**, not conversation truth.

It decides things like:

- which files get generated for a client;
- how the host discovers the Squad coordinator;
- how that host dispatches specialists;
- whether the host reaches the backend through MCP, HTTP, or an in-process path.

It does **not** decide durable sequencing, authorization, approval validity, or replay semantics.

### Plane 2: Control / Broker Plane (`@bradygaster/squad-sdk/backend`)

This is the semantic center of gravity.

It owns the durable conversation contract through `SquadBackend` / `IConversationBackend`, including:

- the ordered event log (`BackendEnvelope`);
- per-conversation monotonic `seq`;
- stable `runId` independent from `seq`;
- opaque resume cursors and reconnect semantics;
- capability negotiation through `/v1/capabilities`;
- versioned approvals;
- `runId`-scoped cancellation;
- canonical error shapes.

```mermaid
sequenceDiagram
participant Host as Client adapter
participant Broker as SquadBackend
participant Harness as HarnessAdapter

Host->>Broker: sendMessage(runId, seq)
Broker-->>Host: BackendAdmission + cursor
Broker->>Harness: submit(runId, seq)
Harness-->>Broker: partial output / task events
Harness-->>Broker: approval-requested(v1)
Host-xBroker: disconnect
Host->>Broker: readEvents(after=cursor)
Broker-->>Host: missed events + nextCursor
Host->>Broker: respondApproval(v2, payloadHash)
Host->>Broker: cancelRun(runId)
Broker-->>Host: terminal turn state
```

A few details matter a lot here:

#### Durable ordered event log

`BackendEnvelope` is the canonical event shape. Every transport can project into it, but the meaning lives here.

#### Monotonic ordering

`seq` is the per-conversation ordering coordinate. It is how the broker preserves canonical history. It is not the thing you cancel.

#### Resume without leaking backend internals

Clients reconnect with opaque cursors. The backend returns `nextCursor`, `lastAcceptedSeq`, and `resumable`. The client treats that cursor as a token, not a structure.

#### Capability intersection

Effective behavior is negotiated, not guessed:

```text
effectiveCapabilities = clientCapabilities ∩ backendCapabilities ∩ policyCapabilities
```

That means:

- the client cannot grant itself powers it does not have;
- the backend cannot accidentally expose things policy denied;
- a config file cannot widen authority just because it listed an optimistic client slug.

#### Versioned approvals

Approvals are durable resources. The pattern is:

- **v1**: request is created;
- **v2**: request is decided;
- the decision must echo the immutable payload digest.

In the current SDK types the response field is named `hash`; in the protocol docs and fixtures you will also see this described as the approval payload hash. Same idea, same safety rule: no stale or mutated approval payload may execute.

#### `runId` cancellation

Cancellation targets the stable run lifecycle, not a host-local completion flag and not a turn sequence. That is why the backend exposes `cancelRun(conversationId, runId, reason?)`.

### Plane 3: Execution Plane (`HarnessAdapter` SPI)

This is where work actually runs.

A `HarnessAdapter` translates the broker contract into a concrete execution primitive such as a local session runner, a durable worker, or a provider-specific runtime.

| Concern | What lives here |
|---|---|
| Work submission | `submit(conversationId, runId, seq, ...)` |
| Event streaming | `subscribe(...)` |
| Lifecycle control | `cancel(...)`, `respondApproval(...)` |
| Runtime description | `describe()` |
| Vendor extensions | `x-<harness>.*` namespaces |

The broker expects the execution plane to respect these rules:

- extension keys stay in the `x-<harness>.*` namespace;
- duplicate submissions are harmless because the broker dedupes on `(runId, seq)`;
- lease / epoch fencing prevents stale workers from writing after a takeover;
- harness-specific behavior must not change the canonical conversation semantics.

---

## Why MCP is a transport bridge, not the wire contract

This distinction is the part that saves Squad 2.0 from turning into five subtly incompatible products.

```mermaid
flowchart TD
host[Client host] --> choice{How does this host reach the backend?}
choice --> mcp[MCP stdio bridge\n.mcp.json / .kiro/settings/mcp.json / opencode.json / .cursor/mcp.json]
choice --> http[Direct HTTP backend\nDurableHttpSquadBackend\nSQUAD_BACKEND_URL]
mcp --> contract[Same SquadBackend contract]
http --> contract
```

### Local bridge path

A host can load Squad through local MCP configuration:

- `.mcp.json`
- `.kiro/settings/mcp.json`
- `opencode.json`
- `.cursor/mcp.json`

That is the **bridge**.

### Direct backend path

A host or application can also talk directly to the backend over HTTP using `DurableHttpSquadBackend` and `SQUAD_BACKEND_URL`.

That is a **different transport** reaching the same contract.

### Why the distinction matters

If MCP were the protocol, every host quirk would become protocol law.

If `SquadBackend` is the protocol, then:

- MCP stays useful for local tool transport;
- HTTP stays useful for durable remote operation;
- both still produce the same durable conversation semantics.

That is the whole game.

---

## Security and conformance gates (`G1`-`G9`)

Squad 2.0 is not "done" because the markdown says so. It is done when the gates hold.

| Gate | What it proves |
|---|---|
| `G1` | stable `runId` exists independently from turn ordering |
| `G2` | per-conversation ordering stays monotonic by `seq` |
| `G3` | approvals are durable, versioned resources with immutable payload hashes |
| `G4` | effective capabilities are negotiated as `client ∩ backend ∩ policy` |
| `G5` | reconnect is cursor-based and reports `lastAcceptedSeq` + `resumable` |
| `G6` | cancellation targets `runId`, not host-local completion state |
| `G7` | health probes stay outside the versioned protocol resource tree |
| `G8` | projections are loss-tolerant for unknown durable events and extensions |
| `G9` | the frozen fixtures and cross-adapter trace pass unchanged |

You can think of them in three buckets:

### Security / isolation

- path guards;
- non-clobbering MCP merges;
- no secrets in generated projections;
- stale writers get fenced.

### Protocol correctness

- ordering is monotonic;
- reconnect is resumable;
- approvals are versioned and hash-bound;
- cancellation is scoped to the run lifecycle.

### Conformance

- every adapter sees the same canonical history;
- unknown future events do not crash a projection;
- the golden fixtures remain immutable across implementations.

---

## The golden conformance trace

The canonical trace frozen under `squad-protocol-v2.0-rc1` is:

```text
TurnAccepted(runId, seq)
-> stream partial output
-> ApprovalRequested(v1)
-> disconnect
-> reconnect(cursor)
-> ApprovalDecided(v2)
-> CancellationRequested(runId)
-> terminal state
```

That trace sounds small. It is doing a lot of work.

It proves that the implementation can survive:

- streaming output before the approval boundary;
- disconnection and reconnect from an opaque cursor;
- exactly-once approval effects even after replay attempts;
- the cancel-versus-complete race;
- identical canonical history across Copilot, Claude, Kiro, OpenCode, and Cursor projections.

If one adapter renders prettier output than another, fine. If one adapter changes the canonical history, not fine.

---

## What this buys you in practice

When Squad 2.0 is wired correctly:

- you can initialize the same repo for multiple clients with `--client` projections;
- you can start local with MCP and later move to `DurableSquad` over HTTP;
- you can swap or add harnesses without rewriting client projections;
- you can reason about failures using one contract instead of five host-specific stories.

That is the point of the split.

Not more abstraction for its own sake. Less accidental coupling.

---

## Related docs

- `docs/tutorials/multi-client-quickstart.md`
- `docs/tutorials/durable-backend-and-custom-harness.md`
- `docs/src/content/docs/concepts/squad-2-0-architecture.md`
- `docs/src/content/docs/scenarios/using-squad-across-clients.md`
- `docs/src/content/docs/scenarios/client-compatibility.md`
Loading
Loading