- profile-alias: refuse to rewrite a managed block whose start marker lacks a
matching end marker instead of appending, which on the next install would
splice from the stale start through the new end and delete intervening user
shell config (data loss in dotfiles).
- dirs/cli: add resolveProfileEnv so OMP_PROFILE takes precedence and an
explicitly-empty OMP_PROFILE selects the default profile instead of falling
through to PI_PROFILE; share the rule across both env-read sites.
- dirs: reject uppercase profile names so profile identity/isolation is stable
across case-sensitive and case-insensitive filesystems.
- profile-bootstrap: treat an unclassified bare long option as a possible
extension string flag and forward its successor untouched (never as a global
--profile/--alias), while exempting known value-less launch flags via
VALUELESS_FLAGS so 'omp --print --profile work' still selects a profile.
- tests: cover all four contracts.
- Allow parsing global profile flags after an early launch token is chosen
- Only the first residual token can terminate subcommand dispatch scanning
- Keep `--profile` parsing active when later argv contains subcommand-like words
- Add coverage for launch argv that include command names before/after profile args
- Reject /bin/sh as unsupported shell instead of mapping it to bash
- Make alias-shadow check for `omp` case-insensitive
- Preserve non-ENOENT read failures when loading shell config
- Treat missing shell config file as empty for fresh installs
- Add targeted tests for case-insensitive alias rejection, sh rejection, and read error behavior
- Use normalizeProfileName in installProfileAlias instead of raw trim/default checks
- Preserve error path for invalid/empty profile names before shell block rendering
- Keep behavior consistent with shared profile normalization logic
- Add POSIX `--` end-of-options handling in argument parser
- Stop consuming flags after `--` and pass remaining tokens as messages
- Stop global `--profile`/alias extraction at first registered subcommand
- Add focused tests for parseArgs and profile bootstrap boundary behavior
Wafer (https://wafer.ai) exposes a single OpenAI-compatible endpoint
(`https://pass.wafer.ai/v1`) for two SKUs whose entitlement differs
server-side, so we model them as two parallel providers — mirroring the
firepass/fireworks split so a user with both subscriptions can switch
without re-pasting:
- `wafer-pass` — flat-rate. `/v1/models` is filtered to entries whose
`wafer.tier === "pass_included"`.
- `wafer-serverless` — pay-as-you-go superset of Pass.
Both issue `wfr_…` keys. `/login wafer-pass` and `/login wafer-serverless`
paste-and-validate via `/v1/models`. `WAFER_PASS_API_KEY` and
`WAFER_SERVERLESS_API_KEY` are wired through `getEnvApiKey`.
Bundled catalog:
- `wafer-pass`: GLM-5.1, Qwen3.5-397B-A17B.
- `wafer-serverless`: GLM-5.1, Qwen3.5-397B-A17B, Kimi-K2.6, Qwen3.6-35B-A3B.
Dynamic discovery via `/v1/models` overlays additional models at runtime
and folds the `wafer` envelope (tier, capabilities, cents/M pricing) into
the canonical `Model<"openai-completions">` shape. GLM-family entries
carry the zai-style thinking compat (`thinkingFormat: "zai"`,
`reasoningContentField: "reasoning_content"`) so reasoning tokens land in
the right field. Cents-per-million → dollars-per-million via /100.
Tests (`packages/ai/test/wafer.test.ts`, 5 cases): bundled catalog
contract for both providers and wire-id pass-through (case-sensitive,
no rewrite — `GLM-5.1` must round-trip verbatim or upstream 404s).
Optional `packages/ai/test/wafer.live.ts` exercises a real round-trip
against `pass.wafer.ai` when `WAFER_PASS_API_KEY` is set.
- Updated the auth-broker CLI to import the transport setter from the logger module.
- Replaced the logger.setTransports call in runServe with the dedicated setTransports helper.
Refactored the optional-value flag handling so per-flag quirks live in
shared metadata instead of the args.ts dispatch loop. Added
OPTIONAL_FLAGS in cli/flag-tables.ts with rejectEmpty and
rejectAtPrefix controls, then updated both parseArgs and the profile
bootstrap to consult the same source of truth.
This restores the pre-refactor behavior for `--resume`, `-r`, and
`--session`: an empty-string argv token is treated as “no value
provided”, leaving resume=true and the empty string to fall through as a
positional message on the next iteration. `--list-models` intentionally
keeps its existing empty-string behavior.
Added regressions for parseArgs(["--resume", ""]), parseArgs(["-r",
""]), parseArgs(["--session", ""]), the preserved
`--list-models` empty-string behavior, and the bootstrap path where an
empty-string resume value precedes `--profile`.
`--approval-mode` is a string-valued flag in args.ts (`args[++i]` with
no `-` prefix check), but profile-bootstrap.ts did not list it in
STRING_VALUE_FLAGS. As a result `omp --approval-mode --profile foo`
was rewritten to `argv: ["--approval-mode"]` and silently activated
profile `foo` instead of passing `--profile` through as the invalid
approval-mode value — the exact corruption the pre-parser exists to
prevent for string-valued flags.
Caught in code review of PR #1435 after upstream merge brought in
`--approval-mode`. `--auto-approve`/`--yolo` are boolean and require
no entry. Added a regression test that mirrors the existing
`--system-prompt` coverage so future upstream merges that add
string-valued flags can be caught the same way.
Added named OMP profiles that isolate agent state (auth credentials,
sessions, settings, model cache, history, memories, blobs, plus
config root subdirs) under `~/.omp/profiles/<name>/agent/`. Activated
via `--profile <name>` or `OMP_PROFILE=<name>`; `default` maps back to
the regular `~/.omp/agent/` tree.
Added `--alias <command>` to generate a shell shortcut (e.g.
`omp-work`) that forwards `omp --profile <name>`. Detects the active
shell (bash, zsh, fish, PowerShell, pwsh), writes a wrapper into the
correct rc file, and preserves subcommands like `update`, `--version`,
and `--model` because the wrapper passes through argv unchanged.
The `--profile`/`--alias` bootstrap pre-parser lives in
`packages/coding-agent/src/cli/profile-bootstrap.ts` and runs before
any module that touches `getAgentDir()` (notably `@oh-my-pi/pi-utils/env`,
which eagerly loads `.env` from the agent directory at its own import
time). The pre-parser mirrors `parseArgs` value-consumption rules and
honors `--`, so commands like `omp --system-prompt --profile foo` pass
the literal `--profile` through as the prompt body instead of silently
activating profile `foo`.
XDG resolution for named profiles is keyed on the profile-specific
XDG path (`$XDG_*_HOME/omp/profiles/<name>`), never the base app root,
so a profile's location is decided once at first activation and stays
stable even after `omp config init-xdg` materializes the base later.
The default profile keeps its existing base-app-root check.
`setProfile(undefined)` (and `setProfile("default")`) restores the
pre-profile `PI_CODING_AGENT_DIR` snapshot taken at first activation
instead of unconditionally deleting it. `setAgentDir` refreshes the
snapshot since that call is the user explicitly redefining the
baseline.
Validation rejects profile names that match `.`/`..`, fail
`/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/`, or hit a Windows reserved
device name (`CON`, `PRN`, `AUX`, `NUL`, `COM0-9`, `LPT0-9`, including
dotted variants like `CON.txt`) — those would let `setProfile` accept
the input only for directory creation to fail later with confusing
errors on Windows.
- Added `ToolTier`, `ToolApproval`, and `ToolApprovalDecision` types and exported approval APIs.
- Updated approval-mode options from `auto|prompt|custom` to `always-ask|write|yolo` and defaulted mode to `yolo`.
- Changed approval resolution to apply per-tool decisions first, then mode-tier limits, with legacy-mode migration.
- Assigned read/write/exec `approval` and approval-detail prompts across built-in, custom, extension, and MCP tools.
- Added a new `approvalMode` argument to CLI parsing with validation for `auto`, `prompt`, and `custom` values.
- Registered `--approval-mode` on the launch command so it appears in generated help output.
- Applied the parsed approval mode as a runtime override on `Settings`, ensuring downstream `tools.approvalMode` reads reflect the CLI value.
Re-introduces the per-tool approval system from luzidd's commit 39124f3 (which
is no longer reachable from main) and improves it before re-landing.
What's restored:
- ApprovalPolicy (allow/deny/prompt) plus DEFAULT_APPROVAL_POLICIES.
- ACTION_EXCEPTIONS registry (LSP read-only, bash critical patterns).
- getApprovalPolicy() six-level resolution order.
- ExtensionToolWrapper.execute() gate before extension handlers.
- --auto-approve / --yolo CLI flag and tools.approval.<tool> user config.
- docs/approval-mode.md user guide.
What's improved over the original:
- Replaced unchecked 'as any' casts with typed unknown narrowing helpers.
- Validate userConfig values: invalid strings, numbers, etc. fall through to
the built-in default instead of being silently honoured (typo no longer
locks a tool out or grants implicit approval).
- Expanded CRITICAL_BASH_PATTERNS: chmod -R /, chown -R /, bash <(curl ...),
writes to /etc/passwd|shadow|sudoers, shutdown/reboot/halt/init 0,
kill -9 1, nc -e / nc -c reverse shells. Pattern shapes require a
command-position boundary so 'npm run reboot-tests' and 'echo "shutdown the
queue"' don't false-positive.
- Added DEBUG_READONLY_ACTIONS exception so DAP inspection actions (threads,
stack_trace, variables, scopes, read_memory, …) auto-allow while
execution-side actions (launch, attach, continue, evaluate, write_memory,
set_breakpoint, …) still prompt.
- formatApprovalPrompt: labels mcp__<server>__<tool> calls as MCP server
tools, surfaces ssh host + command, recognises the modern § hashline header
for edit, and truncates >240-char fields so a heredoc-sized body cannot
blow out the confirmation dialog.
- Test suite grown from 40 to 57 cases — new coverage for invalid user
config, the extended critical-bash patterns, benign-keyword negatives,
debug exceptions, MCP/ssh prompt formatting, and command truncation.
Verification:
- bun test packages/coding-agent/test/tools/approval.test.ts -> 57 pass
- bun x biome check . -> clean
- bun run check:ts across all 9 workspaces -> clean
- Migrated OAuth provider authentication from standalone `pi-ai` CLI to in-process `AuthStorage.login()` flow in coding-agent.
- Made provider argument optional for `login` and `logout` commands with interactive provider picker when omitted.
- Added `list` command to enumerate registered OAuth providers with optional `--json` output format.
- Removed `pi-ai` CLI binary and `bin` entry from @oh-my-pi/ai package; library API remains unchanged.
- Updated documentation and examples to reflect new `omp auth-broker` command interface and in-process OAuth flow.
Converted CLI document file arguments through the Markit path before adding them to the initial prompt, preventing PDF bytes from being sent directly to local vision models. Added a regression test for PDF file arguments.\n\nFixes #1401
- Added `checkCredentials()` with result types/options for per-credential tri-state health checks.
- Added `/v1/credentials/check` endpoint via `handleCredentialsCheck` returning `{ generatedAt, credentials }`.
- Added `omp auth-gateway check` flow with provider grouping, `--json` output, and exit status 1 on failures.
- Added command examples, changelog updates, and tests for expired OAuth refresh, null/missing config, and ordering edge cases.
Rolled back binary updater replacements when post-install version verification fails instead of deleting the previous working binary first.
Added a release workflow gate that downloads the published macOS arm64 asset and verifies codesign plus --version before npm publishing.
Fixes#1240
- Collapsed a multi-line reinstall-warning `console.log` statement into a single line.
- Preserved the existing warning message text and only simplified its formatting.
The fallback reinstall hint printed when omp update can't verify the new
binary pointed at https://raw.githubusercontent.com/can1357/oh-my-pi/main/install.sh,
which returns 404. The installer actually lives at scripts/install.sh
(consistent with the README install instructions).
- Added generation-aware snapshot contracts with generation, serverNowMs, refresher, and rotatesInMs fields.
- Reworked /v1/snapshot serving and client fetching for If-None-Match long-poll with 304/200 status handling.
- Added status checks in remote-store and SDK/CLI snapshot paths, applying updates only when fetch returns 200.
- Added StreamOptions.onAuthError and stream one-shot 401 retry dispatch using refreshed credentials.
- Added a new `setTransports` logger API to swap console and file winston transports at runtime.
- Refactored logger transport creation to lazily build rotating file logs via a shared directory helper.
- Updated auth-broker serve startup/shutdown to use structured logger output and switch to console-only logs for its headless runtime.
- Added `dev.autoqa.consent` setting and single-flight popup handler wired through `InteractiveMode`.
- Added `flushGrievances` to batch-POST unpushed rows to `dev.autoqaPush.endpoint` with cooldown and single-flight deduplication.
- Added `omp grievances push` subcommand with TTY progress bar for manual draining.
- Migrated shared DB logic to `openAutoQaDb` (with `pushed` column migration) exported from `report-tool-issue`.
- Added `omp auth-gateway serve/token/status` — a forward-proxy injecting broker credentials for OpenAI Chat, Anthropic Messages, and OpenAI Responses wire formats.
- Added `GET /v1/usage` to auth-broker and auth-gateway; usage cache switched to 5-min per-credential TTL with jitter and last-good fallback on failure.
- Added `AuthStorage.setConfigApiKey/removeConfigApiKey/clearConfigApiKeys` so `models.yml` `apiKey` beats OAuth tokens without overriding `--api-key`.
- Added `omp auth-broker migrate --from-local` for idempotent upload of local SQLite/env credentials to the broker.
- Added `AuthBrokerClient`, `RemoteAuthCredentialStore`, `AuthBrokerRefresher`, and `startAuthBroker` server in `packages/ai/src/auth-broker`.
- Renamed `AuthCredentialStore` class to `SqliteAuthCredentialStore`; extracted `AuthCredentialStore` as a persistence interface.
- Added `exportSnapshot`, `forceRefreshCredentialById`, `disableCredentialById`, and `upsertCredential` to `AuthStorage` for broker wire protocol.
- Added `omp auth-broker` CLI subcommand (serve, token, login, logout, import, status) and `discoverAuthStorage` broker-mode path keyed on `OMP_AUTH_BROKER_URL`.
- Removed export leakage by demoting many helper and const symbols to module-local scope.
- Renamed underscore-prefixed internals and cache fields, then updated related references and `satisfies never` checks.
- Deleted obsolete logic branches and helpers, including harmony-stream interruption flow and unused benchmark runtime helpers.
- Updated Biome config and manifests by broadening lint coverage and removing an unused `@napi-rs/cli` dev dependency.
- Adjusted tests and utilities to use renamed test helpers and remove redundant private test-only helpers/locals.
Adds a new `rpc-ui` mode that extends the existing headless RPC mode with
interactive tool support (ask tool, extension UI dialogs, etc.).
In plain `rpc` mode the session has `hasUI=false` and no UI context is
wired, so interactive tools are disabled. `rpc-ui` mode sets `hasUI=true`
and wires a single shared `RpcExtensionUIContext` instance into both the
tool context store and the extension runner. Both consumers share the same
`pendingExtensionRequests` map and output closure, so `extension_ui_response`
messages received on stdin are routed to the correct waiting promise
regardless of which code path (tool or extension) created the request.
Changes:
- `args.ts`: add `rpc-ui` to the `Mode` union and the parse guard
- `launch.ts`: expose `rpc-ui` in the OCLIF flag definition and help text
- `main.ts`: propagate `rpc-ui` through all RPC-mode guard conditions and
pass `setToolUIContext` to `runRpcMode` when the mode is `rpc-ui`
- `rpc-mode.ts`: accept optional `setToolUIContext` callback; create one
shared `RpcExtensionUIContext` instance and pass it to both the tool
context store and the extension runner
- Replaced Python execution with a local `python -u runner.py` subprocess and NDJSON stdin/stdout framing.
- Removed shared-gateway architecture, including coordinator lifecycle APIs, `useSharedGateway` wiring, and `jupyter` CLI/actions.
- Simplified setup checks to a plain Python 3 availability probe and removed automatic dependency-install fallbacks.
- Updated kernel cancellation and display processing to use status frames, SIGINT/SIGTERM escalation, and normalized output coercion.
- Added `python-runner` integration and display tests while deleting legacy websocket and kernel lifecycle test suites.
- Added new frustration and revised behavior metrics across stats types, parser mappings, and UI charts.
- Reworked session sync to fan out parsing across a capped worker pool with SyncOptions progress callbacks.
- Added worker-based parse messaging and throttled TTY progress rendering in the stats sync command.
- Updated behavior scoring to strip structured content, ignore noisy prompts, and fix profanity boundary regressions.
- Expanded user message schema and migrations, then repaired assistant model/provider links during sync backfill.
- Updated worker-import guidance in AGENTS.md and recorded sync-progress/metrics updates in package changelogs.
- Removed the read CLI argument and tool schema field so read requests no longer accept custom timeouts.
- Updated URL read handling to stop forwarding timeout values and execute URL reads without a timeout parameter.
- Standardized URL read fetching to a fixed 30-second timeout and dropped timeout metadata from URL call rendering.
- Added configurable `ScanDetail` modes across native fd/glob/ast flows and exposed `size` on `GlobMatch`/types.
- Added parallel grep processing with optional workers, buffered entry visits, and direct small-file reads.
- Changed `filesWithMatches` to stop at first match per file and report `totalMatches` as matching-file count.
- Fixed grep count/offset limits, timeout checks, and cancel handling by enforcing offsets in aggregate results.
- Updated PI_GREP_WORKERS and auto-absorb/read-summarize behavior/docs, including `read.summarize.prose` and pure-insert defaults.
- Added tests for filesWithMatches counts, cancel cases, and count-mode behavior, plus updated grep benchmark imports.
- Registered a new `read` command in the CLI command registry so `omp read` can be invoked.
- Implemented `runReadCommand` to execute the read tool, wrap it with meta notices, and print text or image-result blocks.
- Added a `read` command class with required path input, optional timeout flag, and usage examples.
- Added a `list`/`clean` positional action and examples to the grievances command, along with new `--id`, `--tool`, and `--all` flags for cleaning.
- Implemented `cleanGrievances` to delete grievances by id, tool, or all entries, enforce mutually exclusive selectors, and emit JSON counts when requested.
- Updated grievance DB access to use writable handles for clean operations and reset autoincrement sequence when removing all rows.
The --list-models handler in runRootCommand short-circuited to
listModels() right after Settings.init and modelRegistry.refresh,
exiting before extension loading ran in createAgentSession. As a
result, providers contributed via pi.registerProvider() (from -e
paths or settings.extensions) never appeared in the listing.
Extract a runListModelsCommand entry point in cli/list-models.ts
that loads extensions (CLI -e paths and settings.extensions) into
the supplied ModelRegistry, mirroring sdk.ts's handoff of pending
provider registrations, and then delegates to listModels. The load
is intentionally narrow: no agent loop, no MCP servers, no custom
tools.
Fixes#905
- Added a unified eval framework with parser grammar, backend interfaces, and JS/Python execution result types.
- Added eval tool docs and updated prompts for fenced cells, `eval.py`/`eval.js`, and fallback behavior.
- Replaced the built-in `python` tool with `eval` across registry, rendering, interactive modes, and tool settings.
- Migrated Python execution runtime from `src/ipy` to `src/eval/py`, renamed state fields, and removed legacy introspection.
- Refactored browser tooling from in-process VM helpers to worker-managed tab supervisors and protocol transport.
- Added eval parser fallback and JS tool-bridge tests, updated imports, and removed obsolete python-mode suites.
isPathInDirectory only normalized strings via path.resolve, so on Windows
when Bun is installed via Scoop (~/.bun is a junction to scoop\persist\
Oven-sh.Bun\.bun) the omp path from $which and the bunBinDir from
'bun pm bin -g' compared as different directories, causing 'omp update'
to take the binary-swap path instead of 'bun install -g' and fail with
EPERM unlinking omp.exe.bak (Bun has the running exe open). Layer
fs.realpathSync.native on top of the existing lexical guard, resolving
the file's parent dir so non-existent target paths still fall through.
Fixes#845
- Removed `pi-natives` chunk language classifier modules and all core chunk subsystems (kind, state, render, edit, resolve).
- Removed chunk-mode CLI/read/edit entrypoints, including `read` command and chunk mode registration/prompt tooling.
- Removed chunk selectors from `read` and `grep` tools, switching behavior to raw/L-range handling.
- Fixed poll wait parsing to keep defaulting to `30s` when the provided value is empty.
- Canonicalized file and CLI defaults from `read` to `open` across tool registration and prompts.
- Added `resolveToolAlias()` and applied alias-normalized tool selection so legacy `read` maps to `open`.
- Updated runtime, UI, and export layers to treat `open` as first-class while preserving `read` compatibility.
- Renamed read prompt docs to `open.md`/`open-chunk.md` and refreshed system guidance to recommend `open`.
- Updated tool-related tests and expectations from `read` to `open` (including test fixtures and aliases).
- Added configurable minimizer settings to native and coding-agent APIs, including shellMinimizer options.
- Added minimizer execution plumbing through shell config, session-key generation, and buffered output capture.
- Added command identity detection and dispatching by program/subcommand with safe fallback to passthrough output.
- Added filter suites for git, docker, go, bun, cloud, and system commands to strip ANSI and compact noisy output.
- Added unit tests for detection and minimizer behavior across command support, failures, and passthrough paths.
- Standardized missing-file read errors and now return `File not found: <path>` for absent edit targets.
- Centralized AI provider, usage, and OAuth helpers into shared modules to remove duplicated logic.
- Migrated OAuth/API-key login flows to shared factory helpers and removed inline prompt/token-exchange code.
- Reused shared tools and formatter utilities for discovery, stream tails, LSP batching, and source formatting.
- Consolidated repeated test helpers and fixtures into shared modules, replacing inline helper duplicates.
- Added canonical model equivalence types, cache helpers, and registry APIs for provider variant lookup.
- Changed model resolution to apply canonical ID overrides/excludes with provider order before fallback matching.
- Added canonical and provider model views in list-models and selector UI with canonical sorting/persistence.
- Updated role/model persistence to store selectors while runtime now resolves concrete canonical-backed provider models.
- Added support for embedded URL selectors with `:raw` and `:L#-L#` line range syntax in read command.
- Implemented `parseReadUrlTarget()` function to parse and validate URL read targets with line range support.
- Updated read CLI to delegate URL inputs through read tool pipeline instead of treating as local file paths.
- Added comprehensive test coverage for URL selector parsing and CLI URL delegation.
- Refactored URL handling in read tool to use structured `ParsedReadUrlTarget` object.