Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs-site/src/content/docs/fr/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@ Si Kiro s’arrête sans appeler l’outil d’achèvement, l’adaptateur effec

### Effort de raisonnement

`gpt-5.6-sol` et `claude-opus-5` prennent en charge nativement un niveau d’effort vérifié, mais chaque famille de modèles nomme différemment le champ de la requête. La valeur sélectionnée `low`, `medium`, `high`, `xhigh` ou `max` est envoyée dans `additionalModelRequestFields.reasoning.effort` pour `gpt-5.6-sol`, et dans `additionalModelRequestFields.output_config.effort` pour `claude-opus-5`. Les autres modèles Kiro utilisent actuellement un raisonnement émulé : opencodex convertit le niveau choisi en instructions de réflexion bornées dans le contenu utilisateur, car leur champ d’effort natif n’a pas été vérifié. La présence d’un contrôle d’effort annoncé sur ces modèles ne prouve donc pas la prise en charge native du raisonnement en amont.
Les modèles GPT-5.6 utilisent `additionalModelRequestFields.reasoning.effort`, et `claude-opus-5`
utilise `additionalModelRequestFields.output_config.effort`. Pour `gpt-5.6-luna` et `gpt-5.6-terra`,
seuls `low`, `medium`, `high` et `max` empruntent le chemin natif vérifié. Leur niveau `xhigh`
conserve les instructions de réflexion bornées existantes, car ce niveau natif n’a pas été vérifié.
`gpt-5.6-sol` et `claude-opus-5` conservent leurs niveaux natifs existants : `low`, `medium`, `high`,
`xhigh` et `max`. Les autres modèles Kiro utilisent une émulation ; un réglage d’effort ne prouve pas une prise en charge native.

## `cursor`

Expand Down
10 changes: 6 additions & 4 deletions docs-site/src/content/docs/ja/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,12 @@ filtered incomplete になります。実際のツール呼び出しを伴わな

### Reasoning effort

`gpt-5.6-sol` と `claude-opus-5` はネイティブ effort をサポートし、リクエストフィールド名が異なります。
`low` / `medium` / `high` / `xhigh` / `max` は、前者では
`additionalModelRequestFields.reasoning.effort`、後者では `output_config.effort` として送信されます。

GPT-5.6 系は `additionalModelRequestFields.reasoning.effort`、`claude-opus-5` は
`additionalModelRequestFields.output_config.effort` を使用します。`gpt-5.6-luna` と
`gpt-5.6-terra` では、検証済みの `low`、`medium`、`high`、`max` だけをネイティブフィールドで送信します。
両モデルの `xhigh` は未検証のため、従来の上限付き thinking 指示によるエミュレーションを維持します。
`gpt-5.6-sol` と `claude-opus-5` の既存のネイティブ段階(`low`、`medium`、`high`、`xhigh`、`max`)は変更しません。
その他の Kiro モデルはエミュレーションを使用し、effort の選択肢だけではネイティブ対応を意味しません。

## `cursor`

Expand Down
10 changes: 6 additions & 4 deletions docs-site/src/content/docs/ko/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,12 @@ commentary로 유지하고 비공개 완료 툴을 한 번 검증합니다.

### Reasoning effort

`gpt-5.6-sol`과 `claude-opus-5`는 네이티브 effort를 지원하며 요청 필드 이름이 다릅니다.
`low` / `medium` / `high` / `xhigh` / `max` 값은 각각
`additionalModelRequestFields.reasoning.effort`와 `output_config.effort`로 전송됩니다.

GPT-5.6 계열은 `additionalModelRequestFields.reasoning.effort`를, `claude-opus-5`는
`additionalModelRequestFields.output_config.effort`를 사용합니다. `gpt-5.6-luna`와
`gpt-5.6-terra`는 검증된 `low`, `medium`, `high`, `max`만 네이티브 필드로 전송합니다.
두 모델의 `xhigh`는 네이티브 동작이 검증되지 않아 기존의 제한된 thinking 지시문 방식을 유지합니다.
`gpt-5.6-sol`과 `claude-opus-5`의 기존 네이티브 단계(`low`, `medium`, `high`, `xhigh`, `max`)는
바뀌지 않습니다. 다른 Kiro 모델의 effort는 에뮬레이션이며, 조절 항목이 있다고 네이티브 지원을 뜻하지는 않습니다.

## `cursor`

Expand Down
14 changes: 7 additions & 7 deletions docs-site/src/content/docs/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,13 @@ important than cosmetic de-duplication. Tool-free requests retain normal text co

### Reasoning effort

`gpt-5.6-sol` and `claude-opus-5` have verified native effort support, and each model family names
the request field differently. A selected `low`, `medium`, `high`, `xhigh`, or `max` value is sent
as `additionalModelRequestFields.reasoning.effort` for `gpt-5.6-sol` and as
`additionalModelRequestFields.output_config.effort` for `claude-opus-5`. Other Kiro models currently
use emulated reasoning: opencodex converts the selected level into bounded thinking instructions in
the user content because their native effort field has not been verified. Do not interpret an
advertised effort control on those models as proof of upstream-native reasoning support.
The GPT-5.6 family uses `additionalModelRequestFields.reasoning.effort`; `claude-opus-5`
uses `additionalModelRequestFields.output_config.effort`. For `gpt-5.6-luna` and
`gpt-5.6-terra`, only `low`, `medium`, `high`, and `max` use the verified native path.
Their `xhigh` selection retains the previous bounded thinking instructions in user content
because that native rung has not been verified. `gpt-5.6-sol` and `claude-opus-5` keep
their existing native `low`, `medium`, `high`, `xhigh`, and `max` behavior. Other Kiro
models use emulated reasoning; an advertised effort control is not proof of native support.

## `cursor`

Expand Down
10 changes: 6 additions & 4 deletions docs-site/src/content/docs/ru/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,12 @@ incomplete. `TOOL_USE` без фактического вызова инстру

### Reasoning effort

`gpt-5.6-sol` и `claude-opus-5` поддерживают нативный effort, но называют поле запроса по-разному.
Значения `low` / `medium` / `high` / `xhigh` / `max` отправляются как
`additionalModelRequestFields.reasoning.effort` и `output_config.effort` соответственно.

Семейство GPT-5.6 использует `additionalModelRequestFields.reasoning.effort`, а `claude-opus-5` —
`additionalModelRequestFields.output_config.effort`. Для `gpt-5.6-luna` и `gpt-5.6-terra` нативный
путь проверен только для `low`, `medium`, `high` и `max`. Их `xhigh` сохраняет прежнюю эмуляцию
через ограниченные инструкции thinking, поскольку нативный уровень не проверен.
Существующие нативные уровни `gpt-5.6-sol` и `claude-opus-5` (`low`, `medium`, `high`, `xhigh`, `max`)
не меняются. Остальные модели Kiro используют эмуляцию; наличие настройки effort не доказывает нативную поддержку.

## `cursor`

Expand Down
15 changes: 6 additions & 9 deletions docs-site/src/content/docs/tr/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,15 +268,12 @@ tam olarak tekrarlasa bile, çünkü aşama doğruluğu kozmetik tekilleştirmed

### Akıl yürütme çabası

`gpt-5.6-sol` ve `claude-opus-5` doğrulanmış yerel çaba desteğine sahiptir ve
her model ailesi istek alanını farklı şekilde adlandırır. Seçilen `low`,
`medium`, `high`, `xhigh` veya `max` değeri `gpt-5.6-sol` için
`additionalModelRequestFields.reasoning.effort` olarak ve `claude-opus-5` için
`additionalModelRequestFields.output_config.effort` olarak gönderilir. Diğer
Kiro modelleri şu anda öykünülmüş akıl yürütme kullanır: opencodex yerel çaba
alanları doğrulanmadığı için seçilen seviyeyi kullanıcı içeriğinde sınırlı
düşünme talimatlarına dönüştürür. Bu modellerde bildirilen bir çaba denetimini
yukarı akış yerel akıl yürütme desteğinin kanıtı olarak yorumlamayın.
GPT-5.6 ailesi `additionalModelRequestFields.reasoning.effort`, `claude-opus-5` ise
`additionalModelRequestFields.output_config.effort` alanını kullanır. `gpt-5.6-luna` ve
`gpt-5.6-terra` için yalnızca doğrulanmış `low`, `medium`, `high` ve `max` seviyeleri yerel alandan
gönderilir. Bu iki modelin yerel `xhigh` seviyesi doğrulanmadığı için mevcut sınırlı düşünme
talimatlarıyla öykünme korunur. `gpt-5.6-sol` ve `claude-opus-5` için mevcut yerel `low`, `medium`,
`high`, `xhigh` ve `max` davranışı değişmez. Diğer Kiro modelleri öykünme kullanır; çaba seçeneği yerel desteğin kanıtı değildir.

## `cursor`

Expand Down
10 changes: 6 additions & 4 deletions docs-site/src/content/docs/zh-cn/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,12 @@ Kiro 的 assistant 文本本身没有可靠的回合结束标记,但终止的

### Reasoning effort

`gpt-5.6-sol` 和 `claude-opus-5` 支持原生 effort,且请求字段名不同。`low` / `medium` / `high` /
`xhigh` / `max` 分别通过 `additionalModelRequestFields.reasoning.effort` 和
`output_config.effort` 发送。

GPT-5.6 系列使用 `additionalModelRequestFields.reasoning.effort`,`claude-opus-5` 使用
`additionalModelRequestFields.output_config.effort`。`gpt-5.6-luna` 和 `gpt-5.6-terra`
仅通过原生字段发送已验证的 `low`、`medium`、`high` 和 `max`。
这两个模型的原生 `xhigh` 尚未验证,因此仍使用原有的有界 thinking 指令模拟。
`gpt-5.6-sol` 和 `claude-opus-5` 保留现有原生档位(`low`、`medium`、`high`、`xhigh`、`max`)。
其他 Kiro 模型使用模拟推理;提供 effort 选项并不代表原生支持。

## `cursor`

Expand Down
10 changes: 6 additions & 4 deletions docs-site/src/content/docs/zh-tw/reference/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,12 @@ Kiro 的 assistant 文字本身沒有可靠的回合結束標記,但終止的

### Reasoning effort

`gpt-5.6-sol` 和 `claude-opus-5` 支援原生 effort,且請求欄位名不同。`low` / `medium` / `high` /
`xhigh` / `max` 分別透過 `additionalModelRequestFields.reasoning.effort` 和
`output_config.effort` 傳送。

GPT-5.6 系列使用 `additionalModelRequestFields.reasoning.effort`,`claude-opus-5` 使用
`additionalModelRequestFields.output_config.effort`。`gpt-5.6-luna` 和 `gpt-5.6-terra`
只透過原生欄位傳送已驗證的 `low`、`medium`、`high` 和 `max`。
這兩個模型的原生 `xhigh` 尚未驗證,因此仍使用原有的有界 thinking 指令模擬。
`gpt-5.6-sol` 和 `claude-opus-5` 保留現有原生檔位(`low`、`medium`、`high`、`xhigh`、`max`)。
其他 Kiro 模型使用模擬推理;提供 effort 選項不代表原生支援。

## `cursor`

Expand Down
34 changes: 21 additions & 13 deletions src/adapters/kiro-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { kiroTruncationReason } from "./kiro-truncation";

export type ParsedKiroEvent =
| { type: "content"; data?: string; modelId?: string }
| { type: "reasoning"; data?: string; redactedContent?: string }
| { type: "reasoning"; data?: string; signature?: string; redactedContent?: string }
| { type: "context_usage"; contextUsagePercentage: number }
| { type: "tool"; name?: string; toolUseId?: string; input?: string; stop?: boolean }
| { type: "truncation"; data: string }
Expand Down Expand Up @@ -138,18 +138,26 @@ export function parseKiroEvent(eventType: string, payload: Uint8Array): ParsedKi
: {}),
};
case "reasoningContentEvent":
// `text` is plaintext reasoning; `redactedContent` is the encrypted blob the GPT-5.6 family
// (sol/terra/luna) actually returns — they never send `text`. Keyed off the wire field, not
// the model id. Both may be absent on a bare event.
return {
type: "reasoning",
...(optionalString(eventType, parsed, "text") !== undefined
? { data: optionalString(eventType, parsed, "text") }
: {}),
...(optionalString(eventType, parsed, "redactedContent") !== undefined
? { redactedContent: optionalString(eventType, parsed, "redactedContent") }
: {}),
};
// `text` is plaintext reasoning; the GPT-5.6 family (sol/terra/luna) instead returns an
// encrypted blob, and the field it arrives on has to be replayed unchanged (see
// kiro/reasoning.ts): `signature` carries the `.KTR~~…` value verbatim and is what every
// capture of those models sent, while `redactedContent` — the base64 shape a capture has
// never shown — stays accepted for any model that sends it. Keyed off the wire field, not the
// model id. Any of the three may be absent on a bare event.
{
const text = optionalString(eventType, parsed, "text");
const signature = optionalString(eventType, parsed, "signature");
const redacted = optionalString(eventType, parsed, "redactedContent");
return {
type: "reasoning",
...(text !== undefined ? { data: text } : {}),
...(signature !== undefined
? { signature }
: redacted !== undefined
? { redactedContent: redacted }
: {}),
};
}
case "toolUseEvent":
return {
type: "tool",
Expand Down
20 changes: 17 additions & 3 deletions src/adapters/kiro/payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ import {
validateKiroConversationState,
type KiroTurn,
} from "./conversation";
import { injectKiroThinkingTags, kiroNativeEffortField, KIRO_NATIVE_EFFORTS } from "./reasoning";
import {
injectKiroThinkingTags,
kiroNativeEffortField,
kiroReasoningContent,
KIRO_NATIVE_EFFORTS,
} from "./reasoning";
import { kiroPayloadMessages, userContentText } from "./usage";
import {
kiroToolWireNames,
Expand Down Expand Up @@ -388,7 +393,11 @@ export function buildKiroPayload(
assistantResponseMessage: {
content: turn.content,
...(turn.toolUses.length > 0 ? { toolUses: turn.toolUses } : {}),
...(turn.redactedReasoning ? { reasoningContent: { redactedContent: turn.redactedReasoning } } : {}),
// Replayed on the field it was received on: the GPT-5.6 signature is not base64 and is
// rejected when sent as `redactedContent`.
...(turn.redactedReasoning
? { reasoningContent: kiroReasoningContent(turn.redactedReasoning) }
: {}),
},
}
: {
Expand Down Expand Up @@ -447,7 +456,12 @@ export function buildKiroPayload(
if (!KIRO_NATIVE_EFFORTS.includes(effort)) {
throw new Error(`Kiro ${normalizeKiroModelId(parsed.modelId)} does not support reasoning effort ${JSON.stringify(effort)}`);
}
payload.additionalModelRequestFields = { [effortField]: { effort } };
// Model eligibility still owns unsupported-effort validation above; wire eligibility
// is narrower for luna/terra, whose unverified rungs retain the thinking-tag path.
const verifiedEffortField = kiroNativeEffortField(parsed.modelId, effort);
if (verifiedEffortField) {
payload.additionalModelRequestFields = { [verifiedEffortField]: { effort } };
}
}
if (profileArn) payload.profileArn = profileArn;
return { payload, nameMap, conversationId, completionMode };
Expand Down
77 changes: 70 additions & 7 deletions src/adapters/kiro/reasoning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,46 @@ import type { OcxParsedRequest } from "../../types";
export type KiroReasoningMode = "native" | "emulated";

// Kiro takes a verified native effort field for these models, and each model family names it
// differently: the Sol-only `reasoning.effort` versus the Claude-specific `output_config.effort`.
// Models absent from this table fall back to emulated thinking instructions.
// differently: the GPT-5.6 family's `reasoning.effort` versus the Claude-specific
// `output_config.effort`. Models absent from this table fall back to emulated thinking
// instructions.
//
// The GPT-5.6 entries are measured against the live runtime rather than inferred from the vendor
// schema: the field is accepted (HTTP 200) and the encrypted reasoning blob that comes back grows
// with the effort. On one fixed hard prompt — a primality search plus a 20-bit recurrence count —
// luna's blob measured 5,130 chars at `low`, 16,686 at `medium`, 30,670 at `high` and 48,594 at
// `max`, against 13,118 with no effort signal at all; terra's measured 34,590 and 38,106 at native
// `max` against 11,758 and 17,598 bare, two repetitions each. The channel this replaces — the
// emulated `<thinking_mode>` tag block, which was all those models used to receive — measured
// 21,202 (`low`) and 28,302 (`max`) for luna, i.e. between that model's native `medium` and
// `high`, never reaching native `max`. `gpt-5.6-sol`'s native `max` cross-checked at 30,498 on the
// same prompt. Terra's absence from this table was therefore an omission rather than a capability
// difference: what the earlier Sol-only scope recorded was not reproducible here.
export const KIRO_NATIVE_EFFORT_FIELDS: Record<string, "reasoning" | "output_config"> = {
"gpt-5.6-sol": "reasoning",
"gpt-5.6-terra": "reasoning",
"gpt-5.6-luna": "reasoning",
"claude-opus-5": "output_config",
};

export const KIRO_NATIVE_EFFORTS = ["low", "medium", "high", "xhigh", "max"];

export function kiroNativeEffortField(modelId: string): "reasoning" | "output_config" | undefined {
return KIRO_NATIVE_EFFORT_FIELDS[normalizeKiroModelId(modelId)];
// The newly enabled models have evidence for these rungs only. Keep the previous
// emulation for xhigh, and never widen their native wire when the shared ladder grows.
const KIRO_LUNA_TERRA_NATIVE_EFFORTS = new Set(["low", "medium", "high", "max"]);

export function kiroNativeEffortField(
modelId: string,
effort?: string,
): "reasoning" | "output_config" | undefined {
const model = normalizeKiroModelId(modelId);
if ((model === "gpt-5.6-luna" || model === "gpt-5.6-terra")
&& effort !== undefined && !KIRO_LUNA_TERRA_NATIVE_EFFORTS.has(effort)) return undefined;
return KIRO_NATIVE_EFFORT_FIELDS[model];
}

export function kiroReasoningMode(modelId: string): KiroReasoningMode {
return kiroNativeEffortField(modelId) ? "native" : "emulated";
export function kiroReasoningMode(modelId: string, effort?: string): KiroReasoningMode {
return kiroNativeEffortField(modelId, effort) ? "native" : "emulated";
}

export function kiroThinkingBudget(parsed: OcxParsedRequest): number | undefined {
Expand All @@ -38,7 +63,7 @@ export function kiroThinkingBudget(parsed: OcxParsedRequest): number | undefined
}

export function injectKiroThinkingTags(content: string, parsed: OcxParsedRequest): string {
if (kiroReasoningMode(parsed.modelId) !== "emulated") return content;
if (kiroReasoningMode(parsed.modelId, parsed.options.reasoning) !== "emulated") return content;
const budget = kiroThinkingBudget(parsed);
if (!budget) return content;
const instruction = [
Expand All @@ -54,3 +79,41 @@ export function injectKiroThinkingTags(content: string, parsed: OcxParsedRequest
content,
].join("\n");
}

/**
* The blob from a Kiro `reasoningContentEvent` has two possible homes on a replayed assistant
* turn, and the wire validates the SHAPE of each rather than its content: `signature` takes the
* emitted string verbatim, while `redactedContent` is a base64 member. The `.KTR~~…` value every
* GPT-5.6 capture returns is NOT valid base64, which is exactly why replaying it as
* `redactedContent` — what this proxy did before the field was measured — came back as
* REQUEST_BODY_INVALID ("Improperly formed request").
*
* The blob travels as ONE opaque string: adapter event, `ocxr1:` reasoning envelope, then
* `OcxAssistantMessage.kiroRedactedReasoning`. The field it arrived on therefore rides that same
* string, instead of a second parallel value that could drift from it. Provider data cannot forge
* the tag: the other channel is base64, whose alphabet has no colon.
*/
export const KIRO_REASONING_SIGNATURE_TAG = "signature:";

export function tagKiroReasoningBlob(field: "signature" | "redactedContent", data: string): string {
return field === "signature" ? KIRO_REASONING_SIGNATURE_TAG + data : data;
}

/** The wire field a stored blob arrived on, and its untagged value. */
export function splitKiroReasoningBlob(value: string): { field: "signature" | "redactedContent"; data: string } {
return value.startsWith(KIRO_REASONING_SIGNATURE_TAG)
? { field: "signature", data: value.slice(KIRO_REASONING_SIGNATURE_TAG.length) }
: { field: "redactedContent", data: value };
}

/**
* The `reasoningContent` object on an `assistantResponseMessage`. Exactly one member is set: the
* wire validates the shape, so the two cannot be substituted for each other.
*/
export type KiroReasoningContent = { signature: string } | { redactedContent: string };

/** `reasoningContent` for a replayed `assistantResponseMessage`, carrying the blob verbatim. */
export function kiroReasoningContent(value: string): KiroReasoningContent {
const { field, data } = splitKiroReasoningBlob(value);
return field === "signature" ? { signature: data } : { redactedContent: data };
}
Loading
Loading