diff --git a/extensions/line/src/bot-message-context.test.ts b/extensions/line/src/bot-message-context.test.ts index c3fb0efcae5..f6ac0c25740 100644 --- a/extensions/line/src/bot-message-context.test.ts +++ b/extensions/line/src/bot-message-context.test.ts @@ -335,6 +335,43 @@ describe("buildLineMessageContext", () => { }); }); + it("normalizes canonical LINE targets through the plugin bindings surface", async () => { + const compiled = linePlugin.bindings?.compileConfiguredBinding({ + binding: { + type: "acp", + agentId: "codex", + match: { channel: "line", accountId: "default", peer: { kind: "direct", id: "unused" } }, + } as AgentBinding, + conversationId: "line:U1234567890abcdef1234567890abcdef", + }); + + expect(compiled).toEqual({ + conversationId: "U1234567890abcdef1234567890abcdef", + }); + expect( + linePlugin.bindings?.resolveCommandConversation?.({ + accountId: "default", + originatingTo: "line:U1234567890abcdef1234567890abcdef", + }), + ).toEqual({ + conversationId: "U1234567890abcdef1234567890abcdef", + }); + expect( + linePlugin.bindings?.matchInboundConversation({ + binding: { + type: "acp", + agentId: "codex", + match: { channel: "line", accountId: "default", peer: { kind: "direct", id: "unused" } }, + } as AgentBinding, + compiledBinding: compiled!, + conversationId: "U1234567890abcdef1234567890abcdef", + }), + ).toEqual({ + conversationId: "U1234567890abcdef1234567890abcdef", + matchPriority: 2, + }); + }); + it("routes LINE conversations through active ACP session bindings", async () => { const userId = "U1234567890abcdef1234567890abcdef"; await getSessionBindingService().bind({ diff --git a/extensions/line/src/channel.ts b/extensions/line/src/channel.ts index d5dd6506ec3..713c86d741d 100644 --- a/extensions/line/src/channel.ts +++ b/extensions/line/src/channel.ts @@ -17,7 +17,7 @@ function normalizeLineConversationId(raw?: string | null): string | null { if (!trimmed) { return null; } - const prefixed = trimmed.match(/^line:(?:user|group|room):(.+)$/i)?.[1]; + const prefixed = trimmed.match(/^line:(?:(?:user|group|room):)?(.+)$/i)?.[1]; return (prefixed ?? trimmed).trim() || null; } diff --git a/src/auto-reply/reply/commands-acp/context.test.ts b/src/auto-reply/reply/commands-acp/context.test.ts index b200e0eb251..9d3579a3ff3 100644 --- a/src/auto-reply/reply/commands-acp/context.test.ts +++ b/src/auto-reply/reply/commands-acp/context.test.ts @@ -201,6 +201,24 @@ describe("commands-acp context", () => { }); }); + it("resolves LINE conversation ids from canonical line targets", () => { + const params = buildCommandTestParams("/acp status", baseCfg, { + Provider: "line", + Surface: "line", + OriginatingChannel: "line", + OriginatingTo: "line:U1234567890abcdef1234567890abcdef", + AccountId: "work", + }); + + expect(resolveAcpCommandBindingContext(params)).toEqual({ + channel: "line", + accountId: "work", + threadId: undefined, + conversationId: "U1234567890abcdef1234567890abcdef", + }); + expect(resolveAcpCommandConversationId(params)).toBe("U1234567890abcdef1234567890abcdef"); + }); + it("resolves Matrix thread context from the current room and thread root", () => { const params = buildCommandTestParams("/acp status", baseCfg, { Provider: "matrix",