mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:10:43 +00:00
fix: avoid repeated discord thread starter context
This commit is contained in:
@@ -48,6 +48,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord: preserve attachment and sticker filenames when saving inbound media, so agents can see human-readable file names instead of only UUID-based paths. Fixes #59744. Thanks @xela92 and @rockcent.
|
||||
- Discord: preserve non-ASCII channel names in session display labels while keeping allowlist matching on the existing ASCII slug contract. Thanks @swjeong9.
|
||||
- Discord/PluralKit: canonicalize proxied webhook turns to the original Discord message id for inbound dedupe, while preserving the proxy message id for reply routing. Thanks @acgh213.
|
||||
- Discord: only inject thread starter context on the first turn of the effective thread session, so follow-up thread replies do not repeat the starter block. Fixes #41355; supersedes #44447 and #44449. Thanks @p3nchan.
|
||||
- Gateway/diagnostics: include a bounded redacted startup error message in stability bundles, so crash-loop reports identify the failing plugin or contract without exposing secrets. Refs #75797. Thanks @ymebosma.
|
||||
- Gateway/pricing: abort in-flight model pricing catalog fetches when Gateway shutdown stops the refresh loop, and avoid post-stop cache writes or refresh timers. Fixes #72208. Thanks @rzcq.
|
||||
- Codex/app-server: make startup retry cleanup ownership-aware so concurrent Codex lanes cannot close another lane's freshly restarted shared app-server client. Thanks @vincentkoc.
|
||||
|
||||
@@ -296,6 +296,15 @@ export async function buildDiscordMessageProcessContext(params: {
|
||||
}))
|
||||
: undefined;
|
||||
const originatingTo = autoThreadContext?.OriginatingTo ?? dmConversationTarget ?? replyTarget;
|
||||
const effectiveSessionKey =
|
||||
boundSessionKey ?? autoThreadContext?.SessionKey ?? threadKeys.sessionKey;
|
||||
const effectivePreviousTimestamp =
|
||||
effectiveSessionKey === route.sessionKey
|
||||
? previousTimestamp
|
||||
: readSessionUpdatedAt({
|
||||
storePath,
|
||||
sessionKey: effectiveSessionKey,
|
||||
});
|
||||
|
||||
const ctxPayload = finalizeInboundContext({
|
||||
Body: combinedBody,
|
||||
@@ -306,7 +315,7 @@ export async function buildDiscordMessageProcessContext(params: {
|
||||
...(preflightAudioTranscript !== undefined ? { Transcript: preflightAudioTranscript } : {}),
|
||||
From: effectiveFrom,
|
||||
To: effectiveTo,
|
||||
SessionKey: boundSessionKey ?? autoThreadContext?.SessionKey ?? threadKeys.sessionKey,
|
||||
SessionKey: effectiveSessionKey,
|
||||
AccountId: route.accountId,
|
||||
ChatType: isDirectMessage ? "direct" : "channel",
|
||||
ConversationLabel: fromLabel,
|
||||
@@ -335,7 +344,7 @@ export async function buildDiscordMessageProcessContext(params: {
|
||||
ModelParentSessionKey:
|
||||
autoThreadContext?.ModelParentSessionKey ?? modelParentSessionKey ?? undefined,
|
||||
MessageThreadId: threadChannel?.id ?? autoThreadContext?.createdThreadId ?? undefined,
|
||||
ThreadStarterBody: threadStarterBody,
|
||||
ThreadStarterBody: !effectivePreviousTimestamp ? threadStarterBody : undefined,
|
||||
ThreadLabel: threadLabel,
|
||||
Timestamp: resolveTimestampMs(message.timestamp),
|
||||
...mediaPayload,
|
||||
|
||||
@@ -364,6 +364,7 @@ function getLastDispatchCtx():
|
||||
OriginatingTo?: string;
|
||||
ParentSessionKey?: string;
|
||||
SessionKey?: string;
|
||||
ThreadStarterBody?: string;
|
||||
To?: string;
|
||||
Transcript?: string;
|
||||
}
|
||||
@@ -384,6 +385,7 @@ function getLastDispatchCtx():
|
||||
OriginatingTo?: string;
|
||||
ParentSessionKey?: string;
|
||||
SessionKey?: string;
|
||||
ThreadStarterBody?: string;
|
||||
To?: string;
|
||||
Transcript?: string;
|
||||
};
|
||||
@@ -1053,6 +1055,49 @@ describe("processDiscordMessage session routing", () => {
|
||||
});
|
||||
expect(getLastDispatchCtx()?.ParentSessionKey).toBeUndefined();
|
||||
});
|
||||
|
||||
it("omits thread starter context when the effective thread session already exists", async () => {
|
||||
const threadSessionKey = "agent:main:discord:channel:thread-1";
|
||||
readSessionUpdatedAt.mockImplementation((params?: unknown) => {
|
||||
const sessionKey = (params as { sessionKey?: string } | undefined)?.sessionKey;
|
||||
return sessionKey === threadSessionKey ? 1_700_000_000_000 : undefined;
|
||||
});
|
||||
const rest = {
|
||||
get: vi.fn(async () => ({
|
||||
content: "original thread starter",
|
||||
embeds: [],
|
||||
author: { id: "U2", username: "bob", discriminator: "0" },
|
||||
timestamp: new Date().toISOString(),
|
||||
})),
|
||||
};
|
||||
const ctx = await createBaseContext({
|
||||
baseSessionKey: threadSessionKey,
|
||||
route: BASE_CHANNEL_ROUTE,
|
||||
messageChannelId: "thread-1",
|
||||
message: {
|
||||
id: "m1",
|
||||
channelId: "thread-1",
|
||||
content: "follow-up",
|
||||
timestamp: new Date().toISOString(),
|
||||
attachments: [],
|
||||
},
|
||||
messageText: "follow-up",
|
||||
baseText: "follow-up",
|
||||
threadChannel: { id: "thread-1", name: "child-thread" },
|
||||
threadParentId: "parent-1",
|
||||
client: { rest },
|
||||
channelConfig: { allowed: true, users: ["U2"] },
|
||||
});
|
||||
|
||||
await runProcessDiscordMessage(ctx);
|
||||
|
||||
expect(rest.get).toHaveBeenCalled();
|
||||
expect(getLastDispatchCtx()).toMatchObject({
|
||||
SessionKey: threadSessionKey,
|
||||
MessageThreadId: "thread-1",
|
||||
});
|
||||
expect(getLastDispatchCtx()?.ThreadStarterBody).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("processDiscordMessage draft streaming", () => {
|
||||
|
||||
Reference in New Issue
Block a user