fix: scope history injection to pending-only

This commit is contained in:
Peter Steinberger
2026-01-16 23:52:14 +00:00
parent 56ed5cc2d9
commit e31251293b
21 changed files with 278 additions and 175 deletions

View File

@@ -66,8 +66,6 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
});
};
let didSendReply = false;
// Create mutable context for response prefix template interpolation
let prefixContext: ResponsePrefixContext = {
identityName: resolveIdentityName(cfg, route.agentId),
@@ -88,7 +86,6 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
textLimit: ctx.textLimit,
replyThreadTs,
});
didSendReply = true;
replyPlan.markSent();
},
onError: (err, info) => {
@@ -136,7 +133,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
}
if (!queuedFinal) {
if (prepared.isRoomish && ctx.historyLimit > 0 && didSendReply) {
if (prepared.isRoomish && ctx.historyLimit > 0) {
clearHistoryEntries({
historyMap: ctx.channelHistories,
historyKey: prepared.historyKey,
@@ -168,7 +165,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
});
}
if (prepared.isRoomish && ctx.historyLimit > 0 && didSendReply) {
if (prepared.isRoomish && ctx.historyLimit > 0) {
clearHistoryEntries({
historyMap: ctx.channelHistories,
historyKey: prepared.historyKey,

View File

@@ -2,7 +2,10 @@ import { resolveAckReaction } from "../../../agents/identity.js";
import { hasControlCommand } from "../../../auto-reply/command-detection.js";
import { shouldHandleTextCommands } from "../../../auto-reply/commands-registry.js";
import { formatAgentEnvelope, formatThreadStarterEnvelope } from "../../../auto-reply/envelope.js";
import { buildHistoryContextFromMap } from "../../../auto-reply/reply/history.js";
import {
buildPendingHistoryContextFromMap,
recordPendingHistoryEntry,
} from "../../../auto-reply/reply/history.js";
import { buildMentionRegexes, matchesMentionPatterns } from "../../../auto-reply/reply/mentions.js";
import { logVerbose, shouldLogVerbose } from "../../../globals.js";
import { enqueueSystemEvent } from "../../../infra/system-events.js";
@@ -167,6 +170,19 @@ export async function prepareSlackMessage(params: {
},
});
const baseSessionKey = route.sessionKey;
const threadTs = message.thread_ts;
const hasThreadTs = typeof threadTs === "string" && threadTs.length > 0;
const isThreadReply = hasThreadTs && (threadTs !== message.ts || Boolean(message.parent_user_id));
const threadKeys = resolveThreadSessionKeys({
baseSessionKey,
threadId: isThreadReply ? threadTs : undefined,
parentSessionKey: isThreadReply && ctx.threadInheritParent ? baseSessionKey : undefined,
});
const sessionKey = threadKeys.sessionKey;
const historyKey =
isThreadReply && ctx.threadHistoryScope === "thread" ? sessionKey : message.channel;
const mentionRegexes = buildMentionRegexes(cfg, route.agentId);
const wasMentioned =
opts.wasMentioned ??
@@ -233,6 +249,28 @@ export async function prepareSlackMessage(params: {
const effectiveWasMentioned = mentionGate.effectiveWasMentioned;
if (isRoom && shouldRequireMention && mentionGate.shouldSkip) {
ctx.logger.info({ channel: message.channel, reason: "no-mention" }, "skipping room message");
if (ctx.historyLimit > 0) {
const pendingText = (message.text ?? "").trim();
const fallbackFile = message.files?.[0]?.name
? `[Slack file: ${message.files[0].name}]`
: message.files?.length
? "[Slack file]"
: "";
const pendingBody = pendingText || fallbackFile;
if (pendingBody) {
recordPendingHistoryEntry({
historyMap: ctx.channelHistories,
historyKey,
limit: ctx.historyLimit,
entry: {
sender: senderName,
body: pendingBody,
timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
messageId: message.ts,
},
});
}
}
return null;
}
@@ -277,16 +315,6 @@ export async function prepareSlackMessage(params: {
: null;
const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
const historyEntry =
isRoomish && ctx.historyLimit > 0
? {
sender: senderName,
body: rawBody,
timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
messageId: message.ts,
}
: undefined;
const preview = rawBody.replace(/\s+/g, " ").slice(0, 160);
const inboundLabel = isDirectMessage
? `Slack DM from ${senderName}`
@@ -297,18 +325,6 @@ export async function prepareSlackMessage(params: {
? `slack:channel:${message.channel}`
: `slack:group:${message.channel}`;
const baseSessionKey = route.sessionKey;
const threadTs = message.thread_ts;
const hasThreadTs = typeof threadTs === "string" && threadTs.length > 0;
const isThreadReply = hasThreadTs && (threadTs !== message.ts || Boolean(message.parent_user_id));
const threadKeys = resolveThreadSessionKeys({
baseSessionKey,
threadId: isThreadReply ? threadTs : undefined,
parentSessionKey: isThreadReply && ctx.threadInheritParent ? baseSessionKey : undefined,
});
const sessionKey = threadKeys.sessionKey;
const historyKey =
isThreadReply && ctx.threadHistoryScope === "thread" ? sessionKey : message.channel;
enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
sessionKey,
contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`,
@@ -324,11 +340,10 @@ export async function prepareSlackMessage(params: {
let combinedBody = body;
if (isRoomish && ctx.historyLimit > 0) {
combinedBody = buildHistoryContextFromMap({
combinedBody = buildPendingHistoryContextFromMap({
historyMap: ctx.channelHistories,
historyKey,
limit: ctx.historyLimit,
entry: historyEntry,
currentMessage: combinedBody,
formatEntry: (entry) =>
formatAgentEnvelope({