fix: stabilize Codex harness landing checks

This commit is contained in:
Peter Steinberger
2026-04-10 19:25:35 +01:00
parent d5698038d7
commit 3198c10fba
8 changed files with 60 additions and 46 deletions

View File

@@ -16,7 +16,7 @@ const sourceRoots = ["src/channels", "src/routing", "src/line", "extensions"];
const allowedRawFetchCallsites = new Set([
bundledPluginCallsite("bluebubbles", "src/test-harness.ts", 128),
bundledPluginCallsite("bluebubbles", "src/types.ts", 181),
bundledPluginCallsite("browser", "src/browser/cdp.helpers.ts", 235),
bundledPluginCallsite("browser", "src/browser/cdp.helpers.ts", 253),
bundledPluginCallsite("browser", "src/browser/client-fetch.ts", 192),
bundledPluginCallsite("browser", "src/browser/test-fetch.ts", 24),
bundledPluginCallsite("browser", "src/browser/test-fetch.ts", 27),
@@ -41,8 +41,8 @@ const allowedRawFetchCallsites = new Set([
bundledPluginCallsite("minimax", "oauth.ts", 107),
bundledPluginCallsite("minimax", "tts.ts", 52),
bundledPluginCallsite("msteams", "src/graph.ts", 47),
bundledPluginCallsite("msteams", "src/sdk.ts", 292),
bundledPluginCallsite("msteams", "src/sdk.ts", 333),
bundledPluginCallsite("msteams", "src/sdk.ts", 329),
bundledPluginCallsite("msteams", "src/sdk.ts", 370),
bundledPluginCallsite("ollama", "src/stream.ts", 649),
bundledPluginCallsite("openai", "tts.ts", 133),
bundledPluginCallsite("qa-channel", "src/bus-client.ts", 41),

View File

@@ -29,6 +29,7 @@ vi.mock("@mariozechner/pi-ai", async () => {
});
vi.mock("@mariozechner/pi-coding-agent", () => ({
generateSummary: vi.fn(async () => "summary"),
SessionManager: {
open: () => ({
getLeafEntry: getLeafEntryMock,

View File

@@ -269,6 +269,7 @@ export async function loadCompactHooksHarness(): Promise<{
create: vi.fn(() => ({})),
},
estimateTokens: estimateTokensMock,
generateSummary: vi.fn(async () => "summary"),
}));
vi.doMock("../session-tool-result-guard-wrapper.js", () => ({
@@ -457,6 +458,7 @@ export async function loadCompactHooksHarness(): Promise<{
}));
vi.doMock("../agent-scope.js", () => ({
listAgentEntries: vi.fn(() => []),
resolveSessionAgentId: resolveSessionAgentIdMock,
resolveSessionAgentIds: vi.fn(() => ({ defaultAgentId: "main", sessionAgentId: "main" })),
}));

View File

@@ -2954,6 +2954,19 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
},
additionalProperties: {},
},
embeddedHarness: {
type: "object",
properties: {
runtime: {
type: "string",
},
fallback: {
type: "string",
enum: ["pi", "none"],
},
},
additionalProperties: false,
},
model: {
anyOf: [
{
@@ -5415,6 +5428,19 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
systemPromptOverride: {
type: "string",
},
embeddedHarness: {
type: "object",
properties: {
runtime: {
type: "string",
},
fallback: {
type: "string",
enum: ["pi", "none"],
},
},
additionalProperties: false,
},
model: {
anyOf: [
{

View File

@@ -46,6 +46,7 @@ vi.mock("../agents/subagent-registry.js", () => ({
}));
vi.mock("../config/paths.js", () => ({
STATE_DIR: "/tmp/openclaw-state",
resolveStateDir: vi.fn(() => "/tmp/openclaw-state"),
}));

View File

@@ -143,6 +143,7 @@ describe("gateway startup primary model warmup", () => {
expect(selectAgentHarnessMock).toHaveBeenCalledWith({
provider: "codex",
modelId: "gpt-5.4",
config: cfg,
});
expect(ensureOpenClawModelsJsonMock).not.toHaveBeenCalled();
expect(resolveModelMock).not.toHaveBeenCalled();
@@ -188,6 +189,7 @@ describe("gateway startup primary model warmup", () => {
expect(selectAgentHarnessMock).toHaveBeenCalledWith({
provider: "openai-codex",
modelId: "gpt-5.4",
config: cfg,
});
expect(ensureOpenClawModelsJsonMock).toHaveBeenCalledWith(cfg, "/tmp/agent");
expect(resolveModelMock).toHaveBeenCalled();

View File

@@ -218,7 +218,13 @@ export async function installPackageDir(params: {
candidatePaths: [canonicalTargetDir],
});
stageDir = await fs.mkdtemp(path.join(installBaseRealPath, ".openclaw-install-stage-"));
await fs.cp(params.sourceDir, stageDir, { recursive: true, verbatimSymlinks: true });
await fs.cp(params.sourceDir, stageDir, {
recursive: true,
// Keep relative symlinks relative to the staged copy. Node's default
// rewrites them toward the source tree, which makes valid vendored
// package links look like install-root escapes during post-copy scans.
verbatimSymlinks: true,
});
} catch (err) {
return await fail(`${params.copyErrorPrefix}: ${String(err)}`, err);
}

View File

@@ -187,9 +187,11 @@ async function inspectNodeModulesSymlinkTarget(params: {
const resolvedTargetStats = await fs.stat(resolvedTargetPath);
const resolvedTargetRelativePath = path.relative(params.rootRealPath, resolvedTargetPath);
return {
blockedDirectoryFinding: findBlockedPackageDirectoryInPath({
pathRelativeToRoot: resolvedTargetRelativePath,
}),
blockedDirectoryFinding: resolvedTargetStats.isDirectory()
? findBlockedPackageDirectoryInPath({
pathRelativeToRoot: resolvedTargetRelativePath,
})
: undefined,
blockedFileFinding: resolvedTargetStats.isFile()
? findBlockedPackageFileAliasInPath({
pathRelativeToRoot: resolvedTargetRelativePath,
@@ -262,16 +264,6 @@ function resolvePackageManifestTraversalLimits(): PackageManifestTraversalLimits
};
}
async function resolvePackageManifestPath(dir: string): Promise<string | undefined> {
const manifestPath = path.join(dir, "package.json");
try {
const stats = await fs.stat(manifestPath);
return stats.isFile() ? manifestPath : undefined;
} catch {
return undefined;
}
}
async function collectPackageManifestPaths(
rootDir: string,
): Promise<PackageManifestTraversalResult> {
@@ -280,6 +272,8 @@ async function collectPackageManifestPaths(
const queue: Array<{ depth: number; dir: string }> = [{ depth: 0, dir: rootDir }];
const packageManifestPaths: string[] = [];
const visitedDirectories = new Set<string>();
let firstBlockedDirectoryFinding: BlockedPackageDirectoryFinding | undefined;
let firstBlockedFileFinding: BlockedPackageFileFinding | undefined;
let queueIndex = 0;
while (queueIndex < queue.length) {
@@ -331,19 +325,13 @@ async function collectPackageManifestPaths(
directoryRelativePath: relativeNextPath,
});
if (blockedDirectoryFinding) {
return {
blockedDirectoryFinding,
packageManifestPaths,
};
firstBlockedDirectoryFinding ??= blockedDirectoryFinding;
}
const blockedFileFinding = findBlockedNodeModulesFileAlias({
fileRelativePath: relativeNextPath,
});
if (blockedFileFinding) {
return {
blockedFileFinding,
packageManifestPaths,
};
firstBlockedFileFinding ??= blockedFileFinding;
}
if (pathContainsNodeModulesSegment(relativeNextPath)) {
const symlinkTargetInspection = await inspectNodeModulesSymlinkTarget({
@@ -352,16 +340,10 @@ async function collectPackageManifestPaths(
symlinkRelativePath: relativeNextPath,
});
if (symlinkTargetInspection.blockedDirectoryFinding) {
return {
blockedDirectoryFinding: symlinkTargetInspection.blockedDirectoryFinding,
packageManifestPaths,
};
firstBlockedDirectoryFinding ??= symlinkTargetInspection.blockedDirectoryFinding;
}
if (symlinkTargetInspection.blockedFileFinding) {
return {
blockedFileFinding: symlinkTargetInspection.blockedFileFinding,
packageManifestPaths,
};
firstBlockedFileFinding ??= symlinkTargetInspection.blockedFileFinding;
}
}
continue;
@@ -371,14 +353,7 @@ async function collectPackageManifestPaths(
directoryRelativePath: relativeNextPath,
});
if (blockedDirectoryFinding) {
const manifestPath = await resolvePackageManifestPath(nextPath);
if (manifestPath) {
packageManifestPaths.push(manifestPath);
}
return {
blockedDirectoryFinding,
packageManifestPaths,
};
firstBlockedDirectoryFinding ??= blockedDirectoryFinding;
}
queue.push({ depth: current.depth + 1, dir: nextPath });
continue;
@@ -388,10 +363,7 @@ async function collectPackageManifestPaths(
fileRelativePath: relativeNextPath,
});
if (blockedFileFinding) {
return {
blockedFileFinding,
packageManifestPaths,
};
firstBlockedFileFinding ??= blockedFileFinding;
}
}
if (entry.isFile() && entry.name === "package.json") {
@@ -405,7 +377,11 @@ async function collectPackageManifestPaths(
}
}
return { packageManifestPaths };
return {
packageManifestPaths,
blockedDirectoryFinding: firstBlockedDirectoryFinding,
blockedFileFinding: firstBlockedFileFinding,
};
}
async function scanManifestDependencyDenylist(params: {