Actions and Tools
Actions are the App's behavior boundary. A public Agent Tool, a UI event and an App schedule are three sources of the same request, not three implementations of the product logic.
Define local Actions
async function refresh(args, context) {
const account = String(args.account ?? "").trim();
if (!account) throw new Error("account is required");
const value = await loadProviderState(account);
PocketPi.data.transaction(() => {
saveProviderState(value);
});
return { account, refreshed: true, source: context.source };
}
PocketPi.defineActions({ refresh });The optional second argument contains frozen request context such as{ source: "tool" | "ui" | "schedule" }. Use it for diagnostics or narrowly justified behavior, not to fork the App into separate business systems.
Expose selected Actions as Tools
{
"name": "portfolio.refresh",
"action": "refresh",
"description": "Refresh and persist the selected portfolio.",
"parameters": {
"type": "object",
"properties": {
"account": { "type": "string", "minLength": 1 }
},
"required": ["account"],
"additionalProperties": false
}
}The Tool's name is global and namespaced. Its action is local and must not contain a dot. Installation verifies that every Tool route resolves to an Action registered by actions.js.
Request an Action from the View
View.ActionButton({
label: "REFRESH",
onPress: () => PocketPi.action("refresh", { account: selectedAccount }),
})Returning this event transfers the request to native routing. Do not importactions.js into the View or mutate SQLite in the pointer callback.
Execution envelope and ordering
{"action":"refresh","args":{"account":"..."},"source":"tool|ui|schedule"}One bounded Action queue and one Action runner serve ordinary Apps. Only one Action executes at a time in v1. The Tool call receives one absolute 80-second budget that includes queueing, JavaScript and native transport. Use the remaining value for downstream requests:
const response = await fetch(url, {
timeoutMs: PocketPi.actionContext.remainingMs(),
maxBytes: 96 * 1024,
});Return domain results
Return JSON-serializable values that tell the caller what completed. The framework converts a successful value into Tool result text. Throw an Error for failure; the pending Tool call receives an error result instead of a false empty success.
if (!response.ok) {
throw new Error(`Provider HTTP ${response.status}`);
}
return { refreshedAt, rows: normalizedRows.length };Do not persist everything
A provider response should be returned to the Agent when useful. Persist only normalized state consumed by the App's durable behavior or fixed View. Raw-response caches, generic Tool logs and duplicate details increase memory/storage pressure without strengthening the product model.