feat(sdk): add delete_linked option to MemoryClient.delete (Python + TS)

Plumbs the platform v3 `delete_linked` flag through the hosted MemoryClient in
both SDKs. When true, DELETE /v1/memories/{id}/ also removes the older memories
the target superseded (the linked_memory_ids chain) — the delete-side counterpart
of `latest_only`, so a superseded memory doesn't resurface after the current one
is deleted.

- Python: MemoryClient.delete / AsyncMemoryClient.delete gain `delete_linked: bool = False`
  forwarded as a query param (omitted when False — default byte-identical).
- TypeScript: client.delete accepts `{ deleteLinked?: boolean }`, serialized to
  `delete_linked` via the existing camelToSnakeKeys path (matches latestOnly).
- Unit tests for both: default omits the param; opt-in forwards it.

Client-side passthrough only; no changes to the OSS memory engine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chaithanyak42
2026-05-27 21:33:58 +05:30
co-authored by Claude Opus 4.7
parent 88934304c6
commit 46e4b91a06
5 changed files with 96 additions and 12 deletions
+10 -3
View File
@@ -9,6 +9,7 @@ import {
SearchMemoryOptions,
GetAllMemoryOptions,
DeleteAllMemoryOptions,
DeleteMemoryOptions,
MemoryUpdateBody,
ProjectResponse,
PromptUpdatePayload,
@@ -377,11 +378,17 @@ export default class MemoryClient {
return response;
}
async delete(memoryId: string): Promise<{ message: string }> {
async delete(
memoryId: string,
options: DeleteMemoryOptions = {},
): Promise<{ message: string }> {
if (this.telemetryId === "") await this.ping();
this._captureEvent("delete", []);
this._captureEvent("delete", [Object.keys(options || {})]);
const snakeOptions = camelToSnakeKeys(this._prepareParams(options));
// @ts-ignore
const query = new URLSearchParams(snakeOptions).toString();
return this._fetchWithErrorHandling(
`${this.host}/v1/memories/${memoryId}/`,
`${this.host}/v1/memories/${memoryId}/${query ? `?${query}` : ""}`,
{
method: "DELETE",
headers: this.headers,
+8
View File
@@ -39,6 +39,14 @@ export interface GetAllMemoryOptions {
export interface DeleteAllMemoryOptions extends EntityOptions {}
export interface DeleteMemoryOptions {
// When true, also delete the older memories this one superseded
// (the v3 linked_memory_ids chain), transitively. The delete-side
// counterpart of latestOnly — stops a superseded memory from resurfacing
// after the current one is deleted. Serialized as `delete_linked`.
deleteLinked?: boolean;
}
// ─── Project Options ────────────────────────────────────────
export interface ProjectOptions {
fields?: string[];
@@ -200,9 +200,26 @@ describe("MemoryClient - delete()", () => {
const client = new MemoryClient({ apiKey: TEST_API_KEY });
await client.delete("mem_123");
expect(
findFetchCall(mock, "/v1/memories/mem_123/", "DELETE"),
).toBeDefined();
const call = findFetchCall(mock, "/v1/memories/mem_123/", "DELETE");
expect(call).toBeDefined();
// Default: no cascade query param, URL byte-identical to before.
expect(call![0]).not.toContain("delete_linked");
});
test("serializes deleteLinked as delete_linked query param", async () => {
const extra = new Map<string, { status: number; body: unknown }>();
extra.set("/v1/memories/mem_123/", {
status: 200,
body: { message: "Memory deleted successfully", cascade_count: 1 },
});
const mock = setupMockFetch(extra);
const client = new MemoryClient({ apiKey: TEST_API_KEY });
await client.delete("mem_123", { deleteLinked: true });
const call = findFetchCall(mock, "/v1/memories/mem_123/", "DELETE");
expect(call).toBeDefined();
expect(call![0]).toContain("delete_linked=true");
});
});