@@ -234,6 +234,158 @@ describe("AgentSessionRuntime characterization", () => {
|
||||
expect(events).toEqual([{ type: "session_before_fork", entryId: "missing-entry", position: "at" }]);
|
||||
});
|
||||
|
||||
it("duplicates the current active branch when forking at the current position", async () => {
|
||||
const { runtime } = await createRuntimeForTest(() => {});
|
||||
await runtime.session.prompt("hello");
|
||||
await runtime.session.prompt("again");
|
||||
|
||||
const beforeMessages = runtime.session.messages.map((message) => ({
|
||||
role: message.role,
|
||||
text:
|
||||
message.role === "user"
|
||||
? typeof message.content === "string"
|
||||
? message.content
|
||||
: message.content
|
||||
.filter((part): part is { type: "text"; text: string } => part.type === "text")
|
||||
.map((part) => part.text)
|
||||
.join("")
|
||||
: undefined,
|
||||
}));
|
||||
const previousSessionFile = runtime.session.sessionFile;
|
||||
const leafId = runtime.session.sessionManager.getLeafId();
|
||||
expect(leafId).toBeTruthy();
|
||||
|
||||
const result = await runtime.fork(leafId!, { position: "at" });
|
||||
expect(result).toEqual({ cancelled: false, selectedText: undefined });
|
||||
expect(runtime.session.sessionFile).not.toBe(previousSessionFile);
|
||||
expect(
|
||||
runtime.session.messages.map((message) => ({
|
||||
role: message.role,
|
||||
text:
|
||||
message.role === "user"
|
||||
? typeof message.content === "string"
|
||||
? message.content
|
||||
: message.content
|
||||
.filter((part): part is { type: "text"; text: string } => part.type === "text")
|
||||
.map((part) => part.text)
|
||||
.join("")
|
||||
: undefined,
|
||||
})),
|
||||
).toEqual(beforeMessages);
|
||||
});
|
||||
|
||||
it("duplicates the current active branch in-memory when forking at the current position", async () => {
|
||||
const tempDir = join(tmpdir(), `pi-runtime-suite-in-memory-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
||||
mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
const faux = registerFauxProvider({
|
||||
models: [
|
||||
{ id: "faux-1", reasoning: true },
|
||||
{ id: "faux-2", reasoning: false },
|
||||
],
|
||||
});
|
||||
faux.setResponses([fauxAssistantMessage("one"), fauxAssistantMessage("two"), fauxAssistantMessage("three")]);
|
||||
|
||||
const authStorage = AuthStorage.inMemory();
|
||||
authStorage.setRuntimeApiKey(faux.getModel().provider, "faux-key");
|
||||
|
||||
const runtimeOptions = {
|
||||
agentDir: tempDir,
|
||||
authStorage,
|
||||
model: faux.getModel(),
|
||||
resourceLoaderOptions: {
|
||||
extensionFactories: [
|
||||
(pi: ExtensionAPI) => {
|
||||
pi.registerProvider(faux.getModel().provider, {
|
||||
baseUrl: faux.getModel().baseUrl,
|
||||
apiKey: "faux-key",
|
||||
api: faux.api,
|
||||
models: faux.models.map((registeredModel) => ({
|
||||
id: registeredModel.id,
|
||||
name: registeredModel.name,
|
||||
api: registeredModel.api,
|
||||
reasoning: registeredModel.reasoning,
|
||||
input: registeredModel.input,
|
||||
cost: registeredModel.cost,
|
||||
contextWindow: registeredModel.contextWindow,
|
||||
maxTokens: registeredModel.maxTokens,
|
||||
})),
|
||||
});
|
||||
},
|
||||
],
|
||||
noSkills: true,
|
||||
noPromptTemplates: true,
|
||||
noThemes: true,
|
||||
},
|
||||
};
|
||||
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
||||
const services = await createAgentSessionServices({
|
||||
...runtimeOptions,
|
||||
cwd,
|
||||
});
|
||||
return {
|
||||
...(await createAgentSessionFromServices({
|
||||
services,
|
||||
sessionManager,
|
||||
sessionStartEvent,
|
||||
model: runtimeOptions.model,
|
||||
})),
|
||||
services,
|
||||
diagnostics: services.diagnostics,
|
||||
};
|
||||
};
|
||||
const runtime = await createAgentSessionRuntime(createRuntime, {
|
||||
cwd: tempDir,
|
||||
agentDir: tempDir,
|
||||
sessionManager: SessionManager.inMemory(tempDir),
|
||||
});
|
||||
await runtime.session.bindExtensions({});
|
||||
cleanups.push(async () => {
|
||||
await runtime.dispose();
|
||||
faux.unregister();
|
||||
if (existsSync(tempDir)) {
|
||||
rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
await runtime.session.prompt("hello");
|
||||
await runtime.session.prompt("again");
|
||||
|
||||
const beforeMessages = runtime.session.messages.map((message) => ({
|
||||
role: message.role,
|
||||
text:
|
||||
message.role === "user"
|
||||
? typeof message.content === "string"
|
||||
? message.content
|
||||
: message.content
|
||||
.filter((part): part is { type: "text"; text: string } => part.type === "text")
|
||||
.map((part) => part.text)
|
||||
.join("")
|
||||
: undefined,
|
||||
}));
|
||||
const leafId = runtime.session.sessionManager.getLeafId();
|
||||
expect(leafId).toBeTruthy();
|
||||
expect(runtime.session.sessionFile).toBeUndefined();
|
||||
|
||||
const result = await runtime.fork(leafId!, { position: "at" });
|
||||
expect(result).toEqual({ cancelled: false, selectedText: undefined });
|
||||
expect(runtime.session.sessionFile).toBeUndefined();
|
||||
expect(
|
||||
runtime.session.messages.map((message) => ({
|
||||
role: message.role,
|
||||
text:
|
||||
message.role === "user"
|
||||
? typeof message.content === "string"
|
||||
? message.content
|
||||
: message.content
|
||||
.filter((part): part is { type: "text"; text: string } => part.type === "text")
|
||||
.map((part) => part.text)
|
||||
.join("")
|
||||
: undefined,
|
||||
})),
|
||||
).toEqual(beforeMessages);
|
||||
});
|
||||
|
||||
it("throws when forking with an invalid entry id", async () => {
|
||||
const { runtime } = await createRuntimeForTest(() => {});
|
||||
await expect(runtime.fork("missing-entry")).rejects.toThrow("Invalid entry ID for forking");
|
||||
|
||||
Reference in New Issue
Block a user