Kin / Agents & automation

Local clients

kin --stdio hosts the ordinary interactive session for one local program that owns Kin's standard input and output — the same journal, workspace trust, tools, and questions as the terminal, projected as typed JSON lines. It is how an OS-native surface such as the Kinra OS Front Door carries a live Kin conversation…

Read as Markdown

kin --stdio hosts the ordinary interactive session for one local program that owns Kin’s standard input and output — the same journal, workspace trust, conversation lease, tools, modes, and questions as the terminal, projected as typed JSON lines. It is how an OS-native surface such as the Kinra OS Front Door carries a live Kin conversation without a second harness, transcript, or authority model.

It is not headless mode. kin -p runs one unattended turn and turns every human decision into a needs-human exit; kin --stdio keeps the session open, streams, asks, waits for the person, and takes follow-ups in the same conversation. It is also not a network service: there is no socket or port, and the client is whichever process holds the pipes.

kin --stdio --workdir ~/code/project              # a fresh conversation
kin --stdio --workdir ~/code/project --resume ID  # exactly one saved conversation

The contract in one exchange

Every line is one JSON object. The client speaks first.

→ {"type":"hello","protocol":1,"client":"front-door","capabilities":{"open_url":true}}
← {"type":"welcome","protocol":1,"kin":"0.6.10","capabilities":["open_url"]}
← {"type":"session","session_id":"s_…","model":"…","workspace":"/home/…/project","posture":"…"}
← {"type":"ready"}
← {"type":"mode_changed","mode":"auto"}
→ {"type":"turn","id":"t1","text":"What does this project's release gate check?"}
← {"type":"accepted","of":"turn","id":"t1"}
← {"type":"thinking","active":true}
← {"type":"tool_call","id":"call_1","name":"read_file","arguments":"{\"path\":\"Taskfile.yml\"}"}
← {"type":"tool_result","id":"call_1","name":"read_file","result":"…","result_truncated":true}
← {"type":"stream_chunk","message_id":"m1","content":"The gate runs …"}
← {"type":"stream_done","message_id":"m1"}
← {"type":"done","reason":"stop"}
← {"type":"turn_ended","id":"t1"}
→ {"type":"close"}
← {"type":"accepted","of":"close"}
← {"type":"closed"}

The events between accepted and turn_ended are the ordinary event vocabulary — the same shapes the terminal renders — with a few fields bounded for the pipe.

Client messages

Message Fields Meaning
hello protocol (must be 1), client (≤64 chars, optional), capabilities (object of booleans, optional) Required first message. Declares the protocol and the handoffs the client can perform. Today the only capability is open_url.
turn id (≤64 chars), text (≤32,000 chars), context (optional, ≤8 items) Start one root turn. The text is literal turn data — no ! shell passthrough and no / skill dispatch, exactly like an interactive launch prompt.
cancel Interrupt the running turn, as ++escape++ does in the terminal. Refused as idle when nothing runs.
resolve kind, id, and the answer Answer the exact pending blocking event: question (index, optional text), approval (outcome once/always/deny, optional note, and the display_signature the event carried), plan (index), proposal (decision apply/reject).
handoff_result id, ok, detail (≤500 chars) Report what the environment did with a handoff.
close End the session: the turn is cancelled, the journal closes, the lease is released, and the last line is closed.

End of input, SIGTERM, SIGINT, and SIGHUP close the same way. Anything else is refused by name — never guessed at.

Attention context

A turn may carry a short list of facts about where the person’s attention was when they invoked Kin:

{"type":"turn","id":"t2","text":"Summarize this page for me.",
 "context":[{"kind":"window","label":"Brave","value":"kinra-ai/kin — GitHub"},
            {"kind":"workspace","label":"workspace","value":"2 on DP-5"}]}

kind is one of window, workspace, output, selection, path, or note; labels are capped at 80 characters and values at 500. Kin frames the whole block as untrusted data ahead of the request and says what it is: a window title or path is a hint about context, never proof of a file, a page’s content, or intent. Kin reads or asks before acting on it.

Host messages

Beside the ordinary events, the host writes a small set of its own:

Message Fields Meaning
welcome protocol, kin, capabilities The hello was accepted; the accepted capabilities follow.
accepted of, id A turn, cancel, resolve, handoff_result, or close was taken.
refused of, reason, detail A message was not taken: malformed, unknown, bounds, busy, idle, stale, closing, or protocol. The session is unaffected.
turn_ended id The turn settled, whichever way — after done, interrupted, or an error.
handoff id, kind, payload Kin asks the environment to perform one typed effect; answer with handoff_result.
fatal reason, detail The host cannot serve — protocol, untrusted, lease, resume, backend, or host — and exits after this line.
closed The last line before exit.

One turn, exact answers, bounded lines

Only one turn runs at a time; a second turn is refused as busy until turn_ended. Cancel first if the new request replaces the old one.

A resolve is accepted only for the exact pending block of the stated kind. A wrong id, a wrong kind, a duplicate answer, or an answer after the turn settled is refused as stale and resolves nothing. An approval must echo the display_signature its event carried, so a stale review can never approve a changed call. Git credential and GitHub connection requests are forwarded but cannot be resolved over stdio; they belong to the terminal, and a client hands the conversation off (see below).

Client lines are limited to 64 KiB; an oversize or malformed line is refused and drained, and the stream continues. In the other direction, tool arguments and results are capped and flagged (arguments_truncated, result_truncated), image blocks are dropped, and reasoning, todos, tasks_changed, and tool_call_draft are not forwarded. Review bodies — approvals, plans, proposals — arrive complete.

Handoffs: how an action leaves prose

A client that declares open_url gives the session one extra tool. When Kin decides a page should open, it calls open_url with one exact http/https URL; the host parks a handoff on the client:

← {"type":"handoff","id":"ho_3","kind":"open_url","payload":{"url":"https://kinra.ai/kin"}}
→ {"type":"handoff_result","id":"ho_3","ok":true,"detail":"Brave"}
← {"type":"tool_result","id":"call_4","name":"open_url","result":"opened https://kinra.ai/kin in the person's browser (Brave)"}

The environment owns the effect: it decides how the page opens, may decline ("ok":false), and reports a fact, never authority over Kin. No page content comes back — reading stays with web_fetch. A handoff the environment never answers settles on the tool’s own timeout. Without the capability the tool does not exist, so a plain terminal Kin cannot open a browser. A client must never infer an action from a response; the typed handoff is the only door.

Trust, leases, and handing off to the terminal

A host cannot create workspace trust. Like a headless or resumed launch, it needs a persistent trust record for the workspace before any project material or inference loads; otherwise the first line is {"type":"fatal","reason":"untrusted",…} and the process exits 2. Trust the workspace once from an interactive kin there.

One conversation has one live client. --stdio --resume ID is refused (fatal with reason lease) while a terminal or another host owns that conversation, and the terminal’s --resume is refused while a host lives. To move a conversation to the terminal — for a Git credential prompt, a plan review the client does not render, or simply deeper work — the client sends close, waits for closed, and then launches kin --resume ID. The journal is the transcript; the client keeps at most the opaque session id.

Exit codes

Code Meaning
0 The client closed the session or its end of the pipe.
1 The host failed after the handshake (see the fatal line’s detail).
2 The host could not start: bad hello, untrusted workspace, held lease, unknown resume target, or invalid model configuration. Argparse usage errors also exit 2.

--stdio is mutually exclusive with -p, with --agent, and with a positional prompt; it composes with --workdir, --resume, --continue, --new, --no-save, --mode, and the model and preset flags.