chore: log elevated and reasoning toggles
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
- Auto-reply: restore 300-char heartbeat ack limit and keep >300 char replies instead of dropping them; adjust long heartbeat test content accordingly.
|
||||
- Gateway: `agents.list` now honors explicit `agents.list` config without pulling stray agents from disk; GitHub Copilot CLI auth path uses the updated provider build.
|
||||
- Google: apply patched pi-ai `google-gemini-cli` function call handling (strips ids) after upgrading to pi-ai 0.43.0.
|
||||
- Auto-reply: elevated/reasoning toggles now enqueue system events so the model sees the mode change immediately.
|
||||
|
||||
## 2026.1.11
|
||||
|
||||
|
||||
@@ -1913,6 +1913,67 @@ describe("directive behavior", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("queues a system event when toggling elevated", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
drainSystemEvents(MAIN_SESSION_KEY);
|
||||
const storePath = path.join(home, "sessions.json");
|
||||
|
||||
await getReplyFromConfig(
|
||||
{
|
||||
Body: "/elevated on",
|
||||
From: "+1222",
|
||||
To: "+1222",
|
||||
Provider: "whatsapp",
|
||||
},
|
||||
{},
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openai/gpt-4.1-mini" },
|
||||
workspace: path.join(home, "clawd"),
|
||||
},
|
||||
},
|
||||
tools: { elevated: { allowFrom: { whatsapp: ["*"] } } },
|
||||
whatsapp: { allowFrom: ["*"] },
|
||||
session: { store: storePath },
|
||||
},
|
||||
);
|
||||
|
||||
const events = drainSystemEvents(MAIN_SESSION_KEY);
|
||||
expect(events.some((e) => e.includes("Elevated ON"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("queues a system event when toggling reasoning", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
drainSystemEvents(MAIN_SESSION_KEY);
|
||||
const storePath = path.join(home, "sessions.json");
|
||||
|
||||
await getReplyFromConfig(
|
||||
{
|
||||
Body: "/reasoning stream",
|
||||
From: "+1222",
|
||||
To: "+1222",
|
||||
Provider: "whatsapp",
|
||||
},
|
||||
{},
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "openai/gpt-4.1-mini" },
|
||||
workspace: path.join(home, "clawd"),
|
||||
},
|
||||
},
|
||||
whatsapp: { allowFrom: ["*"] },
|
||||
session: { store: storePath },
|
||||
},
|
||||
);
|
||||
|
||||
const events = drainSystemEvents(MAIN_SESSION_KEY);
|
||||
expect(events.some((e) => e.includes("Reasoning STREAM"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores inline /model and uses the default model", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const storePath = path.join(home, "sessions.json");
|
||||
|
||||
@@ -68,6 +68,17 @@ const withOptions = (line: string, options: string) =>
|
||||
const formatElevatedRuntimeHint = () =>
|
||||
`${SYSTEM_MARK} Runtime is direct; sandboxing does not apply.`;
|
||||
|
||||
const formatElevatedEvent = (level: ElevatedLevel) =>
|
||||
level === "on"
|
||||
? "Elevated ON — exec runs on host; set elevated:false to stay sandboxed."
|
||||
: "Elevated OFF — exec stays in sandbox.";
|
||||
|
||||
const formatReasoningEvent = (level: ReasoningLevel) => {
|
||||
if (level === "stream") return "Reasoning STREAM — emit live <think>.";
|
||||
if (level === "on") return "Reasoning ON — include <think>.";
|
||||
return "Reasoning OFF — hide <think>.";
|
||||
};
|
||||
|
||||
function formatElevatedUnavailableText(params: {
|
||||
runtimeSandboxed: boolean;
|
||||
failures?: Array<{ gate: string; key: string }>;
|
||||
@@ -1070,6 +1081,22 @@ export async function handleDirectiveOnly(params: {
|
||||
}
|
||||
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
const prevElevatedLevel =
|
||||
currentElevatedLevel ??
|
||||
(sessionEntry.elevatedLevel as ElevatedLevel | undefined) ??
|
||||
(elevatedAllowed ? ("on" as ElevatedLevel) : ("off" as ElevatedLevel));
|
||||
const prevReasoningLevel =
|
||||
currentReasoningLevel ??
|
||||
(sessionEntry.reasoningLevel as ReasoningLevel | undefined) ??
|
||||
"off";
|
||||
let elevatedChanged =
|
||||
directives.hasElevatedDirective &&
|
||||
directives.elevatedLevel !== undefined &&
|
||||
elevatedEnabled &&
|
||||
elevatedAllowed;
|
||||
let reasoningChanged =
|
||||
directives.hasReasoningDirective &&
|
||||
directives.reasoningLevel !== undefined;
|
||||
if (directives.hasThinkDirective && directives.thinkLevel) {
|
||||
if (directives.thinkLevel === "off") delete sessionEntry.thinkingLevel;
|
||||
else sessionEntry.thinkingLevel = directives.thinkLevel;
|
||||
@@ -1081,11 +1108,18 @@ export async function handleDirectiveOnly(params: {
|
||||
if (directives.reasoningLevel === "off")
|
||||
delete sessionEntry.reasoningLevel;
|
||||
else sessionEntry.reasoningLevel = directives.reasoningLevel;
|
||||
reasoningChanged =
|
||||
directives.reasoningLevel !== prevReasoningLevel &&
|
||||
directives.reasoningLevel !== undefined;
|
||||
}
|
||||
if (directives.hasElevatedDirective && directives.elevatedLevel) {
|
||||
// Unlike other toggles, elevated defaults can be "on".
|
||||
// Persist "off" explicitly so `/elevated off` actually overrides defaults.
|
||||
sessionEntry.elevatedLevel = directives.elevatedLevel;
|
||||
elevatedChanged =
|
||||
elevatedChanged ||
|
||||
(directives.elevatedLevel !== prevElevatedLevel &&
|
||||
directives.elevatedLevel !== undefined);
|
||||
}
|
||||
if (modelSelection) {
|
||||
if (modelSelection.isDefault) {
|
||||
@@ -1123,6 +1157,22 @@ export async function handleDirectiveOnly(params: {
|
||||
if (storePath) {
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
if (elevatedChanged) {
|
||||
const nextElevated =
|
||||
(sessionEntry.elevatedLevel ?? "off") as ElevatedLevel;
|
||||
enqueueSystemEvent(formatElevatedEvent(nextElevated), {
|
||||
sessionKey,
|
||||
contextKey: "mode:elevated",
|
||||
});
|
||||
}
|
||||
if (reasoningChanged) {
|
||||
const nextReasoning =
|
||||
(sessionEntry.reasoningLevel ?? "off") as ReasoningLevel;
|
||||
enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
|
||||
sessionKey,
|
||||
contextKey: "mode:reasoning",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const parts: string[] = [];
|
||||
@@ -1240,6 +1290,20 @@ export async function persistInlineDirectives(params: {
|
||||
const agentDir = resolveAgentDir(cfg, activeAgentId);
|
||||
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
const prevElevatedLevel =
|
||||
(sessionEntry.elevatedLevel as ElevatedLevel | undefined) ??
|
||||
(agentCfg?.elevatedDefault as ElevatedLevel | undefined) ??
|
||||
(elevatedAllowed ? ("on" as ElevatedLevel) : ("off" as ElevatedLevel));
|
||||
const prevReasoningLevel =
|
||||
(sessionEntry.reasoningLevel as ReasoningLevel | undefined) ?? "off";
|
||||
let elevatedChanged =
|
||||
directives.hasElevatedDirective &&
|
||||
directives.elevatedLevel !== undefined &&
|
||||
elevatedEnabled &&
|
||||
elevatedAllowed;
|
||||
let reasoningChanged =
|
||||
directives.hasReasoningDirective &&
|
||||
directives.reasoningLevel !== undefined;
|
||||
let updated = false;
|
||||
if (directives.hasThinkDirective && directives.thinkLevel) {
|
||||
if (directives.thinkLevel === "off") {
|
||||
@@ -1259,6 +1323,10 @@ export async function persistInlineDirectives(params: {
|
||||
} else {
|
||||
sessionEntry.reasoningLevel = directives.reasoningLevel;
|
||||
}
|
||||
reasoningChanged =
|
||||
reasoningChanged ||
|
||||
(directives.reasoningLevel !== prevReasoningLevel &&
|
||||
directives.reasoningLevel !== undefined);
|
||||
updated = true;
|
||||
}
|
||||
if (
|
||||
@@ -1269,6 +1337,10 @@ export async function persistInlineDirectives(params: {
|
||||
) {
|
||||
// Persist "off" explicitly so inline `/elevated off` overrides defaults.
|
||||
sessionEntry.elevatedLevel = directives.elevatedLevel;
|
||||
elevatedChanged =
|
||||
elevatedChanged ||
|
||||
(directives.elevatedLevel !== prevElevatedLevel &&
|
||||
directives.elevatedLevel !== undefined);
|
||||
updated = true;
|
||||
}
|
||||
const modelDirective =
|
||||
@@ -1341,6 +1413,22 @@ export async function persistInlineDirectives(params: {
|
||||
if (storePath) {
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
if (elevatedChanged) {
|
||||
const nextElevated =
|
||||
(sessionEntry.elevatedLevel ?? "off") as ElevatedLevel;
|
||||
enqueueSystemEvent(formatElevatedEvent(nextElevated), {
|
||||
sessionKey,
|
||||
contextKey: "mode:elevated",
|
||||
});
|
||||
}
|
||||
if (reasoningChanged) {
|
||||
const nextReasoning =
|
||||
(sessionEntry.reasoningLevel ?? "off") as ReasoningLevel;
|
||||
enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
|
||||
sessionKey,
|
||||
contextKey: "mode:reasoning",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user