Functions
defineFunctions is the only way a capability enters the sandbox. Each
entry is the real function, which runs in your page, plus its policy.
import { defineFunctions, fromObject, describe } from '@faustine-ai/browser-sandbox';
const functions = defineFunctions({
'orders.list': (input) => api.orders.list(input), // plain
'orders.cancel': { call: (input) => api.orders.cancel(input), // with a policy
approval: true, description: 'Cancels an order' },
'util.sum': { call: (...nums) => nums.reduce((a, b) => a + b, 0),
positional: true }, // code calls sum(1, 2, 3)
help: (topic) => docs.lookup(topic),
});- A dotted name becomes a path in the tree:
sdk.orders.list(input). Segments are identifiers;__proto__,constructorandprototypeare refused. - A function receives one argument (whatever the code passed) unless
positional: true, in which case it receives the arguments spread. - A name can be both a leaf and a branch (
helpandhelp.topics): it is a function with properties. approval: trueholds the call for a human (see Approval).descriptionandinput(a JSON Schema) are used only bydescribe.
fromObject builds the map from an existing object by listing names,
never by enumerating the object:
const functions = fromObject(client, {
names: ['orders.list', 'orders.cancel', 'customers.get'],
approval: (name) => name.endsWith('.cancel'),
descriptions: (name) => catalog[name]?.summary,
extra: { help: (topic) => docs.lookup(topic) },
});Paths are resolved at call time with the right this, so lazily built
clients work; a missing path throws UnknownFunction into the code.
For the system prompt, describe prints the names exactly as the code will
use them:
describe(functions); // - sdk.orders.list(input)
// - sdk.orders.cancel(input) (requires approval) — Cancels an order
describe(functions, { format: 'ts' }); // declare const sdk: { orders: { list(input?: unknown): Promise<unknown>; … } };