mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 14:50:45 +00:00
Bridge Codex native hooks into OpenClaw
Bridge Codex-native tool events into the OpenClaw plugin hook surface, including native permission approval routing, bounded relay payloads, approval spam protection, and docs/changelog updates.\n\nCo-authored-by: pashpashpash <nik@vault77.ai>
This commit is contained in:
118
extensions/codex/src/app-server/native-hook-relay.test.ts
Normal file
118
extensions/codex/src/app-server/native-hook-relay.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { NativeHookRelayRegistrationHandle } from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildCodexNativeHookRelayConfig,
|
||||
buildCodexNativeHookRelayDisabledConfig,
|
||||
} from "./native-hook-relay.js";
|
||||
|
||||
describe("Codex native hook relay config", () => {
|
||||
it("builds deterministic Codex config overrides with command hooks", () => {
|
||||
const config = buildCodexNativeHookRelayConfig({
|
||||
relay: createRelay(),
|
||||
hookTimeoutSec: 7,
|
||||
});
|
||||
|
||||
expect(config).toEqual({
|
||||
"features.codex_hooks": true,
|
||||
"hooks.PreToolUse": [
|
||||
{
|
||||
matcher: null,
|
||||
hooks: [
|
||||
{
|
||||
type: "command",
|
||||
command:
|
||||
"openclaw hooks relay --provider codex --relay-id relay-1 --event pre_tool_use",
|
||||
timeout: 7,
|
||||
async: false,
|
||||
statusMessage: "OpenClaw native hook relay",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"hooks.PostToolUse": [
|
||||
{
|
||||
matcher: null,
|
||||
hooks: [
|
||||
{
|
||||
type: "command",
|
||||
command:
|
||||
"openclaw hooks relay --provider codex --relay-id relay-1 --event post_tool_use",
|
||||
timeout: 7,
|
||||
async: false,
|
||||
statusMessage: "OpenClaw native hook relay",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"hooks.PermissionRequest": [
|
||||
{
|
||||
matcher: null,
|
||||
hooks: [
|
||||
{
|
||||
type: "command",
|
||||
command:
|
||||
"openclaw hooks relay --provider codex --relay-id relay-1 --event permission_request",
|
||||
timeout: 7,
|
||||
async: false,
|
||||
statusMessage: "OpenClaw native hook relay",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(JSON.stringify(config)).not.toContain("timeoutSec");
|
||||
expect(config).not.toHaveProperty("hooks.SessionStart");
|
||||
expect(config).not.toHaveProperty("hooks.UserPromptSubmit");
|
||||
expect(config).not.toHaveProperty("hooks.Stop");
|
||||
});
|
||||
|
||||
it("includes only requested hook events", () => {
|
||||
expect(
|
||||
buildCodexNativeHookRelayConfig({
|
||||
relay: createRelay(),
|
||||
events: ["permission_request"],
|
||||
}),
|
||||
).toEqual({
|
||||
"features.codex_hooks": true,
|
||||
"hooks.PermissionRequest": [
|
||||
{
|
||||
matcher: null,
|
||||
hooks: [
|
||||
{
|
||||
type: "command",
|
||||
command:
|
||||
"openclaw hooks relay --provider codex --relay-id relay-1 --event permission_request",
|
||||
timeout: 5,
|
||||
async: false,
|
||||
statusMessage: "OpenClaw native hook relay",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds deterministic clearing config when the relay is disabled", () => {
|
||||
expect(buildCodexNativeHookRelayDisabledConfig()).toEqual({
|
||||
"features.codex_hooks": false,
|
||||
"hooks.PreToolUse": [],
|
||||
"hooks.PostToolUse": [],
|
||||
"hooks.PermissionRequest": [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createRelay(): NativeHookRelayRegistrationHandle {
|
||||
return {
|
||||
relayId: "relay-1",
|
||||
provider: "codex",
|
||||
sessionId: "session-1",
|
||||
sessionKey: "agent:main:session-1",
|
||||
runId: "run-1",
|
||||
allowedEvents: ["pre_tool_use", "post_tool_use", "permission_request"],
|
||||
expiresAtMs: Date.now() + 1000,
|
||||
commandForEvent: (event) =>
|
||||
`openclaw hooks relay --provider codex --relay-id relay-1 --event ${event}`,
|
||||
unregister: () => undefined,
|
||||
};
|
||||
}
|
||||
61
extensions/codex/src/app-server/native-hook-relay.ts
Normal file
61
extensions/codex/src/app-server/native-hook-relay.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type {
|
||||
NativeHookRelayEvent,
|
||||
NativeHookRelayRegistrationHandle,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import type { JsonObject, JsonValue } from "./protocol.js";
|
||||
|
||||
export const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
||||
"pre_tool_use",
|
||||
"post_tool_use",
|
||||
"permission_request",
|
||||
] as const satisfies readonly NativeHookRelayEvent[];
|
||||
|
||||
type CodexHookEventName = "PreToolUse" | "PostToolUse" | "PermissionRequest";
|
||||
|
||||
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT: Record<NativeHookRelayEvent, CodexHookEventName> = {
|
||||
pre_tool_use: "PreToolUse",
|
||||
post_tool_use: "PostToolUse",
|
||||
permission_request: "PermissionRequest",
|
||||
};
|
||||
|
||||
export function buildCodexNativeHookRelayConfig(params: {
|
||||
relay: NativeHookRelayRegistrationHandle;
|
||||
events?: readonly NativeHookRelayEvent[];
|
||||
hookTimeoutSec?: number;
|
||||
}): JsonObject {
|
||||
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
||||
const config: JsonObject = {
|
||||
"features.codex_hooks": true,
|
||||
};
|
||||
for (const event of events) {
|
||||
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
||||
config[`hooks.${codexEvent}`] = [
|
||||
{
|
||||
matcher: null,
|
||||
hooks: [
|
||||
{
|
||||
type: "command",
|
||||
command: params.relay.commandForEvent(event),
|
||||
timeout: normalizeHookTimeoutSec(params.hookTimeoutSec),
|
||||
async: false,
|
||||
statusMessage: "OpenClaw native hook relay",
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies JsonValue;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
export function buildCodexNativeHookRelayDisabledConfig(): JsonObject {
|
||||
return {
|
||||
"features.codex_hooks": false,
|
||||
"hooks.PreToolUse": [],
|
||||
"hooks.PostToolUse": [],
|
||||
"hooks.PermissionRequest": [],
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeHookTimeoutSec(value: number | undefined): number {
|
||||
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.ceil(value) : 5;
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
type EmbeddedRunAttemptParams,
|
||||
} from "openclaw/plugin-sdk/agent-harness";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { __testing as nativeHookRelayTesting } from "../../../../src/agents/harness/native-hook-relay.js";
|
||||
import {
|
||||
initializeGlobalHookRunner,
|
||||
resetGlobalHookRunner,
|
||||
@@ -188,7 +189,7 @@ function expectResumeRequest(
|
||||
expect.arrayContaining([
|
||||
{
|
||||
method: "thread/resume",
|
||||
params,
|
||||
params: expect.objectContaining(params),
|
||||
},
|
||||
]),
|
||||
);
|
||||
@@ -258,6 +259,21 @@ function createMessageDynamicTool(
|
||||
};
|
||||
}
|
||||
|
||||
function extractRelayIdFromThreadRequest(params: unknown): string {
|
||||
const command = (
|
||||
params as {
|
||||
config?: {
|
||||
"hooks.PreToolUse"?: Array<{ hooks?: Array<{ command?: string }> }>;
|
||||
};
|
||||
}
|
||||
).config?.["hooks.PreToolUse"]?.[0]?.hooks?.[0]?.command;
|
||||
const match = command?.match(/--relay-id ([^ ]+)/);
|
||||
if (!match?.[1]) {
|
||||
throw new Error(`relay id missing from command: ${command}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
describe("runCodexAppServerAttempt", () => {
|
||||
beforeEach(async () => {
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-run-"));
|
||||
@@ -265,6 +281,7 @@ describe("runCodexAppServerAttempt", () => {
|
||||
|
||||
afterEach(async () => {
|
||||
__testing.resetCodexAppServerClientFactoryForTests();
|
||||
nativeHookRelayTesting.clearNativeHookRelaysForTests();
|
||||
resetGlobalHookRunner();
|
||||
vi.restoreAllMocks();
|
||||
await fs.rm(tempDir, { recursive: true, force: true });
|
||||
@@ -406,6 +423,111 @@ describe("runCodexAppServerAttempt", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers native hook relay config for an enabled Codex turn and cleans it up", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: {
|
||||
enabled: true,
|
||||
events: ["pre_tool_use"],
|
||||
gatewayTimeoutMs: 4321,
|
||||
hookTimeoutSec: 9,
|
||||
},
|
||||
});
|
||||
await harness.waitForMethod("turn/start");
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
const startRequest = harness.requests.find((request) => request.method === "thread/start");
|
||||
expect(startRequest?.params).toEqual(
|
||||
expect.objectContaining({
|
||||
config: expect.objectContaining({
|
||||
"features.codex_hooks": true,
|
||||
"hooks.PreToolUse": [
|
||||
expect.objectContaining({
|
||||
hooks: [
|
||||
expect.objectContaining({
|
||||
type: "command",
|
||||
timeout: 9,
|
||||
command: expect.stringContaining("--event pre_tool_use --timeout 4321"),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
|
||||
expect(nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(relayId)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("sends clearing Codex native hook config when the relay is disabled", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: { enabled: false },
|
||||
});
|
||||
await harness.waitForMethod("turn/start");
|
||||
await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
|
||||
await run;
|
||||
|
||||
const startRequest = harness.requests.find((request) => request.method === "thread/start");
|
||||
expect(startRequest?.params).toEqual(
|
||||
expect.objectContaining({
|
||||
config: {
|
||||
"features.codex_hooks": false,
|
||||
"hooks.PreToolUse": [],
|
||||
"hooks.PostToolUse": [],
|
||||
"hooks.PermissionRequest": [],
|
||||
},
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("cleans up native hook relay state when turn/start fails", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness(async (method) => {
|
||||
if (method === "turn/start") {
|
||||
throw new Error("turn start exploded");
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
await expect(
|
||||
runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: { enabled: true },
|
||||
}),
|
||||
).rejects.toThrow("turn start exploded");
|
||||
|
||||
const startRequest = harness.requests.find((request) => request.method === "thread/start");
|
||||
const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
|
||||
expect(nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(relayId)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("cleans up native hook relay state when the Codex turn aborts", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const harness = createStartedThreadHarness();
|
||||
|
||||
const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
|
||||
nativeHookRelay: { enabled: true },
|
||||
});
|
||||
await harness.waitForMethod("turn/start");
|
||||
const startRequest = harness.requests.find((request) => request.method === "thread/start");
|
||||
const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
|
||||
expect(abortAgentHarnessRun("session-1")).toBe(true);
|
||||
|
||||
const result = await run;
|
||||
|
||||
expect(result.aborted).toBe(true);
|
||||
expect(nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(relayId)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("fires agent_end with failure metadata when the codex turn fails", async () => {
|
||||
const agentEnd = vi.fn();
|
||||
initializeGlobalHookRunner(
|
||||
@@ -1170,6 +1292,58 @@ describe("runCodexAppServerAttempt", () => {
|
||||
expect(request.mock.calls.map(([method]) => method)).toEqual(["thread/start", "thread/resume"]);
|
||||
});
|
||||
|
||||
it("passes native hook relay config on thread start and resume", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
const params = createParams(sessionFile, workspaceDir);
|
||||
const appServer = createThreadLifecycleAppServerOptions();
|
||||
const request = vi.fn(async (method: string) => {
|
||||
if (method === "thread/start") {
|
||||
return threadStartResult("thread-existing");
|
||||
}
|
||||
if (method === "thread/resume") {
|
||||
return threadStartResult("thread-existing");
|
||||
}
|
||||
throw new Error(`unexpected method: ${method}`);
|
||||
});
|
||||
const config = {
|
||||
"features.codex_hooks": true,
|
||||
"hooks.PreToolUse": [],
|
||||
};
|
||||
|
||||
await startOrResumeThread({
|
||||
client: { request } as never,
|
||||
params,
|
||||
cwd: workspaceDir,
|
||||
dynamicTools: [],
|
||||
appServer,
|
||||
config,
|
||||
});
|
||||
await startOrResumeThread({
|
||||
client: { request } as never,
|
||||
params,
|
||||
cwd: workspaceDir,
|
||||
dynamicTools: [],
|
||||
appServer,
|
||||
config,
|
||||
});
|
||||
|
||||
expect(request.mock.calls).toEqual([
|
||||
[
|
||||
"thread/start",
|
||||
expect.objectContaining({
|
||||
config,
|
||||
}),
|
||||
],
|
||||
[
|
||||
"thread/resume",
|
||||
expect.objectContaining({
|
||||
config,
|
||||
}),
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("starts a new Codex thread when dynamic tool schemas change", async () => {
|
||||
const sessionFile = path.join(tempDir, "session.jsonl");
|
||||
const workspaceDir = path.join(tempDir, "workspace");
|
||||
|
||||
@@ -25,10 +25,13 @@ import {
|
||||
runAgentHarnessLlmInputHook,
|
||||
runAgentHarnessLlmOutputHook,
|
||||
runHarnessContextEngineMaintenance,
|
||||
registerNativeHookRelay,
|
||||
setActiveEmbeddedRun,
|
||||
supportsModelTools,
|
||||
type EmbeddedRunAttemptParams,
|
||||
type EmbeddedRunAttemptResult,
|
||||
type NativeHookRelayEvent,
|
||||
type NativeHookRelayRegistrationHandle,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import { handleCodexAppServerApprovalRequest } from "./approval-bridge.js";
|
||||
import {
|
||||
@@ -41,6 +44,11 @@ import { projectContextEngineAssemblyForCodex } from "./context-engine-projectio
|
||||
import { createCodexDynamicToolBridge } from "./dynamic-tools.js";
|
||||
import { handleCodexAppServerElicitationRequest } from "./elicitation-bridge.js";
|
||||
import { CodexAppServerEventProjector } from "./event-projector.js";
|
||||
import {
|
||||
buildCodexNativeHookRelayDisabledConfig,
|
||||
buildCodexNativeHookRelayConfig,
|
||||
CODEX_NATIVE_HOOK_RELAY_EVENTS,
|
||||
} from "./native-hook-relay.js";
|
||||
import {
|
||||
assertCodexTurnStartResponse,
|
||||
readCodexDynamicToolCallParams,
|
||||
@@ -90,7 +98,17 @@ function emitCodexAppServerEvent(
|
||||
|
||||
export async function runCodexAppServerAttempt(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
options: { pluginConfig?: unknown; startupTimeoutFloorMs?: number } = {},
|
||||
options: {
|
||||
pluginConfig?: unknown;
|
||||
startupTimeoutFloorMs?: number;
|
||||
nativeHookRelay?: {
|
||||
enabled?: boolean;
|
||||
events?: readonly NativeHookRelayEvent[];
|
||||
ttlMs?: number;
|
||||
gatewayTimeoutMs?: number;
|
||||
hookTimeoutSec?: number;
|
||||
};
|
||||
} = {},
|
||||
): Promise<EmbeddedRunAttemptResult> {
|
||||
const attemptStartedAt = Date.now();
|
||||
const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig: options.pluginConfig });
|
||||
@@ -241,11 +259,29 @@ export async function runCodexAppServerAttempt(
|
||||
let client: CodexAppServerClient;
|
||||
let thread: CodexAppServerThreadBinding;
|
||||
let trajectoryEndRecorded = false;
|
||||
let nativeHookRelay: NativeHookRelayRegistrationHandle | undefined;
|
||||
try {
|
||||
emitCodexAppServerEvent(params, {
|
||||
stream: "codex_app_server.lifecycle",
|
||||
data: { phase: "startup" },
|
||||
});
|
||||
nativeHookRelay = createCodexNativeHookRelay({
|
||||
options: options.nativeHookRelay,
|
||||
agentId: sessionAgentId,
|
||||
sessionId: params.sessionId,
|
||||
sessionKey: sandboxSessionKey,
|
||||
runId: params.runId,
|
||||
signal: runAbortController.signal,
|
||||
});
|
||||
const nativeHookRelayConfig = nativeHookRelay
|
||||
? buildCodexNativeHookRelayConfig({
|
||||
relay: nativeHookRelay,
|
||||
events: options.nativeHookRelay?.events,
|
||||
hookTimeoutSec: options.nativeHookRelay?.hookTimeoutSec,
|
||||
})
|
||||
: options.nativeHookRelay?.enabled === false
|
||||
? buildCodexNativeHookRelayDisabledConfig()
|
||||
: undefined;
|
||||
({ client, thread } = await withCodexStartupTimeout({
|
||||
timeoutMs: params.timeoutMs,
|
||||
timeoutFloorMs: options.startupTimeoutFloorMs,
|
||||
@@ -259,6 +295,7 @@ export async function runCodexAppServerAttempt(
|
||||
dynamicTools: toolBridge.specs,
|
||||
appServer,
|
||||
developerInstructions: promptBuild.developerInstructions,
|
||||
config: nativeHookRelayConfig,
|
||||
});
|
||||
return { client: startupClient, thread: startupThread };
|
||||
},
|
||||
@@ -268,6 +305,7 @@ export async function runCodexAppServerAttempt(
|
||||
data: { phase: "thread_ready", threadId: thread.threadId },
|
||||
});
|
||||
} catch (error) {
|
||||
nativeHookRelay?.unregister();
|
||||
clearSharedCodexAppServerClient();
|
||||
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
||||
throw error;
|
||||
@@ -464,6 +502,7 @@ export async function runCodexAppServerAttempt(
|
||||
});
|
||||
notificationCleanup();
|
||||
requestCleanup();
|
||||
nativeHookRelay?.unregister();
|
||||
await trajectoryRecorder?.flush();
|
||||
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
||||
throw error;
|
||||
@@ -641,12 +680,46 @@ export async function runCodexAppServerAttempt(
|
||||
clearTimeout(timeout);
|
||||
notificationCleanup();
|
||||
requestCleanup();
|
||||
nativeHookRelay?.unregister();
|
||||
runAbortController.signal.removeEventListener("abort", abortListener);
|
||||
params.abortSignal?.removeEventListener("abort", abortFromUpstream);
|
||||
clearActiveEmbeddedRun(params.sessionId, handle, params.sessionKey);
|
||||
}
|
||||
}
|
||||
|
||||
function createCodexNativeHookRelay(params: {
|
||||
options:
|
||||
| {
|
||||
enabled?: boolean;
|
||||
events?: readonly NativeHookRelayEvent[];
|
||||
ttlMs?: number;
|
||||
gatewayTimeoutMs?: number;
|
||||
}
|
||||
| undefined;
|
||||
agentId: string | undefined;
|
||||
sessionId: string;
|
||||
sessionKey: string | undefined;
|
||||
runId: string;
|
||||
signal: AbortSignal;
|
||||
}): NativeHookRelayRegistrationHandle | undefined {
|
||||
if (params.options?.enabled === false) {
|
||||
return undefined;
|
||||
}
|
||||
return registerNativeHookRelay({
|
||||
provider: "codex",
|
||||
...(params.agentId ? { agentId: params.agentId } : {}),
|
||||
sessionId: params.sessionId,
|
||||
...(params.sessionKey ? { sessionKey: params.sessionKey } : {}),
|
||||
runId: params.runId,
|
||||
allowedEvents: params.options?.events ?? CODEX_NATIVE_HOOK_RELAY_EVENTS,
|
||||
ttlMs: params.options?.ttlMs,
|
||||
signal: params.signal,
|
||||
command: {
|
||||
timeoutMs: params.options?.gatewayTimeoutMs,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function interruptCodexTurnBestEffort(
|
||||
client: CodexAppServerClient,
|
||||
params: {
|
||||
|
||||
@@ -33,6 +33,7 @@ export async function startOrResumeThread(params: {
|
||||
dynamicTools: CodexDynamicToolSpec[];
|
||||
appServer: CodexAppServerRuntimeOptions;
|
||||
developerInstructions?: string;
|
||||
config?: JsonObject;
|
||||
}): Promise<CodexAppServerThreadBinding> {
|
||||
const dynamicToolsFingerprint = fingerprintDynamicTools(params.dynamicTools);
|
||||
const binding = await readCodexAppServerBinding(params.params.sessionFile);
|
||||
@@ -59,6 +60,7 @@ export async function startOrResumeThread(params: {
|
||||
threadId: binding.threadId,
|
||||
appServer: params.appServer,
|
||||
developerInstructions: params.developerInstructions,
|
||||
config: params.config,
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -102,6 +104,7 @@ export async function startOrResumeThread(params: {
|
||||
sandbox: params.appServer.sandbox,
|
||||
...(params.appServer.serviceTier ? { serviceTier: params.appServer.serviceTier } : {}),
|
||||
serviceName: "OpenClaw",
|
||||
...(params.config ? { config: params.config } : {}),
|
||||
developerInstructions:
|
||||
params.developerInstructions ?? buildDeveloperInstructions(params.params),
|
||||
dynamicTools: params.dynamicTools,
|
||||
@@ -139,6 +142,7 @@ export function buildThreadResumeParams(
|
||||
threadId: string;
|
||||
appServer: CodexAppServerRuntimeOptions;
|
||||
developerInstructions?: string;
|
||||
config?: JsonObject;
|
||||
},
|
||||
): CodexThreadResumeParams {
|
||||
const modelProvider = resolveCodexAppServerModelProvider(params.provider);
|
||||
@@ -150,6 +154,7 @@ export function buildThreadResumeParams(
|
||||
approvalsReviewer: options.appServer.approvalsReviewer,
|
||||
sandbox: options.appServer.sandbox,
|
||||
...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
|
||||
...(options.config ? { config: options.config } : {}),
|
||||
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params),
|
||||
persistExtendedHistory: true,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user