Skip to content

Commit f90b8cf

Browse files
committed
perf: prefer built plugin public surfaces
1 parent 1ac037d commit f90b8cf

11 files changed

Lines changed: 135 additions & 18 deletions

src/agents/openclaw-tools.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { selectApplicableRuntimeConfig } from "../config/config.js";
44
import type { OpenClawConfig } from "../config/types.openclaw.js";
55
import { callGateway } from "../gateway/call.js";
66
import { isEmbeddedMode } from "../infra/embedded-mode.js";
7-
import { getActiveSecretsRuntimeSnapshot } from "../secrets/runtime-state.js";
7+
import { getActiveSecretsRuntimeConfigSnapshot } from "../secrets/runtime-state.js";
88
import { getActiveRuntimeWebToolsMetadata } from "../secrets/runtime-web-tools-state.js";
99
import { isCronRunSessionKey } from "../sessions/session-key-utils.js";
1010
import { resolveTranscriptsConfig } from "../transcripts/config.js";
@@ -169,7 +169,7 @@ export function createOpenClawTools(
169169
} & SpawnedToolContext,
170170
): AnyAgentTool[] {
171171
const resolvedConfig = options?.config ?? openClawToolsDeps.config;
172-
const runtimeSnapshot = getActiveSecretsRuntimeSnapshot();
172+
const runtimeSnapshot = getActiveSecretsRuntimeConfigSnapshot();
173173
const availabilityConfig = selectApplicableRuntimeConfig({
174174
inputConfig: resolvedConfig,
175175
runtimeConfig: runtimeSnapshot?.config,

src/agents/tools/web-fetch.provider-fallback.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ vi.mock("../../web-fetch/runtime.js", () => ({
1515
resolveWebFetchDefinition: resolveWebFetchDefinitionMock,
1616
}));
1717
vi.mock("../../secrets/runtime-state.js", () => ({
18-
getActiveSecretsRuntimeSnapshot: () => runtimeState.activeSecretsRuntimeSnapshot,
18+
getActiveSecretsRuntimeConfigSnapshot: () => runtimeState.activeSecretsRuntimeSnapshot,
1919
}));
2020
vi.mock("../../secrets/runtime-web-tools-state.js", () => ({
2121
getActiveRuntimeWebToolsMetadata: () => runtimeState.activeRuntimeWebToolsMetadata,

src/agents/tools/web-search.late-bind.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const mocks = vi.hoisted(() => ({
55
runWebSearch: vi.fn(),
66
resolveManifestContractOwnerPluginId: vi.fn(),
77
getActiveRuntimeWebToolsMetadata: vi.fn(),
8-
getActiveSecretsRuntimeSnapshot: vi.fn(),
8+
getActiveSecretsRuntimeConfigSnapshot: vi.fn(),
99
}));
1010

1111
vi.mock("../../web-search/runtime.js", () => ({
@@ -22,7 +22,7 @@ vi.mock("../../secrets/runtime-web-tools-state.js", () => ({
2222
}));
2323

2424
vi.mock("../../secrets/runtime-state.js", () => ({
25-
getActiveSecretsRuntimeSnapshot: mocks.getActiveSecretsRuntimeSnapshot,
25+
getActiveSecretsRuntimeConfigSnapshot: mocks.getActiveSecretsRuntimeConfigSnapshot,
2626
}));
2727

2828
type RunWebSearchParams = {
@@ -58,8 +58,8 @@ describe("web_search late-bound runtime fallback", () => {
5858
mocks.resolveManifestContractOwnerPluginId.mockReturnValue(undefined);
5959
mocks.getActiveRuntimeWebToolsMetadata.mockReset();
6060
mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue(null);
61-
mocks.getActiveSecretsRuntimeSnapshot.mockReset();
62-
mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue(null);
61+
mocks.getActiveSecretsRuntimeConfigSnapshot.mockReset();
62+
mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue(null);
6363
});
6464

6565
it("falls back to options.runtimeWebSearch when active runtime web tools metadata is absent", async () => {
@@ -79,7 +79,7 @@ describe("web_search late-bound runtime fallback", () => {
7979
expect(firstRunWebSearchParams()?.runtimeWebSearch?.selectedProvider).toBe("brave");
8080
});
8181

82-
it("falls back to options.config when getActiveSecretsRuntimeSnapshot is null", async () => {
82+
it("falls back to options.config when getActiveSecretsRuntimeConfigSnapshot is null", async () => {
8383
const fallbackConfig = {
8484
tools: { web: { search: { provider: "brave" } } },
8585
};
@@ -161,7 +161,7 @@ describe("web_search late-bound runtime fallback", () => {
161161
});
162162

163163
it("honors late-bound disabled search config at execute time", async () => {
164-
mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue({
164+
mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue({
165165
config: { tools: { web: { search: { enabled: false } } } },
166166
});
167167
const tool = createWebSearchTool({

src/agents/tools/web-tool-runtime-context.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66

77
const mocks = vi.hoisted(() => ({
88
getActiveRuntimeWebToolsMetadata: vi.fn(),
9-
getActiveSecretsRuntimeSnapshot: vi.fn(),
9+
getActiveSecretsRuntimeConfigSnapshot: vi.fn(),
1010
resolveManifestContractOwnerPluginId: vi.fn(),
1111
}));
1212

@@ -19,7 +19,7 @@ vi.mock("../../secrets/runtime-web-tools-state.js", () => ({
1919
}));
2020

2121
vi.mock("../../secrets/runtime-state.js", () => ({
22-
getActiveSecretsRuntimeSnapshot: mocks.getActiveSecretsRuntimeSnapshot,
22+
getActiveSecretsRuntimeConfigSnapshot: mocks.getActiveSecretsRuntimeConfigSnapshot,
2323
}));
2424

2525
function latestOwnerLookupParams(): Record<string, unknown> {
@@ -34,8 +34,8 @@ describe("web tool runtime context", () => {
3434
beforeEach(() => {
3535
mocks.getActiveRuntimeWebToolsMetadata.mockReset();
3636
mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue(null);
37-
mocks.getActiveSecretsRuntimeSnapshot.mockReset();
38-
mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue(null);
37+
mocks.getActiveSecretsRuntimeConfigSnapshot.mockReset();
38+
mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue(null);
3939
mocks.resolveManifestContractOwnerPluginId.mockReset();
4040
mocks.resolveManifestContractOwnerPluginId.mockReturnValue(undefined);
4141
});
@@ -44,7 +44,7 @@ describe("web tool runtime context", () => {
4444
const runtimeConfig = {
4545
tools: { web: { search: { provider: "perplexity" } } },
4646
};
47-
mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue({ config: runtimeConfig });
47+
mocks.getActiveSecretsRuntimeConfigSnapshot.mockReturnValue({ config: runtimeConfig });
4848
mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue({
4949
search: {
5050
providerConfigured: "perplexity",

src/agents/tools/web-tool-runtime-context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { OpenClawConfig } from "../../config/types.openclaw.js";
22
import { resolveManifestContractOwnerPluginId } from "../../plugins/plugin-registry.js";
3-
import { getActiveSecretsRuntimeSnapshot } from "../../secrets/runtime-state.js";
3+
import { getActiveSecretsRuntimeConfigSnapshot } from "../../secrets/runtime-state.js";
44
import { getActiveRuntimeWebToolsMetadata } from "../../secrets/runtime-web-tools-state.js";
55
import type {
66
RuntimeWebFetchMetadata,
@@ -64,7 +64,7 @@ function resolveWebToolRuntimeContext<TMetadata extends WebProviderRuntimeMetada
6464
| undefined;
6565
const config =
6666
params.lateBindRuntimeConfig === true
67-
? (getActiveSecretsRuntimeSnapshot()?.config ?? params.capturedConfig)
67+
? (getActiveSecretsRuntimeConfigSnapshot()?.config ?? params.capturedConfig)
6868
: params.capturedConfig;
6969
const providerSelectionId =
7070
resolveRuntimeWebProviderId(runtimeMetadata) ||

src/agents/tools/web-tools.enabled-defaults.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function readConfiguredSearchProvider(config: unknown): string | undefined {
4040
}
4141

4242
vi.mock("../../secrets/runtime-state.js", () => ({
43-
getActiveSecretsRuntimeSnapshot: () => activeSecretsRuntimeSnapshot.current,
43+
getActiveSecretsRuntimeConfigSnapshot: () => activeSecretsRuntimeSnapshot.current,
4444
}));
4545

4646
vi.mock("../../web-search/runtime.js", async () => {

src/plugins/public-surface-loader.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,44 @@ describe("bundled plugin public surface loader", () => {
205205
expect(createJiti).not.toHaveBeenCalled();
206206
});
207207

208+
it("keeps package-local dist public artifacts on the native path for source plugin roots", async () => {
209+
const createJiti = vi.fn(() => vi.fn(() => ({ marker: "jiti-should-not-run" })));
210+
vi.doMock("jiti", () => ({
211+
createJiti,
212+
}));
213+
vi.doMock("./native-module-require.js", () => ({
214+
tryNativeRequireJavaScriptModule: (modulePath: string) => ({
215+
ok: true,
216+
moduleExport: {
217+
marker: modulePath.includes(`${path.sep}dist${path.sep}`) ? "dist" : "source",
218+
},
219+
}),
220+
}));
221+
222+
const publicSurfaceLoader = await importFreshModule<
223+
typeof import("./public-surface-loader.js")
224+
>(import.meta.url, "./public-surface-loader.js?scope=source-root-local-dist-public-artifacts");
225+
const tempRoot = createTempDir();
226+
const bundledPluginsDir = path.join(tempRoot, "extensions");
227+
process.env.OPENCLAW_BUNDLED_PLUGINS_DIR = bundledPluginsDir;
228+
process.env.OPENCLAW_TEST_TRUST_BUNDLED_PLUGINS_DIR = "1";
229+
230+
const sourcePath = path.join(bundledPluginsDir, "demo", "api.ts");
231+
const distPath = path.join(bundledPluginsDir, "demo", "dist", "api.js");
232+
fs.mkdirSync(path.dirname(sourcePath), { recursive: true });
233+
fs.mkdirSync(path.dirname(distPath), { recursive: true });
234+
fs.writeFileSync(sourcePath, 'export const marker = "source";\n', "utf8");
235+
fs.writeFileSync(distPath, 'export const marker = "dist";\n', "utf8");
236+
237+
expect(
238+
publicSurfaceLoader.loadBundledPluginPublicArtifactModuleSync<{ marker: string }>({
239+
dirName: "demo",
240+
artifactBasename: "api.js",
241+
}).marker,
242+
).toBe("dist");
243+
expect(createJiti).not.toHaveBeenCalled();
244+
});
245+
208246
it.runIf(process.platform !== "win32")(
209247
"allows hardlinked bundled public artifacts under the trusted bundled root",
210248
async () => {

src/plugins/public-surface-runtime.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,31 @@ describe("bundled plugin public surface runtime", () => {
7070
).toBe(sourceModulePath);
7171
});
7272

73+
it("prefers package-local dist artifacts before source artifacts in source plugin trees", () => {
74+
const packageRoot = createTempDir();
75+
const sourceModulePath = path.join(packageRoot, "extensions", "demo", "api.ts");
76+
const packageLocalDistModulePath = path.join(
77+
packageRoot,
78+
"extensions",
79+
"demo",
80+
"dist",
81+
"api.js",
82+
);
83+
fs.mkdirSync(path.dirname(sourceModulePath), { recursive: true });
84+
fs.mkdirSync(path.dirname(packageLocalDistModulePath), { recursive: true });
85+
fs.writeFileSync(sourceModulePath, "export const marker = 'source';\n", "utf8");
86+
fs.writeFileSync(packageLocalDistModulePath, "export const marker = 'local-dist';\n", "utf8");
87+
88+
expect(
89+
resolveBundledPluginPublicSurfacePath({
90+
rootDir: packageRoot,
91+
bundledPluginsDir: path.join(packageRoot, "extensions"),
92+
dirName: "demo",
93+
artifactBasename: "api.js",
94+
}),
95+
).toBe(packageLocalDistModulePath);
96+
});
97+
7398
it("prefers source public surfaces over stale auto-resolved dist artifacts in source checkouts", () => {
7499
const packageRoot = createTempDir();
75100
const sourceModulePath = path.join(packageRoot, "extensions", "demo", "api.ts");

src/plugins/public-surface-runtime.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ function resolvePublicSurfaceFromBundledDir(params: {
134134
if (fs.existsSync(builtCandidate)) {
135135
return builtCandidate;
136136
}
137+
const packageLocalBuiltCandidate = path.join(pluginDir, "dist", params.artifactBasename);
138+
if (fs.existsSync(packageLocalBuiltCandidate)) {
139+
return packageLocalBuiltCandidate;
140+
}
137141
return (
138142
resolveBundledPluginSourcePublicSurfacePath({
139143
sourceRoot: params.bundledPluginsDir,

src/secrets/runtime-state.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ import { resolveAuthStatePath, resolveAuthStorePath } from "../agents/auth-profi
66
import { writeCachedAuthProfileStore } from "../agents/auth-profiles/store-cache.js";
77
import { loadAuthProfileStoreForRuntime } from "../agents/auth-profiles/store.js";
88
import type { AuthProfileStore } from "../agents/auth-profiles/types.js";
9-
import { clearSecretsRuntimeSnapshot } from "./runtime-state.js";
9+
import {
10+
activateSecretsRuntimeSnapshotState,
11+
clearSecretsRuntimeSnapshot,
12+
getActiveSecretsRuntimeConfigSnapshot,
13+
getActiveSecretsRuntimeSnapshot,
14+
type PreparedSecretsRuntimeSnapshot,
15+
} from "./runtime-state.js";
1016

1117
function authStore(key: string): AuthProfileStore {
1218
return {
@@ -68,4 +74,32 @@ describe("secrets runtime state", () => {
6874
fs.rmSync(root, { recursive: true, force: true });
6975
}
7076
});
77+
78+
it("exposes the active config pair for hot paths without requiring the full snapshot", () => {
79+
const snapshot: PreparedSecretsRuntimeSnapshot = {
80+
sourceConfig: { agents: { list: [{ id: "source" }] } },
81+
config: { agents: { list: [{ id: "runtime" }] } },
82+
authStores: [],
83+
warnings: [],
84+
webTools: {
85+
search: { providerSource: "none", diagnostics: [] },
86+
fetch: { providerSource: "none", diagnostics: [] },
87+
diagnostics: [],
88+
},
89+
};
90+
91+
activateSecretsRuntimeSnapshotState({
92+
snapshot,
93+
refreshContext: null,
94+
refreshHandler: null,
95+
});
96+
97+
const configSnapshot = getActiveSecretsRuntimeConfigSnapshot();
98+
const fullSnapshot = getActiveSecretsRuntimeSnapshot();
99+
100+
expect(configSnapshot?.config).not.toBe(fullSnapshot?.config);
101+
expect(configSnapshot?.sourceConfig).not.toBe(fullSnapshot?.sourceConfig);
102+
expect(configSnapshot?.config).toEqual(snapshot.config);
103+
expect(configSnapshot?.sourceConfig).toEqual(snapshot.sourceConfig);
104+
});
71105
});

0 commit comments

Comments
 (0)