Files
usestrix__strix/strix/tools/notes/tools.py
T

495 lines
16 KiB
Python
Raw Normal View History

Strip narrative comments and module/helper docstrings Five rounds of sweep across the tree. Net ~544 lines removed. Removed: - Section-divider banners and one-line section labels (# Display utilities, # ----- list_requests -----, # CVSS breakdown, etc.). - Module-level prose docstrings on internal modules. Kept one-line summaries; trimmed multi-paragraph narration about SDK/Strix responsibility splits, cache strategies, three-source precedence. - Internal-helper docstrings that just restate the function name — caido_api helpers (caido_url, get_client, view_request, etc.), settings-class one-liners (LLMSettings, RuntimeSettings, ...), UI helper docstrings. - Args/Returns blocks on non-LLM-facing internal helpers (build_strix_agent, render_system_prompt, create_or_reuse, bootstrap_caido) — kept only the genuinely non-obvious params. - Internal-history phrasing — "Mirrors main-branch shape", "pre-SDK harness", "previous lookup matched no attribute". - Narrative comments inside function bodies that explained what the next line does, design rationale obvious from the surrounding code, or "we used to..." asides. - Trailing periods on every error-string literal across the tool tree. - Duplicated roundtripTime quirk comment (kept the LLM-facing copy in tools/proxy/tools.py). Kept (every one names an upstream bug, vendored-code provenance, or non-obvious data quirk): - core/runner.py: SDK replay-with-empty-initial-input + on_agent_end lifecycle gap. - runtime/docker_client.py: VERBATIM COPY block of the upstream _create_container body, pinned to SDK v0.14.6. - runtime/session_manager.py: NO_PROXY for agent-browser CDP loopback. - tools/proxy/caido_api.py: generated-pydantic Request.raw quirk, replay double-history pitfall. - tools/proxy/tools.py: Caido roundtripTime=0 quirk for proxy captures. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:02:40 -07:00
"""Per-run notes storage — mirrored to {state_dir}/notes.json."""
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
from __future__ import annotations
import asyncio
import json
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
import logging
import tempfile
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
import threading
import uuid
from datetime import UTC, datetime
from pathlib import Path
refactor(notes): drop disk persistence + shared-wiki prose The notes tool no longer touches disk. ``_notes_storage`` lives in memory for the lifetime of one scan process, shared across every agent in that process via the existing RLock. Process exit clears the lot — no notes.jsonl event log, no wiki/<slug>.md Markdown rendering, no replay-on-startup hydration. Removed ~10 internal helpers (``_get_run_dir``, ``_get_notes_jsonl_path``, ``_append_note_event``, ``_load_notes_from_jsonl``, ``_ensure_notes_loaded``, ``_persist_wiki_note``, ``_remove_wiki_note``, ``_get_wiki_directory``, ``_get_wiki_note_path``, ``_sanitize_wiki_title``) plus the ``_loaded_notes_run_dir`` module state, ``wiki_filename`` per-note field, and the ``OSError`` branches that only existed for the wiki write path. The ``wiki`` category is preserved as a free-form long-form bucket; it just no longer has any special persistence behaviour. Skill prompts scrubbed of every "shared wiki memory" / "repo wiki" / "append a delta before agent_finish" instruction: ``coordination/source_aware_whitebox.md``, ``custom/source_aware_sast.md``, ``scan_modes/{quick,standard,deep}.md``, plus the WHITE-BOX TESTING block in ``agents/prompts/system_prompt.jinja``. HARNESS_WIKI.md updated to drop the wiki-as-shared-knowledge-base description, the per-run output-tree references to ``notes/notes.jsonl`` and ``wiki/{note_id}-{slug}.md``, and the ``is_whitebox`` toggle prose. Net: -178 LoC in notes/tools.py, -45 LoC across skills/system_prompt and the wiki doc. The notes tool surface (5 ``@function_tool``s) is unchanged for the agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:56:22 -07:00
from typing import Any
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
2026-04-25 15:17:46 -07:00
from agents import RunContextWrapper, function_tool
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
from strix.tools.nullish import clean_optional
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
logger = logging.getLogger(__name__)
_notes_storage: dict[str, dict[str, Any]] = {}
_VALID_NOTE_CATEGORIES = ["general", "findings", "methodology", "questions", "plan", "wiki"]
_notes_lock = threading.RLock()
_DEFAULT_CONTENT_PREVIEW_CHARS = 280
2026-07-03 05:54:44 +03:00
_NOTE_ID_GENERATION_ATTEMPTS = 1024
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
_notes_path: Path | None = None
def _caller_identity(ctx: RunContextWrapper) -> tuple[str | None, str | None]:
"""Return the (agent_id, agent_name) of the agent invoking this tool."""
inner = ctx.context if isinstance(ctx.context, dict) else {}
raw_agent_id = inner.get("agent_id")
agent_id = raw_agent_id if isinstance(raw_agent_id, str) else None
agent_name: str | None = None
coordinator = inner.get("coordinator")
if agent_id is not None and coordinator is not None:
names = getattr(coordinator, "names", {})
if isinstance(names, dict):
raw_agent_name = names.get(agent_id)
agent_name = raw_agent_name if isinstance(raw_agent_name, str) else None
return agent_id, agent_name
2026-07-03 05:54:44 +03:00
def _generate_note_id() -> str | None:
for _ in range(_NOTE_ID_GENERATION_ATTEMPTS):
note_id = uuid.uuid4().hex[:6]
if note_id not in _notes_storage:
return note_id
return None
2026-04-26 15:01:35 -07:00
def hydrate_notes_from_disk(state_dir: Path) -> None:
global _notes_path # noqa: PLW0603
2026-04-26 15:01:35 -07:00
_notes_path = state_dir / "notes.json"
with _notes_lock:
_notes_storage.clear()
if not _notes_path.exists():
return
try:
data = json.loads(_notes_path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
logger.exception(
"notes.json at %s is unreadable; starting with empty notes",
_notes_path,
)
return
if not isinstance(data, dict):
return
_notes_storage.update(
{
nid: note
for nid, note in data.items()
if isinstance(nid, str) and isinstance(note, dict)
}
)
logger.info(
"notes hydrated from %s (%d note(s))",
_notes_path,
len(_notes_storage),
)
def _persist() -> None:
path = _notes_path
if path is None:
return
try:
payload = json.dumps(_notes_storage, ensure_ascii=False, default=str)
path.parent.mkdir(parents=True, exist_ok=True)
with (
_notes_lock,
tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
dir=str(path.parent),
prefix=f".{path.name}.",
suffix=".tmp",
delete=False,
) as tmp,
):
tmp.write(payload)
tmp_path = Path(tmp.name)
tmp_path.replace(path)
except Exception:
logger.exception("notes persist to %s failed", path)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
def _filter_notes(
category: str | None = None,
tags: list[str] | None = None,
search_query: str | None = None,
) -> list[dict[str, Any]]:
category = clean_optional(category)
search_query = clean_optional(search_query)
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
filtered: list[dict[str, Any]] = []
for note_id, note in _notes_storage.items():
if category and note.get("category") != category:
continue
if tags:
note_tags = note.get("tags", [])
if not any(tag in note_tags for tag in tags):
continue
if search_query:
search_lower = search_query.lower()
title_match = search_lower in note.get("title", "").lower()
content_match = search_lower in note.get("content", "").lower()
if not (title_match or content_match):
continue
entry = note.copy()
entry["note_id"] = note_id
filtered.append(entry)
filtered.sort(key=lambda x: x.get("created_at", ""), reverse=True)
return filtered
def _mark_authorship(
entry: dict[str, Any], note: dict[str, Any], caller_agent_id: str | None
) -> dict[str, Any]:
"""Attach the note's author and flag whether the caller wrote it."""
agent_name = note.get("agent_name")
if agent_name:
entry["agent_name"] = agent_name
agent_id = note.get("agent_id")
if agent_id:
entry["agent_id"] = agent_id
if caller_agent_id is not None and agent_id == caller_agent_id:
entry["by_you"] = True
return entry
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
def _to_note_listing_entry(
note: dict[str, Any],
*,
include_content: bool = False,
caller_agent_id: str | None = None,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
) -> dict[str, Any]:
entry = {
"note_id": note.get("note_id"),
"title": note.get("title", ""),
"category": note.get("category", "general"),
"tags": note.get("tags", []),
"created_at": note.get("created_at", ""),
"updated_at": note.get("updated_at", ""),
}
content = str(note.get("content", ""))
if include_content:
entry["content"] = content
elif content:
if len(content) > _DEFAULT_CONTENT_PREVIEW_CHARS:
entry["content_preview"] = f"{content[:_DEFAULT_CONTENT_PREVIEW_CHARS].rstrip()}..."
else:
entry["content_preview"] = content
return _mark_authorship(entry, note, caller_agent_id)
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
refactor(notes): drop disk persistence + shared-wiki prose The notes tool no longer touches disk. ``_notes_storage`` lives in memory for the lifetime of one scan process, shared across every agent in that process via the existing RLock. Process exit clears the lot — no notes.jsonl event log, no wiki/<slug>.md Markdown rendering, no replay-on-startup hydration. Removed ~10 internal helpers (``_get_run_dir``, ``_get_notes_jsonl_path``, ``_append_note_event``, ``_load_notes_from_jsonl``, ``_ensure_notes_loaded``, ``_persist_wiki_note``, ``_remove_wiki_note``, ``_get_wiki_directory``, ``_get_wiki_note_path``, ``_sanitize_wiki_title``) plus the ``_loaded_notes_run_dir`` module state, ``wiki_filename`` per-note field, and the ``OSError`` branches that only existed for the wiki write path. The ``wiki`` category is preserved as a free-form long-form bucket; it just no longer has any special persistence behaviour. Skill prompts scrubbed of every "shared wiki memory" / "repo wiki" / "append a delta before agent_finish" instruction: ``coordination/source_aware_whitebox.md``, ``custom/source_aware_sast.md``, ``scan_modes/{quick,standard,deep}.md``, plus the WHITE-BOX TESTING block in ``agents/prompts/system_prompt.jinja``. HARNESS_WIKI.md updated to drop the wiki-as-shared-knowledge-base description, the per-run output-tree references to ``notes/notes.jsonl`` and ``wiki/{note_id}-{slug}.md``, and the ``is_whitebox`` toggle prose. Net: -178 LoC in notes/tools.py, -45 LoC across skills/system_prompt and the wiki doc. The notes tool surface (5 ``@function_tool``s) is unchanged for the agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:56:22 -07:00
def _create_note_impl(
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
title: str,
content: str,
category: str = "general",
tags: list[str] | None = None,
agent_id: str | None = None,
agent_name: str | None = None,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
) -> dict[str, Any]:
with _notes_lock:
try:
if not title or not title.strip():
return {"success": False, "error": "Title cannot be empty", "note_id": None}
if not content or not content.strip():
return {"success": False, "error": "Content cannot be empty", "note_id": None}
if category not in _VALID_NOTE_CATEGORIES:
return {
"success": False,
"error": (
f"Invalid category. Must be one of: {', '.join(_VALID_NOTE_CATEGORIES)}"
),
"note_id": None,
}
2026-07-03 05:54:44 +03:00
note_id = _generate_note_id()
if note_id is None:
return {
"success": False,
"error": "Failed to generate a unique note ID",
"note_id": None,
}
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
timestamp = datetime.now(UTC).isoformat()
note = {
"title": title.strip(),
"content": content.strip(),
"category": category,
"tags": tags or [],
"created_at": timestamp,
"updated_at": timestamp,
}
if agent_id:
note["agent_id"] = agent_id
if agent_name:
note["agent_name"] = agent_name
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
_notes_storage[note_id] = note
except (ValueError, TypeError) as e:
return {"success": False, "error": f"Failed to create note: {e}", "note_id": None}
else:
_persist()
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
return {
"success": True,
"note_id": note_id,
"message": f"Note '{title}' created successfully",
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"total_count": len(_notes_storage),
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
}
def _list_notes_impl(
category: str | None = None,
tags: list[str] | None = None,
search: str | None = None,
include_content: bool = False,
caller_agent_id: str | None = None,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
) -> dict[str, Any]:
with _notes_lock:
try:
filtered = _filter_notes(category=category, tags=tags, search_query=search)
notes = [
_to_note_listing_entry(
n, include_content=include_content, caller_agent_id=caller_agent_id
)
for n in filtered
]
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
except (ValueError, TypeError) as e:
return {
"success": False,
"error": f"Failed to list notes: {e}",
"notes": [],
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"filtered_count": 0,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
"total_count": 0,
}
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
return {
"success": True,
"notes": notes,
"filtered_count": len(notes),
"total_count": len(_notes_storage),
}
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
def _get_note_impl(note_id: str, caller_agent_id: str | None = None) -> dict[str, Any]:
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
with _notes_lock:
try:
if not note_id or not note_id.strip():
return {"success": False, "error": "Note ID cannot be empty", "note": None}
note = _notes_storage.get(note_id)
if note is None:
return {
"success": False,
"error": f"Note with ID '{note_id}' not found",
"note": None,
}
note_with_id = note.copy()
note_with_id["note_id"] = note_id
_mark_authorship(note_with_id, note, caller_agent_id)
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
except (ValueError, TypeError) as e:
return {"success": False, "error": f"Failed to get note: {e}", "note": None}
else:
return {"success": True, "note": note_with_id}
def _update_note_impl(
note_id: str,
title: str | None = None,
content: str | None = None,
tags: list[str] | None = None,
) -> dict[str, Any]:
with _notes_lock:
try:
if note_id not in _notes_storage:
return {"success": False, "error": f"Note with ID '{note_id}' not found"}
note = _notes_storage[note_id]
if title is not None:
if not title.strip():
return {"success": False, "error": "Title cannot be empty"}
note["title"] = title.strip()
if content is not None:
if not content.strip():
return {"success": False, "error": "Content cannot be empty"}
note["content"] = content.strip()
if tags is not None:
note["tags"] = tags
note["updated_at"] = datetime.now(UTC).isoformat()
except (ValueError, TypeError) as e:
return {"success": False, "error": f"Failed to update note: {e}"}
else:
_persist()
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
return {
"success": True,
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"note_id": note_id,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
"message": f"Note '{note['title']}' updated successfully",
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"total_count": len(_notes_storage),
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
}
def _delete_note_impl(note_id: str) -> dict[str, Any]:
with _notes_lock:
try:
if note_id not in _notes_storage:
return {"success": False, "error": f"Note with ID '{note_id}' not found"}
note = _notes_storage[note_id]
note_title = note["title"]
del _notes_storage[note_id]
except (ValueError, TypeError) as e:
return {"success": False, "error": f"Failed to delete note: {e}"}
else:
_persist()
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
return {
"success": True,
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"note_id": note_id,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
"message": f"Note '{note_title}' deleted successfully",
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"total_count": len(_notes_storage),
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
}
2026-04-25 15:17:46 -07:00
@function_tool(timeout=30)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
async def create_note(
ctx: RunContextWrapper,
title: str,
content: str,
category: str = "general",
tags: list[str] | None = None,
) -> str:
"""Document an observation, finding, methodology step, or research note.
refactor(notes): drop disk persistence + shared-wiki prose The notes tool no longer touches disk. ``_notes_storage`` lives in memory for the lifetime of one scan process, shared across every agent in that process via the existing RLock. Process exit clears the lot — no notes.jsonl event log, no wiki/<slug>.md Markdown rendering, no replay-on-startup hydration. Removed ~10 internal helpers (``_get_run_dir``, ``_get_notes_jsonl_path``, ``_append_note_event``, ``_load_notes_from_jsonl``, ``_ensure_notes_loaded``, ``_persist_wiki_note``, ``_remove_wiki_note``, ``_get_wiki_directory``, ``_get_wiki_note_path``, ``_sanitize_wiki_title``) plus the ``_loaded_notes_run_dir`` module state, ``wiki_filename`` per-note field, and the ``OSError`` branches that only existed for the wiki write path. The ``wiki`` category is preserved as a free-form long-form bucket; it just no longer has any special persistence behaviour. Skill prompts scrubbed of every "shared wiki memory" / "repo wiki" / "append a delta before agent_finish" instruction: ``coordination/source_aware_whitebox.md``, ``custom/source_aware_sast.md``, ``scan_modes/{quick,standard,deep}.md``, plus the WHITE-BOX TESTING block in ``agents/prompts/system_prompt.jinja``. HARNESS_WIKI.md updated to drop the wiki-as-shared-knowledge-base description, the per-run output-tree references to ``notes/notes.jsonl`` and ``wiki/{note_id}-{slug}.md``, and the ``is_whitebox`` toggle prose. Net: -178 LoC in notes/tools.py, -45 LoC across skills/system_prompt and the wiki doc. The notes tool surface (5 ``@function_tool``s) is unchanged for the agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:56:22 -07:00
Notes are visible to every agent in the same scan for the lifetime
of the run; they live in-memory only and are cleared when the
process exits. Each note records the agent that wrote it, so
``list_notes`` / ``get_note`` show the author (``agent_name``) and
flag your own notes with ``by_you``.
For actionable tasks, use ``todo`` instead — notes are for capturing
information, todos are for tracking work.
Categories:
- ``general`` — default, anything that doesn't fit elsewhere.
- ``findings`` — confirmed vulnerabilities or weaknesses (write
these up promptly; you'll cite them when filing reports).
- ``methodology`` — what you tried, what worked, what didn't —
useful for the final scan report.
- ``questions`` — open questions / things to come back to.
- ``plan`` — multi-step plans you want to track.
refactor(notes): drop disk persistence + shared-wiki prose The notes tool no longer touches disk. ``_notes_storage`` lives in memory for the lifetime of one scan process, shared across every agent in that process via the existing RLock. Process exit clears the lot — no notes.jsonl event log, no wiki/<slug>.md Markdown rendering, no replay-on-startup hydration. Removed ~10 internal helpers (``_get_run_dir``, ``_get_notes_jsonl_path``, ``_append_note_event``, ``_load_notes_from_jsonl``, ``_ensure_notes_loaded``, ``_persist_wiki_note``, ``_remove_wiki_note``, ``_get_wiki_directory``, ``_get_wiki_note_path``, ``_sanitize_wiki_title``) plus the ``_loaded_notes_run_dir`` module state, ``wiki_filename`` per-note field, and the ``OSError`` branches that only existed for the wiki write path. The ``wiki`` category is preserved as a free-form long-form bucket; it just no longer has any special persistence behaviour. Skill prompts scrubbed of every "shared wiki memory" / "repo wiki" / "append a delta before agent_finish" instruction: ``coordination/source_aware_whitebox.md``, ``custom/source_aware_sast.md``, ``scan_modes/{quick,standard,deep}.md``, plus the WHITE-BOX TESTING block in ``agents/prompts/system_prompt.jinja``. HARNESS_WIKI.md updated to drop the wiki-as-shared-knowledge-base description, the per-run output-tree references to ``notes/notes.jsonl`` and ``wiki/{note_id}-{slug}.md``, and the ``is_whitebox`` toggle prose. Net: -178 LoC in notes/tools.py, -45 LoC across skills/system_prompt and the wiki doc. The notes tool surface (5 ``@function_tool``s) is unchanged for the agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:56:22 -07:00
- ``wiki`` — long-form repository or target maps.
Tags are free-form (e.g. ``["sqli", "auth", "critical"]``) — useful
for later ``list_notes(tags=...)`` filtering.
Args:
title: Short headline.
content: Full note body. Markdown is preserved.
category: One of the categories above. Default ``"general"``.
tags: Optional free-form tags.
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
"""
agent_id, agent_name = _caller_identity(ctx)
2026-04-25 15:17:46 -07:00
return json.dumps(
await asyncio.to_thread(
_create_note_impl, title, content, category, tags, agent_id, agent_name
),
2026-04-25 15:17:46 -07:00
ensure_ascii=False,
default=str,
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
)
2026-04-25 15:17:46 -07:00
@function_tool(timeout=30)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
async def list_notes(
ctx: RunContextWrapper,
category: str | None = None,
tags: list[str] | None = None,
search: str | None = None,
include_content: bool = False,
) -> str:
"""List existing notes — metadata-first by default.
Filters compose: passing ``category="findings"`` and
``tags=["sqli"]`` returns notes that are *both* in the findings
category AND have at least one of those tags.
By default each entry includes a ``content_preview`` (first 280
chars). Set ``include_content=True`` to get full bodies — useful
when you need to scan many notes; expensive in tokens for large
notes.
Each entry also carries the author (``agent_name``) and, for notes
you wrote yourself, ``by_you: true``.
Args:
category: Filter by category.
tags: Filter to notes that have any of these tags.
search: Substring match against title and content.
include_content: When False (default) entries have a preview;
when True the full ``content`` is included.
"""
caller_agent_id, _ = _caller_identity(ctx)
2026-04-25 15:17:46 -07:00
return json.dumps(
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
await asyncio.to_thread(
_list_notes_impl,
category=category,
tags=tags,
search=search,
include_content=include_content,
caller_agent_id=caller_agent_id,
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
),
2026-04-25 15:17:46 -07:00
ensure_ascii=False,
default=str,
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
)
2026-04-25 15:17:46 -07:00
@function_tool(timeout=30)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
async def get_note(ctx: RunContextWrapper, note_id: str) -> str:
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
"""Fetch one note by its 6-char ID. Returns the full content.
Args:
note_id: Note id from ``create_note`` or a ``list_notes`` entry.
"""
caller_agent_id, _ = _caller_identity(ctx)
2026-04-25 15:17:46 -07:00
return json.dumps(
await asyncio.to_thread(_get_note_impl, note_id, caller_agent_id),
ensure_ascii=False,
default=str,
2026-04-25 15:17:46 -07:00
)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
2026-04-25 15:17:46 -07:00
@function_tool(timeout=30)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
async def update_note(
ctx: RunContextWrapper,
note_id: str,
title: str | None = None,
content: str | None = None,
tags: list[str] | None = None,
) -> str:
"""Update a note's title, content, or tags.
Pass ``None`` for any field you want left unchanged. Replacing
``content`` is a full overwrite — to append, fetch first with
``get_note``, concat, and pass the result.
Args:
Tighten tool surface consistency Four passes of audit-and-patch on the tool surface, condensed. Tool API shape: - Todo tools collapse to a single list-based form (one arg per tool, always a list, no dual-mode validator). Result-field names line up across the family — created_count / updated_count / marked_count / deleted_count, and _mark returns a single "marked" key plus the new status instead of marked_done / marked_pending. - list_notes splits the overloaded total_count into filtered_count (matches) and total_count (grand total), matching list_todos. All three notes mutations now echo total_count and note_id. - finish_scan drops the machine-code error strings; a single human "error" key carries the reason on every failure path. - scope_rules delete echoes a message so the renderer's success branch has something to surface. Failure-key unification: every tool now uses {"success": False, "error": "..."} on failure paths. Touched thinking, web_search, reporting, and finish. Trailing periods on error strings swept clean across the whole tool tree. Tool prompts (docstring re-imports vs main): - create_vulnerability_report re-imports the CWE reference catalog, multi-part fix rules, fix_before/fix_after PR-suggestion mechanics, the COMMON MISTAKES list, the informational-vs-actionable distinction, and file-path examples. - web_search re-imports concrete example queries. - list_sitemap docstring fixed hasDescendants -> has_descendants (the camelCase reference never matched our snake_case schema). - create_agent.skills description "Comma-separated" -> "List of". - factory.py module docstring no longer claims there's no runtime skill-loading tool. agents_graph module docstring lists stop_agent. - system_prompt nudges loading the matching skill before guessing payloads or syntax from memory. TUI: - proxy_renderer was reading stale field names from the pre-SDK schema (requests / total_count / statusCode / matches / showing_lines); now reads entries / page_info / status_code / hits / page+total_lines. Three proxy operations were rendering empty before this. - Idle-pane placeholder text trimmed to "Loading...". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:16:47 -07:00
note_id: Target note's 6-char ID.
title: New title, or ``None`` to keep.
content: New content, or ``None`` to keep.
tags: New tags list, or ``None`` to keep.
"""
2026-04-25 15:17:46 -07:00
return json.dumps(
refactor: inline non-sandbox actions, strip registry, drop schemas Cleanup pass after the migration: #1 Inline ``*_actions.py`` into wrapper ``tool[s].py`` for the non-sandbox tools (think, todo, notes, reporting, web_search, finish_scan). One file per tool family now. Helpers + public function bodies live alongside the ``@strix_tool``-decorated wrappers that call them. For notes, the sync helpers are renamed to ``_create_note_impl`` / ``_list_notes_impl`` / etc. so the public names ``create_note`` / ``list_notes`` / etc. can be the FunctionTool instances the agent factory imports. ``append_note_content`` (used by the agents-graph wiki-update hook) calls the impl helpers directly. #2 Delete ``strix/tools/_state_adapter.py``. The ``AgentStateAdapter`` shim only existed to feed legacy ``*_actions.py`` functions a ``state.agent_id`` they could read. With the actions inlined, the wrappers read ``ctx.context['agent_id']`` directly. #3 Strip ``strix/tools/registry.py`` from ~250 LOC to ~110. Deleted: XML schema loading, ``_parse_param_schema``, ``get_tools_prompt``, ``get_tool_param_schema``, ``needs_agent_state``, ``should_execute_in_sandbox``, ``validate_tool_availability`` — all for the host-side legacy dispatcher path. Kept the ``register_tool`` decorator (sandbox side), ``get_tool_by_name``, ``get_tool_names``, ``tools`` list, ``clear_registry``. The Jinja prompt template's ``{{ get_tools_prompt() }}`` injection is dropped — the SDK auto-generates tool descriptions from function signatures, so the legacy XML tool block was redundant and stale. #4 Delete every ``*_actions_schema.xml`` (12 files). They were read by the now-removed ``_load_xml_schema`` to build the legacy prompt's tool descriptions. No consumer remains. Side fixes: - ``reporting_renderer.py`` updated to import ``_parse_*_xml`` from the new location with leading underscore. - ``test_local_tools.py``, ``test_notes_jsonl_concurrency.py``, ``test_notes_wiki.py`` updated to point at the new module paths and call the ``_*_impl`` sync helpers. Tests: 279/279 passing. ~1500 LOC of action files moved into the tool wrappers; ~140 LOC of registry boilerplate removed; ~400 lines of dead XML deleted. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 11:26:02 -07:00
await asyncio.to_thread(
_update_note_impl,
note_id=note_id,
title=title,
content=content,
tags=tags,
),
2026-04-25 15:17:46 -07:00
ensure_ascii=False,
default=str,
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
)
2026-04-25 15:17:46 -07:00
@function_tool(timeout=30)
feat(migration): phase 2.1-2.3 — sandbox dispatch + thin slice tool wrappers Phase 2.1 — sandbox dispatch helper: - strix/tools/_sandbox_dispatch.py: post_to_sandbox() centralizes the host->container HTTP wire format. Connect=10s, read=150s timeouts mirror legacy executor.py. 50 MB response cap (C18) prevents OOM from a runaway tool. All errors surface as {"error": str} so the model can recover instead of the run dying. Phase 2.2 — C6 lock-protected JSONL writes: - strix/tools/notes/notes_actions.py: notes.jsonl appends are now wrapped in _notes_lock so concurrent agents can't interleave half-written lines. Regression test in test_notes_jsonl_concurrency.py verifies 1000 parallel writes produce exactly 1000 valid JSON lines. Phase 2.3 — thin-slice SDK wrappers (think + todo + notes): - strix/tools/_legacy_adapter.py: LegacyAgentStateAdapter shim — exposes just enough surface (.agent_id) for legacy tools that close over agent_state, sourced from ctx.context['agent_id']. - strix/tools/thinking/thinking_sdk_tools.py: 1 tool (think). - strix/tools/todo/todo_sdk_tools.py: 6 tools (create/list/update/done/ pending/delete) with bulk-form preserved. - strix/tools/notes/notes_sdk_tools.py: 5 tools (create/list/get/update/ delete) with asyncio.to_thread around the lock-protected file I/O. Tests: 22 new tests pass (10 sandbox dispatch + 2 concurrency + 10 SDK local). Full suite still green. Per-file ruff ignores added for SDK wrapper files: TC002 (RunContextWrapper must be runtime-importable because the SDK calls get_type_hints() to derive the JSON schema) and PLR0911 (sandbox dispatch's 10 short-circuit returns are intentional, each a distinct documented failure mode). Refs: PLAYBOOK.md §3.4, AUDIT_R3.md C6/C18. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 00:13:34 -07:00
async def delete_note(ctx: RunContextWrapper, note_id: str) -> str:
refactor(notes): drop disk persistence + shared-wiki prose The notes tool no longer touches disk. ``_notes_storage`` lives in memory for the lifetime of one scan process, shared across every agent in that process via the existing RLock. Process exit clears the lot — no notes.jsonl event log, no wiki/<slug>.md Markdown rendering, no replay-on-startup hydration. Removed ~10 internal helpers (``_get_run_dir``, ``_get_notes_jsonl_path``, ``_append_note_event``, ``_load_notes_from_jsonl``, ``_ensure_notes_loaded``, ``_persist_wiki_note``, ``_remove_wiki_note``, ``_get_wiki_directory``, ``_get_wiki_note_path``, ``_sanitize_wiki_title``) plus the ``_loaded_notes_run_dir`` module state, ``wiki_filename`` per-note field, and the ``OSError`` branches that only existed for the wiki write path. The ``wiki`` category is preserved as a free-form long-form bucket; it just no longer has any special persistence behaviour. Skill prompts scrubbed of every "shared wiki memory" / "repo wiki" / "append a delta before agent_finish" instruction: ``coordination/source_aware_whitebox.md``, ``custom/source_aware_sast.md``, ``scan_modes/{quick,standard,deep}.md``, plus the WHITE-BOX TESTING block in ``agents/prompts/system_prompt.jinja``. HARNESS_WIKI.md updated to drop the wiki-as-shared-knowledge-base description, the per-run output-tree references to ``notes/notes.jsonl`` and ``wiki/{note_id}-{slug}.md``, and the ``is_whitebox`` toggle prose. Net: -178 LoC in notes/tools.py, -45 LoC across skills/system_prompt and the wiki doc. The notes tool surface (5 ``@function_tool``s) is unchanged for the agent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 23:56:22 -07:00
"""Delete a note.
Args:
note_id: Note id to delete.
"""
2026-04-25 15:17:46 -07:00
return json.dumps(
await asyncio.to_thread(_delete_note_impl, note_id), ensure_ascii=False, default=str
)