
























@@ -440,6 +440,108 @@ describe("active-memory plugin", () => {
440440expect(runEmbeddedPiAgent).toHaveBeenCalledTimes(1);
441441});
442442443+it("blocks gateway callers without admin scope from changing global active-memory config", async () => {
444+const command = registeredCommands["active-memory"];
445+446+for (const { args, gatewayClientScopes } of [
447+{ args: "off --global", gatewayClientScopes: ["operator.write"] },
448+{ args: "on --global", gatewayClientScopes: ["operator.write"] },
449+{ args: "disable --global", gatewayClientScopes: ["operator.write"] },
450+{ args: "enable --global", gatewayClientScopes: ["operator.write"] },
451+{ args: "disabled --global", gatewayClientScopes: ["operator.write"] },
452+{ args: "enabled --global", gatewayClientScopes: ["operator.write"] },
453+{ args: "off --global", gatewayClientScopes: [] },
454+]) {
455+const result = await command.handler({
456+channel: "gateway",
457+isAuthorizedSender: true,
458+ gatewayClientScopes,
459+ args,
460+commandBody: `/active-memory ${args}`,
461+config: {},
462+requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
463+detachConversationBinding: async () => ({ removed: false }),
464+getCurrentConversationBinding: async () => null,
465+});
466+467+expect(result.text).toContain("global enable/disable changes require operator.admin");
468+}
469+470+expect(api.runtime.config.replaceConfigFile).not.toHaveBeenCalled();
471+});
472+473+it("allows admin-scoped gateway callers to change global active-memory config", async () => {
474+const command = registeredCommands["active-memory"];
475+476+const result = await command.handler({
477+channel: "gateway",
478+isAuthorizedSender: true,
479+gatewayClientScopes: ["operator.admin"],
480+args: "off --global",
481+commandBody: "/active-memory off --global",
482+config: {},
483+requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
484+detachConversationBinding: async () => ({ removed: false }),
485+getCurrentConversationBinding: async () => null,
486+});
487+488+expect(result.text).toBe("Active Memory: off globally.");
489+expect(api.runtime.config.replaceConfigFile).toHaveBeenCalledTimes(1);
490+expect(configFile).toMatchObject({
491+plugins: {
492+entries: {
493+"active-memory": {
494+enabled: true,
495+config: {
496+enabled: false,
497+agents: ["main"],
498+},
499+},
500+},
501+},
502+});
503+});
504+505+it("keeps write-scoped gateway callers on non-global-write active-memory paths", async () => {
506+const command = registeredCommands["active-memory"];
507+const sessionKey = "agent:main:write-scoped-active-memory";
508+hoisted.sessionStore[sessionKey] = {
509+sessionId: "s-write-scoped-active-memory",
510+updatedAt: 0,
511+};
512+513+const globalStatusResult = await command.handler({
514+channel: "gateway",
515+isAuthorizedSender: true,
516+gatewayClientScopes: ["operator.write"],
517+args: "status --global",
518+commandBody: "/active-memory status --global",
519+config: {},
520+requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
521+detachConversationBinding: async () => ({ removed: false }),
522+getCurrentConversationBinding: async () => null,
523+});
524+525+expect(globalStatusResult.text).toBe("Active Memory: on globally.");
526+expect(api.runtime.config.replaceConfigFile).not.toHaveBeenCalled();
527+528+const sessionOffResult = await command.handler({
529+channel: "gateway",
530+isAuthorizedSender: true,
531+gatewayClientScopes: ["operator.write"],
532+ sessionKey,
533+args: "off",
534+commandBody: "/active-memory off",
535+config: {},
536+requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
537+detachConversationBinding: async () => ({ removed: false }),
538+getCurrentConversationBinding: async () => null,
539+});
540+541+expect(sessionOffResult.text).toBe("Active Memory: off for this session.");
542+expect(api.runtime.config.replaceConfigFile).not.toHaveBeenCalled();
543+});
544+443545it("uses live runtime config for before_prompt_build enablement", async () => {
444546configFile = {
445547plugins: {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。