refactor: centralize thread helpers
This commit is contained in:
@@ -34,3 +34,17 @@ export function formatAgentEnvelope(params: AgentEnvelopeParams): string {
|
|||||||
const header = `[${parts.join(" ")}]`;
|
const header = `[${parts.join(" ")}]`;
|
||||||
return `${header} ${params.body}`;
|
return `${header} ${params.body}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatThreadStarterEnvelope(params: {
|
||||||
|
provider: string;
|
||||||
|
author?: string;
|
||||||
|
timestamp?: number | Date;
|
||||||
|
body: string;
|
||||||
|
}): string {
|
||||||
|
return formatAgentEnvelope({
|
||||||
|
provider: params.provider,
|
||||||
|
from: params.author,
|
||||||
|
timestamp: params.timestamp,
|
||||||
|
body: params.body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ import {
|
|||||||
listNativeCommandSpecs,
|
listNativeCommandSpecs,
|
||||||
shouldHandleTextCommands,
|
shouldHandleTextCommands,
|
||||||
} from "../auto-reply/commands-registry.js";
|
} from "../auto-reply/commands-registry.js";
|
||||||
import { formatAgentEnvelope } from "../auto-reply/envelope.js";
|
import {
|
||||||
|
formatAgentEnvelope,
|
||||||
|
formatThreadStarterEnvelope,
|
||||||
|
} from "../auto-reply/envelope.js";
|
||||||
import { dispatchReplyFromConfig } from "../auto-reply/reply/dispatch-from-config.js";
|
import { dispatchReplyFromConfig } from "../auto-reply/reply/dispatch-from-config.js";
|
||||||
import {
|
import {
|
||||||
buildMentionRegexes,
|
buildMentionRegexes,
|
||||||
@@ -55,6 +58,7 @@ import {
|
|||||||
buildAgentSessionKey,
|
buildAgentSessionKey,
|
||||||
resolveAgentRoute,
|
resolveAgentRoute,
|
||||||
} from "../routing/resolve-route.js";
|
} from "../routing/resolve-route.js";
|
||||||
|
import { resolveThreadSessionKeys } from "../routing/session-key.js";
|
||||||
import type { RuntimeEnv } from "../runtime.js";
|
import type { RuntimeEnv } from "../runtime.js";
|
||||||
import { loadWebMedia } from "../web/media.js";
|
import { loadWebMedia } from "../web/media.js";
|
||||||
import { fetchDiscordApplicationId } from "./probe.js";
|
import { fetchDiscordApplicationId } from "./probe.js";
|
||||||
@@ -863,9 +867,9 @@ export function createDiscordMessageHandler(params: {
|
|||||||
parentId: threadParentId,
|
parentId: threadParentId,
|
||||||
});
|
});
|
||||||
if (starter?.text) {
|
if (starter?.text) {
|
||||||
const starterEnvelope = formatAgentEnvelope({
|
const starterEnvelope = formatThreadStarterEnvelope({
|
||||||
provider: "Discord",
|
provider: "Discord",
|
||||||
from: starter.author,
|
author: starter.author,
|
||||||
timestamp: starter.timestamp,
|
timestamp: starter.timestamp,
|
||||||
body: starter.text,
|
body: starter.text,
|
||||||
});
|
});
|
||||||
@@ -885,13 +889,19 @@ export function createDiscordMessageHandler(params: {
|
|||||||
}
|
}
|
||||||
const mediaPayload = buildDiscordMediaPayload(mediaList);
|
const mediaPayload = buildDiscordMediaPayload(mediaList);
|
||||||
const discordTo = `channel:${message.channelId}`;
|
const discordTo = `channel:${message.channelId}`;
|
||||||
|
const threadKeys = resolveThreadSessionKeys({
|
||||||
|
baseSessionKey,
|
||||||
|
threadId: threadChannel ? message.channelId : undefined,
|
||||||
|
parentSessionKey,
|
||||||
|
useSuffix: false,
|
||||||
|
});
|
||||||
const ctxPayload = {
|
const ctxPayload = {
|
||||||
Body: combinedBody,
|
Body: combinedBody,
|
||||||
From: isDirectMessage
|
From: isDirectMessage
|
||||||
? `discord:${author.id}`
|
? `discord:${author.id}`
|
||||||
: `group:${message.channelId}`,
|
: `group:${message.channelId}`,
|
||||||
To: discordTo,
|
To: discordTo,
|
||||||
SessionKey: baseSessionKey,
|
SessionKey: threadKeys.sessionKey,
|
||||||
AccountId: route.accountId,
|
AccountId: route.accountId,
|
||||||
ChatType: isDirectMessage ? "direct" : "group",
|
ChatType: isDirectMessage ? "direct" : "group",
|
||||||
SenderName:
|
SenderName:
|
||||||
@@ -909,7 +919,7 @@ export function createDiscordMessageHandler(params: {
|
|||||||
Surface: "discord" as const,
|
Surface: "discord" as const,
|
||||||
WasMentioned: wasMentioned,
|
WasMentioned: wasMentioned,
|
||||||
MessageSid: message.id,
|
MessageSid: message.id,
|
||||||
ParentSessionKey: parentSessionKey,
|
ParentSessionKey: threadKeys.parentSessionKey,
|
||||||
ThreadStarterBody: threadStarterBody,
|
ThreadStarterBody: threadStarterBody,
|
||||||
ThreadLabel: threadLabel,
|
ThreadLabel: threadLabel,
|
||||||
Timestamp: resolveTimestampMs(message.timestamp),
|
Timestamp: resolveTimestampMs(message.timestamp),
|
||||||
|
|||||||
@@ -89,3 +89,20 @@ export function buildAgentPeerSessionKey(params: {
|
|||||||
const peerId = (params.peerId ?? "").trim() || "unknown";
|
const peerId = (params.peerId ?? "").trim() || "unknown";
|
||||||
return `agent:${normalizeAgentId(params.agentId)}:${provider}:${peerKind}:${peerId}`;
|
return `agent:${normalizeAgentId(params.agentId)}:${provider}:${peerKind}:${peerId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveThreadSessionKeys(params: {
|
||||||
|
baseSessionKey: string;
|
||||||
|
threadId?: string | null;
|
||||||
|
parentSessionKey?: string;
|
||||||
|
useSuffix?: boolean;
|
||||||
|
}): { sessionKey: string; parentSessionKey?: string } {
|
||||||
|
const threadId = (params.threadId ?? "").trim();
|
||||||
|
if (!threadId) {
|
||||||
|
return { sessionKey: params.baseSessionKey, parentSessionKey: undefined };
|
||||||
|
}
|
||||||
|
const useSuffix = params.useSuffix ?? true;
|
||||||
|
const sessionKey = useSuffix
|
||||||
|
? `${params.baseSessionKey}:thread:${threadId}`
|
||||||
|
: params.baseSessionKey;
|
||||||
|
return { sessionKey, parentSessionKey: params.parentSessionKey };
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ import {
|
|||||||
listNativeCommandSpecs,
|
listNativeCommandSpecs,
|
||||||
shouldHandleTextCommands,
|
shouldHandleTextCommands,
|
||||||
} from "../auto-reply/commands-registry.js";
|
} from "../auto-reply/commands-registry.js";
|
||||||
import { formatAgentEnvelope } from "../auto-reply/envelope.js";
|
import {
|
||||||
|
formatAgentEnvelope,
|
||||||
|
formatThreadStarterEnvelope,
|
||||||
|
} from "../auto-reply/envelope.js";
|
||||||
import { dispatchReplyFromConfig } from "../auto-reply/reply/dispatch-from-config.js";
|
import { dispatchReplyFromConfig } from "../auto-reply/reply/dispatch-from-config.js";
|
||||||
import {
|
import {
|
||||||
buildMentionRegexes,
|
buildMentionRegexes,
|
||||||
@@ -34,6 +37,7 @@ import {
|
|||||||
resolveStorePath,
|
resolveStorePath,
|
||||||
updateLastRoute,
|
updateLastRoute,
|
||||||
} from "../config/sessions.js";
|
} from "../config/sessions.js";
|
||||||
|
import { resolveThreadSessionKeys } from "../routing/session-key.js";
|
||||||
import { danger, logVerbose, shouldLogVerbose } from "../globals.js";
|
import { danger, logVerbose, shouldLogVerbose } from "../globals.js";
|
||||||
import { enqueueSystemEvent } from "../infra/system-events.js";
|
import { enqueueSystemEvent } from "../infra/system-events.js";
|
||||||
import { getChildLogger } from "../logging.js";
|
import { getChildLogger } from "../logging.js";
|
||||||
@@ -930,12 +934,12 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
const isThreadReply =
|
const isThreadReply =
|
||||||
hasThreadTs &&
|
hasThreadTs &&
|
||||||
(threadTs !== message.ts || Boolean(message.parent_user_id));
|
(threadTs !== message.ts || Boolean(message.parent_user_id));
|
||||||
const threadSessionKey =
|
const threadKeys = resolveThreadSessionKeys({
|
||||||
isThreadReply && threadTs
|
baseSessionKey,
|
||||||
? `${baseSessionKey}:thread:${threadTs}`
|
threadId: isThreadReply ? threadTs : undefined,
|
||||||
: undefined;
|
parentSessionKey: isThreadReply ? baseSessionKey : undefined,
|
||||||
const parentSessionKey = isThreadReply ? baseSessionKey : undefined;
|
});
|
||||||
const sessionKey = threadSessionKey ?? baseSessionKey;
|
const sessionKey = threadKeys.sessionKey;
|
||||||
enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
|
enqueueSystemEvent(`${inboundLabel}: ${preview}`, {
|
||||||
sessionKey,
|
sessionKey,
|
||||||
contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`,
|
contextKey: `slack:message:${message.channel}:${message.ts ?? "unknown"}`,
|
||||||
@@ -978,9 +982,9 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
: null;
|
: null;
|
||||||
const starterName = starterUser?.name ?? starter.userId ?? "Unknown";
|
const starterName = starterUser?.name ?? starter.userId ?? "Unknown";
|
||||||
const starterWithId = `${starter.text}\n[slack message id: ${starter.ts ?? threadTs} channel: ${message.channel}]`;
|
const starterWithId = `${starter.text}\n[slack message id: ${starter.ts ?? threadTs} channel: ${message.channel}]`;
|
||||||
threadStarterBody = formatAgentEnvelope({
|
threadStarterBody = formatThreadStarterEnvelope({
|
||||||
provider: "Slack",
|
provider: "Slack",
|
||||||
from: starterName,
|
author: starterName,
|
||||||
timestamp: starter.ts
|
timestamp: starter.ts
|
||||||
? Math.round(Number(starter.ts) * 1000)
|
? Math.round(Number(starter.ts) * 1000)
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -1007,7 +1011,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
Surface: "slack" as const,
|
Surface: "slack" as const,
|
||||||
MessageSid: message.ts,
|
MessageSid: message.ts,
|
||||||
ReplyToId: message.thread_ts ?? message.ts,
|
ReplyToId: message.thread_ts ?? message.ts,
|
||||||
ParentSessionKey: parentSessionKey,
|
ParentSessionKey: threadKeys.parentSessionKey,
|
||||||
ThreadStarterBody: threadStarterBody,
|
ThreadStarterBody: threadStarterBody,
|
||||||
ThreadLabel: threadLabel,
|
ThreadLabel: threadLabel,
|
||||||
Timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
|
Timestamp: message.ts ? Math.round(Number(message.ts) * 1000) : undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user