feat: extend verbose tool feedback
This commit is contained in:
@@ -81,7 +81,7 @@ describe("directive behavior", () => {
|
||||
|
||||
const text = Array.isArray(res) ? res[0]?.text : res?.text;
|
||||
expect(text).toContain("Current verbose level: on");
|
||||
expect(text).toContain("Options: on, off.");
|
||||
expect(text).toContain("Options: on, full, off.");
|
||||
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
resolvedBlockStreamingBreak: "text_end" | "message_end";
|
||||
applyReplyToMode: (payload: ReplyPayload) => ReplyPayload;
|
||||
shouldEmitToolResult: () => boolean;
|
||||
shouldEmitToolOutput: () => boolean;
|
||||
pendingToolTasks: Set<Promise<void>>;
|
||||
resetSessionAfterCompactionFailure: (reason: string) => Promise<boolean>;
|
||||
resetSessionAfterRoleOrderingConflict: (reason: string) => Promise<boolean>;
|
||||
@@ -335,6 +336,7 @@ export async function runAgentTurnWithFallback(params: {
|
||||
}
|
||||
: undefined,
|
||||
shouldEmitToolResult: params.shouldEmitToolResult,
|
||||
shouldEmitToolOutput: params.shouldEmitToolOutput,
|
||||
onToolResult: onToolResult
|
||||
? (payload) => {
|
||||
// `subscribeEmbeddedPiSession` may invoke tool callbacks without awaiting them.
|
||||
|
||||
@@ -18,17 +18,38 @@ export const createShouldEmitToolResult = (params: {
|
||||
}): (() => boolean) => {
|
||||
return () => {
|
||||
if (!params.sessionKey || !params.storePath) {
|
||||
return params.resolvedVerboseLevel === "on";
|
||||
return params.resolvedVerboseLevel !== "off";
|
||||
}
|
||||
try {
|
||||
const store = loadSessionStore(params.storePath);
|
||||
const entry = store[params.sessionKey];
|
||||
const current = normalizeVerboseLevel(entry?.verboseLevel);
|
||||
if (current) return current === "on";
|
||||
if (current) return current !== "off";
|
||||
} catch {
|
||||
// ignore store read failures
|
||||
}
|
||||
return params.resolvedVerboseLevel === "on";
|
||||
return params.resolvedVerboseLevel !== "off";
|
||||
};
|
||||
};
|
||||
|
||||
export const createShouldEmitToolOutput = (params: {
|
||||
sessionKey?: string;
|
||||
storePath?: string;
|
||||
resolvedVerboseLevel: VerboseLevel;
|
||||
}): (() => boolean) => {
|
||||
return () => {
|
||||
if (!params.sessionKey || !params.storePath) {
|
||||
return params.resolvedVerboseLevel === "full";
|
||||
}
|
||||
try {
|
||||
const store = loadSessionStore(params.storePath);
|
||||
const entry = store[params.sessionKey];
|
||||
const current = normalizeVerboseLevel(entry?.verboseLevel);
|
||||
if (current) return current === "full";
|
||||
} catch {
|
||||
// ignore store read failures
|
||||
}
|
||||
return params.resolvedVerboseLevel === "full";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import type { VerboseLevel } from "../thinking.js";
|
||||
import type { GetReplyOptions, ReplyPayload } from "../types.js";
|
||||
import { runAgentTurnWithFallback } from "./agent-runner-execution.js";
|
||||
import {
|
||||
createShouldEmitToolOutput,
|
||||
createShouldEmitToolResult,
|
||||
finalizeWithFollowup,
|
||||
isAudioPayload,
|
||||
@@ -116,6 +117,11 @@ export async function runReplyAgent(params: {
|
||||
storePath,
|
||||
resolvedVerboseLevel,
|
||||
});
|
||||
const shouldEmitToolOutput = createShouldEmitToolOutput({
|
||||
sessionKey,
|
||||
storePath,
|
||||
resolvedVerboseLevel,
|
||||
});
|
||||
|
||||
const pendingToolTasks = new Set<Promise<void>>();
|
||||
const blockReplyTimeoutMs = opts?.blockReplyTimeoutMs ?? BLOCK_REPLY_SEND_TIMEOUT_MS;
|
||||
@@ -296,6 +302,7 @@ export async function runReplyAgent(params: {
|
||||
resolvedBlockStreamingBreak,
|
||||
applyReplyToMode,
|
||||
shouldEmitToolResult,
|
||||
shouldEmitToolOutput,
|
||||
pendingToolTasks,
|
||||
resetSessionAfterCompactionFailure,
|
||||
resetSessionAfterRoleOrderingConflict,
|
||||
@@ -473,6 +480,7 @@ export async function runReplyAgent(params: {
|
||||
|
||||
// If verbose is enabled and this is a new session, prepend a session hint.
|
||||
let finalPayloads = replyPayloads;
|
||||
const verboseEnabled = resolvedVerboseLevel !== "off";
|
||||
if (autoCompactionCompleted) {
|
||||
const count = await incrementCompactionCount({
|
||||
sessionEntry: activeSessionEntry,
|
||||
@@ -480,12 +488,12 @@ export async function runReplyAgent(params: {
|
||||
sessionKey,
|
||||
storePath,
|
||||
});
|
||||
if (resolvedVerboseLevel === "on") {
|
||||
if (verboseEnabled) {
|
||||
const suffix = typeof count === "number" ? ` (count ${count})` : "";
|
||||
finalPayloads = [{ text: `🧹 Auto-compaction complete${suffix}.` }, ...finalPayloads];
|
||||
}
|
||||
}
|
||||
if (resolvedVerboseLevel === "on" && activeIsNewSession) {
|
||||
if (verboseEnabled && activeIsNewSession) {
|
||||
finalPayloads = [{ text: `🧭 New session: ${followupRun.run.sessionId}` }, ...finalPayloads];
|
||||
}
|
||||
if (responseUsageLine) {
|
||||
|
||||
@@ -137,11 +137,11 @@ export async function handleDirectiveOnly(params: {
|
||||
if (!directives.rawVerboseLevel) {
|
||||
const level = currentVerboseLevel ?? "off";
|
||||
return {
|
||||
text: withOptions(`Current verbose level: ${level}.`, "on, off"),
|
||||
text: withOptions(`Current verbose level: ${level}.`, "on, full, off"),
|
||||
};
|
||||
}
|
||||
return {
|
||||
text: `Unrecognized verbose level "${directives.rawVerboseLevel}". Valid levels: off, on.`,
|
||||
text: `Unrecognized verbose level "${directives.rawVerboseLevel}". Valid levels: off, on, full.`,
|
||||
};
|
||||
}
|
||||
if (directives.hasReasoningDirective && !directives.reasoningLevel) {
|
||||
@@ -333,7 +333,9 @@ export async function handleDirectiveOnly(params: {
|
||||
parts.push(
|
||||
directives.verboseLevel === "off"
|
||||
? formatDirectiveAck("Verbose logging disabled.")
|
||||
: formatDirectiveAck("Verbose logging enabled."),
|
||||
: directives.verboseLevel === "full"
|
||||
? formatDirectiveAck("Verbose logging set to full.")
|
||||
: formatDirectiveAck("Verbose logging enabled."),
|
||||
);
|
||||
}
|
||||
if (directives.hasReasoningDirective && directives.reasoningLevel) {
|
||||
|
||||
@@ -227,7 +227,7 @@ export function createFollowupRunner(params: {
|
||||
sessionKey,
|
||||
storePath,
|
||||
});
|
||||
if (queued.run.verboseLevel === "on") {
|
||||
if (queued.run.verboseLevel && queued.run.verboseLevel !== "off") {
|
||||
const suffix = typeof count === "number" ? ` (count ${count})` : "";
|
||||
finalPayloads.unshift({
|
||||
text: `🧹 Auto-compaction complete${suffix}.`,
|
||||
|
||||
@@ -271,7 +271,8 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
|
||||
const queueMode = args.queue?.mode ?? "unknown";
|
||||
const queueDetails = formatQueueDetails(args.queue);
|
||||
const verboseLabel = verboseLevel === "on" ? "verbose" : null;
|
||||
const verboseLabel =
|
||||
verboseLevel === "full" ? "verbose:full" : verboseLevel === "on" ? "verbose" : null;
|
||||
const elevatedLabel = elevatedLevel === "on" ? "elevated" : null;
|
||||
const optionParts = [
|
||||
`Runtime: ${runtime.label}`,
|
||||
@@ -338,7 +339,7 @@ export function buildStatusMessage(args: StatusArgs): string {
|
||||
export function buildHelpMessage(cfg?: ClawdbotConfig): string {
|
||||
const options = [
|
||||
"/think <level>",
|
||||
"/verbose on|off",
|
||||
"/verbose on|full|off",
|
||||
"/reasoning on|off",
|
||||
"/elevated on|off",
|
||||
"/model <id>",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||
export type VerboseLevel = "off" | "on";
|
||||
export type VerboseLevel = "off" | "on" | "full";
|
||||
export type ElevatedLevel = "off" | "on";
|
||||
export type ReasoningLevel = "off" | "on" | "stream";
|
||||
export type UsageDisplayLevel = "off" | "on";
|
||||
@@ -87,7 +87,8 @@ export function normalizeVerboseLevel(raw?: string | null): VerboseLevel | undef
|
||||
if (!raw) return undefined;
|
||||
const key = raw.toLowerCase();
|
||||
if (["off", "false", "no", "0"].includes(key)) return "off";
|
||||
if (["on", "full", "true", "yes", "1"].includes(key)) return "on";
|
||||
if (["full", "all", "everything"].includes(key)) return "full";
|
||||
if (["on", "minimal", "true", "yes", "1"].includes(key)) return "on";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user