Function recipes
A grab-bag of handler patterns. Each is something a real integration tends to need.
Read DOM selection
client.registerFunction({
name: 'get_selection',
description: 'Returns the text the user has currently selected.',
handler: () => window.getSelection()?.toString() ?? '',
});Navigate (Vue Router)
import { useRouter } from 'vue-router';
const router = useRouter();
client.registerFunction({
name: 'navigate',
description: 'Navigates to a named route.',
parameters: {
type: 'object',
properties: {
name: { type: 'string' },
params: { type: 'object' },
},
required: ['name'],
},
handler: async ({ name, params }: { name: string; params?: Record<string, string> }) => {
await router.push({ name, params });
return { ok: true, path: router.currentRoute.value.fullPath };
},
});Fetch from your own API
client.registerFunction({
name: 'find_orders',
description: 'Searches the user\'s orders.',
parameters: {
type: 'object',
properties: {
q: { type: 'string' },
limit: { type: 'number', default: 10 },
},
required: ['q'],
},
handler: async ({ q, limit }: { q: string; limit?: number }) => {
const res = await fetch(`/api/orders?q=${encodeURIComponent(q)}&limit=${limit ?? 10}`);
if (!res.ok) throw new Error(`API error ${res.status}`);
return res.json();
},
});Confirm a destructive action with the user
Mark the function sensitive: true instead of rolling your own confirmation inside the handler. The client then holds execution until your confirmation handler resolves, so the handler only has to do the work:
client.registerFunction({
name: 'delete_account',
description: 'Permanently deletes the current user account.',
sensitive: true,
handler: async () => {
await fetch('/api/me', { method: 'DELETE' });
return { deleted: true };
},
});
client.setConfirmationHandler(async (req) => {
const ok = window.confirm('Really delete your account? This cannot be undone.');
return ok ? 'allow' : 'deny';
});If the user denies (or no handler is set), the call is denied automatically — the autopilot receives a denied function error and handles it conversationally; the handler never runs. This is fail-closed, so sensitive functions never execute silently. See Sensitive functions.
In a Vue app you don't need to write the handler at all — the store registers one that drives the built-in ConfirmationPrompt UI (with "always allow" support). See the store's confirmation API.
Open a Vue dialog and resolve when it closes
import { ref } from 'vue';
const dialogPromise = ref<{ resolve: (v: boolean) => void } | null>(null);
const dialogOpen = ref(false);
const dialogQuestion = ref('');
function openDialog(question: string) {
dialogQuestion.value = question;
dialogOpen.value = true;
return new Promise<boolean>((resolve) => {
dialogPromise.value = { resolve };
});
}
export function resolveDialog(value: boolean) {
dialogOpen.value = false;
dialogPromise.value?.resolve(value);
dialogPromise.value = null;
}
client.registerFunction({
name: 'ask_user',
description: 'Asks the user a yes/no question via a dialog.',
parameters: {
type: 'object',
properties: { question: { type: 'string' } },
required: ['question'],
},
handler: ({ question }: { question: string }) => openDialog(question),
});Return a typed JSON object
The autopilot can read structured returns better than free-form strings. Whenever you can, return an object:
handler: () => ({
status: 'ok',
user: { id: 42, name: 'Mo' },
flags: { admin: true },
});Avoid concatenating into a sentence ("User 42 is admin") — make the autopilot stringify it.
Long-running operation with progress
The function call is awaited synchronously; you cannot stream progress mid-call. Either:
- Return a job ID and register a follow-up function the autopilot can poll, or
- Update context as the work progresses so the autopilot sees state on its next turn:
client.registerFunction({
name: 'start_export',
handler: async () => {
const job = await startExport();
client.updateContext({ export: { id: job.id, status: 'running' } });
job.onProgress((p) => client.updateContext({ export: { id: job.id, status: 'running', progress: p } }));
job.onDone(() => client.updateContext({ export: { id: job.id, status: 'done' } }));
return { id: job.id };
},
});