fix(agent): restore streamFn extension compatibility

Keep streamFn required for typed callers while preserving the legacy runtime fallback for extensions that omit it.\n\nfixes #6915
This commit is contained in:
Mario Zechner
2026-07-21 18:34:56 +02:00
parent b142504125
commit b9e5c5d941
26 changed files with 210 additions and 88 deletions
+35
View File
@@ -9,6 +9,7 @@ import {
import { Type } from "typebox";
import { describe, expect, it } from "vitest";
import { agentLoop, agentLoopContinue } from "../src/agent-loop.ts";
import { setDefaultStreamFn } from "../src/index.ts";
import type { AgentContext, AgentEvent, AgentLoopConfig, AgentMessage, AgentTool } from "../src/types.ts";
// Mock stream for testing - mimics MockAssistantStream
@@ -80,6 +81,40 @@ function identityConverter(messages: AgentMessage[]): Message[] {
return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[];
}
describe("default stream function compatibility", () => {
it("uses the configured default when a legacy caller omits streamFn", async () => {
let calls = 0;
setDefaultStreamFn(() => {
calls++;
const stream = new MockAssistantStream();
queueMicrotask(() => {
stream.push({
type: "done",
reason: "stop",
message: createAssistantMessage([{ type: "text", text: "fallback" }]),
});
});
return stream;
});
try {
const context: AgentContext = { systemPrompt: "", messages: [], tools: [] };
const config: AgentLoopConfig = { model: createModel(), convertToLlm: identityConverter };
const stream = Reflect.apply(agentLoop, undefined, [
[createUserMessage("Hello")],
context,
config,
undefined,
]) as ReturnType<typeof agentLoop>;
await stream.result();
expect(calls).toBe(1);
} finally {
setDefaultStreamFn(undefined);
}
});
});
describe("agentLoop with AgentMessage", () => {
it("should emit events with AgentMessage types", async () => {
const context: AgentContext = {