Files
openclaw/src/auto-reply/reply/commands-btw.ts
Nimrod Gutman 9aac55d306 Add /btw side questions (#45444)
* feat(agent): add /btw side questions

* fix(agent): gate and log /btw reviews

* feat(btw): isolate side-question delivery

* test(reply): update route reply runtime mocks

* fix(btw): complete side-result delivery across clients

* fix(gateway): handle streamed btw side results

* fix(telegram): unblock btw side questions

* fix(reply): make external btw replies explicit

* fix(chat): keep btw side results ephemeral in internal history

* fix(btw): address remaining review feedback

* fix(chat): preserve btw history on mobile refresh

* fix(acp): keep btw replies out of prompt history

* refactor(btw): narrow side questions to live channels

* fix(btw): preserve channel typing indicators

* fix(btw): keep side questions isolated in chat

* fix(outbound): restore typed channel send deps

* fix(btw): avoid blocking replies on transcript persistence

* fix(btw): keep side questions fast

* docs(commands): document btw slash command

* docs(changelog): add btw side questions entry

* test(outbound): align session transcript mocks
2026-03-14 17:27:54 +02:00

81 lines
2.3 KiB
TypeScript

import { runBtwSideQuestion } from "../../agents/btw.js";
import { extractBtwQuestion } from "./btw-command.js";
import { rejectUnauthorizedCommand } from "./command-gates.js";
import type { CommandHandler } from "./commands-types.js";
const BTW_USAGE = "Usage: /btw <side question>";
export const handleBtwCommand: CommandHandler = async (params, allowTextCommands) => {
if (!allowTextCommands) {
return null;
}
const question = extractBtwQuestion(params.command.commandBodyNormalized);
if (question === null) {
return null;
}
const unauthorized = rejectUnauthorizedCommand(params, "/btw");
if (unauthorized) {
return unauthorized;
}
if (!question) {
return {
shouldContinue: false,
reply: { text: BTW_USAGE },
};
}
if (!params.sessionEntry?.sessionId) {
return {
shouldContinue: false,
reply: { text: "⚠️ /btw requires an active session with existing context." },
};
}
if (!params.agentDir) {
return {
shouldContinue: false,
reply: {
text: "⚠️ /btw is unavailable because the active agent directory could not be resolved.",
},
};
}
try {
await params.typing?.startTypingLoop();
const reply = await runBtwSideQuestion({
cfg: params.cfg,
agentDir: params.agentDir,
provider: params.provider,
model: params.model,
question,
sessionEntry: params.sessionEntry,
sessionStore: params.sessionStore,
sessionKey: params.sessionKey,
storePath: params.storePath,
// BTW is intentionally a quick side question, so do not inherit slower
// session-level think/reasoning settings from the main run.
resolvedThinkLevel: "off",
resolvedReasoningLevel: "off",
blockReplyChunking: params.blockReplyChunking,
resolvedBlockStreamingBreak: params.resolvedBlockStreamingBreak,
opts: params.opts,
isNewSession: false,
});
return {
shouldContinue: false,
reply: reply ? { ...reply, btw: { question } } : reply,
};
} catch (error) {
const message = error instanceof Error ? error.message.trim() : "";
return {
shouldContinue: false,
reply: {
text: `⚠️ /btw failed${message ? `: ${message}` : "."}`,
btw: { question },
isError: true,
},
};
}
};