Merge branch 'graph-overview/trim-hosts-add-done-total'

This commit is contained in:
Autumn-27 committed 2026-09-08 11:19:23 -04:00
commit dc4645753c
3 files changed
+26 -27

No files matched your search

+6 -18
View File
@@ -103,9 +103,8 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) {
if !ok || len(keywords) != 1 || keywords[0] != keyword {
t.Fatalf("company keywords missing: %#v", scopeRows[0]["company_keywords"])
}
hosts, ok := coverage["hosts"].([]string)
if !ok || !containsString(hosts, domain) {
t.Fatalf("company asset host missing from agent context: %#v", coverage["hosts"])
if hc, _ := coverage["host_count"].(int); hc < 1 {
t.Fatalf("company asset host not counted in agent context: %#v", coverage["host_count"])
}
untested := callReadJSON(t, tools.listUntestedAssets(), `{"type":"root_domain","page":1,"page_size":10}`)
if !strings.Contains(fmt.Sprint(untested), domain) {
@@ -153,9 +152,8 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) {
if !ok {
t.Fatalf("coverage-disabled task lost asset context: %#v", disabledOverview["coverage"])
}
disabledHosts, ok := disabledCoverage["hosts"].([]string)
if !ok || !containsString(disabledHosts, domain) {
t.Fatalf("coverage-disabled task lost company asset host: %#v", disabledCoverage["hosts"])
if hc, _ := disabledCoverage["host_count"].(int); hc < 1 {
t.Fatalf("coverage-disabled task lost company asset host count: %#v", disabledCoverage["host_count"])
}
if _, exists := disabledCoverage["denominator"]; exists {
t.Fatalf("coverage-disabled task unexpectedly exposed metrics: %#v", disabledCoverage)
@@ -176,15 +174,6 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) {
}
}
func containsString(items []string, target string) bool {
for _, item := range items {
if item == target {
return true
}
}
return false
}
func companiesName(t *testing.T, companies *db.CompanyStore, companyID int64) string {
t.Helper()
company, err := companies.GetCompany(companyID)
@@ -321,9 +310,8 @@ func TestBlackboardToolsReadDirectSources(t *testing.T) {
if !ok {
t.Fatalf("coverage missing from overview: %#v", overview["coverage"])
}
hosts, ok := coverage["hosts"].([]string)
if !ok || len(hosts) != 1 || hosts[0] != host || coverage["host_count"] != 1 {
t.Fatalf("inherited host context missing: %#v", coverage)
if coverage["host_count"] != 1 {
t.Fatalf("inherited host context missing (want host_count=1): %#v", coverage)
}
facts := callReadJSON(t, tools.listFacts(), `{}`).(map[string]any)["facts"].([]any)
+8 -9
View File
@@ -412,6 +412,12 @@ func (t *ToolSet) graphOverviewData() map[string]any {
}
out["running_intents"] = compactIntents(running, parentsOf, yieldsOf)
out["recent_done_intents"] = compactIntents(recentDone, parentsOf, yieldsOf)
// done_intents_total:已结束意图(done/blocked/exhausted)总数,与 recent_done_intents
// 平行命名——后者只是它的最新窗口(≤15)截断视图。两键并排即自描述:"看到的是 N/总数",
// 让 planner 去重时别把"没显示"当成"没派过",无需在提示词里另行解释。
if dt, err := t.ts.CountFinishedIntents(); err == nil {
out["done_intents_total"] = dt
}
out["frontier_open"] = len(fr)
// findings (confirmed vulns) and facts (worker exploration results) are
// now distinct node kinds. recent_facts surfaces fact summaries (esp.
@@ -532,16 +538,9 @@ func (t *ToolSet) graphOverviewData() map[string]any {
m["scope"] = scope
}
if hosts, err := t.as.HostsByTaskWithSources(t.taskID); err == nil {
const hostContextLimit = 500
visible := hosts
if len(visible) > hostContextLimit {
visible = visible[:hostContextLimit]
}
m["hosts"] = visible
// 只给主机总数,不再把 host 列表平铺进 graph_overview(大范围任务里那是每轮
// 都重复携带的大量字符串,对规划决策价值有限);具体主机按需 list_assets 查。
m["host_count"] = len(hosts)
if len(visible) < len(hosts) {
m["hosts_truncated"] = true
}
}
if len(m) > 0 {
out["coverage"] = m
+12
View File
@@ -706,6 +706,18 @@ func (s *ExplorationStore) countByKindFiltered(kind, q string) (int, error) {
return n, err
}
// CountFinishedIntents counts this exploration's intents in a terminal explored
// state (done/blocked/exhausted) — graph_overview's done_intents_total, so the planner
// knows recent_done_intents (capped at a recent window) is a truncated view and
// stays cautious about "already tried" dedup. Excludes 'stopped' (killed/deleted),
// matching exactly what recent_done_intents surfaces.
func (s *ExplorationStore) CountFinishedIntents() (int, error) {
var n int
err := s.db.QueryRow(`SELECT COUNT(*) FROM exploration_nodes
WHERE exploration_id=$1 AND kind='intent' AND state IN ('done','blocked','exhausted')`, s.expID).Scan(&n)
return n, err
}
// GetNode returns one node of this exploration by id (nil, nil if not found).
func (s *ExplorationStore) GetNode(id int64) (*Node, error) {
n, err := scanNode(s.db.QueryRow(`SELECT `+nodeCols+` FROM exploration_nodes WHERE id=$1 AND exploration_id=$2`, id, s.expID))