refactor: drop legacy room chatType
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
### Breaking
|
### Breaking
|
||||||
- **BREAKING:** Channel auth now prefers config over env for Discord/Telegram/Matrix (env is fallback only). (#1040) — thanks @thewilloftheshadow.
|
- **BREAKING:** Channel auth now prefers config over env for Discord/Telegram/Matrix (env is fallback only). (#1040) — thanks @thewilloftheshadow.
|
||||||
- **BREAKING:** `clawdbot message` and message tool now require `target` (dropping `to`/`channelId` for destinations). (#1034) — thanks @tobalsan.
|
- **BREAKING:** `clawdbot message` and message tool now require `target` (dropping `to`/`channelId` for destinations). (#1034) — thanks @tobalsan.
|
||||||
|
- **BREAKING:** Drop legacy `chatType: "room"` support; use `chatType: "channel"`.
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
- Tools: improve `web_fetch` extraction using Readability (with fallback).
|
- Tools: improve `web_fetch` extraction using Readability (with fallback).
|
||||||
|
|||||||
@@ -309,7 +309,6 @@ export function createSessionStatusTool(opts?: {
|
|||||||
const isGroup =
|
const isGroup =
|
||||||
resolved.entry.chatType === "group" ||
|
resolved.entry.chatType === "group" ||
|
||||||
resolved.entry.chatType === "channel" ||
|
resolved.entry.chatType === "channel" ||
|
||||||
resolved.entry.chatType === "room" ||
|
|
||||||
resolved.key.startsWith("group:") ||
|
resolved.key.startsWith("group:") ||
|
||||||
resolved.key.includes(":group:") ||
|
resolved.key.includes(":group:") ||
|
||||||
resolved.key.includes(":channel:");
|
resolved.key.includes(":channel:");
|
||||||
|
|||||||
@@ -299,8 +299,7 @@ export async function runPreparedReply(
|
|||||||
});
|
});
|
||||||
const isGroupSession =
|
const isGroupSession =
|
||||||
sessionEntry?.chatType === "group" ||
|
sessionEntry?.chatType === "group" ||
|
||||||
sessionEntry?.chatType === "channel" ||
|
sessionEntry?.chatType === "channel";
|
||||||
sessionEntry?.chatType === "room";
|
|
||||||
const isMainSession = !isGroupSession && sessionKey === normalizeMainKey(sessionCfg?.mainKey);
|
const isMainSession = !isGroupSession && sessionKey === normalizeMainKey(sessionCfg?.mainKey);
|
||||||
prefixedBodyBase = await prependSystemEvents({
|
prefixedBodyBase = await prependSystemEvents({
|
||||||
cfg,
|
cfg,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ describe("finalizeInboundContext", () => {
|
|||||||
const ctx: MsgContext = {
|
const ctx: MsgContext = {
|
||||||
Body: "a\\nb\r\nc",
|
Body: "a\\nb\r\nc",
|
||||||
RawBody: "raw\\nline",
|
RawBody: "raw\\nline",
|
||||||
ChatType: "room",
|
ChatType: "channel",
|
||||||
From: "group:123@g.us",
|
From: "group:123@g.us",
|
||||||
GroupSubject: "Test",
|
GroupSubject: "Test",
|
||||||
};
|
};
|
||||||
@@ -35,4 +35,3 @@ describe("finalizeInboundContext", () => {
|
|||||||
expect(ctx.BodyForCommands).toBe("say hi");
|
expect(ctx.BodyForCommands).toBe("say hi");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,6 @@ export function buildStatusMessage(args: StatusArgs): string {
|
|||||||
const isGroupSession =
|
const isGroupSession =
|
||||||
entry?.chatType === "group" ||
|
entry?.chatType === "group" ||
|
||||||
entry?.chatType === "channel" ||
|
entry?.chatType === "channel" ||
|
||||||
entry?.chatType === "room" ||
|
|
||||||
Boolean(args.sessionKey?.includes(":group:")) ||
|
Boolean(args.sessionKey?.includes(":group:")) ||
|
||||||
Boolean(args.sessionKey?.includes(":channel:")) ||
|
Boolean(args.sessionKey?.includes(":channel:")) ||
|
||||||
Boolean(args.sessionKey?.startsWith("group:"));
|
Boolean(args.sessionKey?.startsWith("group:"));
|
||||||
|
|||||||
@@ -8,13 +8,12 @@ describe("normalizeChatType", () => {
|
|||||||
expect(normalizeChatType("dm")).toBe("direct");
|
expect(normalizeChatType("dm")).toBe("direct");
|
||||||
expect(normalizeChatType("group")).toBe("group");
|
expect(normalizeChatType("group")).toBe("group");
|
||||||
expect(normalizeChatType("channel")).toBe("channel");
|
expect(normalizeChatType("channel")).toBe("channel");
|
||||||
expect(normalizeChatType("room")).toBe("channel");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns undefined for empty/unknown values", () => {
|
it("returns undefined for empty/unknown values", () => {
|
||||||
expect(normalizeChatType(undefined)).toBeUndefined();
|
expect(normalizeChatType(undefined)).toBeUndefined();
|
||||||
expect(normalizeChatType("")).toBeUndefined();
|
expect(normalizeChatType("")).toBeUndefined();
|
||||||
expect(normalizeChatType("nope")).toBeUndefined();
|
expect(normalizeChatType("nope")).toBeUndefined();
|
||||||
|
expect(normalizeChatType("room")).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export function normalizeChatType(raw?: string): NormalizedChatType | undefined
|
|||||||
if (!value) return undefined;
|
if (!value) return undefined;
|
||||||
if (value === "direct" || value === "dm") return "direct";
|
if (value === "direct" || value === "dm") return "direct";
|
||||||
if (value === "group") return "group";
|
if (value === "group") return "group";
|
||||||
if (value === "channel" || value === "room") return "channel";
|
if (value === "channel") return "channel";
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const formatAge = (ms: number | null | undefined) => {
|
|||||||
function classifyKey(key: string, entry?: SessionEntry): SessionRow["kind"] {
|
function classifyKey(key: string, entry?: SessionEntry): SessionRow["kind"] {
|
||||||
if (key === "global") return "global";
|
if (key === "global") return "global";
|
||||||
if (key === "unknown") return "unknown";
|
if (key === "unknown") return "unknown";
|
||||||
if (entry?.chatType === "group" || entry?.chatType === "channel" || entry?.chatType === "room") {
|
if (entry?.chatType === "group" || entry?.chatType === "channel") {
|
||||||
return "group";
|
return "group";
|
||||||
}
|
}
|
||||||
if (key.startsWith("group:") || key.includes(":group:") || key.includes(":channel:")) {
|
if (key.startsWith("group:") || key.includes(":group:") || key.includes(":channel:")) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import type { HeartbeatStatus, SessionStatus, StatusSummary } from "./status.typ
|
|||||||
const classifyKey = (key: string, entry?: SessionEntry): SessionStatus["kind"] => {
|
const classifyKey = (key: string, entry?: SessionEntry): SessionStatus["kind"] => {
|
||||||
if (key === "global") return "global";
|
if (key === "global") return "global";
|
||||||
if (key === "unknown") return "unknown";
|
if (key === "unknown") return "unknown";
|
||||||
if (entry?.chatType === "group" || entry?.chatType === "channel" || entry?.chatType === "room") {
|
if (entry?.chatType === "group" || entry?.chatType === "channel") {
|
||||||
return "group";
|
return "group";
|
||||||
}
|
}
|
||||||
if (key.startsWith("group:") || key.includes(":group:") || key.includes(":channel:")) {
|
if (key.startsWith("group:") || key.includes(":group:") || key.includes(":channel:")) {
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ export type SessionChannelId = ChannelId | "webchat";
|
|||||||
export type SessionChatType =
|
export type SessionChatType =
|
||||||
| "direct"
|
| "direct"
|
||||||
| "group"
|
| "group"
|
||||||
| "channel"
|
| "channel";
|
||||||
// Legacy alias for "channel".
|
|
||||||
| "room";
|
|
||||||
|
|
||||||
export type SessionEntry = {
|
export type SessionEntry = {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export type HumanDelayConfig = {
|
|||||||
export type SessionSendPolicyAction = "allow" | "deny";
|
export type SessionSendPolicyAction = "allow" | "deny";
|
||||||
export type SessionSendPolicyMatch = {
|
export type SessionSendPolicyMatch = {
|
||||||
channel?: string;
|
channel?: string;
|
||||||
chatType?: "direct" | "group" | "channel" | "room";
|
chatType?: "direct" | "group" | "channel";
|
||||||
keyPrefix?: string;
|
keyPrefix?: string;
|
||||||
};
|
};
|
||||||
export type SessionSendPolicyRule = {
|
export type SessionSendPolicyRule = {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { AgentElevatedAllowFromConfig, SessionSendPolicyAction } from "./ty
|
|||||||
|
|
||||||
export type MediaUnderstandingScopeMatch = {
|
export type MediaUnderstandingScopeMatch = {
|
||||||
channel?: string;
|
channel?: string;
|
||||||
chatType?: "direct" | "group" | "channel" | "room";
|
chatType?: "direct" | "group" | "channel";
|
||||||
keyPrefix?: string;
|
keyPrefix?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -255,7 +255,6 @@ export const MediaUnderstandingScopeSchema = z
|
|||||||
z.literal("direct"),
|
z.literal("direct"),
|
||||||
z.literal("group"),
|
z.literal("group"),
|
||||||
z.literal("channel"),
|
z.literal("channel"),
|
||||||
z.literal("room"),
|
|
||||||
])
|
])
|
||||||
.optional(),
|
.optional(),
|
||||||
keyPrefix: z.string().optional(),
|
keyPrefix: z.string().optional(),
|
||||||
|
|||||||
@@ -43,8 +43,6 @@ export const SessionSchema = z
|
|||||||
z.literal("direct"),
|
z.literal("direct"),
|
||||||
z.literal("group"),
|
z.literal("group"),
|
||||||
z.literal("channel"),
|
z.literal("channel"),
|
||||||
// Legacy alias for "channel".
|
|
||||||
z.literal("room"),
|
|
||||||
])
|
])
|
||||||
.optional(),
|
.optional(),
|
||||||
keyPrefix: z.string().optional(),
|
keyPrefix: z.string().optional(),
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export function loadSessionEntry(sessionKey: string) {
|
|||||||
export function classifySessionKey(key: string, entry?: SessionEntry): GatewaySessionRow["kind"] {
|
export function classifySessionKey(key: string, entry?: SessionEntry): GatewaySessionRow["kind"] {
|
||||||
if (key === "global") return "global";
|
if (key === "global") return "global";
|
||||||
if (key === "unknown") return "unknown";
|
if (key === "unknown") return "unknown";
|
||||||
if (entry?.chatType === "group" || entry?.chatType === "channel" || entry?.chatType === "room") {
|
if (entry?.chatType === "group" || entry?.chatType === "channel") {
|
||||||
return "group";
|
return "group";
|
||||||
}
|
}
|
||||||
if (key.startsWith("group:") || key.includes(":group:") || key.includes(":channel:")) {
|
if (key.startsWith("group:") || key.includes(":group:") || key.includes(":channel:")) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export type GatewaySessionRow = {
|
|||||||
subject?: string;
|
subject?: string;
|
||||||
room?: string;
|
room?: string;
|
||||||
space?: string;
|
space?: string;
|
||||||
chatType?: "direct" | "group" | "channel" | "room";
|
chatType?: "direct" | "group" | "channel";
|
||||||
updatedAt: number | null;
|
updatedAt: number | null;
|
||||||
sessionId?: string;
|
sessionId?: string;
|
||||||
systemSent?: boolean;
|
systemSent?: boolean;
|
||||||
|
|||||||
@@ -3,17 +3,10 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { normalizeMediaUnderstandingChatType, resolveMediaUnderstandingScope } from "./scope.js";
|
import { normalizeMediaUnderstandingChatType, resolveMediaUnderstandingScope } from "./scope.js";
|
||||||
|
|
||||||
describe("media understanding scope", () => {
|
describe("media understanding scope", () => {
|
||||||
it("normalizes channel/room", () => {
|
it("normalizes chatType", () => {
|
||||||
expect(normalizeMediaUnderstandingChatType("channel")).toBe("channel");
|
expect(normalizeMediaUnderstandingChatType("channel")).toBe("channel");
|
||||||
expect(normalizeMediaUnderstandingChatType("room")).toBe("channel");
|
expect(normalizeMediaUnderstandingChatType("dm")).toBe("direct");
|
||||||
});
|
expect(normalizeMediaUnderstandingChatType("room")).toBeUndefined();
|
||||||
|
|
||||||
it("treats room match as channel", () => {
|
|
||||||
const scope = {
|
|
||||||
rules: [{ action: "deny", match: { chatType: "room" } }],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
expect(resolveMediaUnderstandingScope({ scope, chatType: "channel" })).toBe("deny");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("matches channel chatType explicitly", () => {
|
it("matches channel chatType explicitly", () => {
|
||||||
@@ -24,4 +17,3 @@ describe("media understanding scope", () => {
|
|||||||
expect(resolveMediaUnderstandingScope({ scope, chatType: "channel" })).toBe("deny");
|
expect(resolveMediaUnderstandingScope({ scope, chatType: "channel" })).toBe("deny");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { MediaUnderstandingScopeConfig } from "../config/types.tools.js";
|
import type { MediaUnderstandingScopeConfig } from "../config/types.tools.js";
|
||||||
|
import { normalizeChatType } from "../channels/chat-type.js";
|
||||||
|
|
||||||
export type MediaUnderstandingScopeDecision = "allow" | "deny";
|
export type MediaUnderstandingScopeDecision = "allow" | "deny";
|
||||||
|
|
||||||
@@ -15,12 +16,7 @@ function normalizeMatch(value?: string | null): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeMediaUnderstandingChatType(raw?: string | null): string | undefined {
|
export function normalizeMediaUnderstandingChatType(raw?: string | null): string | undefined {
|
||||||
const value = raw?.trim().toLowerCase();
|
return normalizeChatType(raw ?? undefined);
|
||||||
if (!value) return undefined;
|
|
||||||
if (value === "dm" || value === "direct_message" || value === "private") return "direct";
|
|
||||||
if (value === "groups") return "group";
|
|
||||||
if (value === "room") return "channel";
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveMediaUnderstandingScope(params: {
|
export function resolveMediaUnderstandingScope(params: {
|
||||||
@@ -33,7 +29,7 @@ export function resolveMediaUnderstandingScope(params: {
|
|||||||
if (!scope) return "allow";
|
if (!scope) return "allow";
|
||||||
|
|
||||||
const channel = normalizeMatch(params.channel);
|
const channel = normalizeMatch(params.channel);
|
||||||
const chatType = normalizeMediaUnderstandingChatType(params.chatType) ?? normalizeMatch(params.chatType);
|
const chatType = normalizeMediaUnderstandingChatType(params.chatType);
|
||||||
const sessionKey = normalizeMatch(params.sessionKey) ?? "";
|
const sessionKey = normalizeMatch(params.sessionKey) ?? "";
|
||||||
|
|
||||||
for (const rule of scope.rules ?? []) {
|
for (const rule of scope.rules ?? []) {
|
||||||
@@ -41,8 +37,7 @@ export function resolveMediaUnderstandingScope(params: {
|
|||||||
const action = normalizeDecision(rule.action) ?? "allow";
|
const action = normalizeDecision(rule.action) ?? "allow";
|
||||||
const match = rule.match ?? {};
|
const match = rule.match ?? {};
|
||||||
const matchChannel = normalizeMatch(match.channel);
|
const matchChannel = normalizeMatch(match.channel);
|
||||||
const matchChatType =
|
const matchChatType = normalizeMediaUnderstandingChatType(match.chatType);
|
||||||
normalizeMediaUnderstandingChatType(match.chatType) ?? normalizeMatch(match.chatType);
|
|
||||||
const matchPrefix = normalizeMatch(match.keyPrefix);
|
const matchPrefix = normalizeMatch(match.keyPrefix);
|
||||||
|
|
||||||
if (matchChannel && matchChannel !== channel) continue;
|
if (matchChannel && matchChannel !== channel) continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user