Skip to content

Send application context to AI ​

provideSendContextToAi adds a developer-oriented context inspector to a Craft application. It is useful when an AI assistant needs more than a copied error message: the selected component, the recent user journey, the relevant app state, and optionally the DOM and computed CSS.

Typical uses include:

  • asking an AI assistant to explain or fix a broken screen;
  • preparing a reproducible bug report or a support ticket;
  • investigating a failed HTTP request, navigation, mutation, or query;
  • sending a consistent, structured context to an internal debugging agent.

The feature is user-driven. It does not call an AI service by itself. Without an endpoint, everything stays in the browser and the user copies the prompt or the timeline when they choose to.

Minimal setup ​

Register the provider once in the application providers. The default UI then adds an AI context launcher in the bottom-right corner and a context menu to Craft component hosts.

ts
export const minimalAiContextProviders = [provideSendContextToAi()];

The two entry points are equivalent:

  • open the launcher to start with an empty context, then interact with the app;
  • right-click a component to start with that component already selected.

From the chat, the user can add more elements with another right-click, remove selected elements, write an instruction, record or clear the timeline, and choose which sections to include in the generated Markdown prompt.

What is collected ​

The session combines several kinds of context:

  • Selected elements: tag name, text, outer HTML, and optionally a selector.
  • Component information: the host name, Craft host tags, click coordinates, the clicked element, and truncated host HTML.
  • Timeline: DOM interactions, HTTP requests, router activity, primitive activity, and app snapshot reports. HTTP and navigation entries are linked by operation and correlation IDs when those services provide them.
  • App snapshots: the reports emitted by the app snapshot registry while the context is being prepared.
  • DOM and CSS captures: the selected component or the full page, including computed styles. These captures are optional because they can be large and briefly pause the page while they are collected.

The default prompt contains selected elements, component information, the timeline summary, and app snapshots when they exist. Timeline JSON and DOM/CSS captures are opt-in. The checkboxes in the chat change the Markdown prompt; the webhook also receives the structured fields so an agent can process them without parsing Markdown.

The chat also supports recording a named clip. A clip is a subset of the timeline, which is useful when an investigation contains several unrelated interactions. Copy JSON exports the visible timeline (or the selected clip), whereas Copy prompt builds the AI-oriented Markdown document.

Send the context to an agent ​

Pass a browser-accessible webhook URL to enable the Send action:

ts
export const aiContextProviders = provideSendContextToAi({
  endpoint: 'https://agent.example.com/hooks/context',
});

The browser sends a JSON POST with this versioned shape:

json
{
  "version": 1,
  "prompt": "# Instruction\nInvestigate this screen",
  "instruction": "Investigate this screen",
  "selectedElements": [],
  "events": [],
  "snapshot": [],
  "captures": {},
  "component": {
    "hostName": "OrdersPage",
    "tagList": ["component:OrdersPage#1"],
    "coords": { "x": 120, "y": 80 },
    "outerHTML": "<section>…</section>"
  }
}

prompt is generated from the instruction, the selected options, and the structured fields. component is omitted when the chat was opened from the launcher without a captured component. captures.component and captures.page are present only when their corresponding DOM/CSS options were selected. Events generated by the webhook request itself are excluded from the context sent to that webhook.

Any 2xx response is successful, including 200, 202, and 204. Network failures, timeouts, and non-2xx responses are shown in the chat. Retry reuses the exact same payload, and Copy payload copies that payload only after a failed request.

The endpoint is application configuration shipped to the browser, not a secret. It must allow the application's origin through CORS and accept JSON POST requests. Put authentication and secret management in a protected same-origin proxy or agent gateway.

Configure the webhook ​

provideSendContextToAi currently accepts the browser-accessible endpoint. Omit it to keep the experience copy-only, or pass a URL to enable sending:

ts
export const customizedAiContextProviders = provideSendContextToAi({
  endpoint: '/internal/ai/context',
});

Treat that URL as public application configuration. Put authentication, redaction, and any tenant-specific policy in a protected same-origin proxy or agent gateway; never put credentials in the browser bundle.

The session and its record controller are also injectable:

ts
import {
  SEND_CONTEXT_RECORD_CONTROLLER,
  SEND_CONTEXT_SESSION,
} from '@craft-ts/component';
import { ɵinject as inject } from '@craft-ts/core';

const record = inject(SEND_CONTEXT_RECORD_CONTROLLER);
record.startRecord('Checkout failure');

// Later, from the same application flow:
record.stopRecord();
const summary = record.exportSummary();
const json = record.exportJson();

const session = inject(SEND_CONTEXT_SESSION);
session.capture('custom', 'emitted', {
  name: 'checkout.validation',
  state: { step: 'payment' },
});

Use the record controller for clips and the session for low-level event emission, subscription, clearing, and programmatic export.

Customize the UI with DI ​

There are three levels of UI customization:

ProviderWhat it replaces or addsWhen to use it
provideSendContextChatComponent(() => MyChat)The default chat panelKeep the built-in launcher and context menu, but replace the panel
provideSendContextUiRenderer(() => MyRenderer)The complete rendererOwn the launcher/chat lifecycle and render the whole experience
SEND_CONTEXT_LAUNCHER_COMPONENT / SEND_CONTEXT_CONTEXT_MENU_COMPONENTThe floating launcher or right-click menuMatch the application's controls or visual language

The complete renderer receives SendContextUiContext. It exposes the live session, events, clips, selected targets, captured payload, DOM capture element, recording state, endpoint, and operations such as addTarget, removeTarget, selectClip, and close.

ts
import {
  provideSendContextChatComponent,
  provideSendContextUiRenderer,
  type SendContextUiContext,
} from '@craft-ts/component';

// A chat replacement keeps the default surrounding behavior.
provideSendContextChatComponent(() => MyChat);

// A complete renderer receives the live context as its `context` input.
provideSendContextUiRenderer(() => MyRenderer);

// MyRenderer's input contract is:
// { context: Input<SendContextUiContext>; onClose: Output<() => void> }

provideSendContextChatSection, provideSendContextChatAction, and provideSendContextExportSection are multi providers intended for a custom renderer. They let feature libraries contribute sections, commands, or export views without depending on one global renderer. The built-in chat does not render those extension entries itself; a custom renderer reads them from SendContextUiContext.

For example, a feature can contribute an action that starts a named clip:

ts
import { provideSendContextChatAction } from '@craft-ts/component';

const providers = [
  provideSendContextChatAction({
    id: 'record-checkout',
    label: 'Record checkout flow',
    run: (context) => context.session.startRecord('Checkout flow'),
  }),
];

The UI provider tokens are regular DI contracts, so a custom launcher or menu can be registered directly:

ts
import {
  SEND_CONTEXT_CONTEXT_MENU_COMPONENT,
  SEND_CONTEXT_LAUNCHER_COMPONENT,
} from '@craft-ts/component';

const providers = [
  {
    provide: SEND_CONTEXT_LAUNCHER_COMPONENT,
    useValue: MyLauncher,
  },
  {
    provide: SEND_CONTEXT_CONTEXT_MENU_COMPONENT,
    useValue: MyContextMenu,
  },
];

Complete integration example ​

An application can combine the default UI, a protected endpoint, stricter retention, and application-specific event filtering:

ts
import { craftAppConfig } from '@craft-ts/core';
import {
  provideSendContextEventFilter,
  provideSendContextToAi,
  SEND_CONTEXT_RETENTION_POLICY,
} from '@craft-ts/component';

export const appConfig = craftAppConfig({
  providers: [
    provideSendContextToAi({
      endpoint: '/internal/ai/context',
    }),
    {
      provide: SEND_CONTEXT_RETENTION_POLICY,
      useValue: { maxEvents: 250, maxBytes: 1024 * 1024 },
    },
    provideSendContextEventFilter((event) => event.name !== 'healthcheck'),
  ],
});

The server-side endpoint should validate version, authenticate the user, apply any additional server-side redaction, and then forward either prompt or the structured context to the selected agent.