fix(kiro): improve direct session cache reuse

Reshape Kiro direct requests so resumed client sessions reuse Kiro's
cache-affinity fields instead of starting unrelated CodeWhisperer
conversations.

- keep conversationState.conversationId stable when the client sends an
  explicit session id (x-session-id, session_id, conversation_id, Claude
  Code session metadata)
- add a stable conversationState.agentContinuationId per Kiro session
- send conversationState.agentTaskType: "vibe" and agentMode: "vibe",
  matching the normal Kiro CLI/KAS chat path
- move Kiro thinking instructions into Kiro-compatible systemPrompt /
  additionalModelRequestFields instead of generic top-level thinking
- keep volatile timestamp context out of the top-level systemPrompt; it
  remains only in user content fallback
- suppress additionalModelRequestFields for legacy 4.5-era Claude/Kiro
  models that reject it, while defaulting future Claude/Kiro model ids
  to supported
- preserve Kiro meteringEvent credit usage internally for accounting
  without leaking provider-specific fields into OpenAI-compatible usage
- prevent unrelated headerless Kiro requests from sharing one
  connection-wide continuation
- cap/evict continuation sessions so long-running processes do not grow
  the continuation map unbounded
- treat generated headerless Kiro sessions as one-shot so they do not
  evict real explicit-session continuations
- keep credit-only Kiro metering valid for internal persistence when
  token metrics are unavailable
This commit is contained in:
Edison42
2026-07-16 15:15:05 +07:00
committed by decolua
parent 70e8dc4974
commit 9c58ba645e
10 changed files with 786 additions and 90 deletions
+68 -7
View File
@@ -6,8 +6,8 @@ import "./registerAll.js";
import { translateRequest, translateResponse } from "../../open-sse/translator/index.js";
import { FORMATS } from "../../open-sse/translator/formats.js";
const C2K = (body) =>
translateRequest(FORMATS.CLAUDE, FORMATS.KIRO, "claude-sonnet-4.5", body, true, null, "kiro");
const C2K = (body, credentials = null, model = "claude-sonnet-4.5") =>
translateRequest(FORMATS.CLAUDE, FORMATS.KIRO, model, body, true, credentials, "kiro");
describe("Claude → Kiro (direct route)", () => {
it("produces a Kiro conversationState payload", () => {
@@ -16,6 +16,27 @@ describe("Claude → Kiro (direct route)", () => {
expect(out.conversationState.currentMessage.userInputMessage.content).toContain("hello");
});
it("keeps conversationId stable from client session headers and replays frozen msg0", () => {
const credentials = {
rawHeaders: { "x-session-id": "hermes-session-123-claude-replay" },
connectionId: "kiro-account-1",
};
const first = C2K({ messages: [{ role: "user", content: "first" }] }, credentials);
const second = C2K({ messages: [{ role: "user", content: "second" }] }, credentials);
expect(first.conversationState.conversationId).toBe("hermes-session-123-claude-replay");
expect(second.conversationState.conversationId).toBe("hermes-session-123-claude-replay");
expect(first.conversationState.agentContinuationId).toBeTruthy();
expect(second.conversationState.agentContinuationId).toBe(first.conversationState.agentContinuationId);
expect(first.conversationState.agentTaskType).toBe("vibe");
expect(second.conversationState.history[0].userInputMessage.content).toBe(
first.conversationState.currentMessage.userInputMessage.content
);
expect(second.conversationState.history[0].userInputMessage.modelId).toBe("claude-sonnet-4.5");
expect(second.conversationState.currentMessage.userInputMessage.content).toContain("Current time");
expect(second.conversationState.currentMessage.userInputMessage.content).toContain("second");
});
it("guard 1: with no tools, a dangling tool_result is flattened to text (no structured ref)", () => {
// Client omitted `tools` but kept a tool_result after compaction.
const out = C2K({
@@ -60,20 +81,60 @@ describe("Claude → Kiro (direct route)", () => {
null,
"kiro"
);
expect(out.conversationState.currentMessage.userInputMessage.content).toContain(
expect(out.systemPrompt).toContain(
"<thinking_mode>enabled</thinking_mode>"
);
expect(out.agentMode).toBe("vibe");
});
it("maps output_config.effort high to Kiro max_thinking_length 24576", () => {
it("does not send additionalModelRequestFields for Kiro models without effort support", () => {
const out = C2K({
output_config: { effort: "high" },
messages: [{ role: "user", content: "think with adaptive effort" }],
});
expect(out.conversationState.currentMessage.userInputMessage.content).toContain(
"<max_thinking_length>24576</max_thinking_length>"
);
expect(out.additionalModelRequestFields).toBeUndefined();
expect(out.thinking).toBeUndefined();
expect(out.systemPrompt).toContain("<max_thinking_length>24576</max_thinking_length>");
});
it("maps output_config.effort high to Kiro CLI-style additionalModelRequestFields for effort models", () => {
const out = C2K({
output_config: { effort: "high" },
messages: [{ role: "user", content: "think with adaptive effort" }],
}, null, "claude-sonnet-5");
expect(out.additionalModelRequestFields).toEqual({
thinking: { type: "adaptive", display: "summarized" },
output_config: { effort: "high" },
});
expect(out.thinking).toBeUndefined();
expect(out.systemPrompt).toContain("<max_thinking_length>24576</max_thinking_length>");
});
it("sends Claude system as top-level systemPrompt and keeps a user-content fallback", () => {
const out = C2K({
system: "system-only instruction",
messages: [{ role: "user", content: "hello" }],
});
expect(out.systemPrompt).toContain("system-only instruction");
expect(out.conversationState.currentMessage.userInputMessage.content).toContain("system-only instruction");
});
it("keeps top-level systemPrompt stable across turns", () => {
const first = C2K({
system: "stable instruction",
messages: [{ role: "user", content: "first" }],
});
const second = C2K({
system: "stable instruction",
messages: [{ role: "user", content: "second" }],
});
expect(first.systemPrompt).toBe(second.systemPrompt);
expect(first.systemPrompt).not.toContain("Current time");
expect(first.conversationState.currentMessage.userInputMessage.content).toContain("Current time");
});
});
+57
View File
@@ -1,5 +1,6 @@
import { describe, it, expect } from "vitest";
import { KiroExecutor } from "../../open-sse/executors/kiro.js";
import "../translator/registerAll.js";
function createMockFrame(eventType, payloadObj) {
const payloadStr = JSON.stringify(payloadObj);
@@ -47,6 +48,13 @@ async function readAllSSE(stream) {
return result;
}
async function readNextWithTimeout(reader) {
return Promise.race([
reader.read(),
new Promise((_, reject) => setTimeout(() => reject(new Error("timed out waiting for SSE chunk")), 100)),
]);
}
describe("KiroExecutor thinking tag stripping", () => {
it("strips <thinking> tags from assistantResponseEvent", async () => {
const executor = new KiroExecutor();
@@ -121,4 +129,53 @@ describe("KiroExecutor thinking tag stripping", () => {
const contentChunks = objects.filter(obj => obj.choices[0].delta.content !== undefined);
expect(contentChunks.length).toBe(0);
});
it("emits a terminal chunk at messageStop before the upstream stream closes", async () => {
const executor = new KiroExecutor();
const f1 = createMockFrame("assistantResponseEvent", { content: "OK" });
const f2 = createMockFrame("messageStopEvent", {});
const readableStream = new ReadableStream({
start(controller) {
controller.enqueue(f1);
controller.enqueue(f2);
}
});
const transformedResponse = executor.transformEventStreamToSSE({ body: readableStream }, "claude-test");
const reader = transformedResponse.body.getReader();
const decoder = new TextDecoder();
let output = "";
for (let i = 0; i < 4 && !output.includes("\"finish_reason\":\"stop\""); i++) {
const { value } = await readNextWithTimeout(reader);
output += decoder.decode(value, { stream: true });
}
await reader.cancel();
expect(output).toContain("\"finish_reason\":\"stop\"");
});
it("uses tool_calls finish reason for tool streams without messageStop", async () => {
const executor = new KiroExecutor();
const f1 = createMockFrame("toolUseEvent", { toolUseId: "tool-1", name: "read_file", input: { path: "a.txt" } });
const readableStream = new ReadableStream({
start(controller) {
controller.enqueue(f1);
controller.close();
}
});
const transformedResponse = executor.transformEventStreamToSSE({ body: readableStream }, "claude-test");
const output = await readAllSSE(transformedResponse.body);
const objects = output
.split("\n")
.filter(line => line.startsWith("data: ") && !line.includes("[DONE]"))
.map(line => JSON.parse(line.slice(6)));
const finalChunk = objects.at(-1);
expect(finalChunk.choices[0].finish_reason).toBe("tool_calls");
});
});
+153 -8
View File
@@ -11,6 +11,7 @@ import { openaiToKiroRequest } from "../../open-sse/translator/request/openai-to
const contentOf = (result) =>
result.conversationState.currentMessage.userInputMessage.content;
const systemPromptOf = (result) => result.systemPrompt || "";
describe("openaiToKiroRequest", () => {
describe("basic message conversion", () => {
@@ -293,7 +294,11 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6", body, true, {});
expect(contentOf(result)).toContain("<max_thinking_length>1024</max_thinking_length>");
expect(systemPromptOf(result)).toContain("<max_thinking_length>1024</max_thinking_length>");
expect(result.additionalModelRequestFields).toEqual({
thinking: { type: "adaptive", display: "summarized" },
output_config: { effort: "low" },
});
});
it("maps reasoning_effort high to max_thinking_length 24576", () => {
@@ -304,7 +309,97 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6", body, true, {});
expect(contentOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toEqual({
thinking: { type: "adaptive", display: "summarized" },
output_config: { effort: "high" },
});
});
it("does not send additionalModelRequestFields for legacy Kiro model ids", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Legacy model id should not get adaptive fields" }]
};
const result = openaiToKiroRequest("claude-sonnet-4.5", body, true, {});
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
it("does not send additionalModelRequestFields for date-suffixed Claude 4 model ids", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Date-suffixed Claude 4 should stay legacy" }]
};
const result = openaiToKiroRequest("claude-sonnet-4-20250514", body, true, {});
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
it("does not send additionalModelRequestFields for pre-4 legacy Kiro model ids", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Older model id should not get adaptive fields" }]
};
const result = openaiToKiroRequest("claude-sonnet-3.7", body, true, {});
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
it("does not send additionalModelRequestFields for prefixed pre-4 legacy Kiro model ids", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Prefixed older model id should not get adaptive fields" }]
};
const result = openaiToKiroRequest("kiro/claude-3-7-sonnet-20250219", body, true, {});
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
it("does not send Claude-specific additionalModelRequestFields for prefixed non-Claude aliases", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Prefixed non-Claude alias should not get adaptive fields" }]
};
const result = openaiToKiroRequest("kiro/gpt-4o", body, true, {});
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
it("does not send Claude-specific additionalModelRequestFields for non-Claude aliases", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Non-Claude aliases should not get Claude adaptive fields" }]
};
const result = openaiToKiroRequest("gpt-4o", body, true, {});
expect(systemPromptOf(result)).toContain("<max_thinking_length>24576</max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
it("defaults future Kiro model ids to additionalModelRequestFields support", () => {
const body = {
reasoning_effort: "high",
messages: [{ role: "user", content: "Future model id should get adaptive fields" }]
};
const result = openaiToKiroRequest("claude-sonnet-4.60", body, true, {});
expect(result.additionalModelRequestFields).toEqual({
thinking: { type: "adaptive", display: "summarized" },
output_config: { effort: "high" },
});
});
it("clamps reasoning_effort max to Kiro max_thinking_length 32000", () => {
@@ -315,7 +410,8 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6", body, true, {});
expect(contentOf(result)).toContain("<max_thinking_length>32000</max_thinking_length>");
expect(systemPromptOf(result)).toContain("<max_thinking_length>32000</max_thinking_length>");
expect(result.additionalModelRequestFields?.output_config?.effort).toBe("high");
});
it("clamps OpenAI Responses reasoning.effort xhigh to max_thinking_length 32000", () => {
@@ -326,7 +422,8 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6", body, true, {});
expect(contentOf(result)).toContain("<max_thinking_length>32000</max_thinking_length>");
expect(systemPromptOf(result)).toContain("<max_thinking_length>32000</max_thinking_length>");
expect(result.additionalModelRequestFields?.output_config?.effort).toBe("high");
});
it("uses Claude thinking.budget_tokens as max_thinking_length", () => {
@@ -337,7 +434,7 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6", body, true, {});
expect(contentOf(result)).toContain("<max_thinking_length>4096</max_thinking_length>");
expect(systemPromptOf(result)).toContain("<max_thinking_length>4096</max_thinking_length>");
});
it("uses the default budget for synthetic -thinking models with no explicit config", () => {
@@ -347,7 +444,54 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6-thinking", body, true, {});
expect(contentOf(result)).toContain("<max_thinking_length>16000</max_thinking_length>");
expect(systemPromptOf(result)).toContain("<max_thinking_length>16000</max_thinking_length>");
});
it("keeps top-level systemPrompt stable across turns", () => {
const first = openaiToKiroRequest(
"claude-sonnet-4.6-thinking",
{ messages: [{ role: "user", content: "first" }] },
true,
{}
);
const second = openaiToKiroRequest(
"claude-sonnet-4.6-thinking",
{ messages: [{ role: "user", content: "second" }] },
true,
{}
);
expect(first.systemPrompt).toBe(second.systemPrompt);
expect(first.systemPrompt).not.toContain("Current time");
expect(first.conversationState.currentMessage.userInputMessage.content).toContain("Current time");
});
it("replays frozen msg0 for explicit Kiro sessions while keeping current time fresh", () => {
const credentials = {
connectionId: "kiro-account-openai-replay",
rawHeaders: { "x-session-id": "hermes-session-openai-replay" },
};
const first = openaiToKiroRequest(
"claude-sonnet-4.6",
{ messages: [{ role: "user", content: "first turn" }] },
true,
credentials
);
const second = openaiToKiroRequest(
"claude-sonnet-4.6",
{ messages: [{ role: "user", content: "second turn" }] },
true,
credentials
);
expect(second.conversationState.conversationId).toBe("hermes-session-openai-replay");
expect(second.conversationState.agentContinuationId).toBe(first.conversationState.agentContinuationId);
expect(second.conversationState.history[0].userInputMessage.content).toBe(
first.conversationState.currentMessage.userInputMessage.content
);
expect(second.conversationState.history[0].userInputMessage.modelId).toBe("claude-sonnet-4.6");
expect(second.conversationState.currentMessage.userInputMessage.content).toContain("Current time");
expect(second.conversationState.currentMessage.userInputMessage.content).toContain("second turn");
});
it("does not inject thinking prefix for reasoning_effort none", () => {
@@ -358,8 +502,9 @@ describe("openaiToKiroRequest", () => {
const result = openaiToKiroRequest("claude-sonnet-4.6", body, true, {});
expect(contentOf(result)).not.toContain("<thinking_mode>enabled</thinking_mode>");
expect(contentOf(result)).not.toContain("<max_thinking_length>");
expect(systemPromptOf(result)).not.toContain("<thinking_mode>enabled</thinking_mode>");
expect(systemPromptOf(result)).not.toContain("<max_thinking_length>");
expect(result.additionalModelRequestFields).toBeUndefined();
});
});
});
+166 -2
View File
@@ -1,13 +1,15 @@
// A2: locks resolveSessionId priority/stickiness (codex/kiro/antigravity centralization).
import { describe, it, expect, beforeEach } from "vitest";
import { resolveSessionId, deriveSessionId, clearSessionStore } from "../../open-sse/utils/sessionManager.js";
import { resolveContinuationId, resolveSessionId, resolveSessionIdentity, deriveSessionId, clearSessionStore } from "../../open-sse/utils/sessionManager.js";
// Assistant text must reach ASSISTANT_MIN_LEN (80) to use assistant anchor; else first user message.
const longAssistant = "x".repeat(80);
const bodyWithAssistant = { messages: [{ role: "assistant", content: longAssistant }] };
const bodyWithUserOnly = { messages: [{ role: "user", content: "hello from first user message anchor" }] };
beforeEach(() => clearSessionStore());
beforeEach(() => {
clearSessionStore();
});
describe("resolveSessionId", () => {
it("stickiness: same body+connectionId+scope -> same id", () => {
@@ -55,8 +57,170 @@ describe("resolveSessionId", () => {
expect(got).toBe("client-sess-123");
});
it("does not treat request-scoped x-client-request-id as a session override", () => {
const first = resolveSessionId({
headers: { "x-client-request-id": "req-1" },
body: bodyWithUserOnly,
connectionId: "conn1",
scope: "kiro",
});
const second = resolveSessionId({
headers: { "x-client-request-id": "req-2" },
body: bodyWithUserOnly,
connectionId: "conn1",
scope: "kiro",
});
expect(first).not.toBe("req-1");
expect(second).not.toBe("req-2");
expect(first).not.toBe(second);
});
it("does not treat request-scoped previous_response_id as a Kiro session override", () => {
const first = resolveSessionId({
body: { ...bodyWithUserOnly, previous_response_id: "resp-1" },
connectionId: "conn1",
scope: "kiro",
});
const second = resolveSessionId({
body: { ...bodyWithUserOnly, previous_response_id: "resp-2" },
connectionId: "conn1",
scope: "kiro",
});
expect(first).not.toBe("resp-1");
expect(second).not.toBe("resp-2");
expect(first).not.toBe(second);
});
it("does not treat raw metadata.user_id as a Kiro conversation session", () => {
const first = resolveSessionId({
body: {
metadata: { user_id: "user-123" },
messages: [{ role: "user", content: "new chat about invoices" }],
},
connectionId: "conn1",
scope: "kiro",
});
const second = resolveSessionId({
body: {
metadata: { user_id: "user-123" },
messages: [{ role: "user", content: "unrelated new chat about refunds" }],
},
connectionId: "conn1",
scope: "kiro",
});
expect(first).not.toBe("user-123");
expect(second).not.toBe("user-123");
expect(first).not.toBe(second);
});
it("keeps Claude Code session_id metadata as a Kiro conversation session", () => {
const body = {
metadata: { user_id: JSON.stringify({ session_id: "claude-code-session-123" }) },
messages: [{ role: "user", content: "same Claude Code session" }],
};
expect(resolveSessionId({ body, connectionId: "conn1", scope: "kiro" })).toBe("claude:claude-code-session-123");
});
it("keeps raw metadata.user_id as a non-Kiro session fallback", () => {
const got = resolveSessionId({
body: {
metadata: { user_id: "user-123" },
messages: [{ role: "user", content: "non-Kiro provider" }],
},
connectionId: "conn1",
scope: "codex",
});
expect(got).toBe("user-123");
});
it("keeps x-client-request-id as a session override outside Kiro scope", () => {
const got = resolveSessionId({
headers: { "x-client-request-id": "req-1" },
body: bodyWithAssistant,
connectionId: "conn1",
scope: "codex",
});
expect(got).toBe("req-1");
});
it("workspaceId path: empty body + workspaceId set -> normalized workspaceId", () => {
const got = resolveSessionId({ body: {}, connectionId: "conn1", workspaceId: "ws-abc" });
expect(got).toBe("ws-abc");
});
it("uses fresh Kiro sessions for unrelated headerless requests on the same connection", () => {
const a = resolveSessionId({ body: bodyWithUserOnly, connectionId: "conn1", scope: "kiro" });
const b = resolveSessionId({ body: bodyWithUserOnly, connectionId: "conn1", scope: "kiro" });
expect(a).not.toBe(b);
});
it("marks generated headerless Kiro sessions as ephemeral", () => {
const generated = resolveSessionIdentity({ body: bodyWithUserOnly, connectionId: "conn1", scope: "kiro" });
const explicit = resolveSessionIdentity({
headers: { "x-session-id": "client-sess-123" },
body: bodyWithUserOnly,
connectionId: "conn1",
scope: "kiro",
});
expect(generated.ephemeral).toBe(true);
expect(explicit).toEqual({ sessionId: "client-sess-123", ephemeral: false });
});
it("does not switch Kiro headerless requests to assistant-text session ids mid-conversation", () => {
const withAssistant = { messages: [{ role: "user", content: "same user" }, { role: "assistant", content: "y".repeat(80) }] };
const a = resolveSessionId({ body: withAssistant, connectionId: "conn1", scope: "kiro" });
const b = resolveSessionId({ body: withAssistant, connectionId: "conn1", scope: "kiro" });
expect(a).not.toBe(b);
});
});
describe("resolveContinuationId", () => {
it("keeps continuation id stable for the same Kiro session", () => {
const opts = { sessionId: "kiro-session-1", connectionId: "conn1", scope: "kiro" };
expect(resolveContinuationId(opts)).toBe(resolveContinuationId(opts));
});
it("uses a different continuation id for a different Kiro session", () => {
const a = resolveContinuationId({ sessionId: "kiro-session-1", connectionId: "conn1", scope: "kiro" });
const b = resolveContinuationId({ sessionId: "kiro-session-2", connectionId: "conn1", scope: "kiro" });
expect(a).not.toBe(b);
});
it("does not evict a recently used continuation id when the store exceeds its cap", () => {
const first = resolveContinuationId({ sessionId: "kiro-session-0", connectionId: "conn1", scope: "kiro" });
for (let i = 1; i < 5000; i++) {
resolveContinuationId({ sessionId: `kiro-session-${i}`, connectionId: "conn1", scope: "kiro" });
}
expect(resolveContinuationId({ sessionId: "kiro-session-0", connectionId: "conn1", scope: "kiro" })).toBe(first);
resolveContinuationId({ sessionId: "kiro-session-5000", connectionId: "conn1", scope: "kiro" });
expect(resolveContinuationId({ sessionId: "kiro-session-0", connectionId: "conn1", scope: "kiro" })).toBe(first);
});
it("evicts old continuation ids when the store exceeds its cap", () => {
const first = resolveContinuationId({ sessionId: "kiro-session-0", connectionId: "conn1", scope: "kiro" });
for (let i = 1; i <= 5000; i++) {
resolveContinuationId({ sessionId: `kiro-session-${i}`, connectionId: "conn1", scope: "kiro" });
}
const afterEviction = resolveContinuationId({ sessionId: "kiro-session-0", connectionId: "conn1", scope: "kiro" });
expect(afterEviction).not.toBe(first);
});
it("does not let ephemeral Kiro continuations evict explicit session continuations", () => {
const stable = resolveContinuationId({ sessionId: "explicit-session", connectionId: "conn1", scope: "kiro" });
for (let i = 0; i <= 5000; i++) {
resolveContinuationId({ sessionId: `ephemeral-session-${i}`, connectionId: "conn1", scope: "kiro", ephemeral: true });
}
expect(resolveContinuationId({ sessionId: "explicit-session", connectionId: "conn1", scope: "kiro" })).toBe(stable);
});
});