fix(session): 群聊/陌生人记忆隔离——MemWritable 贯穿 loop(AGENTS.md 红线)
群聊/陌生人查记忆只读不写、不泄露主人敏感;owner 默认行为完全不变。 - loop/sessionenv.go:SessionEnv 类型 + WithSessionEnv/sessionEnvFrom/OwnerEnv——每次 Run 携带会话 隔离上下文,经 context.Context 传递(不改 Run 签名、不存 Loop/Options 字段,避免 16 并发渠道 消息共享 Loop 的数据竞争;Chatter 接口与测试桩签名不变)。 - loop/memory.go 门控:injectMemory 读——MemWritable=true(owner)走原 Retriever.Inject 全量不变; false(群聊/陌生人)改 Retrieve 后只留 ScopeProject(人人可读)、剔除 ScopeUser(owner习惯私有)与 ScopePersona(角色日常=owner敏感),块首加「群聊隔离」标记,无项目记忆不注入;extractMemory 写 false 直接 return 跳过(不调用 extractor 不落库)。 - 渠道入站(Channelbridge)传真实会话 MemWritable/Env;Web/REST/WS/voice chat 绑 OwnerEnv(JWT=owner 行为不变);backthink/automations/orchestration 默认 owner 全量。 - 测试:memory_gating_test 6 用例(owner/群聊只读不写/陌生人受限/无项目记忆不注入/默认owner全量)全绿; loop/session/gateway 既有测试无回归。go build/vet/test 47 包全绿。 遗留:ScopeProject 过滤在 loop 内后处理实现(未改 memory 包),若后续允许可在 Retriever 加 scope 过滤选项消除与 Inject 模板的重复(2处)。
This commit is contained in:
1 parent
dbcf3df8a3
commit
adad4e1f62
7 files changed
+297
-6
No files matched your search
@@ -0,0 +1,62 @@
|
||||
package loop
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/restrict"
|
||||
)
|
||||
|
||||
// SessionEnv is the per-conversation context that gates memory injection and
|
||||
// extraction for one Run call. It travels on the call's context rather than on
|
||||
// the shared Loop because Loop is reused across concurrent sessions (every
|
||||
// channel/group/stranger message resolves its own isolation boundary); storing it
|
||||
// on the Loop struct would race.
|
||||
type SessionEnv struct {
|
||||
// ActorEnv is the resolved actor environment: owner, group_member, stranger.
|
||||
// It is the strongly-typed identity lane decided structurally by the channel
|
||||
// layer (design/11 §0), never inferred from prompt text.
|
||||
ActorEnv restrict.ActorEnv
|
||||
// MemWritable reports whether this conversation may write shared memory. It is
|
||||
// true only for the owner's private conversation; group chats and strangers
|
||||
// are read-only (design/02 §3, design/11 §2).
|
||||
MemWritable bool
|
||||
}
|
||||
|
||||
// Restricted reports whether this is a group-chat or stranger conversation, i.e.
|
||||
// the read-only memory isolation context. Because MemWritable is true only for
|
||||
// the owner's private conversation, !MemWritable is exactly the restricted set.
|
||||
func (e SessionEnv) Restricted() bool { return !e.MemWritable }
|
||||
|
||||
// OwnerEnv returns the full-access owner session environment (private
|
||||
// conversation, memory read+write). It is the Web main-session and internal
|
||||
// automated-task default, matching the pre-existing owner-full behavior.
|
||||
func OwnerEnv() SessionEnv {
|
||||
return SessionEnv{ActorEnv: restrict.ActorOwner, MemWritable: true}
|
||||
}
|
||||
|
||||
// defaultSessionEnv is the owner-full environment used when a caller attaches no
|
||||
// explicit env: full memory read and write, the pre-existing behavior.
|
||||
func defaultSessionEnv() SessionEnv {
|
||||
return OwnerEnv()
|
||||
}
|
||||
|
||||
type sessionEnvKey struct{}
|
||||
|
||||
// WithSessionEnv attaches env to ctx so Run's memory gates honor the
|
||||
// group/stranger read-only isolation per call. It is concurrency-safe: the env
|
||||
// travels with the call's context and is never stored on the shared Loop.
|
||||
func WithSessionEnv(ctx context.Context, env SessionEnv) context.Context {
|
||||
return context.WithValue(ctx, sessionEnvKey{}, env)
|
||||
}
|
||||
|
||||
// sessionEnvFrom returns the env attached to ctx, or the owner-full default when
|
||||
// none was attached (backward compatible).
|
||||
func sessionEnvFrom(ctx context.Context) SessionEnv {
|
||||
if ctx == nil {
|
||||
return defaultSessionEnv()
|
||||
}
|
||||
if env, ok := ctx.Value(sessionEnvKey{}).(SessionEnv); ok {
|
||||
return env
|
||||
}
|
||||
return defaultSessionEnv()
|
||||
}
|
||||
Reference in new issue
Block a user