Apps that work with their agent
Keep an open app current when an agent changes its data, and pass the user's actions and context back to the chat.
What the app and agent can do together
Show agent changes without a reload. If an agent changes the app from another chat, device, or scheduled action, an open view can update as soon as the change arrives.
Give the next agent turn useful context. The app can tell a connected agent which item the user selected, what the user can see, or which action they just took. This adds context without posting a chat message.
Start a follow-up from the app. A button can send a user message into the connected chat, such as asking the agent to plan work from the project on screen.
Keep the standalone app useful. A direct visit has no connected agent thread. The app can detect that case, keep its normal controls working, and hide actions that need a chat.
Technical
Use the injected window.charming runtime for live updates and host communication. The Browser runtime API defines every method and failure shape.
Copy this prompt for your agent
Make my Charming app work with its connected agent. Read
https://charm.ing/docs/guides/agent-connected-apps.md first. Keep the
open UI current when the agent changes data, and pass useful user
actions or view state back to the agent. Ask me before adding any
button that posts a new message to the chat.
If you haven't built a Charming app before, read
https://charm.ing/docs/build-mcp.md for MCP or
https://charm.ing/docs/build-http.md for HTTP before you start.
Refresh when the agent changes data
Subscribe once after the app renders, and refetch on every event. The event fires after any write to the app’s storage, whether it came from the agent, a Routine, a webhook, or a person in another browser, so this one subscription keeps every open view current. Do not add a polling timer: the runtime keeps reconnecting a dropped or refused stream and sends a reconnect-resync event so the callback can catch up.
const api = window.charming.api('project-board');
async function refresh() {
render(await api.getBoard());
}
void refresh();
const unsubscribe = window.charming.onStateChange(() => refresh());
The event names the operation that wrote, not the keys it changed. If render should update in place, change only the affected DOM nodes: a full rerender can discard text the user is typing, focus, and selection. Keep the unsubscribe function if the UI can remove or replace the subscribed view. The Browser runtime API covers the event fields and how to skip the refetch.
Send context without starting a turn
Use updateContext() for the latest state and recordAction() for a short history of user actions. Both are dropped when no agent host is connected.
function selectProject(project) {
showProject(project);
window.charming.updateContext({
selectedProject: { id: project.id, name: project.name },
});
window.charming.recordAction('selected-project', {
projectId: project.id,
});
}
Send the least context the agent needs. Do not send credentials, private browser state, or data that the next agent turn does not need.
Send an explicit follow-up
Use sendFollowUp() only after a clear user action. It posts a user message and can start a model turn, unlike the silent context methods.
const planButton = document.querySelector('[data-plan-project]');
function syncAgentControls() {
planButton.hidden = !window.charming.isConnected;
}
syncAgentControls();
window.charming.onConnectionChange(syncAgentControls);
planButton.addEventListener('click', () => {
window.charming.sendFollowUp('Plan the next three tasks for the selected project.');
});
onConnectionChange() passes the new boolean value to its callback, but a callback can also read isConnected as shown above. It does not run once at subscription time, so set the initial state yourself.
Open links through the host
Use openLink() for an external HTTP or HTTPS URL. It asks an embedded chat host to open the link and falls back to a safe new tab during direct browsing.
docsButton.addEventListener('click', () => {
window.charming.openLink(project.docsUrl);
});
Contract details
See Live state for event sources, result limits, and reconnect behavior. See Agent host connection for connection state, message rate limits, context merge behavior, and action history.