Accounts That Host Agents

The commands an adapter (lnk-<kind>) answers when its account hosts agents, as link_host_contract types them: each reads one link_host_contract::Request as JSON on stdin and prints one link_host_contract::Reply, as every adapter command does.

Whether it hosts and what

The two commands lnk agent asks of every account: whether it hosts agents, and which.

lnk-<kind> agent-host

Optional. {"hosts": true}: the account hosts agents itself, with no box, and answers the commands below. An adapter without it (has no command) hosts none, as AWS's and Google Cloud's: no adapter of Link's answers it, or the agent-* commands.

Asked once

lnk agent asks each account once, and again once it's connected as another, and keeps the answer (~/.lnk/agent/hosts.toml): lnk agent list asks agent-list only of an account that hosts agents.

lnk-<kind> agent-list

Reads owner; {"agents": [{"id", "name", "owner"?, "state"}]}, the agents it hosts for that owner, state in lowercase words (running, paused). A reply without agents says nothing of which it hosts, and is never taken as none.

Who reads it

lnk agent list, lnk agent move (finding an agent there), and lnk secret delete (signed in as the owner a move took an agent there as: listed no longer, the agent took its keys with it, and the account is no longer named).

Moving an agent there and back

The commands a move runs on the account, each carrying the Move Stream.

lnk-<kind> agent-create

Reads create: {"name"} and the account; returns agent: {"id", "name", "owner"?, "state"}. The host supplies the harness and model, without creating an agent on this machine. lnk agent create <name> --on <account> uses it only for an account answering hosts: true.

lnk-<kind> agent-connect

Reads agent and connect: {"connection", "token", "user"?}; returns {"message"} after the host saves the connection. Telegram is the supported connection: token is its bot token and user its owner's Telegram user ID when needed for pairing. The token travels on stdin.

lnk-<kind> agent-in

Reads agent ({"id", "name", "owner"?}) and size (its Home's and memory index's bytes, when the machine it's on knows them); {"move", "have", "seal_to"}, the move it starts and what it holds of the agent already. Read by lnk agent move <account>.

Models supplied by the host

managed_models: true asks the source to omit model and fallback settings from its switch stream. Their credentials stay in the source's secret keeper without a live agent grant. The same agent ID returning there restores those settings; an explicitly removed key stays removed.

Refused

{"refused": {"why", "retry_at"?}} says why it won't now, and when the next move is allowed (Unix seconds), which lnk shows as it is, the time as this machine's.

lnk-<kind> agent-out

Reads agent; {"move", "size"}, the move out it starts and the bytes it holds, or {"refused": ...}. Read by lnk agent move from the account.

lnk-<kind> agent-take

Reads its request as one line (move, phase), then a format-4 stream of that phase on stdin, each file checked against its hash. A copy answers {"have"}, what it holds then; a switch puts the agent in place and answers {"answers": true, "machine"}, the agent running there, on that machine. Read by lnk agent move, lnk bucket push.

Phase files

With phase files and agent instead of a move, Home alone: it prints Home's {"have"} as one line first, then reads a stream of files/..., then prints {}.

A conversation in a stream

A conversation is its folder, files/chats/<folder>/, as link_store_contract::store::folder reads and writes it at every end: events.jsonl, conversation.md, and tools.json, the tools it was narrowed to, when a list narrowed it.

Narrowed before its first event

One narrowed before its first event is a folder of tools.json and a conversation.md holding its first line alone, which names its thread: the folder's name can't, its : a -.

A cloud that keeps conversations otherwise

It keeps each as narrow as its tools.json says, refuses a stream whose list isn't one, and gives the same folder back on agent-give. A conversation's narrowing is its tools.json and nothing else: a folder whose file was removed moves with every tool, since nothing tells it from one never narrowed.

lnk-<kind> agent-give

Reads move, phase, have and, for a switch, seal_to (the key the receiving lnk agent receive said); writes a format-4 stream of that phase on stdout, but what have holds the same. With phase files and agent, Home alone. Read by lnk agent move, lnk bucket pull.

A switch

The complete switch stream is sealed to seal_to (link_secret_value::stream), including its harness and connection files; receive authenticates its final record before success. A switch pauses the agent there first, messages that come meanwhile held.

lnk-<kind> agent-left

Reads move; {"held": [{"connection", "chat", "from", "text"}]} once its copy of the agent is deleted: the messages that came while it was paused there, which lnk agent deliver gives it where it went. Read by lnk agent move, once the agent answers where it went.

lnk-<kind> agent-abort

Reads move; {} once a move in's copy is removed, or a move out's agent answers there again. Read by lnk agent move, after any failure before the other end answers.

Backups and chats

The optional commands for an agent the account runs: its backups, and a chat with it.

lnk-<kind> agent-backup

Reads agent; {"at"} once the account has backed the agent up where it runs, never stopping it (at in Unix seconds). Read by lnk agent backup of a hosted agent. Optional: an adapter without it keeps no backups of the agents it hosts, and lnk agent backup says so.

lnk-<kind> agent-restore

Reads agent; {"at"} once its last backup there is restored (its conversations stay). Read by lnk agent restore of a hosted agent. Optional, as agent-backup is.

lnk-<kind> agent-chat

Reads agent, thread (a link thread, link:<name>) and text (the owner's message, at most a megabyte); writes JSON lines on stdout as the answer comes (link_host_contract::Chatting). Read by lnk agent chat send of a hosted agent, which kills the adapter when it stops waiting (Ctrl-C). Optional: an adapter without it hosts agents lnk agent chat send doesn't reach.

Its lines

{"piece": "..."} for each piece of it, then one end: {"answer": "..."}, the answer whole (at most 16 MB), "steered", the message joined a turn already running, whose answer goes where that turn's does, or {"refused": {"why"}}. An answer nobody waited for isn't kept for the next chat: it's in the agent's conversation.

Hosted teams

A hosting adapter may answer agent-team, taking Request.team (link_host_contract::teams::Action) and returning Reply.team. Unsupported operations fail as any optional adapter command does.

Its operations

list, get, put (the complete team file and version read), delete, invites, join, leave, begin-in, begin-out, backup and restore. The typed replies, team file, invitation and movement descriptors are defined once in link_host_contract::teams.

What a reply shows

An invitation exposes only the requesting agent's row. The owner's team file reply also says which outside principals joined.

A team movement

A team movement contains the parent move ID, the complete team file and each own member's ordinary move ID. Existing agent-take, agent-give, agent-left and agent-abort carry that movement. Each member may answer managed_models: true, which retains and omits that member's source models as an ordinary hosted move does.

Its stream

The outer format-4 manifest names team (the TOML file); files/members/<agent>/<chunk>.part carries each member's format-4 stream in ordered 64 MiB parts, and files/shared/ carries the team's folder. Hash and end checks apply at both levels.

Its switch

The receiver prepares all members before one switch. Only the parent movement can switch, finish or abort the team. A team exit returns Reply.team_held, keyed by the receiving member's ID, so held messages reach that member's conversations.

Restored model credentials

On a team's return, each source model stays retained until the whole team's durable finish receives the host's agent-left acknowledgment. A failed member preparation can then roll back without losing another member's restored source key. Explicit secret removal still revokes it.

A team's folder

With phase files, Request.team set to folder and a permanent team ID selects that team's folder instead of Request.agent. Push and pull keep the existing Home-stream contract.