@sunerpy/kiro-provider
v3.0.1
Published
OpenAI Responses-compatible AWS KiroRuntime provider with native and stateless fallback transports
Readme
kiro-provider
An OpenAI Responses-compatible provider over AWS KiroRuntime, with native Responses and protocol-fidelity stateless fallback transports.
简体中文 · English
Table of Contents
- Features
- Protocol compatibility
- Install
- Quickstart
- Run as a background service
- Configuration
- Proxy
- Security
- Using with an LLM
- Use with Zuno
- Use with Codex CLI
- Use with Claude Code
- Troubleshooting
- Development
- License
Features
- OpenAI Responses creation plus locally mirrored retrieve, delete, input-items,
and cancel routes; Anthropic Messages
POST /v1/messages; legacy Chat Completions behind an explicit switch;GET /v1/models,GET /health, and authenticatedGET /ready. - Legacy OpenAI Chat Completions is available at
POST /v1/chat/completions, but is disabled by default and must be explicitly enabled withenable_legacy_chat_completions. - Bearer API-key gate that fails closed: the server refuses to start with no configured keys, and defaults to binding
127.0.0.1. - Provider-owned authentication by default:
auth_source: "local"stores credentials in~/.config/kiro-provider/accounts.db. Existingopencode-kiro-authaccounts can be imported once withkiro-provider accounts import; after that, kiro-provider refreshes access tokens, usage, quota recovery, and account health without reading or locking OpenCode's database. - Explicit-only session affinity by default: Responses requests can opt in through standard
metadata, compatibilityclient_metadata, orprompt_cache_key; standard clients that resend complete history can also continue through the exact prior assistant-output lineage. User prompts are never fingerprinted to guess a session. A matching Zuno native OpenAI transport suppliesmetadata.zuno_session_idautomatically. - Account-scoped scheduling and cached SDK/transport objects: unrelated accounts can run concurrently, while one account is protected from overlapping Kiro streams. Access-token rotation rebuilds the credential-bound SDK client while retaining the account transport. A production-default service lock prevents multiple processes from silently splitting those queues and pools. Kiro model-call HTTP keep-alive is disabled by default and is an explicit transport opt-in.
- Live per-account model discovery and account-aware routing through Kiro management, with bounded stale/static fallback. Production calls use the live-probe-confirmed
runtime.<region>.kiro.devdialect. Token-usage metadata is an immediate completion witness; the current runtime's valid terminal metering event is accepted only when followed by clean EOF. - Default
v3-autotransport selection: ordinary requests use KiroRuntime's native OpenAI Responses operation and request shapes requiringstore:false, max effort, encrypted reasoning replay, custom grammar, namespace tools, or Codex collaboration use the canonical stateless fallback. - Encrypted reasoning replay for complete native Kiro envelopes: opaque
kr1_...tokens, AES-256-GCM storage, tenant/model/account/conversation/output binding, TTL/LRU cleanup, and account-locked replay. - Multi-account rotation with automatic token refresh and failover. Exhausted accounts are hard-excluded from model attempts, then automatically rejoin only after a bounded, deduplicated Kiro usage probe confirms a new quota window. A provider-owned maintenance loop also refreshes near-expiry tokens and stale usage while the service is idle.
kiro-provider loginandaccounts importwrite directly to the provider-owned local authentication store. The formerauth_source: "opencode-shared"compatibility mode was removed in 0.7.0; a configuration that still selects it fails at startup with migration instructions (import once, then uselocal).- A single global
proxy_urlthat, when set, routes all upstream egress (model requests, token refresh, quota probes, device-code login) through one HTTP(S) proxy. - Ships as a self-contained compiled binary via
bun build --compile— no runtime install required on the target machine.
Protocol compatibility
V3 implements the core OpenAI Responses resource and makes every upstream difference explicit:
- native JSON/SSE creation, instructions, function tools, supported effort and
token controls, and native
previous_response_id; - automatic stateless fallback for
store:false, max effort, encrypted reasoning, custom grammar, namespace tools, and Codex multi-agent items; - tenant-isolated local response mirrors for retrieve, delete, input-items pagination, and continuation;
- field-level OpenAI error envelopes for capabilities Kiro cannot preserve, including Responses conversation objects, background execution, Structured Outputs, hosted tools, remote file references, compact, and exact input-token counting.
The old GenerateAssistantResponse safe mode remains fail-closed because
additionalContext did not preserve instruction content or priority, and the
account does not advertise the private systemPrompt feature. The default
v3-auto path instead uses KiroRuntime CreateResponse's native
instructions field.
For the transport decision table, stored-response contract, data-retention
boundary, verified model controls, and current client evidence, see
docs/PROTOCOL_COMPATIBILITY.md and the
docs/audits/ records.
Install
Pick one of three channels.
1. bunx / bun (fastest, requires Bun)
kiro-provider ships an npm package built on Bun-only APIs (bun:sqlite, Bun.serve), so it runs under Bun or bunx, not npx or plain node. Install Bun first, then:
bunx @sunerpy/kiro-provider serve --helpOr install it globally:
bun add -g @sunerpy/kiro-provider
kiro-provider --help2. Prebuilt binary (no dependencies)
Every release publishes standalone binaries for linux (x64, arm64), darwin (x64, arm64), and windows (x64). Download the one for your platform from Releases, chmod +x it, and run it directly. No Bun or Node.js needed at runtime.
One-line install (Linux/macOS):
curl -fsSL https://raw.githubusercontent.com/sunerpy/kiro-provider/main/scripts/install.sh | shWindows (PowerShell):
irm https://raw.githubusercontent.com/sunerpy/kiro-provider/main/scripts/install.ps1 | iexBoth scripts download the platform asset together with the release's SHA256SUMS, verify the checksum, and abort on a mismatch before installing to ~/.local/bin (override with KIRO_PROVIDER_INSTALL_DIR). By default they follow releases/latest; for reproducible or service installs, pin a release with KIRO_PROVIDER_VERSION (recommended):
curl -fsSL https://raw.githubusercontent.com/sunerpy/kiro-provider/main/scripts/install.sh | KIRO_PROVIDER_VERSION=3.0.0 sh$env:KIRO_PROVIDER_VERSION = "3.0.0"; irm https://raw.githubusercontent.com/sunerpy/kiro-provider/main/scripts/install.ps1 | iex3. From source (developers)
Requires Bun.
git clone https://github.com/sunerpy/kiro-provider.git
cd kiro-provider
bun install
bun run build:binary
./dist/kiro-provider --helpOr run without compiling:
bun install
bun run src/cli/bin.ts --helpIn the rest of this README, ./dist/kiro-provider refers to any of the above; substitute bunx @sunerpy/kiro-provider, your installed binary path, or bun run src/cli/bin.ts depending on which channel you used.
Quickstart
Create a config with your own API key. Only
api_keysis required; every other field has a production default (auth_source: "local",host: "127.0.0.1",port: 8787).mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/kiro-provider" cat > "${XDG_CONFIG_HOME:-$HOME/.config}/kiro-provider/config.json" <<'EOF' { "api_keys": ["sk-your-private-key"] } EOF chmod 600 "${XDG_CONFIG_HOME:-$HOME/.config}/kiro-provider/config.json"Replace
sk-your-private-keywith a private, random value (for exampleopenssl rand -hex 24). The fully annotatedconfig.example.jsonin the repository anddocs/CONFIGURATION.mddescribe every field.Windows locations. On Windows the default config path is
%APPDATA%\kiro-provider\config.json, andaccounts.db, the instance lock, and the reasoning keyring live in that same directory (POSIX uses~/.config/kiro-providerfor all of them). Pass--config <path>to use a different file.Populate the provider-owned authentication store. If you previously authenticated through OpenCode plus
opencode-kiro-auth, import that database once:./dist/kiro-provider accounts importThe default source is
~/.config/opencode/kiro.db; use--from <path>when needed. This is a copy, not a live link: subsequent token and usage refreshes are owned by kiro-provider. Alternatively, authenticate directly:./dist/kiro-provider loginAvoid continuing to use the same imported refresh tokens from two independently running authentication owners.
Inspect or refresh the provider-owned account pool at any time:
./dist/kiro-provider accounts list ./dist/kiro-provider accounts list --details ./dist/kiro-provider accounts refresh --allStart the gateway.
./dist/kiro-provider serveCall the default Responses endpoint.
curl -fsS http://127.0.0.1:8787/v1/models \ -H 'Authorization: Bearer sk-your-private-key'import OpenAI from "openai"; const client = new OpenAI({ baseURL: "http://127.0.0.1:8787/v1", apiKey: "sk-your-private-key", }); const response = await client.responses.create({ model: "auto", input: "Explain this repository.", }); console.log(response.output_text);OpenAI-compatible libraries that only implement Chat Completions require
"enable_legacy_chat_completions": truein the gateway config. For example, with the Vercel AI SDK via@ai-sdk/openai-compatible:import { createOpenAICompatible } from "@ai-sdk/openai-compatible"; import { generateText } from "ai"; const kiro = createOpenAICompatible({ name: "kiro-provider", baseURL: "http://127.0.0.1:8787/v1", apiKey: "sk-your-private-key", }); const { text } = await generateText({ model: kiro("auto"), prompt: "Explain this repository.", });
Run as a background service
For an agent host, run one long-lived provider per OS user and point
compatible OpenAI/Anthropic clients, OpenCode, Zuno, and compatibility probes
for Codex or Claude Code at that local endpoint.
Do not start a new provider for every agent or conversation. Keeping one
process alive lets requests with an explicit affinity key reuse their
persisted account/Kiro-conversation binding, while all requests can reuse
process-local, account-scoped SDK clients and transport objects. A request
without an explicit key starts a fresh Kiro conversation on its first turn,
then can recover the same binding from exact assistant-output history on later
turns. Kiro model-call HTTP sockets are fresh by default
(sdk_http_keep_alive: false); enabling it is a best-effort transport
optimization, never a promise that one session owns one physical TCP
connection. The default enforce_single_instance: true also prevents a second
service process from splitting the in-memory queues and pools.
Use a pinned standalone binary for a service rather than fetching through
bunx on every start. The examples below assume the release installers'
defaults:
- binary:
~/.local/bin/kiro-provideron Linux,%USERPROFILE%\.local\bin\kiro-provider.exeon Windows; - config:
~/.config/kiro-provider/config.jsonon Linux,%APPDATA%\kiro-provider\config.jsonon Windows; - service/task name:
kiro-provider.
Run the one-time import and the service as the same OS user so the service
owns the same ~/.config/kiro-provider/accounts.db, config, keyring, and
instance lock. Running as root, LocalSystem, or another user normally
selects a different local store. Use absolute paths and keep the API key in
the protected config file rather than service arguments. The OpenCode
database is not read again after the import.
Linux: systemd user service
Verify the installed binary and config first:
test -x "$HOME/.local/bin/kiro-provider"
test -r "$HOME/.config/kiro-provider/config.json"
chmod 600 "$HOME/.config/kiro-provider/config.json"Install a systemd user service:
SERVICE_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
install -d -m 700 "$SERVICE_DIR"
cat > "$SERVICE_DIR/kiro-provider.service" <<'EOF'
[Unit]
Description=kiro-provider local Kiro gateway
[Service]
Type=exec
ExecStart=%h/.local/bin/kiro-provider serve --config %h/.config/kiro-provider/config.json
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
UMask=0077
[Install]
WantedBy=default.target
EOF
chmod 600 "$SERVICE_DIR/kiro-provider.service"
systemctl --user daemon-reload
systemctl --user enable --now kiro-provider.serviceIf the binary or config is elsewhere, replace ExecStart with those absolute
paths. For a custom XDG_CONFIG_HOME, also add an explicit
Environment=XDG_CONFIG_HOME=/absolute/path line.
Operate and inspect the service:
systemctl --user is-active kiro-provider.service
systemctl --user restart kiro-provider.service
journalctl --user -u kiro-provider.service -n 100 --no-pagerUser services normally start with that user's service manager. If the
provider must start at boot and remain after logout, an administrator may
enable lingering with loginctl enable-linger <user> after reviewing the
machine's security policy.
To remove the unit:
systemctl --user disable --now kiro-provider.service
rm "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/kiro-provider.service"
systemctl --user daemon-reloadWindows: per-user scheduled task
kiro-provider.exe is a normal foreground executable, not a native Windows
Service Control Manager executable. Do not register it directly with
sc.exe. The built-in, dependency-free option is a
Scheduled Task
that starts at sign-in, runs as the current user, and restarts after failure.
Run the following in PowerShell as the same user that owns the provider's
local authentication database and config. It creates a small launcher so
stdout/stderr are retained under %LOCALAPPDATA%\kiro-provider:
$Binary = Join-Path $HOME ".local\bin\kiro-provider.exe"
$Config = Join-Path $env:APPDATA "kiro-provider\config.json"
$ServiceDir = Join-Path $env:APPDATA "kiro-provider"
$LogDir = Join-Path $env:LOCALAPPDATA "kiro-provider"
$Launcher = Join-Path $ServiceDir "service.ps1"
if (-not (Test-Path -LiteralPath $Binary -PathType Leaf)) {
throw "kiro-provider binary not found: $Binary"
}
if (-not (Test-Path -LiteralPath $Config -PathType Leaf)) {
throw "kiro-provider config not found: $Config"
}
New-Item -ItemType Directory -Force -Path $ServiceDir, $LogDir | Out-Null
@'
$ErrorActionPreference = "Stop"
$Binary = Join-Path $HOME ".local\bin\kiro-provider.exe"
$Config = Join-Path $env:APPDATA "kiro-provider\config.json"
$LogDir = Join-Path $env:LOCALAPPDATA "kiro-provider"
$Log = Join-Path $LogDir "service.log"
$PreviousLog = Join-Path $LogDir "service.previous.log"
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
if ((Test-Path -LiteralPath $Log) -and ((Get-Item -LiteralPath $Log).Length -gt 10MB)) {
Move-Item -Force -LiteralPath $Log -Destination $PreviousLog
}
& $Binary serve --config $Config *>> $Log
exit $LASTEXITCODE
'@ | Set-Content -LiteralPath $Launcher -Encoding UTF8
$User = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$PowerShell = (Get-Command powershell.exe).Source
$Action = New-ScheduledTaskAction `
-Execute $PowerShell `
-Argument ('-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "{0}"' -f $Launcher)
$Trigger = New-ScheduledTaskTrigger -AtLogOn -User $User
$Principal = New-ScheduledTaskPrincipal `
-UserId $User `
-LogonType Interactive `
-RunLevel Limited
$Settings = New-ScheduledTaskSettingsSet `
-RestartCount 999 `
-RestartInterval (New-TimeSpan -Minutes 1) `
-ExecutionTimeLimit ([TimeSpan]::Zero) `
-MultipleInstances IgnoreNew `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable
Stop-ScheduledTask -TaskName "kiro-provider" -ErrorAction SilentlyContinue
Register-ScheduledTask `
-TaskName "kiro-provider" `
-Action $Action `
-Trigger $Trigger `
-Principal $Principal `
-Settings $Settings `
-Description "Local AWS Kiro gateway for AI agents" `
-Force | Out-Null
Start-ScheduledTask -TaskName "kiro-provider"Inspect, restart, and follow logs:
Get-ScheduledTask -TaskName "kiro-provider" | Get-ScheduledTaskInfo
Stop-ScheduledTask -TaskName "kiro-provider"
Start-ScheduledTask -TaskName "kiro-provider"
Get-Content "$env:LOCALAPPDATA\kiro-provider\service.log" -Tail 100 -WaitTo remove the task and launcher:
Stop-ScheduledTask -TaskName "kiro-provider" -ErrorAction SilentlyContinue
Unregister-ScheduledTask -TaskName "kiro-provider" -Confirm:$false
Remove-Item "$env:APPDATA\kiro-provider\service.ps1"This task intentionally runs only in the current user's interactive session,
so it can use that user's network access, provider database, and keyring
without storing a Windows password. A true pre-login Windows service requires
a service wrapper and a deliberately configured user account; do not run it
as LocalSystem and expect the same provider-owned files.
Health checks and automation contract
After either installation, verify both process liveness and authenticated readiness:
curl -fsS http://127.0.0.1:8787/health
curl -fsS http://127.0.0.1:8787/ready \
-H 'Authorization: Bearer sk-your-private-key'PowerShell equivalent:
Invoke-RestMethod "http://127.0.0.1:8787/health"
$Headers = @{ Authorization = "Bearer sk-your-private-key" }
Invoke-RestMethod "http://127.0.0.1:8787/ready" -Headers $HeadersFor an AI agent or installer, treat setup as successful only when:
- the binary and explicit config path exist;
- the service/task runs as the credential-owning user;
/healthsucceeds;- authenticated
/readysucceeds, proving a readable auth source, at least one active account, writable provider state, an available reasoning keyring, and coverage for every key ID referenced by an unexpired replay record.
Use the fixed service/task name above so repeated setup is idempotent. Restart it after changing the config or replacing the binary. Do not make the client responsible for starting a private provider process; configure clients only with the stable base URL and gateway API key.
Configuration
Config is loaded from ~/.config/kiro-provider/config.json (or $XDG_CONFIG_HOME/kiro-provider/config.json; on Windows %APPDATA%\kiro-provider\config.json, with the legacy ~/.config location still read as a fallback), overridable by KIRO_PROVIDER_* environment variables and, for serve, by CLI flags. Unknown keys in the file are rejected with a suggestion, numeric fields are range-checked, and an empty environment variable counts as unset. Precedence is CLI flag > environment variable > config file > schema default.
| Field | Default | Env var |
| --- | --- | --- |
| host | 127.0.0.1 | KIRO_PROVIDER_HOST |
| port | 8787 | KIRO_PROVIDER_PORT |
| api_keys | required, non-empty | KIRO_PROVIDER_API_KEYS |
| enable_legacy_chat_completions | false | KIRO_PROVIDER_ENABLE_LEGACY_CHAT_COMPLETIONS |
| protocol_projection_mode | v3-auto | KIRO_PROVIDER_PROTOCOL_PROJECTION_MODE |
| session_affinity_mode | explicit-only | KIRO_PROVIDER_SESSION_AFFINITY_MODE |
| auth_source | local | KIRO_PROVIDER_AUTH_SOURCE |
| opencode_auth_db_path | null (deprecated since 0.7.0, ignored) | KIRO_PROVIDER_OPENCODE_AUTH_DB_PATH |
| proxy_url | null | KIRO_PROVIDER_PROXY_URL |
| default_region | us-east-1 | KIRO_PROVIDER_DEFAULT_REGION |
| sdk_http_keep_alive | false | KIRO_PROVIDER_SDK_HTTP_KEEP_ALIVE |
| enforce_single_instance | true | KIRO_PROVIDER_ENFORCE_SINGLE_INSTANCE |
| instance_lock_path | platform config directory | KIRO_PROVIDER_INSTANCE_LOCK_PATH |
| runtime_endpoint_mode | kiro-runtime | KIRO_PROVIDER_RUNTIME_ENDPOINT_MODE |
| dynamic_model_catalog | true | KIRO_PROVIDER_DYNAMIC_MODEL_CATALOG |
| model_catalog_ttl_ms | 900000 | KIRO_PROVIDER_MODEL_CATALOG_TTL_MS |
| model_catalog_stale_ttl_ms | 86400000 | KIRO_PROVIDER_MODEL_CATALOG_STALE_TTL_MS |
| model_catalog_request_timeout_ms | 10000 | KIRO_PROVIDER_MODEL_CATALOG_REQUEST_TIMEOUT_MS |
| account_selection_strategy | lowest-usage | KIRO_PROVIDER_ACCOUNT_SELECTION_STRATEGY |
| quota_recheck_interval_ms | 900000 | KIRO_PROVIDER_QUOTA_RECHECK_INTERVAL_MS |
| quota_recheck_timeout_ms | 10000 | KIRO_PROVIDER_QUOTA_RECHECK_TIMEOUT_MS |
| quota_recheck_concurrency | 4 | KIRO_PROVIDER_QUOTA_RECHECK_CONCURRENCY |
| account_maintenance_enabled | true | KIRO_PROVIDER_ACCOUNT_MAINTENANCE_ENABLED |
| account_maintenance_interval_ms | 60000 | KIRO_PROVIDER_ACCOUNT_MAINTENANCE_INTERVAL_MS |
| account_maintenance_timeout_ms | 120000 | KIRO_PROVIDER_ACCOUNT_MAINTENANCE_TIMEOUT_MS |
| account_maintenance_concurrency | 4 | KIRO_PROVIDER_ACCOUNT_MAINTENANCE_CONCURRENCY |
| usage_refresh_interval_ms | 900000 | KIRO_PROVIDER_USAGE_REFRESH_INTERVAL_MS |
| session_affinity_ttl_ms | 86400000 | KIRO_PROVIDER_SESSION_AFFINITY_TTL_MS |
| session_affinity_max_entries | 10000 | KIRO_PROVIDER_SESSION_AFFINITY_MAX_ENTRIES |
| reasoning_replay_key_path | auto-generated config path | KIRO_PROVIDER_REASONING_REPLAY_KEY_PATH |
| reasoning_replay_keys | [] | KIRO_PROVIDER_REASONING_REPLAY_KEYS |
| reasoning_replay_ttl_ms | 86400000 | KIRO_PROVIDER_REASONING_REPLAY_TTL_MS |
| reasoning_replay_max_entries | 10000 | KIRO_PROVIDER_REASONING_REPLAY_MAX_ENTRIES |
| log_level | info | KIRO_PROVIDER_LOG_LEVEL |
The full field reference, including retry/timeout tuning and the test-only test_upstream_endpoint, lives in docs/CONFIGURATION.md.
Proxy
Some networks reach one model family directly while another needs a proxy (for example, GPT direct, Claude via an approved egress). Set proxy_url (config file, KIRO_PROVIDER_PROXY_URL, or serve --proxy) to route all upstream traffic — model calls, token refresh, quota probes, and device-code login — through a single HTTP(S) proxy. Leave it null for direct connections. See docs/CONFIGURATION.md for precedence details and examples.
Security
- Fail-closed authentication. The server will not start without at least one non-empty
api_keysentry. OpenAI routes requireAuthorization: Bearer <key>; Anthropic routes also acceptx-api-key: <key>. - Local bind by default.
hostdefaults to127.0.0.1; only bind0.0.0.0behind a firewall or authenticated reverse proxy. - Single authentication authority. The default local mode makes kiro-provider the sole owner after one-time import. Do not keep two independent processes rotating the same imported refresh token. The explicit shared compatibility mode validates OpenCode's schema and never migrates it.
- Single service owner by default. The compiled service acquires a platform-config lock before listening, so process-local account/session queues and SDK pools cannot be split accidentally.
- Locked-down provider state.
accounts.db(and its WAL/SHM files) are created with mode0600; in default local mode it contains credentials, usage, health, session affinity, and encrypted replay state. - Authenticated reasoning replay. The database stores token/fingerprint hashes and AES-256-GCM ciphertext, not raw
kr1_...tokens. Missing active decryption keys fail startup. - No sensitive content in logs. Gateway/account secrets, replay tokens, signatures, reasoning, and request prompt text are not logged; structured audit fields contain hashes and field names only. Don't commit a real config file, account database, keyring, or gateway key.
Responsible use. kiro-provider reuses AWS Kiro accounts you already control and consumes your own account quota. Supply your own accounts — this project is not a way to share or resell someone else's Kiro access, and it should not be used to circumvent per-account usage limits.
Using with an LLM
Use POST /v1/responses for OpenAI Responses clients. Use
POST /v1/messages for Anthropic Messages clients. Only point a
Chat-Completions-only client (@ai-sdk/openai-compatible, older LangChain
adapters, or an OpenCode custom provider using that package) at
POST /v1/chat/completions after explicitly enabling the legacy endpoint.
The default v3-auto mode accepts standard instructions through KiroRuntime's
native Responses field. Requests that need custom grammar, namespace tools,
Codex collaboration items, encrypted reasoning replay, max effort, or
store:false automatically use the stateless compatibility lane. Unsupported
hosted capabilities fail with a field-level OpenAI error instead of being
silently ignored.
The default session_affinity_mode: "explicit-only" never hashes prompt text
to guess a conversation. Responses checks, in order,
metadata.zuno_session_id, metadata.kiro_provider_session_id, compatibility
client_metadata.thread_id|session_id|conversation_id, and
prompt_cache_key. Chat checks only prompt_cache_key; Anthropic Messages
has no verified explicit affinity field. With no key, the request gets a
fresh Kiro conversation on its first turn; a later full-history request can
reuse the same account/conversation by matching the exact prior assistant
output lineage. It can also reuse account-scoped SDK clients and transport
objects. The Kiro SDK's direct/proxy agents use fresh sockets by default; set
sdk_http_keep_alive: true only when the deployment has validated pooled
socket behavior.
The temporary legacy-initial-input mode restores only the old affinity
heuristics and logs a startup warning; it does not alter request content.
previous_response_id is supported for tenant-local mirrored responses.
conversation objects remain unsupported. Retrieve, delete, input-items, and
cancel use the local mirror; deleting it does not prove deletion of Kiro's
upstream state.
kiro-provider serve [--config <path>] [--host <host>] [--port <port>] [--proxy <url>]— start the gateway.kiro-provider login [--config <path>] [--start-url <url>] [--region <region>]— authenticate directly into the provider-owned local store.kiro-provider accounts list [--details | --json]— show aligned account health/usage; details and JSON include the stable account ID but never credentials.kiro-provider accounts refresh (--all | <id|email>) [--config <path>] [--json]— bypass the usage cache, refresh authoritative Kiro usage, and renew an access token only when needed or rejected upstream.kiro-provider accounts relogin <id|email> [--config <path>] [--start-url <url>] [--region <region>]— re-authenticate a selected account after Kiro identity verification while preserving its internal ID and session-affinity references.kiro-provider accounts import [--from <path>] [--force]— copy authenticated OpenCode Kiro accounts once into the provider-owned local store; rows whose local copy is newer are skipped unless--forceis given; no live database link remains.kiro-provider accounts remove <id|email> [--yes]— remove one account and its affinity/lineage/reasoning state; interactive confirmation is required unless--yesis supplied.
Contract: human-readable status lines go to stdout, errors to stderr, non-zero exit on failure. GET /v1/models, GET /health, and authenticated GET /ready return structured JSON.
Use with Zuno
Run one compiled kiro-provider service as the credential-owning OS user, then configure Zuno's native Rust OpenAI transport. No Node package, AI SDK, private header, or provider-spawn hook is required:
{
"model": "kiro/auto",
"small_model": "kiro/auto",
"provider": {
"kiro": {
"name": "Local kiro-provider",
"transport": "openai",
"surface": "responses",
"env": ["KIRO_GATEWAY_API_KEY"],
"options": {
"baseURL": "http://127.0.0.1:8787/v1",
"maxTokens": null
},
"models": {
"auto": {
"name": "Kiro Auto",
"reasoning": true,
"tool_call": true
}
}
}
}
}Set KIRO_GATEWAY_API_KEY to one key from the provider's api_keys, then
verify the native route:
export KIRO_GATEWAY_API_KEY='sk-your-private-key'
zuno debug config
zuno models kiro --verboseThe matching Zuno OpenAI Responses transport maps the durable Zuno session ID
to standard metadata.zuno_session_id on every main turn and tool
continuation. It does not add that ID to input, messages, instructions, tool
descriptions, or any other model-visible field; internal title/summary calls
do not join the main provider conversation. Therefore one Zuno session is
serialized onto one persisted account/Kiro-conversation binding, while
different sessions remain isolated even if their first prompt and upstream
tool aliases are identical. Tool declaration and alias state remains local to
each request.
Keep surface: "responses" for this integration. Selecting chat requires
the separately enabled legacy endpoint and does not carry the Zuno Responses
session metadata.
Current Zuno sends agent instructions. Valid required-label
additionalContext requests reached Kiro in live GPT and Claude probes, but
the models did not receive the instruction content or preserve its priority.
Consequently the verified functional path currently requires the provider's
explicit protocol_projection_mode: "legacy-user-prefix"; safe correctly
returns unsupported_instruction_projection and never rewrites the request.
Set Zuno options.maxTokens to null as shown so its generic layer does not
add the unsupported max_output_tokens: 32000. Neither setting uses a private
Header or client-side prompt patch; the legacy mode is an explicit migration
exception whose removal is gated on native instruction fidelity or completed
client migration.
Use with Codex CLI
Codex uses the correct Responses endpoint. The last compiled protocol gate
with Codex
0.150.0-alpha.9 and claude-opus-5-max now passes provider model validation,
but its first request is rejected before Kiro at reasoning.summary, which
has no proven native equivalent. The provider does not strip that field or
simulate it with prompt text. The following isolated configuration reproduces
the compatibility check without touching the real ~/.codex state:
export CODEX_TEST_ROOT="$(mktemp -d)"
export CODEX_HOME="$CODEX_TEST_ROOT/home"
export CODEX_SQLITE_HOME="$CODEX_TEST_ROOT/sqlite"
mkdir -p "$CODEX_HOME" "$CODEX_SQLITE_HOME"
export LOCALGW_KEY="sk-...your gateway api key..."
cat > "$CODEX_HOME/config.toml" <<'EOF'
model = "claude-opus-5-max"
model_provider = "localgw"
model_reasoning_effort = "high"
[model_providers.localgw]
name = "Local Gateway"
base_url = "http://127.0.0.1:8787/v1"
env_key = "LOCALGW_KEY"
wire_api = "responses"
EOF
codex exec --skip-git-repo-check "say hi"For Codex 0.150.0-alpha.9 the expected result is a non-zero exit with
unsupported_reasoning_summary at reasoning.summary. A future supported request shape must
then pass a real shell/custom-tool loop, continuation, and restart reasoning
replay before Codex is marked supported. Full details live in
docs/CODEX.md.
Use with Claude Code
Claude Code uses Anthropic Messages. The last compiled protocol run with Claude Code
2.1.209, claude-opus-5, and max effort passes provider model validation but
is rejected before Kiro at context_management, which has no proven native
equivalent. The provider does not discard it. Direct Opus 5 Messages JSON/SSE
within the verified subset passes; the standard Claude Code configuration
below remains a compatibility probe rather than a full support claim:
export ANTHROPIC_BASE_URL="http://127.0.0.1:8787"
export ANTHROPIC_API_KEY="sk-your-private-key"
claude --bare --safe-mode --model claude-opus-5 --effort maxThe gateway accepts either Authorization: Bearer <key> or x-api-key:
<key> for Anthropic routes. Direct Messages requests within the verified
subset support typed JSON/SSE and tools, while /v1/messages/count_tokens is
an explicit estimate. See
docs/CLAUDE_CODE.md.
The current account-management and live-usage validation record is in
docs/audits/kiro-provider-v0.5.0-rc.5-account-management-validation-2026-08-29.md.
The v0.5.0 typed stream-error contract and downstream Zuno handoff are in
docs/STREAM_ERROR_CONTRACT.md and
docs/ZUNO_STREAM_ERROR_HANDOFF.zh.md.
The preceding local-auth lifecycle record is retained in
docs/audits/kiro-provider-v0.5.0-rc.4-local-auth-maintenance-validation-2026-08-29.md.
The preceding protocol/client matrix is retained in
docs/audits/kiro-provider-v0.5.0-rc.3-opus5-validation-2026-08-27.md.
The older docs/E2E_VALIDATION_2026-08-22.md
is retained as historical v0.4 evidence only.
Troubleshooting
docs/TROUBLESHOOTING.md is the symptom-first
runbook: for each symptom it names the audit event, the
accounts list --details availability value, or the HTTP status and
error.code to look at, then the cause and the remedy. It covers
needs-relogin and token-refresh failures, quota-exhausted versus
overage-blocked (stop_on_overage), 503 no_healthy_accounts, the
502 upstream_stream_* codes with the pre-publication retry events, how to
read sdk_stream_terminal when "the assistant announced a next step and
stopped", reasoning-replay 400s, the single-instance lock, configuration
warnings, 413 variants, and proxy failures. It also lists journalctl grep
recipes for the systemd service and the opt-in request_shape debug event.
Development
bun install
bun run lint
bun run typecheck
bun test
bun run build
bun run build:binary
bash scripts/security-check.sh # security regression suite (Linux, needs openssl/curl/ss)make ci runs typecheck, lint, shell-script syntax checks, and the test suite.
make fmt-check (and make fmt) additionally require the oxfmt formatter
for YAML/JSON/Markdown; install the version CI uses with
bun install --global [email protected]. bun run scripts/smoke.ts --help describes
the live end-to-end checks against a running gateway.
