Skip to content

feat: add Kiro CLI ACP provider#4087

Closed
Adithya-Sakaray wants to merge 5 commits into
pingdotgg:mainfrom
Adithya-Sakaray:cursor/add-kiro-provider-0893
Closed

feat: add Kiro CLI ACP provider#4087
Adithya-Sakaray wants to merge 5 commits into
pingdotgg:mainfrom
Adithya-Sakaray:cursor/add-kiro-provider-0893

Conversation

@Adithya-Sakaray

@Adithya-Sakaray Adithya-Sakaray commented Jul 17, 2026

Copy link
Copy Markdown

What Changed

  • Adds Kiro as a first-party ACP provider across contracts, server runtime, settings, and provider/model pickers.
  • Reuses the existing hardened ACP lifecycle (via the shared Grok adapter path) for streaming, permissions, cancellation, resume, model switching, and session shutdown — instead of a third copy of that state machine.
  • Makes ACP authenticate optional for agents such as Kiro that authenticate outside the protocol (kiro-cli login / env).
  • Scopes mock ACP cancellation to the active prompt so follow-up turns stay correct in tests.
  • Adds focused Kiro coverage (adapter, provider probe/support).

Why

Kiro CLI exposes a standards-compliant JSON-RPC ACP server through kiro-cli acp. Wiring it through the existing provider driver SPI keeps T3 Code’s provider-instance and orchestration behavior consistent while avoiding another forked ACP session implementation.

UI Changes

Settings — Kiro provider

Kiro appears under Settings → Providers with display name, accent color, environment variables, binary path, and discovered models.

Kiro provider settings

Model picker — Kiro models

Kiro is available in the provider rail; its models show in the composer model search/picker.

Kiro models in the composer model picker

Validation

  • vp check (0 errors)
  • vp run typecheck
  • Kiro tests: 7 passed (KiroAdapter, KiroProvider, KiroAcpSupport)
  • Grok regression after shared-adapter generalization: 32 passed (3 CLI probes skipped)
  • Live desktop smoke: settings + model picker screenshots above

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Add Kiro CLI as an ACP provider with full session, status, and text generation support

  • Introduces a KiroDriver, KiroAdapter, and KiroAcpSupport layer that connects to the Kiro CLI via the kiro-cli acp command, following the same ACP lifecycle used by the Grok provider.
  • Adds KiroSettings to the contracts layer (enabled, binaryPath, customModels) and registers kiro in provider maps, driver registry, and the web settings/provider picker UI.
  • checkKiroProviderStatus probes the CLI with --version, discovers available models via ACP, and reports error/warning/ready status with version advisory enrichment.
  • makeGrokAdapter is generalized to accept injected provider, providerDisplayName, makeAcpRuntime, and resolveModelId hooks, enabling Kiro to reuse the shared adapter and text generation logic without duplication.
  • Adds idle-turn watchdog to makeGrokAdapter: if no ACP activity occurs within the configured timeout, a runtime.error event is emitted and the active turn is cancelled.
  • Adds ACP elicitation handling: form-mode elicitations emit user-input.requested/user-input.resolved events; non-form modes are immediately declined.
  • Optional user input questions (new optional field on UserInputQuestion) no longer block submission on web and mobile; unanswered optional questions are omitted from answers.
  • Assistant item IDs now include a per-runtime nonce to prevent collisions across resumed sessions or multiple concurrent runtimes.

Macroscope summarized 983df76.

Add Kiro as a first-party provider over the existing ACP lifecycle so
streaming, permissions, cancellation, resume, and model switching stay
shared with Grok rather than forked into a third session state machine.

- Wire Kiro driver/settings/models and optional ACP authenticate skip
- Reuse hardened ACP adapter path with Kiro spawn (`kiro-cli acp`)
- Scope mock ACP cancellation to the active prompt for follow-up turns
- Add focused Kiro unit/integration coverage
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 97d52df7-2be1-4ca8-be03-c9d3c993d78e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Jul 17, 2026
Comment thread apps/server/src/provider/Layers/GrokAdapter.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

3 blocking correctness issues found. New provider feature (Kiro CLI) with substantial new code and modifications to core adapter logic. A high-severity unresolved comment identifies a bug where the idle watchdog can incorrectly cancel legitimate user interactions waiting for approval or elicitation responses.

You can customize Macroscope's approvability policy. Learn more.

@Adithya-Sakaray

Copy link
Copy Markdown
Author

Addressed both review findings:

  1. GrokAdapter: only call resolveModelId when boundModelId is defined (no more advertising grok-build when ACP reports no model).
  2. resolveKiroAcpModelId: keep selectable default as a real slug so selecting it after a custom model issues session/set_model.

Kiro + GrokAdapter tests: 33 passed.

- AcpSessionRuntime: scope assistant item ids with a per-runtime nonce.
  A resumed session (session/load) reuses the ACP session id but a new
  runtime resets the segment counter, so assistant item ids collided with
  a prior turn's message. The upsert kept the stale created_at, sorting the
  resumed reply before the new prompt and making it invisible.
- Shared ACP adapter: handle session/elicitation (form mode) via the
  existing user-input flow; advertise elicitation.form capability for Kiro.
  url-mode elicitations are declined (not renderable).
- build-desktop-artifact: allow overriding appId/productName via
  T3CODE_DESKTOP_APP_ID / T3CODE_DESKTOP_PRODUCT_NAME for side-by-side builds.
- Tests: resume-safety regression + updated cursor item-id assertions.
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Jul 19, 2026
Comment thread apps/server/src/provider/acp/AcpElicitation.ts
Comment thread apps/server/src/provider/acp/AcpElicitation.ts
- coerceAnswer: only accept recognized boolean representations
  (yes/true -> true, no/false -> false); omit the key for unrecognized
  input instead of silently defaulting to false.
- UserInputQuestion: add an optional flag (default false). Mark
  non-required elicitation properties optional so the form can be
  submitted without inventing values; unanswered optional questions are
  omitted from the response.
- Honor optional questions in the web and mobile submission gates.
- Tests: AcpElicitation coercion/optional coverage + web optional-field cases.
Comment on lines +165 to +171
if (property.type === "number" || property.type === "integer") {
const parsed = Number(value);
if (!Number.isFinite(parsed)) {
return undefined;
}
return property.type === "integer" ? Math.trunc(parsed) : parsed;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium acp/AcpElicitation.ts:165

For an integer property, coerceAnswer accepts fractional input like 1.9 and applies Math.trunc, silently truncating it to 1 in the response. A fractional value does not satisfy an integer schema, so it should be rejected (return undefined) rather than rounded toward zero. Consider checking that parsed is already an integer before returning it for type === "integer".

  if (property.type === "number" || property.type === "integer") {
    const parsed = Number(value);
    if (!Number.isFinite(parsed)) {
      return undefined;
    }
-    return property.type === "integer" ? Math.trunc(parsed) : parsed;
+    if (property.type === "integer" && !Number.isInteger(parsed)) {
+      return undefined;
+    }
+    return parsed;
  }
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/AcpElicitation.ts around lines 165-171:

For an `integer` property, `coerceAnswer` accepts fractional input like `1.9` and applies `Math.trunc`, silently truncating it to `1` in the response. A fractional value does not satisfy an integer schema, so it should be rejected (return `undefined`) rather than rounded toward zero. Consider checking that `parsed` is already an integer before returning it for `type === "integer"`.

return [];
}
const schema = formRequestedSchema(request);
const header = truncateHeader(trimmed(request.message) ?? schema.title ?? "Input requested");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium acp/AcpElicitation.ts:96

extractElicitationQuestions returns options: [] for free-form string/number properties, but the mobile parseUserInputQuestions rejects every question with an empty options array. A Kiro elicitation containing only free-form fields is dropped entirely on mobile, leaving the agent paused with no visible prompt; mixed forms silently omit those fields as well. Consider providing a placeholder option (e.g. a free-text entry) so the question survives validation on mobile.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/AcpElicitation.ts around line 96:

`extractElicitationQuestions` returns `options: []` for free-form string/number properties, but the mobile `parseUserInputQuestions` rejects every question with an empty `options` array. A Kiro elicitation containing only free-form fields is dropped entirely on mobile, leaving the agent paused with no visible prompt; mixed forms silently omit those fields as well. Consider providing a placeholder option (e.g. a free-text entry) so the question survives validation on mobile.

- Idle turn watchdog (shared by Kiro/Grok/Cursor): when a prompt is in
  flight but the agent produces no ACP activity for longer than
  T3CODE_ACP_TURN_IDLE_TIMEOUT_MS (default 240s, 0 disables), surface a
  runtime error and cancel the turn instead of spinning forever. Fixes
  the Kiro hang where a shell tool never returned a result.
- Empty-completion notice: when a turn completes with no assistant
  content and no tool activity (e.g. Grok ending the turn after a 402
  usage-limit inference failure), emit a runtime.warning so the user
  sees why instead of a silent stop.
- Track per-turn output + last activity on the session context.
- Tests + a T3_ACP_EMIT_EMPTY_COMPLETION mock-agent flag.
* spinning forever. Generous by default so genuinely long-running tools are not
* killed; override with `T3CODE_ACP_TURN_IDLE_TIMEOUT_MS` (0 disables).
*/
const DEFAULT_TURN_IDLE_TIMEOUT_MS = 240_000;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟠 High Layers/GrokAdapter.ts:91

The turn watchdog cancels legitimate user-interaction waits after 240 seconds. When a prompt is blocked on user approval or elicitation, there may be no ACP activity while the user decides, but the watchdog still fires, interrupts the turn, and rejects the later response as stale. The idle timeout does not exclude or pause for nonempty pendingApprovals/pendingUserInputs, so any user interaction lasting longer than DEFAULT_TURN_IDLE_TIMEOUT_MS is wrongly terminated. Consider pausing or resetting the idle watchdog while user-interaction callbacks are pending, or excluding those states from the watchdog check.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/GrokAdapter.ts around line 91:

The turn watchdog cancels legitimate user-interaction waits after 240 seconds. When a prompt is blocked on user approval or elicitation, there may be no ACP activity while the user decides, but the watchdog still fires, interrupts the turn, and rejects the later response as stale. The idle timeout does not exclude or pause for nonempty `pendingApprovals`/`pendingUserInputs`, so any user interaction lasting longer than `DEFAULT_TURN_IDLE_TIMEOUT_MS` is wrongly terminated. Consider pausing or resetting the idle watchdog while user-interaction callbacks are pending, or excluding those states from the watchdog check.

@juliusmarminge

Copy link
Copy Markdown
Member

Hey, for now we're not adding new providers now. Might revisit this after #2829 has been released

@Adithya-Sakaray

Copy link
Copy Markdown
Author

Cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants