Replies: 3 comments 1 reply
|
我按当前 这条检查到底拒绝什么
if (!isSessionFormatJsonObject(value) || typeof value['kind'] !== 'string'
|| value['kind'].length === 0 || value['kind'] === 'plugin')
throw new SessionFormatError('format v4 message requires a producer-owned source kind')被拒的只有这几种: for (const source of [null, [], {}, { kind: '' }, { kind: 1 }, { kind: 'plugin', plugin: 'compact' }])
expect(() => restore(source)).toThrow(/producer-owned source kind/)检查覆盖每一个声明的消息槽—— 为什么旧日志通常没事,而你有事v3→v4 迁移会自动重写旧包装,所以绝大多数人升级后不会遇到它。测试里的正例( 而 推论(这一条是推断,不是我在你机器上验证的):既然 v3 行会被迁移,那么一条在 v4 admission 时仍带 取证:直接找出是哪个生产方别用 // node scan.mjs "$env:DSH_HOME/sessions" (Node 24 内置 zstd)
import fs from 'node:fs'; import path from 'node:path'; import zlib from 'node:zlib'
const MAGIC = Buffer.from([0x28, 0xb5, 0x2f, 0xfd])
const walk = (p, out = []) => { const s = fs.statSync(p)
if (s.isFile()) { if (/\.jsonl(\.zstd)?$/.test(p)) out.push(p); return out }
for (const e of fs.readdirSync(p, { withFileTypes: true })) walk(path.join(p, e.name), out); return out }
const frames = (b) => { const o = []; let i = b.indexOf(MAGIC); while (i !== -1) { o.push(i); i = b.indexOf(MAGIC, i + 4) } return o }
const decode = (b) => { const o = frames(b); if (!o.length) return ''
return o.map((s, k) => { const e = k + 1 < o.length ? o[k + 1] : b.length
try { return zlib.zstdDecompressSync(b.subarray(s, e)).toString('utf8') } catch { return '' } }).join('') }
const SLOTS = { 'user/message': (d) => [d], 'system/message': (d) => [d.message], 'assistant/message': (d) => [d.message],
'tool/result': (d) => [d.message], 'agent/inbox/spliced': (d) => d.inserted, 'session/title-llm-request': (d) => d.messages }
const hits = new Map()
for (const file of walk(process.argv[2])) {
for (const line of decode(fs.readFileSync(file)).split('\n')) {
const t = line.trim(); if (!t.startsWith('{')) continue
let row; try { row = JSON.parse(t) } catch { continue }
const pick = SLOTS[row?.type]; if (!pick) continue
const msgs = pick(row.data ?? {}); if (!Array.isArray(msgs)) continue
for (const m of msgs) { if (m?.source?.kind !== 'plugin') continue
const who = typeof m.source.plugin === 'string' ? m.source.plugin : '<没有 plugin 字段>'
hits.set(who, (hits.get(who) ?? 0) + 1) }
}
}
console.log(hits.size ? [...hits].sort((a, b) => b[1] - a[1])
: '没有 kind=plugin 的消息——这条规则不会拒你这些日志。')我在一台真实的 0.1.6-alpha.2 机器上跑过这个思路(会话日志 12k 行、7879 帧):它准确报出了 85 条退役包装,并按生产方分组( 两条出路① 立刻恢复工作:拒绝是按行、在 admission 时发生的。新开一个会话不会重新准入那批坏行,所以先开新会话就能干活;旧会话仍可作历史阅读,但别指望在那个 turn 上续聊。若你必须救回那个会话,机械修复与 ② 真正修掉它:生产方必须写 producer-owned 的 kind——第三方就是 把取证脚本报出的那个 |
|
补充一条横向结论:这不是个例,而是所有「按 v3 写法注入消息」的第三方插件在 0.1.7 上都会遇到的版本边界问题——v3 的白名单只认 我把两边的源码与实测数据整理成了一份完整报告(含 v3 → #7556 上面第 ② 步的取证脚本正是为定位这类问题写的;如果你在自己机器上跑到某个第一方名字( |
|
这样做 plugin 太难跟进了,有兼容双版本的建议做法吗 |
Uh oh!
There was an error while loading. Please reload this page.
升级到0.1.7-alpha.2后提示:“本轮运行失败format v4 message requires a producer-owned source kind”,请问该怎么修复?
All reactions