The compiled registry enumerated each `exports` wildcard with a single-level
glob and explicitly skipped any key containing a slash, so a nested subpath
like `slash-commands/helpers/active-oauth-account` never entered the bundled
registry. Node matches `*` across `/`, so that import is legitimate: it
resolves from source, then falls through to `Bun.resolveSync` inside a
compiled binary and dies under bunfs. Reproducible on the published 17.2.1
binary with a real extension (`quota-hud.ts`).
Enumeration is now recursive, with every path segment held to the same
private/hidden rules as the leaf, so a `.private/` or `_internal/` directory
is no more exported than a private file. Directory index modules stay
excluded: `./x/*` must not serve `x/y` from `y/index.ts`, which Node would
not resolve either.
Generated per-module loaders instead of eagerly evaluating the entire bundled compatibility graph during extension bootstrap.
This prevents appserver startup from cycling through its own retained command modules before the Unix socket is created.
Fixes#5568
- Standardized `fastembed` and `onnxruntime-node` as external dependencies for all compiled binaries.
- Removed manual exclusion logic from individual build scripts in favor of a centralized configuration.
- Updated release build dry-run output to reflect consistent external dependency handling.
Fixes#5195
- Centralized message preprocessing for tiny models to handle noise removal, code block stripping, and context formatting.
- Updated title generation logic to support self-closing tags and improved robustness against partial markers.
- Added structured guidance and system prompts for small models to prioritize output consistency.
- Implemented a title-generation benchmark harness and expanded test coverage for message preprocessing.
- Built the generic Windows release binary with Bun's baseline x64 runtime so older Windows 10 CPUs do not hit the AVX2-only modern executable.
- Forced pi-natives release builds to link PCRE2 statically so macOS installs do not depend on Homebrew's libpcre2 dylib.
- Added release dry-run and native-build regression coverage for the portable artifact contracts.
Fixes#5172
- Replaced the command-line execution of bun build with the native Bun.build API.
- Resolved E2BIG errors occurring when passing large docs payloads as command-line arguments.
PluginManager.install (bun install + bun update), PluginManager.uninstall,
PluginManager.#fixMissingPlugin, the legacy installer.ts install/uninstall
helpers, and generate-legacy-pi-bundled-registry.ts's formatInPlace all
called Bun.spawn with stdout/stderr piped and awaited proc.exited before
touching either stream. Once a child's output exceeded the ~64 KiB OS
pipe buffer, the child would block on write(2) while the parent blocked
on exit — a classic pipe-buffer deadlock. Even where Bun's current runtime
happens to buffer eagerly, the pattern silently leaked unbounded bytes.
Each site now starts new Response(proc.stdout).text() and stderr readers
immediately after Bun.spawn and awaits them alongside proc.exited via
Promise.all. Existing error semantics are preserved: install throws with
stderr, uninstall keeps its generic error, and formatInPlace still includes
Biome's stderr in the failure message.
Adds a regression test (plugin-install-git.test.ts) that models the
OS-pipe deadlock by holding proc.exited until both mock streams are
drained — install must read them before awaiting exit, else the test hits
its 2s Promise.race timeout.
Fixes#4230
gen:bundle previously ran gen:docs and gen:docs:reset around the build. Under prepack that reset ran before bun pm pack assembled the tarball, so the published src/ copy of docs-index.generated.txt was empty and consumers importing via subpath (e.g. @oh-my-pi/pi-coding-agent/internal-urls/docs-index) fell through to the missing repo docs/ tree.
Restored gen:docs in prepack so postpack owns the reset (matching the original lifecycle), and made gen:bundle assert the embed is populated and fresh instead of touching it, so a direct gen:bundle outside prepack fails with an actionable message.
Fixes#3934
Added a contributor-facing native crate map (docs/native-crates.md) covering pi-natives, pi-shell, pi-ast, pi-iso, pi-walker, pi_uu_grep, pi-uutils-ctx, and vendored brush crates, and linked it from natives-architecture.md and user-facing-packages.md.
Added a docs-index tool coverage test asserting every BUILTIN_TOOL_NAMES entry and injected custom tool (generate_image, tts) has a docs/tools/<name>.md page served by omp://.
Inlined tiny fail/buildPayloadText/checkDocsIndexFreshness helpers in generate-docs-index.ts per the project rule against single-expression named functions.
Fixes#3934
Added root omp docs for memory_edit, learn, manage_skill, generate_image, and tts, plus package-level coverage for user-facing README-only CLIs.
Added a docs-index freshness check to package check and made gen:bundle generate and reset the docs embed itself.
Fixes#3934
- Added `rewrite-changelog.ts` and `fix-changelogs.ts` utilities to automate the consolidation of release notes using LLM-assisted processing.
- Updated multiple internal changelog files by consolidating redundant entries and improving phrasing for readability.
- Implemented `previewLine` utility in `coding-agent` to prevent visual spillover in status rows by managing text truncation and whitespace.
- Updated `package.json` with new workflow scripts for managing package-level change histories and documentation indexes.
- Added a fallback from `hashline` to `replace` mode for Kimi-family models to resolve compatibility issues.
- Introduced `PI_STRICT_EDIT_MODE` environment variable to bypass automatic model-specific edit-mode fallbacks.
- Updated `getEditVariantForModel` to perform case-insensitive matching for model variant configurations.
- Added comprehensive unit tests for edit mode resolution and settings configuration.
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
legacy-pi-bundled-registry.ts now static-imports
@oh-my-pi/pi-coding-agent/export/html, whose source pulls
./tool-views.generated.js. The root 'prepare' hook builds that
file on bun install, but a clean binary build that skips install
hooks would 'bun build --compile' against the registry entry and
fail resolving the missing generated bundle. build-binary.ts now
runs collab-web's build:tool-views before the compile, matching
what prepack already does for the npm bundle.
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
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
- Introduce `detach_reparent` parameter to command execution to support process reparenting.
- Add `detach_session_reparent` to Unix command extensions using a double-fork technique to orphan processes from the shell descendant tree.
- Update background pipeline logic to automatically apply reparenting when unwrapping transparent wrappers like `nohup`.
- Remove unused `command_is_resolvable` helper.
- Introduced `web-palette.ts` to implement the collab-web pink/purple brand identity for HTML exports.
- Updated `generateThemeVars` to support a `palette` option, allowing users to choose between the brand-web aesthetic and a specific TUI theme.
- Configured public exports and the share-viewer script to default to the brand-web palette rather than inheriting the user's terminal theme.
- Refactored `AgentSession.exportToHtml` to align with the new branding defaults while allowing for per-export theme overrides.
- Adjusted `dark.json` background values to ensure better visual consistency across internal surfaces.
- Implemented `tab.ariaSnapshot()` to capture and represent page structures as ARIA-tree YAML.
- Introduced `tab.ref()` and ref-based selector parsing to enable precise element interaction via unique ARIA identifiers.
- Integrated automated script bundling for cross-environment evaluation of ARIA snapshot logic.
- Updated browser action methods to resolve and target elements using ARIA-ref handles.
The vendored markit engine kept `mupdf` external, but a single-file
`bun --compile` binary has no node_modules to resolve it from, so the
standalone binary aborted at startup with `Cannot find package 'mupdf'`
— the otherwise-lazy import is resolved eagerly at boot. Bundle mupdf and
embed its WASM blob (scripts/embed-mupdf-wasm.ts, reset after the build);
npm and source installs still load mupdf from node_modules.
Import mupdf lazily inside the PDF converter so the bundled markit chunk's
init stays synchronous: mupdf's top-level await otherwise made the chunk
init async and bun's compiled bundler failed to await it through the
barrel, exposing the converters before their module-level const tables
initialized (undefined EXTENSIONS). Also keeps the ~10MB wasm off non-PDF
document conversions.
- Replaced raw TypeScript documentation map with a lazily-inflated gzip blob.
- Reduced bundled binary/npm package size by approximately 0.9MB.
- Encapsulated index logic into `docs-index.ts` to separate header metadata from content.
- Added `postpack` and robust `try/finally` patterns in build scripts to ensure clean artifacts.
- Implemented disk-based fallback during development to maintain existing developer experience.
- Avoided value-imports of `puppeteer-core` in main startup files to prevent eager loading of the browser-data dependency graph.
- Used `import type` and literal string checks to defer `puppeteer-core` initialization until browser tools are actually invoked.
- Added a test to verify that `puppeteer-core` and `@puppeteer/browsers` remain off the eager startup import graph.
- Externalized additional heavy dependencies to reduce bundle size and improved minification settings in the build script.
- Added a command to the setup script to link the omp binary into the global bun bin directory.
- Updated the omp script documentation to reflect the new installation method.
Added a root setup script that installs workspaces and builds the native addon before running the source CLI.
Documented the fresh-clone setup and smoke-check flow in the README development section.
Fixes#2895
- Added `src/export/share.ts`: `/share` now snapshots the session JSON, gzips and seals it with a fresh AES-256-GCM key, and pushes the blob to a secret gist or the share server (1 MB cap with image/string/entry truncation via `sealToFit`); links are `<serverUrl>/<id>#<key>` with the key only in the fragment.
- Added `share-loader.js` and `scripts/generate-share-viewer.ts` building the static viewer the relay serves at `GET /s/<id>`: it fetches the sealed blob, decrypts in-browser, and hands the JSON to the export template via `window.__OMP_SESSION_DATA__`.
- Reworked the `/share` command in `command-controller.ts`/`builtin-registry.ts` off the plaintext-gist HTML upload, exported `LoadedCustomShare`, and exposed the session `SecretObfuscator` getter on `AgentSession` for redaction.
- Added `share.serverUrl` and `share.redactSecrets` settings backed by `DEFAULT_SHARE_URL` from pi-wire.
- HTML exports now embed subagent transcripts: `collectSubSessions` walks `<session>/<AgentId>.jsonl` recursively into `SessionData.subSessions`, with `includeSubSessions` opt-out and the exported `buildSessionData` reused by share snapshots.
- Added `share.test.ts` (snapshot/seal/server-url contracts) and `export-subsessions.test.ts`.
- Replaced the committed `template.generated.ts` with Bun text imports (`with { type: "text" }`) of `template.html/css/js` plus the gitignored `tool-views.generated.js`, composed in a memoized `getTemplate()` in `src/export/html/index.ts`.
- Deleted `scripts/generate-template.ts` and the unused `template.macro.ts`; removed the stale package-level `.gitignore` and the `biome.json` exclusion for the generated file.
- Wired root `prepare` to `generate-docs-index` + new `build-tool-views` script and coding-agent `prepack` to rebuild tool views before `bundle-dist`, so source runs and publishes never need a manual generate step.
- Added narrow ambient text-module declarations (`*.css`, `*/template.js`, `*.generated.js`) to `bun-imports.d.ts`; `*.html` stays untyped because bun-types claims it as `HTMLBundle`, so the import casts at the use site.
- Updated `DEVELOPMENT.md` and `docs/porting-from-pi-mono.md` references; the rewritten import block transiently carries `loadEntriesFromFile`, consumed by the next commit's sub-session loader.
- Moved `fastembed` and `onnxruntime-node` to optional peerDependencies.
- Fixed bundled installs that could not resolve `onnxruntime_binding.node`.
- Added shared `runtime-install` utilities for on-demand module resolution.
- Added tests for runtime-resolution parsing and exact peer-version checks.
- Fixed npm CLI bundling by invoking `generate-client-bundle.ts --generate` before build and `--reset` after.
- Added `PI_BUNDLED` handling in `packages/stats/src/server.ts` and switched bundled distributions to embedded assets.
- Decoded embedded archives via `decodeEmbeddedClientArchive` and treated legacy placeholder content as missing.
- Handled missing dashboard source/build directories as ENOENT during mtime scans to avoid startup crashes.
- Extended `--smoke-test` to start the stats server and validate dashboard HTML contains expected asset markers.
- Renamed `packages/coding-agent/scripts/dev-launch` to `packages/coding-agent/scripts/omp` and updated the preload script reference in that launcher.
- Renamed `packages/coding-agent/scripts/dev-launch-preload.ts` to `packages/coding-agent/scripts/omp.ts` and adjusted its header comment.
- Updated the `install:dev` script in `package.json` to symlink the global `omp` command from the renamed launcher path.
- Rerouted sync, tab, js-eval, and tiny workers to re-enter CLI modes via `__omp_*` selectors.
- Adjusted `cli.ts` startup to dispatch worker entrypoints before parsing and exit 1 on uncaught errors.
- Bundled CLI as `dist/cli.js` in prepack, switching `omp` binary and published files.
- Removed explicit Bun `--compile` worker entrypoints from build/release scripts in favor of host-entry dispatch.
- Added `declareWorkerHostEntry()` and `workerHostEntry()` environment helpers and `PI_COMPILED` binary detection.
- Defer MCP server discovery off the first-paint critical path for UI
sessions; tools and slash commands stream in through the existing
live-refresh channel once each server connects (non-UI modes keep the
blocking path). ~290ms off first paint with MCP servers configured.
- Build the model catalog's canonical-equivalence index lazily on first
read instead of eagerly in the ModelRegistry constructor. A default
interactive launch never reads it pre-paint, moving the ~210ms build
(over ~3,200 models) off the critical path: ~244ms (~16%) off cold boot.
- Memoize per-session read summaries (tree-sitter parse) on the content
hash of the freshly-read bytes; the file is still read fresh each call
so results stay correct. Repeat same-file summary read 17ms -> 2.4ms.
- Reuse the Markdown subtree across streaming reveal ticks, memoize
grapheme counting, and stop re-highlighting finalized thinking blocks.
- Attribute the previously-unlabeled synchronous boot region in the
PI_TIMING table and add a bench:guard boot-regression target.
- Added Bun preload that records inclusive per-module windows and resolved static import edges via plugin hooks.
- Shared events through a dependency-free buffer so the preload and logger avoid importing each other.
- Rendered module spans as a body/TLA-ranked dependency tree, separating graph wait from top-level work.
- Back-folded captured load phase into the root window to shrink the opaque pre-instrumentation figure.
- Updated `install:dev` to symlink `packages/coding-agent/scripts/dev-launch` into Bun's global bin directory as `omp`.
- Added a `dev-launch` shell script that launches Bun from an isolated directory and preserves the caller's working directory for restoration.
- Added a preload shim that restores `OMP_LAUNCH_CWD` before CLI execution so external project `bunfig.toml` preloads are not used.