feat: add elevated ask/full modes
This commit is contained in:
@@ -1981,7 +1981,7 @@ Per-agent override (further restrict):
|
||||
|
||||
Notes:
|
||||
- `tools.elevated` is the global baseline. `agents.list[].tools.elevated` can only further restrict (both must allow).
|
||||
- `/elevated on|off` stores state per session key; inline directives apply to a single message.
|
||||
- `/elevated on|off|ask|full` stores state per session key; inline directives apply to a single message.
|
||||
- Elevated `exec` runs on the host and bypasses sandboxing.
|
||||
- Tool policy still applies; if `exec` is denied, elevated cannot be used.
|
||||
|
||||
|
||||
@@ -91,7 +91,8 @@ Available groups:
|
||||
## Elevated: exec-only “run on host”
|
||||
|
||||
Elevated does **not** grant extra tools; it only affects `exec`.
|
||||
- If you’re sandboxed, `/elevated on` (or `exec` with `elevated: true`) runs on the host.
|
||||
- If you’re sandboxed, `/elevated on` (or `exec` with `elevated: true`) runs on the host (approvals may still apply).
|
||||
- Use `/elevated full` to skip exec approvals for the session.
|
||||
- If you’re already running direct, elevated is effectively a no-op (still gated).
|
||||
- Elevated is **not** skill-scoped and does **not** override tool allow/deny.
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ Option B:
|
||||
## Slash commands
|
||||
- `/exec host=<sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>`
|
||||
- Per-agent, per-session overrides; non-persistent unless saved via config.
|
||||
- `/elevated on|off` remains a shortcut for `host=gateway security=full`.
|
||||
- `/elevated on|off|ask|full` remains a shortcut for `host=gateway security=full` (with `full` skipping approvals).
|
||||
|
||||
## Cross-platform story
|
||||
- The runner service is the portable execution target.
|
||||
|
||||
@@ -6,17 +6,20 @@ read_when:
|
||||
# Elevated Mode (/elevated directives)
|
||||
|
||||
## What it does
|
||||
- `/elevated on` is a **shortcut** for `exec.host=gateway` + `exec.security=full`.
|
||||
- `/elevated on` is a **shortcut** for `exec.host=gateway` + `exec.security=full` (approvals still apply).
|
||||
- `/elevated full` runs on the gateway host **and** auto-approves exec (skips exec approvals).
|
||||
- `/elevated ask` runs on the gateway host but keeps exec approvals (same as `/elevated on`).
|
||||
- Only changes behavior when the agent is **sandboxed** (otherwise exec already runs on the host).
|
||||
- Directive forms: `/elevated on`, `/elevated off`, `/elev on`, `/elev off`.
|
||||
- Only `on|off` are accepted; anything else returns a hint and does not change state.
|
||||
- Directive forms: `/elevated on|off|ask|full`, `/elev on|off|ask|full`.
|
||||
- Only `on|off|ask|full` are accepted; anything else returns a hint and does not change state.
|
||||
|
||||
## What it controls (and what it doesn’t)
|
||||
- **Availability gates**: `tools.elevated` is the global baseline. `agents.list[].tools.elevated` can further restrict elevated per agent (both must allow).
|
||||
- **Per-session state**: `/elevated on|off` sets the elevated level for the current session key.
|
||||
- **Inline directive**: `/elevated on` inside a message applies to that message only.
|
||||
- **Per-session state**: `/elevated on|off|ask|full` sets the elevated level for the current session key.
|
||||
- **Inline directive**: `/elevated on|ask|full` inside a message applies to that message only.
|
||||
- **Groups**: In group chats, elevated directives are only honored when the agent is mentioned. Command-only messages that bypass mention requirements are treated as mentioned.
|
||||
- **Host execution**: elevated forces `exec` onto the gateway host with full security.
|
||||
- **Approvals**: `full` skips exec approvals; `on`/`ask` still honor them.
|
||||
- **Unsandboxed agents**: no-op for location; only affects gating, logging, and status.
|
||||
- **Tool policy still applies**: if `exec` is denied by tool policy, elevated cannot be used.
|
||||
|
||||
@@ -26,8 +29,8 @@ read_when:
|
||||
3. Global default (`agents.defaults.elevatedDefault` in config).
|
||||
|
||||
## Setting a session default
|
||||
- Send a message that is **only** the directive (whitespace allowed), e.g. `/elevated on`.
|
||||
- Confirmation reply is sent (`Elevated mode enabled.` / `Elevated mode disabled.`).
|
||||
- Send a message that is **only** the directive (whitespace allowed), e.g. `/elevated full`.
|
||||
- Confirmation reply is sent (`Elevated mode set to full...` / `Elevated mode disabled.`).
|
||||
- If elevated access is disabled or the sender is not on the approved allowlist, the directive replies with an actionable error and does not change session state.
|
||||
- Send `/elevated` (or `/elevated:`) with no argument to see the current elevated level.
|
||||
|
||||
@@ -41,4 +44,4 @@ read_when:
|
||||
|
||||
## Logging + status
|
||||
- Elevated exec calls are logged at info level.
|
||||
- Session status includes elevated mode (e.g. `elevated=on`).
|
||||
- Session status includes elevated mode (e.g. `elevated=ask`, `elevated=full`).
|
||||
|
||||
@@ -11,7 +11,7 @@ read_when:
|
||||
Exec approvals are the **companion app / node host guardrail** for letting a sandboxed agent run
|
||||
commands on a real host (`gateway` or `node`). Think of it like a safety interlock:
|
||||
commands are allowed only when policy + allowlist + (optional) user approval all agree.
|
||||
Exec approvals are **in addition** to tool policy and elevated gating.
|
||||
Exec approvals are **in addition** to tool policy and elevated gating (unless elevated is set to `full`, which skips approvals).
|
||||
|
||||
If the companion app UI is **not available**, any request that requires a prompt is
|
||||
resolved by the **ask fallback** (default: deny).
|
||||
|
||||
@@ -78,7 +78,7 @@ Text + native (when enabled):
|
||||
- `/think <off|minimal|low|medium|high|xhigh>` (dynamic choices by model/provider; aliases: `/thinking`, `/t`)
|
||||
- `/verbose on|full|off` (alias: `/v`)
|
||||
- `/reasoning on|off|stream` (alias: `/reason`; when on, sends a separate message prefixed `Reasoning:`; `stream` = Telegram draft only)
|
||||
- `/elevated on|off` (alias: `/elev`)
|
||||
- `/elevated on|off|ask|full` (alias: `/elev`; `full` skips exec approvals)
|
||||
- `/exec host=<sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>` (send `/exec` to show current)
|
||||
- `/model <name>` (alias: `/models`; or `/<alias>` from `agents.defaults.models.*.alias`)
|
||||
- `/queue <mode>` (plus options like `debounce:2s cap:25 drop:summarize`; send `/queue` to see current settings)
|
||||
|
||||
@@ -78,7 +78,7 @@ Session controls:
|
||||
- `/verbose <on|full|off>`
|
||||
- `/reasoning <on|off|stream>`
|
||||
- `/usage <off|tokens|full>`
|
||||
- `/elevated <on|off>` (alias: `/elev`)
|
||||
- `/elevated <on|off|ask|full>` (alias: `/elev`)
|
||||
- `/activation <mention|always>`
|
||||
- `/deliver <on|off>`
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ export type { BashSandboxConfig } from "./bash-tools.shared.js";
|
||||
export type ExecElevatedDefaults = {
|
||||
enabled: boolean;
|
||||
allowed: boolean;
|
||||
defaultLevel: "on" | "off";
|
||||
defaultLevel: "on" | "off" | "ask" | "full";
|
||||
};
|
||||
|
||||
const execSchema = Type.Object({
|
||||
@@ -706,12 +706,23 @@ export function createExecTool(
|
||||
: clampNumber(params.yieldMs ?? defaultBackgroundMs, defaultBackgroundMs, 10, 120_000)
|
||||
: null;
|
||||
const elevatedDefaults = defaults?.elevated;
|
||||
const elevatedDefaultOn =
|
||||
elevatedDefaults?.defaultLevel === "on" &&
|
||||
elevatedDefaults.enabled &&
|
||||
elevatedDefaults.allowed;
|
||||
const elevatedRequested =
|
||||
typeof params.elevated === "boolean" ? params.elevated : elevatedDefaultOn;
|
||||
const elevatedDefaultMode =
|
||||
elevatedDefaults?.defaultLevel === "full"
|
||||
? "full"
|
||||
: elevatedDefaults?.defaultLevel === "ask"
|
||||
? "ask"
|
||||
: elevatedDefaults?.defaultLevel === "on"
|
||||
? "ask"
|
||||
: "off";
|
||||
const elevatedMode =
|
||||
typeof params.elevated === "boolean"
|
||||
? params.elevated
|
||||
? elevatedDefaultMode === "full"
|
||||
? "full"
|
||||
: "ask"
|
||||
: "off"
|
||||
: elevatedDefaultMode;
|
||||
const elevatedRequested = elevatedMode !== "off";
|
||||
if (elevatedRequested) {
|
||||
if (!elevatedDefaults?.enabled || !elevatedDefaults.allowed) {
|
||||
const runtime = defaults?.sandbox ? "sandboxed" : "direct";
|
||||
@@ -767,6 +778,10 @@ export function createExecTool(
|
||||
const configuredAsk = defaults?.ask ?? "on-miss";
|
||||
const requestedAsk = normalizeExecAsk(params.ask);
|
||||
let ask = maxAsk(configuredAsk, requestedAsk ?? configuredAsk);
|
||||
const bypassApprovals = elevatedRequested && elevatedMode === "full";
|
||||
if (bypassApprovals) {
|
||||
ask = "off";
|
||||
}
|
||||
|
||||
const sandbox = host === "sandbox" ? defaults?.sandbox : undefined;
|
||||
const rawWorkdir = params.workdir?.trim() || defaults?.cwd || process.cwd();
|
||||
@@ -1031,7 +1046,7 @@ export function createExecTool(
|
||||
};
|
||||
}
|
||||
|
||||
if (host === "gateway") {
|
||||
if (host === "gateway" && !bypassApprovals) {
|
||||
const approvals = resolveExecApprovals(agentId, { security: "allowlist" });
|
||||
const hostSecurity = minSecurity(security, approvals.agent.security);
|
||||
const hostAsk = maxAsk(ask, approvals.agent.ask);
|
||||
|
||||
@@ -76,6 +76,6 @@ export type EmbeddedSandboxInfo = {
|
||||
allowedControlPorts?: number[];
|
||||
elevated?: {
|
||||
allowed: boolean;
|
||||
defaultLevel: "on" | "off";
|
||||
defaultLevel: "on" | "off" | "ask" | "full";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -322,7 +322,7 @@ describe("buildAgentSystemPrompt", () => {
|
||||
|
||||
expect(prompt).toContain("You are running in a sandboxed runtime");
|
||||
expect(prompt).toContain("Sub-agents stay sandboxed");
|
||||
expect(prompt).toContain("User can toggle with /elevated on|off.");
|
||||
expect(prompt).toContain("User can toggle with /elevated on|off|ask|full.");
|
||||
expect(prompt).toContain("Current elevated level: on");
|
||||
});
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ export function buildAgentSystemPrompt(params: {
|
||||
allowedControlPorts?: number[];
|
||||
elevated?: {
|
||||
allowed: boolean;
|
||||
defaultLevel: "on" | "off";
|
||||
defaultLevel: "on" | "off" | "ask" | "full";
|
||||
};
|
||||
};
|
||||
/** Reaction guidance for the agent (for Telegram minimal/extensive modes). */
|
||||
@@ -444,12 +444,14 @@ export function buildAgentSystemPrompt(params: {
|
||||
params.sandboxInfo.elevated?.allowed
|
||||
? "Elevated exec is available for this session."
|
||||
: "",
|
||||
params.sandboxInfo.elevated?.allowed ? "User can toggle with /elevated on|off." : "",
|
||||
params.sandboxInfo.elevated?.allowed
|
||||
? "You may also send /elevated on|off when needed."
|
||||
? "User can toggle with /elevated on|off|ask|full."
|
||||
: "",
|
||||
params.sandboxInfo.elevated?.allowed
|
||||
? `Current elevated level: ${params.sandboxInfo.elevated.defaultLevel} (on runs exec on host; off runs in sandbox).`
|
||||
? "You may also send /elevated on|off|ask|full when needed."
|
||||
: "",
|
||||
params.sandboxInfo.elevated?.allowed
|
||||
? `Current elevated level: ${params.sandboxInfo.elevated.defaultLevel} (ask runs exec on host with approvals; full auto-approves).`
|
||||
: "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
|
||||
@@ -395,9 +395,9 @@ function buildChatCommands(): ChatCommandDefinition[] {
|
||||
args: [
|
||||
{
|
||||
name: "mode",
|
||||
description: "on or off",
|
||||
description: "on, off, ask, or full",
|
||||
type: "string",
|
||||
choices: ["on", "off"],
|
||||
choices: ["on", "off", "ask", "full"],
|
||||
},
|
||||
],
|
||||
argsMenu: "auto",
|
||||
|
||||
@@ -219,7 +219,7 @@ describe("directive behavior", () => {
|
||||
);
|
||||
|
||||
const events = drainSystemEvents(MAIN_SESSION_KEY);
|
||||
expect(events.some((e) => e.includes("Elevated ON"))).toBe(true);
|
||||
expect(events.some((e) => e.includes("Elevated ASK"))).toBe(true);
|
||||
});
|
||||
});
|
||||
it("queues a system event when toggling reasoning", async () => {
|
||||
|
||||
@@ -150,7 +150,7 @@ describe("directive behavior", () => {
|
||||
);
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Elevated mode enabled");
|
||||
expect(text).toContain("Elevated mode set to ask");
|
||||
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ describe("directive behavior", () => {
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Current elevated level: on");
|
||||
expect(text).toContain("Options: on, off.");
|
||||
expect(text).toContain("Options: on, off, ask, full.");
|
||||
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +55,16 @@ describe("directive parsing", () => {
|
||||
expect(res.hasDirective).toBe(true);
|
||||
expect(res.elevatedLevel).toBe("on");
|
||||
});
|
||||
it("matches elevated ask", () => {
|
||||
const res = extractElevatedDirective("/elevated ask please");
|
||||
expect(res.hasDirective).toBe(true);
|
||||
expect(res.elevatedLevel).toBe("ask");
|
||||
});
|
||||
it("matches elevated full", () => {
|
||||
const res = extractElevatedDirective("/elevated full please");
|
||||
expect(res.hasDirective).toBe(true);
|
||||
expect(res.elevatedLevel).toBe("full");
|
||||
});
|
||||
|
||||
it("matches think at start of line", () => {
|
||||
const res = extractThinkDirective("/think:high run slow");
|
||||
|
||||
@@ -129,7 +129,7 @@ describe("trigger handling", () => {
|
||||
cfg,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Elevated mode enabled");
|
||||
expect(text).toContain("Elevated mode set to ask");
|
||||
|
||||
const storeRaw = await fs.readFile(cfg.session.store, "utf-8");
|
||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||
@@ -223,7 +223,7 @@ describe("trigger handling", () => {
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toBe("ok");
|
||||
expect(text).not.toContain("Elevated mode enabled");
|
||||
expect(text).not.toContain("Elevated mode set to ask");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -184,7 +184,7 @@ describe("trigger handling", () => {
|
||||
cfg,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Elevated mode enabled");
|
||||
expect(text).toContain("Elevated mode set to ask");
|
||||
|
||||
const storeRaw = await fs.readFile(cfg.session.store, "utf-8");
|
||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||
@@ -226,7 +226,7 @@ describe("trigger handling", () => {
|
||||
cfg,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Elevated mode enabled");
|
||||
expect(text).toContain("Elevated mode set to ask");
|
||||
|
||||
const storeRaw = await fs.readFile(cfg.session.store, "utf-8");
|
||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||
|
||||
@@ -167,7 +167,7 @@ describe("trigger handling", () => {
|
||||
cfg,
|
||||
);
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Elevated mode enabled");
|
||||
expect(text).toContain("Elevated mode set to ask");
|
||||
|
||||
const storeRaw = await fs.readFile(cfg.session.store, "utf-8");
|
||||
const store = JSON.parse(storeRaw) as Record<string, { elevatedLevel?: string }>;
|
||||
|
||||
@@ -120,7 +120,7 @@ async function resolveContextReport(
|
||||
workspaceAccess: "rw" as const,
|
||||
elevated: {
|
||||
allowed: params.elevated.allowed,
|
||||
defaultLevel: params.resolvedElevatedLevel === "off" ? ("off" as const) : ("on" as const),
|
||||
defaultLevel: (params.resolvedElevatedLevel ?? "off") as "on" | "off" | "ask" | "full",
|
||||
},
|
||||
}
|
||||
: { enabled: false };
|
||||
|
||||
@@ -205,7 +205,7 @@ export async function handleDirectiveOnly(params: {
|
||||
const level = currentElevatedLevel ?? "off";
|
||||
return {
|
||||
text: [
|
||||
withOptions(`Current elevated level: ${level}.`, "on, off"),
|
||||
withOptions(`Current elevated level: ${level}.`, "on, off, ask, full"),
|
||||
shouldHintDirectRuntime ? formatElevatedRuntimeHint() : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
@@ -213,7 +213,7 @@ export async function handleDirectiveOnly(params: {
|
||||
};
|
||||
}
|
||||
return {
|
||||
text: `Unrecognized elevated level "${directives.rawElevatedLevel}". Valid levels: off, on.`,
|
||||
text: `Unrecognized elevated level "${directives.rawElevatedLevel}". Valid levels: off, on, ask, full.`,
|
||||
};
|
||||
}
|
||||
if (directives.hasElevatedDirective && (!elevatedEnabled || !elevatedAllowed)) {
|
||||
@@ -426,7 +426,9 @@ export async function handleDirectiveOnly(params: {
|
||||
parts.push(
|
||||
directives.elevatedLevel === "off"
|
||||
? formatDirectiveAck("Elevated mode disabled.")
|
||||
: formatDirectiveAck("Elevated mode enabled."),
|
||||
: directives.elevatedLevel === "full"
|
||||
? formatDirectiveAck("Elevated mode set to full (auto-approve).")
|
||||
: formatDirectiveAck("Elevated mode set to ask (approvals may still apply)."),
|
||||
);
|
||||
if (shouldHintDirectRuntime) parts.push(formatElevatedRuntimeHint());
|
||||
}
|
||||
|
||||
@@ -16,10 +16,15 @@ export const withOptions = (line: string, options: string) =>
|
||||
export const formatElevatedRuntimeHint = () =>
|
||||
`${SYSTEM_MARK} Runtime is direct; sandboxing does not apply.`;
|
||||
|
||||
export const formatElevatedEvent = (level: ElevatedLevel) =>
|
||||
level === "on"
|
||||
? "Elevated ON — exec runs on host; set elevated:false to stay sandboxed."
|
||||
: "Elevated OFF — exec stays in sandbox.";
|
||||
export const formatElevatedEvent = (level: ElevatedLevel) => {
|
||||
if (level === "full") {
|
||||
return "Elevated FULL — exec runs on host with auto-approval.";
|
||||
}
|
||||
if (level === "ask" || level === "on") {
|
||||
return "Elevated ASK — exec runs on host; approvals may still apply.";
|
||||
}
|
||||
return "Elevated OFF — exec stays in sandbox.";
|
||||
};
|
||||
|
||||
export const formatReasoningEvent = (level: ReasoningLevel) => {
|
||||
if (level === "stream") return "Reasoning STREAM — emit live <think>.";
|
||||
|
||||
@@ -324,7 +324,12 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
const queueDetails = formatQueueDetails(args.queue);
|
||||
const verboseLabel =
|
||||
verboseLevel === "full" ? "verbose:full" : verboseLevel === "on" ? "verbose" : null;
|
||||
const elevatedLabel = elevatedLevel === "on" ? "elevated" : null;
|
||||
const elevatedLabel =
|
||||
elevatedLevel && elevatedLevel !== "off"
|
||||
? elevatedLevel === "on"
|
||||
? "elevated"
|
||||
: `elevated:${elevatedLevel}`
|
||||
: null;
|
||||
const optionParts = [
|
||||
`Runtime: ${runtime.label}`,
|
||||
`Think: ${thinkLevel}`,
|
||||
@@ -395,7 +400,7 @@ export function buildHelpMessage(cfg?: ClawdbotConfig): string {
|
||||
"/think <level>",
|
||||
"/verbose on|full|off",
|
||||
"/reasoning on|off",
|
||||
"/elevated on|off",
|
||||
"/elevated on|off|ask|full",
|
||||
"/model <id>",
|
||||
"/usage off|tokens|full",
|
||||
];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
export type VerboseLevel = "off" | "on" | "full";
|
||||
export type ElevatedLevel = "off" | "on";
|
||||
export type ElevatedLevel = "off" | "on" | "ask" | "full";
|
||||
export type ElevatedMode = "off" | "ask" | "full";
|
||||
export type ReasoningLevel = "off" | "on" | "stream";
|
||||
export type UsageDisplayLevel = "off" | "tokens" | "full";
|
||||
|
||||
@@ -112,10 +113,18 @@ export function normalizeElevatedLevel(raw?: string | null): ElevatedLevel | und
|
||||
if (!raw) return undefined;
|
||||
const key = raw.toLowerCase();
|
||||
if (["off", "false", "no", "0"].includes(key)) return "off";
|
||||
if (["full", "auto", "auto-approve", "autoapprove"].includes(key)) return "full";
|
||||
if (["ask", "prompt", "approval", "approve"].includes(key)) return "ask";
|
||||
if (["on", "true", "yes", "1"].includes(key)) return "on";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function resolveElevatedMode(level?: ElevatedLevel | null): ElevatedMode {
|
||||
if (!level || level === "off") return "off";
|
||||
if (level === "full") return "full";
|
||||
return "ask";
|
||||
}
|
||||
|
||||
// Normalize reasoning visibility flags used to toggle reasoning exposure.
|
||||
export function normalizeReasoningLevel(raw?: string | null): ReasoningLevel | undefined {
|
||||
if (!raw) return undefined;
|
||||
|
||||
@@ -136,7 +136,7 @@ export type AgentDefaultsConfig = {
|
||||
/** Default verbose level when no /verbose directive is present. */
|
||||
verboseDefault?: "off" | "on" | "full";
|
||||
/** Default elevated level when no /elevated directive is present. */
|
||||
elevatedDefault?: "off" | "on";
|
||||
elevatedDefault?: "off" | "on" | "ask" | "full";
|
||||
/** Default block streaming level when no override is present. */
|
||||
blockStreamingDefault?: "off" | "on";
|
||||
/**
|
||||
|
||||
@@ -113,7 +113,9 @@ export const AgentDefaultsSchema = z
|
||||
])
|
||||
.optional(),
|
||||
verboseDefault: z.union([z.literal("off"), z.literal("on"), z.literal("full")]).optional(),
|
||||
elevatedDefault: z.union([z.literal("off"), z.literal("on")]).optional(),
|
||||
elevatedDefault: z
|
||||
.union([z.literal("off"), z.literal("on"), z.literal("ask"), z.literal("full")])
|
||||
.optional(),
|
||||
blockStreamingDefault: z.union([z.literal("off"), z.literal("on")]).optional(),
|
||||
blockStreamingBreak: z.union([z.literal("text_end"), z.literal("message_end")]).optional(),
|
||||
blockStreamingChunk: BlockStreamingChunkSchema.optional(),
|
||||
|
||||
@@ -169,7 +169,7 @@ export async function applySessionsPatchToStore(params: {
|
||||
delete next.elevatedLevel;
|
||||
} else if (raw !== undefined) {
|
||||
const normalized = normalizeElevatedLevel(String(raw));
|
||||
if (!normalized) return invalid('invalid elevatedLevel (use "on"|"off")');
|
||||
if (!normalized) return invalid('invalid elevatedLevel (use "on"|"off"|"ask"|"full")');
|
||||
// Persist "off" explicitly so patches can override defaults.
|
||||
next.elevatedLevel = normalized;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { formatThinkingLevels, listThinkingLevelLabels } from "../auto-reply/thi
|
||||
|
||||
const VERBOSE_LEVELS = ["on", "off"];
|
||||
const REASONING_LEVELS = ["on", "off"];
|
||||
const ELEVATED_LEVELS = ["on", "off"];
|
||||
const ELEVATED_LEVELS = ["on", "off", "ask", "full"];
|
||||
const ACTIVATION_LEVELS = ["mention", "always"];
|
||||
const USAGE_FOOTER_LEVELS = ["off", "tokens", "full"];
|
||||
|
||||
@@ -83,7 +83,7 @@ export function getSlashCommands(options: SlashCommandOptions = {}): SlashComman
|
||||
},
|
||||
{
|
||||
name: "elevated",
|
||||
description: "Set elevated on/off",
|
||||
description: "Set elevated on/off/ask/full",
|
||||
getArgumentCompletions: (prefix) =>
|
||||
ELEVATED_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
|
||||
value,
|
||||
@@ -130,8 +130,8 @@ export function helpText(options: SlashCommandOptions = {}): string {
|
||||
"/verbose <on|off>",
|
||||
"/reasoning <on|off>",
|
||||
"/usage <off|tokens|full>",
|
||||
"/elevated <on|off>",
|
||||
"/elev <on|off>",
|
||||
"/elevated <on|off|ask|full>",
|
||||
"/elev <on|off|ask|full>",
|
||||
"/activation <mention|always>",
|
||||
"/new or /reset",
|
||||
"/abort",
|
||||
|
||||
@@ -371,7 +371,11 @@ export function createCommandHandlers(context: CommandHandlerContext) {
|
||||
}
|
||||
case "elevated":
|
||||
if (!args) {
|
||||
chatLog.addSystem("usage: /elevated <on|off>");
|
||||
chatLog.addSystem("usage: /elevated <on|off|ask|full>");
|
||||
break;
|
||||
}
|
||||
if (!["on", "off", "ask", "full"].includes(args)) {
|
||||
chatLog.addSystem("usage: /elevated <on|off|ask|full>");
|
||||
break;
|
||||
}
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user