Primitives
A provider exposes its capabilities through four primitives. Choosing the right one for a given capability is the most important modelling decision you will make, so this page explains what each is for and how they differ.
Actions — do something
Section titled “Actions — do something”An action is a callable capability with a typed input and a typed output. It is the primary primitive: agents call actions, the sandbox calls actions, and the HTTP API exposes actions.
import { defineAction, z } from '@grundlag/core';
const sendMessage = defineAction({ id: 'sendMessage', name: 'Send message', description: 'Sends a message to a recipient.', input: z.object({ recipientId: z.string(), body: z.string().min(1) }), output: z.object({ messageId: z.string() }), execute: async ({ input }) => ({ messageId: await deliver(input) }),});Reach for an action when the caller wants to cause an effect or compute a result. If the verb in the sentence is “send”, “create”, “summarise”, “set”, or “fetch a specific thing”, it is an action. See Define actions.
Entities — find and fetch a resource
Section titled “Entities — find and fetch a resource”An entity type describes a searchable kind of resource the provider owns: contacts,
files, calendar events, notes. It pairs a data schema (which must include a string id)
with two operations — search (paginated, cursor-based) and get (by id).
import { defineEntityType, z } from '@grundlag/core';
const contact = defineEntityType({ id: 'contact', name: 'Contact', description: 'A person in the address book.', data: z.object({ id: z.string(), displayName: z.string() }), searchInput: z.object({ query: z.string().min(1) }), search: async ({ input, limit, cursor }) => findContacts(input.query, limit, cursor), get: async ({ id }) => getContact(id),});Reach for an entity when the caller needs to discover, select, or retrieve a record it can later reference by id. Entities are nouns; actions are verbs. A calendar provider exposes events as an entity (so an agent can find “my meeting with Sam”) and “create event” as an action. See Expose entities.
Events — react to something happening
Section titled “Events — react to something happening”An event is a signal a provider emits that automations subscribe to: a message
arrived, a device changed state. An event carries a typed payload, and a filter /
check pair so a subscriber can express which occurrences it cares about.
import { defineEvent, z } from '@grundlag/core';
const messageReceived = defineEvent({ id: 'messageReceived', name: 'Message received', description: 'Emitted when a new message arrives.', payload: z.object({ messageId: z.string(), senderId: z.string() }), filter: z.object({ senderId: z.string().optional() }), check: ({ payload, input }) => input.senderId === undefined || input.senderId === payload.senderId,});Reach for an event when the provider needs to push a notification that something changed, rather than waiting to be called. Payloads should carry enough ids for the subscriber to decide what to do next — prefer ids over large embedded objects. See Publish events.
Items — put a typed artifact in the transcript
Section titled “Items — put a typed artifact in the transcript”An item is a typed artifact that lives in a conversation transcript: a rendered document, a chart, a UI widget, a structured result. An item type controls whether and how its data is shown to the model.
import { defineItemType, z } from '@grundlag/core';
const chart = defineItemType({ id: 'chart', name: 'Chart', data: z.object({ title: z.string(), series: z.array(z.number()) }), // Omit `agent` entirely to keep an item presentation-only (UI sees it, // the model does not). Provide `agent.render` to control what the model sees. agent: { render: (data) => `Chart "${data.title}" with ${data.series.length} points` },});Reach for an item when a step produces output a human should see in the transcript, that is not itself a tool result the model must reason over. Items are how a provider or agent contributes rich, typed content to the conversation. See Emit items.
Choosing between them
Section titled “Choosing between them”| You want to… | Use |
|---|---|
| Cause an effect or return a computed result | Action |
| Let a caller search for and fetch a resource by id | Entity |
| Notify subscribers that something happened | Event |
| Add a typed artifact to the conversation transcript | Item |
Actions, entities, and events are registered on a ProviderInstance and are the provider’s
public surface. Storage is not a primitive — neither databases (see
Persist state with a database) nor
buckets (see Store blobs in a bucket). Both
are private implementation detail, and you expose behaviour over stored state through the
primitives above — never directly.