refactor: expose session streamFn on PiAgentSession

Add a narrow `agent: { streamFn: StreamFn }` field to the PiAgentSession
interface, sourced structurally from the real AgentSession.agent (Agent,
from the stable @earendil-works/pi-agent-core package). This is the
resolved-auth/headers/retry "call this model" function pi's own
compaction/branch-summarization code uses internally, and will let
sessionNameGenerator.ts drop its dependency on the deprecated pi-ai
/compat provider registry in a follow-up leg.

No adapter object was needed: AgentSession's real .agent field already
structurally satisfies the new narrow shape, so defaultCreateAgentRuntime
needed no changes.

Adds a unit test proving the field is wired end-to-end via the injected
runtime creator. sessionNameGenerator.ts itself is untouched (leg C).
This commit is contained in:
Federico Jaramillo Martinez
2026-07-02 11:28:18 +02:00
parent e63665549a
commit 6f59a4243e
2 changed files with 28 additions and 0 deletions
@@ -125,6 +125,7 @@ function fakeRuntime(sessionId = "session-1", patch: Partial<TestSession> = {})
setSessionName: (name: string) => { session.sessionName = name; },
compact: () => Promise.resolve({ summary: "", tokensBefore: 0 }),
getUserMessagesForForking: () => [],
agent: { streamFn: () => { throw new Error("streamFn should not be called in this test"); } },
...patch,
};
const runtime: PiSessionRuntime = {
@@ -166,6 +167,23 @@ function emptyArchiveStore(): NonNullable<PiSessionServiceDependencies["archiveS
}
describe("PiSessionService", () => {
it("exposes the session's agent.streamFn for one-off model calls", async () => {
const hub = new CapturingSessionEventHub();
const streamFn = vi.fn();
const fake = fakeRuntime("stream-session", { agent: { streamFn } });
const service = new PiSessionService(hub, {
createAgentRuntime: runtimeCreator(fake.runtime),
sessionManager: sessionGateway([]),
heartbeatIntervalMs: 60_000,
});
await service.start("/workspace");
expect(fake.session.agent.streamFn).toBe(streamFn);
await service.dispose();
});
it("starts sessions through an injected runtime creator", async () => {
const hub = new CapturingSessionEventHub();
const fake = fakeRuntime();
+10
View File
@@ -1,5 +1,6 @@
import { open, readFile, writeFile } from "node:fs/promises";
import type { ImageContent } from "@earendil-works/pi-ai";
import type { StreamFn } from "@earendil-works/pi-agent-core";
import {
AuthStorage,
createAgentSessionFromServices,
@@ -232,6 +233,15 @@ export interface PiAgentSession {
setThinkingLevel(level: ClientThinkingLevel): void;
cycleThinkingLevel(): ClientThinkingLevel | undefined;
setSessionName(name: string): void;
/**
* Narrow re-expression of `AgentSession.agent` (an `@earendil-works/pi-agent-core`
* `Agent`), exposing only `streamFn` — the resolved-auth/headers/retry "call this
* model" function pi's own compaction/branch-summarization code uses internally.
* Lets callers (e.g. session title generation) issue one-off model calls without
* depending on pi-ai's deprecated `/compat` provider registry or leaking the full
* `Agent`/`AgentSession` surface.
*/
agent: { streamFn: StreamFn };
}
export interface PiSessionRuntime {