You give an agent a browser. It opens a page, signs in, and tells you it is through. On the next call you ask it to pull the invoice list, and the snapshot that comes back shows the login form again. Nothing crashed. Nothing was denied. The second call simply arrived somewhere else — another process, another browser, with no recollection of the first — because nothing inside the request said which browser it meant. The session existed. It was just sitting somewhere the message had no way to point at.

The tag is not the coat

Start with the thing being passed around. A handle is an identifier a tool hands back when it makes something that has to outlive the call that made it: a browser it opened, a basket it filled, a job it started. The agent keeps that identifier and includes it as an argument in everything it does next. Nothing about the thing itself travels.

Picture the cloakroom of a crowded place on a cold night. You arrive in a heavy coat, and you do not wear it all evening, and you do not stand there describing it — grey, wool, lining coming away at the shoulder — each time you want it back. You hand it over once and get a small numbered tag. The room holds the bulk. You hold the number. The tag is not a lighter coat. It is a way of pointing at the one hanging up.

So a call that opens a browser returns a browser identifier, and every action and every snapshot afterwards carries that same identifier. The alternative is a room that keeps no tags and recognises faces instead — state hidden inside the connection, remembered by whichever machine happened to answer. That works beautifully until you come back through a different door.

A number is not a permission

Two things follow, and they pull in opposite directions. The good one is that requests stop needing to be related to each other. When the state a call operates on is named in its arguments, any server can take the call; nothing has to route you back to the machine that served you last time, and nothing breaks when that machine is replaced mid-task. Composition becomes visible instead of implied.

The other thing is the mistake everybody makes once. A handle looks like a credential and is not one. Whoever holds the number gets the coat — which is fine for one evening in one room, and much less fine when the number never expires, is never checked against whose it is, and turns up in a log an hour later. So ownership gets verified on every use, and handles get a life span, and neither of those is an optimisation you add afterwards.

The same caution runs through anything holding state between calls. A process kept alive to remember your variables will happily remember somebody else’s if sessions are not separated, and that failure is silent by construction: nothing errors, the wrong data simply shows up in the right answer. This is the part that decides what you are willing to leave an agent alone with.

Some state should never become text

Handles are not only for browsers and jobs. Give an agent an interpreter that does not shut down between turns, and its variables stay where they were: the table it loaded, the frame it built, the model it fitted. The next turn can say filter the frame you just made, and the name is doing exactly what the numbered tag does. Underneath, the harness keeps two histories side by side — what was said, and what was executed, with the in-memory objects still attached — rather than a single transcript of text.

The reason to bother is arithmetic. Serialising a whole table into the prompt is enormous and lossy at once; naming it costs a few characters and loses nothing. It is also the quiet alternative to squeezing the window: instead of compressing everything down to text, part of the state simply stops being text. What compaction throws away cannot throw away what was never in there.

An agent that can point at its state gets to work on it. One that cannot has to wear it.