Skip to content

Commit 0928610

Browse files
committed
fix(gateway): persist trailing @Profile suffix as session auth profile override (#87099)
1 parent 8fa5ecb commit 0928610

3 files changed

Lines changed: 56 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Docs: https://docs.openclaw.ai
88

99
### Fixes
1010

11+
- Gateway: persist `/model ...@profile` suffix as session auth profile override so documented credential pinning reaches the session entry. (#87099)
12+
1113
- Browser/snapshot: validate current tab URLs against the configured SSRF policy before ChromeMCP or direct CDP snapshot reads, closing the local-managed CDP bypass from GHSA-2x93-h3hg-2xfp while preserving existing-session coverage; the PR also rejects existing-session selectors before URL checks, adds focused route coverage, fetches full opengrep CI history, and stabilizes plugin activation normalization tests. Thanks @zsxsoft.
1214

1315
- Crabbox: bootstrap raw AWS macOS JavaScript commands launched through `/usr/bin/env` so native mac runners without preinstalled Node, Corepack, or pnpm can still run wrapped Node and pnpm proof.

src/gateway/sessions-patch.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,4 +804,55 @@ describe("gateway sessions patch", () => {
804804
expect(entry.providerOverride).toBe("synthetic");
805805
expect(entry.modelOverride).toBe("hf:moonshotai/Kimi-K2.5");
806806
});
807+
808+
test("persists trailing @profile suffix as authProfileOverride on model patch", async () => {
809+
const entry = expectPatchOk(
810+
await runPatch({
811+
cfg: createAllowlistedAnthropicModelCfg(),
812+
patch: { key: MAIN_SESSION_KEY, model: "anthropic/claude-sonnet-4-6@myprofile" },
813+
loadGatewayModelCatalog: async () => [
814+
{ provider: "anthropic", id: "claude-sonnet-4-6", name: "claude-sonnet-4-6" },
815+
],
816+
}),
817+
);
818+
expect(entry.providerOverride).toBe("anthropic");
819+
expect(entry.modelOverride).toBe("claude-sonnet-4-6");
820+
expect(entry.authProfileOverride).toBe("myprofile");
821+
expect(entry.authProfileOverrideSource).toBe("user");
822+
expect(entry.liveModelSwitchPending).toBe(true);
823+
});
824+
825+
test("does not set authProfileOverride when profile suffix is missing", async () => {
826+
const entry = expectPatchOk(
827+
await runPatch({
828+
cfg: createAllowlistedAnthropicModelCfg(),
829+
patch: { key: MAIN_SESSION_KEY, model: "anthropic/claude-sonnet-4-6" },
830+
loadGatewayModelCatalog: async () => [
831+
{ provider: "anthropic", id: "claude-sonnet-4-6", name: "claude-sonnet-4-6" },
832+
],
833+
}),
834+
);
835+
expect(entry.providerOverride).toBe("anthropic");
836+
expect(entry.modelOverride).toBe("claude-sonnet-4-6");
837+
expect(entry.authProfileOverride).toBeUndefined();
838+
});
839+
840+
test("persists full provider:profile authProfileOverride on model patch", async () => {
841+
const entry = expectPatchOk(
842+
await runPatch({
843+
cfg: createAllowlistedAnthropicModelCfg(),
844+
patch: {
845+
key: MAIN_SESSION_KEY,
846+
model: "anthropic/claude-sonnet-4-6@openai-codex:[email protected]",
847+
},
848+
loadGatewayModelCatalog: async () => [
849+
{ provider: "anthropic", id: "claude-sonnet-4-6", name: "claude-sonnet-4-6" },
850+
],
851+
}),
852+
);
853+
expect(entry.providerOverride).toBe("anthropic");
854+
expect(entry.modelOverride).toBe("claude-sonnet-4-6");
855+
expect(entry.authProfileOverride).toBe("openai-codex:[email protected]");
856+
expect(entry.authProfileOverrideSource).toBe("user");
857+
});
807858
});

src/gateway/sessions-patch.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
normalizeInheritedToolDenylist,
66
} from "../agents/inherited-tool-deny.js";
77
import type { ModelCatalogEntry } from "../agents/model-catalog.js";
8+
import { splitTrailingAuthProfile } from "../agents/model-ref-profile.js";
89
import {
910
resolveAllowedModelRef,
1011
resolveDefaultModelForAgent,
@@ -525,6 +526,7 @@ export async function applySessionsPatchToStore(params: {
525526
error: errorShape(ErrorCodes.UNAVAILABLE, "model catalog unavailable"),
526527
};
527528
}
529+
const { profile: trailingProfile } = splitTrailingAuthProfile(trimmed);
528530
const resolved = resolveAllowedModelRef({
529531
cfg,
530532
catalog,
@@ -545,6 +547,7 @@ export async function applySessionsPatchToStore(params: {
545547
model: resolved.ref.model,
546548
isDefault,
547549
},
550+
profileOverride: trailingProfile || undefined,
548551
preserveAuthProfileOverride: shouldPreserveSessionAuthProfileOverride({
549552
cfg,
550553
currentProvider: next.providerOverride ?? next.modelProvider ?? resolvedDefault.provider,

0 commit comments

Comments
 (0)