# Changelog

All notable changes to this project are documented here.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2026-08-12

### Added
- **Test suite.** Pure helpers (`isExtensionFile`, the `sanitize*` family, `mergeConfig`) are now unit-tested via `node:test` — `npm test` runs 10 cases covering file classification, input hardening, and config merging. Aligns with the `_shared` + test-scripts convention used by `beval-pi-sync` / `beval-pi-provider`.
- **`pi.image` gallery badge** in `package.json`, matching the pi-sync/pi-provider convention.

### Changed
- **Extracted pure logic to `src/shared.ts`.** The extension entry now imports `isExtensionFile` / `mergeConfig` / types from a pi-independent shared module, instead of duplicating them in the entry file. This removes ~60 lines of duplication and makes the helpers testable in isolation.
- **Full config hardening.** `enabled`/`hotReload` are now boolean-sanitized (a string `"false"` no longer truthy-coerces to enabled), and `lazyDir` is string-sanitized, on top of the existing number/array checks.

### Docs
- Documented that bare `import("...ts")` for child extensions relies on jiti's global ESM loader hook (which pi registers at startup); verified safe in pi's runtime, and noted why it would fail in a bare node process.

## [1.0.3] - 2026-08-12

### Added
- **Config input hardening.** `timeout`, `startDelay`, `whitelist`, and `blacklist` are now sanitized on read: non-numeric / negative / `NaN`/`Infinity` numbers fall back to defaults, and non-array or mixed-type whitelists/blacklists are coerced to clean `string[]`. A malformed `lazyLoader` block in `settings.json` no longer crashes the loader.

### Changed
- Renamed the unused `session_start` event parameter to `_event` for clarity.

## [1.0.2] - 2026-08-12

### Added
- **`.js` extension support.** Lazy extensions may now be `.js` as well as `.ts`, matching pi's own auto-discovery (which accepts `index.js` and top-level `.js`).
- **Type-declaration exclusion.** `.d.ts` / `.d.js` files are now skipped; previously `foo.d.ts` was wrongly matched by `.endsWith(".ts")` and treated as an extension named `foo.d`.

### Fixed
- **Post-shutdown timer leaks.** All deferred timers (the `startDelay` load trigger and the 2s status-bar clear) are now tracked and cleared on `session_shutdown`, so they can never fire after `ctx` is torn down — eliminating a small but real crash window on fast session switches (e.g. `/resume`).
- **Defensive rejection handling.** `loadExtensions`' map over `Promise.allSettled` no longer casts `r.reason` to `LoadResult`; it returns a proper failure object, since the internal loader never rejects (errors are caught into `LoadResult`), keeping the return type sound.

### Changed
- Refactored shared file-classification logic into `isExtensionFile()` so discovery and hot-reload stay in lockstep.
- Removed the dead `timedOutLate` accumulator that was only suppressing an unused-variable warning.

## [1.0.1] - 2026-08-11

### Fixed
- **Timeout no longer leaves phantom loads / double-registration.** A timed-out extension's factory could previously keep running in the background while the result was reported as failed, which meant the extension was missing from the loaded set and could be re-registered by hot reload. Now the underlying load is awaited and, if it eventually completes, marked `completedLate` and recorded as loaded (reported as `Timeout (>Nms) but completed later` in the log). No orphaned in-flight work, no double-registration.
- **Type safety + stale-context guard.** `ctx` is now typed as `ExtensionContext` instead of `any`, and a `shuttingDown` flag gates all deferred callbacks (`setTimeout` load, status-bar updates, hot-reload watcher, `ctx.ui.notify`) so they no longer fire after `session_shutdown` when `ctx` is already torn down.
- **Removed dead `priorities` config.** The `priorities` field was read and stored but never used (no scheduling). It has been removed from the config schema and docs to avoid misleading users.
- **Documented the `index.ts` skip and timing caveat.** The `lazy/index.ts` placeholder is intentionally skipped (it exists only for health/gate checks), and the README now clearly warns that lazy extensions are loaded after `session_start`, so tools/commands they register are not guaranteed to be in the first turn's system prompt — extensions needed on turn 1 belong in the synchronous `extensions/` directory.

## [1.0.0] - 2026-08-11

### Added
- First public release as an installable pi package (`beval-pi-lazy-loader`).
- Defers loading of extensions in `<agentDir>/extensions/lazy/` until after `session_start`, keeping the interactive prompt responsive.
- Reads configuration from the `lazyLoader` key in the agent `settings.json`.
- Whitelist / blacklist filtering, per-extension timeout, and optional hot reload of newly added `.ts` files.
- Portable path resolution via `getAgentDir()` (respects `PI_CODING_AGENT_DIR`), so the package works globally and per-project.

### Fixed
- Removed reliance on a non-existent `pi.settings` accessor that emitted `[lazy-loader] pi.settings not available, using defaults` on every session start and silently discarded user configuration. Config is now read directly from `settings.json`.
