- Added Anthropic prompt-cache refresh scheduling and state management to keep prompts warm across idle sessions.
- Updated pricing models and database stats tracking to calculate and store cost-weighted cache savings.
- Integrated cache savings metrics and efficiency displays into the stats CLI, dashboard routes, and UI components.
- Added support for package renaming, manifest pointer tracking, and installation migration during CLI updates.
- Implemented in-house, zero-dependency utility modules in `pi-utils` covering DOM manipulation, markdown parsing, templating, browser automation helpers, and terminal buffers.
- Migrated packages across the repository to consume the new internal utilities and `omptype` schema validators instead of external dependencies.
- Removed multiple external runtime and development dependencies including Zod, Marked, LRU cache, Turndown, and Puppeteer browser packages.
Versioned the dashboard identity header and required the current security version with no CORS permission before reusing a listener.
Older versioned and headerless stats dashboards are now identified by their process and restarted so loopback-only binding takes effect immediately.
Fixes#7633
Bound the dashboard and reuse probe to IPv4 loopback, removed wildcard CORS, and reported the actual listening hostname.
Added regression coverage for non-loopback refusal and absent cross-origin access.
Fixes#7633
- Added native `FileLock` bindings supporting cross-process advisory locking on Linux, Unix, and Windows.
- Replaced directory-based file locking and custom stale-lock reclamation with OS-backed native locks.
- Updated TypeScript declarations, native bindings, and package documentation for the new API.
- Added comprehensive unit tests and fixtures validating single-owner constraints and process death handoff.
- Moved the coding-agent lock-directory primitive to @oh-my-pi/pi-utils/file-lock
and migrated settings, MCP config-writer, and security store imports.
- Replaced the stats aggregator's parallel ~200-line token/breaker lock protocol
with the shared primitive: dead owners reclaimed immediately, live-but-wedged
owners after STATS_SYNC_LOCK_STALE_MS, unstamped acquisitions after the new
acquireStaleMs grace (10s).
- Shared primitive now treats EPERM kill probes as live owners.
- Rewrote the stats lock-reclamation regressions against the shared protocol
and moved the file-lock contract test into pi-utils.
Installed a five-second SQLite busy timeout before the read-only usage query and covered lock contention with a subprocess-backed regression test.
Fixes#7300
- Add usage history, reporting, and client summary endpoints to the auth broker.
- Implement SQLite persistence, batching, and periodic flushing for observed client usage.
- Integrate usage reporting into the coding agent session for completed assistant messages.
- Update stats aggregator and dashboard to retrieve usage history snapshots from the broker.
- Guarded malformed persisted content blocks so later project files continue ingesting.
- Marked full-session migrations complete only after a successful sync pass.
Fixes#6373
Recorded listener command lines across supported platforms and required an omp-stats-specific command identity before terminating Bun, Node, or omp holders. Unrelated runtimes now take the refusal path without receiving a signal.
Fixes#5970
Stamped an x-omp-stats-dashboard header on dashboard responses and required it (or the models JSON-array shape) in the reuse probe, so a foreign 200 responder such as an SPA dev server catch-all is no longer mistaken for a live dashboard.
Fixes#5970
Reused live stats dashboards sharing the requested port and reclaimed stale Bun, Node, or omp listeners after a failed health probe. Foreign listeners now produce an ownership-specific error.
Fixes#5970
- Anchored advisor compaction on provider-reported context usage (cached
input + generated output) floored by a full local estimate including the
advisor system prompt and tool schemas, so a near-full cached context is no
longer undercounted by the per-message estimate.
- Rejected stale provider usage retained across advisor compaction via a
runtime-only usage-anchor boundary recorded on the summary message.
- Recovered provider overflow by clearing only the advisor's own context at
the current primary cursor, retrying the bounded failing batch once against
a fresh context without replaying old primary history, and keeping later
updates eligible.
- Threaded the selected dashboard range through the stats Recent Errors UI,
API, and database timestamp filter before ordering and the 50-row limit.
Fixes#5282
- Coerce missing `stopReason`, token counts, and timestamps at the parser boundary to satisfy database NOT NULL constraints.
- Skip assistant entries missing essential model attribution or usage data instead of aborting the sync process.
- Filter invalid tool call blocks that lack required identifiers to ensure successful database insertion.
- Resolved a crash occurring when syncing legacy session files that lack usage cost breakdowns.
- Updated cost resolution to fallback to catalog pricing when stored cost is unavailable or zero.
- Adjusted yelling, profanity, and anguish criteria to reduce false-positive metrics.
- Implemented refined regex patterns for negation, blame, and noise reduction.
- Incremented the backfill key to v8 to accommodate updated signal definitions.
- Expanded test suite to validate new constraints for yelling, exclusion words, and sentiment triggers.
getSessionEntry in packages/stats/src/parser.ts previously read the entire session file into memory and parsed every entry just to find one by ID. It now streams the file line-by-line via readLines and returns on the first matching entry, stopping I/O and parsing early. In packages/stats/src/gain-aggregator.ts, readSnapcompactRecords now stats snapcompact-savings.jsonl and caches parsed records keyed by mtimeMs and size, reusing them when the append-only journal has not changed. Cutoff filtering, per-request session:toolCallId deduplication, and project filtering are still applied per call. Verified with bun test test/gain-aggregator.test.ts in packages/stats (3 pass, 0 fail, 16 expect() calls, 1 file, 757.00ms) and bun run check:types.
Closes#4251
- Migrated global service tier settings to a per-model-family architecture (OpenAI, Anthropic, Google).
- Implemented `ServiceTierByFamily` mapping to allow independent configuration and resolution per provider.
- Added automatic migration logic for legacy service tier and fast-mode application settings.
- Updated telemetry, session management, and task execution to support provider-specific tier resolution.
smokeTestSyncWorker spawned a worker via createSyncWorker on every platform, so omp --smoke-test (and the macOS signing pre-launch run in scripts/ci-macos-sign.sh) still hit the Bun-worker re-entry path the serial macOS sync default avoids. Early-return on darwin and document the carve-out.
Fixes#3733
Keyed the serial-parser branch off the caller-requested worker count instead of the per-pool clamp, so a one-file sync still fans out to workers when the caller (or default) asked for more than one.
Fixes#3733
Replaced the stats session parser's Bun.JSONL.parseChunk path with a lenient JS line scanner so large session files do not enter the native parser before /stats launches.
Fixes#3733
Kept the documented workers: 1 path inline and defaulted macOS stats sync to that serial parser path so /stats dashboard launches do not re-enter Bun workers on macOS.
Fixes#3733
- Introduced comprehensive support for multiple concurrent, independently-configured advisors via `WATCHDOG.yml` files.
- Implemented a full-screen TUI overlay for managing advisor rosters, models, tools, and instructions.
- Added session-wide advisor initialization, telemetry aggregation, and named transcript isolation.
- Enhanced advisor security and observability with secret redaction in tool results and secure XML attribute encoding.
- 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.
matchesProject now applies normalizeProjectPath to the raw cwd before
comparing against the selected project root. Without this, a record with
cwd=/repo-worktrees/lane/src would not match the normalized project root
/repo displayed in the selector, causing project-scoped views to show
empty data for any worktree-layout cwd that normalizes to a shorter root.
Addresses codex P2 on commit 8390d278df.
- Parse minimizer JSONL once per request; derive records/unparsed/projects
sets in a single pass instead of three separate reads+parse loops
- Use getTimeRangeConfig from aggregator.ts (export it) instead of a
local RANGE_MS table that would drift on new range additions
- Fix startsWith project filter to be separator-aware (=== project ||
startsWith(project+'/')) via shared matchesProject helper — prevents
false matches on path prefix collisions across all three call sites
- Remove author-specific paths (PycharmProjects, .herdr) from
normalizeProjectPath; replace with generic worktree-suffix regex
covering .wt/, -wt/, .worktrees/, -worktrees/, and /.*/worktrees/
layout patterns, keeping /.omp/wt/ drop
- Exclude snapcompact from project-scoped responses (no cwd field means
no project filter is possible — mixing all-projects savings into a
per-project view is misleading); global view still shows snapcompact
- Fix overall.reductionPercent to only divide minimizer+distill saved
bytes by minimizer+distill original bytes; snapcompact has no
originalBytes so including it in the numerator overstated the ratio
- Key unparsed command map on the full command string; command field in
GainUnparsedCommand now stores the full string (callers truncate for
display) to avoid merging distinct long commands sharing a 120-char
prefix
- Fix api.ts getGainDashboardStats arg order: project before signal to
match sibling convention; update GainRoute.tsx call site accordingly
- Add CHANGELOG entry under [Unreleased]
- GainUnparsedCommand type: actual command strings that had no filter match
(replaces GainMissedSaving which grouped by filter name — not useful for tuning)
- readMinimizerUnparsedRecords(): filter==='missed' && kind==='missed', skips temp paths
- dedupeProjects(): normalize worktree sub-paths → project roots, drop ephemeral dirs
- getGainDashboardStats(range?, project?): project-scoped reads across all sources,
top-25 unparsed commands sorted by hit count
- server.ts: ?project= query param forwarded to aggregator
- shared-types.ts: GainUnparsedCommand, GainDashboardStats.{unparsedCommands,project,projects}
SessionManager.fork() and createBranchedSession() deep-copy a parent's
entries into the child JSONL — same entry_id, timestamp, model,
responseId, token counts, cost. Stats keyed uniqueness on
(session_file, entry_id), so both files contributed to request, token,
and cost totals.
insertMessageStats and insertUserMessageStats now skip rows whose
(entry_id, timestamp) already exists under a different session_file
(first-write-wins). A one-shot migration on initDb, gated by the
fork_dedupe_v1 meta sentinel, collapses pre-existing duplicate rows
in messages and user_messages by keeping the lowest-id row per
(entry_id, timestamp) group.
Fixes#3370
- Implemented agent type classification and persistence in the database, including a backfill for legacy records.
- Added `AgentType` categorization and aggregated token usage metrics to the backend services.
- Created an `AgentTokenShare` visualization component to display usage distribution on the overview dashboard.
- Integrated agent-based tracking into existing data pipelines, view models, and testing suites.
- Migrated the `pi` AI dialect from legacy XML-style elements to a compact, token-frugal sigil-delimited format using `§` for calls, `""` for body fences, and `¤` for thinking.
- Implemented robust parsing for the new format, including support for incremental streaming of tool arguments and automatic escalation of body fences to prevent content collisions.
- Updated documentation, settings configurations, and test suites across the `ai` and `coding-agent` packages to ensure consistency with the new dialect rules.
- Standardized moderation category terminology in `stats` to improve clarity in reporting metrics.
The `.stats-table-desktop-only` wrapper used for mobile-card tables
(Requests, Errors, Overview, Projects) lacked the `overflow-x: auto`
containment that `.stats-table-container` already has, so wide tables
spilled the page body horizontally at narrow-desktop widths (768-1023px).
Add a base rule mirroring `.stats-table-container` so these tables scroll
within their own bounds.
Rewrite the local `omp stats` web UI on the OMP brand palette with a left nav, focused per-section views, a new Projects view, a system-aware light/dark toggle, and flicker-free navigation. Same data, endpoints, and command; the only server change is a 1h time-range bucketing fix in the aggregator.
- Added `WorkerInbox` and `installWorkerInbox(port)` to queue worker messages before bind.
- Added `consumeWorkerInbox()` to replay buffered messages and clear one active inbox.
- Added buffered inbox consumption in JS and tab worker transports before direct message handlers.
- Normalized worker selector arguments to the `__omp_worker_*` naming across workers and tests.
Concurrent omp --session restores after an unclean shutdown crashed
in SqliteAuthCredentialStore.#initializeSchema() with
SQLITE_BUSY_RECOVERY because the multi-statement schema run installed
PRAGMA busy_timeout=5000 AFTER PRAGMA journal_mode=WAL, the first
lock-taking statement during WAL recovery. Bun's default busy_timeout
is 0, so the lock conflict surfaces immediately.
- packages/ai/src/auth-storage.ts: hoisted PRAGMA busy_timeout to a
standalone first statement, dropped it from the multi-statement
schema run, wrapped SqliteAuthCredentialStore.open() in a 4-attempt
exponential-backoff retry loop on the SQLITE_BUSY family, and the
exhausted-retry error now includes the DB path. Exported
isSqliteBusyError(err) (matches code prefix 'SQLITE_BUSY').
- packages/coding-agent/src/session/agent-storage.ts: same hoist and
the existing retry loop now uses isSqliteBusyError so
SQLITE_BUSY_RECOVERY / _SNAPSHOT / _TIMEOUT also trigger backoff.
- Hoisted busy_timeout before journal_mode=WAL in every other shared
SQLite open path: history-storage, autoresearch/storage,
memories/storage, github-cache, report-tool-issue (auto-QA),
catalog/model-cache; stats/db.ts now sets busy_timeout at all.
- packages/ai/test/auth-storage-sqlite-busy.test.ts pins the contract:
isSqliteBusyError matches every BUSY extended code (rejects
SQLITE_LOCKED, non-errors, strings); open() leaves the connection in
WAL mode (proves busy_timeout ran before journal_mode); open() retries
through synthetic SQLITE_BUSY_RECOVERY; non-BUSY errors (SQLITE_CORRUPT)
short-circuit; exhausted retries throw an error mentioning the DB path
with exactly 3 sleeps for a 4-attempt budget.
Fixes#2421
- 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.