fix: empty-list scope, get_tree bool casing, md fence tracking; dedupe base URL

- local get_agent_tools: `set(doc_ids) if doc_ids is not None else None` so
  doc_ids=[] is a scope of nothing (reject all), not open mode. The public
  query path already guarded []; this hardens direct callers.
- legacy get_tree: send summary=true/false (lowercase) instead of Python's
  capitalized True/False, matching the modern CloudBackend and the API.
- markdown parser: track the opening fence character so a ```-fence isn't
  closed by a ~~~ line (CommonMark), keeping '#'-lines inside it out of headings.
- dedupe the cloud base URL: single API_BASE in cloud_api, referenced by
  CloudBackend and PageIndexClient (was three independent copies).

Regression tests for each.
This commit is contained in:
mountain 2026-07-10 11:17:20 +08:00
parent e18ccdeaf8
commit d97231b480
8 changed files with 83 additions and 15 deletions

View file

@ -127,6 +127,18 @@ def test_scoped_mode_allows_in_scope_doc_id(populated_backend):
assert out.get("doc_name") == "alpha.pdf"
def test_empty_doc_ids_is_scoped_to_nothing_not_open_mode(populated_backend):
# doc_ids=[] means "scope to no documents", NOT open mode. It must exclude
# list_documents and reject every doc_id — otherwise an empty list would
# collapse to None (truthiness) and silently grant access to the whole
# collection.
tools = populated_backend.get_agent_tools("papers", doc_ids=[])
by_name = {t.name: t for t in tools.function_tools}
assert "list_documents" not in by_name
out = json.loads(_invoke_tool(by_name["get_document"], {"doc_id": "d1"}))
assert "error" in out and "not in scope" in out["error"]
@pytest.mark.parametrize("bad_pages", ["all", "5-", "abc", "3-1"])
def test_get_page_content_returns_actionable_error_for_bad_page_spec(populated_backend, bad_pages):
# A malformed page spec must come back as a correctable JSON error (like the