mirror of https://github.com/openclaw/openclaw.git
fix: restore protocol outputs and stabilize Windows path CI (#44266)
* fix(ci): restore protocol outputs and stabilize Windows path test Regenerate the Swift protocol models so protocol:check stops failing on main. Align the session target test helper with the sync production realpath behavior so Windows does not compare runneradmin and RUNNER~1 spellings for the same file. Regeneration-Prompt: | Investigate the failing checks from merged PR #34485 and confirm whether they still affect current main before changing code. Keep the fix tight: do not alter runtime behavior beyond what is required to clear the reproduced CI regressions. Commit the generated Swift protocol outputs for the PushTestResult transport field because protocol:check was failing from stale generated files on main. Also fix the Windows-only session target test by making its helper use the same synchronous realpath behavior as production discovery, so path spelling differences like runneradmin versus RUNNER~1 do not cause a false assertion failure. * fix(ci): align session target realpath behavior on Windows Use native realpath for sync session target discovery so it matches the async path on Windows, and update the session target test helper to assert against the same canonical path form. Regeneration-Prompt: | After opening the follow-up PR for the CI regressions from merged PR #34485, inspect the new failing Windows shard instead of assuming the first fix covered every case. Keep scope limited to the session target path mismatch exposed by CI. Fix the inconsistency at the source by making sync session target discovery use the same native realpath canonicalization as the async discovery path on Windows, then update the test helper to match that shared behavior and verify the touched file with targeted tests and file-scoped lint/format checks. * test: make merge config fixtures satisfy provider type After rebasing the PR onto current origin/main, the merge helper test fixtures no longer satisfied ProviderConfig because the anthropic provider examples were missing required provider and model fields. Add a shared fully-typed model fixture and explicit anthropic baseUrl values so the test keeps full type coverage under tsgo. Regeneration-Prompt: | Rebase the PR branch for #44266 onto the current origin/main because the failing CI error only reproduced on the merge ref. Re-run the type-check path and inspect src/agents/models-config.merge.test.ts at the exact compiler lines instead of weakening types globally. Keep the fix test-only: make the anthropic ProviderConfig fixtures structurally valid by supplying the required baseUrl and full model definition fields, and keep the shared fixture typed so tsgo accepts it without unknown casts. * fix: align Windows session store test expectations
This commit is contained in:
parent
f76a3c5225
commit
9cb0fa58c2
|
|
@ -48,6 +48,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Security/exec approvals: fail closed for ambiguous inline loader and shell-payload script execution, bind the real script after POSIX shell value-taking flags, and unwrap `pnpm`/`npm exec`/`npx` script runners before approval binding. (`GHSA-57jw-9722-6rf2`)(`GHSA-jvqh-rfmh-jh27`)(`GHSA-x7pp-23xv-mmr4`)(`GHSA-jc5j-vg4r-j5jx`)(#44247) Thanks @tdjackey and @vincentkoc.
|
||||
- Doctor/gateway service audit: canonicalize service entrypoint paths before comparing them so symlink-vs-realpath installs no longer trigger false "entrypoint does not match the current install" repair prompts. (#43882) Thanks @ngutman.
|
||||
- Doctor/gateway service audit: earlier groundwork for this fix landed in the superseded #28338 branch. Thanks @realriphub.
|
||||
- Gateway/session stores: regenerate the Swift push-test protocol models and align Windows native session-store realpath handling so protocol checks and sync session discovery stop drifting on Windows. (#44266) thanks @jalehman.
|
||||
|
||||
## 2026.3.11
|
||||
|
||||
|
|
|
|||
|
|
@ -1106,6 +1106,7 @@ public struct PushTestResult: Codable, Sendable {
|
|||
public let tokensuffix: String
|
||||
public let topic: String
|
||||
public let environment: String
|
||||
public let transport: String
|
||||
|
||||
public init(
|
||||
ok: Bool,
|
||||
|
|
@ -1114,7 +1115,8 @@ public struct PushTestResult: Codable, Sendable {
|
|||
reason: String?,
|
||||
tokensuffix: String,
|
||||
topic: String,
|
||||
environment: String)
|
||||
environment: String,
|
||||
transport: String)
|
||||
{
|
||||
self.ok = ok
|
||||
self.status = status
|
||||
|
|
@ -1123,6 +1125,7 @@ public struct PushTestResult: Codable, Sendable {
|
|||
self.tokensuffix = tokensuffix
|
||||
self.topic = topic
|
||||
self.environment = environment
|
||||
self.transport = transport
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
|
|
@ -1133,6 +1136,7 @@ public struct PushTestResult: Codable, Sendable {
|
|||
case tokensuffix = "tokenSuffix"
|
||||
case topic
|
||||
case environment
|
||||
case transport
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1106,6 +1106,7 @@ public struct PushTestResult: Codable, Sendable {
|
|||
public let tokensuffix: String
|
||||
public let topic: String
|
||||
public let environment: String
|
||||
public let transport: String
|
||||
|
||||
public init(
|
||||
ok: Bool,
|
||||
|
|
@ -1114,7 +1115,8 @@ public struct PushTestResult: Codable, Sendable {
|
|||
reason: String?,
|
||||
tokensuffix: String,
|
||||
topic: String,
|
||||
environment: String)
|
||||
environment: String,
|
||||
transport: String)
|
||||
{
|
||||
self.ok = ok
|
||||
self.status = status
|
||||
|
|
@ -1123,6 +1125,7 @@ public struct PushTestResult: Codable, Sendable {
|
|||
self.tokensuffix = tokensuffix
|
||||
self.topic = topic
|
||||
self.environment = environment
|
||||
self.transport = transport
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
|
|
@ -1133,6 +1136,7 @@ public struct PushTestResult: Codable, Sendable {
|
|||
case tokensuffix = "tokenSuffix"
|
||||
case topic
|
||||
case environment
|
||||
case transport
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,20 @@ import type { ProviderConfig } from "./models-config.providers.js";
|
|||
|
||||
describe("models-config merge helpers", () => {
|
||||
const preservedApiKey = "AGENT_KEY"; // pragma: allowlist secret
|
||||
const kimiModel: ProviderConfig["models"][number] = {
|
||||
id: "k2p5",
|
||||
name: "Kimi for Coding",
|
||||
input: ["text", "image"],
|
||||
reasoning: true,
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128_000,
|
||||
maxTokens: 8_000,
|
||||
};
|
||||
|
||||
it("refreshes implicit model metadata while preserving explicit reasoning overrides", () => {
|
||||
const merged = mergeProviderModels(
|
||||
|
|
@ -70,27 +84,15 @@ describe("models-config merge helpers", () => {
|
|||
const merged = mergeProviderModels(
|
||||
{
|
||||
api: "anthropic-messages",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
headers: { "User-Agent": "claude-code/0.1.0" },
|
||||
models: [
|
||||
{
|
||||
id: "k2p5",
|
||||
name: "Kimi for Coding",
|
||||
input: ["text", "image"],
|
||||
reasoning: true,
|
||||
},
|
||||
],
|
||||
models: [kimiModel],
|
||||
} as ProviderConfig,
|
||||
{
|
||||
api: "anthropic-messages",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
headers: { "X-Kimi-Tenant": "tenant-a" },
|
||||
models: [
|
||||
{
|
||||
id: "k2p5",
|
||||
name: "Kimi for Coding",
|
||||
input: ["text", "image"],
|
||||
reasoning: true,
|
||||
},
|
||||
],
|
||||
models: [kimiModel],
|
||||
} as ProviderConfig,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import fsSync from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
|
@ -10,7 +11,8 @@ import {
|
|||
} from "./targets.js";
|
||||
|
||||
async function resolveRealStorePath(sessionsDir: string): Promise<string> {
|
||||
return await fs.realpath(path.join(sessionsDir, "sessions.json"));
|
||||
// Match the native realpath behavior used by both discovery paths.
|
||||
return fsSync.realpathSync.native(path.join(sessionsDir, "sessions.json"));
|
||||
}
|
||||
|
||||
describe("resolveSessionStoreTargets", () => {
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ function resolveValidatedDiscoveredStorePathSync(params: {
|
|||
if (stat.isSymbolicLink() || !stat.isFile()) {
|
||||
return undefined;
|
||||
}
|
||||
const realStorePath = fsSync.realpathSync(storePath);
|
||||
const realAgentsRoot = params.realAgentsRoot ?? fsSync.realpathSync(params.agentsRoot);
|
||||
const realStorePath = fsSync.realpathSync.native(storePath);
|
||||
const realAgentsRoot = params.realAgentsRoot ?? fsSync.realpathSync.native(params.agentsRoot);
|
||||
return isWithinRoot(realStorePath, realAgentsRoot) ? realStorePath : undefined;
|
||||
} catch (err) {
|
||||
if (shouldSkipDiscoveryError(err)) {
|
||||
|
|
@ -153,7 +153,7 @@ export function resolveAllAgentSessionStoreTargetsSync(
|
|||
return cached;
|
||||
}
|
||||
try {
|
||||
const realAgentsRoot = fsSync.realpathSync(agentsRoot);
|
||||
const realAgentsRoot = fsSync.realpathSync.native(agentsRoot);
|
||||
realAgentsRoots.set(agentsRoot, realAgentsRoot);
|
||||
return realAgentsRoot;
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ import {
|
|||
resolveSessionStoreKey,
|
||||
} from "./session-utils.js";
|
||||
|
||||
function resolveSyncRealpath(filePath: string): string {
|
||||
return fs.realpathSync.native(filePath);
|
||||
}
|
||||
|
||||
function createSymlinkOrSkip(targetPath: string, linkPath: string): boolean {
|
||||
try {
|
||||
fs.symlinkSync(targetPath, linkPath);
|
||||
|
|
@ -287,7 +291,7 @@ describe("gateway session utils", () => {
|
|||
|
||||
const target = resolveGatewaySessionStoreTarget({ cfg, key: "agent:retired-agent:main" });
|
||||
|
||||
expect(target.storePath).toBe(fs.realpathSync(retiredStorePath));
|
||||
expect(target.storePath).toBe(resolveSyncRealpath(retiredStorePath));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -316,7 +320,7 @@ describe("gateway session utils", () => {
|
|||
|
||||
const loaded = loadSessionEntry("agent:retired-agent:main");
|
||||
|
||||
expect(loaded.storePath).toBe(fs.realpathSync(retiredStorePath));
|
||||
expect(loaded.storePath).toBe(resolveSyncRealpath(retiredStorePath));
|
||||
expect(loaded.entry?.sessionId).toBe("sess-retired");
|
||||
});
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Reference in New Issue