fix(regression): honor internal provider auth for directives

This commit is contained in:
Tak Hoffman
2026-03-27 16:15:09 -05:00
parent f41cd12b54
commit c125c33724
6 changed files with 48 additions and 1 deletions

View File

@@ -99,10 +99,12 @@ export async function handleDirectiveOnly(
}).sandboxed; }).sandboxed;
const shouldHintDirectRuntime = directives.hasElevatedDirective && !runtimeIsSandboxed; const shouldHintDirectRuntime = directives.hasElevatedDirective && !runtimeIsSandboxed;
const allowInternalExecPersistence = canPersistInternalExecDirective({ const allowInternalExecPersistence = canPersistInternalExecDirective({
messageProvider: params.messageProvider,
surface: params.surface, surface: params.surface,
gatewayClientScopes: params.gatewayClientScopes, gatewayClientScopes: params.gatewayClientScopes,
}); });
const allowInternalVerbosePersistence = canPersistInternalVerboseDirective({ const allowInternalVerbosePersistence = canPersistInternalVerboseDirective({
messageProvider: params.messageProvider,
surface: params.surface, surface: params.surface,
gatewayClientScopes: params.gatewayClientScopes, gatewayClientScopes: params.gatewayClientScopes,
}); });

View File

@@ -717,4 +717,39 @@ describe("persistInlineDirectives internal exec scope gate", () => {
expect(sessionEntry.verboseLevel).toBeUndefined(); expect(sessionEntry.verboseLevel).toBeUndefined();
}); });
it("treats internal provider context as authoritative over external surface metadata", async () => {
const allowedModelKeys = new Set(["anthropic/claude-opus-4-5", "openai/gpt-4o"]);
const directives = parseInlineDirectives("/verbose full");
const sessionEntry = {
sessionId: "s1",
updatedAt: Date.now(),
} as SessionEntry;
const sessionStore = { "agent:main:main": sessionEntry };
await persistInlineDirectives({
directives,
cfg: baseConfig(),
sessionEntry,
sessionStore,
sessionKey: "agent:main:main",
storePath: "/tmp/sessions.json",
elevatedEnabled: true,
elevatedAllowed: true,
defaultProvider: "anthropic",
defaultModel: "claude-opus-4-5",
aliasIndex: baseAliasIndex(),
allowedModelKeys,
provider: "anthropic",
model: "claude-opus-4-5",
initialModelLabel: "anthropic/claude-opus-4-5",
formatModelSwitchEvent: (label) => `Switched to ${label}`,
agentCfg: undefined,
messageProvider: "webchat",
surface: "telegram",
gatewayClientScopes: ["operator.write"],
});
expect(sessionEntry.verboseLevel).toBeUndefined();
});
}); });

View File

@@ -31,6 +31,7 @@ export type HandleDirectiveOnlyCoreParams = {
}; };
export type HandleDirectiveOnlyParams = HandleDirectiveOnlyCoreParams & { export type HandleDirectiveOnlyParams = HandleDirectiveOnlyCoreParams & {
messageProvider?: string;
currentThinkLevel?: ThinkLevel; currentThinkLevel?: ThinkLevel;
currentFastMode?: boolean; currentFastMode?: boolean;
currentVerboseLevel?: VerboseLevel; currentVerboseLevel?: VerboseLevel;

View File

@@ -41,6 +41,7 @@ export async function persistInlineDirectives(params: {
initialModelLabel: string; initialModelLabel: string;
formatModelSwitchEvent: (label: string, alias?: string) => string; formatModelSwitchEvent: (label: string, alias?: string) => string;
agentCfg: NonNullable<OpenClawConfig["agents"]>["defaults"] | undefined; agentCfg: NonNullable<OpenClawConfig["agents"]>["defaults"] | undefined;
messageProvider?: string;
surface?: string; surface?: string;
gatewayClientScopes?: string[]; gatewayClientScopes?: string[];
}): Promise<{ provider: string; model: string; contextTokens: number }> { }): Promise<{ provider: string; model: string; contextTokens: number }> {
@@ -63,10 +64,12 @@ export async function persistInlineDirectives(params: {
} = params; } = params;
let { provider, model } = params; let { provider, model } = params;
const allowInternalExecPersistence = canPersistInternalExecDirective({ const allowInternalExecPersistence = canPersistInternalExecDirective({
messageProvider: params.messageProvider,
surface: params.surface, surface: params.surface,
gatewayClientScopes: params.gatewayClientScopes, gatewayClientScopes: params.gatewayClientScopes,
}); });
const allowInternalVerbosePersistence = canPersistInternalVerboseDirective({ const allowInternalVerbosePersistence = canPersistInternalVerboseDirective({
messageProvider: params.messageProvider,
surface: params.surface, surface: params.surface,
gatewayClientScopes: params.gatewayClientScopes, gatewayClientScopes: params.gatewayClientScopes,
}); });

View File

@@ -24,10 +24,14 @@ export const formatInternalVerboseCurrentReplyOnlyText = () =>
"Verbose logging set for the current reply only."; "Verbose logging set for the current reply only.";
function canPersistInternalDirective(params: { function canPersistInternalDirective(params: {
messageProvider?: string;
surface?: string; surface?: string;
gatewayClientScopes?: string[]; gatewayClientScopes?: string[];
}): boolean { }): boolean {
if (!isInternalMessageChannel(params.surface)) { const authoritativeChannel = isInternalMessageChannel(params.messageProvider)
? params.messageProvider
: params.surface;
if (!isInternalMessageChannel(authoritativeChannel)) {
return true; return true;
} }
const scopes = params.gatewayClientScopes ?? []; const scopes = params.gatewayClientScopes ?? [];

View File

@@ -233,6 +233,7 @@ export async function applyInlineDirectiveOverrides(params: {
currentVerboseLevel, currentVerboseLevel,
currentReasoningLevel, currentReasoningLevel,
currentElevatedLevel, currentElevatedLevel,
messageProvider: ctx.Provider,
surface: ctx.Surface, surface: ctx.Surface,
gatewayClientScopes: ctx.GatewayClientScopes, gatewayClientScopes: ctx.GatewayClientScopes,
}); });
@@ -328,6 +329,7 @@ export async function applyInlineDirectiveOverrides(params: {
initialModelLabel, initialModelLabel,
formatModelSwitchEvent, formatModelSwitchEvent,
agentCfg, agentCfg,
messageProvider: ctx.Provider,
surface: ctx.Surface, surface: ctx.Surface,
gatewayClientScopes: ctx.GatewayClientScopes, gatewayClientScopes: ctx.GatewayClientScopes,
}); });