feat(plugins): share native session handoff and soften memory search
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import {afterEach, describe, expect, mock, test} from "bun:test";
|
||||
const shared = await import("../agent-plugin-core/typescript/src/handoff.ts");
|
||||
const run = mock(async (_script: URL, _bundle: unknown) => "Created Codex task");
|
||||
mock.module("../agent-plugin-core/typescript/src/handoff.ts", () => ({...shared, runHandoff: run}));
|
||||
const {createHandoffTool, activeMessages, registerHandoffCommand} = await import("./handoff");
|
||||
afterEach(() => {run.mockReset(); run.mockResolvedValue("Created Codex task");});
|
||||
const user = (id: string, text: string) => ({info: {role: "user", id}, parts: [{type: "text", text}]});
|
||||
const toolMessage = (id: string, tool = "mem0_handoff") => ({info: {role: "assistant", id, time: {completed: 1}}, parts: [{type: "tool", callID: id, tool, state: {status: "running", input: {}}}]});
|
||||
function tool(messages: any[]) {
|
||||
return createHandoffTool({session: {
|
||||
get: async () => ({data: {id: "native", title: "Native task", directory: "/tmp"}}),
|
||||
messages: async () => ({data: messages}),
|
||||
}} as any);
|
||||
}
|
||||
const context = {sessionID: "native", messageID: "handoff"} as any;
|
||||
describe("native OpenCode handoff", () => {
|
||||
test("exports current full context and skips only its own invocation", async () => {
|
||||
const config: any = {};
|
||||
registerHandoffCommand(config);
|
||||
expect(config.command["mem0-handoff"].template).toContain("$ARGUMENTS");
|
||||
expect(await tool([user("u", "x".repeat(20000)), toolMessage("handoff")]).execute({}, context)).toBe("Created Codex task");
|
||||
const bundle = run.mock.calls[0][1] as any;
|
||||
expect(bundle.source.session_id).toBe("native");
|
||||
expect(bundle.items[0].content[0].text.length).toBe(20000);
|
||||
expect(bundle.items).toHaveLength(1);
|
||||
});
|
||||
test("preserves completed tools and refuses any other unfinished tool", async () => {
|
||||
const completed = {info: {role: "assistant", id: "done", time: {completed: 1}}, parts: [{type: "tool", callID: "done", tool: "read", state: {status: "completed", input: {path: "a"}, output: "full result", time: {}}}]};
|
||||
await tool([user("u", "task"), completed, toolMessage("handoff")]).execute({}, context);
|
||||
expect(JSON.stringify(run.mock.calls[0][1])).toContain("full result");
|
||||
await expect(tool([user("u", "task"), toolMessage("other", "read"), toolMessage("handoff")]).execute({}, context)).rejects.toThrow("unfinished");
|
||||
});
|
||||
test("refuses a different unfinished assistant stream", async () => {
|
||||
await expect(tool([user("u", "task"), {info: {role: "assistant", id: "stream", time: {}}, parts: [{type: "text", text: "partial"}]}]).execute({}, context)).rejects.toThrow("unfinished assistant");
|
||||
});
|
||||
test("rejects a failed assistant response even with a completed timestamp", async () => {
|
||||
await expect(tool([user("u", "task"), {
|
||||
info: {role: "assistant", id: "failed", time: {completed: 1}, error: {name: "MessageAbortedError", data: {message: "aborted"}}},
|
||||
parts: [{type: "text", text: "partial answer"}],
|
||||
}]).execute({}, context)).rejects.toThrow("interrupted or failed");
|
||||
});
|
||||
test("native compaction retains its summary and retained tail, excluding older history", () => {
|
||||
const old = user("old", "old context");
|
||||
const tail = user("tail", "retained");
|
||||
const boundary = {info: {role: "user", id: "c"}, parts: [{type: "compaction", tail_start_id: "tail"}]};
|
||||
const summary = {info: {role: "assistant", id: "s", parentID: "c", summary: true, finish: "stop"}, parts: [{type: "text", text: "Summary"}]};
|
||||
expect(activeMessages([old, tail, boundary, summary, user("new", "continue")] as any).map(m => m.info.id)).toEqual(["c", "s", "tail", "new"]);
|
||||
});
|
||||
test("fails on unsupported remote media and propagates importer errors", async () => {
|
||||
const image = {info: {role: "user", id: "u"}, parts: [{type: "file", mime: "image/png", url: "https://private/image.png"}]};
|
||||
await expect(tool([image]).execute({}, context)).rejects.toThrow("unavailable locally");
|
||||
run.mockRejectedValue(new Error("saved at /tmp/retry.json"));
|
||||
await expect(tool([user("u", "hi")]).execute({}, context)).rejects.toThrow("saved at /tmp/retry.json");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user