mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:50:46 +00:00
Add Codex happy path prompt snapshots (#75807)
* Add Codex prompt snapshots * Fix prompt snapshot scenario catalogs * Harden prompt snapshot drift check * Fix CLI compat build export * fix: keep codex snapshots out of core plugin surface * fix: harden prompt snapshot ci checks * fix: accept readonly web search onboarding scopes * fix: repair plugin sdk package boundary types * fix: clear prompt snapshot ci regressions * fix: clear latest main ci checks * fix: resolve latest main discord helper overlap * fix: refresh codex dynamic tool snapshots * fix: align prompt snapshot branch with latest ci * fix: isolate plugin auto enable tests * test: refresh prompt dynamic tool snapshots * fix: stabilize bundled channel auto enable * fix: clean stale prompt snapshots
This commit is contained in:
31
extensions/codex/src/app-server/dynamic-tool-profile.ts
Normal file
31
extensions/codex/src/app-server/dynamic-tool-profile.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { CodexPluginConfig } from "./config.js";
|
||||
|
||||
export const CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES = [
|
||||
"read",
|
||||
"write",
|
||||
"edit",
|
||||
"apply_patch",
|
||||
"exec",
|
||||
"process",
|
||||
"update_plan",
|
||||
] as const;
|
||||
|
||||
export function applyCodexDynamicToolProfile<T extends { name: string }>(
|
||||
tools: T[],
|
||||
config: Pick<CodexPluginConfig, "codexDynamicToolsProfile" | "codexDynamicToolsExclude">,
|
||||
): T[] {
|
||||
const excludes = new Set<string>();
|
||||
const profile = config.codexDynamicToolsProfile ?? "native-first";
|
||||
if (profile === "native-first") {
|
||||
for (const name of CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES) {
|
||||
excludes.add(name);
|
||||
}
|
||||
}
|
||||
for (const name of config.codexDynamicToolsExclude ?? []) {
|
||||
const trimmed = name.trim();
|
||||
if (trimmed) {
|
||||
excludes.add(trimmed);
|
||||
}
|
||||
}
|
||||
return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(tool.name));
|
||||
}
|
||||
@@ -54,6 +54,7 @@ import {
|
||||
type CodexPluginConfig,
|
||||
} from "./config.js";
|
||||
import { projectContextEngineAssemblyForCodex } from "./context-engine-projection.js";
|
||||
import { applyCodexDynamicToolProfile } from "./dynamic-tool-profile.js";
|
||||
import { createCodexDynamicToolBridge, type CodexDynamicToolBridge } from "./dynamic-tools.js";
|
||||
import { handleCodexAppServerElicitationRequest } from "./elicitation-bridge.js";
|
||||
import { CodexAppServerEventProjector } from "./event-projector.js";
|
||||
@@ -99,15 +100,6 @@ const CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS = 3;
|
||||
const CODEX_TURN_COMPLETION_IDLE_TIMEOUT_MS = 60_000;
|
||||
const CODEX_TURN_TERMINAL_IDLE_TIMEOUT_MS = 30 * 60_000;
|
||||
const CODEX_STEER_ALL_DEBOUNCE_MS = 500;
|
||||
const CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES = [
|
||||
"read",
|
||||
"write",
|
||||
"edit",
|
||||
"apply_patch",
|
||||
"exec",
|
||||
"process",
|
||||
"update_plan",
|
||||
] as const;
|
||||
const LOG_FIELD_MAX_LENGTH = 160;
|
||||
|
||||
type OpenClawCodingToolsOptions = NonNullable<
|
||||
@@ -1499,26 +1491,6 @@ async function buildDynamicTools(input: DynamicToolBuildParams) {
|
||||
});
|
||||
}
|
||||
|
||||
function applyCodexDynamicToolProfile<T extends { name: string }>(
|
||||
tools: T[],
|
||||
config: CodexPluginConfig,
|
||||
): T[] {
|
||||
const excludes = new Set<string>();
|
||||
const profile = config.codexDynamicToolsProfile ?? "native-first";
|
||||
if (profile === "native-first") {
|
||||
for (const name of CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES) {
|
||||
excludes.add(name);
|
||||
}
|
||||
}
|
||||
for (const name of config.codexDynamicToolsExclude ?? []) {
|
||||
const trimmed = name.trim();
|
||||
if (trimmed) {
|
||||
excludes.add(trimmed);
|
||||
}
|
||||
}
|
||||
return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(tool.name));
|
||||
}
|
||||
|
||||
async function withCodexStartupTimeout<T>(params: {
|
||||
timeoutMs: number;
|
||||
timeoutFloorMs?: number;
|
||||
|
||||
@@ -97,25 +97,19 @@ export async function startOrResumeThread(params: {
|
||||
}
|
||||
}
|
||||
|
||||
const modelProvider = resolveCodexAppServerModelProvider(params.params.provider);
|
||||
const response = assertCodexThreadStartResponse(
|
||||
await params.client.request("thread/start", {
|
||||
model: params.params.modelId,
|
||||
...(modelProvider ? { modelProvider } : {}),
|
||||
cwd: params.cwd,
|
||||
approvalPolicy: params.appServer.approvalPolicy,
|
||||
approvalsReviewer: params.appServer.approvalsReviewer,
|
||||
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,
|
||||
experimentalRawEvents: true,
|
||||
persistExtendedHistory: true,
|
||||
} satisfies CodexThreadStartParams),
|
||||
await params.client.request(
|
||||
"thread/start",
|
||||
buildThreadStartParams(params.params, {
|
||||
cwd: params.cwd,
|
||||
dynamicTools: params.dynamicTools,
|
||||
appServer: params.appServer,
|
||||
developerInstructions: params.developerInstructions,
|
||||
config: params.config,
|
||||
}),
|
||||
),
|
||||
);
|
||||
const modelProvider = resolveCodexAppServerModelProvider(params.params.provider);
|
||||
const createdAt = new Date().toISOString();
|
||||
await writeCodexAppServerBinding(params.params.sessionFile, {
|
||||
threadId: response.thread.id,
|
||||
@@ -140,6 +134,34 @@ export async function startOrResumeThread(params: {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildThreadStartParams(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
options: {
|
||||
cwd: string;
|
||||
dynamicTools: CodexDynamicToolSpec[];
|
||||
appServer: CodexAppServerRuntimeOptions;
|
||||
developerInstructions?: string;
|
||||
config?: JsonObject;
|
||||
},
|
||||
): CodexThreadStartParams {
|
||||
const modelProvider = resolveCodexAppServerModelProvider(params.provider);
|
||||
return {
|
||||
model: params.modelId,
|
||||
...(modelProvider ? { modelProvider } : {}),
|
||||
cwd: options.cwd,
|
||||
approvalPolicy: options.appServer.approvalPolicy,
|
||||
approvalsReviewer: options.appServer.approvalsReviewer,
|
||||
sandbox: options.appServer.sandbox,
|
||||
...(options.appServer.serviceTier ? { serviceTier: options.appServer.serviceTier } : {}),
|
||||
serviceName: "OpenClaw",
|
||||
...(options.config ? { config: options.config } : {}),
|
||||
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params),
|
||||
dynamicTools: options.dynamicTools,
|
||||
experimentalRawEvents: true,
|
||||
persistExtendedHistory: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildThreadResumeParams(
|
||||
params: EmbeddedRunAttemptParams,
|
||||
options: {
|
||||
|
||||
79
extensions/codex/test-api.ts
Normal file
79
extensions/codex/test-api.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import type {
|
||||
AnyAgentTool,
|
||||
EmbeddedRunAttemptParams,
|
||||
} from "openclaw/plugin-sdk/agent-harness-runtime";
|
||||
import {
|
||||
type CodexAppServerRuntimeOptions,
|
||||
resolveCodexAppServerRuntimeOptions,
|
||||
} from "./src/app-server/config.js";
|
||||
import type { CodexPluginConfig } from "./src/app-server/config.js";
|
||||
import { applyCodexDynamicToolProfile } from "./src/app-server/dynamic-tool-profile.js";
|
||||
import { createCodexDynamicToolBridge } from "./src/app-server/dynamic-tools.js";
|
||||
import type { CodexDynamicToolSpec, JsonObject } from "./src/app-server/protocol.js";
|
||||
import {
|
||||
buildDeveloperInstructions,
|
||||
buildThreadResumeParams,
|
||||
buildThreadStartParams,
|
||||
buildTurnStartParams,
|
||||
} from "./src/app-server/thread-lifecycle.js";
|
||||
|
||||
type CodexHarnessPromptSnapshot = {
|
||||
developerInstructions: string;
|
||||
threadStartParams: ReturnType<typeof buildThreadStartParams>;
|
||||
threadResumeParams: ReturnType<typeof buildThreadResumeParams>;
|
||||
turnStartParams: ReturnType<typeof buildTurnStartParams>;
|
||||
};
|
||||
|
||||
export function resolveCodexPromptSnapshotAppServerOptions(
|
||||
pluginConfig?: unknown,
|
||||
): CodexAppServerRuntimeOptions {
|
||||
return resolveCodexAppServerRuntimeOptions({
|
||||
pluginConfig,
|
||||
env: {},
|
||||
});
|
||||
}
|
||||
|
||||
export function buildCodexHarnessPromptSnapshot(params: {
|
||||
attempt: EmbeddedRunAttemptParams;
|
||||
cwd: string;
|
||||
threadId: string;
|
||||
dynamicTools: CodexDynamicToolSpec[];
|
||||
appServer: CodexAppServerRuntimeOptions;
|
||||
config?: JsonObject;
|
||||
promptText?: string;
|
||||
}): CodexHarnessPromptSnapshot {
|
||||
const developerInstructions = buildDeveloperInstructions(params.attempt);
|
||||
return {
|
||||
developerInstructions,
|
||||
threadStartParams: buildThreadStartParams(params.attempt, {
|
||||
cwd: params.cwd,
|
||||
dynamicTools: params.dynamicTools,
|
||||
appServer: params.appServer,
|
||||
developerInstructions,
|
||||
config: params.config,
|
||||
}),
|
||||
threadResumeParams: buildThreadResumeParams(params.attempt, {
|
||||
threadId: params.threadId,
|
||||
appServer: params.appServer,
|
||||
developerInstructions,
|
||||
config: params.config,
|
||||
}),
|
||||
turnStartParams: buildTurnStartParams(params.attempt, {
|
||||
threadId: params.threadId,
|
||||
cwd: params.cwd,
|
||||
appServer: params.appServer,
|
||||
promptText: params.promptText,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function createCodexDynamicToolSpecsForPromptSnapshot(params: {
|
||||
tools: AnyAgentTool[];
|
||||
pluginConfig?: Pick<CodexPluginConfig, "codexDynamicToolsProfile" | "codexDynamicToolsExclude">;
|
||||
}): CodexDynamicToolSpec[] {
|
||||
const profiledTools = applyCodexDynamicToolProfile(params.tools, params.pluginConfig ?? {});
|
||||
return createCodexDynamicToolBridge({
|
||||
tools: profiledTools,
|
||||
signal: new AbortController().signal,
|
||||
}).specs;
|
||||
}
|
||||
@@ -54,6 +54,7 @@ type GeminiInlinePart = {
|
||||
inlineData: { mimeType: string; data: string };
|
||||
};
|
||||
type GeminiPart = GeminiTextPart | GeminiInlinePart;
|
||||
type GeminiEmbeddingInputPart = NonNullable<EmbeddingInput["parts"]>[number];
|
||||
type GeminiEmbeddingRequest = {
|
||||
content: { parts: GeminiPart[] };
|
||||
taskType: GeminiTaskType;
|
||||
@@ -85,7 +86,7 @@ export function buildGeminiEmbeddingRequest(params: {
|
||||
}): GeminiEmbeddingRequest {
|
||||
const request: GeminiEmbeddingRequest = {
|
||||
content: {
|
||||
parts: params.input.parts?.map((part) =>
|
||||
parts: params.input.parts?.map((part: GeminiEmbeddingInputPart) =>
|
||||
part.type === "text"
|
||||
? ({ text: part.text } satisfies GeminiTextPart)
|
||||
: ({
|
||||
|
||||
Reference in New Issue
Block a user