Files
Cyrene_v2/internal/runtime/trace/graph_props.go
T
AskaEth ec292081b2
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
refactor(trace): 拆 graph.go 471 行 → graph.go / graph_build.go / graph_props.go(纯搬移)
执行方案 DevDocs/audit/_plan.md 阶段 0 · G0-B:该文件距 500 行红线仅 29 行,而后续
11.4.2(memory/guardrail/orchestration 三类 span 生产者)等改动必然推进它 ⇒ 先拆。

- graph.go(159):对外类型与快照 API(FlowStatus* / FlowNode / FlowEdge / FlowGraph /
  NodeSpec / RunGraph;NewRunGraph / Start / SeedInput / SeedOutput / Snapshot)
- graph_build.go(245):构图与折叠(Upsert / rememberParent / laneOf / merge / addEdge /
  AddSpan / AddSpanStart / kindForSpan / labelForSpan / statusForFinished)
- graph_props.go(77):prop 白名单与脱敏(safeSpanProps / safePropKeys / summarize / truncate)

纯搬移机械证明:拆前与拆后(取非空、非 package、非 import 块行)排序后 **md5 相同、
diff 为空**;声明集合 28 条守恒;唯一新增文本 = 两个新文件的 package 行与 import 块。
`safePropKeys`(含 4bf1f7c 刚加入的 "persona")原样落在 graph_props.go,逐字节未动。

验证:go build/vet 绿;internal/runtime/trace + internal/services/gateway 测试绿(含
graph_persona_test 与 flow_update_ws_test 的 graph_update 线上帧成对契约回归);
linux/amd64 + darwin/arm64 交叉编译绿;三文件 159/245/77 行均 ≤500;gofmt -l 无输出。
2026-10-04 10:46:13 +08:00

78 lines
2.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package trace
import (
"encoding/json"
"fmt"
)
// maxSummaryRunes bounds how many runes of a user string / tool result are
// rendered into a node label or prop summary, so sensitive or long content is
// collapsed to a safe, dashboard-friendly prefix.
const maxSummaryRunes = 80
// safeSpanProps carries only a whitelist of span props to the frontend, plus a
// desensitized tool result summary. It never forwards raw arguments or secrets, and
// every string is scrubbed of credential shapes and truncated, so long or sensitive
// content cannot leak a full payload (design/19 §3).
func safeSpanProps(rec SpanRecord) map[string]any {
var props map[string]any
for _, k := range safePropKeys {
v, ok := rec.Props[k]
if !ok {
continue
}
if props == nil {
props = make(map[string]any)
}
props[k] = redactValue(v)
}
if rec.Kind == SpanKindTool {
if v, ok := rec.Props["result"]; ok {
if props == nil {
props = make(map[string]any)
}
props["result_summary"] = RedactText(summarize(v))
}
}
return props
}
// safePropKeys are the span prop keys forwarded to a node's props.
//
// persona 是子代理分支根节点上的「目标角色 id」:orchestration/scheduler.go 只在
// subagent_spawn 显式指定角色时才设置它,且该 id 必须先通过角色注册表校验
// (ErrUnknownPersona 会拒绝未注册的 id,orchestration/persona.go),因此它是注册表里
// 的角色标识,不是用户内容、不是提示词、更不是凭据——同一个 id 本来就随 GET /personas
// 与会话材料对外可见。它是 Docs/api-client.md §3.5(v0.47)对客户端的承诺
// (design/22 §8-2:让流程图显示「这个子代理是以谁的身份在替班」),漏出白名单就会被
// safeSpanProps 静默丢掉。未指定角色派发时该键不存在,符合 v0.47「纯增量」的约定。
var safePropKeys = []string{"stop_reason", "step", "error", "error_kind", "tool", "persona"}
// summarize renders an arbitrary value as a short text string for a prop summary.
func summarize(v any) string {
switch x := v.(type) {
case nil:
return ""
case string:
return x
default:
b, err := json.Marshal(x)
if err != nil {
return fmt.Sprintf("%v", x)
}
return string(b)
}
}
// truncate cuts s to at most max runes, appending an ellipsis when it overflows.
func truncate(s string, max int) string {
if max <= 0 {
return s
}
r := []rune(s)
if len(r) <= max {
return s
}
return string(r[:max]) + "…"
}