
























@@ -6,9 +6,13 @@ import {
66resolveSessionFilePathOptions,
77resolveStorePath,
88} from "../../config/sessions.js";
9+import type { SessionEntry } from "../../config/sessions/types.js";
910import type { OpenClawConfig } from "../../config/types.openclaw.js";
1011import { callGateway } from "../../gateway/call.js";
11-import { deriveSessionTitle } from "../../gateway/session-utils.js";
12+import {
13+deriveSessionTitle,
14+readSessionTitleFieldsFromTranscriptAsync,
15+} from "../../gateway/session-utils.js";
1216import { resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
1317import { normalizeOptionalLowercaseString, readStringValue } from "../../shared/string-coerce.js";
1418import {
@@ -45,6 +49,8 @@ const SessionsListToolSchema = Type.Object({
45494650type GatewayCaller = typeof callGateway;
475152+const SESSIONS_LIST_TRANSCRIPT_FIELD_ROWS = 100;
53+4854function readSessionRunStatus(value: unknown): SessionRunStatus | undefined {
4955return value === "running" ||
5056value === "done" ||
@@ -109,6 +115,8 @@ export function createSessionsListTool(opts?: {
109115const includeDerivedTitles = params.includeDerivedTitles === true;
110116const includeLastMessage = params.includeLastMessage === true;
111117const gatewayCall = opts?.callGateway ?? callGateway;
118+const a2aPolicy = createAgentToAgentPolicy(cfg);
119+const hydrateTranscriptFieldsAfterFiltering = includeDerivedTitles || includeLastMessage;
112120113121const list = await gatewayCall<{ sessions: Array<SessionListRow>; path: string }>({
114122method: "sessions.list",
@@ -118,8 +126,8 @@ export function createSessionsListTool(opts?: {
118126 label,
119127 agentId,
120128 search,
121- includeDerivedTitles,
122- includeLastMessage,
129+includeDerivedTitles: false,
130+includeLastMessage: false,
123131includeGlobal: !restrictToSpawned,
124132includeUnknown: !restrictToSpawned,
125133spawnedBy: restrictToSpawned ? effectiveRequesterKey : undefined,
@@ -128,7 +136,6 @@ export function createSessionsListTool(opts?: {
128136129137const sessions = Array.isArray(list?.sessions) ? list.sessions : [];
130138const storePath = typeof list?.path === "string" ? list.path : undefined;
131-const a2aPolicy = createAgentToAgentPolicy(cfg);
132139const visibilityGuard = await createSessionVisibilityGuard({
133140action: "list",
134141requesterSessionKey: effectiveRequesterKey,
@@ -137,6 +144,13 @@ export function createSessionsListTool(opts?: {
137144});
138145const rows: SessionListRow[] = [];
139146const historyTargets: Array<{ row: SessionListRow; resolvedKey: string }> = [];
147+const titleTargets: Array<{
148+row: SessionListRow;
149+titleEntry: SessionEntry;
150+sessionId: string;
151+sessionFile?: string;
152+agentId: string;
153+}> = [];
140154141155for (const entry of sessions) {
142156if (!entry || typeof entry !== "object") {
@@ -310,17 +324,24 @@ export function createSessionsListTool(opts?: {
310324 lastAccountId,
311325 transcriptPath,
312326};
313-if (sessionId && includeDerivedTitles && !row.derivedTitle) {
314-row.derivedTitle = deriveSessionTitle(
315-{
327+if (
328+sessionId &&
329+hydrateTranscriptFieldsAfterFiltering &&
330+titleTargets.length < SESSIONS_LIST_TRANSCRIPT_FIELD_ROWS
331+) {
332+titleTargets.push({
333+ row,
334+titleEntry: {
316335 sessionId,
317336displayName: row.displayName,
318337label: row.label,
319338subject: readStringValue((entry as { subject?: unknown }).subject),
320339updatedAt: typeof row.updatedAt === "number" ? row.updatedAt : 0,
321340},
322-undefined,
323-);
341+ sessionId,
342+ ...(sessionFile ? { sessionFile } : {}),
343+agentId: resolvedAgentId,
344+});
324345}
325346if (messageLimit > 0) {
326347const resolvedKey = resolveInternalSessionKey({
@@ -333,6 +354,37 @@ export function createSessionsListTool(opts?: {
333354rows.push(row);
334355}
335356357+if (titleTargets.length > 0) {
358+const maxConcurrent = Math.min(4, titleTargets.length);
359+let index = 0;
360+const worker = async () => {
361+while (true) {
362+const next = index;
363+index += 1;
364+if (next >= titleTargets.length) {
365+return;
366+}
367+const target = titleTargets[next];
368+const fields = await readSessionTitleFieldsFromTranscriptAsync(
369+target.sessionId,
370+storePath,
371+target.sessionFile,
372+target.agentId,
373+);
374+if (includeDerivedTitles && !target.row.derivedTitle) {
375+target.row.derivedTitle = deriveSessionTitle(
376+target.titleEntry,
377+fields.firstUserMessage,
378+);
379+}
380+if (includeLastMessage && fields.lastMessagePreview) {
381+target.row.lastMessagePreview = fields.lastMessagePreview;
382+}
383+}
384+};
385+await Promise.all(Array.from({ length: maxConcurrent }, () => worker()));
386+}
387+336388if (messageLimit > 0 && historyTargets.length > 0) {
337389const maxConcurrent = Math.min(4, historyTargets.length);
338390let index = 0;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。