Plugin SDK package boundaries enforced by TypeScript
Extensions can no longer sneak illegal imports past the type checker—a new @openclaw/plugin-sdk package and boundary tsconfig now catch cross-package violations at compile time.
TypeScript could not enforce where bundled extensions fetched their dependencies. Import paths like looked valid inside an extension's local editing context because the repo carried a permissive root alias. Nothing failed until runtime—or worse, until production.
A new @openclaw/plugin-sdk workspace package now provides a proper import surface. The xAI extension has been migrated to use it: instead of openclaw/plugin-sdk/provider-web-search, imports now read @openclaw/plugin-sdk/provider-web-search. A new tsconfig base () wires those paths to generated declaration files, and TypeScript now fails with TS6059 when an extension tries to reach outside its own package root.
The change is surgical—only the xAI extension is opted in, as a canary. Existing bundled extensions continue working through the legacy alias. The groundwork is laid for other extensions to adopt stricter boundaries incrementally. Plugin authors who accidentally reach into forbidden source directories will now hear about it from the compiler, not from a production incident.
View Original GitHub DescriptionFact Check
Summary
- Problem: bundled extensions still rely on the root
openclaw/plugin-sdk/*path alias, so TypeScript does not fail closed when an extension reaches outside its own package root. - Why it matters: an illegal import like
../../src/cli/acp-cli.tscan look valid in normal extension-local editing unless a separate boundary script catches it. - What changed: I added a real internal
@openclaw/plugin-sdkworkspace package, optedextensions/xaiinto a package-boundary tsconfig, and added focused contract tests that prove clean typechecking plusTS6059failure for a bad relative import. - What did NOT change (scope boundary): I did not migrate the rest of the extension tree yet, and this first canary still bridges types through generated plugin-sdk declaration artifacts.
Change Type (select all)
- Bug fix
- Feature
- Refactor required for the fix
- Docs
- Security hardening
- Chore/infra
Scope (select all touched areas)
- Gateway / orchestration
- Skills / tool execution
- Auth / tokens
- Memory / storage
- Integrations
- API / contracts
- UI / DX
- CI/CD / infra
Linked Issue/PR
- Closes #
- Related #
- This PR fixes a bug or regression
Root Cause (if applicable)
- Root cause: extension-local TypeScript projects were not resolving allowed SDK imports through a real package surface, so adding
rootDiralone either did nothing or broke allowed imports along with forbidden ones. - Missing detection / guardrail: the repo had boundary scripts, but normal extension-local TypeScript checking did not fail closed for opted-in packages.
- Contributing context (if known): the root repo still carries a broad
openclaw/plugin-sdk/*alias for legacy extensions, so the incremental rollout needs both legacy and opt-in modes in parallel.
Regression Test Plan (if applicable)
- Coverage level that should have caught this:
- Unit test
- Seam / integration test
- End-to-end test
- Existing coverage already sufficient
- Target test or file:
src/plugins/contracts/extension-package-project-boundaries.test.tsandtest/extension-package-tsc-boundary.test.ts - Scenario the test should lock in:
extensions/xaitypechecks through@openclaw/plugin-sdk/*, and a canary import of../../src/cli/acp-cli.tsfails withTS6059. - Why this is the smallest reliable guardrail: it exercises the real extension-local
tsconfig.jsonboundary instead of only checking custom lint or report scripts. - Existing test that already covers this (if any): the existing
check-extension-plugin-sdk-boundaryscript still covers the relative-outside-package rule. - If no new test is added, why not: N/A
User-visible / Behavior Changes
None.
Diagram (if applicable)
Before:
[xai source] -> openclaw/plugin-sdk/* root alias -> raw src/plugin-sdk/*.ts
[xai source] -> ../../src/... -> allowed by local tsc
After:
[xai source] -> @openclaw/plugin-sdk/* -> internal package runtime + declaration bridge
[xai source] -> ../../src/... -> TS6059 failure
Security Impact (required)
- New permissions/capabilities? (
Yes/No) No - Secrets/tokens handling changed? (
Yes/No) No - New/changed network calls? (
Yes/No) No - Command/tool execution surface changed? (
Yes/No) No - Data access scope changed? (
Yes/No) No - If any
Yes, explain risk + mitigation:
Repro + Verification
Environment
- OS: macOS
- Runtime/container: local Node 22 workspace
- Model/provider: N/A
- Integration/channel (if any): xAI bundled plugin
- Relevant config (redacted): default repo workspace config
Steps
- Run
./node_modules/.bin/tsc -p tsconfig.plugin-sdk.dts.json. - Run
./node_modules/.bin/tsc -p extensions/xai/tsconfig.json --noEmit. - Add a temporary
import * as foo from "../../src/cli/acp-cli.ts";canary underextensions/xaiand rerun the extension typecheck.
Expected
extensions/xaitypechecks cleanly through@openclaw/plugin-sdk/*.- The canary bad import fails with
TS6059.
Actual
- Verified locally.
Evidence
Attach at least one:
- Failing test/log before + passing after
- Trace/log snippets
- Screenshot/recording
- Perf numbers (if relevant)
Human Verification (required)
- Verified scenarios:
tsc -p tsconfig.plugin-sdk.dts.json,tsc -p extensions/xai/tsconfig.json --noEmit,pnpm test src/plugins/contracts/extension-package-project-boundaries.test.ts test/extension-package-tsc-boundary.test.ts,pnpm test extensions/xai/onboard.test.ts, and the deliberate boundary canary through bothtscandnode scripts/check-extension-plugin-sdk-boundary.mjs --mode=relative-outside-package. - Edge cases checked: the bad relative import canary under
extensions/xaifails while the clean package import path still typechecks. - What you did not verify: I did not migrate the other first-wave extensions yet, and I did not yet remove the declaration bridge from this canary.
Review Conversations
- I replied to or resolved every bot review conversation I addressed in this PR.
- I left unresolved only the conversations that still need reviewer or maintainer judgment.
If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.
Compatibility / Migration
- Backward compatible? (
Yes/No) Yes - Config/env changes? (
Yes/No) No - Migration needed? (
Yes/No) No - If yes, exact upgrade steps:
Risks and Mitigations
- Risk: the canary still depends on generated plugin-sdk declaration artifacts for type resolution.
- Mitigation: I kept the rollout to
xaionly and I am following this with a deeper change to make the package own its source/types end to end.
- Mitigation: I kept the rollout to