Take input control
Hand the keyboard from an agent to a person, and back, without the two interleaving.
At most one session may send input to a machine at a time. That is what makes human takeover safe: taking control revokes the agent’s lease rather than interleaving with it, and every input carries the lease generation, so anything from the previous holder is dropped at the gateway rather than landing late.
Reads are never leased. A viewer that cannot type can still see.
The agent side
Nothing to do. Both SDKs open a session and acquire the lease on the first call that types or clicks, and renew it at half its TTL:
await machine.keyboard.type("hello"); // acquires the lease if it does not hold one
machine.holdsInput; // true
Taking it from a person
The lease route is on the contract client, since the SDKs manage the lease themselves:
const session = await client.api.sessions.create({
body: { machine_id: machine.id, type: "human" },
headers: {},
});
const lease = await client.api.inputLeases.acquire({
params: { machine_id: machine.id },
body: { session_id: session.id, ttl_seconds: 60, force: true },
});
force: true is what takes the lease from its current holder. Without it, an agent already
driving the machine causes a conflict naming the holder — which is the right answer for a
program, and the wrong one for a person who has decided to intervene.
What happens to the agent
Its next renewal fails, and it stops:
machine.holdsInput; // false
await machine.keyboard.type("more");
// RasterError: conflict — someone else holds the input lease
The SDKs never re-acquire a lease they lost. A lost lease means a person took over, and
silently taking it back would put an agent and a person on the same keyboard, which is the one
thing single-holder input exists to prevent. Catch the conflict and decide deliberately.
Handing it back
await client.api.inputLeases.release({
params: { machine_id: machine.id },
body: { session_id: session.id },
});
Closing the session drops the lease with it. The agent’s next input call acquires it again normally — the lease is free, so no force is needed.
Watching without taking
A screen stream opens a session but never acquires the lease, so a viewer cannot evict whoever is driving:
const stream = await machine.screen.stream();
Terminals are outside the lease entirely. Two people holding separate ptys is normal — the lease governs the desktop, where there is one mouse and one keyboard.
Further
Sessions and input control for the fence and how enforcement works, and the realtime gateway for the input channel itself.