Agents
Two kinds of agent files. Some teach your coding agent how to build a PurposeOS app. Others describe an agent your app ships to run on the OS.
For your coding agent
Drop these into an app repository. They describe the format that works today, so an agent can scaffold, build and verify an app without guessing.
AGENTS.md available
Repo root of an app
Tells any coding agent how a PurposeOS app is laid out, the rules that fail an install, and how to pack and verify.
# AGENTS.md: building a PurposeOS app
You are helping build a `.papp` for PurposeOS, a local-first Linux OS. Read this before editing.
## Layout
app.json manifest, schema 1. Unknown keys are rejected. Edit with care.
bin/ service binaries (native). Must accept: <exe> serve --socket PATH
ui/ web UI (ui.kind = "web"), plain JS or Dioxus-wasm; QML for action views
assets/ icon (svg)
LICENSE
## Rules you must not break
- App id is [a-z0-9-]. Services are named <id>-<service>. Actions are <id>.* .
- Services talk newline-delimited JSON over a Unix socket. One request per line:
{"id":"1","action":"journal.list","input":{}} -> {"ok":...} or {"error":{"kind":"transient|unavailable|permanent","message":"..."}}
- Every action must be idempotent (calls are retried).
- Never use the network. permissions.network = true is rejected. Never log user text.
- UI code calls services only through window.purpose.call(action, input). No fetch to remote hosts.
- Ask for the narrowest permissions.files grant that works ("ro:~/Documents/x", not "rw:~/").
- Do not add dependencies on pacman packages.
## Commands
purpose-pkg pack . -o dist/<id>-<version>-aarch64.papp --arch aarch64
purpose-pkg install dist/<id>-<version>-aarch64.papp
purpose-pkg verify <id>
## Done means
- `purpose-pkg verify <id>` exits 0.
- Every action in app.json is served by the service (the executor refuses a service that omits one).
- Tests pass for core logic. UI tests use a fake window.purpose.
Download AGENTS.md SKILL.md available
skills/purposeos-app/
A skill with front matter (name, description) so an agent loads it when asked to build or change a PurposeOS app.
---
name: purposeos-app
description: Scaffold, build and package a PurposeOS app (.papp) with an app.json manifest, a native service and a web UI. Use when asked to create or modify a PurposeOS app, service, intent or manifest.
---
# PurposeOS app
1. Pick an id ([a-z0-9-]). Create `app.json` (schema 1) with `ui`, `services`, optional `intents`, and `permissions`.
2. Write the service. Rust: depend on `exec-plugin-service`, call `server::bind` then `server::serve(listener, name, actions, handler, shutdown)`.
Any language works if it speaks the newline-JSON protocol on the socket passed as `serve --socket PATH`.
3. Declare every action in `services[].actions`. Make each idempotent.
4. For voice: add an `intents[]` entry with `phrases`. For dictation into the app use `result: "capture"` with `sink`, `end`, `discard`.
5. UI: `ui/index.html` using `window.purpose.call(action, input)` and `window.purpose.on(event, cb)`.
6. Package: `purpose-pkg pack . -o out.papp --arch aarch64`, then `purpose-pkg install out.papp`.
Pitfalls: unknown manifest keys fail the install; a service that does not serve every declared action is refused;
`permissions.network: true` is rejected; `data/` is reserved in the bundle.
Download SKILL.md This site also publishes /llms.txt, an index of the developer pages for agents that browse.
Agent definitions: an SDK concept draft
An agent is declared the way a service is: a file in the bundle, an allow-list of actions, and phrases for discovery. The executor runs it under your app's identity, so every permission check that applies to your app applies to it.
---
name: summarizer
description: Summarises a journal entry in two sentences. Called by meaning, e.g. "summarise this entry".
model:
kind: local # inference.complete on the on-device engine. Never remote.
size: 8b # 8b | 12b
context: 16384
actions: # the only actions this agent may call; must be within permissions.call
- journal-store/journal.get
- journal-store/journal.update
phrases:
- summarise this entry
- tldr my journal
limits:
max_steps: 4
timeout_ms: 60000
---
You summarise one journal entry. Fetch it with journal.get, write at most two sentences in the
author's own voice, and store them with journal.update as a tag-free summary field. Never quote
more than ten words. If the entry is empty, say so and stop.
Download example Front matter
| Field | Meaning |
|---|---|
name, description | Identity. The description is indexed so the agent can be found by meaning. |
model | kind: local only. size: 8b or 12b. context window. Runs through inferd (inference.complete). |
actions | Allow-list of service/action pairs the agent may call. Must be a subset of permissions.call. Anything else is refused. |
phrases | Voice and search phrases, indexed like intents. |
limits | max_steps and timeout_ms, enforced by the executor. |
"agents": [{ "id": "summarizer", "definition": "agents/summarizer.md" }] Not built yet. Draft: app.json schema 1 rejects an "agents" key today. The proposal adds "agents": [{ "id", "definition" }] in a later schema.
- The agent runs in the executor under the app's own identity, so the same permission checks apply. No network, ever.
- AGENTS.md and SKILL.md work today. They describe the current, shipping format.