@copilotkit/aimock 1.46.0 → 1.47.0
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +12 -1
- package/README.md +4 -1
- package/dist/aws-event-stream.cjs +20 -23
- package/dist/aws-event-stream.cjs.map +1 -1
- package/dist/aws-event-stream.d.cts +0 -1
- package/dist/aws-event-stream.d.ts +0 -1
- package/dist/aws-event-stream.js +20 -24
- package/dist/aws-event-stream.js.map +1 -1
- package/dist/bedrock-converse.cjs +79 -82
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +80 -83
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +112 -121
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +113 -122
- package/dist/bedrock.js.map +1 -1
- package/dist/cohere.cjs +58 -106
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +60 -108
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.cts.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/fixture-loader.cjs +103 -2
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +104 -4
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-interactions.cjs +63 -121
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +65 -123
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +242 -155
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +244 -157
- package/dist/gemini.js.map +1 -1
- package/dist/helpers.cjs +400 -7
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +51 -1
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +51 -1
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +391 -8
- package/dist/helpers.js.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/mcp-fakes.d.cts.map +1 -1
- package/dist/messages.cjs +159 -141
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +161 -143
- package/dist/messages.js.map +1 -1
- package/dist/misbehavior.cjs +62 -3
- package/dist/misbehavior.cjs.map +1 -1
- package/dist/misbehavior.d.cts.map +1 -1
- package/dist/misbehavior.d.ts.map +1 -1
- package/dist/misbehavior.js +63 -4
- package/dist/misbehavior.js.map +1 -1
- package/dist/ndjson-writer.cjs +21 -23
- package/dist/ndjson-writer.cjs.map +1 -1
- package/dist/ndjson-writer.d.cts +0 -1
- package/dist/ndjson-writer.d.cts.map +1 -1
- package/dist/ndjson-writer.d.ts +0 -1
- package/dist/ndjson-writer.d.ts.map +1 -1
- package/dist/ndjson-writer.js +21 -24
- package/dist/ndjson-writer.js.map +1 -1
- package/dist/ollama.cjs +153 -147
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.d.cts.map +1 -1
- package/dist/ollama.d.ts.map +1 -1
- package/dist/ollama.js +155 -149
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-chat.cjs +1 -1
- package/dist/openrouter-chat.js +1 -1
- package/dist/raw-validation.cjs +177 -0
- package/dist/raw-validation.cjs.map +1 -0
- package/dist/raw-validation.js +176 -0
- package/dist/raw-validation.js.map +1 -0
- package/dist/response/deliver.cjs +144 -0
- package/dist/response/deliver.cjs.map +1 -0
- package/dist/response/deliver.js +143 -0
- package/dist/response/deliver.js.map +1 -0
- package/dist/response/frame-loop.cjs +102 -0
- package/dist/response/frame-loop.cjs.map +1 -0
- package/dist/response/frame-loop.js +100 -0
- package/dist/response/frame-loop.js.map +1 -0
- package/dist/response/lifecycle.cjs +223 -0
- package/dist/response/lifecycle.cjs.map +1 -0
- package/dist/response/lifecycle.js +211 -0
- package/dist/response/lifecycle.js.map +1 -0
- package/dist/response/proxy.cjs +71 -0
- package/dist/response/proxy.cjs.map +1 -0
- package/dist/response/proxy.js +70 -0
- package/dist/response/proxy.js.map +1 -0
- package/dist/response/raw.cjs +228 -0
- package/dist/response/raw.cjs.map +1 -0
- package/dist/response/raw.js +227 -0
- package/dist/response/raw.js.map +1 -0
- package/dist/response/timing.cjs +157 -0
- package/dist/response/timing.cjs.map +1 -0
- package/dist/response/timing.js +150 -0
- package/dist/response/timing.js.map +1 -0
- package/dist/responses.cjs +269 -148
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.d.cts.map +1 -1
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +270 -149
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +20 -4
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +7 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +7 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +21 -5
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +89 -120
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +91 -122
- package/dist/server.js.map +1 -1
- package/dist/sse-writer.cjs +49 -30
- package/dist/sse-writer.cjs.map +1 -1
- package/dist/sse-writer.d.cts +0 -1
- package/dist/sse-writer.d.cts.map +1 -1
- package/dist/sse-writer.d.ts +0 -1
- package/dist/sse-writer.d.ts.map +1 -1
- package/dist/sse-writer.js +49 -31
- package/dist/sse-writer.js.map +1 -1
- package/dist/transcription.cjs +19 -33
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.d.cts.map +1 -1
- package/dist/transcription.d.ts.map +1 -1
- package/dist/transcription.js +20 -34
- package/dist/transcription.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +173 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +173 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/ws-framing.cjs +12 -6
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +2 -0
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +2 -0
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +12 -6
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +120 -93
- package/dist/ws-gemini-live.cjs.map +1 -1
- package/dist/ws-gemini-live.js +120 -93
- package/dist/ws-gemini-live.js.map +1 -1
- package/dist/ws-realtime.cjs +80 -70
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +80 -70
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +72 -53
- package/dist/ws-responses.cjs.map +1 -1
- package/dist/ws-responses.d.cts.map +1 -1
- package/dist/ws-responses.d.ts.map +1 -1
- package/dist/ws-responses.js +73 -54
- package/dist/ws-responses.js.map +1 -1
- package/package.json +1 -1
- package/skills/write-fixtures/SKILL.md +34 -2
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { renderError } from "./response/errors.js";
|
|
2
2
|
import { resolveRequestDecisions, strictOverrideField } from "./response/policy.js";
|
|
3
|
-
import { extractOverrides, flattenHeaders, generateToolCallId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isTextResponse, isToolCallResponse, prepareOpenAIChatMisbehavior, requireEmittedFunctionToolCalls, resolveFixtureBlockOutcome, resolveOpenAIChatMisbehaviorUsage, resolveResponse, servedToolArgs, strictNoMatchLogLine, strictNoMatchMessage } from "./helpers.js";
|
|
4
|
-
import {
|
|
3
|
+
import { extractOverrides, flattenHeaders, generateToolCallId, getContext, getTestId, isCombinedFixtureResponse, isErrorResponse, isJsonObject, isRawResponse, isTextResponse, isToolCallResponse, prepareOpenAIChatMisbehavior, requireEmittedFunctionToolCalls, resolveFixtureBlockOutcome, resolveOpenAIChatMisbehaviorUsage, resolveResponse, servedToolArgs, strictNoMatchLogLine, strictNoMatchMessage } from "./helpers.js";
|
|
4
|
+
import { sseCodec, writeErrorResponse } from "./sse-writer.js";
|
|
5
5
|
import { applyChaosAsync } from "./chaos.js";
|
|
6
6
|
import { planMisbehavior, recordMisbehaviorOutcome } from "./misbehavior.js";
|
|
7
7
|
import { matchFixtureDiagnostic } from "./router.js";
|
|
8
8
|
import { proxyAndRecord } from "./recorder.js";
|
|
9
|
-
import {
|
|
9
|
+
import { misbehaviorPayloadLength, servedPayloadLength } from "./response/timing.js";
|
|
10
|
+
import { deliverJson, deliverStream } from "./response/deliver.js";
|
|
11
|
+
import { proxyInterruption, withHangUpDetection } from "./response/proxy.js";
|
|
12
|
+
import { serveRawResponse } from "./response/raw.js";
|
|
10
13
|
import { render } from "./response/usage/codecs/interactions.js";
|
|
11
14
|
|
|
12
15
|
//#region src/gemini-interactions.ts
|
|
@@ -489,33 +492,10 @@ function buildInteractionsContentWithToolCallsSSEEvents(content, toolCalls, inte
|
|
|
489
492
|
});
|
|
490
493
|
return events;
|
|
491
494
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
const { recordedTimings, replaySpeed } = opts;
|
|
497
|
-
const signal = opts.signal;
|
|
498
|
-
const onChunkSent = opts.onChunkSent;
|
|
499
|
-
if (res.writableEnded) return true;
|
|
500
|
-
res.setHeader("Content-Type", "text/event-stream");
|
|
501
|
-
res.setHeader("Cache-Control", "no-cache");
|
|
502
|
-
res.setHeader("Connection", "keep-alive");
|
|
503
|
-
let chunkIndex = 0;
|
|
504
|
-
for (const event of events) {
|
|
505
|
-
const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);
|
|
506
|
-
if (chunkDelay > 0) await delay(chunkDelay, signal);
|
|
507
|
-
if (signal?.aborted) return false;
|
|
508
|
-
if (res.writableEnded) return true;
|
|
509
|
-
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
510
|
-
if (event.event_type === "step.delta") {
|
|
511
|
-
onChunkSent?.();
|
|
512
|
-
chunkIndex++;
|
|
513
|
-
}
|
|
514
|
-
if (signal?.aborted) return false;
|
|
515
|
-
}
|
|
516
|
-
if (!res.writableEnded) res.end();
|
|
517
|
-
return true;
|
|
518
|
-
}
|
|
495
|
+
const interactionsCodec = {
|
|
496
|
+
...sseCodec({ done: false }),
|
|
497
|
+
counts: (event) => event.event_type === "step.delta"
|
|
498
|
+
};
|
|
519
499
|
function prepareInteractionsMisbehavior(plan, streaming) {
|
|
520
500
|
if (!streaming && plan.summary.fault === "stop-length-mid-tool") {
|
|
521
501
|
const response = plan.response;
|
|
@@ -611,7 +591,7 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
611
591
|
const streaming = interactionsReq.stream !== false;
|
|
612
592
|
const model = completionReq.model;
|
|
613
593
|
const testId = getTestId(req);
|
|
614
|
-
const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
|
|
594
|
+
const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, completionReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform, { wire: "gemini-interactions" });
|
|
615
595
|
if (fixture) journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
616
596
|
if (await applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
|
|
617
597
|
method: req.method ?? "POST",
|
|
@@ -643,7 +623,7 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
643
623
|
return;
|
|
644
624
|
}
|
|
645
625
|
if (decisions.upstream && defaults.record) {
|
|
646
|
-
const outcome = await proxyAndRecord(req, res, completionReq, "gemini-interactions", urlPath, fixtures, defaults, raw);
|
|
626
|
+
const { outcome, clientHungUp } = await withHangUpDetection(req, res, () => proxyAndRecord(req, res, completionReq, "gemini-interactions", urlPath, fixtures, defaults, raw));
|
|
647
627
|
if (outcome === "handled_by_hook") return;
|
|
648
628
|
if (outcome !== "not_configured") {
|
|
649
629
|
journal.add({
|
|
@@ -654,7 +634,8 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
654
634
|
response: {
|
|
655
635
|
status: res.statusCode ?? 200,
|
|
656
636
|
fixture: null,
|
|
657
|
-
source: "proxy"
|
|
637
|
+
source: "proxy",
|
|
638
|
+
...proxyInterruption(res, clientHungUp)
|
|
658
639
|
}
|
|
659
640
|
});
|
|
660
641
|
return;
|
|
@@ -720,6 +701,42 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
720
701
|
const latency = fixture.latency ?? defaults.latency;
|
|
721
702
|
const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);
|
|
722
703
|
const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;
|
|
704
|
+
const streamTiming = {
|
|
705
|
+
latency,
|
|
706
|
+
streamingProfile: fixture.streamingProfile,
|
|
707
|
+
recordedTimings: fixture.recordedTimings,
|
|
708
|
+
replaySpeed
|
|
709
|
+
};
|
|
710
|
+
const applied = misbehavior.kind === "applied" ? misbehavior : void 0;
|
|
711
|
+
const jsonOpts = () => ({
|
|
712
|
+
surface: "other",
|
|
713
|
+
payloadLength: applied ? misbehaviorPayloadLength(applied, void 0) : servedPayloadLength(response),
|
|
714
|
+
chunkSize,
|
|
715
|
+
recordedTimings: fixture.recordedTimings,
|
|
716
|
+
duplicateId: !!applied?.duplicateId,
|
|
717
|
+
replaySpeed
|
|
718
|
+
});
|
|
719
|
+
const markIncomplete = (body) => applied?.summary.fault === "stop-length-mid-tool" ? {
|
|
720
|
+
...body,
|
|
721
|
+
status: "incomplete"
|
|
722
|
+
} : body;
|
|
723
|
+
if (isRawResponse(response)) {
|
|
724
|
+
await serveRawResponse({
|
|
725
|
+
wire: "gemini-interactions",
|
|
726
|
+
stream: streaming,
|
|
727
|
+
req,
|
|
728
|
+
res,
|
|
729
|
+
fixture,
|
|
730
|
+
response,
|
|
731
|
+
body: completionReq,
|
|
732
|
+
journal,
|
|
733
|
+
defaults,
|
|
734
|
+
testId,
|
|
735
|
+
misbehaviorSummary: misbehavior.summary,
|
|
736
|
+
errorWire: "interactions"
|
|
737
|
+
});
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
723
740
|
if (isErrorResponse(response)) {
|
|
724
741
|
const status = response.status ?? 500;
|
|
725
742
|
recordMisbehaviorOutcome({
|
|
@@ -764,8 +781,8 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
764
781
|
const usage = render({ usage: appliedUsage }, "applied-reasoning-only");
|
|
765
782
|
if (!streaming) {
|
|
766
783
|
const overrides = isTextResponse(response) || isToolCallResponse(response) || isCombinedFixtureResponse(response) ? extractOverrides(response) : void 0;
|
|
767
|
-
|
|
768
|
-
res
|
|
784
|
+
const reasoning = misbehavior.reasoning ?? "";
|
|
785
|
+
await deliverJson(res, journalEntry, fixture, () => ({
|
|
769
786
|
id: interactionId,
|
|
770
787
|
model: overrides?.model ?? model,
|
|
771
788
|
status: "incomplete",
|
|
@@ -774,14 +791,14 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
774
791
|
signature: "",
|
|
775
792
|
summary: [{
|
|
776
793
|
type: "text",
|
|
777
|
-
text:
|
|
794
|
+
text: reasoning
|
|
778
795
|
}]
|
|
779
796
|
}],
|
|
780
797
|
usage
|
|
781
|
-
}));
|
|
798
|
+
}), jsonOpts());
|
|
782
799
|
return;
|
|
783
800
|
}
|
|
784
|
-
|
|
801
|
+
await deliverStream(res, journalEntry, fixture, interactionsCodec, [
|
|
785
802
|
{
|
|
786
803
|
event_type: "interaction.created",
|
|
787
804
|
interaction: {
|
|
@@ -822,21 +839,7 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
822
839
|
},
|
|
823
840
|
event_id: nextEventId()
|
|
824
841
|
}
|
|
825
|
-
];
|
|
826
|
-
const interruption = createInterruptionSignal(fixture);
|
|
827
|
-
if (!await writeGeminiInteractionsSSEStream(res, events, {
|
|
828
|
-
latency,
|
|
829
|
-
streamingProfile: fixture.streamingProfile,
|
|
830
|
-
recordedTimings: fixture.recordedTimings,
|
|
831
|
-
replaySpeed,
|
|
832
|
-
signal: interruption?.signal,
|
|
833
|
-
onChunkSent: interruption?.tick
|
|
834
|
-
})) {
|
|
835
|
-
if (!res.writableEnded) res.destroy();
|
|
836
|
-
journalEntry.response.interrupted = true;
|
|
837
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
838
|
-
}
|
|
839
|
-
interruption?.cleanup();
|
|
842
|
+
], streamTiming);
|
|
840
843
|
return;
|
|
841
844
|
}
|
|
842
845
|
if (isCombinedFixtureResponse(response)) {
|
|
@@ -864,29 +867,10 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
864
867
|
const functionToolCalls = requireEmittedFunctionToolCalls(response, "Gemini Interactions");
|
|
865
868
|
const interactionId = nextInteractionId();
|
|
866
869
|
if (!streaming) {
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
res
|
|
870
|
-
|
|
871
|
-
status: "incomplete"
|
|
872
|
-
} : body));
|
|
873
|
-
} else {
|
|
874
|
-
const events = buildInteractionsContentWithToolCallsSSEEvents(response.content ?? "", functionToolCalls, interactionId, chunkSize, logger, overrides, response.blocks);
|
|
875
|
-
const interruption = createInterruptionSignal(fixture);
|
|
876
|
-
if (!await writeGeminiInteractionsSSEStream(res, finalizeInteractionsMisbehavior(events, misbehavior), {
|
|
877
|
-
latency,
|
|
878
|
-
streamingProfile: fixture.streamingProfile,
|
|
879
|
-
recordedTimings: fixture.recordedTimings,
|
|
880
|
-
replaySpeed,
|
|
881
|
-
signal: interruption?.signal,
|
|
882
|
-
onChunkSent: interruption?.tick
|
|
883
|
-
})) {
|
|
884
|
-
if (!res.writableEnded) res.destroy();
|
|
885
|
-
journalEntry.response.interrupted = true;
|
|
886
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
887
|
-
}
|
|
888
|
-
interruption?.cleanup();
|
|
889
|
-
}
|
|
870
|
+
const content = response.content ?? "";
|
|
871
|
+
const blocks = response.blocks;
|
|
872
|
+
await deliverJson(res, journalEntry, fixture, () => markIncomplete(buildInteractionsContentWithToolCallsResponse(content, functionToolCalls, model, interactionId, logger, overrides, blocks)), jsonOpts());
|
|
873
|
+
} else await deliverStream(res, journalEntry, fixture, interactionsCodec, finalizeInteractionsMisbehavior(buildInteractionsContentWithToolCallsSSEEvents(response.content ?? "", functionToolCalls, interactionId, chunkSize, logger, overrides, response.blocks), misbehavior), streamTiming);
|
|
890
874
|
return;
|
|
891
875
|
}
|
|
892
876
|
if (isTextResponse(response)) {
|
|
@@ -913,29 +897,9 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
913
897
|
});
|
|
914
898
|
const interactionId = nextInteractionId();
|
|
915
899
|
if (!streaming) {
|
|
916
|
-
const
|
|
917
|
-
res
|
|
918
|
-
|
|
919
|
-
...body,
|
|
920
|
-
status: "incomplete"
|
|
921
|
-
} : body));
|
|
922
|
-
} else {
|
|
923
|
-
const events = buildInteractionsTextSSEEvents(response.content, interactionId, chunkSize, overrides);
|
|
924
|
-
const interruption = createInterruptionSignal(fixture);
|
|
925
|
-
if (!await writeGeminiInteractionsSSEStream(res, finalizeInteractionsMisbehavior(events, misbehavior), {
|
|
926
|
-
latency,
|
|
927
|
-
streamingProfile: fixture.streamingProfile,
|
|
928
|
-
recordedTimings: fixture.recordedTimings,
|
|
929
|
-
replaySpeed,
|
|
930
|
-
signal: interruption?.signal,
|
|
931
|
-
onChunkSent: interruption?.tick
|
|
932
|
-
})) {
|
|
933
|
-
if (!res.writableEnded) res.destroy();
|
|
934
|
-
journalEntry.response.interrupted = true;
|
|
935
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
936
|
-
}
|
|
937
|
-
interruption?.cleanup();
|
|
938
|
-
}
|
|
900
|
+
const content = response.content;
|
|
901
|
+
await deliverJson(res, journalEntry, fixture, () => markIncomplete(buildInteractionsTextResponse(content, model, interactionId, overrides)), jsonOpts());
|
|
902
|
+
} else await deliverStream(res, journalEntry, fixture, interactionsCodec, finalizeInteractionsMisbehavior(buildInteractionsTextSSEEvents(response.content, interactionId, chunkSize, overrides), misbehavior), streamTiming);
|
|
939
903
|
return;
|
|
940
904
|
}
|
|
941
905
|
if (isToolCallResponse(response)) {
|
|
@@ -962,30 +926,8 @@ async function handleGeminiInteractions(req, res, raw, fixtures, journal, defaul
|
|
|
962
926
|
});
|
|
963
927
|
const functionToolCalls = requireEmittedFunctionToolCalls(response, "Gemini Interactions");
|
|
964
928
|
const interactionId = nextInteractionId();
|
|
965
|
-
if (!streaming)
|
|
966
|
-
|
|
967
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
968
|
-
res.end(JSON.stringify(misbehavior.kind === "applied" && misbehavior.summary.fault === "stop-length-mid-tool" ? {
|
|
969
|
-
...body,
|
|
970
|
-
status: "incomplete"
|
|
971
|
-
} : body));
|
|
972
|
-
} else {
|
|
973
|
-
const events = buildInteractionsToolCallSSEEvents(functionToolCalls, interactionId, logger, overrides);
|
|
974
|
-
const interruption = createInterruptionSignal(fixture);
|
|
975
|
-
if (!await writeGeminiInteractionsSSEStream(res, finalizeInteractionsMisbehavior(events, misbehavior), {
|
|
976
|
-
latency,
|
|
977
|
-
streamingProfile: fixture.streamingProfile,
|
|
978
|
-
recordedTimings: fixture.recordedTimings,
|
|
979
|
-
replaySpeed,
|
|
980
|
-
signal: interruption?.signal,
|
|
981
|
-
onChunkSent: interruption?.tick
|
|
982
|
-
})) {
|
|
983
|
-
if (!res.writableEnded) res.destroy();
|
|
984
|
-
journalEntry.response.interrupted = true;
|
|
985
|
-
journalEntry.response.interruptReason = interruption?.reason();
|
|
986
|
-
}
|
|
987
|
-
interruption?.cleanup();
|
|
988
|
-
}
|
|
929
|
+
if (!streaming) await deliverJson(res, journalEntry, fixture, () => markIncomplete(buildInteractionsToolCallResponse(functionToolCalls, model, interactionId, logger, overrides)), jsonOpts());
|
|
930
|
+
else await deliverStream(res, journalEntry, fixture, interactionsCodec, finalizeInteractionsMisbehavior(buildInteractionsToolCallSSEEvents(functionToolCalls, interactionId, logger, overrides), misbehavior), streamTiming);
|
|
989
931
|
return;
|
|
990
932
|
}
|
|
991
933
|
recordMisbehaviorOutcome({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini-interactions.js","names":["renderInteractionsUsage"],"sources":["../src/gemini-interactions.ts"],"sourcesContent":["/**\n * Google Gemini Interactions API support.\n *\n * Translates incoming Interactions requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back\n * into the Gemini Interactions format — either a single JSON response or\n * an SSE stream with event_type-based framing.\n */\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport { renderError } from \"./response/errors.js\";\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n HandlerDefaults,\n RecordedTimings,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n requireEmittedFunctionToolCalls,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n extractOverrides,\n generateToolCallId,\n flattenHeaders,\n isJsonObject,\n getTestId,\n getContext,\n resolveResponse,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n resolveFixtureBlockOutcome,\n servedToolArgs,\n prepareOpenAIChatMisbehavior,\n resolveOpenAIChatMisbehaviorUsage,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse, delay, calculateDelay } from \"./sse-writer.js\";\nimport { createInterruptionSignal } from \"./interruption.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaosAsync } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { render as renderInteractionsUsage } from \"./response/usage/codecs/interactions.js\";\nimport {\n planMisbehavior,\n recordMisbehaviorOutcome,\n type MisbehaviorPlan,\n type MisbehaviorSkip,\n} from \"./misbehavior.js\";\n\n// ─── Interactions request types ────────────────────────────────────────────\n\ninterface InteractionsContentBlock {\n type: string;\n text?: string;\n name?: string;\n call_id?: string;\n id?: string;\n arguments?: Record<string, unknown>;\n output?: unknown;\n result?: unknown;\n}\n\ninterface InteractionsTurn {\n role: string;\n content?: InteractionsContentBlock[];\n parts?: InteractionsContentBlock[];\n}\n\n/**\n * Top-level Step envelope accepted by the live Gemini Interactions API.\n * The SDK's TypeScript union does not include Step[], but the wire contract\n * does — clients following the live API send these at the top level of `input`.\n * Discriminated by `type`; no `role` field (distinguishes from Turn[]).\n */\ninterface InteractionsStep {\n type: string;\n content?: InteractionsContentBlock[];\n call_id?: string;\n id?: string;\n name?: string;\n result?: unknown;\n output?: unknown;\n is_error?: boolean;\n signature?: string;\n}\n\n/** Step types whose payload is a tool/agent result keyed by call_id. */\nconst STEP_RESULT_TYPES = new Set<string>([\n \"function_result\",\n \"code_execution_result\",\n \"url_context_result\",\n \"google_search_result\",\n \"google_maps_result\",\n \"mcp_server_tool_result\",\n \"file_search_result\",\n]);\n\n/** All recognized top-level Step types (used as the Step[] discriminator). */\nconst STEP_TYPES = new Set<string>([\"user_input\", \"model_output\", ...STEP_RESULT_TYPES]);\n\ninterface InteractionsFunctionTool {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n}\n\ninterface InteractionsRequest {\n model?: string;\n input?: string | InteractionsTurn[] | InteractionsStep[] | InteractionsContentBlock[];\n system_instruction?: string;\n tools?: InteractionsFunctionTool[];\n generation_config?: {\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n };\n stream?: boolean;\n previous_interaction_id?: string;\n [key: string]: unknown;\n}\n\n// Check unsupported numeric input and containers consumed by the selected variant.\n// Other values retain their existing normalization, including omitted input.\nfunction validateInteractionsInput(input: unknown): string | null {\n if (typeof input === \"number\") return \"input must not be a number\";\n if (!Array.isArray(input)) return null;\n const first = input[0];\n if (isJsonObject(first) && \"role\" in first) {\n for (const [index, turn] of input.entries()) {\n if (!isJsonObject(turn)) continue;\n const blocks = turn.content ?? turn.parts;\n // Empty strings, falsy values and empty arrays bypass the converter's\n // filters. Preserve that behavior and the content ?? parts selection.\n if (typeof blocks === \"string\" && blocks.length > 0) {\n const field = turn.content != null ? \"content\" : \"parts\";\n return `input[${index}].${field} must be an array`;\n }\n }\n } else if (\n !(isJsonObject(first) && typeof first.type === \"string\" && STEP_TYPES.has(first.type))\n ) {\n const nullIndex = input.findIndex((part) => part === null);\n if (nullIndex !== -1) return `input[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// Preserve tools that the converter ignores; reject only these unsafe reads.\nfunction validateInteractionsTools(tools: unknown): string | null {\n if (typeof tools === \"string\" && tools.length > 0) return \"tools must be an array\";\n if (Array.isArray(tools)) {\n const nullIndex = tools.findIndex((tool) => tool === null);\n if (nullIndex !== -1) return `tools[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// ─── Input conversion: Interactions → ChatCompletionRequest ───────────────\n\nexport function geminiInteractionsToCompletionRequest(\n req: InteractionsRequest,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n const model = req.model ?? \"gemini-2.5-flash\";\n\n // system_instruction → system message\n if (req.system_instruction) {\n messages.push({ role: \"system\", content: req.system_instruction });\n }\n\n // Parse input\n if (req.input !== undefined) {\n if (typeof req.input === \"string\") {\n // Simple string input → single user message\n messages.push({ role: \"user\", content: req.input });\n } else if (Array.isArray(req.input)) {\n // Could be Turn[], Step[], or Content[]\n const firstItem = req.input[0] as\n | InteractionsTurn\n | InteractionsStep\n | InteractionsContentBlock\n | undefined;\n const isStepArray =\n !!firstItem &&\n !(\"role\" in firstItem) &&\n typeof firstItem.type === \"string\" &&\n STEP_TYPES.has(firstItem.type);\n\n if (firstItem && \"role\" in firstItem) {\n // Turn[] format\n for (const turn of req.input as InteractionsTurn[]) {\n const role = turn.role === \"model\" ? \"assistant\" : turn.role;\n const blocks = turn.content ?? turn.parts;\n if (!blocks || blocks.length === 0) {\n if (role === \"user\" || role === \"assistant\") {\n messages.push({ role: role as \"user\" | \"assistant\", content: \"\" });\n }\n continue;\n }\n\n // Check for function_call or function_result parts\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const funcResultParts = blocks.filter((p) => p.type === \"function_result\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n\n if (funcCallParts.length > 0) {\n // Assistant tool call message\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else if (funcResultParts.length > 0) {\n // Tool response messages\n for (const part of funcResultParts) {\n const resultValue = part.result ?? part.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: part.call_id ?? part.id ?? \"\",\n });\n }\n // Any text parts alongside → separate user message\n if (textParts.length > 0) {\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (text) {\n messages.push({ role: \"user\", content: text });\n }\n }\n } else {\n // Text-only turn\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (role === \"user\" || role === \"assistant\" || role === \"system\") {\n messages.push({\n role: role as \"user\" | \"assistant\" | \"system\",\n content: text,\n });\n }\n }\n }\n } else if (isStepArray) {\n // Step[] format — the wire contract Google's /v1beta/interactions accepts.\n for (const step of req.input as InteractionsStep[]) {\n if (step.type === \"user_input\") {\n const text = (step.content ?? [])\n .filter((p) => p.type === \"text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (step.type === \"model_output\") {\n const blocks = step.content ?? [];\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n\n if (funcCallParts.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent });\n }\n } else if (STEP_RESULT_TYPES.has(step.type)) {\n const resultValue = step.result ?? step.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: step.call_id ?? step.id ?? \"\",\n });\n }\n }\n } else {\n // Content[] format — single user message with content blocks\n const textParts = (req.input as InteractionsContentBlock[]).filter(\n (p) => p.type === \"text\",\n );\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({ role: \"user\", content: text || \"\" });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n const funcTools = req.tools.filter((t) => t.type === \"function\");\n if (funcTools.length > 0) {\n tools = funcTools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n },\n }));\n }\n }\n\n return {\n model,\n messages,\n stream: req.stream !== false, // default true\n temperature: req.generation_config?.temperature,\n max_tokens: req.generation_config?.max_output_tokens,\n tools,\n };\n}\n\n// ─── Interaction ID generation ────────────────────────────────────────────\n\nlet interactionCounter = 0;\n\nexport function resetInteractionCounter(): void {\n interactionCounter = 0;\n}\n\nfunction nextInteractionId(): string {\n return `aimock-int-${interactionCounter++}`;\n}\n\n// ─── Usage helpers ────────────────────────────────────────────────────────\n\nfunction interactionsUsage(overrides?: ResponseOverrides): {\n total_input_tokens: number;\n total_output_tokens: number;\n total_tokens: number;\n} {\n return renderInteractionsUsage(overrides);\n}\n\n// ─── Response building: fixture → Interactions format ─────────────────────\n\nexport function buildInteractionsTextResponse(\n content: string,\n model: string,\n interactionId: string,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: content,\n steps: [{ type: \"model_output\", content: [{ type: \"text\", text: content }] }],\n usage: interactionsUsage(overrides),\n };\n}\n\n// Build a single SDK 2.x function_call step from a fixture tool call,\n// serving malformed arguments as `{}` (or rejecting them under strictToolArguments).\nfunction buildFunctionCallStep(tc: ToolCall, logger: Logger): object {\n const args = servedToolArgs(tc, \"object\", logger);\n return {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: args.value,\n };\n}\n\nexport function buildInteractionsToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"requires_action\",\n model: overrides?.model ?? model,\n role: \"model\",\n steps: toolCalls.map((tc) => buildFunctionCallStep(tc, logger)),\n usage: interactionsUsage(overrides),\n };\n}\n\nexport function buildInteractionsContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): object {\n const steps: object[] = [];\n // Collect output_text in step order so the top-level field mirrors the\n // concatenated text steps regardless of where they appear in `steps`.\n let outputText = \"\";\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-stream `steps[]` array is index/step-addressed and\n // ordered, so emit one step per block in fixture ARRAY ORDER. A toolCall\n // block placed before a text block therefore yields a function_call step\n // ahead of the model_output step — tool-first, the opposite of the legacy\n // (text-step-always-first) shape below.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n for (const block of ordered) {\n if (block.type === \"text\") {\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: block.text }] });\n outputText += block.text;\n } else {\n steps.push(\n buildFunctionCallStep(\n { name: block.name, arguments: block.arguments, id: block.id },\n logger,\n ),\n );\n }\n }\n } else {\n // LEGACY PATH: a single text step first, then function_call steps —\n // unchanged from the pre-blocks behavior (text always leads `steps`).\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: content }] });\n outputText = content;\n for (const tc of toolCalls) {\n steps.push(buildFunctionCallStep(tc, logger));\n }\n }\n\n return {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: outputText,\n steps,\n usage: interactionsUsage(overrides),\n };\n}\n\n// ─── SSE event builders ──────────────────────────────────────────────────\n\ninterface InteractionsSSEEvent {\n event_type: string;\n [key: string]: unknown;\n}\n\nlet eventIdCounter = 0;\n\nexport function resetEventIdCounter(): void {\n eventIdCounter = 0;\n}\n\nfunction nextEventId(): string {\n return `evt_${++eventIdCounter}`;\n}\n\nexport function buildInteractionsTextSSEEvents(\n content: string,\n interactionId: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // step.start — text step is the model_output\n events.push({\n event_type: \"step.start\",\n index: 0,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n // step.delta(s) — inner delta shape is unchanged ({ type: \"text\", text })\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n // step.stop\n events.push({\n event_type: \"step.stop\",\n index: 0,\n event_id: nextEventId(),\n });\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\nexport function buildInteractionsToolCallSSEEvents(\n toolCalls: ToolCall[],\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // Each tool call gets its own step.start/delta/stop bracket. In SDK 2.x the\n // call identity (id, name) lives on step.start; the arguments stream as a\n // dedicated `arguments_delta` carrying a JSON-string fragment, and step.start\n // carries an empty `arguments: {}` placeholder.\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index: idx,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n // arguments_delta.arguments is a string fragment. The real SDK may split\n // the args across several fragments that concatenate into valid JSON; the\n // mock emits the whole serialized object as one fragment (a valid\n // degenerate case the collapser handles identically).\n events.push({\n event_type: \"step.delta\",\n index: idx,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index: idx,\n event_id: nextEventId(),\n });\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"requires_action\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// Emit the step.start/delta(s)/stop bracket for a text (model_output) step at\n// a given step `index`. Inner delta shape ({ type: \"text\", text }) and the\n// empty-content single-empty-delta behavior are unchanged from the legacy path.\nfunction pushTextStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n content: string,\n chunkSize: number,\n): void {\n events.push({\n event_type: \"step.start\",\n index,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\n// Emit the step.start/arguments_delta/stop bracket for a function_call step at\n// a given step `index`. Identity (id, name) lives on step.start with an empty\n// `arguments: {}` placeholder; the arguments stream as a single\n// `arguments_delta` JSON-string fragment — unchanged from the legacy path.\nfunction pushFunctionCallStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n tc: ToolCall,\n logger: Logger,\n): void {\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.delta\",\n index,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\nexport function buildInteractionsContentWithToolCallsSSEEvents(\n content: string,\n toolCalls: ToolCall[],\n interactionId: string,\n chunkSize: number,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: stream one step per block in fixture ARRAY ORDER. The step\n // `index` increments with array position, so a toolCall block before a text\n // block yields a function_call step at a LOWER index than the model_output\n // step — tool-first, the opposite of the legacy (text-at-index-0) shape.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n let idx = 0;\n for (const block of ordered) {\n if (block.type === \"text\") {\n pushTextStepEvents(events, idx, block.text, chunkSize);\n } else {\n pushFunctionCallStepEvents(\n events,\n idx,\n {\n name: block.name,\n arguments: block.arguments,\n id: block.id,\n },\n logger,\n );\n }\n idx += 1;\n }\n } else {\n // LEGACY PATH: text content at index 0 (model_output step), tool calls at\n // index 1+ — byte-for-byte unchanged from the pre-blocks behavior.\n pushTextStepEvents(events, 0, content, chunkSize);\n for (let i = 0; i < toolCalls.length; i++) {\n pushFunctionCallStepEvents(events, i + 1, toolCalls[i], logger);\n }\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// ─── SSE writer for Interactions streaming ────────────────────────────────\n\ninterface InteractionsStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nexport async function writeGeminiInteractionsSSEStream(\n res: http.ServerResponse,\n events: InteractionsSSEEvent[],\n optionsOrLatency?: number | InteractionsStreamOptions,\n): Promise<boolean> {\n const opts: InteractionsStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n const latency = opts.latency ?? 0;\n const profile = opts.streamingProfile;\n const { recordedTimings, replaySpeed } = opts;\n const signal = opts.signal;\n const onChunkSent = opts.onChunkSent;\n\n if (res.writableEnded) return true;\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n let chunkIndex = 0;\n for (const event of events) {\n const chunkDelay = calculateDelay(chunkIndex, profile, latency, recordedTimings, replaySpeed);\n if (chunkDelay > 0) await delay(chunkDelay, signal);\n if (signal?.aborted) return false;\n if (res.writableEnded) return true;\n // Data-only SSE (no event: prefix, no [DONE])\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n // Only count step deltas for truncateAfterChunks — framing events\n // (interaction.created, step.start, step.stop, interaction.completed)\n // should not consume chunk budget or trigger the chunk-sent callback.\n if (event.event_type === \"step.delta\") {\n onChunkSent?.();\n chunkIndex++;\n }\n if (signal?.aborted) return false;\n }\n\n if (!res.writableEnded) {\n res.end();\n }\n return true;\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nfunction prepareInteractionsMisbehavior(\n plan: MisbehaviorPlan,\n streaming: boolean,\n): MisbehaviorPlan {\n if (!streaming && plan.summary.fault === \"stop-length-mid-tool\") {\n // The object wire cannot represent the selected partial argument string.\n // Earlier completed calls/text survive; omit the cut call before allocating\n // IDs and preparing servedToolCalls/usage.\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n const outcome =\n combined && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? (response.toolCalls ?? []) : []);\n const toolCalls = calls.filter((_, index) => index !== plan.target?.index);\n let callIndex = 0;\n const blocks = outcome?.ordered.filter(\n (block) => block.type === \"text\" || callIndex++ !== plan.target?.index,\n );\n plan = { ...plan, response: { ...response, toolCalls, ...(blocks ? { blocks } : {}) } };\n }\n // Both modes always emit IDs. The shared preparation preserves effective\n // block order and allocates each wire/journal identity exactly once.\n return prepareOpenAIChatMisbehavior(plan);\n}\n\nfunction finalizeInteractionsMisbehavior(\n events: InteractionsSSEEvent[],\n plan: MisbehaviorPlan | MisbehaviorSkip,\n): InteractionsSSEEvent[] {\n if (plan.kind !== \"applied\" || plan.summary.fault !== \"stop-length-mid-tool\") return events;\n // Approved modeled K5 contract: keep the cut arguments and step.stop, then\n // terminate incomplete. Native truncated-tool closure remains unverified.\n return events.map((event) =>\n event.event_type === \"interaction.completed\" && isJsonObject(event.interaction)\n ? { ...event, interaction: { ...event.interaction, status: \"incomplete\" } }\n : event,\n );\n}\n\nexport async function handleGeminiInteractions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/v1beta/interactions\";\n\n let interactionsReq: InteractionsRequest;\n try {\n interactionsReq = JSON.parse(raw) as InteractionsRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: `Malformed JSON body: ${detail}`,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `interactionsReq.stream` throws a TypeError that\n // surfaces as a 500 instead of a 400.\n if (!isJsonObject(interactionsReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: \"Request body must be a JSON object\",\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n const validationError =\n validateInteractionsInput(interactionsReq.input) ??\n validateInteractionsTools(interactionsReq.tools);\n if (validationError) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: validationError,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = geminiInteractionsToCompletionRequest(interactionsReq);\n // Keep \"chat\" rather than \"gemini-interactions\" — the router's endpoint\n // compatibility filter (router.ts) treats \"chat\" as a pass-through that\n // matches any unendpointed fixture. Switching to \"gemini-interactions\"\n // would make the request fall into the multimedia guard branch, preventing\n // generic chat fixtures from matching and breaking existing users. The\n // recorder would also start emitting `endpoint: \"gemini-interactions\"` in\n // recorded fixtures, creating a one-way compatibility break.\n completionReq._endpointType = \"chat\";\n completionReq._context = getContext(req);\n\n const streaming = interactionsReq.stream !== false; // default true\n const model = completionReq.model;\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const decisions = resolveRequestDecisions(defaults.strict, req.headers);\n if (decisions.fixtureMiss) {\n const strictStatus = 503;\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", urlPath, skippedBySequenceOrTurn));\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n renderError(\"interactions\", \"strict-miss\", {\n message: strictMessage,\n code: \"UNAVAILABLE\",\n }).body,\n );\n return;\n }\n if (decisions.upstream && defaults.record) {\n const outcome = await proxyAndRecord(\n req,\n res,\n completionReq,\n \"gemini-interactions\",\n urlPath,\n fixtures,\n defaults,\n raw,\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: res.statusCode ?? 200,\n fixture: null,\n source: \"proxy\",\n },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n renderError(\"interactions\", \"fixture-miss\", {\n message: \"No fixture matched\",\n code: \"NOT_FOUND\",\n }).body,\n );\n return;\n }\n\n let response = await resolveResponse(fixture, completionReq);\n let misbehavior = planMisbehavior({\n wire: \"gemini-interactions\",\n fixture,\n response,\n request: completionReq,\n stream: streaming,\n emitsToolCallIds: true,\n defaults,\n rawHeaders: req.headers,\n url: req.url,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordMisbehaviorOutcome({ entry, summary: misbehavior.summary, defaults, testId });\n if (!misbehavior.summary?.evaluations.some((evaluation) => evaluation.outcome === \"error\")) {\n logger.error(`${req.method ?? \"POST\"} ${urlPath}: ${misbehavior.message}`);\n }\n writeErrorResponse(\n res,\n misbehavior.status,\n renderError(\"interactions\", \"misbehavior\", {\n message: misbehavior.message,\n code: misbehavior.code,\n }).body,\n );\n return;\n }\n if (misbehavior.kind === \"applied\") {\n misbehavior = prepareInteractionsMisbehavior(misbehavior, streaming);\n response = misbehavior.response;\n }\n const appliedUsage =\n misbehavior.kind === \"applied\"\n ? resolveOpenAIChatMisbehaviorUsage(misbehavior, completionReq)\n : undefined;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n writeErrorResponse(\n res,\n status,\n renderError(\"interactions\", \"fixture\", {\n message: response.error.message,\n code: response.error.type ?? \"ERROR\",\n }).body,\n { retryAfter: response.retryAfter },\n );\n return;\n }\n\n // The content+tool and tool-call branches draw their interaction id after\n // requireEmittedFunctionToolCalls, so a fixture it rejects (a custom tool\n // call or a malformed toolCalls entry) consumes no id. A rejection raised\n // later, inside a builder, comes after the draw and does consume an id:\n // invalid JSON arguments on the non-streaming path, or a malformed block\n // from resolveFixtureBlockOutcome (the streaming path has also consumed\n // event ids by then).\n\n if (misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"reasoning-only\") {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n // Reasoning-only output carries no tool call, so it has no tool-call check\n // to pass before drawing its id.\n const interactionId = nextInteractionId();\n const usage = renderInteractionsUsage({ usage: appliedUsage }, \"applied-reasoning-only\");\n if (!streaming) {\n const overrides =\n isTextResponse(response) ||\n isToolCallResponse(response) ||\n isCombinedFixtureResponse(response)\n ? extractOverrides(response)\n : undefined;\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n id: interactionId,\n model: overrides?.model ?? model,\n status: \"incomplete\",\n steps: [\n {\n type: \"thought\",\n signature: \"\",\n summary: [{ type: \"text\", text: misbehavior.reasoning ?? \"\" }],\n },\n ],\n usage,\n }),\n );\n return;\n }\n const events: InteractionsSSEEvent[] = [\n {\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n },\n { event_type: \"step.start\", index: 0, step: { type: \"thought\" }, event_id: nextEventId() },\n {\n event_type: \"step.delta\",\n index: 0,\n delta: {\n type: \"thought_summary\",\n content: { type: \"text\", text: misbehavior.reasoning ?? \"\" },\n },\n event_id: nextEventId(),\n },\n { event_type: \"step.stop\", index: 0, event_id: nextEventId() },\n {\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"incomplete\",\n usage,\n },\n event_id: nextEventId(),\n },\n ];\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(res, events, {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n });\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n return;\n }\n\n // Content + tool calls response\n if (isCombinedFixtureResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n const body = buildInteractionsContentWithToolCallsResponse(\n response.content ?? \"\",\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n response.blocks,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"stop-length-mid-tool\"\n ? { ...body, status: \"incomplete\" }\n : body,\n ),\n );\n } else {\n const events = buildInteractionsContentWithToolCallsSSEEvents(\n response.content ?? \"\",\n functionToolCalls,\n interactionId,\n chunkSize,\n logger,\n overrides,\n response.blocks,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(\n res,\n finalizeInteractionsMisbehavior(events, misbehavior),\n {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n },\n );\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const interactionId = nextInteractionId();\n if (!streaming) {\n const body = buildInteractionsTextResponse(response.content, model, interactionId, overrides);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"stop-length-mid-tool\"\n ? { ...body, status: \"incomplete\" }\n : body,\n ),\n );\n } else {\n const events = buildInteractionsTextSSEEvents(\n response.content,\n interactionId,\n chunkSize,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(\n res,\n finalizeInteractionsMisbehavior(events, misbehavior),\n {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n },\n );\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n const body = buildInteractionsToolCallResponse(\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n );\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify(\n misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"stop-length-mid-tool\"\n ? { ...body, status: \"incomplete\" }\n : body,\n ),\n );\n } else {\n const events = buildInteractionsToolCallSSEEvents(\n functionToolCalls,\n interactionId,\n logger,\n overrides,\n );\n const interruption = createInterruptionSignal(fixture);\n const completed = await writeGeminiInteractionsSSEStream(\n res,\n finalizeInteractionsMisbehavior(events, misbehavior),\n {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n signal: interruption?.signal,\n onChunkSent: interruption?.tick,\n },\n );\n if (!completed) {\n if (!res.writableEnded) res.destroy();\n journalEntry.response.interrupted = true;\n journalEntry.response.interruptReason = interruption?.reason();\n }\n interruption?.cleanup();\n }\n return;\n }\n\n // Unknown response type\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordMisbehaviorOutcome({ entry: journalEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n 500,\n renderError(\"interactions\", \"unknown-response\", {\n message: \"Fixture response did not match any known type\",\n code: \"INTERNAL\",\n }).body,\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"gemini-interactions.js","names":["renderInteractionsUsage"],"sources":["../src/gemini-interactions.ts"],"sourcesContent":["/**\n * Google Gemini Interactions API support.\n *\n * Translates incoming Interactions requests into the ChatCompletionRequest\n * format used by the fixture router, and converts fixture responses back\n * into the Gemini Interactions format — either a single JSON response or\n * an SSE stream with event_type-based framing.\n */\nimport { resolveRequestDecisions, strictOverrideField } from \"./response/policy.js\";\nimport { renderError } from \"./response/errors.js\";\nimport { serveRawResponse } from \"./response/raw.js\";\n\nimport type * as http from \"node:http\";\nimport type {\n ChatCompletionRequest,\n ChatMessage,\n Fixture,\n FixtureBlock,\n HandlerDefaults,\n RecordedTimings,\n ResponseOverrides,\n StreamingProfile,\n ToolCall,\n ToolDefinition,\n} from \"./types.js\";\nimport {\n requireEmittedFunctionToolCalls,\n isTextResponse,\n isToolCallResponse,\n isCombinedFixtureResponse,\n isErrorResponse,\n isRawResponse,\n extractOverrides,\n generateToolCallId,\n flattenHeaders,\n isJsonObject,\n getTestId,\n getContext,\n resolveResponse,\n strictNoMatchMessage,\n strictNoMatchLogLine,\n resolveFixtureBlockOutcome,\n servedToolArgs,\n prepareOpenAIChatMisbehavior,\n resolveOpenAIChatMisbehaviorUsage,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse, sseCodec } from \"./sse-writer.js\";\nimport { httpSink, runFrameLoop, type FrameCodec } from \"./response/frame-loop.js\";\nimport { deliverJson, deliverStream } from \"./response/deliver.js\";\nimport { proxyInterruption, withHangUpDetection } from \"./response/proxy.js\";\nimport { misbehaviorPayloadLength, servedPayloadLength } from \"./response/timing.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\nimport { applyChaosAsync } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { render as renderInteractionsUsage } from \"./response/usage/codecs/interactions.js\";\nimport {\n planMisbehavior,\n recordMisbehaviorOutcome,\n type MisbehaviorPlan,\n type MisbehaviorSkip,\n} from \"./misbehavior.js\";\n\n// ─── Interactions request types ────────────────────────────────────────────\n\ninterface InteractionsContentBlock {\n type: string;\n text?: string;\n name?: string;\n call_id?: string;\n id?: string;\n arguments?: Record<string, unknown>;\n output?: unknown;\n result?: unknown;\n}\n\ninterface InteractionsTurn {\n role: string;\n content?: InteractionsContentBlock[];\n parts?: InteractionsContentBlock[];\n}\n\n/**\n * Top-level Step envelope accepted by the live Gemini Interactions API.\n * The SDK's TypeScript union does not include Step[], but the wire contract\n * does — clients following the live API send these at the top level of `input`.\n * Discriminated by `type`; no `role` field (distinguishes from Turn[]).\n */\ninterface InteractionsStep {\n type: string;\n content?: InteractionsContentBlock[];\n call_id?: string;\n id?: string;\n name?: string;\n result?: unknown;\n output?: unknown;\n is_error?: boolean;\n signature?: string;\n}\n\n/** Step types whose payload is a tool/agent result keyed by call_id. */\nconst STEP_RESULT_TYPES = new Set<string>([\n \"function_result\",\n \"code_execution_result\",\n \"url_context_result\",\n \"google_search_result\",\n \"google_maps_result\",\n \"mcp_server_tool_result\",\n \"file_search_result\",\n]);\n\n/** All recognized top-level Step types (used as the Step[] discriminator). */\nconst STEP_TYPES = new Set<string>([\"user_input\", \"model_output\", ...STEP_RESULT_TYPES]);\n\ninterface InteractionsFunctionTool {\n type: \"function\";\n name: string;\n description?: string;\n parameters?: object;\n}\n\ninterface InteractionsRequest {\n model?: string;\n input?: string | InteractionsTurn[] | InteractionsStep[] | InteractionsContentBlock[];\n system_instruction?: string;\n tools?: InteractionsFunctionTool[];\n generation_config?: {\n temperature?: number;\n max_output_tokens?: number;\n [key: string]: unknown;\n };\n stream?: boolean;\n previous_interaction_id?: string;\n [key: string]: unknown;\n}\n\n// Check unsupported numeric input and containers consumed by the selected variant.\n// Other values retain their existing normalization, including omitted input.\nfunction validateInteractionsInput(input: unknown): string | null {\n if (typeof input === \"number\") return \"input must not be a number\";\n if (!Array.isArray(input)) return null;\n const first = input[0];\n if (isJsonObject(first) && \"role\" in first) {\n for (const [index, turn] of input.entries()) {\n if (!isJsonObject(turn)) continue;\n const blocks = turn.content ?? turn.parts;\n // Empty strings, falsy values and empty arrays bypass the converter's\n // filters. Preserve that behavior and the content ?? parts selection.\n if (typeof blocks === \"string\" && blocks.length > 0) {\n const field = turn.content != null ? \"content\" : \"parts\";\n return `input[${index}].${field} must be an array`;\n }\n }\n } else if (\n !(isJsonObject(first) && typeof first.type === \"string\" && STEP_TYPES.has(first.type))\n ) {\n const nullIndex = input.findIndex((part) => part === null);\n if (nullIndex !== -1) return `input[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// Preserve tools that the converter ignores; reject only these unsafe reads.\nfunction validateInteractionsTools(tools: unknown): string | null {\n if (typeof tools === \"string\" && tools.length > 0) return \"tools must be an array\";\n if (Array.isArray(tools)) {\n const nullIndex = tools.findIndex((tool) => tool === null);\n if (nullIndex !== -1) return `tools[${nullIndex}] must be an object`;\n }\n return null;\n}\n\n// ─── Input conversion: Interactions → ChatCompletionRequest ───────────────\n\nexport function geminiInteractionsToCompletionRequest(\n req: InteractionsRequest,\n): ChatCompletionRequest {\n const messages: ChatMessage[] = [];\n const model = req.model ?? \"gemini-2.5-flash\";\n\n // system_instruction → system message\n if (req.system_instruction) {\n messages.push({ role: \"system\", content: req.system_instruction });\n }\n\n // Parse input\n if (req.input !== undefined) {\n if (typeof req.input === \"string\") {\n // Simple string input → single user message\n messages.push({ role: \"user\", content: req.input });\n } else if (Array.isArray(req.input)) {\n // Could be Turn[], Step[], or Content[]\n const firstItem = req.input[0] as\n | InteractionsTurn\n | InteractionsStep\n | InteractionsContentBlock\n | undefined;\n const isStepArray =\n !!firstItem &&\n !(\"role\" in firstItem) &&\n typeof firstItem.type === \"string\" &&\n STEP_TYPES.has(firstItem.type);\n\n if (firstItem && \"role\" in firstItem) {\n // Turn[] format\n for (const turn of req.input as InteractionsTurn[]) {\n const role = turn.role === \"model\" ? \"assistant\" : turn.role;\n const blocks = turn.content ?? turn.parts;\n if (!blocks || blocks.length === 0) {\n if (role === \"user\" || role === \"assistant\") {\n messages.push({ role: role as \"user\" | \"assistant\", content: \"\" });\n }\n continue;\n }\n\n // Check for function_call or function_result parts\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const funcResultParts = blocks.filter((p) => p.type === \"function_result\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n\n if (funcCallParts.length > 0) {\n // Assistant tool call message\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else if (funcResultParts.length > 0) {\n // Tool response messages\n for (const part of funcResultParts) {\n const resultValue = part.result ?? part.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: part.call_id ?? part.id ?? \"\",\n });\n }\n // Any text parts alongside → separate user message\n if (textParts.length > 0) {\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (text) {\n messages.push({ role: \"user\", content: text });\n }\n }\n } else {\n // Text-only turn\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n if (role === \"user\" || role === \"assistant\" || role === \"system\") {\n messages.push({\n role: role as \"user\" | \"assistant\" | \"system\",\n content: text,\n });\n }\n }\n }\n } else if (isStepArray) {\n // Step[] format — the wire contract Google's /v1beta/interactions accepts.\n for (const step of req.input as InteractionsStep[]) {\n if (step.type === \"user_input\") {\n const text = (step.content ?? [])\n .filter((p) => p.type === \"text\")\n .map((p) => p.text ?? \"\")\n .join(\"\");\n messages.push({ role: \"user\", content: text });\n } else if (step.type === \"model_output\") {\n const blocks = step.content ?? [];\n const funcCallParts = blocks.filter((p) => p.type === \"function_call\");\n const textParts = blocks.filter((p) => p.type === \"text\");\n const textContent = textParts.map((p) => p.text ?? \"\").join(\"\");\n\n if (funcCallParts.length > 0) {\n messages.push({\n role: \"assistant\",\n content: textContent || null,\n tool_calls: funcCallParts.map((p) => ({\n id: p.id ?? p.call_id ?? generateToolCallId(),\n type: \"function\" as const,\n function: {\n name: p.name ?? \"\",\n arguments: JSON.stringify(p.arguments ?? {}),\n },\n })),\n });\n } else {\n messages.push({ role: \"assistant\", content: textContent });\n }\n } else if (STEP_RESULT_TYPES.has(step.type)) {\n const resultValue = step.result ?? step.output;\n messages.push({\n role: \"tool\",\n content:\n typeof resultValue === \"string\" ? resultValue : JSON.stringify(resultValue ?? \"\"),\n tool_call_id: step.call_id ?? step.id ?? \"\",\n });\n }\n }\n } else {\n // Content[] format — single user message with content blocks\n const textParts = (req.input as InteractionsContentBlock[]).filter(\n (p) => p.type === \"text\",\n );\n const text = textParts.map((p) => p.text ?? \"\").join(\"\");\n messages.push({ role: \"user\", content: text || \"\" });\n }\n }\n }\n\n // Convert tools\n let tools: ToolDefinition[] | undefined;\n if (req.tools && req.tools.length > 0) {\n const funcTools = req.tools.filter((t) => t.type === \"function\");\n if (funcTools.length > 0) {\n tools = funcTools.map((t) => ({\n type: \"function\" as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters,\n },\n }));\n }\n }\n\n return {\n model,\n messages,\n stream: req.stream !== false, // default true\n temperature: req.generation_config?.temperature,\n max_tokens: req.generation_config?.max_output_tokens,\n tools,\n };\n}\n\n// ─── Interaction ID generation ────────────────────────────────────────────\n\nlet interactionCounter = 0;\n\nexport function resetInteractionCounter(): void {\n interactionCounter = 0;\n}\n\nfunction nextInteractionId(): string {\n return `aimock-int-${interactionCounter++}`;\n}\n\n// ─── Usage helpers ────────────────────────────────────────────────────────\n\nfunction interactionsUsage(overrides?: ResponseOverrides): {\n total_input_tokens: number;\n total_output_tokens: number;\n total_tokens: number;\n} {\n return renderInteractionsUsage(overrides);\n}\n\n// ─── Response building: fixture → Interactions format ─────────────────────\n\nexport function buildInteractionsTextResponse(\n content: string,\n model: string,\n interactionId: string,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: content,\n steps: [{ type: \"model_output\", content: [{ type: \"text\", text: content }] }],\n usage: interactionsUsage(overrides),\n };\n}\n\n// Build a single SDK 2.x function_call step from a fixture tool call,\n// serving malformed arguments as `{}` (or rejecting them under strictToolArguments).\nfunction buildFunctionCallStep(tc: ToolCall, logger: Logger): object {\n const args = servedToolArgs(tc, \"object\", logger);\n return {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: args.value,\n };\n}\n\nexport function buildInteractionsToolCallResponse(\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): object {\n return {\n id: interactionId,\n status: \"requires_action\",\n model: overrides?.model ?? model,\n role: \"model\",\n steps: toolCalls.map((tc) => buildFunctionCallStep(tc, logger)),\n usage: interactionsUsage(overrides),\n };\n}\n\nexport function buildInteractionsContentWithToolCallsResponse(\n content: string,\n toolCalls: ToolCall[],\n model: string,\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): object {\n const steps: object[] = [];\n // Collect output_text in step order so the top-level field mirrors the\n // concatenated text steps regardless of where they appear in `steps`.\n let outputText = \"\";\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: the non-stream `steps[]` array is index/step-addressed and\n // ordered, so emit one step per block in fixture ARRAY ORDER. A toolCall\n // block placed before a text block therefore yields a function_call step\n // ahead of the model_output step — tool-first, the opposite of the legacy\n // (text-step-always-first) shape below.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n for (const block of ordered) {\n if (block.type === \"text\") {\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: block.text }] });\n outputText += block.text;\n } else {\n steps.push(\n buildFunctionCallStep(\n { name: block.name, arguments: block.arguments, id: block.id },\n logger,\n ),\n );\n }\n }\n } else {\n // LEGACY PATH: a single text step first, then function_call steps —\n // unchanged from the pre-blocks behavior (text always leads `steps`).\n steps.push({ type: \"model_output\", content: [{ type: \"text\", text: content }] });\n outputText = content;\n for (const tc of toolCalls) {\n steps.push(buildFunctionCallStep(tc, logger));\n }\n }\n\n return {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n model: overrides?.model ?? model,\n role: \"model\",\n output_text: outputText,\n steps,\n usage: interactionsUsage(overrides),\n };\n}\n\n// ─── SSE event builders ──────────────────────────────────────────────────\n\ninterface InteractionsSSEEvent {\n event_type: string;\n [key: string]: unknown;\n}\n\nlet eventIdCounter = 0;\n\nexport function resetEventIdCounter(): void {\n eventIdCounter = 0;\n}\n\nfunction nextEventId(): string {\n return `evt_${++eventIdCounter}`;\n}\n\nexport function buildInteractionsTextSSEEvents(\n content: string,\n interactionId: string,\n chunkSize: number,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // step.start — text step is the model_output\n events.push({\n event_type: \"step.start\",\n index: 0,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n // step.delta(s) — inner delta shape is unchanged ({ type: \"text\", text })\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index: 0,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n // step.stop\n events.push({\n event_type: \"step.stop\",\n index: 0,\n event_id: nextEventId(),\n });\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\nexport function buildInteractionsToolCallSSEEvents(\n toolCalls: ToolCall[],\n interactionId: string,\n logger: Logger,\n overrides?: ResponseOverrides,\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n // Each tool call gets its own step.start/delta/stop bracket. In SDK 2.x the\n // call identity (id, name) lives on step.start; the arguments stream as a\n // dedicated `arguments_delta` carrying a JSON-string fragment, and step.start\n // carries an empty `arguments: {}` placeholder.\n for (let idx = 0; idx < toolCalls.length; idx++) {\n const tc = toolCalls[idx];\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index: idx,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n // arguments_delta.arguments is a string fragment. The real SDK may split\n // the args across several fragments that concatenate into valid JSON; the\n // mock emits the whole serialized object as one fragment (a valid\n // degenerate case the collapser handles identically).\n events.push({\n event_type: \"step.delta\",\n index: idx,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index: idx,\n event_id: nextEventId(),\n });\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"requires_action\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// Emit the step.start/delta(s)/stop bracket for a text (model_output) step at\n// a given step `index`. Inner delta shape ({ type: \"text\", text }) and the\n// empty-content single-empty-delta behavior are unchanged from the legacy path.\nfunction pushTextStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n content: string,\n chunkSize: number,\n): void {\n events.push({\n event_type: \"step.start\",\n index,\n step: { type: \"model_output\" },\n event_id: nextEventId(),\n });\n\n if (content.length === 0) {\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: \"\" },\n event_id: nextEventId(),\n });\n } else {\n for (let i = 0; i < content.length; i += chunkSize) {\n const slice = content.slice(i, i + chunkSize);\n events.push({\n event_type: \"step.delta\",\n index,\n delta: { type: \"text\", text: slice },\n event_id: nextEventId(),\n });\n }\n }\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\n// Emit the step.start/arguments_delta/stop bracket for a function_call step at\n// a given step `index`. Identity (id, name) lives on step.start with an empty\n// `arguments: {}` placeholder; the arguments stream as a single\n// `arguments_delta` JSON-string fragment — unchanged from the legacy path.\nfunction pushFunctionCallStepEvents(\n events: InteractionsSSEEvent[],\n index: number,\n tc: ToolCall,\n logger: Logger,\n): void {\n const args = servedToolArgs(tc, \"string\", logger);\n\n events.push({\n event_type: \"step.start\",\n index,\n step: {\n type: \"function_call\",\n id: tc.id || generateToolCallId(),\n name: tc.name,\n arguments: {},\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.delta\",\n index,\n delta: {\n type: \"arguments_delta\",\n arguments: args.text,\n },\n event_id: nextEventId(),\n });\n\n events.push({\n event_type: \"step.stop\",\n index,\n event_id: nextEventId(),\n });\n}\n\nexport function buildInteractionsContentWithToolCallsSSEEvents(\n content: string,\n toolCalls: ToolCall[],\n interactionId: string,\n chunkSize: number,\n logger: Logger,\n overrides?: ResponseOverrides,\n blocks?: FixtureBlock[],\n): InteractionsSSEEvent[] {\n const events: InteractionsSSEEvent[] = [];\n\n // interaction.created\n events.push({\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n });\n\n let hasToolCalls = true;\n if (blocks && blocks.length > 0) {\n // NEW PATH: stream one step per block in fixture ARRAY ORDER. The step\n // `index` increments with array position, so a toolCall block before a text\n // block yields a function_call step at a LOWER index than the model_output\n // step — tool-first, the opposite of the legacy (text-at-index-0) shape.\n const outcome = resolveFixtureBlockOutcome(blocks);\n const ordered = outcome.ordered;\n hasToolCalls = outcome.hasToolCalls;\n let idx = 0;\n for (const block of ordered) {\n if (block.type === \"text\") {\n pushTextStepEvents(events, idx, block.text, chunkSize);\n } else {\n pushFunctionCallStepEvents(\n events,\n idx,\n {\n name: block.name,\n arguments: block.arguments,\n id: block.id,\n },\n logger,\n );\n }\n idx += 1;\n }\n } else {\n // LEGACY PATH: text content at index 0 (model_output step), tool calls at\n // index 1+ — byte-for-byte unchanged from the pre-blocks behavior.\n pushTextStepEvents(events, 0, content, chunkSize);\n for (let i = 0; i < toolCalls.length; i++) {\n pushFunctionCallStepEvents(events, i + 1, toolCalls[i], logger);\n }\n }\n\n // interaction.completed\n events.push({\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: hasToolCalls ? \"requires_action\" : \"completed\",\n usage: interactionsUsage(overrides),\n },\n event_id: nextEventId(),\n });\n\n return events;\n}\n\n// ─── SSE writer for Interactions streaming ────────────────────────────────\n\n// Data-only SSE (no event: prefix, no [DONE]). Only step deltas count for the\n// chunk index and truncateAfterChunks: framing events (interaction.created,\n// step.start, step.stop, interaction.completed) pay the delay at the current\n// index but do not consume chunk budget or trigger the chunk-sent callback.\nconst interactionsCodec: FrameCodec<InteractionsSSEEvent> = {\n ...sseCodec<InteractionsSSEEvent>({ done: false }),\n counts: (event) => event.event_type === \"step.delta\",\n};\n\ninterface InteractionsStreamOptions {\n latency?: number;\n streamingProfile?: StreamingProfile;\n recordedTimings?: RecordedTimings;\n replaySpeed?: number;\n signal?: AbortSignal;\n onChunkSent?: () => void;\n}\n\nexport async function writeGeminiInteractionsSSEStream(\n res: http.ServerResponse,\n events: InteractionsSSEEvent[],\n optionsOrLatency?: number | InteractionsStreamOptions,\n): Promise<boolean> {\n const opts: InteractionsStreamOptions =\n typeof optionsOrLatency === \"number\" ? { latency: optionsOrLatency } : (optionsOrLatency ?? {});\n // No lifecycle: a caller gets close-awareness only through its own `signal`.\n // With no events an aborted signal does not stop the writer (it still ends\n // the response and returns true), so the loop gets no signal then.\n return runFrameLoop(\n httpSink(res),\n events,\n interactionsCodec,\n {\n latency: opts.latency,\n streamingProfile: opts.streamingProfile,\n recordedTimings: opts.recordedTimings,\n replaySpeed: opts.replaySpeed,\n },\n { signal: events.length > 0 ? opts.signal : undefined, onChunkSent: opts.onChunkSent },\n );\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nfunction prepareInteractionsMisbehavior(\n plan: MisbehaviorPlan,\n streaming: boolean,\n): MisbehaviorPlan {\n if (!streaming && plan.summary.fault === \"stop-length-mid-tool\") {\n // The object wire cannot represent the selected partial argument string.\n // Earlier completed calls/text survive; omit the cut call before allocating\n // IDs and preparing servedToolCalls/usage.\n const response = plan.response;\n const combined = isCombinedFixtureResponse(response);\n const outcome =\n combined && response.blocks?.length ? resolveFixtureBlockOutcome(response.blocks) : undefined;\n const calls =\n outcome?.toolCalls ??\n (combined || isToolCallResponse(response) ? (response.toolCalls ?? []) : []);\n const toolCalls = calls.filter((_, index) => index !== plan.target?.index);\n let callIndex = 0;\n const blocks = outcome?.ordered.filter(\n (block) => block.type === \"text\" || callIndex++ !== plan.target?.index,\n );\n plan = { ...plan, response: { ...response, toolCalls, ...(blocks ? { blocks } : {}) } };\n }\n // Both modes always emit IDs. The shared preparation preserves effective\n // block order and allocates each wire/journal identity exactly once.\n return prepareOpenAIChatMisbehavior(plan);\n}\n\nfunction finalizeInteractionsMisbehavior(\n events: InteractionsSSEEvent[],\n plan: MisbehaviorPlan | MisbehaviorSkip,\n): InteractionsSSEEvent[] {\n if (plan.kind !== \"applied\" || plan.summary.fault !== \"stop-length-mid-tool\") return events;\n // Approved modeled K5 contract: keep the cut arguments and step.stop, then\n // terminate incomplete. Native truncated-tool closure remains unverified.\n return events.map((event) =>\n event.event_type === \"interaction.completed\" && isJsonObject(event.interaction)\n ? { ...event, interaction: { ...event.interaction, status: \"incomplete\" } }\n : event,\n );\n}\n\nexport async function handleGeminiInteractions(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n const urlPath = req.url ?? \"/v1beta/interactions\";\n\n let interactionsReq: InteractionsRequest;\n try {\n interactionsReq = JSON.parse(raw) as InteractionsRequest;\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: `Malformed JSON body: ${detail}`,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object (e.g. `null`) before\n // touching fields — otherwise `interactionsReq.stream` throws a TypeError that\n // surfaces as a 500 instead of a 400.\n if (!isJsonObject(interactionsReq)) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: \"Request body must be a JSON object\",\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n const validationError =\n validateInteractionsInput(interactionsReq.input) ??\n validateInteractionsTools(interactionsReq.tools);\n if (validationError) {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n renderError(\"interactions\", \"validation\", {\n message: validationError,\n code: \"INVALID_ARGUMENT\",\n }).body,\n );\n return;\n }\n\n // Convert to ChatCompletionRequest for fixture matching\n const completionReq = geminiInteractionsToCompletionRequest(interactionsReq);\n // Keep \"chat\" rather than \"gemini-interactions\" — the router's endpoint\n // compatibility filter (router.ts) treats \"chat\" as a pass-through that\n // matches any unendpointed fixture. Switching to \"gemini-interactions\"\n // would make the request fall into the multimedia guard branch, preventing\n // generic chat fixtures from matching and breaking existing users. The\n // recorder would also start emitting `endpoint: \"gemini-interactions\"` in\n // recorded fixtures, creating a one-way compatibility break.\n completionReq._endpointType = \"chat\";\n completionReq._context = getContext(req);\n\n const streaming = interactionsReq.stream !== false; // default true\n const model = completionReq.model;\n\n const testId = getTestId(req);\n const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n fixtures,\n completionReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n { wire: \"gemini-interactions\" },\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n {\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n },\n fixture ? \"fixture\" : \"proxy\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n const decisions = resolveRequestDecisions(defaults.strict, req.headers);\n if (decisions.fixtureMiss) {\n const strictStatus = 503;\n const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n logger.error(strictNoMatchLogLine(req.method ?? \"POST\", urlPath, skippedBySequenceOrTurn));\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: strictStatus,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n strictStatus,\n renderError(\"interactions\", \"strict-miss\", {\n message: strictMessage,\n code: \"UNAVAILABLE\",\n }).body,\n );\n return;\n }\n if (decisions.upstream && defaults.record) {\n const { outcome, clientHungUp } = await withHangUpDetection(req, res, () =>\n proxyAndRecord(\n req,\n res,\n completionReq,\n \"gemini-interactions\",\n urlPath,\n fixtures,\n defaults,\n raw,\n ),\n );\n if (outcome === \"handled_by_hook\") return;\n if (outcome !== \"not_configured\") {\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: res.statusCode ?? 200,\n fixture: null,\n source: \"proxy\",\n ...proxyInterruption(res, clientHungUp),\n },\n });\n return;\n }\n }\n journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: {\n status: 404,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 404,\n renderError(\"interactions\", \"fixture-miss\", {\n message: \"No fixture matched\",\n code: \"NOT_FOUND\",\n }).body,\n );\n return;\n }\n\n let response = await resolveResponse(fixture, completionReq);\n let misbehavior = planMisbehavior({\n wire: \"gemini-interactions\",\n fixture,\n response,\n request: completionReq,\n stream: streaming,\n emitsToolCallIds: true,\n defaults,\n rawHeaders: req.headers,\n url: req.url,\n });\n if (misbehavior.kind === \"error\") {\n const entry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: misbehavior.status, fixture },\n });\n recordMisbehaviorOutcome({ entry, summary: misbehavior.summary, defaults, testId });\n if (!misbehavior.summary?.evaluations.some((evaluation) => evaluation.outcome === \"error\")) {\n logger.error(`${req.method ?? \"POST\"} ${urlPath}: ${misbehavior.message}`);\n }\n writeErrorResponse(\n res,\n misbehavior.status,\n renderError(\"interactions\", \"misbehavior\", {\n message: misbehavior.message,\n code: misbehavior.code,\n }).body,\n );\n return;\n }\n if (misbehavior.kind === \"applied\") {\n misbehavior = prepareInteractionsMisbehavior(misbehavior, streaming);\n response = misbehavior.response;\n }\n const appliedUsage =\n misbehavior.kind === \"applied\"\n ? resolveOpenAIChatMisbehaviorUsage(misbehavior, completionReq)\n : undefined;\n const latency = fixture.latency ?? defaults.latency;\n const chunkSize = Math.max(1, fixture.chunkSize ?? defaults.chunkSize);\n const replaySpeed = fixture.replaySpeed ?? defaults.replaySpeed;\n const streamTiming = {\n latency,\n streamingProfile: fixture.streamingProfile,\n recordedTimings: fixture.recordedTimings,\n replaySpeed,\n };\n // Non-streaming replies size their (opt-in) wait from the served payload.\n // An applied plan is merged into `response`, so every JSON site below is a\n // misbehavior path when one applied. Interactions serves no fixture\n // reasoning, so there is no effective reasoning to count.\n const applied = misbehavior.kind === \"applied\" ? misbehavior : undefined;\n const jsonOpts = () => ({\n surface: \"other\" as const,\n payloadLength: applied\n ? misbehaviorPayloadLength(applied, undefined)\n : servedPayloadLength(response),\n chunkSize,\n recordedTimings: fixture.recordedTimings,\n duplicateId: !!applied?.duplicateId,\n replaySpeed,\n });\n const markIncomplete = <T extends object>(body: T): T | (T & { status: \"incomplete\" }) =>\n applied?.summary.fault === \"stop-length-mid-tool\" ? { ...body, status: \"incomplete\" } : body;\n\n // Raw passthrough (#549): the authored events or JSON, framed for this wire.\n // `streaming` is the handler's own rule: a stream unless `stream` is false.\n if (isRawResponse(response)) {\n await serveRawResponse({\n wire: \"gemini-interactions\",\n stream: streaming,\n req,\n res,\n fixture,\n response,\n body: completionReq,\n journal,\n defaults,\n testId,\n misbehaviorSummary: misbehavior.summary,\n errorWire: \"interactions\",\n });\n return;\n }\n\n // Error response\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n writeErrorResponse(\n res,\n status,\n renderError(\"interactions\", \"fixture\", {\n message: response.error.message,\n code: response.error.type ?? \"ERROR\",\n }).body,\n { retryAfter: response.retryAfter },\n );\n return;\n }\n\n // The content+tool and tool-call branches draw their interaction id after\n // requireEmittedFunctionToolCalls, so a fixture it rejects (a custom tool\n // call or a malformed toolCalls entry) consumes no id. A rejection raised\n // later, inside a builder, comes after the draw and does consume an id:\n // invalid JSON arguments on the non-streaming path, or a malformed block\n // from resolveFixtureBlockOutcome (the streaming path has also consumed\n // event ids by then).\n\n if (misbehavior.kind === \"applied\" && misbehavior.summary.fault === \"reasoning-only\") {\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n // Reasoning-only output carries no tool call, so it has no tool-call check\n // to pass before drawing its id.\n const interactionId = nextInteractionId();\n const usage = renderInteractionsUsage({ usage: appliedUsage }, \"applied-reasoning-only\");\n if (!streaming) {\n const overrides =\n isTextResponse(response) ||\n isToolCallResponse(response) ||\n isCombinedFixtureResponse(response)\n ? extractOverrides(response)\n : undefined;\n const reasoning = misbehavior.reasoning ?? \"\";\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () => ({\n id: interactionId,\n model: overrides?.model ?? model,\n status: \"incomplete\",\n steps: [\n {\n type: \"thought\",\n signature: \"\",\n summary: [{ type: \"text\", text: reasoning }],\n },\n ],\n usage,\n }),\n jsonOpts(),\n );\n return;\n }\n const events: InteractionsSSEEvent[] = [\n {\n event_type: \"interaction.created\",\n interaction: { id: interactionId, status: \"in_progress\" },\n event_id: nextEventId(),\n },\n { event_type: \"step.start\", index: 0, step: { type: \"thought\" }, event_id: nextEventId() },\n {\n event_type: \"step.delta\",\n index: 0,\n delta: {\n type: \"thought_summary\",\n content: { type: \"text\", text: misbehavior.reasoning ?? \"\" },\n },\n event_id: nextEventId(),\n },\n { event_type: \"step.stop\", index: 0, event_id: nextEventId() },\n {\n event_type: \"interaction.completed\",\n interaction: {\n id: interactionId,\n status: \"incomplete\",\n usage,\n },\n event_id: nextEventId(),\n },\n ];\n await deliverStream(res, journalEntry, fixture, interactionsCodec, events, streamTiming);\n return;\n }\n\n // Content + tool calls response\n if (isCombinedFixtureResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n const content = response.content ?? \"\";\n const blocks = response.blocks;\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n markIncomplete(\n buildInteractionsContentWithToolCallsResponse(\n content,\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n blocks,\n ),\n ),\n jsonOpts(),\n );\n } else {\n const events = buildInteractionsContentWithToolCallsSSEEvents(\n response.content ?? \"\",\n functionToolCalls,\n interactionId,\n chunkSize,\n logger,\n overrides,\n response.blocks,\n );\n await deliverStream(\n res,\n journalEntry,\n fixture,\n interactionsCodec,\n finalizeInteractionsMisbehavior(events, misbehavior),\n streamTiming,\n );\n }\n return;\n }\n\n // Text response\n if (isTextResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const interactionId = nextInteractionId();\n if (!streaming) {\n const content = response.content;\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n markIncomplete(buildInteractionsTextResponse(content, model, interactionId, overrides)),\n jsonOpts(),\n );\n } else {\n const events = buildInteractionsTextSSEEvents(\n response.content,\n interactionId,\n chunkSize,\n overrides,\n );\n await deliverStream(\n res,\n journalEntry,\n fixture,\n interactionsCodec,\n finalizeInteractionsMisbehavior(events, misbehavior),\n streamTiming,\n );\n }\n return;\n }\n\n // Tool call response\n if (isToolCallResponse(response)) {\n if (response.webSearches?.length) {\n logger.warn(\n \"webSearches in fixture response are not supported for Gemini Interactions API — ignoring\",\n );\n }\n const overrides = {\n ...extractOverrides(response),\n ...(appliedUsage ? { usage: appliedUsage } : {}),\n };\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 200, fixture },\n });\n recordMisbehaviorOutcome({\n entry: journalEntry,\n summary: misbehavior.summary,\n defaults,\n testId,\n });\n const functionToolCalls = requireEmittedFunctionToolCalls(response, \"Gemini Interactions\");\n const interactionId = nextInteractionId();\n if (!streaming) {\n await deliverJson(\n res,\n journalEntry,\n fixture,\n () =>\n markIncomplete(\n buildInteractionsToolCallResponse(\n functionToolCalls,\n model,\n interactionId,\n logger,\n overrides,\n ),\n ),\n jsonOpts(),\n );\n } else {\n const events = buildInteractionsToolCallSSEEvents(\n functionToolCalls,\n interactionId,\n logger,\n overrides,\n );\n await deliverStream(\n res,\n journalEntry,\n fixture,\n interactionsCodec,\n finalizeInteractionsMisbehavior(events, misbehavior),\n streamTiming,\n );\n }\n return;\n }\n\n // Unknown response type\n const journalEntry = journal.add({\n method: req.method ?? \"POST\",\n path: urlPath,\n headers: flattenHeaders(req.headers),\n body: completionReq,\n response: { status: 500, fixture },\n });\n recordMisbehaviorOutcome({ entry: journalEntry, summary: misbehavior.summary, defaults, testId });\n writeErrorResponse(\n res,\n 500,\n renderError(\"interactions\", \"unknown-response\", {\n message: \"Fixture response did not match any known type\",\n code: \"INTERNAL\",\n }).body,\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAsGA,MAAM,oBAAoB,IAAI,IAAY;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,MAAM,aAAa,IAAI,IAAY;CAAC;CAAc;CAAgB,GAAG;CAAkB,CAAC;AA0BxF,SAAS,0BAA0B,OAA+B;AAChE,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,CAAC,MAAM,QAAQ,MAAM,CAAE,QAAO;CAClC,MAAM,QAAQ,MAAM;AACpB,KAAI,aAAa,MAAM,IAAI,UAAU,MACnC,MAAK,MAAM,CAAC,OAAO,SAAS,MAAM,SAAS,EAAE;AAC3C,MAAI,CAAC,aAAa,KAAK,CAAE;EACzB,MAAM,SAAS,KAAK,WAAW,KAAK;AAGpC,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,EAEhD,QAAO,SAAS,MAAM,IADR,KAAK,WAAW,OAAO,YAAY,QACjB;;UAIpC,EAAE,aAAa,MAAM,IAAI,OAAO,MAAM,SAAS,YAAY,WAAW,IAAI,MAAM,KAAK,GACrF;EACA,MAAM,YAAY,MAAM,WAAW,SAAS,SAAS,KAAK;AAC1D,MAAI,cAAc,GAAI,QAAO,SAAS,UAAU;;AAElD,QAAO;;AAIT,SAAS,0BAA0B,OAA+B;AAChE,KAAI,OAAO,UAAU,YAAY,MAAM,SAAS,EAAG,QAAO;AAC1D,KAAI,MAAM,QAAQ,MAAM,EAAE;EACxB,MAAM,YAAY,MAAM,WAAW,SAAS,SAAS,KAAK;AAC1D,MAAI,cAAc,GAAI,QAAO,SAAS,UAAU;;AAElD,QAAO;;AAKT,SAAgB,sCACd,KACuB;CACvB,MAAM,WAA0B,EAAE;CAClC,MAAM,QAAQ,IAAI,SAAS;AAG3B,KAAI,IAAI,mBACN,UAAS,KAAK;EAAE,MAAM;EAAU,SAAS,IAAI;EAAoB,CAAC;AAIpE,KAAI,IAAI,UAAU,QAChB;MAAI,OAAO,IAAI,UAAU,SAEvB,UAAS,KAAK;GAAE,MAAM;GAAQ,SAAS,IAAI;GAAO,CAAC;WAC1C,MAAM,QAAQ,IAAI,MAAM,EAAE;GAEnC,MAAM,YAAY,IAAI,MAAM;GAK5B,MAAM,cACJ,CAAC,CAAC,aACF,EAAE,UAAU,cACZ,OAAO,UAAU,SAAS,YAC1B,WAAW,IAAI,UAAU,KAAK;AAEhC,OAAI,aAAa,UAAU,UAEzB,MAAK,MAAM,QAAQ,IAAI,OAA6B;IAClD,MAAM,OAAO,KAAK,SAAS,UAAU,cAAc,KAAK;IACxD,MAAM,SAAS,KAAK,WAAW,KAAK;AACpC,QAAI,CAAC,UAAU,OAAO,WAAW,GAAG;AAClC,SAAI,SAAS,UAAU,SAAS,YAC9B,UAAS,KAAK;MAAQ;MAA8B,SAAS;MAAI,CAAC;AAEpE;;IAIF,MAAM,gBAAgB,OAAO,QAAQ,MAAM,EAAE,SAAS,gBAAgB;IACtE,MAAM,kBAAkB,OAAO,QAAQ,MAAM,EAAE,SAAS,kBAAkB;IAC1E,MAAM,YAAY,OAAO,QAAQ,MAAM,EAAE,SAAS,OAAO;AAEzD,QAAI,cAAc,SAAS,GAAG;KAE5B,MAAM,cAAc,UAAU,KAAK,MAAM,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG;AAC/D,cAAS,KAAK;MACZ,MAAM;MACN,SAAS,eAAe;MACxB,YAAY,cAAc,KAAK,OAAO;OACpC,IAAI,EAAE,MAAM,EAAE,WAAW,oBAAoB;OAC7C,MAAM;OACN,UAAU;QACR,MAAM,EAAE,QAAQ;QAChB,WAAW,KAAK,UAAU,EAAE,aAAa,EAAE,CAAC;QAC7C;OACF,EAAE;MACJ,CAAC;eACO,gBAAgB,SAAS,GAAG;AAErC,UAAK,MAAM,QAAQ,iBAAiB;MAClC,MAAM,cAAc,KAAK,UAAU,KAAK;AACxC,eAAS,KAAK;OACZ,MAAM;OACN,SACE,OAAO,gBAAgB,WAAW,cAAc,KAAK,UAAU,eAAe,GAAG;OACnF,cAAc,KAAK,WAAW,KAAK,MAAM;OAC1C,CAAC;;AAGJ,SAAI,UAAU,SAAS,GAAG;MACxB,MAAM,OAAO,UAAU,KAAK,MAAM,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG;AACxD,UAAI,KACF,UAAS,KAAK;OAAE,MAAM;OAAQ,SAAS;OAAM,CAAC;;WAG7C;KAEL,MAAM,OAAO,UAAU,KAAK,MAAM,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG;AACxD,SAAI,SAAS,UAAU,SAAS,eAAe,SAAS,SACtD,UAAS,KAAK;MACN;MACN,SAAS;MACV,CAAC;;;YAIC,aAET;SAAK,MAAM,QAAQ,IAAI,MACrB,KAAI,KAAK,SAAS,cAAc;KAC9B,MAAM,QAAQ,KAAK,WAAW,EAAE,EAC7B,QAAQ,MAAM,EAAE,SAAS,OAAO,CAChC,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG;AACX,cAAS,KAAK;MAAE,MAAM;MAAQ,SAAS;MAAM,CAAC;eACrC,KAAK,SAAS,gBAAgB;KACvC,MAAM,SAAS,KAAK,WAAW,EAAE;KACjC,MAAM,gBAAgB,OAAO,QAAQ,MAAM,EAAE,SAAS,gBAAgB;KAEtE,MAAM,cADY,OAAO,QAAQ,MAAM,EAAE,SAAS,OAAO,CAC3B,KAAK,MAAM,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG;AAE/D,SAAI,cAAc,SAAS,EACzB,UAAS,KAAK;MACZ,MAAM;MACN,SAAS,eAAe;MACxB,YAAY,cAAc,KAAK,OAAO;OACpC,IAAI,EAAE,MAAM,EAAE,WAAW,oBAAoB;OAC7C,MAAM;OACN,UAAU;QACR,MAAM,EAAE,QAAQ;QAChB,WAAW,KAAK,UAAU,EAAE,aAAa,EAAE,CAAC;QAC7C;OACF,EAAE;MACJ,CAAC;SAEF,UAAS,KAAK;MAAE,MAAM;MAAa,SAAS;MAAa,CAAC;eAEnD,kBAAkB,IAAI,KAAK,KAAK,EAAE;KAC3C,MAAM,cAAc,KAAK,UAAU,KAAK;AACxC,cAAS,KAAK;MACZ,MAAM;MACN,SACE,OAAO,gBAAgB,WAAW,cAAc,KAAK,UAAU,eAAe,GAAG;MACnF,cAAc,KAAK,WAAW,KAAK,MAAM;MAC1C,CAAC;;UAGD;IAKL,MAAM,OAHa,IAAI,MAAqC,QACzD,MAAM,EAAE,SAAS,OACnB,CACsB,KAAK,MAAM,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG;AACxD,aAAS,KAAK;KAAE,MAAM;KAAQ,SAAS,QAAQ;KAAI,CAAC;;;;CAM1D,IAAI;AACJ,KAAI,IAAI,SAAS,IAAI,MAAM,SAAS,GAAG;EACrC,MAAM,YAAY,IAAI,MAAM,QAAQ,MAAM,EAAE,SAAS,WAAW;AAChE,MAAI,UAAU,SAAS,EACrB,SAAQ,UAAU,KAAK,OAAO;GAC5B,MAAM;GACN,UAAU;IACR,MAAM,EAAE;IACR,aAAa,EAAE;IACf,YAAY,EAAE;IACf;GACF,EAAE;;AAIP,QAAO;EACL;EACA;EACA,QAAQ,IAAI,WAAW;EACvB,aAAa,IAAI,mBAAmB;EACpC,YAAY,IAAI,mBAAmB;EACnC;EACD;;AAKH,IAAI,qBAAqB;AAEzB,SAAgB,0BAAgC;AAC9C,sBAAqB;;AAGvB,SAAS,oBAA4B;AACnC,QAAO,cAAc;;AAKvB,SAAS,kBAAkB,WAIzB;AACA,QAAOA,OAAwB,UAAU;;AAK3C,SAAgB,8BACd,SACA,OACA,eACA,WACQ;AACR,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,OAAO,WAAW,SAAS;EAC3B,MAAM;EACN,aAAa;EACb,OAAO,CAAC;GAAE,MAAM;GAAgB,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAS,CAAC;GAAE,CAAC;EAC7E,OAAO,kBAAkB,UAAU;EACpC;;AAKH,SAAS,sBAAsB,IAAc,QAAwB;CACnE,MAAM,OAAO,eAAe,IAAI,UAAU,OAAO;AACjD,QAAO;EACL,MAAM;EACN,IAAI,GAAG,MAAM,oBAAoB;EACjC,MAAM,GAAG;EACT,WAAW,KAAK;EACjB;;AAGH,SAAgB,kCACd,WACA,OACA,eACA,QACA,WACQ;AACR,QAAO;EACL,IAAI;EACJ,QAAQ;EACR,OAAO,WAAW,SAAS;EAC3B,MAAM;EACN,OAAO,UAAU,KAAK,OAAO,sBAAsB,IAAI,OAAO,CAAC;EAC/D,OAAO,kBAAkB,UAAU;EACpC;;AAGH,SAAgB,8CACd,SACA,WACA,OACA,eACA,QACA,WACA,QACQ;CACR,MAAM,QAAkB,EAAE;CAG1B,IAAI,aAAa;CAEjB,IAAI,eAAe;AACnB,KAAI,UAAU,OAAO,SAAS,GAAG;EAM/B,MAAM,UAAU,2BAA2B,OAAO;EAClD,MAAM,UAAU,QAAQ;AACxB,iBAAe,QAAQ;AACvB,OAAK,MAAM,SAAS,QAClB,KAAI,MAAM,SAAS,QAAQ;AACzB,SAAM,KAAK;IAAE,MAAM;IAAgB,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM,MAAM;KAAM,CAAC;IAAE,CAAC;AACnF,iBAAc,MAAM;QAEpB,OAAM,KACJ,sBACE;GAAE,MAAM,MAAM;GAAM,WAAW,MAAM;GAAW,IAAI,MAAM;GAAI,EAC9D,OACD,CACF;QAGA;AAGL,QAAM,KAAK;GAAE,MAAM;GAAgB,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAS,CAAC;GAAE,CAAC;AAChF,eAAa;AACb,OAAK,MAAM,MAAM,UACf,OAAM,KAAK,sBAAsB,IAAI,OAAO,CAAC;;AAIjD,QAAO;EACL,IAAI;EACJ,QAAQ,eAAe,oBAAoB;EAC3C,OAAO,WAAW,SAAS;EAC3B,MAAM;EACN,aAAa;EACb;EACA,OAAO,kBAAkB,UAAU;EACpC;;AAUH,IAAI,iBAAiB;AAErB,SAAgB,sBAA4B;AAC1C,kBAAiB;;AAGnB,SAAS,cAAsB;AAC7B,QAAO,OAAO,EAAE;;AAGlB,SAAgB,+BACd,SACA,eACA,WACA,WACwB;CACxB,MAAM,SAAiC,EAAE;AAGzC,QAAO,KAAK;EACV,YAAY;EACZ,aAAa;GAAE,IAAI;GAAe,QAAQ;GAAe;EACzD,UAAU,aAAa;EACxB,CAAC;AAGF,QAAO,KAAK;EACV,YAAY;EACZ,OAAO;EACP,MAAM,EAAE,MAAM,gBAAgB;EAC9B,UAAU,aAAa;EACxB,CAAC;AAGF,KAAI,QAAQ,WAAW,EACrB,QAAO,KAAK;EACV,YAAY;EACZ,OAAO;EACP,OAAO;GAAE,MAAM;GAAQ,MAAM;GAAI;EACjC,UAAU,aAAa;EACxB,CAAC;KAEF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;EAClD,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,UAAU;AAC7C,SAAO,KAAK;GACV,YAAY;GACZ,OAAO;GACP,OAAO;IAAE,MAAM;IAAQ,MAAM;IAAO;GACpC,UAAU,aAAa;GACxB,CAAC;;AAKN,QAAO,KAAK;EACV,YAAY;EACZ,OAAO;EACP,UAAU,aAAa;EACxB,CAAC;AAGF,QAAO,KAAK;EACV,YAAY;EACZ,aAAa;GACX,IAAI;GACJ,QAAQ;GACR,OAAO,kBAAkB,UAAU;GACpC;EACD,UAAU,aAAa;EACxB,CAAC;AAEF,QAAO;;AAGT,SAAgB,mCACd,WACA,eACA,QACA,WACwB;CACxB,MAAM,SAAiC,EAAE;AAGzC,QAAO,KAAK;EACV,YAAY;EACZ,aAAa;GAAE,IAAI;GAAe,QAAQ;GAAe;EACzD,UAAU,aAAa;EACxB,CAAC;AAMF,MAAK,IAAI,MAAM,GAAG,MAAM,UAAU,QAAQ,OAAO;EAC/C,MAAM,KAAK,UAAU;EACrB,MAAM,OAAO,eAAe,IAAI,UAAU,OAAO;AAEjD,SAAO,KAAK;GACV,YAAY;GACZ,OAAO;GACP,MAAM;IACJ,MAAM;IACN,IAAI,GAAG,MAAM,oBAAoB;IACjC,MAAM,GAAG;IACT,WAAW,EAAE;IACd;GACD,UAAU,aAAa;GACxB,CAAC;AAMF,SAAO,KAAK;GACV,YAAY;GACZ,OAAO;GACP,OAAO;IACL,MAAM;IACN,WAAW,KAAK;IACjB;GACD,UAAU,aAAa;GACxB,CAAC;AAEF,SAAO,KAAK;GACV,YAAY;GACZ,OAAO;GACP,UAAU,aAAa;GACxB,CAAC;;AAIJ,QAAO,KAAK;EACV,YAAY;EACZ,aAAa;GACX,IAAI;GACJ,QAAQ;GACR,OAAO,kBAAkB,UAAU;GACpC;EACD,UAAU,aAAa;EACxB,CAAC;AAEF,QAAO;;AAMT,SAAS,mBACP,QACA,OACA,SACA,WACM;AACN,QAAO,KAAK;EACV,YAAY;EACZ;EACA,MAAM,EAAE,MAAM,gBAAgB;EAC9B,UAAU,aAAa;EACxB,CAAC;AAEF,KAAI,QAAQ,WAAW,EACrB,QAAO,KAAK;EACV,YAAY;EACZ;EACA,OAAO;GAAE,MAAM;GAAQ,MAAM;GAAI;EACjC,UAAU,aAAa;EACxB,CAAC;KAEF,MAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,WAAW;EAClD,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,UAAU;AAC7C,SAAO,KAAK;GACV,YAAY;GACZ;GACA,OAAO;IAAE,MAAM;IAAQ,MAAM;IAAO;GACpC,UAAU,aAAa;GACxB,CAAC;;AAIN,QAAO,KAAK;EACV,YAAY;EACZ;EACA,UAAU,aAAa;EACxB,CAAC;;AAOJ,SAAS,2BACP,QACA,OACA,IACA,QACM;CACN,MAAM,OAAO,eAAe,IAAI,UAAU,OAAO;AAEjD,QAAO,KAAK;EACV,YAAY;EACZ;EACA,MAAM;GACJ,MAAM;GACN,IAAI,GAAG,MAAM,oBAAoB;GACjC,MAAM,GAAG;GACT,WAAW,EAAE;GACd;EACD,UAAU,aAAa;EACxB,CAAC;AAEF,QAAO,KAAK;EACV,YAAY;EACZ;EACA,OAAO;GACL,MAAM;GACN,WAAW,KAAK;GACjB;EACD,UAAU,aAAa;EACxB,CAAC;AAEF,QAAO,KAAK;EACV,YAAY;EACZ;EACA,UAAU,aAAa;EACxB,CAAC;;AAGJ,SAAgB,+CACd,SACA,WACA,eACA,WACA,QACA,WACA,QACwB;CACxB,MAAM,SAAiC,EAAE;AAGzC,QAAO,KAAK;EACV,YAAY;EACZ,aAAa;GAAE,IAAI;GAAe,QAAQ;GAAe;EACzD,UAAU,aAAa;EACxB,CAAC;CAEF,IAAI,eAAe;AACnB,KAAI,UAAU,OAAO,SAAS,GAAG;EAK/B,MAAM,UAAU,2BAA2B,OAAO;EAClD,MAAM,UAAU,QAAQ;AACxB,iBAAe,QAAQ;EACvB,IAAI,MAAM;AACV,OAAK,MAAM,SAAS,SAAS;AAC3B,OAAI,MAAM,SAAS,OACjB,oBAAmB,QAAQ,KAAK,MAAM,MAAM,UAAU;OAEtD,4BACE,QACA,KACA;IACE,MAAM,MAAM;IACZ,WAAW,MAAM;IACjB,IAAI,MAAM;IACX,EACD,OACD;AAEH,UAAO;;QAEJ;AAGL,qBAAmB,QAAQ,GAAG,SAAS,UAAU;AACjD,OAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,IACpC,4BAA2B,QAAQ,IAAI,GAAG,UAAU,IAAI,OAAO;;AAKnE,QAAO,KAAK;EACV,YAAY;EACZ,aAAa;GACX,IAAI;GACJ,QAAQ,eAAe,oBAAoB;GAC3C,OAAO,kBAAkB,UAAU;GACpC;EACD,UAAU,aAAa;EACxB,CAAC;AAEF,QAAO;;AAST,MAAM,oBAAsD;CAC1D,GAAG,SAA+B,EAAE,MAAM,OAAO,CAAC;CAClD,SAAS,UAAU,MAAM,eAAe;CACzC;AAqCD,SAAS,+BACP,MACA,WACiB;AACjB,KAAI,CAAC,aAAa,KAAK,QAAQ,UAAU,wBAAwB;EAI/D,MAAM,WAAW,KAAK;EACtB,MAAM,WAAW,0BAA0B,SAAS;EACpD,MAAM,UACJ,YAAY,SAAS,QAAQ,SAAS,2BAA2B,SAAS,OAAO,GAAG;EAItF,MAAM,aAFJ,SAAS,cACR,YAAY,mBAAmB,SAAS,GAAI,SAAS,aAAa,EAAE,GAAI,EAAE,GACrD,QAAQ,GAAG,UAAU,UAAU,KAAK,QAAQ,MAAM;EAC1E,IAAI,YAAY;EAChB,MAAM,SAAS,SAAS,QAAQ,QAC7B,UAAU,MAAM,SAAS,UAAU,gBAAgB,KAAK,QAAQ,MAClE;AACD,SAAO;GAAE,GAAG;GAAM,UAAU;IAAE,GAAG;IAAU;IAAW,GAAI,SAAS,EAAE,QAAQ,GAAG,EAAE;IAAG;GAAE;;AAIzF,QAAO,6BAA6B,KAAK;;AAG3C,SAAS,gCACP,QACA,MACwB;AACxB,KAAI,KAAK,SAAS,aAAa,KAAK,QAAQ,UAAU,uBAAwB,QAAO;AAGrF,QAAO,OAAO,KAAK,UACjB,MAAM,eAAe,2BAA2B,aAAa,MAAM,YAAY,GAC3E;EAAE,GAAG;EAAO,aAAa;GAAE,GAAG,MAAM;GAAa,QAAQ;GAAc;EAAE,GACzE,MACL;;AAGH,eAAsB,yBACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,MAAM,UAAU,IAAI,OAAO;CAE3B,IAAI;AACJ,KAAI;AACF,oBAAkB,KAAK,MAAM,IAAI;UAC1B,UAAU;EACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,qBACE,KACA,KACA,YAAY,gBAAgB,cAAc;GACxC,SAAS,wBAAwB;GACjC,MAAM;GACP,CAAC,CAAC,KACJ;AACD;;AAMF,KAAI,CAAC,aAAa,gBAAgB,EAAE;AAClC,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,qBACE,KACA,KACA,YAAY,gBAAgB,cAAc;GACxC,SAAS;GACT,MAAM;GACP,CAAC,CAAC,KACJ;AACD;;CAGF,MAAM,kBACJ,0BAA0B,gBAAgB,MAAM,IAChD,0BAA0B,gBAAgB,MAAM;AAClD,KAAI,iBAAiB;AACnB,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,qBACE,KACA,KACA,YAAY,gBAAgB,cAAc;GACxC,SAAS;GACT,MAAM;GACP,CAAC,CAAC,KACJ;AACD;;CAIF,MAAM,gBAAgB,sCAAsC,gBAAgB;AAQ5E,eAAc,gBAAgB;AAC9B,eAAc,WAAW,WAAW,IAAI;CAExC,MAAM,YAAY,gBAAgB,WAAW;CAC7C,MAAM,QAAQ,cAAc;CAE5B,MAAM,SAAS,UAAU,IAAI;CAC7B,MAAM,EAAE,SAAS,4BAA4B,uBAC3C,UACA,eACA,QAAQ,6BAA6B,OAAO,EAC5C,SAAS,kBACT,EAAE,MAAM,uBAAuB,CAChC;AAED,KAAI,QACF,SAAQ,2BAA2B,SAAS,UAAU,OAAO;AAG/D,KACE,MAAM,gBACJ,KACA,SACA,SAAS,OACT,IAAI,SACJ,IAAI,KACJ,SACA;EACE,QAAQ,IAAI,UAAU;EACtB,MAAM;EACN,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACP,EACD,UAAU,YAAY,SACtB,SAAS,UACT,SAAS,OACV,CAED;AAEF,KAAI,CAAC,SAAS;EACZ,MAAM,YAAY,wBAAwB,SAAS,QAAQ,IAAI,QAAQ;AACvE,MAAI,UAAU,aAAa;GACzB,MAAM,eAAe;GACrB,MAAM,gBAAgB,qBAAqB,wBAAwB;AACnE,UAAO,MAAM,qBAAqB,IAAI,UAAU,QAAQ,SAAS,wBAAwB,CAAC;AAC1F,WAAQ,IAAI;IACV,QAAQ,IAAI,UAAU;IACtB,MAAM;IACN,SAAS,eAAe,IAAI,QAAQ;IACpC,MAAM;IACN,UAAU;KACR,QAAQ;KACR,SAAS;KACT,GAAG,oBAAoB,SAAS,QAAQ,IAAI,QAAQ;KACrD;IACF,CAAC;AACF,sBACE,KACA,cACA,YAAY,gBAAgB,eAAe;IACzC,SAAS;IACT,MAAM;IACP,CAAC,CAAC,KACJ;AACD;;AAEF,MAAI,UAAU,YAAY,SAAS,QAAQ;GACzC,MAAM,EAAE,SAAS,iBAAiB,MAAM,oBAAoB,KAAK,WAC/D,eACE,KACA,KACA,eACA,uBACA,SACA,UACA,UACA,IACD,CACF;AACD,OAAI,YAAY,kBAAmB;AACnC,OAAI,YAAY,kBAAkB;AAChC,YAAQ,IAAI;KACV,QAAQ,IAAI,UAAU;KACtB,MAAM;KACN,SAAS,eAAe,IAAI,QAAQ;KACpC,MAAM;KACN,UAAU;MACR,QAAQ,IAAI,cAAc;MAC1B,SAAS;MACT,QAAQ;MACR,GAAG,kBAAkB,KAAK,aAAa;MACxC;KACF,CAAC;AACF;;;AAGJ,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IACR,QAAQ;IACR,SAAS;IACT,GAAG,oBAAoB,SAAS,QAAQ,IAAI,QAAQ;IACrD;GACF,CAAC;AACF,qBACE,KACA,KACA,YAAY,gBAAgB,gBAAgB;GAC1C,SAAS;GACT,MAAM;GACP,CAAC,CAAC,KACJ;AACD;;CAGF,IAAI,WAAW,MAAM,gBAAgB,SAAS,cAAc;CAC5D,IAAI,cAAc,gBAAgB;EAChC,MAAM;EACN;EACA;EACA,SAAS;EACT,QAAQ;EACR,kBAAkB;EAClB;EACA,YAAY,IAAI;EAChB,KAAK,IAAI;EACV,CAAC;AACF,KAAI,YAAY,SAAS,SAAS;AAQhC,2BAAyB;GAAE,OAPb,QAAQ,IAAI;IACxB,QAAQ,IAAI,UAAU;IACtB,MAAM;IACN,SAAS,eAAe,IAAI,QAAQ;IACpC,MAAM;IACN,UAAU;KAAE,QAAQ,YAAY;KAAQ;KAAS;IAClD,CAAC;GACgC,SAAS,YAAY;GAAS;GAAU;GAAQ,CAAC;AACnF,MAAI,CAAC,YAAY,SAAS,YAAY,MAAM,eAAe,WAAW,YAAY,QAAQ,CACxF,QAAO,MAAM,GAAG,IAAI,UAAU,OAAO,GAAG,QAAQ,IAAI,YAAY,UAAU;AAE5E,qBACE,KACA,YAAY,QACZ,YAAY,gBAAgB,eAAe;GACzC,SAAS,YAAY;GACrB,MAAM,YAAY;GACnB,CAAC,CAAC,KACJ;AACD;;AAEF,KAAI,YAAY,SAAS,WAAW;AAClC,gBAAc,+BAA+B,aAAa,UAAU;AACpE,aAAW,YAAY;;CAEzB,MAAM,eACJ,YAAY,SAAS,YACjB,kCAAkC,aAAa,cAAc,GAC7D;CACN,MAAM,UAAU,QAAQ,WAAW,SAAS;CAC5C,MAAM,YAAY,KAAK,IAAI,GAAG,QAAQ,aAAa,SAAS,UAAU;CACtE,MAAM,cAAc,QAAQ,eAAe,SAAS;CACpD,MAAM,eAAe;EACnB;EACA,kBAAkB,QAAQ;EAC1B,iBAAiB,QAAQ;EACzB;EACD;CAKD,MAAM,UAAU,YAAY,SAAS,YAAY,cAAc;CAC/D,MAAM,kBAAkB;EACtB,SAAS;EACT,eAAe,UACX,yBAAyB,SAAS,OAAU,GAC5C,oBAAoB,SAAS;EACjC;EACA,iBAAiB,QAAQ;EACzB,aAAa,CAAC,CAAC,SAAS;EACxB;EACD;CACD,MAAM,kBAAoC,SACxC,SAAS,QAAQ,UAAU,yBAAyB;EAAE,GAAG;EAAM,QAAQ;EAAc,GAAG;AAI1F,KAAI,cAAc,SAAS,EAAE;AAC3B,QAAM,iBAAiB;GACrB,MAAM;GACN,QAAQ;GACR;GACA;GACA;GACA;GACA,MAAM;GACN;GACA;GACA;GACA,oBAAoB,YAAY;GAChC,WAAW;GACZ,CAAC;AACF;;AAIF,KAAI,gBAAgB,SAAS,EAAE;EAC7B,MAAM,SAAS,SAAS,UAAU;AAQlC,2BAAyB;GACvB,OARmB,QAAQ,IAAI;IAC/B,QAAQ,IAAI,UAAU;IACtB,MAAM;IACN,SAAS,eAAe,IAAI,QAAQ;IACpC,MAAM;IACN,UAAU;KAAE;KAAQ;KAAS;IAC9B,CAAC;GAGA,SAAS,YAAY;GACrB;GACA;GACD,CAAC;AACF,qBACE,KACA,QACA,YAAY,gBAAgB,WAAW;GACrC,SAAS,SAAS,MAAM;GACxB,MAAM,SAAS,MAAM,QAAQ;GAC9B,CAAC,CAAC,MACH,EAAE,YAAY,SAAS,YAAY,CACpC;AACD;;AAWF,KAAI,YAAY,SAAS,aAAa,YAAY,QAAQ,UAAU,kBAAkB;EACpF,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,2BAAyB;GACvB,OAAO;GACP,SAAS,YAAY;GACrB;GACA;GACD,CAAC;EAGF,MAAM,gBAAgB,mBAAmB;EACzC,MAAM,QAAQA,OAAwB,EAAE,OAAO,cAAc,EAAE,yBAAyB;AACxF,MAAI,CAAC,WAAW;GACd,MAAM,YACJ,eAAe,SAAS,IACxB,mBAAmB,SAAS,IAC5B,0BAA0B,SAAS,GAC/B,iBAAiB,SAAS,GAC1B;GACN,MAAM,YAAY,YAAY,aAAa;AAC3C,SAAM,YACJ,KACA,cACA,gBACO;IACL,IAAI;IACJ,OAAO,WAAW,SAAS;IAC3B,QAAQ;IACR,OAAO,CACL;KACE,MAAM;KACN,WAAW;KACX,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM;MAAW,CAAC;KAC7C,CACF;IACD;IACD,GACD,UAAU,CACX;AACD;;AA6BF,QAAM,cAAc,KAAK,cAAc,SAAS,mBA3BT;GACrC;IACE,YAAY;IACZ,aAAa;KAAE,IAAI;KAAe,QAAQ;KAAe;IACzD,UAAU,aAAa;IACxB;GACD;IAAE,YAAY;IAAc,OAAO;IAAG,MAAM,EAAE,MAAM,WAAW;IAAE,UAAU,aAAa;IAAE;GAC1F;IACE,YAAY;IACZ,OAAO;IACP,OAAO;KACL,MAAM;KACN,SAAS;MAAE,MAAM;MAAQ,MAAM,YAAY,aAAa;MAAI;KAC7D;IACD,UAAU,aAAa;IACxB;GACD;IAAE,YAAY;IAAa,OAAO;IAAG,UAAU,aAAa;IAAE;GAC9D;IACE,YAAY;IACZ,aAAa;KACX,IAAI;KACJ,QAAQ;KACR;KACD;IACD,UAAU,aAAa;IACxB;GACF,EAC0E,aAAa;AACxF;;AAIF,KAAI,0BAA0B,SAAS,EAAE;AACvC,MAAI,SAAS,aAAa,OACxB,QAAO,KACL,2FACD;EAEH,MAAM,YAAY;GAChB,GAAG,iBAAiB,SAAS;GAC7B,GAAI,eAAe,EAAE,OAAO,cAAc,GAAG,EAAE;GAChD;EACD,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,2BAAyB;GACvB,OAAO;GACP,SAAS,YAAY;GACrB;GACA;GACD,CAAC;EACF,MAAM,oBAAoB,gCAAgC,UAAU,sBAAsB;EAC1F,MAAM,gBAAgB,mBAAmB;AACzC,MAAI,CAAC,WAAW;GACd,MAAM,UAAU,SAAS,WAAW;GACpC,MAAM,SAAS,SAAS;AACxB,SAAM,YACJ,KACA,cACA,eAEE,eACE,8CACE,SACA,mBACA,OACA,eACA,QACA,WACA,OACD,CACF,EACH,UAAU,CACX;QAWD,OAAM,cACJ,KACA,cACA,SACA,mBACA,gCAda,+CACb,SAAS,WAAW,IACpB,mBACA,eACA,WACA,QACA,WACA,SAAS,OACV,EAMyC,YAAY,EACpD,aACD;AAEH;;AAIF,KAAI,eAAe,SAAS,EAAE;AAC5B,MAAI,SAAS,aAAa,OACxB,QAAO,KACL,2FACD;EAEH,MAAM,YAAY;GAChB,GAAG,iBAAiB,SAAS;GAC7B,GAAI,eAAe,EAAE,OAAO,cAAc,GAAG,EAAE;GAChD;EACD,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,2BAAyB;GACvB,OAAO;GACP,SAAS,YAAY;GACrB;GACA;GACD,CAAC;EACF,MAAM,gBAAgB,mBAAmB;AACzC,MAAI,CAAC,WAAW;GACd,MAAM,UAAU,SAAS;AACzB,SAAM,YACJ,KACA,cACA,eAEE,eAAe,8BAA8B,SAAS,OAAO,eAAe,UAAU,CAAC,EACzF,UAAU,CACX;QAQD,OAAM,cACJ,KACA,cACA,SACA,mBACA,gCAXa,+BACb,SAAS,SACT,eACA,WACA,UACD,EAMyC,YAAY,EACpD,aACD;AAEH;;AAIF,KAAI,mBAAmB,SAAS,EAAE;AAChC,MAAI,SAAS,aAAa,OACxB,QAAO,KACL,2FACD;EAEH,MAAM,YAAY;GAChB,GAAG,iBAAiB,SAAS;GAC7B,GAAI,eAAe,EAAE,OAAO,cAAc,GAAG,EAAE;GAChD;EACD,MAAM,eAAe,QAAQ,IAAI;GAC/B,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;AACF,2BAAyB;GACvB,OAAO;GACP,SAAS,YAAY;GACrB;GACA;GACD,CAAC;EACF,MAAM,oBAAoB,gCAAgC,UAAU,sBAAsB;EAC1F,MAAM,gBAAgB,mBAAmB;AACzC,MAAI,CAAC,UACH,OAAM,YACJ,KACA,cACA,eAEE,eACE,kCACE,mBACA,OACA,eACA,QACA,UACD,CACF,EACH,UAAU,CACX;MAQD,OAAM,cACJ,KACA,cACA,SACA,mBACA,gCAXa,mCACb,mBACA,eACA,QACA,UACD,EAMyC,YAAY,EACpD,aACD;AAEH;;AAWF,0BAAyB;EAAE,OAPN,QAAQ,IAAI;GAC/B,QAAQ,IAAI,UAAU;GACtB,MAAM;GACN,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,UAAU;IAAE,QAAQ;IAAK;IAAS;GACnC,CAAC;EAC8C,SAAS,YAAY;EAAS;EAAU;EAAQ,CAAC;AACjG,oBACE,KACA,KACA,YAAY,gBAAgB,oBAAoB;EAC9C,SAAS;EACT,MAAM;EACP,CAAC,CAAC,KACJ"}
|