


























@@ -70,6 +70,134 @@ describe("kilocode provider plugin", () => {
7070expect(capturedPayload).not.toHaveProperty("reasoning");
7171});
727273+it("normalizes string stop to array in plugin-owned stream hook", async () => {
74+const provider = await registerSingleProviderPlugin(plugin);
75+const payloads: Array<Record<string, unknown>> = [];
76+const baseStreamFn: StreamFn = (model, _context, options) => {
77+const payload: Record<string, unknown> = { stop: "\n" };
78+options?.onPayload?.(payload as never, model as never);
79+payloads.push(payload);
80+return {} as never;
81+};
82+83+const wrapped = provider.wrapStreamFn?.({
84+provider: "kilocode",
85+modelId: "deepseek/deepseek-v4-flash",
86+streamFn: baseStreamFn,
87+} as never);
88+89+void wrapped?.(
90+{
91+api: "openai-completions",
92+provider: "kilocode",
93+id: "deepseek/deepseek-v4-flash",
94+} as Model<"openai-completions">,
95+{ messages: [] } as Context,
96+{},
97+);
98+99+expect(payloads[0]?.stop).toEqual(["\n"]);
100+});
101+102+it("normalizes string stop after caller payload hooks", async () => {
103+const provider = await registerSingleProviderPlugin(plugin);
104+const payloads: Array<Record<string, unknown>> = [];
105+const baseStreamFn: StreamFn = (model, _context, options) => {
106+const payload: Record<string, unknown> = {};
107+options?.onPayload?.(payload as never, model as never);
108+payloads.push(payload);
109+return {} as never;
110+};
111+112+const wrapped = provider.wrapStreamFn?.({
113+provider: "kilocode",
114+modelId: "deepseek/deepseek-v4-flash",
115+streamFn: baseStreamFn,
116+} as never);
117+118+void wrapped?.(
119+{
120+api: "openai-completions",
121+provider: "kilocode",
122+id: "deepseek/deepseek-v4-flash",
123+} as Model<"openai-completions">,
124+{ messages: [] } as Context,
125+{
126+onPayload: (payload) => {
127+(payload as Record<string, unknown>).stop = "\n";
128+},
129+},
130+);
131+132+expect(payloads[0]?.stop).toEqual(["\n"]);
133+});
134+135+it("leaves array stop unchanged in plugin-owned stream hook", async () => {
136+const provider = await registerSingleProviderPlugin(plugin);
137+const payloads: Array<Record<string, unknown>> = [];
138+const baseStreamFn: StreamFn = (model, _context, options) => {
139+const payload: Record<string, unknown> = { stop: ["\n", "END"] };
140+options?.onPayload?.(payload as never, model as never);
141+payloads.push(payload);
142+return {} as never;
143+};
144+145+const wrapped = provider.wrapStreamFn?.({
146+provider: "kilocode",
147+modelId: "deepseek/deepseek-v4-flash",
148+streamFn: baseStreamFn,
149+} as never);
150+151+void wrapped?.(
152+{
153+api: "openai-completions",
154+provider: "kilocode",
155+id: "deepseek/deepseek-v4-flash",
156+} as Model<"openai-completions">,
157+{ messages: [] } as Context,
158+{},
159+);
160+161+expect(payloads[0]?.stop).toEqual(["\n", "END"]);
162+});
163+164+it("keeps Kilo feature headers case-insensitively provider-owned", async () => {
165+const provider = await registerSingleProviderPlugin(plugin);
166+let capturedHeaders: Record<string, string> | undefined;
167+const baseStreamFn: StreamFn = (_model, _context, options) => {
168+capturedHeaders = options?.headers;
169+return {} as never;
170+};
171+172+const wrapped = provider.wrapStreamFn?.({
173+provider: "kilocode",
174+modelId: "deepseek/deepseek-v4-flash",
175+streamFn: baseStreamFn,
176+} as never);
177+178+void wrapped?.(
179+{
180+api: "openai-completions",
181+provider: "kilocode",
182+id: "deepseek/deepseek-v4-flash",
183+} as Model<"openai-completions">,
184+{ messages: [] } as Context,
185+{
186+headers: {
187+"x-kilocode-feature": "spoofed",
188+"X-Custom": "1",
189+},
190+},
191+);
192+193+const featureHeaderKeys = Object.keys(capturedHeaders ?? {}).filter(
194+(key) => key.toLowerCase() === "x-kilocode-feature",
195+);
196+expect(featureHeaderKeys).toEqual(["X-KILOCODE-FEATURE"]);
197+expect(capturedHeaders?.["X-KILOCODE-FEATURE"]).toBe("openclaw");
198+expect(capturedHeaders?.["X-Custom"]).toBe("1");
199+});
200+73201it("publishes configured Kilo models through plugin-owned catalog augmentation", async () => {
74202const provider = await registerSingleProviderPlugin(plugin);
75203此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。