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_interrupt custom 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) and cancel(interruptId) accumulate responses until every interrupt is addressed, then resume once.
  • Omitting interruptId while multiple standard interrupts are open defaults to the first interrupt and logs a warning.
  • Expired standard interrupts are not resumed.
  • result is inferred from the handler return type and is always TResult | null.
  • When enabled returns false, the interrupt is ignored: result and slotProps stay null and nothing is published to chat.
  • With renderInChat omitted or true, slotProps is published into <CopilotChat> for the #interrupt slot. With renderInChat: false, no chat publishing occurs and you render from the returned refs.
  • cancel() on legacy interrupts just dismisses without resuming.

Related