pi-mono/packages/coding-agent/test/experimental-plugin-reload.test.ts
Mario Zechner 48dd1e2f0f feat(coding-agent): port the experimental client/server to pi-durable
Session workers now host a durable Harness over a per-session sqlite storage.
The server keeps each session in <sessionDir>/<id>/ with meta.json and
session.sqlite and never opens the storage itself. Workers hold retirement
while the Harness task graph has live tasks and resume interrupted work on
open.

Transcript serves the root conversation's durable view directly, Models
follows the conversation's agent document, and AgentController maps prompt,
steer, follow-up, queue cancellation, abort, and compaction onto the root
conversation, plus waitForPrompt. navigate, nextRun, and resume are dropped
(see experimental/services/README.md TODO). The durable TUI and the worker
share harness-setup.ts.

pi-server exports its own SessionMetadata and no longer depends on
pi-agent-core. Chord accepts readonly JSON types in service contracts without
walking them, which recursive JSON types like pi-ai's made too deep.
2026-10-01 14:45:01 +02:00

56 lines
1.7 KiB
TypeScript

import { defineFacet, type FacetLoader } from "@earendil-works/chord";
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
import { describe, expect, test, vi } from "vitest";
import { SessionPlugins } from "../src/experimental/services/plugins.ts";
import { createSessionWorkerServices } from "../src/experimental/services/worker.ts";
import { openFauxConversation } from "./experimental-durable-support.ts";
describe("experimental plugin reload", () => {
test("loads and cuts over a fresh Session facet generation", async () => {
const activations: number[] = [];
const disposals: number[] = [];
let generation = 0;
const facetLoader: FacetLoader = {
async load() {
const current = ++generation;
return {
facets: [
defineFacet({
id: "reloadable-session-plugin",
setup(env) {
env.onActivate(() => {
activations.push(current);
});
},
}),
],
async dispose() {
disposals.push(current);
},
};
},
};
const { harness, conversation, close } = await openFauxConversation();
const services = await createSessionWorkerServices({
harness,
conversation,
modelRuntime: undefined,
facetLoader,
publish: vi.fn(async () => {}),
});
try {
expect(activations).toEqual([1]);
await services.invoke(
{ serviceId: SessionPlugins.id, member: "reload", args: [] },
{ serverConnectionId: "server-1", attachmentId: "attachment-1" },
BACKGROUND_CONTEXT,
);
expect(activations).toEqual([1, 2]);
expect(disposals).toEqual([1]);
} finally {
await services.dispose();
await close();
}
expect(disposals).toEqual([1, 2]);
});
});