From 491e53ef3c6ec9d8db0673e2c4ac125d4a7cae50 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Thu, 3 Sep 2026 13:33:47 -0700 Subject: [PATCH] =?UTF-8?q?simplify(compat):=20tool=5Fsearch/skills=5Fsync?= =?UTF-8?q?=5Fclient=20=E2=80=94=20drop=2022=20re-exports,=20repoint=205?= =?UTF-8?q?=20callers=20+=205=20test=20files;=20drop=20stale=20re-export?= =?UTF-8?q?=20prose=20in=20web=5Ftools=5Ftruncate/file=5Foperations=5Flint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cli.py | 3 +- gateway/run.py | 2 +- hermes_cli/main_platform_setup.py | 9 +-- tests/agent/test_org_skill_namespace.py | 33 +++++------ tests/tools/test_deferral_fixes.py | 5 +- tests/tools/test_skills_sync_client.py | 66 +++++++++++----------- tests/tools/test_tool_search.py | 5 +- tests/tools/test_tool_search_multiquery.py | 9 ++- tools/file_operations_lint.py | 2 +- tools/skill_manager_guards.py | 3 +- tools/skills_sync_client.py | 20 +++---- tools/tool_search.py | 9 ++- tools/web_tools_truncate.py | 9 +-- 13 files changed, 85 insertions(+), 90 deletions(-) diff --git a/cli.py b/cli.py index 5758be1290..3ea142f6bd 100644 --- a/cli.py +++ b/cli.py @@ -3761,7 +3761,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix # the token (only issued for multi-member orgs), so a solo account never hits # the network here. Both fail-quiet. try: - from tools.skills_sync_client import maybe_pull_org_skills, maybe_pull_skills + from tools.skills_sync_client import maybe_pull_skills + from tools.skills_sync_client_org import maybe_pull_org_skills except Exception: return for pull in (maybe_pull_skills, maybe_pull_org_skills): diff --git a/gateway/run.py b/gateway/run.py index 01cb426982..8559158622 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -4439,7 +4439,7 @@ def _housekeeping_skill_sync() -> None: def _housekeeping_org_skill_sync() -> None: """Gated on real org membership (the token must carry an org role): solo accounts never reach the network.""" - from tools.skills_sync_client import maybe_pull_org_skills + from tools.skills_sync_client_org import maybe_pull_org_skills maybe_pull_org_skills() diff --git a/hermes_cli/main_platform_setup.py b/hermes_cli/main_platform_setup.py index 1c521d7583..7e737d6dd2 100644 --- a/hermes_cli/main_platform_setup.py +++ b/hermes_cli/main_platform_setup.py @@ -1,8 +1,7 @@ """Interactive messaging-platform setup wizards: WhatsApp (bridge + Cloud API), Slack manifest, Skill Sync. -Split out of ``hermes_cli/main.py``, which re-imports every name (``hermes_cli.main.`` keeps -resolving and monkeypatching). Names that stay in main are imported lazily at call time. +Split out of ``hermes_cli/main.py``. Names that still live in main are imported lazily at call time. """ import contextlib @@ -246,9 +245,10 @@ def _sync_device(args, ssc) -> int: def _sync_propose(args, ssc) -> int: + from tools.skills_sync_client_org import propose_skill name = args.name try: - result = ssc.propose_skill(name, message=args.message) + result = propose_skill(name, message=args.message) except ssc.SyncInertError as e: _err(f"cannot share this skill: {e}") return 1 @@ -310,7 +310,8 @@ def _sync_pull(ssc, identity): result = ssc.pull_skills(identity=identity) # Refresh the org mirror too when this account belongs to an organisation (no-op # otherwise), so one pull covers both. - org_result = ssc.maybe_pull_org_skills() + from tools.skills_sync_client_org import maybe_pull_org_skills + org_result = maybe_pull_org_skills() if org_result: n = len(org_result.get("updated") or []) _err(f"org: refreshed {n} shared skill(s) from your organisation.") diff --git a/tests/agent/test_org_skill_namespace.py b/tests/agent/test_org_skill_namespace.py index 08c299f1f8..1d05b4de2e 100644 --- a/tests/agent/test_org_skill_namespace.py +++ b/tests/agent/test_org_skill_namespace.py @@ -334,42 +334,43 @@ class TestLocalEditsSurviveOrgUpdates: monkeypatch.setattr( ssc, "_org_dir", lambda: skills / sku.ORG_MIRROR_DIR_NAME ) - return ssc, skills, d + from tools import skills_sync_client_org as org + return org, skills, d def test_unmodified_skill_is_not_flagged(self, tmp_path, monkeypatch): - ssc, _skills, d = self._mirror(tmp_path, monkeypatch) - ssc._write_org_baseline( + org, _skills, d = self._mirror(tmp_path, monkeypatch) + org._write_org_baseline( "org-1", - {"shared-x": {"fingerprint": ssc._skill_dir_fingerprint(d), "tree": "t1"}}, + {"shared-x": {"fingerprint": org._skill_dir_fingerprint(d), "tree": "t1"}}, ) - assert ssc.org_skill_is_locally_modified("shared-x", "org-1") is False - assert ssc.list_locally_modified_org_skills("org-1") == [] + assert org.org_skill_is_locally_modified("shared-x", "org-1") is False + assert org.list_locally_modified_org_skills("org-1") == [] def test_edited_skill_is_detected(self, tmp_path, monkeypatch): - ssc, _skills, d = self._mirror(tmp_path, monkeypatch) - ssc._write_org_baseline( + org, _skills, d = self._mirror(tmp_path, monkeypatch) + org._write_org_baseline( "org-1", - {"shared-x": {"fingerprint": ssc._skill_dir_fingerprint(d), "tree": "t1"}}, + {"shared-x": {"fingerprint": org._skill_dir_fingerprint(d), "tree": "t1"}}, ) (d / "SKILL.md").write_text("---\nname: shared-x\n---\nEDITED\n", encoding="utf-8") - assert ssc.org_skill_is_locally_modified("shared-x", "org-1") is True - assert ssc.list_locally_modified_org_skills("org-1") == ["shared-x"] + assert org.org_skill_is_locally_modified("shared-x", "org-1") is True + assert org.list_locally_modified_org_skills("org-1") == ["shared-x"] def test_missing_baseline_does_not_cry_wolf(self, tmp_path, monkeypatch): - ssc, _skills, _d = self._mirror(tmp_path, monkeypatch) + org, _skills, _d = self._mirror(tmp_path, monkeypatch) # Mirror pulled before baselines existed — must not be reported as # modified (that would block every update with a phantom conflict). - assert ssc.org_skill_is_locally_modified("shared-x", "org-1") is False + assert org.org_skill_is_locally_modified("shared-x", "org-1") is False def test_fingerprint_is_content_based_not_mtime(self, tmp_path, monkeypatch): import os import time - ssc, _skills, d = self._mirror(tmp_path, monkeypatch) - before = ssc._skill_dir_fingerprint(d) + org, _skills, d = self._mirror(tmp_path, monkeypatch) + before = org._skill_dir_fingerprint(d) time.sleep(0.01) os.utime(d / "SKILL.md", None) # touch: mtime changes, content doesn't - assert ssc._skill_dir_fingerprint(d) == before + assert org._skill_dir_fingerprint(d) == before def test_auto_propose_defaults_off(self, monkeypatch): from tools import skills_sync_client as ssc diff --git a/tests/tools/test_deferral_fixes.py b/tests/tools/test_deferral_fixes.py index 35fa272ad8..676eb6e736 100644 --- a/tests/tools/test_deferral_fixes.py +++ b/tests/tools/test_deferral_fixes.py @@ -28,7 +28,8 @@ from types import SimpleNamespace import pytest from agent.tool_dispatch_helpers import _plan_tool_batch_segments -from tools.tool_search import _short_desc, build_catalog, search_catalog +from tools.tool_search import build_catalog, search_catalog +from tools.tool_search_catalog import _short_desc def _tc(name, arguments="{}", call_id=None): @@ -300,7 +301,7 @@ class TestSourceNameIndexing: # Compare in token space: the tokenizer may stem (e.g. # "catalogsource" -> "catalogsourc"), and the contract is that # the label lands in the document exactly once either way. - from tools.tool_search import _tokenize + from tools.tool_search_catalog import _tokenize label_token = _tokenize(source_label)[0] tokens_by_name = {entry.name: entry._tokens for entry in catalog} assert tokens_by_name[names[0]].count(label_token) == 1 diff --git a/tests/tools/test_skills_sync_client.py b/tests/tools/test_skills_sync_client.py index a6fdcdb884..dbe647069e 100644 --- a/tests/tools/test_skills_sync_client.py +++ b/tests/tools/test_skills_sync_client.py @@ -22,6 +22,8 @@ from pathlib import Path import pytest import tools.skills_sync_client as ssc +import tools.skills_sync_client_org as org +import tools.skills_sync_client_wire as wire # --------------------------------------------------------------------------- @@ -268,7 +270,7 @@ def _jwt(claims: dict) -> str: class TestAddressing: def test_full_64_hex_address(self): - addr = ssc.wire_address(b"") + addr = wire.wire_address(b"") # sha256 of empty is the well-known e3b0... digest, full 64 hex. assert addr == ( "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" @@ -278,21 +280,21 @@ class TestAddressing: def test_address_differs_from_local_truncated_namespace(self): # The wire full-64-hex must NOT equal the local truncated 16-hex form. data = b"hello world" - full = ssc.wire_address(data) + full = wire.wire_address(data) truncated = "sha256:" + hashlib.sha256(data).hexdigest()[:16] assert full != truncated assert len(full.split(":")[1]) == 64 assert len(truncated.split(":")[1]) == 16 def test_canonical_json_sorted_no_whitespace(self): - out = ssc.canonical_json_bytes({"b": 1, "a": 2}) + out = wire.canonical_json_bytes({"b": 1, "a": 2}) assert out == b'{"a":2,"b":1}' assert b" " not in out assert not out.endswith(b"\n") def test_canonical_json_stable(self): obj = {"type": "tree", "entries": [{"name": "x", "hash": "sha256:aa"}]} - assert ssc.canonical_json_bytes(obj) == ssc.canonical_json_bytes(dict(obj)) + assert wire.canonical_json_bytes(obj) == wire.canonical_json_bytes(dict(obj)) # --------------------------------------------------------------------------- @@ -366,11 +368,11 @@ class TestObjectBuilding: assert tree_hash.startswith("sha256:") # tree object present and canonical kind, data = objects.objects[tree_hash] - assert kind == ssc.KIND_TREE + assert kind == wire.KIND_TREE tree = json.loads(data) entries = {e["name"]: e for e in tree["entries"]} - assert entries["SKILL.md"]["mode"] == ssc.MODE_FILE - assert entries["run.sh"]["mode"] == ssc.MODE_EXEC + assert entries["SKILL.md"]["mode"] == wire.MODE_FILE + assert entries["run.sh"]["mode"] == wire.MODE_EXEC # entries sorted by name (byte order) names = [e["name"] for e in tree["entries"]] assert names == sorted(names) @@ -494,14 +496,14 @@ class TestEndToEnd: client = ssc.SyncClient(base, "tok") caps = client.capabilities() assert caps["hsp_version"] == "1" - ssc._check_version(caps) # no raise + wire._check_version(caps) # no raise def test_version_mismatch_raises(self, mock_server): base, state = mock_server state.hsp_version = "2" client = ssc.SyncClient(base, "tok") with pytest.raises(ssc.SyncError): - ssc._check_version(client.capabilities()) + wire._check_version(client.capabilities()) def test_push_uploads_and_cas(self, mock_server, synced_env): base, state = mock_server @@ -514,7 +516,7 @@ class TestEndToEnd: assert head == result["head"] # commit object is present and well-formed kind, data = state.objects[head] - assert kind == ssc.KIND_COMMIT + assert kind == wire.KIND_COMMIT commit = json.loads(data) assert commit["author"]["owner"] == "owner1" assert commit["parents"] == [] # first commit @@ -636,7 +638,7 @@ class TestOptInFlag: class TestSyncManifest: def test_build_parse_roundtrip(self): data = ssc.build_sync_manifest_bytes({"beta": True, "alpha": False}) - parsed = ssc.parse_sync_manifest(data) + parsed = wire.parse_sync_manifest(data) assert parsed == {"alpha": False, "beta": True} def test_manifest_wire_shape(self): @@ -654,18 +656,18 @@ class TestSyncManifest: def test_parse_rejects_malformed(self): # Strict: unknown type, bad version, non-array skills, malformed entry. - assert ssc.parse_sync_manifest(b"not json") is None - assert ssc.parse_sync_manifest(b'{"type":"nope","version":1,"skills":[]}') is None - assert ssc.parse_sync_manifest(b'{"type":"sync-manifest","version":2,"skills":[]}') is None - assert ssc.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":{}}') is None + assert wire.parse_sync_manifest(b"not json") is None + assert wire.parse_sync_manifest(b'{"type":"nope","version":1,"skills":[]}') is None + assert wire.parse_sync_manifest(b'{"type":"sync-manifest","version":2,"skills":[]}') is None + assert wire.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":{}}') is None assert ( - ssc.parse_sync_manifest( + wire.parse_sync_manifest( b'{"type":"sync-manifest","version":1,"skills":[{"name":"x"}]}' ) is None ) # A malformed manifest must NOT be mistaken for "no skills opted in". - assert ssc.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":[]}') == {} + assert wire.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":[]}') == {} def test_snapshot_embeds_manifest_root_blob(self, mock_server, synced_env): # snapshot_profile must add a root-level `sync-manifest` blob recording @@ -914,7 +916,7 @@ class TestOrgEndToEnd: home, skills, identity = synced_env identity = {**identity, "org_id": "org-1", "org_role": "ADMIN"} client = ssc.SyncClient(base, identity["api_key"]) - result = ssc.propose_skill("alpha", client, identity=identity) + result = org.propose_skill("alpha", client, identity=identity) assert result["ok"] is True assert result.get("merged") is True head = state.refs["refs/org/org-1/HEAD"] @@ -930,7 +932,7 @@ class TestOrgEndToEnd: # Seed an org HEAD as admin first. admin_ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"} client = ssc.SyncClient(base, identity["api_key"]) - seeded = ssc.propose_skill("alpha", client, identity=admin_ident) + seeded = org.propose_skill("alpha", client, identity=admin_ident) # Member edits beta and proposes: server converts to 202. state.org_role_admin = False @@ -938,7 +940,7 @@ class TestOrgEndToEnd: "---\nname: beta\n---\nbeta v2 member edit\n", encoding="utf-8" ) member_ident = {**identity, "org_id": "org-1", "org_role": "MEMBER"} - result = ssc.propose_skill("beta", client, identity=member_ident) + result = org.propose_skill("beta", client, identity=member_ident) assert result["ok"] is True assert result.get("proposal_pending") is True assert result["proposal_id"] == 1 @@ -955,12 +957,12 @@ class TestOrgEndToEnd: home, skills, identity = synced_env admin_ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"} client = ssc.SyncClient(base, identity["api_key"]) - ssc.propose_skill("alpha", client, identity=admin_ident) - ssc.propose_skill("beta", client, identity=admin_ident) + org.propose_skill("alpha", client, identity=admin_ident) + org.propose_skill("beta", client, identity=admin_ident) state.org_role_admin = False member_ident = {**identity, "org_id": "org-1", "org_role": "MEMBER"} - result = ssc.propose_skill("alpha", client, identity=member_ident) + result = org.propose_skill("alpha", client, identity=member_ident) # Walk the proposed commit's root: both skills present. commit = json.loads(state.org_objects[result["commit"]][1]) root = json.loads(state.org_objects[commit["tree"]][1]) @@ -972,9 +974,9 @@ class TestOrgEndToEnd: home, skills, identity = synced_env admin_ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"} client = ssc.SyncClient(base, identity["api_key"]) - ssc.propose_skill("alpha", client, identity=admin_ident) + org.propose_skill("alpha", client, identity=admin_ident) - result = ssc.pull_org_skills(client, identity=admin_ident) + result = org.pull_org_skills(client, identity=admin_ident) assert result["ok"] is True assert "alpha" in result["updated"] mirrored = skills / "_org" / "org-1" / "alpha" / "SKILL.md" @@ -986,7 +988,7 @@ class TestOrgEndToEnd: home, skills, identity = synced_env ident = {**identity, "org_id": "org-1", "org_role": "MEMBER"} client = ssc.SyncClient(base, identity["api_key"]) - result = ssc.pull_org_skills(client, identity=ident) + result = org.pull_org_skills(client, identity=ident) assert result["ok"] is True assert result["head"] is None assert result["updated"] == [] @@ -998,7 +1000,7 @@ class TestOrgEndToEnd: ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"} client = ssc.SyncClient(base, identity["api_key"]) with pytest.raises(ssc.SyncInertError): - ssc.propose_skill("alpha", client, identity=ident) + org.propose_skill("alpha", client, identity=ident) def test_maybe_pull_org_inert_without_role(self, monkeypatch): # Personal org: no org_role claim -> None, never raises. @@ -1006,7 +1008,7 @@ class TestOrgEndToEnd: import hermes_cli.auth as auth_mod monkeypatch.setattr(auth_mod, "resolve_nous_runtime_credentials", lambda **kw: {"api_key": token}) - assert ssc.maybe_pull_org_skills() is None + assert org.maybe_pull_org_skills() is None class TestOrgEndpointScoping: @@ -1048,12 +1050,12 @@ class TestOrgEndpointScoping: client = ssc.SyncClient(base, identity["api_key"]) # `synced_env` already seeds alpha and beta (beta under devops/). - first = ssc.propose_skill("alpha", client, identity=admin) + first = org.propose_skill("alpha", client, identity=admin) assert first["ok"] is True # Previously: base_head read as None -> CAS from None -> 409 -> # SyncConflict escaped to the caller. - second = ssc.propose_skill("beta", client, identity=admin) + second = org.propose_skill("beta", client, identity=admin) assert second["ok"] is True # And the splice preserved the first skill rather than replacing it. @@ -1074,9 +1076,9 @@ class TestOrgEndpointScoping: home, skills, identity = synced_env admin = self._admin(identity) client = ssc.SyncClient(base, identity["api_key"]) - ssc.propose_skill("alpha", client, identity=admin) + org.propose_skill("alpha", client, identity=admin) - result = ssc.pull_org_skills(client=client, identity=admin) + result = org.pull_org_skills(client=client, identity=admin) assert result["ok"] is True assert result["head"] == state.refs["refs/org/org-1/HEAD"], ( "pull must resolve the real org HEAD, not None" diff --git a/tests/tools/test_tool_search.py b/tests/tools/test_tool_search.py index 4a6b654d82..898b432dea 100644 --- a/tests/tools/test_tool_search.py +++ b/tests/tools/test_tool_search.py @@ -254,7 +254,8 @@ class TestThresholdGate: class TestRetrieval: def _fake_catalog(self): """Build a catalog directly without touching the registry.""" - from tools.tool_search import CatalogEntry, _tokenize, _entry_search_text + from tools.tool_search import CatalogEntry + from tools.tool_search_catalog import _tokenize, _entry_search_text defs = [ _td("github_create_issue", "Open a new issue in a GitHub repository", {"title": {"type": "string"}, "body": {"type": "string"}}), @@ -644,7 +645,7 @@ class TestCatalogListing: assert result.listing_form in {"names", "groups", "mixed"} def test_short_desc_first_sentence_and_clip(self): - from tools.tool_search import _short_desc + from tools.tool_search_catalog import _short_desc assert _short_desc("Open an issue. Second sentence dropped.") == "Open an issue." long = "word " * 40 s = _short_desc(long) diff --git a/tests/tools/test_tool_search_multiquery.py b/tests/tools/test_tool_search_multiquery.py index 6c5923d0aa..be77be0207 100644 --- a/tests/tools/test_tool_search_multiquery.py +++ b/tests/tools/test_tool_search_multiquery.py @@ -64,7 +64,7 @@ def issue_defs(): class TestStemming: def test_tokenize_stems_index_and_query_identically(self): - from tools.tool_search import _tokenize + from tools.tool_search_catalog import _tokenize # Same stem on both sides is the whole contract. assert _tokenize("issues") == _tokenize("issue") assert _tokenize("creating messages") == _tokenize("create message") @@ -87,7 +87,7 @@ class TestStemming: assert names == ["mq_slack_post_message"] def test_single_token_stems_are_cached(self): - from tools.tool_search import _stem, _tokenize + from tools.tool_search_catalog import _stem, _tokenize _stem.cache_clear() corpus = "issues creating issues creating" @@ -102,12 +102,11 @@ class TestStemming: def test_parallel_tokenize_search_and_dispatch_are_deterministic(self, issue_defs): from tools.tool_search import ( ToolSearchConfig, - _stem, - _tokenize, build_catalog, dispatch_tool_search, search_catalog, ) + from tools.tool_search_catalog import _stem, _tokenize corpus = ( "issues", @@ -174,7 +173,7 @@ class TestStemming: concurrently on the underlying per-thread instances. A shared stemmer's mutable parse state produces wrong stems or raises here. """ - from tools.tool_search import _stem + from tools.tool_search_catalog import _stem words = ["issues", "creating", "meetings", "categories", "searching"] diff --git a/tools/file_operations_lint.py b/tools/file_operations_lint.py index aa5224fde9..edc4e4e1f1 100644 --- a/tools/file_operations_lint.py +++ b/tools/file_operations_lint.py @@ -1,7 +1,7 @@ """Syntax-lint and LSP-diagnostics tier for ``tools.file_operations``. ``ShellFileOperations`` inherits ``LintMixin``; module constants and in-process -linters are pure and re-imported into ``tools.file_operations`` for back-compat. +linters are pure functions importable from this module. """ import ast diff --git a/tools/skill_manager_guards.py b/tools/skill_manager_guards.py index c2427cdd21..06df3cd695 100644 --- a/tools/skill_manager_guards.py +++ b/tools/skill_manager_guards.py @@ -279,7 +279,8 @@ def _maybe_auto_propose_org_edit(name: str, skill_path: Path) -> Optional[str]: f"This skill is shared by your organisation. Your edit is " f"saved locally and will not be overwritten by org updates. " f"Run `hermes sync propose {name}` to share it back.") - result = ssc.propose_skill(name) + from tools.skills_sync_client_org import propose_skill + result = propose_skill(name) if result.get("proposal_pending"): return ( f"Auto-proposed to your organisation as proposal " diff --git a/tools/skills_sync_client.py b/tools/skills_sync_client.py index 60e52d2baa..20fe644ce3 100644 --- a/tools/skills_sync_client.py +++ b/tools/skills_sync_client.py @@ -17,12 +17,13 @@ from contextlib import suppress from pathlib import Path, PurePosixPath from typing import Any, Callable, Dict, List, Optional, Tuple -from tools.skills_sync_client_wire import ( # noqa: F401 (re-exports) - DEFAULT_MAX_OBJECT_BYTES, KIND_BLOB, KIND_COMMIT, KIND_TREE, MODE_EXEC, MODE_FILE, ObjectSet, - SyncClient, SyncConflict, SyncError, _check_version, assemble_root_from_skill_trees, build_commit, - build_root_tree, build_sync_manifest_bytes, build_tree, canonical_json_bytes, checked_capabilities, - materialize_tree, merge_skill, nest_skill_tree, parse_sync_manifest, read_manifest_of_root, - read_ref_hash, root_tree_of_commit, skill_trees_of_root, wire_address) +from tools.skills_sync_client_wire import ( + DEFAULT_MAX_OBJECT_BYTES, KIND_BLOB, ObjectSet, SyncClient, SyncConflict, SyncError, + assemble_root_from_skill_trees, build_commit, build_root_tree, build_sync_manifest_bytes, build_tree, + checked_capabilities, materialize_tree, merge_skill, nest_skill_tree, read_manifest_of_root, + read_ref_hash, root_tree_of_commit, skill_trees_of_root) +from tools.skills_sync_client_org import ( + ORG_DIR_NAME, list_locally_modified_org_skills, list_org_skill_names, resolve_org_identity) logger = logging.getLogger(__name__) # Gate claim (NAS's wire name; means "Nous admin" / Permissions.ADMIN_ACCESS). The bearer comes @@ -495,10 +496,3 @@ def sync_status() -> Dict[str, Any]: except Exception as e: logger.debug("skills_sync_client: sync_status org lookup failed: %s", e) return status - - -# Imported last: skills_sync_client_org reads this module's state lazily. -from tools.skills_sync_client_org import ( # noqa: E402,F401 (re-exports) - ORG_DIR_NAME, _skill_dir_fingerprint, _write_org_baseline, list_locally_modified_org_skills, - list_org_skill_names, maybe_pull_org_skills, org_skill_is_locally_modified, propose_skill, - pull_org_skills, resolve_org_identity) diff --git a/tools/tool_search.py b/tools/tool_search.py index 8387ce7772..04fb88b846 100644 --- a/tools/tool_search.py +++ b/tools/tool_search.py @@ -17,12 +17,11 @@ from dataclasses import dataclass from typing import Any, Dict, Iterable, List, Optional, Tuple from tools.registry import tool_error -from tools.tool_search_catalog import ( # noqa: F401 — re-exported public/test names +from tools.tool_search_catalog import ( BRIDGE_TOOL_NAMES, CHARS_PER_TOKEN, TOOL_CALL_NAME, TOOL_DESCRIBE_NAME, TOOL_SEARCH_NAME, - CatalogEntry, _corpus_stats, _entry_search_text, _fn, _listing_group_label, - _registry_entry, _registry_toolset, _short_desc, _stem, _tokenize, build_catalog, - build_catalog_listing_with_form, search_catalog) -from tools.tool_search_validation import validate_deferred_call_args # noqa: F401 + CatalogEntry, _corpus_stats, _fn, _listing_group_label, _registry_entry, _registry_toolset, + build_catalog, build_catalog_listing_with_form, search_catalog) +from tools.tool_search_validation import validate_deferred_call_args logger = logging.getLogger("tools.tool_search") _MAX_QUERIES_PER_CALL = _MAX_DESCRIBE_NAMES_PER_CALL = 10 # bound the work one bridge call requests diff --git a/tools/web_tools_truncate.py b/tools/web_tools_truncate.py index 9920f4055b..4570eb2f7f 100644 --- a/tools/web_tools_truncate.py +++ b/tools/web_tools_truncate.py @@ -14,13 +14,8 @@ logger = logging.getLogger("tools.web_tools") # Per-page char budget sent to the model (override: web.extract_char_limit); larger pages are head+tail # truncated, full text stored on disk. -# ─── Parallel / Tavily / Firecrawl helpers — moved into plugins ────────────── After PR #25182, the -# per-vendor client construction, request helpers, and response normalizers all live in -# plugins.web..provider: - parallel: plugins/web/parallel/provider.py - tavily: -# plugins/web/tavily/provider.py - firecrawl: plugins/web/firecrawl/provider.py The names from the firecrawl -# plugin (Firecrawl proxy, _get_firecrawl_client, _to_plain_object, _normalize_result_list, -# _extract_web_search_results, _extract_scrape_payload, _is_tool_gateway_ready, etc.) are re-exported at the -# top of this module for backward-compat with integration tests and unit-test patches. +# Per-vendor client construction, request helpers and response normalizers live in +# plugins.web..provider (parallel / tavily / firecrawl) since PR #25182. DEFAULT_EXTRACT_CHAR_LIMIT = 15000 # Ceiling on the full-text file written to cache/web so a multi-MB page can't write unbounded bytes on # every extract; the model only ever sees char_limit.