Skip to content

Commit b051f4e

Browse files
committed
fix(cli): force dynamicBankGranularity=[agent] for SDA openclaw setups
SDA agents are local/per-agent — they don't have a channel or sender, so the plugin's default granularity ["agent","channel","user"] produces banks like "marketing-seo::unknown::anonymous" once a session starts. Fix: after enabling knowledge tools, ensure the plugin's dynamicBankGranularity is ["agent"]. On a fresh install we set it silently; on an existing config that's already set to something else, ask the user to confirm before overwriting. Also update the dry-run preview default in resolveFromPlugin to match.
1 parent bbe1c21 commit b051f4e

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

src/cli.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,33 @@ function enableKnowledgeTools(): void {
158158
writeFileSync(OPENCLAW_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
159159
}
160160

161+
// SDA agents are isolated per-agent only — no channel/user. The plugin's default
162+
// (["agent","channel","user"]) produces banks like "marketing-seo::unknown::anonymous"
163+
// for local sessions where channel/sender aren't populated.
164+
async function ensureOpenClawAgentGranularity(): Promise<void> {
165+
const config = readOpenClawConfig();
166+
if (!config) return;
167+
const pc = config.plugins?.entries?.["hindsight-openclaw"]?.config;
168+
if (!pc) return;
169+
170+
const current: string[] | undefined = pc.dynamicBankGranularity;
171+
const desired = ["agent"];
172+
const matches = Array.isArray(current) && current.length === 1 && current[0] === "agent";
173+
if (matches) return;
174+
175+
if (current && current.length > 0) {
176+
const ok = await p.confirm({
177+
message: `Plugin has dynamicBankGranularity=${color.yellow(JSON.stringify(current))}. SDA agents need ${color.cyan('["agent"]')}. Override?`,
178+
initialValue: true,
179+
});
180+
if (p.isCancel(ok) || !ok) return;
181+
}
182+
183+
pc.dynamicBankGranularity = desired;
184+
writeFileSync(OPENCLAW_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n");
185+
p.log.success("Set dynamicBankGranularity = [\"agent\"] for per-agent bank isolation");
186+
}
187+
161188
const MIN_PLUGIN_VERSION = "0.7.2";
162189

163190
function getInstalledPluginVersion(): string | null {
@@ -211,7 +238,7 @@ function resolveFromPlugin(agentId: string): { apiUrl: string; bankId: string; a
211238
if (pc.dynamicBankId === false && pc.bankId) {
212239
bankId = pc.bankId;
213240
} else {
214-
const granularity: string[] = pc.dynamicBankGranularity || ["agent", "channel", "user"];
241+
const granularity: string[] = pc.dynamicBankGranularity || ["agent"];
215242
const fieldMap: Record<string, string> = {
216243
agent: agentId,
217244
channel: "unknown",
@@ -831,9 +858,11 @@ async function main() {
831858
if (harness === "openclaw") {
832859
await ensurePlugin();
833860
enableKnowledgeTools();
861+
await ensureOpenClawAgentGranularity();
834862
({ apiUrl, bankId, apiToken } = resolveFromPlugin(agentId));
835863
} else if (harness === "nemoclaw") {
836864
await ensureNemoClawPlugin(sandbox!, agentId);
865+
await ensureOpenClawAgentGranularity();
837866
({ apiUrl, bankId, apiToken } = resolveFromPlugin(agentId));
838867
} else if (harness === "hermes") {
839868
// Resolve Hindsight connection: hermes config > openclaw config > prompt

src/tests/cli.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,38 @@ describe("claude-code allowed-tools merge", () => {
739739
});
740740
});
741741

742+
describe("openclaw dynamicBankGranularity enforcement", () => {
743+
// Mirrors ensureOpenClawAgentGranularity's decision logic. SDA agents are
744+
// local/per-agent — they don't have channel/user, so the plugin's default
745+
// ["agent","channel","user"] produces "<agent>::unknown::anonymous" banks.
746+
type Action = "noop" | "set-fresh" | "ask-confirm";
747+
function decide(current: unknown): Action {
748+
if (Array.isArray(current) && current.length === 1 && current[0] === "agent") {
749+
return "noop";
750+
}
751+
if (Array.isArray(current) && current.length > 0) {
752+
return "ask-confirm";
753+
}
754+
return "set-fresh";
755+
}
756+
757+
it("sets [agent] when not configured (fresh install)", () => {
758+
expect(decide(undefined)).toBe("set-fresh");
759+
expect(decide(null)).toBe("set-fresh");
760+
expect(decide([])).toBe("set-fresh");
761+
});
762+
763+
it("noop when already [agent]", () => {
764+
expect(decide(["agent"])).toBe("noop");
765+
});
766+
767+
it("asks confirmation when set to a different value", () => {
768+
expect(decide(["agent", "channel", "user"])).toBe("ask-confirm");
769+
expect(decide(["channel"])).toBe("ask-confirm");
770+
expect(decide(["agent", "project"])).toBe("ask-confirm");
771+
});
772+
});
773+
742774
describe("claude-code Hindsight config persistence", () => {
743775
// Mirrors the config-write logic: if existing config has a connection
744776
// (hindsightApiUrl or llmProvider), don't prompt; otherwise prompt.

0 commit comments

Comments
 (0)