Repository navigation
Description
Overview
SDK-First Squad Mode introduces an opt-in TypeScript-based workflow for defining squads, agents, routing, ceremonies, and hooks — with compile-time validation and full observability. Instead of hand-editing markdown files in .squad/, power users define their team in TypeScript using typed builder functions (defineTeam(), defineAgent(), defineRouting(), etc.), then run squad build to generate the standard .squad/ directory for Copilot CLI compatibility.
Who it's for: Developer teams who want type safety, programmatic routing, testable configurations, and — critically — telemetry. SDK mode activates the existing OpenTelemetry infrastructure that is currently dead code in markdown-only mode.
Why it matters: Squad already has 6+ OTel runtime modules (otel-init, otel-metrics, otel-bridge, event-bus, squad-observer, cost-tracker) that cannot fire in markdown-only mode because Copilot CLI owns the runtime loop. SDK mode flips this: the SDK owns the runtime, EventBus fires on every event, and OTel bridge captures spans. This is the activation path for observability that the team has been building toward since Wave A.
Design principle: Dual-mode, not replacement. Markdown stays the default — zero-dependency, works everywhere, non-developer accessible. SDK is the power-user upgrade path. The bridge (squad build: TS → markdown) lets both modes coexist. This is the Next.js pattern: simple pages for beginners, full framework for power users.
Problem Statement
1. Telemetry Blindness
Squad runs inside Copilot CLI's sandbox. We have no hook into session creation, tool calls, routing decisions, or token usage. The OTel infrastructure (initSquadTelemetry(), EventBus.subscribe(), SquadObserver, metric recorders) exists but has no activation path. Teams running Squad in production cannot answer: "How many tokens did Fenster use on that feature?" or "Why did the coordinator route to Verbal instead of Edie?"
2. No Type Safety for Team Definitions
team.md, routing.md, and agent charters are free-form markdown. A typo in an agent name in routing.md silently routes to nobody. A ceremony referencing a non-existent agent fails at runtime. There is no compile-time validation that the team definition is internally consistent.
3. No Programmatic Routing
Routing rules in routing.md are markdown tables parsed at runtime. Complex routing logic (e.g., "route security issues to Baer, unless Baer is overloaded, then fall back to Kujan with context about the security domain") requires markdown gymnastics. Functions are the natural unit for routing logic.
4. No Testable Configuration
You cannot unit test that your routing rules are correct, that your hook pipeline blocks the right commands, or that your ceremony definitions are well-formed. With TypeScript definitions, you can import your squad config into Vitest and assert against it.
5. No Composability
Multi-squad configurations (e.g., importing agents from a shared library squad into a project squad) require manual copy-paste of markdown files. TypeScript imports solve this naturally.
Proposed Solution: Dual-Mode Architecture with Bridge
┌─────────────────────────────────────────────────────┐
│ SDK Mode (TypeScript source of truth) │
│ │
│ squad/ │
│ ├── team.ts → defineTeam() │
│ ├── agents/ │
│ │ ├── keaton.ts → defineAgent() │
│ │ └── fenster.ts → defineAgent() │
│ ├── routing.ts → defineRouting() │
│ ├── ceremonies.ts → defineCeremony() │
│ ├── hooks.ts → defineHooks() │
│ └── index.ts → export { team, agents, ... } │
│ │
│ squad build │
│ ─────────── ↓ generates ↓ ────────── │
│ │
│ .squad/ (standard markdown format) │
│ ├── team.md (generated, do not edit) │
│ ├── routing.md (generated, do not edit) │
│ ├── agents/ │
│ │ ├── keaton/charter.md (generated) │
│ │ └── fenster/charter.md (generated) │
│ ├── ceremonies.md (generated) │
│ ├── decisions.md (PRESERVED — user-owned) │
│ └── agents/*/history.md (PRESERVED — user-owned) │
│ │
│ .squad/decisions.md stays markdown (append-only) │
│ .squad/agents/*/history.md stays markdown │
│ .squad/orchestration-log/ stays markdown │
└─────────────────────────────────────────────────────┘
Key invariant: squad build NEVER overwrites decisions.md, history.md, or orchestration-log/. These are append-only, human-readable, merge-driver-compatible files that stay markdown regardless of mode.
CLI Commands
squad init --sdk
Scaffolds an SDK-mode squad project.
squad init --sdkWhat it creates:
squad/
├── team.ts # Team definition with defineTeam()
├── agents/
│ └── index.ts # Agent definitions with defineAgent()
├── routing.ts # Routing rules with defineRouting()
├── ceremonies.ts # Ceremony definitions with defineCeremony()
├── hooks.ts # Hook pipeline with defineHooks()
├── index.ts # Barrel export
└── tsconfig.json # TypeScript config (extends root)
.squad/ # Generated (gitignored except decisions/history)
├── team.md
├── routing.md
├── decisions.md # User-owned, NOT generated
└── agents/*/history.md # User-owned, NOT generated
Flags:
--sdk— Enable SDK mode (TypeScript definitions)--template <name>— Starter template (minimal,full,monorepo)--telemetry— Pre-wire OTel configuration (addstelemetry.tswith Aspire/OTLP defaults)--no-build— Skip initialsquad buildafter scaffolding
Behavior:
- Detects project type (
detectProjectType()) for smart defaults - Scaffolds
squad/directory with typed definitions - Adds
@bradygaster/squad-sdkas a devDependency - Runs
squad buildto generate initial.squad/directory - Adds
.squad/team.mdand.squad/routing.mdto.gitignore(generated files) - Preserves
.squad/decisions.mdand.squad/agents/*/history.mdin git
squad build
Compiles TypeScript squad definitions into .squad/ markdown format.
squad build [--watch] [--check] [--dry-run]Flags:
--watch— Rebuild on file changes (usesSquadObserver.classifyFile())--check— Validate without writing (CI mode)--dry-run— Show what would be generated without writing
What it does:
- Loads
squad/index.ts(orsquad.config.tsfor hybrid mode) - Validates the typed config at compile time (TypeScript) and runtime (Zod schemas)
- Generates
.squad/team.mdfromdefineTeam()output - Generates
.squad/routing.mdfromdefineRouting()output - Generates
.squad/agents/*/charter.mdfromdefineAgent()outputs - Generates
.squad/ceremonies.mdfromdefineCeremony()outputs - Stamps generated files with
<!-- Generated by squad build. Do not edit. -->header - SKIPS
decisions.md,history.md,orchestration-log/(user-owned)
Exit codes:
0— Success1— Validation error (type mismatch, missing agent reference, etc.)2— File system error
squad run (SDK Runtime Mode)
Launches the squad with SDK owning the runtime loop — activating telemetry.
squad run [--telemetry] [--otel-endpoint <url>]Flags:
--telemetry— Activate OTel pipeline (equivalent toSQUAD_TELEMETRY=1)--otel-endpoint <url>— OTLP endpoint (default:http://localhost:4317)--aspire— Use Aspire dashboard defaults
What it does:
- Calls
initSquadTelemetry()fromotel-init.ts - Creates
EventBusinstance and subscribesSquadObserver - Starts
HookPipelinewith policy config fromdefineHooks() - Launches coordinator with
CopilotSessionfrom SDK client - Routes work through typed routing functions (not markdown table parsing)
- Fires events on every lifecycle transition (
session:created,session:message,coordinator:routing,session:tool_call, etc.) - OTel bridge captures spans for every event
squad migrate --to-sdk
Converts an existing markdown-only squad to SDK mode.
squad migrate --to-sdk [--dry-run] [--preserve-markdown]Flags:
--to-sdk— Convert markdown → TypeScript--dry-run— Show migration plan without executing--preserve-markdown— Keep original.squad/files as backup
What it does:
- Reads
.squad/team.md→ generatessquad/team.tswithdefineTeam() - Reads
.squad/routing.md→ generatessquad/routing.tswithdefineRouting() - Reads
.squad/agents/*/charter.md→ generatessquad/agents/*.tswithdefineAgent() - Reads
.squad/ceremonies.md→ generatessquad/ceremonies.tswithdefineCeremony() - Creates
squad/index.tsbarrel - Runs
squad build --checkto validate round-trip fidelity - Updates
.gitignorefor generated files - Preserves
decisions.md,history.md,orchestration-log/untouched
Coordinator Prompt Changes
The coordinator (squad.agent.md / copilot-instructions.md) needs to detect and adapt to SDK mode.
Detection
The coordinator checks for SDK mode presence:
## Mode Detection
If the file `squad/index.ts` exists in the project root:
→ This is an SDK-mode squad. TypeScript definitions in `squad/` are the source of truth.
→ `.squad/` markdown files are generated artifacts. Do NOT edit them directly.
→ Direct users to edit `squad/*.ts` files and run `squad build`.
If `squad/index.ts` does NOT exist:
→ This is a markdown-mode squad. `.squad/` files are the source of truth.
→ Edit `.squad/*.md` files directly as usual.
Behavioral Changes in SDK Mode
-
Routing awareness: The coordinator reads routing logic from the compiled config (loaded via
loadConfig()) rather than parsingrouting.mdtables. This enables the coordinator to understand programmatic routing rules that can't be expressed in markdown tables. -
Agent discovery: Agent capabilities are read from the typed
defineAgent()definitions (compiled and available viasquad buildoutput), giving the coordinator richer metadata (tool lists, model tier preferences, capability tags) than markdown charters alone. -
Edit redirection: When a user says "add a new agent" or "change the routing," the coordinator routes to the appropriate squad member with instructions to modify
squad/*.tsfiles, NOT.squad/*.mdfiles. -
Build reminder: After any structural change (new agent, routing update, ceremony change), the coordinator reminds the user to run
squad buildto regenerate.squad/artifacts.
Type Surface
Core Builder Functions
import {
defineTeam,
defineAgent,
defineRouting,
defineCeremony,
defineHooks,
defineConfig
} from '@bradygaster/squad-sdk';
// Team definition
export const team = defineTeam({
name: 'squad-sdk',
description: 'The programmable multi-agent runtime for GitHub Copilot',
project: {
stack: ['TypeScript', 'Node.js', 'Vitest'],
repository: 'bradygaster/squad',
},
agents: [keaton, fenster, verbal, edie /* ... */],
coordinator: {
routingMode: 'eager', // 'eager' | 'conservative' | 'custom'
scribeAutoRuns: false,
allowRecursiveSpawn: false,
},
});Agent Builder
export const keaton = defineAgent({
name: 'Keaton',
role: 'Lead',
expertise: ['architecture', 'code-review', 'trade-offs'],
style: 'Decisive. Opinionated when it matters.',
owns: ['Product direction', 'Architectural decisions', 'Quality gates'],
model: { tier: 'premium', preferred: 'claude-opus-4.6' },
tools: ['github-mcp-server', 'code-review'],
capabilities: {
canWrite: ['.squad/decisions/**', 'docs/proposals/**'],
canRead: ['**/*'],
},
});Routing Builder
export const routing = defineRouting({
rules: [
{
match: (task) => task.workType === 'architecture',
route: keaton,
confidence: 'high',
},
{
match: (task) => task.workType === 'core-runtime',
route: fenster,
confidence: 'high',
},
{
match: (task) => task.files?.some(f => f.endsWith('.test.ts')),
route: hockney,
confidence: 'medium',
},
{
// Complex routing: security with fallback
match: (task) => task.workType === 'security',
route: (task, context) => {
if (context.agentLoad('baer') > 0.8) {
return { agent: kujan, context: 'Security fallback — Baer at capacity' };
}
return { agent: baer };
},
confidence: 'high',
},
],
moduleOwnership: {
'src/adapter/': { primary: fenster, secondary: kujan },
'src/agents/': { primary: verbal, secondary: fenster },
'src/runtime/': { primary: fortier, secondary: fenster },
},
fanOut: {
trigger: /^team,?\s/i, // "Team, ..." messages fan out
parallel: true,
},
});Key Types
interface TeamDefinition {
name: string;
description: string;
project: ProjectContext;
agents: AgentDefinition[];
coordinator: CoordinatorConfig;
}
interface AgentDefinition {
name: string;
role: string;
expertise: string[];
style: string;
owns: string[];
model?: ModelPreference;
tools?: string[];
capabilities?: AgentCapabilities;
hooks?: AgentHooks;
}
interface RoutingRule {
match: (task: TaskContext) => boolean;
route: AgentDefinition | RoutingFunction;
confidence: 'high' | 'medium' | 'low';
description?: string;
}
interface RoutingFunction {
(task: TaskContext, context: RoutingContext): RoutingDecision;
}
interface TaskContext {
workType: WorkType;
description: string;
files?: string[];
labels?: string[];
priority?: Priority;
}
interface RoutingContext {
agentLoad: (name: string) => number;
recentRouting: RoutingDecision[];
activeAgents: string[];
}
type WorkType =
| 'feature-dev' | 'bug-fix' | 'testing' | 'documentation'
| 'refactoring' | 'architecture' | 'research' | 'triage'
| 'security' | 'meta' | string; // extensibleImport/Export
squad export with SDK Mode
When exporting an SDK-mode squad, squad-export.json includes both the compiled markdown AND the TypeScript source:
{
"version": "2.0",
"exported_at": "2026-03-06T...",
"squad_version": "0.9.0",
"mode": "sdk",
"sdk_sources": {
"squad/team.ts": "export const team = defineTeam({ ... })",
"squad/agents/keaton.ts": "export const keaton = defineAgent({ ... })",
"squad/routing.ts": "export const routing = defineRouting({ ... })",
"squad/ceremonies.ts": "...",
"squad/hooks.ts": "...",
"squad/index.ts": "..."
},
"compiled": {
"casting": { "registry": { ... }, "policy": { ... }, "history": { ... } },
"agents": {
"keaton": { "charter": "...", "history": "..." }
},
"skills": ["..."]
}
}Key decisions:
version: "2.0"— New schema version to distinguish from1.0(markdown-only)mode: "sdk" | "markdown"— Explicit mode fieldsdk_sources— Raw TypeScript source files for full round-trip fidelitycompiled— Standard1.0-compatible structure for backward compatibility- History/decisions are always in
compiled— never insdk_sources(they're markdown regardless of mode)
squad import with SDK Mode
squad import squad-export.json [--mode sdk|markdown|auto]--mode auto(default): Imports in the mode specified by the export file'smodefield--mode sdk: Forces SDK mode import even from a1.0export (generates TypeScript from markdown)--mode markdown: Forces markdown import even from a2.0export (usescompiledblock only)
Round-trip fidelity: squad export | squad import --mode sdk | squad build must produce identical .squad/ output (modulo timestamps).
Backward Compatibility
2.0exports always includecompiledblock → importable by older1.0tooling1.0exports are importable into SDK mode via--mode sdk(generates TypeScript from parsed markdown)squad-export.jsonremains the single portable format
Casting in SDK Mode
Casting (agent personality/universe assignments) moves from JSON config to typed definitions:
import { defineCasting } from '@bradygaster/squad-sdk';
export const casting = defineCasting({
universe: 'The Usual Suspects',
allowlist: ['The Usual Suspects', 'Breaking Bad', 'The Wire', 'Firefly'],
overflowStrategy: 'generic', // when universe exhausted
assignments: {
keaton: { character: 'Keaton', persistent: true },
fenster: { character: 'Fenster', persistent: true },
verbal: { character: 'Verbal', persistent: true },
// ... auto-assigned agents omitted, handled by CastingEngine
},
policy: {
legacyNamed: false, // no pre-existing name overrides
recastOnConflict: 'skip', // 'skip' | 'reassign' | 'error'
},
});What stays in JSON: casting-history.json (append-only log of casting decisions) stays as a JSON file managed by CastingEngine — same rationale as decisions.md staying markdown. It's operational history, not configuration.
Build output: squad build generates casting-registry.json and casting-policy.json from the typed defineCasting() output, maintaining compatibility with the current CastingEngine runtime.
Ceremonies in SDK Mode
Ceremonies (team rituals like standups, retros, reviews) get typed definitions:
import { defineCeremony } from '@bradygaster/squad-sdk';
export const standup = defineCeremony({
name: 'standup',
description: 'Daily sync — what each agent accomplished and plans next',
trigger: 'manual', // 'manual' | 'schedule' | 'event'
participants: [keaton, fenster, verbal, edie, hockney],
moderator: keaton,
format: {
each: (agent) => [
`What ${agent.name} accomplished since last standup`,
`What ${agent.name} plans to work on next`,
`Any blockers`,
],
output: 'summary', // 'summary' | 'full-transcript' | 'decisions-only'
},
hooks: {
afterCeremony: async (result) => {
// Auto-append decisions to decisions.md
if (result.decisions.length > 0) {
await appendToDecisions(result.decisions);
}
},
},
});
export const codeReview = defineCeremony({
name: 'code-review',
description: 'Structured review of proposed changes',
trigger: { event: 'pull_request.opened' },
participants: (pr) => {
// Dynamic participant selection based on changed files
const owners = resolveCodeOwners(pr.changedFiles);
return [keaton, ...owners]; // Lead always participates
},
format: {
phases: ['architecture', 'correctness', 'style', 'testing'],
output: 'review-comments',
},
});Build output: squad build generates .squad/ceremonies.md with human-readable ceremony descriptions. The programmatic hooks and dynamic participant selection are noted as "SDK-only features" in the generated markdown.
Telemetry Activation
This is the killer feature. SDK mode finally activates the OTel infrastructure.
Current State (Dead Code)
The SDK already has:
| Module | What it does | Status today |
|---|---|---|
otel-init.ts |
initSquadTelemetry() — bootstraps OTel SDK |
|
otel-metrics.ts |
recordAgentSpawn(), recordAgentDuration(), etc. |
|
otel-bridge.ts |
OTLP exporter to collector | |
event-bus.ts |
EventBus with subscribe() API |
|
squad-observer.ts |
classifyFile(), file change detection |
|
cost-tracker.ts |
Token/cost per agent per session |
SDK Mode Activation
// squad/telemetry.ts
import { defineTelemetry } from '@bradygaster/squad-sdk';
export const telemetry = defineTelemetry({
enabled: true,
provider: 'otlp', // 'otlp' | 'aspire' | 'console' | 'custom'
endpoint: 'http://localhost:4317', // OTLP gRPC endpoint
serviceName: 'squad-sdk',
metrics: {
agentSpawn: true,
agentDuration: true,
agentTokens: true,
routingDecisions: true,
toolCalls: true,
sessionLifecycle: true,
},
traces: {
sampleRate: 1.0, // 100% in dev, lower in prod
propagation: 'w3c', // W3C TraceContext
},
costTracking: {
enabled: true,
budgetPerSession: null, // null = unlimited
alertThreshold: 10.00, // USD — warn at $10/session
},
});What Lights Up
When squad run --telemetry executes:
initSquadTelemetry()bootstraps the OTel SDK with the config fromdefineTelemetry()EventBusstarts firing on every lifecycle event:session:created/session:destroyed— session lifecycle spanssession:message— user message received (with token count)coordinator:routing— routing decision made (which agent, why, confidence)session:tool_call— tool invocation (which tool, duration, success/failure)agent:milestone— agent completed a significant step
otel-bridgeexports spans to the configured OTLP endpointotel-metricsrecords counters and histograms:squad.agent.spawn_count(counter, by agent name)squad.agent.duration_ms(histogram, by agent name)squad.agent.tokens(counter, by agent name, input/output)squad.routing.decision_count(counter, by route target)squad.session.cost_usd(gauge, by session)
cost-trackeraggregates token usage and cost per agent per sessionsquad-observerwatches for file changes and classifies them
Aspire Integration
For the Aspire dashboard (per Brady's directive):
squad run --aspire
# Equivalent to:
# SQUAD_TELEMETRY=1
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# OTEL_SERVICE_NAME=squad-sdkThis enables the Aspire dashboard to show:
- Distributed traces across agent interactions
- Token usage dashboards per agent
- Routing decision flow visualization
- Cost tracking over time
Migration Path
Markdown → SDK Upgrade
# Step 1: Preview what migration will do
squad migrate --to-sdk --dry-run
# Step 2: Execute migration
squad migrate --to-sdk
# Step 3: Validate round-trip
squad build --check
# Step 4: Run with telemetry
squad run --telemetryWhat squad migrate --to-sdk Does
- Parses
.squad/team.md→ extracts team metadata, agent list - Parses
.squad/routing.md→ extracts routing rules table, module ownership - Parses
.squad/agents/*/charter.md→ extracts agent definitions - Parses
.squad/ceremonies.md→ extracts ceremony definitions - Generates
squad/team.ts,squad/agents/*.ts,squad/routing.ts,squad/ceremonies.ts,squad/hooks.ts(empty scaffold),squad/index.ts - Validates by running
squad build --checkto ensure generated markdown matches original - Updates
.gitignoreto ignore generated.squad/files (except decisions/history) - Creates
squad/tsconfig.json
Backward Compatibility Guarantees
- Markdown mode is never removed.
squad init(no--sdk) continues to work exactly as today. - SDK mode always generates
.squad/. Any tool that reads.squad/(Copilot CLI, VS Code, GitHub Actions) continues to work. squad buildis idempotent. Running it twice produces identical output.- No breaking changes to
squad-export.jsonv1.0. Thecompiledblock in v2.0 exports is identical to v1.0 format. squad.config.tscontinues to work. Existing typed config at project root is respected; SDK mode extends it, doesn't replace it.
What Stays Markdown
| File | Why it stays markdown |
|---|---|
.squad/decisions.md |
Append-only semantics. merge=union git driver. Human-readable history. Multiple agents append concurrently. Moving to TS would lose merge-driver compatibility and add compile step to append operations. |
.squad/agents/*/history.md |
Same rationale as decisions. Append-only, merge-driver-compatible, human-readable learning log. |
.squad/orchestration-log/ |
Diagnostic output. Append-only. Not configuration. No benefit from typing. |
.squad/log/ |
Session logs. Same as orchestration-log. |
.squad/decisions/inbox/*.md |
Decision proposals. Written by agents asynchronously. Scribe merges into decisions.md. Markdown is the right format for prose proposals. |
The principle: Configuration is TypeScript. History is markdown. If it's append-only and benefits from merge=union, it stays markdown.
Acceptance Criteria
Phase 1: squad build Bridge
-
squad buildreadssquad/index.tsand generates.squad/team.md,.squad/routing.md,.squad/agents/*/charter.md,.squad/ceremonies.md - Generated markdown files include
<!-- Generated by squad build. Do not edit. -->header -
squad buildNEVER touchesdecisions.md,history.md, ororchestration-log/ -
squad build --checkexits 0 if generated files match disk, 1 if drift detected -
squad build --watchrebuilds on.tsfile changes insquad/ -
squad build --dry-runprints diff without writing - Round-trip test:
squad migrate --to-sdk && squad buildproduces markdown equivalent to original - All existing tests (2931+) continue to pass
-
defineTeam(),defineAgent(),defineRouting(),defineCeremony()builders export from@bradygaster/squad-sdk - Zod runtime validation catches invalid configs with actionable error messages
Phase 2: squad init --sdk
-
squad init --sdkscaffoldssquad/directory with typed definitions -
squad init --sdk --template minimalcreates minimal 1-agent squad -
squad init --sdk --template fullcreates full squad with all current agents -
squad init --sdk --telemetryaddssquad/telemetry.tswith OTel defaults - Scaffolded project compiles with
tsc --noEmit(zero type errors) -
squad init --sdkrunssquad buildautomatically (unless--no-build) -
.gitignoreupdated correctly (generated.squad/files ignored, decisions/history preserved) -
squad doctordetects SDK mode and validatessquad/directory structure
Phase 3: squad run with SDK Runtime
-
squad runlaunches coordinator with SDK owning the runtime loop -
squad run --telemetrycallsinitSquadTelemetry()and activates EventBus - OTel spans exported for:
session:created,coordinator:routing,session:tool_call,agent:milestone - Metrics recorded:
squad.agent.spawn_count,squad.agent.duration_ms,squad.agent.tokens -
squad run --aspireconfigures Aspire-compatible OTLP endpoint - Cost tracker aggregates token usage per agent per session
- Routing uses typed routing functions (not markdown table parsing)
- Hook pipeline enforces policy config from
defineHooks() - Ctrl+C gracefully shuts down, flushes pending telemetry
Phase 4: squad migrate --to-sdk
-
squad migrate --to-sdkconverts existing.squad/tosquad/*.tsfiles -
squad migrate --to-sdk --dry-runshows plan without executing -
squad migrate --to-sdk --preserve-markdownkeeps original files as backup - Migration validates round-trip with
squad build --check - All agent charters, routing rules, and ceremonies survive migration
-
decisions.mdandhistory.mduntouched by migration
Import/Export
-
squad exportin SDK mode producesversion: "2.0"withsdk_sourcesandcompiledblocks -
squad importof2.0export restores bothsquad/and.squad/directories -
squad import --mode markdownof2.0export restores only.squad/(usescompiledblock) -
squad import --mode sdkof1.0export generates TypeScript from markdown - Backward compatibility:
2.0export'scompiledblock is importable by1.0tooling
Phases
Phase 1: squad build Bridge (Foundation)
Dependencies: None
Estimated scope: Medium
Delivers: Builder functions, build pipeline, generated markdown, --check/--watch/--dry-run
Why first: Everything else depends on the TS → markdown bridge working correctly. This is the architectural foundation.
Phase 2: squad init --sdk (Scaffolding)
Dependencies: Phase 1
Estimated scope: Medium
Delivers: SDK mode project scaffolding, templates, doctor integration
Why second: Users need a way to create SDK-mode projects before they can use squad run.
Phase 3: squad run with SDK Runtime (Telemetry Goes Live)
Dependencies: Phase 1, Phase 2
Estimated scope: Large
Delivers: SDK-owned runtime loop, telemetry activation, programmatic routing, Aspire integration
Why third: This is the highest-value phase but depends on the build pipeline and scaffolding being solid.
Phase 4: squad migrate --to-sdk (Conversion)
Dependencies: Phase 1, Phase 2
Estimated scope: Medium
Delivers: Markdown → SDK migration, round-trip validation, backward compatibility
Why last: Migration is a convenience feature. Users can manually create SDK squads via init --sdk until this ships.
Risks & Mitigations
| Risk | Impact | Likelihood | Mitigation |
|---|---|---|---|
Build step friction — SDK mode requires squad build before Copilot CLI can use the squad |
High | High | squad build --watch for dev. CI integration docs. squad run auto-builds. |
| Markdown generation drift — generated markdown diverges from hand-written quality | Medium | Medium | Golden file tests. Round-trip validation in CI. Human-review of generated output. |
| TypeScript barrier — non-developer users can't use SDK mode | Medium | Low | Markdown mode stays default. SDK mode is explicitly opt-in. Generated markdown is human-readable. |
| OTel overhead — telemetry adds latency to agent interactions | Medium | Low | Telemetry is opt-in (--telemetry flag). Sample rate configurable. Console provider for zero-overhead dev mode. |
| Export format fragmentation — two schema versions to maintain | Low | Medium | compiled block ensures backward compat. Version field enables graceful degradation. |
Copilot CLI compatibility — Copilot CLI may not read generated .squad/ correctly |
High | Low | Generated markdown matches hand-written format exactly. Integration tests verify Copilot CLI reads generated files. |
| Config complexity — TypeScript definitions become too complex for casual users | Medium | Medium | --template minimal for simple squads. Progressive complexity: start with defineTeam() only, add routing/ceremonies/hooks incrementally. |
Open Questions
-
Should
squad buildbe automatic? Whensquad runorsquad startis called in SDK mode, should it implicitly runsquad buildfirst? (Leaning yes — reduce friction.) -
How do we handle
squad.config.tsvssquad/index.ts? Currentlysquad.config.tsexists at project root. SDK mode introducessquad/index.ts. Do they merge? Does one supersede the other? (Leaning:squad/index.tsimports fromsquad.config.tsfor backward compat.) -
Should routing functions have access to conversation history? The
RoutingContexttype could include recent messages for context-aware routing. Privacy implications need consideration. -
What's the minimum viable
squad build? Could Phase 1 ship with justdefineTeam()+defineAgent(), deferring routing/ceremonies to Phase 2? (Leaning: include routing — it's the most valuable typed surface.) -
Plugin/marketplace interaction: How do SDK-mode squads interact with the plugin marketplace? Can plugins be imported as TypeScript modules? (Defer to Phase 3+.)
-
Multi-squad composition: TypeScript imports enable
import { securityTeam } from '@company/security-squad'. How doessquad buildhandle external squad imports? (Defer — valuable but complex.) -
Hot reload in
squad run: Shouldsquad run --watch(orsquad build --watch+ auto-restart) support hot reload of routing functions without restarting the session? (Defer — nice-to-have.)
Activity
[-]PRD: SDK-First Squad Mode ΓÇö TypeScript-based team definitions with telemetry, type safety, and programmatic routing[/-][+]PRD: SDK-First Squad Mode - TypeScript-based team definitions with telemetry, type safety, and programmatic routing[/+][-]PRD: SDK-First Squad Mode - TypeScript-based team definitions with telemetry, type safety, and programmatic routing[/-][+]PRD: SDK-First Squad Mode — TypeScript-based team definitions with telemetry, type safety, and programmatic routing[/+]chore: SDK M0 audit findings for #190, #193, #194
fix: swap @github/copilot-sdk from file: to npm ^0.1.25
bradygaster commentedon Mar 5, 2026
Ideally, a user who starts with markdown charters and histories could keep using those and the TS layer would sit atop it, read from the markdown, and use the markdown as the source. In fact, i like this as a principle - ideally the things the agents learn are always in - or primarily in - markdown and read from disk when the agent runs or is spun up. if there's a way the agents could stay in memory and we avoid ANY startup time on them, too? that would be utterly amazing. i think you're alluding to this with the bridge concept but just wanted to make sure first. a user should never not be able to change their agent by changing their markdown. this should be a thing.
the user should never have to build. that HAS to be transparent each time the agents need to build. i don't think they need to build each time they learn; ideally my "markdown as the source of truth" rule above grounds this.
you have
--preserve-markdownas "keep original squad files as backup. this would need to change based on my principle above. the markdown need to still be the source of truth. think of the agent typescript "stuff" as a facade so we can use the underlying markdown with all the features. i'd prefer not move the content into the ts. and to that end, it almost feels like you could have a single ts "agent" file. like it would be like a programmatic interface exposing instances of that interface, for each agent, but read from the markdown.will feed this back to squad for thought and updates on the prd.
also i adore with dance moves the idea about knowing how much each agent cost in terms of tokens per set of work in the UI. that's amazing and much-much desired.
feat: add SDK mode detection to coordinator prompt
feat(builders): add SDK-First Squad Mode builder functions (#194 Phas…
feat: implement squad build CLI command (#194)
test: add Phase 1 SDK-First tests + agent learnings (#194)
bradygaster commentedon Mar 7, 2026
✅ CLOSED: SDK-First PRD has been implemented. This work is complete.
docs(ai-team): Pre-release triage complete — v0.8.21 ready pending #248…