102 lines
4.8 KiB
TypeScript
102 lines
4.8 KiB
TypeScript
import { InterruptEvent, InterruptHandlerProps, InterruptRenderProps } from "../types/interrupt.mjs";
|
|
import React from "react";
|
|
|
|
//#region src/hooks/use-interrupt.d.ts
|
|
type InterruptHandlerFn<TValue, TResult> = (props: InterruptHandlerProps<TValue>) => TResult | PromiseLike<TResult>;
|
|
type InterruptResultFromHandler<THandler> = THandler extends ((...args: never[]) => infer TResult) ? TResult extends PromiseLike<infer TResolved> ? TResolved | null : TResult | null : null;
|
|
type InterruptResult<TValue, TResult> = InterruptResultFromHandler<InterruptHandlerFn<TValue, TResult>>;
|
|
type InterruptRenderInChat = boolean | undefined;
|
|
type UseInterruptReturn<TRenderInChat extends InterruptRenderInChat> = TRenderInChat extends false ? React.ReactElement | null : TRenderInChat extends true | undefined ? void : React.ReactElement | null | void;
|
|
/**
|
|
* Configuration options for `useInterrupt`.
|
|
*/
|
|
interface UseInterruptConfigBase<TValue = unknown, TResult = never> {
|
|
/**
|
|
* Render function for the interrupt UI.
|
|
*
|
|
* This is called once an interrupt is finalized and accepted by `enabled` (if provided).
|
|
* Use `resolve` from render props to resume the agent run with user input.
|
|
*/
|
|
render: (props: InterruptRenderProps<TValue, InterruptResult<TValue, TResult>>) => React.ReactElement;
|
|
/**
|
|
* Optional pre-render handler invoked when an interrupt is received.
|
|
*
|
|
* Return either a sync value or an async value to pass into `render` as `result`.
|
|
* Rejecting/throwing falls back to `result = null`.
|
|
*/
|
|
handler?: InterruptHandlerFn<TValue, TResult>;
|
|
/**
|
|
* Optional predicate to filter which interrupts should be handled by this hook.
|
|
* Return `false` to ignore an interrupt.
|
|
*/
|
|
enabled?: (event: InterruptEvent<TValue>) => boolean;
|
|
/** Optional agent id. Defaults to the current configured chat agent. */
|
|
agentId?: string;
|
|
}
|
|
type UseInterruptConfig<TValue = unknown, TResult = never, TRenderInChat extends InterruptRenderInChat = undefined> = UseInterruptConfigBase<TValue, TResult> & {
|
|
/** When true (default), the interrupt UI renders inside `<CopilotChat>` automatically. Set to false to render it yourself. */renderInChat?: TRenderInChat;
|
|
};
|
|
/**
|
|
* Handles agent interrupts (`on_interrupt`) with optional filtering, preprocessing, and resume behavior.
|
|
*
|
|
* The hook listens to custom events on the active agent, stores interrupt payloads per run,
|
|
* and surfaces a render callback once the run finalizes. Call `resolve` from your UI to resume
|
|
* execution with user-provided data.
|
|
*
|
|
* - `renderInChat: true` (default): the element is published into `<CopilotChat>` and this hook returns `void`.
|
|
* - `renderInChat: false`: the hook returns the interrupt element so you can place it anywhere in your component tree.
|
|
*
|
|
* `event.value` is typed as `any` since the interrupt payload shape depends on your agent.
|
|
* Type-narrow it in your callbacks (e.g. `handler`, `enabled`, `render`) as needed.
|
|
*
|
|
* @typeParam TResult - Inferred from `handler` return type. Exposed as `result` in `render`.
|
|
* @param config - Interrupt configuration (renderer, optional handler/filter, and render mode).
|
|
* @returns When `renderInChat` is `false`, returns the interrupt element (or `null` when idle).
|
|
* Otherwise returns `void` and publishes the element into chat. In `render`, `result` is always
|
|
* either the handler's resolved return value or `null` (including when no handler is provided,
|
|
* when filtering skips the interrupt, or when handler execution fails).
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* import { useInterrupt } from "@copilotkitnext/react";
|
|
*
|
|
* function InterruptUI() {
|
|
* useInterrupt({
|
|
* render: ({ event, resolve }) => (
|
|
* <div>
|
|
* <p>{event.value.question}</p>
|
|
* <button onClick={() => resolve({ approved: true })}>Approve</button>
|
|
* <button onClick={() => resolve({ approved: false })}>Reject</button>
|
|
* </div>
|
|
* ),
|
|
* });
|
|
*
|
|
* return null;
|
|
* }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* import { useInterrupt } from "@copilotkitnext/react";
|
|
*
|
|
* function CustomPanel() {
|
|
* const interruptElement = useInterrupt({
|
|
* renderInChat: false,
|
|
* enabled: (event) => event.value.startsWith("approval:"),
|
|
* handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
|
|
* render: ({ event, result, resolve }) => (
|
|
* <aside>
|
|
* <strong>{result?.label ?? ""}</strong>
|
|
* <button onClick={() => resolve({ value: event.value })}>Continue</button>
|
|
* </aside>
|
|
* ),
|
|
* });
|
|
*
|
|
* return <>{interruptElement}</>;
|
|
* }
|
|
* ```
|
|
*/
|
|
declare function useInterrupt<TResult = never, TRenderInChat extends InterruptRenderInChat = undefined>(config: UseInterruptConfig<any, TResult, TRenderInChat>): UseInterruptReturn<TRenderInChat>;
|
|
//#endregion
|
|
export { UseInterruptConfig, useInterrupt };
|
|
//# sourceMappingURL=use-interrupt.d.mts.map
|