The Contract
What a harness adapter says of itself when asked lnk-<name> contract:
the version of this contract it speaks, and each part of the contract
its harness takes. Read by lnk-agent, which asks once a process.
lnk-<name> contract
Prints link_harness_contract::harness::Contract as JSON: {"version", "proxy", "proof"?, "agent"?, "ports"?, "instructions"?, "from"?, "recover"?, "tools"?, "parts"?, "folders"?, "prompt"?, "conversations"?, "shared"?, "blank"?, "acp"?, "state"?, "notes"?, "native_memory"?, "identity"?, "features"?}. Each key left out is no,
unless it says otherwise.
Its version and its model
What contract it speaks, how it reaches its model, and how many ports
it takes. The version is the version of this contract it speaks: 2.
proxy
Whether its harness reaches a hosted model at model.base_url, Link's
proxy, which then holds the key: keys on the wire, by default.
ports
How many ports from LNK_PORT its harness takes. Link gives the agent
that many and one more, for its proxy on a Mac; left out, 200.
Its endpoint
What its endpoint does beyond answering a message
(The Endpoint).
proof
Whether its endpoint proves it's the harness before it's sent the
key, answering a challenge with an HMAC made with the key.
agent
Whether its endpoint reads which agent a request is for, so one
process can answer for many agents.
from
Whether its endpoint reads who a message is from. Left out, it's
never sent a message from anyone but the owner, and gets no scheduler.
recover
Whether its endpoint hands over the answers of turns it finished
after a crash. Left out, they stay in the conversation.
tools
Whether its endpoint keeps a conversation's tools. Left out, a
subagent has all its parent's tools, and the bridge refuses to send a
list.
parts
Whether its endpoint takes a message's images and documents as
OpenAI's content parts. Each connection Link carries then downloads the
photos and documents a message holds, PNG, JPEG, GIF or WebP images and
PDF or text documents of at most 10 MiB and 8 a message
(link_connection_contract::{MEDIA, PART_MAX, PARTS_MAX}).
How the parts are sent
They're sent as image_url and file parts with data: URLs, the
message's text naming each one left out and why. Left out, none is ever
sent, and a message holding only them is answered that the agent reads
text only.
Its folders and conversations
What of the agent's folder it keeps to, and what it answers of the agent's prompt and conversations.
folders
Whether it keeps to the agent's folder's layout: it writes only
chats/ of the agent's own folder itself, so its sandbox reads
Home and the own folder's instructions/ and skills/, and writes
only its chats/ and the folders the agent's sandbox file's
[files] write names. With it, and its sandbox on, the agent gets
Link's run and file.
Without it
Left out, its sandbox writes the whole of Home and never reaches the
agent's own folder, and a start says [files] holds only for Link's
tools.
instructions
Whether it takes the agent spec's [instructions]. Left out, a spec
setting them is refused for it.
prompt
Whether it answers instructions (Conversations).
Left out, lnk agent show --instructions says it can't.
chats
Whether it answers conversation and conversations move
(Conversations). Left out, lnk agent chat show
and lnk agent chat convert say it can't.
How it runs
Two ways one process of it may run: for many agents of one owner, or started ahead with nothing of anyone's.
shared
Whether one process of it may serve several agents of one owner:
<command> --agents <file>, its command's argv with those two added,
serves each agent link_harness_contract::harness::SharedAgents names,
on its own port, as that agent. Link runs it so only where
machine.toml says shared_harness = true, in one sandbox whose policy
has reached.
What each agent is served with
SharedAgents is [{"name", "id", "owner", "home", "files", "agent"?, "port", "env"}]: each agent's own home, Home and own folder (agent;
left out, everything in files), and its tools from env, the
environment it would have had alone.
blank
Whether it can start blank, for a machine that runs an agent only while
it answers: <command> --blank <control>, its command's argv with
those two added, loads nothing of any agent's and answers on no port,
listening on the Unix socket <control>, mode 0600, outside its
sandbox, until one line of link_harness_contract::harness::Bind
arrives.
The binding
Bind is {"agent": {"name", "id", "owner", "teams"?}, "key", "settings", "store"?, "files"?}: the agent, a key made for this binding
alone, its endpoint's and the one it proves its bridge calls with, its
settings as configure takes them with a placeholder for every secret,
and the sockets of its store bridge and files bridge.
Its answer
It answers {} once it answers as that agent alone, on its usual port,
or {"error"} and stays blank (link_harness_contract::harness::bind).
A second Bind is refused, and nothing unbinds it: the process ends
when the agent sleeps.
Without a contract
An adapter without contract speaks version 0 and answers no to every
key, but for the three harnesses Link's releases once carried
(lnk-openclaw, lnk-hermes, lnk-deepseek), which keep their keys
on the wire as they did. A version 0 adapter also gets 180 s to answer
after a start, and lnk agent start says it gets no updates.
Who reads the version
lnk-agent reads it for the default of lnk agent secret, and for
what it assumes of an adapter that answers no contract: the
version 0 defaults above.
Features
A contract's features lists the features its harness honors, each by
its id (link_harness_contract::harness::features::Feature), as
"spec", as the agent's spec says (it reads the feature's keys, or
Link does for it, as for the connections it carries), or "native",
its own way, which Link leaves to it: {"repo-instructions": "native", "groups": "spec"}.
A spec it doesn't honor
lnk agent start refuses a spec whose own keys, or its profile's, turn
on a feature the list doesn't hold, naming the feature and its key
(Features and
Profiles).
A feature or a way this Link doesn't know is left out of the list,
never the whole contract refused.
Left out
Left out, the list is what the contract's flags say: instructions,
repo-instructions, persona and skills; parts, images and
attachments; from, subagents, schedules and proactive;
folders, Link's tools, shell, file-read, file-edit,
code-search, diffs and lsp; and groups and streaming for
every harness.
Each harness's
Only a harness that tells its calls over ACP (tool_call) honors
activity. Link Harness honors every feature as the spec says. Hermes
and OpenClaw honor persona and skills as the spec says (their
identity) and the rest natively, and DeepSeek TUI its own natively,
with groups and streaming as the spec says on the connections Link
carries to it.
What a switch leaves behind
A contract's state says what its endpoint's agent state exports and
imports over ASP, notes whether it reads the agent's notes, and
native_memory whether it keeps memory files of its own.
state
{"export", "import"}, each a list of ASP's kinds (sessions,
instructions, skills, tools, memory, settings), as its
initialize advertises agentState: Link Harness's and Hermes's take
and give sessions, OpenClaw's gives them and takes none. Left out,
none: a switch carries no conversation in or out of it.
notes
true says the harness reads the agent's notes (memory/notes.md) at
each turn, where a switch writes what the agent remembered on other
harnesses: Link Harness's.
native_memory
true says the harness keeps memory files of its own, which no other
harness reads.
What lnk agent use lists
lnk agent use lists what the destination won't carry before it
switches (Contract::left_behind): its conversations unless the one
it leaves exports them and the other takes them in, the harness it
leaves's own memory files unless it carries
who the agent is and the other reads them, its skills and persona
unless both read them from the agent's spec, and each feature the
agent's spec turns on that it honors and the other doesn't
(Contract::not_honored).
Agreeing to it
use asks, or --yes agrees ahead; a no, or no terminal to ask in,
changes nothing.
Who the agent is
A contract's identity, true, says the harness carries who the agent
is both ways (link_harness_contract::harness::identity).
Into the harness
configure's settings hold identity, {"persona"?, "owner"?, "standing"?, "skills"?}: each text the agent's spec turns on, read from
the agent's own folder, and its skills' names, which Link copies into
the harness's home at .lnk/agent/skills/<name>/ before it, the folder
there even when empty.
Into its files
The adapter writes the texts into a block of its harness's own files,
between <!-- link:begin ... --> and <!-- link:end -->
(identity::write_block), the rest of each file kept, and points the
harness's skills setting at the copy.
Out of the harness
lnk-<name> state export prints what the harness holds of its own in
ASP's portable forms (link_harness_contract::asp::State,
{"instructions"?, "skills"?, "memory"?}), and lnk-<name> state
alone still prints the paths lnk agent move carries.
What it exports
One AGENTS.md of its files' text outside Link's block that isn't the
harness's own default, each text under the heading Identity::all
gives it (## Who you are, ## Your owner, ## Standing instructions); each skill it made as an Agent Skills folder, {"name", "files"}, its text files by their paths; and its memory files as
notes, {"path", "text", "source"}.
Taken in a switch
At lnk agent use away from it, once the switch is agreed, Link writes
each text into the agent's file where that file is missing or empty
(Identity::of_agents_md), and adds each skill whose name the agent
has none of, saying what came. What it remembered there is kept with
the rest of its state, in the agent's folder.
Into its memory
configure's settings hold remembered, [{"path", "text", "source"}]: what the agent remembers, as ASP's notes, Link's notes
(source link) and each harness's memory as a switch away from it
left it (hermes, openclaw), deleted ones left out.
Kept apart from its own
The adapter writes those not its own into its harness's memory files, marked as Link's, and its export leaves what it wrote out, so nothing comes back as the harness's own.
Session content
session_content lists non-text content the harness can use after a
session import: tools, thinking, images, audio, resources or
other. Left out, session support promises text only. Link names actual
content outside that list before a switch, retaining the source snapshot.