From 672df5b920a818351c646c7b001e5e9bdb3a2db1 Mon Sep 17 00:00:00 2001 From: SimoneBottoni Date: Thu, 1 Oct 2026 15:13:07 +0200 Subject: [PATCH 1/3] fix: clear caches in SourceryKitTokenStore and add tests for session handling --- src/sourcerykit/_provably.py | 3 +++ src/sourcerykit/ui/server.py | 1 - tests/unit/test_provably_glue.py | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/sourcerykit/_provably.py b/src/sourcerykit/_provably.py index 0437b77..4b8cdf5 100644 --- a/src/sourcerykit/_provably.py +++ b/src/sourcerykit/_provably.py @@ -43,6 +43,8 @@ class SourceryKitTokenStore: """The session in sourcerykit's global JSON, or in an embedding app's ``.env``.""" def load(self) -> OAuthTokens | None: + load_app_dir_config.cache_clear() + get_settings.cache_clear() # Raises SourceryKitConfigError when sourcerykit is not set up, as before the SDK split. settings = get_settings() # sourcerykit only ever signs in as its own client, so its tokens are that client's. @@ -72,6 +74,7 @@ def clear_refresh_token(self) -> None: os.environ.pop("PROVABLY_REFRESH_TOKEN", None) load_local_env.cache_clear() else: + load_app_dir_config.cache_clear() payload = load_app_dir_config() payload.pop("refresh_token", None) CONFIG_FILE.write_text(json.dumps(payload)) diff --git a/src/sourcerykit/ui/server.py b/src/sourcerykit/ui/server.py index 4ada55c..9c9ae5d 100644 --- a/src/sourcerykit/ui/server.py +++ b/src/sourcerykit/ui/server.py @@ -110,7 +110,6 @@ def launch(trace_id: str, host: str = "127.0.0.1", port: int = 8743) -> None: url = f"http://{host}:{port}/?id={trace_id}" print(f"Opening trace dashboard at {url}") - # ponytail: open browser after uvicorn binds; 0.5s is enough for a local server threading.Timer(0.5, webbrowser.open, args=[url]).start() import uvicorn diff --git a/tests/unit/test_provably_glue.py b/tests/unit/test_provably_glue.py index 160cfb0..c9e0165 100644 --- a/tests/unit/test_provably_glue.py +++ b/tests/unit/test_provably_glue.py @@ -1,5 +1,6 @@ """Tests for sourcerykit._provably — how sourcerykit plugs into the Provably SDK.""" +import json import os from pathlib import Path from unittest.mock import patch @@ -93,6 +94,38 @@ def test_load_reads_the_session_from_settings(self, monkeypatch: pytest.MonkeyPa get_settings.cache_clear() +class TestSessionSharedBetweenProcesses: + @pytest.fixture + def session_file(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path: + from sourcerykit import config + + path = tmp_path / "config.json" + monkeypatch.setattr(config, "CONFIG_FILE", path) + monkeypatch.setattr(_provably, "CONFIG_FILE", path) + for name in ("SOURCERYKIT_TOKEN_STORE", "PROVABLY_ACCESS_TOKEN", "PROVABLY_REFRESH_TOKEN"): + monkeypatch.delenv(name, raising=False) + path.write_text(json.dumps({"token": "at", "refresh_token": "rt", "org_id": _ORG})) + return path + + def test_load_sees_a_session_another_process_rotated(self, session_file: Path) -> None: + store = SourceryKitTokenStore() + assert store.load() == OAuthTokens(access_token="at", refresh_token="rt", client_id="sourcerykit-cli") + + session_file.write_text(json.dumps({"token": "at2", "refresh_token": "rt2", "org_id": _ORG})) + + assert store.load() == OAuthTokens(access_token="at2", refresh_token="rt2", client_id="sourcerykit-cli") + + def test_clearing_the_refresh_token_keeps_another_process_s_newer_access_token(self, session_file: Path) -> None: + from sourcerykit.config import load_app_dir_config + + load_app_dir_config() # this process's cached copy still holds "at" + session_file.write_text(json.dumps({"token": "at2", "refresh_token": "rt2", "org_id": _ORG})) + + SourceryKitTokenStore().clear_refresh_token() + + assert json.loads(session_file.read_text()) == {"token": "at2", "org_id": _ORG} + + def test_consent_page_has_its_own_setting(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("SOURCERYKIT_CONSENT_URL", raising=False) # The app URL is the base of query-record links, so it must not move the consent page. From 728614aa0539e7c5ad63fa4b68a149e812458ba2 Mon Sep 17 00:00:00 2001 From: SimoneBottoni Date: Thu, 1 Oct 2026 15:13:40 +0200 Subject: [PATCH 2/3] fix: update version to 1.4.1 and raise provably-sdk minimum to 0.3.3 --- CHANGELOG.md | 5 +++++ pyproject.toml | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a3bc17..2d896b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +## 1.4.1 + +### Changes +- **`provably-sdk` minimum raised to 0.3.3.** + ## 1.4.0 ### Changes diff --git a/pyproject.toml b/pyproject.toml index b346781..4df9d37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "sourcerykit" -version = "1.4.0" +version = "1.4.1" description = "Counterspell for hallucinating agents. Python SDK that breaks the illusion on every tool call, API response, and MCP handoff before bad outputs propagate." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.12" @@ -47,7 +47,7 @@ dependencies = [ "requests >=2.34", "jsonschema>=4.0", "psycopg[binary]>=3.1", - "provably-sdk>=0.3.2,<1", + "provably-sdk>=0.3.3,<1", "pydantic>=2.6", "python-dotenv>=1.2", "sqlalchemy>=2.0", @@ -154,7 +154,7 @@ exclude_also = ["if TYPE_CHECKING:", "raise NotImplementedError", "\\.\\.\\."] sourcerykit = "sourcerykit.cli.main:app" [tool.bumpversion] -current_version = "1.4.0" +current_version = "1.4.1" commit = false tag = false tag_name = "v{new_version}" From 94a1fe7afa32acd9eca135f5db9fc7b0a6fa8d1e Mon Sep 17 00:00:00 2001 From: SimoneBottoni Date: Thu, 1 Oct 2026 17:35:05 +0200 Subject: [PATCH 3/3] fix: update provably-sdk to version 0.3.3 and bump sourcerykit to 1.4.1 --- uv.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uv.lock b/uv.lock index 2189436..97e35a7 100644 --- a/uv.lock +++ b/uv.lock @@ -1860,7 +1860,7 @@ wheels = [ [[package]] name = "provably-sdk" -version = "0.3.2" +version = "0.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -1868,9 +1868,9 @@ dependencies = [ { name = "pydantic" }, { name = "structlog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/ea/f7691721076846e1ef3e4d25d2faba9cf06c841feb3f9067cd9e21419823/provably_sdk-0.3.2.tar.gz", hash = "sha256:463f3f6da5ccdba8e8f905297a209ca3208652e6d1ae71dba87441a1855c8206", size = 67963, upload-time = "2026-09-24T13:59:56.216Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/19/13ee32df0574a0171e64a9751d1ad4f8bcb9a82a9e731c51878ae08acbbc/provably_sdk-0.3.3.tar.gz", hash = "sha256:f27a3e6fe0e43566873cbfaf15fc99834069ad57bf8e1a9a447d001d829b998f", size = 68494, upload-time = "2026-10-01T15:32:04.533Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/29/b353d8bb38a597b41a68bf988aa049d306bb64a045613e8cb19836ed3f01/provably_sdk-0.3.2-py3-none-any.whl", hash = "sha256:6ce9c253133f90c219f36934ab8244b9e8d57e6a91dd6407c72ea3786b40115b", size = 25978, upload-time = "2026-09-24T13:59:54.802Z" }, + { url = "https://files.pythonhosted.org/packages/0b/09/155a093e7d15446de4d9bd0532b5fac3fc9f8b3c1c7aacffa53639344475/provably_sdk-0.3.3-py3-none-any.whl", hash = "sha256:bdd389f5a9b4b49597c7163b61b244d5abb35e16c3afdf9732b62203954528d9", size = 26267, upload-time = "2026-10-01T15:32:03.346Z" }, ] [[package]] @@ -2423,7 +2423,7 @@ wheels = [ [[package]] name = "sourcerykit" -version = "1.4.0" +version = "1.4.1" source = { editable = "." } dependencies = [ { name = "aiohttp" }, @@ -2476,7 +2476,7 @@ requires-dist = [ { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10" }, { name = "openai-agents", marker = "extra == 'dev'", specifier = ">=0.0.3" }, { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=4.0" }, - { name = "provably-sdk", specifier = ">=0.3.2,<1" }, + { name = "provably-sdk", specifier = ">=0.3.3,<1" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.1" }, { name = "pydantic", specifier = ">=2.6" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },