feat: 修复审计落差 #1/#2/#3/#6 + 管理面板移动端适配
#1 工具布尔标记生效(agent-106, 安全红线): loop toolSchemas/runToolCalls 加 allow-set 安全门 restrictSchemas/applyToolSecurityGate——群聊/陌生人只暴露允许工具(默认关), 被拒not_allowed软提示+AuditEvent, owner全工具不变; 身份靠 ActorEnv(非提示词) #2 checkpoint 恢复(agent-109): run.go LoadLatest + resumableStopReason门控(只恢复in-flight工具循环,不出答案不续跑), 幂等不重放副作用, 新会话不误恢复; resume_test 4用例 #3 tool_result(agent-108, 协议层): Anthropic 原生tool_result块(tool_use_id/is_error)非user纯文本 + OpenAI tool消息补tool_call_id; Content加ToolCallID/IsError等; (loop接线层2处留待,见备注) #6 capacity_awaken推送(agent-107): onCapacity从只Debug改推WS hub(BroadcastCapacityAwaken, sessionId定向+payload对齐前端AwakenPayload) 管理面板移动端(agent-110): AdminApp 汉堡菜单+抽屉侧栏+标题truncate防换行, RBAC守卫不受影响 全仓 build/vet/test绿 + 前端build绿; 前端无涉差异已对齐 备注: #3 loop接线层2处(assistant工具轮append历史 + Message.ToolCallID/IsError)留给后续; #4代审/#5子代理触发/#7拆文件后置(主人说后三不急)
This commit is contained in:
1 parent
aedae2190b
commit
7e39153a69
24 files changed
+1286
-56
No files matched your search
@@ -859,6 +859,26 @@ func TestHubBroadcastStream(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerBroadcastCapacityAwaken(t *testing.T) {
|
||||
t.Parallel()
|
||||
env := newEnv(t, nil, false)
|
||||
c := newFakeConn(env.srv.hub, 32)
|
||||
env.srv.hub.register(c)
|
||||
|
||||
env.srv.BroadcastCapacityAwaken("web", "quiet", "awake", "web_search")
|
||||
ev := readFakeFrame(t, c)
|
||||
if ev.Type != "capacity_awaken" {
|
||||
t.Fatalf("frame type = %q, want capacity_awaken (frame=%+v)", ev.Type, ev)
|
||||
}
|
||||
if ev.SessionID != "web" {
|
||||
t.Fatalf("frame sessionId = %q, want web", ev.SessionID)
|
||||
}
|
||||
data, ok := ev.Data.(map[string]any)
|
||||
if !ok || data["fromState"] != "quiet" || data["toState"] != "awake" || data["tool"] != "web_search" {
|
||||
t.Fatalf("frame data = %+v, want {fromState:quiet,toState:awake,tool:web_search}", ev.Data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHubStreamChatIdle(t *testing.T) {
|
||||
t.Parallel()
|
||||
hb := newTestHub(nil, nil) // nil runner
|
||||
|
||||
@@ -508,6 +508,28 @@ func (s *Server) BroadcastPersisted(msg history.Message) {
|
||||
s.hub.broadcast(wsEvent{Type: "message_persisted", SessionID: msg.SessionID, Data: map[string]any{"message": wire}})
|
||||
}
|
||||
|
||||
// wsCapacityAwaken is the WS event type for a capacity state transition
|
||||
// (design/14 §5). Its data field is the frontend AwakenPayload {fromState,toState,tool}.
|
||||
const wsCapacityAwaken = "capacity_awaken"
|
||||
|
||||
// BroadcastCapacityAwaken fans a capacity state transition to the Web UI as a
|
||||
// `capacity_awaken` frame (design/14 §5). sessionID is the conversation that
|
||||
// transitioned, so the client targets the correct session rather than treating the
|
||||
// wake event as global. data matches the frontend AwakenPayload {fromState, toState,
|
||||
// tool}; tool may be empty (no single tool was lit, the client then keeps the prior
|
||||
// tool badge).
|
||||
func (s *Server) BroadcastCapacityAwaken(sessionID, fromState, toState, tool string) {
|
||||
s.hub.broadcast(wsEvent{
|
||||
Type: wsCapacityAwaken,
|
||||
SessionID: sessionID,
|
||||
Data: map[string]any{
|
||||
"fromState": fromState,
|
||||
"toState": toState,
|
||||
"tool": tool,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// sourcedLog returns a logger tagged with source "gateway", substituting a
|
||||
// discard logger for nil so Server can always log safely.
|
||||
func sourcedLog(l *slog.Logger) *slog.Logger {
|
||||
|
||||
Reference in new issue
Block a user