Upstream @earendil-works/pi-ai re-exported isContextOverflow from its
package root, but omp's @oh-my-pi/pi-ai barrel forwards only
./error/rate-limit, so the shim's `export * from "@oh-my-pi/pi-ai"` left
it off the surface. Plugins importing it (pi-blackhole) tripped Bun's
static named-export check during validation, both on disk and through the
omp-legacy-pi-bundled: virtual namespace.
Audited the upstream root surface rather than fix one symbol at a time:
of its runtime exports only isContextOverflow (now under
@oh-my-pi/pi-ai/error) and parseJsonWithRepair/parseStreamingJson/
repairJson (relocated to @oh-my-pi/pi-utils) still exist in omp. Bridge
exactly that set through the shim and pin it with a regression test that
exercises each helper's behavior.
Fixes#6859
Historical @earendil-works/pi-ai exports isRetryableAssistantError from its
package root (utils/retry.ts), but OMP's legacy-pi-ai-shim re-exports
@oh-my-pi/pi-ai, whose root never carried the symbol. Plugins importing it
(e.g. @router-for-me/pi-cliproxyapi-provider >= 1.4.9) failed Bun's static
named-export validation, rolling back install.
Port the upstream transient-error classifier into the shim, preserving the
provider-error wording tables so legacy retry semantics match.
Fixes#6847
- Added a prepareToolCall phase to the agent loop running before tool scheduling for validation and hooks.
- Updated BeforeToolCallContext and result types to support argument replacement instead of in-place mutation.
- Updated coding-agent extension handling and runner to track emitted tool calls and re-evaluate approvals on input revisions.
- Added comprehensive test coverage for argument replacement, concurrency resolution, and schema validation.
- Add conditional attributes to silence dead-code warnings on platform-specific code and fields.
- Update target dependencies in pi-walker/Cargo.toml with explicit windows-sys feature sets.
- Simplify time cast expressions in linux_reflink and rcopy modules.
- Handler wrappers spread ctx, snapshotting the model accessor; a handler
calling pi.setModel() then reading ctx.model saw the stale model.
- Scoped contexts now delegate via Object.create(ctx) with an own ui
property, so all accessors stay live.
- Review follow-up for PR #6806.
Replaced the scoped UI object spread with a delegating proxy that binds inherited methods to the original context while overriding only abort-capable dialogs.
Extended watchdog coverage with a prototype-backed notification method matching RPC UI contexts.
Attached the session-stop abort listener and rechecked cancellation before invoking extension work, preventing synchronous ctx.abort() calls from being missed.
Added deterministic coverage for a handler that aborts and then waits on non-UI work.
Forwarded confirmation dialog options in the interactive TUI and scoped extension UI dialogs to each handler watchdog signal.
Added regressions for direct confirmation cancellation and fail-closed tool-call timeout cleanup.
Fixes#6805
Upstream Pi's deleteKittyImage/deleteAllKittyImages return unwrapped control
sequences; legacy callers such as pi-sprite wrap tmux passthrough themselves.
Aliasing OMP's auto-wrapping encodeKittyDeleteImage (and hand-wrapping
deleteAllKittyImages) double-wrapped under tmux, so the outer terminal dropped
the delete command. Match the upstream bare-sequence contract and pin it in
the regression test.
Follow-up to the earlier approval re-check, which missed the
prompt-to-prompt case: if the original and revised inputs both resolve
to `prompt`, a handler could swap in different prompt-gated args that ran
under approval granted for the original.
Emit the `tool_call` event before the approval gate instead of after, so
the gate resolves policy and shows the interactive prompt against the
input that actually executes. The user always approves what runs, and
deny/allow-to-prompt/prompt-to-prompt transitions are all covered by one
gate rather than special-cased. A `deny` on the original input still
short-circuits before the runner is touched, so an already-denied tool
never emits `tool_call`.
Tests: the approval prompt reflects the revised input, `tool_call` fires
before `tool_approval_requested`, plus the existing deny/computer/
multi-handler cases.
Addresses PR review feedback.
- Re-gate approval (P1): the extension wrapper's approval/safety gate resolved
against the original `params`, but execution ran with the overridden input, so
a handler could rewrite approved args into ones a deny/critical policy would
have blocked. After an override, re-resolve the policy on the revised input and
block a revision that newly resolves to `deny` (or, outside yolo, newly requires
a prompt) instead of running it unapproved.
- Computer skip in hook wrapper (P2): the hook wrapper applied the override to
`computer` tool calls, contradicting the documented contract; it now skips them
like the extension wrapper does.
Docs: the shared-events contract note is now accurate for both wrappers and
documents the approval re-check. Tests: added the re-gate (blocks-deny,
allows-benign), multi-handler last-wins, and hook computer-skip cases.
A `tool_call` handler (extension or hook) could previously only block a
tool. It can now also return `input` to replace the arguments the tool
executes with, so a handler can normalize or rewrite a built-in's input
without reimplementing the tool.
The returned object is the raw execution input passed to the tool's
`execute` (the handler owns its correctness), not the normalized
`event.input` view, which may carry derived gate-only fields (e.g.
hashline `edit` `path`/`paths`) that are not real parameters. It is
ignored when `block` is set, and not applied to `computer` tool calls
whose event input is a synthetic actions view rather than the real
params. When multiple handlers set `input`, the last one wins.
Honored in both the extension and hook tool wrappers; documented in
docs/extensions.md, docs/hooks.md, and docs/skills/authoring-hooks.md.
Restored the historical clampThinkingLevel export through the legacy pi-ai shim and exposed ModelRegistry.getApiKeyAndHeaders for extension request authentication.
Added compatibility and auth result regression coverage.
Fixes#6648
Legacy pi's @earendil-works/pi-coding-agent re-exported estimateTokens
from its coding-agent package root. In omp it lives in
@oh-my-pi/pi-agent-core/compaction and the coding-agent barrel does not
forward it, so the shim's `export * from "../index"` left it off the
surface. A named import tripped Bun's static export check during plugin
validation (e.g. `omp plugin install pi-blackhole`).
Re-export estimateTokens from the shim and pin it with a regression test.
Fixes#6583
Raced session_stop handlers against the active settle signal and discarded cancellation without timeout errors.
Added runner and AgentSession regressions for pre-dispatch and in-flight aborts, timeout preservation, and stale continuation suppression.
Fixes#6489
- Added the upstream keyText helper to the legacy package-root shim.
- Covered active keybinding formatting and documented the compatibility fix.
Fixes#6470
Extract the first-wins global registration into an exported
ensureGraphCommonJsRequireRegistered() seam and assert its idempotent
(first-wins) contract directly, instead of copying the module and importing
the copy at runtime. Removes the inline await import() banned under
packages/coding-agent and keeps regression coverage: the test fails if the
registration reverts to an unconditional set.
Fixes#6449
On npm/source-link installs the @(scope)/pi-coding-agent root shim is served
from src/, so an extension's import evaluates a second instance of
legacy-pi-compat.ts. Its unconditional top-level
Reflect.set(globalThis, COMMONJS_REQUIRE_GLOBAL, evaluateGraphCommonJs)
clobbered the host bundle's populated CommonJS graph bridge with an empty-state
copy, breaking transitive CommonJS dependency resolution for legacy pi
extensions ("Missing graph-owned CommonJS definition").
Guard the registration to first-wins so the host-owned bridge survives a
second module instantiation.
Fixes#6449
Collect TSImportEqualsDeclaration/TSExternalModuleReference targets so
legacy .ts/.cts extensions using `import x = require("pkg")` get their
bare dependencies pinned like plain require() calls. Fold of the #6256
follow-up (comicchang/oh-my-pi@1e54b68) requested on #6324.
Aligns the shim's runtime safeParse/__validator with the wire/tool-call
path, so legacy draft-07 documents (tuple items) accept the same values
validateToolArguments does. Adds a regression test.
customToolToDefinition rebuilt ToolDefinition without strict, so the
Task proxies' (and startup MCP tools') explicit strict:false was dropped
before RegisteredToolAdapter and the OpenAI-family serializers saw it.
Declare strict on ToolDefinition, copy it in the bridge, and cover the
proxy -> definition -> registered adapter path in the parity test.
Emitted session_shutdown after model rendering and centralized managed timer cleanup across one-shot listings and agent sessions.
Added regression coverage for the extension shutdown lifecycle.
Fixes#6297
Used the shared JSON Schema validator for Type.Unsafe so direct safeParse calls and composed shim schemas reject invalid values while preserving valid input identity.
Added direct and composed runtime regression assertions.
Preserved raw JSON Schema documents while keeping the shim validator identity-based, allowing provider wire conversion and runtime argument validation to consume MCP input schemas.
Added direct shim and remapped-extension regression coverage.
Fixes#6221
Passed the per-request model through SDK payload callbacks and ExtensionRunner context creation.
Added regression coverage for Codex-primary and Anthropic-request contexts.
Fixes#6006
omp's canonical getPackageDir() returns undefined inside a bun --compile
binary (import.meta.dir is /$bunfs/root, no owning package.json — issue
#1423). Re-exporting it directly broke pi's string-valued contract:
legacy extensions doing path.join(getPackageDir(), ...) crashed at
runtime in the shipped binary, the primary distribution.
Wrap the canonical helper so the shim always returns a string, falling
back to the executable's directory in compiled mode (where the binary is
the install root). PI_PACKAGE_DIR and dev/source/npm-dist walk-up still
win. Test covers the compiled-mode fallback via isCompiledBinary spy.
Fixes#5968
The legacy pi compatibility shim only forwarded getAgentDir (via
`export * from "../index"`). getProjectDir and getPackageDir were absent,
so any pi extension importing either from @earendil-works/pi-coding-agent
failed Bun's static export check during extension validation and the
install was rolled back.
Re-export getProjectDir from @oh-my-pi/pi-utils and getPackageDir from
omp's canonical config helper (returns the coding-agent package root,
matching pi semantics).
Fixes#5968
Create the resolved agent database parent before the synchronous compatibility store opens SQLite, and cover a fresh nested agent directory.
Fixes#5879