Skip to content

Commit 708fda5

Browse files
committed
Discord: fix /codex_resume identity resolution in brand-new threads
In brand-new Discord threads, the slash interaction can set ctx.from to the slash user identity (e.g. "slash:user-id") while the real channel target is in ctx.to. The previous ctx.from ?? ctx.to expression normalized a slash identity to undefined, causing toConversationTargetFromCommand() to return null and /cas_resume to fail on the first attempt. Fix: when ctx.from starts with "slash:", use ctx.to as the channel source instead. The existing behavior for established channels (ctx.from holds the channel identity) is unchanged. Adds a regression test covering the brand-new-thread scenario. Fixes #30
1 parent 97e3084 commit 708fda5

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

src/controller.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,33 @@ describe("Discord controller flows", () => {
320320
);
321321
});
322322

323+
it("resolves channel identity from ctx.to when ctx.from is a slash identity in a new Discord thread", async () => {
324+
// Regression test for brand-new Discord threads where the slash interaction
325+
// places the slash user identity in ctx.from and the channel target in ctx.to.
326+
const { controller, sendComponentMessage } = await createControllerHarness();
327+
328+
const reply = await controller.handleCommand(
329+
"cas_resume",
330+
buildDiscordCommandContext({
331+
from: "slash:user-1",
332+
to: "discord:channel:chan-1",
333+
}),
334+
);
335+
336+
expect(reply).toEqual({
337+
text: "Sent a Codex thread picker to this Discord conversation.",
338+
});
339+
expect(sendComponentMessage).toHaveBeenCalledWith(
340+
"channel:chan-1",
341+
expect.objectContaining({
342+
text: expect.stringContaining("Showing recent Codex sessions"),
343+
}),
344+
expect.objectContaining({
345+
accountId: "default",
346+
}),
347+
);
348+
});
349+
323350
it("sends Discord model pickers directly instead of returning Telegram buttons", async () => {
324351
const { controller, sendComponentMessage } = await createControllerHarness();
325352
await (controller as any).store.upsertBinding({

src/controller.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,12 @@ function toConversationTargetFromCommand(ctx: PluginCommandContext): Conversatio
271271
};
272272
}
273273
if (isDiscordChannel(ctx.channel)) {
274-
const conversationId = normalizeDiscordConversationId(ctx.from ?? ctx.to);
274+
// In brand-new Discord threads, the slash interaction may place the slash
275+
// user identity in ctx.from (e.g. "slash:user-id") while ctx.to holds the
276+
// real channel target. Prefer ctx.to when ctx.from is a slash identity so
277+
// /cas_resume resolves to the correct channel from the first attempt.
278+
const sourceId = ctx.from?.startsWith("slash:") ? ctx.to : (ctx.from ?? ctx.to);
279+
const conversationId = normalizeDiscordConversationId(sourceId);
275280
if (!conversationId) {
276281
return null;
277282
}

0 commit comments

Comments
 (0)