Skip to content

[Pi] typebox should be peerDependencies — as a runtime dependency the plugin both installs and bundles a second copy next to Pi's pinned 1.3.27 #525

Description

@trim21

What happened?

packages/pi-plugin/package.json declares typebox as a regular runtime dependency:

"dependencies": {
  ...
  "typebox": "^1.3.1",
  "zod": "^4.1.8"
}

The host this plugin runs inside pins the same library to an exact version — @earendil-works/pi-agent-core, @earendil-works/pi-ai and @earendil-works/pi-coding-agent all declare "typebox": "1.3.27".

Two things follow from that:

  1. ^1.3.1 resolves to the newest 1.x, so installing the plugin pulls a second copy into the tree. In my Pi extension install (~/.pi/agent/npm/pnpm-lock.yaml) the typebox that ends up hoisted is magic-context's:
  '@cortexkit/pi-magic-context@0.42.6(@types/node@26.6.2)':
    dependencies:
      typebox: 1.3.34
  1. The build inlines it. dist/index-r918mh6w.js ships TypeBox's runtime, with bundler comments keeping the source path:
// ../../node_modules/.bun/typebox@1.3.7/node_modules/typebox/build/system/memory/metrics.mjs
// ../../node_modules/.bun/typebox@1.3.7/node_modules/typebox/build/guard/guard.mjs

So the plugin executes its own copy (built against whatever version bun resolved at build time) while Pi executes 1.3.27 — two different instances of the same library inside one process, plus a third version installed next to it in node_modules.

Expected

TypeBox is part of the host API surface: extension code hands TSchema values to Pi's tool registration and validation, so schema objects cross the plugin/host boundary. It should be host-provided, the way @earendil-works/* already is:

"peerDependencies": {
  "@earendil-works/pi-tui": ">=0.80.2",
  "@earendil-works/pi-coding-agent": ">=0.80.2",
  "typebox": ">=1.3.1"
},
"devDependencies": {
  "typebox": "1.3.27"
}

with the dev-only copy pinned to whatever Pi currently pins, so local type-checking matches the runtime. Marking the peer optional (peerDependenciesMeta) is fine if you would rather not require it — the point is that it should not be a hard runtime dependency of the published plugin, nor bundled into dist/.

Impact

  • Three typebox instances per install: one in node_modules, one bundled in dist/, and neither is the version Pi runs.
  • Plugin and host can drift apart silently; Pi pins its own exactly, so nothing keeps them aligned.
  • Extensions installed alongside that declare typebox correctly as a peer dependency inherit 1.3.34 from this dependency edge instead of Pi's pinned 1.3.27. That is how I ran into it: a second extension in the same install ended up resolving typebox from magic-context's edge rather than from the host.

Environment

  • @cortexkit/pi-magic-context 0.42.6 (current latest)
  • Harness: Pi (the same package serves OMP)
  • Platform: linux x64

No runtime diagnostics apply here — this is a dependency-declaration/packaging issue rather than a runtime failure, so doctor --issue output would not add anything.

Activity

  1. magic-alfonso commented on Sep 24, 2026

    @magic-alfonso

    Thanks, confirmed. typebox is a regular dependency in packages/pi-plugin/package.json (^1.3.1), and the build doesn't mark it external, so it's also bundled into dist/. Schemas cross into Pi's tool registration, so it should come from the host. We'll make it a peer dependency, stop bundling it, and pin the dev copy to Pi's version.

    Before we ship, we need to verify one thing on a real install: that OMP also resolves a typebox the plugin can import. OMP has its own host package, and this plugin serves both. This will come after the release that's in final checks now, since it changes how the Pi package installs.

  2. trim21 commented on Sep 24, 2026

    @trim21
    Author

    I don't use omp so I'm not sure

  3. magic-alfonso commented on Sep 25, 2026

    @magic-alfonso

    We tried the change you suggested: TypeBox as a peer dependency, left out of the bundle. On Pi (0.87.1, installed with npm and with pnpm) the plugin loads fine that way. On Oh My Pi (18.3.1) it breaks every install: OMP doesn't ship the typebox package. Its extension loader rewrites every bare typebox import to its own compatibility shim, and the schemas that shim builds can't be cloned, so the plugin fails to load with "The object can not be cloned". Since one package serves both hosts, TypeBox has to stay bundled for now. We added a test so it doesn't get externalized again by accident.

    To look for a fix that works on both hosts: what concrete problem does the bundled copy cause you? For example package size, a version clash with another extension, or a check that compares TypeBox classes or symbols across copies. A short description, or the error you see, would help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions