From b153c3a6df9dcc91ec4cf783777c885a2f90e11f Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Wed, 15 Jul 2026 21:07:33 +0000 Subject: [PATCH 1/6] chore: require Inkbox SDK 0.5.0 --- inkbox_claude/doctor.py | 2 +- inkbox_claude/gateway.py | 2 +- inkbox_claude/setup_wizard.py | 4 ++-- pyproject.toml | 2 +- tests/test_setup_wizard.py | 8 ++++---- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/inkbox_claude/doctor.py b/inkbox_claude/doctor.py index 74c9769..95bbd16 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.20,<1.0.0'")) + checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.5.0,<1.0.0'")) try: import claude_agent_sdk # noqa: F401 diff --git a/inkbox_claude/gateway.py b/inkbox_claude/gateway.py index 4c8719f..89eb9c2 100644 --- a/inkbox_claude/gateway.py +++ b/inkbox_claude/gateway.py @@ -488,7 +488,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.20,<1.0.0'") + raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.5.0,<1.0.0'") 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 73855b4..fc65651 100644 --- a/inkbox_claude/setup_wizard.py +++ b/inkbox_claude/setup_wizard.py @@ -35,8 +35,8 @@ # Packages the wizard itself needs to talk to Inkbox during setup. The # gateway's other dependency (claude-agent-sdk) is checked by doctor. -INKBOX_MIN_VERSION = (0, 4, 15) -INKBOX_REQUIREMENTS = ("inkbox>=0.4.20,<1.0.0", "aiohttp>=3.9") +INKBOX_MIN_VERSION = (0, 5, 0) +INKBOX_REQUIREMENTS = ("inkbox>=0.5.0,<1.0.0", "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 e40268b..8b258ef 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.11" dependencies = [ "aiohttp>=3.9", - "inkbox>=0.4.24,<1.0.0", + "inkbox>=0.5.0,<1.0.0", "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 d2c0606..75f6a63 100644 --- a/tests/test_setup_wizard.py +++ b/tests/test_setup_wizard.py @@ -88,7 +88,7 @@ def test_install_command_prefers_uv_when_available(monkeypatch): "install", "--python", "/tmp/venv/bin/python", - "inkbox>=0.4.20,<1.0.0", + "inkbox>=0.5.0,<1.0.0", "aiohttp>=3.9", ]] @@ -98,10 +98,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.20,<1.0.0", "aiohttp>=3.9"]], + [["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.5.0,<1.0.0", "aiohttp>=3.9"]], [ ["/tmp/venv/bin/python", "-m", "ensurepip", "--upgrade"], - ["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.20,<1.0.0", "aiohttp>=3.9"], + ["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.5.0,<1.0.0", "aiohttp>=3.9"], ], ] @@ -120,7 +120,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.20,<1.0.0" in out + assert "inkbox>=0.5.0,<1.0.0" in out # ---------------------------------------------------------------------- From 526a301a97fcfb9a7b6885579f81ea6a3b512cc6 Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Sat, 18 Jul 2026 01:58:52 +0000 Subject: [PATCH 2/6] feat: align organization-wide contacts --- README.md | 4 +++- inkbox_claude/prompts.py | 7 ++++--- inkbox_claude/tools.py | 8 ++++---- tests/test_prompts.py | 4 +++- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3a5f4f2..ea6187c 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,9 @@ The agent reaches you (or third parties) through an in-process MCP server: - `inkbox_list_text_conversations` · `inkbox_get_text_conversation` — browse SMS threads and history. - `inkbox_list_imessage_conversations` · `inkbox_get_imessage_conversation` — browse iMessage threads and history (find the `conversation_id` to send into). - `inkbox_lookup_contact` · `inkbox_list_contacts` · `inkbox_get_contact` — resolve and read address-book contacts (reverse-lookup by email/phone, free-text search, or full record by id). -- `inkbox_create_contact` · `inkbox_update_contact` · `inkbox_delete_contact` — save, edit, and remove contacts. Reads and writes are filtered server-side to what this identity may see. vCard export/import is not exposed. +- `inkbox_create_contact` · `inkbox_update_contact` · `inkbox_delete_contact` — save, edit, and remove organization-wide contacts. vCard export/import is not exposed. + +Contacts and generated contact facts are organization-wide. Contact `notes` remain user-managed profile text, separate from source-grounded generated facts. Correspondence still follows the configured identity's channel permissions. Unified correspondence and generated-fact tools are deferred until the installed SDK exposes those resources; this plugin does not use raw API calls as a substitute. On a live call, the OpenAI Realtime voice agent additionally gets `consult_agent`, `register_post_call_action` / `edit_post_call_action` / `delete_post_call_action`, and `hang_up_call` — see [Voice](#voice). diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index fbceab3..4a9bcbd 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -70,7 +70,7 @@ # Inkbox contacts -Claude can read and write Inkbox contacts visible to this configured identity. +Claude can read and write organization-wide Inkbox contacts. - Use inkbox_list_contacts for name-based searches like "who is Alex?". - Use inkbox_lookup_contact when you have an exact or partial email/phone filter. @@ -78,8 +78,9 @@ - Use inkbox_create_contact when the user asks you to save a new person or contact card. - Use inkbox_update_contact when the user asks you to change an existing contact; look up the contact first if you do not already have its UUID. - Use inkbox_delete_contact only after the target contact is explicit and confirmed. -- There is no vCard export/import, contact access, or contact rule tool in this harness. -- Contact tools operate only on contacts visible/writable to the configured identity. +- There is no vCard export/import or contact rule tool in this harness. Contacts do not have per-identity access grants. +- Contact notes are user-managed profile text. Generated contact facts are separate, source-grounded organization-wide memory; do not copy or overwrite generated facts through contact notes. +- Contact correspondence remains scoped to this identity's authorized channels. The installed SDK exposes neither unified correspondence nor generated-fact reads, so do not claim those tools exist or reconstruct them with raw requests. """.strip() diff --git a/inkbox_claude/tools.py b/inkbox_claude/tools.py index 6eae9c5..1206ffa 100644 --- a/inkbox_claude/tools.py +++ b/inkbox_claude/tools.py @@ -587,8 +587,8 @@ def _run(): return _error(str(exc)) # ------------------------------------------------------------------ - # Contacts — the org address book, filtered server-side to what this - # identity may see. + # Contacts and generated facts are organization-wide. Channel history is + # still scoped to this identity. # ------------------------------------------------------------------ @tool( @@ -615,7 +615,7 @@ def _run(): @tool( "inkbox_list_contacts", "Search the address book by free text (matches name, company, job " - "title, notes). Use for name-based queries like 'find Ada'. " + "title, user-managed notes). Use for name-based queries like 'find Ada'. " "order is 'recent' or 'name'.", {"q": str, "order": str, "limit": int}, ) @@ -649,7 +649,7 @@ def _run(): @tool( "inkbox_create_contact", "Save a new contact in the address book. Provide any of given_name, " - "family_name, preferred_name, company_name, job_title, notes, and " + "family_name, preferred_name, company_name, job_title, user-managed notes, and " "emails / phones as lists of strings (first entry is marked primary).", {"given_name": str, "family_name": str, "preferred_name": str, "company_name": str, "job_title": str, "notes": str, diff --git a/tests/test_prompts.py b/tests/test_prompts.py index 8e8a0b6..e0e7022 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -56,11 +56,13 @@ def test_channel_prompt_mentions_identity_and_dir(): assert "dev-agent@inkbox.ai" in text assert "jargon" in text.lower() assert "AskUserQuestion" in text - assert "Claude can read and write Inkbox contacts" in text + assert "organization-wide Inkbox contacts" in text assert "inkbox_create_contact" in text assert "inkbox_update_contact" in text assert "inkbox_delete_contact" in text assert "vCard export/import" in text + assert "Generated contact facts are separate" in text + assert "correspondence remains scoped to this identity" in text def test_strip_markdown(): From e179c5240454f899e36975d30bad2f074ceee33e Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Mon, 20 Jul 2026 02:53:25 +0000 Subject: [PATCH 3/6] ci: test against prerelease SDK source --- .github/workflows/tests.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f3086ad..1ad25c3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,14 +44,20 @@ jobs: with: python-version: "3.12" + - uses: astral-sh/setup-uv@v8.1.0 + with: + version: "latest" + - uses: actions/setup-node@v7 with: node-version: 22 - name: Install bridge + latest SDK run: | - pip install -e . pytest - pip install -U claude-agent-sdk + uv pip install --system \ + "inkbox @ git+https://github.com/inkbox-ai/inkbox.git@b2bb11a45c9f4dc89f3bcfbadbd4de5d66d0e9e5#subdirectory=sdk/python" \ + -e . pytest + uv pip install --system -U claude-agent-sdk - name: Install latest Claude Code CLI run: npm install -g @anthropic-ai/claude-code From 74915e0fb727ba8c434e38be764fed52e458a50e Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Mon, 20 Jul 2026 04:43:01 +0000 Subject: [PATCH 4/6] Keep contact access identity-scoped --- README.md | 4 +--- inkbox_claude/prompts.py | 7 +++---- inkbox_claude/tools.py | 8 ++++---- tests/test_prompts.py | 4 +--- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ea6187c..3a5f4f2 100644 --- a/README.md +++ b/README.md @@ -237,9 +237,7 @@ The agent reaches you (or third parties) through an in-process MCP server: - `inkbox_list_text_conversations` · `inkbox_get_text_conversation` — browse SMS threads and history. - `inkbox_list_imessage_conversations` · `inkbox_get_imessage_conversation` — browse iMessage threads and history (find the `conversation_id` to send into). - `inkbox_lookup_contact` · `inkbox_list_contacts` · `inkbox_get_contact` — resolve and read address-book contacts (reverse-lookup by email/phone, free-text search, or full record by id). -- `inkbox_create_contact` · `inkbox_update_contact` · `inkbox_delete_contact` — save, edit, and remove organization-wide contacts. vCard export/import is not exposed. - -Contacts and generated contact facts are organization-wide. Contact `notes` remain user-managed profile text, separate from source-grounded generated facts. Correspondence still follows the configured identity's channel permissions. Unified correspondence and generated-fact tools are deferred until the installed SDK exposes those resources; this plugin does not use raw API calls as a substitute. +- `inkbox_create_contact` · `inkbox_update_contact` · `inkbox_delete_contact` — save, edit, and remove contacts. Reads and writes are filtered server-side to what this identity may see. vCard export/import is not exposed. On a live call, the OpenAI Realtime voice agent additionally gets `consult_agent`, `register_post_call_action` / `edit_post_call_action` / `delete_post_call_action`, and `hang_up_call` — see [Voice](#voice). diff --git a/inkbox_claude/prompts.py b/inkbox_claude/prompts.py index 4a9bcbd..fbceab3 100644 --- a/inkbox_claude/prompts.py +++ b/inkbox_claude/prompts.py @@ -70,7 +70,7 @@ # Inkbox contacts -Claude can read and write organization-wide Inkbox contacts. +Claude can read and write Inkbox contacts visible to this configured identity. - Use inkbox_list_contacts for name-based searches like "who is Alex?". - Use inkbox_lookup_contact when you have an exact or partial email/phone filter. @@ -78,9 +78,8 @@ - Use inkbox_create_contact when the user asks you to save a new person or contact card. - Use inkbox_update_contact when the user asks you to change an existing contact; look up the contact first if you do not already have its UUID. - Use inkbox_delete_contact only after the target contact is explicit and confirmed. -- There is no vCard export/import or contact rule tool in this harness. Contacts do not have per-identity access grants. -- Contact notes are user-managed profile text. Generated contact facts are separate, source-grounded organization-wide memory; do not copy or overwrite generated facts through contact notes. -- Contact correspondence remains scoped to this identity's authorized channels. The installed SDK exposes neither unified correspondence nor generated-fact reads, so do not claim those tools exist or reconstruct them with raw requests. +- There is no vCard export/import, contact access, or contact rule tool in this harness. +- Contact tools operate only on contacts visible/writable to the configured identity. """.strip() diff --git a/inkbox_claude/tools.py b/inkbox_claude/tools.py index 1206ffa..6eae9c5 100644 --- a/inkbox_claude/tools.py +++ b/inkbox_claude/tools.py @@ -587,8 +587,8 @@ def _run(): return _error(str(exc)) # ------------------------------------------------------------------ - # Contacts and generated facts are organization-wide. Channel history is - # still scoped to this identity. + # Contacts — the org address book, filtered server-side to what this + # identity may see. # ------------------------------------------------------------------ @tool( @@ -615,7 +615,7 @@ def _run(): @tool( "inkbox_list_contacts", "Search the address book by free text (matches name, company, job " - "title, user-managed notes). Use for name-based queries like 'find Ada'. " + "title, notes). Use for name-based queries like 'find Ada'. " "order is 'recent' or 'name'.", {"q": str, "order": str, "limit": int}, ) @@ -649,7 +649,7 @@ def _run(): @tool( "inkbox_create_contact", "Save a new contact in the address book. Provide any of given_name, " - "family_name, preferred_name, company_name, job_title, user-managed notes, and " + "family_name, preferred_name, company_name, job_title, notes, and " "emails / phones as lists of strings (first entry is marked primary).", {"given_name": str, "family_name": str, "preferred_name": str, "company_name": str, "job_title": str, "notes": str, diff --git a/tests/test_prompts.py b/tests/test_prompts.py index e0e7022..8e8a0b6 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -56,13 +56,11 @@ def test_channel_prompt_mentions_identity_and_dir(): assert "dev-agent@inkbox.ai" in text assert "jargon" in text.lower() assert "AskUserQuestion" in text - assert "organization-wide Inkbox contacts" in text + assert "Claude can read and write Inkbox contacts" in text assert "inkbox_create_contact" in text assert "inkbox_update_contact" in text assert "inkbox_delete_contact" in text assert "vCard export/import" in text - assert "Generated contact facts are separate" in text - assert "correspondence remains scoped to this identity" in text def test_strip_markdown(): From 4d47c85fb34ba82498f50c41df12ae0ec249af7b Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Mon, 20 Jul 2026 04:44:19 +0000 Subject: [PATCH 5/6] Pin validation to updated SDK source --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ad25c3..a5be41e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -55,7 +55,7 @@ jobs: - name: Install bridge + latest SDK run: | uv pip install --system \ - "inkbox @ git+https://github.com/inkbox-ai/inkbox.git@b2bb11a45c9f4dc89f3bcfbadbd4de5d66d0e9e5#subdirectory=sdk/python" \ + "inkbox @ git+https://github.com/inkbox-ai/inkbox.git@bcff3526d700c2be6546e7ebdbff60073a6ec7e0#subdirectory=sdk/python" \ -e . pytest uv pip install --system -U claude-agent-sdk From 034d1247bf78af230a6aa0cd1f31376a7e690f8b Mon Sep 17 00:00:00 2001 From: alex-w-99 Date: Mon, 20 Jul 2026 06:41:10 +0000 Subject: [PATCH 6/6] Pin tests to corrected SDK head --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5be41e..2709a5e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -55,7 +55,7 @@ jobs: - name: Install bridge + latest SDK run: | uv pip install --system \ - "inkbox @ git+https://github.com/inkbox-ai/inkbox.git@bcff3526d700c2be6546e7ebdbff60073a6ec7e0#subdirectory=sdk/python" \ + "inkbox @ git+https://github.com/inkbox-ai/inkbox.git@8f4b37c56cd153a2acc6cfabe27dbbeb43510f00#subdirectory=sdk/python" \ -e . pytest uv pip install --system -U claude-agent-sdk