Browse documentation

Start here

OverviewGetting startedThe mental model

Use the runtime

Run the simulatorPi Agent and workspaceInstall and manage AppsESP32-P4 reference targetESP32-S3 supported target

Build Apps

App developer guideBuild your first AppApp source and packageData and migrationsActions and ToolsView and interactionNetworking and native servicesApp resourcesSchedulesPackage and updateTesting and debugging

Understand the runtime

Runtime flowGuests and lifecycleLayers and ownershipHarness boundary

Security

Trust and capabilitiesData isolationLifecycle and recovery

Reference

App manifestPocketPi APIView APICLI referenceLimits and compatibility

Examples

Exa App walkthroughRobinhood App walkthrough

Project

Current boundariesValidation status

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.