99 lines
4.5 KiB
Go
99 lines
4.5 KiB
Go
package loop
|
||
|
||
import (
|
||
"context"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/restrict"
|
||
)
|
||
|
||
// SessionEnv 是每次 Run 调用中控制记忆注入与
|
||
// 提取的按会话上下文。它挂在调用的 context 上而不是
|
||
// 共享的 Loop 上,因为 Loop 会在并发会话间复用(每条
|
||
// channel/群聊/陌生人消息都解析出自己的隔离边界);把它
|
||
// 存在 Loop 结构体上会产生数据竞争。
|
||
type SessionEnv struct {
|
||
// ActorEnv 是解析出的 actor 环境:owner、group_member、stranger。
|
||
// 它是由 channel 层结构化判定的强类型身份车道
|
||
//(design/11 §0),绝不从提示词文本推断。
|
||
ActorEnv restrict.ActorEnv
|
||
// MemWritable 报告该会话是否可以写共享记忆。它仅
|
||
// 在主人的私聊中为 true;群聊与陌生人
|
||
// 只读(design/02 §3, design/11 §2)。
|
||
MemWritable bool
|
||
// PersonaID 是该会话发言所作为的角色(design/22 §2/§4.2),由调用方
|
||
// 每 turn 解析(通常是 gateway 的会话绑定解析器)。为空
|
||
// 表示「继承循环配置的角色」(Options.PersonaID,全局默认
|
||
// 角色),这使所有多角色之前的调用方 —— channelbridge、backthink、
|
||
// 内部自主流程 —— 行为与从前完全一致。它界定系统
|
||
// 提示词、记忆命名空间、情绪机与思考预算的作用域;它
|
||
// 不是身份或权限输入:仅由 ActorEnv/MemWritable 决定访问
|
||
// (项目红线)。
|
||
PersonaID string
|
||
}
|
||
|
||
// Restricted 报告这是否为群聊或陌生人会话,即
|
||
// 只读记忆隔离上下文。由于 MemWritable 仅在
|
||
// 主人的私聊中为 true,!MemWritable 恰好就是受限集合。
|
||
func (e SessionEnv) Restricted() bool { return !e.MemWritable }
|
||
|
||
// OwnerEnv 返回全权限的主人会话环境(私聊,
|
||
// 记忆读写)。它是 Web 主会话与内部
|
||
// 自动化任务的默认值,与既有的 owner 全权限行为一致。它不绑定
|
||
// 显式角色(PersonaID 为空),因此循环回退到其配置的
|
||
// 默认角色 —— 知道会话角色的调用方在返回值上
|
||
// 设置 PersonaID(design/22 §4.2)。
|
||
func OwnerEnv() SessionEnv {
|
||
return SessionEnv{ActorEnv: restrict.ActorOwner, MemWritable: true}
|
||
}
|
||
|
||
// OwnerEnvFor 返回绑定了显式角色的主人会话环境。它是**主人车道的唯一构造入口**:
|
||
// Web 主会话回合(gateway 的 turnEnvResolver.owner)与 IM 主人私聊回合
|
||
// (channelbridge 的私聊车道)都经它派生自己的 runEnv,两处因此逐字段同源 ——
|
||
// SessionEnv 将来新增字段时,两条车道会一起拿到它,而不会各自漂移出「同一个她、
|
||
// 两套环境」。
|
||
//
|
||
// personaID 为空表示「继承循环配置的默认角色」(与 OwnerEnv 同义,见 design/22 §4.2)。
|
||
// 它只界定提示词/记忆命名空间/情绪机/思考预算的作用域,**不是身份或权限输入**
|
||
// (项目红线:身份与权限在 channel 层的 actor 元数据上)。
|
||
func OwnerEnvFor(personaID string) SessionEnv {
|
||
env := OwnerEnv()
|
||
env.PersonaID = personaID
|
||
return env
|
||
}
|
||
|
||
// defaultSessionEnv 是调用方未挂载显式 env 时使用的
|
||
// owner 全权限环境:记忆全读写,即既有行为。
|
||
func defaultSessionEnv() SessionEnv {
|
||
return OwnerEnv()
|
||
}
|
||
|
||
type sessionEnvKey struct{}
|
||
|
||
// WithSessionEnv 把 env 挂到 ctx 上,使 Run 的记忆门按调用遵循
|
||
// 群聊/陌生人只读隔离。它是并发安全的:env
|
||
// 随调用的 context 传递,绝不存放在共享的 Loop 上。
|
||
func WithSessionEnv(ctx context.Context, env SessionEnv) context.Context {
|
||
return context.WithValue(ctx, sessionEnvKey{}, env)
|
||
}
|
||
|
||
// SessionEnvFrom 返回 WithSessionEnv 挂到 ctx 上的环境;
|
||
// 未挂载时返回 owner 全权限默认值(向后兼容)。它是
|
||
// WithSessionEnv 的读侧,供只拿到 context 的调用方使用 —— 例如
|
||
// 必须知道该 turn 角色/隔离边界的 Chatter 实现,或
|
||
// 断言某入站路径解析结果的测试。该值仅是展示/作用域元数据:
|
||
// 身份与权限在 env 存在之前就已由 actor 元数据决定
|
||
// (项目红线)。
|
||
func SessionEnvFrom(ctx context.Context) SessionEnv { return sessionEnvFrom(ctx) }
|
||
|
||
// sessionEnvFrom 返回挂到 ctx 上的 env;未挂载时返回
|
||
// owner 全权限默认值(向后兼容)。
|
||
func sessionEnvFrom(ctx context.Context) SessionEnv {
|
||
if ctx == nil {
|
||
return defaultSessionEnv()
|
||
}
|
||
if env, ok := ctx.Value(sessionEnvKey{}).(SessionEnv); ok {
|
||
return env
|
||
}
|
||
return defaultSessionEnv()
|
||
}
|