Lesson 4 of 6
Build an AI chat
Add AI to an app without API keys or billing code. VibeOS runs the model on the signed-in person's plan, so your job is a good conversation and clear messages when something gets in the way.
What you'll build
You'll need
You'll learn
- How to turn on AI for your app
- How to send a conversation and show the reply
- How to check whether AI is available before people type
- How to turn errors into helpful messages
How AI works in VibeOS
You send a list of messages; you get the reply back as text. Each message has a role: "system" sets the assistant's personality, "user" is the person and "assistant" is earlier replies. The AI doesn't remember anything between calls, so you send the conversation so far every time.
Try it before writing any code
await window.vos.ai.chat([{ role: "user", content: "Say hi in five words" }])Step 1 — Turn on AI
Add the ai permission
In the Info tab, tick AI. Your manifest gets "ai": true. Keep file access with empty folder lists: the chat is saved in the private folder.
var FILE = "chat.json";
var SYSTEM = { role: "system", content: "You are a friendly, concise assistant inside a VibeOS app." };
var dir = null;
var messages = []; // [{ role: "user" | "assistant", content }]
var busy = false;Step 2 — Send a message
Call vos.ai.chat with the conversation
Add the person's message, show "Thinking…", then send the system prompt plus the last 20 turns. Sending only recent turns keeps long chats fast. If the call fails, the question goes back in the text box so nothing is lost.
function send(text) {
if (busy || !text.trim()) return;
busy = true;
messages.push({ role: "user", content: text.trim() });
render(true);
// Send the system prompt plus the most recent turns.
var history = [SYSTEM].concat(messages.slice(-20));
window.vos.ai.chat(history).then(function (reply) {
messages.push({ role: "assistant", content: reply });
showNotice("");
save();
}).catch(function (err) {
// Put the question back so nothing is lost, and explain what happened.
var failed = messages.pop();
$("input").value = failed.content;
showNotice(friendly(err.message));
}).then(function () {
busy = false;
render(false);
});
}Step 3 — Handle limits kindly
Explain what went wrong
AI can be unavailable for ordinary reasons. Errors arrive as plain Error objects, so look at err.message and say what the person can do about it:
| Situation | What to tell people |
|---|---|
| Not signed in | Sign in to VibeOS to use AI. |
| Plan doesn't include AI | AI chat isn't included in your current plan. |
| Out of credits | You're out of AI credits for now. |
| Too many requests | Wait a moment and try again. |
| Offline | Check your connection. |
// Turn SDK error messages into something a person can act on.
// (Errors arrive as plain Error objects: check err.message, not err.code.)
function friendly(message) {
var m = String(message || "");
if (/disabled/i.test(m)) return 'AI is switched off for this app. Add "ai": true to its permissions.';
if (/sign.?in|auth/i.test(m)) return "Sign in to VibeOS to use AI.";
if (/credit|quota/i.test(m)) return "You're out of AI credits for now. They refill with your plan.";
if (/plan|upgrade/i.test(m)) return "AI chat isn't included in your current plan.";
if (/rate|too many/i.test(m)) return "That's a lot of messages. Wait a moment and try again.";
if (/network|fetch|offline/i.test(m)) return "You seem to be offline. Check your connection.";
return "The AI couldn't answer: " + m;
}
function showNotice(text) {
$("notice").textContent = text || "";
$("notice").hidden = !text;
}Check before they type
vos.ai.canUse("chat") answers "can this person use AI right now?" without spending anything. Call it on start so the notice shows before someone writes a long question.
function checkAccess() {
// canUse answers "can this user chat right now?" before they type.
return window.vos.ai.canUse("chat").then(function (res) {
showNotice(res.ok ? "" : (res.message || friendly(res.code)));
});
}Step 4 — Remember the chat
Load on start, save after each reply
The conversation is just a list, so it's saved exactly like the to-do list in Lesson 2: one JSON file in the private folder.
function start() {
window.vos.app.getInfo().then(function (info) {
dir = info.sandboxPath;
return window.vos.fs.readFile(dir, FILE);
}).then(function (text) {
messages = text ? JSON.parse(text) : [];
render(false);
return checkAccess();
}).catch(function (err) { showNotice(friendly(err.message)); });
}chat.json shows in the Files tab.Practice
Give it a personality
Show a solution
var SYSTEM = {
role: "system",
content: "You are a pirate. Answer in one or two sentences, in pirate speak."
};Add quick-start buttons
Each button calls send() with a ready-made question.
Summarise a note from Lesson 3
Documents/Notes and asks the AI for a three-bullet summary.Add "home/Documents/Notes" to read permissions, read a file, then send its text in a user message.
Recap
- Turn on AI with "ai": true. No API keys, and it runs on the person's plan.
- Send the whole recent conversation each time; the AI doesn't remember.
- Use canUse() on start and explain errors in plain words.
- Save the conversation as JSON in the private folder.
Full details on limits and messages are in the vos.ai reference.