






















@@ -22,6 +22,7 @@ import {
2222validateChatAbortParams,
2323validateChatHistoryParams,
2424validateChatInjectParams,
25+validateChatMessageGetParams,
2526validateChatSendParams,
2627} from "../../../packages/gateway-protocol/src/index.js";
2728import { CHAT_SEND_SESSION_KEY_MAX_LENGTH } from "../../../packages/gateway-protocol/src/schema.js";
@@ -127,11 +128,13 @@ import {
127128createManagedOutgoingImageBlocks,
128129} from "../managed-image-attachments.js";
129130import { ADMIN_SCOPE } from "../method-scopes.js";
130-import { getMaxChatHistoryMessagesBytes } from "../server-constants.js";
131+import { getMaxChatHistoryMessagesBytes, MAX_PAYLOAD_BYTES } from "../server-constants.js";
131132import { readSessionTranscriptIndex } from "../session-transcript-index.fs.js";
132133import {
133134capArrayByJsonBytes,
134135loadSessionEntry,
136+readSessionMessageByIdAsync,
137+readSessionMessagesAsync,
135138resolveGatewayModelSupportsImages,
136139resolveGatewaySessionThinkingDefault,
137140resolveDeletedAgentIdFromSessionKey,
@@ -1387,11 +1390,26 @@ export function buildOversizedHistoryPlaceholder(message?: unknown): Record<stri
13871390typeof (message as { timestamp?: unknown }).timestamp === "number"
13881391 ? (message as { timestamp: number }).timestamp
13891392 : Date.now();
1393+const rawMetadata =
1394+message && typeof message === "object"
1395+ ? (message as Record<string, unknown>)["__openclaw"]
1396+ : undefined;
1397+const metadata =
1398+rawMetadata && typeof rawMetadata === "object" && !Array.isArray(rawMetadata)
1399+ ? (rawMetadata as Record<string, unknown>)
1400+ : {};
1401+const metadataId = typeof metadata.id === "string" ? metadata.id : undefined;
1402+const metadataSeq = typeof metadata.seq === "number" ? metadata.seq : undefined;
13901403return {
13911404 role,
13921405 timestamp,
13931406content: [{ type: "text", text: CHAT_HISTORY_OVERSIZED_PLACEHOLDER }],
1394-__openclaw: { truncated: true, reason: "oversized" },
1407+__openclaw: {
1408+ ...(metadataId ? { id: metadataId } : {}),
1409+ ...(metadataSeq !== undefined ? { seq: metadataSeq } : {}),
1410+truncated: true,
1411+reason: "oversized",
1412+},
13951413};
13961414}
13971415@@ -2330,6 +2348,35 @@ export function dropPreSessionStartAnnouncePairs(
23302348return changed ? kept : messages;
23312349}
233223502351+function readChatHistoryMessageId(message: unknown): string | undefined {
2352+const metadata = asOptionalRecord(asOptionalRecord(message)?.["__openclaw"]);
2353+return typeof metadata?.id === "string" ? metadata.id : undefined;
2354+}
2355+2356+async function isChatMessageIdVisibleAfterHistoryFilters(params: {
2357+sessionId: string;
2358+storePath: string | undefined;
2359+sessionFile: string | undefined;
2360+messageId: string;
2361+sessionStartedAt?: number;
2362+}): Promise<boolean> {
2363+if (params.sessionStartedAt === undefined) {
2364+return true;
2365+}
2366+const messages = await readSessionMessagesAsync(
2367+params.sessionId,
2368+params.storePath,
2369+params.sessionFile,
2370+{
2371+mode: "full",
2372+reason: "chat.message.get visibility",
2373+},
2374+);
2375+return dropPreSessionStartAnnouncePairs(messages, params.sessionStartedAt).some(
2376+(message) => readChatHistoryMessageId(message) === params.messageId,
2377+);
2378+}
2379+23332380function dropLocalHistoryOverreadContextMessage(
23342381messages: unknown[],
23352382contextMessage: unknown,
@@ -2474,6 +2521,94 @@ export const chatHandlers: GatewayRequestHandlers = {
24742521 verboseLevel,
24752522});
24762523},
2524+"chat.message.get": async ({ params, respond, context }) => {
2525+if (!validateChatMessageGetParams(params)) {
2526+respond(
2527+false,
2528+undefined,
2529+errorShape(
2530+ErrorCodes.INVALID_REQUEST,
2531+`invalid chat.message.get params: ${formatValidationErrors(validateChatMessageGetParams.errors)}`,
2532+),
2533+);
2534+return;
2535+}
2536+const { sessionKey, messageId, maxChars } = params as {
2537+sessionKey: string;
2538+agentId?: string;
2539+messageId: string;
2540+maxChars?: number;
2541+};
2542+const agentIdOverride = normalizeOptionalText((params as { agentId?: string }).agentId);
2543+const requestedAgentId = resolveRequestedChatAgentId({
2544+cfg: (context as { getRuntimeConfig?: () => OpenClawConfig }).getRuntimeConfig?.(),
2545+requestedSessionKey: sessionKey,
2546+agentId: agentIdOverride,
2547+});
2548+const sessionLoadOptions = requestedAgentId ? { agentId: requestedAgentId } : undefined;
2549+const { cfg, storePath, entry } = loadSessionEntry(sessionKey, sessionLoadOptions);
2550+const selectedAgent = validateChatSelectedAgent({
2551+ cfg,
2552+requestedSessionKey: sessionKey,
2553+agentId: requestedAgentId,
2554+});
2555+if (!selectedAgent.ok) {
2556+respond(false, undefined, errorShape(ErrorCodes.INVALID_REQUEST, selectedAgent.error));
2557+return;
2558+}
2559+const sessionId = entry?.sessionId;
2560+if (!sessionId) {
2561+respond(true, { ok: false, unavailableReason: "not_found" });
2562+return;
2563+}
2564+2565+const resolved = await readSessionMessageByIdAsync(
2566+sessionId,
2567+storePath,
2568+entry?.sessionFile,
2569+messageId,
2570+);
2571+if (!resolved.found) {
2572+respond(true, { ok: false, unavailableReason: "not_found" });
2573+return;
2574+}
2575+const visible = await isChatMessageIdVisibleAfterHistoryFilters({
2576+ sessionId,
2577+ storePath,
2578+sessionFile: entry?.sessionFile,
2579+ messageId,
2580+sessionStartedAt:
2581+typeof entry?.sessionStartedAt === "number" ? entry.sessionStartedAt : undefined,
2582+});
2583+if (!visible) {
2584+respond(true, { ok: false, unavailableReason: "not_found" });
2585+return;
2586+}
2587+if (resolved.oversized) {
2588+respond(true, { ok: false, unavailableReason: "oversized" });
2589+return;
2590+}
2591+2592+const effectiveMaxChars =
2593+typeof maxChars === "number" ? maxChars : Math.min(MAX_PAYLOAD_BYTES, 1_000_000);
2594+const projectedMessage = resolved.message
2595+ ? projectChatDisplayMessage(resolved.message, {
2596+maxChars: effectiveMaxChars,
2597+})
2598+ : undefined;
2599+const projected = projectedMessage
2600+ ? augmentChatHistoryWithCanvasBlocks([projectedMessage])[0]
2601+ : undefined;
2602+if (!projected) {
2603+respond(true, { ok: false, unavailableReason: "not_visible" });
2604+return;
2605+}
2606+2607+respond(true, {
2608+ok: true,
2609+message: projected,
2610+});
2611+},
24772612"chat.abort": async ({ params, respond, context, client }) => {
24782613if (!validateChatAbortParams(params)) {
24792614respond(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。