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 { if !ok || len(keywords) != 1 || keywords[0] != keyword {
t.Fatalf("company keywords missing: %#v", scopeRows[0]["company_keywords"]) t.Fatalf("company keywords missing: %#v", scopeRows[0]["company_keywords"])
} }
hosts, ok := coverage["hosts"].([]string) if hc, _ := coverage["host_count"].(int); hc < 1 {
if !ok || !containsString(hosts, domain) { t.Fatalf("company asset host not counted in agent context: %#v", coverage["host_count"])
t.Fatalf("company asset host missing from agent context: %#v", coverage["hosts"])
} }
untested := callReadJSON(t, tools.listUntestedAssets(), `{"type":"root_domain","page":1,"page_size":10}`) untested := callReadJSON(t, tools.listUntestedAssets(), `{"type":"root_domain","page":1,"page_size":10}`)
if !strings.Contains(fmt.Sprint(untested), domain) { if !strings.Contains(fmt.Sprint(untested), domain) {
@@ -153,9 +152,8 @@ func TestGraphOverviewExpandsAssociatedCompanyScope(t *testing.T) {
if !ok { if !ok {
t.Fatalf("coverage-disabled task lost asset context: %#v", disabledOverview["coverage"]) t.Fatalf("coverage-disabled task lost asset context: %#v", disabledOverview["coverage"])
} }
disabledHosts, ok := disabledCoverage["hosts"].([]string) if hc, _ := disabledCoverage["host_count"].(int); hc < 1 {
if !ok || !containsString(disabledHosts, domain) { t.Fatalf("coverage-disabled task lost company asset host count: %#v", disabledCoverage["host_count"])
t.Fatalf("coverage-disabled task lost company asset host: %#v", disabledCoverage["hosts"])
} }
if _, exists := disabledCoverage["denominator"]; exists { if _, exists := disabledCoverage["denominator"]; exists {
t.Fatalf("coverage-disabled task unexpectedly exposed metrics: %#v", disabledCoverage) 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 { func companiesName(t *testing.T, companies *db.CompanyStore, companyID int64) string {
t.Helper() t.Helper()
company, err := companies.GetCompany(companyID) company, err := companies.GetCompany(companyID)
@@ -321,9 +310,8 @@ func TestBlackboardToolsReadDirectSources(t *testing.T) {
if !ok { if !ok {
t.Fatalf("coverage missing from overview: %#v", overview["coverage"]) t.Fatalf("coverage missing from overview: %#v", overview["coverage"])
} }
hosts, ok := coverage["hosts"].([]string) if coverage["host_count"] != 1 {
if !ok || len(hosts) != 1 || hosts[0] != host || coverage["host_count"] != 1 { t.Fatalf("inherited host context missing (want host_count=1): %#v", coverage)
t.Fatalf("inherited host context missing: %#v", coverage)
} }
facts := callReadJSON(t, tools.listFacts(), `{}`).(map[string]any)["facts"].([]any) 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["running_intents"] = compactIntents(running, parentsOf, yieldsOf)
out["recent_done_intents"] = compactIntents(recentDone, 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) out["frontier_open"] = len(fr)
// findings (confirmed vulns) and facts (worker exploration results) are // findings (confirmed vulns) and facts (worker exploration results) are
// now distinct node kinds. recent_facts surfaces fact summaries (esp. // now distinct node kinds. recent_facts surfaces fact summaries (esp.
@@ -532,16 +538,9 @@ func (t *ToolSet) graphOverviewData() map[string]any {
m["scope"] = scope m["scope"] = scope
} }
if hosts, err := t.as.HostsByTaskWithSources(t.taskID); err == nil { if hosts, err := t.as.HostsByTaskWithSources(t.taskID); err == nil {
const hostContextLimit = 500 // 只给主机总数,不再把 host 列表平铺进 graph_overview(大范围任务里那是每轮
visible := hosts // 都重复携带的大量字符串,对规划决策价值有限);具体主机按需 list_assets 查。
if len(visible) > hostContextLimit {
visible = visible[:hostContextLimit]
}
m["hosts"] = visible
m["host_count"] = len(hosts) m["host_count"] = len(hosts)
if len(visible) < len(hosts) {
m["hosts_truncated"] = true
}
} }
if len(m) > 0 { if len(m) > 0 {
out["coverage"] = m out["coverage"] = m
+12
View File
@@ -706,6 +706,18 @@ func (s *ExplorationStore) countByKindFiltered(kind, q string) (int, error) {
return n, err 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). // GetNode returns one node of this exploration by id (nil, nil if not found).
func (s *ExplorationStore) GetNode(id int64) (*Node, error) { 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)) n, err := scanNode(s.db.QueryRow(`SELECT `+nodeCols+` FROM exploration_nodes WHERE id=$1 AND exploration_id=$2`, id, s.expID))