docs(comments): 注释中文化 · internal/services/channelbridge(纯替换)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 只翻注释/文档字符串:剥掉注释后代码行逐字节不变(机械校验只剩允许的行尾注释形态)
- 术语对齐既有译法(owner→主人、persona→角色、engage→参与度判定、quiet hours→安静时段、backoff→退避)
- 复核:go build 绿 + channelbridge 包测试绿;gofmt 干净
This commit is contained in:
AskaEth committed 2026-10-04 13:33:06 +08:00
1 parent 61cf80d5ee
commit 093f38d986
28 files changed
+1089 -1089

No files matched your search

+46 -46
View File
@@ -8,58 +8,58 @@ import (
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/history"
)
// This file owns which role an inbound IM turn speaks as (DevDocs/design/22 §2
// 裁决规则 + §7-3 + §7-4). Identity and permission stay structural — the actor
// metadata and the session's isolation boundary decide who may do what — while the
// role only scopes the prompt, the memory namespace, the emotion machine and the
// thinking budget.
// 本文件掌管一轮入站 IM 对话以哪个角色说话(DevDocs/design/22 §2
// 裁决规则 + §7-3 + §7-4)。身份与权限仍是结构性的——actor
// 元数据与会话的隔离边界决定谁可以做什么——而
// 角色只界定提示词、记忆命名空间、情绪机器与
// 思考预算的范围。
//
// Precedence, top down (design/22 §2):
// 1. the session-level binding — for an owner private chat that is the main web
// session the message is mirrored into, so the IM mirror answers with the same
// role the Web client is talking to (design/22 §7-3);
// 2. the channel-level binding — the account's channels.json personaId, used when
// the main session binds no role of its own (design/22 §2 方案 b);
// 3. the global default — expressed as "", which the loop resolves to its
// configured default role.
// 优先顺序,自上而下(design/22 §2):
// 1. 会话级绑定——对主人私聊而言即消息所镜像进的 Web 主
// 会话,因此 IM 镜像用 Web 客户端正在对话的同一
// 角色作答(design/22 §7-3);
// 2. 渠道级绑定——账号在 channels.json 中的 personaId,用于
// 主会话自身未绑定角色时(design/22 §2 方案 b);
// 3. 全局默认——以 "" 表示,循环会把它解析为
// 自己配置的默认角色。
//
// A restricted conversation (group chat or stranger, i.e. every isolated sess-xxx
// session) is deliberately excluded from 1 and 2: it is forced onto the default
// role (design/22 §7-4, the conservative choice), so a channel binding can never
// put a third party's conversation into a role's memory namespace.
// 受限会话(群聊或陌生人,即每个隔离的 sess-xxx
// 会话)被刻意排除在 1 和 2 之外:它被强制使用默认
// 角色(design/22 §7-4 的保守选择),因此渠道绑定绝不会
// 把第三方的会话放进某个角色的记忆命名空间。
// sessionPersonaReader is the optional narrower view of the history store used to
// read a session's bound role. *history.Store implements it; a bridge built
// without one (or with a store that predates design/22) simply resolves no session
// binding, which is the global default.
// sessionPersonaReader 是历史存储的可选窄接口视图,用于
// 读取会话绑定的角色。*history.Store 实现了它;未装配它
// (或所用存储早于 design/22)的桥接器解析不出会话
// 绑定,即全局默认。
type sessionPersonaReader interface {
SessionPersona(ctx context.Context, sessionID string) (string, error)
}
// resolvePersona returns the role id one inbound turn runs as. target says where
// the turn's conversation lives (the mirrored main session vs an isolated
// session), sess carries the structurally resolved isolation boundary and account
// is the receiving adapter's registration name (platform:confId) used for the
// channel-level lookup.
// resolvePersona 返回一轮入站对话所用的角色 id。target 指明
// 该轮对话所在之处(镜像的主会话还是隔离
// 会话),sess 携带结构化解析出的隔离边界,account
// 是接收方适配器的注册名(platform:confId),用于
// 渠道级查询。
func (b *Bridge) resolvePersona(ctx context.Context, sess session.Session, target inboundTarget, account string) string {
// design/22 §7-4: a group/stranger conversation never plays a bound role.
// design/22 §7-4:群聊/陌生人会话绝不扮演绑定角色。
if !target.private {
return ""
}
// design/22 §7-3: an owner private chat mirrors into the main session, so it
// answers as whatever role that session is bound to.
// design/22 §7-3:主人私聊镜像进主会话,因此它
// 以该会话绑定的角色作答。
if pid := b.mainSessionPersona(ctx); pid != "" {
return pid
}
// design/22 §2 方案 b: the account's channel-level role, used only when the main
// session binds no role of its own.
// design/22 §2 方案 b:账号的渠道级角色,仅当主
// 会话自身未绑定角色时使用。
return b.personaOfAdapter(account)
}
// mainSessionPersona returns the role bound to the main web session, or "" when
// the session inherits the global default. A missing reader or a read error also
// yields "": a chat must never be blocked by a metadata lookup, and the fallback is
// the pre-existing default role.
// mainSessionPersona 返回 Web 主会话绑定的角色;会话继承
// 全局默认时返回 ""。读取器缺失或读取出错同样
// 返回 "":聊天绝不能因元数据查询而被阻塞,而回退
// 就是原有的默认角色。
func (b *Bridge) mainSessionPersona(ctx context.Context) string {
if b.history == nil {
return ""
@@ -75,20 +75,20 @@ func (b *Bridge) mainSessionPersona(ctx context.Context) string {
return strings.TrimSpace(pid)
}
// personaOfAdapter returns the cached channel-level role of one adapter
// registration name, or "" when none is bound (or none was resolved at connect
// time).
// personaOfAdapter 返回某个适配器注册名已缓存的渠道级角色,
// 未绑定(或连接时未解析出)时
// 返回 ""。
func (b *Bridge) personaOfAdapter(account string) string {
b.mu.RLock()
defer b.mu.RUnlock()
return b.adapterPersona[account]
}
// rememberAdapterPersona resolves and caches the channel-level role of an adapter
// that just connected. Resolving at connect/reload time (rather than per message)
// keeps the inbound hot path free of config reads, while a panel save still takes
// effect immediately: it writes channels.json and calls Reload, which rebuilds and
// reconnects the platform's adapters, refreshing this cache.
// rememberAdapterPersona 解析并缓存刚连接的适配器的渠道级
// 角色。在连接/重载时解析(而不是按消息解析)
// 让入站热路径无需读配置,同时面板保存仍能
// 立即生效:它写 channels.json 并调用 Reload,后者重建并
// 重连该平台的适配器,刷新此缓存。
func (b *Bridge) rememberAdapterPersona(account string) {
if b.channelPersona == nil {
return
@@ -104,9 +104,9 @@ func (b *Bridge) rememberAdapterPersona(account string) {
b.adapterPersona[account] = pid
}
// forgetAdapterPersona drops an adapter's cached channel role when the account is
// removed (or renamed) by a reload, so a removed account can never keep answering
// as a role that is no longer configured.
// forgetAdapterPersona 在账号被重载移除(或更名)时丢弃该适配器
// 缓存的渠道角色,使已移除的账号绝不会继续
// 以不再配置的角色作答。
func (b *Bridge) forgetAdapterPersona(account string) {
b.mu.Lock()
defer b.mu.Unlock()