useInterrupt
Vue 3 composable for handling agent interrupt events and resuming execution with user input
Open your coding agent in your project's folder, or in an empty folder for a new app.This runs in a coding agent on your computer.
Overview
useInterrupt is a Vue 3 composable for handling agent interrupts. It supports both the AG-UI standard interrupt flow and the legacy on_interrupt custom-event flow, then exposes the active interrupt as reactive state plus resolve and cancel methods for resuming execution.
By default, the composable publishes its state into CopilotChat so you can render the interrupt through the chat's #interrupt slot. Set renderInChat: false if you want to place the UI yourself.
event.value is typed by the TValue type parameter, which defaults to unknown. Narrow it in your handler and enabled callbacks, or pass an explicit TValue when you call the composable.
resolve and cancel are the primary methods. resolveInterrupt and
cancelInterrupt remain as aliases for back-compat.
Signature
import { useInterrupt } from "@copilotkit/vue/v2";
function useInterrupt<TValue = unknown, TResult = never>(
config?: UseInterruptConfig<TValue, TResult>,
): UseInterruptResult<TValue, TResult>;Parameters
Prop
Type
Return Value
Prop
Type
Usage
In-chat interrupt UI
<script setup lang="ts">
import { CopilotChat, useInterrupt } from "@copilotkit/vue/v2";
interface Approval {
question: string;
}
useInterrupt<Approval>();
</script>
<template>
<CopilotChat>
<template #interrupt="{ event, resolve }">
<div class="rounded border p-3">
<p>{{ (event.value as Approval).question }}</p>
<div class="mt-2 flex gap-2">
<button @click="resolve({ approved: true })">Approve</button>
<button @click="resolve({ approved: false })">Reject</button>
</div>
</div>
</template>
</CopilotChat>
</template>Manual placement with async preprocessing
<script setup lang="ts">
import { useInterrupt } from "@copilotkit/vue/v2";
const { interrupt, result, hasInterrupt, resolve } = useInterrupt<
string,
{ label: string }
>({
renderInChat: false,
enabled: (event) => event.value.startsWith("approval:"),
handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
});
</script>
<template>
<aside v-if="hasInterrupt" class="rounded border p-3">
<div class="font-medium">{{ result?.label ?? "" }}</div>
<div class="mt-2">{{ interrupt?.value }}</div>
<button class="mt-2" @click="resolve({ accepted: true })">Continue</button>
</aside>
</template>Reading reactive state in script
import { useInterrupt } from "@copilotkit/vue/v2";
const { interrupt, hasInterrupt, result, cancel, resolve } = useInterrupt({
handler: ({ event }) => ({ label: String(event.value) }),
});
// `interrupt` and `result` are reactive refs; `hasInterrupt` is a computed.
// Call `resolve(response)` or `cancel()` to address the interrupt.Behavior
- Interrupts are collected from AG-UI standard interrupt events and from legacy
on_interruptcustom events. - Standard interrupts win if both flows appear for the same run.
- The pending interrupt is surfaced when the run finalizes.
- Starting a new run clears pending interrupt state, and a failed run discards the in-flight interrupt.
- When multiple standard interrupts are open,
resolve(payload, interruptId)andcancel(interruptId)accumulate responses until every interrupt is addressed, then resume once. - Omitting
interruptIdwhile multiple standard interrupts are open defaults to the first interrupt and logs a warning. - Expired standard interrupts are not resumed.
resultis inferred from thehandlerreturn type and is alwaysTResult | null.- When
enabledreturnsfalse, the interrupt is ignored:resultandslotPropsstaynulland nothing is published to chat. - With
renderInChatomitted ortrue,slotPropsis published into<CopilotChat>for the#interruptslot. WithrenderInChat: false, no chat publishing occurs and you render from the returned refs. cancel()on legacy interrupts just dismisses without resuming.