Kit is a small, directory-rooted coding-agent runtime.
It exposes:
- ACP over stdio for clients and editors
- A2A v1 JSON-RPC for agent collaboration
- a Ratatui ACP client
- one model-visible tool:
compose, backed by released Runlet - hidden compose children for bundled Kit documentation, shell commands, hunk edits, reusable ACP subagents, A2A calls, Agent Skills, and MCP discovery/authentication
AGENTS.mdinstructions loaded from the runtime root and its ancestors- Agent Skills discovered from
<root>/.agents/skills,~/.agents/skills, and validated local or checksum-pinned archive Agent Plugins - OpenAI subscription access through Kit's native ChatGPT OAuth login
- OpenRouter through AgentKit's OpenRouter provider adapter
- Speakeasy AI Control Plane through its OpenRouter-compatible completions endpoint
It intentionally has no general interactive permissions framework, provenance ledger, rollback system, control-plane authentication, or web UI.
Source code and prebuilt binaries are published from the public
speakeasy-api/kit repository.
Install the latest release with mise:
mise use -g github:speakeasy-api/kit
kit --versionPin a release by appending its version, for example
github:speakeasy-api/kit@0.1.83.
Authenticate your ChatGPT subscription once, then start the TUI:
cargo run -- auth login openai --credential-store keychain
cargo run -- tui --root /path/to/project --credential-store keychainThe native login uses OAuth authorization code flow with PKCE, state, and nonce.
It listens only on registered loopback callback ports 1455 and 1457, validates
OpenAI RS256 tokens against the pinned JWKS endpoint, and uses the same selected
credential backend as MCP. Standalone login rejects the default memory backend;
select persistent keychain or file storage.
Protocol source attribution and reproduced upstream licenses are in
THIRD_PARTY_NOTICES.md.
Every TUI conversation is persisted as an append-only, versioned JSONL transcript
under ~/.kit/sessions. Sessions from the legacy <root>/.kit/sessions location
remain resumable and are copied to the global location on resume. When reported
context use reaches 80% of the model's window, Kit automatically checkpoints older
history while preserving bootstrap instructions and a tool-safe recent tail; the
replacement is persisted for resume.
The session id is shown in the header. Resume it with:
cargo run -- tui --root /path/to/project --resume <session-id>A per-session filesystem lock prevents two live Kit processes from mutating the
same transcript. If a crashed process left its lock file behind, add --force;
Kit will reclaim it only when no live process still holds the OS lock.
For a non-interactive smoke test, run one prompt and print the resumable session id:
cargo run -- prompt --root /path/to/project "Reply with a short project summary"The command prints the model response followed by session_id: ... and exits.
Pass that id to prompt --resume <session-id> or tui --resume <session-id>.
Run ACP on stdio with an optional A2A and remote ACP HTTP listener, or use the dedicated stdio-only command:
cargo run -- serve --root /path/to/project # A2A HTTP
cargo run -- serve --root /path/to/project --remote-acp # A2A + ACP HTTP
cargo run -- serve --root /path/to/project --remote-acp --no-a2a # ACP HTTP only
cargo run -- serve --remote-acp --no-a2a --no-stdio --http 0.0.0.0:8081 # ACP daemon
cargo run -- acp --root /path/to/project # no HTTP listenerBoth commands reserve stdout for ACP and send diagnostics to stderr. serve
chooses an available loopback port by default; pass --a2a 127.0.0.1:7331
(or its visible alias --http) to request a specific address. Remote ACP uses
HTTP/SSE or WebSocket at /acp, while A2A discovery uses the Agent Card
endpoint. Add --server-credential-file /private/token to require that file's
single bearer token on every HTTP request. --no-stdio requires --remote-acp and keeps
serve running independently of stdin. SIGINT and SIGTERM stop new connections, interrupt
active ACP sessions, and drain them for up to about five seconds before aborting stragglers.
acp never starts an HTTP listener. Never expose a non-loopback daemon without authentication
and appropriate network controls.
Inspect or remove the credential with kit auth status openai and
kit auth logout openai. Logout revokes the refresh token before deleting the
local record; if remote revocation is unavailable, credentials are retained. Use
--local-only only when you intentionally need deletion without revocation.
Kit refreshes expiring credentials proactively and retries one rejected credential
with a synchronized forced refresh.
To use OpenRouter, sign in to the shared credential store or supply an API key, then select the provider and pass an OpenRouter model identifier:
cargo run -- auth login openrouter --credential-store keychain
cargo run -- auth status openrouter --credential-store keychain
cargo run -- prompt --credential-store keychain \
--provider openrouter --model anthropic/claude-sonnet-4 \
--root /path/to/project "Reply with a short project summary"
cargo run -- auth logout openrouter --credential-store keychainRuntime and OpenRouter auth status resolve keys in this order: a non-empty global
--openrouter-api-key, a non-empty OPENROUTER_API_KEY, then the stored
openrouter/default credential. An empty flag is rejected; an empty environment value
is treated as absent. The flag is available to serve, acp, prompt, tui, and auth, but its value is
present in Kit's initial process argv and can be exposed by shell history or process
inspection. Prefer the environment or stored credentials. Kit never rewrites its own
process environment and never adds the key to child argv. It overrides
OPENROUTER_API_KEY only in the TUI's Kit serve child and built-in or configured
acp.kit children. Kit removes its resolved OpenRouter key from external ACP child
environments rather than exposing the credential to arbitrary configured agents.
kit auth status openrouter reports whether the active source is the flag,
environment, or stored credentials without printing the key. Logout removes only the
stored record and cannot revoke an OpenRouter key automatically. If the flag or
OPENROUTER_API_KEY remains set, logout warns that it is still active; the
environment value is retained.
Kit uses the selected --model or configured model; OPENROUTER_MODEL does not
override it. The adapter also honors its optional OPENROUTER_BASE_URL,
OPENROUTER_APP_NAME, OPENROUTER_SITE_URL, OPENROUTER_MAX_COMPLETION_TOKENS,
OPENROUTER_TEMPERATURE, and OPENROUTER_REASONING_EFFORT settings. Stored OAuth
credentials are restricted to OpenRouter's canonical API endpoint; custom base URLs
require an explicit flag or environment key.
Kit also reads the OpenRouter model catalog's
context_length for the selected model, enabling the normal context gauge and
80% automatic-compaction threshold. Catalog lookup is best-effort; an unknown
model or unavailable catalog leaves usage reporting intact but disables the gauge
and automatic compaction rather than guessing a window.
To sign in with Speakeasy, use the same persistent credential store for login and runtime commands:
cargo run -- auth login speakeasy --credential-store keychain
cargo run -- prompt --credential-store keychain \
--provider speakeasy --model anthropic/claude-sonnet-4 \
--root /path/to/project "Reply with a short project summary"Login opens app.getgram.ai, receives the producer-scoped Gram API key through a
loopback form POST, verifies its accessible projects, and stores the key and selected
project. Inference sends them as Gram-Key and Gram-Project to
https://app.getgram.ai/chat/completions. Kit also maps its durable session ID to
Gram's deterministic UUIDv5 Gram-Chat-ID, so new and resumed conversations appear
as the same session in AI Control Plane. OpenRouter settings
and credentials remain isolated. The AI Control Plane does not currently expose a
public model-catalog endpoint, so the Speakeasy selector uses the OpenRouter catalog
as a v0 fallback. Models outside Speakeasy's allowlist can be selected but fail at
runtime. kit auth logout speakeasy removes the local key; revoke it remotely in
the Speakeasy dashboard.
Run kit init to create ~/.kit/config.toml and an empty ~/.kit/mcp.json when they do not exist. The generated setup selects gpt-5.6-sol and file-backed credentials in ~/.kit/credentials. Existing files are left unchanged.
Kit loads optional defaults from ~/.kit/config.toml. Command-line values take
precedence, and omitted values that are not configured retain Kit's built-in
defaults (root = ".", provider = "openai-subscription",
model = "gpt-5.4", and in-memory OAuth credentials). Supported keys are:
root = "/path/to/project"
provider = "openai-subscription" # or openrouter or speakeasy
model = "gpt-5.4"
reasoning_effort = "medium" # low, medium, or high
a2a = "127.0.0.1:7331"
otel_endpoint = "http://localhost:4317"
otel_capture_message_content = false
otel_message_content_max_messages = 64
otel_message_content_max_bytes = 16384
mcp_config = "/path/to/mcp.json"
credential_store = "file" # memory, keychain, or file
credential_dir = "/path/to/private/credentials"
[acp.review]
command = "review-agent"
args = ["acp"]
permissions = "deny" # deny (default) or cancel
# Override only the Kit executable/base argv; Kit appends its runtime flags.
[acp.kit]
command = "kit"
args = ["acp"]
[subagent]
harness = "acp.review"root, provider, model, and reasoning_effort apply to runtime commands. Credential settings also apply to authentication commands. --reasoning-effort low|medium|high overrides TOML; --reasoning-effort default removes Kit's explicit selection and preserves provider defaults (including OPENROUTER_REASONING_EFFORT). a2a supplies the shared HTTP listen address for
serve and tui. otel_endpoint enables OTLP/gRPC trace export for AgentKit's
GenAI spans. Use a collector endpoint such as http://localhost:4317 without a
/v1/traces suffix. The endpoint can also be set with --otel-endpoint or
OTEL_EXPORTER_OTLP_ENDPOINT; CLI values take precedence over TOML, which takes
precedence over the environment. Message capture is off by default because prompts,
tool arguments, outputs, file content, and compaction summaries can contain secrets.
Kit resolves --otel-capture-message-content BOOL,
otel_capture_message_content, and
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT in that order and requires the
environment value to be true or false. The message and byte bounds can be set by
CLI or TOML and default to 64 messages and 16384 UTF-8 bytes per input or output
attribute. Enabling capture configures both the gen_ai.input.messages and
gen_ai.output.messages structured message arrays. Input capture keeps the newest
bounded tail; output capture keeps the bounded head. Content that exceeds the byte
budget is represented by a structured truncation entry, so each array remains valid
JSON and truncation never splits UTF-8. Capture applies to main agents, ACP sessions,
nested in-process agents, and compaction summarizer prompts and outputs. Kit forwards
the fully resolved settings, including an explicit false, to its TUI server and
nested acp.kit children. The OTLP subscriber exports only AgentKit loop/MCP
semantic targets and omits source location, thread, tracing target, and span
busy/idle metadata. ACP profiles are trusted, strict command/args argv
configurations: Kit does not invoke a shell and always sets the child cwd to the
runtime root. Multiple names may be configured. [subagent].harness selects the
default; references must use the fully qualified acp.<name> form. When
omitted, acp.kit runs the current executable with acp as its base argv. An
explicit [acp.kit] overrides that executable/base argv. In both cases Kit then
appends root, provider, model, the resolved reasoning effort, persistent session, resume, MCP, credential, and inherited
depth flags, and the profile remains eligible for isolated Kit transcript fork
fallback. Other profiles remain literal generic ACP argv. Configured child
processes inherit Kit's environment except for resolved OpenRouter credentials:
acp.kit receives the key as a child-only OPENROUTER_API_KEY override, while
generic ACP profiles have OPENROUTER_API_KEY removed.
Missing config files are ignored; unreadable or invalid files and unknown selected
harness references produce an error rather than being silently discarded.
Any ACP v1 agent that speaks newline-delimited JSON-RPC over stdio can be a
profile. Put only the executable in command and each argument in args; Kit
spawns it directly, so shell quoting, pipes, environment assignments, and
compound commands do not work. Use an executable on PATH for a portable
configuration, or an absolute path for a machine-specific one. The agent must
keep stdout protocol-only, may log to stderr, and must support initialize,
session/new, and session/prompt. Kit sets its cwd to the runtime root and
inherits the parent environment.
For example, these profiles pin the npm adapters while leaving the executable lookup portable:
[acp.claude]
command = "npx"
args = ["-y", "@agentclientprotocol/claude-agent-acp@0.63.0"]
permissions = "deny"
[acp.codex]
command = "npx"
args = ["-y", "@agentclientprotocol/codex-acp@1.4.0"]
[acp.cursor]
command = "cursor-agent"
args = ["acp"]
[subagent]
harness = "acp.claude"Install Node.js for the npm-backed profiles and install the
Cursor CLI for cursor-agent.
Authenticate each agent before starting Kit (claude auth login, codex login,
or cursor-agent login respectively); Kit does not initiate a generic agent's
ACP authentication flow. A generic child's permission request is handled by
the profile's fail-closed permissions policy: deny by default, or cancel.
Configure any additional non-interactive policy in the agent itself, with the
same care as for running that agent directly.
This compatibility snapshot records only the capabilities relevant to Kit,
based on each listed release's initialize response as inspected on 2026-08-16.
It is not a claim of full feature parity, and a different release may advertise
different capabilities.
| Agent | Version inspected | Profile argv | New session and prompt | Advertises session/fork |
Kit fork |
|---|---|---|---|---|---|
| Claude Agent ACP | 0.63.0 (2026-07-27) | npx -y @agentclientprotocol/claude-agent-acp@0.63.0 |
Yes | Yes | Native ACP fork |
| Codex ACP | 1.4.0 (2026-08-16) | npx -y @agentclientprotocol/codex-acp@1.4.0 |
Yes | No | Unsupported for a generic profile |
| Cursor CLI ACP | 2026.08.04-aaa8809 (checked 2026-08-16) | cursor-agent acp |
Yes | No | Unsupported for a generic profile |
Kit decides from the initialization response at runtime rather than from this
table. If a generic agent does not advertise session/fork, ordinary subagent
and prompt calls still work, but fork returns an explicit unsupported error.
Only acp.kit has Kit's isolated transcript fallback.
Nested ACP permission policy is configured only in these trusted local profiles.
The default permissions = "deny" selects reject_always, or reject_once when
that is the only rejection offered; if the child offers no rejection, Kit
cancels the request. permissions = "cancel" always cancels. Kit never selects
an allow option: ACP permission requests contain no trustworthy, machine-verifiable
scope that could safely support unattended approval.
Kit loads validated Agent Plugin packages from local directories and SHA-256-pinned ZIP, tar.gz, or tar URLs. Plugin skills join the existing activate_skill catalog, and supported plugin MCP declarations work without an explicit MCP JSON file. Kit runs plugin stdio servers and connects streamable-http servers; deprecated sse entries are skipped with a stderr diagnostic. An explicit --mcp-config or mcp_config file overlays plugins by server name and remains live-reloadable. See Agent Plugins for placeholders, data directories, collisions, precedence, and security details.
MCP servers can come from configured Agent Plugins, an explicit JSON file supplied
with --mcp-config or mcp_config, or both. Kit does not scan for other MCP
configuration automatically, and plugin-only operation needs no MCP JSON file. An
explicit file uses this format:
{
"mcpServers": {
"local": { "command": "my-mcp-server", "args": ["--stdio"] },
"linear": {
"url": "https://mcp.example.com/mcp",
"description": "Issues and project management"
}
}
}cargo run -- tui --root /path/to/project --mcp-config /path/to/mcp.jsonKit validates and registers the combined MCP configuration at startup and begins
connecting every server in the background with stored credentials only. Interactive
OAuth never starts implicitly. Kit reloads an explicit file before each tool_search
and auth call; searches wait for newly added or changed servers to settle before
ranking connected tools globally. Explicit entries override same-named plugin entries.
Removing an override restores the plugin server, and duplicate supported server names
across plugins are startup errors. Descriptions should name concrete capabilities and
likely search terms. The exact query mcp returns a compact server-status list without
tool schemas and reports if the 32 KiB response cap omits tail entries.
Protected remote servers report authentication_required from their HTTP Bearer
challenge; this applies to plugin and config-declared Streamable HTTP servers.
Explicit OAuth configuration is optional and only supplies client or scope overrides.
The agent calls auth only when needed and gives the returned URL to the user.
Completing that browser flow updates the catalog and notifies the originating ACP
session, which resumes automatically without requiring a manual follow-up search.
Interactive OAuth is available in tui, serve, and acp, but not the one-shot
prompt command. Static bearerToken and Authorization headers remain
authoritative and are never silently replaced with inferred OAuth.
Runlet schedules independent top-level calls concurrently, including effectful
tool and subagent calls. Source order does not sequence them. An ordinary data
reference creates a dependency, but when later work must wait for a prerequisite
whose value it does not consume, express that control ordering explicitly with
after prerequisite { return tool(...) }:
prepared = shell({ command: "./prepare-workspace" })
published = after prepared {
return shell({ command: "./publish-workspace" })
}
return published
Calls lexically created inside the after block start only after the prerequisite
succeeds. Without a data dependency or after edge, adjacent tool or subagent
calls may overlap.
The model can detach an entire compose program from its turn with the optional
top-level background argument. background: true starts it in the background;
background: 60 runs it in the foreground for up to 60 seconds and then detaches
it if it is still running. false or omission keeps normal foreground behavior.
The delay must be an integer from 1 through 86,400 seconds.
Press Command+B in the TUI to move the newest running foreground top-level
compose call into the background. This shortcut requires a terminal that reports
the Command key through the Kitty keyboard protocol; there is no control-key equivalent. Detached calls
remain visible and selectable in the TUI runtime graph. Interrupting the originating
turn does not stop them. The model receives each detached call's ID
and can cancel it with close({ call_id: "call_..." }); the selected running
background call can also be killed with Ctrl+K in the TUI. Completion and
cancellation are delivered through the normal background-result lifecycle. Detached
calls are scoped to the live Kit session/process; closing it does not create durable
external jobs.
Nested agents are parent-owned named ACP subprocesses. They are ordinary Runlet values rather than background tasks:
first = subagent({ prompt: "Inspect the parser" })
second = prompt({ subagent: first, prompt: "Now propose the smallest fix" })
branch = fork({ subagent: second, prompt: "Try the alternative design" })
active = subagents({})
closed = close({ id: branch.id })
return { main: second.output, alternative: branch.output, active, closed }
The optional harness argument overrides the user's configured harness preference
with the selected value. Default to omitting it.
subagents({}) lists the current reusable handles, including idle completed sessions, and reports an initial turn still in progress as { id, status: "starting" }. Starting entries can be closed by ID but cannot be prompted or forked until the initial subagent call returns a complete handle. close(handle) or close({ id: handle.id }) explicitly terminates one. Closing removes the handle from the parent registry and releases its capacity. For native-fork siblings sharing a process, the harness must advertise ACP session/close; otherwise Kit reports that it cannot close only one sibling. All retained subagents are terminated when their owning parent session closes. A parent session can retain at most 120 subagents.
Each turn-producing call returns { id, output, generation }. By default, output remains the
agent's text. When a turn also emits non-text agent content, tool calls, tool-call
updates, or plans, the value gains an optional updates field containing the
ordered ACP update objects and a truncated flag. Text-only turns keep the
existing three-field shape. Capture is limited to 64 updates and 64 KiB of
serialized update data per turn; an oversized or excess update is omitted and
sets truncated to true. Agent thoughts, user-message echoes, usage, modes,
commands, configuration, and session metadata are not exposed. To require
structured output, pass an output_schema JSON Schema to any turn-producing
call:
review = subagent({
harness: "acp.claude",
prompt: "Review the proposed change",
output_schema: {
type: "object",
properties: {
approved: { type: "boolean" },
reason: { type: "string" }
},
required: ["approved", "reason"],
additionalProperties: false
}
})
return if review.output.approved {
return { reason: review.output.reason }
} else {
return fail("REJECTED", review.output.reason)
}
Kit appends the schema to the child prompt, requires the response to be a bare
JSON value, parses it, and validates it before returning the turn. Invalid schemas
are rejected before dispatch. If a completed response is malformed or does not
match the schema, its raw text is returned as output and the session generation
still advances, allowing a repair prompt in the same session. The schema may be
changed on a later prompt or fork. In the current pinned Runlet version,
output is dynamically typed: its
fields can be used in control flow as above, but Runlet does not statically check
field names or types against the per-call schema. Runtime validation determines
whether output is the parsed value or the raw-text fallback.
The optional harness is used only by subagent; prompt and fork infer the original profile from the
parent-owned session. Parent IDs remain distinct from ACP child session IDs.
fork uses native session/fork only when initialization advertised that
capability; sibling fork sessions can prompt concurrently while each individual
session remains serialized. Otherwise the acp.kit profile creates an isolated durable transcript/process
fallback, whether its executable/base argv is implicit or configured; other profiles return an explicit
unsupported error. A stale generation is rejected, which makes concurrent reuse
explicit in the Runlet dataflow. The pinned Runlet/AgentKit bridge exposes
one JSON argument per hidden tool, so prompt and fork use the small object
form above instead of two positional arguments. Built-in Kit children inherit
root, provider, model, explicit MCP configuration, credential storage, cancellation,
and nesting depth, and reload configured plugins from the same global Kit config.
Generic profiles do not inherit Kit plugin declarations or MCP configuration; they
receive standard ACP initialization/new-session/prompt
traffic and run with the root as cwd. Sessions remain reusable only for the
lifetime of the parent Kit process. Built-in Kit transcripts remain on disk
afterward; persistence for generic harnesses is agent-defined. A max-token stop
returns the partial output as a successful, reusable completed turn; cancellation,
refusal, protocol errors, and other uncertain stops retire the child instead. Permission requests from a
headless child are conservatively cancelled so they cannot hang. Nested built-in
children use kit acp and never start A2A listeners.
OpenAI, OpenRouter, Speakeasy, and MCP use one shared credential backend, selected with
--credential-store or credential_store. The default memory backend is
process-local, so credentials disappear at exit and are not available to the
TUI server process or nested Kit children. Use persistent storage when those
processes need the same credentials. Standalone provider login rejects memory:
# Operating-system credential store
kit auth login openai --credential-store keychain
kit auth login speakeasy --credential-store keychain
kit tui --mcp-config mcp.json --credential-store keychain
# Plain JSON files in an explicit private directory
kit auth login openai --credential-store file \
--credential-dir ~/.local/share/kit/credentials
kit tui --mcp-config mcp.json --credential-store file \
--credential-dir ~/.local/share/kit/credentialsThe file backend creates a 0700 directory and 0600 credential files on
Unix, rejects unsafe paths and permissions, and does not encrypt tokens. Persistent OpenAI and Speakeasy credentials are restored when Kit starts. MCP credentials are
restored and refreshed when a matching search initializes the server, so prompt
can use credentials created earlier by
tui or serve without starting an interactive browser flow. MCP refreshes are
serialized across processes sharing that backend, with credentials reloaded only
after the lock is held so rotating refresh tokens are not reused.
Build and sign the final release binary with a stable certificate and identifier before using Keychain storage:
scripts/sign-release.sh
target/release/kit tui --mcp-config mcp.json \
--credential-store keychainOn macOS, .cargo/config.toml routes cargo run through a runner that signs the
fresh debug binary before executing it. Set KIT_CODESIGN_IDENTITY, or put the
certificate name in the gitignored .kit-codesign-identity file. Both paths use
the stable identifier com.danielkov.kit, overridable with
KIT_CODESIGN_IDENTIFIER. A changed identity, a missing certificate, or a
locked Keychain may prompt again. cargo install
does not run the runner; sign its installed binary separately. Apple Development
certificates are suitable for local use. Public distribution and notarization
require a Developer ID Application certificate.
tui starts a kit serve child and drives it over ACP, so the client sees the
same protocol any editor would.
While a compose call runs, the right-hand pane draws its runtime graph: the
Runlet program parsed into its call, loop, branch, and boundary structure, with
each nested shell, edit, subagent, prompt, fork, subagents, close, A2A, and MCP meta-tool dispatch shown live under the node
that most likely issued it, including concurrent fan-out and failures. Child
call lifecycle is exact; when the same tool appears in several places, node
attribution is a stable heuristic. Nested-call
lifecycle reaches the client on stderr as marked JSON lines, enabled for the
child process with KIT_RUNTIME_EVENTS=1; other ACP hosts never see them.
| Key | Action |
|---|---|
⏎ |
send |
/new |
start a fresh persisted session (/new prompt sends its first prompt) |
/compact |
compact context now (/compact prompt starts the next turn with prompt) |
/effort |
choose default, low, medium, or high; Tab toggles saving the default |
/model |
open the searchable provider-grouped model picker |
/model name |
switch immediately to the closest catalog match |
⇧⏎, ⌥⏎, ^j |
newline |
esc |
interrupt the running turn |
⌘b |
move the newest running foreground compose call to the background |
^c |
interrupt, or quit when idle |
⌥←/→, ^a/^e, home/end |
word and line movement |
⌥⌫, ^w |
delete the previous word |
⌘⌫, ^u / ^k |
delete to line start / end |
↑/↓ |
move between prompt lines, then browse history |
⇧↑/⇧↓, pgup/pgdn, wheel |
scroll the transcript |
^g / ^l / ^t |
runtime graph / agent log / reasoning |
click a tool card, ^o |
fold its raw output open or shut |
⌘ and ⇧⏎ need a terminal that speaks the Kitty keyboard protocol (Ghostty,
Kitty, WezTerm, recent iTerm2); the control-key equivalents work everywhere.
If the agent dies before the session opens — a taken A2A port, a missing root, no credentials — the client exits with that agent's own last diagnostic rather than waiting on a handshake that will never finish.
/new clears the visible transcript but leaves the prior persisted session
intact and resumable. /model switches the main agent and compactor in the same
live ACP session at the next safe turn boundary; it does not rewrite transcript
history or restart the child process. The picker groups models by provider and
ranks fuzzy matches as you type. Press tab before confirming to also replace the provider
and model defaults in the active user's ~/.kit/config.toml; this safely
rewrites the TOML but does not preserve its formatting or comments. Otherwise, the
choice lasts only for this live ACP session. Catalog discovery is bounded and
falls back to a small built-in list when a provider catalog is unavailable.
/compact ends after compaction when used alone; text after
the command is retained as a new user message and starts the next model turn.
Slash commands are recognized only as exact leading
tokens. Unknown slash commands are sent to the model unchanged. Pasting
multi-line text puts line breaks in the prompt instead of sending it.
The client asks the terminal to bracket pastes; where that is unavailable a
paste arrives as a key burst, and a return inside such a burst is read as a
line break rather than a send. Either way the whole paste is applied in one
redraw.
Dragging local PNG, JPEG, GIF, WebP, WAV, or MP3 files into the terminal adds
them as [Image #N] or [Audio #N] prompt attachments. Kit recognizes a drop
only when every pasted token is a supported file; otherwise it preserves the
whole paste as text. A prompt can contain at most 8 attachments, 10 MiB each
and 20 MiB total. Kit sends the bytes through OpenRouter or OpenAI subscription
while retaining canonical local file:// links in model-facing text. Model
modality support varies. Video, terminal image rendering, and audio playback are
not supported, and Kit never displays base64 or data: URLs. See
the TUI guide.
The root is a working directory, not a sandbox. Shell commands can access the host. Edits are atomic per file but multi-file changes are not transactional. Subagents are child ACP processes, use the same root as their working directory, and are bounded to depth two.