Restored the pi-ai OAuth device-code helper expected by legacy provider packages and rewrote extension-owned bare dependencies to file URLs during validation so compiled binaries do not rely on Bun's runtime bare resolver.
Excluded worker entry modules from the compiled legacy bundled registry so validation does not import worker-only code on the main thread.
Fixes#3508
The first pass only enumerated non-wildcard `exports` entries, so
patterns like pi-ai's `./oauth/*` left every concrete target
(`@oh-my-pi/pi-ai/oauth/anthropic` and friends) outside the
bundled registry. Compiled-mode resolution then fell back through
`Bun.resolveSync` → original peer specifier → missing peer dep,
reproducing the original `Cannot find module` failure for any
plugin that imports a wildcard-only subpath (e.g.
`@mariozechner/pi-ai/utils/oauth/anthropic`, remapped via
PI_SUBPATH_REMAPS).
The generator now runs a second pass over wildcard exports,
parses each single-asterisk pattern into prefix/suffix halves,
globs the matching source directory, and emits a registry entry
per concrete `.ts` file. Root catch-all wildcards (`./*` /
`./*.js`) are skipped on purpose — they'd static-import top-level
files like the coding-agent's own `cli.ts` and explode the bundle
through the binary entry's transitive graph. Test, `.d`,
`.generated`, `.bench` files and `index` basenames are filtered
out so the registry stays focused on importable surfaces.
A new test case in
test/extensibility/legacy-pi-bundled-subpath-overrides.test.ts
asserts the reviewer's cited `@oh-my-pi/pi-ai/oauth/anthropic`
key now routes through the virtual namespace and that root
catch-all wildcards remain unbundled.
Fixes#3442
Compiled-binary extension validation rewrote @(scope)/pi-ai/oauth →
@oh-my-pi/pi-ai/oauth, but LEGACY_PI_PACKAGE_ROOT_OVERRIDES only
covered bare package roots. resolveCanonicalPiSpecifier therefore
fell through to Bun.resolveSync, which fails inside bunfs on Bun
1.3.14+, then the rewriteLegacyPiImports catch left the original
specifier alone. Bun's native resolver then failed because most
plugins (e.g. @charmland/pi-hyper-provider) declare @(scope)/pi-ai
as a peerDependency only and never materialize a real install.
A new scripts/generate-legacy-pi-bundled-registry.ts reads every
bundled pi-* package's non-wildcard exports field and emits both
the heavy legacy-pi-bundled-registry.ts (static imports + map) and
a light legacy-pi-bundled-keys.ts. legacy-pi-compat.ts statically
imports the keys file to seed the override map without paying the
legacy-pi-coding-agent-shim → ../index → export/html/... cascade,
so subpath imports now route to the same omp-legacy-pi-bundled:
virtual namespace that already serves the roots.
scripts/build-binary.ts runs the generator before bun build
--compile so new pi-* subpaths added under packages/*/package.json
ship without manual regeneration; --check verifies the committed
output stays in sync.
Fixes#3442
Serve bundled pi-* and TypeBox through an in-process virtual namespace
on Bun 1.3.14+ where `--compile` extras are unreachable via any
filesystem API (issue #3423).
Merge resolution:
- Reconciled `TYPEBOX_SHIM_PATH` with main's #3414 fall-through:
`__resolveTypeBoxShimPath(isCompiled, sourcePath, exists)` returns the
`omp-legacy-pi-bundled:` virtual specifier in compiled mode (no FS
probe) and the on-disk source path otherwise, dropping to null when
the shim file is missing so bare typebox imports fall through to native
resolution.
- Removed the now-dead `--compile` extras path: dropped
`LEGACY_COMPAT_BUILD_ENTRYPOINTS` usage from both build-binary.ts and
ci-release-build-binaries.ts and deleted scripts/binary-entrypoints.ts;
the bundler reaches every surface via legacy-pi-bundled-registry.ts.
- Deleted obsolete tests for the removed bunfs machinery
(legacy-pi-compat-entrypoints, legacy-pi-typebox-shim-validation) and
added regression coverage for __resolveTypeBoxShimPath.
- Dropped the binary-compiling smoke driver per maintainer request.
Verified: bun check clean; 77 extensibility tests pass; instrumented
compiled-binary run confirmed onLoad fires for all bundled specifiers.
Bun 1.3.14 stopped exposing `--compile` extras through every filesystem-style API: `fs.existsSync`, `Bun.file().exists()`, `Bun.resolveSync`, and `await import()` on `/$bunfs/...` or `file:///$bunfs/...` all fail; only `/$bunfs/root/<binary-name>` itself answers. The pre-existing legacy-pi rewrite emitted `file:///$bunfs/...` URLs that Bun then could not load, so every legacy extension that imported `@oh-my-pi/pi-*` or `@sinclair/typebox` failed on the `omp-darwin-arm64` release binary.
`legacy-pi-compat.ts` now keeps a JS-heap reference to every bundled pi-* surface in a lazy-loaded sibling `legacy-pi-bundled-registry.ts` and serves them through an `omp-legacy-pi-bundled:` virtual namespace whose `Bun.plugin().onLoad` synthesizes a re-export module — no bunfs path ever leaves the module in compiled mode. Dev / source-link / installed-package modes keep the historical `file://` rewrite (source files exist on disk). The matching `--compile` extras in `scripts/build-binary.ts` are gone; `BUNFS_PACKAGE_ROOT`, `bunfsPath`, `__computeBunfsPackageRoot`, and `__joinBunfsPath` are deleted as dead code. `scripts/smoke-3423.ts` compiles a tiny binary that loads a fixture extension end-to-end through the new path.
Fixes#3423
- Extracted the legacy `--compile` entrypoint list to `scripts/binary-entrypoints.ts` and consumed it from both the release CI script and the local dev `build-binary.ts`, so the two cannot drift apart.
- `scripts/ci-release-build-binaries.ts` no longer ships release binaries without the typebox shim, legacy pi shims, and `@oh-my-pi/{agent,natives,tui,utils}` package barrels in bunfs. Commit dc5c93462f removed worker entrypoints and false-comment-claimed the legacy entrypoints were "still" listed, so every published `omp-<platform>-<arch>` since shipped without them and the resolver emitted bunfs URLs to missing files.
- Validated TYPEBOX_SHIM_PATH at module init via __resolveTypeBoxShimPath, mirroring __validateLegacyPiPackageRootOverrides (#2168). When the shim is absent the rewriter leaves bare typebox / @sinclair/typebox imports alone so Bun falls through to native node_modules resolution.
- Pinned both halves of the contract with tests: release+dev scripts must source from the shared constant, every shim path computed in legacy-pi-compat.ts must appear in it, and __resolveTypeBoxShimPath drops missing candidates.
Fixes#3414
Migrate 203 test files (356 call sites) from fs.rm/fs.rmSync to
removeWithRetries/removeSyncWithRetries to reduce EBUSY test failures
on Windows. removeWithRetries is now exported from @oh-my-pi/pi-utils.
The migration uses a regex-based approach that:
- Replaces fs.rm(path, { recursive, force }) → removeWithRetries(path)
- Replaces fs.rmSync(path, { recursive, force }) → removeSyncWithRetries(path)
- Replaces fs.rm(path) → removeWithRetries(path) (no options)
- Skips fs.rm/fs.rmSync inside template literals (bun --eval scripts)
- Adds imports to existing @oh-my-pi/pi-utils import or creates new one
- Removes unused fs imports where fs.rm was the only fs usage (4 files)
`__computeBunfsPackageRoot` now returns `//root/packages` for the Bun 1.3.14
`//root/<binary>` import.meta.dir shape, but production immediately joined that
root with shim and package segments through `path.join`, which collapses the
POSIX double-slash bunfs mount back to `/root`. That still made override
validation miss the embedded shim files.
Added a bunfs join helper that preserves the `//root` mount prefix after joining
production descendants, wired `bunfsPath` through it, and extended the #3329
regression test to assert the full typebox shim path stays under
`//root/packages/...`.
Fixes#3329
The reporter clarified that the failing binary is the pre-built
`omp-darwin-arm64` release asset from GitHub Releases; Homebrew is only a
local-tap wrapper that downloads that asset. The fix already covers every
cross-compiled `<bunfs-root>/<binary>` shape, but the source/test docstrings
and changelog blurb framed it as a Homebrew-build-specific bug. Updated those
three call sites to name the release asset and note the Homebrew tap as a
downstream consumer of the same binary; no code change.
Bun 1.3.14 reports `import.meta.dir` as `<bunfs-mount>/<binary-basename>` for
the compiled entry on some hosts — e.g. the Homebrew darwin-arm64 build sees
`//root/omp-darwin-arm64` instead of the bunfs root alone. The pre-fix path
joined `metaDir` with `"packages"` and baked the binary basename into every
bunfs path, so the typebox / legacy-pi shim overrides failed `existsSync`
validation, `resolveCanonicalPiSpecifier` fell through to a bunfs
`Bun.resolveSync` that also could not find the module, and every third-party
`@oh-my-pi/pi-*` extension was silently dropped.
`__computeBunfsPackageRoot` now detects the trailing binary-basename segment
(`path.basename(path.dirname(metaDir)) === "root"`) and strips it off the
original `metaDir` via string slicing rather than `path.join`, so Bun's
bunfs-native `//root` and `B:\~BUN\root` prefixes survive verbatim
(`path.posix.join` would collapse `//root` to `/root`). The single-segment
`<bunfs-root>` and deep `<bunfs>/packages/coding-agent/src/extensibility/plugins`
paths keep their existing branches.
Regression test added in `legacy-pi-bunfs-root.test.ts` for the POSIX
`//root/<bin>`, POSIX `/$bunfs/root/<bin>`, and Win32 `<drive>:\~BUN\root\<bin>.exe`
shapes.
Fixes#3329
- Support bare 'typebox' package specifier in on-resolve hooks and source remapping, mirroring '@sinclair/typebox' remapping onto the in-repo Zod-backed shim.
- Support 'getModel', 'getModels', and 'StringEnum' imports from the '@oh-my-pi/pi-ai' package root in the legacy pi-ai compatibility shim.
Restored StringEnum's plain string enum wire schema and returned legacy coding tool placeholders that let createAgentSession keep the real built-in tools active.
Fixes#2858
Added compatibility exports for legacy plugin validation, including defineTool, StringEnum, TypeBox bare imports, frontmatter helpers, SettingsManager, and createCodingTools.
Updated SDK settingsManager alias handling and regression coverage for the affected shim surfaces.
Fixes#2858
- Imported and injected `zod/v4` under `zod` in runtime environments for custom commands, custom tools, extensions, and hooks.
- Added corresponding Type definitions to `CustomCommandAPI`, `CustomToolAPI`, `ExtensionAPI`, and `HookAPI` interfaces.
- Re-exported Zod and its alias from the main package entry point to expand the public API surface.
- Refactored the internal Typebox validation shim to support parsing via standard `.safeParse()` and `.toJSON()` methods.
- Advanced Typebox shim utility capabilities with support for dual-key record validation and enhanced schema structure handling.
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
Redirected legacy pi-ai utils/oauth subpaths through the compatibility loader so background workers load the relocated OAuth registry modules.\n\nFixes #2566
The package-root override branch of `resolveCanonicalPiSpecifier` returned
the bunfs override path without checking the target was actually present,
so when `bun --compile` quietly dropped one of the extra entrypoints
(observed on macOS arm64 release binaries), the static rewrite emitted a
`file://` URL to a missing module. The #1216 fallback only fired on the
throwing `getResolvedSpecifier` path, so the override never threw and the
rewrite committed the bad URL — extensions silently failed to load.
Each override target is now checked with `fs.existsSync` at module init.
Missing entries are dropped from `LEGACY_PI_PACKAGE_ROOT_OVERRIDES` so
`resolveCanonicalPiSpecifier` falls through to `getResolvedSpecifier`,
which throws under bunfs and triggers the existing rewrite catch — Bun
then resolves the canonical `@oh-my-pi/pi-*` specifier from the
extension's own `node_modules`.
Fixes#2168
- Derived descriptors, default-model map, env keys, login list, and refresh dispatch from one ProviderDefinition per provider.
- Disabled OpenAI Codex stream obfuscation and interrupted whitespace-only tool-call argument deltas.
- Derived auth-broker callback ports and paste-code login set from the registry.
- Aligned handoff, reminder, and system-prompt expectations with shortened copy.
- Added HTTP transport test for required initialize failures.
- Guarded SSE startup timeout against stale connection races.
- Grouped `isRepository`, `workspaceRoot`, and `clearWorkspaceRootCache` under a `repo` namespace (`repo.is`, `repo.root`, `repo.clearRootCache`).
- Promoted `diff` to a named export with a `changedFiles` sub-method via `Object.assign`.
- Added `ensureAvailable` check and `nameOnly` support to `diff`.
- Updated callers and tests to use the new API surface.
- Replaced `jj workspace root` subprocess call with a local `.jj/repo/store` directory traversal, eliminating process spawning overhead.
- Added LRU cache for resolved workspace roots to avoid redundant filesystem walks.
- Renamed internal `runCommand` helpers to `git`/`jj` for clarity.
- Replaced git subprocess setup in review test with mocked `git.status` and `git.diff` calls.
Address review of the in-place loader: the directory-subtree filter had two
regressions vs the old mirror.
- It only rewrote files under the entry's package root, so a `dist/`
entry importing `../../shared/helper.ts` (or a symlink-escaping sibling)
left that module's legacy `@(scope)/pi-*` / `@sinclair/typebox` imports
un-rewritten.
- `findExtensionRoot` walked up to the nearest package.json, which for an
ad-hoc extension under a project (e.g. `/repo/.omp/extensions/foo.ts`)
resolved to the project root — so the permanent onLoad hook would then
rewrite unrelated project/host source imported later.
Replace the directory filter with a precise scope: pre-walk the entry's
relative-import graph (static + dynamic `./`/`../` specifiers), collect each
module's realpath, and build the onLoad filter as an exact-path alternation
of just those modules. This matches exactly the set the old mirror tracked
(minus the copy): it covers `../src`/symlinked siblings and never touches
the host, other extensions, node_modules deps, or unrelated project files.
Adds a regression test that a non-imported sibling stays outside the rewrite
scope, and renames the ../src test to reflect graph-following.
Legacy Pi extensions were mirrored module-by-module into a flat temp dir
(`omp-legacy-pi-file/entry-<hash>/`) with imports rewritten to absolute
URLs. Running from that temp root made `import.meta.url`/`__dirname`
resolve to the mirror, so `readFileSync(join(__dirname, "ui.html"))`-style
asset loads ENOENT'd — e.g. @plannotator/pi-extension's HTML never loaded
and it auto-approved plans (#1674). The standing remedy (#1675) copied
~30MB of .html/.css per startup with a fragile extension whitelist.
Bun's runtime plugins don't fire onResolve for transitive imports, which
is why the mirror pre-resolved everything. But onLoad does fire
transitively for file-namespace modules matching its filter, and a real
file import keeps import.meta.url pointing at the source. So:
- Load the extension entry in place via `import(pathToFileURL(real))`;
realpath first so the path matches what Bun hands onLoad (macOS
/var->/private/var, bun link/pnpm symlinks).
- Register one Bun.plugin() onLoad per extension *package root* (nearest
package.json), filtered to that root's .js/.ts but excluding any
node_modules segment, that rewrites only `@(scope)/pi-*` and the bare
`@sinclair/typebox` specifier to absolute bundled/shim URLs.
- Everything else — relative siblings (incl. ../src), the extension's own
node_modules deps (CJS/ESM), and bundled assets — resolves natively.
Removes the flat mirror, the temp-dir writes, the asset-copy problem, and
the now-dead `omp-legacy-pi-file:` namespace machinery. import.meta.url is
the real source file, so assets resolve exactly as under the original Pi
runtime. Adds an in-place load regression test covering asset reads,
submodule .css siblings, node_modules-excluded native deps, and
package-root-scoped ../src rewrites.
Fixes#1674.
Bun 1.3 compiled modules report the bunfs mount root from import.meta.dir, not their source-layout module directory. The prior helper walked four directories above that value and escaped the embedded root.
Append packages to the compiled bunfs root, while keeping a guarded suffix path for future module-specific import.meta.dir semantics. Update regression tests to pin the compiled-root behavior observed by a bun build --compile probe.
Fixes#1514