From 7f92994526c1f04c8db073d792a17ab5cf85a551 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 04:01:17 +0000 Subject: [PATCH 1/9] feat: tag claude-code harness on signup + plugin-setup prompt Pass harness="claude-code" to Inkbox.signup so self-signups record which harness created them (forward-compatible with the SDK's new optional harness param). Also add a brief channel-prompt note telling the agent to ask the human for permission before configuring this plugin, since it wires native messaging channels into the runtime. Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/prompts.py | 7 +++++++ inkbox_claude/setup_wizard.py | 1 + 2 files changed, 8 insertions(+) diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index b1e7414..77db56a 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -54,6 +54,13 @@ proactively — e.g. "email me the full report" or a cron-style ping. Replies on the channel you were messaged on are sent automatically; only use these tools for a *different* channel or recipient. + +# Setting up this plugin + +If you self-signed-up to Inkbox but this plugin isn't yet configured in +your runtime, ask the human for permission before setting it up — the +plugin wires native messaging channels directly into your runtime, so +that's the human's call to make, not yours. """.strip() diff --git a/inkbox_claude/setup_wizard.py b/inkbox_claude/setup_wizard.py index 4e18d00..6d2f824 100644 --- a/inkbox_claude/setup_wizard.py +++ b/inkbox_claude/setup_wizard.py @@ -1109,6 +1109,7 @@ def _self_signup_flow(base_url: str, Inkbox: Any, InkboxAPIError: Any) -> tuple[ note_to_human=note, agent_handle=handle, base_url=base_url, + harness="claude-code", # tag which harness self-signed-up ) break except InkboxAPIError as exc: From b85ddc159e4b265293ef82828933ac198b8f6950 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 04:09:47 +0000 Subject: [PATCH 2/9] chore: align harness plugin-setup copy with the shared canonical wording Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/prompts.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index 77db56a..20c8100 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -55,12 +55,13 @@ Replies on the channel you were messaged on are sent automatically; only use these tools for a *different* channel or recipient. -# Setting up this plugin +# Setting up the Inkbox plugin -If you self-signed-up to Inkbox but this plugin isn't yet configured in -your runtime, ask the human for permission before setting it up — the -plugin wires native messaging channels directly into your runtime, so -that's the human's call to make, not yours. +If you self-signed up to Inkbox but this plugin isn't configured in your +runtime yet, ask the human for permission before setting it up — it's the +human's call to make, not yours. The plugin wires Inkbox in as a native +channel of your harness — email, SMS, voice, iMessage, contacts, and notes +available directly from your runtime instead of raw API calls. """.strip() From f5178f93017c00fa871941eb3e0bf5509ded1465 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 04:31:53 +0000 Subject: [PATCH 3/9] Require inkbox>=0.4.9 for harness signup kwarg The signup flow passes harness="claude-code", a kwarg only supported by the Inkbox SDK from 0.4.9 onward. Bump the pin across pyproject, the wizard's INKBOX_REQUIREMENTS, and the doctor/gateway hints, and guard the bootstrap against an already-installed too-old SDK: _ensure_inkbox_sdk now treats inkbox < 0.4.9 like an unsatisfied requirement and routes it through the existing pip-install/upgrade path so it upgrades to a harness-capable SDK before signup is attempted. Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/doctor.py | 2 +- inkbox_claude/gateway.py | 2 +- inkbox_claude/setup_wizard.py | 39 ++++++++++++++++++++++++++++++++--- pyproject.toml | 2 +- tests/test_setup_wizard.py | 8 +++---- 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/inkbox_claude/doctor.py b/inkbox_claude/doctor.py index 507166b..c152fb9 100644 --- a/inkbox_claude/doctor.py +++ b/inkbox_claude/doctor.py @@ -33,7 +33,7 @@ def run_doctor() -> List[Tuple[str, bool, str]]: import inkbox # noqa: F401 checks.append(("inkbox SDK", True, "installed")) except ImportError: - checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.4.7'")) + checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.4.9'")) try: import claude_agent_sdk # noqa: F401 diff --git a/inkbox_claude/gateway.py b/inkbox_claude/gateway.py index fa6729b..5ece08c 100644 --- a/inkbox_claude/gateway.py +++ b/inkbox_claude/gateway.py @@ -211,7 +211,7 @@ async def run(self) -> None: if not AIOHTTP_AVAILABLE: raise RuntimeError("aiohttp is not installed; run: pip install aiohttp") if not INKBOX_AVAILABLE: - raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.4.7'") + raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.4.9'") if not self.cfg.api_key or not self.cfg.identity: raise RuntimeError("INKBOX_API_KEY and INKBOX_IDENTITY must be set (see README)") diff --git a/inkbox_claude/setup_wizard.py b/inkbox_claude/setup_wizard.py index 6d2f824..11bff78 100644 --- a/inkbox_claude/setup_wizard.py +++ b/inkbox_claude/setup_wizard.py @@ -36,7 +36,10 @@ # Packages the wizard itself needs to talk to Inkbox during setup. The # gateway's other dependency (claude-agent-sdk) is checked by doctor. -INKBOX_REQUIREMENTS = ("inkbox>=0.4.7", "aiohttp>=3.9") +# The harness= kwarg on Inkbox.signup landed in inkbox 0.4.9; older SDKs +# raise TypeError before the request ever reaches the server. +INKBOX_MIN_VERSION = (0, 4, 9) +INKBOX_REQUIREMENTS = ("inkbox>=0.4.9", "aiohttp>=3.9") _BRACKETED_PASTE_PATTERN = re.compile(r"\x1b\[\s*200~|\x1b\[\s*201~") # Bundled avatar attached to the agent's Inkbox contact card during setup. @@ -366,6 +369,28 @@ def _load_inkbox_symbols() -> dict[str, Any]: } +def _inkbox_version_ok() -> bool: + # Guard against an already-installed but too-old SDK that predates the + # harness= signup kwarg. Prefer importlib.metadata + packaging; fall back + # to a tuple comparison parsed off the version string. + try: + from importlib.metadata import version + + raw = version("inkbox") + except Exception: + return True # can't tell — let the import/runtime path surface it + try: + from packaging.version import Version + + return Version(raw) >= Version(".".join(str(p) for p in INKBOX_MIN_VERSION)) + except Exception: + parts = [] + for chunk in raw.split(".")[:3]: + digits = "".join(c for c in chunk if c.isdigit()) + parts.append(int(digits) if digits else 0) + return tuple(parts) >= INKBOX_MIN_VERSION + + def _ensure_inkbox_sdk() -> dict[str, Any] | None: """Import the Inkbox SDK, offering to install it into this env if missing. @@ -373,11 +398,19 @@ def _ensure_inkbox_sdk() -> dict[str, Any] | None: dict[str, Any] | None: SDK symbols on success, None if unavailable. """ try: - return _load_inkbox_symbols() + symbols = _load_inkbox_symbols() + # A too-old SDK imports fine but lacks the harness= signup kwarg — + # treat it like an unsatisfied requirement and route to the upgrade path. + if _inkbox_version_ok(): + return symbols + first_error = ( + "the installed inkbox SDK is older than " + f"{'.'.join(str(p) for p in INKBOX_MIN_VERSION)}" + ) except Exception as exc: first_error = exc - print_warning("The Python Inkbox SDK is not available in this environment.") + print_warning("The Python Inkbox SDK is missing or too old in this environment.") print_info("The setup command is running under:") print_info(f" {sys.executable}") print_info("Install or upgrade the SDK in that exact environment with:") diff --git a/pyproject.toml b/pyproject.toml index 832b009..b59c3e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Inkbox bridge for Claude Code — talk to your coding agent over requires-python = ">=3.10" dependencies = [ "aiohttp>=3.9", - "inkbox>=0.4.7", + "inkbox>=0.4.9", "claude-agent-sdk>=0.1.0", "segno>=1.5", # terminal QR codes for the iMessage connect step ] diff --git a/tests/test_setup_wizard.py b/tests/test_setup_wizard.py index 5469d23..bf18900 100644 --- a/tests/test_setup_wizard.py +++ b/tests/test_setup_wizard.py @@ -83,7 +83,7 @@ def test_install_command_prefers_uv_when_available(monkeypatch): "install", "--python", "/tmp/venv/bin/python", - "inkbox>=0.4.7", + "inkbox>=0.4.9", "aiohttp>=3.9", ]] @@ -93,10 +93,10 @@ def test_install_command_falls_back_to_pip_and_ensurepip(monkeypatch): monkeypatch.setattr(setup_wizard.shutil, "which", lambda _name: None) assert setup_wizard._install_commands() == [ - [["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.7", "aiohttp>=3.9"]], + [["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.9", "aiohttp>=3.9"]], [ ["/tmp/venv/bin/python", "-m", "ensurepip", "--upgrade"], - ["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.7", "aiohttp>=3.9"], + ["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.9", "aiohttp>=3.9"], ], ] @@ -115,7 +115,7 @@ def fail_import(): out = capsys.readouterr().out assert "/tmp/venv/bin/python" in out assert "uv pip install --python" in out - assert "inkbox>=0.4.7" in out + assert "inkbox>=0.4.9" in out # ---------------------------------------------------------------------- From c860b25ce037572e9fdff5d5fd970d7d3b252d7b Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 04:44:56 +0000 Subject: [PATCH 4/9] Bump inkbox SDK pin to 0.4.10 for agent-signup harness 0.4.10 is the release that introduces the agent-signup harness param; 0.4.9 was an earlier release that predates it. Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/doctor.py | 2 +- inkbox_claude/gateway.py | 2 +- inkbox_claude/setup_wizard.py | 6 +++--- pyproject.toml | 2 +- tests/test_setup_wizard.py | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/inkbox_claude/doctor.py b/inkbox_claude/doctor.py index c152fb9..d11346c 100644 --- a/inkbox_claude/doctor.py +++ b/inkbox_claude/doctor.py @@ -33,7 +33,7 @@ def run_doctor() -> List[Tuple[str, bool, str]]: import inkbox # noqa: F401 checks.append(("inkbox SDK", True, "installed")) except ImportError: - checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.4.9'")) + checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.4.10'")) try: import claude_agent_sdk # noqa: F401 diff --git a/inkbox_claude/gateway.py b/inkbox_claude/gateway.py index 5ece08c..b5703b4 100644 --- a/inkbox_claude/gateway.py +++ b/inkbox_claude/gateway.py @@ -211,7 +211,7 @@ async def run(self) -> None: if not AIOHTTP_AVAILABLE: raise RuntimeError("aiohttp is not installed; run: pip install aiohttp") if not INKBOX_AVAILABLE: - raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.4.9'") + raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.4.10'") if not self.cfg.api_key or not self.cfg.identity: raise RuntimeError("INKBOX_API_KEY and INKBOX_IDENTITY must be set (see README)") diff --git a/inkbox_claude/setup_wizard.py b/inkbox_claude/setup_wizard.py index 11bff78..f25a777 100644 --- a/inkbox_claude/setup_wizard.py +++ b/inkbox_claude/setup_wizard.py @@ -36,10 +36,10 @@ # Packages the wizard itself needs to talk to Inkbox during setup. The # gateway's other dependency (claude-agent-sdk) is checked by doctor. -# The harness= kwarg on Inkbox.signup landed in inkbox 0.4.9; older SDKs +# The harness= kwarg on Inkbox.signup landed in inkbox 0.4.10; older SDKs # raise TypeError before the request ever reaches the server. -INKBOX_MIN_VERSION = (0, 4, 9) -INKBOX_REQUIREMENTS = ("inkbox>=0.4.9", "aiohttp>=3.9") +INKBOX_MIN_VERSION = (0, 4, 10) +INKBOX_REQUIREMENTS = ("inkbox>=0.4.10", "aiohttp>=3.9") _BRACKETED_PASTE_PATTERN = re.compile(r"\x1b\[\s*200~|\x1b\[\s*201~") # Bundled avatar attached to the agent's Inkbox contact card during setup. diff --git a/pyproject.toml b/pyproject.toml index b59c3e8..3572f3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ description = "Inkbox bridge for Claude Code — talk to your coding agent over requires-python = ">=3.10" dependencies = [ "aiohttp>=3.9", - "inkbox>=0.4.9", + "inkbox>=0.4.10", "claude-agent-sdk>=0.1.0", "segno>=1.5", # terminal QR codes for the iMessage connect step ] diff --git a/tests/test_setup_wizard.py b/tests/test_setup_wizard.py index bf18900..a4fdfbc 100644 --- a/tests/test_setup_wizard.py +++ b/tests/test_setup_wizard.py @@ -83,7 +83,7 @@ def test_install_command_prefers_uv_when_available(monkeypatch): "install", "--python", "/tmp/venv/bin/python", - "inkbox>=0.4.9", + "inkbox>=0.4.10", "aiohttp>=3.9", ]] @@ -93,10 +93,10 @@ def test_install_command_falls_back_to_pip_and_ensurepip(monkeypatch): monkeypatch.setattr(setup_wizard.shutil, "which", lambda _name: None) assert setup_wizard._install_commands() == [ - [["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.9", "aiohttp>=3.9"]], + [["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.10", "aiohttp>=3.9"]], [ ["/tmp/venv/bin/python", "-m", "ensurepip", "--upgrade"], - ["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.9", "aiohttp>=3.9"], + ["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.10", "aiohttp>=3.9"], ], ] @@ -115,7 +115,7 @@ def fail_import(): out = capsys.readouterr().out assert "/tmp/venv/bin/python" in out assert "uv pip install --python" in out - assert "inkbox>=0.4.9" in out + assert "inkbox>=0.4.10" in out # ---------------------------------------------------------------------- From a9ae4d901c9d5b570c3f960134b3792029f87c11 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 05:10:27 +0000 Subject: [PATCH 5/9] Require human confirmation before plugin setup Rework the setup-guidance copy so the agent confirms with its human and sets up the plugin together, never starting on its own. Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/prompts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index 20c8100..40cda1d 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -58,8 +58,8 @@ # Setting up the Inkbox plugin If you self-signed up to Inkbox but this plugin isn't configured in your -runtime yet, ask the human for permission before setting it up — it's the -human's call to make, not yours. The plugin wires Inkbox in as a native +runtime yet, don't set it up on your own — confirm with your human and do +it together before you start. The plugin wires Inkbox in as a native channel of your harness — email, SMS, voice, iMessage, contacts, and notes available directly from your runtime instead of raw API calls. """.strip() From f9063ed95320f5ec132c770733bb9a4400302c3e Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 05:33:23 +0000 Subject: [PATCH 6/9] Clarify Inkbox plugin self-setup requires asking human permission first Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/prompts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index 40cda1d..dd0451b 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -58,8 +58,8 @@ # Setting up the Inkbox plugin If you self-signed up to Inkbox but this plugin isn't configured in your -runtime yet, don't set it up on your own — confirm with your human and do -it together before you start. The plugin wires Inkbox in as a native +runtime yet, ask your human for permission before you start — once they +approve, you can set it up yourself. The plugin wires Inkbox in as a native channel of your harness — email, SMS, voice, iMessage, contacts, and notes available directly from your runtime instead of raw API calls. """.strip() From 5073034ff61c7b4433a91e45f6842954bacc58d0 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 18:21:11 +0000 Subject: [PATCH 7/9] chore: drop redundant plugin-setup prompt block The plugin's setup wizard already configures the plugin during signup, so a separate agent-facing "ask permission, then set it up yourself" instruction is redundant. Drop it; keep the harness= signup tagging. Co-Authored-By: Claude Opus 4.8 (1M context) --- inkbox_claude/prompts.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index dd0451b..b1e7414 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -54,14 +54,6 @@ proactively — e.g. "email me the full report" or a cron-style ping. Replies on the channel you were messaged on are sent automatically; only use these tools for a *different* channel or recipient. - -# Setting up the Inkbox plugin - -If you self-signed up to Inkbox but this plugin isn't configured in your -runtime yet, ask your human for permission before you start — once they -approve, you can set it up yourself. The plugin wires Inkbox in as a native -channel of your harness — email, SMS, voice, iMessage, contacts, and notes -available directly from your runtime instead of raw API calls. """.strip() From 2e1c9b4b92b42f7ae4e00353964b6eb3580773b0 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Thu, 25 Jun 2026 18:24:00 +0000 Subject: [PATCH 8/9] ci: run pytest on PRs Mirror openclaw-plugin's CI. The inkbox SDK is deliberately not installed (tests mock it, and inkbox>=0.4.10 with the harness= kwarg isn't on PyPI yet), so CI installs only the deps the tests actually import. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..bd9e197 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + # The inkbox SDK is intentionally not installed here: the tests mock it, + # and the pinned inkbox>=0.4.10 (with the harness= signup kwarg) isn't on + # PyPI yet. claude-agent-sdk is needed for the gateway-health tests. + - name: Install test deps + run: pip install pytest aiohttp segno claude-agent-sdk + + - name: Test + run: pytest -q From 31e1da6d255371c0b6540a08655813552c3b56e6 Mon Sep 17 00:00:00 2001 From: dimavrem22 Date: Fri, 26 Jun 2026 20:11:23 +0000 Subject: [PATCH 9/9] chore: trim out-of-place harness/version comments Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 4 +--- inkbox_claude/setup_wizard.py | 9 +-------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd9e197..e12f42a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,9 +17,7 @@ jobs: with: python-version: "3.12" - # The inkbox SDK is intentionally not installed here: the tests mock it, - # and the pinned inkbox>=0.4.10 (with the harness= signup kwarg) isn't on - # PyPI yet. claude-agent-sdk is needed for the gateway-health tests. + # inkbox is mocked in the tests, so install only what they import. - name: Install test deps run: pip install pytest aiohttp segno claude-agent-sdk diff --git a/inkbox_claude/setup_wizard.py b/inkbox_claude/setup_wizard.py index f25a777..87a5803 100644 --- a/inkbox_claude/setup_wizard.py +++ b/inkbox_claude/setup_wizard.py @@ -36,8 +36,6 @@ # Packages the wizard itself needs to talk to Inkbox during setup. The # gateway's other dependency (claude-agent-sdk) is checked by doctor. -# The harness= kwarg on Inkbox.signup landed in inkbox 0.4.10; older SDKs -# raise TypeError before the request ever reaches the server. INKBOX_MIN_VERSION = (0, 4, 10) INKBOX_REQUIREMENTS = ("inkbox>=0.4.10", "aiohttp>=3.9") _BRACKETED_PASTE_PATTERN = re.compile(r"\x1b\[\s*200~|\x1b\[\s*201~") @@ -370,9 +368,6 @@ def _load_inkbox_symbols() -> dict[str, Any]: def _inkbox_version_ok() -> bool: - # Guard against an already-installed but too-old SDK that predates the - # harness= signup kwarg. Prefer importlib.metadata + packaging; fall back - # to a tuple comparison parsed off the version string. try: from importlib.metadata import version @@ -399,8 +394,6 @@ def _ensure_inkbox_sdk() -> dict[str, Any] | None: """ try: symbols = _load_inkbox_symbols() - # A too-old SDK imports fine but lacks the harness= signup kwarg — - # treat it like an unsatisfied requirement and route to the upgrade path. if _inkbox_version_ok(): return symbols first_error = ( @@ -1142,7 +1135,7 @@ def _self_signup_flow(base_url: str, Inkbox: Any, InkboxAPIError: Any) -> tuple[ note_to_human=note, agent_handle=handle, base_url=base_url, - harness="claude-code", # tag which harness self-signed-up + harness="claude-code", ) break except InkboxAPIError as exc: