@sunerpy/kiro-provider 0.3.1 → 0.5.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +457 -24
  2. package/dist/cli.js +260 -99
  3. package/package.json +5 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # kiro-provider
2
2
 
3
- > A standalone OpenAI-compatible HTTP gateway for AWS Kiro (CodeWhisperer) point any OpenAI SDK or agent at your own Kiro accounts.
3
+ > A protocol-fidelity gateway exposing a verified OpenAI Responses and Anthropic Messages subset over AWS Kiro (CodeWhisperer).
4
4
 
5
5
  [![CI](https://github.com/sunerpy/kiro-provider/actions/workflows/ci.yml/badge.svg)](https://github.com/sunerpy/kiro-provider/actions/workflows/ci.yml)
6
6
  [![codecov](https://codecov.io/gh/sunerpy/kiro-provider/branch/main/graph/badge.svg)](https://codecov.io/gh/sunerpy/kiro-provider)
@@ -12,25 +12,77 @@
12
12
  ## Table of Contents
13
13
 
14
14
  - [Features](#features)
15
+ - [Protocol compatibility](#protocol-compatibility)
15
16
  - [Install](#install)
16
17
  - [Quickstart](#quickstart)
18
+ - [Run as a background service](#run-as-a-background-service)
17
19
  - [Configuration](#configuration)
18
20
  - [Proxy](#proxy)
19
21
  - [Security](#security)
20
22
  - [Using with an LLM](#using-with-an-llm)
23
+ - [Use with Zuno](#use-with-zuno)
21
24
  - [Use with Codex CLI](#use-with-codex-cli)
25
+ - [Use with Claude Code](#use-with-claude-code)
22
26
  - [Development](#development)
23
27
  - [License](#license)
24
28
 
25
29
  ## Features
26
30
 
27
- - OpenAI-compatible `POST /v1/chat/completions` (streaming SSE and non-streaming JSON), `POST /v1/responses` (OpenAI Responses API, streaming typed SSE and non-streaming JSON), `GET /v1/models`, and `GET /health`.
31
+ - OpenAI Responses `POST /v1/responses` and Anthropic Messages `POST /v1/messages` (both streaming and non-streaming), plus `POST /v1/messages/count_tokens`, `GET /v1/models`, `GET /health`, and authenticated `GET /ready`.
32
+ - Legacy OpenAI Chat Completions is available at `POST /v1/chat/completions`, but is disabled by default and must be explicitly enabled with `enable_legacy_chat_completions`.
28
33
  - Bearer API-key gate that fails closed: the server refuses to start with no configured keys, and defaults to binding `127.0.0.1`.
29
- - Multi-account rotation with automatic token refresh and failover, backed by a local `bun:sqlite` account store with tombstone-based removal.
30
- - `accounts import` to reuse accounts already authenticated by [OpenCode's Kiro auth](https://opencode.ai/) instead of repeating device-code login.
34
+ - Live OpenCode authentication reuse by default: `auth_source: "opencode-shared"` reads the same `~/.config/opencode/kiro.db`, honors tombstones, updates shared health/usage, and uses the account schema and refresh-lock behavior of `opencode-kiro-auth` v0.20.7.
35
+ - Explicit-only session affinity by default: Responses requests can opt in through standard `metadata`, compatibility `client_metadata`, or `prompt_cache_key`; requests without an explicit key never derive identity from prompt text. A matching Zuno native OpenAI transport supplies `metadata.zuno_session_id` automatically.
36
+ - Account-scoped scheduling and cached SDK/transport objects: unrelated accounts can run concurrently, while one account is protected from overlapping Kiro streams; access-token refresh updates the cached client instead of rebuilding it. Kiro model-call HTTP keep-alive is disabled by default and is an explicit transport opt-in.
37
+ - Zero provider-owned prompt injection in the default `safe` mode: a canonical IR preserves client text, roles, content-block boundaries, tool identity, ordering, and source paths; unsupported guarantees are rejected before Kiro instead of being emulated with hidden instructions.
38
+ - 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.
39
+ - Multi-account rotation with automatic token refresh and failover. Shared mode treats OpenCode's database as the authentication authority; the provider database stores session affinity only.
40
+ - An explicit `auth_source: "local"` compatibility mode retains `kiro-provider login` and `accounts import`; imported accounts are snapshots and must not be confused with live shared authentication.
31
41
  - A single global `proxy_url` that, when set, routes all upstream egress (model requests, token refresh, device-code login) through one HTTP(S) proxy.
32
42
  - Ships as a self-contained compiled binary via `bun build --compile` — no runtime install required on the target machine.
33
43
 
44
+ ## Protocol compatibility
45
+
46
+ v0.5 is intentionally a **verified compatibility subset**. It does not accept
47
+ fields and silently discard them. The default `protocol_projection_mode:
48
+ "safe"` never prepends or rewrites client instructions, merges adjacent
49
+ messages, clears repeated assistant output, removes trailing text such as `{`,
50
+ or creates model-visible compensation prose.
51
+
52
+ Key boundaries:
53
+
54
+ - plain text, consecutive same-role turns, function/custom tool declarations,
55
+ calls, and results retain their original structure and order;
56
+ - a message containing multiple top-level text blocks returns
57
+ `unsupported_content_block_projection`, because Kiro exposes only one text
58
+ field and concatenation would erase block boundaries;
59
+ - `instructions`, `system`, and `developer` return
60
+ `unsupported_instruction_projection` in safe mode because the tested Kiro
61
+ `additionalContext` channel was rejected;
62
+ - `tool_choice: auto` is supported; required/named choice,
63
+ `parallel_tool_calls: false`, strict schemas, custom grammars, and namespace
64
+ tools are rejected rather than weakened;
65
+ - base64/data-URL images are supported, while remote image URLs and detail
66
+ controls are rejected;
67
+ - an output-token limit is probe-confirmed only for `claude-sonnet-5` variants
68
+ in the range 1,024–128,000;
69
+ - stateful Responses fields and native Web Search remain unsupported, and the
70
+ provider never fabricates search/citation events.
71
+
72
+ Current compiled-binary acceptance on 2026-08-26: OpenAI JavaScript SDK 7.5.0
73
+ passes Responses, explicit Chat, function/custom tool loops, and encrypted
74
+ reasoning replay across restart. OpenCode Responses passes only in explicit
75
+ `legacy-user-prefix` mode with Claude Sonnet 5; OpenCode Chat is blocked by
76
+ its nonstandard `cache_control`. Codex 0.149.0-alpha.4.1 is blocked by
77
+ `parallel_tool_calls: false`, and Claude Code 2.1.209 is blocked by
78
+ `output_config.format`, `context_management`, and its retry that places
79
+ `system` in `messages.1.role`. These are RC findings; stable v0.5.0 remains
80
+ gated rather than silently discarding or relocating those fields.
81
+
82
+ For the complete capability matrix, error codes, reasoning replay contract,
83
+ and v0.4 migration steps, see
84
+ [`docs/PROTOCOL_COMPATIBILITY.md`](docs/PROTOCOL_COMPATIBILITY.md).
85
+
34
86
  ## Install
35
87
 
36
88
  Pick one of three channels.
@@ -91,15 +143,20 @@ In the rest of this README, `./dist/kiro-provider` refers to any of the above; s
91
143
 
92
144
  ## Quickstart
93
145
 
94
- 1. **Get an account into the local store.** Either sign in interactively:
146
+ 1. **Authenticate Kiro through OpenCode.** The default shared-auth mode uses
147
+ OpenCode's live account database:
95
148
 
96
149
  ```bash
97
- ./dist/kiro-provider login
150
+ opencode auth login
98
151
  ```
99
152
 
100
- or import accounts already authenticated by OpenCode:
153
+ Select Kiro and complete the normal login flow. If you intentionally want
154
+ an independent compatibility store instead, set `"auth_source": "local"`
155
+ and then use:
101
156
 
102
157
  ```bash
158
+ ./dist/kiro-provider login
159
+ # or take a one-time snapshot:
103
160
  ./dist/kiro-provider accounts import
104
161
  ```
105
162
 
@@ -117,7 +174,7 @@ In the rest of this README, `./dist/kiro-provider` refers to any of the above; s
117
174
  ./dist/kiro-provider serve
118
175
  ```
119
176
 
120
- 4. **Call it with an OpenAI-compatible client.**
177
+ 4. **Call the default Responses endpoint.**
121
178
 
122
179
  ```bash
123
180
  curl -fsS http://127.0.0.1:8787/v1/models \
@@ -132,15 +189,18 @@ In the rest of this README, `./dist/kiro-provider` refers to any of the above; s
132
189
  apiKey: "sk-your-private-key",
133
190
  });
134
191
 
135
- const completion = await client.chat.completions.create({
192
+ const response = await client.responses.create({
136
193
  model: "auto",
137
- messages: [{ role: "user", content: "Explain this repository." }],
194
+ input: "Explain this repository.",
138
195
  });
139
196
 
140
- console.log(completion.choices[0]?.message.content);
197
+ console.log(response.output_text);
141
198
  ```
142
199
 
143
- Or with the [Vercel AI SDK](https://sdk.vercel.ai/) via `@ai-sdk/openai-compatible`:
200
+ OpenAI-compatible libraries that only implement Chat Completions require
201
+ `"enable_legacy_chat_completions": true` in the gateway config. For example,
202
+ with the [Vercel AI SDK](https://sdk.vercel.ai/) via
203
+ `@ai-sdk/openai-compatible`:
144
204
 
145
205
  ```ts
146
206
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
@@ -158,6 +218,230 @@ In the rest of this README, `./dist/kiro-provider` refers to any of the above; s
158
218
  });
159
219
  ```
160
220
 
221
+ ## Run as a background service
222
+
223
+ For an agent host, run **one long-lived provider per OS user** and point
224
+ compatible OpenAI/Anthropic clients, OpenCode, Zuno, and compatibility probes
225
+ for Codex or Claude Code at that local endpoint.
226
+ Do not start a new provider for every agent or conversation. Keeping one
227
+ process alive lets requests with an explicit affinity key reuse their
228
+ persisted account/Kiro-conversation binding, while all requests can reuse
229
+ process-local, account-scoped SDK clients and transport objects. A request
230
+ without an explicit key gets a fresh Kiro conversation. Kiro model-call HTTP
231
+ sockets are fresh by default (`sdk_http_keep_alive: false`); enabling it is a
232
+ best-effort transport optimization, never a promise that one session owns one
233
+ physical TCP connection.
234
+
235
+ Use a pinned standalone binary for a service rather than fetching through
236
+ `bunx` on every start. The examples below assume the release installers'
237
+ defaults:
238
+
239
+ - binary: `~/.local/bin/kiro-provider` on Linux,
240
+ `%USERPROFILE%\.local\bin\kiro-provider.exe` on Windows;
241
+ - config: `~/.config/kiro-provider/config.json`;
242
+ - service/task name: `kiro-provider`.
243
+
244
+ Run the service as the **same OS user** that ran `opencode auth login`.
245
+ Default `auth_source: "opencode-shared"` resolves that user's OpenCode
246
+ database and home/XDG directories; running as `root`, `LocalSystem`, or
247
+ another user will normally select different credentials. Use absolute paths,
248
+ keep the API key in the protected config file rather than service arguments,
249
+ and set `opencode_auth_db_path` explicitly if the service has a different XDG
250
+ environment.
251
+
252
+ ### Linux: systemd user service
253
+
254
+ Verify the installed binary and config first:
255
+
256
+ ```bash
257
+ test -x "$HOME/.local/bin/kiro-provider"
258
+ test -r "$HOME/.config/kiro-provider/config.json"
259
+ chmod 600 "$HOME/.config/kiro-provider/config.json"
260
+ ```
261
+
262
+ Install a [systemd user service](https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html):
263
+
264
+ ```bash
265
+ SERVICE_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
266
+ install -d -m 700 "$SERVICE_DIR"
267
+ cat > "$SERVICE_DIR/kiro-provider.service" <<'EOF'
268
+ [Unit]
269
+ Description=kiro-provider local Kiro gateway
270
+
271
+ [Service]
272
+ Type=exec
273
+ ExecStart=%h/.local/bin/kiro-provider serve --config %h/.config/kiro-provider/config.json
274
+ Restart=on-failure
275
+ RestartSec=5s
276
+ TimeoutStopSec=30s
277
+ UMask=0077
278
+
279
+ [Install]
280
+ WantedBy=default.target
281
+ EOF
282
+ chmod 600 "$SERVICE_DIR/kiro-provider.service"
283
+
284
+ systemctl --user daemon-reload
285
+ systemctl --user enable --now kiro-provider.service
286
+ ```
287
+
288
+ If the binary or config is elsewhere, replace `ExecStart` with those absolute
289
+ paths. For a custom `XDG_CONFIG_HOME`, also add an explicit
290
+ `Environment=XDG_CONFIG_HOME=/absolute/path` line or configure
291
+ `opencode_auth_db_path`.
292
+
293
+ Operate and inspect the service:
294
+
295
+ ```bash
296
+ systemctl --user is-active kiro-provider.service
297
+ systemctl --user restart kiro-provider.service
298
+ journalctl --user -u kiro-provider.service -n 100 --no-pager
299
+ ```
300
+
301
+ User services normally start with that user's service manager. If the
302
+ provider must start at boot and remain after logout, an administrator may
303
+ enable lingering with `loginctl enable-linger <user>` after reviewing the
304
+ machine's security policy.
305
+
306
+ To remove the unit:
307
+
308
+ ```bash
309
+ systemctl --user disable --now kiro-provider.service
310
+ rm "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/kiro-provider.service"
311
+ systemctl --user daemon-reload
312
+ ```
313
+
314
+ ### Windows: per-user scheduled task
315
+
316
+ `kiro-provider.exe` is a normal foreground executable, not a native Windows
317
+ Service Control Manager executable. Do not register it directly with
318
+ `sc.exe`. The built-in, dependency-free option is a
319
+ [Scheduled Task](https://learn.microsoft.com/powershell/module/scheduledtasks/register-scheduledtask)
320
+ that starts at sign-in, runs as the current user, and restarts after failure.
321
+
322
+ Run the following in PowerShell as the same user that owns the OpenCode
323
+ credentials. It creates a small launcher so stdout/stderr are retained under
324
+ `%LOCALAPPDATA%\kiro-provider`:
325
+
326
+ ```powershell
327
+ $Binary = Join-Path $HOME ".local\bin\kiro-provider.exe"
328
+ $Config = Join-Path $HOME ".config\kiro-provider\config.json"
329
+ $ServiceDir = Join-Path $HOME ".config\kiro-provider"
330
+ $LogDir = Join-Path $env:LOCALAPPDATA "kiro-provider"
331
+ $Launcher = Join-Path $ServiceDir "service.ps1"
332
+
333
+ if (-not (Test-Path -LiteralPath $Binary -PathType Leaf)) {
334
+ throw "kiro-provider binary not found: $Binary"
335
+ }
336
+ if (-not (Test-Path -LiteralPath $Config -PathType Leaf)) {
337
+ throw "kiro-provider config not found: $Config"
338
+ }
339
+
340
+ New-Item -ItemType Directory -Force -Path $ServiceDir, $LogDir | Out-Null
341
+ @'
342
+ $ErrorActionPreference = "Stop"
343
+ $Binary = Join-Path $HOME ".local\bin\kiro-provider.exe"
344
+ $Config = Join-Path $HOME ".config\kiro-provider\config.json"
345
+ $LogDir = Join-Path $env:LOCALAPPDATA "kiro-provider"
346
+ $Log = Join-Path $LogDir "service.log"
347
+ $PreviousLog = Join-Path $LogDir "service.previous.log"
348
+
349
+ New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
350
+ if ((Test-Path -LiteralPath $Log) -and ((Get-Item -LiteralPath $Log).Length -gt 10MB)) {
351
+ Move-Item -Force -LiteralPath $Log -Destination $PreviousLog
352
+ }
353
+
354
+ & $Binary serve --config $Config *>> $Log
355
+ exit $LASTEXITCODE
356
+ '@ | Set-Content -LiteralPath $Launcher -Encoding UTF8
357
+
358
+ $User = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
359
+ $PowerShell = (Get-Command powershell.exe).Source
360
+ $Action = New-ScheduledTaskAction `
361
+ -Execute $PowerShell `
362
+ -Argument ('-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "{0}"' -f $Launcher)
363
+ $Trigger = New-ScheduledTaskTrigger -AtLogOn -User $User
364
+ $Principal = New-ScheduledTaskPrincipal `
365
+ -UserId $User `
366
+ -LogonType Interactive `
367
+ -RunLevel Limited
368
+ $Settings = New-ScheduledTaskSettingsSet `
369
+ -RestartCount 999 `
370
+ -RestartInterval (New-TimeSpan -Minutes 1) `
371
+ -ExecutionTimeLimit ([TimeSpan]::Zero) `
372
+ -MultipleInstances IgnoreNew `
373
+ -AllowStartIfOnBatteries `
374
+ -DontStopIfGoingOnBatteries `
375
+ -StartWhenAvailable
376
+
377
+ Stop-ScheduledTask -TaskName "kiro-provider" -ErrorAction SilentlyContinue
378
+ Register-ScheduledTask `
379
+ -TaskName "kiro-provider" `
380
+ -Action $Action `
381
+ -Trigger $Trigger `
382
+ -Principal $Principal `
383
+ -Settings $Settings `
384
+ -Description "Local AWS Kiro gateway for AI agents" `
385
+ -Force | Out-Null
386
+ Start-ScheduledTask -TaskName "kiro-provider"
387
+ ```
388
+
389
+ Inspect, restart, and follow logs:
390
+
391
+ ```powershell
392
+ Get-ScheduledTask -TaskName "kiro-provider" | Get-ScheduledTaskInfo
393
+ Stop-ScheduledTask -TaskName "kiro-provider"
394
+ Start-ScheduledTask -TaskName "kiro-provider"
395
+ Get-Content "$env:LOCALAPPDATA\kiro-provider\service.log" -Tail 100 -Wait
396
+ ```
397
+
398
+ To remove the task and launcher:
399
+
400
+ ```powershell
401
+ Stop-ScheduledTask -TaskName "kiro-provider" -ErrorAction SilentlyContinue
402
+ Unregister-ScheduledTask -TaskName "kiro-provider" -Confirm:$false
403
+ Remove-Item "$HOME\.config\kiro-provider\service.ps1"
404
+ ```
405
+
406
+ This task intentionally runs only in the current user's interactive session,
407
+ so it can use that user's network access and OpenCode credentials without
408
+ storing a Windows password. A true pre-login Windows service requires a
409
+ service wrapper and a deliberately configured user account; do not run it as
410
+ `LocalSystem` and expect the same OpenCode database.
411
+
412
+ ### Health checks and automation contract
413
+
414
+ After either installation, verify both process liveness and authenticated
415
+ readiness:
416
+
417
+ ```bash
418
+ curl -fsS http://127.0.0.1:8787/health
419
+ curl -fsS http://127.0.0.1:8787/ready \
420
+ -H 'Authorization: Bearer sk-your-private-key'
421
+ ```
422
+
423
+ PowerShell equivalent:
424
+
425
+ ```powershell
426
+ Invoke-RestMethod "http://127.0.0.1:8787/health"
427
+ $Headers = @{ Authorization = "Bearer sk-your-private-key" }
428
+ Invoke-RestMethod "http://127.0.0.1:8787/ready" -Headers $Headers
429
+ ```
430
+
431
+ For an AI agent or installer, treat setup as successful only when:
432
+
433
+ 1. the binary and explicit config path exist;
434
+ 2. the service/task runs as the credential-owning user;
435
+ 3. `/health` succeeds;
436
+ 4. authenticated `/ready` succeeds, proving a readable auth source, at least
437
+ one active account, writable provider state, an available reasoning keyring,
438
+ and coverage for every key ID referenced by an unexpired replay record.
439
+
440
+ Use the fixed service/task name above so repeated setup is idempotent. Restart
441
+ it after changing the config or replacing the binary. Do not make the client
442
+ responsible for starting a private provider process; configure clients only
443
+ with the stable base URL and gateway API key.
444
+
161
445
  ## Configuration
162
446
 
163
447
  Config is loaded from `~/.config/kiro-provider/config.json` (or `$XDG_CONFIG_HOME/kiro-provider/config.json`), overridable by `KIRO_PROVIDER_*` environment variables and, for `serve`, by CLI flags. Precedence is **CLI flag > environment variable > config file > schema default**.
@@ -167,9 +451,21 @@ Config is loaded from `~/.config/kiro-provider/config.json` (or `$XDG_CONFIG_HOM
167
451
  | `host` | `127.0.0.1` | `KIRO_PROVIDER_HOST` |
168
452
  | `port` | `8787` | `KIRO_PROVIDER_PORT` |
169
453
  | `api_keys` | required, non-empty | `KIRO_PROVIDER_API_KEYS` |
454
+ | `enable_legacy_chat_completions` | `false` | `KIRO_PROVIDER_ENABLE_LEGACY_CHAT_COMPLETIONS` |
455
+ | `protocol_projection_mode` | `safe` | `KIRO_PROVIDER_PROTOCOL_PROJECTION_MODE` |
456
+ | `session_affinity_mode` | `explicit-only` | `KIRO_PROVIDER_SESSION_AFFINITY_MODE` |
457
+ | `auth_source` | `opencode-shared` | `KIRO_PROVIDER_AUTH_SOURCE` |
458
+ | `opencode_auth_db_path` | `null` (uses the OpenCode default) | `KIRO_PROVIDER_OPENCODE_AUTH_DB_PATH` |
170
459
  | `proxy_url` | `null` | `KIRO_PROVIDER_PROXY_URL` |
171
460
  | `default_region` | `us-east-1` | `KIRO_PROVIDER_DEFAULT_REGION` |
461
+ | `sdk_http_keep_alive` | `false` | `KIRO_PROVIDER_SDK_HTTP_KEEP_ALIVE` |
172
462
  | `account_selection_strategy` | `lowest-usage` | `KIRO_PROVIDER_ACCOUNT_SELECTION_STRATEGY` |
463
+ | `session_affinity_ttl_ms` | `86400000` | `KIRO_PROVIDER_SESSION_AFFINITY_TTL_MS` |
464
+ | `session_affinity_max_entries` | `10000` | `KIRO_PROVIDER_SESSION_AFFINITY_MAX_ENTRIES` |
465
+ | `reasoning_replay_key_path` | auto-generated config path | `KIRO_PROVIDER_REASONING_REPLAY_KEY_PATH` |
466
+ | `reasoning_replay_keys` | `[]` | `KIRO_PROVIDER_REASONING_REPLAY_KEYS` |
467
+ | `reasoning_replay_ttl_ms` | `86400000` | `KIRO_PROVIDER_REASONING_REPLAY_TTL_MS` |
468
+ | `reasoning_replay_max_entries` | `10000` | `KIRO_PROVIDER_REASONING_REPLAY_MAX_ENTRIES` |
173
469
  | `log_level` | `info` | `KIRO_PROVIDER_LOG_LEVEL` |
174
470
 
175
471
  The full field reference, including retry/timeout tuning and the test-only `test_upstream_endpoint`, lives in [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md).
@@ -180,36 +476,139 @@ Some networks reach one model family directly while another needs a proxy (for e
180
476
 
181
477
  ## Security
182
478
 
183
- - **Fail-closed authentication.** The server will not start without at least one non-empty `api_keys` entry, and every route requires `Authorization: Bearer <key>`.
479
+ - **Fail-closed authentication.** The server will not start without at least one non-empty `api_keys` entry. OpenAI routes require `Authorization: Bearer <key>`; Anthropic routes also accept `x-api-key: <key>`.
184
480
  - **Local bind by default.** `host` defaults to `127.0.0.1`; only bind `0.0.0.0` behind a firewall or authenticated reverse proxy.
185
- - **Locked-down account store.** `accounts.db` (and its WAL/SHM files) are created with mode `0600`.
186
- - **No secrets in logs.** Proxy URLs and account tokens are never printed; don't commit a real config file, account database, or gateway key.
481
+ - **Single authentication authority.** Shared mode reads and updates OpenCode's existing Kiro database and fails closed on an incompatible schema; it never runs provider-owned migrations against that database.
482
+ - **Locked-down provider state.** `accounts.db` (and its WAL/SHM files) are created with mode `0600`; in shared mode this database contains affinity/state, not the authoritative credentials.
483
+ - **Authenticated reasoning replay.** The database stores token/fingerprint hashes and AES-256-GCM ciphertext, not raw `kr1_...` tokens. Missing active decryption keys fail startup.
484
+ - **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.
187
485
 
188
486
  > **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.
189
487
 
190
488
  ## Using with an LLM
191
489
 
192
- Point any OpenAI-compatible client (`openai`, `@ai-sdk/openai-compatible`, LangChain, etc.) at `http://<host>:<port>/v1` with one of your configured `api_keys`.
490
+ Use `POST /v1/responses` for OpenAI Responses clients. Use
491
+ `POST /v1/messages` for Anthropic Messages clients. Only point a
492
+ Chat-Completions-only client (`@ai-sdk/openai-compatible`, older LangChain
493
+ adapters, or an OpenCode custom provider using that package) at
494
+ `POST /v1/chat/completions` after explicitly enabling the legacy endpoint.
495
+
496
+ Standard clients must also stay within the verified subset. In safe mode a
497
+ client that always sends system/developer instructions, custom grammars,
498
+ namespace tools, or Anthropic `cache_control` receives a field-level 400; the
499
+ gateway does not modify that request to force it through Kiro. The optional
500
+ `legacy-user-prefix` projection is a temporary instruction-only migration aid
501
+ for v0.5.x/v0.6.x and is scheduled for removal in v0.7.0.
502
+
503
+ The default `session_affinity_mode: "explicit-only"` never hashes prompt text
504
+ to guess a conversation. Responses checks, in order,
505
+ `metadata.zuno_session_id`, `metadata.kiro_provider_session_id`, compatibility
506
+ `client_metadata.thread_id|session_id|conversation_id`, and
507
+ `prompt_cache_key`. Chat checks only `prompt_cache_key`; Anthropic Messages
508
+ has no verified explicit affinity field. With no key, the request gets a
509
+ fresh Kiro conversation but can still reuse account-scoped SDK clients and
510
+ transport objects. The Kiro SDK's direct/proxy agents use fresh sockets by default;
511
+ set `sdk_http_keep_alive: true` only when the deployment has validated pooled
512
+ socket behavior.
513
+ The temporary `legacy-initial-input` mode restores only the old affinity
514
+ heuristics and logs a startup warning; it does not alter request content.
515
+
516
+ Stateful Responses fields `previous_response_id` and `conversation` are
517
+ rejected until the gateway has a real response-state store, so clients must
518
+ resend the complete input.
193
519
 
194
520
  <details>
195
521
  <summary>Agent command reference</summary>
196
522
 
197
523
  - `kiro-provider serve [--config <path>] [--host <host>] [--port <port>] [--proxy <url>]` — start the gateway.
198
- - `kiro-provider login [--config <path>] [--start-url <url>] [--region <region>]` — device-code login (AWS Builder ID, or IAM Identity Center with `--start-url`).
199
- - `kiro-provider accounts list` — list stored accounts and their health.
200
- - `kiro-provider accounts import [--from <path>] [--config <path>]` — import accounts from an OpenCode `kiro.db` (default source: `~/.config/opencode/kiro.db`).
201
- - `kiro-provider accounts remove <id|email>` — remove one account (writes a tombstone).
524
+ - `kiro-provider login [--config <path>] [--start-url <url>] [--region <region>]` — local compatibility mode only; shared mode directs you to `opencode auth login`.
525
+ - `kiro-provider accounts list` — list accounts in the local compatibility store.
526
+ - `kiro-provider accounts import [--from <path>] [--config <path>]` — take a one-time snapshot into the local compatibility store.
527
+ - `kiro-provider accounts remove <id|email>` — remove one account from the local compatibility store.
202
528
 
203
- Contract: human-readable status lines go to stdout, errors to stderr, non-zero exit on failure. `GET /v1/models` and `GET /health` return structured JSON.
529
+ 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.
204
530
 
205
531
  </details>
206
532
 
533
+ ## Use with Zuno
534
+
535
+ Run one compiled kiro-provider service as the credential-owning OS user, then
536
+ configure Zuno's native Rust OpenAI transport. No Node package, AI SDK, private
537
+ header, or provider-spawn hook is required:
538
+
539
+ ```json
540
+ {
541
+ "model": "kiro/auto",
542
+ "small_model": "kiro/auto",
543
+ "provider": {
544
+ "kiro": {
545
+ "name": "Local kiro-provider",
546
+ "transport": "openai",
547
+ "surface": "responses",
548
+ "env": ["KIRO_GATEWAY_API_KEY"],
549
+ "options": {
550
+ "baseURL": "http://127.0.0.1:8787/v1",
551
+ "maxTokens": null
552
+ },
553
+ "models": {
554
+ "auto": {
555
+ "name": "Kiro Auto",
556
+ "reasoning": true,
557
+ "tool_call": true
558
+ }
559
+ }
560
+ }
561
+ }
562
+ }
563
+ ```
564
+
565
+ Set `KIRO_GATEWAY_API_KEY` to one key from the provider's `api_keys`, then
566
+ verify the native route:
567
+
568
+ ```bash
569
+ export KIRO_GATEWAY_API_KEY='sk-your-private-key'
570
+ zuno debug config
571
+ zuno models kiro --verbose
572
+ ```
573
+
574
+ The matching Zuno OpenAI Responses transport maps the durable Zuno session ID
575
+ to standard `metadata.zuno_session_id` on every main turn and tool
576
+ continuation. It does not add that ID to input, messages, instructions, tool
577
+ descriptions, or any other model-visible field; internal title/summary calls
578
+ do not join the main provider conversation. Therefore one Zuno session is
579
+ serialized onto one persisted account/Kiro-conversation binding, while
580
+ different sessions remain isolated even if their first prompt and upstream
581
+ tool aliases are identical. Tool declaration and alias state remains local to
582
+ each request.
583
+
584
+ Keep `surface: "responses"` for this integration. Selecting `chat` requires
585
+ the separately enabled legacy endpoint and does not carry the Zuno Responses
586
+ session metadata.
587
+
588
+ Current Zuno sends agent instructions, while the live Kiro
589
+ `additionalContext` probe did not prove a lossless instruction projection.
590
+ Consequently the verified functional path currently requires the provider's
591
+ explicit `protocol_projection_mode: "legacy-user-prefix"`; `safe` correctly
592
+ returns `unsupported_instruction_projection` and never rewrites the request.
593
+ Set Zuno `options.maxTokens` to `null` as shown so its generic layer does not
594
+ add the unsupported `max_output_tokens: 32000`. Neither setting uses a private
595
+ Header or client-side prompt patch; the legacy mode is an explicit migration
596
+ exception scheduled for removal in v0.7.0.
597
+
207
598
  ## Use with Codex CLI
208
599
 
209
- kiro-provider's `POST /v1/responses` endpoint speaks the OpenAI Responses wire format, so [Codex CLI](https://github.com/openai/codex) (verified against 0.144.6) can use it as a custom `model_provider` with `wire_api = "responses"`. Test it with an isolated `CODEX_HOME` so your real `~/.codex` config is never touched:
600
+ Codex uses the correct Responses endpoint, but the current compiled RC gate
601
+ against 0.149.0-alpha.4.1 does not pass: Codex sends
602
+ `parallel_tool_calls: false`, and Kiro cannot guarantee that constraint. The
603
+ provider returns `unsupported_parallel_tool_calls` before Kiro instead of
604
+ ignoring the field. The following isolated configuration reproduces the
605
+ compatibility check without touching the real `~/.codex` state:
210
606
 
211
607
  ```bash
212
- export CODEX_HOME="$(mktemp -d)" # isolated; your real ~/.codex is untouched
608
+ export CODEX_TEST_ROOT="$(mktemp -d)"
609
+ export CODEX_HOME="$CODEX_TEST_ROOT/home"
610
+ export CODEX_SQLITE_HOME="$CODEX_TEST_ROOT/sqlite"
611
+ mkdir -p "$CODEX_HOME" "$CODEX_SQLITE_HOME"
213
612
  export LOCALGW_KEY="sk-...your gateway api key..."
214
613
  cat > "$CODEX_HOME/config.toml" <<'EOF'
215
614
  model = "gpt-5.6-sol"
@@ -223,14 +622,48 @@ EOF
223
622
  codex exec --skip-git-repo-check "say hi"
224
623
  ```
225
624
 
226
- Requires the gateway running (`kiro-provider serve`) with an account already imported or logged in. Reasoning models work through Codex the same way they do for `/v1/chat/completions` (Claude via your configured proxy, GPT direct). Full details, plus a ready-made isolated smoke test (`scripts/codex-smoke.sh`), live in [`docs/CODEX.md`](docs/CODEX.md).
625
+ For Codex 0.149.0-alpha.4.1 the expected RC result is a non-zero exit carrying
626
+ that field-level error. A future successful basic request must still pass a
627
+ real shell/custom-tool loop, continuation, and restart reasoning replay before
628
+ Codex is marked supported. Full details live in
629
+ [`docs/CODEX.md`](docs/CODEX.md).
630
+
631
+ ## Use with Claude Code
632
+
633
+ Claude Code uses Anthropic Messages, but Claude Code 2.1.209 currently sends
634
+ `output_config.format` and `context_management`; after the first rejection it
635
+ also retries with `system` inside `messages.1.role`, which is not a valid
636
+ Anthropic Messages role and cannot be silently moved. The provider rejects
637
+ these shapes before Kiro in both safe and legacy instruction modes. The
638
+ standard configuration below is therefore a compatibility probe, not a
639
+ current support claim:
640
+
641
+ ```bash
642
+ export ANTHROPIC_BASE_URL="http://127.0.0.1:8787"
643
+ export ANTHROPIC_AUTH_TOKEN="sk-your-private-key"
644
+ claude
645
+ ```
646
+
647
+ The gateway accepts either `Authorization: Bearer <key>` or `x-api-key:
648
+ <key>` for Anthropic routes. Direct Messages requests within the verified
649
+ subset support typed JSON/SSE and tools, while `/v1/messages/count_tokens` is
650
+ an explicit estimate. See
651
+ [`docs/CLAUDE_CODE.md`](docs/CLAUDE_CODE.md).
652
+
653
+ The current compiled-service validation record is in
654
+ [`docs/audits/kiro-provider-v0.5.0-rc.1-validation-2026-08-26.md`](docs/audits/kiro-provider-v0.5.0-rc.1-validation-2026-08-26.md).
655
+ The older [`docs/E2E_VALIDATION_2026-08-22.md`](docs/E2E_VALIDATION_2026-08-22.md)
656
+ is retained as historical v0.4 evidence only.
227
657
 
228
658
  ## Development
229
659
 
230
660
  ```bash
231
661
  bun install
662
+ bun run lint
232
663
  bun run typecheck
233
664
  bun test
665
+ bun run build
666
+ bun run build:binary
234
667
  bash scripts/security-check.sh # security regression suite (Linux, needs openssl/curl/ss)
235
668
  ```
236
669