Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm install -D @opencode-ai/plugin@latest
- name: Install dependencies from the unpublished SDK source
run: |
npm ci --prefix .ci/inkbox/sdk/typescript
npm run build --prefix .ci/inkbox/sdk/typescript
npm install --no-save --package-lock=false \
./.ci/inkbox/sdk/typescript \
@opencode-ai/sdk@latest @opencode-ai/plugin@latest
- run: npm run lint
- run: npm run typecheck
- run: npm test
Expand All @@ -27,11 +37,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- name: Install dependencies from the unpublished SDK source
run: |
npm ci --prefix .ci/inkbox/sdk/typescript
npm run build --prefix .ci/inkbox/sdk/typescript
npm install --no-save --package-lock=false \
./.ci/inkbox/sdk/typescript \
@opencode-ai/sdk@1.17.18 @opencode-ai/plugin@1.17.18
- run: npm install -g opencode-ai@latest
- run: bash scripts/smoke-loader.sh

Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/live-a2a.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Live — Agent2Agent

# Four real protocol legs cover both roles and conversation lengths:
# inbound/outbound × single-turn/multi-turn. The plugin and remote identities
# are preconfigured to allow one another in both directions.
on:
workflow_call:
inputs:
timeout_s:
description: "Seconds to wait for each A2A transition"
required: false
type: string
default: "300"
orchestrated:
description: "True when the full-stack workflow owns the shared live lock"
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
timeout_s:
description: "Seconds to wait for each A2A transition"
required: false
default: "300"

permissions:
contents: read

concurrency:
group: ${{ inputs.orchestrated && format('inkbox-live-child-{0}', github.run_id) || 'inkbox-live-aut-tunnel' }}
cancel-in-progress: false
queue: max

jobs:
a2a:
runs-on: ubuntu-latest
timeout-minutes: 40
strategy:
fail-fast: false
max-parallel: 1
matrix:
scenario:
- inbound-single
- inbound-multi
- outbound-single
- outbound-multi

steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm

- uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Install protocol driver
run: pip install 'inkbox==0.5.6'

- name: Install plugin and host
run: |
npm ci
npm install --no-save --package-lock=false \
@inkbox/sdk@0.5.6 \
@opencode-ai/sdk@1.17.18 @opencode-ai/plugin@1.17.18
npm install -g opencode-ai@latest

- name: Boot the AUT gateway
env:
MODE: real
AUT_INKBOX_API_KEY: ${{ secrets.AUT_INKBOX_API_KEY }}
AUT_INKBOX_SIGNING_KEY: ${{ secrets.AUT_INKBOX_SIGNING_KEY }}
INKBOX_BASE_URL: ${{ vars.INKBOX_BASE_URL || 'https://inkbox.ai' }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: bash scripts/live-aut.sh

- name: Run ${{ matrix.scenario }}
env:
A2A_SCENARIO: ${{ matrix.scenario }}
A2A_TIMEOUT_S: ${{ inputs.timeout_s || '300' }}
AUT_INKBOX_API_KEY: ${{ secrets.AUT_INKBOX_API_KEY }}
REMOTE_INKBOX_API_KEY: ${{ secrets.REMOTE_INKBOX_API_KEY }}
INKBOX_BASE_URL: ${{ vars.INKBOX_BASE_URL || 'https://inkbox.ai' }}
run: python3 tests/live/a2a_driver.py

- name: Dump gateway logs on failure
if: failure()
run: |
tail -n 300 "$AUT_GATEWAY_LOG" 2>/dev/null || true
tail -n 100 "$AUT_SERVE_LOG" 2>/dev/null || true

- name: Stop gateway
if: always()
run: |
kill "$AUT_GATEWAY_PID" 2>/dev/null || true
kill "$AUT_SERVE_PID" 2>/dev/null || true
14 changes: 13 additions & 1 deletion .github/workflows/live-channels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,22 @@ jobs:

steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- name: Install dependencies from the unpublished SDK source
run: |
npm ci --prefix .ci/inkbox/sdk/typescript
npm run build --prefix .ci/inkbox/sdk/typescript
npm install --no-save --package-lock=false \
./.ci/inkbox/sdk/typescript \
@opencode-ai/sdk@1.17.18 @opencode-ai/plugin@1.17.18
- run: npm install -g opencode-ai@latest

- name: Start mock model
Expand All @@ -69,6 +80,7 @@ jobs:
- name: Boot the AUT gateway (${{ matrix.mode }})
env:
MODE: ${{ matrix.mode }}
INKBOX_SDK_PATH: ${{ github.workspace }}/.ci/inkbox/sdk/typescript
AUT_INKBOX_API_KEY: ${{ secrets.AUT_INKBOX_API_KEY }}
AUT_INKBOX_SIGNING_KEY: ${{ secrets.AUT_INKBOX_SIGNING_KEY }}
INKBOX_BASE_URL: ${{ vars.INKBOX_BASE_URL }}
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/live-external-events.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,28 @@ jobs:

steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- name: Install dependencies from the unpublished SDK source
run: |
npm ci --prefix .ci/inkbox/sdk/typescript
npm run build --prefix .ci/inkbox/sdk/typescript
npm install --no-save --package-lock=false \
./.ci/inkbox/sdk/typescript \
@opencode-ai/sdk@1.17.18 @opencode-ai/plugin@1.17.18
- run: npm install -g opencode-ai@latest

- name: Boot the AUT gateway (real model, external events on)
env:
MODE: real
INKBOX_SDK_PATH: ${{ github.workspace }}/.ci/inkbox/sdk/typescript
AUT_INKBOX_API_KEY: ${{ secrets.AUT_INKBOX_API_KEY }}
AUT_INKBOX_SIGNING_KEY: ${{ secrets.AUT_INKBOX_SIGNING_KEY }}
INKBOX_BASE_URL: ${{ vars.INKBOX_BASE_URL }}
Expand Down
26 changes: 21 additions & 5 deletions .github/workflows/live-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,22 @@ jobs:
orchestrated: true
secrets: inherit

voice:
a2a:
needs: channels
if: >
!cancelled() &&
((github.event_name == 'pull_request' && github.event.pull_request.draft == false &&
github.event.pull_request.head.repo.full_name == github.repository) ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main'))
uses: ./.github/workflows/live-a2a.yml
with:
orchestrated: true
secrets: inherit

voice:
needs: a2a
if: >
!cancelled() &&
((github.event_name == 'pull_request' && github.event.pull_request.draft == false &&
Expand Down Expand Up @@ -62,7 +76,7 @@ jobs:

full-stack:
name: full-stack
needs: [channels, voice, external-events]
needs: [channels, a2a, voice, external-events]
if: >
!cancelled() &&
((github.event_name == 'pull_request' && github.event.pull_request.draft == false &&
Expand All @@ -75,11 +89,12 @@ jobs:
- name: Require every live suite to pass
env:
CHANNELS_RESULT: ${{ needs.channels.result }}
A2A_RESULT: ${{ needs.a2a.result }}
VOICE_RESULT: ${{ needs.voice.result }}
EXTERNAL_EVENTS_RESULT: ${{ needs.external-events.result }}
run: |
failed=0
for suite in CHANNELS VOICE EXTERNAL_EVENTS; do
for suite in CHANNELS A2A VOICE EXTERNAL_EVENTS; do
result_var="${suite}_RESULT"
result="${!result_var}"
echo "$suite: $result"
Expand All @@ -88,13 +103,14 @@ jobs:
exit "$failed"

notify:
needs: [channels, voice, external-events, full-stack]
needs: [channels, a2a, voice, external-events, full-stack]
if: >
always() && github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'schedule' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main' &&
(needs.channels.result != 'success' || needs.voice.result != 'success' ||
(needs.channels.result != 'success' || needs.a2a.result != 'success' ||
needs.voice.result != 'success' ||
needs.external-events.result != 'success' || needs.full-stack.result != 'success')
runs-on: ubuntu-latest
timeout-minutes: 2
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/live-voice.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,28 @@ jobs:

steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
node-version: 22
cache: npm
- run: npm ci
- name: Install dependencies from the unpublished SDK source
run: |
npm ci --prefix .ci/inkbox/sdk/typescript
npm run build --prefix .ci/inkbox/sdk/typescript
npm install --no-save --package-lock=false \
./.ci/inkbox/sdk/typescript \
@opencode-ai/sdk@1.17.18 @opencode-ai/plugin@1.17.18
- run: npm install -g opencode-ai@latest

- name: Boot the AUT gateway (voice; ${{ matrix.scenario }})
env:
MODE: real
INKBOX_SDK_PATH: ${{ github.workspace }}/.ci/inkbox/sdk/typescript
AUT_INKBOX_API_KEY: ${{ secrets.AUT_INKBOX_API_KEY }}
AUT_INKBOX_SIGNING_KEY: ${{ secrets.AUT_INKBOX_SIGNING_KEY }}
INKBOX_BASE_URL: ${{ vars.INKBOX_BASE_URL }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: 199bbd27c8dab2f70e379de52ca9cc910b0e141d
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
Expand All @@ -39,7 +39,7 @@ jobs:
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: 199bbd27c8dab2f70e379de52ca9cc910b0e141d
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
Expand All @@ -65,7 +65,7 @@ jobs:
- uses: actions/checkout@v7
with:
repository: inkbox-ai/inkbox
ref: 199bbd27c8dab2f70e379de52ca9cc910b0e141d
ref: fdea6e55ac117f246624852aedeef0feabe08b9a
path: .ci/inkbox
- uses: actions/setup-node@v7
with:
Expand Down
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# Changelog

## 0.1.1 (unreleased)

- Adds identity-bound A2A 1.0 client tools plus durable inbound task serving:
context-scoped sessions, restart catch-up, task-addressed cancellation, and
explicit complete/ask/fail intents. Outbound calls and replies use the
existing approval and recipient-allowlist controls.
- Adds paginated task and message history with direction, participant,
lifecycle, context, role, keyword, and timestamp filters.
- The plugin requires `@inkbox/sdk` 0.5.6 or newer.

## 0.1.0 (unreleased)

Initial release.

- Requires `@inkbox/sdk` 0.5.1 or newer.
- 48 `inkbox_*` tools across email, SMS/MMS, iMessage, calls, contacts, notes,
contact rules, note access grants, encrypted vault, and diagnostics. 27 are
- Requires `@inkbox/sdk` 0.5.6 or newer.
- 56 `inkbox_*` tools across A2A, email, SMS/MMS, iMessage, calls, contacts,
notes, contact rules, note access grants, encrypted vault, and diagnostics. 35 are
enabled by default; the rest are opt-in via the `tools.enable` plugin option
(`inkbox_doctor` reports what is off and how to enable it).
- Outbound sends and calls gate through opencode's native permission prompts,
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,13 @@ groups are enabled.

## Tools

27 tools are enabled by default; 21 more are opt-in (see
35 tools are enabled by default; 21 more are opt-in (see
[Enabling more tools](#enabling-more-tools)). Names are stable — treat renames
as breaking.

| Group | Enabled by default | Opt-in |
|---|---|---|
| `a2a` | `inkbox_a2a_call`, `inkbox_a2a_check`, `inkbox_a2a_reply`, `inkbox_list_a2a_tasks`, `inkbox_list_a2a_messages`, `inkbox_a2a_complete`, `inkbox_a2a_ask_caller`, `inkbox_a2a_fail` | — |
| `email` | `inkbox_send_email`, `inkbox_list_unread_emails`, `inkbox_list_emails`, `inkbox_get_email`, `inkbox_get_email_thread` | `inkbox_forward_email`, `inkbox_mark_emails_read` |
| `sms` | `inkbox_send_sms`, `inkbox_list_text_conversations`, `inkbox_get_text_conversation` | `inkbox_list_texts`, `inkbox_get_text`, `inkbox_mark_text_read`, `inkbox_mark_text_conversation_read` |
| `imessage` | `inkbox_send_imessage`, `inkbox_list_imessage_conversations`, `inkbox_get_imessage_conversation` | `inkbox_imessage_triage_number`, `inkbox_list_imessage_assignments`, `inkbox_send_imessage_reaction`, `inkbox_mark_imessage_conversation_read` |
Expand Down Expand Up @@ -154,10 +155,11 @@ export default async (input: any) => InkboxPlugin(input, {

## Outbound safety

Sends and calls are gated before anything leaves:
Sends, calls, and A2A writes are gated before anything leaves:

- **Approval prompts** (default): `inkbox_send_email`, `inkbox_send_sms`,
`inkbox_send_imessage`, `inkbox_forward_email`, and `inkbox_place_call`
`inkbox_send_imessage`, `inkbox_forward_email`, `inkbox_place_call`,
`inkbox_a2a_call`, and `inkbox_a2a_reply`
request approval through opencode's native permission system. Approve once,
or persist an allow rule from the prompt.
- **Recipient allowlist**: set `outbound.allowedRecipients` (exact email
Expand Down
Loading