Skip to content

feat(acp): export typed ACP registry + CI drift check vs openhands-sdk#173

Merged
simonrosenberg merged 1 commit into
mainfrom
feat-acp-registry
May 20, 2026
Merged

feat(acp): export typed ACP registry + CI drift check vs openhands-sdk#173
simonrosenberg merged 1 commit into
mainfrom
feat-acp-registry

Conversation

@simonrosenberg
Copy link
Copy Markdown
Contributor

@simonrosenberg simonrosenberg commented May 20, 2026

Summary

Expose the ACP provider registry from @openhands/typescript-client so consumers (notably agent-canvas) can stop maintaining their own hand-kept TypeScript mirrors of openhands.sdk.settings.acp_providers.ACP_PROVIDERS, and add a CI check that fails the build if the TS-side registry drifts from the Python source of truth.

Closes agent-canvas#587 (drift risk) and unblocks a follow-up agent-canvas PR that deletes src/constants/acp-providers.ts and pins this client.

Design

  • src/models/acp-providers.json — the registry data, single source of truth on this side. Hand-edited to match openhands.sdk.settings.acp_providers.ACP_PROVIDERS field-for-field.
  • src/models/acp.ts — thin types-and-helpers layer that imports the JSON and exports:
    • ACPProviderKey literal type ('claude-code' | 'codex' | 'gemini-cli'; no 'custom' — that's a UI-side discriminator with no registry entry)
    • ACPProviderInfo interface mirroring the Python dataclass
    • ACP_PROVIDERS: Readonly<Record<ACPProviderKey, ACPProviderInfo>>
    • getAcpProvider(key) lookup
    • ACP_SETTINGS_KEYS allow-list (for clients filtering ACP-only fields out of non-ACP payloads)
  • scripts/check-acp-drift.py — single Python script. Imports openhands.sdk.settings.acp_providers.ACP_PROVIDERS from a pinned install, reads src/models/acp-providers.json, normalises both to JSON, diffs. Exits non-zero with a unified diff on mismatch.
  • CI job validate-acp-providers — Python 3.12 + pip install -r scripts/requirements-acp-check.txt + python scripts/check-acp-drift.py. No Node, no tsx. Not gated by continue-on-error — drift blocks merging.
  • scripts/copy-json-assets.mjs — small build helper that copies .json from src/ to dist/ after tsc, so the JSON import resolves at runtime in the published package.

Scope decisions

  • No version bump. Purely additive (registry export + CI job). Stays on 0.1.1. The prior version of this PR tightened the ACPAgentConfig interface into a strict shape, which was a type-level breaking change and required a minor bump on 0.x — that change is dropped here. We can revisit type tightening as a separate, deliberate PR when there's a real consumer asking for it.
  • No new TypeScript unit tests for the registry. The drift check IS the snapshot test against the live Python SDK. If the JSON matches Python and the TypeScript compiles, the data is correct by construction.
  • JSON-data architecture rather than parsing TS in Python. Lets the Python script consume the data directly with no Node dependency in CI.

What changed vs. main

.github/workflows/ci.yml           |  +30 / -1
CONTRIBUTING.md                    |  +13 / -0
package.json                       |   +1 / -1
scripts/check-acp-drift.py         |  +81 (new)
scripts/copy-json-assets.mjs       |  +39 (new)
scripts/requirements-acp-check.txt |   +5 (new)
src/index.ts                       |   +4 / -0
src/models/acp-providers.json      |  +35 (new)
src/models/acp.ts                  |  +79 (new)

287 added / 7 deleted across 9 files. (For context, an earlier version of this PR carried ~1,292 added lines across 12 files plus a 0.2.0 version bump; that scope was trimmed per review.)

Verified locally

  • npm run build — succeeds, including the JSON-copy step. dist/models/acp-providers.json ships alongside the compiled .js.
  • npm test — 199 / 199 passing.
  • npm run lint — clean (warnings pre-existing in this branch).
  • npm run format:check — clean.
  • python scripts/check-acp-drift.py against the live openhands-sdk — reports OK: src/models/acp-providers.json matches openhands-sdk (3 providers). and exits 0.
  • Intentionally deleted the codex entry from acp-providers.json and re-ran — exits 1 with a unified diff pointing at the missing block. Restored.

Test plan

  • CI: test, build, security, Integration Tests, validate-acp-providers all pass on this PR.
  • Reviewer can run python scripts/check-acp-drift.py locally after pip install -r scripts/requirements-acp-check.txt and observe OK: ….
  • Reviewer can confirm dist/models/acp-providers.json is present after npm run build.

Next steps

  • After merge + publish (regular patch, no major bump needed), agent-canvas follow-up will:
    • Bump @openhands/typescript-client
    • Replace src/constants/acp-providers.ts with re-exports from the SDK
    • Delete the local mirror

References

  • agent-canvas#587 — drift risk that motivated this PR
  • openhands.sdk.settings.acp_providers in software-agent-sdk — the canonical Python source

🤖 Generated with Claude Code

@simonrosenberg
Copy link
Copy Markdown
Contributor Author

@OpenHands fix CI errors

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 20, 2026

I'm on it! simonrosenberg can track my progress at all-hands.dev

simonrosenberg pushed a commit that referenced this pull request May 20, 2026
Two CI failures on PR #173:

1. CI/security (npm audit): brace-expansion 5.0.2-5.0.5 (GHSA-jxxr-4gwj-5jf2)
   and ws 8.0.0-8.20.0 (GHSA-58qx-3vcg-4xpx) advisories landed between
   the last main run and this branch. ws clears via npm audit fix; the
   vulnerable brace-expansion@5.0.5 comes from eslint -> minimatch@10,
   so nest a 'brace-expansion: ^5.0.6' override under the existing
   eslint.minimatch entry. Audit is clean (0 vulnerabilities).

2. Integration Tests/integration-test (TS compile error): the
   deterministic ACP test was passing { kind: 'Agent', llm: {...} } to
   manager.acp.createConversation, which only typechecked under the old
   pass-through ACPAgentConfig. Update it to a real ACPAgentConfig with
   kind: 'ACPAgent', acp_server: 'claude-code', and acp_command sourced
   from ACP_PROVIDERS['claude-code'].default_command. Still pure CRUD
   (create / count / get / batch-get), so no ACP subprocess is spawned.

Local: npm run lint, build, test (216/216), format:check, audit all green.

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Copy Markdown
Contributor Author

Pushed 5daddb7 fixing both CI failures:

  1. CI/security (npm audit) — Two new GHSA advisories landed since the last main run: brace-expansion@5.0.2–5.0.5 (GHSA-jxxr-4gwj-5jf2) and ws@8.0.0–8.20.0 (GHSA-58qx-3vcg-4xpx).

    • ws clears via plain npm audit fix (transitive lockfile bump).
    • The vulnerable brace-expansion@5.0.5 comes from eslint → minimatch@10. I nested a "brace-expansion": "^5.0.6" override under the existing eslint.minimatch entry rather than adding a top-level override (which would also bump unrelated v1.x / v2.x trees from glob / test-exclude). Audit is now clean — 0 vulnerabilities.
  2. Integration Tests / integration-test (TS compile error)src/__tests__/integration/deterministic-api.integration.test.ts:138 was passing { kind: 'Agent', llm: { ... } } to manager.acp.createConversation, which only typechecked under the old pass-through ACPAgentConfig. That's exactly the kind of accidental cross-shape coupling this PR is trying to kill, so I updated the test to send a real ACPAgentConfig (kind: 'ACPAgent', acp_server: 'claude-code', acp_command sourced from ACP_PROVIDERS['claude-code'].default_command). The test still only does CRUD (create / count / get / batch-get), so no ACP subprocess is spawned server-side.

Local sanity: npm run lint, npm run build, npm test (216/216), npm run format:check, npm audit --audit-level=moderate all green. Watching the CI rerun now.

This comment was created by an AI agent (OpenHands) on behalf of the user.

@openhands-ai
Copy link
Copy Markdown

openhands-ai Bot commented May 20, 2026

Summary

Request: Fix CI errors on PR #173 (feat-acp-registry).

Done: ✅ All 7 CI checks now pass on commit 5daddb7.

Checklist

  • Diagnosed both failing checks via gh CLI logs (CI/security, Integration Tests/integration-test).
  • Fixed CI/security — two fresh GHSA advisories on transitive deps:
    • ws@8.0.0–8.20.0 resolved via npm audit fix (lockfile-only).
    • brace-expansion@5.0.5 (pulled in by eslint → minimatch@10) resolved by nesting a "brace-expansion": "^5.0.6" override under the existing eslint.minimatch entry — narrowly targeted so glob's v1.x and test-exclude's v2.x trees stay untouched.
  • Fixed Integration Tests/integration-test — TS compile error at deterministic-api.integration.test.ts:138. The test was passing { kind: 'Agent', llm: { ... } } to manager.acp.createConversation, which only typechecked under the old pass-through ACPAgentConfig. Updated it to a real ACPAgentConfig (kind: 'ACPAgent', acp_server: 'claude-code', acp_command sourced from ACP_PROVIDERS['claude-code'].default_command). Test scope unchanged (CRUD only — no ACP subprocess spawned).
  • Verified locally: lint, build, 216/216 unit tests, format:check, and npm audit --audit-level=moderate (0 vulnerabilities).
  • Pushed to feat-acp-registry; watched CI to green (security, build, test (20.x), test (22.x), validate-acp-providers, smoke-test, integration-test all pass).
  • Posted an explanatory comment on the PR.

Conciseness

Only three files changed: package.json (5 lines for the override), package-lock.json (regenerated by npm), and the integration test (5 lines: import + 3-line payload swap). No extraneous changes — the PR's intended scope (typed ACP registry) is untouched.

@simonrosenberg simonrosenberg added the acp ACP Agents label May 20, 2026
@simonrosenberg simonrosenberg self-assigned this May 20, 2026
…-sdk

Mirror `openhands.sdk.settings.acp_providers.ACP_PROVIDERS` from
software-agent-sdk so downstream TS consumers (notably agent-canvas)
can stop hand-keeping their own copies, and add a CI check that fails
the build if the mirror drifts from the Python source of truth.

Design:

- `src/models/acp-providers.json` is the single source of truth on the
  TS side — pure data, hand-edited to match the Python registry.
- `src/models/acp.ts` is a thin types-and-helpers layer that imports
  the JSON. Exports `ACPProviderKey`, `ACPProviderInfo`, `ACP_PROVIDERS`,
  `getAcpProvider`, and `ACP_SETTINGS_KEYS`.
- `scripts/check-acp-drift.py` reads the JSON directly and diffs against
  the live `ACP_PROVIDERS` from a pinned `openhands-sdk` install. Exits
  non-zero with a unified diff on mismatch.
- CI job `validate-acp-providers` runs the Python script (no Node, no
  tsx). Not gated by continue-on-error — drift blocks merging.
- `scripts/copy-json-assets.mjs` copies `.json` from `src/` to `dist/`
  after `tsc` so the JSON import resolves at runtime in the published
  package.

Purely additive — no version bump needed, no breaking type changes.
The `ACPAgentConfig` placeholder in `src/models/conversation.ts` is
left untouched; tightening can come later as a separate, deliberate PR.

Closes part of agent-canvas#587.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@simonrosenberg simonrosenberg merged commit f6a4948 into main May 20, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

acp ACP Agents

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant