From 5e0b78552ef95bdd2ee0aa10f9194588b6e81abf Mon Sep 17 00:00:00 2001 From: Daoji Wang <627665797@qq.com> Date: Wed, 30 Sep 2026 11:10:47 +0800 Subject: [PATCH 1/4] fix(dsh): use the current recall message source and assistant event DeepSeek Harness no longer accepts a generic plugin message source, and completed assistant turns are recorded as assistant/message. Co-authored-by: Cursor --- .../skill-writer/deepseek-harness/tests/target.test.ts | 2 +- .../skill-writer/templates/memmy-deepseek-harness-plugin.ts | 2 +- Memory/adapters/dsh/index.js | 6 +++--- .../integration/templates/memmy-deepseek-harness-plugin.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts b/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts index 068bdef41..2b6b62e43 100644 --- a/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts +++ b/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts @@ -361,7 +361,7 @@ describe("DeepSeek Harness skill target", () => { ) as { messages: Array<{ source: { kind: string }; content: Array<{ text: string }> }> }; expect(decision.messages[0]).toBe(userMessage); - expect(decision.messages[1]?.source.kind).toBe("plugin"); + expect(decision.messages[1]?.source.kind).toBe("memmy-memory"); expect(decision.messages[1]?.content[0]?.text).toContain("User prefers concise answers."); expect(decision.messages[1]?.content[0]?.text).toContain("\n检查 README"); expect(decision.messages[2]).toBe(runtimeContext); diff --git a/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts b/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts index f0f71d635..3357fb471 100644 --- a/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts +++ b/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts @@ -76,7 +76,7 @@ export function apply(ctx, config = {}) { pendingL3.delete(String(payload.agent.session.id)); if (!markdown && !l3) return decision; const memory = createUserMessage({ - source: { kind: "plugin", plugin: name, form: "recall" }, + source: { kind: "memmy-memory", form: "recall" }, content: [{ type: "text", text: [l3, markdown ? renderMemoryPacket(markdown, "turn_start", query) : ""].filter(Boolean).join("\n\n") }] }); return { ...decision, messages: insertAfterUserMessage(decision.messages, memory) }; diff --git a/Memory/adapters/dsh/index.js b/Memory/adapters/dsh/index.js index 8a07a268b..15931e4fa 100644 --- a/Memory/adapters/dsh/index.js +++ b/Memory/adapters/dsh/index.js @@ -60,17 +60,17 @@ export async function apply(ctx, config) { if (query) { const started = await request("/turns/start", profileId, { sessionId, query }, config.recallTimeoutMs); turns.set(String(agent?.id || "default"), { sessionId, query, turnId: started.turnId }); - if (started.injectedContext && Array.isArray(payload?.messages)) payload.messages.push({ role: "user", content: [{ type: "text", text: started.injectedContext }], source: { kind: "plugin", plugin: name, form: "recall" } }); + if (started.injectedContext && Array.isArray(payload?.messages)) payload.messages.push({ role: "user", content: [{ type: "text", text: started.injectedContext }], source: { kind: "memmy-memory", form: "recall" } }); } } catch (error) { ctx.logger.warn(`memmy-memory recall unavailable: ${String(error)}`); } return next(); })); disposers.push(ctx.on("session/event", (session, event) => { - if (!config.captureEnabled || event?.type !== "assistant") return; + if (!config.captureEnabled || event?.type !== "assistant/message") return; const active = turns.get(String(session?.id || "default")); if (!active) return; turns.delete(String(session?.id || "default")); - const answer = String(event?.message?.content?.map?.((part) => part.text || "").join("\n") || event?.content || ""); + const answer = String(event?.data?.message?.content?.map?.((part) => part.text || "").join("\n") || ""); void request(`/turns/${encodeURIComponent(active.turnId)}/complete`, profileId, { sessionId: active.sessionId, query: active.query, answer, status: "succeeded" }, 10000).catch(() => undefined); })); disposers.push(ctx.on("session/disposed", (session) => { const key = String(session?.id || "default"); const id = sessions.get(key); sessions.delete(key); if (id) void request(`/sessions/${encodeURIComponent(id)}/close`, profileId, {}).catch(() => undefined); })); diff --git a/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts b/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts index f0f71d635..3357fb471 100644 --- a/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts +++ b/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts @@ -76,7 +76,7 @@ export function apply(ctx, config = {}) { pendingL3.delete(String(payload.agent.session.id)); if (!markdown && !l3) return decision; const memory = createUserMessage({ - source: { kind: "plugin", plugin: name, form: "recall" }, + source: { kind: "memmy-memory", form: "recall" }, content: [{ type: "text", text: [l3, markdown ? renderMemoryPacket(markdown, "turn_start", query) : ""].filter(Boolean).join("\n\n") }] }); return { ...decision, messages: insertAfterUserMessage(decision.messages, memory) }; From 5b4727865a95091466a3e932e6c8ea374fc50af0 Mon Sep 17 00:00:00 2001 From: jiachengzhen Date: Wed, 30 Sep 2026 12:47:11 +0800 Subject: [PATCH 2/4] chore(release): prepare v1.2.0 metadata --- App/backend/src/project-version.ts | 2 +- App/backend/src/tests/project-version.test.ts | 2 +- App/memmy-agent/package-lock.json | 4 +- App/memmy-agent/package.json | 2 +- App/shell/desktop/package.json | 2 +- .../tests/deepseek-harness-template.test.ts | 13 ++++++ package-lock.json | 6 +-- package.json | 2 +- tests/smoke/memory-layer-smoke-plan.test.ts | 42 ++++++++++++++++++- 9 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 Memory/tests/deepseek-harness-template.test.ts diff --git a/App/backend/src/project-version.ts b/App/backend/src/project-version.ts index 6a80273d9..fe9edffc1 100644 --- a/App/backend/src/project-version.ts +++ b/App/backend/src/project-version.ts @@ -1,2 +1,2 @@ /** Generated from the root package.json by scripts/sync-project-version.mjs. */ -export const MEMMY_VERSION = "1.1.9"; +export const MEMMY_VERSION = "1.2.0"; diff --git a/App/backend/src/tests/project-version.test.ts b/App/backend/src/tests/project-version.test.ts index f8111c08d..b3859a646 100644 --- a/App/backend/src/tests/project-version.test.ts +++ b/App/backend/src/tests/project-version.test.ts @@ -11,7 +11,7 @@ describe("project version", () => { ); expect(MEMMY_VERSION).toBe(rootManifest.version); - expect(MEMMY_VERSION).toBe("1.1.9"); + expect(MEMMY_VERSION).toBe("1.2.0"); expect(MEMMY_VERSION).toMatch(/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/); expect(readFileSync(resolve(repoRoot, "App/backend/src/project-version.ts"), "utf8")) .toContain(`MEMMY_VERSION = ${JSON.stringify(rootManifest.version)}`); diff --git a/App/memmy-agent/package-lock.json b/App/memmy-agent/package-lock.json index d58a3c307..817de4d9a 100644 --- a/App/memmy-agent/package-lock.json +++ b/App/memmy-agent/package-lock.json @@ -1,12 +1,12 @@ { "name": "memmy-agent", - "version": "1.1.9", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "memmy-agent", - "version": "1.1.9", + "version": "1.2.0", "dependencies": { "@anthropic-ai/sdk": "^0.100.1", "@aws-sdk/client-bedrock-runtime": "^3.1061.0", diff --git a/App/memmy-agent/package.json b/App/memmy-agent/package.json index a2bd5e906..78ec3cfaf 100644 --- a/App/memmy-agent/package.json +++ b/App/memmy-agent/package.json @@ -1,6 +1,6 @@ { "name": "memmy-agent", - "version": "1.1.9", + "version": "1.2.0", "description": "TypeScript refactor of memmy's agent runtime.", "type": "module", "main": "./dist/index.js", diff --git a/App/shell/desktop/package.json b/App/shell/desktop/package.json index 7f4092303..809bf448d 100644 --- a/App/shell/desktop/package.json +++ b/App/shell/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@memmy/desktop", - "version": "1.1.9", + "version": "1.2.0", "private": true, "type": "module", "description": "Memmy desktop client.", diff --git a/Memory/tests/deepseek-harness-template.test.ts b/Memory/tests/deepseek-harness-template.test.ts new file mode 100644 index 000000000..a48598e1f --- /dev/null +++ b/Memory/tests/deepseek-harness-template.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from "vitest"; +import { DEEPSEEK_HARNESS_PLUGIN_INDEX } from "../src/agent-source/integration/templates/memmy-deepseek-harness-plugin.js"; + +describe("DeepSeek Harness plugin template", () => { + it("marks recalled context as Memmy memory", () => { + expect(DEEPSEEK_HARNESS_PLUGIN_INDEX).toContain( + 'source: { kind: "memmy-memory", form: "recall" }' + ); + expect(DEEPSEEK_HARNESS_PLUGIN_INDEX).not.toContain( + 'source: { kind: "plugin", plugin: name, form: "recall" }' + ); + }); +}); diff --git a/package-lock.json b/package-lock.json index 22e36c01b..e1b078070 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "memmy-agent", - "version": "1.1.9", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "memmy-agent", - "version": "1.1.9", + "version": "1.2.0", "workspaces": [ "Migrations", "AgentSourceCore", @@ -588,7 +588,7 @@ }, "App/shell/desktop": { "name": "@memmy/desktop", - "version": "1.1.9", + "version": "1.2.0", "dependencies": { "@memmy/backend": "0.0.0", "@memmy/desktop-interface": "0.0.0", diff --git a/package.json b/package.json index 5f5917e60..6a7814a8e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "memmy-agent", - "version": "1.1.9", + "version": "1.2.0", "private": true, "type": "module", "description": "Local-first agent memory substrate with desktop and CLI surfaces.", diff --git a/tests/smoke/memory-layer-smoke-plan.test.ts b/tests/smoke/memory-layer-smoke-plan.test.ts index 1dcd07e07..c79182ad5 100644 --- a/tests/smoke/memory-layer-smoke-plan.test.ts +++ b/tests/smoke/memory-layer-smoke-plan.test.ts @@ -9,7 +9,8 @@ import { DEFAULT_MEMMY_CONFIG, MemoryDb, MemoryService, - type Embedder + type Embedder, + type LlmClient } from "../../Memory/src/index.js"; const tempRoots: string[] = []; @@ -45,6 +46,7 @@ describe("memory layer smoke plan", () => { db, mode: "dev", config: DEFAULT_MEMMY_CONFIG, + llm: createSmokeSummaryLlm(), embedder: createSmokeEmbedder(), fetchAppMemoryBudget: async () => null }); @@ -200,6 +202,44 @@ function createSmokeEmbedder(): Embedder { }; } +function createSmokeSummaryLlm(): LlmClient { + return { + config: { + ...DEFAULT_MEMMY_CONFIG.summary, + provider: "host", + endpoint: "http://127.0.0.1/summary", + model: "smoke-summary" + }, + isConfigured: () => true, + async complete() { + return JSON.stringify({ title: "Smoke turn", summary: "Verified smoke summary" }); + }, + async completeJson>(messages) { + const system = messages + .filter((message) => message.role === "system") + .map((message) => message.content) + .join("\n"); + if (system.includes("Judge L1 and User Memory independently")) { + return { + l1: { + title: "Release verification", + summary: "Verified release contracts and attachments.", + evidence: [{ quote: "release workflow", role: "user", kind: "task_request" }] + }, + user: null + } as unknown as T; + } + return { title: "Smoke turn", summary: "Verified smoke summary" } as unknown as T; + }, + status: () => ({ + provider: "host", + model: "smoke-summary", + configured: true, + remote: true + }) + }; +} + function captureFetch(requests: CapturedRequest[], responseBody: unknown): typeof fetch { return (async (input: string | URL | Request, init?: RequestInit) => { const rawUrl = input instanceof Request ? input.url : String(input); From 64c2471f00a139da88fd5aa35cead7b9b203de82 Mon Sep 17 00:00:00 2001 From: jiachengzhen Date: Wed, 30 Sep 2026 14:45:17 +0800 Subject: [PATCH 3/4] docs(release): add v1.2.0 release notes --- .github/release-notes/v1.2.0.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/release-notes/v1.2.0.md diff --git a/.github/release-notes/v1.2.0.md b/.github/release-notes/v1.2.0.md new file mode 100644 index 000000000..96416d5f9 --- /dev/null +++ b/.github/release-notes/v1.2.0.md @@ -0,0 +1,17 @@ +# Memmy v1.2.0 + +## Highlights + +- Updated the DeepSeek Harness integration to mark recalled context with the current `memmy-memory` source and consume completed `assistant/message` events. +- Synchronized Memmy Desktop and Agent release metadata to version `1.2.0`. + +## Agent and Desktop improvements + +- Kept DeepSeek Harness recall and completed-turn capture aligned with the current event and message schemas. +- Added regression coverage for the generated DeepSeek Harness template and release version metadata. + +## Upgrade notes + +- Memmy Desktop and Agent are version `1.2.0`. +- The bundled Memory service, viewer, and CLI remain independently versioned. +- Memory protocol version remains `1`. From eb5b725c98688ba73908ff9c12ec7cdfa65d2db3 Mon Sep 17 00:00:00 2001 From: Daoji Wang <627665797@qq.com> Date: Wed, 30 Sep 2026 16:33:18 +0800 Subject: [PATCH 4/4] fix(dsh): give the memory plugin a package version DeepSeek Harness rejects official model requests when an enabled plugin manifest has no version. Co-authored-by: Cursor --- .../outbound/skill-writer/deepseek-harness/tests/target.test.ts | 2 +- .../skill-writer/templates/memmy-deepseek-harness-plugin.ts | 1 + .../integration/templates/memmy-deepseek-harness-plugin.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts b/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts index 2b6b62e43..54f5e3561 100644 --- a/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts +++ b/App/backend/src/adapters/outbound/skill-writer/deepseek-harness/tests/target.test.ts @@ -44,6 +44,7 @@ describe("DeepSeek Harness skill target", () => { const packageManifest = JSON.parse(readFileSync(packagePath, "utf8")) as Record; expect(packageManifest).toMatchObject({ name: "@memmy/memmy-memory", + version: "1.0.0", type: "module", exports: { ".": "./index.mjs", @@ -51,7 +52,6 @@ describe("DeepSeek Harness skill target", () => { }, dsh: { client: { platform: "web" } } }); - expect(packageManifest).not.toHaveProperty("version"); expect(readFileSync(skillPath, "utf8")).toContain('memmy-memory search "query text" --source deepseek_harness'); expect(readFileSync(resumeSkillPath, "utf8")).toContain("--source deepseek_harness"); expect(patch).toContain("id: user-plugin"); diff --git a/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts b/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts index 3357fb471..22f003bdc 100644 --- a/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts +++ b/App/backend/src/adapters/outbound/skill-writer/templates/memmy-deepseek-harness-plugin.ts @@ -653,6 +653,7 @@ export const DEEPSEEK_HARNESS_PLUGIN_CLIENT = String.raw`window.__ModuleLoader__ export function createDeepseekHarnessPluginPackageManifest(): Record { return { name: "@memmy/memmy-memory", + version: "1.0.0", private: true, type: "module", exports: { diff --git a/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts b/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts index 3357fb471..22f003bdc 100644 --- a/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts +++ b/Memory/src/agent-source/integration/templates/memmy-deepseek-harness-plugin.ts @@ -653,6 +653,7 @@ export const DEEPSEEK_HARNESS_PLUGIN_CLIENT = String.raw`window.__ModuleLoader__ export function createDeepseekHarnessPluginPackageManifest(): Record { return { name: "@memmy/memmy-memory", + version: "1.0.0", private: true, type: "module", exports: {