feat: add /exec session overrides

This commit is contained in:
Peter Steinberger
2026-01-18 06:11:38 +00:00
parent 1d8614c7c2
commit 8f7f7ee7dc
28 changed files with 615 additions and 8 deletions
@@ -1,8 +1,9 @@
import { resolveAgentDir, resolveSessionAgentId } from "../../agents/agent-scope.js";
import { resolveAgentConfig, resolveAgentDir, resolveSessionAgentId } from "../../agents/agent-scope.js";
import type { ModelAliasIndex } from "../../agents/model-selection.js";
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
import type { ClawdbotConfig } from "../../config/config.js";
import { type SessionEntry, updateSessionStore } from "../../config/sessions.js";
import type { ExecAsk, ExecHost, ExecSecurity } from "../../infra/exec-approvals.js";
import { enqueueSystemEvent } from "../../infra/system-events.js";
import { applyVerboseOverride } from "../../sessions/level-overrides.js";
import { formatThinkingLevels, formatXHighModelHint, supportsXHighThinking } from "../thinking.js";
@@ -23,6 +24,38 @@ import {
} from "./directive-handling.shared.js";
import type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel } from "./directives.js";
function resolveExecDefaults(params: {
cfg: ClawdbotConfig;
sessionEntry?: SessionEntry;
agentId?: string;
}): { host: ExecHost; security: ExecSecurity; ask: ExecAsk; node?: string } {
const globalExec = params.cfg.tools?.exec;
const agentExec = params.agentId
? resolveAgentConfig(params.cfg, params.agentId)?.tools?.exec
: undefined;
return {
host:
(params.sessionEntry?.execHost as ExecHost | undefined) ??
(agentExec?.host as ExecHost | undefined) ??
(globalExec?.host as ExecHost | undefined) ??
"sandbox",
security:
(params.sessionEntry?.execSecurity as ExecSecurity | undefined) ??
(agentExec?.security as ExecSecurity | undefined) ??
(globalExec?.security as ExecSecurity | undefined) ??
"deny",
ask:
(params.sessionEntry?.execAsk as ExecAsk | undefined) ??
(agentExec?.ask as ExecAsk | undefined) ??
(globalExec?.ask as ExecAsk | undefined) ??
"on-miss",
node:
(params.sessionEntry?.execNode as string | undefined) ??
agentExec?.node ??
globalExec?.node,
};
}
export async function handleDirectiveOnly(params: {
cfg: ClawdbotConfig;
directives: InlineDirectives;
@@ -189,6 +222,42 @@ export async function handleDirectiveOnly(params: {
}),
};
}
if (directives.hasExecDirective) {
if (directives.invalidExecHost) {
return {
text: `Unrecognized exec host "${directives.rawExecHost ?? ""}". Valid hosts: sandbox, gateway, node.`,
};
}
if (directives.invalidExecSecurity) {
return {
text: `Unrecognized exec security "${directives.rawExecSecurity ?? ""}". Valid: deny, allowlist, full.`,
};
}
if (directives.invalidExecAsk) {
return {
text: `Unrecognized exec ask "${directives.rawExecAsk ?? ""}". Valid: off, on-miss, always.`,
};
}
if (directives.invalidExecNode) {
return {
text: "Exec node requires a value.",
};
}
if (!directives.hasExecOptions) {
const execDefaults = resolveExecDefaults({
cfg: params.cfg,
sessionEntry,
agentId: activeAgentId,
});
const nodeLabel = execDefaults.node ? `node=${execDefaults.node}` : "node=(unset)";
return {
text: withOptions(
`Current exec defaults: host=${execDefaults.host}, security=${execDefaults.security}, ask=${execDefaults.ask}, ${nodeLabel}.`,
"host=sandbox|gateway|node, security=deny|allowlist|full, ask=off|on-miss|always, node=<id>",
),
};
}
}
const queueAck = maybeHandleQueueDirective({
directives,
@@ -254,6 +323,20 @@ export async function handleDirectiveOnly(params: {
elevatedChanged ||
(directives.elevatedLevel !== prevElevatedLevel && directives.elevatedLevel !== undefined);
}
if (directives.hasExecDirective && directives.hasExecOptions) {
if (directives.execHost) {
sessionEntry.execHost = directives.execHost;
}
if (directives.execSecurity) {
sessionEntry.execSecurity = directives.execSecurity;
}
if (directives.execAsk) {
sessionEntry.execAsk = directives.execAsk;
}
if (directives.execNode) {
sessionEntry.execNode = directives.execNode;
}
}
if (modelSelection) {
if (modelSelection.isDefault) {
delete sessionEntry.providerOverride;
@@ -355,6 +438,16 @@ export async function handleDirectiveOnly(params: {
);
if (shouldHintDirectRuntime) parts.push(formatElevatedRuntimeHint());
}
if (directives.hasExecDirective && directives.hasExecOptions) {
const execParts: string[] = [];
if (directives.execHost) execParts.push(`host=${directives.execHost}`);
if (directives.execSecurity) execParts.push(`security=${directives.execSecurity}`);
if (directives.execAsk) execParts.push(`ask=${directives.execAsk}`);
if (directives.execNode) execParts.push(`node=${directives.execNode}`);
if (execParts.length > 0) {
parts.push(formatDirectiveAck(`Exec defaults set (${execParts.join(", ")}).`));
}
}
if (shouldDowngradeXHigh) {
parts.push(
`Thinking level set to high (xhigh not supported for ${resolvedProvider}/${resolvedModel}).`,