mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-23 02:18:06 +00:00
feat(channel): N1b —— 输出通道授权集合(三处过滤一致)+ 输出通道→目标 agent 的 inputch 解析
设计:docs/zh/resident-subagent-design.md §4.5(里程碑 N1b)。 ## 输出通道授权集合(默认完整授权,父可收窄) `AgentConfig.AllowedOutputs`(nil/空 = 完整授权)。三处过滤点必须一致, 否则会出现「列表里看不到、按名字还能调」的裂缝: 1. **工具表**:不为未授权的通道生成 output_send__X(模型看不到就不会调) 2. **列表工具**:output_list_channels 只列授权的 3. **调用点**:凭名字直调未授权的输出门必须被拒(纵深防御) ## 输出通道 → 目标 agent 的 inputch 解析 `ChannelRegistry.BindOutputTarget / ResolveOutputTarget`: 把输出通道解析成「目标 agent + 目标 inputch」,这是"输出可寻址到具体 agent" (子→父、父→指定子)的**数据面**;真正的跨 agent 投递在里程碑 N4。 未登记的输出通道 ok=false —— 表示由传输层 device 自行处理(qq/webui 这类)。 已登记目标的输出通道,在 output_list_channels 里会标出「目标: <agent> / inputch <名字>」。 ## 验收 `internal/agent/core/output_grant_test.go`(3 项): - 默认完整授权:全部输出门生成 + 列表含全部 - 白名单收窄:三个过滤点同时生效(工具表 / 列表 / 直调被拒) - 目标解析:绑定/解析、未登记由传输层处理、列表标出目标、空名报错 全仓 go test ./... 37 包 ok / 0 FAIL。
This commit is contained in:
@ -49,11 +49,25 @@ type InputChannel struct {
|
||||
type ChannelRegistry struct {
|
||||
mu sync.RWMutex
|
||||
channels map[string]InputChannel
|
||||
// outputTargets 把**输出通道**解析成"目标 agent 的哪个 inputch"。
|
||||
// 这是"输出可寻址到具体 agent"的依据(子→父、父→指定子)。
|
||||
outputTargets map[string]OutputTarget
|
||||
}
|
||||
|
||||
// OutputTarget 是一个输出通道的投递目标。
|
||||
type OutputTarget struct {
|
||||
// AgentID 是目标 agent("" = 本 agent / 由传输层通道 device 自行处理)。
|
||||
AgentID string `json:"agent_id,omitempty"`
|
||||
// InputCh 是目标 agent 上接收它的 inputch("" = 与输出通道同名)。
|
||||
InputCh string `json:"inputch,omitempty"`
|
||||
}
|
||||
|
||||
// NewChannelRegistry 构造一个空的 inputch 登记表。
|
||||
func NewChannelRegistry() *ChannelRegistry {
|
||||
return &ChannelRegistry{channels: make(map[string]InputChannel)}
|
||||
return &ChannelRegistry{
|
||||
channels: make(map[string]InputChannel),
|
||||
outputTargets: make(map[string]OutputTarget),
|
||||
}
|
||||
}
|
||||
|
||||
// Register 登记/更新一个 inputch。
|
||||
@ -144,6 +158,43 @@ func (r *ChannelRegistry) ListByOwner(agentID string) []InputChannel {
|
||||
return out
|
||||
}
|
||||
|
||||
// BindOutputTarget 登记"输出通道 → 目标 agent 的 inputch"的解析。
|
||||
//
|
||||
// 例:父把子用的输出通道 "to-child-1" 绑到 (child-1, "sub/in"),
|
||||
// 于是子经该通道发出的消息会投进 child-1 的 sub/in。
|
||||
func (r *ChannelRegistry) BindOutputTarget(output, agentID, inputCh string) error {
|
||||
if output == "" {
|
||||
return errors.New("输出通道名不能为空")
|
||||
}
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.outputTargets == nil {
|
||||
r.outputTargets = make(map[string]OutputTarget)
|
||||
}
|
||||
r.outputTargets[output] = OutputTarget{AgentID: agentID, InputCh: inputCh}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolveOutputTarget 解析一个输出通道的目标;未登记时 ok=false
|
||||
// (意味着由传输层通道自行处理,如 qq/webui 这类 device 通道)。
|
||||
func (r *ChannelRegistry) ResolveOutputTarget(output string) (OutputTarget, bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
t, ok := r.outputTargets[output]
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// ListOutputTargets 返回全部已登记的目标解析(按输出通道名排序)。
|
||||
func (r *ChannelRegistry) ListOutputTargets() map[string]OutputTarget {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
out := make(map[string]OutputTarget, len(r.outputTargets))
|
||||
for k, v := range r.outputTargets {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Count 返回已注册 inputch 数量。
|
||||
func (r *ChannelRegistry) Count() int {
|
||||
r.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user