refactor: share reply prefix context
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { LocationMessageEventContent, MatrixClient } from "matrix-bot-sdk";
|
import type { LocationMessageEventContent, MatrixClient } from "matrix-bot-sdk";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
createReplyPrefixContext,
|
||||||
createTypingCallbacks,
|
createTypingCallbacks,
|
||||||
formatAllowlistMatchMeta,
|
formatAllowlistMatchMeta,
|
||||||
type RuntimeEnv,
|
type RuntimeEnv,
|
||||||
@@ -553,6 +554,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|||||||
channel: "matrix",
|
channel: "matrix",
|
||||||
accountId: route.accountId,
|
accountId: route.accountId,
|
||||||
});
|
});
|
||||||
|
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
||||||
const typingCallbacks = createTypingCallbacks({
|
const typingCallbacks = createTypingCallbacks({
|
||||||
start: () => sendTypingMatrix(roomId, true, undefined, client),
|
start: () => sendTypingMatrix(roomId, true, undefined, client),
|
||||||
stop: () => sendTypingMatrix(roomId, false, undefined, client),
|
stop: () => sendTypingMatrix(roomId, false, undefined, client),
|
||||||
@@ -565,8 +567,8 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|||||||
});
|
});
|
||||||
const { dispatcher, replyOptions, markDispatchIdle } =
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
core.channel.reply.createReplyDispatcherWithTyping({
|
core.channel.reply.createReplyDispatcherWithTyping({
|
||||||
responsePrefix: core.channel.reply.resolveEffectiveMessagesConfig(cfg, route.agentId)
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
.responsePrefix,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
await deliverMatrixReplies({
|
await deliverMatrixReplies({
|
||||||
@@ -596,6 +598,7 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|||||||
replyOptions: {
|
replyOptions: {
|
||||||
...replyOptions,
|
...replyOptions,
|
||||||
skillFilter: roomConfig?.skills,
|
skillFilter: roomConfig?.skills,
|
||||||
|
onModelSelected: prefixContext.onModelSelected,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
markDispatchIdle();
|
markDispatchIdle();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
RuntimeEnv,
|
RuntimeEnv,
|
||||||
} from "clawdbot/plugin-sdk";
|
} from "clawdbot/plugin-sdk";
|
||||||
import {
|
import {
|
||||||
|
createReplyPrefixContext,
|
||||||
createTypingCallbacks,
|
createTypingCallbacks,
|
||||||
buildPendingHistoryContextFromMap,
|
buildPendingHistoryContextFromMap,
|
||||||
clearHistoryEntriesIfEnabled,
|
clearHistoryEntriesIfEnabled,
|
||||||
@@ -31,12 +32,9 @@ import {
|
|||||||
} from "./client.js";
|
} from "./client.js";
|
||||||
import {
|
import {
|
||||||
createDedupeCache,
|
createDedupeCache,
|
||||||
extractShortModelName,
|
|
||||||
formatInboundFromLabel,
|
formatInboundFromLabel,
|
||||||
rawDataToString,
|
rawDataToString,
|
||||||
resolveIdentityName,
|
|
||||||
resolveThreadSessionKeys,
|
resolveThreadSessionKeys,
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "./monitor-helpers.js";
|
} from "./monitor-helpers.js";
|
||||||
import { sendMessageMattermost } from "./send.js";
|
import { sendMessageMattermost } from "./send.js";
|
||||||
|
|
||||||
@@ -710,9 +708,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
|||||||
accountId: account.accountId,
|
accountId: account.accountId,
|
||||||
});
|
});
|
||||||
|
|
||||||
let prefixContext: ResponsePrefixContext = {
|
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
||||||
identityName: resolveIdentityName(cfg, route.agentId),
|
|
||||||
};
|
|
||||||
|
|
||||||
const typingCallbacks = createTypingCallbacks({
|
const typingCallbacks = createTypingCallbacks({
|
||||||
start: () => sendTypingIndicator(channelId, threadRootId),
|
start: () => sendTypingIndicator(channelId, threadRootId),
|
||||||
@@ -722,9 +718,8 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
|||||||
});
|
});
|
||||||
const { dispatcher, replyOptions, markDispatchIdle } =
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
core.channel.reply.createReplyDispatcherWithTyping({
|
core.channel.reply.createReplyDispatcherWithTyping({
|
||||||
responsePrefix: core.channel.reply.resolveEffectiveMessagesConfig(cfg, route.agentId)
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
.responsePrefix,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
|
||||||
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(cfg, route.agentId),
|
||||||
deliver: async (payload: ReplyPayload) => {
|
deliver: async (payload: ReplyPayload) => {
|
||||||
const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
||||||
@@ -766,12 +761,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
|||||||
...replyOptions,
|
...replyOptions,
|
||||||
disableBlockStreaming:
|
disableBlockStreaming:
|
||||||
typeof account.blockStreaming === "boolean" ? !account.blockStreaming : undefined,
|
typeof account.blockStreaming === "boolean" ? !account.blockStreaming : undefined,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: prefixContext.onModelSelected,
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
markDispatchIdle();
|
markDispatchIdle();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
createReplyPrefixContext,
|
||||||
createTypingCallbacks,
|
createTypingCallbacks,
|
||||||
resolveChannelMediaMaxBytes,
|
resolveChannelMediaMaxBytes,
|
||||||
type ClawdbotConfig,
|
type ClawdbotConfig,
|
||||||
@@ -48,17 +49,20 @@ export function createMSTeamsReplyDispatcher(params: {
|
|||||||
// Typing indicator is best-effort.
|
// Typing indicator is best-effort.
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const prefixContext = createReplyPrefixContext({
|
||||||
|
cfg: params.cfg,
|
||||||
|
agentId: params.agentId,
|
||||||
|
});
|
||||||
|
|
||||||
return core.channel.reply.createReplyDispatcherWithTyping({
|
const { dispatcher, replyOptions, markDispatchIdle } =
|
||||||
responsePrefix: core.channel.reply.resolveEffectiveMessagesConfig(
|
core.channel.reply.createReplyDispatcherWithTyping({
|
||||||
params.cfg,
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
params.agentId,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
).responsePrefix,
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
|
||||||
humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
|
deliver: async (payload) => {
|
||||||
deliver: async (payload) => {
|
const tableMode = core.channel.text.resolveMarkdownTableMode({
|
||||||
const tableMode = core.channel.text.resolveMarkdownTableMode({
|
cfg: params.cfg,
|
||||||
cfg: params.cfg,
|
channel: "msteams",
|
||||||
channel: "msteams",
|
|
||||||
});
|
});
|
||||||
const messages = renderReplyPayloadsToMessages([payload], {
|
const messages = renderReplyPayloadsToMessages([payload], {
|
||||||
textChunkLimit: params.textLimit,
|
textChunkLimit: params.textLimit,
|
||||||
@@ -90,21 +94,27 @@ export function createMSTeamsReplyDispatcher(params: {
|
|||||||
mediaMaxBytes,
|
mediaMaxBytes,
|
||||||
});
|
});
|
||||||
if (ids.length > 0) params.onSentMessageIds?.(ids);
|
if (ids.length > 0) params.onSentMessageIds?.(ids);
|
||||||
},
|
},
|
||||||
onError: (err, info) => {
|
onError: (err, info) => {
|
||||||
const errMsg = formatUnknownError(err);
|
const errMsg = formatUnknownError(err);
|
||||||
const classification = classifyMSTeamsSendError(err);
|
const classification = classifyMSTeamsSendError(err);
|
||||||
const hint = formatMSTeamsSendErrorHint(classification);
|
const hint = formatMSTeamsSendErrorHint(classification);
|
||||||
params.runtime.error?.(
|
params.runtime.error?.(
|
||||||
`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`,
|
`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`,
|
||||||
);
|
);
|
||||||
params.log.error("reply failed", {
|
params.log.error("reply failed", {
|
||||||
kind: info.kind,
|
kind: info.kind,
|
||||||
error: errMsg,
|
error: errMsg,
|
||||||
classification,
|
classification,
|
||||||
hint,
|
hint,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onReplyStart: typingCallbacks.onReplyStart,
|
onReplyStart: typingCallbacks.onReplyStart,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
dispatcher,
|
||||||
|
replyOptions: { ...replyOptions, onModelSelected: prefixContext.onModelSelected },
|
||||||
|
markDispatchIdle,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
41
src/channels/reply-prefix.ts
Normal file
41
src/channels/reply-prefix.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { resolveEffectiveMessagesConfig, resolveIdentityName } from "../agents/identity.js";
|
||||||
|
import type { ClawdbotConfig } from "../config/config.js";
|
||||||
|
import type { GetReplyOptions } from "../auto-reply/types.js";
|
||||||
|
import {
|
||||||
|
extractShortModelName,
|
||||||
|
type ResponsePrefixContext,
|
||||||
|
} from "../auto-reply/reply/response-prefix-template.js";
|
||||||
|
|
||||||
|
type ModelSelectionContext = Parameters<NonNullable<GetReplyOptions["onModelSelected"]>>[0];
|
||||||
|
|
||||||
|
export type ReplyPrefixContextBundle = {
|
||||||
|
prefixContext: ResponsePrefixContext;
|
||||||
|
responsePrefix?: string;
|
||||||
|
responsePrefixContextProvider: () => ResponsePrefixContext;
|
||||||
|
onModelSelected: (ctx: ModelSelectionContext) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createReplyPrefixContext(params: {
|
||||||
|
cfg: ClawdbotConfig;
|
||||||
|
agentId: string;
|
||||||
|
}): ReplyPrefixContextBundle {
|
||||||
|
const { cfg, agentId } = params;
|
||||||
|
const prefixContext: ResponsePrefixContext = {
|
||||||
|
identityName: resolveIdentityName(cfg, agentId),
|
||||||
|
};
|
||||||
|
|
||||||
|
const onModelSelected = (ctx: ModelSelectionContext) => {
|
||||||
|
// Mutate the object directly instead of reassigning to ensure closures see updates.
|
||||||
|
prefixContext.provider = ctx.provider;
|
||||||
|
prefixContext.model = extractShortModelName(ctx.model);
|
||||||
|
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
||||||
|
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefixContext,
|
||||||
|
responsePrefix: resolveEffectiveMessagesConfig(cfg, agentId).responsePrefix,
|
||||||
|
responsePrefixContextProvider: () => prefixContext,
|
||||||
|
onModelSelected,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,17 +1,9 @@
|
|||||||
import {
|
import { resolveAckReaction, resolveHumanDelayConfig } from "../../agents/identity.js";
|
||||||
resolveAckReaction,
|
|
||||||
resolveEffectiveMessagesConfig,
|
|
||||||
resolveHumanDelayConfig,
|
|
||||||
resolveIdentityName,
|
|
||||||
} from "../../agents/identity.js";
|
|
||||||
import {
|
|
||||||
extractShortModelName,
|
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "../../auto-reply/reply/response-prefix-template.js";
|
|
||||||
import {
|
import {
|
||||||
removeAckReactionAfterReply,
|
removeAckReactionAfterReply,
|
||||||
shouldAckReaction as shouldAckReactionGate,
|
shouldAckReaction as shouldAckReactionGate,
|
||||||
} from "../../channels/ack-reactions.js";
|
} from "../../channels/ack-reactions.js";
|
||||||
|
import { createReplyPrefixContext } from "../../channels/reply-prefix.js";
|
||||||
import { createTypingCallbacks } from "../../channels/typing.js";
|
import { createTypingCallbacks } from "../../channels/typing.js";
|
||||||
import {
|
import {
|
||||||
formatInboundEnvelope,
|
formatInboundEnvelope,
|
||||||
@@ -318,10 +310,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
|||||||
? deliverTarget.slice("channel:".length)
|
? deliverTarget.slice("channel:".length)
|
||||||
: message.channelId;
|
: message.channelId;
|
||||||
|
|
||||||
// Create mutable context for response prefix template interpolation
|
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
||||||
let prefixContext: ResponsePrefixContext = {
|
|
||||||
identityName: resolveIdentityName(cfg, route.agentId),
|
|
||||||
};
|
|
||||||
const tableMode = resolveMarkdownTableMode({
|
const tableMode = resolveMarkdownTableMode({
|
||||||
cfg,
|
cfg,
|
||||||
channel: "discord",
|
channel: "discord",
|
||||||
@@ -329,8 +318,8 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
||||||
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
||||||
deliver: async (payload: ReplyPayload) => {
|
deliver: async (payload: ReplyPayload) => {
|
||||||
const replyToId = replyReference.use();
|
const replyToId = replyReference.use();
|
||||||
@@ -371,11 +360,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext)
|
|||||||
? !discordConfig.blockStreaming
|
? !discordConfig.blockStreaming
|
||||||
: undefined,
|
: undefined,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: (ctx) => {
|
||||||
// Mutate the object directly instead of reassigning to ensure the closure sees updates
|
prefixContext.onModelSelected(ctx);
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
|
|
||||||
import {
|
import { resolveHumanDelayConfig } from "../../agents/identity.js";
|
||||||
resolveEffectiveMessagesConfig,
|
|
||||||
resolveHumanDelayConfig,
|
|
||||||
resolveIdentityName,
|
|
||||||
} from "../../agents/identity.js";
|
|
||||||
import {
|
|
||||||
extractShortModelName,
|
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "../../auto-reply/reply/response-prefix-template.js";
|
|
||||||
import { resolveTextChunkLimit } from "../../auto-reply/chunk.js";
|
import { resolveTextChunkLimit } from "../../auto-reply/chunk.js";
|
||||||
import { hasControlCommand } from "../../auto-reply/command-detection.js";
|
import { hasControlCommand } from "../../auto-reply/command-detection.js";
|
||||||
import {
|
import {
|
||||||
@@ -31,6 +23,7 @@ import {
|
|||||||
} from "../../auto-reply/reply/history.js";
|
} from "../../auto-reply/reply/history.js";
|
||||||
import { buildMentionRegexes, matchesMentionPatterns } from "../../auto-reply/reply/mentions.js";
|
import { buildMentionRegexes, matchesMentionPatterns } from "../../auto-reply/reply/mentions.js";
|
||||||
import { createReplyDispatcher } from "../../auto-reply/reply/reply-dispatcher.js";
|
import { createReplyDispatcher } from "../../auto-reply/reply/reply-dispatcher.js";
|
||||||
|
import { createReplyPrefixContext } from "../../channels/reply-prefix.js";
|
||||||
import { recordInboundSession } from "../../channels/session.js";
|
import { recordInboundSession } from "../../channels/session.js";
|
||||||
import { loadConfig } from "../../config/config.js";
|
import { loadConfig } from "../../config/config.js";
|
||||||
import {
|
import {
|
||||||
@@ -531,14 +524,11 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create mutable context for response prefix template interpolation
|
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
||||||
let prefixContext: ResponsePrefixContext = {
|
|
||||||
identityName: resolveIdentityName(cfg, route.agentId),
|
|
||||||
};
|
|
||||||
|
|
||||||
const dispatcher = createReplyDispatcher({
|
const dispatcher = createReplyDispatcher({
|
||||||
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
await deliverReplies({
|
await deliverReplies({
|
||||||
@@ -565,13 +555,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P
|
|||||||
typeof accountInfo.config.blockStreaming === "boolean"
|
typeof accountInfo.config.blockStreaming === "boolean"
|
||||||
? !accountInfo.config.blockStreaming
|
? !accountInfo.config.blockStreaming
|
||||||
: undefined,
|
: undefined,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: prefixContext.onModelSelected,
|
||||||
// Mutate the object directly instead of reassigning to ensure the closure sees updates
|
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!queuedFinal) {
|
if (!queuedFinal) {
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ export {
|
|||||||
shouldAckReactionForWhatsApp,
|
shouldAckReactionForWhatsApp,
|
||||||
} from "../channels/ack-reactions.js";
|
} from "../channels/ack-reactions.js";
|
||||||
export { createTypingCallbacks } from "../channels/typing.js";
|
export { createTypingCallbacks } from "../channels/typing.js";
|
||||||
|
export { createReplyPrefixContext } from "../channels/reply-prefix.js";
|
||||||
export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js";
|
export { resolveChannelMediaMaxBytes } from "../channels/plugins/media-limits.js";
|
||||||
export type { NormalizedLocation } from "../channels/location.js";
|
export type { NormalizedLocation } from "../channels/location.js";
|
||||||
export { formatLocationText, toLocationContext } from "../channels/location.js";
|
export { formatLocationText, toLocationContext } from "../channels/location.js";
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { resolveHumanDelayConfig } from "../../agents/identity.js";
|
||||||
resolveEffectiveMessagesConfig,
|
|
||||||
resolveHumanDelayConfig,
|
|
||||||
resolveIdentityName,
|
|
||||||
} from "../../agents/identity.js";
|
|
||||||
import {
|
|
||||||
extractShortModelName,
|
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "../../auto-reply/reply/response-prefix-template.js";
|
|
||||||
import { hasControlCommand } from "../../auto-reply/command-detection.js";
|
import { hasControlCommand } from "../../auto-reply/command-detection.js";
|
||||||
import {
|
import {
|
||||||
formatInboundEnvelope,
|
formatInboundEnvelope,
|
||||||
@@ -24,6 +16,7 @@ import {
|
|||||||
} from "../../auto-reply/reply/history.js";
|
} from "../../auto-reply/reply/history.js";
|
||||||
import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
|
import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
|
||||||
import { createReplyDispatcherWithTyping } from "../../auto-reply/reply/reply-dispatcher.js";
|
import { createReplyDispatcherWithTyping } from "../../auto-reply/reply/reply-dispatcher.js";
|
||||||
|
import { createReplyPrefixContext } from "../../channels/reply-prefix.js";
|
||||||
import { recordInboundSession } from "../../channels/session.js";
|
import { recordInboundSession } from "../../channels/session.js";
|
||||||
import { createTypingCallbacks } from "../../channels/typing.js";
|
import { createTypingCallbacks } from "../../channels/typing.js";
|
||||||
import { readSessionUpdatedAt, resolveStorePath } from "../../config/sessions.js";
|
import { readSessionUpdatedAt, resolveStorePath } from "../../config/sessions.js";
|
||||||
@@ -178,10 +171,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
|
|||||||
logVerbose(`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`);
|
logVerbose(`signal inbound: from=${ctxPayload.From} len=${body.length} preview="${preview}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create mutable context for response prefix template interpolation
|
const prefixContext = createReplyPrefixContext({ cfg: deps.cfg, agentId: route.agentId });
|
||||||
let prefixContext: ResponsePrefixContext = {
|
|
||||||
identityName: resolveIdentityName(deps.cfg, route.agentId),
|
|
||||||
};
|
|
||||||
|
|
||||||
const typingCallbacks = createTypingCallbacks({
|
const typingCallbacks = createTypingCallbacks({
|
||||||
start: async () => {
|
start: async () => {
|
||||||
@@ -198,8 +188,8 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
||||||
responsePrefix: resolveEffectiveMessagesConfig(deps.cfg, route.agentId).responsePrefix,
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
humanDelay: resolveHumanDelayConfig(deps.cfg, route.agentId),
|
humanDelay: resolveHumanDelayConfig(deps.cfg, route.agentId),
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
await deps.deliverReplies({
|
await deps.deliverReplies({
|
||||||
@@ -228,11 +218,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
|
|||||||
disableBlockStreaming:
|
disableBlockStreaming:
|
||||||
typeof deps.blockStreaming === "boolean" ? !deps.blockStreaming : undefined,
|
typeof deps.blockStreaming === "boolean" ? !deps.blockStreaming : undefined,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: (ctx) => {
|
||||||
// Mutate the object directly instead of reassigning to ensure the closure sees updates
|
prefixContext.onModelSelected(ctx);
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
import {
|
import { resolveHumanDelayConfig } from "../../../agents/identity.js";
|
||||||
resolveEffectiveMessagesConfig,
|
|
||||||
resolveHumanDelayConfig,
|
|
||||||
resolveIdentityName,
|
|
||||||
} from "../../../agents/identity.js";
|
|
||||||
import {
|
|
||||||
extractShortModelName,
|
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "../../../auto-reply/reply/response-prefix-template.js";
|
|
||||||
import { dispatchInboundMessage } from "../../../auto-reply/dispatch.js";
|
import { dispatchInboundMessage } from "../../../auto-reply/dispatch.js";
|
||||||
import { clearHistoryEntriesIfEnabled } from "../../../auto-reply/reply/history.js";
|
import { clearHistoryEntriesIfEnabled } from "../../../auto-reply/reply/history.js";
|
||||||
import { removeAckReactionAfterReply } from "../../../channels/ack-reactions.js";
|
import { removeAckReactionAfterReply } from "../../../channels/ack-reactions.js";
|
||||||
|
import { createReplyPrefixContext } from "../../../channels/reply-prefix.js";
|
||||||
import { createTypingCallbacks } from "../../../channels/typing.js";
|
import { createTypingCallbacks } from "../../../channels/typing.js";
|
||||||
import { createReplyDispatcherWithTyping } from "../../../auto-reply/reply/reply-dispatcher.js";
|
import { createReplyDispatcherWithTyping } from "../../../auto-reply/reply/reply-dispatcher.js";
|
||||||
|
import { resolveStorePath, updateLastRoute } from "../../../config/sessions.js";
|
||||||
import { danger, logVerbose, shouldLogVerbose } from "../../../globals.js";
|
import { danger, logVerbose, shouldLogVerbose } from "../../../globals.js";
|
||||||
import { removeSlackReaction } from "../../actions.js";
|
import { removeSlackReaction } from "../../actions.js";
|
||||||
import { resolveSlackThreadTargets } from "../../threading.js";
|
import { resolveSlackThreadTargets } from "../../threading.js";
|
||||||
@@ -25,6 +19,23 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
|||||||
const cfg = ctx.cfg;
|
const cfg = ctx.cfg;
|
||||||
const runtime = ctx.runtime;
|
const runtime = ctx.runtime;
|
||||||
|
|
||||||
|
if (prepared.isDirectMessage) {
|
||||||
|
const sessionCfg = cfg.session;
|
||||||
|
const storePath = resolveStorePath(sessionCfg?.store, {
|
||||||
|
agentId: route.agentId,
|
||||||
|
});
|
||||||
|
await updateLastRoute({
|
||||||
|
storePath,
|
||||||
|
sessionKey: route.mainSessionKey,
|
||||||
|
deliveryContext: {
|
||||||
|
channel: "slack",
|
||||||
|
to: `user:${message.user}`,
|
||||||
|
accountId: route.accountId,
|
||||||
|
},
|
||||||
|
ctx: prepared.ctxPayload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { statusThreadTs } = resolveSlackThreadTargets({
|
const { statusThreadTs } = resolveSlackThreadTargets({
|
||||||
message,
|
message,
|
||||||
replyToMode: ctx.replyToMode,
|
replyToMode: ctx.replyToMode,
|
||||||
@@ -69,14 +80,11 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create mutable context for response prefix template interpolation
|
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
||||||
let prefixContext: ResponsePrefixContext = {
|
|
||||||
identityName: resolveIdentityName(cfg, route.agentId),
|
|
||||||
};
|
|
||||||
|
|
||||||
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
const { dispatcher, replyOptions, markDispatchIdle } = createReplyDispatcherWithTyping({
|
||||||
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
||||||
deliver: async (payload) => {
|
deliver: async (payload) => {
|
||||||
const replyThreadTs = replyPlan.nextThreadTs();
|
const replyThreadTs = replyPlan.nextThreadTs();
|
||||||
@@ -112,11 +120,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
|
|||||||
? !account.config.blockStreaming
|
? !account.config.blockStreaming
|
||||||
: undefined,
|
: undefined,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: (ctx) => {
|
||||||
// Mutate the object directly instead of reassigning to ensure the closure sees updates
|
prefixContext.onModelSelected(ctx);
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { resolveEffectiveMessagesConfig, resolveIdentityName } from "../agents/identity.js";
|
|
||||||
import {
|
|
||||||
extractShortModelName,
|
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "../auto-reply/reply/response-prefix-template.js";
|
|
||||||
import { EmbeddedBlockChunker } from "../agents/pi-embedded-block-chunker.js";
|
import { EmbeddedBlockChunker } from "../agents/pi-embedded-block-chunker.js";
|
||||||
import { clearHistoryEntriesIfEnabled } from "../auto-reply/reply/history.js";
|
import { clearHistoryEntriesIfEnabled } from "../auto-reply/reply/history.js";
|
||||||
import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
|
import { dispatchReplyWithBufferedBlockDispatcher } from "../auto-reply/reply/provider-dispatcher.js";
|
||||||
import { removeAckReactionAfterReply } from "../channels/ack-reactions.js";
|
import { removeAckReactionAfterReply } from "../channels/ack-reactions.js";
|
||||||
|
import { createReplyPrefixContext } from "../channels/reply-prefix.js";
|
||||||
import { createTypingCallbacks } from "../channels/typing.js";
|
import { createTypingCallbacks } from "../channels/typing.js";
|
||||||
import { danger, logVerbose } from "../globals.js";
|
import { danger, logVerbose } from "../globals.js";
|
||||||
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
import { resolveMarkdownTableMode } from "../config/markdown-tables.js";
|
||||||
@@ -122,10 +118,7 @@ export const dispatchTelegramMessage = async ({
|
|||||||
Boolean(draftStream) ||
|
Boolean(draftStream) ||
|
||||||
(typeof telegramCfg.blockStreaming === "boolean" ? !telegramCfg.blockStreaming : undefined);
|
(typeof telegramCfg.blockStreaming === "boolean" ? !telegramCfg.blockStreaming : undefined);
|
||||||
|
|
||||||
// Create mutable context for response prefix template interpolation
|
const prefixContext = createReplyPrefixContext({ cfg, agentId: route.agentId });
|
||||||
let prefixContext: ResponsePrefixContext = {
|
|
||||||
identityName: resolveIdentityName(cfg, route.agentId),
|
|
||||||
};
|
|
||||||
const tableMode = resolveMarkdownTableMode({
|
const tableMode = resolveMarkdownTableMode({
|
||||||
cfg,
|
cfg,
|
||||||
channel: "telegram",
|
channel: "telegram",
|
||||||
@@ -136,8 +129,8 @@ export const dispatchTelegramMessage = async ({
|
|||||||
ctx: ctxPayload,
|
ctx: ctxPayload,
|
||||||
cfg,
|
cfg,
|
||||||
dispatcherOptions: {
|
dispatcherOptions: {
|
||||||
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
responsePrefix: prefixContext.responsePrefix,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
deliver: async (payload, info) => {
|
deliver: async (payload, info) => {
|
||||||
if (info.kind === "final") {
|
if (info.kind === "final") {
|
||||||
await flushDraft();
|
await flushDraft();
|
||||||
@@ -176,11 +169,7 @@ export const dispatchTelegramMessage = async ({
|
|||||||
: undefined,
|
: undefined,
|
||||||
disableBlockStreaming,
|
disableBlockStreaming,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: (ctx) => {
|
||||||
// Mutate the object directly instead of reassigning to ensure the closure sees updates
|
prefixContext.onModelSelected(ctx);
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
import {
|
import { resolveIdentityNamePrefix } from "../../../agents/identity.js";
|
||||||
resolveEffectiveMessagesConfig,
|
|
||||||
resolveIdentityName,
|
|
||||||
resolveIdentityNamePrefix,
|
|
||||||
} from "../../../agents/identity.js";
|
|
||||||
import {
|
|
||||||
extractShortModelName,
|
|
||||||
type ResponsePrefixContext,
|
|
||||||
} from "../../../auto-reply/reply/response-prefix-template.js";
|
|
||||||
import { resolveTextChunkLimit } from "../../../auto-reply/chunk.js";
|
import { resolveTextChunkLimit } from "../../../auto-reply/chunk.js";
|
||||||
import {
|
import {
|
||||||
formatInboundEnvelope,
|
formatInboundEnvelope,
|
||||||
@@ -22,6 +14,7 @@ import type { ReplyPayload } from "../../../auto-reply/types.js";
|
|||||||
import { shouldComputeCommandAuthorized } from "../../../auto-reply/command-detection.js";
|
import { shouldComputeCommandAuthorized } from "../../../auto-reply/command-detection.js";
|
||||||
import { finalizeInboundContext } from "../../../auto-reply/reply/inbound-context.js";
|
import { finalizeInboundContext } from "../../../auto-reply/reply/inbound-context.js";
|
||||||
import { toLocationContext } from "../../../channels/location.js";
|
import { toLocationContext } from "../../../channels/location.js";
|
||||||
|
import { createReplyPrefixContext } from "../../../channels/reply-prefix.js";
|
||||||
import type { loadConfig } from "../../../config/config.js";
|
import type { loadConfig } from "../../../config/config.js";
|
||||||
import {
|
import {
|
||||||
readSessionUpdatedAt,
|
readSessionUpdatedAt,
|
||||||
@@ -247,22 +240,20 @@ export async function processMessage(params: {
|
|||||||
? await resolveWhatsAppCommandAuthorized({ cfg: params.cfg, msg: params.msg })
|
? await resolveWhatsAppCommandAuthorized({ cfg: params.cfg, msg: params.msg })
|
||||||
: undefined;
|
: undefined;
|
||||||
const configuredResponsePrefix = params.cfg.messages?.responsePrefix;
|
const configuredResponsePrefix = params.cfg.messages?.responsePrefix;
|
||||||
const resolvedMessages = resolveEffectiveMessagesConfig(params.cfg, params.route.agentId);
|
const prefixContext = createReplyPrefixContext({
|
||||||
|
cfg: params.cfg,
|
||||||
|
agentId: params.route.agentId,
|
||||||
|
});
|
||||||
const isSelfChat =
|
const isSelfChat =
|
||||||
params.msg.chatType !== "group" &&
|
params.msg.chatType !== "group" &&
|
||||||
Boolean(params.msg.selfE164) &&
|
Boolean(params.msg.selfE164) &&
|
||||||
normalizeE164(params.msg.from) === normalizeE164(params.msg.selfE164 ?? "");
|
normalizeE164(params.msg.from) === normalizeE164(params.msg.selfE164 ?? "");
|
||||||
const responsePrefix =
|
const responsePrefix =
|
||||||
resolvedMessages.responsePrefix ??
|
prefixContext.responsePrefix ??
|
||||||
(configuredResponsePrefix === undefined && isSelfChat
|
(configuredResponsePrefix === undefined && isSelfChat
|
||||||
? (resolveIdentityNamePrefix(params.cfg, params.route.agentId) ?? "[clawdbot]")
|
? (resolveIdentityNamePrefix(params.cfg, params.route.agentId) ?? "[clawdbot]")
|
||||||
: undefined);
|
: undefined);
|
||||||
|
|
||||||
// Create mutable context for response prefix template interpolation
|
|
||||||
let prefixContext: ResponsePrefixContext = {
|
|
||||||
identityName: resolveIdentityName(params.cfg, params.route.agentId),
|
|
||||||
};
|
|
||||||
|
|
||||||
const ctxPayload = finalizeInboundContext({
|
const ctxPayload = finalizeInboundContext({
|
||||||
Body: combinedBody,
|
Body: combinedBody,
|
||||||
RawBody: params.msg.body,
|
RawBody: params.msg.body,
|
||||||
@@ -334,7 +325,7 @@ export async function processMessage(params: {
|
|||||||
replyResolver: params.replyResolver,
|
replyResolver: params.replyResolver,
|
||||||
dispatcherOptions: {
|
dispatcherOptions: {
|
||||||
responsePrefix,
|
responsePrefix,
|
||||||
responsePrefixContextProvider: () => prefixContext,
|
responsePrefixContextProvider: prefixContext.responsePrefixContextProvider,
|
||||||
onHeartbeatStrip: () => {
|
onHeartbeatStrip: () => {
|
||||||
if (!didLogHeartbeatStrip) {
|
if (!didLogHeartbeatStrip) {
|
||||||
didLogHeartbeatStrip = true;
|
didLogHeartbeatStrip = true;
|
||||||
@@ -393,13 +384,7 @@ export async function processMessage(params: {
|
|||||||
typeof params.cfg.channels?.whatsapp?.blockStreaming === "boolean"
|
typeof params.cfg.channels?.whatsapp?.blockStreaming === "boolean"
|
||||||
? !params.cfg.channels.whatsapp.blockStreaming
|
? !params.cfg.channels.whatsapp.blockStreaming
|
||||||
: undefined,
|
: undefined,
|
||||||
onModelSelected: (ctx) => {
|
onModelSelected: prefixContext.onModelSelected,
|
||||||
// Mutate the object directly instead of reassigning to ensure the closure sees updates
|
|
||||||
prefixContext.provider = ctx.provider;
|
|
||||||
prefixContext.model = extractShortModelName(ctx.model);
|
|
||||||
prefixContext.modelFull = `${ctx.provider}/${ctx.model}`;
|
|
||||||
prefixContext.thinkingLevel = ctx.thinkLevel ?? "off";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user