Conversations

Every conversation of Link Harness is a folder in your agent's files, so it moves with your agent (lnk agent move) and stays when you switch harness. What it costs and how much of it the model sees are here too.

Read your conversations

Each chat's folder is named by its connection and chat, and conversation.md in it is a copy to read, added to as the agent answers (what it holds).

ls ~/Link/Agents/main/Agent/chats/
cat ~/Link/Agents/main/Agent/chats/telegram-123456789/conversation.md
cat ~/Link/Agents/main/Agent/chats/slack-D0123ABCDEF/conversation.md

events.jsonl

events.jsonl holds each message as a line of JSON, in order, only ever added to: a line is never changed once written. It holds the message's time, model, tokens as the provider counted them, and cost.

What its events hold

A tool's call is in the message that makes it, and its answer is a line of its own, as is a summary of what left the window, and a command you ran (/chat:create).

lnk agent chat show

lnk agent chat show <chat> prints a chat as conversation.md holds it, however it's kept.

lnk agent chat show telegram:123456789

History shows only the owner's messages and the agent's answers, with their original event numbers and UTC times.

lnk agent chat list --json
lnk agent chat show link:main --json
lnk agent chat fork link:main --before 7

The fork

The fork copies every event before the selected owner message into a fresh link:fork-… thread and prints its history. Its source remains unchanged. Send the edited message to the new thread with lnk agent chat send --chat <name>.

What it copies

The copied prefix includes tool calls, results, provider thinking and signatures, summaries and window marks, and keeps the source's tool restrictions.

The provider's cache

OpenAI, Anthropic, OpenRouter, Bedrock through a model service, and other providers receive their usual cacheable prompt prefix; only the edited suffix changes. Provider cache lifetimes and minimum sizes still apply.

Separate chats

lnk agent chat start opens a new chat by default, with its own folder under Agent/chats/. Follow-ups stay in that session. Select a named chat with --chat <name> to continue it. Discord and Slack threads have distinct chats too; replies in one thread share its context.

Saved memory across chats

Saved memory notes are shared by the agent. A fact only mentioned in a chat is transcript history until the agent saves it with its memory tool.

Keep them smaller, or in a database

How a new chat is kept is your agent's to say, in its spec ([storage] conversations).

[storage]
conversations = "compact"

log

Link's. Each chat's folder holds events.jsonl and conversation.md, read with cat, jq or lnk agent chat show. A spec that names file, the agent's own folder, as lnk agent show --opinions prints it, keeps each chat this way too.

compact

Each chat's folder holds events.zst, its messages in zstd frames, and events.jsonl, those since the last frame, read with lnk agent chat show. compact compresses the event log to reduce disk use. It reads the whole conversation for each turn, including requests for its latest messages, so a long compact conversation needs more read and decompression work than a log or store driver.

sqlite

A store driver is a plugin, lnk-<name> (its contract), named here by its name: each chat's folder holds an empty store.sqlite, and the driver keeps its messages. Link's is SQLite: lnk plugin add sqlite, then conversations = "sqlite", and every chat of the agent is in one file, Agent/chats/events.sqlite.

Where the driver runs

Its program runs outside Link harness's sandbox, behind the agent's store bridge, which takes each call only as the agent's own; memory runs it in memory's sandbox.

gcs

Off the machine, so any machine reading the bucket can take your agent's next turn, it's Google Cloud Storage: lnk plugin add gcs, then conversations = "gcs".

Its objects

Each message is an object of its own, <prefix>/<owner>/<agent>/<chat>/events/<number>, written only if it isn't there, so of two machines writing one number, one does; a turn's lease is one object beside them, replaced only if it's still the one read.

Its settings

Its settings are its environment: the bucket (LNK_GCS_BUCKET), where in it (LNK_GCS_PREFIX), and a token (LNK_GCS_TOKEN or LNK_GCS_TOKEN_FILE), each. Link harness starts with an environment of Link's own, which doesn't carry them, so lnk-gcs runs under a harness started with them.

Reads and clocks

Reading a chat costs a request per message, eight at a time. A lease lapses by the clocks of the machines that took and renewed it, so they keep time (NTP).

Move the old ones

Changing the key moves nothing: each chat stays where it is, read there, and a new chat starts the new way. To move the old ones too, stop your agent and run:

lnk agent stop
lnk agent chat convert

What isn't a message

Whatever the way, a chat's folder holds what isn't a message as files of its own: the tools it was narrowed to (tools.json), and while a turn runs, its lease (turn.json; a driver keeps that too) and whether it was asked to stop. Memory searches a compact or driver's chat as it does a log's: it makes the copy itself.

Through the storage bridge

Handed the storage bridge (its settings' storage, which lnk agent start gives it on every machine), it reads and writes whatever its machine keeps off its folder (in SQLite, a bucket, or a box's store) only through it. What's kept at file stays in its folder as above.

Nothing on the disk

The driver of that place keeps a chat's messages, its lease and the tools it was narrowed to, and whether a turn was asked to stop is held in memory, so nothing of such a chat is on the machine's disk. A turn whose tools it can't read fails, rather than offer every tool.

Messages from its schedules and subagents

Its scheduler (Link Scheduler) lets it start subagents and schedule messages to itself, on a team message its teammates (scheduler__send), and a background command it started tells it how it ended.

As from them

Such a message comes as from them, never from you. It's kept with who sent it, the model is told, and each line of it is quoted, so nothing in it reads as yours: in conversation.md too, where it's headed "Others", not "You". Its prompt names each teammate with its agent's ID and purpose, and what of the team's folder it may reach.

Send another message while it answers

A message you send while it answers is kept at once, and then does what its spec says in [turns] busy.

[turns] busy

steer is Link's: the answer takes the message in at its next step, and goes on to answer it too, the reply coming on your first message. queue answers it after, on its own. interrupt stops the answer where it is, keeps it marked interrupted, cancels the tools still running, and answers your new message.

After a crash

If the harness stops mid-answer, as in a crash or a restart, it finishes the answer when it starts again, and the answer reaches your chat within a minute. If Link's bridge stops instead, the harness finishes the answer all the same, and it reaches your chat once the bridge is back.

See what it costs

After each turn the harness reports numbers only: its tokens, its cost, its tool calls and failures, and how long it took to ask the model, to send the first piece of the answer and to finish it.

lnk measure --agent main            # its turns, tokens, spend and times, over the last day

What a turn counts

Each turn counts only the answers and tool calls it wrote itself, even when the next turn has begun by the time they're counted. They never include the words. The measure plugin collects them (Measure).

A message's cost

A message's cost is the provider's when it reports one, and otherwise its tokens at the model's price (lnk model show).

How much it remembers

The model sees as much of the conversation as the spec sends, up to 128,000 tokens in Link's, so a long conversation never runs up a surprise bill.

Its window

What Link knows of a model sets its window (lnk model show), and your own figures win. A model on your computer gets the window its runtime says it runs with (A Model's Details), and a model nobody described gets 32,768 tokens. A [window] max you set is used as you set it, whatever Link knows.

Past three quarters

Past three quarters of it, the oldest turns leave the window half of it at a time, so the provider's cache keeps the prompt cheap. What leaves is summarized, once the turn has answered, and the window starts with the summary, so a conversation keeps going however long it gets. The files keep everything.

Notes and the summary

Where the agent has notes (Link Memory's remember, with lnk agent memory start), the model saves what matters to them before it summarizes. The summary is kept in the conversation's events, and never sent to you.

[window] compact_at and save_notes

When it compacts, and whether it saves first, are [window] compact_at and save_notes in the spec; compact_at = "never" lets the oldest turns leave unsummarized.