refactor: drop history subcommand memory_history tool, update platform email mapping, adjust tests
This commit is contained in:
@@ -958,9 +958,8 @@ export function registerCliCommands(
|
|||||||
auto_capture: "autoCapture",
|
auto_capture: "autoCapture",
|
||||||
top_k: "topK",
|
top_k: "topK",
|
||||||
mode: "mode",
|
mode: "mode",
|
||||||
// Dot notation (matches Python CLI: platform.api_key, defaults.user_id)
|
|
||||||
"platform.api_key": "apiKey",
|
"platform.api_key": "apiKey",
|
||||||
"platform.base_url": "baseUrl",
|
"platform.email": "userEmail",
|
||||||
"defaults.user_id": "userId",
|
"defaults.user_id": "userId",
|
||||||
"defaults.org_id": "orgId",
|
"defaults.org_id": "orgId",
|
||||||
"defaults.project_id": "projectId",
|
"defaults.project_id": "projectId",
|
||||||
|
|||||||
@@ -34,9 +34,6 @@ Identify which memories need action. Use the tools to investigate.
|
|||||||
**Search for recent additions:**
|
**Search for recent additions:**
|
||||||
Call `memory_search` with a `created_at` filter to find memories added since the last consolidation. These are the most likely to need merging or cleanup.
|
Call `memory_search` with a `created_at` filter to find memories added since the last consolidation. These are the most likely to need merging or cleanup.
|
||||||
|
|
||||||
**Check edit history on suspicious entries:**
|
|
||||||
If a memory looks like it was updated multiple times or has contradictory content, call `memory_history` on it to understand how it evolved.
|
|
||||||
|
|
||||||
**Classify each target into one of these actions:**
|
**Classify each target into one of these actions:**
|
||||||
- DELETE: contains credentials, expired by TTL, pure noise, raw tool output, standalone timestamps
|
- DELETE: contains credentials, expired by TTL, pure noise, raw tool output, standalone timestamps
|
||||||
- MERGE: two or more memories express the same fact in different words, or a series tracks incremental changes to the same entity
|
- MERGE: two or more memories express the same fact in different words, or a series tracks incremental changes to the same entity
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ interface MockCommand {
|
|||||||
_args: Array<{ name: string; desc: string }>;
|
_args: Array<{ name: string; desc: string }>;
|
||||||
command(name: string): MockCommand;
|
command(name: string): MockCommand;
|
||||||
description(desc: string): MockCommand;
|
description(desc: string): MockCommand;
|
||||||
|
configureHelp(opts: any): MockCommand;
|
||||||
|
hook(event: string, fn: (...args: any[]) => any): MockCommand;
|
||||||
option(flags: string, desc: string, defaultVal?: string): MockCommand;
|
option(flags: string, desc: string, defaultVal?: string): MockCommand;
|
||||||
argument(name: string, desc: string): MockCommand;
|
argument(name: string, desc: string): MockCommand;
|
||||||
action(fn: (...args: any[]) => any): MockCommand;
|
action(fn: (...args: any[]) => any): MockCommand;
|
||||||
@@ -80,6 +82,12 @@ function createMockCommand(name: string): MockCommand {
|
|||||||
cmd._description = desc;
|
cmd._description = desc;
|
||||||
return cmd;
|
return cmd;
|
||||||
},
|
},
|
||||||
|
configureHelp(_opts: any) {
|
||||||
|
return cmd;
|
||||||
|
},
|
||||||
|
hook(_event: string, _fn: (...args: any[]) => any) {
|
||||||
|
return cmd;
|
||||||
|
},
|
||||||
option(flags: string, desc: string, defaultVal?: string) {
|
option(flags: string, desc: string, defaultVal?: string) {
|
||||||
cmd._options.push({ flags, desc, defaultVal });
|
cmd._options.push({ flags, desc, defaultVal });
|
||||||
return cmd;
|
return cmd;
|
||||||
@@ -284,7 +292,6 @@ describe("registerCliCommands", () => {
|
|||||||
expect(names).toContain("list");
|
expect(names).toContain("list");
|
||||||
expect(names).toContain("update");
|
expect(names).toContain("update");
|
||||||
expect(names).toContain("delete");
|
expect(names).toContain("delete");
|
||||||
expect(names).toContain("history");
|
|
||||||
expect(names).toContain("status");
|
expect(names).toContain("status");
|
||||||
expect(names).toContain("config");
|
expect(names).toContain("config");
|
||||||
expect(names).toContain("dream");
|
expect(names).toContain("dream");
|
||||||
@@ -321,7 +328,6 @@ describe("registerCliCommands", () => {
|
|||||||
expect(writePluginAuth).toHaveBeenCalledWith(
|
expect(writePluginAuth).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
apiKey: "m0-my-key-1234",
|
apiKey: "m0-my-key-1234",
|
||||||
baseUrl: "https://api.mem0.ai",
|
|
||||||
mode: "platform",
|
mode: "platform",
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -857,48 +863,6 @@ describe("registerCliCommands", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// history subcommand
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
describe("history subcommand", () => {
|
|
||||||
it("calls provider.history and prints entries as JSON", async () => {
|
|
||||||
const { mem0, provider } = setup();
|
|
||||||
const historyCmd = findCommand(mem0, "history")!;
|
|
||||||
|
|
||||||
await historyCmd._action!("m1");
|
|
||||||
|
|
||||||
expect(provider.history).toHaveBeenCalledWith("m1");
|
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
|
||||||
expect.stringContaining("UPDATE"),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("prints message when no history found", async () => {
|
|
||||||
const { mem0, provider } = setup();
|
|
||||||
provider.history.mockResolvedValueOnce([]);
|
|
||||||
const historyCmd = findCommand(mem0, "history")!;
|
|
||||||
|
|
||||||
await historyCmd._action!("m1");
|
|
||||||
|
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith(
|
|
||||||
"No history found for this memory.",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles history errors gracefully", async () => {
|
|
||||||
const { mem0, provider } = setup();
|
|
||||||
provider.history.mockRejectedValueOnce(new Error("history boom"));
|
|
||||||
const historyCmd = findCommand(mem0, "history")!;
|
|
||||||
|
|
||||||
await historyCmd._action!("m1");
|
|
||||||
|
|
||||||
expect(consoleSpy.error).toHaveBeenCalledWith(
|
|
||||||
expect.stringContaining("History failed"),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// status subcommand
|
// status subcommand
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@@ -965,7 +929,7 @@ describe("registerCliCommands", () => {
|
|||||||
expect(allOutput).toContain("Key");
|
expect(allOutput).toContain("Key");
|
||||||
expect(allOutput).toContain("Value");
|
expect(allOutput).toContain("Value");
|
||||||
expect(allOutput).toContain("platform.api_key");
|
expect(allOutput).toContain("platform.api_key");
|
||||||
expect(allOutput).toContain("platform.base_url");
|
expect(allOutput).toContain("platform.email");
|
||||||
expect(allOutput).toContain("defaults.user_id");
|
expect(allOutput).toContain("defaults.user_id");
|
||||||
expect(allOutput).toContain("Config file:");
|
expect(allOutput).toContain("Config file:");
|
||||||
});
|
});
|
||||||
@@ -1031,9 +995,9 @@ describe("registerCliCommands", () => {
|
|||||||
const configCmd = findCommand(mem0, "config")!;
|
const configCmd = findCommand(mem0, "config")!;
|
||||||
const getCmd = findCommand(configCmd, "get")!;
|
const getCmd = findCommand(configCmd, "get")!;
|
||||||
|
|
||||||
getCmd._action!("platform.base_url");
|
getCmd._action!("platform.email");
|
||||||
|
|
||||||
expect(consoleSpy.log).toHaveBeenCalledWith("https://api.mem0.ai");
|
expect(consoleSpy.log).toHaveBeenCalledWith("(not set)");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { createMemoryGetTool } from "../tools/memory-get.ts";
|
|||||||
import { createMemoryDeleteTool } from "../tools/memory-delete.ts";
|
import { createMemoryDeleteTool } from "../tools/memory-delete.ts";
|
||||||
import { createMemoryListTool } from "../tools/memory-list.ts";
|
import { createMemoryListTool } from "../tools/memory-list.ts";
|
||||||
import { createMemoryUpdateTool } from "../tools/memory-update.ts";
|
import { createMemoryUpdateTool } from "../tools/memory-update.ts";
|
||||||
import { createMemoryHistoryTool } from "../tools/memory-history.ts";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mock helper
|
// Mock helper
|
||||||
@@ -80,10 +79,10 @@ function createMockToolDeps(overrides = {}): ToolDeps {
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
describe("registerAllTools", () => {
|
describe("registerAllTools", () => {
|
||||||
it("calls api.registerTool exactly 7 times", () => {
|
it("calls api.registerTool exactly 6 times", () => {
|
||||||
const ctx = createMockToolDeps();
|
const ctx = createMockToolDeps();
|
||||||
registerAllTools(ctx);
|
registerAllTools(ctx);
|
||||||
expect(ctx.api.registerTool).toHaveBeenCalledTimes(7);
|
expect(ctx.api.registerTool).toHaveBeenCalledTimes(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("registers tools with the correct names", () => {
|
it("registers tools with the correct names", () => {
|
||||||
@@ -103,7 +102,6 @@ describe("registerAllTools", () => {
|
|||||||
"memory_list",
|
"memory_list",
|
||||||
"memory_update",
|
"memory_update",
|
||||||
"memory_delete",
|
"memory_delete",
|
||||||
"memory_history",
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -883,115 +881,3 @@ describe("memory_update execute", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// memory_history execute
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
describe("memory_history execute", () => {
|
|
||||||
it("returns formatted history", async () => {
|
|
||||||
const historyEntries = [
|
|
||||||
{
|
|
||||||
event: "ADD",
|
|
||||||
created_at: "2026-01-01T00:00:00Z",
|
|
||||||
old_memory: null,
|
|
||||||
new_memory: "Initial memory text",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
event: "UPDATE",
|
|
||||||
created_at: "2026-01-02T00:00:00Z",
|
|
||||||
old_memory: "Initial memory text",
|
|
||||||
new_memory: "Updated memory text",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const ctx = createMockToolDeps({
|
|
||||||
provider: {
|
|
||||||
search: vi.fn(),
|
|
||||||
add: vi.fn(),
|
|
||||||
getAll: vi.fn(),
|
|
||||||
update: vi.fn(),
|
|
||||||
delete: vi.fn(),
|
|
||||||
get: vi.fn(),
|
|
||||||
history: vi.fn().mockResolvedValue(historyEntries),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const tool = createMemoryHistoryTool(ctx);
|
|
||||||
|
|
||||||
const result = await tool.execute("call-1", { memoryId: "mem-abc" });
|
|
||||||
|
|
||||||
expect(ctx.provider!.history).toHaveBeenCalledWith("mem-abc");
|
|
||||||
expect(result.content[0].text).toContain("History for memory mem-abc");
|
|
||||||
expect(result.content[0].text).toContain("2 entries");
|
|
||||||
expect(result.content[0].text).toContain("[ADD]");
|
|
||||||
expect(result.content[0].text).toContain("[UPDATE]");
|
|
||||||
expect(result.content[0].text).toContain("Initial memory text");
|
|
||||||
expect(result.content[0].text).toContain("Updated memory text");
|
|
||||||
expect(result.content[0].text).toContain("Old: (none)");
|
|
||||||
expect(result.details.count).toBe(2);
|
|
||||||
expect(result.details.history).toEqual(historyEntries);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles empty history", async () => {
|
|
||||||
const ctx = createMockToolDeps({
|
|
||||||
provider: {
|
|
||||||
search: vi.fn(),
|
|
||||||
add: vi.fn(),
|
|
||||||
getAll: vi.fn(),
|
|
||||||
update: vi.fn(),
|
|
||||||
delete: vi.fn(),
|
|
||||||
get: vi.fn(),
|
|
||||||
history: vi.fn().mockResolvedValue([]),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const tool = createMemoryHistoryTool(ctx);
|
|
||||||
|
|
||||||
const result = await tool.execute("call-2", { memoryId: "mem-empty" });
|
|
||||||
|
|
||||||
expect(result.content[0].text).toBe(
|
|
||||||
"No history found for memory mem-empty.",
|
|
||||||
);
|
|
||||||
expect(result.details.count).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles null history", async () => {
|
|
||||||
const ctx = createMockToolDeps({
|
|
||||||
provider: {
|
|
||||||
search: vi.fn(),
|
|
||||||
add: vi.fn(),
|
|
||||||
getAll: vi.fn(),
|
|
||||||
update: vi.fn(),
|
|
||||||
delete: vi.fn(),
|
|
||||||
get: vi.fn(),
|
|
||||||
history: vi.fn().mockResolvedValue(null),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const tool = createMemoryHistoryTool(ctx);
|
|
||||||
|
|
||||||
const result = await tool.execute("call-3", { memoryId: "mem-null" });
|
|
||||||
|
|
||||||
expect(result.content[0].text).toBe(
|
|
||||||
"No history found for memory mem-null.",
|
|
||||||
);
|
|
||||||
expect(result.details.count).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("handles errors gracefully", async () => {
|
|
||||||
const ctx = createMockToolDeps({
|
|
||||||
provider: {
|
|
||||||
search: vi.fn(),
|
|
||||||
add: vi.fn(),
|
|
||||||
getAll: vi.fn(),
|
|
||||||
update: vi.fn(),
|
|
||||||
delete: vi.fn(),
|
|
||||||
get: vi.fn(),
|
|
||||||
history: vi.fn().mockRejectedValue(new Error("history unavailable")),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const tool = createMemoryHistoryTool(ctx);
|
|
||||||
|
|
||||||
const result = await tool.execute("call-4", { memoryId: "mem-err" });
|
|
||||||
|
|
||||||
expect(result.content[0].text).toContain("Memory history failed");
|
|
||||||
expect(result.content[0].text).toContain("history unavailable");
|
|
||||||
expect(result.details.error).toContain("history unavailable");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user