BEROUNOS for AI-native devices

Agents & A2A

Specialists inside apps.

An app publishes agents in its manifest. Caroline starts them as tasks, follows them, and relays their questions to the owner. A2A opens them to the outside.

An app can contain specialists: agents that take whole tasks in its field and report back. The app publishes them in its manifest, Caroline starts and follows them, the owner is asked when an agent needs a decision, and A2A lets outside systems call them too.

Declaring an agent

Agents are an array in the same manifest. The manifest is the approval: what an agent declares is what it may do, and nobody clicks anything.

An agent in a manifest (example)
"agents": [{ "id": "planner", "description": "Plans the owner's week from the calendar and the task list", "skills": [{ "name": "plan_week", "description": "Proposes a plan for the coming week", "input": { "focus": "string?" }, "output": "text", "long": true, // takes more than a few seconds: run as a task "confirm": false // no effect outside the app }], "host": { "service": "calendard", "endpoint": "agent/planner" }, "permissions": ["notifications.post", "apps.open"], "camerlengo": ["ai:stt"], // the owner's cloud commands it may call "a2a": { "expose": false } }]
FieldMeaning
descriptionRequired. One line telling Caroline what the agent is for
skillsAt least one: name, description, input (same types as tools; a trailing ? marks an optional parameter), output (text or json), long, confirm
hostWhere the agent answers. Exactly one of: {"service","endpoint"} (an endpoint of the app's service that allows the agent caller), {"mcp": id}, {"page": true} (the app's page over /appws), or {"a2a": card-url, "key": name} for a remote A2A agent
permissionsSystem access beyond the app's own data: files.read, files.write, net.status, net.manage, settings.read, settings.write, notifications.read, notifications.post, sound, vibration, message_box, apps.open. Absent means none
camerlengoThe owner's Camerlengo commands the agent may call through the shell, charged to the owner's wallet. Absent means none
a2aexpose, confirm, tags; see below

How an agent solves a task is its own business. The format has no model field, so an agent may be a script, a service calling an AI model or a remote system.

Tasks and their life

The broker in the shell keeps a registry built from manifests and runs every agent call as a task.

submitted working input-required completed failed canceled a question to the owner is answered an unanswered question fails the task after 10 minutes
A task moves from submitted to working, may pause for the owner, and ends completed, failed or canceled.
  • Questions to the owner. An agent can ask with a set of choices. Caroline relays the question and the answer. An unanswered question fails the task after 600 seconds.
  • The journal records each task, and totals of calls and spend are kept per agent.
  • Caroline's agents group: agent_list, agent_start, agent_status, agent_answer, agent_cancel.
  • Apps' pages may not command agents. Settings shows the list read-only.

Calling the owner's cloud

An agent that needs the owner's cloud service (speech recognition, mail, other AI commands) never holds credentials. It calls the shell, which adds the owner's session and key, runs only the commands the manifest lists, and journals the call: agent, command, outcome, cost. Parameters and results are never written to the journal.

// from the agent's page const answer = await Platform.sw.call('ai:stt', { /* the command's parameters */ }, 'planner'); // the same call over HTTP, as the agent's service makes it curl -XPOST localhost:8765/api/agents/sw \ -H 'X-IntelliCat-Agent: calendar/planner' \ -d '{"command":"ai:stt","params":{...}}'

Errors come back as a kind with a status: network 503, auth 401, balance 402, config 500, service 502. A command that isn't in the manifest answers 403.

A2A: open an agent to the outside

"a2a": {"expose": true} publishes the agent over the open A2A 1.0 protocol. The shell serves an Agent Card and JSON-RPC (SendMessage, SendStreamingMessage, SubscribeToTask, GetTask, CancelTask, ListTasks) on its A2A listener.

# the Agent Card curl http://127.0.0.1:48772/a2a/calendar/planner/.well-known/agent-card.json # a call, with the device key (file agents/a2a.key, mode 600) curl -XPOST http://127.0.0.1:48772/a2a/calendar/planner/ \ -H "Authorization: Bearer $(cat agents/a2a.key)" \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage", "params":{"message":{"role":"ROLE_USER","messageId":"m1", "parts":[{"text":"the launch"}]}}}'
  • Naming the skill. An agent with one skill needs no name: the text goes to its string parameter, as above. With several skills, send a data part, {"data": {"skill": "plan_week", "input": {"focus": "the launch"}}}.
  • Results. A text skill returns its text as an artifact; a json skill returns a data part. A skill marked long is waited for unless the call asks to return at once.
  • Give clients the address with the closing slash. A client that resolves the card path as a URL otherwise drops the last segment.
  • Skills marked confirm are not offered outside unless a2a.confirm is true. An agent whose every skill is marked confirm can't be exposed without it.
  • No port, key or address is ever written in a manifest.

A2A: use someone else's agent

A remote A2A agent can be installed like a local one. Its card gives the skills, and Caroline commands it like any other agent.

"agents": [{ "id": "travel", "description": "Finds and compares trips", "host": { "a2a": "https://agents.example.com/travel/", "key": "travel-key" } }]

key is the name of a key kept in the device's key file, never the key itself. A remote agent has no camerlengo and no permissions, and can't be exposed again. What Caroline gives such an agent leaves the device, so she treats it like any outside party.