refactor(auto-reply): split reply pipeline
This commit is contained in:
256
src/auto-reply/reply/get-reply-inline-actions.ts
Normal file
256
src/auto-reply/reply/get-reply-inline-actions.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
import { getChannelDock } from "../../channels/dock.js";
|
||||
import type { ClawdbotConfig } from "../../config/config.js";
|
||||
import type { SessionEntry } from "../../config/sessions.js";
|
||||
import type { MsgContext, TemplateContext } from "../templating.js";
|
||||
import type {
|
||||
ElevatedLevel,
|
||||
ReasoningLevel,
|
||||
ThinkLevel,
|
||||
VerboseLevel,
|
||||
} from "../thinking.js";
|
||||
import type { GetReplyOptions, ReplyPayload } from "../types.js";
|
||||
import { getAbortMemory } from "./abort.js";
|
||||
import { buildStatusReply, handleCommands } from "./commands.js";
|
||||
import type { InlineDirectives } from "./directive-handling.js";
|
||||
import { isDirectiveOnly } from "./directive-handling.js";
|
||||
import type { createModelSelectionState } from "./model-selection.js";
|
||||
import { extractInlineSimpleCommand } from "./reply-inline.js";
|
||||
import type { TypingController } from "./typing.js";
|
||||
|
||||
export type InlineActionResult =
|
||||
| { kind: "reply"; reply: ReplyPayload | ReplyPayload[] | undefined }
|
||||
| {
|
||||
kind: "continue";
|
||||
directives: InlineDirectives;
|
||||
abortedLastRun: boolean;
|
||||
};
|
||||
|
||||
export async function handleInlineActions(params: {
|
||||
ctx: MsgContext;
|
||||
sessionCtx: TemplateContext;
|
||||
cfg: ClawdbotConfig;
|
||||
agentId: string;
|
||||
sessionEntry?: SessionEntry;
|
||||
sessionStore?: Record<string, SessionEntry>;
|
||||
sessionKey: string;
|
||||
storePath?: string;
|
||||
sessionScope: Parameters<typeof buildStatusReply>[0]["sessionScope"];
|
||||
workspaceDir: string;
|
||||
isGroup: boolean;
|
||||
opts?: GetReplyOptions;
|
||||
typing: TypingController;
|
||||
allowTextCommands: boolean;
|
||||
inlineStatusRequested: boolean;
|
||||
command: Parameters<typeof handleCommands>[0]["command"];
|
||||
directives: InlineDirectives;
|
||||
cleanedBody: string;
|
||||
elevatedEnabled: boolean;
|
||||
elevatedAllowed: boolean;
|
||||
elevatedFailures: Array<{ gate: string; key: string }>;
|
||||
defaultActivation: Parameters<
|
||||
typeof buildStatusReply
|
||||
>[0]["defaultGroupActivation"];
|
||||
resolvedThinkLevel: ThinkLevel | undefined;
|
||||
resolvedVerboseLevel: VerboseLevel | undefined;
|
||||
resolvedReasoningLevel: ReasoningLevel;
|
||||
resolvedElevatedLevel: ElevatedLevel;
|
||||
resolveDefaultThinkingLevel: Awaited<
|
||||
ReturnType<typeof createModelSelectionState>
|
||||
>["resolveDefaultThinkingLevel"];
|
||||
provider: string;
|
||||
model: string;
|
||||
contextTokens: number;
|
||||
directiveAck?: ReplyPayload;
|
||||
abortedLastRun: boolean;
|
||||
}): Promise<InlineActionResult> {
|
||||
const {
|
||||
ctx,
|
||||
sessionCtx,
|
||||
cfg,
|
||||
agentId,
|
||||
sessionEntry,
|
||||
sessionStore,
|
||||
sessionKey,
|
||||
storePath,
|
||||
sessionScope,
|
||||
workspaceDir,
|
||||
isGroup,
|
||||
opts,
|
||||
typing,
|
||||
allowTextCommands,
|
||||
inlineStatusRequested,
|
||||
command,
|
||||
directives: initialDirectives,
|
||||
cleanedBody: initialCleanedBody,
|
||||
elevatedEnabled,
|
||||
elevatedAllowed,
|
||||
elevatedFailures,
|
||||
defaultActivation,
|
||||
resolvedThinkLevel,
|
||||
resolvedVerboseLevel,
|
||||
resolvedReasoningLevel,
|
||||
resolvedElevatedLevel,
|
||||
resolveDefaultThinkingLevel,
|
||||
provider,
|
||||
model,
|
||||
contextTokens,
|
||||
directiveAck,
|
||||
abortedLastRun: initialAbortedLastRun,
|
||||
} = params;
|
||||
|
||||
let directives = initialDirectives;
|
||||
let cleanedBody = initialCleanedBody;
|
||||
|
||||
const sendInlineReply = async (reply?: ReplyPayload) => {
|
||||
if (!reply) return;
|
||||
if (!opts?.onBlockReply) return;
|
||||
await opts.onBlockReply(reply);
|
||||
};
|
||||
|
||||
const inlineCommand =
|
||||
allowTextCommands && command.isAuthorizedSender
|
||||
? extractInlineSimpleCommand(cleanedBody)
|
||||
: null;
|
||||
if (inlineCommand) {
|
||||
cleanedBody = inlineCommand.cleaned;
|
||||
sessionCtx.Body = cleanedBody;
|
||||
sessionCtx.BodyStripped = cleanedBody;
|
||||
}
|
||||
|
||||
const handleInlineStatus =
|
||||
!isDirectiveOnly({
|
||||
directives,
|
||||
cleanedBody: directives.cleaned,
|
||||
ctx,
|
||||
cfg,
|
||||
agentId,
|
||||
isGroup,
|
||||
}) && inlineStatusRequested;
|
||||
if (handleInlineStatus) {
|
||||
const inlineStatusReply = await buildStatusReply({
|
||||
cfg,
|
||||
command,
|
||||
sessionEntry,
|
||||
sessionKey,
|
||||
sessionScope,
|
||||
provider,
|
||||
model,
|
||||
contextTokens,
|
||||
resolvedThinkLevel,
|
||||
resolvedVerboseLevel: resolvedVerboseLevel ?? "off",
|
||||
resolvedReasoningLevel,
|
||||
resolvedElevatedLevel,
|
||||
resolveDefaultThinkingLevel,
|
||||
isGroup,
|
||||
defaultGroupActivation: defaultActivation,
|
||||
});
|
||||
await sendInlineReply(inlineStatusReply);
|
||||
directives = { ...directives, hasStatusDirective: false };
|
||||
}
|
||||
|
||||
if (inlineCommand) {
|
||||
const inlineCommandContext = {
|
||||
...command,
|
||||
rawBodyNormalized: inlineCommand.command,
|
||||
commandBodyNormalized: inlineCommand.command,
|
||||
};
|
||||
const inlineResult = await handleCommands({
|
||||
ctx,
|
||||
cfg,
|
||||
command: inlineCommandContext,
|
||||
agentId,
|
||||
directives,
|
||||
elevated: {
|
||||
enabled: elevatedEnabled,
|
||||
allowed: elevatedAllowed,
|
||||
failures: elevatedFailures,
|
||||
},
|
||||
sessionEntry,
|
||||
sessionStore,
|
||||
sessionKey,
|
||||
storePath,
|
||||
sessionScope,
|
||||
workspaceDir,
|
||||
defaultGroupActivation: defaultActivation,
|
||||
resolvedThinkLevel,
|
||||
resolvedVerboseLevel: resolvedVerboseLevel ?? "off",
|
||||
resolvedReasoningLevel,
|
||||
resolvedElevatedLevel,
|
||||
resolveDefaultThinkingLevel,
|
||||
provider,
|
||||
model,
|
||||
contextTokens,
|
||||
isGroup,
|
||||
});
|
||||
if (inlineResult.reply) {
|
||||
if (!inlineCommand.cleaned) {
|
||||
typing.cleanup();
|
||||
return { kind: "reply", reply: inlineResult.reply };
|
||||
}
|
||||
await sendInlineReply(inlineResult.reply);
|
||||
}
|
||||
}
|
||||
|
||||
if (directiveAck) {
|
||||
await sendInlineReply(directiveAck);
|
||||
}
|
||||
|
||||
const isEmptyConfig = Object.keys(cfg).length === 0;
|
||||
const skipWhenConfigEmpty = command.channelId
|
||||
? Boolean(getChannelDock(command.channelId)?.commands?.skipWhenConfigEmpty)
|
||||
: false;
|
||||
if (
|
||||
skipWhenConfigEmpty &&
|
||||
isEmptyConfig &&
|
||||
command.from &&
|
||||
command.to &&
|
||||
command.from !== command.to
|
||||
) {
|
||||
typing.cleanup();
|
||||
return { kind: "reply", reply: undefined };
|
||||
}
|
||||
|
||||
let abortedLastRun = initialAbortedLastRun;
|
||||
if (!sessionEntry && command.abortKey) {
|
||||
abortedLastRun = getAbortMemory(command.abortKey) ?? false;
|
||||
}
|
||||
|
||||
const commandResult = await handleCommands({
|
||||
ctx,
|
||||
cfg,
|
||||
command,
|
||||
agentId,
|
||||
directives,
|
||||
elevated: {
|
||||
enabled: elevatedEnabled,
|
||||
allowed: elevatedAllowed,
|
||||
failures: elevatedFailures,
|
||||
},
|
||||
sessionEntry,
|
||||
sessionStore,
|
||||
sessionKey,
|
||||
storePath,
|
||||
sessionScope,
|
||||
workspaceDir,
|
||||
defaultGroupActivation: defaultActivation,
|
||||
resolvedThinkLevel,
|
||||
resolvedVerboseLevel: resolvedVerboseLevel ?? "off",
|
||||
resolvedReasoningLevel,
|
||||
resolvedElevatedLevel,
|
||||
resolveDefaultThinkingLevel,
|
||||
provider,
|
||||
model,
|
||||
contextTokens,
|
||||
isGroup,
|
||||
});
|
||||
if (!commandResult.shouldContinue) {
|
||||
typing.cleanup();
|
||||
return { kind: "reply", reply: commandResult.reply };
|
||||
}
|
||||
|
||||
return {
|
||||
kind: "continue",
|
||||
directives,
|
||||
abortedLastRun,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user