mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
fix(agents): prevent invalid session filters from listing every session (#129198)
* fix(agents): preserve explicit session kind filtering * test(agents): refresh session kind prompt snapshots
This commit is contained in:
committed by
GitHub
parent
8160c6e90f
commit
79722d2353
@@ -32,7 +32,8 @@ export {
|
||||
} from "./sessions-resolution.js";
|
||||
|
||||
/** Coarse session category used by session list/status tools. */
|
||||
type SessionKind = "main" | "group" | "cron" | "hook" | "node" | "other";
|
||||
export const SESSION_LIST_KINDS = ["main", "group", "cron", "hook", "node", "other"] as const;
|
||||
type SessionKind = (typeof SESSION_LIST_KINDS)[number];
|
||||
|
||||
const SESSION_KIND_BY_CLASSIFICATION: Readonly<Record<string, SessionKind>> = {
|
||||
main: "main",
|
||||
|
||||
@@ -182,6 +182,46 @@ describe("sessions-list-tool", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("limits session kind arguments to the documented classification values", () => {
|
||||
const tool = createSessionsListTool({ config: VALID_CONFIG });
|
||||
|
||||
expect(
|
||||
Value.Check(tool.parameters, { kinds: ["main", "group", "cron", "hook", "node", "other"] }),
|
||||
).toBe(true);
|
||||
expect(Value.Check(tool.parameters, { kinds: ["unknown"] })).toBe(false);
|
||||
expect(Value.Check(tool.parameters, { kinds: [" "] })).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "unknown-only", kinds: ["unknown"], expected: [] },
|
||||
{ name: "whitespace-only", kinds: [" "], expected: [] },
|
||||
{ name: "unknown scalar", kinds: "unknown", expected: [] },
|
||||
{ name: "whitespace scalar", kinds: " ", expected: [] },
|
||||
{ name: "known scalar", kinds: "MAIN", expected: ["agent:main:main"] },
|
||||
{ name: "mixed known and unknown", kinds: ["unknown", "MAIN"], expected: ["agent:main:main"] },
|
||||
{
|
||||
name: "empty",
|
||||
kinds: [],
|
||||
expected: ["agent:main:main", "agent:main:slack:channel:team-room"],
|
||||
},
|
||||
])("never broadens an explicit $name session kind filter", async ({ kinds, expected }) => {
|
||||
mocks.gatewayCall.mockResolvedValue({
|
||||
sessions: [
|
||||
sessionRow("agent:main:main", "main"),
|
||||
sessionRow("agent:main:slack:channel:team-room", "channel"),
|
||||
sessionRow("agent:other:main", "main", "other"),
|
||||
],
|
||||
});
|
||||
|
||||
const result = await createSessionsListTool({ config: VALID_CONFIG }).execute("filter-kinds", {
|
||||
kinds,
|
||||
});
|
||||
|
||||
expect(getSessionsListDetails(result).sessions?.map((session) => session.key)).toEqual(
|
||||
expected,
|
||||
);
|
||||
});
|
||||
|
||||
it("lists unspawned same-agent sessions from the canonical main session under tree visibility", async () => {
|
||||
mocks.resolveEffectiveSessionToolsVisibility.mockReturnValue("tree");
|
||||
mocks.resolveSandboxedSessionToolContext.mockReturnValue({
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
*
|
||||
* Lists visible sessions and optionally hydrates titles, last messages, and transcript-derived metadata.
|
||||
*/
|
||||
import {
|
||||
normalizeOptionalLowercaseString,
|
||||
readStringValue,
|
||||
} from "@openclaw/normalization-core/string-coerce";
|
||||
import { readStringValue } from "@openclaw/normalization-core/string-coerce";
|
||||
import pMap from "p-map";
|
||||
import { Type } from "typebox";
|
||||
import type { SessionRunStatus } from "../../../packages/gateway-protocol/src/schema/sessions-row.js";
|
||||
@@ -21,6 +18,7 @@ import { resolveSessionAgentIds } from "../agent-scope.js";
|
||||
import {
|
||||
optionalNonNegativeIntegerSchema,
|
||||
optionalPositiveIntegerSchema,
|
||||
stringEnum,
|
||||
} from "../schema/typebox.js";
|
||||
import {
|
||||
describeSessionLinkRule,
|
||||
@@ -51,12 +49,13 @@ import {
|
||||
resolveEffectiveSessionToolsVisibility,
|
||||
resolveInternalSessionKey,
|
||||
resolveSandboxedSessionToolContext,
|
||||
SESSION_LIST_KINDS,
|
||||
type GatewaySessionListRow,
|
||||
type SessionListRow,
|
||||
} from "./sessions-helpers.js";
|
||||
|
||||
const SessionsListToolSchema = Type.Object({
|
||||
kinds: Type.Optional(Type.Array(Type.String())),
|
||||
kinds: Type.Optional(Type.Array(stringEnum(SESSION_LIST_KINDS))),
|
||||
limit: optionalPositiveIntegerSchema(),
|
||||
activeMinutes: optionalPositiveIntegerSchema(),
|
||||
messageLimit: optionalNonNegativeIntegerSchema(),
|
||||
@@ -73,14 +72,7 @@ const SessionListRowOutputSchema = Type.Object(
|
||||
key: Type.String(),
|
||||
sessionId: Type.Optional(Type.String()),
|
||||
agentId: Type.String(),
|
||||
kind: Type.Union([
|
||||
Type.Literal("main"),
|
||||
Type.Literal("group"),
|
||||
Type.Literal("cron"),
|
||||
Type.Literal("hook"),
|
||||
Type.Literal("node"),
|
||||
Type.Literal("other"),
|
||||
]),
|
||||
kind: stringEnum(SESSION_LIST_KINDS),
|
||||
channel: Type.String(),
|
||||
archived: Type.Boolean(),
|
||||
pinned: Type.Boolean(),
|
||||
@@ -185,13 +177,13 @@ export function createSessionsListTool(opts?: {
|
||||
sandboxed: opts?.sandboxed === true,
|
||||
});
|
||||
|
||||
const kindsRaw = readStringArrayParam(params, "kinds")
|
||||
?.map((value) => normalizeOptionalLowercaseString(value))
|
||||
.filter((value): value is string => Boolean(value));
|
||||
const allowedKindsList = (kindsRaw ?? []).filter((value) =>
|
||||
["main", "group", "cron", "hook", "node", "other"].includes(value),
|
||||
);
|
||||
const allowedKinds = allowedKindsList.length ? new Set(allowedKindsList) : undefined;
|
||||
const kindsRaw = readStringArrayParam(params, "kinds")?.map((value) => value.toLowerCase());
|
||||
const requestedKinds = params.kinds;
|
||||
const allowedKinds =
|
||||
(Array.isArray(requestedKinds) || typeof requestedKinds === "string") &&
|
||||
requestedKinds.length > 0
|
||||
? new Set(kindsRaw)
|
||||
: undefined;
|
||||
|
||||
const limit = readPositiveIntegerParam(params, "limit");
|
||||
const activeMinutes = readPositiveIntegerParam(params, "activeMinutes");
|
||||
|
||||
+1
@@ -1119,6 +1119,7 @@
|
||||
},
|
||||
"kinds": {
|
||||
"items": {
|
||||
"enum": ["main", "group", "cron", "hook", "node", "other"],
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
|
||||
Vendored
+4
-4
@@ -231,8 +231,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 0
|
||||
},
|
||||
"dynamicToolsJson": {
|
||||
"chars": 54999,
|
||||
"roughTokens": 13750
|
||||
"chars": 55201,
|
||||
"roughTokens": 13801
|
||||
},
|
||||
"openClawDeveloperInstructions": {
|
||||
"chars": 4499,
|
||||
@@ -243,8 +243,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 7221
|
||||
},
|
||||
"totalWithDynamicToolsJson": {
|
||||
"chars": 83883,
|
||||
"roughTokens": 20971
|
||||
"chars": 84085,
|
||||
"roughTokens": 21022
|
||||
},
|
||||
"userInputText": {
|
||||
"chars": 1300,
|
||||
|
||||
test/fixtures/agents/prompt-snapshots/codex-runtime-happy-path/telegram-direct-codex-message-tool.md
Vendored
+4
-4
@@ -231,8 +231,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 0
|
||||
},
|
||||
"dynamicToolsJson": {
|
||||
"chars": 54691,
|
||||
"roughTokens": 13673
|
||||
"chars": 54893,
|
||||
"roughTokens": 13724
|
||||
},
|
||||
"openClawDeveloperInstructions": {
|
||||
"chars": 3390,
|
||||
@@ -243,8 +243,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 6851
|
||||
},
|
||||
"totalWithDynamicToolsJson": {
|
||||
"chars": 82095,
|
||||
"roughTokens": 20524
|
||||
"chars": 82297,
|
||||
"roughTokens": 20575
|
||||
},
|
||||
"userInputText": {
|
||||
"chars": 929,
|
||||
|
||||
Vendored
+4
-4
@@ -226,8 +226,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 0
|
||||
},
|
||||
"dynamicToolsJson": {
|
||||
"chars": 56248,
|
||||
"roughTokens": 14062
|
||||
"chars": 56450,
|
||||
"roughTokens": 14113
|
||||
},
|
||||
"openClawDeveloperInstructions": {
|
||||
"chars": 3390,
|
||||
@@ -238,8 +238,8 @@ This is the deterministic model-bound layer stack OpenClaw can snapshot for the
|
||||
"roughTokens": 6955
|
||||
},
|
||||
"totalWithDynamicToolsJson": {
|
||||
"chars": 84068,
|
||||
"roughTokens": 21017
|
||||
"chars": 84270,
|
||||
"roughTokens": 21068
|
||||
},
|
||||
"userInputText": {
|
||||
"chars": 1271,
|
||||
|
||||
Reference in New Issue
Block a user