From ed2dfee7d79e775647a47ab27a911f76b74a0d08 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Mon, 22 Jun 2026 14:26:37 -0700 Subject: [PATCH] feat(qa): expose active memory toggles to scenarios (#95858) --- .../qa-lab/src/scenario-runtime-api.test.ts | 2 + extensions/qa-lab/src/scenario-runtime-api.ts | 6 +++ extensions/qa-lab/src/suite-runtime-flow.ts | 38 ++++++++++++++++++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/extensions/qa-lab/src/scenario-runtime-api.test.ts b/extensions/qa-lab/src/scenario-runtime-api.test.ts index eb5a30cb1795..5444bc57a501 100644 --- a/extensions/qa-lab/src/scenario-runtime-api.test.ts +++ b/extensions/qa-lab/src/scenario-runtime-api.test.ts @@ -74,6 +74,8 @@ function createDeps(overrides?: Partial): QaScenarioRunti extractQaToolPayload: fn, formatMemoryDreamingDay: fn, resolveSessionTranscriptsDirForAgent: fn, + activeMemoryToggleKey: fn, + setActiveMemorySessionDisabled: fn, buildAgentSessionKey: fn, normalizeLowercaseStringOrEmpty: fn, formatErrorMessage: fn, diff --git a/extensions/qa-lab/src/scenario-runtime-api.ts b/extensions/qa-lab/src/scenario-runtime-api.ts index 0e6073448139..3744c8c45a27 100644 --- a/extensions/qa-lab/src/scenario-runtime-api.ts +++ b/extensions/qa-lab/src/scenario-runtime-api.ts @@ -86,6 +86,8 @@ export type QaScenarioRuntimeDeps = { extractQaToolPayload: QaScenarioRuntimeFunction; formatMemoryDreamingDay: QaScenarioRuntimeFunction; resolveSessionTranscriptsDirForAgent: QaScenarioRuntimeFunction; + activeMemoryToggleKey: QaScenarioRuntimeFunction; + setActiveMemorySessionDisabled: QaScenarioRuntimeFunction; buildAgentSessionKey: QaScenarioRuntimeFunction; normalizeLowercaseStringOrEmpty: QaScenarioRuntimeFunction; formatErrorMessage: QaScenarioRuntimeFunction; @@ -177,6 +179,8 @@ type QaScenarioRuntimeApi< extractQaToolPayload: TDeps["extractQaToolPayload"]; formatMemoryDreamingDay: TDeps["formatMemoryDreamingDay"]; resolveSessionTranscriptsDirForAgent: TDeps["resolveSessionTranscriptsDirForAgent"]; + activeMemoryToggleKey: TDeps["activeMemoryToggleKey"]; + setActiveMemorySessionDisabled: TDeps["setActiveMemorySessionDisabled"]; buildAgentSessionKey: TDeps["buildAgentSessionKey"]; normalizeLowercaseStringOrEmpty: TDeps["normalizeLowercaseStringOrEmpty"]; formatErrorMessage: TDeps["formatErrorMessage"]; @@ -283,6 +287,8 @@ export function createQaScenarioRuntimeApi< extractQaToolPayload: params.deps.extractQaToolPayload, formatMemoryDreamingDay: params.deps.formatMemoryDreamingDay, resolveSessionTranscriptsDirForAgent: params.deps.resolveSessionTranscriptsDirForAgent, + activeMemoryToggleKey: params.deps.activeMemoryToggleKey, + setActiveMemorySessionDisabled: params.deps.setActiveMemorySessionDisabled, buildAgentSessionKey: params.deps.buildAgentSessionKey, normalizeLowercaseStringOrEmpty: params.deps.normalizeLowercaseStringOrEmpty, formatErrorMessage: params.deps.formatErrorMessage, diff --git a/extensions/qa-lab/src/suite-runtime-flow.ts b/extensions/qa-lab/src/suite-runtime-flow.ts index 65db97410292..74fa9536d650 100644 --- a/extensions/qa-lab/src/suite-runtime-flow.ts +++ b/extensions/qa-lab/src/suite-runtime-flow.ts @@ -1,11 +1,12 @@ // Qa Lab plugin module implements suite runtime flow behavior. -import { randomUUID } from "node:crypto"; +import { createHash, randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; import { setTimeout as sleep } from "node:timers/promises"; import { formatMemoryDreamingDay } from "openclaw/plugin-sdk/memory-core-host-status"; import { resolveSessionTranscriptsDirForAgent } from "openclaw/plugin-sdk/memory-host-core"; import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing"; +import { createPluginStateSyncKeyedStore } from "openclaw/plugin-sdk/runtime-doctor"; import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime"; import { callQaBrowserRequest, @@ -85,6 +86,39 @@ type QaSuiteScenarioFlowEnv = { transport: QaSuiteRuntimeEnv["transport"] & QaScenarioRuntimeEnv["transport"]; } & Omit; +function activeMemoryToggleKey(sessionKey: string) { + return createHash("sha256").update(sessionKey, "utf8").digest("hex"); +} + +function setActiveMemorySessionDisabled( + env: QaSuiteScenarioFlowEnv, + sessionKey: string, + disabled: boolean, +) { + const store = createPluginStateSyncKeyedStore<{ + sessionKey: string; + disabled: true; + updatedAt: number; + }>("active-memory", { + namespace: "session-toggles", + maxEntries: 10_000, + env: { + ...process.env, + OPENCLAW_STATE_DIR: path.join(env.gateway.tempRoot, "state"), + }, + }); + const key = activeMemoryToggleKey(sessionKey); + if (disabled) { + store.register(key, { + sessionKey, + disabled: true, + updatedAt: Date.now(), + }); + return; + } + store.delete(key); +} + type QaSuiteStep = { name: string; run: () => Promise; @@ -204,6 +238,8 @@ function createQaSuiteScenarioDeps(params: QaSuiteScenarioDepsParams) { extractQaToolPayload, formatMemoryDreamingDay, resolveSessionTranscriptsDirForAgent, + activeMemoryToggleKey, + setActiveMemorySessionDisabled, buildAgentSessionKey, normalizeLowercaseStringOrEmpty, formatErrorMessage: params.formatErrorMessage,