Merge origin/main into codex/btw-side-question

合入 main 的 norma v0.3.7、会话分组与流量证据更新,保留 /btw 快照和输入布局。同步自动放行、关闭流量绑定的测试断言,并等待归档测试夹具的旁路后台退出。
This commit is contained in:
ji ruo committed 2026-09-11 00:54:51 +08:00
commit 2930c4b915
94 files changed
+9737 -1241

No files matched your search

+31
View File
@@ -7,6 +7,7 @@ import (
"unicode/utf8"
"github.com/Autumn-27/artex/db"
"github.com/Autumn-27/artex/intercept"
"github.com/Autumn-27/artex/sidequestion"
"github.com/Autumn-27/norma/agentcore"
"github.com/Autumn-27/norma/harness"
@@ -34,8 +35,13 @@ func captureRun(ctx context.Context, opts agentcore.Options, input string, emit
// multiple prompts on the SAME conversation (e.g. a settlement round that reuses
// the worker's accumulated context after the main run hit max_turns).
func captureRunSession(ctx context.Context, s *agentcore.Session, input string, emit func(db.Activity)) (string, harness.TerminalReason, error) {
ctx, auditTrace := intercept.WithTrace(ctx, input, approvalHistory(s.Messages()))
defer auditTrace.Finish()
var reason harness.TerminalReason
rec := func(r db.Activity) {
if r.Kind == "text" || r.Kind == "tool_result" {
auditTrace.Append(db.InterceptContextEntry{Kind: r.Kind, Tool: r.Tool, ToolUseID: r.ToolUseID, Text: r.Detail, IsError: r.IsError})
}
if emit != nil {
emit(r)
}
@@ -91,6 +97,7 @@ func captureRunSession(ctx context.Context, s *agentcore.Session, input string,
toolNames[ev.ToolUse.ID] = ev.ToolUse.Name
in := string(ev.ToolUse.Input)
lastTool.start(ev.ToolUse.ID, ev.ToolUse.Name, in)
auditTrace.Start(ev.ToolUse.ID, ev.ToolUse.Name, ev.ToolUse.Input)
rec(db.Activity{Kind: "tool_use", Tool: ev.ToolUse.Name, ToolUseID: ev.ToolUse.ID,
Summary: ev.ToolUse.Name + " " + firstLine(in, 200), Detail: in})
case harness.KindToolResult:
@@ -100,6 +107,7 @@ func captureRunSession(ctx context.Context, s *agentcore.Session, input string,
flush()
out := blocksText(ev.ToolResult.Content)
lastTool.done(ev.ToolResult.ToolUseID)
auditTrace.Complete(ev.ToolResult.ToolUseID, out, ev.ToolResult.IsError)
rec(db.Activity{Kind: "tool_result", Tool: toolNames[ev.ToolResult.ToolUseID], ToolUseID: ev.ToolResult.ToolUseID,
IsError: ev.ToolResult.IsError, Summary: firstLine(out, 200), Detail: out})
case harness.KindText:
@@ -188,3 +196,26 @@ func firstLine(s string, max int) string {
}
return s
}
// Preserve the recorded session's visible messages, excluding thinking blocks.
// This is audit context; it is not presented as input to the existing tool-only judge.
func approvalHistory(messages []llm.Message) []db.InterceptContextEntry {
var entries []db.InterceptContextEntry
for _, message := range messages {
for _, block := range message.Content {
entry := db.InterceptContextEntry{Kind: string(message.Role)}
switch block.Type {
case llm.BlockText:
entry.Text = block.Text
case llm.BlockToolUse:
entry.Kind, entry.Tool, entry.ToolUseID, entry.Text = "tool_use", block.Name, block.ID, string(block.Input)
case llm.BlockToolResult:
entry.Kind, entry.ToolUseID, entry.Text, entry.IsError = "tool_result", block.ToolUseID, blocksText(block.Content), block.IsError
default:
continue
}
entries = append(entries, entry)
}
}
return entries
}