From 7efd850645686bfcd3581ad25f741bc700e35bd6 Mon Sep 17 00:00:00 2001 From: "jinli.yl" Date: Wed, 26 Aug 2026 19:05:31 +0800 Subject: [PATCH] fix(dsh): deduplicate pending memory guidance --- packages/typescript/src/dsh/guidance.ts | 30 +++++++++++++------- packages/typescript/src/dsh/index.ts | 6 +++- packages/typescript/tests/dsh-index.test.mjs | 7 +++++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/packages/typescript/src/dsh/guidance.ts b/packages/typescript/src/dsh/guidance.ts index 0ab5f678..4065be0c 100644 --- a/packages/typescript/src/dsh/guidance.ts +++ b/packages/typescript/src/dsh/guidance.ts @@ -25,17 +25,25 @@ export function memoryGuidance(language: "en" | "zh" = "en"): string { return GUIDANCE[language]; } -export function hasGuidance(session: DshSession): boolean { - return (session.events || []).some((event) => { - const source = isRecord(event.data) ? event.data.source : undefined; - return ( - event.type === "user/message" && - isRecord(source) && - source.kind === "plugin" && - source.plugin === REME_PLUGIN_SOURCE && - source.form === "instructions" - ); - }); +export function hasGuidance( + session: DshSession, + pendingMessages: readonly unknown[] = [], +): boolean { + return ( + (session.events || []).some( + (event) => event.type === "user/message" && isGuidance(event.data), + ) || pendingMessages.some(isGuidance) + ); +} + +function isGuidance(value: unknown): boolean { + const source = isRecord(value) ? value.source : undefined; + return ( + isRecord(source) && + source.kind === "plugin" && + source.plugin === REME_PLUGIN_SOURCE && + source.form === "instructions" + ); } function isRecord(value: unknown): value is Record { diff --git a/packages/typescript/src/dsh/index.ts b/packages/typescript/src/dsh/index.ts index cf59d6c2..6a08fb34 100644 --- a/packages/typescript/src/dsh/index.ts +++ b/packages/typescript/src/dsh/index.ts @@ -61,7 +61,11 @@ export function apply(ctx: Context, input: ReMeConfigInput = {}): void { () => () => runtime.dispose(agent.session), "remeMemory.disposeSession()", ); - if (agent.status !== "idle" || hasGuidance(agent.session)) return; + if ( + agent.status !== "idle" || + hasGuidance(agent.session, agent.inbox.nextStep) + ) + return; agent.inject( createUserMessage({ content: [{ type: "text", text: memoryGuidance(config.language) }], diff --git a/packages/typescript/tests/dsh-index.test.mjs b/packages/typescript/tests/dsh-index.test.mjs index 58e99929..9e49f885 100644 --- a/packages/typescript/tests/dsh-index.test.mjs +++ b/packages/typescript/tests/dsh-index.test.mjs @@ -42,11 +42,14 @@ test("composes root-agent guidance and reme_search on supported DSH releases", a const injected = []; const agentCleanups = []; + const nextStep = []; const agent = { status: "idle", session: { id: "root", header: {}, events: [] }, + inbox: { nextStep }, inject(message) { injected.push(message); + nextStep.push(message); }, ctx: { effect(execute) { @@ -62,6 +65,9 @@ test("composes root-agent guidance and reme_search on supported DSH releases", a assert.equal(injected[0].source.plugin, "reme-memory"); assert.match(injected[0].content[0].text, /长期记忆/); + handlers.get("agent/session-start")({ agent, source: "resume" }); + assert.equal(injected.length, 1); + await Promise.all(agentCleanups.map((cleanup) => cleanup())); await Promise.all(cleanups.map((cleanup) => cleanup())); }); @@ -162,6 +168,7 @@ test("registers a ReMe settings namespace and reads changed values for new sessi agent: { status: "idle", session: { id: "settings-session", header: {}, events: [] }, + inbox: { nextStep: [] }, inject(message) { injected.push(message); },