Sessions: split chat type derivation seam

This commit is contained in:
Peter Steinberger
2026-04-07 12:50:59 +08:00
parent fdacaf0853
commit c569e5faba
3 changed files with 89 additions and 52 deletions

View File

@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { deriveSessionChatType } from "../sessions/session-chat-type.js";
import { deriveSessionChatTypeFromKey } from "../sessions/session-chat-type-shared.js";
import {
getSubagentDepth,
isCronSessionKey,
@@ -70,7 +70,7 @@ describe("isCronSessionKey", () => {
});
});
describe("deriveSessionChatType", () => {
describe("deriveSessionChatTypeFromKey", () => {
it.each([
{ key: "agent:main:discord:direct:user1", expected: "direct" },
{ key: "agent:main:telegram:group:g1", expected: "group" },
@@ -83,7 +83,7 @@ describe("deriveSessionChatType", () => {
{ key: "agent:main", expected: "unknown" },
{ key: "", expected: "unknown" },
] as const)("derives chat type for %j => $expected", ({ key, expected }) => {
expect(deriveSessionChatType(key)).toBe(expected);
expect(deriveSessionChatTypeFromKey(key)).toBe(expected);
});
});

View File

@@ -0,0 +1,67 @@
import { parseAgentSessionKey } from "./session-key-utils.js";
export type SessionKeyChatType = "direct" | "group" | "channel" | "unknown";
function deriveBuiltInLegacySessionChatType(
scopedSessionKey: string,
): SessionKeyChatType | undefined {
if (/^group:[^:]+$/.test(scopedSessionKey)) {
return "group";
}
if (/^[0-9]+(?:-[0-9]+)*@g\.us$/.test(scopedSessionKey)) {
return "group";
}
if (/^whatsapp:(?!.*:group:).+@g\.us$/.test(scopedSessionKey)) {
return "group";
}
if (/^discord:(?:[^:]+:)?guild-[^:]+:channel-[^:]+$/.test(scopedSessionKey)) {
return "channel";
}
return undefined;
}
export function deriveSessionChatTypeFromScopedKey(
scopedSessionKey: string,
deriveLegacySessionChatTypes: Array<
(scopedSessionKey: string) => SessionKeyChatType | undefined
> = [],
): SessionKeyChatType {
const tokens = new Set(scopedSessionKey.split(":").filter(Boolean));
if (tokens.has("group")) {
return "group";
}
if (tokens.has("channel")) {
return "channel";
}
if (tokens.has("direct") || tokens.has("dm")) {
return "direct";
}
const builtInLegacy = deriveBuiltInLegacySessionChatType(scopedSessionKey);
if (builtInLegacy) {
return builtInLegacy;
}
for (const deriveLegacySessionChatType of deriveLegacySessionChatTypes) {
const derived = deriveLegacySessionChatType(scopedSessionKey);
if (derived) {
return derived;
}
}
return "unknown";
}
/**
* Best-effort chat-type extraction from session keys across canonical and legacy formats.
*/
export function deriveSessionChatTypeFromKey(
sessionKey: string | undefined | null,
deriveLegacySessionChatTypes: Array<
(scopedSessionKey: string) => SessionKeyChatType | undefined
> = [],
): SessionKeyChatType {
const raw = (sessionKey ?? "").trim().toLowerCase();
if (!raw) {
return "unknown";
}
const scoped = parseAgentSessionKey(raw)?.rest ?? raw;
return deriveSessionChatTypeFromScopedKey(scoped, deriveLegacySessionChatTypes);
}

View File

@@ -1,54 +1,24 @@
import { iterateBootstrapChannelPlugins } from "../channels/plugins/bootstrap-registry.js";
import { parseAgentSessionKey } from "./session-key-utils.js";
import {
deriveSessionChatTypeFromKey,
type SessionKeyChatType,
} from "./session-chat-type-shared.js";
export type SessionKeyChatType = "direct" | "group" | "channel" | "unknown";
export {
deriveSessionChatTypeFromKey,
type SessionKeyChatType,
} from "./session-chat-type-shared.js";
function deriveBuiltInLegacySessionChatType(
scopedSessionKey: string,
): SessionKeyChatType | undefined {
if (/^group:[^:]+$/.test(scopedSessionKey)) {
return "group";
}
if (/^[0-9]+(?:-[0-9]+)*@g\.us$/.test(scopedSessionKey)) {
return "group";
}
if (/^whatsapp:(?!.*:group:).+@g\.us$/.test(scopedSessionKey)) {
return "group";
}
if (/^discord:(?:[^:]+:)?guild-[^:]+:channel-[^:]+$/.test(scopedSessionKey)) {
return "channel";
}
return undefined;
}
/**
* Best-effort chat-type extraction from session keys across canonical and legacy formats.
*/
export function deriveSessionChatType(sessionKey: string | undefined | null): SessionKeyChatType {
const raw = (sessionKey ?? "").trim().toLowerCase();
if (!raw) {
return "unknown";
}
const scoped = parseAgentSessionKey(raw)?.rest ?? raw;
const tokens = new Set(scoped.split(":").filter(Boolean));
if (tokens.has("group")) {
return "group";
}
if (tokens.has("channel")) {
return "channel";
}
if (tokens.has("direct") || tokens.has("dm")) {
return "direct";
}
const builtInLegacy = deriveBuiltInLegacySessionChatType(scoped);
if (builtInLegacy) {
return builtInLegacy;
}
for (const plugin of iterateBootstrapChannelPlugins()) {
const derived = plugin.messaging?.deriveLegacySessionChatType?.(scoped);
if (derived) {
return derived;
}
}
return "unknown";
return deriveSessionChatTypeFromKey(
sessionKey,
iterateBootstrapChannelPlugins()
.map((plugin) => plugin.messaging?.deriveLegacySessionChatType)
.filter(
(
deriveLegacySessionChatType,
): deriveLegacySessionChatType is NonNullable<typeof deriveLegacySessionChatType> =>
Boolean(deriveLegacySessionChatType),
),
);
}