群聊/陌生人查记忆只读不写、不泄露主人敏感;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处)。
63 lines
2.5 KiB
Go
63 lines
2.5 KiB
Go
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()
|
|
}
|