Merge origin/master (v0.5.75) into gitea/new_feature

Resolve conflicts:
- package.json / cli/package.json: take 0.5.75
- .gitignore: union both sides (upstream 9router-*/temp files + local state dirs)
- CHANGELOG.md: keep both blocks, v0.5.75 above v0.5.70
- nonStreamingHandler.js: merge imports (unwrapClineEnvelope +
  tokensForDetail/shouldPersistRequestDetail); drop dead appendRequestLog
- providers/[id]/page.js: union useState blocks (compatible-model states
  + importingClineModels)

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
2026-09-17 14:14:43 +07:00
113 changed files with 6634 additions and 482 deletions

View File

@@ -242,9 +242,9 @@ export function claudeToKiroRequest(model, body, stream, credentials) {
? (credentials?.providerSpecificData?.profileArn || "")
: (credentials?.providerSpecificData?.profileArn || resolveDefaultProfileArn(authMethod));
// Kiro CLI/KAS sends system prompt as top-level `systemPrompt`. Keep a
// content fallback too because the CodeWhisperer surface does not always
// enforce top-level systemPrompt for direct calls.
// The system prompt travels inside the first user turn's content (contentPrefix):
// the CodeWhisperer surface rejects a top-level `systemPrompt` with
// 400 REQUEST_BODY_INVALID, so the value below is only a replay cache key.
const timestamp = new Date().toISOString();
const systemPromptParts = [];
if (thinkingBudget !== null && !usesNativeGptEffort) {
@@ -316,14 +316,11 @@ export function claudeToKiroRequest(model, body, stream, credentials) {
conversationState: {
chatTriggerType: "MANUAL",
conversationId,
agentContinuationId: continuationId,
agentTaskType: "vibe",
currentMessage: {
userInputMessage,
},
history: canonical.history,
},
agentMode: "vibe",
};
if (profileArn) payload.profileArn = profileArn;

View File

@@ -142,6 +142,13 @@ function systemReminderText(content) {
// Convert single Claude message - returns single message or array of messages
function convertClaudeMessage(msg) {
// Some clients send content as a single block object; normalize to the
// one-element array every branch below (the system-reminder fold included)
// expects. Must run BEFORE the role branch: systemReminderText only reads
// arrays and strings, so a bare-object system turn was dropped outright.
if (msg.content && typeof msg.content === "object" && !Array.isArray(msg.content)) {
msg.content = [msg.content];
}
// Mid-conversation system message -> user (per Anthropic placement rules)
if (msg.role === ROLE.SYSTEM) {
const text = systemReminderText(msg.content);

View File

@@ -15,7 +15,8 @@ import {
generateRequestId,
generateSessionId,
generateProjectId,
cleanJSONSchemaForAntigravity
cleanJSONSchemaForAntigravity,
normalizeGeminiContents
} from "../formats/gemini.js";
import { deriveSessionId, toNumericSessionId } from "../../utils/sessionManager.js";
import { ROLE, GEMINI_ROLE, OPENAI_BLOCK, CLAUDE_BLOCK } from "../schema/index.js";
@@ -35,17 +36,6 @@ function sanitizeGeminiFunctionName(name) {
return sanitized.substring(0, 64);
}
function normalizeGeminiContents(contents) {
const out = [];
for (const c of contents || []) {
if (!c?.role || !Array.isArray(c.parts) || c.parts.length === 0) continue;
const last = out.at(-1);
if (last?.role === c.role) last.parts.push(...c.parts);
else out.push({ ...c, parts: [...c.parts] });
}
return out;
}
// Core: Convert OpenAI request to Gemini format (base for all variants)
function openaiToGeminiBase(model, body, stream, signature = DEFAULT_THINKING_AG_SIGNATURE, sessionId = null) {
const result = {
@@ -163,12 +153,14 @@ function openaiToGeminiBase(model, body, stream, signature = DEFAULT_THINKING_AG
}
// Check if there are actual tool responses in the next messages
const hasActualResponses = toolCallIds.some(fid => toolResponses[fid]);
const isIntermediate = i < body.messages.length - 1;
const hasActualResponses = toolCallIds.some(fid => toolResponses[fid] !== undefined);
if (hasActualResponses) {
if (hasActualResponses || isIntermediate) {
const toolParts = [];
for (const fid of toolCallIds) {
if (!toolResponses[fid]) continue;
let resp = toolResponses[fid];
if (resp === undefined) resp = "";
let name = tcID2Name[fid];
if (!name) {
@@ -180,7 +172,6 @@ function openaiToGeminiBase(model, body, stream, signature = DEFAULT_THINKING_AG
}
}
let resp = toolResponses[fid];
let parsedResp = tryParseJSON(resp);
if (parsedResp === null) {
parsedResp = { result: resp };

View File

@@ -340,9 +340,9 @@ export function openaiToKiroRequest(model, body, stream, credentials) {
const timestamp = new Date().toISOString();
// Kiro CLI/KAS sends these as top-level systemPrompt. Keep a content fallback
// too because the CodeWhisperer surface does not always enforce top-level
// systemPrompt for direct calls.
// The system prompt travels inside the first user turn's content (contentPrefix):
// the CodeWhisperer surface rejects a top-level `systemPrompt` with
// 400 REQUEST_BODY_INVALID, so the value below is only a replay cache key.
const systemPromptParts = [];
if (thinkingBudget !== null && !usesNativeGptEffort) {
systemPromptParts.push(buildThinkingSystemPrefix(thinkingBudget));
@@ -397,8 +397,6 @@ export function openaiToKiroRequest(model, body, stream, credentials) {
conversationState: {
chatTriggerType: "MANUAL",
conversationId,
agentContinuationId: continuationId,
agentTaskType: "vibe",
currentMessage: {
userInputMessage: {
content: replayCurrent.content || "",
@@ -414,7 +412,6 @@ export function openaiToKiroRequest(model, body, stream, credentials) {
},
history: canonical.history
},
agentMode: "vibe",
};
if (profileArn) {