mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-29 01:52:04 +00:00
fix(plugins): forward plugin subagent overrides (#48277)
Merged via squash.
Prepared head SHA: ffa45893e0
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
@@ -303,6 +303,107 @@ describe("gateway agent handler", () => {
|
||||
expect(capturedEntry?.acp).toEqual(existingAcpMeta);
|
||||
});
|
||||
|
||||
it("forwards provider and model overrides for admin-scoped callers", async () => {
|
||||
primeMainAgentRun();
|
||||
|
||||
await invokeAgent(
|
||||
{
|
||||
message: "test override",
|
||||
agentId: "main",
|
||||
sessionKey: "agent:main:main",
|
||||
provider: "anthropic",
|
||||
model: "claude-haiku-4-5",
|
||||
idempotencyKey: "test-idem-model-override",
|
||||
},
|
||||
{
|
||||
reqId: "test-idem-model-override",
|
||||
client: {
|
||||
connect: {
|
||||
scopes: ["operator.admin"],
|
||||
},
|
||||
} as AgentHandlerArgs["client"],
|
||||
},
|
||||
);
|
||||
|
||||
const lastCall = mocks.agentCommand.mock.calls.at(-1);
|
||||
expect(lastCall?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
provider: "anthropic",
|
||||
model: "claude-haiku-4-5",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects provider and model overrides for write-scoped callers", async () => {
|
||||
primeMainAgentRun();
|
||||
mocks.agentCommand.mockClear();
|
||||
const respond = vi.fn();
|
||||
|
||||
await invokeAgent(
|
||||
{
|
||||
message: "test override",
|
||||
agentId: "main",
|
||||
sessionKey: "agent:main:main",
|
||||
provider: "anthropic",
|
||||
model: "claude-haiku-4-5",
|
||||
idempotencyKey: "test-idem-model-override-write",
|
||||
},
|
||||
{
|
||||
reqId: "test-idem-model-override-write",
|
||||
client: {
|
||||
connect: {
|
||||
scopes: ["operator.write"],
|
||||
},
|
||||
} as AgentHandlerArgs["client"],
|
||||
respond,
|
||||
},
|
||||
);
|
||||
|
||||
expect(mocks.agentCommand).not.toHaveBeenCalled();
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
false,
|
||||
undefined,
|
||||
expect.objectContaining({
|
||||
message: "provider/model overrides are not authorized for this caller.",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards provider and model overrides when internal override authorization is set", async () => {
|
||||
primeMainAgentRun();
|
||||
|
||||
await invokeAgent(
|
||||
{
|
||||
message: "test override",
|
||||
agentId: "main",
|
||||
sessionKey: "agent:main:main",
|
||||
provider: "anthropic",
|
||||
model: "claude-haiku-4-5",
|
||||
idempotencyKey: "test-idem-model-override-internal",
|
||||
},
|
||||
{
|
||||
reqId: "test-idem-model-override-internal",
|
||||
client: {
|
||||
connect: {
|
||||
scopes: ["operator.write"],
|
||||
},
|
||||
internal: {
|
||||
allowModelOverride: true,
|
||||
},
|
||||
} as AgentHandlerArgs["client"],
|
||||
},
|
||||
);
|
||||
|
||||
const lastCall = mocks.agentCommand.mock.calls.at(-1);
|
||||
expect(lastCall?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
provider: "anthropic",
|
||||
model: "claude-haiku-4-5",
|
||||
senderIsOwner: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves cliSessionIds from existing session entry", async () => {
|
||||
const existingCliSessionIds = { "claude-cli": "abc-123-def" };
|
||||
const existingClaudeCliSessionId = "abc-123-def";
|
||||
|
||||
@@ -71,6 +71,12 @@ function resolveSenderIsOwnerFromClient(client: GatewayRequestHandlerOptions["cl
|
||||
return scopes.includes(ADMIN_SCOPE);
|
||||
}
|
||||
|
||||
function resolveAllowModelOverrideFromClient(
|
||||
client: GatewayRequestHandlerOptions["client"],
|
||||
): boolean {
|
||||
return resolveSenderIsOwnerFromClient(client) || client?.internal?.allowModelOverride === true;
|
||||
}
|
||||
|
||||
async function runSessionResetFromAgent(params: {
|
||||
key: string;
|
||||
reason: "new" | "reset";
|
||||
@@ -162,6 +168,8 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
const request = p as {
|
||||
message: string;
|
||||
agentId?: string;
|
||||
provider?: string;
|
||||
model?: string;
|
||||
to?: string;
|
||||
replyTo?: string;
|
||||
sessionId?: string;
|
||||
@@ -192,6 +200,21 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
inputProvenance?: InputProvenance;
|
||||
};
|
||||
const senderIsOwner = resolveSenderIsOwnerFromClient(client);
|
||||
const allowModelOverride = resolveAllowModelOverrideFromClient(client);
|
||||
const requestedModelOverride = Boolean(request.provider || request.model);
|
||||
if (requestedModelOverride && !allowModelOverride) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
errorShape(
|
||||
ErrorCodes.INVALID_REQUEST,
|
||||
"provider/model overrides are not authorized for this caller.",
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
const providerOverride = allowModelOverride ? request.provider : undefined;
|
||||
const modelOverride = allowModelOverride ? request.model : undefined;
|
||||
const cfg = loadConfig();
|
||||
const idem = request.idempotencyKey;
|
||||
const normalizedSpawned = normalizeSpawnedRunMetadata({
|
||||
@@ -584,6 +607,8 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
ingressOpts: {
|
||||
message,
|
||||
images,
|
||||
provider: providerOverride,
|
||||
model: modelOverride,
|
||||
to: resolvedTo,
|
||||
sessionId: resolvedSessionId,
|
||||
sessionKey: resolvedSessionKey,
|
||||
@@ -619,6 +644,7 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
workspaceDir: sessionEntry?.spawnedWorkspaceDir,
|
||||
}),
|
||||
senderIsOwner,
|
||||
allowModelOverride,
|
||||
},
|
||||
runId,
|
||||
idempotencyKey: idem,
|
||||
|
||||
@@ -21,6 +21,10 @@ export type GatewayClient = {
|
||||
canvasHostUrl?: string;
|
||||
canvasCapability?: string;
|
||||
canvasCapabilityExpiresAtMs?: number;
|
||||
/** Internal-only auth context that cannot be supplied through gateway RPC payloads. */
|
||||
internal?: {
|
||||
allowModelOverride?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type RespondFn = (
|
||||
|
||||
Reference in New Issue
Block a user