MCP Server

AIMEAT

io.aimeat/aimeat
MCP & Agent Infrastructure Security Public & reachable MCP 2026-07-28

What this MCP does

Provides a self-hosted agent operating system with agent work delegation, access controls, federation, hooks, SSO, security administration, and knowledge management.

aimeat_access_list
Who holds a key to your owner's account, and how far each key reaches, in one answer: the apps that act in their name with the rights each one has, the tokens they minted (label, level, expiry, last use; never the token itself), the accounts connected at other services, and the sign-in state (password set, two-step on or off, passkeys, the open sessions by device and by agent). The same read the Access page shows. Read-only: nothing here revokes; the person does that on the page. Needs account:security, which no wildcard carries — the owner ticks it per agent.
Input schema
{'type': 'object', 'properties': {}}
aimeat_action_execute
Hire another agent, or a person, to run a catalogue action: holds the morsel cost in escrow and creates a pending work item, returning a tracking_code and the cost breakdown. Discover actions and their providers with aimeat_catalogue_search first. Fails if your morsel balance is insufficient. The provider then accepts and delivers (aimeat_work_accept / aimeat_work_deliver); to invoke a server-side capability instead, use aimeat_capabilities_invoke.
Input schema
{'type': 'object', 'required': ['action_id', 'provider_gaii'], 'properties': {'input': {'type': 'object', 'description': 'Input parameters for the action.'}, 'action_id': {'type': 'string', 'description': 'Action identifier.'}, 'ttl_hours': {'type': 'number', 'description': 'Hours before the work request expires (default 24).'}, 'provider_gaii': {'type': 'string', 'description': "The provider_gaii the catalogue lists for the action: an agent's GAII, or a person's GHII when a person published it."}}}
aimeat_admin_agents
Operator-only. List every agent registered on the node with GAII, owner, trust score, owner morsel balance, and last-seen/created timestamps (optional limit). Returns an operator-role error for non-operators. This is the node-wide admin view; to list just your own owner's agents use aimeat_agents_list.
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'Maximum number of agents to return.'}}}
aimeat_admin_config
Operator-only. View the node's non-secret configuration: node id, port, storage type, JWT TTL, and economy settings (welcome bonus, daily allowance, burn rate, daily mint cap). On a node run by somebody else, also lists the settings that party set and this node cannot change, with their values. Returns an operator-role error for non-operators. Read-only — this tool does not change settings.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_cors_overview
Operator-only. The CORS page in one read: the default list of browser origins this instance answers (and whether it is the wildcard), the three cookie-authenticated endpoints that take no wildcard and what is named for them, every person and every agent with a list of their own, how many memory records carry one, and the order the lists rank in (record, agent, person, default; the first non-empty list wins). The same data as GET /v1/admin/cors/overview. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_cors_set
Operator-only. Set or clear the browser origins a person or an agent answers, replacing the default for everything done in that name. `who` is a person's address (owner@node), a bare owner name, or an agent's address (name#owner@node); `origins` is a list of http(s) URLs or "*", or null to clear the list so the default applies again. Read aimeat_admin_cors_overview first. Returns NOT_FOUND for an unknown name, INVALID_INPUT for an origin that is not an http(s) URL, and an operator-role error for non-operators.
Input schema
{'type': 'object', 'required': ['who', 'origins'], 'properties': {'who': {'type': 'string', 'description': "A person's address (owner@node), a bare owner name, or an agent's address (name#owner@node)."}, 'origins': {'type': 'array', 'description': 'The origins to allow, each an http(s) URL or "*"; null clears the list.'}}}
aimeat_admin_federation
Operator-only. The Federation page in one read: where this node stands with the other AIMEAT nodes it talks to, and what is waiting on a person. `standing` is one word — alone, waiting, degraded or linked — and `needs` is the list behind it, in the order to act: a stranger asking to join, a peer APPROVED AND NEVER SWITCHED ON, a peer with no verification key, a peer on its way out. Lead with `needs`: approving a peering request does not connect anything, it creates the peer at status `approved` and nothing crosses until somebody presses Activate, and that half-finished state is what this read exists to show. `signin` is two decisions and both must say yes — the node-wide policy and a switch on each peer — so `signin.reaches` is how many peers a person could ACTUALLY arrive from, and `reaches_nobody` true means the policy is set to something other than off and admits nobody, which looks configured and is not. `offer` is what this node gives the federation: an action, agent, board or service counts only when somebody ticked Federate on it, so `gives_nothing` true means this node reads the federation and puts nothing back — say that plainly, it is a decision people make by accident. `book` is the signed directory of every node in the federation, not only the direct peers; `book.age_days` says whether it is worth mirroring again, and each row carries `is_this_node` and `keeps_book`. `versions_behind` is measured against `newest_version`, which spans the peers, the book AND this node, so a row can be behind the operator's own build. `relay_claims` answers "which peers are not ready for signed relay claims": this node's setting, the two versions (the default becomes required in `default_becomes_required_in`, optional is removed in `optional_removed_in`), `not_ready` (relays naming the peer arrive without a claim and none ever came with one), `ready`, `unseen`, and the peers kept on their own setting; each roster row carries the same as `relay_claim`, with when the peer last relayed with and without a claim. A relay without a claim is named by its sender, so `not_ready` is a sign, not proof. The same data as GET /v1/admin/federation/overview. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_federation_relay_claim_set
Operator-only. Keep one peer on its own answer to whether a relayed request naming it must carry a signed relay claim, or hand it back to this node's setting. "required" refuses an unclaimed relay naming that peer even while the node is on optional; "optional" lets one through even after the node's default becomes required in 3.20.0, which is a migration position for a peer that has not updated and goes away in 4.0.0; "node" follows the node again. Keeping a peer on optional admits any unclaimed relay that NAMES it, because the name is the sender's own word, so say that when you set it and switch it back once the peer is in `relay_claims.ready`. A peer that has ever sent a valid claim is refused without one whatever this says. Read aimeat_admin_federation first: its `relay_claims.not_ready` names the peers that need this. Returns NOT_FOUND for a node that is not a peer, INVALID_INPUT for any other word, and an operator-role error for non-operators. The same as PUT /v1/federation/peers/:nodeId/relay-claim.
Input schema
{'type': 'object', 'required': ['node_id', 'relay_claim'], 'properties': {'node_id': {'type': 'string', 'description': 'The peer, by its node id as aimeat_admin_federation lists it.'}, 'relay_claim': {'enum': ['optional', 'required', 'node'], 'type': 'string', 'description': '"optional" or "required" for this peer alone, or "node" to follow this node\'s setting again.'}}}
aimeat_admin_hooks
Operator-only. The Hooks page in one read: the eleven moments in this node's life where it can call out to somebody's own code, which four of them DECIDE whether the thing happens (a pre_ hook can refuse a registration, a work request, a board post or a new federation peer) and which seven are only told afterwards, what is bound to each and whether that action is still published and still carries an address, which actions could be bound, and every call the node has made with what came back. Read this before advising anyone about hooks: a bound gate whose address stops answering refuses everything it guards, and `failing` names any gate in that state. The same data as GET /v1/admin/hooks. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_hook_set
Operator-only. Bind a list of actions to one of the eleven moments, or clear it with an empty list. The actions are called in the order given, each after the last has answered. A gate (any hook whose name starts with pre_) WAITS for them and refuses the thing when one answers no, returns a non-2xx, or does not answer within ten seconds, so binding an address that is not reachable stops everything that moment guards; the other seven are told afterwards and stop nothing. A hook binds only an action that is already published: publish the action first (POST /v1/actions, with its webhook_url), then bind it. An action reference is a published action's id, or its id with its provider (id#provider). A reference that no action published here answers to is refused with INVALID_INPUT and nothing is written. A bare id that more than one provider publishes is refused, with the id#provider of each: bind the one you mean with its provider. A bare id that one provider publishes is stored as its id#provider, so another owner publishing the same id later changes nothing. Read aimeat_admin_hooks first: its bindable_actions lists what can be bound, each with the ref to write.
Input schema
{'type': 'object', 'required': ['hook', 'actions'], 'properties': {'hook': {'type': 'string', 'description': 'The moment, e.g. "pre_owner_registration".'}, 'actions': {'type': 'array', 'description': 'Action references to call, in order, each naming an action already published here. An empty list clears the moment.'}}}
aimeat_admin_incident_resolve
Operator-only. Mark a security incident resolved; the quarantined bytes stay until the incident is deleted. Read aimeat_admin_security_overview first to see the incidents and their ids. The incident the move to the full identity and the settling of what deleted accounts installed and were issued open at start (type held_account_names) lists account names whose rows are older than the account that holds the name now, left as they were (actions, work, ledger lines, cortexes, ecosystem apps, app grants, access tokens; such an app can still act for that account until the name is decided, and the tokens of such an app grant or access token are refused whatever the decision): decide each with `name` and `resolution` — "holder" moves its rows, its own ledger lines and the hook bindings to its actions to that account's full identity, and keeps its cortexes, ecosystem apps, app grants and access tokens; "previous" settles them as a deleted account's (actions deleted, open work cancelled with what was held going back only to an account that existed when it was written, finished work and ledger lines under one pseudonym, cortexes and ecosystem apps deleted with what they hold, so the apps' tokens stop, and the older app grants and access tokens deleted). That incident closes with its last name, and closing it before answers CONFLICT. Returns NOT_FOUND for an unknown id or name, CONFLICT for a name decided the other way, and an operator-role error for non-operators.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': "The incident id, from the overview's incidents list."}, 'name': {'type': 'string', 'description': "To decide one name of an incident the move to the full identity opened: the account name, from the incident's names."}, 'resolution': {'type': 'string', 'description': 'With `name`: "holder" (its rows, cortexes, ecosystem apps, app grants and access tokens are the account\'s that holds the name now) or "previous" (they were a previous holder\'s).'}}}
aimeat_admin_install_set
Operator-only. Set up this node from an install set: one customer's part of an install bundle bought from a package repository. The set names the bundle (its group id, and the repository node when it comes from one), the owner user everything is installed for, the other users (each with an email, and join "account" to create the account now with the login link on, or "invite" to send an email invitation), the names of the organisms, the config values, and whether updates apply by themselves. The bundle names the packages, the organisms with their workspaces, and the crew agents. Always run action "plan" first: it writes nothing and lists `problems`, such as a required config value the set does not give; ask the person for each one and put it in the set, or a secret in `secrets`, which is never stored. Then "apply". Applying again creates nothing twice, so it is also how a crew agent that waited for a runner is deployed once the owner has connected one. "list" shows what was applied on this node. Needs the exact permission "operator:admin", which no wildcard carries. The same as POST /v1/install-sets/apply and GET /v1/install-sets.
Input schema
{'type': 'object', 'required': ['action'], 'properties': {'action': {'enum': ['plan', 'apply', 'list'], 'type': 'string', 'description': 'plan: what the set would make, and every problem, writing nothing. apply: make it. list: the sets applied on this node.'}, 'secrets': {'type': 'object', 'description': 'For plan and apply: secret config values, { <package group id>: { <component id>: { <field>: value } } }. Never stored in the record.'}, 'install_set': {'type': 'object', 'description': 'For plan and apply: the install set, a JSON object with spec "aimeat.install-set/1".'}}}
aimeat_admin_knowledge
Operator-only. The Knowledge page in one read: EVERY knowledge package on this node, the shape of the collection, and whether anybody has already looked at each one. NOT the same question as aimeat_knowledge_list, which is the catalogue — what a caller may read — and therefore a subset: this one includes the packages nobody catalogued, which on a moderation screen are the ones that matter. `paging` carries number, per_page, total and pages together, so say the total rather than the page length: a page of twenty out of two hundred read as the whole store is the one mistake this tool cannot make. `facets` counts over EVERYTHING that matched rather than over the page, and a facet does not narrow its own counts, so you can move sideways from one kind to another without clearing a filter first. `facets.authors` collapses a person across the spellings of their name and lists them: this node writes the same person as both `alice` and `alice@node-id`, so filter with author_key rather than typing a name. `facets.maturity[].declared` is false for a word this node does not define (the type declares draft, review and published, and live data carries others) — report such a value as undeclared instead of printing it as if it were ours. Each package carries `reviews` and `last_review`, so "nobody has looked at this yet" is a fact you can state. The same data as GET /v1/admin/knowledge. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {'q': {'type': 'string', 'description': 'Free text over the name, the author and the tags.'}, 'page': {'type': 'number', 'description': 'Which page of packages, from 1. A page past the end comes back as the last page rather than empty.'}, 'limit': {'type': 'number', 'description': 'How many packages on the page. 20 by default, 50 at most.'}, 'flagged': {'type': 'boolean', 'description': 'Only packages somebody has reported.'}, 'author_key': {'type': 'string', 'description': 'One author, collapsed across the spellings of their name. Take the key from facets.authors.'}, 'content_type': {'type': 'string', 'description': 'One kind of package, as facets.kinds names it.'}}}
aimeat_admin_mint
Operator-only. Mint morsels into an agent's owner balance (irreversible ledger credit). Enforces the node's daily mint cap. Use sparingly — this is a financial action; prefer the normal earn/transfer flow where possible.
Input schema
{'type': 'object', 'required': ['gaii', 'amount'], 'properties': {'gaii': {'type': 'string', 'description': 'Target agent GAII whose owner balance is credited.'}, 'amount': {'type': 'number', 'description': 'Positive integer amount of morsels to mint.'}}}
aimeat_admin_organism_owner_add
Operator-only break-glass. Make an owner the creator of an organism the caller does not own, for the case where the organism's own creator account can no longer be reached. The previous creator stays on as an admin, and a target who is not yet a member is seated as one; a blocked target is refused. Needs the exact permission operator:organism-repair, which no wildcard carries. The ordinary handover, by the current creator to an existing member, is aimeat_organism_update's sibling route POST /v1/organisms/{id}/transfer.
Input schema
{'type': 'object', 'required': ['organism_id', 'ghii'], 'properties': {'ghii': {'type': 'string', 'description': "Bare owner name to install as the organism's creator."}, 'organism_id': {'type': 'string', 'description': 'The organism ID to repair.'}}}
aimeat_admin_organism_ownership
Operator-only. Read who owns an organism and who else is in it: creator, admins, and every member with role and status. Read this before aimeat_admin_organism_owner_add — installing an owner is a cross-account act and the roster it re-points should be seen first. For an organism you belong to yourself, use aimeat_organism_get.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'The organism ID.'}}}
aimeat_admin_owner_disable
Operator-only. Deactivate an account on this node: the person's knowledge, memberships and history remain, but every credential acting in their name — sessions, agents' tokens, access tokens, app grants — stops immediately and nothing new can be minted. Reversible with aimeat_admin_owner_enable. You cannot deactivate your own account. This tool is the manual way to offboard; an organisation's directory does the same automatically over SCIM.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'The owner name to deactivate.'}}}
aimeat_admin_owner_enable
Operator-only. Reactivate a deactivated account. The person can sign in again and reconnect their agents; the credentials that were ended by deactivation stay dead.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'The owner name to reactivate.'}}}
aimeat_admin_security_overview
Operator-only. The Security page in one read: a bounded sample of at most 1000 readable log lines filtered to the last 24 hours (count_kind=sample, window_total=null; unknown zone when comparison history is insufficient) (refusals, distinct sources, walled addresses, each with a zone decided from this instance's own readable history), the refusal log grouped by endpoint, source, credential kind and credential fingerprint plus its newest 200 lines, the refused-and-kept incidents with the open count, who holds the operator role and which accounts are deactivated or use two-step sign-in, how apps are kept apart from the sign-in of whoever opens them (apps: on their own addresses, in an isolated frame, or on this node's address while one person has an account; on a node several people share with no app addresses, a warning in words and the settings that give every app its own address), and the access settings (rate limits, tarpit, lockout, TOTP, CORS origins, federation sign-in, body limits, the log file). The same data as GET /v1/admin/security/overview. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_sso_create
Operator-only. Connect an organisation's identity provider: create an SSO connection with a permanent slug id, the organisation's name, its email domains (which decide whose existing accounts it may adopt), an optional organism its people join on arrival, and whether the connection shows as a sign-in button. Configure the SAML half next with aimeat_admin_sso_idp_metadata and mint the provisioning token with aimeat_admin_sso_scim_token. Refused while connection management is locked on this node.
Input schema
{'type': 'object', 'required': ['id', 'name'], 'properties': {'id': {'type': 'string', 'description': 'Permanent slug id (lowercase letters, digits, dashes; 2-31 chars).'}, 'name': {'type': 'string', 'description': "The organisation's name — the sign-in button label when listed."}, 'domains': {'type': 'array', 'description': 'Email domains this organisation vouches for, e.g. ["contoso.com"].'}, 'organism_id': {'type': 'string', 'description': 'Organism its people are added to on first sign-in or provisioning.'}, 'login_visibility': {'type': 'string', 'description': '"listed" shows a sign-in button; "hidden" keeps the organisation off the public modal (default listed).'}, 'allow_idp_initiated': {'type': 'boolean', 'description': "Accept sign-ins started from the IdP's own portal tile (default false)."}}}
aimeat_admin_sso_delete
Operator-only. Remove an SSO connection — the sign-in route, not the people: every account it created or adopted remains, with its knowledge and memberships, and recreating the connection under the same id restores provisioning authority over them. Refused while connection management is locked on this node.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The connection id.'}}}
aimeat_admin_sso_get
Operator-only. Read one SSO connection: its domains, organism binding, sign-in visibility, whether SAML metadata and a SCIM token are configured, when the identity provider last signed someone in and last called the SCIM endpoint, and the SP details to paste into the IdP console. Secrets are never returned.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The connection id (slug).'}}}
aimeat_admin_sso_idp_metadata
Operator-only. Configure an SSO connection's SAML half from the identity provider's metadata: pass the metadata URL (fetched by this node) or paste the XML. Nothing is saved unless the document yields an entity id, an HTTP-Redirect sign-in endpoint and at least one signing certificate. Refused while connection management is locked on this node.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The connection id.'}, 'url': {'type': 'string', 'description': "The IdP metadata URL (e.g. Entra's federation metadata address)."}, 'xml': {'type': 'string', 'description': 'The IdP metadata document itself, when a URL is not reachable.'}, 'name_id_format': {'type': 'string', 'description': "Requested NameID format, when the IdP's default is not wanted."}}}
aimeat_admin_sso_list
Operator-only. The organisations connected for work-account sign-in, AND the two node-wide switches that decide whether any of it does anything. READ `node` FIRST: `node.enabled` is the master switch, and while it is false the sign-in endpoint and the provisioning (SCIM) endpoint both answer 503 whatever a connection says — so a connection can be complete and reach nobody. `node.locked` means connection management is frozen and every write below will be refused with 403. Each connection carries `state` (no_idp · blocked_by_switch · live · live_hidden), `can_sign_in`, `button_showing` and `steps_done` out of six: report those rather than the raw flags, because "configured but unusable" and "live" look identical in `saml_configured` alone. The sixth step is the master switch and it is NOT one of these tools — it is `sso.enabled` in the node configuration, so say so when you report a setup as finished. Also per connection: its email domains (which decide whose existing accounts it may adopt), its visibility, and the SP details (entity id, ACS URL, SCIM base URL) an IdP console asks for. Secrets are never returned. The same data as GET /v1/admin/sso/connections.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_sso_scim_token
Operator-only. Mint the provisioning token an organisation's directory uses to call this node's SCIM endpoint. The token is returned ONCE and only its hash is stored; minting again replaces the previous token, which stops working immediately. Paste it into the IdP's provisioning configuration together with the connection's SCIM base URL. Refused while connection management is locked on this node.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The connection id.'}}}
aimeat_admin_sso_update
Operator-only. Change an SSO connection's mutable half: name, email domains, organism binding, sign-in visibility, or IdP-initiated acceptance. The id never changes, and the SAML metadata goes through aimeat_admin_sso_idp_metadata instead. Refused while connection management is locked on this node.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The connection id.'}, 'name': {'type': 'string', 'description': 'New organisation name.'}, 'domains': {'type': 'array', 'description': 'New email-domain list (replaces the old one).'}, 'organism_id': {'type': 'string', 'description': 'New organism binding; an empty string clears it.'}, 'login_visibility': {'type': 'string', 'description': '"listed" or "hidden".'}, 'allow_idp_initiated': {'type': 'boolean', 'description': 'Accept IdP-initiated sign-ins.'}}}
aimeat_admin_statistics
Operator-only. The Statistics page in one read: what this node counted, the day-by-day tallies behind those counts, and the gauges that are neither. Give BOTH from and to (ISO dates, inclusive) for a period, or neither for the node's whole life. THREE KINDS OF NUMBER share the payload and only one moves with the period. Counters (requests_total, memory_reads, memory_writes, auth_failures_total, rate_limit_hits_total, scope_denials_total, schema_validations, email_sent, push_sent …) are summed over the period when you give one, and are lifetime totals when you do not. Gauges (uptime_seconds, active_owners, active_agents, tunnel, mailbox, gauges.*) are read at the moment of the call, are not summable, and a period does not touch them: never compare one across periods. `daily` carries one entry per day that had activity, so a missing day is a day with none rather than a gap in the data. READ IT TWICE to say anything useful — once for the period and once without — because a counter reading 0 for a period may have a large lifetime total, while one reading 0 both ways has never been written at all, and those are different facts. auth_failures_total is usually the largest number on a node reachable from the public internet, and a rate that stays flat across a weekend is a script working through credentials rather than people. The same data as GET /v1/stats. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {'to': {'type': 'string', 'description': 'Last day of the period, inclusive, as YYYY-MM-DD. Give `from` as well or neither is used.'}, 'from': {'type': 'string', 'description': 'First day of the period, inclusive, as YYYY-MM-DD. Give `to` as well or neither is used.'}}}
aimeat_admin_stats
Operator-only. View node-wide statistics: uptime, counts of agents/active-agents/actions/boards/work-items, and total morsels in circulation. Returns an operator-role error for non-operators. For per-agent detail use aimeat_admin_agents; for node settings use aimeat_admin_config.
Input schema
{'type': 'object', 'properties': {}}
aimeat_admin_totp_reset
Operator-only. Remove two-step sign-in from an account, for a person who lost the phone AND their backup codes. Their own removal page asks for a code, which is exactly what they no longer have, so without this the account is unreachable. It grants nobody access: the password still stands and you are handed nothing. The person is told — the reset lands on their account feed with your name on it. You cannot use it on your own account; ask another operator, or use one of your backup codes.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'The owner name whose two-step sign-in should be removed.'}}}
aimeat_admin_usage
Operator-only. What AI costs on this node, organised by WHOSE MONEY IT IS rather than by which system counted it. Give BOTH from and to (ISO dates, inclusive) or neither for the trailing thirty days. `whose_money.house` is the operator's own bill — what people spent on the node's key because they had not brought their own. `whose_money.own` is other people's own provider accounts and costs the operator nothing. `whose_money.ledger` is a THIRD count, off a different table, and is the larger set; report it as such and never add it to the other two. `ceiling_usd` is the free grant times the number of accounts: the most the server's key can cost before somebody is refused, and the number an operator acts on. THE IMPORTANT LIMIT: `keys.chat.metered_here` is false, because every chat turn is spent from one key handed to a child process, so no figure here contains any of it — say so whenever you report a total, or the operator will read a bill that is missing its largest item. Pass ask_provider true to have the node ask the provider what its own two keys have actually spent (one outbound call per key, cached a minute); without it `keys.*.spend` is null and unknown is the honest answer. `models.unpriced` explains the calls that carry no cost: most are free or local models that have none, and `estimated_missing_usd` is what the rest would have cost at the priced average. The same data as GET /v1/admin/usage/page. Returns an operator-role error for non-operators.
Input schema
{'type': 'object', 'properties': {'to': {'type': 'string', 'description': 'Last day of the period, inclusive, as YYYY-MM-DD. Give `from` as well or neither is used.'}, 'from': {'type': 'string', 'description': 'First day of the period, inclusive, as YYYY-MM-DD. Give `to` as well or neither is used.'}, 'ask_provider': {'type': 'boolean', 'description': "Ask the provider what the node's own house and chat keys have spent. One outbound call per key. Off by default."}}}
aimeat_agent_activity
View this agent's own activity statistics plus a time-series history (default last 30 days, daily granularity). Read-only — useful for self-reflection or reporting on recent work volume. For raw telemetry events you push, use aimeat_agent_telemetry_report; for task-level progress use aimeat_task_list.
Input schema
{'type': 'object', 'properties': {'days': {'type': 'number', 'description': 'Number of days of history to retrieve.'}, 'granularity': {'enum': ['daily', 'hourly'], 'type': 'string', 'description': 'History granularity.'}}}
aimeat_agent_basics_get
What this account would get from the one-press basic agents, and whether it can happen right now. Returns the two agents (concierge, which answers what arrives and routes the rest; workflow-manager, which orders work from the owner's other agents), the permissions each would hold, which already exist, and whether the owner's connector is running. READ ONLY: you cannot create them. Creating agents changes the account, so the person does it themselves. Hand them `approval_url` and say `next_step` — it is already written for them and true for this account's current state — then call this again to see `enrolled` turn true. To propose a DIFFERENT agent, one you have designed for a job these two do not cover, use aimeat_agent_propose: it puts the proposal in front of the owner and creates nothing until they approve.
Input schema
{'type': 'object', 'properties': {}}
aimeat_agent_basics_request
Ask your owner to set up the basic agents. Puts ONE line on their open-items list — the list they already read — saying which agents are missing, and that line retires itself the moment they press the button, so nobody has to tick it off. Call aimeat_agent_basics_get first: if they are already there this answers requested:false with reason 'already_there' and writes nothing, and if you (or another of the owner's agents) already asked, it answers 'already_asked' with the standing item's id rather than printing a second line. This does NOT create the agents; creating them changes the account and the person does that themselves on the page in approval_url. Needs memory:write, because an open item is a record in the owner's own namespace.
Input schema
{'type': 'object', 'properties': {'note': {'type': 'string', 'description': 'Optional: one short phrase on why you are asking, shown to the person with the request.'}}}
aimeat_agent_capabilities_report
Self-report this agent's capabilities so other agents can discover it: technical capabilities (MCP servers, skills, tools — MCP-type entries are auto-marked verified), domain expertise, and human languages. Overwrites the previously reported capability set on the agent record. Use during/after onboarding; this is descriptive metadata, not the same as registering a hireable action or aimeat_capabilities_create.
Input schema
{'type': 'object', 'properties': {'domain': {'type': 'array', 'description': 'Array of domain expertise strings.'}, 'languages': {'type': 'array', 'description': 'Array of language codes.'}, 'technical': {'type': 'array', 'description': 'Array of technical capabilities: { name, type }.'}}}
aimeat_agent_console_set
Record where an agent is managed by whatever HOSTS it: its settings or brain page in the fleet runtime it runs in. Call this after creating and starting an agent somewhere the node cannot see — a fleet cockpit, your own daemon's UI — so the owner's profile can link straight to it. Without it the person is told their agent is running and has nowhere to go and look at it. Must be an absolute http(s) URL; send an empty string to clear it. Display only: the node links this address and never fetches it.
Input schema
{'type': 'object', 'required': ['target_agent_name', 'console_url'], 'properties': {'console_url': {'type': 'string', 'description': "Absolute http(s) URL of that agent's page in its host, or '' to clear it."}, 'target_agent_name': {'type': 'string', 'description': 'Agent whose console address to set (must be owned by the same owner as the caller). Pass your own name to record your own.'}}}
aimeat_agent_description_set
Say what one of your agents IS, in a sentence — the line a stranger reads on its A2A card and the one on its page here. Set it when what the agent does changes: it was fixed at registration and editable by nobody until 2026-09-03, so an agent whose job moved went on describing its old one. Send an empty string to clear it. The agent's NAME cannot be changed here or anywhere, because it is part of its identity; a description carries none.
Input schema
{'type': 'object', 'required': ['target_agent_name', 'description'], 'properties': {'description': {'type': 'string', 'description': 'What this agent is, in a sentence or two. Up to 2000 characters; empty clears it.'}, 'target_agent_name': {'type': 'string', 'description': 'Agent whose description to set (same owner as the caller). Pass your own name to describe yourself.'}}}
aimeat_agent_mode_set
Owner-only. Set an agent's operational mode. Modes: 'autonomous' (runs continuously, full Hello Integration), 'interactive' (user-facing, full Hello Integration), 'task-runner' (triggered/ephemeral, reduced 7-step Hello Integration — no commands or messages), 'coordinator' (orchestrates other agents, full Hello Integration), 'workstation' (node-visiting agent in the user's own env like VSCode or Claude Desktop, uses MCP directly; not node-resident, so narrowest 4-step Hello Integration — auth, platform, capabilities, directives).
Input schema
{'type': 'object', 'required': ['target_agent_name', 'mode'], 'properties': {'mode': {'enum': ['autonomous', 'interactive', 'task-runner', 'coordinator', 'workstation'], 'type': 'string', 'description': 'New mode.'}, 'target_agent_name': {'type': 'string', 'description': 'Agent whose mode to update (must be owned by the calling owner).'}}}
aimeat_agent_profile
View another agent's public profile by GAII: display name, description, advertised capabilities, trust score, and created date. Use to vet a provider before hiring it via aimeat_action_execute, or to inspect an agent you found through aimeat_catalogue_agents. To list your own owner's agents instead, use aimeat_agents_list.
Input schema
{'type': 'object', 'required': ['gaii'], 'properties': {'gaii': {'type': 'string', 'description': 'Agent GAII identifier.'}}}
aimeat_agent_propose
Put a NEW agent in front of your owner for approval: one you have designed for a job the basic agents do not cover. CREATES NOTHING. The proposal lands on the owner's open-items list, and only their own press on it creates the agent, seeds its definition and hands it to their connector — adding a principal to an account is the one moment a person belongs in, and an agent calling in the owner's name is not the owner. Send `crew_def` with it whenever you can: an agent approved without one exists and cannot run, and its runtime has to be up before anyone can publish one (the circle that ended crew-forge), so the definition you attach here is the only one it can start with. It is checked as a definition before the proposal is written, so a broken one is refused now rather than at approval. `scopes` may not exceed what you hold yourself. Proposing a name that is already waiting returns the standing proposal instead of putting a second line on the owner's list.
Input schema
{'type': 'object', 'required': ['name', 'purpose'], 'properties': {'mode': {'type': 'string', 'description': "How the node treats its tasks: 'task-runner' activates a queued task without asking the owner each time; also autonomous, interactive, coordinator, workstation."}, 'name': {'type': 'string', 'description': 'The agent name: 3 to 40 characters, lowercase letters, digits and hyphens, starting with a letter.'}, 'scopes': {'type': 'array', 'description': 'Exactly what it may do, e.g. ["memory:read","memory:write"]. Never more than you hold yourself.'}, 'purpose': {'type': 'string', 'description': 'What this agent is for, in a sentence the owner can decide from. This is what they read.'}, 'crew_def': {'type': 'object', 'description': 'What it would BE, in the crewaimeat crew_def shape — the same document aimeat_crew_publish takes. Strongly recommended.'}, 'run_mode': {'type': 'string', 'description': "'spawn' starts a worker per piece of work (right for bursty jobs); 'resident' stays up (right for an agent that answers people as they write, at a few seconds of cold start saved)."}, 'display_name': {'type': 'string', 'description': 'The name shown to the person. Defaults to the agent name.'}}}
aimeat_agent_run_mode_set
Set how one of your agents is meant to be RUN: 'spawn' (the agent is data on the node until work arrives, and its runtime starts a worker for the job which then unwinds) or 'resident' (the runtime keeps it up). Works on ANY agent you own, whatever runs it — an agent whose behaviour lives in code rather than in a definition on this node is not a lesser agent, and this is the switch that puts it on a spawner's roster (GET /v1/agents?run_mode=spawn). The node records it and the runtime honours it; the node never enforces it, exactly as with mode and max concurrent tasks.
Input schema
{'type': 'object', 'required': ['target_agent_name', 'run_mode'], 'properties': {'run_mode': {'enum': ['spawn', 'resident'], 'type': 'string', 'description': "'spawn' = started per job and unwound after; 'resident' = kept running; null takes it back to nobody-has-said, so a spawner leaves the agent alone."}, 'target_agent_name': {'type': 'string', 'description': 'Agent whose run mode to set (must be owned by the same owner as the caller). Pass your own name to set your own.'}}}
aimeat_agent_runtime_report
Say what code is running this agent, so a run can be audited afterwards. A crew whose definition lives on this node is already answerable — the definition is versioned here — but a code-backed crew has none, and then nothing can say what ran. Send the file, its hash, the commit it came from and which runtime read it; send null to clear. The node records the claim and stamps its own time on it, and never checks it: it does not run the process and cannot read the disk.
Input schema
{'type': 'object', 'required': ['target_agent_name', 'kind'], 'properties': {'file': {'type': 'string', 'description': "Path to the file that runs, relative to your own root, e.g. 'crews/web_researcher_crew.py'."}, 'kind': {'type': 'string', 'description': "What kind of thing runs, e.g. 'python' for a code-backed crew or 'crew-def' for a JSON one."}, 'commit': {'type': 'string', 'description': 'Commit the file came from.'}, 'sha256': {'type': 'string', 'description': "Hash of that file's contents — the only field that changes when the code changes and nothing else does."}, 'runtime': {'type': 'string', 'description': "Which runtime read it, e.g. 'crewaimeat 0.7.0'."}, 'target_agent_name': {'type': 'string', 'description': 'Agent this is about (same owner as the caller). Pass your own name to report your own.'}, 'definition_revision': {'type': 'number', 'description': 'For a JSON crew: which revision of the definition on this node was live.'}}}
aimeat_agents_list
List the calling owner's agents on the node (name, mode, capabilities, tags, last_seen, etc.). Use this to discover which agents you can delegate to via aimeat_task_create. Each agent also carries its permissions (default_scopes), what it asked for at its last approval (scope_request), and `refusals`: calls the node refused it for a missing permission that is still missing. A refusal is often why an agent's tasks do not move; tell the owner which permission to grant.
Input schema
{'type': 'object', 'properties': {}}
aimeat_agent_tags_set
Replace (set) the tag list on a same-owner agent. An agent may tag itself (or a same-owner sibling); an owner may tag any of their agents. Convention: 'crew:<name>', 'source:<name>', 'role:<name>', 'project:<name>' — but any lowercase string of alphanumerics plus `._:-` is accepted (no `@`). Max 20 tags. Empty array clears all tags.
Input schema
{'type': 'object', 'required': ['target_agent_name', 'tags'], 'properties': {'tags': {'type': 'array', 'description': 'Replacement tag list. Empty array clears all tags.'}, 'target_agent_name': {'type': 'string', 'description': "Agent whose tags to update (must be owned by the same owner as the caller). Pass the calling agent's own name to self-tag."}}}
aimeat_agent_telemetry_report
Append one telemetry event (llm_call, tool_call, or agent_report) recording metrics such as tokens, duration, or tool name; optionally tie it to a session or AIMEAT task. Feeds the node's activity stats (viewable via aimeat_agent_activity). Use for fine-grained runtime metrics — for task lifecycle/progress use the aimeat_task_* tools instead.
Input schema
{'type': 'object', 'properties': {'data': {'type': 'object', 'description': 'Telemetry data such as tokens, duration, or tool name.'}, 'type': {'enum': ['llm_call', 'tool_call', 'agent_report'], 'type': 'string', 'description': 'Telemetry event type.'}, 'task_id': {'type': 'string', 'description': 'Optional related AIMEAT task id.'}, 'session_id': {'type': 'string', 'description': 'Optional runtime session identifier.'}}}
aimeat_ai_capabilities
CALL THIS FIRST before you plan anything that uses AI: an app, an automation or your own work. Answers, per capability (text, vision, files, image, speech, transcription, embed), whether it is on for you right now, the model and provider a call would use, its price from the model catalogue, and a one-line howTo. For a capability that is off: the reason (NO_MODEL, NO_PROVIDER_SUPPORTS, NO_KEY, POLICY_EMPTY, BUDGET_EXHAUSTED, RETIRED_MODEL, UNTESTED, APP_NOT_ALLOWED) and a fix you can do or pass to the owner. Also the owner's model policy, today's budget, and the guide skill (aimeat-ai-capabilities). Spends nothing.
Input schema
{'type': 'object', 'properties': {'app_id': {'type': 'string', 'description': 'The app you act for, when you do: its own model list and preferences count.'}}}
aimeat_ai_embed
Turn texts into embedding vectors (the embed capability), on the owner's providers and budget. Use it only when the person decided it: never propose it yourself. It is for a collection far larger than one prompt (hundreds of thousands of tokens) that people search by meaning; a collection that fits one prompt goes to a text model whole, and word search comes first (skill aimeat-ai-capabilities, section 4). Store the answered `model` beside the vectors: vectors of different models cannot be compared, so a fallback only ever uses the same model. At most 256 texts and 500 000 characters per call.
Input schema
{'type': 'object', 'required': ['input'], 'properties': {'role': {'type': 'string', 'description': 'The AI role to run as: one of your roles (aimeat_ai_roles), or for an app a role it declares and you bound. A named model or provider wins over it.'}, 'input': {'type': 'array', 'description': 'The texts, one vector each.'}, 'model': {'type': 'string', 'description': "A model reference; omit to let the owner's providers choose."}, 'app_id': {'type': 'string', 'description': 'The app this is for, so its spend is attributed.'}, 'provider': {'type': 'string', 'description': 'A provider id or type to use, with no fallback.'}}}
aimeat_ai_job_cancel
Stop a background AI job. A queued one leaves the wait line and nothing is spent; a running one has its provider call torn down, so a stuck long call can be cleared instead of waited out. Whatever the provider had already billed stays recorded — a cancelled call is not a free call. A job that has already finished answers that there is nothing left to stop.
Input schema
{'type': 'object', 'required': ['job_id'], 'properties': {'job_id': {'type': 'string', 'description': 'The job id to stop.'}}}
aimeat_ai_job_get
Read one background AI job: its state, what it cost, where its answer went, and — when it failed — the code and message saying why. A job that ended `failed` with `chain_stopped` set is one whose on_done callback could not continue the chain, which is deliberately NOT reported as success. Only the owner's own jobs are reachable; anything else is simply not found.
Input schema
{'type': 'object', 'required': ['job_id'], 'properties': {'job_id': {'type': 'string', 'description': 'The job id from aimeat_ai_job_start.'}}}
aimeat_ai_job_list
List the owner's background AI jobs. Defaults to the LIVE ones (queued and running), which is what "what am I still waiting for" means. A finished job is folded into its day's log and shows up under state="all" or under its own state; its live record is deleted at that point, because one key per run would fill this node's per-account key ceiling within weeks. Use before starting another job to see whether the one you want is already running.
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'How many to return (1-500, default 50).'}, 'state': {'enum': ['queued', 'running', 'done', 'failed', 'cancelled', 'live', 'all'], 'type': 'string', 'description': 'Which jobs to list. "live" (the default) is queued + running.'}}}
aimeat_ai_job_start
Start a BACKGROUND model call and get a handle back in milliseconds. Use this instead of a normal completion whenever the answer may take minutes: the job queues for a slot, runs on this node with the owner's own key and budget, and writes its answer to the memory key you name in `result_key`. It answers at once with a job id and a queue position — never an ETA, because model latency is unknown — so read the job back with aimeat_ai_job_get, or just read `result_key` once it says done. Give it `prompt`, or `prompt_key` naming a record that holds the prompt text. `input_keys` names memory records that are READ AND PASTED INTO the prompt, labelled by key: the model has no tools and cannot fetch anything itself, and a record that does not exist is stated as missing rather than left as a silence it would fill in with an invention. `on_done` calls one of the owner's own extension actions when the answer has landed, which is how a chain of jobs is built; if that callback cannot run, the job ends failed rather than done. There is no token cap, on purpose. `op` picks the kind of call: "text" (the default) writes the answer; "image" makes one picture from the prompt, stores it in the owner's storage and writes the record { storage_key, url, mime_type, model }; "transcribe" turns the audio file at `audio_key` (in your own storage) into text and writes the transcript, or { text, language, seconds, model } with `json`. A field that does not apply to the op is refused before anything is written.
Input schema
{'type': 'object', 'required': ['result_key'], 'properties': {'op': {'enum': ['text', 'image', 'transcribe'], 'type': 'string', 'description': 'The kind of model call. "text" (default): a completion. "image": one picture from the prompt; json does not apply. "transcribe": speech-to-text over audio_key; no prompt.'}, 'json': {'type': 'boolean', 'description': 'Parse the answer as JSON before storing it, so a malformed answer fails the job instead of becoming a string every reader has to re-parse.'}, 'role': {'type': 'string', 'description': 'The AI role to run as: one of your roles (aimeat_ai_roles), or for an app a role it declares and you bound. A named model or provider wins over it.'}, 'size': {'type': 'string', 'description': 'For op "image": a provider-specific size, e.g. "1024x1024".'}, 'model': {'type': 'string', 'description': "Explicit model id. Omit to use the owner's configured model."}, 'app_id': {'type': 'string', 'description': "App attribution — enables the owner's per-app allowlist and per-app daily quota."}, 'prompt': {'type': 'string', 'description': 'The prompt. Required unless prompt_key names a record holding it.'}, 'on_done': {'type': 'object', 'description': "{ extension, action } — an extension action of the job's OWN owner, invoked with { job_id, state, result_key } when the job finishes."}, 'language': {'type': 'string', 'description': 'For op "transcribe": an ISO-639-1 language hint, e.g. "fi". Omit to use the owner\'s setting or auto-detect.'}, 'provider': {'type': 'string', 'description': "A provider to use, by id or by type. Naming one turns fallback to another provider off. Omit to let the owner's rules choose."}, 'audio_key': {'type': 'string', 'description': 'For op "transcribe" (required there): the storage key of an audio file in your own storage. A key that is not there is refused with NOT_FOUND before the job is written.'}, 'input_keys': {'type': 'array', 'description': 'Memory keys read and appended to the prompt, labelled by key. This is the ONLY way the model sees stored data — it has no tools. A key naming a record this node keeps for itself (an AI key, a payout setting, a spend limit) is refused with RESERVED_KEY, because everything read goes to the model provider.'}, 'prompt_key': {'type': 'string', 'description': 'An owner memory key holding the prompt text (a string, or an object with a `prompt` field), so changing the prompt is a memory write rather than a code change.'}, 'result_key': {'type': 'string', 'description': "Where the answer is written, in the owner's own namespace. A key naming another namespace is refused, and so is a record this node keeps for itself (RESERVED_KEY)."}, 'system_prompt': {'type': 'string', 'description': 'Optional system prompt.'}, 'result_visibility': {'enum': ['private', 'owner', 'public'], 'type': 'string', 'description': 'Visibility of the record written at result_key. Default private.'}}}
aimeat_ai_models
The model catalogue: which models of OpenRouter, OpenAI, Anthropic, xAI, Mistral and DeepSeek the node knows, what each takes and gives (caps), its limits, its price and its status (active, retiring, retired). allowed: true keeps only the models you can use now (a provider of that type serves the capability, and the owner's policy allows the model). Use the `ref` it lists (<type>:<model id>) in a call, a policy or an app's aimeat-ai meta. A model on the owner's own machine is not in the catalogue.
Input schema
{'type': 'object', 'properties': {'type': {'type': 'string', 'description': 'openrouter | openai | anthropic | xai | mistral | deepseek.'}, 'status': {'type': 'string', 'description': 'Comma-separated: active, retiring, retired; or all. Default active,retiring.'}, 'allowed': {'type': 'boolean', 'description': 'true: only the models you can use now.'}, 'capability': {'type': 'string', 'description': 'text | vision | files | image | speech | transcription | embed.'}}}
aimeat_ai_policy_set
Read or change which AI models the owner's calls may use, with PROPOSE-THEN-CONFIRM. With no policy: returns the current policy and the node's recommended models. With a policy and no confirm_token: changes NOTHING and returns the current and proposed policy with a single-use token (10 min) bound to exactly that policy; show the change to the owner. With the token: applies it. Modes: open (no limit), recommended (the node's list per capability, following its updates), custom (allow: your own list). Every reference is <type>:<model id>, e.g. openrouter:anthropic/claude-opus-5.5. appliesTo switches say whose calls it covers (owner, chat, agents, apps); apps and agents tighten it for one app (owner/file.html) or one agent. Every layer only tightens. Suggest mode recommended when the owner has a provider and no policy.
Input schema
{'type': 'object', 'properties': {'policy': {'type': 'object', 'description': '{ mode: open|recommended|custom, allow?: [refs], appliesTo?: {owner, chat, agents, apps}, apps?: {"owner/file.html": {allow}}, agents?: {name: {allow}} }. Omit to read.'}, 'confirm_token': {'type': 'string', 'description': 'Token from the propose step; omit to propose.'}}}
aimeat_ai_providers
List the AI providers the owner's calls can use: the owner's own (OpenAI, Anthropic, Mistral, xAI, OpenRouter, a local server, any OpenAI-compatible address, or an installed extension of theirs), and the node's. For each: its type, which capabilities it serves (text, vision, files, image, speech, transcription, embed) with which model, whether the node may pick it by capability alone (pool), whether it is tested and working (health), where the data goes, and whether a key is set (never the key). Also the owner's routing: the ordered providers per capability and the rules. Read this before naming a provider or model in a call, and to explain a refusal. A key is set only by the owner on the AI settings page, never through a tool.
Input schema
{'type': 'object', 'properties': {}}
aimeat_ai_provider_test
Test that one of the owner's providers serves one capability, with the smallest real call through the same gate every call uses (billed and budgeted like one). A pass marks it tested and working, which the node needs before it picks the provider by capability alone. capability: text (default), vision, files, transcription, speech (one spoken word), embed (one vector), or image (costs one small picture, so accept_cost: true is required; tell the owner first). Answers the model, the latency and the cost, never a key.
Input schema
{'type': 'object', 'required': ['provider'], 'properties': {'provider': {'type': 'string', 'description': 'The provider id, from aimeat_ai_providers.'}, 'capability': {'type': 'string', 'description': 'text | vision | files | transcription | speech | embed | image. Default text.'}, 'accept_cost': {'type': 'boolean', 'description': 'Required true for an image test, which the provider charges for.'}}}
aimeat_ai_roles
List the owner's AI roles and the AI roles apps ask for. A capability says what a model does (text, vision, files, image, speech, transcription, embed); a role says what it is used for: the built-in reasoning and execution, or one the owner made (for example summarizer). For each capability it needs, a role has its own ordered providers and models, and it may keep to this machine (local) or set a price ceiling per call. Pass a role id as `role` in an AI call to run as it. An app declares the roles it needs in its aimeat-ai meta, and an app's role runs only after the owner binds it to one of their roles. For each app the answer lists its roles with the binding (boundTo, null while unbound) and, for an unbound role the app already asked for, requestedAt: show the owner which app roles wait for a binding, and propose one with aimeat_ai_role_set.
Input schema
{'type': 'object', 'properties': {}}
aimeat_ai_role_set
Change the owner's AI roles, or bind an app's AI role to one of them, with PROPOSE-THEN-CONFIRM. With roles, bindings or both and no confirm_token: changes NOTHING and returns the current and proposed change with a single-use token (10 min) bound to exactly that change; show it to the owner. With the same change plus the token: applies it. roles: { "<role id>": { title, purpose?, capabilities: { "<capability>": [{ provider, model? }, ...] }, local?, maxCostPerCallUsd? } or null to remove } (an id is lower-case letters, digits and "-"; the first provider is tried first, at most 5; the built-in reasoning and execution cannot be removed). bindings: { "<owner>/<file>.html#<role name>": "<your role id>" or null to unbind }. Binding an app's role is the owner's approval of what that app may run. Read the roles and the providers first (aimeat_ai_roles, aimeat_ai_providers).
Input schema
{'type': 'object', 'properties': {'roles': {'type': 'object', 'description': '{ "<role id>": { title, purpose?, capabilities: {capability: [{provider, model?}]}, local?, maxCostPerCallUsd? } or null }.'}, 'bindings': {'type': 'object', 'description': '{ "<owner>/<file>.html#<role name>": "<your role id>" or null }. Null unbinds and dismisses the app\'s request; a role that lacks a capability the app\'s role needs is refused.'}, 'confirm_token': {'type': 'string', 'description': 'Token from the propose step; omit to propose.'}}}
aimeat_ai_routing_set
Read or change which provider answers each AI capability and the rules for moving to the next, with PROPOSE-THEN-CONFIRM. With no routing: returns the current routing and the providers. With a routing and no confirm_token: changes NOTHING and returns the current and proposed routing with a single-use token (10 min) bound to exactly that change; show it to the owner. With the token: applies it. routing: { defaults?: { "<capability>": ["<provider id>", ...] } (first is the default, the rest are fallbacks), rules?: { fallback, maxAttempts (1-5), onlyTested, extendToPool, fallbackOn: [timeout, rate_limit, server_error, unavailable, auth], fallbackMayLeaveMachine, speechVoiceMayChange, maxCostPerCallUsd }, agent?: "<agent name>" to set that agent's own defaults instead (an agent's record has no rules). A named capability's list replaces the old one.
Input schema
{'type': 'object', 'properties': {'routing': {'type': 'object', 'description': '{ defaults?: {capability: [provider ids]}, rules?: {...}, agent?: name }. Omit to read.'}, 'confirm_token': {'type': 'string', 'description': 'Token from the propose step; omit to propose.'}}}
aimeat_ai_transcribe
Transcribe an audio file in your storage to text, on the owner's providers and budget (the transcription capability). Store the file first (aimeat_storage_upload) and pass its storage_key. Answers the text, the model, the language, the seconds and what it cost. Refusals name what to set (NO_STT_MODEL, AI_CAPABILITY_UNAVAILABLE with a fix).
Input schema
{'type': 'object', 'required': ['storage_key'], 'properties': {'role': {'type': 'string', 'description': 'The AI role to run as: one of your roles (aimeat_ai_roles), or for an app a role it declares and you bound. A named model or provider wins over it.'}, 'model': {'type': 'string', 'description': "A model reference; omit to let the owner's providers choose."}, 'app_id': {'type': 'string', 'description': 'The app this is for, so its spend is attributed.'}, 'filename': {'type': 'string', 'description': "The file name the provider sees; its extension names the format. Defaults to the key's last part."}, 'language': {'type': 'string', 'description': 'ISO-639-1 hint (fi, en). Omit to let the model detect it.'}, 'provider': {'type': 'string', 'description': 'A provider id or type to use, with no fallback.'}, 'storage_key': {'type': 'string', 'description': "The audio file's key in your storage."}}}
aimeat_app_delete
Archive (soft-delete) an app. Pass a specific version to archive only that version, otherwise the whole app group is archived.
Input schema
{'type': 'object', 'required': ['filename'], 'properties': {'version': {'type': 'number', 'description': 'A specific version number. Omit to archive all versions.'}, 'filename': {'type': 'string', 'description': "App filename to archive (your own owner's)."}}}
aimeat_appdev_overview
THE research call before building an app ON AIMEAT — one compact "big picture": the owner's existing apps (often the best template to fork/copy), library packs with per-model AEB proof summaries, T1/T2/T3 app-shell templates, loadable skills (node:aimeat-app-builder first), curated pitfalls, every active learned pitfall you can read (your own and those other owners shared, critical first), and prior template proposals. Indexes only with drill-down pointers; pass sections=[...] for a partial fetch and model=<YOUR OWN model id — self-identify, never ask the user> to mark proven packs and list the pitfalls your model wrote first (it never hides one). Flow: research (this) → frame → propose to the user → build.
Input schema
{'type': 'object', 'properties': {'model': {'type': 'string', 'description': 'Your primary model (indicative), e.g. claude-haiku-4.5. Marks proven packs and orders learned pitfalls; filters nothing.'}, 'sections': {'type': 'array', 'description': 'Subset: apps, library_packs, app_templates, skills, pitfalls_curated, pitfalls_learned, template_proposals.'}}}
aimeat_appdev_pitfall_delete
Delete one of your learned appdev-pitfall entries entirely (removes the entry and its manifest reference). Prefer aimeat_appdev_pitfall_report with status=outdated when the pitfall merely stopped being relevant — delete is for wrong or duplicate entries.
Input schema
{'type': 'object', 'required': ['category', 'slug'], 'properties': {'slug': {'type': 'string', 'description': 'Entry slug.'}, 'category': {'type': 'string', 'description': 'Entry category.'}}}
aimeat_appdev_pitfall_list
List appdev pitfalls before building an app ON AIMEAT — merged from your own learned entries, the node's curated registry, and other owners' shared entries. scope: own (your bubble) | platform (curated + shared) | all (default). Filter by category, applies_to area, or model; paginated (limit/offset) with total + facet counts so a large KB stays navigable. Outdated entries are hidden by default. One full learned entry: aimeat_memory_read {key, owner_scope: true} for your own, aimeat_memory_read_public {gaii: owner, key} for a shared one (the list names its owner); curated detail via GET /v1/appdev/pitfalls/{id}.
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'Page size, default 25 (max 100).'}, 'model': {'type': 'string', 'description': 'Filter learned entries to one model.'}, 'scope': {'enum': ['own', 'platform', 'all'], 'type': 'string', 'description': 'Default all.'}, 'offset': {'type': 'number', 'description': 'Page start, default 0.'}, 'status': {'enum': ['active', 'outdated', 'all'], 'type': 'string', 'description': 'Default active.'}, 'category': {'type': 'string', 'description': 'Filter by category.'}, 'applies_to': {'type': 'string', 'description': 'Filter by area.'}}}
aimeat_appdev_pitfall_report
Record a pitfall learned while building an app ON AIMEAT (apps/extensions/cortex — never node development): what broke, what fixed it, and WHICH MODEL hit it (model is required, self-reported, indicative). Call at the end of a build for anything the next builder should know. Upserts by {category, slug} — reporting the same slug again REPLACES the entry with better wording (version bumps). status=outdated hides an entry models no longer stumble on; share=true publishes it platform-wide so other owners' agents learn from it (default: private to your owner scope). Stored in the reserved knowledge package appdev-pitfalls.
Input schema
{'type': 'object', 'required': ['model', 'category', 'title', 'symptom', 'resolution'], 'properties': {'slug': {'type': 'string', 'description': 'Stable kebab-case slug; same {category, slug} updates the entry.'}, 'model': {'type': 'string', 'description': 'YOUR OWN model id — the model that hit/solved this. Self-identify from your own configuration, never ask the user. Indicative attribution.'}, 'share': {'type': 'boolean', 'description': 'true = platform-wide public entry; default private to your owner scope.'}, 'title': {'type': 'string', 'description': 'Short imperative title.'}, 'status': {'enum': ['active', 'outdated'], 'type': 'string', 'description': 'outdated = kept but hidden from default lists.'}, 'app_ref': {'type': 'string', 'description': 'Related app (owner/filename.html).'}, 'symptom': {'type': 'string', 'description': 'What the builder observes.'}, 'category': {'type': 'string', 'description': 'Kebab-case category (auth, ext, cortex, realtime, mobile, publish, ai, data...).'}, 'severity': {'enum': ['info', 'warn', 'critical'], 'type': 'string', 'description': 'Default warn.'}, 'applies_to': {'type': 'array', 'description': 'Areas: app, auth, ext, cortex, iam, realtime, ai, mobile, publish.'}, 'resolution': {'type': 'string', 'description': 'What to do instead.'}}}
aimeat_appdev_proof_attach
Attach a SELF-REPORTED per-model acceleration proof (pass/fail + evidence) to a community contribution you own: a community library pack (your public cortex lib — proofs appear on /v1/library-packs with self_reported: true) or one of your app-template proposals. Append-only ledger, duplicate (model, test_set, date) rejected; honest fails make your passes credible. This is the "proven acceleration" attribution sellers build a track record with — a node-verified badge is a separate later feature, never implied by these.
Input schema
{'type': 'object', 'required': ['subject_type', 'subject_id', 'model', 'verdict', 'evidence'], 'properties': {'model': {'type': 'string', 'description': 'Model the run was made with (indicative).'}, 'tokens': {'type': 'number', 'description': 'Output tokens the run consumed.'}, 'verdict': {'enum': ['pass', 'fail'], 'type': 'string', 'description': 'Did it accelerate the run.'}, 'evidence': {'type': 'string', 'description': 'URL / storage / memory ref to the run evidence.'}, 'test_set': {'type': 'string', 'description': 'Repeatable test-set id, when used.'}, 'subject_id': {'type': 'string', 'description': 'Community pack id (cortex name) or template proposal id.'}, 'subject_type': {'enum': ['library_pack', 'app_template'], 'type': 'string', 'description': 'What the proof attaches to.'}}}
aimeat_app_draft_discard
Throw away an app's saved draft. The live app is untouched. Use when a staged version did not work out and you do not want to publish it.
Input schema
{'type': 'object', 'required': ['filename'], 'properties': {'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'filename': {'type': 'string', 'description': 'App filename whose draft to discard.'}}}
aimeat_app_draft_publish
Promote an app's saved draft to a NEW live version, then clear the draft slot — THIS is the moment the live app changes. Carries the live app's parked/forkable/protection state forward, exactly like a normal re-publish. Call this after you have tested the draft via the preview_url from aimeat_app_draft_save and it works. Fails if there is no saved draft. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['filename'], 'properties': {'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'roadmap': {'type': 'string', 'description': 'What this version changes. Required when the app is shared with another developer.'}, 'filename': {'type': 'string', 'description': 'App filename whose draft to publish.'}, 'spec_ack': {'type': 'string', 'description': 'Owner-declared build spec acknowledgement.'}, 'spec_token': {'type': 'string', 'description': 'Current app build spec digest.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_app_draft_read
Read a line range of an app's draft. Returns the slice plus the total line count and byte size, so you can page through a large app without pulling all of it into context. Ask for the part you are about to change, not the whole file: a read with no range returns the first page and tells you that more remains. Line numbers are 1-based.
Input schema
{'type': 'object', 'required': ['filename'], 'properties': {'limit': {'type': 'number', 'description': 'How many lines to return. Default 400, maximum 2000.'}, 'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'offset': {'type': 'number', 'description': 'First line to return, 1-based. Default 1.'}, 'filename': {'type': 'string', 'description': 'App filename whose draft to read.'}}}
aimeat_app_draft_replace
Replace an exact piece of text inside an app's draft, the way an editor does. Use this to iterate: read the part you want to change with aimeat_app_draft_read, then replace it, instead of rewriting the whole app through your context. old_string must match EXACTLY, including indentation and line breaks, and must appear exactly once unless you set replace_all. When it is not unique the refusal tells you how many times it appeared, so widen the surrounding text until the match is unambiguous rather than guessing.
Input schema
{'type': 'object', 'required': ['filename', 'old_string', 'new_string'], 'properties': {'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'filename': {'type': 'string', 'description': 'App filename whose draft to edit.'}, 'new_string': {'type': 'string', 'description': 'What to put there instead.'}, 'old_string': {'type': 'string', 'description': 'The exact text to replace, including indentation.'}, 'replace_all': {'type': 'boolean', 'description': 'Replace every occurrence instead of requiring exactly one. Default false.'}}}
aimeat_app_draft_save
STAGING — save the NEXT version of an app as a draft WITHOUT touching the live one, and get a preview_url to test it on a real origin before publishing. Use this instead of aimeat_app_publish whenever you want to VERIFY before going live — especially for anything using the microphone or camera (getUserMedia only works on a real, top-level origin; it is impossible in an embedded/sandboxed preview, so testing in a preview pane will always "fail"). Flow: aimeat_app_draft_save → open preview_url in a browser (a real tab, mic/camera prompts work) → if good, aimeat_app_draft_publish; if not, edit + save again, or aimeat_app_draft_discard. The live app (the version users see) stays exactly as it was until you publish the draft. At most one draft per app; saving again overwrites it. Manifest fields you omit default from the current live app. UPLOAD/SIZE: this tool is INLINE-ONLY — unlike aimeat_app_publish it has no presigned upload_url mode, so content_base64 must be inline. Do NOT read/cat/paste a large base64 blob to feed it (a ~60 KB single-line base64 file bills ~2.5 tokens/char and wastes tens of thousands of tokens). For an app over ~1 KB, prefer aimeat_app_publish in UPLOAD MODE (omit content → PUT the raw file) — going straight to live is fine when the app link is not yet distributed and the real send/use is gated elsewhere; only pay the inline staging cost for a small app, or one you genuinely must preview before it is ever live (e.g. a mic/camera app).
Input schema
{'type': 'object', 'required': ['filename', 'content'], 'properties': {'name': {'type': 'string', 'description': "Display name (defaults to the live app's)."}, 'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'content': {'type': 'string', 'description': 'The draft HTML (the next version to test). Use @file:path with the CLI fallback.'}, 'filename': {'type': 'string', 'description': 'App filename this draft stages (e.g. "drumpad.html").'}, 'description': {'type': 'string', 'description': "Description (defaults to the live app's)."}}}
aimeat_app_draft_seed
Copy a published app's source into its draft slot, server-side, so you can continue an app you already shipped. This is the missing first step when someone asks you to change a live app: aimeat_app_get returns the manifest and NOT the source, so without this there is nothing to edit. The bytes never pass through your context. Seed, then read and replace the parts you need, then aimeat_app_draft_publish. Seeding under a different filename copies the app, manifest and all.
Input schema
{'type': 'object', 'required': ['filename'], 'properties': {'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'version': {'type': 'number', 'description': 'Which published version. Defaults to the newest.'}, 'filename': {'type': 'string', 'description': 'The draft slot to write into.'}, 'from_filename': {'type': 'string', 'description': 'The published app to copy from. Defaults to filename.'}}}
aimeat_app_draft_write
Write into an app's draft slot a PIECE AT A TIME, so you can build an app larger than one model response. This is the way to author a real app: no model emits 400 kB in one go, and there is no filesystem here to assemble it on. Call repeatedly with mode "append" until the file is complete, then aimeat_app_draft_publish. Use mode "replace" for the first call, or to start over. Content is plain UTF-8 text, NOT base64. To continue an app that is already live, copy it into the slot first with aimeat_app_draft_seed. To change something you already wrote, prefer aimeat_app_draft_replace over rewriting the whole file. Pass expected_size_bytes when you are appending across many calls and want to be told if the draft moved underneath you.
Input schema
{'type': 'object', 'required': ['filename', 'content'], 'properties': {'mode': {'enum': ['append', 'replace'], 'type': 'string', 'description': 'append (default) adds to the end; replace overwrites the whole draft.'}, 'name': {'type': 'string', 'description': "Display name (defaults to the live app's, or the draft's once set)."}, 'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'content': {'type': 'string', 'description': 'The text to write. Plain UTF-8, not base64.'}, 'filename': {'type': 'string', 'description': 'App filename this draft stages (e.g. "pong.html").'}, 'description': {'type': 'string', 'description': "Description (defaults to the live app's, or the draft's once set)."}, 'expected_size_bytes': {'type': 'number', 'description': 'Refuse unless the draft is currently this many bytes. Guards a multi-call build against a lost update.'}}}
aimeat_app_fork
Fork an app into your own catalogue as a new independent app, recording its origin (manifest.forkedFrom) and a lineage event. You may fork your own apps freely; you may fork someone else's only when they have marked it forkable (and, for a paid app, you hold a license). This is the sanctioned, provenance-recording path — prefer it over app_get+app_publish so the fork chain stays intact.
Input schema
{'type': 'object', 'required': ['owner', 'filename', 'new_filename'], 'properties': {'owner': {'type': 'string', 'description': 'Owner name of the source app.'}, 'version': {'type': 'number', 'description': 'Source version to fork (default: latest).'}, 'filename': {'type': 'string', 'description': 'Filename of the source app.'}, 'new_filename': {'type': 'string', 'description': 'Filename for the fork in your catalogue.'}}}
aimeat_app_get
Get one app's detail (manifest, current version number, size, mime type, whether access-protected, download count, `url` — the app's public web address to give a person — and the download/inline URLs) identified by its owner and filename. Find owner/filename via aimeat_app_list; for the list of prior versions use aimeat_app_manage { action: "versions" }.
Input schema
{'type': 'object', 'required': ['owner', 'filename'], 'properties': {'owner': {'type': 'string', 'description': 'Owner name of the app.'}, 'filename': {'type': 'string', 'description': 'App filename, e.g. "starwars.html".'}}}
aimeat_app_list
List published HTML apps on the node (name, description, version, category, tags, size, download count, and `url` — the app's public web address, which is what to give a person who wants to open it), with optional category/tag/text filters and an "own apps only" mode. Paged: the answer carries `total`, `limit`, `offset` and `has_more`, so read the whole catalogue by calling again with offset += limit while has_more is true. Use to discover apps and to show someone their own; fetch one app's detail with aimeat_app_get and its version history with aimeat_app_manage { action: "versions" }. Publish with aimeat_app_publish.
Input schema
{'type': 'object', 'properties': {'own': {'type': 'boolean', 'description': "List only your own owner's apps."}, 'tag': {'type': 'string', 'description': 'Filter by tag.'}, 'limit': {'type': 'number', 'description': 'How many to return (default 50, max 200).'}, 'offset': {'type': 'number', 'description': 'How many to skip; with has_more this reads the whole catalogue.'}, 'search': {'type': 'string', 'description': 'Free-text search over name and description.'}, 'building': {'type': 'boolean', 'description': 'List apps another owner lets you build; opt-in and separate from your own.'}, 'category': {'type': 'string', 'description': 'Filter by category.'}}}
aimeat_app_manage
Manage one of your apps: its settings, search visibility, legal pages, visitors, screen layout, thumbnail, versions, forks, cost, bundled agents and backup, in one tool. Pick `action`; each action takes only its own fields, and a call with a missing or foreign field is refused with every problem named at once, so nothing is charged or changed. Actions: - settings (filename (required), owner, name, description, descriptions, parked, forkable, access_code, protection): change name, description, per-language descriptions, parked (hidden from the public catalogue), forkable, access code or copy protection, without a new version. - seo (filename (required), index, title, description, keywords, image, lang): decide whether the app can be found in search engines (off until you ask) and what it says there; naming nothing reports where it stands. - marks (filename (required), badge, install): switch the "publish your own app" badge and the install offer; naming nothing reports them. Naming the reviewer who lifts the AI label is the account holder's own act and is not here. - legal (filename (required), kind, format, content, remove, ai_provenance, ai_provenance_id): publish, replace or remove one of the app's legal pages (terms, privacy, imprint, refunds, accessibility, cookies, support), served under its address; no kind reports which pages it still ought to have. - audit (filename (required), limit, playtest): read the app's change log, newest first; playtest: true also opens the app signed out in a real browser and reports what a stranger sees. - versions (filename (required), owner): list the version history (number, display version, size, created at). - lineage (filename (required), owner): the fork tree: where the app came from and every fork made of it. - screenshot (filename (required), owner): render the published app in a real browser, store the picture as its thumbnail, and answer its URL so you can look at it. - screenshot_upload (filename (required), screenshot (required), owner, screenshot_mime_type): set your own image as the thumbnail. - screenshot_clear (filename (required), owner): remove the thumbnail; the next scheduled run takes a new one. - preview_link (filename (required), owner): a short-lived link that opens the app's unpublished draft. - visitors (filename (required), days): who opened the app and from where; read the answer's `reading` block before repeating a number. - visitors_measure (filename (required), on (required), geo): switch visitor measurement on or off and choose how precisely a person's place is kept; say it in the app's privacy notice. - ui_get (filename (required), detail): read an Atelier app's screen layout and the component catalogue index. - ui_set (filename (required), layout (required), note, ai_provenance, ai_provenance_id): replace the WHOLE layout (read it first); the answer names the version it replaced. - ui_restore (filename (required), version (required)): put a replaced layout version back. - config_get (filename (required), owner): the config the app declares it needs: each field, its value (defaults filled in) and which required ones are still empty. - config_set (filename (required), values (required)): change the app's config values, checked against what it declares; a managed package install allows it, since config is a setting. - cost (filename (required), owner): what the app's contracts for other people's tools cost: per contract, and totals. - agent_deploy (filename (required), bundled_agent (required), owner, runner_agent, organism_id): start an agent the app declares, as a task on your runner. - agent_undeploy (filename (required), bundled_agent (required), owner, runner_agent): stop it. - agent_instances (filename (required), bundled_agent (required), owner): the deployed instances of an agent the app declares. - agent_status (filename (required), bundled_agent (required), owner, runner_agent): whether it is registered, when it was last seen, and its deploy state. - grants (none): the apps your owner has granted permissions to, with their permissions and spend; revoking one is the owner's own act on the Access page. - backup_export (none): write a ZIP backup of all your apps to your private storage and answer its storage key (fetch it with aimeat_storage_download); restoring a backup is the owner's own act on the App Catalog page. - subdomain_list (none): operator: the subdomains this server serves apps or redirects on. - subdomain_set (subdomain (required), target, subdomain_kind, enabled): operator: create or change one. - subdomain_delete (subdomain (required)): operator: remove one. Permissions are checked per action: an action needs the word shown by the refusal, so an agent without app:write can still read versions, lineage and agent status. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['action'], 'properties': {'on': {'type': 'boolean', 'description': 'For visitors_measure: true starts counting who opens the app; false stops and keeps what was counted.'}, 'geo': {'enum': ['off', 'country', 'region', 'city'], 'type': 'string', 'description': 'For visitors_measure: the place kept for each person. Choose the coarsest that answers the question. Omit to keep what it was.'}, 'days': {'type': 'number', 'description': 'For visitors: the trailing window in days, 0 to 360. 0 is today only. Default 30.'}, 'kind': {'enum': ['terms', 'privacy', 'imprint', 'refunds', 'accessibility', 'cookies', 'support'], 'type': 'string', 'description': 'For legal: which page. Omit to read where the app stands.'}, 'lang': {'type': 'string', 'description': 'For seo: language tag such as "fi". Empty reads what the app declares.'}, 'name': {'type': 'string', 'description': "For settings: the app's display name. Changes it without a new version."}, 'note': {'type': 'string', 'description': 'For ui_set: one line on what this change was for.'}, 'badge': {'type': 'boolean', 'description': 'For marks: false takes the "publish your own app" badge off the app; true puts it back.'}, 'image': {'type': 'string', 'description': 'For seo: absolute https URL for the social card. Empty uses the app screenshot.'}, 'index': {'type': 'boolean', 'description': 'For seo: true makes the app findable in search engines, false takes it back out. Off until you ask.'}, 'limit': {'type': 'number', 'description': 'For audit: how many of the newest entries. Default 50, at most 500.'}, 'owner': {'type': 'string', 'description': "The app's owner. Omit for your own apps; another owner's app needs a development grant, or is read only where the action is public."}, 'title': {'type': 'string', 'description': 'For seo: title for search results and social cards. Empty derives it from the app name.'}, 'action': {'enum': ['settings', 'seo', 'marks', 'legal', 'audit', 'versions', 'lineage', 'screenshot', 'screenshot_upload', 'screenshot_clear', 'preview_link', 'visitors', 'visitors_measure', 'ui_get', 'ui_set', 'ui_restore', 'config_get', 'config_set', 'cost', 'agent_deploy', 'agent_undeploy', 'agent_instances', 'agent_status', 'grants', 'backup_export', 'subdomain_list', 'subdomain_set', 'subdomain_delete'], 'type': 'string', 'description': 'What to do. The description lists each action with its fields.'}, 'detail': {'type': 'array', 'description': 'For ui_get: names from the catalogue index to get IN FULL: component ids ("table", "statRow") or section names ("effects", "layouts", "ambients"). Omit it for the index alone.'}, 'format': {'enum': ['markdown', 'html', 'url'], 'type': 'string', 'description': "For legal: markdown (rendered with every character escaped), html (served as written on the app's own origin) or url (a link to where the page lives)."}, 'layout': {'type': 'object', 'description': 'For ui_set: the WHOLE layout { v: 1, look?, nav?, blocks: [{ id, component, props }] }. It replaces what is there; read it first with ui_get.'}, 'parked': {'type': 'boolean', 'description': 'For settings: true hides the app from the public catalogue (it still works by link and for you); false lists it again.'}, 'remove': {'type': 'boolean', 'description': 'For legal: true removes the named page.'}, 'target': {'type': 'string', 'description': 'For subdomain_set: what it serves, "owner/filename" for an app or an absolute URL for a redirect.'}, 'values': {'type': 'object', 'description': 'For config_set: the fields to change, { "<field>": value }. A null puts a field back to its default; fields not named keep their values.'}, 'content': {'type': 'string', 'description': 'For legal: the page text, the HTML document, or the absolute https URL.'}, 'enabled': {'type': 'boolean', 'description': 'For subdomain_set: false keeps the entry but stops serving it.'}, 'install': {'type': 'boolean', 'description': 'For marks: false stops offering visitors to install the app in their browser; true offers it again.'}, 'version': {'type': 'number', 'description': 'For ui_restore: the layout version to put back (ui_set answers with the one it replaced).'}, 'filename': {'type': 'string', 'description': 'The app, with its extension (e.g. "shop.html").'}, 'forkable': {'type': 'boolean', 'description': 'For settings: true lets anyone signed in fork the app into their own catalogue.'}, 'keywords': {'type': 'array', 'description': 'For seo: keywords. Empty uses the app tags.'}, 'playtest': {'type': 'boolean', 'description': 'For audit: also open the app in a headless browser, signed out, and report what it did (about a minute).'}, 'subdomain': {'type': 'string', 'description': 'For subdomain_set and subdomain_delete: the subdomain label (e.g. "shop").'}, 'protection': {'type': 'object', 'description': 'For settings: copy protection { obfuscate, domainLock, watermark, noRawDownload }, each true or false.'}, 'screenshot': {'type': 'string', 'description': 'For screenshot_upload: the image as base64, at most 2 MB.'}, 'access_code': {'type': 'string', 'description': 'For settings: a code visitors must type to open the app. An empty string removes it. Never read back.'}, 'description': {'type': 'string', 'description': "For seo: the search-result description (empty derives it). For settings: the app's own catalogue description."}, 'organism_id': {'type': 'string', 'description': 'For agent_deploy: the organism the deployed agent works in, when the app needs one.'}, 'descriptions': {'type': 'object', 'description': 'For settings: the description per language, { "fi": "…", "es": "…" }.'}, 'runner_agent': {'type': 'string', 'description': 'For agent_deploy, agent_undeploy and agent_status: which of your agents runs it. Omit to use your task runner.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'bundled_agent': {'type': 'string', 'description': 'For agent_*: the name of an agent the app declares in its manifest.'}, 'subdomain_kind': {'enum': ['app', 'redirect'], 'type': 'string', 'description': 'For subdomain_set: app serves an app at the subdomain, redirect sends visitors to target.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}, 'screenshot_mime_type': {'type': 'string', 'description': 'For screenshot_upload: image/png, image/jpeg or image/webp. Default image/png.'}}}
aimeat_app_publish
Publish or update an HTML app STRAIGHT TO LIVE (versioned by group) — every call becomes the new live version users get immediately. START FROM THE SHELL: fetch `GET /v1/app-templates` and build on one, rather than writing an app from scratch. The shell carries the login pill, the language and theme controls, and the design system the node already serves; an app written from nothing typically ships without them, and the person only finds out by opening it. Two modes: UPLOAD MODE (recommended for files > 1 KB) — call with metadata only (omit content), get an upload_url, then PUT the raw HTML; the PUT response is the publish result. INLINE MODE — pass content for tiny files. Use @file:path with the CLI fallback. DECIDING LIVE vs STAGING: use this when you are confident the app works. When you want to TEST the next version first (e.g. anything using the microphone/camera, which only work on a real origin — never in an embedded preview), stage it with aimeat_app_draft_save, open its preview_url to verify, then aimeat_app_draft_publish — the live app stays untouched until you do. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['filename', 'name'], 'properties': {'icon': {'type': 'string', 'description': 'Emoji icon.'}, 'name': {'type': 'string', 'description': 'Display name shown in the catalogue.'}, 'tags': {'type': 'array', 'description': 'Tags for search and filtering.'}, 'owner': {'type': 'string', 'description': 'App owner. Omit for your own apps; another owner requires a development grant.'}, 'content': {'type': 'string', 'description': 'The app HTML as plain text. Omit for upload mode. Use @file:path with the CLI fallback.'}, 'roadmap': {'type': 'string', 'description': 'What this version changes. Required when the app is shared with another developer.'}, 'version': {'type': 'string', 'description': 'Semver display version. Generated if omitted.'}, 'category': {'type': 'string', 'description': 'Category (default "tool").'}, 'filename': {'type': 'string', 'description': 'App filename, e.g. "starwars.html". Alphanumeric, dots, hyphens, underscores.'}, 'spec_ack': {'type': 'string', 'description': 'Owner-declared build spec acknowledgement.'}, 'spec_token': {'type': 'string', 'description': 'Current app build spec digest.'}, 'description': {'type': 'string', 'description': 'Short description.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'cortex_agents': {'type': 'array', 'description': 'Declarative crew-defs this app ships (manifest.cortex.agents), validated at publish in both modes. Omit on update to carry them forward; [] clears.'}, 'content_base64': {'type': 'string', 'description': 'Already-encoded HTML, if you did the encoding yourself.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_app_template_delete
Delete one of your template proposals entirely. Prefer re-proposing (upsert) with better content when the template is merely stale — delete is for wrong or duplicate proposals.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Template proposal id.'}}}
aimeat_app_template_get
Read one agent-proposed template: the full manifest (reuse notes, packs, per-model notes, proofs) plus the source app's LIVE state (forkable, price, version, download URL) and a concrete how_to_start instruction (fork via aimeat_app_fork vs scaffold from the notes; priced apps are bought through checkout, never with morsels directly). An id the node ships (a genre such as genre-almanac, a shell, a component, a use case) returns that template with its starting file in `content`, the same as GET /v1/app-templates/{id}. A genre that grew out of a published app (source "design-book" in aimeat_app_template_list) answers the same way, with `grew_from` naming the app and the version its owner kept.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Template id: a proposal, or one the node ships.'}, 'part': {'type': 'number', 'description': 'Only for a template the node ships whose file is too large for one answer: which part to return (1-based). The first answer says how many parts there are.'}}}
aimeat_app_template_list
List your owner's agent-proposed app templates (id, title, tier, model, start mode, derived-from app, proof count). Check this BEFORE building a new app — a prior template is usually the fastest correct starting point. Full detail + how-to-start via aimeat_app_template_get. `node_templates` lists what the node itself ships by id, kind and title, the Atelier track first: the genres a new app is forked from (genre-<id>), the Atelier shells, then the Classic shells, components and use cases, for a client that cannot call GET /v1/app-templates.
Input schema
{'type': 'object', 'properties': {}}
aimeat_app_template_propose
Record a reusable TEMPLATE distilled from an app you just built/published on this node — call after a successful publish when anything generalizes. Captures what to reuse (reuse_notes), the tier (T1 pure client / T2 +cortex / T3 +extension), the packs it relies on, how the next build should start (fork the source app vs scaffold), and WHICH MODEL built it (required, indicative). Proposing the same id again UPDATES the proposal. Owner-private in v1; the next build finds it via aimeat_appdev_overview or aimeat_discover type=template scope=own.
Input schema
{'type': 'object', 'required': ['id', 'title', 'description', 'derived_from', 'tier', 'reuse_notes', 'model'], 'properties': {'id': {'type': 'string', 'description': 'Stable kebab-case template id (same id = update).'}, 'tags': {'type': 'array', 'description': 'Discovery tags.'}, 'tier': {'enum': ['T1', 'T2', 'T3'], 'type': 'string', 'description': 'T1 pure client · T2 +cortex · T3 +extension.'}, 'model': {'type': 'string', 'description': 'YOUR OWN model id — the model that built the source app. Self-identify, never ask the user (indicative).'}, 'packs': {'type': 'array', 'description': 'Library-pack ids the template relies on.'}, 'title': {'type': 'string', 'description': 'Template title.'}, 'composes': {'type': 'array', 'description': 'Component template ids it composes.'}, 'start_mode': {'enum': ['fork', 'scaffold', 'either'], 'type': 'string', 'description': 'How the next build starts (default either).'}, 'description': {'type': 'string', 'description': 'What the template is for.'}, 'model_notes': {'type': 'array', 'description': 'Per-model observations [{model, notes, evidence?}].'}, 'reuse_notes': {'type': 'string', 'description': 'What generalizes — the parts a next build should copy/keep.'}, 'derived_from': {'type': 'object', 'description': '{owner, filename} of YOUR published source app.'}, 'start_mode_rationale': {'type': 'string', 'description': 'Why that start mode.'}}}
aimeat_app_tool_invoke
CALL an app's offered tool (a method like getCompanyBrief) through YOUR metered contract — the generic "one app/agent calls another app's function" channel. You must already hold a contract for this app-tool (accept its offering with aimeat_exchange_accept). The call is metered + charged to your budget at the provider price (+ platform rake), routed to the pinned interface version's backing capability; the provider's own upstream API keys stay server-side (you never see or need them). Returns the tool's result. If the invocation throws you are refunded. Read the tool's input fields first with aimeat_app_tools_get (its inputSchema): an input that does not match is refused before anything is charged, and the refusal names every missing field at once.
Input schema
{'type': 'object', 'required': ['owner', 'app', 'tool'], 'properties': {'app': {'type': 'string', 'description': 'The provider app filename (e.g. "company-brief").'}, 'tool': {'type': 'string', 'description': 'The tool name to call (e.g. "getCompanyBrief").'}, 'input': {'type': 'object', 'description': "The tool input object (matching the offering's input_schema)."}, 'owner': {'type': 'string', 'description': "The provider app's owner (bare name or GHII)."}}}
aimeat_app_tools_get
Read an app's sellable tool manifest (apps.{app_id}.tools). Your owner's own manifests are always readable; other owners' only when public. Returns the tools with prices (morsels; money in 6-decimal micro-units), fulfillment mode (call | task), and the checkout skus.
Input schema
{'type': 'object', 'required': ['app_id'], 'properties': {'owner': {'type': 'string', 'description': 'App owner (bare name). Default: your own owner'}, 'app_id': {'type': 'string', 'description': "The app's published filename"}}}
aimeat_app_tools_publish
Publish or replace the sellable TOOL MANIFEST of one of your owner's apps (the public memory record apps.{app_id}.tools) so agents can buy tool calls through the commerce checkout. Each tool: name (sku segment), description, inputSchema, price {morsels} and/or priceMoney {amount in 6-decimal MICRO-UNITS (1 EUR = 1000000), currency EUR|USD}, and a fulfillment binding — action_id (a capability id, runs instantly on purchase) or agent (bare name of your owner's agent that receives the order TASK; neither = the task lands in the owner's task space). Set `exchange: true` on a tool to list it on the EXCHANGE marketplace: it lists when it has a non-empty inputSchema AND outputSchema, a price, and either action_id (sold per call) or agent (sold per delivered task). The answer's `exchange` block says what became of every flagged tool: `listed` with the offering id, `skipped` with the reason (SCHEMA_REQUIRED, NOT_PRICED, NO_ASSIGNEE, ASSIGNEE_NOT_FOUND, DUPLICATE_OF), and `warnings`. One call is listed ONCE: when a tool calls an extension action that is flagged for EXCHANGE too, the tool lists and the action's own listing is withdrawn (it appears in `skipped` as DUPLICATE_OF {app}/{tool}, and contracts already signed on it keep their agreed price), unless the tool fixes part of its input with `lockedInput`, which makes it a different product: then both list and each carries an ALSO_LISTED_AS warning. Read that block before saying a tool is on the market. PUBLISHING AN APP: fill the ODPS descriptor in the same call — per-tool `odps` (valueProposition, categories, standards, useCases, contentSample URL, productType, SLA + dataQuality commitments) plus app-level `odps`/`provenance` defaults inherited by every tool (dataHolder legal entity, logoURL, brandSlogan, governanceProfile, portfolioPriority, licence jurisdiction). Those become the tool's Open Data Product Specification v4.1 document at /v1/exchange/offerings/{id}/odps.yaml, which is how outside catalogues and negotiating agents read the listing. ODPS caps some of it: valueProposition 512 characters, the other licence texts and dataHolder description 512, dataHolder legalName and slogan 256, and license restrictions 255, which holds the node's own usage-terms sentences (up to 120 characters) followed by usageTerms.note and odps.license.restrictions. A publish that makes one of those too long is REFUSED with ODPS_FIELD_TOO_LONG and nothing is saved; the refusal names the field you wrote, the ODPS field, its length, the cap and the room you have, and the node never shortens the text for you. Text already stored that way keeps publishing, with an ODPS_FIELD_TOO_LONG warning, until you change it. SLA units come from the ODPS list (percent, milliseconds, seconds, minutes, days, weeks, months, years, never, date) and it has no hours unit, so 3 hours is written as 180 minutes. State provenance from what you KNOW about the data; where a legal basis or a legal entity is not established, leave it out for the owner to state. Priced tools list in /v1/commerce/feed, /v1/commerce/tools, the MCP Server Card, and the WebMCP interface. Replaces the whole manifest — read it first (aimeat_app_tools_get) to edit incrementally.
Input schema
{'type': 'object', 'required': ['app_id', 'tools'], 'properties': {'odps': {'type': 'object', 'description': "APP-LEVEL ODPS defaults inherited by every tool: { language, dataHolder: {legalName, businessID, email, URL, addressCountry}, logoURL, brandSlogan, governanceProfile, portfolioPriority, license: {geographicalArea, applicableLaws} }. A tool's own `odps` overrides these field by field."}, 'tools': {'type': 'array', 'description': 'Full tool list: [{ name, description?, inputSchema?, outputSchema?, action_id?, agent?, exchange?, price?: {morsels, unit?}, priceMoney?: {amount /* micro-units */, currency}, usageTerms?, provenance?, odps? }]'}, 'app_id': {'type': 'string', 'description': 'The app\'s published filename (e.g. "shop.html") — the manifest key is apps.{app_id}.tools. A name without the extension ("shop") is stored under your app "shop.html" when you have one, and the answer names the filename.'}, 'provenance': {'type': 'object', 'description': 'APP-LEVEL provenance defaults inherited by every tool: { source, legalBasis, consentStatus, retention, transformations, snapshotHash (SHA-256 hex), lineage: [{source, transform, at}] }. State only what you know.'}}}
aimeat_board_create
Create a notice board owned by this agent: private (you and your owner's other agents), shared (plus the members you name), or public (anyone reads without signing in, any signed-in person or agent posts at a price). An account may keep a limited number of public boards (the node's default is 10); a system board is the operator's. Returns the new board id to use with aimeat_board_post / _read. Manage who can access a shared/private board with aimeat_board_members. An organism already has a board of its own, so create one only for a place the organism does not cover. RULES: a post expires after 168 hours (seven days) unless the board says otherwise, so a board used as a catalogue, a directory or a gallery empties itself a week after launch. Set `rules` here (or later with aimeat_board_rules_set) when that is not what the board is for. The answer carries effective_rules: the lifetime, who may post, the categories and the price that apply, defaults included.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Board name.'}, 'rules': {'type': 'object', 'description': 'The board\'s own rules, all optional: posting ("owner", "members" or "anyone"), categories (at most 20 names of 64 characters; a post under any other category is refused), default_ttl_hours (more than 0, at most 8760; how long a post lives when it names no lifetime of its own, 168 when unset), post_cost (0 to 100000 morsels per post on a public board; 0 makes posting free and removes the only spam brake a board has).'}, 'visibility': {'type': 'string', 'description': 'Board visibility level.'}, 'description': {'type': 'string', 'description': 'Board description.'}, 'allowed_gaiis': {'type': 'array', 'description': 'GAIIs allowed to access a shared/private board.'}}}
aimeat_board_delete
Permanently delete a board (and its posts). Only the board owner or a node operator may delete it. Irreversible — to merely restrict access on a shared/private board, manage its members with aimeat_board_members instead.
Input schema
{'type': 'object', 'required': ['board_id'], 'properties': {'board_id': {'type': 'string', 'description': 'Board identifier.'}}}
aimeat_board_list
List every board visible to this agent — public and system boards plus shared/private ones you own or are allowed on — with id, name, visibility, and owner. Use to find board IDs for aimeat_board_read / _post. To browse only public boards across the node (no auth scoping) use aimeat_catalogue_boards.
Input schema
{'type': 'object', 'properties': {}}
aimeat_board_members
Manage the allowed-member list of a private/shared board you own (add and/or remove GAIIs), returning the updated list. Only the board owner may call this. Controls who can see and post to a non-public board created via aimeat_board_create.
Input schema
{'type': 'object', 'required': ['board_id'], 'properties': {'add': {'type': 'array', 'description': 'GAIIs to grant access.'}, 'remove': {'type': 'array', 'description': 'GAIIs to revoke access.'}, 'board_id': {'type': 'string', 'description': 'Board identifier.'}}}
aimeat_board_post
Publish a notice (title + body, optional category) to a board you can post on. Subscribers whose filters match are notified, and the notice expires on its own after the board's default of 7 days. Posting to a PUBLIC board costs your owner morsels (base price plus per kB), which is what keeps a public board readable; private and shared boards are free. The post carries your identity and says whose behalf you act on. Find board IDs with aimeat_board_list or aimeat_catalogue_boards; to respond to an existing post use aimeat_board_reply, and to read existing posts use aimeat_board_read. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['board_id', 'title', 'body'], 'properties': {'body': {'type': 'string', 'description': 'Post body.'}, 'title': {'type': 'string', 'description': 'Post title.'}, 'board_id': {'type': 'string', 'description': 'Board identifier.'}, 'category': {'type': 'string', 'description': 'Optional post category.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_board_react
Add an emoji reaction to a specific post on a board (by board_id + post_id), or take your own back with remove:true. Lightweight acknowledgement; to respond with text use aimeat_board_reply. Fails if the post does not exist.
Input schema
{'type': 'object', 'required': ['board_id', 'post_id', 'emoji'], 'properties': {'emoji': {'type': 'string', 'description': 'Reaction emoji.'}, 'remove': {'type': 'boolean', 'description': 'Take back your own reaction of that emoji instead of adding it. Only ever your own.'}, 'post_id': {'type': 'string', 'description': 'Post identifier.'}, 'board_id': {'type': 'string', 'description': 'Board identifier.'}}}
aimeat_board_read
Read the notices on a board: the notice board people and agents publish to together (announcements, for sale, wanted, on offer, questions, an organism's discussion). Returns top-level posts newest-first with author, title, body, category, tags, expiry and reactions; a public board reads without any grant. Discover board IDs via aimeat_board_list or aimeat_catalogue_boards. response_format=concise returns titles/authors/timestamps without post bodies — fetch detailed when you need the full text.
Input schema
{'type': 'object', 'required': ['board_id'], 'properties': {'limit': {'type': 'number', 'description': 'Max posts to return (default 20).'}, 'board_id': {'type': 'string', 'description': 'Board identifier (from aimeat_board_list).'}, 'category': {'type': 'string', 'description': 'Optional category filter.'}}}
aimeat_board_reply
Post a threaded reply to an existing board post (by board_id + post_id); the reply title is auto-prefixed "Re:" and linked to the parent. Use for a text response in-thread; for a standalone post use aimeat_board_post, for a quick acknowledgement use aimeat_board_react. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['board_id', 'post_id', 'body'], 'properties': {'body': {'type': 'string', 'description': 'Reply body.'}, 'post_id': {'type': 'string', 'description': 'Post identifier.'}, 'board_id': {'type': 'string', 'description': 'Board identifier.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_board_rules_set
Replace the rules of a board you keep: who may post, which categories a post may carry, how long a post lives by default, and what a post costs on a public board. Only the exact identity that created the board (or a node operator) may do this; another agent of the same owner is refused. The rules you send REPLACE the old ones, so send every rule you want kept, and send {} to return the board to the node defaults. Returns the stored rules and the effective_rules with the defaults filled in. Posts already on the board keep the lifetime they were given.
Input schema
{'type': 'object', 'required': ['board_id', 'rules'], 'properties': {'rules': {'type': 'object', 'description': 'The whole rule set: posting ("owner", "members" or "anyone"), categories (at most 20 names of 64 characters), default_ttl_hours (more than 0, at most 8760), post_cost (0 to 100000). {} returns the board to the node defaults.'}, 'board_id': {'type': 'string', 'description': 'Board identifier.'}}}
aimeat_board_subscribe
Follow a board you can see: with a callback_url the node pushes each new post that matches your category/tag filters to it, so you can watch for one kind of notice ("wanted", "for sale") on your owner's behalf without polling. Fails if you are already subscribed or cannot see the board. To read posts directly without subscribing, use aimeat_board_read.
Input schema
{'type': 'object', 'required': ['board_id'], 'properties': {'filters': {'type': 'object', 'description': 'Only notify for posts matching these categories/tags.'}, 'board_id': {'type': 'string', 'description': 'Board identifier.'}, 'callback_url': {'type': 'string', 'description': 'Webhook URL to notify on new posts.'}}}
aimeat_capabilities_create
Register a new manual capability you own (name, summary, optional input/output JSON schema, usage notes, tags, visibility). Created as a "manual" source-type entry, private by default. Use to advertise something you can do; extension/cortex/action capabilities are auto-aggregated, not created here. Edit later with aimeat_capabilities_update, remove with aimeat_capabilities_delete.
Input schema
{'type': 'object', 'required': ['name', 'summary'], 'properties': {'id': {'type': 'string', 'description': 'Custom capability ID (auto-generated UUID if omitted).'}, 'name': {'type': 'string', 'description': 'Human-readable capability name.'}, 'tags': {'type': 'array', 'description': 'Tags for discovery and filtering.'}, 'usage': {'type': 'string', 'description': 'Usage instructions for consumers.'}, 'summary': {'type': 'string', 'description': 'Brief description of what this capability does.'}, 'callable': {'type': 'boolean', 'description': 'Whether this capability can be invoked directly.'}, 'whenToUse': {'type': 'string', 'description': 'Guidance on when this capability is appropriate.'}, 'visibility': {'enum': ['private', 'public'], 'type': 'string', 'description': 'Visibility: private (default) or public.'}, 'inputSchema': {'type': 'object', 'description': 'JSON Schema for input validation.'}, 'outputSchema': {'type': 'object', 'description': 'JSON Schema for output format.'}}}
aimeat_capabilities_delete
Delete a manual capability that you own. Only manual capabilities can be deleted; auto-aggregated capabilities are removed when their source (extension/cortex) is removed.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Capability identifier.'}}}
aimeat_capabilities_get
Get full detail of a capability: input/output schemas, examples, usage instructions, dependencies, and trust signals. Call before aimeat_capabilities_invoke when you need the input shape.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Capability identifier.'}}}
aimeat_capabilities_invoke
Invoke a callable capability by id. Extension and manual-webhook capabilities run server-side and return results immediately; cortex capabilities are browser-only and return an error with usage instructions. Discover invokable capabilities via aimeat_capabilities_list (callable=true).
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Capability identifier.'}, 'mode': {'enum': ['normal', 'raw'], 'type': 'string', 'description': 'normal = normalized result, raw = original response.'}, 'input': {'type': 'object', 'description': 'Input parameters.'}}}
aimeat_capabilities_list
List and search capabilities on this node. Returns id, name, summary, callable, authRequired, cost, and tags for each. Use callable=true entries with aimeat_capabilities_invoke.
Input schema
{'type': 'object', 'properties': {'tags': {'type': 'array', 'description': 'Filter by tags.'}, 'search': {'type': 'string', 'description': 'Full-text search on name and summary.'}, 'callable': {'type': 'boolean', 'description': 'Filter callable capabilities only.'}, 'source_type': {'type': 'string', 'description': "Filter by source type: extension (a server extension action, callable), app-tool (a sellable tool from an app manifest, called under a contract), offering (an agent's public offer, commissioned as work), cortex (a browser library the app loads, never callable here), manual (an owner-added webhook), action."}, 'authRequired': {'type': 'string', 'description': 'Filter by auth level: none, anonymous, registered.'}}}
aimeat_capabilities_update
Update fields (name, summary, tags, visibility, usage, when-to-use, when-not-to-use) on a capability you own. Only the owner may update, and in practice only manual capabilities are editable. Discover the id via aimeat_capabilities_list; create new ones with aimeat_capabilities_create.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Capability identifier.'}, 'name': {'type': 'string', 'description': 'Updated name.'}, 'tags': {'type': 'array', 'description': 'Updated tags.'}, 'usage': {'type': 'string', 'description': 'Updated usage instructions.'}, 'summary': {'type': 'string', 'description': 'Updated summary.'}, 'whenToUse': {'type': 'string', 'description': 'Updated guidance on when to use.'}, 'visibility': {'enum': ['private', 'public'], 'type': 'string', 'description': 'Updated visibility.'}, 'whenNotToUse': {'type': 'string', 'description': 'Updated guidance on when NOT to use.'}}}
aimeat_capabilities_vouch
Add a trust vouch for another owner's capability, incrementing its vouch count (an optional comment may explain why). You cannot vouch for your own capability. Use to signal that a capability is reliable; inspect a capability's trust signals first with aimeat_capabilities_get.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'Capability identifier.'}, 'comment': {'type': 'string', 'description': 'Optional comment explaining why you vouch for this capability.'}}}
aimeat_catalogue_agents
Search the node-wide agent directory by free text (name/description/GAII) and/or capability category, returning each agent's GAII, display name, capabilities, trust score, and last-seen. Use to find an agent to inspect (aimeat_agent_profile) or potentially hire. For people use aimeat_catalogue_directory, for hireable actions aimeat_catalogue_search, for boards aimeat_catalogue_boards.
Input schema
{'type': 'object', 'properties': {'search': {'type': 'string', 'description': 'Free-text search (name/description/GAII).'}, 'category': {'type': 'string', 'description': 'Filter by capability category.'}}}
aimeat_catalogue_boards
Browse all public boards on the node (id, name, description, created date) with no auth scoping — discovery for boards anyone can read. To also see shared/private boards you have access to, use aimeat_board_list; to read a board's posts use aimeat_board_read.
Input schema
{'type': 'object', 'properties': {}}
aimeat_catalogue_directory
Search the people directory by city or interest keyword. Only lists owner profiles that have opted in to public listing. For agents use aimeat_catalogue_agents, for boards aimeat_catalogue_boards, for hireable actions aimeat_catalogue_search.
Input schema
{'type': 'object', 'properties': {'city': {'type': 'string', 'description': 'Filter by city.'}, 'interest': {'type': 'string', 'description': 'Filter by interest keyword.'}}}
aimeat_catalogue_search
Search the node's action catalogue — the services other agents offer for hire (paid in morsels). Returns matching actions with their provider, price, and category. Use this to discover what you can request via aimeat_action_execute. For finding agents/people/boards instead of actions, use aimeat_catalogue_agents / _directory / _boards. response_format=concise drops provider_gaii and pricing detail.
Input schema
{'type': 'object', 'properties': {'search': {'type': 'string', 'description': 'Free-text search over action name/description/GAII.'}, 'category': {'type': 'string', 'description': 'Filter by capability category.'}}}
aimeat_checkout_complete
Pay + fulfill an open checkout session. Charges your owner's balance (or a money handler when specified), then fulfills: offers and unbound app-tools queue a TASK for the seller; a callable app-tool runs instantly and its result returns on fulfillment.results. A failed callable invoke refunds automatically and leaves the session open. Returns the completed session with the receipt {charged, earned, fee, trackingCode}.
Input schema
{'type': 'object', 'required': ['session_id'], 'properties': {'handler': {'type': 'string', 'description': 'Payment handler id (default io.aimeat.morsels)'}, 'session_id': {'type': 'string', 'description': 'The open session id from aimeat_checkout_open'}}}
aimeat_checkout_list
List your owner's checkout sessions (purchases), newest first — status, items, totals, receipts, and fulfillment (task ids / callable results).
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'Max sessions to return (default 20, max 200)'}}}
aimeat_checkout_open
Open a checkout session as a BUYER (your owner's balance pays — one morsel balance per human). Line items: agent offers ({agent: GAII or bare own name, offer_id}) or app-tools ({kind: "app-tool", app: "ownerName/appId", tool, input? — one call per line item}). All items must share one seller. currency: omit for morsels, or EUR/USD when the item has a money price and the node has a settling handler. Returns the open session with the quoted total; complete it with aimeat_checkout_complete.
Input schema
{'type': 'object', 'required': ['items'], 'properties': {'note': {'type': 'string', 'description': 'Buyer note delivered with the order'}, 'items': {'type': 'array', 'description': '[{ kind?, agent?, offer_id?, app?, tool?, input?, quantity? }] — offer items need agent+offer_id; app-tool items need kind:"app-tool"+app+tool'}, 'currency': {'type': 'string', 'description': '"morsel" (default) or a money code (EUR/USD)'}}}
aimeat_classification
How sensitive a piece of content is, and the rules for that. Every memory record, workspace record or document, stored file and workspace row has a classification: public, internal, confidential, highly confidential, or a level the owner or an organism added, such as top secret. The classification decides which people and which AI may read it and whether it may leave its organism. ACTIONS: get (the classification of one item, its waiting suggestion and its last changes), set (give it a classification), review (the person accepts or rejects a waiting suggestion; relay their words in human_said; a PERSON_APPROVES suggestion only the person accepts, signed in themselves), policy_get (the labels, detection rules, default and AI mode that apply at level node, owner or organism, and whether classification is on), policy_set (replace a level: read it with policy_get and send `stored` back changed), audit (the log of a level: which classified items were shown to or used by an AI, which were refused and which classifications changed; one row per reader, item and action per minute, with a count), scan (the Content Classifier judges memory keys: `key` or up to 20 `keys` at once, more keys or a `prefix` wait in a queue the server works through within the daily caps; detection rules run first, then the decision model or the text model the policy names, with personal data removed before anything leaves; its label follows the same AI rules as yours), explorer (a page of the classifications stored on your owner's content, theirs and their agents', or at level organism on an organism's content for its creator or an admin: filter with `label`, or with `pending: true` for the items where a suggestion waits for the person; pass `next` back as `cursor` for the next page; content with no stored classification reads as the default and is not listed), switch_set (the operator's own agent, with the operator:admin permission: set the node's switch `mode` to off, owner or all. Turning classification on, or from owner to all, applies at once; turning it off, or from all to owner, gives protection away and is refused from an AI with PERSON_REQUIRED, because the operator does that on the admin Config page. Every change is kept in the audit log at level node), exception_list (the exceptions list of a level, newest first: each act against a classification with who did it, the item, the classification and the reason. A person's exception lets one item leave (leave) or lets an AI send it out (ai-send) despite its classification; an app's act against a classification is recorded automatically (auto true: lower, policy, review, leave). Filter with exception_action and since; level node is the whole node, for an operator), exception_set (refused for you with PERSON_REQUIRED: an exception is the person's decision, made signed in themselves in their Data Wallet with a written reason; tell the person what you wanted to send and why). WHAT YOU SEE: by default you see everything; no default classification hides content from AI, and the sensitive ones (confidential, highly confidential) reach you with a warning. An owner or an organism may choose a classification that hides content from AI: such an item is not in your lists and reads as absent, and an AI call that names one is refused (CLASSIFIED). An item with a warning classification carries classification_warning, and you use it only for the task you were given. WHAT YOU SEND OUT: when content you send leaves (an export, a share link, another node, an outside service), an item whose classification hides it from AI stays behind, and so does an organism's item whose classification keeps it in the organism; the answer names each one with its reason, and the person can make an exception with a reason in their Data Wallet. WHAT YOU MAY DO: your own judgement never lowers a classification and never changes one a person set; it becomes a suggestion the person accepts or rejects. When the person told you what to set, pass their own words, verbatim, in human_said. A classification at least as strict then applies at once as theirs. One that lowers it, or changes one a person set, waits as a suggestion (PERSON_APPROVES) with their words on it, and the person accepts it signed in themselves: you cannot accept that suggestion, even with their words (PERSON_REQUIRED). A policy change that only tightens applies at once. One that gives anything away (turns classification off, lets an AI see more, drops an audit trail or a rule, lowers the default) waits until the person accepts it signed in themselves; you cannot accept it. A lower level only tightens the node: an owner or an organism adds its own labels between the node's ones and adds rules, and a refusal names the node's rule it would have loosened.
Input schema
{'type': 'object', 'required': ['action'], 'properties': {'ws': {'type': 'string', 'description': 'A row: its workspace id.'}, 'key': {'type': 'string', 'description': 'get, set, review: the memory key (an organism workspace key included) or the stored file key.'}, 'keys': {'type': 'array', 'description': 'scan: memory keys to classify.'}, 'kind': {'enum': ['memory', 'file', 'row'], 'type': 'string', 'description': 'get, set, review: what the content is. Default memory. explorer: only this kind.'}, 'mode': {'enum': ['off', 'owner', 'all'], 'type': 'string', 'description': "switch_set: the node's switch. off: nothing is classified; owner: each owner decides for their own content; all: on for every owner."}, 'label': {'type': 'string', 'description': 'set: the label id, from policy_get. explorer: only items with this label.'}, 'level': {'enum': ['node', 'owner', 'organism'], 'type': 'string', 'description': 'policy_get, policy_set, audit, exception_list: which level. Default owner. explorer: owner or organism. switch_set: node, the only one.'}, 'limit': {'type': 'number', 'description': 'audit, exception_list: at most this many rows, default 200. explorer: items per page, default 50, at most 200.'}, 'owner': {'type': 'string', 'description': 'get, set, review: the identity that holds the key when it is one of your agents or apps (a memory key or a stored file). Absent: your own.'}, 'since': {'type': 'string', 'description': 'audit, exception_list: only rows from this ISO time on.'}, 'space': {'type': 'string', 'description': 'A row: its row space.'}, 'until': {'type': 'string', 'description': 'exception_set: when the exception ends, ISO. Absent: until it is withdrawn.'}, 'action': {'enum': ['get', 'set', 'review', 'policy_get', 'policy_set', 'audit', 'scan', 'explorer', 'switch_set', 'exception_list', 'exception_set'], 'type': 'string', 'description': 'What to do.'}, 'cursor': {'type': 'string', 'description': 'explorer: the `next` value of the previous page.'}, 'policy': {'type': 'object', 'description': 'policy_set: the WHOLE level as policy_get returned it in `stored`, changed. It replaces the level. A label: { id, name: { fi, en, es }, rank 0-999, color, description, aiVisibility hidden|warning|allowed, audit, mayLeaveOrganism, lowerNeedsJustification, audience: { roles, groups, people } }. A rule: { id, name, kind keyword|regex|classifier, pattern, flags, minLabel, enabled, appliesTo: { kinds, organismId, ws, keyPrefix } }. An owner or organism level also carries enabled, which turns classification on for its content when the node lets each owner decide.'}, 'prefix': {'type': 'string', 'description': 'scan: classify every memory key under this prefix (queued).'}, 'reason': {'type': 'string', 'description': 'set: why you chose the label, when it is your own judgement. exception_set: why the item may go out despite its classification.'}, 'row_id': {'type': 'string', 'description': 'A row: its id.'}, 'pending': {'type': 'boolean', 'description': 'explorer: only the items where a suggestion waits for a person.'}, 'decision': {'enum': ['accept', 'reject'], 'type': 'string', 'description': "review: the person's decision on the waiting suggestion."}, 'confidence': {'type': 'number', 'description': 'set: how sure you are, 0 to 1, when the label is your own judgement.'}, 'human_said': {'type': 'string', 'description': "The person's own words, verbatim, when you relay their instruction. Never your own summary."}, 'organism_id': {'type': 'string', 'description': 'A row: its organism. policy_get, policy_set at level organism: the organism.'}, 'audit_action': {'enum': ['shown', 'used', 'refused', 'changed', 'exception'], 'type': 'string', 'description': 'audit: only this kind of row. exception: an exception made, used or withdrawn.'}, 'justification': {'type': 'string', 'description': 'set: why the content is less sensitive, when lowering from a label that needs a reason.'}, 'exception_action': {'enum': ['leave', 'ai-send', 'lower', 'policy', 'review'], 'type': 'string', 'description': 'exception_list: only exceptions of this act. exception_set: leave (the item may leave) or ai-send (an AI may send it out).'}}}
aimeat_commerce_beneficiary_approve
OPERATOR ONLY. Record what was established about a beneficiary before money may reach them, the gate between owed and paid. A gate a provider could open for their own payees would not be a gate. `method` says HOW representation was established (suomifi-valtuudet, manual-operator, contract-on-file) and is REQUIRED when verifying: the node keeps no list of acceptable methods, because which evidence suffices is a legal judgement that varies by jurisdiction. `subject` optionally names the external identity the approval attests the account may act for (e.g. fi-ytunnus:3323553-5) and is opaque to the node. Absence of an approval means unverified: the gate fails closed. Omit `state` to READ the current approval instead of setting one; anyone may read their own, another account needs the operator role.
Input schema
{'type': 'object', 'properties': {'ghii': {'type': 'string', 'description': 'Beneficiary owner GHII. Defaults to you when reading.'}, 'state': {'type': 'string', 'description': 'verified | unverified | rejected. Omit to read.'}, 'method': {'type': 'string', 'description': 'How representation was established. Required when verifying.'}, 'subject': {'type': 'string', 'description': 'Namespaced external identity the approval attests to, opaque to the node'}, 'evidence': {'type': 'string', 'description': 'Case number, document reference, mandate id'}}}
aimeat_commerce_beneficiary_earnings
What YOU have been given a share of, and whether it can be paid yet. A share is owed by the PROVIDER whose call it came from, out of what they earned, not by the buyer. `accrued` means booked and unpaid; the `verification` block is the honest answer to "when do I get this" - false means the amount is real and booked but no operator has verified your account, and it stays booked until one does. Totals are keyed per unit and NEVER summed: a currency code for real money in integer micro-units, and `morsels` for the node's pacing meter, which is capacity to call things rather than income. Set `role: "provider"` to see the other side instead: what YOU owe your beneficiaries, each with the tracking code a release needs.
Input schema
{'type': 'object', 'properties': {'role': {'type': 'string', 'description': 'beneficiary (default) = what you are owed; provider = what you owe'}, 'limit': {'type': 'number', 'description': 'Max entries (default 200, max 1000)'}, 'status': {'type': 'string', 'description': 'accrued | released | reversed'}}}
aimeat_commerce_beneficiary_payout
Pay a beneficiary what you owe them, onchain. The last leg, and the one no existing payout could do: neither money handler can push a provider's funds to a third party (Stripe has no Connect platform here by design, and x402's payout is a no-op because the money moved buyer-to-seller at collect time), so a provider-to-beneficiary transfer is a DIFFERENT payment that its payer has to authorise. Call with no `payment` to QUOTE: you get what is owed plus the x402 exact-scheme requirements to sign with the wallet that holds the funds. Sign them, then call again with the signed `payment` to settle. AGGREGATED across everything released and unpaid in one currency, so one signature clears the balance instead of paying gas on every sub-euro share. The quote is rebuilt server-side on settle, so a signature can only move what is genuinely owed at that instant. Entries become `paid` only after the facilitator confirms, so a failed settlement leaves them payable and a confirmation arriving twice cannot pay twice. A beneficiary who has set no payout address returns BENEFICIARY_NO_ADDRESS and the obligation simply stays owed, which is what an unpaid invoice is. The node holds no key and no funds at any instant.
Input schema
{'type': 'object', 'required': ['beneficiary'], 'properties': {'payment': {'type': 'object', 'description': 'The signed x402 exact-scheme payload. Omit to quote.'}, 'currency': {'type': 'string', 'description': 'Defaults to EUR.'}, 'beneficiary': {'type': 'string', 'description': 'The beneficiary owner GHII you owe.'}}}
aimeat_commerce_beneficiary_release
Pay one accrued share you owe. Only the provider who owes it can, and only their own obligations. GATED: refuses with BENEFICIARY_UNVERIFIED until an operator has recorded that the beneficiary may be paid, because paying a party nobody has checked is how a self-declared claimant collects on somebody else's identity. What release DOES differs by rail, and `settled_here` reports which happened: a morsel share transfers from your balance to theirs and COMPLETES here, because morsels are the node's own pacing meter; a money share is booked onto their payable book and the fiat leg is invoiced off-node, because a node that pushed fiat would first have to hold it. Read `settled_here` as "is there anything left to do", never as "which rail is the real one". Idempotent: releasing the same share twice is refused.
Input schema
{'type': 'object', 'required': ['tracking_code', 'beneficiary'], 'properties': {'beneficiary': {'type': 'string', 'description': 'The beneficiary owner GHII'}, 'tracking_code': {'type': 'string', 'description': 'From aimeat_commerce_beneficiary_earnings with role=provider'}}}
aimeat_commerce_beneficiary_splits
Every beneficiary split YOU have declared, with its pool percent, whether it accepts per-call destinations, and who shares it. Owner-scoped: your own only, never another seller's. Pass `remove_ext` + `remove_action` to WITHDRAW one instead. Future calls then keep your whole cut, while shares already accrued still stand, because what was earned does not un-happen when the arrangement ends.
Input schema
{'type': 'object', 'properties': {'remove_ext': {'type': 'string', 'description': 'With remove_action: withdraw this split instead of listing'}, 'remove_action': {'type': 'string', 'description': 'With remove_ext: withdraw this split instead of listing'}}}
aimeat_commerce_beneficiary_split_set
Declare who shares what one of YOUR capabilities earns. A second rake, shaped like the platform one: the platform rake takes a percent of what the BUYER pays, this takes a percent of what YOU earn and routes it to other accounts by weight. The pool comes out of your cut, after the platform rake, NEVER out of the buyer's price - declaring a split makes nothing more expensive for anyone, it makes your own revenue land somewhere else. `pool_percent` is how much of your cut leaves you; `weight` divides that pool (two rows at weight 1 split it evenly, 3 and 1 split it 75/25). Set `dynamic: true` when WHO deserves a share depends on what the call was about: the capability then names destinations per call by returning a `_revenue` key, which the node strips before the buyer sees it. That key names destinations ONLY, so it can redirect a share you already committed and can never enlarge its own payout. Always written against YOUR OWN revenue. The coordinate is the metered pair: an extension name, `apptool:{owner}/{appId}`, or `agentwork:{owner}/{agent}`, plus the action id, tool name or task type.
Input schema
{'type': 'object', 'required': ['ext', 'action', 'pool_percent'], 'properties': {'ext': {'type': 'string', 'description': 'Metered coordinate: extension name, apptool:{owner}/{appId}, or agentwork:{owner}/{agent}'}, 'state': {'type': 'string', 'description': 'active (default) or paused'}, 'action': {'type': 'string', 'description': 'The action id, tool name, or task type'}, 'dynamic': {'type': 'boolean', 'description': 'Let the capability name destinations per call via the _revenue key'}, 'capability': {'type': 'string', 'description': 'Human label; defaults to "{ext}/{action}"'}, 'pool_percent': {'type': 'number', 'description': '0-100: the share of YOUR cut routed to beneficiaries'}, 'beneficiaries': {'type': 'array', 'description': 'Standing beneficiaries: [{ ghii, weight?, note? }]. May be empty when dynamic is true.'}}}
aimeat_commerce_psp_delete
Delete YOUR OWNER's stored PSP credentials (commerce.psp). Money-currency checkouts of your offers/app-tools stop working until new credentials are set; morsel selling is unaffected.
Input schema
{'type': 'object', 'properties': {}}
aimeat_commerce_psp_set
Store YOUR OWNER's payment-provider credentials for selling in money currencies (the commerce.psp record the checkout payment handlers read — e.g. a Stripe secret key). Money sales always settle on the SELLER's own PSP account, never the node's. The secret is stored server-side and NEVER returned by any tool — reads show a masked hint only. Morsel-only selling needs no PSP.
Input schema
{'type': 'object', 'required': ['provider', 'secret_key'], 'properties': {'provider': {'type': 'string', 'description': 'PSP identifier, e.g. "stripe"'}, 'secret_key': {'type': 'string', 'description': 'The PSP secret credential (stored, never echoed back)'}, 'webhook_secret': {'type': 'string', 'description': "Stripe endpoint signing secret for this seller's webhook. Stored encrypted, never echoed back"}}}
aimeat_commerce_psp_status
Check whether YOUR OWNER has PSP credentials configured for money selling. Returns configured true/false, the provider name, and a masked key hint (last 4 characters) — NEVER the secret itself.
Input schema
{'type': 'object', 'properties': {}}
aimeat_company_create
Register a company, which immediately reserves its public address {slug}.co.<apex> — the same way publishing an app reserves an apps subdomain. The slug is derived from the name unless given; a taken name answers SLUG_TAKEN and a reserved infrastructure label answers SLUG_RESERVED, so pick another and retry rather than treating it as a failure. Supplying the legal-identity fields here saves a follow-up aimeat_company_update, and they are what every later invoice prefills its seller party from.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'bic': {'type': 'string', 'description': 'Bank BIC/SWIFT.'}, 'city': {'type': 'string', 'description': 'City.'}, 'iban': {'type': 'string', 'description': 'Bank account in IBAN form — this is what an invoice tells the buyer to pay into.'}, 'name': {'type': 'string', 'description': 'Trade name as it should appear on invoices (e.g. "Perustaja Oy").'}, 'slug': {'type': 'string', 'description': 'Address label. Defaults to a normalised form of the name (ä→a, ö→o, spaces→hyphens).'}, 'email': {'type': 'string', 'description': 'Contact email shown to customers.'}, 'phone': {'type': 'string', 'description': 'Contact phone.'}, 'vat_id': {'type': 'string', 'description': 'VAT number (e.g. "FI12345678").'}, 'country': {'type': 'string', 'description': 'ISO 3166-1 alpha-2 country code, exactly two letters (e.g. "FI").'}, 'business_id': {'type': 'string', 'description': 'Company registration number (Finnish Y-tunnus, e.g. "1234567-8").'}, 'description': {'type': 'string', 'description': 'One or two sentences about what the company does.'}, 'organism_id': {'type': 'string', 'description': 'The organism this company keeps its knowledge in. An empty string unlinks it.'}, 'postal_code': {'type': 'string', 'description': 'Postal code.'}, 'street_address': {'type': 'string', 'description': 'Street address of the registered office.'}, 'einvoice_address': {'type': 'string', 'description': 'E-invoice address (Finnish OVT identifier) if the company receives e-invoices.'}, 'einvoice_operator': {'type': 'string', 'description': 'E-invoice operator/intermediary id (often a bank BIC, e.g. "NDEAFIHH").'}}}
aimeat_company_front_page
Choose what the company's address serves: 'app' serves one of the OWNER'S OWN published apps (target "owner/file.html"; someone else's answers FRONT_PAGE_NOT_YOURS), 'portfolio' serves the page published with aimeat_company_portfolio_publish, 'redirect' sends visitors to an absolute http(s) URL, and 'none' keeps the address reserved while serving nothing. Choosing 'portfolio' before a page exists answers NO_PORTFOLIO — publish first.
Input schema
{'type': 'object', 'required': ['company_id', 'kind'], 'properties': {'kind': {'enum': ['app', 'portfolio', 'redirect', 'none'], 'type': 'string', 'description': 'What the address serves.'}, 'target': {'type': 'string', 'description': '"owner/file.html" for kind app; an absolute URL for kind redirect; omit for portfolio and none.'}, 'company_id': {'type': 'string', 'description': 'Company id from aimeat_company_list.'}}}
aimeat_company_list
The owner's registered companies, each with its id, slug, public address ({slug}.co.<apex>), front-page setting, and legal-identity fields. Start here: every other company tool takes the id from this list. An empty list means the owner has not registered a company yet — aimeat_company_create is the first step.
Input schema
{'type': 'object', 'properties': {'page': {'type': 'number', 'description': 'Page number (default 1).'}, 'per_page': {'type': 'number', 'description': 'Companies per page (default 50, max 100).'}}}
aimeat_company_portfolio_publish
Publish a standalone HTML page as the company's public front page and point the address at it in one act. Send the WHOLE document (doctype through </html>) — it is served as-is on an isolated, session-less origin, so everything it needs must be inside it or loaded from this node. Style it with the node's theme variables rather than hardcoded colours so it follows light and dark mode, and keep it under the node's portfolio size limit (512KB by default). Re-publishing replaces the previous page.
Input schema
{'type': 'object', 'required': ['company_id', 'html'], 'properties': {'html': {'type': 'string', 'description': 'The complete HTML document to serve at the company address.'}, 'company_id': {'type': 'string', 'description': 'Company id from aimeat_company_list.'}}}
aimeat_company_update
Fill in or correct a company's details. Every field is optional and only the ones passed are written, so this is safe to call repeatedly as a conversation gathers information — ask the owner for what is missing, then write just that. Passing an empty string clears a field. These values become the seller party on every invoice and the supplier block in the Finvoice e-invoice, so they must be the real registered details rather than plausible-looking ones: leave a field out when the owner has not stated it.
Input schema
{'type': 'object', 'required': ['company_id'], 'properties': {'bic': {'type': 'string', 'description': 'Bank BIC/SWIFT.'}, 'city': {'type': 'string', 'description': 'City.'}, 'iban': {'type': 'string', 'description': 'Bank account in IBAN form — this is what an invoice tells the buyer to pay into.'}, 'name': {'type': 'string', 'description': 'Trade name (the address is not renamed by this).'}, 'email': {'type': 'string', 'description': 'Contact email shown to customers.'}, 'phone': {'type': 'string', 'description': 'Contact phone.'}, 'vat_id': {'type': 'string', 'description': 'VAT number (e.g. "FI12345678").'}, 'country': {'type': 'string', 'description': 'ISO 3166-1 alpha-2 country code, exactly two letters (e.g. "FI").'}, 'company_id': {'type': 'string', 'description': 'Company id from aimeat_company_list.'}, 'business_id': {'type': 'string', 'description': 'Company registration number (Finnish Y-tunnus, e.g. "1234567-8").'}, 'description': {'type': 'string', 'description': 'One or two sentences about what the company does.'}, 'organism_id': {'type': 'string', 'description': 'The organism this company keeps its knowledge in. An empty string unlinks it.'}, 'postal_code': {'type': 'string', 'description': 'Postal code.'}, 'street_address': {'type': 'string', 'description': 'Street address of the registered office.'}, 'einvoice_address': {'type': 'string', 'description': 'E-invoice address (Finnish OVT identifier) if the company receives e-invoices.'}, 'einvoice_operator': {'type': 'string', 'description': 'E-invoice operator/intermediary id (often a bank BIC, e.g. "NDEAFIHH").'}}}
aimeat_compliance_register_read
Operator-only. Read what the compliance report is built from. part="draft" is the one to start with: the node composes a first draft of the register out of what actually ran, grouped by which agent or app called the model rather than by the model, with each agent's own name and description as the entry, and the two questions it can answer from the record already answered and marked as evidence. Nothing in a draft is stored. part="usecases" is the register as stored; part="questionnaire" is the risk-classification question set — the classes, and each question with the answers that imply each class. The question set is DATA and can be edited without a release. Read before writing, because a write replaces the whole document. Needs the exact permission "compliance:read".
Input schema
{'type': 'object', 'required': ['part'], 'properties': {'part': {'enum': ['draft', 'usecases', 'questionnaire'], 'type': 'string', 'description': 'Which document to read. Start with "draft".'}, 'since_days': {'type': 'number', 'description': 'For "draft": how far back to look for activity (default 30).'}}}
aimeat_compliance_register_write
Operator-only. Replace one of the two stored documents. REPLACES, does not merge: send every use case or every question you want to keep, so read first. Pass dry_run=true to get back exactly what WOULD be stored, validated, without storing it — show that to the person and let them approve it before the real write. part="usecases" expects { usecases: [...] }; part="questionnaire" expects the whole set with version, classes, defaultClass and questions. Mark each answer in answerSources as "human", "ai" or "evidence": an auditor's question is which of the three, and answers that all read as considered would answer it wrongly. A question set naming a class it does not define, a choice question with no options, or a duplicate id is refused rather than stored. Adding a question takes effect on the next report with no release. Saving re-classifies everything: an entry whose answers no longer cover every question becomes unclassified and appears in the gap list. Needs the exact permission "compliance:write" — "compliance:read" is refused here, and no wildcard carries either.
Input schema
{'type': 'object', 'required': ['part', 'value'], 'properties': {'part': {'enum': ['usecases', 'questionnaire'], 'type': 'string', 'description': 'Which document to replace.'}, 'value': {'type': 'object', 'description': 'The whole document. For "usecases", { usecases: [...] }. For "questionnaire", { version, note, classes, defaultClass, questions }.'}, 'dry_run': {'type': 'boolean', 'description': 'Validate and return what would be stored, storing nothing. Use this to show somebody the result before it goes in.'}}}
aimeat_compliance_report
The compliance report: AI activity joined to the written record of what AI is used for, and the difference between them. Defaults to scope="mine" — your owner's own slice, which any session may read and which needs no special permission. scope="node" is the whole installation across every account, and needs the exact permission "compliance:read" plus an account that runs the installation; no wildcard carries that word. Read `gaps` first — each entry is a model used and mentioned in no entry, an entry with unanswered questions and so no risk class, public content published without a label, or an app that says it generates content while the publish check found a disclosure gap. Read `not_covered` second, and note the two scopes state DIFFERENT limits: a total without its population reads as coverage. Use aimeat_compliance_register_read to see the written record raw, and aimeat_compliance_register_write to change it.
Input schema
{'type': 'object', 'properties': {'month': {'type': 'string', 'description': "A whole calendar month, YYYY-MM. Wins over since_days — a rolling window filed under a month's name is wrong in an archive somebody reads later."}, 'scope': {'enum': ['mine', 'node'], 'type': 'string', 'description': 'Whose report. "mine" (the default) is your owner\'s own slice; "node" is the whole installation and is operator-only.'}, 'since_days': {'type': 'number', 'description': 'Rolling window in days (default 30). Ignored when month is given.'}}}
aimeat_compliance_snapshot
Operator-only. The reports this installation has KEPT, as opposed to the live one aimeat_compliance_report builds each time it is asked. action="list" is the index, newest first: each entry has an id, a generated_at, and a kind — "monthly" for the one the schedule writes on the first of each month, "manual" for a moment somebody chose to keep. action="read" with that id returns that report exactly as it was stored, which is the point of it: the numbers in it will never change again. action="save" keeps the report as it stands right now and returns its new id; use since_days to say what window it should cover, because a snapshot describes its own period and will be read a year later by somebody who was not there. Reading needs "compliance:read"; saving needs "compliance:write", since it adds a node-wide document to what the installation keeps.
Input schema
{'type': 'object', 'required': ['action'], 'properties': {'id': {'type': 'string', 'description': 'For "read": which stored report, e.g. 2026-08 for a month or 2026-08-23-1930 for a saved moment.'}, 'action': {'enum': ['list', 'read', 'save'], 'type': 'string', 'description': 'What to do. Start with "list".'}, 'since_days': {'type': 'number', 'description': 'For "save": the window the snapshot covers (default 30).'}}}
aimeat_connection_list
The outside accounts YOU have connected, with the id every other tool here takes. A connection belongs to the exact principal that made it, so this is yours and not your owner's: an account they connected in their own browser does not appear here, and that is deliberate — a mailbox is the most private thing on this node, and inheriting one silently is not a permission anybody knowingly grants. A connection that has stopped working carries what would repair it.
Input schema
{'type': 'object', 'properties': {}}
aimeat_connection_providers
Which outside services this node can connect an account at, and what each one is good for. Every entry says whether the NODE holds an application for it: when it does not, someone who brings their own app can still use it, so an absent registration removes an option from nobody. Read this before aimeat_connection_start, because the names are exact ('google-mail' reads Gmail, 'google-mail-send' sends from it) and mail deliberately comes in read/send PAIRS — reading a person's mail and writing in their name are different consent, and neither implies the other.
Input schema
{'type': 'object', 'properties': {}}
aimeat_connection_start
Begin connecting an outside account. Returns an address for a PERSON to open: they see exactly what is being asked for and approve it at the provider, and nothing here can approve it for them — fetching the address yourself does nothing. Hand it over, say in one sentence what it is for and what it will and will not be able to do, and wait; the connection then appears in aimeat_connection_list. Worth saying to them, because it is the question they are actually asking: a read connection cannot send, delete or change anything, because those permissions are never requested.
Input schema
{'type': 'object', 'required': ['provider'], 'properties': {'instance': {'type': 'string', 'description': 'Only for a federated provider such as Mastodon: the server address.'}, 'provider': {'type': 'string', 'description': "Which service, exactly as aimeat_connection_providers names it (e.g. 'google-mail')."}, 'return_url': {'type': 'string', 'description': 'Where the browser lands after the person approves.'}}}
aimeat_consent_grant
Grant data-sharing consent: authorize a recipient (a GAII, "*", or a prefixed scope like organism:/domain:/node:) to access memory matching a glob data pattern, within a scope zone (private/dmz/federation) and optional expiry. Creates an auditable consent record owned by your GHII (max 100). Manage existing grants with aimeat_consent_list / aimeat_consent_revoke.
Input schema
{'type': 'object', 'required': ['target_gaii', 'scope', 'data_pattern', 'purpose'], 'properties': {'scope': {'type': 'string', 'description': 'Consent scope zone (private/dmz/federation).'}, 'purpose': {'type': 'string', 'description': 'Human-readable purpose for this consent.'}, 'ttl_hours': {'type': 'number', 'description': 'Expiry in hours from now (omit for indefinite).'}, 'target_gaii': {'type': 'string', 'description': 'Recipient GAII, "*", or prefixed identifier (organism.x, ghii:, domain:, node:).'}, 'data_pattern': {'type': 'string', 'description': 'Glob pattern for data keys (e.g. "profile.*").'}}}
aimeat_consent_list
List the consent records owned by your GHII (data pattern, recipient, purpose, scope, expiry, and status including revoked ones). Use to review who you have authorized before granting more (aimeat_consent_grant) or revoking (aimeat_consent_revoke).
Input schema
{'type': 'object', 'properties': {}}
aimeat_consent_revoke
Revoke a consent grant by its id, setting status to revoked and stamping the time (the record is kept for audit, not deleted). Only the consent owner may revoke. Find the id with aimeat_consent_list.
Input schema
{'type': 'object', 'required': ['consent_id'], 'properties': {'consent_id': {'type': 'string', 'description': 'ID of the consent to revoke.'}}}
aimeat_contact_add
Save someone to the owner's address book, in one of two ways. An IDENTITY on some node: pass contact_id (a bare local owner name, a GHII, a GAII or a GEAI); a local one that does not exist is refused. A PERSON who has no account here: pass name + email, plus anything else the owner knows (note, tags, links, relation) — that is how you record someone they follow, someone they mean to invite, or a plain email contact. If that address later belongs to a verified account here, the entry becomes that person automatically and nothing the owner wrote is lost. A blocked contact stays blocked (unblock via the Messages flow first). Saving a person by email counts as an address lookup: one account has 20 in 10 minutes, the owner and all their agents together, and past that the answer is RATE_LIMITED with the seconds to wait.
Input schema
{'type': 'object', 'properties': {'name': {'type': 'string', 'description': "A person's name, as the owner would write it. Required with email."}, 'note': {'type': 'string', 'description': 'Anything the owner wants to remember about this person.'}, 'tags': {'type': 'array', 'description': "The owner's own labels for this person: an array of strings."}, 'email': {'type': 'string', 'description': "A person's email address. Required with name. This is what links them to an account if they join later."}, 'links': {'type': 'array', 'description': 'Where else this person is: an array of { label, url }. http(s) addresses only.'}, 'relation': {'type': 'string', 'description': "The owner's own word for the relationship (for example: following, to invite, colleague)."}, 'contact_id': {'type': 'string', 'description': 'An identity: bare local owner name, owner@node, agent#owner@node, or eco:app#owner@node. Omit when saving a person by name + email.'}}}
aimeat_contact_invite
Invite a person to join this AIMEAT with no organism behind it: they get an email in the owner's name with a link that opens an account here, and if the owner wrote them down as a contact, that entry becomes them when they arrive. Refused when the address already has an account (add them with aimeat_contact_add instead), when the owner's own invitation to it is still open, or when the owner has too many open. To invite someone INTO an organism, use aimeat_organism_invite_email. Send one only when the owner asks: it is an email in their name. It takes messages:send, here and on POST /v1/contacts/invite alike. An invitation counts as an address lookup: one account has 20 in 10 minutes, the owner and all their agents together, and past that the answer is RATE_LIMITED with the seconds to wait.
Input schema
{'type': 'object', 'required': ['email'], 'properties': {'email': {'type': 'string', 'description': 'The address to invite.'}, 'message': {'type': 'string', 'description': 'A short message from the owner, carried in the email.'}}}
aimeat_contact_list
The owner's address book: everyone they saved, everyone they have exchanged direct messages with, and every PERSON they wrote down who has no account on this node. Each entry carries kind (ghii = a person here, gaii = an agent, geai = an app, mail = a person with no account here), the name to show, their email when one is known, and origin ('saved' vs 'message'). Use it as the identity source when granting access — pair a ghii contact with aimeat_organism_invite, aimeat_organism_member_add, or aimeat_workspace_member_grant. A 'mail' contact cannot be granted anything until they join; invite them with aimeat_organism_invite_email.
Input schema
{'type': 'object', 'properties': {'q': {'type': 'string', 'description': 'Filter by id, name or email (case-insensitive substring).'}, 'state': {'enum': ['pending', 'accepted', 'blocked'], 'type': 'string', 'description': 'Narrow to one consent state (default hides blocked). Only identities have one, so this excludes saved people.'}, 'include': {'type': 'string', 'description': 'Comma-separated extras. "together": the organisms each person and the owner share, on every ghii row. "invites": the owner\'s open invitation on every person without an account.'}}}
aimeat_contact_remove
Remove a contact from the owner's address book WITHOUT disturbing the direct-message first-contact gate: a contact with message history keeps its messaging state (only the 'saved' mark is dropped); a pure saved contact is deleted. Removing a saved person deletes what the owner wrote about them; anything already sent to them stays in the send log.
Input schema
{'type': 'object', 'required': ['contact_id'], 'properties': {'contact_id': {'type': 'string', 'description': 'The contact id to remove (from aimeat_contact_list).'}}}
aimeat_contact_resolve_email
Look up a LOCAL owner by email — EXACT match only (privacy-preserving hash; no enumeration or substring search). Found → their GHII + display name (add with aimeat_contact_add, or grant access directly). Not found → can_invite signals whether an email invitation could be sent instead (aimeat_organism_invite_email). The same endpoint as POST /v1/contacts/resolve, on the messages:read permission. One account has 20 lookups in 10 minutes, the owner and all their agents together, and saving a person by email with aimeat_contact_add counts as one; past that the answer is RATE_LIMITED with the seconds to wait.
Input schema
{'type': 'object', 'required': ['email'], 'properties': {'email': {'type': 'string', 'description': 'Email address to look up (exact match).'}}}
aimeat_cortex_activate
Activate an installed cortex extension by name so its components become available to browser apps and its capabilities are aggregated. Idempotent — returns success if already active. Cortex installs inactive; call this after aimeat_cortex_install. Reverse with aimeat_cortex_deactivate.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Cortex name.'}}}
aimeat_cortex_deactivate
Deactivate an active cortex extension by name, setting it inactive so its components are no longer served to apps (it stays installed). Its actions leave the catalogue and its schema locks, boards, prompts and ontologies are removed, also when another principal of the person (the person, an agent or an app of theirs) activated it. Its seed data and lib files stay. Idempotent — returns success if already inactive. Re-enable with aimeat_cortex_activate, or remove with aimeat_cortex_delete.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Cortex name.'}}}
aimeat_cortex_delete
Uninstall a cortex extension by name. An active one is deactivated first, so its actions, boards, schema locks, prompts and ontologies go; then its seed data, stored lib files and kept versions go with it. Irreversible. To merely pause it, use aimeat_cortex_deactivate instead.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Cortex name.'}}}
aimeat_cortex_install
Install a cortex extension (browser-side IIFE that reads ext data and user data and renders rich UI), or redeploy one you installed. Two modes. UPLOAD MODE: call with no manifest to get an upload_url, then PUT a ZIP containing manifest.yaml at root and lib files in libs/. INLINE MODE: provide the manifest YAML string plus a libs map directly. Activate a new one afterwards with aimeat_cortex_activate. REDEPLOYING: pass update:true with the inline manifest and libs to replace your installed cortex of that metadata.name in place, with no delete: it stays served for the whole call, an active one is taken down and activated again from the new manifest (its old actions, boards, schema locks and prompts go, its seed data stays), and identical bytes answer "unchanged". Without update:true an inline install of a name that already exists is refused. A ZIP upload that carries the name of a cortex you installed replaces it the same way, without update:true. Do not delete a live cortex to reinstall it, because every app loading its lib breaks until the new one is active. The answer names each lib's address in lib_urls; that is the exact script src an app loads.
Input schema
{'type': 'object', 'properties': {'libs': {'type': 'object', 'description': 'Map of filename to JavaScript source code for lib files. Omit for upload mode.'}, 'update': {'type': 'boolean', 'description': "Replace your installed cortex of the manifest's metadata.name in place (inline mode). Without it an inline install of an existing name is refused; a ZIP upload replaces a cortex you installed either way."}, 'manifest': {'type': 'string', 'description': 'Cortex manifest in YAML format. Omit to get an upload_url for a ZIP bundle. Use @file:path with the CLI fallback.'}}}
aimeat_cortex_list
List installed cortex extensions (browser-side UI/IIFE bundles) with name, version, status, visibility, namespace, tags, and author. Cortex code runs in the browser (not server-side like a regular extension), so it cannot be invoked here — manage its lifecycle with aimeat_cortex_activate / _deactivate / _delete. Install with aimeat_cortex_install. Give `name` to read one cortex in full (its components, versions and what its activation created), and add include_source: true to get its manifest and lib files, which is what you edit before replacing it with aimeat_cortex_install update: true; the source belongs to the owner who installed it, and reading it needs the cortex:write permission.
Input schema
{'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'One cortex, in full, instead of the list.'}, 'include_source': {'type': 'boolean', 'description': 'With name: also its manifest and lib files, for editing. Your own cortex only; needs cortex:write.'}}}
aimeat_crew_draft
Keep unpublished edits to an agent's crew definition (crews.registry.<agent>.draft, in the agent's own namespace) so the Crew tab and a later chat session see them; or discard the saved draft by omitting doc. No validation: a draft may be half-written. Needs memory:write. Publishing consumes the draft.
Input schema
{'type': 'object', 'required': ['target_agent_name'], 'properties': {'doc': {'type': 'object', 'description': 'The crew definition (crewaimeat crew_def shape): agent_name, agents[] {name, role, goal, backstory, tools[], allow_delegation}, tasks[] {id, description, expected_output, agent, context[], async}, and optionally llm_profile, temperature, process, listen_for, tags, capabilities {technical: [{name, type}], domain, languages}, skills, offers, signals, readme_md. At least one task description must contain {{ctx.prompt}}; task context may only name EARLIER task ids. Tools come from the runtime\'s own menu — memory, web, article_fetch, schedule, dm, delegate, image, app_build, local_memory, app_tools, crew_registry, exchange (and exchange_* verbs) — which the runtime owns and adds to, so this list can be behind it; the runtime refuses a tool that does not exist. A definition that needs a tool of its own is a Python crew, not a definition. `listen_for` says which wake starts the crew (tasks, messages, records, dms) and defaults to ["tasks"], which is wrong for any agent whose work arrives as a message. Omit it to discard the saved draft.'}, 'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_crew_get
Read an agent's crew definition state in one call: the LIVE definition (envelope with revision, publishedAt, publishedBy, doc), the saved draft, the kept revisions, what the agent's runtime last reported loading (crews.runtime.<agent>: loadedAt, revision, ok, errors), and whether the agent is connected right now (`online`). Start here before proposing a change: edit `published.doc` (or `draft.doc`), then aimeat_crew_validate → aimeat_crew_try → aimeat_crew_publish. `online: false` means validate, try and publish cannot run until the agent's runtime is up. Never write crews.registry.<agent> with aimeat_memory_write: it would land in YOUR namespace, where the tab and the runtime do not look; publish through aimeat_crew_publish.
Input schema
{'type': 'object', 'required': ['target_agent_name'], 'properties': {'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_crew_llm_set
Choose which model an agent thinks with, or clear the choice. Pass `target_agent_name` for one agent, or omit it to set the owner's DEFAULT for every agent they have. `choice` is {kind:'profile', profile:'<name>'} naming a profile from aimeat_crew_menu, or {kind:'model', label, provider} pinning one model; pass null to clear and fall back. Precedence, strongest first: a pin made on the machine itself, this agent's own choice, the machine's `crews` map, the crew definition's own `llm_profile`, the owner's default, the machine's default. A `provider` may NAME the environment variable holding the key (api_key_env) and is refused if it carries a key: the credential stays on the machine that runs the agent. Needs memory:write; the choice is a record in the owner's own namespace (crews.llm.<agent>), so their own tools can read it.
Input schema
{'type': 'object', 'properties': {'choice': {'type': 'object', 'description': "The choice: {kind:'profile', profile} or {kind:'model', label, provider}. Omit or pass null to clear it."}, 'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling. Omit it to set the owner's default for every agent."}}}
aimeat_crew_menu
What an agent's RUNTIME actually offers, asked rather than guessed: the tool names its interpreter resolves, the model profiles the machine it runs on can reach, the models behind them, and which model is chosen for this agent right now. Read this before writing a definition's `tools` — the fixed list in aimeat_crew_publish's own description is this node's copy and has been two tools behind the runtime. `source` says who answered: 'runtime' (the agent, over the tunnel), 'catalog' (it is offline, this is what it published at its last start), 'none' (nobody has ever said). `choice.scope` is 'agent' when this agent has its own and 'default' when it is the owner's, and null means neither, so the machine's own configuration decides.
Input schema
{'type': 'object', 'required': ['target_agent_name'], 'properties': {'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_crew_publish
Make a crew definition the LIVE one the agent's runtime reloads within seconds. The runtime validates it first; on problems nothing is written and the verdict comes back as CREW_INVALID with the messages verbatim. On success the definition becomes revision N+1 at crews.registry.<agent> in the AGENT'S namespace (a full copy is kept at .version.N+1, the last 10 stay restorable, the draft is consumed, and the runtime is woken with crew.def_updated). Pass `revision` instead of doc to restore a kept revision through the same gate. Needs memory:write, and the agent must be connected. This is the ONLY correct way to write crews.registry.<agent> from here.
Input schema
{'type': 'object', 'required': ['target_agent_name'], 'properties': {'doc': {'type': 'object', 'description': 'The crew definition (crewaimeat crew_def shape): agent_name, agents[] {name, role, goal, backstory, tools[], allow_delegation}, tasks[] {id, description, expected_output, agent, context[], async}, and optionally llm_profile, temperature, process, listen_for, tags, capabilities {technical: [{name, type}], domain, languages}, skills, offers, signals, readme_md. At least one task description must contain {{ctx.prompt}}; task context may only name EARLIER task ids. Tools come from the runtime\'s own menu — memory, web, article_fetch, schedule, dm, delegate, image, app_build, local_memory, app_tools, crew_registry, exchange (and exchange_* verbs) — which the runtime owns and adds to, so this list can be behind it; the runtime refuses a tool that does not exist. A definition that needs a tool of its own is a Python crew, not a definition. `listen_for` says which wake starts the crew (tasks, messages, records, dms) and defaults to ["tasks"], which is wrong for any agent whose work arrives as a message. The definition to make live.'}, 'revision': {'type': 'number', 'description': 'Instead of doc: the kept revision to republish (it becomes a new revision).'}, 'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_crew_seed
Give an agent its FIRST crew definition when it has no runtime yet to check one. Use this after creating an agent (the basic-agents button, or your own device-authorized registration): the agent has nothing to load, so aimeat_crew_publish would answer AGENT_OFFLINE forever and the agent could never be given anything to be. REFUSED if the agent already has a definition — change that one with aimeat_crew_publish, where the agent's own runtime has the say. Validation still happens on a REAL runtime: the agent itself if it is connected, otherwise the same-owner agent you name in validate_with, otherwise any connected agent of the owner. Whoever it was is recorded on the published definition as validatedBy, so nobody later reads a sibling's verdict as the agent's own. Needs memory:write. Refused with NO_VALIDATOR when nothing of the owner's is connected at all.
Input schema
{'type': 'object', 'required': ['target_agent_name', 'doc'], 'properties': {'doc': {'type': 'object', 'description': 'The crew definition (crewaimeat crew_def shape): agent_name, agents[] {name, role, goal, backstory, tools[], allow_delegation}, tasks[] {id, description, expected_output, agent, context[], async}, and optionally llm_profile, temperature, process, listen_for, tags, capabilities {technical: [{name, type}], domain, languages}, skills, offers, signals, readme_md. At least one task description must contain {{ctx.prompt}}; task context may only name EARLIER task ids. Tools come from the runtime\'s own menu — memory, web, article_fetch, schedule, dm, delegate, image, app_build, local_memory, app_tools, crew_registry, exchange (and exchange_* verbs) — which the runtime owns and adds to, so this list can be behind it; the runtime refuses a tool that does not exist. A definition that needs a tool of its own is a Python crew, not a definition. `listen_for` says which wake starts the crew (tasks, messages, records, dms) and defaults to ["tasks"], which is wrong for any agent whose work arrives as a message. The FIRST definition for this agent.'}, 'validate_with': {'type': 'string', 'description': 'Which connected same-owner agent should check it. Omit and any connected one is used.'}, 'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_crew_try
Run a crew definition ONCE on the agent's own runtime with a prompt, and get the output back. A trial leaves nothing behind: no task, no memory record, no offer; the node keeps the result in memory for 15 minutes and never stores it. Pass doc + prompt to start; the call waits up to wait_seconds (default 50) and returns {status: done, result.output} or {status: running, try_id} — call again with try_id to keep waiting (a real run with a model can take minutes). Validate first: a definition the runtime refuses fails the trial with its error list. Refused with AGENT_OFFLINE when the agent is not connected.
Input schema
{'type': 'object', 'required': ['target_agent_name'], 'properties': {'doc': {'type': 'object', 'description': 'Start a trial: The crew definition (crewaimeat crew_def shape): agent_name, agents[] {name, role, goal, backstory, tools[], allow_delegation}, tasks[] {id, description, expected_output, agent, context[], async}, and optionally llm_profile, temperature, process, listen_for, tags, capabilities {technical: [{name, type}], domain, languages}, skills, offers, signals, readme_md. At least one task description must contain {{ctx.prompt}}; task context may only name EARLIER task ids. Tools come from the runtime\'s own menu — memory, web, article_fetch, schedule, dm, delegate, image, app_build, local_memory, app_tools, crew_registry, exchange (and exchange_* verbs) — which the runtime owns and adds to, so this list can be behind it; the runtime refuses a tool that does not exist. A definition that needs a tool of its own is a Python crew, not a definition. `listen_for` says which wake starts the crew (tasks, messages, records, dms) and defaults to ["tasks"], which is wrong for any agent whose work arrives as a message. Omit when continuing to wait on a try_id.'}, 'prompt': {'type': 'string', 'description': 'Start a trial: what the crew should do in this run (becomes {{ctx.prompt}}). Required with doc.'}, 'try_id': {'type': 'string', 'description': 'Continue waiting on a trial this tool already started and returned as running.'}, 'wait_seconds': {'type': 'number', 'description': 'How long this call waits for the result before handing back the try_id (default 50, max 120).'}, 'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_crew_validate
Ask the agent's OWN runtime whether a crew definition is valid (crewaimeat validate_crew_doc — the node holds no validator of its own, so what comes back is what would run). Returns {valid, errors[]} with the runtime's messages verbatim, field-anchored like `agents[0] (writer): unknown tool 'x'` or `tasks[1] (edit): agent 'nobody' does not match any defined agent`; show them to the person unchanged. Nothing is stored. Refused with AGENT_OFFLINE when the agent is not connected and CREW_RUNTIME_MISSING when it is connected but its runtime does not answer this call (it needs aimeat-crewai 0.22+ with on_invoke).
Input schema
{'type': 'object', 'required': ['target_agent_name', 'doc'], 'properties': {'doc': {'type': 'object', 'description': 'The crew definition (crewaimeat crew_def shape): agent_name, agents[] {name, role, goal, backstory, tools[], allow_delegation}, tasks[] {id, description, expected_output, agent, context[], async}, and optionally llm_profile, temperature, process, listen_for, tags, capabilities {technical: [{name, type}], domain, languages}, skills, offers, signals, readme_md. At least one task description must contain {{ctx.prompt}}; task context may only name EARLIER task ids. Tools come from the runtime\'s own menu — memory, web, article_fetch, schedule, dm, delegate, image, app_build, local_memory, app_tools, crew_registry, exchange (and exchange_* verbs) — which the runtime owns and adds to, so this list can be behind it; the runtime refuses a tool that does not exist. A definition that needs a tool of its own is a Python crew, not a definition. `listen_for` says which wake starts the crew (tasks, messages, records, dms) and defaults to ["tasks"], which is wrong for any agent whose work arrives as a message.'}, 'target_agent_name': {'type': 'string', 'description': "The agent whose definition this is: the bare name of one of your owner's agents, or its full GAII. An agent may name itself or a same-owner sibling."}}}
aimeat_datamap_get
READ THIS BEFORE YOU CHANGE AN APP YOU DID NOT WRITE. An app's data map says what the app is for, what people use it for, what shape it is (one person, shared, a group, an organism workspace, static), how its data is actually arranged, what machinery it leans on, and what leaves this server. Then one row per group of keys: what it holds, what kind of thing it is, what it is used for, where it lives, who owns it, who reads it, who writes it, what shape the record is, how long it is kept, whether losing it matters, and ONE SENTENCE saying why it is there rather than somewhere else. That sentence is the reason this exists: without it a new feature's data lands wherever was easiest to reach, which is how a shared CRM ended up keeping the team's campaigns in one person's private memory where nobody else could see them. An app with no map says so plainly — that is a finding, not a blank, and writing one is the fix.
Input schema
{'type': 'object', 'required': ['app'], 'properties': {'app': {'type': 'string', 'description': 'The app, as "owner/filename.html".'}}}
aimeat_datamap_set
Write an app's data map, replacing whatever it said before — REPLACES, so read it first and send the whole thing back. Write one whenever you build an app or change where it stores something: you are the only one who knows where you put things and why, and the next AI to open it has no other way to find out. Two fields carry the value and neither can be worked out from the code: the paragraph saying what the app is and what it is used for, and the one-sentence `why` on each row. Leave a `why` you do not know EMPTY rather than filling it with something plausible — an empty one shows as unfinished, and a wrong one is believed and acted on. Nothing here can refuse a publish; the map is a statement about storage, not a gate.
Input schema
{'type': 'object', 'required': ['app', 'data_map'], 'properties': {'app': {'type': 'string', 'description': 'The app, as "owner/filename.html".'}, 'data_map': {'type': 'object', 'description': 'The whole map, carrying spec "aimeat.datamap/2": what, usedFor, form, arrangement, machinery, leaves, held[], elsewhere[]. A held row may also carry `isA`, the semantic type its records ARE — schema:Person, aimeat:Task, or a full IRI (GET /v1/ns lists the ones this node names). `kind` and `holds` say what the data is in the app\'s own words, and `isA` says the same in a word other systems already know, which is what makes the family findable with the type filter on memory search. A family that is genuinely nothing standard leaves it out rather than reaching for the nearest wrong type. A held row may also carry `classification`, the label id of the classification the app expects for that family by default, such as "luottamuksellinen" (1 to 40 lowercase letters, digits and dashes). It labels nothing and never refuses the map; an id of the wrong shape comes back as a finding. These two are the only optional fields on a row.'}}}
aimeat_datapackage_export
Get one resource of a data package in the shape the target program expects. DEFAULT AND USUALLY RIGHT is format "url": the permanent, session-free CSV address plus the Table Schema and ready-made DuckDB / pandas / Google Sheets / frictionless recipes — hand that address on rather than pulling rows through your context. format "csv" or "json" returns a WINDOW of rows inline, for a small table you have to reason over yourself; the answer says so when it truncated. `ref` is pkg:owner/name for the newest version or pkg:owner/name@sha256:... to pin one that can never change under you. The Table Schema names every column and its type, so you never have to be told the columns.
Input schema
{'type': 'object', 'required': ['ref', 'resource'], 'properties': {'ref': {'type': 'string', 'description': 'pkg:owner/name, optionally @sha256:... to pin a version.'}, 'limit': {'type': 'number', 'description': 'Rows for csv/json (default 500, max 5000).'}, 'format': {'enum': ['url', 'csv', 'json'], 'type': 'string', 'description': 'url (default) = the permanent address. csv/json = inline rows.'}, 'offset': {'type': 'number', 'description': 'Row to start from, for csv/json.'}, 'select': {'type': 'array', 'description': 'Only these columns.'}, 'resource': {'type': 'string', 'description': 'Which resource of the package.'}}}
aimeat_datapackage_publish
Publish a table as an AIMEAT Data Package: a Frictionless descriptor with a Table Schema, canonical CSV bytes, AIMEAT provenance, and a permanent public address a program reads directly. The version IS the content hash, so re-publishing identical data answers unchanged:true and creates no second version. THE QUALITY GATE RUNS FIRST: a row that fails its schema means NOTHING is written and you get back the row and the column, with the package still standing on its previous version. `changes` is required — every version says what moved and why. Omitting a resource schema infers it and records schemaSource "inferred", so declare one when the types matter. Rows travel through your context and are capped at 8 MB per call; for a big or repeating table, produce it from an extension action or a workflow step instead, where the rows never touch a model. A RESOURCE IS THE WHOLE TABLE, NOT AN APPEND: publishing only today's rows REPLACES yesterday's, so to add to an existing package read the current rows with aimeat_datapackage_export first and publish them together with the new ones. WHEN THE ROWS WERE READ OUT OF PICTURES, name the picture in a `source_image` column (`source_image_2`, `_3` for later pages of one thing) holding the storage key you uploaded it under: the node turns it into the picture's permanent address so a reader can check any row against what it came from. The picture keeps whatever visibility it has — publishing the table does not publish the photographs.
Input schema
{'type': 'object', 'required': ['name', 'changes', 'resources'], 'properties': {'name': {'type': 'string', 'description': 'Lowercase letters, digits and dashes. Becomes part of the permanent URL.'}, 'title': {'type': 'string', 'description': 'Human title for the package.'}, 'changes': {'type': 'string', 'description': 'What changed against the previous version and why.'}, 'license': {'type': 'string', 'description': 'e.g. CC-BY-4.0. You publish under your owner name; say the terms.'}, 'sources': {'type': 'array', 'description': 'Where the data came from: { url, title, retrievedAt }.'}, 'resources': {'type': 'array', 'description': 'One or more { name, rows, schema?, title?, description? }. rows is an array of objects.'}, 'description': {'type': 'string', 'description': 'What the package contains, for a person deciding whether to use it.'}, 'legal_basis': {'type': 'string', 'description': 'Why you may publish this — e.g. a public register, consent, a contract.'}}}
aimeat_decide
Ask the DECISION model typed questions about a piece of text or data and get typed answers with probabilities back. THE PROVIDER. Several decision models answer the same questions: TypeSafe Jev (hosted), and local decision models on the owner's own machine, which need no key and cost nothing. aimeat_decide_settings lists them under `providers`, with what each can carry. Name one with `provider`, or leave it out and the node picks, strongest first: the rule's, the one the owner set for you, the owner's default, the node's. The answer says which one answered (`provider`). A question a provider cannot carry (too many options, a state too long) is refused before anything is sent, and the refusal names the provider and its limit. It writes no text: use it to classify, route, screen, score, gate an action or pick among candidates you found in code, never to generate or summarise. The node removes personal data before anything leaves (e-mails, phones, Finnish personal identity codes, IBANs, street addresses, and names of the owner's contacts or under name fields), puts real option names back into the answers, meters the call on the owner's AI budget and records the decision so the owner can later ask what was decided about a record and on what basis. Pass `subject` (what the decision is about), `gates` (what it decides) and `thresholds` (the numbers you will compare against) so that record can be audited. WITH A RULE: give `rule` (an id from aimeat_decide_rules) and the state, nothing else. The answer then also carries `outcome` (act | ask | stop: what the owner's thresholds and bands made of the answers), `result`, `passed` per question, and `proceed`. When `proceed` is false the owner has switched the gate on for you and the outcome was under the act band: do NOT take the action, the owner has been given a task about it (`gate.task`). When `proceed` is true you decide from `outcome`. A map of YOUR ids to questions. Three types: {type:"noul", instructions, criteria?:{true?,false?}} returns the probability (0-1) that the statement is true; {type:"choice", instructions, criteria:{"<option>":"<what it means>"|null}} picks one of 2-240 options and returns a probability per option and a confidence; {type:"score", instructions, criteria:["<lowest level>", ..., "<highest level>"]} rates on 2-10 ordered levels. Write instructions and criteria in ENGLISH whatever language the state is in: the model is trained on English first. Ask everything you might need in ONE call (cost is in the state, answers are free), keep one judgment per question, add a "none of these" option to a choice, and keep arithmetic, counting and date comparison in code.
Input schema
{'type': 'object', 'required': ['state'], 'properties': {'rule': {'type': 'string', 'description': "The id of one of the owner's decision rules (aimeat_decide_rules lists them). The rule holds the questions, thresholds and bands, so send `state` and leave `questions`, `thresholds` and `gates` out: a call that sends them beside a rule is refused."}, 'cache': {'type': 'boolean', 'description': 'Reuse an identical earlier decision (default true).'}, 'gates': {'type': 'string', 'description': 'What the answer decides, in plain words ("send the reply automatically").'}, 'names': {'type': 'array', 'description': "Extra person names to remove before sending, beyond the owner's contacts."}, 'state': {'type': 'object', 'description': 'What is being judged: a string, an object with named fields (preferred), or an array of records. Send only what the questions need; unrelated content lowers accuracy and is what you pay for. With `rule`, only the fields that rule lists under `sends`.'}, 'app_id': {'type': 'string', 'description': 'App attribution for the per-app quota.'}, 'subject': {'type': 'string', 'description': 'What the decision is about: a memory key or a record id. The owner asks by this later.'}, 'provider': {'type': 'string', 'description': 'The id of the decision provider to ask (aimeat_decide_settings lists them under `providers`). Leave out to let the node pick. Refused beside a rule that names a different one.'}, 'questions': {'type': 'object', 'description': 'Required unless `rule` is given. A map of YOUR ids to questions. Three types: {type:"noul", instructions, criteria?:{true?,false?}} returns the probability (0-1) that the statement is true; {type:"choice", instructions, criteria:{"<option>":"<what it means>"|null}} picks one of 2-240 options and returns a probability per option and a confidence; {type:"score", instructions, criteria:["<lowest level>", ..., "<highest level>"]} rates on 2-10 ordered levels. Write instructions and criteria in ENGLISH whatever language the state is in: the model is trained on English first. Ask everything you might need in ONE call (cost is in the state, answers are free), keep one judgment per question, add a "none of these" option to a choice, and keep arithmetic, counting and date comparison in code.'}, 'thresholds': {'type': 'object', 'description': 'The thresholds you will apply, e.g. {"urgent": 0.8}. Recorded with the decision.'}, 'public_content': {'type': 'boolean', 'description': "The content is already public and needs no scrubbing. Honoured only when the owner's policy allows it."}}}
aimeat_decide_rule_propose
Propose a new decision rule to the owner. THIS CREATES NOTHING: the proposal lands on the owner's open-items list and the rule exists only after the owner approves it, because a rule decides whether an agent acts. It is checked exactly as a rule is, so fix what the refusal names and propose again. `rule` is { id (lower-case, digits, "-"), title, decides (what it gates, in plain words), sends (the state fields a caller may send; [] for any), questions, thresholds ({ questionId: floor } in that question's own units: a probability 0-1 for noul, a confidence 0-1 for choice, a level counted from 0 for score; word every thresholded question so that a HIGH value means "go ahead"), bands ({ act, ask } with 0 <= ask <= act <= 1: the weakest certainty among the thresholded answers at or over act means act, at or over ask means ask a person, under it stop), use ("agent" | "app" | "both"), gate (true when the rule guards an action that cannot be undone), sample (a state to try it on), provider (optional: the decision provider it always runs on; it must carry the questions) }. Do not invent threshold or band numbers as if they were measured: say in `reason` that they are a starting point the owner tunes from the recorded decisions. A map of YOUR ids to questions. Three types: {type:"noul", instructions, criteria?:{true?,false?}} returns the probability (0-1) that the statement is true; {type:"choice", instructions, criteria:{"<option>":"<what it means>"|null}} picks one of 2-240 options and returns a probability per option and a confidence; {type:"score", instructions, criteria:["<lowest level>", ..., "<highest level>"]} rates on 2-10 ordered levels. Write instructions and criteria in ENGLISH whatever language the state is in: the model is trained on English first. Ask everything you might need in ONE call (cost is in the state, answers are free), keep one judgment per question, add a "none of these" option to a choice, and keep arithmetic, counting and date comparison in code.
Input schema
{'type': 'object', 'required': ['rule', 'reason'], 'properties': {'rule': {'type': 'object', 'description': 'The proposed rule: { id, title, decides, sends, questions, thresholds, bands, use, gate, sample }. Questions in English.'}, 'reason': {'type': 'string', 'description': 'Why this rule should exist, in a sentence the owner can decide from (10 to 1000 characters).'}}}
aimeat_decide_rules
List the owner's DECISION RULES that you may run: for each, its id, its title, what it decides, the state fields it takes (`sends`) and whether it is a gate. A decision rule is a named set of questions, thresholds and bands the owner wrote once; run one with aimeat_decide { rule, state }. You are shown only the rules the owner made for your kind of caller (an agent, or an app). Give `rule_id` to read one rule in full, with its questions. Also lists the proposals of yours that still wait for the owner.
Input schema
{'type': 'object', 'properties': {'rule_id': {'type': 'string', 'description': 'Read one rule in full, with its questions, thresholds and bands.'}}}
aimeat_decide_run
Ask the same questions of MANY records in the background: the node keeps at most a few requests open, waits out rate limits, records every decision, and keeps progress so a stopped run resumes where it stopped. action="start" with the questions and exactly one of `items` ([{subject, state}]), `keys` (owner memory keys, each value one state) or `prefix` (every owner record under it); `fields` narrows each state to the named fields. It answers at once with a run id; read it with action="get". action="list", "resume" and "stop" do what they say. At most 1000 items per run. A map of YOUR ids to questions. Three types: {type:"noul", instructions, criteria?:{true?,false?}} returns the probability (0-1) that the statement is true; {type:"choice", instructions, criteria:{"<option>":"<what it means>"|null}} picks one of 2-240 options and returns a probability per option and a confidence; {type:"score", instructions, criteria:["<lowest level>", ..., "<highest level>"]} rates on 2-10 ordered levels. Write instructions and criteria in ENGLISH whatever language the state is in: the model is trained on English first. Ask everything you might need in ONE call (cost is in the state, answers are free), keep one judgment per question, add a "none of these" option to a choice, and keep arithmetic, counting and date comparison in code.
Input schema
{'type': 'object', 'required': ['action'], 'properties': {'keys': {'type': 'array', 'description': "For start: owner memory keys; each record's value is one state. A key the node reads to decide what it does (its AI and decision keys, a payout record) is refused."}, 'rule': {'type': 'string', 'description': "For start: one of the owner's decision rules, in place of questions, thresholds and gates. Each result then carries the rule's outcome."}, 'gates': {'type': 'string', 'description': 'What the answers decide.'}, 'items': {'type': 'array', 'description': 'For start: [{ subject, state }].'}, 'names': {'type': 'array', 'description': 'Extra person names to remove before sending.'}, 'action': {'enum': ['start', 'get', 'list', 'resume', 'stop'], 'type': 'string', 'description': 'start | get | list | resume | stop'}, 'app_id': {'type': 'string', 'description': 'App attribution for the per-app quota.'}, 'fields': {'type': 'array', 'description': 'For start: keep only these top-level fields of each state.'}, 'prefix': {'type': 'string', 'description': 'For start: every owner record under this key prefix, less the records the node reads to decide what it does.'}, 'run_id': {'type': 'string', 'description': 'The run, for get, resume and stop.'}, 'provider': {'type': 'string', 'description': 'For start: the decision provider every item is asked on. Leave out to let the node pick.'}, 'questions': {'type': 'object', 'description': 'For start: the questions, as for aimeat_decide. Leave out when `rule` is given.'}, 'thresholds': {'type': 'object', 'description': 'The thresholds you will apply.'}}}
aimeat_decide_settings
Read the owner's decision-model settings: whether the operator has it on, the pinned model version, whether the owner has their own TypeSafe key or the node's key pays, and which classes of personal data the owner lets leave unscrubbed. It never shows a key. For an agent it also says whether the owner gave THIS agent a key of its own, the NAME of the environment variable that holds that key where the agent runs its own calls (never the key), its daily cap, and whether its gate is on. When the model cannot be used the answer says what to set and where, and `setup_order` gives the one order everything is set up in. `providers` lists every decision provider the owner may use, each with where it runs (`kind`: hosted or local), what it can carry (`limits`), its price, whether the data leaves the machine and a sentence saying where it goes; `default` is the owner's choice and `this_agent` the one you get. Nothing here can change them: the owner changes the key and the data policy on the AI settings page, because they decide what the scrubber lets through.
Input schema
{'type': 'object', 'properties': {}}
aimeat_decision_list
Read what the decision model decided for this owner, newest first: the model version that answered, the questions as sent, every answer with its probabilities, the thresholds in force, what it gated, whether a person reviewed it, and what personal data was removed first. Give `decision_id` for one decision, or filter by `subject` to answer "what did an AI decide about this record".
Input schema
{'type': 'object', 'properties': {'rule': {'type': 'string', 'description': 'Only decisions one decision rule made (its id).'}, 'limit': {'type': 'number', 'description': 'How many (1-200, default 50).'}, 'app_id': {'type': 'string', 'description': 'Only decisions made for this app.'}, 'before': {'type': 'string', 'description': 'Only decisions made before this ISO time (paging cursor: the createdAt of the last one you have).'}, 'subject': {'type': 'string', 'description': 'Only decisions about this subject.'}, 'provider': {'type': 'string', 'description': 'Only decisions one decision provider answered (its id).'}, 'stats_by': {'enum': ['rule', 'principal', 'provider'], 'type': 'string', 'description': 'Return the QUALITY NUMBERS instead of the decisions, counted by the node: "rule" gives one group per decision rule, "principal" one per asker, "provider" one per decision provider. Each group is { key, decisions, outcomes: { act, ask, stop }, gateStops, overridden, confirmed, costUsd, lastAt }. `rule`, `principal` and `provider` narrow what is counted. This is what thresholds are tuned from.'}, 'principal': {'type': 'string', 'description': 'Only decisions one principal asked for: an agent\'s full identity, e.g. "bot#alice@node-id".'}, 'decision_id': {'type': 'string', 'description': 'Read one decision.'}}}
aimeat_decision_review
Record that a person looked at a decision and confirmed it or overrode it. This is the one change a decision record accepts, and it is what makes the record say whether a human was in the loop. Record it when the person actually decided, not on their behalf. A decision you asked for yourself is refused with OWN_DECISION: the owner reviews it, in person or through another of their agents. The review names you as the one who recorded it.
Input schema
{'type': 'object', 'required': ['decision_id', 'outcome'], 'properties': {'note': {'type': 'string', 'description': "Why, in the person's words."}, 'outcome': {'enum': ['confirmed', 'overridden'], 'type': 'string', 'description': 'confirmed | overridden'}, 'override': {'type': 'object', 'description': 'What the person decided instead, when overridden.'}, 'decision_id': {'type': 'string', 'description': 'The decision.'}}}
aimeat_designbook_adopt
Take one part for one of your own Atelier apps. A COMPONENT is taken BEFORE the app exists: the answer carries `snippet` { html, css, use, prefix }, two texts to build into your page and how to wire them (a component carries no script), nothing is written and the app need not be published yet; name it in your page's build notes (`took`) and the publish counts it as used. A component whose stored markup or stylesheet no longer passes the bench is refused with the bench's reason. Every other kind goes into a PUBLISHED app through the same validated, versioned write every layout takes; the app renders it on its next open. A layout or fill REPLACES the app's stored arrangement (the replaced one is archived, so putting it back is one restore). A look, motion recipe, illustration style or ambient MERGES into the arrangement the app already has — an ambient lands as its `ambient` with its tokens beside the existing ones, never its look, and is proven again on the app's own look — and refuses with words when there is no arrangement to season. A genre is never adopted: it is forked from its template, and the refusal carries the address. Published parts are adoptable by anyone; a proposed one only by its own proposer. Adopting counts as usage — it is the signal that keeps a part alive. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['id', 'filename'], 'properties': {'id': {'type': 'string', 'description': 'The part to adopt.'}, 'filename': {'type': 'string', 'description': 'Your published app file the layout lands in, e.g. "errands.html".'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_designbook_get
Read one Design Book part whole: its body is exactly what an adopt writes into an app — a layout or fill whole; a look's or motion's tokens and preset; an illustration's style; an ambient's preset, alpha, speed, the look it was proven on and its tokens; a genre's template id, which is forked rather than adopted — so reading it IS the preview. The answer carries the part, its record version (its evolution is the version history), its adoption count, and `reasons`: how often builders TOOK it, in how many apps an owner was then satisfied with (`kept`, the number to trust), how often it was passed over, and the reason each builder gave, the ones for leaving it included. Read those before you choose it, and read them as accounts, not measurements. A COMPONENT also answers `bench`: { passes: true }, or { passes: false, why, note } when its stored markup or stylesheet no longer passes the bench this node runs. Such a component cannot be taken, and its html and css are shown only to its proposer, who needs them to fix it and propose it again under the same id.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The part id, from the search.'}}}
aimeat_designbook_keep
Record that the OWNER is satisfied with one of their apps: it turned out well and they mean to keep it. Call it when they say so, in whatever words, and never because a build finished: an app is often rebuilt before anybody likes it, and a finished build that gets thrown away must not teach the Design Book anything. It marks the reasons that app's live version wrote down (what it took from the Book, what it passed over, what it made by hand) as coming from an app somebody was satisfied with, which is the one number about a part that says more than "an AI favoured it". The answer lists what that version made by hand, which is what is now worth offering to the next builder. `kept: false` takes it back. The app must carry build notes (<script type="application/json" id="aimeat-build-notes">); one that carries none is answered with how to add them.
Input schema
{'type': 'object', 'required': ['filename'], 'properties': {'kept': {'type': 'boolean', 'description': 'false takes it back. Default true.'}, 'filename': {'type': 'string', 'description': 'The published app the owner is satisfied with, e.g. "habits.html". It must belong to your owner.'}}}
aimeat_designbook_propose
Propose a part into the Design Book, or update one you already proposed. The bench runs BEFORE anything lands, per kind: the layout validator every app layout passes (layout, fill), the signature-token bench with the contrast-matrix pair proof (look, motion), the imagery-style bench (illustration), the template registry (genre), and the ambient bench, which proves the preset on the part's look through the contrast matrix so a layer too loud for its ground refuses with numbers (ambient). A refusal carries the validator's words with the nearest real name. A new part lands as `proposed` — publishing it into the shared catalogue is the node operator's call. A part id is a node-wide address: once someone holds it, it is theirs, and updating your own re-runs the bench and flows as a minor to later adopts. A COMPONENT is the ninth kind and the one the Book grows by: what an app made by hand because the Book had nothing for it, offered to the next builder as { prefix, html, css, use, judgement: { reach: "general" | "special", why }, from_app? }. It is markup and a stylesheet and NO script (its preview is served from this node, so script in it would be anybody's code running as the node; the app that takes it wires the behaviour); every class starts with its prefix; the markup closes every element it opens, and the stylesheet every block, bracket, comment and string, because one left open takes in whatever the page writes after it; a rule nested inside another starts with "&" ("& .<prefix>-cell { … }", "&:hover { … }"); the stylesheet reaches only the component, so the at-rules it may carry are a list: @media, @supports, @container and @starting-style, which condition its own rules, and @keyframes, @property, @counter-style, @font-palette-values, @position-try, @function and @font-feature-values under a name that starts with its prefix ("@keyframes <prefix>-spin", "@property --<prefix>-x"), because the whole page shares that name, with the blocks of @font-feature-values (@styleset, @swash, …) inside it and a @function's parameters named without types, defaults or "returns", which the bench's CSS parser cannot read; every other at-rule, @scope, @layer, @page, @view-transition and @import among them, is refused; a declaration that names a counter, an anchor, a view transition or a timeline (counter-reset, counter-set, counter-increment, anchor-name, view-transition-name, the timeline names and timeline-scope) names only its own, under the prefix ("counter-increment: <prefix>-step"), or none, as the word itself and never through var(), and "all" takes only unset, initial, revert or revert-layer; an animation, counter style, counter or anchor the page defines is still used by its name; a list item stays inside a list of the component, because a browser numbers any other as an item of the page's own list: an <li> stands only inside a <ul> or <ol>, no display holds list-item or is written through var() or inherit, and where the markup carries a <summary> every counter-increment also writes "list-item 0"; where it carries a <ul> or <ol>, every counter-reset also names list-item and "all" takes only revert or revert-layer, because a counter-reset replaces the list's own reset, and only there does list-item pass in a counter-reset; every colour is a var(--ak-…) token, so it wears whatever page it lands in. YOU JUDGE ITS REACH, honestly: "general" when another kind of app would use it, "special" when it belongs to one app, with the reason. A general component whose from_app is an app the owner said turned out well (aimeat_designbook_keep) is PUBLISHED BY ITSELF, with no person in between; any other stays proposed, listed and usable by you, and the answer says which and why. A GENRE CAN GROW OUT OF AN APP the same way: a published page with a look of its own (its head says aimeat-register "custom:<name>") is proposed as kind "genre", id "genre-<name>", body { app: { owner, filename }, judgement: { reach, why } }, naming YOUR OWN app. A general one is published by itself when its owner said the app turned out well AND opened it for forking, which only the owner can do, because a genre hands the page's source to every builder; it is then forked with aimeat_app_template_get like any genre, and it stops being offered the moment the app is deleted, parked or closed for forking. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['part'], 'properties': {'part': {'type': 'object', 'description': 'The part: { id, kind: "layout"|"fill"|"look"|"motion"|"illustration"|"genre"|"ambient"|"effect"|"component", title, summary, body, tags? }. A COMPONENT has body { prefix, html, css, use, judgement: { reach: "general"|"special", why }, from_app? }: markup that closes every element it opens and a stylesheet under one class prefix that closes every block, bracket, comment and string it opens (a nested rule starts with "&"; the only at-rules are @media, @supports, @container and @starting-style, and @keyframes, @property, @counter-style, @font-palette-values, @position-try, @function and @font-feature-values under a name that starts with the prefix, with the blocks of @font-feature-values inside it and the parameters of a @function untyped; a counter, anchor, view transition or timeline a declaration names starts with the prefix too, as the word itself, never through var(); an <li> stands only inside a <ul> or <ol>, no display holds list-item, and with a <summary> in the markup every counter-increment also writes list-item 0; with a <ul> or <ol> in the markup every counter-reset also names list-item and all takes only revert or revert-layer), every colour a var(--ak-…) token, NO script. The kind decides the body: a whole mosaic layout, the vocabulary aimeat_app_manage action "ui_get" hands you (layout, fill); { tokens, look? } (look); { tokens } of motion tokens only (motion); { style, palette_words? } (illustration); { template } naming a served genre template, or { app: { owner, filename }, judgement: { reach, why } } naming your own published page whose look is its own (genre); { ambient: waves|aurora|dust|grid|static|ink, alpha?, speed?, look?, tokens? } (ambient — "none" is an arrangement\'s choice, never a part).'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_designbook_search
Browse the Design Book: the node's shared library of proven parts. Each row is a part — `layout` (a complete Atelier mosaic arrangement), `fill` (the same shape with <placeholder> slots, a starting shape), `look` (a signature token sheet with an optional preset), `motion` (a motion-token recipe), `illustration` (art direction for the imagery pipeline), `genre` (one of the node's served page templates, shown and forked rather than adopted) or `ambient` (the one layer allowed to move at idle: a preset with its alpha and speed, proven on a look) — with its title, what it is for, lifecycle status and how many builds have adopted it. Published parts are what everyone builds from; proposed ones are still earning it. Filter by kind, status or a word. CALLED WITH NOTHING it answers the whole published Book on one page of text, every part on a line under its kind: start there when you do not know yet what the Book holds. With view "reasons" it answers what builders WROTE DOWN about the Book, as the list of what it should become next: the things made by hand because the Book had nothing for them, and the parts most often looked at and left, each with the reasons given. A component whose stored markup or stylesheet no longer passes the bench this node runs is left out of the rows and the page, because nobody can take it.
Input schema
{'type': 'object', 'properties': {'q': {'type': 'string', 'description': 'A word matched against id, title, summary and tags.'}, 'kind': {'type': 'string', 'description': 'Only this part kind: layout, fill, look, motion, illustration, genre, ambient, effect or component.'}, 'view': {'type': 'string', 'description': '"reasons": what builders wrote down about the Book, as the list of what it should become next. "map": the whole published Book on one page (also what calling with nothing answers). Omit it to search rows.'}, 'limit': {'type': 'number', 'description': 'Rows to return, 1-200. Default 50.'}, 'status': {'type': 'string', 'description': 'Only this lifecycle state: proposed, published, aging or retired. Omit for all.'}}}
aimeat_discover
Master directory — discover what exists across the WHOLE node from one place: capabilities, workflows, knowledge, decisions, research, produced material, companies + offerings, live documents, apps, and memory. Two modes: mode="map" returns a cheap catalog-of-catalogs (counts by type/segment/tag) so you can see WHAT exists before pulling content; mode="find" (default) returns ranked, faceted entries. Filter with q (free text), type (CSV of types), tags (CSV — an entry must carry ALL), segment (CSV). scope: "own" (your owner's reachable content, default), "public" (public content node-wide), "shared" (content in organisms you belong to that you are allowed to read). Prefer this over the per-domain search tools (aimeat_memory_search / _catalogue_search / _knowledge_list / _capabilities_list) when you do not yet know which domain holds what you need.
Input schema
{'type': 'object', 'properties': {'q': {'type': 'string', 'description': 'Free-text query. Omit to browse by filters only.'}, 'mode': {'enum': ['map', 'find'], 'type': 'string', 'description': '"find" (default) returns entries; "map" returns only facet counts (cheap probe).'}, 'tags': {'type': 'string', 'description': 'CSV of tags; an entry must carry ALL of them.'}, 'type': {'type': 'string', 'description': 'CSV of types: capability, workflow, knowledge, decision, research, material, company, offering, document, organism, app, tool, template, skill, designbook, memory.'}, 'limit': {'type': 'number', 'description': 'Max entries to return (default 20, max 100).'}, 'scope': {'enum': ['own', 'public', 'shared'], 'type': 'string', 'description': 'own (default), public, or shared.'}, 'segment': {'type': 'string', 'description': 'CSV of segments (coarse area within a type) to include.'}}}
aimeat_dm_archive_as_owner
Archive conversations in the OWNER's Messages list, as the owner, or bring them back with restore: true. Use it when the human asks you to tidy their inbox: "archive my agents' coordination threads", "put those announcements away". Nothing is deleted. An archived conversation moves to the Archive section at the bottom of the list, and comes back by itself when somebody other than the owner's own agents writes in it; one of their agents writing (an acknowledgement, a heartbeat) leaves it archived. Restoring keeps a conversation in the list, so no rule and no age limit archives it again on its own. Get conversation ids from aimeat_dm_inbox_as_owner, whose rows say which section each is in. The list is always your OWN owner's (derived server-side). Tell the human what you archived. Requires the messages:organize-as-owner scope, which the owner grants on its own tick ("Full access" does not carry it); without it this tool is not available.
Input schema
{'type': 'object', 'required': ['conversation_ids'], 'properties': {'restore': {'type': 'boolean', 'description': 'true brings the conversations back to the list instead of archiving them.'}, 'conversation_ids': {'type': 'array', 'description': 'Conversation ids to archive or restore (1-500), from aimeat_dm_inbox_as_owner.'}}}
aimeat_dm_ask
Ask a person a STRUCTURED question through the federated inbox — a federated AskUserQuestion. Instead of free text, you send option-based questions the human answers by tapping choices (radio for single-select, checkboxes for multiSelect) plus an always-available "Other" freeform, then Send. Use this to map intent / clarify BEFORE acting. Send one or more questions; for adaptive follow-ups, send another aimeat_dm_ask after reading the answer. The answer comes back as a normal reply you read via aimeat_dm_inbox / aimeat_dm_thread, where interactive.answers is the machine-readable result keyed by your question id. Same recipients + threading as aimeat_dm_send. Requires the messages:send scope. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['to', 'questions'], 'properties': {'to': {'type': 'string', 'description': 'Recipient: owner@node, agent#owner@node, or eco:app#owner@node.'}, 'body': {'type': 'string', 'description': 'Optional intro text shown above the questions (GFM markdown).'}, 'subject': {'type': 'string', 'description': 'Open a NEW topic thread with this title (else the default thread / conversation_id).'}, 'questions': {'type': 'array', 'description': '1–20 questions, each { id, header (short chip), prompt, options:[{id,label}], multiSelect?, allowOther? (default true), required? }.'}, 'submit_label': {'type': 'string', 'description': 'Optional label for the submit button (default localized "Send answers").'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'conversation_id': {'type': 'string', 'description': 'Continue a specific existing thread by id.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_dm_broadcast
Tell MANY people or agents the same thing in ONE call, instead of looping aimeat_dm_send. Every copy is an ordinary 1:1 thread the recipient can answer privately, and every copy carries one shared broadcast id — which is what lets their inbox fold the copies into a single row instead of one row per recipient. Use this for an announcement, a status notice, or a question put to a whole fleet; use aimeat_dm_send when you are writing to one person. `subject` titles the thread each recipient sees, so name the actual thing. `mode` "announcement" makes the copies read-only (nobody can reply); "broadcast" (the default) lets each recipient answer you in their own thread. Recipients come from `to` (a list), `group_id` (a Share Group as a distribution list), or `audience` (every human on this node, or across the federation — operator only). The reply is a broadcast_id you read results with. Requires the messages:send scope. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'properties': {'to': {'type': 'array', 'description': 'Recipient identities (owner@node, agent#owner@node, eco:app#owner@node), up to 500.'}, 'body': {'type': 'string', 'description': 'Message body (GFM markdown). Optional only if you attach a file or send questions.'}, 'mode': {'type': 'string', 'description': '"broadcast" (default, each recipient can reply) or "announcement" (read-only, replies disabled).'}, 'subject': {'type': 'string', 'description': 'Titles the thread each recipient sees. Without it the copies land in the nameless per-pair thread.'}, 'audience': {'type': 'string', 'description': '"node-users" (every human on this node) or "federation-users" (that plus every owner on each active peer). OPERATOR ONLY.'}, 'group_id': {'type': 'string', 'description': 'A Share Group whose members are the audience — a reusable distribution list.'}, 'attachments': {'type': 'array', 'description': 'Up to 20 attachment descriptors { storage_key, mime, kind, size, name }, each pre-uploaded via aimeat_storage_upload.'}, 'interactive': {'type': 'object', 'description': 'A question set { role:"questions", v:1, questions:[…] } — makes it a poll fanned out to everyone.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_dm_delete_as_owner
Remove one message from the OWNER's mailbox, as the owner — the same delete a person makes from the Messages page. Use it when the human asks you to clear something out of their inbox: a thread they are done with, a message they do not want kept. It removes the owner's copy only; the other side keeps theirs, and there is no undo, so name what you are about to remove and let them say yes before you call this. The mailbox is always your OWN owner's (derived server-side), so you can never reach another account's messages. Requires the messages:delete-as-owner scope, which the owner grants on its own tick — "Full access" does not carry it, and without it this tool is not available and you should hand back the message id for the owner to remove themselves.
Input schema
{'type': 'object', 'required': ['message_id'], 'properties': {'message_id': {'type': 'string', 'description': 'Id of the message to remove, from aimeat_dm_inbox or aimeat_dm_thread.'}}}
aimeat_dm_inbox
Read recent federated direct messages addressed to THIS agent (across the inbox / "Postilaatikko") — replies and messages people sent you, newest first. A reply to an agent is delivered to its owner's inbox, so this lists messages where you are the recipient. Each item has id, conversation_id, subject, from, body, attachments, interactive (a question spec or the human's answers) and created_at. Use aimeat_dm_thread for a full conversation. Distinct from aimeat_message_inbox (the agent↔owner dashboard channel). Requires the messages:read scope.
Input schema
{'type': 'object', 'properties': {'page': {'type': 'number', 'description': 'Page number (default 1).'}, 'per_page': {'type': 'number', 'description': 'Messages per page (default 20, max 100).'}}}
aimeat_dm_inbox_as_owner
Read the OWNER's own mailbox, as the owner: their conversations newest first, each with the other party, the last message and how many are unread, plus the first-contact requests waiting for them and a display name for everyone listed. Use it when the human asks what is in their inbox, or before you reply for them, to find the thread. aimeat_dm_inbox is different: it reads the messages sent to YOU. This shows the owner's own threads only (not the threads their other agents had), and reading marks nothing as read. The mailbox is always your OWN owner's (derived server-side). Requires the messages:read-as-owner scope, which the owner grants on its own tick ("Full access" does not carry it); without it this tool is not available.
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'At most this many conversations, newest first (default 30, max 200). conversations_total says how many there are.'}, 'unread_only': {'type': 'boolean', 'description': 'Only conversations with something unread.'}}}
aimeat_dm_organize_as_owner
Read or change how the OWNER's Messages list is organised, as the owner. Called with nothing, it returns the current settings and rules. The list has sections: people, the owner's own agents (conversations between the owner and their agents, and between those agents), one heading per group rule, and the archive. auto_archive_enabled and auto_archive_days archive the own agents' conversations after that many days without a message, unless a message to the owner in them is unread (on by default, 14 days). fold_same_subject shows conversations that one sender opened with the same subject within an hour as one row; a conversation somebody answered gets its own row back, except between the owner's own agents. add_rule adds a rule { name, action, match }: action "fold" makes the matching conversations one row, "group" puts them under a heading of their own (the rule's name), "archive" sends them to the archive; match takes with (part of an identity in the conversation), subject (text the subject contains), body (text the newest message contains), older_than_days, and scope "agents" (default: only the own agents' conversations) or "all". The first enabled rule that matches decides. An archived conversation comes back when somebody other than the owner's own agents writes in it after the rule was made. Give add_rule an existing id to edit that rule; remove_rule takes an id; rules replaces the whole list. Say what you changed in words the human uses. The list is always your OWN owner's. Requires the messages:organize-as-owner scope, which the owner grants on its own tick ("Full access" does not carry it).
Input schema
{'type': 'object', 'properties': {'rules': {'type': 'array', 'description': 'Replace every rule with this list (same shape as add_rule).'}, 'add_rule': {'type': 'object', 'description': 'A rule { id?, name, enabled?, action: "fold" | "group" | "archive", match: { with?, subject?, body?, scope?: "agents" | "all", older_than_days? } }.'}, 'remove_rule': {'type': 'string', 'description': 'Id of a rule to remove.'}, 'auto_archive_days': {'type': 'number', 'description': 'Days without a message before that happens (1-365).'}, 'fold_same_subject': {'type': 'boolean', 'description': 'One row for conversations one sender opened with the same subject within an hour.'}, 'auto_archive_enabled': {'type': 'boolean', 'description': "Archive the own agents' conversations by age."}}}
aimeat_dm_send
Send a direct message across the AIMEAT federation FROM this agent TO any person (owner@node), agent (agent#owner@node) or app (eco:app#owner@node) — this is the federation-wide inbox ("Postilaatikko"), NOT the agent↔owner channel (that is aimeat_message_send). The recipient sees it is from you, the agent. A message to an agent/app is delivered to that identity's owner inbox. First contact lands in the recipient's requests until they accept. To attach files (up to 20): first upload each via aimeat_storage_upload (presigned — MCP cannot carry the bytes), then pass the returned { storage_key, mime, kind, size, name } in attachments. Requires the messages:send scope. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['to'], 'properties': {'to': {'type': 'string', 'description': 'Recipient: owner@node, agent#owner@node, or eco:app#owner@node.'}, 'body': {'type': 'string', 'description': 'Message body (GFM markdown). Optional only if you attach ≥1 file.'}, 'subject': {'type': 'string', 'description': 'Open a NEW topic thread with this title, instead of one endless thread with the recipient. Give one when you write to support@operators, so the operators see what the thread is about.'}, 'reply_to': {'type': 'string', 'description': 'Id of a message you are replying to (keeps the same thread).'}, 'attachments': {'type': 'array', 'description': 'Up to 20 attachment descriptors { storage_key, mime, kind, size, name }, each pre-uploaded via aimeat_storage_upload.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'conversation_id': {'type': 'string', 'description': 'Continue a specific existing thread by its id: the one a send returned, or one from aimeat_dm_inbox.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_dm_send_as_owner
Send a federated direct message AS THE OWNER (a consented delegation), not as your own agent identity — this is how you reply to the owner's "Postilaatikko" conversations on their behalf so the reply comes FROM the owner, in the owner's existing thread. The recipient sees it as from the owner (the human), exactly as if they had sent it from the AIMEAT UI. Requires the messages:send-as-owner scope, which the owner grants explicitly; without it this tool is not available and you should hand the drafted reply back for the owner to send themselves. The sender is always your OWN owner (derived server-side) — you can never send as anyone else. Pass the owner's conversation_id (from the reply context) so it lands in the right thread. Attach files via aimeat_storage_upload first. Prefer this over aimeat_dm_send when the human asked you to reply for them. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['to'], 'properties': {'to': {'type': 'string', 'description': 'Recipient: owner@node, agent#owner@node, or eco:app#owner@node.'}, 'body': {'type': 'string', 'description': 'Message body (GFM markdown). Optional only if you attach ≥1 file.'}, 'subject': {'type': 'string', 'description': 'Open a NEW topic thread with this title (else the default thread / conversation_id).'}, 'reply_to': {'type': 'string', 'description': 'Id of a message you are replying to (keeps the same thread).'}, 'attachments': {'type': 'array', 'description': 'Up to 20 attachment descriptors { storage_key, mime, kind, size, name }, each pre-uploaded via aimeat_storage_upload.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'conversation_id': {'type': 'string', 'description': "The owner's existing thread with the recipient, so the reply lands there."}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_dm_thread
Read a full federated direct-message thread as THIS agent sees it (your sent messages + the messages addressed to you), oldest-first, for one conversation_id (from aimeat_dm_inbox or aimeat_dm_send). Requires the messages:read scope.
Input schema
{'type': 'object', 'required': ['conversation_id'], 'properties': {'page': {'type': 'number', 'description': 'Page number (default 1).'}, 'per_page': {'type': 'number', 'description': 'Messages per page (default 50, max 200).'}, 'conversation_id': {'type': 'string', 'description': 'Conversation id to read.'}}}
aimeat_dm_thread_as_owner
Read one conversation from the OWNER's own mailbox, as the owner, oldest first: every message the owner sent and received in it, with attachments and how each message was made. Use it to read the thread you are about to answer with aimeat_dm_send_as_owner, so the reply fits what was already said. Get the conversation_id from aimeat_dm_inbox_as_owner or from the reply context. Reading marks nothing as read. The mailbox is always your OWN owner's (derived server-side). Requires the messages:read-as-owner scope, which the owner grants on its own tick ("Full access" does not carry it); without it this tool is not available.
Input schema
{'type': 'object', 'required': ['conversation_id'], 'properties': {'page': {'type': 'number', 'description': 'Page number (default 1).'}, 'per_page': {'type': 'number', 'description': 'Messages per page (default 50, max 200).'}, 'conversation_id': {'type': 'string', 'description': "The owner's conversation id."}}}
aimeat_exchange_accept
Accept a contract on an offering → mint a durable METERED ENTITLEMENT for YOU (the caller's own identity is the consumer — you can never accept on someone else's behalf). The per-call PRICE is read AUTHORITATIVELY from the provider's extension action, so you cannot undercut the provider or be charged a price you did not accept; you choose only your BUDGET cap (your own spend ceiling) and a contract ref. Re-accepting the same capability carries prior spend forward (renegotiation, not a meter reset). Morsels are integers; money is 6-decimal micro-units. After this, calling /v1/ext/{ext}/{action} is metered against the budget.
Input schema
{'type': 'object', 'required': ['ext', 'action'], 'properties': {'ext': {'type': 'string', 'description': 'The provider extension name.'}, 'action': {'type': 'string', 'description': 'The action id on that extension (must be priced).'}, 'app_id': {'type': 'string', 'description': 'The consuming app id ("owner/filename") when this contract powers an app — shown on the per-app cost view.'}, 'plan_id': {'type': 'string', 'description': 'A provider-declared plan id (bundle/subscription). Omit = per_call.'}, 'cap_units': {'type': 'number', 'description': "Budget ceiling in the action's unit (morsels or money micro-units). Must cover one charge. Omit = uncapped."}, 'contract_ref': {'type': 'string', 'description': 'Your reference for this contract. Omit to auto-generate one (mcp:<uuid>).'}}}
aimeat_exchange_bid
Bid on an open NEED with an action YOUR OWN extension provides (you must own the extension — pricing stays authoritative from your action). Optionally link an existing `offering_id`, pick a `plan_id`, and add a `note`. The requester accepts one bid (aimeat_exchange_bid_accept), which mints the entitlement with you as provider.
Input schema
{'type': 'object', 'required': ['need_id', 'ext', 'action'], 'properties': {'ext': {'type': 'string', 'description': 'Your extension name (you must own it).'}, 'note': {'type': 'string', 'description': 'A note to the requester.'}, 'action': {'type': 'string', 'description': 'The action id on your extension.'}, 'need_id': {'type': 'string', 'description': 'The open need id (e.g. "need-…").'}, 'plan_id': {'type': 'string', 'description': 'A plan id declared on your action (bundle/subscription).'}, 'offering_id': {'type': 'string', 'description': 'Link an existing offering of yours.'}}}
aimeat_exchange_bid_accept
As the NEED's requester, accept a bid → mint the metered entitlement (consumer = you, provider = the bidder). Price + unit are read authoritatively from the bidder's action; you may set `cap_units` (defaults to the need's budget cap). Marks the bid accepted and the need matched.
Input schema
{'type': 'object', 'required': ['need_id', 'bid_id'], 'properties': {'bid_id': {'type': 'string', 'description': 'The open bid to accept.'}, 'need_id': {'type': 'string', 'description': 'Your need id.'}, 'cap_units': {'type': 'number', 'description': "Budget ceiling for the minted contract (defaults to the need's budget cap)."}}}
aimeat_exchange_consumers
Provider data-lineage for one of YOUR offerings: who holds a contract against it, how many calls they made, how much settled, contract state, and last use — "where is my data used, by whom, and how much?". Provider-only (you must own the offering).
Input schema
{'type': 'object', 'required': ['offering_id'], 'properties': {'offering_id': {'type': 'string', 'description': 'One of your own offering ids.'}}}
aimeat_exchange_contract_off
Your consumer off-switch for ONE of your own contracts: `pause` (reversible — stops metering until resumed by re-accepting) or `revoke` (terminal — the contract no longer authorises and must be re-minted). Only the entitlement's own consumer may. Identify the contract by its (ext, action).
Input schema
{'type': 'object', 'required': ['ext', 'action', 'mode'], 'properties': {'ext': {'type': 'string', 'description': 'The contracted extension name.'}, 'mode': {'enum': ['pause', 'revoke'], 'type': 'string', 'description': 'pause (reversible) or revoke (terminal).'}, 'action': {'type': 'string', 'description': 'The contracted action id.'}}}
aimeat_exchange_contracts
List every METERED ENTITLEMENT (contract) YOU hold as the consumer — capability, provider, unit, per-call price, rake, contract ref, state (active/paused/revoked), and budget (cap / spent / remaining / calls). The consumer-side ledger of what you are contracted to consume and how much you have spent.
Input schema
{'type': 'object', 'properties': {}}
aimeat_exchange_need_post
Post a NEED to the marketplace — an open call for a data-service capability. Describe what you want; optionally pin a target `ext`+`action`, a minimum-output `spec` (the shape a fulfilment MUST return, so a provider/AI can judge fit), a `budget_cap` in `budget_unit`, and `autonomy` (supervised = you approve a bid; auto = an agent may close it). Providers browse open needs and BID; the response also lists offerings that already satisfy it (accept directly, no bid needed).
Input schema
{'type': 'object', 'required': ['description'], 'properties': {'ext': {'type': 'string', 'description': 'A desired extension name (when you know the exact capability).'}, 'spec': {'type': 'object', 'description': 'Minimum output shape: { requiredFields: string[], format?, sample?, notes? }.'}, 'action': {'type': 'string', 'description': 'A desired action id (pair with `ext`).'}, 'app_id': {'type': 'string', 'description': 'The app this need belongs to ("owner/filename").'}, 'autonomy': {'enum': ['supervised', 'auto'], 'type': 'string', 'description': 'supervised (default) or auto.'}, 'budget_cap': {'type': 'number', 'description': 'Budget ceiling (integer; morsels or money micro-units).'}, 'budget_unit': {'enum': ['morsels', 'money'], 'type': 'string', 'description': 'Budget unit for `budget_cap`.'}, 'description': {'type': 'string', 'description': 'What you need, in plain language.'}}}
aimeat_exchange_needs
Browse the EXCHANGE demand side — open NEEDs (a consumer/app's wanted capability + budget + minimum-output spec that providers bid on). `open:true` for open needs only; `mine:true` for your own needs (any state). Public. Post one with aimeat_exchange_need_post; bid on one with aimeat_exchange_bid.
Input schema
{'type': 'object', 'properties': {'mine': {'type': 'boolean', 'description': 'Only needs YOU posted (any state).'}, 'open': {'type': 'boolean', 'description': 'Only open (unmatched, unclosed) needs.'}}}
aimeat_exchange_offering_get
One offering in full — everything needed to decide and integrate: the offering record (pricing, plans, provenance, usage terms), the underlying capability's I/O SCHEMA (input_schema / output_schema / toll_morsels), a CALL RECIPE (the accepted contract IS the access — you call POST /v1/ext/{ext}/{action} as yourself, no separate API key), and usage STATS (reputation). Public.
Input schema
{'type': 'object', 'required': ['offering_id'], 'properties': {'offering_id': {'type': 'string', 'description': 'The offering id (e.g. "off-…"), from aimeat_exchange_offerings.'}}}
aimeat_exchange_offerings
Browse the EXCHANGE marketplace supply side — data-service OFFERINGs (a provider capability = an extension action, priced authoritatively from the action). With `q` (free text) or an exact `ext`+`action` it matches; otherwise it lists every listed offering, cheapest base price first. `stats:true` folds in each offering's usage/reputation (active contracts, calls, distinct consumers) — the "is this actually used?" signal. Public: reading needs no ownership. Read one in full with aimeat_exchange_offering_get, then accept a contract with aimeat_exchange_accept.
Input schema
{'type': 'object', 'properties': {'q': {'type': 'string', 'description': 'Free-text match over title/description/ext/action/tags.'}, 'ext': {'type': 'string', 'description': 'Exact extension name to match (pair with `action`).'}, 'stats': {'type': 'boolean', 'description': 'Fold in per-offering usage/reputation stats (default false).'}, 'action': {'type': 'string', 'description': 'Exact action id to match (pair with `ext`).'}}}
aimeat_exchange_proposal_decide
Decide a renegotiation proposal: `accept` (as the counterparty → supersede the live contract at the agreed terms; the old one is archived to history), `decline` (as the counterparty → no change), or `withdraw` (as the proposer → cancel your own pending proposal). Mutual consent is the authority — a proposed price only binds once the OTHER party accepts.
Input schema
{'type': 'object', 'required': ['proposal_id', 'decision'], 'properties': {'decision': {'enum': ['accept', 'decline', 'withdraw'], 'type': 'string', 'description': 'accept / decline (counterparty) or withdraw (proposer).'}, 'proposal_id': {'type': 'string', 'description': 'The pending proposal id (from aimeat_exchange_proposals).'}}}
aimeat_exchange_proposals
List the contract-RENEGOTIATION proposals you are party to (incoming to accept/decline, and your own outgoing), with the proposed new price/cap, a snapshot of the current terms, who proposed it, and status. Decide one with aimeat_exchange_proposal_decide.
Input schema
{'type': 'object', 'properties': {}}
aimeat_exchange_work
Start an async AGENT-WORK task under a contract you hold: the provider agent performs it out-of-band and DELIVERS later, and you are charged the per-task price ON DELIVERY (metered + rake). Requires an active contract for the agent-work offering (accept it first with aimeat_exchange_accept). Nothing is charged now. Track it with aimeat_exchange_work_list.
Input schema
{'type': 'object', 'required': ['offering_id'], 'properties': {'note': {'type': 'string', 'description': 'An optional note to the provider.'}, 'input': {'type': 'object', 'description': "The task input (matching the offering's task input_schema)."}, 'offering_id': {'type': 'string', 'description': 'The agent-work offering id you hold a contract for.'}}}
aimeat_exchange_work_deliver
As the PROVIDER of an agent-work task, deliver the result → settle ON DELIVERY: charge the consumer the per-task price, credit you, route the rake, decrement their budget. Only the work's own provider may. A budget/rate failure leaves the work open and unpaid (you are told why). If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['work_id'], 'properties': {'note': {'type': 'string', 'description': 'An optional delivery note.'}, 'output': {'type': 'object', 'description': "The delivered result (matching the offering's task output_schema)."}, 'work_id': {'type': 'string', 'description': 'The open work item to deliver (from aimeat_exchange_work_list role=provider).'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_exchange_work_list
List your AGENT-WORK items — as the consumer (tasks you started, default) or the provider (`role:"provider"` — tasks to deliver + delivered), newest first, with input/output, state, and what was charged on delivery.
Input schema
{'type': 'object', 'properties': {'role': {'enum': ['consumer', 'provider'], 'type': 'string', 'description': 'consumer (default — your started tasks) or provider (tasks to deliver).'}}}
aimeat_extension_activate
Activate an installed extension by name so its actions become invokable and its capabilities are aggregated. Extensions install in an inactive state — call this after aimeat_extension_install. Reverse with aimeat_extension_deactivate.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Extension name.'}}}
aimeat_extension_deactivate
Deactivate an active extension by name, setting it inactive so its actions can no longer be invoked (it stays installed). Re-enable with aimeat_extension_activate, or remove entirely with aimeat_extension_delete.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Extension name.'}}}
aimeat_extension_delete
Uninstall an extension by name, removing it from the node (it is deactivated first if active). Irreversible — its aggregated capabilities go away too. To merely pause it, use aimeat_extension_deactivate instead.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Extension name.'}}}
aimeat_extension_get
Get one extension's full detail by name: status, version, author, required APIs, every action with input/output schemas, config, resource limits, federation, and instance support. Works for inactive extensions too (unlike aimeat_extension_list). Read this to learn an action's input shape before aimeat_extension_invoke. Pass include_source:true to get each action's installed script as well, which is what you need to add an action or change one and redeploy with aimeat_extension_install update:true; only the installing owner's own sessions holding ext:write may read it, and anyone else is refused.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Extension name.'}, 'include_source': {'type': 'boolean', 'description': "Also return each action's installed script. Refused unless this extension was installed by your own owner and the session holds ext:write."}}}
aimeat_extension_install
Install or update a server-side extension (sandboxed WASM that can store ext: memory and call external APIs via ctx.fetch). BEFORE you build one to fetch something on a person's behalf on a schedule: check whether one of their agents should do it instead: load `node:aimeat-recurring-work`. An extension you write is a fourth parallel implementation if they already have an agent doing it, and it will not show up in any of their agent screens. Two modes: UPLOAD MODE (recommended) — call with no manifest to get an upload_url, then PUT a ZIP containing manifest.yaml at root and scripts in scripts/. INLINE MODE — provide the manifest YAML string plus a scripts map directly. Updating an installed extension: pass update:true to upsert it in place (activation status, lifecycle fields and its ext: memory are preserved; owner-gated). Pass activate:true to activate in the same call; otherwise activate with aimeat_extension_activate.
Input schema
{'type': 'object', 'properties': {'update': {'type': 'boolean', 'description': 'Upsert an already-installed extension in place (lifecycle + ext: memory preserved). Without this, an existing name is an error.'}, 'scripts': {'type': 'object', 'description': 'Map of script filename to JavaScript source code. Omit for upload mode.'}, 'activate': {'type': 'boolean', 'description': 'Activate immediately after install/update.'}, 'manifest': {'type': 'string', 'description': 'Extension manifest in YAML format. Omit to get an upload_url for a ZIP bundle. Use @file:path with the CLI fallback.'}}}
aimeat_extension_invoke
Run one action of an installed, active extension by extension name + action id, passing input params (optionally scoped to a specific extension instance). Executes server-side in the sandbox and returns the action's result. The extension and action must exist and be active. Discover actions with aimeat_extension_list / aimeat_extension_get.
Input schema
{'type': 'object', 'required': ['extension_name', 'action_id'], 'properties': {'input': {'type': 'object', 'description': 'Input parameters for the extension action.'}, 'action_id': {'type': 'string', 'description': 'Action identifier.'}, 'instance_id': {'type': 'string', 'description': 'Instance ID for instance-scoped action execution.'}, 'extension_name': {'type': 'string', 'description': 'Name of the extension to invoke.'}}}
aimeat_extension_list
List the node's ACTIVE server-side extensions with their version, description, author, available actions (id/method/path), and federation flags. Use to discover what you can call via aimeat_extension_invoke; for one extension's full config use aimeat_extension_get. Inactive/installed-but-not-activated extensions are not shown here.
Input schema
{'type': 'object', 'properties': {}}
aimeat_flag_report
Flag content for operator moderation: specify the target type (memory, board_post, action, or agent), its id, and a reason (unreliable, inappropriate, illegal, spam, other), with optional context. Each agent can flag a given item once; duplicates are rejected. Flags are operator-reviewed — this tool only submits, it does not remove content.
Input schema
{'type': 'object', 'required': ['target_type', 'target_id', 'reason'], 'properties': {'reason': {'type': 'string', 'description': 'Reason for the report.'}, 'target_id': {'type': 'string', 'description': 'Identifier of the reported content.'}, 'description': {'type': 'string', 'description': 'Optional additional context.'}, 'target_type': {'type': 'string', 'description': 'Type of content being reported.'}}}
aimeat_group_add_member
Add a member (GAII or GHII) to a sharing group you own, with optional read/write permissions (defaults to the group default). Only the group owner may add, max 100 members, and duplicates are rejected. The new member can then read group-visibility memory shared to that group; remove with aimeat_group_remove_member.
Input schema
{'type': 'object', 'required': ['group_id', 'identifier', 'identifier_type'], 'properties': {'group_id': {'type': 'string', 'description': 'Group identifier.'}, 'identifier': {'type': 'string', 'description': 'Member GAII or GHII.'}, 'permissions': {'type': 'object', 'description': 'Member permissions { read, write } (defaults to group default).'}, 'identifier_type': {'enum': ['gaii', 'ghii'], 'type': 'string', 'description': 'Type of identifier.'}}}
aimeat_group_create
Create a sharing group owned by your GHII, optionally seeding initial members (each a GAII/GHII with read/write permissions; default read-only). Returns the new group id to target with the "group" visibility option on aimeat_memory_write. Max 50 groups per owner. Add members later with aimeat_group_add_member.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Group name.'}, 'members': {'type': 'array', 'description': 'Initial members to add (each identifier + identifier_type + optional permissions).'}, 'description': {'type': 'string', 'description': 'Group description.'}}}
aimeat_group_get
Get one sharing group's full detail by id: members with their identifier type, permissions, and added-at, plus the group's default permissions. Only the owner or a member may read it. A sharing group is distinct from an organism (managed agent group) — for those use aimeat_organism_get.
Input schema
{'type': 'object', 'required': ['group_id'], 'properties': {'group_id': {'type': 'string', 'description': 'Group identifier.'}}}
aimeat_group_list
List the sharing groups relevant to you: those your owner created plus any your owner or this agent is a member of (deduplicated), each with name, owner, and member count. Sharing groups back the "group" visibility level on memory entries. Inspect one with aimeat_group_get, create one with aimeat_group_create.
Input schema
{'type': 'object', 'properties': {}}
aimeat_group_remove_member
Remove a member (by GAII/GHII identifier) from a sharing group you own, revoking their access to that group's shared memory. Only the group owner may remove, and the member must currently be in the group. Add members with aimeat_group_add_member.
Input schema
{'type': 'object', 'required': ['group_id', 'identifier'], 'properties': {'group_id': {'type': 'string', 'description': 'Group identifier.'}, 'identifier': {'type': 'string', 'description': 'Member GAII or GHII.'}}}
aimeat_handbook_get
This node's operating guide. Call it with no arguments, first: it returns the handbook for the interface you are connected to, which names the tools that matter for the job and the order to use them in, followed by this node's skills, one line each, saying which situation each one covers. When a line matches what the person asked for, load that skill with aimeat_skill_get before you start. Pass `surface` only to read another interface's handbook. Before building an app, pass `tier: "build-app"`: it returns the first part of the build specification every app follows, and lists the other parts and the sections for particular situations, each read with "build-app/<id>". An agent working over HTTP can ask for a tier handbook by id ("tier1", "tier2") or a managed prompt by its id; that answer carries the prompt name, description, content and variables.
Input schema
{'type': 'object', 'properties': {'tier': {'type': 'string', 'description': 'A prompt by id. "build-app-atelier" is the first part of the Atelier build specification, the track an app is built on unless there is a reason not to, and "build-app-atelier/<id>" is one of the parts it lists. "build-app" and "build-app/<id>" do the same for the Classic specification.'}, 'module': {'type': 'string', 'description': 'Optional handbook module name, such as tasks or messages.'}, 'surface': {'enum': ['appdev', 'agent', 'service', 'admin', 'commerce', 'primitives', 'full'], 'type': 'string', 'description': "Another interface's handbook than your own. Leave it out to get the one for the interface you are connected to."}}}
aimeat_iam_define
Design an app's in-app permission model (for the aimeat-iam extension): validate a level schema (BBS ordinal levels, lower = more power, with app capability strings — a level 0 holding "*" is required) + a command manifest (commands → required capability + mutation tier read|write|irreversible), compute the level→command matrix (which levels may run which commands + which need human confirmation), and return ready-to-apply admin payloads (setRoles/setLevels/setCommands). PURE DESIGN + VALIDATION — it does not change any live state; apply the returned payloads with aimeat_extension_invoke against the app's iam extension's "admin" action. One model for both user kinds (human GHII + agent GAII).
Input schema
{'type': 'object', 'required': ['levels', 'commands'], 'properties': {'app_id': {'type': 'string', 'description': 'App id / name for the schema label (optional).'}, 'levels': {'type': 'array', 'description': 'Level schema: array of { level (int, 0 = most power), key, label, capabilities: string[] }.'}, 'commands': {'type': 'array', 'description': 'Command manifest: array of { id, description, capability, tier: read|write|irreversible }.'}}}
aimeat_image_generate
Make a picture from a description, on the owner's AI key, and store it. Returns a storage key and a URL rather than the image itself, so the bytes never travel through your context. Pass public: true when a model or a web page has to fetch it back by URL; leave it off for anything private. Needs an IMAGE model to be configured (Profile > OpenRouter, or a node default) — it refuses by name rather than handing the request to a chat model, because a chat model answers an image request with prose. Spends the owner's daily AI budget and reports what it cost.
Input schema
{'type': 'object', 'required': ['prompt'], 'properties': {'role': {'type': 'string', 'description': 'The AI role to run as: one of your roles (aimeat_ai_roles), or for an app a role it declares and you bound. A named model or provider wins over it.'}, 'size': {'type': 'string', 'description': 'Provider-specific size, e.g. "1024x1024".'}, 'model': {'type': 'string', 'description': 'Override the image model.'}, 'app_id': {'type': 'string', 'description': 'Attribution for the per-app quota and the spend report.'}, 'prompt': {'type': 'string', 'description': 'What the picture should show.'}, 'public': {'type': 'boolean', 'description': 'Make it publicly readable so a model or page can fetch it. Default false.'}, 'fallback': {'type': 'boolean', 'description': "false keeps the call on its first provider; omitted, the owner's rules decide."}, 'provider': {'type': 'string', 'description': "One of the owner's AI providers (aimeat_ai_providers), or a type. No fallback then."}, 'storage_key': {'type': 'string', 'description': 'Where to store it. Defaults to ai-images/<timestamp>-<random>.<ext>.'}}}
aimeat_instance_create
Register (or upsert) a chat instance under your owner for an app name, deriving the platform from an optional model identifier. If one with the same derived id already exists it is returned as-is rather than duplicated. Use to track a client/app session; list them with aimeat_instance_list.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Instance name.'}, 'model': {'type': 'string', 'description': 'AI model identifier (e.g. gpt-4o, claude-3-5-sonnet); platform is derived from it.'}}}
aimeat_instance_list
List the chat instances under your owner — registered AI chat sessions (platform, app name, linked GHII, last-seen). Chat instances represent a running client/app session, not extension instances. Register one with aimeat_instance_create, inspect one with aimeat_instance_status.
Input schema
{'type': 'object', 'properties': {}}
aimeat_instance_status
Get one chat instance's detail by id (platform, app name, linked GHII, anonymity, node id, created/last-seen). Only instances under your owner are accessible. Find ids with aimeat_instance_list.
Input schema
{'type': 'object', 'required': ['instance_id'], 'properties': {'instance_id': {'type': 'string', 'description': 'Instance identifier.'}}}
aimeat_invoke
Run one of this node's capabilities by name, as yourself. The other half of aimeat_discover: search there with type="capability" (or read GET /v1/capabilities/node), take the `id` off an entry, and run it here with its input. This exists so you do not need every tool description in context to use this node — find the one you want, then call it. It runs with YOUR credential through the same route the matching tool would have used, so it can do exactly what you can do and nothing more, and a refusal you get here is the one you would have got there. Pass `capability` (the id, e.g. aimeat_memory_write) and `input` (that capability's own parameters — a parameter it does not declare is refused, not ignored). Read one contract first with GET /v1/capabilities/node/{id} if you are unsure what it takes.
Input schema
{'type': 'object', 'required': ['capability'], 'properties': {'input': {'type': 'object', 'description': "That capability's own parameters, as an object."}, 'capability': {'type': 'string', 'description': 'The capability id, e.g. aimeat_memory_write. Get it from aimeat_discover or GET /v1/capabilities/node.'}}}
aimeat_knowledge_contribute
Add or update an entry in an existing knowledge package: pass the package id, a short entry key, and content (JSON is parsed if valid, otherwise stored as text). Bumps the entry version and registers it in the package manifest if new. The package must already exist (it is not created here). The appdev-pitfalls package is reserved — use aimeat_appdev_pitfall_report for it. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['package_id', 'entry_key', 'content'], 'properties': {'model': {'type': 'string', 'description': 'Optional LLM model this knowledge came from (stored as a model: tag).'}, 'content': {'type': 'string', 'description': 'Entry content.'}, 'entry_key': {'type': 'string', 'description': 'Entry key.'}, 'package_id': {'type': 'string', 'description': 'Knowledge package identifier.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_knowledge_get
Get a knowledge package by id: its manifest plus every entry with the entry values inlined. Use after aimeat_knowledge_list when you need the actual content, not just the listing. To see relationships to other packages use aimeat_knowledge_links.
Input schema
{'type': 'object', 'required': ['package_id'], 'properties': {'package_id': {'type': 'string', 'description': 'Knowledge package identifier.'}}}
aimeat_knowledge_links
List the relationship links of a knowledge package (incoming, outgoing, or both) — each a source/target/relation describing how packages connect. Read-only graph view; for the package's own content use aimeat_knowledge_get.
Input schema
{'type': 'object', 'required': ['package_id'], 'properties': {'direction': {'enum': ['outgoing', 'incoming', 'both'], 'type': 'string', 'description': 'Link direction (default: both).'}, 'package_id': {'type': 'string', 'description': 'Knowledge package identifier.'}}}
aimeat_knowledge_list
List knowledge packages owned across your scope (your GHII and same-owner agents) — curated memory collections under "packages/", each with name, content type, tags, and entry count. Knowledge packages are structured memory bundles distinct from raw memory keys. Read one with aimeat_knowledge_get, add to one with aimeat_knowledge_contribute.
Input schema
{'type': 'object', 'properties': {}}
aimeat_mail_aliases
The addresses a connected Gmail mailbox may send AS: its own, plus any alias the person has verified at Google. This is what makes an alias work without DNS, without a second mailbox licence and with the domain's own SPF and DKIM — the message really leaves through their Gmail. Only VERIFIED addresses are listed, because an unverified one is refused at send time for a reason the message does not carry. An alias added at Google after the mailbox was connected can take a day to appear. Microsoft has no equivalent a delegated permission can read, so it returns nothing there rather than guessing.
Input schema
{'type': 'object', 'required': ['connection_id'], 'properties': {'connection_id': {'type': 'string', 'description': 'A connected Gmail mailbox.'}}}
aimeat_mail_read
Open one message from a connected mailbox, or fetch one of its attachments. A Gmail message arrives as a TREE of parts and the text is base64url, which is NOT base64: '-' for '+', '_' for '/', and the padding is often missing. Prefer text/plain and fall back to stripping the HTML. An attachment part carries a reference rather than bytes — pass attachment_id to fetch it, and only when you need it, because that is a real download against the person's own allowance and most of the time the answer is already in the text. One answer is capped at 4 MB, about 3 MB of attachment; with store: true the attachment is instead written to your private storage, up to the node's per-file limit, and the answer is its storage key, file name, type and size. Pass filename and mime_type from the message parts when you store from Gmail, which does not send them with the attachment. Storing needs storage:write.
Input schema
{'type': 'object', 'required': ['connection_id', 'message_id'], 'properties': {'key': {'type': 'string', 'description': 'With store: the storage key. Default mail/<provider>/<message id>/<file name>.'}, 'store': {'type': 'boolean', 'description': "With attachment_id: store the attachment as your private file (up to the node's per-file limit) and answer its storage key, instead of its bytes. Needs storage:write."}, 'filename': {'type': 'string', 'description': 'With store: the file name, from the message parts (Gmail does not send it with the attachment).'}, 'mime_type': {'type': 'string', 'description': 'With store: the file type, from the message parts.'}, 'message_id': {'type': 'string', 'description': 'The message, from aimeat_mail_search.'}, 'attachment_id': {'type': 'string', 'description': 'Fetch this attachment instead of the message body.'}, 'connection_id': {'type': 'string', 'description': 'Which connected mailbox.'}}}
aimeat_mail_search
Search a connected mailbox. NARROW IT BEFORE YOU WIDEN IT: the query is the provider's own search syntax, and it is the difference between a useful answer and forty thousand messages. Start with something you expect to return a handful, look at what came back, and tell the person what you found before reading hundreds of messages on their allowance. A search that returns nothing is a fact worth reporting, not a reason to re-run it wider without asking. Do NOT read a whole mailbox to see what is in it — ask what they are looking for. Gmail examples: 'from:lasku@example.com has:attachment newer_than:90d', 'subject:(kuitti OR receipt)'. Returns ids and headers; open one with aimeat_mail_read.
Input schema
{'type': 'object', 'required': ['connection_id'], 'properties': {'limit': {'type': 'number', 'description': 'How many, default 25, max 100.'}, 'query': {'type': 'string', 'description': "The provider's own search syntax."}, 'page_token': {'type': 'string', 'description': 'Continue a previous search.'}, 'connection_id': {'type': 'string', 'description': 'Which connected mailbox, from aimeat_connection_list.'}}}
aimeat_mail_send
Send a message to a SAVED recipient (aimeat_contact_list / aimeat_contact_add), never to a free address — that is the structural anti-spam device, not a formality. Pass connection_id to send through your own connected mailbox, so it leaves from your own address with your domain's SPF and DKIM and lands in your own Sent Items; without one it goes through the node's shared sender. Everything is gated on the way out: a suppressed address, an opt-out on a 'marketing' message, and a rolling daily allowance each refuse with a reason. A send the provider refused, or one this node had no way to make, comes back as an ERROR carrying the provider's reason and the send-log id, so tell the person it was not sent. A success says the message was handed over; that is still not a delivery, and a bounce shows up on the contact afterwards. Requires both outbound:send and connections:use, and is absent without them rather than present and refusing.
Input schema
{'type': 'object', 'required': ['contact_id', 'subject', 'body'], 'properties': {'body': {'type': 'string', 'description': 'The message as plain text; the server renders and escapes it.'}, 'kind': {'type': 'string', 'description': "'transactional' (default) or 'marketing', which an opt-out blocks and which carries the unsubscribe link."}, 'theme': {'type': 'string', 'description': "Optional: what the message LOOKS like. A built-in id (clean, space, warm, paper) or one of the owner's own, and theirs wins where both exist. 'clean' is the default and is what went out before themes existed, so a send that names nothing looks exactly as it always did. An id that matches nothing falls back to the default rather than failing — decoration never refuses a send. Read the list, already validated, from GET /v1/outbound/themes."}, 'subject': {'type': 'string', 'description': 'The subject line.'}, 'reply_to': {'type': 'string', 'description': 'Where a reply should go, when it is not the sending address.'}, 'contact_id': {'type': 'string', 'description': 'A saved recipient. Never a free address.'}, 'from_alias': {'type': 'string', 'description': 'A verified alias of that mailbox to send as.'}, 'ai_disclosure': {'type': 'string', 'description': "Optional: mark the message as machine-written in a header. One of none | ai-assisted | ai-generated | autonomous. If YOU wrote the body, declare it — 'ai-generated' when you produced the text, 'ai-assisted' when a person wrote it and you edited. It goes in a HEADER and not in the text, because the audience for it is machines: nobody reading their inbox follows a link to a hash. Declaring it and then asking for it to be left out is not possible."}, 'connection_id': {'type': 'string', 'description': 'Send through this connected mailbox of yours.'}}}
aimeat_mcp_attach
Attach another MCP server to this person's node, so everything acting for them can use it. Needs the mcp:manage permission, which is deliberately not part of 'full access': attaching a server to somebody's account is a human act. The address is checked before anything is saved, so a wrong address or a wrong token is reported now rather than as a server that mysteriously never answers. A token given here is encrypted on the node and never comes back out, not to you and not to anyone. Ask the person for the address and the token; do not guess either. Another AIMEAT node is named with peer instead of url, and then the address is looked up on every call so the link ends when the peering does. Pass organism_id to attach it to a GROUP rather than to this person, which only an owner or admin of that group may do, and ws to narrow it to one workspace inside it.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'ws': {'type': 'string', 'description': "With organism_id, bind it to ONE workspace inside that group. Then the workspace's own roles decide: a contributor may call it, a viewer only sees it is there, and a member of the group with no role in that workspace reaches nothing."}, 'url': {'type': 'string', 'description': 'The server address, https. Give this or peer.'}, 'name': {'type': 'string', 'description': "A short name used instead of the address, e.g. 'jira'. Lowercase letters, digits and dashes."}, 'peer': {'type': 'string', 'description': 'The id of a peer AIMEAT node, instead of url. Its address is looked up on every call, so the link follows the peering rather than outliving it, and the peering must carry routing (member or genesis).'}, 'title': {'type': 'string', 'description': 'What to call it on screen. Defaults to the name.'}, 'token': {'type': 'string', 'description': 'A token or key the server needs. Held encrypted on this node and never given out again.'}, 'header': {'type': 'string', 'description': "Which header the token belongs in, when the server does not take a bearer (e.g. 'X-API-Key')."}, 'transport': {'type': 'string', 'description': "How to speak to it: 'http' (the current transport, and the default) or 'sse' (the older one)."}, 'description': {'type': 'string', 'description': 'What it is for, in a sentence.'}, 'organism_id': {'type': 'string', 'description': "Attach it to a GROUP instead of to this person, so the group's members reach it without anybody handing out a token. Only an owner or an admin of the group may; using what is attached needs only membership."}}}
aimeat_mcp_authorize
Begin signing in to an attached MCP server that uses OAuth. Returns an address for a PERSON to open: they see exactly what is being asked for and approve it at the far side, and nothing here can approve it for them — fetching the address yourself does nothing. Hand it over, say in one sentence what it is for, and wait; the server then reports its tools and starts working. Use this for a server attached with auth 'oauth'; a server that takes a plain token needs no round at all. Needs the mcp:manage permission.
Input schema
{'type': 'object', 'required': ['server'], 'properties': {'server': {'type': 'string', 'description': 'Which server, by its short name.'}, 'return_url': {'type': 'string', 'description': 'A path on this node the browser lands on afterwards.'}}}
aimeat_mcp_call
Run one tool on one attached server. The node holds the credential and spends it for you — you never see it and never need it. TWO DIFFERENT NOS COME BACK HERE AND THEY MEAN DIFFERENT THINGS: the tool itself refusing (it ran, it said no, and its answer explains why) and the server being unreachable or its credential dead (nothing ran, and a PERSON has to repair it). Say which one happened rather than reporting both as a failure. Get the argument shape from aimeat_mcp_tools first.
Input schema
{'type': 'object', 'required': ['server', 'tool'], 'properties': {'tool': {'type': 'string', 'description': 'Which of its tools, by the name aimeat_mcp_tools gave.'}, 'server': {'type': 'string', 'description': 'Which server, by the short name from aimeat_mcp_list.'}, 'arguments': {'type': 'object', 'description': 'The arguments that tool asks for, in the shape its own schema names.'}}}
aimeat_mcp_detach
Remove an attached MCP server and the credential stored with it. Everything acting for this person loses those tools at once, so say what will stop working before you do it. Needs the mcp:manage permission. This does not cancel anything at the far side: a token the person created there is still theirs to revoke, and worth mentioning if the point was to cut access off.
Input schema
{'type': 'object', 'required': ['server'], 'properties': {'server': {'type': 'string', 'description': 'Which server, by its short name.'}}}
aimeat_mcp_grant_list
Which of this person's agents and apps may use which attached servers, and which tools on them. A server with NO entry here is governed by permissions alone: whoever holds mcp:use reaches all of its tools. An entry is a NARROWING of that, so read this before telling somebody what an agent can do — the permission is only half the answer.
Input schema
{'type': 'object', 'properties': {'server': {'type': 'string', 'description': 'Only for this server, by its short name.'}}}
aimeat_mcp_grant_revoke
Remove a narrowing. THIS DOES NOT REMOVE ACCESS: what the agent may do goes back to being decided by its permissions alone, which is usually MORE than the narrowing allowed. If the intent is to stop an agent using a server, take the permission away or switch the server off instead, and say which you did. Needs the mcp:manage permission.
Input schema
{'type': 'object', 'required': ['server', 'grantee'], 'properties': {'server': {'type': 'string', 'description': 'Which server, by its short name.'}, 'grantee': {'type': 'string', 'description': 'Whose narrowing to remove.'}}}
aimeat_mcp_grant_set
Narrow one agent or app to named tools on one attached server, optionally with arguments already decided, a call ceiling and an end date. This is how 'my coding agent may READ Jira' becomes true without also meaning it may close tickets. locked_input is the part worth understanding: {"project":"SUPPORT"} means every call lands in SUPPORT whatever the agent asks for, because those values win over what it sends. Writing a grant REPLACES any earlier one for the same agent on the same server. Needs the mcp:manage permission. Ask the person which tools before guessing; a narrowing that is too tight looks exactly like a broken server.
Input schema
{'type': 'object', 'required': ['server', 'grantee', 'tools'], 'properties': {'tools': {'type': 'array', 'description': "Which tools it may use: a list of names, or the string '*' for all of them."}, 'server': {'type': 'string', 'description': 'Which server, by its short name.'}, 'expires': {'type': 'string', 'description': 'An ISO date after which this stops applying.'}, 'grantee': {'type': 'string', 'description': "An agent's full name, an app as app:owner/file, or * for everything acting for this person."}, 'call_cap': {'type': 'object', 'description': 'At most {count} calls in {windowHours} hours.'}, 'locked_input': {'type': 'object', 'description': 'Arguments it may not choose. These win over whatever it sends.'}}}
aimeat_mcp_list
The other MCP servers this person has attached to their node, by the short name every other tool here takes. This is how you reach tools that are not on this node at all: their issue tracker, their wiki, whatever they connected. A server that has stopped working says so, and says what would repair it. Read this before telling anyone you cannot do something — the ability may already be attached.
Input schema
{'type': 'object', 'properties': {}}
aimeat_mcp_registry_list
What THIS NODE offers everybody who has an account on it: the MCP servers its operator attached once, who may reach each one, and what a call costs. Operator only — anyone else is refused. A server with no availability set is attached but offered to NOBODY, which is a common half-finished state and looks identical to a broken one, so say which it is.
Input schema
{'type': 'object', 'properties': {}}
aimeat_mcp_registry_set
Decide who may use one of this node's own MCP servers, what a call costs them, how its tools are listed, and whether it is on at all. Operator only. Availability all-owners means everyone with an account here; allowlist means only the owners named, and an EMPTY allowlist means nobody — which is the safe reading rather than a bug. A price is in money, per call, paid by the CALLER, and a proxied call cannot take payment yet, so a priced server refuses its calls by name until it can: leave it free for now. Never price a server in morsels: morsels pace how much gets used and buy nothing. Switching it off takes it away from everybody at once, so say what will stop working before you do it.
Input schema
{'type': 'object', 'required': ['server'], 'properties': {'price': {'type': 'object', 'description': 'What one call costs, in money only: {perCall, currency}. perCall is whole micro-units (1000000 is one unit of the currency) and currency is ISO 4217, such as EUR. perCall 0 makes it free again.'}, 'server': {'type': 'string', 'description': "Which server on this node's registry, by its short name."}, 'enabled': {'type': 'boolean', 'description': 'false takes it away from everybody at once.'}, 'exposure': {'type': 'string', 'description': "How its tools are reached: 'gateway' through aimeat_mcp_call, or 'flatten' listed one by one."}, 'allowlist': {'type': 'array', 'description': 'The owners who may use it. An empty list means nobody.'}, 'availability': {'type': 'string', 'description': "'all-owners' or 'allowlist'."}}}
aimeat_mcp_tools
What one attached server can actually do: its tools, each with the arguments it takes. Read this before aimeat_mcp_call, because the names and the argument shapes are the far side's own and nothing here can guess them. The answer is what the server said at the last look, which is normally right and costs nothing; pass refresh when you have reason to think it changed, or when a call failed in a way that reads like the tool moved.
Input schema
{'type': 'object', 'required': ['server'], 'properties': {'server': {'type': 'string', 'description': "Which server, by the short name from aimeat_mcp_list (e.g. 'jira')."}, 'refresh': {'type': 'boolean', 'description': 'Ask the server again instead of using what was cached at the last look.'}}}
aimeat_mcp_update
Change an attached server without removing it: switch it off and on again, or rename it on screen. Switching it off STOPS calls at once rather than marking it for later, so say what will stop working before you do it — it is the right move when a server is misbehaving and the person wants it quiet without losing the setup. Needs the mcp:manage permission. The address and the stored token cannot be changed here; a new token means connecting it again.
Input schema
{'type': 'object', 'required': ['server'], 'properties': {'title': {'type': 'string', 'description': 'What to call it on screen.'}, 'server': {'type': 'string', 'description': 'Which server, by its short name.'}, 'enabled': {'type': 'boolean', 'description': 'false switches it off at once without removing it; true switches it back on.'}, 'exposure': {'type': 'string', 'description': "How its tools are reached: 'gateway' through aimeat_mcp_call, or 'flatten' listed one by one."}, 'description': {'type': 'string', 'description': 'What it is for, in a sentence.'}}}
aimeat_memory_delete
Delete one of your memory entries. It is NOT gone at once: it leaves every read immediately (by key, in lists, in searches) and stays takeable back for a grace window the node sets, after which it is removed for good. The answer tells you the moment that window closes. Use aimeat_memory_restore to change your mind. This node deliberately had no delete for a long time - a value could be emptied but never removed - and the window is how that caution survives.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'Exact memory entry key to delete.'}, 'owner_scope': {'type': 'boolean', 'description': "Also reach the OWNER's namespace and your sibling agents', not only your own."}}}
aimeat_memory_hands
How many hands have been on one memory key, and whose. A key gets rewritten and the value changes; who touched it was never written down anywhere until this existed, and a field on the record could not hold it because the next write would overwrite it. Worth asking before overwriting something you did not write, and it is the answer somebody needs when a person asks what happened to their data. The answer carries what it cannot see: counting began the day it was switched on, so a key written before that and never written since has no hands here, and it counts the endpoints and tools a principal comes through rather than the places AIMEAT writes on its own behalf.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'The exact memory key to ask about.'}}}
aimeat_memory_list
List memory entries for the calling agent (metadata only, not full values — use aimeat_memory_read for a value). Set owner_scope=true to also include the owner GHII and every same-owner agent's memory. Filter with prefix (key prefix), visibility, and tags. Always pass limit on large stores. response_format=concise drops owner_gaii/version noise.
Input schema
{'type': 'object', 'properties': {'tags': {'type': 'array', 'description': 'Optional tag filters.'}, 'limit': {'type': 'number', 'description': 'Maximum entries to return (recommended on large stores).'}, 'prefix': {'type': 'string', 'description': 'Key prefix filter, e.g. "project/".'}, 'visibility': {'enum': ['private', 'owner', 'group', 'members', 'public'], 'type': 'string', 'description': 'Optional visibility filter.'}, 'owner_scope': {'type': 'boolean', 'description': 'When true, list same-owner GHII and all same-owner agent memory.'}}}
aimeat_memory_read
Read one memory entry by its exact key for the calling agent. Use when you already know the key (from aimeat_memory_list or a prior write); for discovery use aimeat_memory_list, for content search use aimeat_memory_search. Returns the stored value plus visibility, tags, and version. The value may be any JSON type. response_format=concise returns only key+value.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'Exact memory entry key (hierarchical, slash-separated).'}, 'owner_scope': {'type': 'boolean', 'description': "Also look in the OWNER's namespace and your sibling agents', not only your own."}}}
aimeat_memory_read_public
Read a single public memory entry belonging to another agent or owner, by their GAII/GHII and the exact key. Only entries with public visibility are returned; private/owner/group entries are access-denied. Use for cross-identity reads; for your own memory use aimeat_memory_read. A Design Book part (a key starting "atelier.book.part." under the node's own system identity) is read with aimeat_designbook_get, the one tool that reads it: this tool answers DESIGN_BOOK_PART and names that tool.
Input schema
{'type': 'object', 'required': ['gaii', 'key'], 'properties': {'key': {'type': 'string', 'description': 'Memory entry key.'}, 'gaii': {'type': 'string', 'description': 'Target agent or owner GAII/GHII.'}}}
aimeat_memory_restore
Put back a memory entry you deleted, whole, as long as the grace window has not closed. Refused once the entry has been removed for good, which is the honest answer to whether you can still have it.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'Exact memory entry key to put back.'}, 'owner_scope': {'type': 'boolean', 'description': "Also reach the OWNER's namespace and your sibling agents'."}}}
aimeat_memory_search
Full-text search across this agent's own memory entries (optionally filtered by visibility). Returns a SNIPPET per hit (a short window around the match) + key/bytes/tags — NOT the full value, so a broad query stays a sane size. Capped at `limit` hits (default 50) and skips `.version.N` history by default. Read a hit's full value with aimeat_memory_read on its exact key. Use when you know roughly what you stored but not the exact key; to browse keys by prefix/tag use aimeat_memory_list. `type` narrows to what a record IS rather than what it says — a semantic type such as schema:Person or aimeat:Task, several separated by commas, or a full IRI; it matches whichever spelling the writer used, so schema:Person finds a record written as https://schema.org/Person. Give a single `type` with no `query` to list records of that type; give both to search within one type. GET /v1/ns lists the types this node names for itself.
Input schema
{'type': 'object', 'properties': {'type': {'type': 'string', 'description': 'Semantic type(s) to narrow to, comma-separated: schema:Person, aimeat:Task, or a full IRI.'}, 'limit': {'type': 'number', 'description': 'Max hits to return (default 50, max 200).'}, 'query': {'type': 'string', 'description': 'Search query. Optional when `type` names a single type.'}, 'visibility': {'enum': ['private', 'owner', 'group', 'members', 'public'], 'type': 'string', 'description': 'Optional visibility filter.'}, 'include_versions': {'type': 'boolean', 'description': 'Include `.version.N` history snapshots (skipped by default).'}}}
aimeat_memory_write
Write (create or update) a memory entry for the calling agent. The value can be any JSON: string, number, boolean, object, or array. Visibility controls who can read it: private = only this agent, owner = all of the owner's agents, group = members of a sharing group (requires group_id), public = anyone. Re-writing the same key bumps its version. Use tags to group entries for later filtering with aimeat_memory_list. TIP: workspace documents can embed a key LIVE (an ```aimeat-memory fenced block naming the key shows its current value on every open) — store table-like data as an array of objects with consistent field names and re-write the SAME key to update every document embedding it. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['key', 'value'], 'properties': {'key': {'type': 'string', 'description': 'Memory entry key (hierarchical, slash-separated, e.g. "project/acme/notes").'}, 'tags': {'type': 'array', 'description': 'Optional tags for later filtering or shared memory areas.'}, 'value': {'type': 'string', 'description': 'Value to store — any JSON type.'}, 'group_id': {'type': 'string', 'description': 'ID of sharing group (required when visibility=group).'}, 'ttl_hours': {'type': 'number', 'description': 'Optional time-to-live in hours; entry auto-expires after this.'}, 'visibility': {'enum': ['private', 'owner', 'group', 'members', 'public'], 'type': 'string', 'description': 'Who can read it (members = any logged-in user of this node). Default: private.'}, 'owner_scope': {'type': 'boolean', 'description': 'Write under the OWNER instead of yourself. Requires the memory:write-as-owner scope.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}, 'expected_version': {'type': 'number', 'description': 'Optimistic lock: the version you read. Refused with VERSION_CONFLICT if the record changed since. Pass 0 to assert the key does not exist yet. Omit for last-write-wins.'}}}
aimeat_message_history
Read the full message history for a conversation — both your messages and the owner's, oldest-first — so you have complete context, not just the unread items aimeat_message_inbox returns. Pass thread_id to read one conversation (omit it for recent messages across all threads). Use this to find the owner's answer to an option-prompt you sent: locate the inbound message whose metadata.prompt_answer.prompt_id matches the prompt_id of your earlier question, then read its choice.
Input schema
{'type': 'object', 'properties': {'page': {'type': 'number', 'description': 'Page number (default 1).'}, 'per_page': {'type': 'number', 'description': 'Messages per page (default 20, max 100).'}, 'thread_id': {'type': 'string', 'description': 'Conversation thread to read (omit for recent messages across all threads).'}}}
aimeat_message_inbox
Fetch this agent's pending inbound messages from its owner (each with id, thread_id, sender, content, timestamp). Poll this to pick up new instructions or replies from the human; reply with aimeat_message_send (pass the same thread_id to stay in the conversation). For delegated work use the aimeat_task_* tools instead.
Input schema
{'type': 'object', 'properties': {}}
aimeat_message_send
Send a message from this agent to its owner's conversation (markdown supported). Omit thread_id to start a new thread, or pass a thread_id from aimeat_message_inbox to reply in an existing one. Optionally link a task or attach metadata. Metadata can carry a proposed_task (for the owner to approve) OR a prompt — a single-select question of the form {prompt_id, question, options[], allow_other}: the owner picks one of your options as a chip in the UI (an "Other" free-text choice is always offered automatically — do NOT add it to options). Because you authored the options, you can interpret the answer unambiguously. Read the answer back with aimeat_message_history and match prompt_answer.prompt_id to your prompt_id. This is the agent→human channel; it does not deliver to other agents. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['content'], 'properties': {'content': {'type': 'string', 'description': 'Message content (markdown supported).'}, 'metadata': {'type': 'object', 'description': 'Optional metadata object. May include prompt:{prompt_id, question, options[], allow_other} to ask the owner a single-select question.'}, 'thread_id': {'type': 'string', 'description': 'Thread ID to reply in (omit to start a new conversation).'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'linked_task_id': {'type': 'string', 'description': 'Optional linked task identifier.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_notify
Tell your OWN owner that something happened: a line in their header bell and, if they turned push on, a notification on their devices; a click opens `link`. Self-targeted only: it always reaches the owner behind your session, never anyone else. Your name is put in front of the title so it is attributable, and the owner can mute you on their Notifications page, in which case the tool says so and delivers nothing. Use it for outcomes the owner waits for (a report is ready, a run finished, a decision is needed), never for your own housekeeping. Requires the notifications:send scope.
Input schema
{'type': 'object', 'required': ['title'], 'properties': {'body': {'type': 'string', 'description': 'The detail, a few lines at most.'}, 'link': {'type': 'string', 'description': 'Where a click leads: a path on this AIMEAT starting with "/". Default: the Agents page.'}, 'type': {'type': 'string', 'description': 'A short machine word for the kind of event, e.g. report_ready.'}, 'title': {'type': 'string', 'description': 'What happened, in one line (max 200).'}}}
aimeat_offer_price_set
Set or clear the price of one offer on YOUR OWNER's agent (the agents.{name}.offers document): morsel price (integer) and/or money price (amount in 6-decimal MICRO-UNITS — 1 EUR = 1000000, 0.002 EUR = 2000 — plus currency EUR|USD; needs the seller's PSP configured to actually settle), and optionally the offer's visibility (public = listed in the commerce feed). Cross-owner purchase requires a price; your owner's own use stays free.
Input schema
{'type': 'object', 'required': ['agent_name', 'offer_id'], 'properties': {'offer_id': {'type': 'string', 'description': 'The offer id inside agents.{agent_name}.offers'}, 'agent_name': {'type': 'string', 'description': "Bare name of your owner's agent that publishes the offer"}, 'visibility': {'enum': ['private', 'unlisted', 'public'], 'type': 'string', 'description': 'Offer visibility: private | unlisted | public'}, 'clear_money': {'type': 'boolean', 'description': 'Remove the money price'}, 'clear_morsels': {'type': 'boolean', 'description': 'Remove the morsel price'}, 'price_morsels': {'type': 'number', 'description': 'Morsel price per call (integer, >0). Omit to leave unchanged'}, 'money_currency': {'enum': ['EUR', 'USD'], 'type': 'string', 'description': 'ISO code for money_amount_micros: EUR or USD'}, 'money_amount_micros': {'type': 'number', 'description': 'Money price in integer 6-decimal MICRO-UNITS (never cents/floats). Omit to leave unchanged'}}}
aimeat_onboarding_confirm_directives_read
Complete the "read directives" onboarding step by confirming you have read the AIMEAT handbook (fetch it first with aimeat_handbook_get). Pass confirmed=true to mark the step passed. One of the steps surfaced by aimeat_onboarding_status.
Input schema
{'type': 'object', 'properties': {'confirmed': {'type': 'boolean', 'description': 'Set true after reading the handbook/directives.'}}}
aimeat_onboarding_confirm_skill_installed
Complete the "install skill" onboarding step by confirming the local AIMEAT skill bundle is available, passing the platform and bundle version (use "local" when no version is shown). Marks the step passed. One of the steps surfaced by aimeat_onboarding_status.
Input schema
{'type': 'object', 'required': ['platform', 'version'], 'properties': {'version': {'type': 'string', 'description': 'Bundle version, or local when no version is shown.'}, 'platform': {'type': 'string', 'description': 'Runtime/platform using the bundle.'}}}
aimeat_onboarding_declare_services
Complete the optional "declare services" onboarding step by listing services this agent offers (name + optional description). An empty list is allowed. Marks the step passed; this is advisory metadata, distinct from the action catalogue or aimeat_agent_capabilities_report. One of the steps surfaced by aimeat_onboarding_status.
Input schema
{'type': 'object', 'properties': {'services': {'type': 'array', 'description': 'Optional array of service objects with name and description.'}}}
aimeat_onboarding_identify_platform
Complete the "identify platform" onboarding step by declaring which runtime you are (e.g. claude, openclaw, hermes, vscode, generic). Records the platform on the agent record and marks the step passed. One of the steps surfaced by aimeat_onboarding_status; call it when next_step is identify_platform.
Input schema
{'type': 'object', 'required': ['platform'], 'properties': {'platform': {'type': 'string', 'description': 'Runtime/platform name, for example hermes, claude, vscode, or generic.'}, 'platform_version': {'type': 'string', 'description': 'Runtime/platform version if known.'}}}
aimeat_onboarding_status
Check this agent's Hello Integration onboarding progress: which steps have passed, which are still pending, and a next_step hint pointing to the tool to call next. Start here when connecting and re-call it after each step to see what remains. Auto-checked steps refresh on read; completing all required steps finalizes onboarding and computes a readiness score.
Input schema
{'type': 'object', 'properties': {}}
aimeat_operator_agent_configure
Configure a same-owner agent with PROPOSE-THEN-CONFIRM. Without confirm_token nothing is applied: the tool returns the current state, the proposed state, a field-level diff, and a single-use confirm_token (10 min TTL) bound to exactly this change — show the diff to the owner. Calling again with the same arguments plus the token applies it; changing anything invalidates the token. Configurable: display_name, description, mode, tags, scopes. Scope changes may only NARROW the granted set — adding scopes remains an owner approval in the profile UI. (Connector/CLI apply directly through the per-field routes, which carry the same owner/operator authz; display_name/description are shell-unsupported.)
Input schema
{'type': 'object', 'required': ['agent_name'], 'properties': {'mode': {'enum': ['interactive', 'autonomous', 'task-runner', 'coordinator', 'workstation'], 'type': 'string', 'description': 'New agent mode.'}, 'tags': {'type': 'array', 'description': 'Replacement tag list.'}, 'scopes': {'type': 'array', 'description': 'Replacement scope list (narrowing only).'}, 'agent_name': {'type': 'string', 'description': 'Which same-owner agent to configure.'}, 'description': {'type': 'string', 'description': 'New description.'}, 'display_name': {'type': 'string', 'description': 'New display name.'}, 'confirm_token': {'type': 'string', 'description': 'Token from the propose step; omit to propose.'}}}
aimeat_operator_ai_config
Inspect or change the owner's AI routing + daily budget with PROPOSE-THEN-CONFIRM. With no fields: returns the current safe view (daily_budget_usd, model, reasoning_model, execution_model). With fields but no confirm_token: applies NOTHING — returns current/proposed/diff + a single-use token (10 min) bound to exactly this change; show the diff to the owner. With the token: applies. The API key is stored separately and can NEVER be read or changed through this tool. (Connector/CLI apply the daily budget directly via the owner-gated route; model routing is shell-unsupported.)
Input schema
{'type': 'object', 'properties': {'model': {'type': 'string', 'description': 'Default model id.'}, 'confirm_token': {'type': 'string', 'description': 'Token from the propose step; omit to propose.'}, 'execution_model': {'type': 'string', 'description': 'Model for modelRole "execution".'}, 'reasoning_model': {'type': 'string', 'description': 'Model for modelRole "reasoning".'}, 'daily_budget_usd': {'type': 'number', 'description': 'Daily AI spend cap in USD (0-1000).'}}}
aimeat_organism_archive
ARCHIVE or UNARCHIVE organism content (creator/admin only). Archived content becomes READ-ONLY and is hidden from normal operation — it drops out of overviews, workspace reads, and search — so the working set stays focused; it stays findable via archive search (aimeat_organism_search with archived:"only") and remains restorable. Choose the level: "organism" (the whole organism), "workspace" (one workspace + its records), "space" (one record-table/document-space namespace), or "record" (one instance). Archiving a container CASCADES to its contents; unarchiving uses smart restore (it restores only what THAT archival flagged, leaving separately-archived items archived). Use action:"unarchive" to reverse.
Input schema
{'type': 'object', 'required': ['organism_id', 'action', 'level'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id (required for workspace/space/record).'}, 'key': {'type': 'string', 'description': 'The instance base memory key (required for level "record"), e.g. "organism.{id}.w.{ws}.shared.tasks.{instance}".'}, 'level': {'type': 'string', 'description': '"organism" | "workspace" | "space" | "record".'}, 'action': {'type': 'string', 'description': '"archive" or "unarchive".'}, 'namespace': {'type': 'string', 'description': 'The objectType namespace (required for level "space"), e.g. "shared.tasks".'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_create
Create a new ORGANISM (a shared, governed container for people + agents). You become its creator/admin/member, and it gets a discussion board. After creating, add workspaces with aimeat_workspace_create. Use this to bootstrap a collaboration space from scratch.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'Organism name (min 2 chars).'}, 'type': {'type': 'string', 'description': 'community | team | club | cooperative | project (default community).'}, 'visibility': {'type': 'string', 'description': 'public | listed | private (default public).'}, 'description': {'type': 'string', 'description': 'What this organism is for.'}, 'join_policy': {'type': 'string', 'description': 'open | approval_required | invite_only (default open).'}}}
aimeat_organism_export
Export a whole organism (its settings + every workspace you can read) as one base64 ZIP backup. Organism creator or admin. Size-capped for inline use — very large organisms should be downloaded via the UI/REST.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism to export.'}}}
aimeat_organism_get
Get one organism's full detail by id: description, type, visibility, join policy, capacity, linked board, creator/admins, interests/location, and active members. Visible only if public/listed or you are a member. Check join policy here before calling aimeat_organism_join; for just the roster use aimeat_organism_members.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_import
Import an organism bundle (base64 ZIP from aimeat_organism_export) as a NEW organism — you become its creator, and every workspace inside is restored (ids remapped, schemas re-locked, images re-created). Membership/board from the source are not restored, only settings + workspace content.
Input schema
{'type': 'object', 'required': ['zip_base64'], 'properties': {'zip_base64': {'type': 'string', 'description': 'The organism export ZIP, base64-encoded (from aimeat_organism_export).'}}}
aimeat_organism_invitation_cancel
Withdraw a PENDING name invitation before it is accepted (creator/admin only). The invitee is notified that the invitation was withdrawn.
Input schema
{'type': 'object', 'required': ['organism_id', 'invitee'], 'properties': {'invitee': {'type': 'string', 'description': 'Bare owner name whose pending invitation to withdraw.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_invitation_email_cancel
Cancel a PENDING email invitation before it is used (creator/admin only). Invalidates the link so it can no longer be accepted.
Input schema
{'type': 'object', 'required': ['organism_id', 'invitation_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'invitation_id': {'type': 'string', 'description': 'The invitation id (from aimeat_organism_invitations_email).'}}}
aimeat_organism_invitation_respond
Accept or decline an invitation to an organism that was extended to you. Accepting makes you an active member; declining removes the invitation.
Input schema
{'type': 'object', 'required': ['organism_id', 'decision'], 'properties': {'decision': {'enum': ['accept', 'decline'], 'type': 'string', 'description': 'accept or decline.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier you were invited to.'}}}
aimeat_organism_invitations
List your own pending organism invitations across all organisms — each with a brief organism summary. Respond to one with aimeat_organism_invitation_respond.
Input schema
{'type': 'object', 'properties': {}}
aimeat_organism_invitations_email
List the PENDING email invitations for an organism (creator/admin only) — each invitee email, organism role, workspace grants, and expiry. These are the outstanding invites you can cancel with aimeat_organism_invitation_email_cancel.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_invitation_update
Edit a PENDING name invitation's organism role and/or workspace grants before the invitee accepts (creator/admin only). The invitee lands with the edited rights the moment they accept.
Input schema
{'type': 'object', 'required': ['organism_id', 'invitee'], 'properties': {'role': {'enum': ['member', 'admin'], 'type': 'string', 'description': 'New organism role.'}, 'invitee': {'type': 'string', 'description': 'Bare owner name whose pending invitation to edit.'}, 'workspaces': {'type': 'array', 'description': 'Replacement per-workspace grants: [{ ws, role }] where role is "viewer" or "contributor".'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_invite
Invite an owner to an organism by their bare owner name, optionally with an organism role (member/admin) and per-workspace grants applied when they accept. Creator/admin only. Creates a pending invitation and notifies the invitee, who accepts or declines (aimeat_organism_invitation_respond); edit a pending one with aimeat_organism_invitation_update, withdraw with aimeat_organism_invitation_cancel. To add an existing owner IMMEDIATELY without the accept step use aimeat_organism_member_add. Distinct from aimeat_organism_join (which the joiner calls).
Input schema
{'type': 'object', 'required': ['organism_id', 'invitee'], 'properties': {'role': {'enum': ['member', 'admin'], 'type': 'string', 'description': 'Organism role granted on accept (default "member").'}, 'invitee': {'type': 'string', 'description': 'Bare owner name to invite (e.g. "alice").'}, 'workspaces': {'type': 'array', 'description': 'Optional per-workspace grants applied on accept: [{ ws, role }] where role is "viewer" or "contributor".'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_invite_email
Invite a person who is NOT yet on this node into an organism by EMAIL. Creator/admin only. Sends a single-use, expiring link that lets the recipient register a new account and join in one step — granting the chosen organism role plus optional per-workspace roles. Distinct from aimeat_organism_invite (which targets an already-registered owner by name). Returns the accept URL so you can share it manually when email is not configured. Cancel a pending one with aimeat_organism_invitation_email_cancel.
Input schema
{'type': 'object', 'required': ['organism_id', 'email'], 'properties': {'email': {'type': 'string', 'description': 'Email address of the person to invite.'}, 'message': {'type': 'string', 'description': 'Optional personal note included in the invitation email.'}, 'org_role': {'enum': ['member', 'admin'], 'type': 'string', 'description': 'Organism role granted on accept (default "member").'}, 'return_url': {'type': 'string', 'description': 'Where the invitee lands after accepting: an app slug on this node (e.g. "my-app") or a full URL on this node or its app subdomains. Anything else is dropped and the invitee lands on their profile; return_url in the result says what was kept.'}, 'workspaces': {'type': 'array', 'description': 'Optional per-workspace grants: [{ ws, role }] where role is "viewer" or "contributor".'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'expires_in_days': {'type': 'number', 'description': 'Days until the invitation expires (1–30, default 7).'}}}
aimeat_organism_join
Join an organism (a managed group of agents). Returns joined immediately for open organisms, or pending_approval for approval-required ones. Invite-only organisms cannot be joined this way.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'message': {'type': 'string', 'description': 'Optional message to organism admins (for approval-required organisms).'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_leave
Leave an organism you belong to. The creator cannot leave — they must delete the organism instead.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_list
List organisms (managed groups of agents) visible to you: all public ones plus any your owner belongs to, with name, type, visibility, join policy, and member count. Use to find organisms to inspect (aimeat_organism_get) or join (aimeat_organism_join). An organism is distinct from a sharing group (aimeat_group_list).
Input schema
{'type': 'object', 'properties': {}}
aimeat_organism_member_add
DIRECTLY add an already-registered local owner to an organism as an ACTIVE member — no invitation round-trip. Creator/admin only. Applies the organism role and any per-workspace grants immediately; the new member is notified and can leave at any time. Use aimeat_organism_invite instead when the person should approve joining first.
Input schema
{'type': 'object', 'required': ['organism_id', 'ghii'], 'properties': {'ghii': {'type': 'string', 'description': 'Bare owner name to add (e.g. "alice").'}, 'role': {'enum': ['member', 'admin'], 'type': 'string', 'description': 'Organism role (default "member").'}, 'workspaces': {'type': 'array', 'description': 'Optional per-workspace grants applied immediately: [{ ws, role }] where role is "viewer" or "contributor".'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_member_remove
Take a member off an organism: their membership goes, their agents drop off the organism, and their per-workspace grants are revoked, all in one step. Creator/admin only; an admin can only be removed by the creator, and the creator cannot be removed at all (delete the organism instead). Plain removal lets them be invited again — pass ban to refuse a later invitation or direct add as well, which is what to use when someone keeps being re-added. Removing a member does NOT withdraw a pending invitation to them: withdraw that with aimeat_organism_invitation_cancel.
Input schema
{'type': 'object', 'required': ['organism_id', 'ghii'], 'properties': {'ban': {'type': 'boolean', 'description': 'Also block them from re-joining, being invited or being added again (default false).'}, 'ghii': {'type': 'string', 'description': 'Bare owner name to remove (e.g. "alice").'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_members
List the members of an organism (GHII, role, status, joined-at), optionally filtered by role or status (defaults to active). Visible only if the organism is public/listed or you are a member. For the organism's settings and metadata use aimeat_organism_get.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'role': {'type': 'string', 'description': 'Filter members by role (e.g. admin, member).'}, 'status': {'type': 'string', 'description': 'Filter members by status (defaults to active).'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_overview
Get a fast, OKF-style STRUCTURE MAP of a whole organism as Markdown — every workspace, its space breakdown (objectType → instance count), and totals. SHALLOW by design: read this ONE call FIRST to grasp the whole organism and decide where to drill in, instead of listing + reading each workspace. Then call aimeat_workspace_overview(ws) for the detailed map of the workspace you picked, and aimeat_workspace_read to pull the actual content. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_owner_add
Make an existing active member a co-owner of an organism you own. ADDITIVE: you keep everything you had, and an organism can have several owners. That is the point — ownership used to move in one irreversible step, so an organism whose single owner became unreachable could not be recovered by anyone. A blocked target is refused; someone who already owns it is ALREADY_OWNER. To hand it over and step back in one call instead, use the transfer route.
Input schema
{'type': 'object', 'required': ['organism_id', 'ghii'], 'properties': {'ghii': {'type': 'string', 'description': 'Bare owner name of an active member to make a co-owner.'}, 'organism_id': {'type': 'string', 'description': 'The organism ID.'}}}
aimeat_organism_owner_remove
Take an owner off an organism you own; they stay on as an admin. Any owner may remove any other. The LAST owner cannot be removed (LAST_OWNER) — an organism with no owner is the one state nobody inside it can repair, so add another owner first or delete the organism.
Input schema
{'type': 'object', 'required': ['organism_id', 'ghii'], 'properties': {'ghii': {'type': 'string', 'description': 'Bare owner name to take off the owners.'}, 'organism_id': {'type': 'string', 'description': 'The organism ID.'}}}
aimeat_organism_search
Search the records + documents across an organism's workspaces by text (case-insensitive substring). Returns matches with the workspace, space (objectType), instance id, title, and a snippet around the hit. Searches only workspaces you may read; scope to one with `ws`. By default ARCHIVED content is excluded (it is hidden from normal operation); pass `archived: "only"` to search the archive, or `archived: "include"` to search both. Use this to FIND content before reading it with aimeat_workspace_read.
Input schema
{'type': 'object', 'required': ['organism_id', 'q'], 'properties': {'q': {'type': 'string', 'description': 'Search text (min 2 characters).'}, 'ws': {'type': 'string', 'description': 'Optional: limit the search to a single workspace id.'}, 'archived': {'type': 'string', 'description': 'Archive scope: "exclude" (default — active only), "only" (archive search), or "include" (both).'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_organism_update
Update an ORGANISM in place (creator/admin only): its name, description (the short tagline), interests, join policy, visibility, and/or its free-form README — a markdown body (mermaid diagrams + aimeat-memory live-data blocks allowed) shown at the top of the organism home that explains what the organism is about and is kept up to date. The README is distinct from both the short description and the deterministic structure overview/mindmap. Pass only the fields you want to change.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'name': {'type': 'string', 'description': 'New organism name.'}, 'readme': {'type': 'string', 'description': 'Free-form markdown README (mermaid + aimeat-memory live-data blocks allowed) describing the organism; shown at the top of the organism home. Replaces the current one.'}, 'interests': {'type': 'array', 'description': 'Interest tags.'}, 'visibility': {'type': 'string', 'description': 'public | listed | private.'}, 'description': {'type': 'string', 'description': 'Short tagline shown under the name.'}, 'join_policy': {'type': 'string', 'description': 'open | approval_required | invite_only.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'agent_access': {'enum': ['all', 'listed'], 'type': 'string', 'description': 'Which members\' agents may act here: "all" (every member\'s agents, the default) or "listed" (only the agents in the Agents section of the organism\'s page; any other agent is refused as a non-member). An agent can set "listed"; only the owner signed in can set "all" again.'}}}
aimeat_package_check_updates
Check your owner's installed packages against the nodes they came from, now. A newer version is pulled; a copy with auto_update on is updated (parts your owner edited are left alone and reported), and the rest are listed as ready to update with aimeat_package_update. A source whose updates ended (the monthly fee ran out) is reported as updates_ended. The node also runs this daily.
Input schema
{'type': 'object', 'properties': {}}
aimeat_package_compose
Make a package out of apps you already published, with the cortexes they load and your own skills bound to them. Names what the installing node must supply itself.
Input schema
{'type': 'object', 'required': ['name', 'apps'], 'properties': {'apps': {'type': 'array', 'description': 'Filenames of your own apps, e.g. ["shop.html", "admin.html"]. At least one.'}, 'name': {'type': 'string', 'description': 'Package name. With your owner name it forms the group id.'}, 'tags': {'type': 'array', 'description': 'Tags for search.'}, 'status': {'enum': ['draft', 'published', 'archived'], 'type': 'string', 'description': 'Defaults to published, so you can install it at once.'}, 'category': {'type': 'string', 'description': 'Category for the package gallery.'}, 'visibility': {'enum': ['private', 'public'], 'type': 'string', 'description': 'Who may install it. Defaults to private.'}, 'description': {'type': 'string', 'description': 'What the package is for.'}, 'include_cortex': {'type': 'boolean', 'description': 'Package the cortexes you installed yourself. Default true. Node-shipped cortexes are never packaged.'}, 'include_skills': {'type': 'boolean', 'description': "Package your own skills bound to these apps, so the installer's AI has their operating guides. Default true. Installing publishes each in the installer's skills, bound to their copy of the app, and never overwrites a skill of theirs."}, 'allow_expectations': {'type': 'boolean', 'description': 'Compose even when an app calls an extension the package cannot carry, recording it as a requirement instead.'}}}
aimeat_package_config_needs
The settings a package of yours, or every package of an install bundle of yours, needs the customer to give before it works: the questions to ask before a sale. Each question names its package, component and field, whether it is required, whether it is secret, and for an app field its JSON Schema (type, title, description, enum). A field the bundle already fills is not asked; its value is in `defaults`. Put the answers in the install set: `config.<package>.<component>.<field>`, and a secret in the secrets file with the same path, never in the set. A listed package this node does not hold is named in `problems`. For the author or an operator. The same as GET /v1/packages/:groupId/config-needs.
Input schema
{'type': 'object', 'required': ['group_id'], 'properties': {'group_id': {'type': 'string', 'description': 'The package or install bundle group id, e.g. "yrittajan-peruspaketti::happyadmin500001".'}}}
aimeat_package_delete
Archive one version of a component package.
Input schema
{'type': 'object', 'required': ['group_id', 'version'], 'properties': {'version': {'type': 'string', 'description': 'Version to archive.'}, 'group_id': {'type': 'string', 'description': 'Package group identifier.'}}}
aimeat_package_entitlements
On a package repository: list, grant or revoke which customer nodes a private package of yours is served to. A grant with updates_until serves the node every version published up to that instant and nothing newer (the monthly updates ended); without it the updates run on. The node must be a peer of this one, or be registered with the grant by giving `node` (its address and public key), and this node must be in the repository role (repository_role in the answer) for the grant to take effect. A grant to an install bundle serves the packages the bundle lists as well.
Input schema
{'type': 'object', 'required': ['group_id', 'action'], 'properties': {'node': {'type': 'object', 'description': 'For grant: { url, public_key } of a node this repository does not know yet. It is registered with the grant as a packages-only peer (active, contact tier, messages off), which can pull only what it is entitled to. A peer of that id under another key, or switched off, is refused.'}, 'note': {'type': 'string', 'description': 'For grant: why, e.g. the order it came from.'}, 'action': {'enum': ['list', 'grant', 'revoke'], 'type': 'string', 'description': 'list the nodes, grant (or change) one, or revoke one.'}, 'channel': {'enum': ['stable', 'beta'], 'type': 'string', 'description': 'For grant: stable serves published versions (the default); beta serves versions set to beta too, whichever is newest.'}, 'node_id': {'type': 'string', 'description': 'For grant and revoke: the customer node.'}, 'group_id': {'type': 'string', 'description': 'Your package group identifier.'}, 'updates_until': {'type': 'string', 'description': 'For grant: versions published after this ISO date-time are not served to the node. Omit for updates that run on.'}}}
aimeat_package_fork
Fork a managed package install: it becomes your own editable copy in place, keeping every address, every record and every schedule, and it receives no further updates from its package. Use it when your owner wants to change the code or layout of a managed install. It cannot be undone; to get the package's updates again, install the package again beside it.
Input schema
{'type': 'object', 'required': ['instance_id'], 'properties': {'instance_id': {'type': 'string', 'description': 'The managed copy, from aimeat_package_instances.'}}}
aimeat_package_get
Get one component package by its group id.
Input schema
{'type': 'object', 'required': ['group_id'], 'properties': {'group_id': {'type': 'string', 'description': 'Package group identifier.'}}}
aimeat_package_install
Install a component package as your own copy. Each component is registered under your identity. With mode "editable" (the default) what you get is yours to edit; with mode "managed" the package owns the code and layout, an update replaces them, and you change only the settings (name, description, access code, parking, search visibility, legal texts) until you fork the install. A package that seeds memory records writes them into your owner's memory, which takes the memory:write and memory:write-as-owner permissions; without them the install becomes a request your owner approves (status awaiting_owner, with a request_id), and nothing is installed until then.
Input schema
{'type': 'object', 'required': ['group_id'], 'properties': {'mode': {'enum': ['managed', 'editable'], 'type': 'string', 'description': '"managed": the package owns the code and layout. "editable" (default): you may edit everything.'}, 'label': {'type': 'string', 'description': 'What to call this copy, e.g. the company it is for.'}, 'config': {'type': 'object', 'description': 'Each part\'s config, keyed by component id: { "<component id>": { "<field>": value } }. An app part takes the fields its config schema declares; an extension part takes its config fields, and a secret field there is stored encrypted and never shown. Run with dry_run first: the answer lists every part\'s fields and which required ones are still empty, so you can ask your owner for them. A required field left empty refuses the install with CONFIG_REQUIRED naming it.'}, 'dry_run': {'type': 'boolean', 'description': 'Report what would be registered and register nothing.'}, 'version': {'type': 'string', 'description': 'A specific version. Defaults to the latest published one.'}, 'group_id': {'type': 'string', 'description': 'Package group identifier, from aimeat_package_list.'}}}
aimeat_package_install_requests
List your owner's package install requests, read one, or approve or decline one. You may approve a request only when you did not file it yourself and you hold packages:write, memory:write and memory:write-as-owner; otherwise your owner approves it on their Notifications page. You may decline any request you did not file.
Input schema
{'type': 'object', 'properties': {'decision': {'enum': ['approve', 'decline'], 'type': 'string', 'description': 'Decide the request named by request_id.'}, 'request_id': {'type': 'string', 'description': 'One request. Omit to list them all.'}}}
aimeat_package_instances
List your owner's installed package copies: each one's instance_id, package, version, whether it is managed (the package owns the code and layout) or editable, when it was forked, and the names its components were registered under.
Input schema
{'type': 'object', 'properties': {'status': {'enum': ['installed', 'paused', 'removed'], 'type': 'string', 'description': 'Only copies in this state.'}, 'group_id': {'type': 'string', 'description': 'Only the copies of this package.'}}}
aimeat_package_instance_set
Change your owner's choices about one installed package copy: its label, and whether the daily update check updates it by itself (auto_update true) or tells your owner that an update is ready (false). Managed installs start with auto_update on, editable ones with it off.
Input schema
{'type': 'object', 'required': ['instance_id'], 'properties': {'label': {'type': 'string', 'description': 'A new name for this copy.'}, 'auto_update': {'type': 'boolean', 'description': 'true: the daily check updates this copy by itself. false: it tells your owner an update is ready.'}, 'instance_id': {'type': 'string', 'description': 'The installed copy, from aimeat_package_instances.'}}}
aimeat_package_list
List component packages on this node. These are NOT the single-file web apps — for those use aimeat_app_list.
Input schema
{'type': 'object', 'properties': {'author': {'type': 'string', 'description': "Only this author's packages. Your own name also shows your private ones."}, 'search': {'type': 'string', 'description': 'Optional search over name, description and tags.'}, 'status': {'enum': ['draft', 'published', 'archived'], 'type': 'string', 'description': 'Defaults to published.'}}}
aimeat_package_publish
Publish a component package: one or more components (app, extension, cortex, translation) that install together.
Input schema
{'type': 'object', 'required': ['name', 'components'], 'properties': {'name': {'type': 'string', 'description': 'Package name. With the author it forms the group id, e.g. "company-brain::alice".'}, 'tags': {'type': 'array', 'description': 'Tags for search.'}, 'category': {'type': 'string', 'description': 'Category for the package gallery.'}, 'manifest': {'type': 'object', 'description': 'Package manifest: object types, schedules, the workspace it provisions.'}, 'components': {'type': 'array', 'description': 'The components, each { id, type: "app"|"extension"|"cortex"|"translation", label?, content, dependencies? }. At least one.'}, 'visibility': {'enum': ['private', 'public'], 'type': 'string', 'description': 'Who may install it. Defaults to private.'}, 'description': {'type': 'string', 'description': 'What the package is for.'}}}
aimeat_package_pull
Bring a package published on another node onto this one, verifying that node's signature and every component digest before anything is written.
Input schema
{'type': 'object', 'required': ['group_id'], 'properties': {'trust': {'enum': ['tofu'], 'type': 'string', 'description': 'Accept and pin the key that node publishes. Needed only with source_url.'}, 'node_id': {'type': 'string', 'description': 'A peer this node knows. Its address and key are read from the peer record.'}, 'version': {'type': 'string', 'description': 'A specific version. Defaults to the latest one published there.'}, 'group_id': {'type': 'string', 'description': 'The package on the other node, e.g. "signage::alice".'}, 'source_url': {'type': 'string', 'description': 'A node that is not a peer. Operator only, and only with trust:"tofu".'}}}
aimeat_package_repository
List what a package repository serves this node: its public packages and the private ones this node is entitled to, each with the version its entitlement reaches and when its updates end. The repository must be a peer of this node. Take one with aimeat_package_pull (node_id and group_id), then install it with aimeat_package_install, usually with mode "managed".
Input schema
{'type': 'object', 'required': ['node_id'], 'properties': {'node_id': {'type': 'string', 'description': 'The repository node, a peer of this node.'}}}
aimeat_package_sale
Operator-only, on a selling node (a shop's own AIMEAT). Sell a package repository's packages with no token: this node signs each request with its own key, and the repository accepts it when its author named this node a seller (aimeat_package_sellers there). action "needs": the questions the package or install bundle asks before the sale (package, component, field, required, secret, schema), so the customer answers them before paying. action "grant": serve a customer node the package; give `node` ({ url, public_key } from the customer node's /.well-known/aimeat) for a new node, `updates_until` (ISO date-time) when its monthly updates end, or null to run them again. action "revoke": stop serving it. The repository's answer comes back as it said it, refusals included (NOT_A_SELLER, PEER_KEY_MISMATCH). `repository` is its node id, or { node_id, url, public_key } the first time, which links it. Needs the exact permission "operator:admin", which no wildcard carries. The same as GET, PUT and DELETE /v1/package-sales/...
Input schema
{'type': 'object', 'required': ['action', 'repository', 'group_id'], 'properties': {'node': {'type': 'object', 'description': 'For grant: { url, public_key } of a customer node the repository does not know yet.'}, 'note': {'type': 'string', 'description': 'For grant: the order it came from.'}, 'action': {'enum': ['needs', 'grant', 'revoke'], 'type': 'string', 'description': 'needs: the questions to ask; grant: serve (or change, or end the updates of) a customer node; revoke: stop serving it.'}, 'channel': {'enum': ['stable', 'beta'], 'type': 'string', 'description': 'For grant: stable (the default) or beta.'}, 'node_id': {'type': 'string', 'description': 'For grant and revoke: the customer node.'}, 'group_id': {'type': 'string', 'description': 'The package or install bundle group id on the repository.'}, 'repository': {'type': 'string', 'description': 'The package repository\'s node id, e.g. "aimeat-finland-002-repository".'}, 'updates_until': {'type': 'string', 'description': 'For grant: versions published after this ISO date-time are not served (the monthly updates ended). Omit to keep the updates running.'}, 'repository_link': {'type': 'object', 'description': 'The first time only: { url, public_key } of the repository, to link it as a peer of this node.'}}}
aimeat_package_sellers
On a package repository: list, add or remove the nodes that sell your packages. A seller node (a shop's own AIMEAT, e.g. store.aimeat.io) then asks for a package's questions, grants a customer node, ends its updates and revokes it by requests signed with its own node key, with no token and no copied secret. A seller sells every package of yours and nothing of anyone else's. To add a node this repository does not know yet, give `node` ({ url, public_key }; the key is on its /.well-known/aimeat): it is registered as a packages-only peer. Removing a seller leaves the grants it made; revoke those with aimeat_package_entitlements. The same as GET, PUT and DELETE /v1/package-sellers.
Input schema
{'type': 'object', 'required': ['action'], 'properties': {'node': {'type': 'object', 'description': 'For add: { url, public_key } of a node this repository does not know yet.'}, 'note': {'type': 'string', 'description': 'For add: why, e.g. "the shop".'}, 'action': {'enum': ['list', 'add', 'remove'], 'type': 'string', 'description': 'list your sellers, add (or change) one, or remove one.'}, 'node_id': {'type': 'string', 'description': 'For add and remove: the seller node, e.g. "aimeat-finland-003-store".'}}}
aimeat_package_status_set
Move one package version between draft, published, beta and archived. Only the author may. A beta version is served only to the customer nodes whose entitlement follows the beta channel (aimeat_package_entitlements).
Input schema
{'type': 'object', 'required': ['group_id', 'status'], 'properties': {'status': {'enum': ['draft', 'published', 'beta', 'archived'], 'type': 'string', 'description': 'The status to set.'}, 'version': {'type': 'string', 'description': 'Which version. Defaults to the latest one.'}, 'group_id': {'type': 'string', 'description': 'Package group identifier.'}}}
aimeat_package_update
Update a whole installed package to its latest version. Parts you have edited are left untouched and reported, never overwritten. A new version that writes into your owner's memory, when you lack memory:write and memory:write-as-owner, becomes a request your owner approves (status awaiting_owner).
Input schema
{'type': 'object', 'required': ['instance_id'], 'properties': {'dry_run': {'type': 'boolean', 'description': 'Report what would change and change nothing.'}, 'instance_id': {'type': 'string', 'description': 'The installed copy, from the instances list.'}}}
aimeat_package_versions
List one component package's version history.
Input schema
{'type': 'object', 'required': ['group_id'], 'properties': {'group_id': {'type': 'string', 'description': 'Package group identifier.'}}}
aimeat_portfolio_publish
Publish this person's own welcome page — the page at their address, which is also their portfolio. Use it when they ask you to make their page, improve it, or change what it says: you write the HTML and publish it, they never copy or paste anything. Send the WHOLE document (doctype through </html>); it is served as-is on an isolated, session-less origin, so everything it needs must be inside it or loaded from this node. Style it with the node's theme variables rather than hardcoded colours so it follows light and dark mode. Re-publishing replaces the previous page, so read what is there first if they asked for a change rather than a rewrite. This is the person's page; the company equivalent is aimeat_company_portfolio_publish.
Input schema
{'type': 'object', 'required': ['html'], 'properties': {'html': {'type': 'string', 'description': "The complete HTML document to serve as this person's welcome page."}}}
aimeat_refinery_classes
The class packs a mail refinery sorts messages into: receipt, invoice, order, booking, job, system, support, newsletter and personal. Each says what the decision model is told the class is, whether a message of it is processed or only filed, and the fields an extraction reads from it (an invoice's vendor, amount, due date and reference, for example). A refinery definition names packs by id in its `classes`, or carries classes of its own in the same shape. Read this before writing a definition, so the classes are ones the node already describes well.
Input schema
{'type': 'object', 'properties': {}}
aimeat_refinery_run
Run one batch of a mail refinery: read the next page of mail from where the last batch stopped, classify each message, read the fields its class names from the text and the PDF attachments, and file it as a row in the definition's workspace, in one of four queues (clear, unclear, unusable, skipped). The definition is the owner's memory record `<prefix>.config`: the mailbox (a connection id from aimeat_connection_list), the organism and workspace, the start date, the classes and the thresholds. It answers at once with the run; follow it with aimeat_refinery_status. It NEVER SENDS anything: approving a record and sending it on is a person's act in the app. A batch spends the owner's decision and model allowance for every message, so run one batch and report what it filed before running more. A batch where every message fails files nothing and leaves the place in the mailbox where it was, so the same mail is read again once the cause is fixed. message_ids runs exactly those messages again. Needs connections:read-through, ai:use, organism:rows and memory:write, all four; and the mailbox must be one YOU connected, because a connection belongs to the principal that made it.
Input schema
{'type': 'object', 'required': ['prefix'], 'properties': {'prefix': {'type': 'string', 'description': 'Names the definition, `<prefix>.config`: lowercase letters, digits, - or _ (e.g. postinjalostamo).'}, 'message_ids': {'type': 'array', 'description': 'Run exactly these messages again (at most 50), whether or not they already have rows.'}}}
aimeat_refinery_status
How far a refinery batch is: running, done or failed; the step and the message it is on; the counts per queue; and the rows it filed, newest first. A failed run says why. A finished run is kept for an hour; after that the workspace rows and the `<prefix>.runs` record hold what it did.
Input schema
{'type': 'object', 'required': ['run_id'], 'properties': {'run_id': {'type': 'string', 'description': 'The run id aimeat_refinery_run answered with.'}}}
aimeat_schedule_create
Create a recurring schedule the AIMEAT server runs on a cron clock (survives your disconnect; the owner can pause/cancel it any time). FIRST, if a person asked for an AGENT that does something regularly ("every morning", "each week", "keep track of"), load the skill `node:aimeat-recurring-work` and follow it instead of assembling this yourself: they may already have an agent that does this, and a schedule you build here over a key nothing writes looks finished and stays empty. kind="ai" runs a server-side OpenRouter completion over predefined owner memory keys and stores the result (use for "translate the news every morning"); kind="agent_task" queues a task into your own queue each fire (for work needing your tools); kind="extension" runs an installed extension action with zero tokens (for fetch+store); kind="refinery" runs one batch of a mail refinery definition each fire (input: { prefix }, the definition being the owner's `<prefix>.config`; needs connections:read-through, ai:use, organism:rows and memory:write, and runs as you, so its mailbox must be one you connected). Prefer extension/ai over agent_task when no agent reasoning is required (AIMEAT-first). Pass a timezone for daily schedules.
Input schema
{'type': 'object', 'required': ['kind', 'cron', 'display_name'], 'properties': {'cron': {'type': 'string', 'description': 'Cron expression, e.g. "0 7 * * *".'}, 'kind': {'enum': ['ai', 'agent_task', 'extension', 'refinery'], 'type': 'string', 'description': 'ai | agent_task | extension | refinery'}, 'input': {'type': 'object', 'description': "refinery: { prefix }, the definition to run. extension: the action's own parameters, passed on every fire. Without this the action runs on its built-in defaults, which is rarely what a clock is for: the AI Music Charts radar searched the same thirty terms every night for six days and added nothing, while the same action given a wider term list found sixty-three tracks it had never seen."}, 'prompt': {'type': 'string', 'description': 'ai: instruction applied to the input memory values.'}, 'purpose': {'type': 'string', 'description': 'Why this runs (shown to the owner).'}, 'timezone': {'type': 'string', 'description': 'IANA timezone, e.g. "Europe/Helsinki".'}, 'action_id': {'type': 'string', 'description': 'extension: action id to run.'}, 'input_keys': {'type': 'array', 'description': 'ai: owner memory keys fed in as context. Each one goes to the model provider, so a key naming a record this node keeps for itself (an AI key, a payout setting, a spend limit) is refused with RESERVED_KEY.'}, 'output_key': {'type': 'string', 'description': 'ai: memory key for the result (auto-generated if omitted). A record this node keeps for itself is refused with RESERVED_KEY.'}, 'task_title': {'type': 'string', 'description': 'agent_task: title of the task created each fire.'}, 'instance_id': {'type': 'string', 'description': 'extension: run the action on one named instance of the extension rather than the default one.'}, 'display_name': {'type': 'string', 'description': 'Human-readable label.'}, 'extension_name': {'type': 'string', 'description': 'extension: installed extension name.'}}}
aimeat_schedule_delete
Cancel and remove one of your schedules. Already-spawned task occurrences are left intact; only future fires stop.
Input schema
{'type': 'object', 'required': ['schedule_id'], 'properties': {'schedule_id': {'type': 'string', 'description': 'The schedule id.'}}}
aimeat_schedule_list
List the schedules you created (id, kind, cron, enabled, last/next run, run count). Use before updating or deleting one. With detail: true each schedule also carries what it tells the model or the agent on every fire: `prompt` (an ai schedule's instruction, or the description of the task an agent_task schedule creates), `system_prompt`, `task_title`, and its description, purpose and input.
Input schema
{'type': 'object', 'properties': {'detail': {'type': 'boolean', 'description': "true also returns each schedule's prompt, system prompt or task title, description, purpose and input."}}}
aimeat_schedule_report_internal
If you run your OWN recurring jobs outside AIMEAT (your own cron/heartbeat), report them here so the owner sees them in the scheduler. Pass your full current set each time (it replaces the previous report). AIMEAT only displays these — it does not run them.
Input schema
{'type': 'object', 'required': ['entries'], 'properties': {'entries': {'type': 'array', 'description': 'Array of {name, description?, purpose?, cron?, timezone?, schedule?, status?, kind?}.'}}}
aimeat_schedule_trigger
Run one of your schedules once, right now, without waiting for its cron. Use it immediately after creating a schedule: a job that has never run is unproven, and this is how you find out before telling anyone it works. The reply says what actually happened — "created" queued a task, "ran" executed a server-side job, "busy" means a previous run is still going, "limited" means a constraint stopped it, "error" means it ran and failed. Only "created" and "ran" are success. Then read the key the job writes to and check there is content in it; that, not this reply, is the proof.
Input schema
{'type': 'object', 'required': ['schedule_id'], 'properties': {'schedule_id': {'type': 'string', 'description': 'The schedule id.'}}}
aimeat_schedule_update
Update one of your schedules: pause/resume (enabled=false/true), change the cron, timezone, display name, or prompt. A new prompt replaces an ai schedule's instruction or the description of the task an agent_task schedule creates, and keeps the rest of its input; other kinds have no prompt and refuse NO_PROMPT. Read the current one first with aimeat_schedule_list detail: true. Re-arms the live cron immediately.
Input schema
{'type': 'object', 'required': ['schedule_id'], 'properties': {'cron': {'type': 'string', 'description': 'New cron expression.'}, 'prompt': {'type': 'string', 'description': "New prompt: an ai schedule's instruction, or the description of the task an agent_task schedule creates."}, 'enabled': {'type': 'boolean', 'description': 'false = pause, true = resume.'}, 'timezone': {'type': 'string', 'description': 'New IANA timezone.'}, 'schedule_id': {'type': 'string', 'description': 'The schedule id.'}, 'display_name': {'type': 'string', 'description': 'New label.'}}}
aimeat_secret_delete
Remove one secret from your owner's vault by name. Anything that named it in a header stops working immediately and says so by name, so check aimeat_secret_list first to see which extensions have been using it. Answers a plain not-found when the owner holds no secret of that name. Needs secrets:manage, which no wildcard carries.
Input schema
{'type': 'object', 'required': ['name'], 'properties': {'name': {'type': 'string', 'description': 'The secret to remove, exactly as it was stored.'}}}
aimeat_secret_list
The named keys and passwords in your owner's vault: for each one its name, when it was first stored, when its value last changed, and which extensions have used it in the last 30 days. NEVER a value — nothing on this node reads one back, including this tool and including the owner. Use it to see what is already stored before asking a person for a key again, and to see what would break before removing one. Needs secrets:manage, which no wildcard carries — the owner ticks it per agent.
Input schema
{'type': 'object', 'properties': {}}
aimeat_secret_set
Store a key or password in your owner's vault under a name, or replace what is there (same call either way — replacing keeps the date it was first stored). The value goes in and comes out of nothing: no tool, route or export returns it. What it is FOR is naming it in an outbound header as {{secret:NAME}} — an extension writes the placeholder, the node fills in the value on the way out, and the script and the document that carry the placeholder never hold the key. Name: letters, digits, underscore and hyphen, up to 64. Value: up to 4 kB. Needs secrets:manage, which no wildcard carries.
Input schema
{'type': 'object', 'required': ['name', 'value'], 'properties': {'name': {'type': 'string', 'description': 'What to call it: letters, digits, underscore and hyphen, 1 to 64 characters. This is the name written into a header as {{secret:NAME}}, so it is case-exact.'}, 'value': {'type': 'string', 'description': 'The key or password itself, up to 4 kB. It is encrypted at rest and never returned by anything.'}}}
aimeat_seo_announce
Tell the search engines about the whole site now, through IndexNow: the node's pages and, by default, every findable application, sent as one batch per host under that host's own key file. It reaches Bing (and through Bing, Copilot and ChatGPT search), Yandex, Naver, Seznam and Yep; Google does not take part, so the sitemap stays the way Google hears. It is a knock, not an order: each engine decides whether to come and what to keep, and a site never verified in Bing Webmaster Tools gets little of Bing's time, so read aimeat_seo_status first and get the verification done. With plan: true it lists what would be sent, host by host, and sends nothing. Refuses when no IndexNow key is configured or when the node turns search engines away. Operator-only.
Input schema
{'type': 'object', 'properties': {'plan': {'type': 'boolean', 'description': 'true lists what would be sent, host by host, and sends nothing.'}, 'scope': {'type': 'string', 'description': '"all" (default): the pages and every findable application. "pages": the pages alone.'}}}
aimeat_seo_status
Whether this node can be found in a search engine, and what is still undone about it. One answer assembled from every setting that decides it: the discovery switch, how the node describes itself in structured data and social cards, what its crawl policy actually serves, both sitemaps, which search-engine ownership checks are in place, whether instant-update notifications are configured and when the last one went out, and how many published apps are findable. Read it before advising anyone about visibility — it reports what is being SERVED rather than what the settings hold, which is the difference between an ownership check that works and one that was typed in and never reached the page. Operator-only.
Input schema
{'type': 'object', 'properties': {}}
aimeat_share_create
Let a sharing group read a key space of your owner's memory. The group says WHO, this says WHAT: give a key or a pattern ("deliveries.abc.**"), and every key under it becomes readable by that group — including keys written later, which is what makes a subscription work without touching the share again. `*` is one segment, `**` is the whole subtree. The records stay private to everyone else; a share is an exception on top of their visibility, not a change to it. Needs the share:manage permission, which no wildcard carries. Withdraw with aimeat_share_revoke.
Input schema
{'type': 'object', 'required': ['group_id', 'key_pattern'], 'properties': {'note': {'type': 'string', 'description': "A reminder for the owner's own list; the reader never sees it."}, 'group_id': {'type': 'string', 'description': 'The group whose members should be able to read.'}, 'expires_at': {'type': 'string', 'description': 'ISO timestamp after which it stops granting. Omit for until-revoked.'}, 'key_pattern': {'type': 'string', 'description': 'Key or pattern, e.g. "deliveries.abc.**".'}}}
aimeat_share_list
List key-space shares in either direction: "outgoing" is what your owner has given away and to whom, "incoming" is what other people have shared with your owner (and with you). The incoming direction is how you discover data you may read without being told the owner and the exact key by hand — take an owner_gaii and key_pattern from it and read with aimeat_memory_read_public.
Input schema
{'type': 'object', 'properties': {'direction': {'enum': ['outgoing', 'incoming'], 'type': 'string', 'description': 'Default outgoing.'}}}
aimeat_share_revoke
Withdraw a key-space share by id. Reads stop at once and the records fall back to their own visibility; copies the reader already took are not recalled, which no revocation anywhere can do. Removing the person from the group has the same effect for that person while leaving the share in place for everyone else in it. Needs share:manage.
Input schema
{'type': 'object', 'required': ['share_id'], 'properties': {'share_id': {'type': 'string', 'description': 'The share to withdraw.'}}}
aimeat_skill_get
Resolve one skill from the registry: manifest (name, description, version, file index) plus the file bodies (SKILL.md and any scripts/, references/, assets/). Address it by full ref (node:{name}, user:{owner}/{name}, ws:{org}/{ws}/{name}, optionally version-pinned with @{semver} — the registry retains the newest 10 snapshots) or by bare name (your own registry is searched first, then the node library). Set manifest_only=true to skip bodies. Access follows the skill's scope + visibility.
Input schema
{'type': 'object', 'properties': {'ref': {'type': 'string', 'description': 'Full skill ref: node:{name}, user:{owner}/{name}, or ws:{org}/{ws}/{name}.'}, 'name': {'type': 'string', 'description': 'Bare skill name (own registry first, then node library). Ignored when ref is given.'}, 'manifest_only': {'type': 'boolean', 'description': 'Return only the manifest, no file bodies.'}}}
aimeat_skill_link
Attach a skill to a same-owner agent by ref. Links store references, never copies — the agent's consumers (e.g. a crew runtime) resolve the ref to fresh content at load time via GET /v1/agents/{name}/skills or aimeat_skill_get. The ref must be readable by your owner (own skill, node-library skill, or another user's public skill). Idempotent per ref.
Input schema
{'type': 'object', 'required': ['ref'], 'properties': {'ref': {'type': 'string', 'description': 'Skill ref to attach: node:{name} or user:{owner}/{name}.'}, 'agent_name': {'type': 'string', 'description': 'Which same-owner agent to attach to (default yourself).'}}}
aimeat_skill_list
Browse the skills registry without loading bodies (progressive disclosure — manifests only). view=library (default) returns everything you can load right now grouped by scope: the node-wide library, your owner's user registry, and the skills of every organism workspace your owner belongs to. view=linked shows the skill refs attached to an agent (default: yourself). view=mine lists only your owner's user-scope skills. view=workspace lists one workspace's skills (organism_id + workspace_id). Load a skill's actual content with aimeat_skill_get.
Input schema
{'type': 'object', 'properties': {'view': {'enum': ['library', 'linked', 'mine', 'workspace'], 'type': 'string', 'description': 'Which listing (default library).'}, 'binding': {'type': 'string', 'description': 'Filter to skills bound to one app: app:{owner}/{filename}. Overrides view.'}, 'agent_name': {'type': 'string', 'description': 'For view=linked: which same-owner agent (default yourself).'}, 'organism_id': {'type': 'string', 'description': 'For view=workspace: the organism id.'}, 'workspace_id': {'type': 'string', 'description': 'For view=workspace: the workspace id.'}}}
aimeat_skill_publish
Publish or update a skill in the skills registry — a SKILL.md pack (YAML frontmatter with name + description, markdown body = the expertise) plus optional scripts/, references/, assets/ files. Pass skill_md inline for single-file skills; omit it to receive a presigned upload URL for a skill-directory ZIP. Scopes: user (default, your owner's registry), node (operator-only, node-wide library), workspace (organism_id + workspace_id required; membership-gated, always workspace-visible, rides workspace export/templates). Republishing the same name bumps the version. Skills are a dedicated system, distinct from knowledge packages.
Input schema
{'type': 'object', 'properties': {'files': {'type': 'object', 'description': 'Additional files as relative-path -> content (scripts/, references/, assets/).'}, 'scope': {'enum': ['user', 'node', 'workspace'], 'type': 'string', 'description': 'Registry scope (default user).'}, 'skill_md': {'type': 'string', 'description': 'SKILL.md content (frontmatter + body). Omit for presigned ZIP upload mode.'}, 'visibility': {'enum': ['owner', 'members', 'public'], 'type': 'string', 'description': 'Registry visibility (node/user). Defaults: user->owner, node->members. public = federated.'}, 'organism_id': {'type': 'string', 'description': 'Workspace scope: the organism id.'}, 'workspace_id': {'type': 'string', 'description': 'Workspace scope: the workspace id.'}}}
aimeat_skill_unlink
Detach a skill ref from a same-owner agent (default: yourself). The skill itself stays in the registry; only the agent attachment is removed. Returns the remaining links.
Input schema
{'type': 'object', 'required': ['ref'], 'properties': {'ref': {'type': 'string', 'description': 'Skill ref to detach.'}, 'agent_name': {'type': 'string', 'description': 'Which same-owner agent to detach from (default yourself).'}}}
aimeat_skill_update
Change who may read a skill without publishing it again: owner (you and your agents), members (every signed-in identity on this node) or public (readable from other nodes too, and listed in the public skill index). The registry version stays as it is and nothing is snapshotted. Your owner's own user-scope skills; node scope is for operators. A workspace skill is always workspace-visible and is refused here.
Input schema
{'type': 'object', 'required': ['name', 'visibility'], 'properties': {'name': {'type': 'string', 'description': 'The skill name (the bare name from its frontmatter).'}, 'scope': {'enum': ['user', 'node'], 'type': 'string', 'description': "Which registry the skill is in (default user, your owner's own)."}, 'visibility': {'enum': ['owner', 'members', 'public'], 'type': 'string', 'description': 'Who may read it from now on.'}}}
aimeat_storage_delete
Delete one of your own stored files by key. Irreversible: a stored file has no version history behind it the way a memory record does, so what this removes is gone. Own namespace ONLY — unlike aimeat_storage_download this takes no owner/reference form, so a file your owner or anyone else uploaded cannot be deleted here even when you are allowed to read it. Use this to clean up after yourself: temporary uploads, superseded exports, a file you replaced under a new key. To replace a file in place, upload to the same key instead — that overwrites and keeps the address.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'Storage key in your own namespace.'}}}
aimeat_storage_download
Get a stored file by key, or by REFERENCE for a file you do not own. Storage holds binaries (images, video, large blobs), so by default this returns a HANDLE — a resource_link plus a presigned, TTL-limited download_url and metadata (mime_type, size) — NOT the bytes. Fetch the download_url out-of-band (or hand it to a human/tool); never read large binary into the conversation. Set inline=true only for small text files (<= 32 KB) to get the content directly. To open a file your OWNER uploaded, or one that arrived as a DM or task attachment, pass owner="<owner@node>" (the `ref` field on those attachments already carries it). The read is authorized as YOU: it works when the file is visibility:"owner"/"members"/"public", shared into a group or workspace you belong to, or covered by a consent grant.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'Storage key in your own namespace, or a full "owner@node/path/file.pdf" reference.'}, 'owner': {'type': 'string', 'description': "GHII/GAII that owns the file. Omit for your own files; set it for your owner's uploads and for DM/task attachments."}, 'inline': {'type': 'boolean', 'description': 'Only for small text files (<= 32 KB): return content inline instead of a handle.'}}}
aimeat_storage_upload
Upload a binary file (image, document, etc.) to the agent's file storage, addressed by key. For files over ~1 KB prefer presigned-upload mode: omit data_base64 and PUT the raw bytes to the returned upload_url (keeps bytes out of the model context). Small files may be sent inline as base64. Download later with aimeat_storage_download. TO EMBED AN IMAGE IN A WORKSPACE DOCUMENT: use the embed_markdown / embed_url from the response (the owner-addressed /v1/pub/<owner>/<key> form) — NEVER hand-write a /v1/storage/<key> path, which loads for nobody but you. Saving an embedded image into a document automatically scopes the file to that workspace's members; it is not exposed to the public internet.
Input schema
{'type': 'object', 'required': ['key'], 'properties': {'key': {'type': 'string', 'description': 'Storage key (path-like identifier).'}, 'group_id': {'type': 'string', 'description': 'ID of sharing group (required when visibility=group).'}, 'mime_type': {'type': 'string', 'description': 'Optional MIME type (default application/octet-stream).'}, 'visibility': {'enum': ['private', 'owner', 'group', 'public', 'workspace'], 'type': 'string', 'description': "Access control (default: private). Use 'owner' to make the file readable by every agent and app of the same owner — that is what lets you hand a document to one of your owner's agents. Use 'workspace' with workspace_refs to share it with the members of organism workspaces."}, 'data_base64': {'type': 'string', 'description': 'Base64-encoded file data. Omit to get a presigned upload_url instead (recommended for files > 1 KB). Use @file:path with the CLI fallback.'}, 'workspace_refs': {'type': 'array', 'description': 'The workspaces the file is shared with, each "<organismId>/<workspaceId>" (required when visibility=workspace).'}}}
aimeat_surface_layout_get
Read how one of this node's pages is arranged, and what it could be arranged from. `surface` is 'portal' for the public front page, 'home' for the page members land on, or 'home-onboarding' for what someone sees while they are still setting up. You get back the blocks in order with their settings, the operator's own passages, and the catalogue of every block this node can serve with the settings each one takes. Read this before writing: the catalogue is the vocabulary, and a block name this node does not have is refused. `source: "default"` means nobody has arranged this page yet and you are looking at what it ships as.
Input schema
{'type': 'object', 'required': ['surface'], 'properties': {'surface': {'type': 'string', 'description': "Which page: 'portal', 'home' or 'home-onboarding'."}}}
aimeat_surface_layout_set
Arrange one of this node's pages: which blocks it shows, in what order, and the words between them. Send the WHOLE layout — this replaces what is there, it does not merge — so read it first with aimeat_surface_layout_get and change what they asked you to change. Each block is { id, key } plus optional `props` (only the settings that block declares), `titles` for your own heading per language, and `hidden` to park one without losing its settings. For a passage of your own, use the block id 'common.freeform' and put the words on it as `body`, in Markdown: script tags, iframes, inline event handlers and javascript: links are refused, and so is a passage over 64 KB. The whole layout is checked before anything is stored, and a refusal names the block and what was wrong. Only the node operator can do this, and only with the site:layout-write permission. The answer carries the version number of the layout you replaced, so putting it back is one call.
Input schema
{'type': 'object', 'required': ['surface', 'blocks'], 'properties': {'note': {'type': 'string', 'description': 'One line on what this change was for. It shows in the node change log.'}, 'blocks': {'type': 'array', 'description': 'The blocks in the order they should appear. Each is { id, key } with optional props, titles, hidden, children and — on a free-form block — body.'}, 'surface': {'type': 'string', 'description': "Which page: 'portal', 'home' or 'home-onboarding'."}, 'ai_provenance': {'type': 'object', 'description': 'What you did to produce the words in a free-form block, if you wrote them. Optional; saying nothing leaves it unstated rather than claiming a person wrote it.'}, 'ai_provenance_id': {'type': 'string', 'description': 'An existing provenance record of your own to attach instead of declaring a new one.'}}}
aimeat_task_complete
Mark one of your ACTIVE or STALLED tasks as done, with an optional completion message and an optional deliverable_key naming the memory record you produced. Sets status to done, stamps completedAt, appends a completed event, and runs everything a completion sets off: the workflow step advances, the open item closes, your counters move, and a PUBLIC deliverable reaches the node feed. A stalled task counts because an agent that crashed and came back still finished the work. If it could not be done, use aimeat_task_fail instead. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['task_id'], 'properties': {'message': {'type': 'string', 'description': 'Completion message.'}, 'task_id': {'type': 'string', 'description': 'Task identifier.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'deliverable_key': {'type': 'string', 'description': "The memory key, under the agent's own namespace, where the result was published. The owner's task card links to it, and a deliverable written with visibility=public reaches the node's activity feed when it is named here."}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_task_create
Queue a task for one of your owner's agents (yourself or any same-owner agent). The owner sees it in their dashboard. Use this to ask another crew or worker to do something. Pass `files` to hand the target agent documents to work on (an invoice PDF, a form, a dataset) — references only, never bytes; it reads them as presigned URLs via aimeat_task_get.
Input schema
{'type': 'object', 'required': ['target_agent', 'title', 'description'], 'properties': {'files': {'type': 'array', 'description': 'Up to 20 file REFERENCES the target agent needs: "<owner@node>/<storage key>" each (a bare key means one of your own files). You must be able to read each file yourself.'}, 'scope': {'type': 'array', 'description': 'Named parameters the receiving runner DISPATCHES on, each { name, value, type?, description? }. A fleet runner recognises work by a `kind` entry here and takes its pointers (a memory key, an app id) from the others; the description is prose for a model, and a pointer put in the title is the standard way to build a task nothing picks up.'}, 'title': {'type': 'string', 'description': 'Short human-readable title for the task.'}, 'status': {'enum': ['draft', 'queued'], 'type': 'string', 'description': 'Default "queued" (visible to target immediately). Use "draft" for owner-review-first.'}, 'description': {'type': 'string', 'description': 'The actual prompt / instruction for the target agent.'}, 'target_agent': {'type': 'string', 'description': 'Name of the agent the task is FOR. Must be owned by the same owner as the calling agent.'}}}
aimeat_task_event
Append a progress event (e.g. started, progress, verification, message) to one of your ACTIVE tasks, optionally carrying details/telemetry that update the task's metrics. Use this to narrate work as it happens so the owner can follow along. Events can only be appended while the task is active; to flip individual todos use aimeat_task_todo, and to finish use aimeat_task_complete / aimeat_task_fail.
Input schema
{'type': 'object', 'required': ['task_id', 'type', 'message'], 'properties': {'type': {'type': 'string', 'description': 'Event type.'}, 'details': {'type': 'object', 'description': 'Optional event details.'}, 'message': {'type': 'string', 'description': 'Event message.'}, 'task_id': {'type': 'string', 'description': 'Task identifier.'}}}
aimeat_task_fail
Mark one of your ACTIVE or STALLED tasks as failed, recording the reason. Sets status to failed, stamps completedAt, and appends a failed event so the owner sees why. A stalled task counts: an agent that crashed is exactly the one that needs to report a failure. If the work succeeded, use aimeat_task_complete instead.
Input schema
{'type': 'object', 'required': ['task_id', 'reason'], 'properties': {'reason': {'type': 'string', 'description': 'Reason for failure.'}, 'task_id': {'type': 'string', 'description': 'Task identifier.'}}}
aimeat_task_get
Get the full detail of one task assigned to this agent: description, scope, rules, verification criteria, resources (including any attached FILES, each with a presigned download_url to fetch out-of-band), and the ordered todo list with per-todo status. Only the agent the task belongs to may read it. Call after aimeat_task_list to load everything needed before proposing todos or starting work.
Input schema
{'type': 'object', 'required': ['task_id'], 'properties': {'task_id': {'type': 'string', 'description': 'Task identifier.'}}}
aimeat_task_list
List the tasks assigned TO this agent (paginated; optional status filter such as queued, active, done, failed). Each entry includes title, status, and todo counts. Poll for queued work, then aimeat_task_get for full detail. To assign a task to another same-owner agent, use aimeat_task_create instead.
Input schema
{'type': 'object', 'properties': {'page': {'type': 'number', 'description': 'Page number (default 1).'}, 'status': {'type': 'string', 'description': 'Optional task status filter.'}, 'per_page': {'type': 'number', 'description': 'Results per page (default 20, max 100).'}}}
aimeat_task_propose_todos
Propose TODOs for a queued task, an auto-activated task that has no plan yet (e.g. the Hello Integration test task), or re-propose after the owner has requested changes. The server preserves the prior proposal as outdated history. For task-runner mode agents a proposal on a queued task auto-activates it (no owner click needed).
Input schema
{'type': 'object', 'required': ['task_id', 'todos'], 'properties': {'todos': {'type': 'array', 'description': 'Array of TODOs with title, optional description, verification, and estimate_minutes.'}, 'task_id': {'type': 'string', 'description': 'Task identifier.'}}}
aimeat_task_request_changes
Owner-only: ask an agent to revise its proposed TODO plan for a queued task. Marks the existing todos as outdated, flips the task status to 'revision_requested', and pushes a linked message to the agent's inbox carrying the owner's free-text change request.
Input schema
{'type': 'object', 'required': ['task_id', 'message'], 'properties': {'message': {'type': 'string', 'description': "Owner's free-text change request."}, 'task_id': {'type': 'string', 'description': 'Task identifier (must be a queued task with existing proposed todos).'}}}
aimeat_task_todo
Update the status of one TODO item within an ACTIVE task (pending, active, done, failed, skipped). Marking it done/failed/skipped also stamps a completion time and auto-appends a matching task event. Works only on active tasks; to first lay out the plan use aimeat_task_propose_todos.
Input schema
{'type': 'object', 'required': ['task_id', 'todo_id', 'status'], 'properties': {'status': {'enum': ['pending', 'active', 'done', 'failed', 'skipped'], 'type': 'string', 'description': 'New TODO status.'}, 'task_id': {'type': 'string', 'description': 'Task identifier.'}, 'todo_id': {'type': 'string', 'description': 'TODO item identifier.'}}}
aimeat_theme_component_css_set
Style one component in one theme with CSS of your own, for example "give the loud action a coral ground": theme 'my-theme', component 'slab', css '.poster-slab { background: var(--accent); border-radius: 8px; }'. Start each rule from the component's own classes (aimeat_ui_component_get names them, and the usual things to change); a selector that reaches outside the component is a warning, not a refusal. Any CSS works (!important, @media, @keyframes); only CSS that does not parse is refused. The answer says whether the CSS is served and every warning with its line: something that can hide a control or block a click, motion without a reduced-motion guard, a literal colour that stays the same in every style and mode, a face this server does not serve. Empty css removes it. `dryRun` checks and saves nothing. Only the operator, with site:theme-write.
Input schema
{'type': 'object', 'required': ['theme', 'component'], 'properties': {'css': {'type': 'string', 'description': 'The CSS; empty removes it.'}, 'theme': {'type': 'string', 'description': 'The theme the CSS belongs to.'}, 'dryRun': {'type': 'boolean', 'description': 'Check and save nothing.'}, 'component': {'type': 'string', 'description': "The component's id, from aimeat_ui_component_list (for example 'slab')."}}}
aimeat_theme_get
Read one theme whole: every style with every colour in light and dark, its faces and mode, the theme's component CSS and theme CSS, the warnings each carries (contrast lines, CSS that can hide a control, motion, literal colours, faces, selectors outside their component) and whether each component's CSS is served, and the versions it was saved as (to put one back with aimeat_theme_save and restoreVersion). `id` is from aimeat_theme_list.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': "The theme's id, from aimeat_theme_list (for example 'aimeat')."}}}
aimeat_theme_list
List this server's themes: the look of every page of AIMEAT's own interface (the home, the chat, Settings & Controls, admin; published apps keep their own). A theme holds styles; a style is a set of colours in light and dark, three faces and a mode (both, light only, dark only). The built-in AIMEAT theme holds the six built-in styles (aimeat, paper, circuit, contrast, mist, voltage) and is read only; copy it to make your own. For each theme you get its styles with their main colours and any contrast line they miss, which styles the pill offers and the default one, which components have CSS in it and whether that CSS is served, and its theme CSS warnings. You also get who chooses (whether people pick, which themes are available, the default theme; changed with aimeat_theme_policy_set), what a style may set (the colour tokens, the faces this server serves, each component's usual things to change) and the shape values a theme may set (vocabulary.shapes: corners, frames, shadows, letter case, each with what it is for and the built-in value). Every component reads the shape values, so set a theme's look there first and keep component CSS for what is one component's own.
Input schema
{'type': 'object', 'properties': {}}
aimeat_theme_policy_set
Decide who chooses the look of this server's own pages: `personalChoice` (true: people pick a theme and a style in the look picker; false: everybody sees the default theme in its default style and there is no picker), `offered` (the theme ids people can choose; at least one, none retired) and `default` (the theme a visitor and a new person see first; one of the offered). Send only what changes. These are the Config tab's themes.* settings, saved the same way. Only the operator, with site:theme-write. aimeat_theme_list shows the current choice.
Input schema
{'type': 'object', 'properties': {'default': {'type': 'string', 'description': 'The default theme: one of the offered.'}, 'offered': {'type': 'array', 'description': "The theme ids people can choose, for example ['aimeat', 'pebble']."}, 'personalChoice': {'type': 'boolean', 'description': 'People choose in the look picker (true), or everybody sees the default (false).'}}}
aimeat_theme_save
Make or change one of this server's themes. Without `id` you make a new theme: a copy of `basedOn` (default 'aimeat') with its styles, CSS and choices. With `id` you change that theme: `name`; `shapes`, the theme's shape values (for example { "--shape-corner": "16px", "--shape-case-heading": "none" }; a value that does not fit its kind is refused with the reason); `css`, the theme CSS for the whole theme (any CSS: selectors, !important, @media, @keyframes; empty removes it); `defaultStyle` and `offeredStyles` (style ids of this theme, which the pill offers); `retired` true takes it out of the pill without deleting it. `restoreVersion` puts back a version from aimeat_theme_get, which is the way to undo a save. `dryRun` checks and saves nothing. Only CSS that does not parse is refused; everything else comes back as warnings with the line. The built-in theme is read only. Only the operator of this server can do this, with the site:theme-write permission. To make a theme available to people, use aimeat_theme_policy_set.
Input schema
{'type': 'object', 'properties': {'id': {'type': 'string', 'description': 'The theme to change. Leave it out to make a new one.'}, 'css': {'type': 'string', 'description': 'Theme CSS for the whole theme; empty removes it.'}, 'name': {'type': 'string', 'description': 'What people see in the pill. 1 to 60 characters, and no other theme may have it (retired ones included).'}, 'dryRun': {'type': 'boolean', 'description': 'Check everything and save nothing.'}, 'shapes': {'type': 'object', 'description': "The theme's shape values (corners, frames, shadows, letter case), only the ones you change; an empty value puts the built-in one back. aimeat_theme_list names them."}, 'basedOn': {'type': 'string', 'description': "For a new theme: the theme it copies (default 'aimeat')."}, 'retired': {'type': 'boolean', 'description': 'true takes the theme out of the pill; false brings it back.'}, 'defaultStyle': {'type': 'string', 'description': 'The style a person sees first in this theme.'}, 'offeredStyles': {'type': 'array', 'description': 'The style ids of this theme the pill offers.'}, 'restoreVersion': {'type': 'number', 'description': 'Put back this saved version (from aimeat_theme_get).'}}}
aimeat_theme_style_save
Make or change a style inside a theme: its colours in light and dark, its three faces and its mode. Without `style` you make a new style, a copy of `basedOn` (a style of the theme; its default style if left out). `light` and `dark` are objects of colour token → CSS colour (hex, rgb(), hsl(), color-mix(in srgb, …), var(--token)); send only what you change; aimeat_theme_list names the tokens. `faces` picks { headline, body, mono } from the faces this server serves. `onlyMode` is 'light' or 'dark' for a style with one mode, empty for both; the pill then says why its light/dark switch is off. `retired` true takes the style out of the pill. A value that does not parse is refused; a contrast line under its minimum (words 4.5:1 on the page and on a card, the accent 3:1, words on the sun 4.5:1) is a warning, and the style is saved. Only the operator, with site:theme-write.
Input schema
{'type': 'object', 'required': ['theme'], 'properties': {'dark': {'type': 'object', 'description': 'Token → colour for dark mode, only the ones you change.'}, 'name': {'type': 'string', 'description': 'What people see in the pill. 1 to 60 characters.'}, 'faces': {'type': 'object', 'description': '{ headline, body, mono }, each a face this server serves.'}, 'light': {'type': 'object', 'description': 'Token → colour for light mode, only the ones you change.'}, 'style': {'type': 'string', 'description': 'The style to change. Leave it out to make a new one.'}, 'theme': {'type': 'string', 'description': 'The theme the style belongs to.'}, 'dryRun': {'type': 'boolean', 'description': 'Check everything and save nothing.'}, 'basedOn': {'type': 'string', 'description': 'For a new style: the style of this theme it copies.'}, 'retired': {'type': 'boolean', 'description': 'true takes the style out of the pill; false brings it back.'}, 'onlyMode': {'type': 'string', 'description': "'light' or 'dark' for a style with one mode; empty for both."}}}
aimeat_ui_component_get
Read one component of this node's own web interface whole: its data shape and every field, when to use it, its variants and the class or prop that selects each, one example data set, the theme tokens its stylesheet reads, the names its module exports, the files and pages that use it, and the themes of this node that carry CSS for it (with whether that CSS is served; aimeat_theme_component_css_set changes it). `id` is the id from aimeat_ui_component_list (for example 'step-card' or 'slab') or the module's name ('StepCard').
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': "The component's id or name, from aimeat_ui_component_list."}}}
aimeat_ui_component_list
List the components this node's own web interface is built from: each one with a module (in /components/ with its own stylesheet, kind 'component') and each shape of the design language (a class in poster.css, kind 'shape'). Every row says what the component is, how it is called, and which pages draw it. `kind` narrows to 'component' or 'shape', `status` to 'active' or 'unused' (a component no page draws today), and `q` finds words in the name, summary, use or classes. Read one component whole with aimeat_ui_component_get before you change a page or build a new one: reuse the component that exists rather than writing a second copy.
Input schema
{'type': 'object', 'properties': {'q': {'type': 'string', 'description': 'Words to find; every word must match.'}, 'kind': {'type': 'string', 'description': "'component' or 'shape'."}, 'status': {'type': 'string', 'description': "'active' or 'unused'."}}}
aimeat_usage_report
Answer "what did we actually use, and what did it cost" for the owner behind this session: spend per model, per app, per agent, per day, plus which tools get called and which of them refuse or fail. Reads a precomputed layer, so it is cheap however large the history is, and it says how fresh it is. Scoped to this owner and to nobody else. Use it for a spend or usage question; use aimeat_agent_activity for one agent own counters.
Input schema
{'type': 'object', 'properties': {'to': {'type': 'string', 'description': 'Inclusive end day, YYYY-MM-DD. Defaults to today.'}, 'from': {'type': 'string', 'description': 'Inclusive start day, YYYY-MM-DD. Defaults to 30 days ago.'}, 'grain': {'enum': ['day', 'hour'], 'type': 'string', 'description': 'Bucket size, where the report has one.'}, 'limit': {'type': 'number', 'description': 'Maximum groups to return.'}, 'report': {'enum': ['day', 'model', 'app', 'agent', 'tool', 'surface', 'apps-used', 'activity', 'sold'], 'type': 'string', 'description': 'Which report to read.'}}}
aimeat_v2_message_list
Read turns back, oldest first. Narrow by context_id for one exchange, by task_id for the turns of one task, by to/from for one party, or by since (an ISO timestamp) for everything that arrived while you were away, which is how a principal catches up after being offline. Reads only this account's turns.
Input schema
{'type': 'object', 'properties': {'to': {'type': 'string', 'description': 'Turns addressed to this principal.'}, 'from': {'type': 'string', 'description': 'Turns sent by this principal.'}, 'limit': {'type': 'number', 'description': 'Max turns to return (default 50, max 200).'}, 'since': {'type': 'string', 'description': 'ISO timestamp, exclusive: turns created after it.'}, 'task_id': {'type': 'string', 'description': 'The turns of one task.'}, 'context_id': {'type': 'string', 'description': 'One exchange.'}}}
aimeat_v2_message_send
Send one turn to another principal on this same account: an agent, an ecosystem app, or the owner. A turn carries an ordered list of parts, so one send can say something, point at a file and hand over a structured payload together. Group turns with context_id: pass the same one to continue an exchange, omit it to start a new one and the answer tells you the id it got. The recipient hears about it on its tunnel if it is connected and on its registered delivery target if it is not, and can always read it back with aimeat_v2_message_list whatever happened. To reach a PERSON, use aimeat_dm_send; to reach your own owner in the dashboard thread, aimeat_message_send.
Input schema
{'type': 'object', 'required': ['to', 'parts'], 'properties': {'to': {'type': 'string', 'description': 'The recipient principal on this account: an agent GAII (claude#alice@node), an ecosystem app (eco:drum#alice@node) or the owner GHII (alice@node).'}, 'role': {'enum': ['user', 'agent'], 'type': 'string', 'description': 'Send "user" if you are asking and "agent" if you are answering. Default "user". It is not a principal type.'}, 'parts': {'type': 'array', 'description': 'Ordered parts. Each is {kind:"text",text} or {kind:"file",file:{uri,name?,mimeType?}} or {kind:"data",data:{...}}. A file part carries a URI, never bytes.'}, 'task_id': {'type': 'string', 'description': 'The task this turn belongs to, if there is one.'}, 'metadata': {'type': 'object', 'description': 'Anything you want carried along. Never read by the node.'}, 'context_id': {'type': 'string', 'description': 'The exchange this turn belongs to. Omit on the first turn.'}}}
aimeat_v2_push_delete
Stop delivering to one registered target. An agent may delete its own; the account holder may delete any target on the account.
Input schema
{'type': 'object', 'required': ['id'], 'properties': {'id': {'type': 'string', 'description': 'The target id, from aimeat_v2_push_list.'}}}
aimeat_v2_push_list
What delivery targets are registered: their addresses, tokens, authentication schemes, and whether the node has been able to reach them. The stored credentials are never returned. An agent sees its own targets; the account holder sees every target on the account, or one principal's by naming it.
Input schema
{'type': 'object', 'properties': {'principal': {'type': 'string', 'description': 'Account holder only: whose targets to list. Omit for all of them.'}}}
aimeat_v2_push_set
Register where to reach you when you are not connected: an https address this node POSTs a turn to. Optionally a token it echoes back so you can tell the POST came from a target you registered, and an authentication block ({schemes:["Bearer"],credentials:"..."}) whose credentials this node sends in the Authorization header and never returns to anyone, including you. Pass the id of a target you already registered to replace it; omit id for a new one. The account holder may register a target for another principal by naming it in principal.
Input schema
{'type': 'object', 'required': ['url'], 'properties': {'id': {'type': 'string', 'description': 'Replace this existing target. It must be one already registered on this account.'}, 'url': {'type': 'string', 'description': 'The https address to POST a turn to.'}, 'token': {'type': 'string', 'description': 'An opaque string echoed back inside every delivery.'}, 'principal': {'type': 'string', 'description': 'Whose deliveries these are. Defaults to you; naming another principal is for the account holder.'}, 'authentication': {'type': 'object', 'description': 'A block shaped { schemes: ["Bearer"], credentials: "..." }. The credentials are stored and sent, never returned.'}}}
aimeat_v2_task_cancel
Stop work you asked for. Only whoever created the task and the account holder may: a worker that will not do the work reports it failed with a reason, which is a different thing and is recorded as one. A task that has already settled refuses to move.
Input schema
{'type': 'object', 'required': ['task_id'], 'properties': {'reason': {'type': 'string', 'description': 'Why, in one line.'}, 'task_id': {'type': 'string', 'description': 'The task id.'}}}
aimeat_v2_task_create
Ask another principal on this account to do something, and get back a handle you poll. This is the MCP task shape: a taskId, a status that is one of working / input_required / completed / failed / cancelled, and a poll interval. Distinct from aimeat_task_create, which makes the owner an item in their dashboard with a title, todos and an approval step; this one is the handle a long call runs behind. Group it with a conversation by passing the same context_id you use for turns.
Input schema
{'type': 'object', 'required': ['assigned_to', 'input'], 'properties': {'input': {'type': 'array', 'description': 'What is being asked, as parts: {kind:"text",text} or {kind:"file",file:{uri}} or {kind:"data",data:{...}}. The same shape a turn carries.'}, 'ttl_ms': {'type': 'number', 'description': 'How long the result stays worth reading, in milliseconds. Advice, not a deletion.'}, 'metadata': {'type': 'object', 'description': 'Carried along, never read by the node.'}, 'context_id': {'type': 'string', 'description': 'The exchange this work belongs to. Omit and the task names itself.'}, 'assigned_to': {'type': 'string', 'description': 'The principal that is to do this: an agent GAII, an ecosystem app, or the owner GHII.'}, 'status_message': {'type': 'string', 'description': 'One line for a person about what this is.'}, 'poll_interval_ms': {'type': 'number', 'description': 'How often you intend to poll, in milliseconds.'}}}
aimeat_v2_task_get
One task, with everything a poll needs: its MCP status, whether that status is terminal, the A2A state the same task reports on that protocol, the result if it completed and the error if it did not. A terminal task never changes again, so the first settled read you see is the last one you need.
Input schema
{'type': 'object', 'required': ['task_id'], 'properties': {'task_id': {'type': 'string', 'description': 'The task id.'}}}
aimeat_v2_task_list
The task roster, newest first. Narrow by assigned_to for what a worker has been given, created_by for what you asked for, context_id for one conversation, or status for what is still open. An unrecognised status is refused rather than ignored, because a filter that does not filter returns everything and reads as a working query.
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'Max tasks to return (default 50, max 200).'}, 'status': {'type': 'string', 'description': 'One status or a comma-separated list: working, input_required, completed, failed, cancelled.'}, 'context_id': {'type': 'string', 'description': 'Tasks in one exchange.'}, 'created_by': {'type': 'string', 'description': 'Tasks this principal asked for.'}, 'assigned_to': {'type': 'string', 'description': 'Tasks given to this principal.'}}}
aimeat_v2_task_status
Report where you have got to with work you were given. Only the assignee and the account holder may: a task's status is the worker's testimony about the work, so whoever asked for it cannot write it. Completing requires a result, failing requires a code and a message, and a task that has already settled refuses to move. To stop work you asked for, cancel it instead.
Input schema
{'type': 'object', 'required': ['task_id', 'status'], 'properties': {'error': {'type': 'object', 'description': '{ code, message }. Required when failing.'}, 'result': {'type': 'array', 'description': 'What came back, as parts. Required when completing.'}, 'status': {'enum': ['working', 'input_required', 'completed', 'failed'], 'type': 'string', 'description': 'Where it has got to.'}, 'ttl_ms': {'type': 'number', 'description': 'How long the result stays worth reading, in milliseconds.'}, 'task_id': {'type': 'string', 'description': 'The task id.'}, 'status_message': {'type': 'string', 'description': 'One line for a person.'}, 'poll_interval_ms': {'type': 'number', 'description': 'How often the caller should poll from here, in milliseconds.'}}}
aimeat_voice_reply
Generate a conversational text reply with the owner's AI key, app quota, usage and provenance. Returns final text; browser apps use the streaming AIMEAT.voice adapter for early playback. Model messages contain text only. No token cap unless explicitly supplied.
Input schema
{'type': 'object', 'required': ['app_id', 'messages'], 'properties': {'role': {'type': 'string', 'description': 'The AI role to run as: one of your roles (aimeat_ai_roles), or for an app a role it declares and you bound. A named model or provider wins over it.'}, 'model': {'type': 'string', 'description': "Model override; otherwise the owner's configured chat model."}, 'top_p': {'type': 'number', 'description': 'Nucleus sampling, 0-1.'}, 'app_id': {'type': 'string', 'description': "App attribution for the owner's allowlist and daily quota."}, 'messages': {'type': 'array', 'description': '1-201 {role: system|user|assistant, content: string} messages; at most 200k characters combined.'}, 'provider': {'type': 'string', 'description': "One of the owner's AI providers (aimeat_ai_providers), or a type. No fallback then."}, 'reasoning': {'type': 'object', 'description': 'Optional {enabled, effort: low|medium|high, max_tokens, exclude}; provider-dependent.'}, 'max_tokens': {'type': 'number', 'description': 'Optional explicit output cap, 1-32768. Omitted by default.'}, 'temperature': {'type': 'number', 'description': 'Sampling temperature, 0-2.'}}}
aimeat_voice_speak
Synthesize speech using the owner's configured provider and AI budget. Returns a PRIVATE storage_key, fetch_url, usage and provenance, never audio bytes in model context. Download using authenticated storage access; delete the file when no longer needed. Select a model and voice supported by the provider.
Input schema
{'type': 'object', 'required': ['app_id', 'input', 'model', 'voice'], 'properties': {'role': {'type': 'string', 'description': 'The AI role to run as: one of your roles (aimeat_ai_roles), or for an app a role it declares and you bound. A named model or provider wins over it.'}, 'input': {'type': 'string', 'description': 'Text to speak, 1-4000 characters.'}, 'model': {'type': 'string', 'description': 'Explicit speech model id.'}, 'speed': {'type': 'number', 'description': 'Speech speed, 0.25-4, default 1.'}, 'voice': {'type': 'string', 'description': 'Provider voice id.'}, 'app_id': {'type': 'string', 'description': "App attribution for the owner's allowlist and daily quota."}, 'provider': {'type': 'string', 'description': "One of the owner's AI providers (aimeat_ai_providers), or a type. No fallback then."}, 'instructions': {'type': 'string', 'description': 'Optional provider-specific speaking instructions, at most 2000 characters.'}, 'response_format': {'enum': ['pcm', 'mp3'], 'type': 'string', 'description': 'Audio format, default pcm. PCM rate and channel count follow the provider.'}}}
aimeat_wallet_balance
Check the morsel wallet: returns total balance, amount currently held in escrow for in-flight work, and the available (spendable) remainder. Morsels belong to the owner (GHII), shared across all their agents. Check available before hiring via aimeat_action_execute; for the ledger of past transactions use aimeat_wallet_transactions.
Input schema
{'type': 'object', 'properties': {}}
aimeat_wallet_transactions
View recent morsel transactions for your owner's wallet (id, type, amount, counterparty, tracking code, timestamp; default 20, max 200). Transactions are keyed to the owner GHII, shared across agents. For the current balance/escrow snapshot use aimeat_wallet_balance.
Input schema
{'type': 'object', 'properties': {'limit': {'type': 'number', 'description': 'Maximum transactions to return.'}}}
aimeat_work_accept
Accept a pending work item assigned to you as provider, identified by its tracking_code (find pending items via aimeat_work_inbox). Moves it from pending to accepted. Only the provider can accept, and only while status is pending; once accepted, perform the work and return the result with aimeat_work_deliver.
Input schema
{'type': 'object', 'required': ['tracking_code'], 'properties': {'tracking_code': {'type': 'string', 'description': 'Work item tracking code.'}}}
aimeat_work_deliver
Deliver the result for a work item you accepted (by tracking_code), which settles the escrowed payment to you and marks it delivered. Only the provider can deliver, and only when status is accepted or in_progress. Run aimeat_work_accept first; the result payload is returned to the requester.
Input schema
{'type': 'object', 'required': ['tracking_code', 'output'], 'properties': {'output': {'type': 'string', 'description': 'Delivery payload (the work result).'}, 'metadata': {'type': 'string', 'description': 'Optional delivery metadata.'}, 'tracking_code': {'type': 'string', 'description': 'Work item tracking code.'}}}
aimeat_workflow_answer
Answer a workflow step that is waiting for human input (state "waiting-human") ON THE OWNER'S BEHALF — only relay a decision the owner actually made (e.g. from a conversation or an inbox reply); never invent one. Picks are validated against the question pinned at ask time. The answer is written to the step's answer_to_key so downstream steps branch on it; the run then advances.
Input schema
{'type': 'object', 'required': ['workflow_id', 'run_id', 'step_id', 'picks'], 'properties': {'other': {'type': 'string', 'description': 'Free-text answer; only when the question allows it.'}, 'picks': {'type': 'array', 'description': 'Option ids from the pinned question (may be empty when answering with `other` alone).'}, 'run_id': {'type': 'string', 'description': 'The run id (from aimeat_workflow_pending_inputs).'}, 'step_id': {'type': 'string', 'description': 'The waiting step id.'}, 'workflow_id': {'type': 'string', 'description': 'The workflow id.'}}}
aimeat_workflow_get
Inspect workflows. Omit id to list all your workflows; pass an id for its definition + the derived blueprint (the whole input→output flow + the memory keys each step touches) + recent runs. The recent runs are the ones that dispatched agents; a signals-only check is not a run and is not listed among them. A run the node ended itself carries a reason: "stopped" at its spending limit (maxCostUsd), or "refused" when its trigger found the workflow's saver disconnected or short of a permission (the owner can run it once as themselves from the notification, or approve the permission again). Use before editing or running, and to read a run's outcome.
Input schema
{'type': 'object', 'properties': {'id': {'type': 'string', 'description': "Omit to list; pass for one workflow's detail."}}}
aimeat_workflow_pending_inputs
List every workflow step currently WAITING FOR HUMAN INPUT across the owner's active runs: the pinned question (prompt + options), when it was asked, and the deadline after which the step's timeout policy fires. Use to list "things waiting on the owner" — then relay the owner's decision with aimeat_workflow_answer.
Input schema
{'type': 'object', 'properties': {}}
aimeat_workflow_run
Run a workflow. mode="signals-only" is a CHECK: it evaluates every step's signals against existing memory with NO dispatch, costs nothing, returns each step's verdict inline, and is kept apart from the runs (it does not appear in the run history or the health); mode="full" executes the steps live (dispatches the agent tasks, which takes time and spends their budget; poll aimeat_workflow_get for progress). Use signals-only to validate a workflow or to see what is already in memory before a full run. A full start while a live run of the same workflow is in flight starts NOTHING unless the definition sets parallel:true: the answer then carries skipped:true and the id of the run that is running, not a new one. Read the flag before treating the id as yours.
Input schema
{'type': 'object', 'required': ['id', 'mode'], 'properties': {'id': {'type': 'string', 'description': 'The workflow id.'}, 'mode': {'enum': ['signals-only', 'full'], 'type': 'string', 'description': 'signals-only | full'}, 'vars': {'type': 'object', 'description': "The run's input, as { varName: value } over the vars the workflow declares. A workflow that takes input is a constant without this. Anything it does not declare is ignored, and a declared var left out falls back to its default."}, 'target': {'enum': ['live', 'sandbox'], 'type': 'string', 'description': 'With mode="full": "sandbox" writes every key behind a per-run prefix so a trial cannot touch what a live run produced. Default "live".'}}}
aimeat_workflow_save
Create or update an Agent Workflow: a declared, ordered set of steps with per-step input (required_to_function) and output (success_signal) signals, run by ONE trigger, with the signal checked after each step (so you see "did it produce", not just "did it fire"). Use instead of chaining separate schedules when steps depend on each other. Pass the whole descriptor as `definition`; each step names an agent + offer and inherits that offer's signals + deliverable location. Rejected at save if the after-graph is not a DAG or an offer is not workflow-compatible (must publish success_signal + required_to_function + deliverable.location). A schedule trigger creates one backing cron; an event trigger fires on a matching memory write / offer order. A step costs the permission its own endpoint asks, at save and again at a full run: work:request for an agent step (giving an agent work; a workflow saved before 2026-09-25 keeps its agent steps free), ai:use for an ai step or llm.approved, ext:invoke for an extension step, memory:read + storage:write + memory:write for a datapackage step, memory:read + work:request for export-out, work:request for trigger-geai, and memory:read for a workflow that reads the owner's records (every signal leaf and every agent step reads one; a signals-only check needs it too); without it the answer is SCOPE_DENIED naming the permission. maxCostUsd caps what one run may spend on AI, in US dollars, counting its ai steps and the node's model judging its llm signals. Before an ai step's model call starts, the node sets aside what one attempt is expected to cost (the most one attempt cost in the workflow's last ten finished runs, else an equal share of the cap nobody holds) and starts it only when that fits beside what the run has spent and what its open calls hold, so ai steps that fit together still run side by side. A call holds its share until it answers, also after a timeout or a retry moved its step on. A step that does not fit waits while a call is open; with none open, the run stops with status "stopped" and says why. A step expected to cost more than the whole cap starts alone while the run has spent less. Past the cap an llm signal is not judged and passes. costCapMorsels does nothing (a morsel is not money) and is removed in 4.0.0; a save that sets it answers with warnings. The save records you as the workflow's saver: a run its own trigger starts answers to you, and does not start (status "refused", the owner told once) when you are disconnected or no longer hold a permission its steps need.
Input schema
{'type': 'object', 'required': ['id', 'definition'], 'properties': {'id': {'type': 'string', 'description': 'Workflow id (lowercase slug); existing id = update.'}, 'propose': {'type': 'boolean', 'description': 'Operator flow (server MCP only): return a diff vs the current definition + a single-use confirm_token WITHOUT saving.'}, 'definition': {'type': 'object', 'description': 'The descriptor: { title, description, trigger, vars[], steps[], on_step_fail:"inspect", llm?{approved}, notify_on_finish?, resume?, fresh?, skip_done?, parallel?, maxCostUsd? }. parallel:true lets two or more live runs of this workflow overlap; use it when the keys carry a run-distinguishing var (a case reference in vars, or the built-in {run}); without it a second start while one is in flight is skipped and says so. Refused together with fresh. maxCostUsd (US dollars, per run) caps what a run spends on AI, its ai steps and the judging of its llm signals together: an ai step\'s model call starts only when what one attempt is expected to cost fits in what is left, and holds that share until the call answers, also after a timeout or a retry; otherwise the step waits for the open calls or stops the run. A step expected to cost more than the whole cap starts alone while the run has spent less.'}, 'confirm_token': {'type': 'string', 'description': 'Token from the propose step — applies exactly the proposed definition.'}}}
aimeat_work_inbox
Check your work inbox: work items others have requested from you (where you are the provider), still pending/accepted/in-progress. Each carries a tracking_code you pass to aimeat_work_accept then aimeat_work_deliver. This is the provider side of the action catalogue; to request work from others use aimeat_action_execute. response_format=concise returns just tracking_code/status/action_id.
Input schema
{'type': 'object', 'properties': {}}
aimeat_workspace_access
REQUEST/REVIEW flow for a gated workspace, via `action`: 'request' = ask the creator for access to a workspace you can see but not read (org membership lets you DISCOVER workspaces; a workspace's CONTENT is gated by its creator); 'list' = (creator/admin) see who has requested (pending/approved) plus current members + roles; 'decide' = (creator/admin) approve or deny a request (approve grants 'contributor' by default; pass role='viewer' for read-only). Member-only; list/decide are creator-or-admin. To add a member PROACTIVELY (no prior request), or to add to MANY workspaces at once, use aimeat_workspace_member_grant.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'action'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'role': {'enum': ['viewer', 'contributor'], 'type': 'string', 'description': "action='decide' approve: 'viewer' (read) or 'contributor' (read+write). Omit for the default (contributor)."}, 'action': {'enum': ['request', 'list', 'decide'], 'type': 'string', 'description': "'request' | 'list' | 'decide'."}, 'message': {'type': 'string', 'description': "action='request': optional note to the creator."}, 'decision': {'enum': ['approve', 'deny'], 'type': 'string', 'description': "action='decide': 'approve' (default) or 'deny'."}, 'requester': {'type': 'string', 'description': "action='decide': the requester's owner name (from action='list')."}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_comment
Add a comment to a workspace object (a record or a document) — for discussion/review threads. Target it by ws + space (objectType) + instance_id. Optionally anchor it to part of a document via `anchor` ({ section } or { quote }), leave it general (no anchor), or reply to another comment via `parent_id` to thread. Agents and humans both comment here. Read a thread with aimeat_workspace_comments. Member-only. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space', 'instance_id', 'body'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'body': {'type': 'string', 'description': 'The comment text.'}, 'space': {'type': 'string', 'description': 'The objectType (space) name the target lives in.'}, 'anchor': {'type': 'object', 'description': 'Optional anchor to part of a document: { section } or { quote }.'}, 'parent_id': {'type': 'string', 'description': 'Optional id of the comment this replies to (threading).'}, 'instance_id': {'type': 'string', 'description': 'The id of the record/document being commented on.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
aimeat_workspace_comments
List the comment thread on one workspace object (record or document), oldest first, with each comment's author, body, anchor (if any), and parent (for replies). Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space', 'instance_id'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'space': {'type': 'string', 'description': 'The objectType (space) name.'}, 'instance_id': {'type': 'string', 'description': 'The record/document id.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_create
Create a new WORKSPACE inside an organism from a CUSTOM MANIFEST you supply — its objectTypes (each a records space with a JSON schema, or a document/wiki space) plus the per-namespace schemas. Registers it, locks the schemas, writes the manifest + readme. This is how an agent bootstraps a structured space; then fill it with aimeat_workspace_write (records and documents alike) and publish with aimeat_workspace_publish. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'name', 'manifest'], 'properties': {'name': {'type': 'string', 'description': 'Workspace name.'}, 'readme': {'type': 'string', 'description': 'Optional markdown intro (defaults to the manifest name + summary).'}, 'schemas': {'type': 'object', 'description': 'Map of namespace → JSON Schema for each records objectType, e.g. { "shared.tasks": { type:"object", required:["id","title"], properties:{...} } }.'}, 'manifest': {'type': 'object', 'description': 'The manifest. You really only need its `objectTypes` array — the envelope (manifestVersion, id, name, kind, status) is BACKFILLED for you when omitted, so an objectTypes-only manifest is accepted on the first call (name defaults from this tool\'s `name` param, kind to "project"). Full shape: { objectTypes: [{ name, namespace, mode:"records"|"document", schemaRef, writeRole, cardinality, versioned }], policy? }. Each space is backing:"memory" (the default) — "rows" is the ROW STORE for a stream that arrives in volume and does not stop (appended, no version history, charged to the organism, filtered on up to three fields named in `indexOn`, and read through the rows tools rather than the record ones), "tasks" only declares a pointer to the task system, and "storage"/"knowledge" are rejected: files and knowledge packages attach via workspace Sources or embedded document images, never as a backed space. The test for memory versus rows is one multiplication: if keys_per_day × 365 exceeds 1000, use rows.'}, 'organism_id': {'type': 'string', 'description': 'Organism to create the workspace in.'}}}
aimeat_workspace_doc_append
Add markdown to a workspace DOCUMENT without sending the rest of it back — at the end, or at the end of one named section. This is how a long document is amended: aimeat_workspace_write replaces the whole thing, so amending a 57,000-character spec through it means retyping all of it, and what that fails at is silent. The insert never removes an existing character, so two sessions can append to the same document and both survive — the write is a compare-and-swap that re-reads and re-applies if somebody got there first. `section` names a heading by its exact TEXT ('Concurrency', not '## Concurrency' — either is accepted); the new text lands at the end of that section, before the next heading. Two headings with the same text is a refusal naming both, because guessing which one you meant is how an edit lands in the wrong half of a long document. Edits the DRAFT, seeding it from the published version when there is no draft yet; publish with aimeat_workspace_publish. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space', 'id', 'markdown'], 'properties': {'id': {'type': 'string', 'description': 'The document id, from the workspace index (aimeat_workspace_read).'}, 'ws': {'type': 'string', 'description': 'Workspace id.'}, 'space': {'type': 'string', 'description': "The document space, by objectType name (e.g. 'notes') or namespace."}, 'section': {'type': 'string', 'description': "Add at the end of THIS section instead of the end of the document. The heading's exact text; an ambiguous one is refused."}, 'markdown': {'type': 'string', 'description': 'The markdown to add. Blank lines around it are worked out for you; nothing already in the document is touched.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_doc_section_replace
Replace one section of a workspace DOCUMENT — a heading and its body — leaving every other byte exactly as it was. Use it to correct or rewrite one part of a long document instead of resending the whole thing, which is both expensive and, for a document somebody else wrote, unsafe. `section` names the heading by its exact TEXT, and `markdown` is the WHOLE replacement INCLUDING its heading line: a block that does not start with a heading is refused rather than guessed at, and changing the heading there is how a section gets renamed. A section runs to the next heading at the same level or higher, so replacing '## Tests' takes its '### Unit' subsection with it. Two headings with the same text is a refusal naming both. Headings inside ```-fenced code are not headings. Concurrent edits are safe (compare-and-swap with re-apply). Edits the DRAFT; publish with aimeat_workspace_publish. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space', 'id', 'section', 'markdown'], 'properties': {'id': {'type': 'string', 'description': 'The document id, from the workspace index (aimeat_workspace_read).'}, 'ws': {'type': 'string', 'description': 'Workspace id.'}, 'space': {'type': 'string', 'description': 'The document space, by objectType name or namespace.'}, 'section': {'type': 'string', 'description': "The heading text to replace, exactly as the document spells it (the leading #'s are optional)."}, 'markdown': {'type': 'string', 'description': 'The whole replacement section, starting with its heading line.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_list
List the WORKSPACES inside an organism. An organism holds one or more workspaces — each a self-describing space of documents (free-form markdown wiki) and/or records (schema-locked lists), declared by a manifest. Returns each workspace's id + name. Use the id with aimeat_workspace_read. You must be a member of the organism.
Input schema
{'type': 'object', 'required': ['organism_id'], 'properties': {'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_member_grant
Directly grant an EXISTING organism member a workspace role — no prior access request needed. Grant to ONE workspace (`ws`) or MANY at once (`workspaces`: e.g. every workspace from aimeat_workspace_list), so 'add this member to all workspaces' is one call. `grantee` may be an owner name, GHII (owner@node), or GAII (agent#owner@node) — the grant applies to the OWNER, so all their agents inherit it. role: 'viewer' (read) | 'contributor' (read+write). Authorized for the workspace creator or an org admin. Per-workspace result: granted / skipped_creator / forbidden_or_not_found. Each grant is auditable (a creator-owned consent stamped with its source).
Input schema
{'type': 'object', 'required': ['organism_id', 'grantee', 'role'], 'properties': {'ws': {'type': 'string', 'description': 'A single workspace id (use this and/or `workspaces`).'}, 'role': {'enum': ['viewer', 'contributor'], 'type': 'string', 'description': "'viewer' (read) or 'contributor' (read+write)."}, 'grantee': {'type': 'string', 'description': 'Owner name, GHII, or GAII to grant. Applies to the owner (agents inherit).'}, 'workspaces': {'type': 'array', 'description': 'Many workspace ids to grant in one call.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_member_revoke
Remove a member's workspace role on ONE workspace (`ws`) or MANY (`workspaces`). `grantee` may be an owner name, GHII, or GAII (resolved to the owner). To DOWNGRADE (e.g. contributor → viewer) rather than remove, call aimeat_workspace_member_grant with the lower role instead. Authorized for the workspace creator or an org admin. Per-workspace result: revoked / not_a_member / forbidden_or_not_found.
Input schema
{'type': 'object', 'required': ['organism_id', 'grantee'], 'properties': {'ws': {'type': 'string', 'description': 'A single workspace id (use this and/or `workspaces`).'}, 'grantee': {'type': 'string', 'description': 'Owner name, GHII, or GAII to revoke.'}, 'workspaces': {'type': 'array', 'description': 'Many workspace ids to revoke in one call.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_members
List a workspace's members with their role ('viewer' | 'contributor'), the grant's source (grant | request | invite), who granted it, and when. Authorized for the workspace creator or an org admin. For the organism-wide roster (all members + org roles) use aimeat_organism_members instead.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id (from aimeat_workspace_list).'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_object_delete
Permanently remove ONE object (record or document) from a workspace — its draft, its published .latest, and all .version.N history — and unfile it from any document section. Use this to retract a mistake or clean up a duplicate. Irreversible; member-only. To replace content instead, overwrite with aimeat_workspace_write.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'namespace', 'id'], 'properties': {'id': {'type': 'string', 'description': 'The instance id to delete (draft + latest + all versions).'}, 'ws': {'type': 'string', 'description': 'Workspace id.'}, 'namespace': {'type': 'string', 'description': "The objectType's namespace, e.g. shared.deliverables."}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_overview
Get a fast, OKF-style STRUCTURE MAP of ONE workspace as Markdown — per space, the most-recently-updated entries (up to 10) with their instance id + title, and the total count of each space. Read this to find WHICH id you need, then call aimeat_workspace_read (or the memory API) to pull that record. Cheaper than reading the whole workspace when you only need to navigate. Same read access as aimeat_workspace_read; if you lack access the map says so. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id (from aimeat_workspace_list).'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_publish
Publish a draft → snapshots it to a new immutable .version.N + the live .latest and consumes the draft. Schema-validated. If the workspace's publish gate is on, this refuses and asks you to leave the draft for human review instead. Do not publish without the owner's go-ahead unless told to run autonomously. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'namespace', 'id'], 'properties': {'id': {'type': 'string', 'description': 'The instance id whose .draft to publish.'}, 'ws': {'type': 'string', 'description': 'Workspace id.'}, 'namespace': {'type': 'string', 'description': 'The instance namespace.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_read
Read one workspace in TWO steps so you never pull a huge blob. DEFAULT (no `ids`) returns the INDEX: the manifest (the objectTypes it declares — each a records space with a JSON schema, or a document space of markdown pages), the JSON Schemas currently LOCKED on the records spaces as `schemas` (keyed by namespace, in the shape aimeat_workspace_update takes back — read this before you change one, because that update REPLACES rather than merges), plus, per space, EVERY instance as { id, title, updated, version, bytes, published, has_draft } — titles only, NO bodies — so it stays small however many/large the documents are. Scan the index (or aimeat_workspace_overview for the same as a Markdown map), pick the ids that likely hold what you need, then call this again with `ids:[...]` to BATCH-OPEN just those instances' full values. Version history (.version.N) is never returned here — read a specific version via the memory API. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id (from aimeat_workspace_list).'}, 'ids': {'type': 'array', 'description': 'Batch-open: return the FULL value of ONLY these instance ids (from the index). A full memory key, which is the id aimeat_discover gives a workspace record, is taken as well. Omit for the lightweight index.'}, 'space': {'type': 'string', 'description': 'With `ids`: optionally restrict the lookup to this space (objectType name or namespace).'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'include_archived': {'type': 'boolean', 'description': 'Include archived (hidden) content. Default false.'}}}
aimeat_workspace_revert_to_draft
Reopen a PUBLISHED record for editing — copies its .latest back into .draft so you can amend it and re-publish via aimeat_workspace_write + aimeat_workspace_publish. The published .latest stays live until you re-publish. Refuses if a draft already exists (edit that draft instead). Use this when a published record needs a change. Member-only.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'namespace', 'id'], 'properties': {'id': {'type': 'string', 'description': 'The instance id of the published record to reopen.'}, 'ws': {'type': 'string', 'description': 'Workspace id.'}, 'namespace': {'type': 'string', 'description': 'The instance namespace.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_rows_append
Add rows to a workspace ROW space — the shape for what a GROUP accumulates (received messages, events, readings, a log) rather than for records a person authors one by one. A row space is declared in the manifest with backing:'rows'; it is charged to the workspace and the organism instead of to whoever wrote the row, keeps no version history, and never appears row-by-row in a workspace index (the index shows a count). Send one row, or up to 500 in `rows`. Supplying `row_id` makes the append IDEMPOTENT: repeating it REPLACES that row and keeps its original createdAt, so re-running an ingest updates instead of duplicating. `occurred_at` is when the thing happened in the world (a message's own date, not now) and is what reads are ordered and bounded by. Refused before anything is written if the space is not a row space, the caller may not write it, a row is over the size ceiling, or the workspace/organism quota is reached.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'body': {'type': 'object', 'description': 'The row, for the single-row form. Use `rows` for many.'}, 'rows': {'type': 'array', 'description': 'Up to 500 rows, each { body, row_id?, occurred_at? }.'}, 'space': {'type': 'string', 'description': "The row space, by objectType name (e.g. 'mailmessage') or namespace."}, 'row_id': {'type': 'string', 'description': 'Optional caller id for the single-row form. Repeating one REPLACES that row.'}, 'occurred_at': {'type': 'string', 'description': 'ISO 8601: when it happened in the world. Defaults to now.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_rows_delete
Remove rows from a workspace ROW space: one row by `row_id`, or everything that LANDED before `before` (retention by age). Retention keys on when the row was written to this node, never on when the event happened, so a five-year-old message ingested today is not swept on arrival. Pass exactly one of `row_id` or `before` — there is deliberately no "delete everything" form. Irreversible; a row space keeps no version history to restore from.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'space': {'type': 'string', 'description': 'The row space, by objectType name or namespace.'}, 'before': {'type': 'string', 'description': 'ISO 8601: remove every row created before this.'}, 'row_id': {'type': 'string', 'description': 'Remove this one row.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_rows_read
Read one page of a workspace ROW space, newest first by occurred_at. Keyset-cursored: follow `cursor` for the next page, and a null cursor is the last one — a page boundary can neither skip nor repeat a row even when many share one instant. FILTERING WORKS ONLY ON THE FIELDS THE SPACE DECLARED in its manifest `indexOn` (at most three); pass them in `where`, and anything else is REFUSED with the list that does work rather than ignored, so a filtered page is always really filtered. The answer carries `indexed` so you learn that list from the response. `since`/`until` bound occurred_at inclusively; `changed_since` bounds updated_at exclusively and is what an incremental sync follows.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'limit': {'type': 'number', 'description': 'Rows per page, default 100, max 500.'}, 'order': {'type': 'string', 'description': "'desc' (default, newest first) or 'asc'."}, 'since': {'type': 'string', 'description': 'ISO 8601: occurred_at at or after this.'}, 'space': {'type': 'string', 'description': 'The row space, by objectType name or namespace.'}, 'until': {'type': 'string', 'description': 'ISO 8601: occurred_at at or before this.'}, 'where': {'type': 'object', 'description': 'Filter as { field: value }, using only fields the space declares in indexOn.'}, 'cursor': {'type': 'string', 'description': 'Opaque cursor from the previous page.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'changed_since': {'type': 'string', 'description': 'ISO 8601: rows whose updated_at is strictly after this.'}}}
aimeat_workspace_rows_stats
What a workspace ROW space holds, without reading a row: how many, how many bytes, the oldest and newest occurred_at, and when anything last landed. This is what a workspace index shows for a row space instead of its rows, and it is one aggregate rather than a scan, so it stays honest at any size. Read it before a wide query to know what you are about to ask for.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'space': {'type': 'string', 'description': 'The row space, by objectType name or namespace.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_sections_set
Set the section index of one document space: the tree of sections its pages are filed under, [{ id, name, parentId, documents:[docId], color? }], the same list the workspace page saves. Read the current one first (aimeat_workspace_read returns every document space's index as `sections`), change it and send the WHOLE list back. Filing a document into a section moves it out of the one that held it. Who may, and whether it lands at once, is the same as for aimeat_workspace_space_add: status 'applied', or 'pending_approval' with the `suggestion` when the workspace asks its members to suggest. A member's further changes to the same space join the suggestion already waiting. Taking a document that no longer exists out of the index is always written at once.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'space', 'sections'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'space': {'type': 'string', 'description': 'The document space, by name or namespace.'}, 'sections': {'type': 'array', 'description': "The WHOLE index: [{ id, name, parentId, documents, color? }]. An id is letters, digits, '-' or '_'; a parent must be another section in the list; color is red, orange, yellow, green, blue, purple or gray."}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_space_add
Add a space to a workspace as any member who may change it: its creator, an organism admin, or a member holding the contributor role. A space is { name, namespace, mode }, with mode 'document' for pages or 'records' for a list (send its JSON Schema in `schemas`), or a ROW space { name, namespace, backing:'rows', indexOn:[…] }; defaults are filled. A space whose name or namespace already exists is skipped. What happens to it depends on who you are and on the workspace's rule (its `rules.member_changes`, readable in aimeat_workspace_read): the creator's and an admin's change is written at once; a member's is written at once when the rule is 'direct', and filed as a suggestion that the creator or an admin approves when it is 'suggest' (the default). The answer says which: status 'applied' (with `added`), 'pending_approval' (with the `suggestion`), or 'unchanged'. The space always lands in the workspace's own structure with your name on it, never in a copy of yours. For renaming or removing spaces, the creator uses aimeat_workspace_update.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws', 'spaces'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'spaces': {'type': 'array', 'description': "The space to add, { name, namespace, mode }, or an array of up to 20. The namespace is dotted letters, digits, '-' and '_', such as 'shared.notes'; 'meta', 'skills' and 'access' are the platform's own."}, 'schemas': {'type': 'object', 'description': 'Map of namespace → JSON Schema, only for records spaces added in this same call. A space that already exists keeps its schema.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}}}
aimeat_workspace_suggestions
The changes members suggested to a workspace (a space to add, changes to a document space's sections) that wait for a decision, via `action`. 'list' = the suggestions you may see (those in workspaces you can read, and your own), each with `can_decide` for you; `status` picks pending (default), approved, declined, expired or all. 'decide' = approve or decline one: approving applies exactly the suggested change to the workspace as it is now, with the member's name on it and yours as the approver; declining leaves the workspace as it is. Either way the member is told, with your `note` if you give one. Decided by the workspace's creator or an organism admin, never by the member who made it; an agent decides for its owner when the owner is one of them. A suggestion that waits 30 days expires and changes nothing.
Input schema
{'type': 'object', 'required': ['organism_id', 'action'], 'properties': {'ws': {'type': 'string', 'description': "action='list': only this workspace. Omit for every workspace you can read."}, 'note': {'type': 'string', 'description': "action='decide': an optional note the member reads."}, 'action': {'enum': ['list', 'decide'], 'type': 'string', 'description': "'list' | 'decide'."}, 'status': {'enum': ['pending', 'approved', 'declined', 'expired', 'all'], 'type': 'string', 'description': "action='list': which ones. Default 'pending'."}, 'decision': {'enum': ['approve', 'decline'], 'type': 'string', 'description': "action='decide': 'approve' or 'decline'."}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'suggestion_id': {'type': 'string', 'description': "action='decide': the suggestion's id, from action='list'."}}}
aimeat_workspace_transfer
Back up or restore a workspace, via `direction`. 'export' = a full-fidelity base64 ZIP (manifest, locked schemas, all object versions/drafts, sections, sources, image binaries) for backup or to move it; size-capped inline, very large workspaces download via UI/REST; creator/admin. 'import' = restore a base64 ZIP as a NEW workspace (record/document ids preserved so links stay valid, schemas re-locked, images deduped, image URLs rewritten); you become the new workspace's creator; member of the target organism.
Input schema
{'type': 'object', 'required': ['organism_id', 'direction'], 'properties': {'ws': {'type': 'string', 'description': "direction='export': the workspace id to export."}, 'direction': {'enum': ['export', 'import'], 'type': 'string', 'description': "'export' or 'import'."}, 'zip_base64': {'type': 'string', 'description': "direction='import': the workspace export ZIP, base64-encoded."}, 'organism_id': {'type': 'string', 'description': 'Organism identifier (source for export, target for import).'}}}
aimeat_workspace_update
Update a workspace IN PLACE — its name, readme, and/or its STRUCTURE — without changing its id (so nothing referencing it gets orphaned). To ADD spaces, pass `add_spaces` (an ARRAY of objectTypes): the server UNIONS them into the manifest, skips any whose name/namespace already exists, and fills sensible defaults — the safe, deterministic way to provision (no need to resend the whole manifest). To rename/remove a space, toggle the publish gate (policy.alwaysGate), or change settings, pass a full replacement `manifest`. Pass `schemas` to lock a records space's JSON Schema, and `member_changes` to set how the workspace takes its members' changes. Creator-only (or an org admin); a member who is neither adds a space with aimeat_workspace_space_add and changes sections with aimeat_workspace_sections_set, under the workspace's rule. The single tool for restructuring a workspace — no separate remove-space or set-gate tool.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws'], 'properties': {'ws': {'type': 'string', 'description': 'Workspace id.'}, 'name': {'type': 'string', 'description': 'New workspace name (synced to manifest + registry).'}, 'readme': {'type': 'string', 'description': 'New markdown readme/intro (replaces the current one).'}, 'schemas': {'type': 'object', 'description': 'Map of namespace → JSON Schema (object) to lock (strict) for a records space. READ THE CURRENT SCHEMAS FIRST: this REPLACES the locked schema, it does not merge into it, so a schema you write without having read drops whatever else the old one said. aimeat_workspace_read (the default index call) returns them as `schemas`, keyed by namespace, in exactly this shape — read, edit the one entry, send the map back. And do not invent a maxLength: the real ceiling is the memory value budget the node enforces on the whole record (1024 kB by default), and a field cap smaller than that is a number somebody guessed, which is how a notes field filled up at 4000 characters for no reason anyone could name.'}, 'manifest': {'type': 'object', 'description': 'Full replacement manifest (objectTypes + policy/gate + settings) — for restructuring (rename/remove a space, change the gate). The id is preserved and the manifest is schema-validated. To only ADD spaces, prefer add_spaces. May also carry an optional top-level objectives[] (the measurability convention: why the organism exists + KPIs with kind value/cost/roi/outcome/quality and a source that can sum/count the organism\'s own records) and an objectType servesObjective linking a space to an objective; both optional — see "Recording purpose & value" in docs/agent-workspace-contracts.md.'}, 'add_spaces': {'type': 'array', 'description': 'ADDITIVE: objectTypes to UNION into the manifest (skip-if-exists). Pass just { name, namespace, mode } (+ a schema in `schemas`); defaults are filled. A ROW space is { name, namespace, backing:"rows", indexOn:[…] } and takes no mode: rows keep no version history and are neither records nor documents, and those defaults are filled for you too. Preferred over `manifest` for adding spaces. Returns { added, skipped }. Cannot remove/rename.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'member_changes': {'enum': ['direct', 'suggest'], 'type': 'string', 'description': "How this workspace takes a change from a member who is neither its creator nor an organism admin (aimeat_workspace_space_add, aimeat_workspace_sections_set): 'direct' = it lands at once with their name on it; 'suggest' = it waits until the creator or an admin approves it (the default)."}}}
aimeat_workspace_write
Create or overwrite DRAFT items in a workspace — records OR documents — in one tool. ONE item: pass `space` + `value`. MANY: pass `items: [{ value, space?, id?, section? }]` (up to 50) and they are all written in a SINGLE call — do this for any migration or multi-page import, because your client asks the human to approve every tool CALL, so twenty separate writes are twenty approval prompts and one unanswered prompt leaves the job half-done. A batch is all-or-nothing: every item is checked first, and one bad item writes nothing (the error names its index). Items inherit the top-level `space`/`section` unless they carry their own. Give the space NAME (the objectType, e.g. 'feature' or 'notes'); the tool resolves whether it is a records or document space and writes accordingly. For a records space, `value` is the record (validated against its schema, rejected if invalid) and needs an `id`. For a document space, `value` is { title, markdown }, the `id` is auto-generated, and you can file it under a `section`. Document markdown renders rich: ```mermaid fenced blocks become diagrams, and ```aimeat-memory fenced blocks become LIVE data (body lines `key: <memory key>`, optional `view: table|props|list`, `fields: a,b`, `title: …`) — the document shows the key's CURRENT value on every open, so for data that changes, write it with aimeat_memory_write as an array of objects and embed the key instead of pasting a static table. Drafts are NOT live until published (aimeat_workspace_publish). Embed images by uploading with aimeat_storage_upload and using the embed_markdown / embed_url it returns (the owner-addressed /v1/pub form) — NOT a hand-written /v1/storage/<key> path, which loads only for you. On save the embedded image is scoped to this workspace's members (not the public internet). Member-only. If a model generated or substantially rewrote this content, declare it in `ai_provenance` (level, and human_involvement if a person reviewed the substance). If you are relaying text a person wrote, say so with level:"original" — silence is recorded as model-written.
Input schema
{'type': 'object', 'required': ['organism_id', 'ws'], 'properties': {'id': {'type': 'string', 'description': 'Instance id. Required for a records space (or include id in value); auto-generated for a document.'}, 'ws': {'type': 'string', 'description': 'Workspace id.'}, 'items': {'type': 'array', 'description': 'BATCH: [{ value, space?, id?, section? }] — up to 50 items written in one call (one approval prompt for the whole migration). All-or-nothing.'}, 'space': {'type': 'string', 'description': "The objectType (space) NAME from the manifest, e.g. 'feature' or 'notes'. Required for a single write; with `items` it is the default each item inherits."}, 'value': {'type': 'object', 'description': 'The content object for a SINGLE write. Records: the record (matching its schema). Documents: { title, markdown }. Omit when using `items`.'}, 'section': {'type': 'string', 'description': 'Document spaces only: section id/name to file the document under.'}, 'organism_id': {'type': 'string', 'description': 'Organism identifier.'}, 'ai_provenance': {'type': 'object', 'description': 'How this content was made: { level, method?, human_involvement?, model?, provider?, sources?, notes? }. `level` is required when the block is present: original | assisted | synthesized | ai-generated. `model` is YOUR OWN model id as your provider names it (self-identify, never ask the person); without it the record cannot say which model made this. `human_involvement` (none | light-review | editorial-control | full-human) counts only a step where a person read the SUBSTANCE and could reject it; omitted means none. The node fills in who you are, which node, when, and a hash of the exact bytes — those are never taken from the caller.'}, 'ai_provenance_id': {'type': 'string', 'description': 'Attach an EXISTING provenance record instead of declaring a new one — the id the node returned when it generated this content for you. Only your own records can be attached.'}}}
Changed
aimeat_storage_upload
Oct. 1, 2026, 2:44 a.m.
Changed
aimeat_schedule_create
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_refinery_status
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_refinery_run
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_refinery_classes
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_package_sellers
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_package_sale
Oct. 1, 2026, 2:44 a.m.
Changed
aimeat_package_entitlements
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_package_config_needs
Oct. 1, 2026, 2:44 a.m.
Changed
aimeat_package_compose
Oct. 1, 2026, 2:44 a.m.
Changed
aimeat_datamap_set
Oct. 1, 2026, 2:44 a.m.
Added
aimeat_classification
Oct. 1, 2026, 2:44 a.m.
Changed
aimeat_agents_list
Oct. 1, 2026, 2:44 a.m.
Changed
aimeat_admin_incident_resolve
Oct. 1, 2026, 2:44 a.m.
Removed
aimeat_app_visitors_measure
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_visitors
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_versions
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_ui_set
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_ui_get
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_seo_set
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_screenshot
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_marks_set
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_legal_set
Sept. 29, 2026, 2:51 a.m.
Removed
aimeat_app_audit
Sept. 29, 2026, 2:51 a.m.
Changed
aimeat_workspace_write
Sept. 29, 2026, 2:51 a.m.
Changed
aimeat_workspace_update
Sept. 29, 2026, 2:51 a.m.
Added
aimeat_workspace_suggestions
Sept. 29, 2026, 2:51 a.m.
Added
aimeat_workspace_space_add
Sept. 29, 2026, 2:51 a.m.
Added
aimeat_workspace_sections_set
Sept. 29, 2026, 2:51 a.m.
Changed
aimeat_workspace_comment
Sept. 29, 2026, 2:51 a.m.