28 KiB
OneTalk 消息内容原始格式与媒体同步能力调查报告
调查日期:2026-09-02
适用页面:
https://onetalk.alibaba.com/message/weblitePWA.htm调试环境:Chromium
154.0.8012.0,CDP127.0.0.1:9222,Trade Message Center0.8.6调查方式:通过 Chromium CDP 观察真实 OneTalk WebSocket 响应、调用页面只读历史 SDK,并对照扩展 IndexedDB、共享协议和 Bright 存储代码。调查过程中没有发送消息、没有调用会改变已读状态的 API,也没有记录正文、账号、token、完整 URL 或 URL 查询参数值。
v6 同步说明(2026-09-11):下列 raw 样本和当时能力结论保留为调查证据;当前规范性图片合同为无尺寸的
fileId、extension、sizeBytes、isOriginal、md5、previewUrl、urlScope(另有version、kind)。MAIN decoder 忽略 rawwidth/height,它们不跨 normalized boundary;本报告不是 v6 live runtime 验收。
1. 结论摘要
以下结论是本报告调查时(2026-09-02)的历史快照:当时系统并不是完全没有采集图片和附件,而是只实现了文本的语义化解析:
- 文本能够从
content.text.content提取为message.text。 - 图片和附件能够作为原始
contentType=101/customJSON 被观察、写入 IndexedDB,并通过 Bright ACK。 - 图片和附件当时没有规范化的
kind、文件名、扩展名、大小、缩略图或下载地址合同;当前 v6 image canonical contract 见第 6 节。 - 消费端如果只读取
message.text,就会表现为“文本存在,图片和附件不存在”。
这不是当前 v6 数据流。当前 raw OneTalk content 只在 MAIN decoder 内解码和收窄;页面桥、Service Worker、IndexedDB、Bright JSONB、HTTP read 和 Mind 下游只接收 normalized content,Bright 不保存 raw JSON。
真实样本已确认以下映射:
| 业务类型 | WebSocket 原始类型 | Base64 解码后的判定 | OneTalk SDK 归一化类型 |
|---|---|---|---|
| 文本 | contentType=1 |
text.content |
msgType=101, subType=1 |
| 图片 | contentType=101, custom.type=7 |
图片元数据对象 | msgType=102, subType=60 |
| 文件附件 | contentType=101, custom.type=10010 |
cardType=12 |
msgType=10010, subType=61 |
| 其它业务卡片 | contentType=101, custom.type=10010 |
例如 cardType=2000 |
不是附件 |
本次真实附件样本为 PDF;没有抓到 TXT 附件、实时图片 push 或实时文件 push,因此这些场景不能标记为已验证。
调查时还发现一个必须优先修复的安全问题:文本内容的 text.extension.basicMessageInfo 是一段序列化 JSON,真实样本中包含 chatToken 键。页面观察器会复制整个原始 content,而 Bright 的清洗器对字符串直接原样放行,因此嵌套在字符串或 Base64 中的敏感字段可能进入持久化。
2. 调查范围与证据边界
2.1 使用的 Chromium/CDP 启动方式
/Applications/Chromium.app/Contents/MacOS/Chromium \
--remote-debugging-address=127.0.0.1 \
--remote-debugging-port=9222 \
--user-data-dir="/Users/ybf/work/demo-playwright/.chromium-profile"
CDP 确认:
Browser: Chrome/154.0.8012.0
Protocol-Version: 1.3
2.2 只读历史方法
调查只调用了页面现有的只读方法:
const sdk = window.IcbuIM.IMBaaSSDK.default;
const conversationService = sdk.getConversationServiceV2();
const messageService = sdk.getMessageService();
await conversationService.getConversationListByPagination({
cursor: 0,
count: 20,
});
await messageService.fetchMessagesWithoutUpdateToRead(options, conversation);
没有调用以下会改变已读状态或页面 store 的方法:
fetchMessages
updateMessageToRead
2.3 本次运行态证据
本次调查完成时,扩展 IndexedDB 快照为:
数据库:trade-message-center
版本:4
onetalk_messages:135
contentType=1:125
contentType=101/custom.type=7:1
contentType=101/custom.type=10010:9
onetalk_sync_candidates:135
confirmed:135
anomaly:0
onetalk_sync_checkpoints:2
phase=uploading:2
syncResult=succeeded:2
注意:
custom.type=10010同时包含文件和非文件业务卡片,不能把 9 条全部解释为附件。confirmed表示插件已经收到 Bright 的accepted或duplicateACK,不表示 Mind UI 已经能渲染媒体。- checkpoint 仍为
uploading,说明同步完成/锚点最终收敛是另一个独立问题;它不改变本报告对消息内容格式的判断。
3. OneTalk 历史 WebSocket 原始响应
3.1 顶层 envelope
真实 WebSocket 历史响应结构如下。所有业务值都已省略:
{
"headers": {
"<protocol-header>": "<redacted>"
},
"code": 200,
"body": {
"degradeFailover": "<value>",
"hasMore": 1,
"nextCursor": "<cursor>",
"userMessageModels": []
}
}
本次网络原帧中的分页字段为:
body.hasMore:0 | 1
body.nextCursor:number | null
3.2 单条历史消息包装
{
"message": {
"cid": "<raw-conversation-id>",
"messageId": "<raw-message-id>",
"createAt": 0,
"content": {},
"displayStyle": "<value>",
"extension": "<value>",
"msgReadStatusSetting": "<value>",
"receiverCount": 0,
"receivers": [],
"redPointPolicy": "<value>",
"searchableContent": "<value>",
"sender": {
"uid": "<participant-id>"
},
"unreadCount": 0
},
"msgStatus": 1,
"readStatus": 2,
"recallFeature": "<value>",
"userExtension": "<value>"
}
当前页面观察器正是从这一层读取:
message.cid
message.messageId
message.createAt
message.content
message.sender.uid
message.unreadCount
item.msgStatus
item.readStatus
4. OneTalk SDK 返回结构
同一次请求在 SDK Promise 中返回的不是 WebSocket 原始 envelope,而是 SDK 归一化后的扁平结果:
{
"hasMore": true,
"list": [],
"nextCursor": 0
}
这里的字段类型与网络原帧不同:
SDK hasMore:boolean
WebSocket hasMore:0 | 1
调查时 SDK list[] 条目确认包含:
autoReply
contact
contactRead
content
conversationCode
extInfo
localExt
messageId
messageType
msgType
opId
originExt
originalData
owner
receiver
sendTime
sender
spamStatus
status
subType
type
unread
uuid
viewType
这份 SDK 字段清单是历史 raw/boundary-only 证据,包含的 originalData.width / originalData.height 只可在 MAIN decoder 边界被忽略,不能成为 canonical 字段、存储字段或下游输入。
其中:
content已经被 SDK 转成展示字符串。- 媒体的结构化字段位于
originalData。 - 现有扩展历史观察链使用 WebSocket 原始消息作为事实来源,不应直接切换为 SDK 扁平消息并替换现有消息 ID。
- 更安全的做法是直接解码 WebSocket
custom.data,从而保留当前channelAccountId + conversationId + messageId幂等边界。
5. 文本原始格式
5.1 WebSocket 原始 content
{
"contentType": 1,
"text": {
"content": "<message-text-redacted>",
"extension": {
"basicMessageInfo": "<serialized-json-string>",
"messageDisplayInfo": "<serialized-json-string>",
"messageEventInfo": "<serialized-json-string>"
}
}
}
5.2 SDK 归一化结果
msgType:101
type:1
subType:1
viewType:0
content:string
originalData keys:text
5.3 当前可以安全保留的字段
{
"kind": "text",
"text": "<message-text>"
}
不应跨页面边界发送:
text.extension
basicMessageInfo
messageDisplayInfo
messageEventInfo
真实 basicMessageInfo 字符串解析后包含 chatToken 键,因此不能把这段字符串当作普通文本保存。
6. 图片原始格式
6.1 WebSocket 原始 content
{
"contentType": 101,
"custom": {
"type": 7,
"data": "<base64-json>",
"degrade": "",
"summary": "",
"title": ""
}
}
本次图片样本:
custom.data 字符长度:532
Base64 解码后 UTF-8 长度:397
Base64 解码结果:JSON object
6.2 Base64 解码后的 JSON
{
"fileId": "<file-id>",
"height": 1188,
"isOriginal": 1,
"md5": "<md5>",
"size": 370256,
"suffix": "jpeg",
"url": "<https-action-url>",
"width": 1192
}
这是 OneTalk raw payload 证据,不是 normalized contract。raw width / height 可保留在此样本中,但 v6 MAIN decoder 忽略它们,绝不将其传过页面桥或写入 canonical content。
6.3 SDK 归一化结果
msgType:102
type:1
subType:60
viewType:0
SDK originalData 的字段与 Base64 解码结果一致:
fileId
height
isOriginal
md5
size
suffix
url
width
6.4 可实现的规范化图片合同
type OneTalkImageContent = {
version: 1;
kind: "image";
fileId: string;
extension: string;
sizeBytes: number;
isOriginal: boolean;
md5: string | null;
previewUrl: string | null;
urlScope: "onetalk_session";
};
规范化规则:
custom.type必须为7。custom.data必须是有大小上限的合法 Base64。- Base64 解码结果必须是 UTF-8 JSON object。
fileId、suffix、url必须为非空字符串。size必须为有限非负整数,并设置合理上限;rawwidth/height被 MAIN decoder 忽略,不参与校验或输出。isOriginal的0/1显式转换为 boolean。url只接受绝对 HTTPS URL,并在确认真实主机后映射为previewUrl;urlScope固定为onetalk_session。- 不根据
suffix猜造 OneTalk 未提供的 MIME;UI 可以使用安全扩展名映射做展示提示。
7. 文件附件原始格式
7.1 WebSocket 原始 content
{
"contentType": 101,
"custom": {
"type": 10010,
"data": "<base64-json>",
"degrade": "",
"summary": "",
"title": ""
}
}
本次 PDF 样本:
custom.data 字符长度:912
Base64 解码后 UTF-8 长度:680
Base64 解码结果:JSON object
7.2 Base64 解码后的 JSON
{
"cardType": 12,
"params": {
"ctime": "<epoch-string>",
"downloadUrl": "",
"extensionType": "pdf",
"id": "<file-id>",
"md5": "<md5>",
"name": "<redacted>.pdf",
"parentId": "<parent-id>",
"size": "<decimal-string>",
"thumbnailUrl": "<https-action-url>",
"type": "<value>",
"url": "<https-action-url>",
"version": "<value>"
}
}
重要事实:
- 文件判定不能只看
custom.type=10010。 - 真实附件还需要
cardType=12。 params.size是 decimal string,不是 number。params.downloadUrl在本次样本中为空。- 实际可用候选位于
params.url;thumbnailUrl是另一个操作地址。 - URL 路径表现为
.htm操作入口,并带有appkey、fileAction、id、parentId、scene、secOperateAliId等查询键。报告没有记录查询值。
7.3 SDK 归一化结果
msgType:10010
type:1
subType:61
viewType:0
originalData.cardType:12
originalData.params.extensionType:pdf
7.4 可实现的规范化文件合同
type OneTalkFileContent = {
kind: "file";
fileId: string;
fileName: string;
extension: string;
sizeBytes: number;
md5?: string;
sourceUrl: string;
thumbnailUrl?: string;
};
规范化规则:
contentType必须为101。custom.type必须为10010。- Base64 JSON 的
cardType必须为12。 params.id、params.name、params.extensionType、params.size、params.url必须通过边界校验。params.size只接受十进制正整数字符串,并转换为安全整数。name后缀和extensionType不一致时 fail closed 或降级为 unsupported,不静默选择其中一个。url、非空的downloadUrl、thumbnailUrl只接受绝对 HTTPS URL 和固定 host allowlist。- 文件名用于展示时必须由 UI 作为文本渲染,不得作为 HTML。
PDF 已有真实样本。TXT 是否沿用完全相同的 cardType=12 结构尚无真实证据;实现可以设计为通用文件合同,但 TXT 的验收必须等待真实样本。
8. custom.type=10010 不等于附件
本次还抓到以下 WebSocket 原始类型:
contentType=101
custom.type=10010
custom.data=<base64-json>
但 Base64 解码结果为:
{
"cardType": 2000,
"params": {
"country": "<value>",
"ctime": "<value>",
"ids": "<value>",
"type": "<value>"
}
}
SDK 将其归一化为:
msgType=10010
subType=2000
这是一类业务卡片,不是附件。若代码只写:
if (content.custom.type === 10010) return file;
就会错误地把商品、国家或其它业务卡片当成文件。
正确判定必须至少包含:
contentType=101
custom.type=10010
decoded.cardType=12
decoded.params 通过文件 schema
其它合法但未支持的 cardType 应返回受控的 unsupported 内容,不应丢弃整条消息,也不应保留完整 raw payload。
9. 调查时的代码为什么表现为“只有文本”
9.1 页面观察器
当前 apps/chrome-extension/src/onetalk/main-page/message-observer/model.ts:
const contentRecord = isRecord(message.content) ? message.content : null;
if (contentRecord && isFiniteNumber(contentRecord.contentType)) {
output.contentType = contentRecord.contentType;
}
if (contentRecord) output.content = contentRecord;
const contentText = contentRecord?.text;
if (isRecord(contentText) && typeof contentText.content === "string") {
output.text = contentText.content;
} else {
output.text = null;
}
因此:
- 文本:得到
contentType=1、完整 raw content 和text=正文。 - 图片/文件:得到
contentType=101、完整 raw content 和text=null。 - 当前没有任何代码把 Base64
custom.data变成image或file。
9.2 Service Worker 与 IndexedDB
pageMessageToObserved() 会复制页面观察字段。IndexedDB 只检查:
content 是合法 JSON value
contentType 是有限整数
text 是 string | null | undefined
所以图片和文件不会因为 text=null 被拒绝。它们以 opaque JSON 进入:
onetalk_messages
onetalk_sync_candidates
本次运行态的 confirmed 计数证明图片/附件 raw 记录确实通过了现有 ACK 链路。
9.3 共享协议与 Bright
调查时的 OneTalkMessage 定义:
type OneTalkMessage = {
messageId: string;
conversationId: string;
senderId: string;
direction: "sent" | "received";
sentAtMs: number;
content: OneTalkJsonValue;
contentType: number;
text?: string | null;
participantIds: string[];
readStatus: number;
messageStatus: number;
unreadCount: number;
};
content 只有“可序列化 JSON”约束,没有 text/image/file 语义。
Bright 数据库当前保存:
content_type integer
text text nullable
content jsonb
因此调查时的 Bright 能保存媒体 raw JSON,但不能告诉 Mind:
这是图片还是文件
哪个 URL 用于预览
哪个 URL 用于下载
文件名和大小是什么
未知 card 应如何展示
9.4 当前展示边界
仓库内的 Bright harness 只执行:
JSON.stringify({ content: message.content }, null, 2);
它没有:
<img>
附件链接
文件名
文件大小
类型图标
unsupported fallback
真实 Mind UI 如果只读取 message.text,媒体消息自然不可见。
10. 调查时发现的安全风险
10.1 字符串内部的敏感字段绕过清洗
Bright 当前的敏感键规则能删除普通对象中的:
token
cookie
csrf
authorization
secret
password
credential
sid
app-key
但清洗器遇到字符串会立即原样返回:
if (typeof value === "string" || typeof value === "boolean") return value;
以下两种载荷因此不会被递归检查:
text.extension.basicMessageInfo中的序列化 JSON。custom.data中的 Base64 JSON。
真实 basicMessageInfo 解析后已确认含有 chatToken 键。调查时的实现把完整 raw content 交给 Bright,违反了数据库注释中“不得写入带认证信息的完整 envelope”的不变量。
10.2 推荐的安全边界
媒体支持不能靠“服务端收到 raw 后再尽量清洗”。应改为:
OneTalk raw message
→ MAIN world 严格解码
→ 显式构造白名单 OneTalkMessageContent
→ 页面桥
→ Service Worker exact decoder
→ Bright exact decoder
→ JSONB / message.created / HTTP history
禁止跨边界的字段包括:
raw content object
text.extension
basicMessageInfo
custom.data
chatToken
Cookie
Authorization
完整 OneTalk response/envelope
原始 SDK row
10.3 URL 风险
图片和文件样本中的 URL 是带查询参数的 HTTPS 操作地址,而不是直接媒体 URL。实现前还必须验证:
- 是否依赖 OneTalk 登录 Cookie。
- 是否包含短期授权或一次性参数。
- 是否可以从 Mind 页面所在 Origin 使用。
- 是否会发生 302 跳转,以及跳转地址的生命周期。
- 是否允许服务端代理;若允许,如何做授权和 SSRF 防护。
- URL 是否适合长期持久化,还是只能保存媒体 ID 并在需要时换取临时地址。
在上述行为没有真实验证前,只能称其为 sourceUrl,不能承诺“Mind 可直接下载”。
11. 当前 v6 内容边界
当前规范性数据流是:
OneTalk raw message
→ MAIN decoder(raw 仅在此处出现)
→ exact normalized content
→ page bridge / Service Worker / IndexedDB
→ Bright canonical JSONB / message.created / HTTP history
→ Mind read model
因此 Bright 不保存 raw payload、custom.data 或 SDK row;它只保存共享 guard 已接受的 normalized content。下列合同说明该边界的目标形状。
建议让 content 成为消息展示内容的唯一事实源:
type OneTalkNormalizedContent =
| {
kind: "text";
text: string;
}
| {
version: 1;
kind: "image";
fileId: string;
extension: string;
sizeBytes: number;
isOriginal: boolean;
md5: string | null;
previewUrl: string | null;
urlScope: "onetalk_session";
}
| {
kind: "file";
fileId: string;
fileName: string;
extension: string;
sizeBytes: number;
md5?: string;
sourceUrl: string;
thumbnailUrl?: string;
}
| {
kind: "unsupported";
sourceContentType: number;
sourceCustomType?: number;
cardType?: number;
};
兼容期可以保留顶层 message.text,但它必须始终从:
message.content.kind === "text" ? message.content.text : null;
派生,不能成为第二个可独立写入的事实源。
现有 JSONB 列可以保存这个 union。只有在需要文件类型索引、全文搜索或独立媒体生命周期时,才需要新增数据库列或媒体表;第一阶段不应为了渲染图片/PDF创建第二套消息表。
12. 可以做到什么
12.1 调查时已经做到
- 观察文本、图片和 custom card 的原始 WebSocket content。
- 使用 raw
channelAccountId + conversationId + messageId保持消息幂等。 - 将非文本消息写入 IndexedDB。
- 将非文本消息作为 JSON observation 发送给 Bright。
- Bright 可以 ACK 图片/附件 raw observation。
- Bright JSONB 可以保存规范化后的
text/image/file/unsupportedunion,无需立即增加第二张消息表。
12.2 基于现有证据可以立即实现
- 文本严格白名单提取,删除全部 extension。
custom.type=7图片 Base64 JSON 解码和字段校验。custom.type=10010 + cardType=12通用文件解码。- PDF 文件名、扩展名、大小、源 URL、缩略图元数据同步。
- 未支持 card 的安全 fallback,不丢失消息身份和时间线位置。
- Mind 侧按
content.kind渲染文本、图片占位/预览和附件链接。 - 在共享协议、HTTP history 和
message.created中使用同一 typed content。 - 添加敏感字段负向测试,证明 raw content、Base64 和序列化 JSON 不再跨边界。
12.3 当前还不能承诺
- TXT 附件与 PDF 完全同构:没有真实 TXT 样本。
- 实时图片/文件 push 与历史响应完全同构:没有实时媒体 push 样本。
sourceUrl能在 Mind 页面直接打开:未验证 Cookie、CORS、跳转和有效期。- 图片/附件可以永久下载:当前 URL 可能只是临时操作入口。
- 支持音频、视频、语音、压缩包或所有 OneTalk custom card:没有真实样本和枚举。
- checkpoint 已完整收敛:本次末态仍为
uploading/succeeded。
13. 当前 v6 不变量
R1. 单一内容合同
扩展、Bright 和 Mind 必须共用一个 OneTalkNormalizedContent 判别联合。页面 raw payload、服务端 JSONB 和 UI 本地模型不得分别维护不同的媒体判定逻辑。
R2. 页面边界白名单解码
MAIN world 是 OneTalk raw content 的唯一解码所有者:
- 文本只取
text.content。 - 图片只接受
contentType=101/custom.type=7的有效 Base64 JSON。 - 文件只接受
contentType=101/custom.type=10010/cardType=12。 - 其它类型输出有限
unsupported元数据。 - raw content 和 custom data 不得进入页面桥。
R3. 保留事实身份
媒体规范化不得改变现有幂等键:
channelAccountId + conversationId + raw messageId
不得用 SDK 展示层 ID、时间戳、hash 或合成 ID 代替 raw message ID。
R4. URL 与文件元数据校验
- URL 只允许 HTTPS 和固定 OneTalk host allowlist。
- URL query/hash/userinfo 不进入日志或错误。
- 文件大小和时间字段必须有上下限;图片 raw
width/height由 MAIN decoder 忽略,不能成为 normalized 字段、匹配条件或展示数据。 - 文件扩展名规范化为小写有限字符集。
- 文件名按纯文本处理并限制长度。
- MD5 只能作为来源元数据,不能替代消息 ID 或安全签名。
R5. 历史与实时一致
历史 body.userMessageModels[].message.content 和实时 lastMessage.message.content 必须走同一个 decoder。只有在真实 live 媒体样本证明 envelope 不同后,才允许增加窄化适配层。
R6. 未支持类型行为
合法但未支持的 custom card:
- 不伪装成文本或附件。
- 不保存 raw content。
- 保留消息事实并使用
kind=unsupported。 - 输出稳定的安全类型码或诊断,不输出 raw data。
非法 Base64、非法 JSON、字段类型错误、超限或非 HTTPS URL:
- 进入明确 anomaly 或受控 unsupported 策略。
- 不得静默返回空文本。
- 不得阻塞同批其它有效消息。
R7. 跨层一致性
以下边界必须同时升级并由共享 decoder 约束:
MAIN observed message
page bridge
Service Worker storage/candidate
message.observed
Bright normalization
PostgreSQL JSONB
message.created
HTTP history
Mind UI model
如果合同为不兼容变更,应提升 OneTalk 协议版本并让旧插件得到明确升级错误,不能在同一版本内让 content 同时表示 raw 与 normalized 两种含义。
R8. 安全清除
任何通过网络、数据库、日志或诊断的消息都必须证明不包含:
chatToken
basicMessageInfo
custom.data
Cookie
Authorization
accountIdEncrypt
aliIdEncrypt
原始 URL query 值
完整 raw payload
14. 推荐验收标准
- 真实文本历史消息归一化为
{ kind: "text", text },并且 content 不含 extension。 - 真实 JPEG 样本从
custom.type=7Base64 JSON 归一化为无尺寸 v6 image;fileId、extension、sizeBytes、isOriginal、md5、previewUrl与urlScope通过 canonical contract,rawwidth/height不跨 MAIN 边界。 - 真实 PDF 样本从
custom.type=10010/cardType=12归一化为 file,文件名、扩展名、大小和 URL 元数据一致。 custom.type=10010/cardType=2000不会被识别成文件。- 非法 Base64、非法 JSON、超限数据、非 HTTPS URL、缺字段和大小溢出均 fail closed,不影响同批其它消息。
- page bridge、IndexedDB、Bright DB、HTTP history 和
message.created中均只存在 normalized content。 - 序列化最终 frame、数据库输入和诊断事件,断言不包含
chatToken、basicMessageInfo、custom.data或加密身份字段。 - Mind UI 能渲染文本、图片状态和 PDF 附件;URL 不可用时显示明确失败,不显示空白消息。
- unknown custom card 显示“暂不支持的消息类型”,同时保留正确的消息时间线和幂等身份。
- 获得真实 TXT 样本后补充同构性测试;未获得前不宣称 TXT 已验证。
- 获得真实 live 图片和附件 push 后证明历史与实时共用同一 decoder。
- 验证媒体 URL 在有 Cookie、无 Cookie、跨 Origin、过期和跳转场景的行为,再决定保存 URL、保存 ID 或增加受控代理。
15. 调查时的相关代码位置
| 层 | 文件 | 当前行为 |
|---|---|---|
| WebSocket 分流 | apps/chrome-extension/src/onetalk/main-page/message-observer/index.ts |
识别 history/new envelope |
| 页面消息提取 | apps/chrome-extension/src/onetalk/main-page/message-observer/model.ts |
复制 raw content,仅提取 text |
| 历史只读 SDK | apps/chrome-extension/src/onetalk/main-page/current-conversation-history/sdk.ts |
调用 fetchMessagesWithoutUpdateToRead |
| 页面到 SW | apps/chrome-extension/src/onetalk/service-worker/sync-engine/helpers.ts |
原样复制观察字段 |
| IndexedDB 校验 | apps/chrome-extension/src/onetalk/service-worker/storage.ts |
只检查 JSON 与基础字段类型 |
| 共享合同 | apps/onetalk-contract/src/model.ts |
content 为任意 JSON value |
| 共享 decoder | apps/onetalk-contract/src/decoder.ts |
不理解 text/image/file 语义 |
| Bright 清洗 | apps/server/src/onetalk/service.ts |
按键过滤对象,字符串原样通过 |
| Bright 持久化 | apps/server/src/database/schema/onetalk.ts |
content jsonb、text nullable |
| 开发展示 | apps/mind-test-harness/src/harness.ts |
只展示 raw JSON |
| 现有媒体覆盖 | apps/chrome-extension/test/onetalk-websocket-tap.test.js |
只有简化 custom.type=10010 fixture |
16. 最终判断
历史调查证明 OneTalk raw payload 提供实现图片和文件同步所需的核心元数据。当前 v6 不再沿用该报告中 raw content 跨页面桥、IndexedDB、Bright 或 Mind 的历史路径;实现核心已经收敛为 MAIN decoder 的单一白名单边界:
opaque raw content
→ 严格、安全、可测试的 text/image/file/unsupported 合同
→ Bright 持久化与事件只使用 normalized content
→ Mind read model 只接收 normalized content
raw 字符串中嵌套凭证进入 Bright 是本报告记录的历史风险;当前 v6 边界以不让 raw payload 离开 MAIN decoder 的方式关闭它。真实 v6 Chromium 运行态验收仍未执行。