mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:40:44 +00:00
feat: expose mailbox session discovery in sessions_list
This commit is contained in:
committed by
Peter Steinberger
parent
dcc243c889
commit
1a4c32e366
@@ -16,7 +16,7 @@ orchestrate sub-agents.
|
||||
|
||||
| Tool | What it does |
|
||||
| ------------------ | --------------------------------------------------------------------------- |
|
||||
| `sessions_list` | List sessions with optional filters (kind, recency) |
|
||||
| `sessions_list` | List sessions with optional filters (kind, label, agent, recency, preview) |
|
||||
| `sessions_history` | Read the transcript of a specific session |
|
||||
| `sessions_send` | Send a message to another session and optionally wait |
|
||||
| `sessions_spawn` | Spawn an isolated sub-agent session for background work |
|
||||
@@ -26,9 +26,11 @@ orchestrate sub-agents.
|
||||
|
||||
## Listing and reading sessions
|
||||
|
||||
`sessions_list` returns sessions with their key, kind, channel, model, token
|
||||
counts, and timestamps. Filter by kind (`main`, `group`, `cron`, `hook`,
|
||||
`node`) or recency (`activeMinutes`).
|
||||
`sessions_list` returns sessions with their key, agentId, kind, channel, model,
|
||||
token counts, and timestamps. Filter by kind (`main`, `group`, `cron`, `hook`,
|
||||
`node`), exact `label`, exact `agentId`, search text, or recency
|
||||
(`activeMinutes`). When you need mailbox-style triage, it can also ask for
|
||||
derived titles, last-message previews, or bounded recent messages.
|
||||
|
||||
`sessions_history` fetches the conversation transcript for a specific session.
|
||||
By default, tool results are excluded -- pass `includeTools: true` to see them.
|
||||
|
||||
@@ -200,10 +200,15 @@ describe("sessions tools", () => {
|
||||
expect(schemaProp("sessions_list", "limit").type).toBe("number");
|
||||
expect(schemaProp("sessions_list", "activeMinutes").type).toBe("number");
|
||||
expect(schemaProp("sessions_list", "messageLimit").type).toBe("number");
|
||||
expect(schemaProp("sessions_list", "label").type).toBe("string");
|
||||
expect(schemaProp("sessions_list", "agentId").type).toBe("string");
|
||||
expect(schemaProp("sessions_list", "search").type).toBe("string");
|
||||
expect(schemaProp("sessions_list", "includeDerivedTitles").type).toBe("boolean");
|
||||
expect(schemaProp("sessions_list", "includeLastMessage").type).toBe("boolean");
|
||||
expect(schemaProp("sessions_send", "timeoutSeconds").type).toBe("number");
|
||||
});
|
||||
|
||||
it("sessions_list filters kinds and includes messages", async () => {
|
||||
it("sessions_list forwards mailbox filters and includes messages", async () => {
|
||||
callGatewayMock.mockImplementation(async (opts: unknown) => {
|
||||
const request = opts as { method?: string };
|
||||
if (request.method === "sessions.list") {
|
||||
@@ -216,6 +221,8 @@ describe("sessions tools", () => {
|
||||
sessionId: "s-main",
|
||||
updatedAt: 10,
|
||||
lastChannel: "whatsapp",
|
||||
derivedTitle: "Main mailbox",
|
||||
lastMessagePreview: "Latest assistant update",
|
||||
},
|
||||
{
|
||||
key: "discord:group:dev",
|
||||
@@ -229,6 +236,8 @@ describe("sessions tools", () => {
|
||||
runtimeMs: 42,
|
||||
estimatedCostUsd: 0.0042,
|
||||
childSessions: ["agent:main:subagent:worker"],
|
||||
derivedTitle: "Dev room",
|
||||
lastMessagePreview: "Need review on the patch",
|
||||
},
|
||||
{
|
||||
key: "agent:main:dashboard:child",
|
||||
@@ -275,11 +284,36 @@ describe("sessions tools", () => {
|
||||
throw new Error("missing sessions_list tool");
|
||||
}
|
||||
|
||||
const result = await tool.execute("call1", { messageLimit: 1 });
|
||||
const result = await tool.execute("call1", {
|
||||
agentId: "main",
|
||||
label: "mailbox",
|
||||
search: "review",
|
||||
includeDerivedTitles: true,
|
||||
includeLastMessage: true,
|
||||
messageLimit: 1,
|
||||
});
|
||||
expect(callGatewayMock).toHaveBeenNthCalledWith(1, {
|
||||
method: "sessions.list",
|
||||
params: {
|
||||
activeMinutes: undefined,
|
||||
agentId: "main",
|
||||
includeDerivedTitles: true,
|
||||
includeGlobal: true,
|
||||
includeLastMessage: true,
|
||||
includeUnknown: true,
|
||||
label: "mailbox",
|
||||
limit: undefined,
|
||||
search: "review",
|
||||
spawnedBy: undefined,
|
||||
},
|
||||
});
|
||||
const details = result.details as {
|
||||
sessions?: Array<{
|
||||
key?: string;
|
||||
agentId?: string;
|
||||
channel?: string;
|
||||
derivedTitle?: string;
|
||||
lastMessagePreview?: string;
|
||||
spawnedBy?: string;
|
||||
status?: string;
|
||||
startedAt?: number;
|
||||
@@ -292,7 +326,10 @@ describe("sessions tools", () => {
|
||||
};
|
||||
expect(details.sessions).toHaveLength(5);
|
||||
const main = details.sessions?.find((s) => s.key === "main");
|
||||
expect(main?.agentId).toBe("main");
|
||||
expect(main?.channel).toBe("whatsapp");
|
||||
expect(main?.derivedTitle).toBe("Main mailbox");
|
||||
expect(main?.lastMessagePreview).toBe("Latest assistant update");
|
||||
expect(main?.messages?.length).toBe(1);
|
||||
expect(main?.messages?.[0]?.role).toBe("assistant");
|
||||
|
||||
@@ -302,6 +339,8 @@ describe("sessions tools", () => {
|
||||
expect(group?.runtimeMs).toBe(42);
|
||||
expect(group?.estimatedCostUsd).toBe(0.0042);
|
||||
expect(group?.childSessions).toEqual(["agent:main:subagent:worker"]);
|
||||
expect(group?.derivedTitle).toBe("Dev room");
|
||||
expect(group?.lastMessagePreview).toBe("Need review on the patch");
|
||||
|
||||
const dashboardChild = details.sessions?.find((s) => s.key === "agent:main:dashboard:child");
|
||||
expect(dashboardChild?.parentSessionKey).toBe("agent:main:main");
|
||||
|
||||
@@ -2,7 +2,7 @@ export const EXEC_TOOL_DISPLAY_SUMMARY = "Run shell commands that start now.";
|
||||
export const PROCESS_TOOL_DISPLAY_SUMMARY = "Inspect and control running exec sessions.";
|
||||
export const CRON_TOOL_DISPLAY_SUMMARY = "Schedule cron jobs, reminders, and wake events.";
|
||||
export const SESSIONS_LIST_TOOL_DISPLAY_SUMMARY =
|
||||
"List visible sessions and optional recent messages.";
|
||||
"List visible sessions with mailbox filters and optional previews.";
|
||||
export const SESSIONS_HISTORY_TOOL_DISPLAY_SUMMARY =
|
||||
"Read sanitized message history for a visible session.";
|
||||
export const SESSIONS_SEND_TOOL_DISPLAY_SUMMARY = "Send a message to another visible session.";
|
||||
@@ -12,7 +12,7 @@ export const UPDATE_PLAN_TOOL_DISPLAY_SUMMARY = "Track a short structured work p
|
||||
|
||||
export function describeSessionsListTool(): string {
|
||||
return [
|
||||
"List visible sessions with optional filters for kind, recent activity, and last messages.",
|
||||
"List visible sessions with optional filters for kind, label, agentId, search, recent activity, derived titles, and last-message previews.",
|
||||
"Use this to discover a target session before calling sessions_history or sessions_send.",
|
||||
].join(" ");
|
||||
}
|
||||
|
||||
@@ -287,7 +287,17 @@ export const TOOL_DISPLAY_CONFIG: ToolDisplayConfig = {
|
||||
sessions_list: {
|
||||
emoji: "🗂️",
|
||||
title: "Sessions",
|
||||
detailKeys: ["kinds", "limit", "activeMinutes", "messageLimit"],
|
||||
detailKeys: [
|
||||
"kinds",
|
||||
"label",
|
||||
"agentId",
|
||||
"search",
|
||||
"limit",
|
||||
"activeMinutes",
|
||||
"includeDerivedTitles",
|
||||
"includeLastMessage",
|
||||
"messageLimit",
|
||||
],
|
||||
},
|
||||
sessions_send: {
|
||||
emoji: "📨",
|
||||
|
||||
@@ -50,6 +50,7 @@ export type SessionRunStatus = "running" | "done" | "failed" | "killed" | "timeo
|
||||
|
||||
export type SessionListRow = {
|
||||
key: string;
|
||||
agentId?: string;
|
||||
kind: SessionKind;
|
||||
channel: string;
|
||||
origin?: {
|
||||
@@ -59,6 +60,8 @@ export type SessionListRow = {
|
||||
spawnedBy?: string;
|
||||
label?: string;
|
||||
displayName?: string;
|
||||
derivedTitle?: string;
|
||||
lastMessagePreview?: string;
|
||||
parentSessionKey?: string;
|
||||
deliveryContext?: SessionListDeliveryContext;
|
||||
updatedAt?: number | null;
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
SESSIONS_LIST_TOOL_DISPLAY_SUMMARY,
|
||||
} from "../tool-description-presets.js";
|
||||
import type { AnyAgentTool } from "./common.js";
|
||||
import { jsonResult, readStringArrayParam } from "./common.js";
|
||||
import { jsonResult, readStringArrayParam, readStringParam } from "./common.js";
|
||||
import {
|
||||
createSessionVisibilityGuard,
|
||||
createAgentToAgentPolicy,
|
||||
@@ -35,6 +35,11 @@ const SessionsListToolSchema = Type.Object({
|
||||
limit: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
activeMinutes: Type.Optional(Type.Number({ minimum: 1 })),
|
||||
messageLimit: Type.Optional(Type.Number({ minimum: 0 })),
|
||||
label: Type.Optional(Type.String({ minLength: 1 })),
|
||||
agentId: Type.Optional(Type.String({ minLength: 1, maxLength: 64 })),
|
||||
search: Type.Optional(Type.String({ minLength: 1 })),
|
||||
includeDerivedTitles: Type.Optional(Type.Boolean()),
|
||||
includeLastMessage: Type.Optional(Type.Boolean()),
|
||||
});
|
||||
|
||||
type GatewayCaller = typeof callGateway;
|
||||
@@ -97,6 +102,11 @@ export function createSessionsListTool(opts?: {
|
||||
? Math.max(0, Math.floor(params.messageLimit))
|
||||
: 0;
|
||||
const messageLimit = Math.min(messageLimitRaw, 20);
|
||||
const label = readStringParam(params, "label");
|
||||
const agentId = readStringParam(params, "agentId");
|
||||
const search = readStringParam(params, "search");
|
||||
const includeDerivedTitles = params.includeDerivedTitles === true;
|
||||
const includeLastMessage = params.includeLastMessage === true;
|
||||
const gatewayCall = opts?.callGateway ?? callGateway;
|
||||
|
||||
const list = await gatewayCall<{ sessions: Array<SessionListRow>; path: string }>({
|
||||
@@ -104,6 +114,11 @@ export function createSessionsListTool(opts?: {
|
||||
params: {
|
||||
limit,
|
||||
activeMinutes,
|
||||
label,
|
||||
agentId,
|
||||
search,
|
||||
includeDerivedTitles,
|
||||
includeLastMessage,
|
||||
includeGlobal: !restrictToSpawned,
|
||||
includeUnknown: !restrictToSpawned,
|
||||
spawnedBy: restrictToSpawned ? effectiveRequesterKey : undefined,
|
||||
@@ -215,6 +230,7 @@ export function createSessionsListTool(opts?: {
|
||||
|
||||
const row: SessionListRow = {
|
||||
key: displayKey,
|
||||
agentId: resolveAgentIdFromSessionKey(key),
|
||||
kind,
|
||||
channel: derivedChannel,
|
||||
origin:
|
||||
@@ -235,6 +251,8 @@ export function createSessionsListTool(opts?: {
|
||||
: undefined,
|
||||
label: readStringValue(entry.label),
|
||||
displayName: readStringValue(entry.displayName),
|
||||
derivedTitle: readStringValue(entry.derivedTitle),
|
||||
lastMessagePreview: readStringValue(entry.lastMessagePreview),
|
||||
parentSessionKey:
|
||||
typeof entry.parentSessionKey === "string"
|
||||
? resolveDisplaySessionKey({
|
||||
|
||||
Reference in New Issue
Block a user