Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
4.9 kB · 107 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108import * as fs from "node:fs";import * as os from "node:os";import * as path from "node:path";import { afterEach, beforeEach, describe, expect, it } from "vitest";import { AuthStorage } from "../src/core/auth-storage.js";import { discoverAndLoadExtensions } from "../src/core/extensions/loader.js";import { ExtensionRunner } from "../src/core/extensions/runner.js";import { ModelRegistry } from "../src/core/model-registry.js";import { SessionManager } from "../src/core/session-manager.js";
describe("Input Event", () => { let tempDir: string; let extensionsDir: string;
beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-input-test-")); extensionsDir = path.join(tempDir, "extensions"); fs.mkdirSync(extensionsDir); delete (globalThis as any).testVar; });
afterEach(() => fs.rmSync(tempDir, { recursive: true, force: true }));
async function createRunner(...extensions: string[]) { fs.rmSync(extensionsDir, { recursive: true, force: true }); fs.mkdirSync(extensionsDir); for (let i = 0; i < extensions.length; i++) fs.writeFileSync(path.join(extensionsDir, `e${i}.ts`), extensions[i]); const result = await discoverAndLoadExtensions([], tempDir, tempDir); const sm = SessionManager.inMemory(); const mr = ModelRegistry.create(AuthStorage.create(path.join(tempDir, "auth.json"))); return new ExtensionRunner(result.extensions, result.runtime, tempDir, sm, mr); }
it("returns continue when no handlers, undefined return, or explicit continue", async () => { expect((await (await createRunner()).emitInput("x", undefined, "interactive")).action).toBe("continue"); let r = await createRunner(`export default p => p.on("input", async () => {});`); expect((await r.emitInput("x", undefined, "interactive")).action).toBe("continue"); r = await createRunner(`export default p => p.on("input", async () => ({ action: "continue" }));`); expect((await r.emitInput("x", undefined, "interactive")).action).toBe("continue"); });
it("transforms text and preserves images when omitted", async () => { const r = await createRunner( `export default p => p.on("input", async e => ({ action: "transform", text: "T:" + e.text }));`, ); const imgs = [{ type: "image" as const, data: "orig", mimeType: "image/png" }]; const result = await r.emitInput("hi", imgs, "interactive"); expect(result).toEqual({ action: "transform", text: "T:hi", images: imgs }); });
it("transforms and replaces images when provided", async () => { const r = await createRunner( `export default p => p.on("input", async () => ({ action: "transform", text: "X", images: [{ type: "image", data: "new", mimeType: "image/jpeg" }] }));`, ); const result = await r.emitInput("hi", [{ type: "image", data: "orig", mimeType: "image/png" }], "interactive"); expect(result).toEqual({ action: "transform", text: "X", images: [{ type: "image", data: "new", mimeType: "image/jpeg" }], }); });
it("chains transforms across multiple handlers", async () => { const r = await createRunner( `export default p => p.on("input", async e => ({ action: "transform", text: e.text + "[1]" }));`, `export default p => p.on("input", async e => ({ action: "transform", text: e.text + "[2]" }));`, ); const result = await r.emitInput("X", undefined, "interactive"); expect(result).toEqual({ action: "transform", text: "X[1][2]", images: undefined }); });
it("short-circuits on handled and skips subsequent handlers", async () => { (globalThis as any).testVar = false; const r = await createRunner( `export default p => p.on("input", async () => ({ action: "handled" }));`, `export default p => p.on("input", async () => { globalThis.testVar = true; });`, ); expect(await r.emitInput("X", undefined, "interactive")).toEqual({ action: "handled" }); expect((globalThis as any).testVar).toBe(false); });
it("passes source correctly for all source types", async () => { const r = await createRunner( `export default p => p.on("input", async e => { globalThis.testVar = e.source; return { action: "continue" }; });`, ); for (const source of ["interactive", "rpc", "extension"] as const) { await r.emitInput("x", undefined, source); expect((globalThis as any).testVar).toBe(source); } });
it("catches handler errors and continues", async () => { const r = await createRunner(`export default p => p.on("input", async () => { throw new Error("boom"); });`); const errs: string[] = []; r.onError((e) => errs.push(e.error)); const result = await r.emitInput("x", undefined, "interactive"); expect(result.action).toBe("continue"); expect(errs).toContain("boom"); });
it("hasHandlers returns correct value", async () => { let r = await createRunner(); expect(r.hasHandlers("input")).toBe(false); r = await createRunner(`export default p => p.on("input", async () => {});`); expect(r.hasHandlers("input")).toBe(true); });});