fix(wechat): scrub image_url from sessions and harden vision handoff.
Memind CI / Test, build, and release guards (pull_request) Failing after 19s

Strip image_url from persisted session payloads, keep image turns scoped to the active request, and align proxy/vision/page-data paths so WeChat image history does not leak into fresh sessions.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-29 14:04:15 +08:00
parent 2f51041822
commit 53c5f6d9c2
9 changed files with 222 additions and 7 deletions
+53 -7
View File
@@ -34,6 +34,7 @@ import {
import { extractAttachmentText } from './mindspace-attachment-text.mjs';
import {
buildCurrentTurnImageScopeNote,
conversationHasImageUrlContent,
extractCurrentTurnImageUrls,
scrubConversationHistoricalImageAttachments,
} from './chat-image-turn-scope.mjs';
@@ -974,6 +975,9 @@ export async function buildVisionPayload({
'不要向用户展示 HTML 代码块。';
let updatedContent = Array.isArray(userMessage?.content) ? [...userMessage.content] : [];
// Text-only Goose providers cannot accept image_url parts. After VL analysis,
// keep only text (with the injected vision note) for the agent turn.
updatedContent = updatedContent.filter((item) => item?.type !== 'image_url');
for (const item of imageItems) {
updatedContent = updatedContent.map((c) => {
if (c?.type !== 'text' || typeof c.text !== 'string') return c;
@@ -1698,27 +1702,59 @@ export function createTkmindProxy({
return { changed: false, updated: false, status: upstream.status };
}
const session = await upstream.json().catch(() => null);
const { conversation, changed } = scrubConversationHistoricalImageAttachments(
session?.conversation ?? [],
const conversation = Array.isArray(session?.conversation) ? session.conversation : [];
const hasImageUrlContent = conversationHasImageUrlContent(conversation, {
excludeMessageId: activeId,
});
const { conversation: scrubbedConversation, changed } = scrubConversationHistoricalImageAttachments(
conversation,
activeId,
);
if (!changed) return { changed: false, updated: false };
// Text-only providers (DeepSeek) reject any lingering image_url parts. If Goose
// cannot persist a scrub (405/404), callers must rotate to a fresh session.
if (!changed && !hasImageUrlContent) {
return { changed: false, updated: false, hasImageUrlContent: false };
}
if (!changed && hasImageUrlContent) {
return {
changed: true,
updated: false,
status: 'image_url_content_present',
hasImageUrlContent: true,
};
}
const update = await apiFetch(
target,
apiSecret,
`/sessions/${encodeURIComponent(sessionId)}`,
{
method: 'PUT',
body: JSON.stringify({ conversation }),
body: JSON.stringify({ conversation: scrubbedConversation }),
},
);
if (!update.ok) {
console.warn(
`Historical image scrub skipped for session ${sessionId}: upstream ${update.status}`,
);
return { changed: true, updated: false, status: update.status };
return {
changed: true,
updated: false,
status: update.status,
hasImageUrlContent:
hasImageUrlContent
|| conversationHasImageUrlContent(scrubbedConversation, {
excludeMessageId: activeId,
}),
};
}
return { changed: true, updated: true, status: update.status };
return {
changed: true,
updated: true,
status: update.status,
hasImageUrlContent: conversationHasImageUrlContent(scrubbedConversation, {
excludeMessageId: activeId,
}),
};
} catch (err) {
console.warn(
'Historical image scrub skipped:',
@@ -1938,7 +1974,17 @@ export function createTkmindProxy({
if (!user) throw new Error('用户不存在');
if (requireHistoricalImageIsolation || messageHasImages(userMessage)) {
const imageIsolation = await syncHistoricalImageTurnIsolation(sessionId, userMessage?.id);
if (requireHistoricalImageIsolation && imageIsolation.changed && !imageIsolation.updated) {
const scrubUnsupported =
imageIsolation.changed
&& !imageIsolation.updated
&& (
requireHistoricalImageIsolation
|| imageIsolation.hasImageUrlContent
|| Number(imageIsolation.status) === 404
|| Number(imageIsolation.status) === 405
|| imageIsolation.status === 'image_url_content_present'
);
if (scrubUnsupported) {
const error = new Error(
`historical_image_session_update_unsupported:${imageIsolation.status ?? 'unknown'}`,
);