Skip to content

feat(yoke): add Kiro CLI as a first-class agent runtime via ACP#1

Closed
hmkim wants to merge 5 commits into
mainfrom
feat/kiro-cli-integration
Closed

feat(yoke): add Kiro CLI as a first-class agent runtime via ACP#1
hmkim wants to merge 5 commits into
mainfrom
feat/kiro-cli-integration

Conversation

@hmkim

@hmkim hmkim commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds AWS Kiro CLI as a first-class agent runtime in Clay, alongside Claude Code and Codex, through the YOKE adapter layer. Users pick the vendor, create a session, chat, approve tools, and switch models — with no user-visible difference between runtimes.

Kiro exposes the Agent Client Protocol (ACP) via kiro-cli acp — standard JSON-RPC 2.0 over stdio, the same editor-agnostic protocol used by Zed. This mirrors the transport strategy already used for codex app-server.

Clay session (vendor=kiro)
  -> YOKE Kiro Adapter   (lib/yoke/adapters/kiro.js)
  -> KiroAcpServer       (lib/yoke/kiro-acp-server.js)  spawn `kiro-cli acp`
  -> kiro-cli binary     (JSON-RPC 2.0 over stdio)

What's included

New files

  • lib/yoke/kiro-acp-server.js — child-process JSON-RPC transport
  • lib/yoke/adapters/kiro.js — YOKE adapter (dynamic model catalog, session lifecycle, session/update event flattening, permission routing, resume, abort)
  • lib/kiro-defaults.js — single source of truth for Kiro defaults
  • lib/public/kiro-avatar.svg — branded avatar
  • docs/guides/KIRO-INTEGRATION.md + EN/KO summaries

Wiring (mirrors every codex touch-point)

  • yoke/index.js — factory switch, auth (kiro-cli whoami), install detection, createAdapters
  • sdk-bridge.jsdetectInstalledVendors, login command, KIRO adapterOptions, neutral interrupt message
  • sdk-message-processor.js, project-notifications.js — auth titles
  • project-sessions.js — GUI-only session mode for kiro
  • client UI: vendor toggle, new-session buttons (install-gated), effort levels, avatar/name maps

Key design decisions

  • Dynamic model catalog (like Claude, unlike Codex's hardcoded list): init() runs kiro-cli chat --list-models --format json, filtering out [Internal]/[Deprecated] entries; default is the auto router.
  • Permission name recovery: session/request_permission carries only { toolCallId, title }, so the adapter caches kind + rawInput from the preceding tool_call notification to pass a canonical tool name (Bash, Edit, ...) to canUseTool. Without this, Clay's permission whitelist matching would break.
  • Split tool output (fix commit): Kiro emits tool output across two tool_call_update events (interim content, then status: completed with the output in rawOutput). The adapter accumulates content per toolCallId and falls back to rawOutput so tool_result is never empty.
  • GUI-only sessions, same as Codex; per-project adapter scoped to a cwd/slug.

Testing

Verified end-to-end against the live kiro-cli 2.7.0 binary, and on a real device (iPhone via Tailscale):

  • Init discovers 15 curated models; default auto
  • Text streams in real time; result emitted with usage
  • Model switching works (session/set_model; confirmed opus-4.8 / haiku-4.5 on device)
  • Tool approval routes through canUseTool with canonical name Bash + {command}; approve and deny paths verified
  • tool_result shows real command output (echo hello-kiro-42)
  • Abort interrupts the turn cleanly
  • Registers alongside claude/codex in the daemon adapter path

Commits

  • feat(yoke): add Kiro CLI adapter via Agent Client Protocol
  • fix(yoke): populate Kiro tool_result content from split ACP updates

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR successfully integrates Kiro CLI as a first-class agent runtime via the Agent Client Protocol (ACP). The implementation follows good patterns mirroring existing Claude and Codex adapters, includes comprehensive error handling, and maintains security best practices through tool whitelisting and permission controls.

Key Changes:

  • New KiroAcpServer transport layer for JSON-RPC 2.0 communication
  • KiroAdapter implementing the YOKE interface with dynamic model catalog, session lifecycle management, and permission routing
  • Integration wiring throughout the codebase (auth, vendor detection, UI controls)
  • Comprehensive documentation

Findings:

  • 1 logic error identified in binary path resolution

The changes are well-tested according to the PR description, with verification against kiro-cli 2.7.0 including model discovery, streaming, tool approval, and session management.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

var execFileSync = require("child_process").execFileSync;
var out = process.platform === "win32"
? execFileSync("where", [binName], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] })
: execFileSync("which", ["kiro-cli"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 Logic Error: The where command on Windows receives binName as argument but which on Unix receives hardcoded "kiro-cli" string. This inconsistency causes the Windows binary name (possibly "kiro-cli.exe") to be ignored on Unix systems, preventing discovery of the correct binary when binName is set to a different value than "kiro-cli".

Suggested change
: execFileSync("which", ["kiro-cli"], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });
: execFileSync("which", [binName], { timeout: 3000, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] });

chadbyte and others added 5 commits July 20, 2026 15:20
* feat(auth): solo auto-login safety net for absorbed single-user (phase 1)

Groundwork for folding single-user mode into multi-user. When a multi-user
instance has exactly one user with no PIN, authenticate them automatically
instead of showing a login wall, preserving the frictionless single-user
onramp.

Central change: getMultiUserFromReq() falls back to users.getSoloNoPinUser()
when there's no valid token, so every HTTP and WS auth gate (all route
through this) is covered by one path. Disabled the moment a PIN is set or a
second user is added.

Inert today: only triggers when multiUser is true (single-user deploys are
unaffected until the later migration phase flips them).

* feat(auth): auto-migrate legacy single-user deploys to multi-user (phase 2)

On daemon boot, detect a legacy single-user deploy and fold it into the
multi-user model with zero manual steps:
- provision one "admin" user that inherits the single-user PIN hash (same
  PIN still logs in), profile.json, and settings (chatLayout,
  autoContinue, matesEnabled, terminalFont, deletedBuiltinKeys,
  mateOnboardingShown)
- move legacy flat mates into the per-user mates dir
- backfill ownerId onto legacy sessions so they belong to the user
- flip multiUser = true

Runs before projects/mates/sessions load. Heavily guarded: backs up
users.json/daemon.json/profile.json first, is idempotent (config marker),
tolerates partial failure per step, and is a no-op on fresh installs and
already-multi-user deploys. Combined with the phase-1 solo auto-login, a
no-PIN single-user deploy upgrades with no login wall.

* feat(auth): default new installs to multi-user, drop the mode prompt (phase 3)

There is no separate single-user runtime anymore. Every deploy runs in the
(general) multi-user model:

- Remove the "Just me (single user) / Multiple users" setup question. Fresh
  installs go straight to multi-user; OS-user isolation stays an opt-in
  (offered on Linux at setup, toggleable later).
- daemon boot calls ensureMultiUser(): migrate a legacy single-user deploy,
  or provision a default no-PIN "admin" on a fresh one. With phase-1 solo
  auto-login, a solo user gets in with no account creation and no login wall.
- Flip every mode fallback from "single" to "multi".

Solo onramp is unchanged in feel (npx clay-server -> just works); it's now a
one-user multi-user deploy under the hood.

* docs: handoff for single-user absorption phases 4-6

Capture the plan, what's done (phases 1-3 + verification), and the remaining
cleanup (phase 4 settings dual-path, phase 5 !isMultiUser branch removal,
phase 6 flag/dead-code removal) with file:line targets and watch-outs.
Add the GPT-5.6 model family (gpt-5.6-sol, gpt-5.6-terra,
gpt-5.6-luna) to the Codex adapter's model list and make
gpt-5.6-terra the default (5.5-class quality at lower cost).

Register the 5.6 context window (272000) so the context panel
reports usage correctly instead of falling back to 200000.

Verified the bundled @openai/codex 0.144.4 binary already
recognizes all three model ids.
Integrate AWS Kiro CLI as a first-class agent runtime alongside Claude
Code and Codex, driven through `kiro-cli acp` (Agent Client Protocol,
JSON-RPC 2.0 over stdio) — mirroring the codex app-server transport.

New:
- lib/yoke/kiro-acp-server.js: child-process JSON-RPC transport
- lib/yoke/adapters/kiro.js: YOKE adapter (dynamic model catalog,
  session lifecycle, session/update event flattening, permission
  routing, resume, abort)
- lib/kiro-defaults.js: single source of truth for Kiro defaults
- lib/public/kiro-avatar.svg: branded avatar
- docs/guides/KIRO-INTEGRATION*.md: protocol reference + EN/KO summaries

Wiring (mirrors every codex touch-point):
- yoke/index.js: factory switch, auth (kiro-cli whoami), install
  detection, createAdapters
- sdk-bridge.js: detectInstalledVendors, login command, KIRO
  adapterOptions, neutral interrupt message
- sdk-message-processor.js, project-notifications.js: auth titles
- project-sessions.js: GUI-only session mode for kiro
- client UI: vendor toggle, new-session buttons (install-gated),
  effort levels, avatar/name maps

Permission requests carry only { toolCallId, title }, so the adapter
caches kind/rawInput from the preceding tool_call notification to pass a
canonical tool name to canUseTool, keeping the permission whitelist
functional.
Kiro splits tool output across two tool_call_update events: an interim one
carrying `content` (no status) and a later `status: "completed"` one whose
`content` is empty (the output lives in `rawOutput`). The adapter only read
the completed event's content, so tool_result bubbles were empty.

Accumulate content chunks per toolCallId across events and fall back to the
structured `rawOutput` (stdout/stderr) at completion, so tool_result is never
empty. Verified against kiro-cli 2.7.0: `echo hello-kiro-42` now yields the
real command output instead of a blank result.
@hmkim
hmkim force-pushed the feat/kiro-cli-integration branch from a317658 to d81000e Compare July 21, 2026 04:47
@hmkim

hmkim commented Jul 21, 2026

Copy link
Copy Markdown
Owner Author

Superseded by the upstream PR chadbyte#385 (rebased on latest main). Closing to avoid confusion — the work continues there.

@hmkim hmkim closed this Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants