feat: implement append_note_content function and update related tests
This commit is contained in:
@@ -738,7 +738,7 @@ def _parse_name_status_z(raw_output: bytes) -> list[DiffEntry]:
|
|||||||
# Backward-compat fallback if output is tab-delimited unexpectedly.
|
# Backward-compat fallback if output is tab-delimited unexpectedly.
|
||||||
status_fallback, has_tab, first_path = token.partition("\t")
|
status_fallback, has_tab, first_path = token.partition("\t")
|
||||||
if not has_tab:
|
if not has_tab:
|
||||||
break
|
break
|
||||||
fallback_code = status_fallback[:1]
|
fallback_code = status_fallback[:1]
|
||||||
fallback_similarity: int | None = None
|
fallback_similarity: int | None = None
|
||||||
if len(status_fallback) > 1 and status_fallback[1:].isdigit():
|
if len(status_fallback) > 1 and status_fallback[1:].isdigit():
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class LLM:
|
|||||||
ordered_skills.append(f"scan_modes/{self.config.scan_mode}")
|
ordered_skills.append(f"scan_modes/{self.config.scan_mode}")
|
||||||
if self.config.is_whitebox:
|
if self.config.is_whitebox:
|
||||||
ordered_skills.append("coordination/source_aware_whitebox")
|
ordered_skills.append("coordination/source_aware_whitebox")
|
||||||
ordered_skills.append("source_aware_sast")
|
ordered_skills.append("custom/source_aware_sast")
|
||||||
|
|
||||||
deduped: list[str] = []
|
deduped: list[str] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
|
|||||||
@@ -36,10 +36,13 @@ mkdir -p "$ART"
|
|||||||
|
|
||||||
semgrep scan --config p/default --config p/golang --config p/secrets \
|
semgrep scan --config p/default --config p/golang --config p/secrets \
|
||||||
--metrics=off --json --output "$ART/semgrep.json" .
|
--metrics=off --json --output "$ART/semgrep.json" .
|
||||||
sg scan --json . > "$ART/ast-grep.json"
|
# Ruleless AST pass (works without sgconfig.yml/rules project setup)
|
||||||
|
sg run --pattern '$F($$$ARGS)' --json=stream . > "$ART/ast-grep.json" 2> "$ART/ast-grep.log" || true
|
||||||
gitleaks detect --source . --report-format json --report-path "$ART/gitleaks.json" || true
|
gitleaks detect --source . --report-format json --report-path "$ART/gitleaks.json" || true
|
||||||
trufflehog filesystem --no-update --json --no-verification . > "$ART/trufflehog.json" || true
|
trufflehog filesystem --no-update --json --no-verification . > "$ART/trufflehog.json" || true
|
||||||
trivy fs --format json --output "$ART/trivy-fs.json" .
|
# Keep trivy focused on vuln/misconfig (secrets already covered above) and increase timeout for large repos
|
||||||
|
trivy fs --scanners vuln,misconfig --timeout 30m --offline-scan \
|
||||||
|
--format json --output "$ART/trivy-fs.json" . || true
|
||||||
```
|
```
|
||||||
|
|
||||||
If one tool is skipped or fails, record that in the shared wiki note along with the reason.
|
If one tool is skipped or fails, record that in the shared wiki note along with the reason.
|
||||||
@@ -64,7 +67,8 @@ If diff scope is active, restrict to changed files first, then expand only when
|
|||||||
Use `sg` for structure-aware code hunting:
|
Use `sg` for structure-aware code hunting:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sg scan --json . > /workspace/.strix-source-aware/ast-grep.json
|
# Ruleless one-off structural pass (no sgconfig.yml required)
|
||||||
|
sg run --pattern '$F($$$ARGS)' --json=stream . > /workspace/.strix-source-aware/ast-grep.json 2> /workspace/.strix-source-aware/ast-grep.log || true
|
||||||
```
|
```
|
||||||
|
|
||||||
Target high-value patterns such as:
|
Target high-value patterns such as:
|
||||||
@@ -95,7 +99,8 @@ trufflehog filesystem --json . > /workspace/.strix-source-aware/trufflehog.json
|
|||||||
Run repository-wide dependency and config checks:
|
Run repository-wide dependency and config checks:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
trivy fs --format json --output /workspace/.strix-source-aware/trivy-fs.json .
|
trivy fs --scanners vuln,misconfig --timeout 30m --offline-scan \
|
||||||
|
--format json --output /workspace/.strix-source-aware/trivy-fs.json . || true
|
||||||
```
|
```
|
||||||
|
|
||||||
## Converting Static Signals Into Exploits
|
## Converting Static Signals Into Exploits
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ def _append_wiki_update_on_finish(
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from strix.tools.notes.notes_actions import update_note
|
from strix.tools.notes.notes_actions import append_note_content
|
||||||
|
|
||||||
note = _load_primary_wiki_note(agent_state)
|
note = _load_primary_wiki_note(agent_state)
|
||||||
if not note:
|
if not note:
|
||||||
@@ -133,7 +133,6 @@ def _append_wiki_update_on_finish(
|
|||||||
if not isinstance(note_id, str) or not note_id:
|
if not isinstance(note_id, str) or not note_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
existing_content = str(note.get("content") or "")
|
|
||||||
timestamp = datetime.now(UTC).isoformat()
|
timestamp = datetime.now(UTC).isoformat()
|
||||||
summary = " ".join(str(result_summary).split())
|
summary = " ".join(str(result_summary).split())
|
||||||
if len(summary) > 1200:
|
if len(summary) > 1200:
|
||||||
@@ -151,8 +150,7 @@ def _append_wiki_update_on_finish(
|
|||||||
"Recommendations:\n"
|
"Recommendations:\n"
|
||||||
f"{recommendation_lines}\n"
|
f"{recommendation_lines}\n"
|
||||||
)
|
)
|
||||||
updated_content = f"{existing_content.rstrip()}{delta}"
|
append_note_content(note_id=note_id, delta=delta)
|
||||||
update_note(note_id=note_id, content=updated_content)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Best-effort update; never block agent completion on note persistence.
|
# Best-effort update; never block agent completion on note persistence.
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -364,6 +364,26 @@ def get_note(note_id: str) -> dict[str, Any]:
|
|||||||
return {"success": True, "note": note_with_id}
|
return {"success": True, "note": note_with_id}
|
||||||
|
|
||||||
|
|
||||||
|
def append_note_content(note_id: str, delta: str) -> dict[str, Any]:
|
||||||
|
with _notes_lock:
|
||||||
|
try:
|
||||||
|
_ensure_notes_loaded()
|
||||||
|
|
||||||
|
if note_id not in _notes_storage:
|
||||||
|
return {"success": False, "error": f"Note with ID '{note_id}' not found"}
|
||||||
|
|
||||||
|
if not isinstance(delta, str):
|
||||||
|
return {"success": False, "error": "Delta must be a string"}
|
||||||
|
|
||||||
|
note = _notes_storage[note_id]
|
||||||
|
existing_content = str(note.get("content") or "")
|
||||||
|
updated_content = f"{existing_content.rstrip()}{delta}"
|
||||||
|
return update_note(note_id=note_id, content=updated_content)
|
||||||
|
|
||||||
|
except (ValueError, TypeError) as e:
|
||||||
|
return {"success": False, "error": f"Failed to append note content: {e}"}
|
||||||
|
|
||||||
|
|
||||||
@register_tool(sandbox_execution=False)
|
@register_tool(sandbox_execution=False)
|
||||||
def update_note(
|
def update_note(
|
||||||
note_id: str,
|
note_id: str,
|
||||||
|
|||||||
@@ -164,14 +164,14 @@ def test_agent_finish_appends_wiki_update_for_whitebox(monkeypatch) -> None:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def fake_update_note(note_id: str, content: str):
|
def fake_append_note_content(note_id: str, delta: str):
|
||||||
captured["note_id"] = note_id
|
captured["note_id"] = note_id
|
||||||
captured["content"] = content
|
captured["delta"] = delta
|
||||||
return {"success": True, "note_id": note_id}
|
return {"success": True, "note_id": note_id}
|
||||||
|
|
||||||
monkeypatch.setattr("strix.tools.notes.notes_actions.list_notes", fake_list_notes)
|
monkeypatch.setattr("strix.tools.notes.notes_actions.list_notes", fake_list_notes)
|
||||||
monkeypatch.setattr("strix.tools.notes.notes_actions.get_note", fake_get_note)
|
monkeypatch.setattr("strix.tools.notes.notes_actions.get_note", fake_get_note)
|
||||||
monkeypatch.setattr("strix.tools.notes.notes_actions.update_note", fake_update_note)
|
monkeypatch.setattr("strix.tools.notes.notes_actions.append_note_content", fake_append_note_content)
|
||||||
|
|
||||||
state = SimpleNamespace(agent_id=child_id, parent_id=parent_id)
|
state = SimpleNamespace(agent_id=child_id, parent_id=parent_id)
|
||||||
result = agents_graph_actions.agent_finish(
|
result = agents_graph_actions.agent_finish(
|
||||||
@@ -185,8 +185,8 @@ def test_agent_finish_appends_wiki_update_for_whitebox(monkeypatch) -> None:
|
|||||||
assert result["agent_completed"] is True
|
assert result["agent_completed"] is True
|
||||||
assert captured_get["note_id"] == "wiki-note-1"
|
assert captured_get["note_id"] == "wiki-note-1"
|
||||||
assert captured["note_id"] == "wiki-note-1"
|
assert captured["note_id"] == "wiki-note-1"
|
||||||
assert "Agent Update: Child" in captured["content"]
|
assert "Agent Update: Child" in captured["delta"]
|
||||||
assert "AST pass completed" in captured["content"]
|
assert "AST pass completed" in captured["delta"]
|
||||||
|
|
||||||
|
|
||||||
def test_run_agent_in_thread_injects_shared_wiki_context_in_whitebox(monkeypatch) -> None:
|
def test_run_agent_in_thread_injects_shared_wiki_context_in_whitebox(monkeypatch) -> None:
|
||||||
|
|||||||
@@ -137,3 +137,36 @@ def test_get_note_returns_full_note(tmp_path: Path, monkeypatch) -> None:
|
|||||||
finally:
|
finally:
|
||||||
_reset_notes_state()
|
_reset_notes_state()
|
||||||
set_global_tracer(previous_tracer) # type: ignore[arg-type]
|
set_global_tracer(previous_tracer) # type: ignore[arg-type]
|
||||||
|
|
||||||
|
|
||||||
|
def test_append_note_content_appends_delta(tmp_path: Path, monkeypatch) -> None:
|
||||||
|
monkeypatch.chdir(tmp_path)
|
||||||
|
_reset_notes_state()
|
||||||
|
|
||||||
|
previous_tracer = get_global_tracer()
|
||||||
|
tracer = Tracer("append-note-run")
|
||||||
|
set_global_tracer(tracer)
|
||||||
|
|
||||||
|
try:
|
||||||
|
created = notes_actions.create_note(
|
||||||
|
title="Repo wiki",
|
||||||
|
content="base",
|
||||||
|
category="wiki",
|
||||||
|
tags=["repo:demo"],
|
||||||
|
)
|
||||||
|
assert created["success"] is True
|
||||||
|
note_id = created["note_id"]
|
||||||
|
assert isinstance(note_id, str)
|
||||||
|
|
||||||
|
appended = notes_actions.append_note_content(
|
||||||
|
note_id=note_id,
|
||||||
|
delta="\n\n## Agent Update: worker\nSummary: done",
|
||||||
|
)
|
||||||
|
assert appended["success"] is True
|
||||||
|
|
||||||
|
loaded = notes_actions.get_note(note_id=note_id)
|
||||||
|
assert loaded["success"] is True
|
||||||
|
assert loaded["note"]["content"] == "base\n\n## Agent Update: worker\nSummary: done"
|
||||||
|
finally:
|
||||||
|
_reset_notes_state()
|
||||||
|
set_global_tracer(previous_tracer) # type: ignore[arg-type]
|
||||||
|
|||||||
Reference in New Issue
Block a user