fix(plugins): inject harness tool authority

This commit is contained in:
Vincent Koc
2026-08-07 08:11:48 +02:00
parent 62aff372ec
commit 228478bffe
22 changed files with 162 additions and 75 deletions
+8
View File
@@ -33,6 +33,11 @@ type CodexAppServerAgentHarness = AgentHarness & {
): Promise<AgentHarnessCompactResult | undefined>;
};
type CodexAgentHarnessToolAuthority = {
createForAttempt: (typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarness"];
createForSideQuestion: (typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarnessSideQuestion"];
};
async function disposeSharedCodexAppServerClients(): Promise<void> {
const dispose = (
globalThis as typeof globalThis & {
@@ -56,6 +61,7 @@ export function createCodexAppServerAgentHarness(options: {
runtime?: PluginRuntime;
bindingStore: CodexAppServerBindingStore;
sessionCatalogControl?: CodexSessionCatalogControl;
toolAuthority?: CodexAgentHarnessToolAuthority;
}): AgentHarness {
const harnessRuntimeId = options?.id ?? "codex";
const normalizedHarnessRuntimeId = harnessRuntimeId.trim().toLowerCase();
@@ -180,6 +186,7 @@ export function createCodexAppServerAgentHarness(options: {
// cold provider catalog reads do not pull in the whole Codex runtime.
const { runCodexAppServerAttempt } = await import("./src/app-server/run-attempt.js");
return runCodexAppServerAttempt(params, {
agentHarnessCodingToolsFactory: options.toolAuthority?.createForAttempt,
bindingStore: options.bindingStore,
pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
nativeHookRelay: { enabled: true },
@@ -221,6 +228,7 @@ export function createCodexAppServerAgentHarness(options: {
runSideQuestion: async (params) => {
const { runCodexAppServerSideQuestion } = await import("./src/app-server/side-question.js");
return runCodexAppServerSideQuestion(params, {
agentHarnessCodingToolsFactory: options.toolAuthority?.createForSideQuestion,
bindingStore: options.bindingStore,
pluginConfig: options?.resolvePluginConfig?.() ?? options?.pluginConfig,
nativeHookRelay: { enabled: true },
+2
View File
@@ -852,6 +852,7 @@ describe("codex plugin", () => {
expect(runCodexAppServerAttemptMock).toHaveBeenCalledWith(
{ prompt: "hello" },
{
agentHarnessCodingToolsFactory: undefined,
bindingStore: testCodexAppServerBindingStore,
pluginConfig: { appServer: {} },
nativeHookRelay: { enabled: true },
@@ -917,6 +918,7 @@ describe("codex plugin", () => {
expect(runCodexAppServerAttemptMock).toHaveBeenCalledWith(
{ prompt: "calendar" },
{
agentHarnessCodingToolsFactory: expect.any(Function),
bindingStore: expect.any(Object),
pluginConfig: liveConfig.plugins.entries.codex.config,
nativeHookRelay: { enabled: true },
+8
View File
@@ -2,6 +2,10 @@
* Bundled Codex plugin entry: app-server harness, media understanding,
* migration provider, CLI-session commands, and binding hooks.
*/
import {
createOpenClawCodingToolsForAgentHarness,
createOpenClawCodingToolsForAgentHarnessSideQuestion,
} from "openclaw/plugin-sdk/agent-harness-tool-authority-runtime";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { mutateConfigFile } from "openclaw/plugin-sdk/config-mutation";
import {
@@ -166,6 +170,10 @@ export default definePluginEntry({
createCodexAppServerAgentHarness({
bindingStore,
sessionCatalogControl,
toolAuthority: {
createForAttempt: createOpenClawCodingToolsForAgentHarness,
createForSideQuestion: createOpenClawCodingToolsForAgentHarnessSideQuestion,
},
resolveConfig: resolveCurrentConfig,
resolvePluginConfig: resolveCurrentPluginConfig,
runtime: api.runtime,
@@ -1,6 +1,6 @@
type OpenClawCodingToolsFactory =
(typeof import("openclaw/plugin-sdk/agent-harness"))["createOpenClawCodingTools"];
type AgentHarnessCodingToolsFactory =
export type AgentHarnessCodingToolsFactory =
(typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarness"];
/** Mutable dependency seam shared by dynamic-tool construction and its behavioral tests. */
@@ -1,33 +1,21 @@
import { afterEach, expect, it, vi } from "vitest";
import { expect, it, vi } from "vitest";
import { buildDynamicTools } from "./dynamic-tool-build.js";
afterEach(() => {
vi.doUnmock("openclaw/plugin-sdk/agent-harness-tool-authority-runtime");
vi.resetModules();
});
it("does not load the private tool builder before a tool-capable turn", async () => {
let toolBuilderImports = 0;
vi.doMock("openclaw/plugin-sdk/agent-harness-tool-authority-runtime", () => {
toolBuilderImports += 1;
return {
createOpenClawCodingToolsForAgentHarness: vi.fn(() => []),
};
});
const { buildDynamicTools } = await import("./dynamic-tool-build.js");
await import("./side-question.js");
expect(toolBuilderImports).toBe(0);
it("does not invoke host tool authority before a tool-capable turn", async () => {
const agentHarnessCodingToolsFactory = vi.fn(async () => []);
await expect(
buildDynamicTools({
agentHarnessCodingToolsFactory,
attributionAttempt: {} as never,
params: { disableTools: true } as never,
} as never),
).resolves.toEqual([]);
expect(toolBuilderImports).toBe(0);
expect(agentHarnessCodingToolsFactory).not.toHaveBeenCalled();
await expect(
buildDynamicTools({
agentHarnessCodingToolsFactory,
attributionAttempt: {} as never,
params: {
disableTools: false,
@@ -35,5 +23,5 @@ it("does not load the private tool builder before a tool-capable turn", async ()
} as never,
} as never),
).resolves.toEqual([]);
expect(toolBuilderImports).toBe(0);
expect(agentHarnessCodingToolsFactory).not.toHaveBeenCalled();
});
@@ -165,7 +165,7 @@ describe("Codex app-server dynamic tool build", () => {
admittedAttempt.runtimePlan = createCodexRuntimePlanFixture();
const runtimeParams = { ...admittedAttempt };
const factory = vi.fn<NonNullable<typeof dynamicToolBuildState.agentHarnessCodingToolsFactory>>(
() => [],
async () => [],
);
dynamicToolBuildState.agentHarnessCodingToolsFactory = factory;
@@ -21,7 +21,10 @@ import {
import { resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
import { isToolAllowed } from "openclaw/plugin-sdk/sandbox";
import { readCodexPluginConfig, type CodexPluginConfig } from "./config.js";
import { dynamicToolBuildState } from "./dynamic-tool-build-state.js";
import {
dynamicToolBuildState,
type AgentHarnessCodingToolsFactory,
} from "./dynamic-tool-build-state.js";
import {
filterCodexDynamicTools,
filterCodexDynamicToolsWithOpenClawShell,
@@ -83,6 +86,7 @@ function preserveRingZeroSystemAgentTool<T extends { name: string; catalogMode?:
}
/** Runtime inputs needed to derive the exact Codex dynamic tool surface for a turn. */
type DynamicToolBuildParams = {
agentHarnessCodingToolsFactory?: AgentHarnessCodingToolsFactory;
attributionAttempt: EmbeddedRunAttemptParams;
params: EmbeddedRunAttemptParams;
resolvedWorkspace: string;
@@ -232,11 +236,11 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
let agentHarnessModule: typeof import("openclaw/plugin-sdk/agent-harness") | undefined;
const loadAgentHarnessModule = async () =>
(agentHarnessModule ??= await import("openclaw/plugin-sdk/agent-harness"));
const agentHarnessCodingToolsFactory = injectedOpenClawCodingToolsFactory
? undefined
: (dynamicToolBuildState.agentHarnessCodingToolsFactory ??
(await import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))
.createOpenClawCodingToolsForAgentHarness);
const agentHarnessCodingToolsFactory =
input.agentHarnessCodingToolsFactory ?? dynamicToolBuildState.agentHarnessCodingToolsFactory;
if (!injectedOpenClawCodingToolsFactory && !agentHarnessCodingToolsFactory) {
throw new Error("[codex] host tool authority is unavailable");
}
const createOpenClawCodingTools =
injectedOpenClawCodingToolsFactory ??
((options: OpenClawCodingToolsOptions) =>
@@ -244,7 +248,7 @@ export async function buildDynamicTools(input: DynamicToolBuildParams) {
toolBuildStages.mark("load-agent-harness-tools");
const sessionKeys = resolveOpenClawCodingToolsSessionKeys(params, input.sandboxSessionKey);
const nativeExecutionPolicy = resolveCodexNativeExecutionPolicyForDynamicTools(input);
const allTools = createOpenClawCodingTools({
const allTools = await createOpenClawCodingTools({
agentId: input.sessionAgentId,
...buildEmbeddedAttemptToolRunContext(params),
exec: {
@@ -159,6 +159,12 @@ export function runCodexAppServerAttempt(
};
const promise = runCodexAppServerAttemptImpl(trackedParams, {
...options,
agentHarnessCodingToolsFactory:
options.agentHarnessCodingToolsFactory ??
(async (_attempt, toolOptions) => {
const factory = dynamicToolBuildState.openClawCodingToolsFactory;
return factory ? await factory(toolOptions) : [];
}),
bindingStore: options.bindingStore ?? testCodexAppServerBindingStore,
...(clientFactory ? { clientFactory } : {}),
}).finally(() => {
@@ -109,6 +109,7 @@ export async function prepareCodexAttemptTools(runtime: CodexAttemptRuntime) {
frameImageIdentity?: string;
} = { value: 0 };
const commonToolParams = {
agentHarnessCodingToolsFactory: connection.options.agentHarnessCodingToolsFactory,
// Private attribution is bound to the exact host-admitted object. Codex
// runtime/tool clones carry behavior only and must not replace this owner.
attributionAttempt: params,
@@ -6,6 +6,7 @@ import type { CodexAppServerBindingStore } from "./session-binding.js";
import type { CodexAppServerClientFactory } from "./shared-client.js";
export type CodexRunAttemptOptions = {
agentHarnessCodingToolsFactory?: (typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarness"];
bindingStore: CodexAppServerBindingStore;
pluginConfig?: unknown;
startupTimeoutFloorMs?: number;
@@ -115,9 +115,17 @@ const bindingStore: CodexAppServerBindingStore = {
function runCodexAppServerSideQuestion(
params: Parameters<typeof runCodexAppServerSideQuestionImpl>[0],
options: Omit<Parameters<typeof runCodexAppServerSideQuestionImpl>[1], "bindingStore"> = {},
options: Omit<
Parameters<typeof runCodexAppServerSideQuestionImpl>[1],
"agentHarnessCodingToolsFactory" | "bindingStore"
> = {},
) {
return runCodexAppServerSideQuestionImpl(params, { ...options, bindingStore });
return runCodexAppServerSideQuestionImpl(params, {
...options,
agentHarnessCodingToolsFactory: (attributionParams, toolOptions) =>
createOpenClawCodingToolsForSideQuestionMock(attributionParams, toolOptions),
bindingStore,
});
}
function createFakeClient() {
@@ -161,6 +161,7 @@ Do not modify files, source, git state, permissions, configuration, workspace st
export async function runCodexAppServerSideQuestion(
params: AgentHarnessSideQuestionParams,
options: {
agentHarnessCodingToolsFactory?: (typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarnessSideQuestion"];
bindingStore: CodexAppServerBindingStore;
pluginConfig?: unknown;
nativeHookRelay?: {
@@ -413,6 +414,7 @@ export async function runCodexAppServerSideQuestion(
})
: "unsupported";
const { toolBridge, webSearchPlan } = await createCodexSideToolBridge({
agentHarnessCodingToolsFactory: options.agentHarnessCodingToolsFactory,
attributionParams: params,
params: effectiveParams,
cwd,
@@ -921,6 +923,7 @@ function buildSideRunAttemptParams(
}
async function createCodexSideToolBridge(input: {
agentHarnessCodingToolsFactory?: (typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarnessSideQuestion"];
attributionParams: AgentHarnessSideQuestionParams;
params: AgentHarnessSideQuestionParams;
cwd: string;
@@ -937,8 +940,9 @@ async function createCodexSideToolBridge(input: {
const messageToolProvider = resolveCodexMessageToolProvider(input.params);
let tools: AnyAgentTool[] = [];
if (supportsModelTools(runtimeModel)) {
const { createOpenClawCodingToolsForAgentHarnessSideQuestion } =
await import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime");
if (!input.agentHarnessCodingToolsFactory) {
throw new Error("[codex] host side-question tool authority is unavailable");
}
const sandboxSessionKey =
input.params.sandboxSessionKey?.trim() ||
input.params.sessionKey?.trim() ||
@@ -949,7 +953,7 @@ async function createCodexSideToolBridge(input: {
sessionKey: sandboxSessionKey,
workspaceDir: input.cwd,
});
const allTools = createOpenClawCodingToolsForAgentHarnessSideQuestion(input.attributionParams, {
const allTools = await input.agentHarnessCodingToolsFactory(input.attributionParams, {
agentId: input.sessionAgentId,
sessionKey: sandboxSessionKey,
runSessionKey:
+6 -1
View File
@@ -39,6 +39,7 @@ type AgentHarnessIsolatedCompletionResult = Awaited<ReturnType<AgentHarnessIsola
const COPILOT_PROVIDER_IDS: ReadonlySet<string> = new Set(["github-copilot"]);
interface CreateCopilotAgentHarnessOptions {
agentHarnessCodingToolsFactory?: (typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarness"];
id?: string;
label?: string;
pluginConfig?: unknown;
@@ -691,7 +692,10 @@ export function createCopilotAgentHarness(
// uncaught harness rejection. Finalization cannot safely create a new
// incompatible session and therefore keeps the failure closed.
if (operation === "attempt" && isCopilotByokUnsupportedProviderError(error)) {
return runCopilotAttempt(params, { pool });
return runCopilotAttempt(params, {
pool,
createOpenClawCodingToolsForAgentHarness: options?.agentHarnessCodingToolsFactory,
});
}
throw error;
}
@@ -765,6 +769,7 @@ export function createCopilotAgentHarness(
const result = await runCopilotAttempt(effectiveParams, {
pool,
createOpenClawCodingToolsForAgentHarness: options?.agentHarnessCodingToolsFactory,
...(operation === "settled-tool-finalization" ? { operation } : {}),
onSessionEstablished:
operation === "attempt" && openclawSessionId
+2
View File
@@ -1,4 +1,5 @@
// Copilot plugin entrypoint registers its OpenClaw integration.
import { createOpenClawCodingToolsForAgentHarness } from "openclaw/plugin-sdk/agent-harness-tool-authority-runtime";
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
import { createCopilotAgentHarness, type CopilotSessionBinding } from "./harness.js";
@@ -42,6 +43,7 @@ export default definePluginEntry({
api.registerAgentHarness(
createCopilotAgentHarness({
agentHarnessCodingToolsFactory: createOpenClawCodingToolsForAgentHarness,
...(poolOptions ? { poolOptions } : {}),
sessionStore,
}),
@@ -260,6 +260,7 @@ export async function runCopilotExecution(context: {
if (!settledToolFinalization) {
try {
const toolBridge = await createToolBridge({
agentHarnessCodingToolsFactory: deps.createOpenClawCodingToolsForAgentHarness,
allowModelTools: poolAcquire.provider.mode === "byok",
modelProvider: modelRef.provider,
modelId: modelRef.id,
+2 -1
View File
@@ -12,7 +12,7 @@ import type { OnAssistantDeltaPayload } from "./event-bridge.js";
import type { CopilotHooksConfig } from "./hooks-bridge.js";
import type { CopilotPermissionPolicy } from "./permission-bridge.js";
import type { CopilotClientPool, PooledClient } from "./runtime.js";
import type { createCopilotToolBridge } from "./tool-bridge.js";
import type { AgentHarnessCodingToolsFactory, createCopilotToolBridge } from "./tool-bridge.js";
export const BACKGROUND_COMPACTION_CANCEL_TIMEOUT_MS = 5_000;
export const COPILOT_ASK_USER_AVAILABLE_TOOLS = ["builtin:ask_user"] as const;
export const COPILOT_SETTLED_FINALIZATION_SYSTEM_MESSAGE =
@@ -144,6 +144,7 @@ export interface CopilotAttemptDeps {
operation?: CopilotAttemptOperation;
now?: () => number;
createToolBridge?: typeof createCopilotToolBridge;
createOpenClawCodingToolsForAgentHarness?: AgentHarnessCodingToolsFactory;
isHostScopedToolActive?: (toolName: string) => boolean;
resolveSandboxContextOverride?: ResolveSandboxContextFn;
onSessionEstablished?: (info: {
+6 -1
View File
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os";
import path from "node:path";
import type { CopilotClient, Tool as SdkTool } from "@github/copilot-sdk";
import { expectDefined } from "@openclaw/normalization-core";
import { createOpenClawCodingTools } from "openclaw/plugin-sdk/agent-harness";
import {
abortAgentHarnessRun,
attachModelProviderRequestTransport,
@@ -552,7 +553,11 @@ describe("runCopilotAttempt", () => {
disableTools: false,
config: { tools: { codeMode: true } },
} as never),
{ pool: makeFakePool(sdk) },
{
createOpenClawCodingToolsForAgentHarness: async (_attempt, options) =>
createOpenClawCodingTools(options),
pool: makeFakePool(sdk),
},
);
expect(result.codeModeEngaged).toBe(true);
@@ -1,24 +1,12 @@
import { afterEach, expect, it, vi } from "vitest";
import { expect, it, vi } from "vitest";
import { createCopilotToolBridge } from "./tool-bridge.js";
afterEach(() => {
vi.doUnmock("openclaw/plugin-sdk/agent-harness-tool-authority-runtime");
vi.resetModules();
});
it("loads the private tool builder only after the tool-construction guard", async () => {
let toolBuilderImports = 0;
vi.doMock("openclaw/plugin-sdk/agent-harness-tool-authority-runtime", () => {
toolBuilderImports += 1;
return {
createOpenClawCodingToolsForAgentHarness: vi.fn(() => []),
};
});
const { createCopilotToolBridge } = await import("./tool-bridge.js");
expect(toolBuilderImports).toBe(0);
it("invokes host tool authority only after the tool-construction guard", async () => {
const agentHarnessCodingToolsFactory = vi.fn(async () => []);
await expect(
createCopilotToolBridge({
agentHarnessCodingToolsFactory,
admittedAttempt: {} as never,
agentId: "agent-1",
attemptParams: { disableTools: true } as never,
@@ -27,10 +15,11 @@ it("loads the private tool builder only after the tool-construction guard", asyn
sessionId: "session-1",
}),
).resolves.toEqual({ codeModeEngaged: false, sdkTools: [], sourceTools: [] });
expect(toolBuilderImports).toBe(0);
expect(agentHarnessCodingToolsFactory).not.toHaveBeenCalled();
await expect(
createCopilotToolBridge({
agentHarnessCodingToolsFactory,
admittedAttempt: {} as never,
agentId: "agent-1",
attemptParams: {} as never,
@@ -39,5 +28,5 @@ it("loads the private tool builder only after the tool-construction guard", asyn
sessionId: "session-1",
}),
).resolves.toMatchObject({ sdkTools: [], sourceTools: [] });
expect(toolBuilderImports).toBe(1);
expect(agentHarnessCodingToolsFactory).toHaveBeenCalledOnce();
});
+16 -10
View File
@@ -27,6 +27,8 @@ import { createAgentHarnessToolSurfaceRuntime } from "openclaw/plugin-sdk/agent-
type CreateOpenClawCodingTools =
(typeof import("openclaw/plugin-sdk/agent-harness"))["createOpenClawCodingTools"];
type OpenClawCodingToolsOptions = NonNullable<Parameters<CreateOpenClawCodingTools>[0]>;
export type AgentHarnessCodingToolsFactory =
(typeof import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime"))["createOpenClawCodingToolsForAgentHarness"];
type AgentHarnessToolSurfaceRuntime = ReturnType<typeof createAgentHarnessToolSurfaceRuntime>;
type CatalogExecuteParams = Parameters<
NonNullable<AgentHarnessToolSurfaceRuntime["toolSearchCatalogExecutor"]>
@@ -71,6 +73,7 @@ type CopilotToolCompletion = {
};
interface CopilotToolBridgeInput {
agentHarnessCodingToolsFactory?: AgentHarnessCodingToolsFactory;
allowModelTools?: boolean;
/** Invalidates screenshot-bound computer actions after context compaction. */
computerContextEpoch?: {
@@ -136,7 +139,9 @@ interface CopilotToolBridgeInput {
onYieldDetected?: (message?: string) => void;
onToolCompleted?: (completion: CopilotToolCompletion) => void | Promise<void>;
observeToolTerminal?: CopilotToolTerminalObserver;
createOpenClawCodingTools?: (opts: unknown) => AnyAgentTool[] | Promise<AnyAgentTool[]>;
createOpenClawCodingTools?: (
options: OpenClawCodingToolsOptions,
) => AnyAgentTool[] | Promise<AnyAgentTool[]>;
beforeExecute?: (ctx: {
toolName: string;
toolCallId: string;
@@ -198,15 +203,16 @@ export async function createCopilotToolBridge(
}
const admittedAttempt = input.admittedAttempt;
const createOpenClawCodingTools =
input.createOpenClawCodingTools ??
(admittedAttempt
? async (options: OpenClawCodingToolsOptions) => {
const { createOpenClawCodingToolsForAgentHarness } =
await import("openclaw/plugin-sdk/agent-harness-tool-authority-runtime");
return createOpenClawCodingToolsForAgentHarness(admittedAttempt, options);
}
: (await import("openclaw/plugin-sdk/agent-harness")).createOpenClawCodingTools);
let createOpenClawCodingTools = input.createOpenClawCodingTools;
if (!createOpenClawCodingTools && admittedAttempt) {
if (!input.agentHarnessCodingToolsFactory) {
throw new Error("[copilot-tool-bridge] host tool authority is unavailable");
}
createOpenClawCodingTools = (options: OpenClawCodingToolsOptions) =>
input.agentHarnessCodingToolsFactory!(admittedAttempt, options);
}
createOpenClawCodingTools ??= (await import("openclaw/plugin-sdk/agent-harness"))
.createOpenClawCodingTools;
const toolSurfaceRuntime = createAgentHarnessToolSurfaceRuntime({
abortSignal: input.abortSignal,
@@ -1,7 +1,3 @@
import {
createOpenClawCodingToolsForAgentHarness as createCoreOpenClawCodingToolsForAgentHarness,
createOpenClawCodingToolsForAgentHarnessSideQuestion as createCoreOpenClawCodingToolsForAgentHarnessSideQuestion,
} from "../agents/agent-tools-internal.js";
import type {
AgentHarnessSideQuestionParams,
EmbeddedRunAttemptParams,
@@ -15,10 +11,16 @@ type OpenClawCodingToolsOptions = NonNullable<
* Build tools for the exact host-admitted attempt without exposing its private
* execution attribution to plugin code.
*/
export function createOpenClawCodingToolsForAgentHarness(
export async function createOpenClawCodingToolsForAgentHarness(
attempt: EmbeddedRunAttemptParams,
options?: OpenClawCodingToolsOptions,
): ReturnType<typeof createCoreOpenClawCodingToolsForAgentHarness> {
): Promise<
ReturnType<
typeof import("../agents/agent-tools-internal.js").createOpenClawCodingToolsForAgentHarness
>
> {
const { createOpenClawCodingToolsForAgentHarness: createCoreOpenClawCodingToolsForAgentHarness } =
await import("../agents/agent-tools-internal.js");
return createCoreOpenClawCodingToolsForAgentHarness(attempt, options);
}
@@ -26,9 +28,17 @@ export function createOpenClawCodingToolsForAgentHarness(
* Build tools for the exact host-admitted side-question request without
* exposing its private execution attribution to plugin code.
*/
export function createOpenClawCodingToolsForAgentHarnessSideQuestion(
export async function createOpenClawCodingToolsForAgentHarnessSideQuestion(
params: AgentHarnessSideQuestionParams,
options?: OpenClawCodingToolsOptions,
): ReturnType<typeof createCoreOpenClawCodingToolsForAgentHarnessSideQuestion> {
): Promise<
ReturnType<
typeof import("../agents/agent-tools-internal.js").createOpenClawCodingToolsForAgentHarnessSideQuestion
>
> {
const {
createOpenClawCodingToolsForAgentHarnessSideQuestion:
createCoreOpenClawCodingToolsForAgentHarnessSideQuestion,
} = await import("../agents/agent-tools-internal.js");
return createCoreOpenClawCodingToolsForAgentHarnessSideQuestion(params, options);
}
+32 -1
View File
@@ -1,7 +1,7 @@
// Verifies native plugin SDK resolver behavior and import aliases.
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import { createRequire } from "node:module";
import Module, { createRequire } from "node:module";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
@@ -637,6 +637,37 @@ describe("installOpenClawPluginSdkNativeResolver", () => {
expect(() => requireFromPlugin.resolve("openclaw/plugin-sdk/source-only")).toThrow();
});
it("keeps harness tool authority out of the forgeable native resolver", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-sdk-native-authority-"));
const { loaderModulePath } = writeFakeOpenClawPackage(root);
addFakePluginSdkDistExport(root, "agent-harness-tool-authority-runtime");
const installedCodexEntry = writeExternalPluginEntry(
fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-sdk-native-installed-codex-")),
);
const installedAliases = installOpenClawPluginSdkNativeResolver({
modulePath: loaderModulePath,
pluginModulePath: installedCodexEntry,
pluginSdkResolution: "dist",
trustedInstalledPrivateSdkOwner: "codex",
});
const authoritySpecifier = "openclaw/plugin-sdk/agent-harness-tool-authority-runtime";
expect(installedAliases).not.toContain(authoritySpecifier);
const resolveFilename = (
Module as unknown as {
_resolveFilename: (request: string, parent: NodeJS.Module, isMain: boolean) => string;
}
)._resolveFilename;
expect(() =>
resolveFilename(
authoritySpecifier,
{ filename: installedCodexEntry } as NodeJS.Module,
false,
),
).toThrow();
});
it("scopes private SSRF SDK aliases to bundled local IPC native parents", () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-sdk-native-ssrf-"));
const { loaderModulePath } = writeFakeOpenClawPackage(root);
@@ -311,6 +311,13 @@ function listPluginSdkNativeAliases(
}
const aliases = Object.entries(aliasMap)
.filter(([specifier]) => isPluginSdkAliasSpecifier(specifier))
// This capability is injected through the owner-scoped plugin loader.
// A process-global resolver cannot authenticate caller-supplied parent metadata.
.filter(
([specifier]) =>
specifier !== "openclaw/plugin-sdk/agent-harness-tool-authority-runtime" &&
specifier !== "openclaw/plugin-sdk/agent-harness-tool-authority-runtime.js",
)
.filter(([, target]) => isNativeLoadableSdkTarget(target))
.flatMap(([specifier, target]) => {
if (specifier.endsWith(".js")) {