


















@@ -11,6 +11,7 @@ const rootSdk = require(rootAliasPath) as Record<string, unknown>;
1111const rootAliasSource = fs.readFileSync(rootAliasPath, "utf-8");
1212const compatPath = fileURLToPath(new URL("../../plugin-sdk/compat.ts", import.meta.url));
1313const packageJsonPath = fileURLToPath(new URL("../../../package.json", import.meta.url));
14+const diagnosticEventsStateKey = Symbol.for("openclaw.diagnosticEvents.state.v1");
1415const legacyRootExportNames = [
1516"registerContextEngine",
1617"buildMemorySystemPromptAddition",
@@ -36,6 +37,10 @@ type EmptySchema = {
3637};
3738};
383940+type DiagnosticEventsStateFixture = {
41+listeners: Set<(event: { type: string }, metadata: { trusted: boolean }) => void>;
42+};
43+3944function requirePropertyDescriptor(
4045target: Record<string, unknown>,
4146propertyName: string,
@@ -183,6 +188,56 @@ function loadDiagnosticEventsAlias(distEntries: string[]) {
183188});
184189}
185190191+function ensureDiagnosticEventsStateFixture(
192+context: Record<PropertyKey, unknown>,
193+): DiagnosticEventsStateFixture {
194+const existing = context[diagnosticEventsStateKey] as DiagnosticEventsStateFixture | undefined;
195+if (existing) {
196+return existing;
197+}
198+const state = vm.runInNewContext(
199+`({
200+ marker: Symbol.for("openclaw.diagnosticEvents.state.v1"),
201+ enabled: true,
202+ seq: 0,
203+ listeners: new Set(),
204+ dispatchDepth: 0,
205+ asyncQueue: [],
206+ asyncDrainScheduled: false,
207+ asyncDroppedEvents: 0,
208+ asyncDroppedTrustedEvents: 0,
209+ asyncDroppedUntrustedEvents: 0,
210+ asyncDroppedPriorityEvents: 0,
211+ })`,
212+context,
213+) as DiagnosticEventsStateFixture;
214+Object.defineProperty(context, diagnosticEventsStateKey, {
215+configurable: true,
216+enumerable: false,
217+value: state,
218+writable: false,
219+});
220+return state;
221+}
222+223+function requireDiagnosticEventsStateFixture(
224+lazyModule: ReturnType<typeof loadRootAliasWithStubs>,
225+): DiagnosticEventsStateFixture {
226+const state = lazyModule.globalContext[diagnosticEventsStateKey] as
227+| DiagnosticEventsStateFixture
228+| undefined;
229+if (!state) {
230+throw new Error("expected diagnostic events state fixture");
231+}
232+return state;
233+}
234+235+function emitFixtureDiagnosticEvent(state: DiagnosticEventsStateFixture): void {
236+for (const registered of state.listeners) {
237+registered({ type: "model.usage" }, { trusted: false });
238+}
239+}
240+186241function expectDiagnosticEventAccessor(lazyModule: ReturnType<typeof loadRootAliasWithStubs>) {
187242expect(
188243typeof (lazyModule.moduleExports.onDiagnosticEvent as (listener: () => void) => () => void)(
@@ -525,6 +580,136 @@ describe("plugin-sdk root alias", () => {
525580);
526581});
527582583+it("resolves the diagnostic event export by function name when dist aliases shift", () => {
584+let subscribeCount = 0;
585+let unsubscribeCount = 0;
586+const lazyModule = loadRootAliasWithStubs({
587+aliasPath: createDistAliasPath(),
588+distEntries: ["diagnostic-events-W3Hz61fI.js"],
589+monolithicExports: {
590+r: function emitFailoverEvent(): void {
591+throw new Error("wrong diagnostic event alias selected");
592+},
593+u: function onDiagnosticEvent(_listener: () => void): () => void {
594+subscribeCount += 1;
595+return () => {
596+unsubscribeCount += 1;
597+};
598+},
599+},
600+});
601+602+const unsubscribe = (
603+lazyModule.moduleExports.onDiagnosticEvent as (
604+listener: (event: { type: string }) => void,
605+) => () => void
606+)(() => undefined);
607+unsubscribe();
608+609+expect(subscribeCount).toBe(1);
610+expect(unsubscribeCount).toBe(1);
611+});
612+613+it("falls back and removes stale diagnostic listeners when the dist subscription is invalid", () => {
614+const seen: string[] = [];
615+let lazyModule!: ReturnType<typeof loadRootAliasWithStubs>;
616+const preexistingListener = (): void => undefined;
617+lazyModule = loadRootAliasWithStubs({
618+aliasPath: createDistAliasPath(),
619+distEntries: ["diagnostic-events-W3Hz61fI.js"],
620+monolithicExports: {
621+onDiagnosticEvent(listener: (event: { type: string }) => void): undefined {
622+const state = ensureDiagnosticEventsStateFixture(lazyModule.globalContext);
623+state.listeners.add((event, metadata) => {
624+if (!metadata.trusted) {
625+listener(event);
626+}
627+});
628+return undefined;
629+},
630+},
631+});
632+const state = ensureDiagnosticEventsStateFixture(lazyModule.globalContext);
633+state.listeners.add(preexistingListener);
634+635+const unsubscribe = (
636+lazyModule.moduleExports.onDiagnosticEvent as (
637+listener: (event: { type: string }) => void,
638+) => () => void
639+)((event) => {
640+seen.push(event.type);
641+});
642+643+expect(state.listeners.size).toBe(2);
644+expect(state.listeners.has(preexistingListener)).toBe(true);
645+emitFixtureDiagnosticEvent(state);
646+unsubscribe();
647+648+expect(seen).toEqual(["model.usage"]);
649+expect(state.listeners.size).toBe(1);
650+expect(state.listeners.has(preexistingListener)).toBe(true);
651+});
652+653+it("falls back to shared diagnostic state when the dist subscription throws", () => {
654+const seen: string[] = [];
655+let subscribeCount = 0;
656+const lazyModule = loadRootAliasWithStubs({
657+aliasPath: createDistAliasPath(),
658+distEntries: ["diagnostic-events-W3Hz61fI.js"],
659+monolithicExports: {
660+onDiagnosticEvent(): never {
661+subscribeCount += 1;
662+throw new Error("stale diagnostic subscription");
663+},
664+},
665+});
666+667+const unsubscribe = (
668+lazyModule.moduleExports.onDiagnosticEvent as (
669+listener: (event: { type: string }) => void,
670+) => () => void
671+)((event) => {
672+seen.push(event.type);
673+});
674+const state = requireDiagnosticEventsStateFixture(lazyModule);
675+676+expect(subscribeCount).toBe(1);
677+expect(state.listeners.size).toBe(1);
678+emitFixtureDiagnosticEvent(state);
679+unsubscribe();
680+681+expect(seen).toEqual(["model.usage"]);
682+expect(state.listeners.size).toBe(0);
683+});
684+685+it("removes the shared-state fallback listener when diagnostic cleanup throws", () => {
686+let diagnosticUnsubscribeCount = 0;
687+const lazyModule = loadRootAliasWithStubs({
688+aliasPath: createDistAliasPath(),
689+distEntries: ["diagnostic-events-W3Hz61fI.js"],
690+monolithicExports: {
691+onDiagnosticEvent(): () => void {
692+return () => {
693+diagnosticUnsubscribeCount += 1;
694+throw new Error("diagnostic cleanup failed");
695+};
696+},
697+},
698+});
699+700+const unsubscribe = (
701+lazyModule.moduleExports.onDiagnosticEvent as (
702+listener: (event: { type: string }) => void,
703+) => () => void
704+)(() => undefined);
705+const state = requireDiagnosticEventsStateFixture(lazyModule);
706+707+expect(state.listeners.size).toBe(1);
708+expect(() => unsubscribe()).toThrow("diagnostic cleanup failed");
709+expect(diagnosticUnsubscribeCount).toBe(1);
710+expect(state.listeners.size).toBe(0);
711+});
712+528713it("bridges diagnostic listeners through shared process state when the lazy module is isolated", () => {
529714const seen: string[] = [];
530715const lazyModule = loadDiagnosticEventsAlias(["diagnostic-events-W3Hz61fI.js"]);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。