diff --git a/docs/bug-sheet-review.md b/docs/bug-sheet-review.md index b17df174..9edcbc66 100644 --- a/docs/bug-sheet-review.md +++ b/docs/bug-sheet-review.md @@ -158,3 +158,11 @@ HTTP 原共享监听器凭据无法证明具体会话身份。现生成客户端 ### 离线命令收尾及串行派发(表格第 37 行) 看门狗原仅标记会话离线,已发送命令无错误和完成时间。现对 dead 会话已发送 exec/shell 命令,超过客户端既有 timeout_seconds(默认 60 秒)加最小宽限期后写失败、完成时间及远端终止尚未确认说明;不推断远端进程已经停止。显式长超时、queued、成功/取消及非命令任务不受影响,迟到成功不能覆盖失败终态。TCP 串行客户端原一次领取多条并提前标记 sent,现同会话有 sent/running 任务时不再派发,一次领取一条,后续任务保持 queued。回归覆盖离线默认超时、300 秒显式期限、等待任务、正常终态、非命令任务、迟到成功与串行下一任务派发。C2 包测试通过。 + +### 历史工作流进度核对结论(表格第 29 行) + +当前 workflowAgentProgress 转发节点关联的 iteration/tool_call/tool_result;新增 handler 集成测试确认 workflow_node_start、iteration、tool_call、tool_result、workflow_node_result 均落库并发送至流,故现有路径未复现全程零进度。原截图与描述没有可重放运行日志及配置,不能断言历史现象不存在。已有工具节点期限未执行的缺陷已经修复;Agent 节点没有独立无进度超时配置,不凭历史耗时增加新的监控机制或任意期限。默认提示持续扩展探测的冲突另已修复。取消审批场景通过真实 pending → cancel → 历史与 done 的集成测试,第 24 行同步结论。 + +### 最终整仓验证 + +当前修复集合 go test ./... 全部通过。前端 node --test web/static/js/*.test.cjs 共 217 项,215 项通过;2 项 HITL UI 源码正则断言失败(刷新默认审批配置、服务端默认审批人更新),相关实现和测试相对基线未改动,另在原基线归档核验同项失败。新增及受影响场景针对性测试通过。未执行真实模型或真实目标探测,不将提示改进解释为所有模型行为保证。 diff --git a/docs/bug-sheet-status.md b/docs/bug-sheet-status.md index 9d9b4614..48b311e8 100644 --- a/docs/bug-sheet-status.md +++ b/docs/bug-sheet-status.md @@ -26,12 +26,12 @@ | 21 | 19 | webshell 连接口令明文 | 已修复并有针对性验证 | | 22 | 20 | 不知道这个是啥,应该是样式问题 | 与第 25 行同一 WebShell 路径/假文件问题,已修复 | | 23 | 21 | 创建监听器 必填字段不清楚,交互上不知道是哪个必填 | 已修复必填标识、逐字段提示及端口严格校验;前后端回归通过 | -| 24 | 22 | 已经取消的任务,结束之后标签会变成成功,好像没有取消成功 | C2 与 Agent 取消终态保护已修复,done 展示保留真实状态;审批中断场景继续核对 | +| 24 | 22 | 已经取消的任务,结束之后标签会变成成功,好像没有取消成功 | C2 与 Agent 取消终态保护已修复;done 展示保留真实状态,真实审批等待取消集成测试通过 | | 25 | 23 | 【WebShell 文件管理】目录树用相对路径导致点进去必报错,且报错文本被解析成假文件条目步骤 | 已修复并有针对性验证 | | 26 | 24 | 工作流 id 由客户端随便填、零校验 | 已修复新 ID 格式/长度、重复创建及编辑改 ID 校验 | | 27 | 25 | 样式问题 | 已修复:连续中文换行、普通节点随标签增高、条件节点文字宽度限制 | | 28 | 26 | 导入相同的工作流id的包,还能点确认,并且按钮显示导入完成。拦截的交互可以优化 | 已修复确认及结果按钮语义;跳过/保留明确显示,14 项包导入测试通过 | -| 29 | 27 | Agent 节点 16 分 43 秒 / 118 次工具调用,全程零进度、零超时 | 待核对 | +| 29 | 27 | Agent 节点 16 分 43 秒 / 118 次工具调用,全程零进度、零超时 | 当前零进度未复现:节点/迭代/工具事件转发、落库和流发送测试通过;已有工具超时未执行已修复;历史运行缺日志,Agent 独立无进度超时未实现,不扩展能力 | | 30 | 28 | 工具节点在会话容器内执行 nmap 默认参数必失败 | 当前仓库未实现,不扩展能力 | | 31 | 29 | 参数模板未解析被原样下发,流程「成功」但结论与事实相反 | 已修复 JSON 参数模板展开,嵌套/转义及实际工具运行回归通过 | | 32 | 30 | 角色名零校验:含 /、.. 的名字能创建,但之后删不掉 | 已修复并有针对性验证 | @@ -50,7 +50,7 @@ | 45 | — | 无工具节点的工作流成功完成后,被误判为缺少执行证据 | 已修复无工具工作流终结与审批拒绝原因,实际运行回归通过 | | 46 | — | Agent ID 可重复- 两个不同文件可使用同一个 ID。 | 已修复并有针对性验证 | | 47 | — | nmap 参数缺少前置校验 | 已修复 scan_type 拆开 --script 与脚本名的拼接错误;实际 YAML 参数回归通过 | -| 48 | — | 对单个资产发起“只读连通性与服务指纹核验”后,Agent 已通过 nmap 和 HTTP 指纹工 | 已修复默认提示范围冲突和自定义模板缺少写回提示;写回工具测试通过,真实模型及时收尾仍待核对 | +| 48 | — | 对单个资产发起“只读连通性与服务指纹核验”后,Agent 已通过 nmap 和 HTTP 指纹工 | 已修复默认提示范围冲突和自定义模板缺少写回提示;写回工具测试通过;未运行真实模型,及时收尾行为保留验证限制 | | 49 | — | C2 会话终端执行没有换行结尾的命令时,系统内部用于获取当前目录的 pwd 输出会与用户命令输出 | 已修复,Unix Shell 回归通过 | | 50 | — | 监听器编辑后点击保存没有反应 | 已修复弹窗事件冒泡阻断;实际委托保存和失败提示回归通过 | | 51 | — | 创建 Malleable Profile 时 UA、响应头和 Jitter 配置被静默丢弃 | 已修复并有针对性验证 | diff --git a/internal/handler/workflow_progress_delivery_test.go b/internal/handler/workflow_progress_delivery_test.go new file mode 100644 index 00000000..70a7b30a --- /dev/null +++ b/internal/handler/workflow_progress_delivery_test.go @@ -0,0 +1,47 @@ +package handler + +import ( + "context" + "cyberstrike-ai/internal/database" + "go.uber.org/zap" + "path/filepath" + "testing" +) + +func TestWorkflowActivityIsPersistedAndDeliveredToStream(t *testing.T) { + db, err := database.NewDB(filepath.Join(t.TempDir(), "progress.db"), zap.NewNop()) + if err != nil { + t.Fatal(err) + } + defer db.Close() + conversation, err := db.CreateConversation("test-only-workflow", database.ConversationCreateMeta{}) + if err != nil { + t.Fatal(err) + } + message, err := db.AddMessage(conversation.ID, "assistant", "", nil) + if err != nil { + t.Fatal(err) + } + handler := &AgentHandler{db: db, logger: zap.NewNop(), tasks: NewAgentTaskManager()} + streamed := map[string]int{} + ctx, cancel := context.WithCancelCause(context.Background()) + defer cancel(nil) + callback := handler.createProgressCallback(ctx, cancel, conversation.ID, message.ID, func(kind, message string, data interface{}) { streamed[kind]++ }) + kinds := []string{"workflow_node_start", "iteration", "tool_call", "tool_result", "workflow_node_result"} + for _, kind := range kinds { + callback(kind, "test-only-progress", map[string]interface{}{"workflowNodeId": "agent-1", "workflowRunId": "test-only-run", "nodeId": "agent-1", "nodeType": "agent", "iteration": 1, "toolCallId": "test-only-call", "tool": "test-only-tool", "arguments": map[string]interface{}{}, "result": "test-only-result", "success": true}) + } + details, err := db.GetProcessDetails(message.ID) + if err != nil { + t.Fatal(err) + } + persisted := map[string]int{} + for _, detail := range details { + persisted[detail.EventType]++ + } + for _, kind := range kinds { + if streamed[kind] != 1 || persisted[kind] != 1 { + t.Fatalf("%s delivered=%d persisted=%d", kind, streamed[kind], persisted[kind]) + } + } +}