BuildersPut it in your productLet the agent act on your page
Let the agent act on your page
Lets an agent call functions in your page, tells it what the user is looking at, and fills in values the model must never choose.
Let the billing-help agent open an invoice in our app, and tell it which invoice the user is looking at. Check the user may see the invoice before opening it.The agent's turn loop runs in the page, so it can call JavaScript you define there, with no round trip to Mindset. Three commands connect your app to the agent, on both the mindset-agent element and the UI-less client:
| Command | What it does |
|---|---|
setPageTools(tools) | Functions in your page the agent may call |
setSituationalAwareness(entries) | Facts about what the user is looking at, as a { key: value } map of JSON values, added to the agent's context on every turn |
setPassthroughParams(params) | Values a tool needs that the model must never see |
Each replaces its whole set, so pass the full picture each time. Passing a shorter set is how you remove an entry. Each is read fresh on every turn. On the element, configure() also takes them as pageTools, situationalAwareness and passthroughParams.
Page tools#
<script src="https://eu.mindset.ai/sdk/mindset-agent.js"></script>
<mindset-agent agent="billing-help"></mindset-agent>
<script>
const agent = document.querySelector("mindset-agent");
agent.configure({
getSession: async () => {
const r = await fetch("/api/session");
if (!r.ok) throw new Error("Could not start a session");
return r.json();
},
});
agent.setPageTools([
{
name: "open_invoice",
description: "Open one of the signed-in user's invoices in the app.",
jsonSchema: {
type: "object",
properties: { invoiceId: { type: "string" } },
required: ["invoiceId"],
},
runningDescription: "Opening the invoice",
completedDescription: "Opened the invoice",
execute: async (args) => {
const id = typeof args.invoiceId === "string" ? args.invoiceId : "";
if (!/^inv_[a-z0-9]+$/.test(id)) return "That is not a valid invoice ID.";
const r = await fetch(`/api/invoices/${id}`); // your backend checks this user may see it
if (!r.ok) throw new Error("That invoice can't be opened.");
window.location.hash = `#/invoices/${id}`;
return { opened: id };
},
},
]);
</script>On the UI-less client, call chat.setPageTools([...]) with the same array.
| Field | What it does |
|---|---|
name, description, jsonSchema | What the model reads to decide when to call the tool and with what arguments |
execute(args) | Runs in your page with the arguments the model chose. May be async |
runningDescription, completedDescription | Optional. What the element's activity row reads while the tool runs and when it's done. A string, or one per language, such as { en: "Saving", fr: "Enregistrement" }. Never sent to the model |
What execute returns goes back to the agent as the tool's result. A string goes as it is, undefined or null reads as {"success":true}, and anything else as its JSON. If execute throws, the agent is told the tool failed, with your error's message (up to 500 characters) and never its stack.
Tools written for Mindset 3, as { name, description, parameters, handler }, work unchanged.
Page tool safety#
The model decides whether to call your tool and with what arguments, and anything in its context can influence it, including knowledge sources and other tools' output.
jsonSchemais mostly guidance. The SDK checks one thing beforeexecuteruns: a call missing an argument your schema lists asrequiredis refused, and the model is told why. A value supplied through pass-through counts. Types, formats and allowed values aren't checked, so validate them yourself.- Mindset can't re-authorize a page tool. It runs in your page, so nothing checks that this user may take that action when the handler runs.
The org API key never reaches the browser, and tools that run on Mindset's side still authorize there.
Situational awareness#
chat.setSituationalAwareness({
page: "Invoice detail",
invoiceId: "inv_4471",
invoiceStatus: "overdue",
});Call it whenever the view changes. The agent treats it as context only, and it isn't a security boundary. What the agent may reach is still decided by Mindset.
Pass-through values#
chat.setPassthroughParams({
"lookup_account.tenantId": "acme-eu",
region: "eu-west-1",
});A "tool_name.param" key scopes a value to one tool. A plain param key goes to every page tool, and to each tool that runs on Mindset's side and declares that parameter. When both name the same parameter, the tool_name.param key wins. Mindset removes the parameter from the schema the model sees, and your value is added to the tool's arguments when the tool is called. The model can't see or choose it.
The turn loop runs in the page, so pass-through values sit in the browser, where the user and every script on the page can read them. They also reach the model if a tool puts them in its result. Use pass-through to stop the model choosing a value.