mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-02 20:45:16 +00:00
Derive explicit source-reply command turns from authorized control-command bodies when legacy command source metadata is missing. Preserve native/text structured command semantics, keep unauthorized native commands and structured normal command bodies on plugin-owned fallback paths, and pass bot username normalization through the derived detection. Co-authored-by: Alex Knight <aknight@atlassian.com>
211 lines
5.5 KiB
TypeScript
211 lines
5.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import {
|
|
createCommandTurnContext,
|
|
isAuthorizedTextSlashCommandTurn,
|
|
isExplicitCommandTurn,
|
|
isNativeCommandTurn,
|
|
resolveCommandTurnContext,
|
|
resolveCommandTurnTargetSessionKey,
|
|
} from "./command-turn-context.js";
|
|
import { isExplicitCommandTurnContext } from "./command-turn-detection.js";
|
|
|
|
const emptyConfig = {} as const satisfies OpenClawConfig;
|
|
|
|
describe("resolveCommandTurnContext", () => {
|
|
it("derives native command turns from legacy context fields", () => {
|
|
expect(
|
|
resolveCommandTurnContext({
|
|
CommandSource: "native",
|
|
CommandAuthorized: true,
|
|
CommandBody: "/status now",
|
|
}),
|
|
).toEqual({
|
|
kind: "native",
|
|
source: "native",
|
|
authorized: true,
|
|
commandName: "status",
|
|
body: "/status now",
|
|
});
|
|
});
|
|
|
|
it("derives text slash command turns from legacy context fields", () => {
|
|
expect(
|
|
resolveCommandTurnContext({
|
|
CommandSource: "text",
|
|
CommandAuthorized: true,
|
|
CommandBody: "/model gpt-5.5",
|
|
}),
|
|
).toMatchObject({
|
|
kind: "text-slash",
|
|
source: "text",
|
|
authorized: true,
|
|
commandName: "model",
|
|
});
|
|
});
|
|
|
|
it("keeps normal message turns non-explicit even when command auth is true elsewhere", () => {
|
|
const commandTurn = resolveCommandTurnContext({
|
|
CommandAuthorized: true,
|
|
CommandBody: "hello",
|
|
});
|
|
expect(commandTurn).toMatchObject({
|
|
kind: "normal",
|
|
source: "message",
|
|
authorized: false,
|
|
});
|
|
expect(isExplicitCommandTurn(commandTurn)).toBe(false);
|
|
});
|
|
|
|
it("treats authorized control command bodies as explicit without legacy source tags", () => {
|
|
expect(
|
|
isExplicitCommandTurnContext(
|
|
{
|
|
CommandAuthorized: true,
|
|
CommandBody: "/reset",
|
|
},
|
|
emptyConfig,
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
isExplicitCommandTurnContext(
|
|
{
|
|
CommandAuthorized: true,
|
|
CommandBody: "hey can you /status please",
|
|
},
|
|
emptyConfig,
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("keeps structured normal command turns non-explicit", () => {
|
|
expect(
|
|
isExplicitCommandTurnContext(
|
|
{
|
|
CommandTurn: {
|
|
kind: "normal",
|
|
source: "message",
|
|
authorized: false,
|
|
body: "/think high through this",
|
|
},
|
|
CommandAuthorized: true,
|
|
Body: "through this",
|
|
RawBody: "through this",
|
|
CommandBody: "/think high through this",
|
|
},
|
|
emptyConfig,
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("uses cleaned command bodies for command-shaped structured normal turns", () => {
|
|
expect(
|
|
isExplicitCommandTurnContext(
|
|
{
|
|
CommandTurn: {
|
|
kind: "normal",
|
|
source: "message",
|
|
authorized: false,
|
|
body: "/reset",
|
|
},
|
|
CommandAuthorized: true,
|
|
Body: "/reset@openclaw",
|
|
RawBody: "/reset@openclaw",
|
|
CommandBody: "/reset",
|
|
},
|
|
emptyConfig,
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("normalizes bot-mentioned command bodies for structured normal turns", () => {
|
|
expect(
|
|
isExplicitCommandTurnContext(
|
|
{
|
|
CommandTurn: {
|
|
kind: "normal",
|
|
source: "message",
|
|
authorized: false,
|
|
body: "/reset@openclaw",
|
|
},
|
|
CommandAuthorized: true,
|
|
Body: "/reset@openclaw",
|
|
RawBody: "/reset@openclaw",
|
|
CommandBody: "/reset@openclaw",
|
|
BotUsername: "openclaw",
|
|
},
|
|
emptyConfig,
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("lets structured command turns override legacy command fields", () => {
|
|
expect(
|
|
resolveCommandTurnContext({
|
|
CommandTurn: {
|
|
kind: "text-slash",
|
|
source: "text",
|
|
authorized: false,
|
|
commandName: "status",
|
|
body: "/status",
|
|
},
|
|
CommandSource: "native",
|
|
CommandAuthorized: true,
|
|
}),
|
|
).toEqual({
|
|
kind: "text-slash",
|
|
source: "text",
|
|
authorized: false,
|
|
commandName: "status",
|
|
body: "/status",
|
|
});
|
|
});
|
|
|
|
it("rejects inconsistent structured command turn pairs", () => {
|
|
expect(
|
|
resolveCommandTurnContext({
|
|
CommandTurn: {
|
|
kind: "native",
|
|
source: "message",
|
|
authorized: true,
|
|
},
|
|
CommandSource: "text",
|
|
CommandAuthorized: true,
|
|
CommandBody: "/status",
|
|
}),
|
|
).toMatchObject({
|
|
kind: "text-slash",
|
|
source: "text",
|
|
authorized: true,
|
|
});
|
|
});
|
|
|
|
it("exposes native/text helper predicates and target session resolution", () => {
|
|
const nativeTurn = createCommandTurnContext("native", {
|
|
authorized: true,
|
|
body: "/stop",
|
|
});
|
|
const textTurn = createCommandTurnContext("text", {
|
|
authorized: true,
|
|
body: "/status",
|
|
});
|
|
|
|
expect(isNativeCommandTurn(nativeTurn)).toBe(true);
|
|
expect(isAuthorizedTextSlashCommandTurn(textTurn)).toBe(true);
|
|
expect(
|
|
resolveCommandTurnTargetSessionKey({
|
|
CommandTurn: nativeTurn,
|
|
CommandTargetSessionKey: " target-session ",
|
|
}),
|
|
).toBe("target-session");
|
|
expect(
|
|
resolveCommandTurnTargetSessionKey({
|
|
CommandSource: "native",
|
|
CommandAuthorized: true,
|
|
CommandTargetSessionKey: " legacy-target ",
|
|
}),
|
|
).toBe("legacy-target");
|
|
expect(isExplicitCommandTurn(undefined)).toBe(false);
|
|
});
|
|
});
|