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:
@@ -2,9 +2,21 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### New Features
|
||||
|
||||
- **Verifiable release source archives** — GitHub releases now include deterministic, checksummed source archives with instructions for rebuilding standalone binaries. See [Building standalone binaries from release source](../../README.md#building-standalone-binaries-from-release-source).
|
||||
- **Resilient compaction and branch summaries** — Transient provider failures now follow the configured retry policy, with retry lifecycle events available to interactive, JSON, RPC, and SDK consumers. See [Compaction & Branch Summarization](docs/compaction.md) and [RPC retry events](docs/rpc.md#summarization_retry_scheduled--summarization_retry_attempt_start--summarization_retry_finished).
|
||||
|
||||
### Added
|
||||
|
||||
- Added deterministic, checksummed source archives to GitHub releases with documented standalone binary rebuild instructions ([#6913](https://github.com/earendil-works/pi/pull/6913) by [@christianklotz](https://github.com/christianklotz)).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed compaction and branch summarization to retry transient provider failures using the configured retry policy, with retry lifecycle events exposed to interactive, JSON, RPC, and SDK consumers ([#6901](https://github.com/earendil-works/pi/pull/6901) by [@davidbrai](https://github.com/davidbrai)).
|
||||
- Fixed interactive startup waiting for background model catalog refresh while computing the footer provider count.
|
||||
- Restored the default stream fallback for extensions using the pre-0.81 agent-core API ([#6915](https://github.com/earendil-works/pi/issues/6915)).
|
||||
- Fixed inherited Kimi K3 models from Moonshot AI and Moonshot AI China to use the OpenAI thinking format and expose reasoning effort support.
|
||||
|
||||
## [0.81.0] - 2026-07-21
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { join } from "node:path";
|
||||
import { Agent, type AgentMessage, type ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import { clampThinkingLevel, type Message, type Model } from "@earendil-works/pi-ai/compat";
|
||||
import { Agent, type AgentMessage, setDefaultStreamFn, type ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import { clampThinkingLevel, type Message, type Model, streamSimple } from "@earendil-works/pi-ai/compat";
|
||||
import { getAgentDir } from "../config.ts";
|
||||
import { resolvePath } from "../utils/paths.ts";
|
||||
import { AgentSession } from "./agent-session.ts";
|
||||
@@ -30,6 +30,11 @@ import {
|
||||
withFileMutationQueue,
|
||||
} from "./tools/index.ts";
|
||||
|
||||
// Preserve the pre-0.81 fallback for extensions that construct Agent instances
|
||||
// or invoke low-level agent loops without supplying streamFn. Agent core remains
|
||||
// provider-agnostic and does not import pi-ai/compat itself.
|
||||
setDefaultStreamFn(streamSimple);
|
||||
|
||||
export interface CreateAgentSessionOptions {
|
||||
/** Working directory for project-local discovery. Default: process.cwd() */
|
||||
cwd?: string;
|
||||
@@ -294,7 +299,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
||||
tools: [],
|
||||
},
|
||||
convertToLlm: convertToLlmWithBlockImages,
|
||||
streamFunction: async (model, context, options) => {
|
||||
streamFn: async (model, context, options) => {
|
||||
const providerRetrySettings = settingsManager.getProviderRetrySettings();
|
||||
const httpIdleTimeoutMs = settingsManager.getHttpIdleTimeoutMs();
|
||||
// SDKs treat timeout=0 as 0ms (immediate timeout), not "no timeout".
|
||||
|
||||
@@ -24,7 +24,7 @@ describe("AgentSession auto-compaction queue resume", () => {
|
||||
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5")!;
|
||||
const agent = new Agent({
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
initialState: {
|
||||
model,
|
||||
systemPrompt: "Test",
|
||||
|
||||
@@ -49,7 +49,7 @@ describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => {
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5")!;
|
||||
const agent = new Agent({
|
||||
getApiKey: () => API_KEY,
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
initialState: {
|
||||
model,
|
||||
systemPrompt: "You are a helpful assistant. Be concise.",
|
||||
|
||||
@@ -90,7 +90,7 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
systemPrompt: "Test",
|
||||
tools: [],
|
||||
},
|
||||
streamFunction: (_model, _context, options) => {
|
||||
streamFn: (_model, _context, options) => {
|
||||
abortSignal = options?.signal;
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
@@ -195,7 +195,7 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
systemPrompt: "Test",
|
||||
tools: [],
|
||||
},
|
||||
streamFunction: (_model, context, options) => {
|
||||
streamFn: (_model, context, options) => {
|
||||
abortSignal = options?.signal;
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
@@ -301,7 +301,7 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
systemPrompt: "Test",
|
||||
tools: [],
|
||||
},
|
||||
streamFunction: () => {
|
||||
streamFn: () => {
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
stream.push({ type: "start", partial: createAssistantMessage("") });
|
||||
@@ -362,7 +362,7 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
systemPrompt: "Test",
|
||||
tools: [tool],
|
||||
},
|
||||
streamFunction: async (_model, context) => {
|
||||
streamFn: async (_model, context) => {
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
const toolResultCount = context.messages.filter((message) => message.role === "toolResult").length;
|
||||
@@ -508,7 +508,7 @@ describe("AgentSession concurrent prompt guard", () => {
|
||||
systemPrompt: "Test",
|
||||
tools: [tool],
|
||||
},
|
||||
streamFunction: async (_model, context) => {
|
||||
streamFn: async (_model, context) => {
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
const hasToolResult = context.messages.some((message) => message.role === "toolResult");
|
||||
|
||||
@@ -82,7 +82,7 @@ describe("AgentSession retry", () => {
|
||||
const agent = new Agent({
|
||||
getApiKey: () => "test-key",
|
||||
initialState: { model, systemPrompt: "Test", tools: [] },
|
||||
streamFunction: () => {
|
||||
streamFn: () => {
|
||||
callCount++;
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
@@ -203,7 +203,7 @@ describe("AgentSession retry", () => {
|
||||
const agent = new Agent({
|
||||
getApiKey: () => "test-key",
|
||||
initialState: { model, systemPrompt: "Test", tools: [] },
|
||||
streamFunction: streamFn,
|
||||
streamFn: streamFn,
|
||||
});
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
const settingsManager = SettingsManager.create(tempDir, tempDir);
|
||||
@@ -255,7 +255,7 @@ describe("AgentSession retry", () => {
|
||||
const agent = new Agent({
|
||||
getApiKey: () => "test-key",
|
||||
initialState: { model, systemPrompt: "Test", tools: [] },
|
||||
streamFunction: () => {
|
||||
streamFn: () => {
|
||||
callCount++;
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
|
||||
@@ -75,7 +75,7 @@ async function createSession() {
|
||||
const session = new AgentSession({
|
||||
agent: new Agent({
|
||||
getApiKey: () => "test-key",
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
initialState: {
|
||||
model,
|
||||
systemPrompt: "You are a helpful assistant.",
|
||||
|
||||
@@ -89,7 +89,7 @@ describe.skipIf(!API_KEY)("Compaction extensions", () => {
|
||||
const model = getModel("anthropic", "claude-sonnet-4-5")!;
|
||||
const agent = new Agent({
|
||||
getApiKey: () => API_KEY,
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
initialState: {
|
||||
model,
|
||||
systemPrompt: "You are a helpful assistant. Be concise.",
|
||||
|
||||
@@ -114,7 +114,7 @@ async function createRuntimeHost(options: { withAuth: boolean; responseDelayMs:
|
||||
systemPrompt: "Test",
|
||||
tools: [],
|
||||
},
|
||||
streamFunction: (_model, _context, _options) => {
|
||||
streamFn: (_model, _context, _options) => {
|
||||
const stream = new MockAssistantStream();
|
||||
queueMicrotask(() => {
|
||||
stream.push({ type: "start", partial: createAssistantMessage("") });
|
||||
|
||||
@@ -137,7 +137,7 @@ export async function createHarness(options: HarnessOptions = {}): Promise<Harne
|
||||
|
||||
const agent = new Agent({
|
||||
getApiKey: () => (withConfiguredAuth ? "faux-key" : undefined),
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
initialState: {
|
||||
model,
|
||||
systemPrompt: options.systemPrompt ?? "You are a test assistant.",
|
||||
|
||||
@@ -61,7 +61,7 @@ describe("regression #5596: missing configured theme export", () => {
|
||||
tools: [],
|
||||
},
|
||||
convertToLlm,
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
});
|
||||
const session = new AgentSession({
|
||||
agent,
|
||||
|
||||
@@ -378,7 +378,7 @@ async function createHarnessWithResourceLoader(
|
||||
systemPrompt: options.systemPrompt ?? "You are a test assistant.",
|
||||
tools: options.tools ?? [],
|
||||
},
|
||||
streamFunction: streamFn,
|
||||
streamFn: streamFn,
|
||||
});
|
||||
|
||||
const sessionManager = SessionManager.inMemory();
|
||||
|
||||
@@ -246,7 +246,7 @@ export async function createTestSession(options: TestSessionOptions = {}): Promi
|
||||
systemPrompt: options.systemPrompt ?? "You are a helpful assistant. Be extremely concise.",
|
||||
tools: createCodingTools(process.cwd()),
|
||||
},
|
||||
streamFunction: streamSimple,
|
||||
streamFn: streamSimple,
|
||||
});
|
||||
|
||||
const sessionManager = options.inMemory ? SessionManager.inMemory() : SessionManager.create(tempDir);
|
||||
|
||||
Reference in New Issue
Block a user