@sunerpy/kiro-provider 0.4.0 → 0.5.0-rc.2

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 +202 -37
  2. package/dist/cli.js +131 -91
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # kiro-provider
2
2
 
3
- > A standalone OpenAI Responses and Anthropic Messages gateway for AWS Kiro (CodeWhisperer).
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,6 +12,7 @@
12
12
  ## Table of Contents
13
13
 
14
14
  - [Features](#features)
15
+ - [Protocol compatibility](#protocol-compatibility)
15
16
  - [Install](#install)
16
17
  - [Quickstart](#quickstart)
17
18
  - [Run as a background service](#run-as-a-background-service)
@@ -19,6 +20,7 @@
19
20
  - [Proxy](#proxy)
20
21
  - [Security](#security)
21
22
  - [Using with an LLM](#using-with-an-llm)
23
+ - [Use with Zuno](#use-with-zuno)
22
24
  - [Use with Codex CLI](#use-with-codex-cli)
23
25
  - [Use with Claude Code](#use-with-claude-code)
24
26
  - [Development](#development)
@@ -29,15 +31,67 @@
29
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`.
30
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`.
31
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`.
32
- - 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 a refresh lock compatible with `opencode-kiro-auth` v0.20.6.
33
- - Standard-field session affinity: Codex/OpenAI, OpenCode, and Claude Code requests reuse a persisted account binding and Kiro conversation ID without private headers, cookies, or client patches.
34
- - Account-scoped scheduling and keep-alive transport pools: unrelated accounts can run concurrently, while one account is protected from overlapping Kiro streams; access-token refresh updates the cached client instead of rebuilding its connection pool.
35
- - Zero provider-owned prompt injection: request adapters preserve client text and structured protocol fields, and reject unsupported guarantees instead of emulating them with hidden instructions.
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
38
+ input IR preserves client text, roles, content-block boundaries, tool
39
+ identity, ordering, and source paths; Kiro output is normalized into a
40
+ separate canonical completion/event IR before protocol-specific encoding.
41
+ - 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.
36
42
  - 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.
37
43
  - 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.
38
44
  - A single global `proxy_url` that, when set, routes all upstream egress (model requests, token refresh, device-code login) through one HTTP(S) proxy.
39
45
  - Ships as a self-contained compiled binary via `bun build --compile` — no runtime install required on the target machine.
40
46
 
47
+ ## Protocol compatibility
48
+
49
+ v0.5 is intentionally a **verified compatibility subset**. It does not accept
50
+ fields and silently discard them. The default `protocol_projection_mode:
51
+ "safe"` never prepends or rewrites client instructions, merges adjacent
52
+ messages, clears repeated assistant output, removes trailing text such as `{`,
53
+ or creates model-visible compensation prose.
54
+
55
+ Key boundaries:
56
+
57
+ - plain text, consecutive same-role turns, function/custom tool declarations,
58
+ calls, and results retain their original structure and order;
59
+ - a message containing multiple top-level text blocks returns
60
+ `unsupported_content_block_projection`, because Kiro exposes only one text
61
+ field and concatenation would erase block boundaries;
62
+ - `instructions`, `system`, and `developer` return
63
+ `unsupported_instruction_projection` in safe mode because the tested Kiro
64
+ `additionalContext` channel was rejected;
65
+ - `tool_choice: auto` is supported; `parallel_tool_calls: false` is accepted as
66
+ a no-op only when no callable tool can run (including `tool_choice: none`),
67
+ and otherwise returns `unsupported_parallel_tool_calls`; required/named
68
+ choice, strict schemas, custom grammars, and namespace tools are rejected
69
+ rather than weakened;
70
+ - base64/data-URL images are supported, while remote image URLs and detail
71
+ controls are rejected;
72
+ - an output-token limit is probe-confirmed only for `claude-sonnet-5` variants
73
+ in the range 1,024–128,000;
74
+ - stateful Responses fields and native Web Search remain unsupported, and the
75
+ provider never fabricates search/citation events.
76
+
77
+ Current compiled-binary acceptance on 2026-08-27: OpenAI JavaScript SDK 7.5.0
78
+ passes Responses, explicit Chat, function/custom tool loops, and encrypted
79
+ reasoning replay across restart. OpenCode Responses passes only in explicit
80
+ `legacy-user-prefix` mode with Claude Sonnet 5; OpenCode Chat is blocked by
81
+ its nonstandard `cache_control`. Codex 0.149.0-alpha.4.1 is blocked by
82
+ `text.verbosity`; its captured request also contains further unsupported
83
+ reasoning, tool-serialization, grammar, and namespace controls. Claude Code
84
+ 2.1.209 first sends unsupported `output_config.format`, then retries with
85
+ invalid `system` in `messages.1.role`; an earlier redacted capture also
86
+ contained `context_management`. Zuno was intentionally not rerun for RC.2,
87
+ and no Zuno source or configuration was changed for this release. These are
88
+ RC findings; stable v0.5.0 remains gated rather than silently discarding or
89
+ relocating those fields.
90
+
91
+ For the complete capability matrix, error codes, reasoning replay contract,
92
+ and v0.4 migration steps, see
93
+ [`docs/PROTOCOL_COMPATIBILITY.md`](docs/PROTOCOL_COMPATIBILITY.md).
94
+
41
95
  ## Install
42
96
 
43
97
  Pick one of three channels.
@@ -176,12 +230,16 @@ In the rest of this README, `./dist/kiro-provider` refers to any of the above; s
176
230
  ## Run as a background service
177
231
 
178
232
  For an agent host, run **one long-lived provider per OS user** and point
179
- Codex, OpenCode, Claude Code, Zuno, and other clients at that local endpoint.
233
+ compatible OpenAI/Anthropic clients, OpenCode, Zuno, and compatibility probes
234
+ for Codex or Claude Code at that local endpoint.
180
235
  Do not start a new provider for every agent or conversation. Keeping one
181
- process alive lets those standard clients share the provider's persisted
182
- session/account affinity and its process-local, account-scoped keep-alive
183
- pools. This remains best-effort connection reuse, not a promise that every
184
- request uses one physical TCP connection.
236
+ process alive lets requests with an explicit affinity key reuse their
237
+ persisted account/Kiro-conversation binding, while all requests can reuse
238
+ process-local, account-scoped SDK clients and transport objects. A request
239
+ without an explicit key gets a fresh Kiro conversation. Kiro model-call HTTP
240
+ sockets are fresh by default (`sdk_http_keep_alive: false`); enabling it is a
241
+ best-effort transport optimization, never a promise that one session owns one
242
+ physical TCP connection.
185
243
 
186
244
  Use a pinned standalone binary for a service rather than fetching through
187
245
  `bunx` on every start. The examples below assume the release installers'
@@ -384,8 +442,9 @@ For an AI agent or installer, treat setup as successful only when:
384
442
  1. the binary and explicit config path exist;
385
443
  2. the service/task runs as the credential-owning user;
386
444
  3. `/health` succeeds;
387
- 4. authenticated `/ready` succeeds, proving a readable auth source and at
388
- least one active account.
445
+ 4. authenticated `/ready` succeeds, proving a readable auth source, at least
446
+ one active account, writable provider state, an available reasoning keyring,
447
+ and coverage for every key ID referenced by an unexpired replay record.
389
448
 
390
449
  Use the fixed service/task name above so repeated setup is idempotent. Restart
391
450
  it after changing the config or replacing the binary. Do not make the client
@@ -402,13 +461,20 @@ Config is loaded from `~/.config/kiro-provider/config.json` (or `$XDG_CONFIG_HOM
402
461
  | `port` | `8787` | `KIRO_PROVIDER_PORT` |
403
462
  | `api_keys` | required, non-empty | `KIRO_PROVIDER_API_KEYS` |
404
463
  | `enable_legacy_chat_completions` | `false` | `KIRO_PROVIDER_ENABLE_LEGACY_CHAT_COMPLETIONS` |
464
+ | `protocol_projection_mode` | `safe` | `KIRO_PROVIDER_PROTOCOL_PROJECTION_MODE` |
465
+ | `session_affinity_mode` | `explicit-only` | `KIRO_PROVIDER_SESSION_AFFINITY_MODE` |
405
466
  | `auth_source` | `opencode-shared` | `KIRO_PROVIDER_AUTH_SOURCE` |
406
467
  | `opencode_auth_db_path` | `null` (uses the OpenCode default) | `KIRO_PROVIDER_OPENCODE_AUTH_DB_PATH` |
407
468
  | `proxy_url` | `null` | `KIRO_PROVIDER_PROXY_URL` |
408
469
  | `default_region` | `us-east-1` | `KIRO_PROVIDER_DEFAULT_REGION` |
470
+ | `sdk_http_keep_alive` | `false` | `KIRO_PROVIDER_SDK_HTTP_KEEP_ALIVE` |
409
471
  | `account_selection_strategy` | `lowest-usage` | `KIRO_PROVIDER_ACCOUNT_SELECTION_STRATEGY` |
410
472
  | `session_affinity_ttl_ms` | `86400000` | `KIRO_PROVIDER_SESSION_AFFINITY_TTL_MS` |
411
473
  | `session_affinity_max_entries` | `10000` | `KIRO_PROVIDER_SESSION_AFFINITY_MAX_ENTRIES` |
474
+ | `reasoning_replay_key_path` | auto-generated config path | `KIRO_PROVIDER_REASONING_REPLAY_KEY_PATH` |
475
+ | `reasoning_replay_keys` | `[]` | `KIRO_PROVIDER_REASONING_REPLAY_KEYS` |
476
+ | `reasoning_replay_ttl_ms` | `86400000` | `KIRO_PROVIDER_REASONING_REPLAY_TTL_MS` |
477
+ | `reasoning_replay_max_entries` | `10000` | `KIRO_PROVIDER_REASONING_REPLAY_MAX_ENTRIES` |
412
478
  | `log_level` | `info` | `KIRO_PROVIDER_LOG_LEVEL` |
413
479
 
414
480
  The full field reference, including retry/timeout tuning and the test-only `test_upstream_endpoint`, lives in [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md).
@@ -423,26 +489,42 @@ Some networks reach one model family directly while another needs a proxy (for e
423
489
  - **Local bind by default.** `host` defaults to `127.0.0.1`; only bind `0.0.0.0` behind a firewall or authenticated reverse proxy.
424
490
  - **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.
425
491
  - **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.
426
- - **No secrets in logs.** Proxy URLs and account tokens are never printed; don't commit a real config file, account database, or gateway key.
492
+ - **Authenticated reasoning replay.** The database stores token/fingerprint hashes and AES-256-GCM ciphertext, not raw `kr1_...` tokens. Missing active decryption keys fail startup.
493
+ - **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.
427
494
 
428
495
  > **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.
429
496
 
430
497
  ## Using with an LLM
431
498
 
432
- Use `POST /v1/responses` for new OpenAI clients and Codex. Use
433
- `POST /v1/messages` for Anthropic clients and Claude Code. Only point a
499
+ Use `POST /v1/responses` for OpenAI Responses clients. Use
500
+ `POST /v1/messages` for Anthropic Messages clients. Only point a
434
501
  Chat-Completions-only client (`@ai-sdk/openai-compatible`, older LangChain
435
502
  adapters, or an OpenCode custom provider using that package) at
436
503
  `POST /v1/chat/completions` after explicitly enabling the legacy endpoint.
437
504
 
438
- No client-specific session extension is required. The gateway derives
439
- affinity from standard/native request fields when present and otherwise from
440
- the initial user turn, stores only an irreversible key hash, and persists the
441
- selected account plus Kiro conversation ID. Connection reuse is best-effort
442
- through an account-scoped keep-alive pool; HTTP and upstream behavior can
443
- still select a different physical socket. Stateful Responses fields
444
- `previous_response_id` and `conversation` are rejected until the gateway has
445
- a real response-state store, so clients must resend the complete input.
505
+ Standard clients must also stay within the verified subset. In safe mode a
506
+ client that always sends system/developer instructions, custom grammars,
507
+ namespace tools, or Anthropic `cache_control` receives a field-level 400; the
508
+ gateway does not modify that request to force it through Kiro. The optional
509
+ `legacy-user-prefix` projection is a temporary instruction-only migration aid
510
+ for v0.5.x/v0.6.x and is scheduled for removal in v0.7.0.
511
+
512
+ The default `session_affinity_mode: "explicit-only"` never hashes prompt text
513
+ to guess a conversation. Responses checks, in order,
514
+ `metadata.zuno_session_id`, `metadata.kiro_provider_session_id`, compatibility
515
+ `client_metadata.thread_id|session_id|conversation_id`, and
516
+ `prompt_cache_key`. Chat checks only `prompt_cache_key`; Anthropic Messages
517
+ has no verified explicit affinity field. With no key, the request gets a
518
+ fresh Kiro conversation but can still reuse account-scoped SDK clients and
519
+ transport objects. The Kiro SDK's direct/proxy agents use fresh sockets by default;
520
+ set `sdk_http_keep_alive: true` only when the deployment has validated pooled
521
+ socket behavior.
522
+ The temporary `legacy-initial-input` mode restores only the old affinity
523
+ heuristics and logs a startup warning; it does not alter request content.
524
+
525
+ Stateful Responses fields `previous_response_id` and `conversation` are
526
+ rejected until the gateway has a real response-state store, so clients must
527
+ resend the complete input.
446
528
 
447
529
  <details>
448
530
  <summary>Agent command reference</summary>
@@ -457,9 +539,83 @@ Contract: human-readable status lines go to stdout, errors to stderr, non-zero e
457
539
 
458
540
  </details>
459
541
 
542
+ ## Use with Zuno
543
+
544
+ Run one compiled kiro-provider service as the credential-owning OS user, then
545
+ configure Zuno's native Rust OpenAI transport. No Node package, AI SDK, private
546
+ header, or provider-spawn hook is required:
547
+
548
+ ```json
549
+ {
550
+ "model": "kiro/auto",
551
+ "small_model": "kiro/auto",
552
+ "provider": {
553
+ "kiro": {
554
+ "name": "Local kiro-provider",
555
+ "transport": "openai",
556
+ "surface": "responses",
557
+ "env": ["KIRO_GATEWAY_API_KEY"],
558
+ "options": {
559
+ "baseURL": "http://127.0.0.1:8787/v1",
560
+ "maxTokens": null
561
+ },
562
+ "models": {
563
+ "auto": {
564
+ "name": "Kiro Auto",
565
+ "reasoning": true,
566
+ "tool_call": true
567
+ }
568
+ }
569
+ }
570
+ }
571
+ }
572
+ ```
573
+
574
+ Set `KIRO_GATEWAY_API_KEY` to one key from the provider's `api_keys`, then
575
+ verify the native route:
576
+
577
+ ```bash
578
+ export KIRO_GATEWAY_API_KEY='sk-your-private-key'
579
+ zuno debug config
580
+ zuno models kiro --verbose
581
+ ```
582
+
583
+ The matching Zuno OpenAI Responses transport maps the durable Zuno session ID
584
+ to standard `metadata.zuno_session_id` on every main turn and tool
585
+ continuation. It does not add that ID to input, messages, instructions, tool
586
+ descriptions, or any other model-visible field; internal title/summary calls
587
+ do not join the main provider conversation. Therefore one Zuno session is
588
+ serialized onto one persisted account/Kiro-conversation binding, while
589
+ different sessions remain isolated even if their first prompt and upstream
590
+ tool aliases are identical. Tool declaration and alias state remains local to
591
+ each request.
592
+
593
+ Keep `surface: "responses"` for this integration. Selecting `chat` requires
594
+ the separately enabled legacy endpoint and does not carry the Zuno Responses
595
+ session metadata.
596
+
597
+ Current Zuno sends agent instructions, while the live Kiro
598
+ `additionalContext` probe did not prove a lossless instruction projection.
599
+ Consequently the verified functional path currently requires the provider's
600
+ explicit `protocol_projection_mode: "legacy-user-prefix"`; `safe` correctly
601
+ returns `unsupported_instruction_projection` and never rewrites the request.
602
+ Set Zuno `options.maxTokens` to `null` as shown so its generic layer does not
603
+ add the unsupported `max_output_tokens: 32000`. Neither setting uses a private
604
+ Header or client-side prompt patch; the legacy mode is an explicit migration
605
+ exception scheduled for removal in v0.7.0.
606
+
460
607
  ## Use with Codex CLI
461
608
 
462
- kiro-provider's `POST /v1/responses` endpoint speaks the OpenAI Responses wire format, so [Codex CLI](https://github.com/openai/codex) (verified end-to-end against 0.149.0-alpha.4.1 on 2026-08-22) 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:
609
+ Codex uses the correct Responses endpoint, but the compiled RC.2 gate against
610
+ 0.149.0-alpha.4.1 does not pass. Its first field-level failure is
611
+ `text.verbosity`, which has no proven Kiro equivalent, so the provider returns
612
+ `unsupported_parameter` with `param: "text.verbosity"` before Kiro. The
613
+ redacted captured request also contains `reasoning.context`,
614
+ `parallel_tool_calls: false` while callable additional tools are active, and
615
+ custom grammar/namespace semantics. The provider does not strip these fields
616
+ or simulate them with prompt text. The following isolated configuration
617
+ reproduces the compatibility check without touching the real `~/.codex`
618
+ state:
463
619
 
464
620
  ```bash
465
621
  export CODEX_TEST_ROOT="$(mktemp -d)"
@@ -479,16 +635,22 @@ EOF
479
635
  codex exec --skip-git-repo-check "say hi"
480
636
  ```
481
637
 
482
- Requires the gateway running (`kiro-provider serve`) with at least one active
483
- OpenCode Kiro account in the default shared mode, or an account in the
484
- explicit local compatibility store. Full details, plus a ready-made isolated
485
- smoke test (`scripts/codex-smoke.sh`), live in
638
+ For Codex 0.149.0-alpha.4.1 the expected RC.2 result is a non-zero exit with
639
+ the `text.verbosity` field-level error. A future supported request shape must
640
+ then pass a real shell/custom-tool loop, continuation, and restart reasoning
641
+ replay before Codex is marked supported. Full details live in
486
642
  [`docs/CODEX.md`](docs/CODEX.md).
487
643
 
488
644
  ## Use with Claude Code
489
645
 
490
- Claude Code uses the Anthropic Messages protocol rather than OpenAI Chat
491
- Completions. Point it at the gateway root:
646
+ Claude Code uses Anthropic Messages, but the final Claude Code 2.1.209 RC.2
647
+ run first sent unsupported `output_config.format` and then retried with
648
+ `system` inside `messages.1.role`. That role is invalid in Anthropic Messages
649
+ and cannot be silently moved. An earlier redacted capture from the same
650
+ version also contained `context_management`. The provider rejects these
651
+ shapes before Kiro in both safe and legacy instruction modes. The standard
652
+ configuration below is therefore a compatibility probe, not a current
653
+ support claim:
492
654
 
493
655
  ```bash
494
656
  export ANTHROPIC_BASE_URL="http://127.0.0.1:8787"
@@ -497,22 +659,25 @@ claude
497
659
  ```
498
660
 
499
661
  The gateway accepts either `Authorization: Bearer <key>` or `x-api-key:
500
- <key>` for Anthropic routes. Streaming text and tool calls are translated to
501
- Anthropic SSE. Extended-thinking signatures are not fabricated or exposed,
502
- and `/v1/messages/count_tokens` is an explicit estimate (the response carries
503
- `x-kiro-token-count-mode: estimate`). See
662
+ <key>` for Anthropic routes. Direct Messages requests within the verified
663
+ subset support typed JSON/SSE and tools, while `/v1/messages/count_tokens` is
664
+ an explicit estimate. See
504
665
  [`docs/CLAUDE_CODE.md`](docs/CLAUDE_CODE.md).
505
666
 
506
- The real-client validation record for OpenCode, Codex, Claude Code, shared
507
- authentication, affinity reuse, and the legacy Chat gate is in
508
- [`docs/E2E_VALIDATION_2026-08-22.md`](docs/E2E_VALIDATION_2026-08-22.md).
667
+ The current compiled-service validation record is in
668
+ [`docs/audits/kiro-provider-v0.5.0-rc.2-validation-2026-08-27.md`](docs/audits/kiro-provider-v0.5.0-rc.2-validation-2026-08-27.md).
669
+ The older [`docs/E2E_VALIDATION_2026-08-22.md`](docs/E2E_VALIDATION_2026-08-22.md)
670
+ is retained as historical v0.4 evidence only.
509
671
 
510
672
  ## Development
511
673
 
512
674
  ```bash
513
675
  bun install
676
+ bun run lint
514
677
  bun run typecheck
515
678
  bun test
679
+ bun run build
680
+ bun run build:binary
516
681
  bash scripts/security-check.sh # security regression suite (Linux, needs openssl/curl/ss)
517
682
  ```
518
683