` form. ### `rax version` [Section titled “rax version”](#rax-version) ```bash rax version rax --version rax -v ``` ### `rax help` [Section titled “rax help”](#rax-help) ```bash rax help rax --help rax -h ```
# Compose API
> Reference for .compose(), harness transforms, phase hooks, and pattern matching
The Compose API lets you intercept and reshape any signal the agent kernel emits — from system prompts to tool results to nudges — using a declarative composition model. ## Quick start [Section titled “Quick start”](#quick-start) ```ts import { ReactiveAgents } from 'reactive-agents'; import { maxIterations, budgetLimit } from 'reactive-agents/compose/killswitches'; const agent = await ReactiveAgents.create() .withProvider('anthropic') .compose(budgetLimit({ maxTokens: 50_000 })) .compose(maxIterations(20)) .compose((harness) => { harness.tap('observation.tool-result', (result, ctx) => { console.log(`[iter ${ctx.iteration}] tool result:`, result.content); }); }) .build(); ``` ## `.compose(fn)` [Section titled “.compose(fn)”](#composefn) **Signature:** `compose(fn: (harness: Harness) => void): this` Registers a composition block. Multiple `.compose()` calls accumulate in registration order. `fn` receives a `Harness` instance with methods to register transforms, taps, and phase hooks. All registrations are compiled once at `.build()` time. `.compose()` is the canonical entry point. `.withHarness()` is an identical alias. ## `harness.on(pattern, fn)` — Transform [Section titled “harness.on(pattern, fn) — Transform”](#harnessonpattern-fn--transform) Intercept and replace an emission’s payload. **Signature:** ```ts harness.on( pattern: TagPattern | TagPattern[], fn: (payload: PayloadFor, ctx: ContextFor
) => | PayloadFor
// replace payload | undefined // keep current payload | null // suppress emission | Promise<...> ): Harness ``` **Pattern types:** | Pattern | Matches | | ------------------ | -------------------------------------------------- | | `'prompt.system'` | Exact tag | | `'prompt.*'` | All single-segment `prompt.X` tags | | `'nudge.**'` | All `nudge.X` and `nudge.X.Y` tags (multi-segment) | | `'**'` | Every tag | | `(tag) => boolean` | Custom predicate | **Transform semantics:** * Return a value → **replaces** current payload * Return `undefined` → **keeps** current payload (pass-through) * Return `null` → **suppresses** the emission (removed from pipeline) * Multiple transforms on same tag chain in order: broadest pattern first, most-specific last **Example — suppress all nudges in a bare-LLM ablation:** ```ts harness.on('nudge.*', () => null) ``` **Example — localize system prompt:** ```ts harness.on('prompt.system', (text, ctx) => `[locale: fr]\n${text}`) ``` ## `harness.tap(pattern, fn)` — Side Effect [Section titled “harness.tap(pattern, fn) — Side Effect”](#harnesstappattern-fn--side-effect) Observe an emission without changing it. Runs after all transforms. **Signature:** ```ts harness.tap( pattern: TagPattern | TagPattern[], fn: (payload: PayloadFor
, ctx: ContextFor
) => void | Promise ): Harness ``` Taps run in registration order, after transforms are finalized. A tap that throws is a bug — they run unconditionally with the final value. **Example — telemetry:** ```ts harness.tap('**', (payload, ctx) => { otel.record(ctx.phase, ctx.iteration, payload); }); ``` ## `harness.before(phase, fn)` — Phase Pre-Hook [Section titled “harness.before(phase, fn) — Phase Pre-Hook”](#harnessbeforephase-fn--phase-pre-hook) Run before a kernel phase. Can abort or skip the iteration. **Signature:** ```ts harness.before( phase: Phase, fn: (ctx: { phase: Phase; iteration: number; state: KernelStateLike }) => | void | Promise | { readonly abort: 'stop' | 'terminate'; readonly reason?: string } | { readonly skip: true } ): Harness ``` **Return values:** | Return | Effect | | ------------------------ | ------------------------------------ | | `void` / `undefined` | Continue normally | | `{ abort: 'stop' }` | End loop gracefully (status: done) | | `{ abort: 'terminate' }` | End loop as failure (status: failed) | | `{ skip: true }` | Skip this iteration, continue loop | **Example — custom iteration limit:** ```ts harness.before('think', (ctx) => { if (ctx.iteration >= 15) return { abort: 'stop', reason: 'custom-limit' }; }); ``` ## `harness.after(phase, fn)` — Phase Post-Hook [Section titled “harness.after(phase, fn) — Phase Post-Hook”](#harnessafterphase-fn--phase-post-hook) Run after a kernel phase completes. Same signature as `.before()` but fires after. ## `harness.onError(phase, fn)` — Error Hook [Section titled “harness.onError(phase, fn) — Error Hook”](#harnessonerrorphase-fn--error-hook) Run when a phase throws. Can optionally recover by returning a replacement state. **Signature:** ```ts harness.onError( phase: Phase | '*', fn: (error: unknown, ctx: { phase: Phase | '*'; iteration: number }) => | void | Promise | { readonly recover: KernelStateLike } ): Harness ``` Use `'*'` to catch errors from any phase. Return `{ recover: newState }` to inject a replacement state and continue the loop. ## `harness.emit(tag, payload)` — Inject at Build Time [Section titled “harness.emit(tag, payload) — Inject at Build Time”](#harnessemittag-payload--inject-at-build-time) Inject a payload directly at build time. Use for initial seeding. ## `harness.use(fn)` — Sub-composition [Section titled “harness.use(fn) — Sub-composition”](#harnessusefn--sub-composition) Nest a composition block. Useful for reusable plugin patterns. ```ts harness.use((h) => { h.tap('observation.tool-result', logFn); h.before('act', approvalFn); }); ``` ## Available Phases [Section titled “Available Phases”](#available-phases) ```plaintext bootstrap → guardrail → cost-route → strategy-select → think → act → observe → verify → memory-flush → cost-track → audit → complete ``` Phase hooks fire in this order per iteration. `bootstrap` and `complete` fire once per run. ## Context Fields [Section titled “Context Fields”](#context-fields) All hook/transform callbacks receive a `ctx` with at minimum: ```ts { iteration: number; // 0-indexed phase: Phase; // current phase name state: KernelStateLike; // current kernel state snapshot strategy: string; // active reasoning strategy ('reactive', 'tot', etc.) } ``` Some tags carry richer contexts — see [Harness Tag Reference](/reference/harness-tags). ## Killswitches [Section titled “Killswitches”](#killswitches) Prebuilt compositions from `reactive-agents/compose/killswitches`: ```ts import { budgetLimit, timeoutAfter, maxIterations, requireApprovalFor, watchdog } from 'reactive-agents/compose/killswitches'; ``` See [Composition Recipes](/cookbook/composition-recipes) for usage examples.
# Configuration Reference
> Complete reference of all builder methods, defaults, and environment variables
# Configuration Reference [Section titled “Configuration Reference”](#configuration-reference) Every aspect of Reactive Agents is configurable through the builder API. This page documents all available options, their defaults, and how they affect agent behavior. For ready-made chains, see [Common builder stacks](/cookbook/builder-stacks/). ## Builder Methods [Section titled “Builder Methods”](#builder-methods) ### Core [Section titled “Core”](#core) | Method | Default | Description | | --------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------ | | `.withName(name)` | `"agent"` | Agent identifier used in logs and metrics | | `.withProvider(provider)` | `"test"` | LLM provider: `"anthropic"` \| `"openai"` \| `"gemini"` \| `"ollama"` \| `"litellm"` \| `"test"` | | `.withModel(model)` | Provider default | Model string or `ModelParams` (`model`, `thinking?`, `temperature?`, `maxTokens?`) | | `.withSystemPrompt(prompt)` | none | Custom system prompt prepended to all LLM calls | | `.withPersona(persona)` | none | Structured persona: `{ name?, role?, background?, instructions?, tone? }` | | `.withEnvironment(context)` | none | Extra `Record` merged into system prompt (beyond built-in date/tz/platform) | | `.withMaxIterations(n)` | `10` | Maximum reasoning loop iterations before stopping | | `.withTimeout(ms)` | none | Per-execution timeout in milliseconds | | `.withStrictValidation()` | off | Missing API keys / mismatches become build errors | | `.withRetryPolicy({ maxRetries, backoffMs })` | `maxRetries: 0` | Transient LLM retries | | `.withErrorHandler(fn)` | none | Observe-only callback when `run()` fails | ### Reasoning [Section titled “Reasoning”](#reasoning) | Method | Default | Description | | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `.withReasoning(options?)` | disabled | Strategies, ICS (`synthesis`, `synthesisModel`, …), strategy switching, `adaptive`, per-strategy bundles (may include e.g. `kernelMaxIterations` on `reflexion`). See [Reasoning](/guides/reasoning/) and [Builder API](/reference/builder-api/) | ### Tools & context [Section titled “Tools & context”](#tools--context) | Method | Default | Description | | ---------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | `.withTools(options?)` | disabled | `{ tools?` (custom defs + **Effect** handlers), `resultCompression?`, `allowedTools?`, `adaptive?` } | | `.withDocuments(docs)` | none | `DocumentSpec[]` ingested at build for `rag-search` | | `.withRequiredTools(config)` | none | `{ tools?, adaptive?, maxRetries? }` | | `.withMCP(config)` | none | MCP: `{ name, transport, command?, args?, endpoint?, headers?, env?, cwd? }` (see [Builder API](/reference/builder-api/) transport table) | | `.withMetaTools(config?)` | on with tools | Conductor suite; pass `false` to disable defaults | ### LLM resilience & pricing [Section titled “LLM resilience & pricing”](#llm-resilience--pricing) | Method | Default | Description | | ------------------------------- | ------------- | -------------------------------------------------------------- | | `.withCircuitBreaker(config?)` | off until set | Provider circuit breaker (`failureThreshold`, `cooldownMs`, …) | | `.withRateLimiting(config?)` | off until set | RPM / TPM / concurrency limits | | `.withModelPricing(registry)` | none | Static $/1M token overrides | | `.withDynamicPricing(provider)` | none | Fetch pricing at build | | `.withFallbacks(config)` | none | Provider/model chain + `errorThreshold` | | `.withCacheTimeout(ms)` | `3_600_000` | Semantic cache TTL (1h) | ### Memory [Section titled “Memory”](#memory) | Method | Default | Description | | ----------------------------------- | -------- | ------------------------------------------------------------------------------------- | | `.withMemory(options?)` | disabled | Enable memory. No args = standard tier. Options: `{ tier: "standard" \| "enhanced" }` | | `.withMemoryConsolidation(config?)` | disabled | Background memory intelligence: `{ threshold?, decayFactor?, pruneThreshold? }` | | `.withExperienceLearning()` | disabled | Cross-agent tool-use pattern learning | ### Safety & control [Section titled “Safety & control”](#safety--control) | Method | Default | Description | | ------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------- | | `.withGuardrails(options?)` | disabled | Toggles: `{ injection?, pii?, toxicity? }` (default **true** each when enabled), plus `customBlocklist?` | | `.withVerification(options?)` | disabled | Strategy toggles + thresholds (`passThreshold`, `hallucinationDetection`, …) | | `.withKillSwitch()` | disabled | Pause / resume / stop / terminate | | `.withBehavioralContracts(contract)` | none | Behavioral contract passed to guardrails layer | ### Cost & context [Section titled “Cost & context”](#cost--context) | Method | Default | Description | | ------------------------------ | ------------- | ----------------------------------------------------------------------------------------------------- | | `.withCostTracking(options?)` | disabled | Budget enforcement (USD): `{ perRequest?, perSession?, daily?, monthly? }` | | `.withContextProfile(profile)` | auto-detected | Model-adaptive context budgets / compaction — see [Context engineering](/guides/context-engineering/) | ### Observability & streaming [Section titled “Observability & streaming”](#observability--streaming) | Method | Default | Description | | ------------------------------ | --------------------------------- | ---------------------------------------------------------------------------- | | `.withObservability(options?)` | disabled | `{ verbosity?, live?, file?` (JSONL), `logPrefix?, logModelIO? }` | | `.withStreaming(options?)` | `"tokens"` | Default `agent.runStream()` density: `{ density?: "tokens" \| "full" }` | | `.withTelemetry(config?)` | `{ mode: "isolated" }` if enabled | Telemetry privacy / contribute modes | | `.withLogging(config)` | none | Structured logs: level, format, `output` (console / file / stream), rotation | | `.withAudit()` | disabled | Compliance audit logging | | `.withEvents()` | — | Wire EventBus for `agent.subscribe()` | ### Identity & Orchestration [Section titled “Identity & Orchestration”](#identity--orchestration) | Method | Default | Description | | ------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- | | `.withIdentity()` | disabled | Ed25519 agent certificates + RBAC | | `.withOrchestration()` | disabled | Multi-agent workflow engine | | `.withInteraction()` | disabled | 5 autonomy modes + checkpoints | | `.withSelfImprovement()` | disabled | Cross-task strategy outcome learning | | `.withReactiveIntelligence(false)` | on | Pass `false` to disable entropy/controller/telemetry stack | | `.withReactiveIntelligence(options?)` | defaults | Entropy, controller, hooks, `autonomy`, `constraints` — see [Reactive Intelligence](/features/reactive-intelligence/) | | `.withHealthCheck()` | disabled | Exposes `agent.health()` | ### Sub-agents & A2A [Section titled “Sub-agents & A2A”](#sub-agents--a2a) | Method | Default | Description | | --------------------------------- | ---------------- | ---------------------------------------------- | | `.withA2A(options?)` | `{ port: 3000 }` | Local A2A JSON-RPC server (`port`, `basePath`) | | `.withAgentTool(name, config)` | none | Register a static sub-agent as a tool | | `.withDynamicSubAgents(options?)` | disabled | Allow LLM to spawn sub-agents at runtime | | `.withRemoteAgent(name, url)` | none | Connect to a remote agent via A2A protocol | ### Gateway [Section titled “Gateway”](#gateway) | Method | Default | Description | | ------------------------ | -------- | --------------------------------------------------------------------------------------------- | | `.withGateway(options?)` | disabled | Persistent autonomous harness: `{ heartbeat?, crons?, webhooks?, policies?, accessControl? }` | ### Build, test & serialization [Section titled “Build, test & serialization”](#build-test--serialization) | Method | Default | Description | | ------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------- | | `.withTestScenario(turns)` | none | Deterministic **test** provider. `TestTurn[]` from `@reactive-agents/llm-provider`; forces `provider: "test"`. | | `.withLayers(layers)` | none | Merge custom Effect `Layer`s into the runtime | | `.withSkills(config?)` | disabled | Living skills: `paths`, `packages`, `evolution`, `overrides` | | `.toConfig()` / `ReactiveAgents.fromConfig()` / `fromJSON()` | — | **Agent as Data** — round-trip via `agentConfigToJSON` / `agentConfigFromJSON` (`reactive-agents` or `@reactive-agents/runtime`) | | `agentFn` / `pipe` / `parallel` / `race` | — | Promise-based multi-agent composition (see [Builder API](/reference/builder-api/)) | | `agent.registerTool()` / `unregisterTool()` / `ingest()` | — | Runtime tool + RAG ingestion on built agents | ## Environment Variables [Section titled “Environment Variables”](#environment-variables) | Variable | Required For | Default | Description | | ---------------------- | --------------------------- | -------------------------- | --------------------------------------------------------------------- | | `ANTHROPIC_API_KEY` | Anthropic provider | — | Anthropic API key | | `OPENAI_API_KEY` | OpenAI/LiteLLM provider | — | OpenAI API key | | `GOOGLE_API_KEY` | Gemini provider | — | Google AI API key | | `TAVILY_API_KEY` | Web search tool (primary) | — | Tavily search API key | | `BRAVE_SEARCH_API_KEY` | Web search tool (secondary) | — | Brave Search API key (`X-Subscription-Token`); alias: `BRAVE_API_KEY` | | `EMBEDDING_PROVIDER` | Enhanced memory tier | `"openai"` | Embedding provider | | `EMBEDDING_MODEL` | Enhanced memory tier | `"text-embedding-3-small"` | Embedding model name | | `LLM_DEFAULT_MODEL` | All providers | Provider default | Override default model | ## Hardcoded Defaults [Section titled “Hardcoded Defaults”](#hardcoded-defaults) These values have sensible defaults but are not currently configurable via the builder: | Value | Default | Where | Notes | | ------------------------- | ------------ | ------------------------- | -------------------------------------------- | | Max sub-agent iterations | 4 | `packages/tools/src/` | Sub-agents capped at 4 iterations | | Max recursion depth | 3 | `packages/tools/src/` | Nested sub-agent limit | | Parent context forwarding | 2000 chars | `packages/tools/src/` | Max parent context sent to sub-agents | | Memory decay half-life | 7 days | `packages/memory/src/` | Episodic memory decay rate | | Compaction trigger | 6 iterations | `packages/reasoning/src/` | Steps before context compaction (local tier) |
# Harness Tag Reference
> Complete catalog of harness emission tags, payloads, and contexts (Wave A–D)
Harness tags are the interception points that `.compose()` blocks can observe and reshape. Each tag has a typed payload and a typed context. > **Note:** This catalog covers the Wave A–D tag set (7 tags). The full v0.12 catalog will expand to 24+ tags via build-time codegen. ## Tag Catalog [Section titled “Tag Catalog”](#tag-catalog) ### `prompt.system` [Section titled “prompt.system”](#promptsystem) Emitted when the kernel assembles the system prompt for an LLM call. **Payload:** `string` — the full system prompt text\ **Context:** `BaseCtx`\ **Phase:** `think` ```ts harness.on('prompt.system', (text, ctx) => { return `[tenant: ${ctx.strategy}]\n${text}`; }); ``` *** ### `nudge.loop-detected` [Section titled “nudge.loop-detected”](#nudgeloop-detected) Emitted when the loop detector identifies a repetitive pattern. **Payload:** `string` — the nudge message injected into context\ **Context:** `NudgeCtx` — includes `trigger: string`, `severity: 'info' | 'warn' | 'critical'`\ **Phase:** `think` ```ts harness.on('nudge.loop-detected', (msg, ctx) => { console.warn(`Loop at iter ${ctx.iteration} [${ctx.severity}]: ${ctx.trigger}`); return msg; // pass through unchanged }); ``` *** ### `nudge.healing-failure` [Section titled “nudge.healing-failure”](#nudgehealing-failure) Emitted when tool call healing fails after all recovery stages. **Payload:** `string` — the healing failure nudge message\ **Context:** `NudgeCtx` — includes `trigger: string`, `severity`\ **Phase:** `act` *** ### `message.tool-result` [Section titled “message.tool-result”](#messagetool-result) Emitted when a tool result is added to the conversation thread (what the LLM sees). **Payload:** `KernelMessageLike` — the message object: ```ts type KernelMessageLike = | { role: 'assistant'; content: string; toolCalls?: unknown[] } | { role: 'tool_result'; toolCallId: string; toolName: string; content: string; isError?: boolean } | { role: 'user'; content: string } ``` **Context:** `ToolResultCtx` — includes `toolName`, `callId`, `healed: boolean`, `durationMs`\ **Phase:** `act` ```ts // Redact PII from tool results before LLM sees them harness.on('message.tool-result', (msg) => { if (msg.role === 'tool_result') { return { ...msg, content: redact(msg.content) }; } return msg; }); ``` *** ### `observation.tool-result` [Section titled “observation.tool-result”](#observationtool-result) Emitted when a tool result is recorded as an observation step (what systems observe). **Payload:** `ObservationStepLike`: ```ts type ObservationStepLike = { type: string; content?: string; metadata?: Record; } ``` **Context:** `ToolResultCtx`\ **Phase:** `act` ```ts harness.tap('observation.tool-result', (obs, ctx) => { metrics.record('tool.duration', ctx.durationMs, { tool: ctx.toolName }); }); ``` *** ### `lifecycle.failure` [Section titled “lifecycle.failure”](#lifecyclefailure) Emitted when the agent enters a failure state. **Payload:** `LifecycleFailurePayload`: ```ts type LifecycleFailurePayload = { reason: 'tool-error' | 'llm-refusal' | 'verifier-rejection'; errorMessage: string; attemptNumber: number; failureStreak: number; currentStrategy: string; } ``` **Context:** `BaseCtx` ```ts harness.tap('lifecycle.failure', (failure) => { alerting.trigger({ reason: failure.reason, streak: failure.failureStreak }); }); ``` *** ### `control.strategy-evaluated` [Section titled “control.strategy-evaluated”](#controlstrategy-evaluated) Emitted when the strategy evaluator scores the current strategy. **Payload:** `ControlStrategyEvaluatedPayload`: ```ts type ControlStrategyEvaluatedPayload = { currentStrategy: string; score: number; failureStreak: number; recommendedAction: 'continue' | 'switch' | 'escalate'; availableStrategies: string[]; } ``` **Context:** `BaseCtx` ```ts harness.tap('control.strategy-evaluated', (eval) => { if (eval.recommendedAction === 'escalate') { notify.ops(`Strategy escalation: ${eval.currentStrategy} (score: ${eval.score})`); } }); ``` *** ## Context Types [Section titled “Context Types”](#context-types) ### `BaseCtx` [Section titled “BaseCtx”](#basectx) ```ts { iteration: number; phase: Phase; state: Readonly; strategy: string; } ``` ### `NudgeCtx` (extends BaseCtx) [Section titled “NudgeCtx (extends BaseCtx)”](#nudgectx-extends-basectx) ```ts { trigger: string; // what triggered the nudge severity: 'info' | 'warn' | 'critical'; } ``` ### `ToolResultCtx` (extends BaseCtx) [Section titled “ToolResultCtx (extends BaseCtx)”](#toolresultctx-extends-basectx) ```ts { toolName: string; callId: string; healed: boolean; // true if tool call was auto-healed durationMs: number; // wall-clock tool execution time } ```
# API Stability & Versioning
> SemVer commitments, stability tiers, what's stable vs experimental in v0.10, and the deprecation policy.
This page is the honest answer to “is this safe to depend on?” Reactive Agents follows **Semantic Versioning** (`major.minor.patch`). The framework is currently in `0.x`, which under SemVer means **minor bumps may include breaking changes** to anything not marked stable below. We document each break in the [CHANGELOG](https://github.com/tylerjrbuell/reactive-agents-ts/blob/main/CHANGELOG.md) and ship a migration note for anything user-facing. ## Stability tiers [Section titled “Stability tiers”](#stability-tiers) Every public surface falls into one of three tiers. Tier is declared by JSDoc tag on the export — `@stable`, `@unstable`, `@experimental` — and summarized below. | Tier | Promise | Breaks allowed | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- | | **Stable** (`@stable`) | Source-compatible across `0.x` minor bumps. Behavior changes get a deprecation warning + one minor cycle before removal. | Patch versions only fix bugs. | | **Unstable** (`@unstable`) | API may change between minor versions with a CHANGELOG note. Suitable for production if you pin exactly. | Yes, between minors, with a migration note. | | **Experimental** (`@experimental`) | Active R\&D. May change shape between any release. Use at your own risk; expect to update code on each upgrade. | Anytime, including patch. | ## What’s stable in v0.10 [Section titled “What’s stable in v0.10”](#whats-stable-in-v010) The following surfaces are tier-1 stable. We will not break these without a major bump. * **Builder entry point** — `ReactiveAgents.create()` and the `.with*()` chain syntax * **Provider selection** — `.withProvider("anthropic" | "openai" | "google" | "ollama" | "litellm" | "local")` and the `LLMProvider` interface * **Reasoning core** — `.withReasoning()` with the documented `ReasoningOptions` shape; the five strategies (`ReAct`, `Reflexion`, `Plan-Execute`, `Tree-of-Thought`, `Adaptive`) * **Tool surface** — `.withTools()`, `defineTool()`, MCP attachment via `.withMCP()`, and the `Tool` interface * **Event bus** — All event tags consumed by the public observability layer (`ToolCallStarted`, `ToolCallCompleted`, `LLMExchangeEmitted`, `StrategySwitched`, `VerifierVerdictEmitted`, plus the 30+ tags listed in `event-bus.ts`) * **Lifecycle hooks** — `.withHook(phase, timing, fn)` for the 12 phases and `before` / `after` / `on-error` timings * **Compose API** — `.compose()` (alias: `.withHarness()`) for harness composition; `.on()`, `.tap()`, `.before()`, `.after()`, `.onError()` transforms and hooks; all 12-phase composition and tag pattern matching * **Snapshot & Replay** (v0.11) — `@reactive-agents/replay` package: `loadRecordedRun`, `replay`, `makeReplayController`, `makeReplayToolLayer`, `diffTraces`, `computeArgsHash`. The `ToolCallCompleted` event payload’s new `args`, `result`, `error`, `resultTruncated` fields are also stable. * **AgentResult shape** — `.run()` and `.runStream()` return values * **Raw provider clients** — `AnthropicProviderLive`, `OpenAIProviderLive`, `LocalProviderLive`, `GeminiProviderLive`, `LiteLLMProviderLive` exported as standalone Effect Layers (you can skip the harness entirely) ## What’s `@unstable` in v0.10 [Section titled “What’s @unstable in v0.10”](#whats-unstable-in-v010) These work, but the **shape may change** in `0.11`. Pin exact versions if you depend on them. * **`KernelHooks` interface** — the inner-loop event taps (`onThought`, `onAction`, `onObservation`, etc.). The 12-phase outer hooks are stable; the inner kernel taps may consolidate. * **Healing pipeline stages** — `runHealingPipeline` and the 4 built-in stages are exported, but the stage list is not user-extensible yet. A `.withHealing(stages)` builder is planned for 0.11. * **Context curator internals** — `withContextProfile` is stable; the curator’s compression strategy is not user-replaceable yet. * **Arbitrator** — `withCustomTermination(predicate)` is stable for boolean overrides; full `withArbitrator(impl)` for replacing the termination pipeline is Phase 2. * **Verifier strategy** — `withVerification(options)` accepts options today; replaceable verifier impl is Phase 2. * **Cost router policy** — `withCostTracking()` records spend (stable); routing policy itself is not user-replaceable yet. * **Strategy switcher heuristic** — toggleable via `ReasoningOptions.strategySwitching` (stable); the heuristic itself is not yet replaceable. * **Calibration field schema** — fields are growing; consumer count is small. Expect additions and possible renames. ## What’s `@experimental` in v0.10 [Section titled “What’s @experimental in v0.10”](#whats-experimental-in-v010) Use at your own risk. Will change. * **A2A protocol surface** (`packages/a2a`) — wire format and JSON-RPC method names may change as the spec evolves * **Reactive observer / entropy scoring tunables** — thresholds, scoring functions * **Living Skills runtime in Cortex** — UI and persistence schema not finalized * **Sub-agent delegation API** — `.withSubAgents()` shape under iteration ## Deprecation policy [Section titled “Deprecation policy”](#deprecation-policy) When a stable surface is being replaced: 1. The old API stays functional and gets `@deprecated` JSDoc with a pointer to the replacement 2. A console warning fires at runtime (suppressible via `RA_SUPPRESS_DEPRECATION=1`) 3. Removal happens **no sooner than** one full minor cycle later (e.g., deprecated in 0.11 → removed earliest in 0.12) 4. The CHANGELOG lists the migration step for every removal We will **never** silently change the behavior of a stable API. If a bugfix changes observable behavior, it ships behind a flag or in a major bump. ## How to depend on Reactive Agents [Section titled “How to depend on Reactive Agents”](#how-to-depend-on-reactive-agents) | Risk tolerance | Recommendation | | ------------------------------------ | ---------------------------------------------------------------------------------------------------- | | Production app, low-touch upgrades | Pin patch versions (`"reactive-agents": "0.10.2"`). Consume only `@stable` APIs. | | Active development, monthly upgrades | Pin minor (`"~0.10.0"`). Read the CHANGELOG before bumping. `@unstable` OK if covered by your tests. | | Following main, contributing | Pin to a commit SHA or use `workspace:*`. `@experimental` is fair game. | ## What we want feedback on [Section titled “What we want feedback on”](#what-we-want-feedback-on) If you’ve adopted Reactive Agents and want a specific component promoted from `@unstable` to `@stable`, [open an issue](https://github.com/tylerjrbuell/reactive-agents-ts/issues). The promotion criteria are: 30+ days at current shape with no reported design issues, and at least one production user. We’d rather under-promise on stability today than break your code tomorrow.