mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-15 12:15:16 +00:00
fix(proc)!: shm 桥 v2 —— fExtraBag 透传插件自建 Extra 键,修复绑桁权限门失效
根因(2026-10-12 生产实证):shm 桥此前只透传 4 个具名 Extra 键 (media_blocks/media_type/input_source/output_channel),插件自建键 (qq 的 qq_auth 绑帧身份)在 WriteDirty/captureLocal 处被静默丢弃, 下一 stage ReadInto 重建的 Extra 永远没有它 → authOnFrame 恒 miss → before_toolcall 回落到可变全局身份。owner 轮被并发的非 owner 中断 合并降权后,自己的 memory_commit 被硬私名单误拒(日志 22:20:58 「改写了 1 个字段」);反向时序则可能把非 owner 误放为 owner。 - shm.go:stageField 加 fExtraBag(19 字段),shmVersion 1→2 (AttachSegment 与插件握手对旧版失败即拒,不静默错读新布局) - shmcodec.go:captureLocal/WriteAll/ReadInto/newSnapshotFromLocal 全链接入 bag;写侧过滤具名键(bag 不偷具名键槽位);读侧 bag 先 打平、具名键后覆盖(具名键永远赢);不可序列化值显式报错—— 宁失败不静默丢键(丢键对 qq 是权限失效) - extra_bag_test.go:判据 8 条(往返无损/具名键语义不变/不可序列化 报错/WriteDirty 零写与脏写/跨 stage 帧存活/袋污染具名键必输/ raw-JSON 写侧过滤/快照脏检),变异 M1-M3 全杀 配套:SDK 仓 777c3a0(模板 v2 同步 + qq 身份可序列化 + 插件侧判据)。 部署注意:shmVersion 2 后全部 23 个 proc 插件须用新 hmapdev 重编。
This commit is contained in:
@ -66,6 +66,36 @@ type localCtx struct {
|
||||
ExtraMediaType interface{}
|
||||
ExtraInputSource interface{}
|
||||
ExtraOutputChan interface{}
|
||||
// ExtraBag 是 Extra 里具名 4 键之外的全部键(插件自建,如 qq_auth)。
|
||||
// nil 表示无额外键;非 nil 时整体 JSON 进 fExtraBag。
|
||||
ExtraBag map[string]interface{}
|
||||
}
|
||||
|
||||
// namedExtraKeys 是被提升为具名字段的 4 个 Extra 键。bag 必须排除它们,
|
||||
// 否则同一键会被具名字段与 bag 各带一份,回读时语义冲突。
|
||||
var namedExtraKeys = map[string]bool{
|
||||
ExtraKeyMediaBlocks: true,
|
||||
ExtraKeyMediaType: true,
|
||||
ExtraKeyInputSource: true,
|
||||
ExtraKeyOutputChannel: true,
|
||||
}
|
||||
|
||||
// bagFromExtra 把 Extra 里非具名键收集成 bag(无额外键时返回 nil)。
|
||||
func bagFromExtra(extra map[string]interface{}) map[string]interface{} {
|
||||
if len(extra) == 0 {
|
||||
return nil
|
||||
}
|
||||
var bag map[string]interface{}
|
||||
for k, v := range extra {
|
||||
if namedExtraKeys[k] {
|
||||
continue
|
||||
}
|
||||
if bag == nil {
|
||||
bag = make(map[string]interface{})
|
||||
}
|
||||
bag[k] = v
|
||||
}
|
||||
return bag
|
||||
}
|
||||
|
||||
func captureLocal(sc *pubsdk.StageContext) *localCtx {
|
||||
@ -91,6 +121,7 @@ func captureLocal(sc *pubsdk.StageContext) *localCtx {
|
||||
l.ExtraMediaType = sc.Extra[ExtraKeyMediaType]
|
||||
l.ExtraInputSource = sc.Extra[ExtraKeyInputSource]
|
||||
l.ExtraOutputChan = sc.Extra[ExtraKeyOutputChannel]
|
||||
l.ExtraBag = bagFromExtra(sc.Extra)
|
||||
}
|
||||
return l
|
||||
}
|
||||
@ -166,6 +197,9 @@ func (s *Segment) writeLocal(l *localCtx) error {
|
||||
{fExtraMediaType, l.ExtraMediaType},
|
||||
{fExtraInputSource, l.ExtraInputSource},
|
||||
{fExtraOutputChannel, l.ExtraOutputChan},
|
||||
// bag 空时写成未设置(sliceOrNil);非空时整体 marshal。
|
||||
// 不可序列化的值在这里让 putJSON 显式报错(宁失败不静默丢键)。
|
||||
{fExtraBag, sliceOrNil(len(l.ExtraBag), l.ExtraBag)},
|
||||
}
|
||||
for _, step := range jsonFields {
|
||||
if err := putJSON(step.f, step.v); err != nil {
|
||||
@ -263,6 +297,16 @@ func (s *Segment) ReadInto(sc *pubsdk.StageContext) error {
|
||||
}
|
||||
|
||||
extra := map[string]interface{}{}
|
||||
// bag 先打平,具名键随后覆盖 → 具名键永远赢。
|
||||
// 顺序是防御:即便 bag 里混入具名键(写侧 bug/旧数据),
|
||||
// 也不会用 bag 的陈旧值顶掉具名字段的新值。
|
||||
var bag map[string]interface{}
|
||||
if err := getJSON(fExtraBag, &bag); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range bag {
|
||||
extra[k] = v
|
||||
}
|
||||
for _, pair := range []struct {
|
||||
f stageField
|
||||
key string
|
||||
@ -281,6 +325,7 @@ func (s *Segment) ReadInto(sc *pubsdk.StageContext) error {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var respPtr *string
|
||||
if s.getFlag(flagResponseSet) {
|
||||
r, err := getStr(fResponse)
|
||||
@ -461,6 +506,7 @@ func newSnapshotFromLocal(l *localCtx) *Snapshot {
|
||||
sn.jsons[fExtraMediaType] = marshal(l.ExtraMediaType)
|
||||
sn.jsons[fExtraInputSource] = marshal(l.ExtraInputSource)
|
||||
sn.jsons[fExtraOutputChannel] = marshal(l.ExtraOutputChan)
|
||||
sn.jsons[fExtraBag] = marshal(sliceOrNil(len(l.ExtraBag), l.ExtraBag))
|
||||
|
||||
if l.Response != nil {
|
||||
sn.response = *l.Response
|
||||
@ -509,6 +555,8 @@ func (f stageField) String() string {
|
||||
return "extra." + ExtraKeyInputSource
|
||||
case fExtraOutputChannel:
|
||||
return "extra." + ExtraKeyOutputChannel
|
||||
case fExtraBag:
|
||||
return "extra.bag"
|
||||
}
|
||||
return fmt.Sprintf("field(%d)", int(f))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user