This commit is contained in:
Rio Griya Putra 2026-10-14 10:49:13 -05:00 • committed by GitHub
commit 4feb1356d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 71 additions and 13 deletions

View file

@ -364,8 +364,11 @@ def child_initial_input(
)
parts.append(
f"You are agent {name} ({child_id}); your parent is {parent_id}. "
"Maintain your own identity. Call agent_finish when your task "
"is complete.",
"Maintain your own identity. If authorized to report findings, file any "
"confirmed vulnerabilities using create_vulnerability_report (or "
"create_dependency_report) BEFORE calling agent_finish (or ensure your "
"reporting specialist has filed them) — passing findings to agent_finish "
"does not file reports. Call agent_finish when your task is complete.",
)
parts.append(task)
return [{"role": "user", "content": "\n\n".join(parts)}]

View file

@ -101,6 +101,12 @@ def _render_completion_report(
lines.extend(_render_filed_report(r) for r in filed_reports)
else:
lines.append("- (none)")
if findings:
lines.append(
" [NOTICE: This agent noted narrative findings above with 0 formal "
"reports filed. If any finding represents an unfiled vulnerability, ensure it "
"is reported via create_vulnerability_report.]"
)
lines.append("")
lines.append("Open items (unresolved, need follow-up):")
if open_items:
@ -693,6 +699,7 @@ async def agent_finish(
filed_reports = _filed_reports_by(me)
filed_report_ids = [str(r.get("id")) for r in filed_reports]
unfiled_findings = bool((findings or []) and not filed_report_ids)
parent_notified = False
if report_to_parent and await coordinator.claim_parent_notice(me):
@ -718,6 +725,7 @@ async def agent_finish(
"type": "completion",
"priority": "high",
"filed_report_ids": filed_report_ids,
"unfiled_findings": unfiled_findings,
},
)
parent_notified = True
@ -735,19 +743,34 @@ async def agent_finish(
len(filed_report_ids),
parent_notified,
)
if unfiled_findings:
logger.info(
"agent_finish: %s reported %d narrative finding(s) with 0 reports filed",
me,
len(findings or []),
)
response_payload: dict[str, Any] = {
"success": True,
"agent_completed": True,
"parent_notified": parent_notified,
"agent_id": me,
"summary": result_summary,
"filed_report_ids": filed_report_ids,
"findings_count": len(findings or []),
"open_items_count": len(open_items or []),
"has_recommendations": bool(final_recommendations),
}
if unfiled_findings:
response_payload["notice"] = (
f"Reported {len(findings or [])} finding(s)/observation(s) with 0 reports filed. "
"Narrative findings in agent_finish do not register vulnerabilities in the scan "
"report. If any finding represents an unfiled vulnerability, ensure it is filed "
"via create_vulnerability_report."
)
return json.dumps(
{
"success": True,
"agent_completed": True,
"parent_notified": parent_notified,
"agent_id": me,
"summary": result_summary,
"filed_report_ids": filed_report_ids,
"findings_count": len(findings or []),
"open_items_count": len(open_items or []),
"has_recommendations": bool(final_recommendations),
},
response_payload,
ensure_ascii=False,
default=str,
)

View file

@ -256,3 +256,33 @@ async def test_agent_finish_without_report_state_still_completes() -> None:
assert result["success"] is True
assert result["filed_report_ids"] == []
@pytest.mark.asyncio
@pytest.mark.usefixtures("report_state")
async def test_agent_finish_warns_when_findings_reported_without_filing() -> None:
coordinator = await _graph(interactive=False)
result = await _call(
agent_finish,
coordinator,
"child",
{
"result_summary": "discovered injection",
"findings": ["SQL injection on /login.php"],
},
parent_id="root",
)
assert result["success"] is True
assert result["filed_report_ids"] == []
assert "notice" in result
assert "Reported 1 finding(s)/observation(s) with 0 reports filed" in result["notice"]
delivered = coordinator.runtimes["root"].mailbox
assert len(delivered) == 1
assert delivered[0]["unfiled_findings"] is True
body = delivered[0]["content"]
assert "0 formal reports filed" in body
assert "create_vulnerability_report" in body

View file

@ -35,6 +35,7 @@ def test_child_initial_input_single_message_without_history() -> None:
content = result[0]["content"]
assert "agent scout (agent-2)" in content
assert "Audit the login flow." in content
assert "create_vulnerability_report" in content
assert "Inherited context" not in content
@ -49,6 +50,7 @@ def test_child_initial_input_single_message_with_history() -> None:
assert "previous work" in content
assert "agent scout (agent-2)" in content
assert "Audit the login flow." in content
assert "create_vulnerability_report" in content
@pytest.mark.parametrize(