























@@ -95,6 +95,7 @@ import {
9595type CodexUserInput,
9696isJsonObject,
9797type CodexServerNotification,
98+type CodexDynamicToolSpec,
9899type CodexDynamicToolCallParams,
99100type CodexDynamicToolCallResponse,
100101type CodexTurnStartResponse,
@@ -158,6 +159,11 @@ type OpenClawCodingToolsOptions = NonNullable<
158159>;
159160type OpenClawCodingToolsFactory =
160161(typeof import("openclaw/plugin-sdk/agent-harness"))["createOpenClawCodingTools"];
162+type CodexBootstrapContext = Awaited<ReturnType<typeof resolveBootstrapContextForRun>>;
163+type CodexBootstrapFile = CodexBootstrapContext["bootstrapFiles"][number];
164+type CodexSystemPromptReport = NonNullable<EmbeddedRunAttemptResult["systemPromptReport"]>;
165+type CodexToolReportEntry = CodexSystemPromptReport["tools"]["entries"][number];
166+type CodexWorkspaceBootstrapContext = CodexBootstrapContext & { instructions?: string };
161167162168const testClientFactoryStorage = new AsyncLocalStorage<CodexAppServerClientFactory | undefined>();
163169const clientFactory = defaultCodexAppServerClientFactory;
@@ -571,13 +577,14 @@ export async function runCodexAppServerAttempt(
571577// Build the workspace bootstrap block before finalizing developer
572578// instructions so persona files (SOUL.md, IDENTITY.md, ...) reach Codex
573579// through the explicit `developerInstructions` field.
574-const workspaceBootstrapInstructions = await buildCodexWorkspaceBootstrapInstructions({
580+const workspaceBootstrapContext = await buildCodexWorkspaceBootstrapContext({
575581 params,
576582 resolvedWorkspace,
577583 effectiveWorkspace,
578584sessionKey: sandboxSessionKey,
579585 sessionAgentId,
580586});
587+const workspaceBootstrapInstructions = workspaceBootstrapContext.instructions;
581588let promptText = params.prompt;
582589let developerInstructions = joinPresentSections(
583590baseDeveloperInstructions,
@@ -639,6 +646,14 @@ export async function runCodexAppServerAttempt(
639646messages: historyMessages,
640647ctx: hookContext,
641648});
649+const systemPromptReport = buildCodexSystemPromptReport({
650+attempt: params,
651+sessionKey: sandboxSessionKey,
652+workspaceDir: effectiveWorkspace,
653+developerInstructions: promptBuild.developerInstructions,
654+ workspaceBootstrapContext,
655+tools: toolBridge.specs,
656+});
642657const trajectoryRecorder = createCodexTrajectoryRecorder({
643658attempt: params,
644659cwd: effectiveWorkspace,
@@ -1528,6 +1543,7 @@ export async function runCodexAppServerAttempt(
15281543aborted: finalAborted,
15291544promptError: finalPromptError,
15301545promptErrorSource: finalPromptErrorSource,
1546+ systemPromptReport,
15311547};
15321548} finally {
15331549emitLifecycleTerminal({
@@ -2195,15 +2211,15 @@ async function readMirroredSessionHistoryMessages(
21952211return messages;
21962212}
219722132198-async function buildCodexWorkspaceBootstrapInstructions(params: {
2214+async function buildCodexWorkspaceBootstrapContext(params: {
21992215params: EmbeddedRunAttemptParams;
22002216resolvedWorkspace: string;
22012217effectiveWorkspace: string;
22022218sessionKey: string;
22032219sessionAgentId: string;
2204-}): Promise<string | undefined> {
2220+}): Promise<CodexWorkspaceBootstrapContext> {
22052221try {
2206-const { contextFiles } = await resolveBootstrapContextForRun({
2222+const bootstrapContext = await resolveBootstrapContextForRun({
22072223workspaceDir: params.resolvedWorkspace,
22082224config: params.params.config,
22092225sessionKey: params.sessionKey,
@@ -2213,19 +2229,156 @@ async function buildCodexWorkspaceBootstrapInstructions(params: {
22132229contextMode: params.params.bootstrapContextMode,
22142230runKind: params.params.bootstrapContextRunKind,
22152231});
2216-return renderCodexWorkspaceBootstrapInstructions(
2217-contextFiles.map((file) =>
2218-remapCodexContextFilePath({
2219- file,
2220-sourceWorkspaceDir: params.resolvedWorkspace,
2221-targetWorkspaceDir: params.effectiveWorkspace,
2222-}),
2223-),
2232+const contextFiles = bootstrapContext.contextFiles.map((file) =>
2233+remapCodexContextFilePath({
2234+ file,
2235+sourceWorkspaceDir: params.resolvedWorkspace,
2236+targetWorkspaceDir: params.effectiveWorkspace,
2237+}),
22242238);
2239+return {
2240+ ...bootstrapContext,
2241+ contextFiles,
2242+instructions: renderCodexWorkspaceBootstrapInstructions(contextFiles),
2243+};
22252244} catch (error) {
22262245embeddedAgentLog.warn("failed to load codex workspace bootstrap instructions", { error });
2227-return undefined;
2246+return { bootstrapFiles: [], contextFiles: [] };
2247+}
2248+}
2249+2250+function buildCodexSystemPromptReport(params: {
2251+attempt: EmbeddedRunAttemptParams;
2252+sessionKey: string;
2253+workspaceDir: string;
2254+developerInstructions: string;
2255+workspaceBootstrapContext: CodexWorkspaceBootstrapContext;
2256+tools: CodexDynamicToolSpec[];
2257+}): CodexSystemPromptReport {
2258+const toolEntries = params.tools.map(buildCodexToolReportEntry);
2259+const schemaChars = toolEntries.reduce((sum, tool) => sum + tool.schemaChars, 0);
2260+const projectContextChars = params.workspaceBootstrapContext.instructions?.length ?? 0;
2261+const bootstrapMaxChars = readPositiveNumber(
2262+params.attempt.config?.agents?.defaults?.bootstrapMaxChars,
2263+);
2264+const bootstrapTotalMaxChars = readPositiveNumber(
2265+params.attempt.config?.agents?.defaults?.bootstrapTotalMaxChars,
2266+);
2267+return {
2268+source: "run",
2269+generatedAt: Date.now(),
2270+sessionId: params.attempt.sessionId,
2271+sessionKey: params.sessionKey,
2272+provider: params.attempt.provider,
2273+model: params.attempt.modelId,
2274+workspaceDir: params.workspaceDir,
2275+ ...(bootstrapMaxChars ? { bootstrapMaxChars } : {}),
2276+ ...(bootstrapTotalMaxChars ? { bootstrapTotalMaxChars } : {}),
2277+systemPrompt: {
2278+chars: params.developerInstructions.length,
2279+ projectContextChars,
2280+nonProjectContextChars: Math.max(
2281+0,
2282+params.developerInstructions.length - projectContextChars,
2283+),
2284+},
2285+injectedWorkspaceFiles: buildCodexBootstrapInjectionStats({
2286+bootstrapFiles: params.workspaceBootstrapContext.bootstrapFiles,
2287+injectedFiles: params.workspaceBootstrapContext.contextFiles,
2288+}),
2289+skills: {
2290+promptChars: 0,
2291+entries: [],
2292+},
2293+tools: {
2294+listChars: 0,
2295+ schemaChars,
2296+entries: toolEntries,
2297+},
2298+};
2299+}
2300+2301+function buildCodexToolReportEntry(tool: CodexDynamicToolSpec): CodexToolReportEntry {
2302+const summary = tool.description.trim();
2303+if (tool.deferLoading === true) {
2304+return {
2305+name: tool.name,
2306+summaryChars: summary.length,
2307+schemaChars: 0,
2308+propertiesCount: null,
2309+};
2310+}
2311+return {
2312+name: tool.name,
2313+summaryChars: summary.length,
2314+ ...buildCodexToolSchemaStats(tool.inputSchema),
2315+};
2316+}
2317+2318+function buildCodexToolSchemaStats(
2319+schema: JsonValue,
2320+): Pick<CodexToolReportEntry, "schemaChars" | "propertiesCount"> {
2321+const schemaChars = (() => {
2322+try {
2323+return JSON.stringify(schema).length;
2324+} catch {
2325+return 0;
2326+}
2327+})();
2328+const properties =
2329+isJsonObject(schema) && isJsonObject(schema.properties) ? schema.properties : null;
2330+return {
2331+ schemaChars,
2332+propertiesCount: properties ? Object.keys(properties).length : null,
2333+};
2334+}
2335+2336+function buildCodexBootstrapInjectionStats(params: {
2337+bootstrapFiles: CodexBootstrapFile[];
2338+injectedFiles: EmbeddedContextFile[];
2339+}): CodexSystemPromptReport["injectedWorkspaceFiles"] {
2340+const injectedByPath = new Map<string, string>();
2341+const injectedByBaseName = new Map<string, string>();
2342+for (const file of params.injectedFiles) {
2343+const pathValue = readNonEmptyString(file.path);
2344+if (!pathValue) {
2345+continue;
2346+}
2347+if (!injectedByPath.has(pathValue)) {
2348+injectedByPath.set(pathValue, file.content);
2349+}
2350+const baseName = path.posix.basename(pathValue.replaceAll("\\", "/"));
2351+if (!injectedByBaseName.has(baseName)) {
2352+injectedByBaseName.set(baseName, file.content);
2353+}
22282354}
2355+return params.bootstrapFiles.map((file) => {
2356+const pathValue = readNonEmptyString(file.path) ?? file.name;
2357+const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
2358+const injected =
2359+injectedByPath.get(pathValue) ??
2360+injectedByPath.get(file.name) ??
2361+injectedByBaseName.get(file.name);
2362+const injectedChars = injected?.length ?? 0;
2363+return {
2364+name: file.name,
2365+path: pathValue,
2366+missing: file.missing,
2367+ rawChars,
2368+ injectedChars,
2369+truncated: !file.missing && injectedChars < rawChars,
2370+};
2371+});
2372+}
2373+2374+function readPositiveNumber(value: unknown): number | undefined {
2375+return typeof value === "number" && Number.isFinite(value) && value > 0
2376+ ? Math.floor(value)
2377+ : undefined;
2378+}
2379+2380+function readNonEmptyString(value: unknown): string | undefined {
2381+return typeof value === "string" && value.trim().length > 0 ? value : undefined;
22292382}
2230238322312384function renderCodexWorkspaceBootstrapInstructions(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。