Skip to content

Improve Kiro provider catalog, credits, streaming, endpoints, and TUI integration#96

Open
garysassano wants to merge 7 commits into
tickernelz:masterfrom
garysassano:pr/kiro-provider-tui-usage
Open

Improve Kiro provider catalog, credits, streaming, endpoints, and TUI integration#96
garysassano wants to merge 7 commits into
tickernelz:masterfrom
garysassano:pr/kiro-provider-tui-usage

Conversation

@garysassano
Copy link
Copy Markdown
Contributor

@garysassano garysassano commented May 23, 2026

Preview

kiro

Highlights

Fixed thinking stream ordering

This PR fixes the thinking stream transform so Kiro reasoning is emitted before the final answer text. The previous parser could leak pre-thinking fragments or emit content in the wrong order when <thinking> tags arrived split across streaming chunks.

The new shared parser handles:

  • split <thinking> / </thinking> tags across chunks
  • quoted or backticked literal thinking tags
  • thinking models that never emit a thinking block
  • normal non-thinking models without parsing their literal tag text

Updated Kiro model catalog

The default Kiro model catalog has been refreshed against the official Kiro CLI model list: https://kiro.dev/docs/cli/models/

This includes newer Claude and open-weight models such as Opus 4.7, Opus 4.6, Sonnet 4.6, DeepSeek 3.2, MiniMax M2.5, GLM-5, MiniMax M2.1, and Qwen3 Coder Next.

The provider injects the model catalog automatically, so users no longer need to copy model definitions into their OpenCode config. Legacy/dotted/hyphenated/*-thinking/older *-1m aliases are still converted where possible for compatibility with existing configs, but they are not advertised as default models.

Added an OpenCode TUI plugin sidebar panel

OpenCode documents plugins here: https://opencode.ai/docs/plugins/ and its terminal UI as the TUI here: https://opencode.ai/docs/tui/.

This package now exposes a separate TUI plugin entrypoint at ./tui. When installed/configured in tui.jsonc, it adds a compact Kiro section to the session sidebar showing only:

  • Kiro
  • Plan: <plan>
  • Credits: <used> / <limit>

The panel mirrors the useful account/credits information from the official Kiro IDE/CLI flow while keeping the OpenCode UI quiet. It is hidden for non-Kiro sessions and reads kiro.db in readonly mode without loading access tokens, refresh tokens, client secrets, or OIDC credentials.

Matched Kiro CLI credit precision

Usage tracking now matches the official kiro-cli /usage behavior more closely:

  • reads currentUsageWithPrecision / usageLimitWithPrecision
  • stores quota columns as REAL so fractional credits are preserved
  • displays used credits with two decimals
  • preserves the fully qualified remote plan label, for example KIRO PRO+
  • stops incrementing usage locally during account selection
  • avoids stale integer snapshots overwriting newer precise remote quota values

Uses Kiro's new runtime and management endpoints

imgs

Kiro's firewall docs list the new service endpoints under runtime.<region>.kiro.dev and management.<region>.kiro.dev, while documenting q.<region>.amazonaws.com as legacy but still required until deprecation completes: https://kiro.dev/docs/cli/privacy-and-security/firewalls/

Other Changes

Auth and provider integration

  • Uses kiro as the primary provider ID while retaining a bridge for older kiro-auth config.
  • Adds OpenCode auth placeholder setup so synced Kiro CLI credentials can activate the provider flow correctly.
  • Improves IAM Identity Center support with profile ARN handling and OIDC client credential caching.
  • Carries subscription plan and precise usage through auth, Kiro CLI sync, storage, and the TUI read path.

Runtime and reasoning support

  • Keeps explicit reasoning effort support using OpenCode’s low, medium, and high effort values without advertising fake model variants.
  • Centralizes streaming thinking parsing for both SDK and legacy stream transforms.
  • Adds request logging that reflects which endpoint mode was used.

Packaging and README

  • Exposes separate package exports for the server plugin and the TUI plugin.
  • Adds the OpenTUI/Solid build step needed for dist/tui.js.
  • Updates the README to document install, auth, automatic model injection, compatibility aliases, and the TUI sidebar panel.

Verification

  • bun run format --check
  • bun run check
  • git diff --check
  • live authenticated smoke test: runtime.us-east-1.kiro.dev generation returned endpoint-ok
  • live authenticated smoke test: management.us-east-1.kiro.dev/getUsageLimits returned quota/plan data
  • live authenticated smoke test: legacy q.us-east-1.amazonaws.com generation and usage still work

Test coverage added/updated for:

  • model catalog and alias resolution
  • usage precision and plan normalization
  • account quota merge behavior
  • TUI usage snapshot/sidebar visibility logic
  • thinking stream parsing and ordering
  • Kiro runtime endpoint fallback behavior
  • package exports for the TUI entrypoint

@tickernelz
Copy link
Copy Markdown
Owner

Looks like this PR is still actively changing — I noticed another commit landed after review started.

To avoid reviewing/merging a moving target, can you mark this PR as Draft while you’re still iterating? Once the implementation is stable, please switch it back to Ready for review or comment here, and I’ll do the final merge-readiness review then.

@garysassano garysassano marked this pull request as draft May 23, 2026 05:00
@garysassano garysassano force-pushed the pr/kiro-provider-tui-usage branch from dcd8249 to 0bfd515 Compare May 24, 2026 03:08
@garysassano garysassano force-pushed the pr/kiro-provider-tui-usage branch from 0bfd515 to 6cf82d3 Compare May 24, 2026 03:12
@garysassano garysassano force-pushed the pr/kiro-provider-tui-usage branch from 853acbd to 725f3bb Compare May 24, 2026 03:23
@garysassano garysassano changed the title Improve Kiro provider catalog, usage tracking, and TUI integration Improve Kiro provider catalog, credits, streaming, and TUI integration May 24, 2026
@garysassano garysassano marked this pull request as ready for review May 24, 2026 03:31
@garysassano garysassano marked this pull request as draft May 24, 2026 04:37
@garysassano garysassano changed the title Improve Kiro provider catalog, credits, streaming, and TUI integration Improve Kiro provider catalog, credits, streaming, endpoints, and TUI integration May 24, 2026
@garysassano garysassano marked this pull request as ready for review May 24, 2026 04:44
Copy link
Copy Markdown
Owner

@tickernelz tickernelz left a comment

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Changes requested

PR scope is good, but I found one blocking streaming regression that can leak the closing </thinking> tag and answer text into reasoning output.

Critical

  • src/plugin/streaming/stream-parser.ts / src/plugin/streaming/thinking-parser.tsfindRealThinkingEndTag() only accepts </thinking> when followed by a blank line or stream end. Valid streams like <thinking>reason</thinking>Answer are not closed, so </thinking>Answer is emitted as reasoning_content instead of normal answer content.

    I reproduced locally with:

    createContentDeltaEvents('<thinking>reason</thinking>Answer', createStreamState(true))

    Actual output includes:

    { "type": "thinking_delta", "thinking": "</thinking>Answer" }

    This is a response-shape correctness regression. The parser should close on a real, unquoted/non-code-fenced </thinking> regardless of whether the model inserts a double newline after it; then separately strip optional leading newlines from the answer, as the PR already tries to do.

Warnings / follow-ups

  • src/plugin/sync/kiro-cli.ts — stale CLI sync can still overwrite a healthier/newer stored account when usage fetch succeeds. The skip guard no longer protects token fields in that case, while the later upsertAccount() writes accessToken, refreshToken, and expiresAt from the CLI snapshot. Consider preserving newer credentials and only merging quota fields when the CLI token snapshot is older.
  • src/tui-usage.ts — TUI sidebar picks the first healthy account globally by last_used, not necessarily the active account for the current session. This can show the wrong quota in multi-account setups. If OpenCode exposes active provider auth/session account metadata, wire it through; otherwise document this limitation.

Verified locally

  • bun install --frozen-lockfile
  • bun run check
  • git diff --check origin/master...HEAD
  • npm pack --dry-run

All declared checks pass after dependencies are installed.

@garysassano
Copy link
Copy Markdown
Contributor Author

  • Fixed thinking parsing so a real unquoted/non-code-fenced </thinking> closes reasoning even when answer text follows immediately, e.g. <thinking>reason</thinking>Answer.
  • Added regression coverage for immediate answer text, split end tags, CRLF stripping, and code-fenced literal tags.
  • Hardened account merging so stale CLI credential snapshots do not overwrite newer healthy stored tokens while newer quota snapshots can still merge.
  • Clarified the TUI quota limitation: it only applies when kiro.db contains multiple healthy credential rows; a single logged-in account matches the active account.

@garysassano
Copy link
Copy Markdown
Contributor Author

The TUI right panel now supports optional display fields via tui.jsonc.

Defaults stay compact/private:

  • show_account_email: false
  • show_plan: true
  • show_credits: true

Users who want to distinguish multiple stored Kiro credentials can opt in to showing the account email:

{
  "$schema": "https://opencode.ai/tui.json",
  "plugin": [
    [
      "@zhafron/opencode-kiro-auth",
      {
        "show_account_email": true,
        "show_plan": true,
        "show_credits": true
      }
    ]
  ]
}

@garysassano garysassano requested a review from tickernelz May 24, 2026 16:46
@tickernelz
Copy link
Copy Markdown
Owner

is it done?

@garysassano
Copy link
Copy Markdown
Contributor Author

@tickernelz Yes, I've been using it for a few days without issues.

Copy link
Copy Markdown
Owner

@tickernelz tickernelz left a comment

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Changes requested

Thanks for the follow-up. The previous immediate </thinking>Answer regression is fixed and the declared checks pass, but I found a few remaining correctness/data-loss issues that should be fixed before merge.

Blocking

  • src/plugin/streaming/stream-parser.ts:142-156 / src/plugin/streaming/thinking-parser.ts:63-119 — quoted/code-fenced tag handling is still not robust. findRealTag() only checks whether the tag is immediately adjacent to a quote/backtick and recomputes code-fence state from the current buffer. That means tags inside a quoted span like "... </thinking> ..." still close reasoning, and split code fences can make the whole thinking block flush as answer text.

    Reproduced locally:

    // Expected: reasoning includes the quoted literal tag, content is "Done"
    ['<thinking>quote "keep </thinking> inside" still thinking</thinking>Done']
    // Actual: reasoning = 'quote "keep ', content = ' inside" still thinking</thinking>Done'
    
    // Expected: reasoning includes the fenced literal tag, content is "Done"
    ['<thinking>```text\ninside code ', '</thinking>\n```\nstill thinking</thinking>Done']
    // Actual: content = '<thinking>```text\ninside code </thinking>\n```\nstill thinking</thinking>Done'

    The parser needs persistent lexical state (inside quote / inside fenced block), or another approach that does not discard that context when streamState.buffer is drained.

  • src/plugin/sync/kiro-cli.ts:76-109,182-201 + src/plugin/storage/locked-operations.ts:68-87 — a failed CLI usage fetch can still overwrite good quota data with 0/0. syncFromKiroCli() initializes usedCount/limitCount to zero and still upserts with lastSync: Date.now() when fetchUsageLimits() fails. mergeAccounts() treats that newer lastSync as a newer quota snapshot, so previously precise remote usage gets replaced by zeros.

    Minimal repro against mergeAccounts():

    existing: { "usedCount": 120.5, "limitCount": 2000, "lastSync": 1000 }
    incoming after failed usage fetch: { "usedCount": 0, "limitCount": 0, "lastSync": 2000 }
    result: { "usedCount": 0, "limitCount": 0 }

    Token freshness and quota freshness need to be tracked separately, or failed usage fetches should preserve existing quota fields / not advance the quota sync timestamp.

  • src/plugin/sync/kiro-cli.ts:47,218-225 — read-side now accepts both odic and oidc, but write-back only targets kirocli:odic:token. If a user's CLI DB uses kirocli:oidc:token, refreshed tokens are never written back, then later sync can re-import the stale token snapshot. I reproduced with a temp SQLite auth_kv row named kirocli:oidc:token; writeToKiroCli() left the old access/refresh tokens unchanged.

  • src/plugin/opencode-auth.ts:18-30,48-57 — malformed OpenCode auth.json is silently overwritten with only the Kiro placeholder. readAuthFile() logs that it is skipping placeholder setup on parse failure, but returns {}; ensureOpenCodeAuthPlaceholder() then writes that empty object plus the placeholder. This can drop other provider credentials if auth.json is temporarily truncated/corrupt. On parse failure, this should be a hard no-write path.

  • src/index.ts:3-6 / tsconfig.build.json:4-6 / index.ts:1-10 — the package root public API regresses after build. The repo-root index.ts still exports authorizeKiroIDC, createKiroPlugin, and KiroAuthDetails, but the build now only compiles src/**/*, so the published dist/index.js comes from src/index.ts and exports only:

    ["KIRO_LEGACY_PROVIDER_ID","KIRO_PROVIDER_ID","KiroOAuthPlugin","default"]

    Existing consumers importing authorizeKiroIDC / createKiroPlugin from the package root will break. Either keep the old root exports in src/index.ts or intentionally remove/update the stale repo-root entrypoint and docs/tests.

Warning / follow-up

  • src/constants.ts:244-249 / README.md:55-58 — older claude-sonnet-4-5-1m and claude-sonnet-4-5-1m-thinking aliases were previously accepted but now throw Unsupported model, while the README still claims older *-1m aliases are converted where possible. Either preserve those aliases (probably to claude-sonnet-4.5) or narrow the compatibility claim.

Verified locally

  • bun run check — 45 tests pass, typecheck pass, build pass
  • bunx prettier --check 'src/**/*.{ts,tsx}' 'scripts/**/*.ts' 'README.md' 'package.json' — pass
  • git diff --check origin/master...HEAD — pass
  • npm pack --dry-run — pass
  • Additional custom repros for streaming parser, quota merge, oidc write-back, malformed auth.json, and built package exports as noted above

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.

2 participants