generative-a11y

@generative-a11y/core

Turn application events into scheduled accessibility announcements.

Install and import

Core does not depend on the DOM, React, or an AI framework. Your app sends it RuntimeEvent objects, and it creates AnnouncementIntent objects when there is something useful to announce.

Terminal
npm install @generative-a11y/core

Use core in any JavaScript runtime, including tests without a document. Browser apps add DOM or React for delivery.

accessibility-runtime.ts
import { createRuntime } from "@generative-a11y/core";

const runtime = createRuntime({
  onAnnouncement: (intent) => console.log(intent.text),
});

// Call runtime.dispose() when its owner ends.

This listener records prepared output. Connect browser delivery before sending lifecycle events in a web app.

Public export map

Use the narrowest public export for the behavior you own.

CategoryExportsReference
RuntimecreateRuntime, Runtime/api/core/create-runtime
EventsRuntimeEvent and event family types/api/core/events
WorkflowsWorkflowContext, run and step lifecycle types/api/core/workflows
Policypresets, resolvePolicy, PolicyOverrides, WorkflowPolicy/api/core/policy
SchedulercreateScheduler and scheduler types/api/core/scheduler
TestingManualClock, createRecorder/api/core/testing
DiagnosticsAnnouncementIntent, AnnouncementDiagnostic/api/core/diagnostics

Messages

English notices work without configuration. For custom copy, import en and Messages from @generative-a11y/core/messages and pass messages to createRuntime. This entry point also exports MessageMap, MessageParams, MessageKey, AdapterCopy, and normalizeAdapterCopy. These exports are not available from the core root.

See localized announcements for examples and API naming changes for the breaking rename reference.

Public declarations: @generative-a11y/core

All exported values and types for this entry point. Import these names from @generative-a11y/core; the signatures below are reference material, not a replacement for the ownership and lifecycle guidance above.

AdapterFidelity — type
interface AdapterFidelity {
    /** Evidence quality for top-level and delegated run lifecycle. */
    runs: "exact" | "partial" | "unavailable";
    /** Evidence quality for step lifecycle and stable step identity. */
    steps: "exact" | "partial" | "unavailable";
    /** Evidence quality for parent-child relationships. */
    hierarchy: "exact" | "partial" | "unavailable";
    /** Evidence quality for tool lifecycle and workflow attribution. */
    tools: "exact" | "partial" | "unavailable";
    /** Evidence quality for human-interaction lifecycle and attribution. */
    interactions: "exact" | "partial" | "unavailable";
    /** Ability to suppress lifecycle duplicates when historical events replay. */
    replay: "exact" | "partial" | "unavailable";
    /** Evidence quality after a transport reconnects. */
    reconnection: "exact" | "partial" | "unavailable";
    /** Custom protocol events require a host-supplied explicit mapping. */
    customEvents: "explicit-mapping" | "unsupported";
    interruption: "exact" | "action-wrapper" | "unavailable";
    retries: "exact" | "action-wrapper" | "unavailable";
    connection: "exact" | "inferred" | "unavailable";
    optionalEvents?: Array<"tool.progress" | "tool.failed" | "citation.available" | "interaction.requested">;
}
AnnouncementCapacityPriority — type
type AnnouncementCapacityPriority = "status" | "content";
AnnouncementChannel — type
type AnnouncementChannel = "polite" | "assertive";
AnnouncementDiagnostic — type
interface AnnouncementDiagnostic {
    at: number;
    disposition: DiagnosticDisposition;
    reason: DiagnosticReason;
    /** Number of suppressed decisions or events represented by an aggregate. */
    count?: number;
    announcement?: AnnouncementIntent;
    sourceType?: RuntimeEvent["type"];
    sourceEventId?: string;
    responseId?: string;
    toolId?: string;
    interactionId?: string;
    /** Stable logical run identity associated with this decision. */
    runId?: string;
    /** Stable run attempt identity associated with this decision. */
    runInstanceId?: string;
    /** Stable logical step identity associated with this decision. */
    stepId?: string;
    /** Stable step attempt identity associated with this decision. */
    stepInstanceId?: string;
    scheduledAt?: number;
    dueAt?: number;
    delayMs?: number;
    queueSequence?: number;
}
AnnouncementIntent — type
interface AnnouncementIntent {
    id: string;
    at: number;
    channel: AnnouncementChannel;
    text: string;
    sourceType: RuntimeEvent["type"];
    sourceEventId?: string;
    responseId?: string;
    toolId?: string;
    interactionId?: string;
    /** Stable logical run identity associated with this intent. */
    runId?: string;
    /** Stable run attempt identity associated with this intent. */
    runInstanceId?: string;
    /** Stable logical step identity associated with this intent. */
    stepId?: string;
    /** Stable step attempt identity associated with this intent. */
    stepInstanceId?: string;
    locale?: string;
}
AnnouncementListener — type
type AnnouncementListener = (announcement: AnnouncementIntent) => void;
AnnouncementPolicy — type
interface AnnouncementPolicy {
    /** Optional for compatibility with existing caller-authored policies. */
    attention?: AttentionPolicy;
    text: TextPolicy;
    tools: ToolPolicy;
    workflows: WorkflowPolicy;
    announceResponseStarted: boolean;
    announceResponseCompleted: boolean;
    announceInterruption: boolean;
    announceRetry: boolean;
    announceInteractions: boolean;
    announceConnections: boolean;
    announceCitations: boolean;
    errorChannel: AnnouncementChannel;
    minimumGapMs: number;
    dedupeWindowMs: number;
    maxQueueSize: number;
    maxActiveEntities: number;
}
AnnouncementPurpose — type
/** Candidate purpose is independent of its source event and delivery channel. */
type AnnouncementPurpose = "response-text" | "routine-status" | "notice";
AttentionMode — type
/** Conservative evidence labels, never proof of reading or user intent. */
type AttentionMode = "foreground" | "background" | "reading-history" | "away" | "unknown";
AttentionOverride — type
/** Explicit host/user choice takes precedence over observed evidence. */
type AttentionOverride = "auto" | "normal" | "quiet";
AttentionPolicy — type
interface AttentionPolicy {
    enabled: boolean;
    quietWhen: readonly Exclude<AttentionMode, "foreground" | "unknown">[];
}
AttentionState — type
interface AttentionState {
    readonly observed: AttentionMode;
    readonly override: AttentionOverride;
    readonly effective: "normal" | "quiet";
}
Clock — type
interface Clock {
    now(): number;
    setTimeout(callback: () => void, delayMs: number): ClockTimer;
    clearTimeout(timer: ClockTimer): void;
}
ClockTimer — type
type ClockTimer = unknown;
createRecorder — value
declare function createRecorder(options?: Omit<RuntimeOptions, "clock" | "onAnnouncement" | "onDiagnostic"> & {
    startAt?: number;
}): Recorder;
createRuntime — value
declare function createRuntime(options?: RuntimeOptions): Runtime;
createScheduler — value
declare function createScheduler(options: SchedulerOptions): Scheduler;
DiagnosticDisposition — type
type DiagnosticDisposition = "queued" | "merged" | "suppressed" | "cancelled" | "announced";
DiagnosticListener — type
type DiagnosticListener = (diagnostic: AnnouncementDiagnostic) => void;
DiagnosticPendingAnnouncement — type
/** A content-free queued announcement projection for diagnostic consumers. */
interface DiagnosticPendingAnnouncement {
    id: string;
    channel: AnnouncementChannel;
    sourceType: RuntimeEvent["type"];
    sourceEventId?: string;
    responseId?: string;
    toolId?: string;
    interactionId?: string;
    /** Stable logical run identity associated with this queued intent. */
    runId?: string;
    /** Stable run attempt identity associated with this queued intent. */
    runInstanceId?: string;
    /** Stable logical step identity associated with this queued intent. */
    stepId?: string;
    /** Stable step attempt identity associated with this queued intent. */
    stepInstanceId?: string;
    locale?: string;
    scheduledAt: number;
    dueAt: number;
    delayMs: number;
    sequence: number;
}
DiagnosticReason — type
type DiagnosticReason = "catalog-format-error" | "scheduled" | "coalesced" | "duplicate" | "policy-silent" | "attention-quiet" | "attention-updated" | "unknown-response" | "terminal-response" | "stale-response" | "empty-text" | "scope-cancelled" | "runtime-disposed" | "queue-capacity" | "delivery-error" | "unknown-tool" | "terminal-tool" | "stale-tool" | "unknown-run" | "terminal-run" | "stale-run" | "unknown-step" | "terminal-step" | "stale-step" | "unknown-parent" | "open-children" | "partial-identity" | "invalid-event" | "progress-threshold" | "delivered";
DiagnosticResponseSnapshot — type
interface DiagnosticResponseSnapshot {
    responseId: string;
    epoch: number;
    instanceId?: string;
    status: "active" | "completed" | "interrupted" | "failed";
    locale?: string;
}
DiagnosticRunSnapshot — type
/** Content-free immutable projection of one tracked run attempt. */
interface DiagnosticRunSnapshot {
    /** Stable logical run identity. */
    runId: string;
    /** Stable attempt identity when supplied by the source. */
    instanceId?: string;
    /** Stable logical parent run identity. */
    parentRunId?: string;
    /** Parent attempt identity when supplied by the source. */
    parentRunInstanceId?: string;
    /** Tool that delegated to this run, when explicitly exposed. */
    parentToolId?: string;
    /** Response that owns this run, when explicitly exposed. */
    parentResponseId?: string;
    /** Current lifecycle state for this run attempt. */
    status: "active" | "completed" | "interrupted" | "failed";
    /** Number of identified steps completed in this attempt. */
    completedSteps: number;
    /** Number of identified steps failed in this attempt. */
    failedSteps: number;
}
DiagnosticStepSnapshot — type
/** Content-free immutable projection of one identified step attempt. */
interface DiagnosticStepSnapshot {
    /** Stable logical owner run identity. */
    runId: string;
    /** Owner run attempt identity when supplied by the source. */
    runInstanceId?: string;
    /** Stable logical step identity. */
    stepId: string;
    /** Stable step attempt identity when supplied by the source. */
    instanceId?: string;
    /** Stable logical parent step identity. */
    parentStepId?: string;
    /** Parent step attempt identity when supplied by the source. */
    parentStepInstanceId?: string;
    /** Current lifecycle state for this step attempt. */
    status: "active" | "completed" | "interrupted" | "failed";
    /** Injected-clock timestamp when this attempt started. */
    startedAt: number;
    /** Last coalesced progress bucket, or -1 before progress. */
    lastProgressBucket: number;
}
DiagnosticToolSnapshot — type
interface DiagnosticToolSnapshot {
    toolId: string;
    instanceId?: string;
    status: "active" | "completed" | "failed";
    locale?: string;
    lastProgressBucket: number;
}
InteractionKind — type
type InteractionKind = "approval" | "confirmation" | "input" | (string & {});
ManualClock — value
declare class ManualClock implements Clock {
    #private;
    constructor(startAt?: number);
    now(): number;
    setTimeout(callback: () => void, delayMs: number): number;
    clearTimeout(timer: ClockTimer): void;
    advanceBy(durationMs: number): void;
    advanceTo(timestamp: number): void;
    runNext(): boolean;
    runUntilIdle(maxTasks?: number): void;
    pendingCount(): number;
}
normalizeAnnouncementText — value
declare function normalizeAnnouncementText(text: string): string;
PolicyOverrides — type
type PolicyOverrides = Partial<Omit<AnnouncementPolicy, "text" | "tools" | "workflows" | "attention">> & {
    attention?: Partial<NonNullable<AnnouncementPolicy["attention"]>>;
    text?: Partial<AnnouncementPolicy["text"]>;
    tools?: Partial<AnnouncementPolicy["tools"]>;
    workflows?: Partial<AnnouncementPolicy["workflows"]>;
};
PresetName — type
type PresetName = "minimal" | "balanced" | "verbose" | "completion-only";
presets — value
declare const presets: Readonly<Record<PresetName, ReadonlyAnnouncementPolicy>>;
ReadonlyAnnouncementPolicy — type
type ReadonlyAnnouncementPolicy = Readonly<Omit<AnnouncementPolicy, "text" | "tools" | "workflows" | "attention"> & {
    attention?: Readonly<AttentionPolicy>;
    text: Readonly<TextPolicy>;
    tools: Readonly<ToolPolicy>;
    workflows: Readonly<WorkflowPolicy>;
}>;
Recorder — type
interface Recorder {
    runtime: ReturnType<typeof createRuntime>;
    clock: ManualClock;
    transcript(): AnnouncementIntent[];
    diagnosticTranscript(): AnnouncementDiagnostic[];
    clear(): void;
}
resolvePolicy — value
declare function resolvePolicy(preset?: PresetName, overrides?: PolicyOverrides): ReadonlyAnnouncementPolicy;
Runtime — type
interface Runtime {
    dispatch(event: RuntimeEvent): boolean;
    getPolicy(): ReadonlyAnnouncementPolicy;
    pendingCount(): number;
    subscribeAnnouncements(listener: AnnouncementListener): () => void;
    subscribeDiagnostics(listener: DiagnosticListener): () => void;
    subscribeDiagnosticEvents(listener: RuntimeDiagnosticListener): () => void;
    getDiagnosticSnapshot(): RuntimeDiagnosticSnapshotV1;
    dispose(): void;
}
RuntimeDiagnosticEventV1 — type
type RuntimeDiagnosticEventV1 = {
    schemaVersion: 1;
    sequence: number;
    at: number;
    kind: "event-observed";
    event: RuntimeEvent;
} | {
    schemaVersion: 1;
    sequence: number;
    at: number;
    kind: "decision";
    decision: AnnouncementDiagnostic;
};
RuntimeDiagnosticListener — type
type RuntimeDiagnosticListener = (event: RuntimeDiagnosticEventV1) => void;
RuntimeDiagnosticSnapshotV1 — type
interface RuntimeDiagnosticSnapshotV1 {
    readonly messages?: Readonly<{
        catalogId: string;
        locale: string;
    }>;
    schemaVersion: 1;
    at: number;
    policy: ReadonlyAnnouncementPolicy;
    /** Cached immutable state, present only when attention policy is enabled. */
    attention?: AttentionState;
    pending: {
        announcements: readonly DiagnosticPendingAnnouncement[];
        flushes: readonly {
            responseId: string;
            epoch: number;
            dueAt: number;
        }[];
    };
    responses: readonly DiagnosticResponseSnapshot[];
    tools: readonly DiagnosticToolSnapshot[];
    /** Present when the runtime supports hierarchical workflow diagnostics. */
    runs?: readonly DiagnosticRunSnapshot[];
    /** Present when the runtime supports identified step diagnostics. */
    steps?: readonly DiagnosticStepSnapshot[];
    pendingCount: number;
}
RuntimeEvent — type
type RuntimeEvent = (EventMetadata & {
    type: "attention.changed";
    mode: AttentionMode;
}) | (EventMetadata & {
    type: "attention.override";
    mode: AttentionOverride;
}) | (ContextualEventMetadata & {
    type: "response.started";
    responseId: string;
    responseInstanceId?: string;
}) | (ContextualEventMetadata & {
    type: "response.text.delta";
    responseId: string;
    responseInstanceId?: string;
    delta: string;
}) | (ContextualEventMetadata & {
    type: "response.completed";
    responseId: string;
    responseInstanceId?: string;
}) | (ContextualEventMetadata & {
    type: "response.interrupted";
    responseId: string;
    responseInstanceId?: string;
}) | (ContextualEventMetadata & {
    type: "response.failed";
    responseId: string;
    responseInstanceId?: string;
    error?: string;
    announcement?: string;
}) | (ContextualEventMetadata & {
    type: "response.retrying";
    responseId: string;
    responseInstanceId?: string;
    nextResponseInstanceId?: string;
    attempt?: number;
}) | (ContextualEventMetadata & {
    type: "tool.started";
    toolId: string;
    toolInstanceId?: string;
    label: string;
}) | (ContextualEventMetadata & {
    type: "tool.progress";
    toolId: string;
    toolInstanceId?: string;
    label: string;
    /** Normalized progress from 0 to 1. */
    progress?: number;
    message?: string;
}) | (ContextualEventMetadata & {
    type: "tool.completed";
    toolId: string;
    toolInstanceId?: string;
    label: string;
    summary?: string;
}) | (ContextualEventMetadata & {
    type: "tool.failed";
    toolId: string;
    toolInstanceId?: string;
    label: string;
    error?: string;
    announcement?: string;
}) | (ContextualEventMetadata & {
    type: "interaction.requested";
    interactionId: string;
    kind: InteractionKind;
    label: string;
    urgent?: boolean;
}) | (ContextualEventMetadata & {
    type: "interaction.resolved";
    interactionId: string;
    kind: InteractionKind;
    outcome: "approved" | "rejected" | "submitted" | "cancelled";
    label?: string;
}) | (ContextualEventMetadata & {
    type: "approval.requested";
    approvalId: string;
    label: string;
    urgent?: boolean;
}) | (ContextualEventMetadata & {
    type: "approval.resolved";
    approvalId: string;
    outcome: "approved" | "rejected" | "cancelled";
    label?: string;
}) | (EventMetadata & {
    type: "run.started";
    /** Stable logical run identity. */
    runId: string;
    /** Stable identity for this run attempt. */
    runInstanceId?: string;
    /** Stable logical parent run, when explicitly exposed. */
    parentRunId?: string;
    /** Attempt identity of the explicit parent run. */
    parentRunInstanceId?: string;
    /** Tool that delegated to this run. */
    parentToolId?: string;
    /** Response that owns this run. */
    parentResponseId?: string;
    /** Localized display copy; never used as identity. */
    label?: string;
}) | (EventMetadata & {
    type: "run.completed" | "run.interrupted" | "run.failed";
    /** Stable logical run identity. */
    runId: string;
    /** Stable identity for this run attempt. */
    runInstanceId?: string;
    /** Diagnostic-only backend detail; never announced automatically. */
    error?: string;
    /** Short localized user-safe terminal copy. */
    announcement?: string;
}) | (EventMetadata & {
    type: "run.retrying";
    /** Stable logical run identity. */
    runId: string;
    /** Attempt being replaced. */
    runInstanceId?: string;
    /** Stable identity of the replacement attempt. */
    nextRunInstanceId?: string;
    /** Human-readable one-based attempt number. */
    attempt?: number;
}) | (EventMetadata & {
    type: "step.started";
    /** Stable logical owner run identity. */
    runId: string;
    /** Stable owner run attempt identity. */
    runInstanceId?: string;
    /** Stable logical step identity; omit for name-only evidence. */
    stepId?: string;
    /** Stable identity for this step attempt. */
    stepInstanceId?: string;
    /** Stable logical parent step identity. */
    parentStepId?: string;
    /** Attempt identity of the explicit parent step. */
    parentStepInstanceId?: string;
    /** Localized display copy; never used as identity. */
    label: string;
}) | (EventMetadata & {
    type: "step.progress";
    /** Stable logical owner run identity. */
    runId: string;
    /** Stable owner run attempt identity. */
    runInstanceId?: string;
    /** Stable logical step identity; omit for name-only evidence. */
    stepId?: string;
    /** Stable identity for this step attempt. */
    stepInstanceId?: string;
    /** Localized display copy; never used as identity. */
    label: string;
    /** Normalized progress from 0 to 1. */
    progress?: number;
    /** Optional localized user-safe progress copy. */
    message?: string;
}) | (EventMetadata & {
    type: "step.completed" | "step.interrupted" | "step.failed";
    /** Stable logical owner run identity. */
    runId: string;
    /** Stable owner run attempt identity. */
    runInstanceId?: string;
    /** Stable logical step identity; omit for name-only evidence. */
    stepId?: string;
    /** Stable identity for this step attempt. */
    stepInstanceId?: string;
    /** Localized display copy; never used as identity. */
    label: string;
    /** Diagnostic-only backend detail; never announced automatically. */
    error?: string;
    /** Short localized user-safe terminal copy. */
    announcement?: string;
}) | (EventMetadata & {
    type: "step.retrying";
    /** Stable logical owner run identity. */
    runId: string;
    /** Stable owner run attempt identity. */
    runInstanceId?: string;
    /** Stable logical step identity; omit for name-only evidence. */
    stepId?: string;
    /** Attempt being replaced. */
    stepInstanceId?: string;
    /** Stable identity of the replacement attempt. */
    nextStepInstanceId?: string;
    /** Human-readable one-based attempt number. */
    attempt?: number;
    /** Localized display copy; never used as identity. */
    label: string;
}) | (EventMetadata & {
    type: "connection.lost";
    label?: string;
}) | (EventMetadata & {
    type: "connection.restored";
    label?: string;
}) | (EventMetadata & {
    type: "citation.available";
    count: number;
});
RuntimeOptions — type
interface RuntimeOptions {
    messages?: Messages;
    preset?: PresetName;
    policy?: PolicyOverrides;
    clock?: Clock;
    onAnnouncement?: (announcement: AnnouncementIntent) => void;
    onDeliveryError?: (error: unknown, announcement: AnnouncementIntent) => void;
    onDiagnostic?: (diagnostic: AnnouncementDiagnostic) => void;
}
ScheduleAnnouncement — type
interface ScheduleAnnouncement {
    /** Defaults to notice; independent of channel and source event. */
    purpose?: AnnouncementPurpose;
    channel: AnnouncementChannel;
    text: string;
    sourceType: RuntimeEvent["type"];
    sourceEventId?: string;
    responseId?: string;
    toolId?: string;
    interactionId?: string;
    runId?: string;
    runInstanceId?: string;
    stepId?: string;
    stepInstanceId?: string;
    locale?: string;
    delayMs?: number;
    scope?: string;
    coalesceKey?: string;
    dedupeKey?: string;
    capacityPriority?: AnnouncementCapacityPriority;
}
Scheduler — type
interface Scheduler {
    schedule(candidate: ScheduleAnnouncement): string | undefined;
    cancelScope(scope: string): void;
    /** Cancel only queued candidates of these purposes; already delivered output cannot be retracted. */
    cancelPurposes(purposes: readonly AnnouncementPurpose[], reason?: DiagnosticReason): void;
    dispose(): void;
    pendingCount(): number;
    getDiagnosticSnapshot(): readonly DiagnosticPendingAnnouncement[];
}
SchedulerOptions — type
interface SchedulerOptions {
    clock: Clock;
    minimumGapMs: number;
    dedupeWindowMs: number;
    maxQueueSize: number;
    onAnnouncement: (announcement: AnnouncementIntent) => void;
    onDeliveryError?: (error: unknown, announcement: AnnouncementIntent) => void;
    onDiagnostic?: (diagnostic: AnnouncementDiagnostic) => void;
}
SegmentationResult — type
interface SegmentationResult {
    complete: string[];
    remainder: string;
}
segmentText — value
declare function segmentText(text: string, strategy: Exclude<TextStrategy, "silent" | "completion">, locale?: string): SegmentationResult;
systemClock — value
declare const systemClock: Clock;
TextPolicy — type
interface TextPolicy {
    strategy: TextStrategy;
    minimumCharacters: number;
    maximumDelayMs: number;
}
TextStrategy — type
type TextStrategy = "silent" | "sentence" | "paragraph" | "completion";
ToolPolicy — type
interface ToolPolicy {
    announceStart: boolean;
    announceStartAfterMs: number;
    announceProgress: boolean;
    progressEveryPercent: number;
    announceCompletion: boolean;
    announceFailure: boolean;
}
WorkflowContext — type
/** Explicit workflow ownership supplied by a source integration. */
type WorkflowContext = {
    runId?: undefined;
    runInstanceId?: never;
    stepId?: never;
    stepInstanceId?: never;
} | ({
    /** Stable logical run identity. */
    runId: string;
    /** Stable identity for one attempt of the logical run. */
    runInstanceId?: string;
} & ({
    stepId?: undefined;
    stepInstanceId?: never;
} | {
    /** Stable logical step identity. */
    stepId: string;
    /** Stable identity for one attempt of the logical step. */
    stepInstanceId?: string;
}));
WorkflowPolicy — type
/** Announcement controls for hierarchical run and step lifecycle. */
interface WorkflowPolicy {
    /** Run boundary verbosity. */
    runs: "silent" | "terminal" | "all";
    /** Identified step boundary verbosity. */
    steps: "silent" | "long-running" | "all";
    /** Delay and duration threshold for long-running step announcements. */
    announceStepAfterMs: number;
    /** Whether explicit step progress may be coalesced and announced. */
    announceProgress: boolean;
    /** Whether identified nested steps may produce announcements. */
    announceNestedSteps: boolean;
}