/**
* AnthropicAdapter — Tier 2 of the agenttool path: a thin wrapper over the
* official `@anthropic-ai/sdk` client that gives every Messages call
* two superpowers without changing the provider event stream:
*
* 1. **Auto-injects the agent's wake doc** as `system=`, fetched once
* from `/v1/wake?format=anthropic` and cached for 5 minutes (matches
* Anthropic's prompt-cache window). The stable block carries
* `cache_control: ephemeral`; the volatile block refreshes per wake.
* User-supplied `system=` blocks are appended AFTER the wake.
*
* 2. **Auto-records traces** when the call carries
* `metadata.agenttool.trace = "decision"`. Posts to `/v1/traces`
* with the response text as the conclusion and the user message as
* the observation. Returns the trace_id on the augmented response
* so the agent can chain via `parent_trace_id` on a follow-up.
*
* 3. **(Mode b) Parses `...` markup** in the
* assistant's response. Recognised children:
*
* ......
*
*
* ......
*
* The agent decides what's load-bearing by writing the tag; the shim
* does the plumbing. Mirrors the friction-tier UX in the dashboard.
*
* A `` tag is a model-authored write to the agent's own
* identity record, so it is gated: the emission goes through
* `at.chronicle.write` (which enforces the type union and the title
* bounds) and only after a `beforeChronicleWrite` hook returns
* literal `true`. With no hook installed the write is refused. See
* {@link ChronicleBeforeWriteHook}.
*
* Posture: zero dependency on @anthropic-ai/sdk types. The adapter takes
* any object with a `messages.create(params)` method and optionally a
* `messages.stream(params)` helper, so the wrapping works whether the
* agent uses the official SDK, Bedrock SDK, or a custom HTTP client.
*
* Streaming boundary:
* - Low-level `messages.create({ stream: true })` is a transparent
* event-stream pass-through. It injects wake and strips local metadata,
* but cannot promise final-response tracing or markup. Decision tracing
* therefore fails before wake or provider I/O on that path.
* - `messages.stream(...)` uses the provider's final-message helper. Its
* completed message is finalized exactly once, so tracing and markup
* retain the same meaning as a non-streaming call.
*
* Doctrine: docs/IDENTITY-ANCHOR.md.
*/
import type { AgentTool } from "./client.js";
import type { WakeProfile, WakeProviderMeta } from "./wake.js";
/** Minimal shape of the Anthropic Messages client.
*
* `never[]` deliberately describes only the existence of callable provider
* methods. It does not impose AgentTool's parameter or return types on the
* provider's overloads, so an official `@anthropic-ai/sdk` client remains
* structurally assignable without taking a dependency on its types. Calls are
* narrowed at the adapter boundary after AgentTool has prepared the request.
*/
export interface AnthropicMessagesLike {
messages: {
create: (...args: never[]) => unknown;
stream?: (...args: never[]) => unknown;
};
}
/** Minimal low-level stream shape returned by
* `messages.create({ stream: true })`. Every property and event is delegated
* unchanged; these optional lifecycle methods name the cleanup surface the
* adapter preserves. */
export interface AnthropicLowLevelStreamLike extends AsyncIterable {
controller?: AbortController;
abort?: () => unknown;
close?: () => unknown;
}
/** Minimal high-level stream helper shape returned by `messages.stream(...)`.
*
* The callables are existential on purpose: this public structural type checks
* that the provider has the relevant surface without requiring its exact
* Message, event, listener, or response types to be assignable to AgentTool's
* local projections. The adapter narrows those values after runtime checks.
*/
export interface AnthropicManagedStreamLike extends AsyncIterable {
controller?: AbortController;
finalMessage: (...args: never[]) => unknown;
finalText?: (...args: never[]) => unknown;
done?: (...args: never[]) => unknown;
abort?: (...args: never[]) => unknown;
close?: (...args: never[]) => unknown;
on?: (...args: never[]) => unknown;
once?: (...args: never[]) => unknown;
off?: (...args: never[]) => unknown;
emitted?: (...args: never[]) => unknown;
withResponse?: (...args: never[]) => unknown;
}
/** Subset of the Anthropic Messages API response we read. The full shape
* is forwarded verbatim with only `.agenttool` augmentation added. */
export interface AnthropicMessageResponse {
id: string;
model: string;
content?: Array<{
type: string;
text?: string;
}>;
stop_reason?: string;
usage?: Record;
[key: string]: unknown;
}
/** The `metadata.agenttool` extension the adapter reads off the call params.
* Stripped from the forwarded request before it hits Anthropic. */
export interface AgentToolMetadata {
/**
* Opt-in auto-trace. When set to `"decision"`, the adapter posts to
* /v1/traces after the messages.create call returns. Default: omit
* (no trace fired).
*/
trace?: "decision" | false;
/** Optional parent trace to chain this decision to. */
parent_trace_id?: string;
/** Optional explicit decision_type override (default `"decision"`). */
decision_type?: string;
/** Tags propagated to the trace. */
tags?: string[];
/** Override the agent_id stamped on the trace. */
agent_id?: string;
/** Skip the wake auto-injection for this call. */
skip_wake?: boolean;
/** Skip parsing of markup in the response for this call. */
skip_markup?: boolean;
}
/** Immutable local review context for one model-authored chronicle write. */
export interface ChronicleBeforeWriteContext {
/** Always `"anthropic_markup"` — the entry was authored by the model,
* not by the calling program. */
readonly source: "anthropic_markup";
/** The raw type string the model emitted, before validation. */
readonly type: string;
/** The title the model emitted, before validation. */
readonly title: string;
/** The body the model emitted, when it supplied one. */
readonly body?: string;
}
/**
* Local gate for model-authored chronicle writes.
*
* The hook may review synchronously or asynchronously. It must return
* literal `true` to proceed; every other result fails closed locally. The
* hook's return value is neither persisted nor signed — it only decides
* whether the entry is written.
*/
export type ChronicleBeforeWriteHook = (context: ChronicleBeforeWriteContext) => boolean | Promise;
export interface AnthropicAdapterOptions {
/** Identity id for multi-identity projects (passed through to /v1/wake). */
identityId?: string;
/** Wake projection used for automatic system injection. Default `full`. */
wakeProfile?: WakeProfile;
/** Disable parsing of ... markup globally. */
disableMarkupParsing?: boolean;
/**
* Review gate for `` tags the model emits. Required for those
* emissions to be written at all: without it every model-authored
* chronicle write is refused, because a chronicle entry is a mark on the
* agent's own identity record. `` emissions are unaffected.
*/
beforeChronicleWrite?: ChronicleBeforeWriteHook;
}
/** A markup emission produced by parsing `...` from
* the response. Each entry records what was posted and the resulting id
* (or the error if the post failed). */
export interface MarkupEmission {
kind: "chronicle" | "trace";
/** API id returned on success (e.g. "ch_..." or "tr_..."). */
id: string | null;
/** Error message if the emission failed; null on success. */
error: string | null;
/** The parsed source for debugging. */
source: Record;
}
/** Augmentation added to the Anthropic response by the adapter. The
* underlying response is forwarded verbatim alongside this. */
export interface AgentToolAugmentation {
/** Trace id when `metadata.agenttool.trace = "decision"` fired. */
trace_id: string | null;
/** Whether wake auto-injection ran for this call. */
wake_used: boolean;
/** Cache eligibility echoed from /v1/wake?format=anthropic _meta. */
cache_eligible: WakeProviderMeta["cache_eligible"] | null;
/** Per-emission outcomes from markup parsing. Empty when
* the response carried no markup or parsing was disabled. */
markup_emissions: MarkupEmission[];
}
export type AdaptedResponse = AnthropicMessageResponse & {
agenttool: AgentToolAugmentation;
};
/** Low-level streaming receipt. It is local-only and never changes, consumes,
* or reconstructs provider events. */
export interface AdaptedLowLevelStream extends AnthropicLowLevelStreamLike {
readonly agenttool: AgentToolAugmentation;
}
/** AgentTool-managed helper returned by `adapter.messages.stream(...)`.
* Unknown provider events are yielded by reference and the core asynchronous
* lifecycle is preserved. It intentionally does not claim the provider's
* synchronous inspection fields because wake retrieval finishes before the
* provider helper exists. */
export interface AdaptedManagedStream extends AsyncIterable {
readonly agenttool: AgentToolAugmentation;
readonly controller: AbortController;
finalMessage(): Promise;
finalText(): Promise;
done(): Promise;
emitted(event: string): Promise;
withResponse(): Promise>;
abort(): void;
close(): Promise;
on(event: string, listener: (...args: unknown[]) => void): this;
once(event: string, listener: (...args: unknown[]) => void): this;
off(event: string, listener: (...args: unknown[]) => void): this;
}
export interface AnthropicAdapterMessages {
create(params: object & {
stream: true;
}, ...requestOptions: unknown[]): Promise;
create(params: object, ...requestOptions: unknown[]): Promise;
stream(params: object, ...requestOptions: unknown[]): AdaptedManagedStream;
}
export declare class AnthropicAdapter {
private readonly anthropic;
private readonly at;
private readonly options;
constructor(anthropic: AnthropicMessagesLike, at: AgentTool, options?: AnthropicAdapterOptions);
/** Mirrors the shape of `anthropic.messages` so callers can swap in the
* adapter without changing call sites: `adapter.messages.create({...})`
* or `adapter.messages.stream({...})`. */
get messages(): AnthropicAdapterMessages;
private _inspectRequest;
private _prepareRequest;
private _emptyAugmentation;
private _finalizeResponse;
/** Post a trace built from the call-boundary input and provider response.
* The shape matches `/v1/traces` POST schema
* (decision/reasoning/context). */
private _recordDecisionTrace;
/** Fail-closed gate for one model-authored chronicle write. Mirrors the
* `covenants.create` before_submit discipline: only literal `true`
* proceeds, a throwing hook is a refusal, and the hook's output is
* neither persisted nor signed. Absence of a hook is also a refusal —
* a covenant is submitted by the calling program, but a ``
* tag is written by the model, so the capability starts closed. */
private _reviewChronicleWrite;
/** Walk the response text for ... blocks and
* emit each child to the right endpoint. Tolerant of whitespace and
* child ordering; failures are recorded per-emission, not thrown. */
private _parseAndEmitMarkup;
}