The #3063 fix introduced a second mutating step — `bun update <name>` —
that rewrites bun.lock before extension validation runs. Three failure
paths could still leave the rejected commit pinned in the lockfile or
active tree:
- Extension validation throwing after `bun update` had refreshed
bun.lock — rollback restored package.json and node_modules/<name>
but never touched bun.lock.
- Feature validation (`omp plugin install pkg[ghost]`) throwing
outside the rollback block entirely.
- Runtime-config save failing after a successful install with no
rollback path.
Snapshot bun.lock alongside package.json before `bun install` runs and
route every post-install step (resolution, update, package.json read,
feature validation, extension validation, runtime-config save) through
one outer catch that restores all three (package.json + bun.lock +
node_modules/<name> from snapshot). `#rollbackFailedInstall` now
tolerates an unresolved `actualName` for failures that throw before
the dep key is known.
Three regression tests in plugin-install-validation.test.ts pin the
new contract: bun.lock restoration after a git reinstall fails
validation, bun.lock removal when it didn't exist pre-install, and
rollback on an unknown feature request.
Addresses review feedback on #3069.
bun install <spec> respects the existing bun.lock pin when the spec is
unchanged and never re-resolves the remote ref, so re-running
`omp plugin install github:owner/repo` on an already-installed plugin
reported success while silently keeping the user on the original
resolved commit (1ms no-op, no network).
PluginManager.install now follows a git re-install with
`bun update <name>` to force re-resolution of the ref against the
upstream. First-time installs (no prior dep entry) skip the update —
the initial bun install already fetches HEAD. bun update failures
trigger the same rollback path as validation failures.
Fixes#3063
- Delay the creation of the extension context until a relevant handler is identified.
- Avoid unnecessary performance overhead for events that have no registered handlers, such as frequent streaming updates.
- Added a `mode` property to `CompactOptions` to allow fine-grained control over compaction strategies.
- Implemented `soft`, `remote`, and `snapcompact` submode overrides for the `/compact` command.
- Integrated `parseCompactArgs` to enable robust subcommand routing and validation, including focus instruction rejection for specific modes.
- Established a `CompactMode` registry to manage compaction strategies and verify remote availability.
- Support bare 'typebox' package specifier in on-resolve hooks and source remapping, mirroring '@sinclair/typebox' remapping onto the in-repo Zod-backed shim.
- Support 'getModel', 'getModels', and 'StringEnum' imports from the '@oh-my-pi/pi-ai' package root in the legacy pi-ai compatibility shim.
Restored StringEnum's plain string enum wire schema and returned legacy coding tool placeholders that let createAgentSession keep the real built-in tools active.
Fixes#2858
Added compatibility exports for legacy plugin validation, including defineTool, StringEnum, TypeBox bare imports, frontmatter helpers, SettingsManager, and createCodingTools.
Updated SDK settingsManager alias handling and regression coverage for the affected shim surfaces.
Fixes#2858
- Imported and injected `zod/v4` under `zod` in runtime environments for custom commands, custom tools, extensions, and hooks.
- Added corresponding Type definitions to `CustomCommandAPI`, `CustomToolAPI`, `ExtensionAPI`, and `HookAPI` interfaces.
- Re-exported Zod and its alias from the main package entry point to expand the public API surface.
- Refactored the internal Typebox validation shim to support parsing via standard `.safeParse()` and `.toJSON()` methods.
- Advanced Typebox shim utility capabilities with support for dual-key record validation and enhanced schema structure handling.
- Added explicit ArkType schema descriptions across all coding agent tool definitions.
- Updated schema definitions in autoresearch and commit tools with descriptive wrappers.
- Documented tool schema enhancements in the packages/coding-agent CHANGELOG.
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
- Added context snapshot metadata to AssistantMessage for prompt and non-message token history.
- Anchored context usage calculations on assistant snapshots and computed percent numerically.
- Updated status-line, /context, selector, and interactive mode flows to share session usage totals.
- Extended status-line cache fingerprinting and invalidation for assistant usage and prompt/tool/skill changes.
Preserved Settings.disabledExtensions fallback when extension discovery is called without an explicit disabled list and kept hookCapability paths out of extension-module basename filtering.
Fixes#2796
Included JS and TS hookCapability results in extension path discovery so hooks under hooks/pre and hooks/post bind to the extension runner without explicit settings entries.
Fixes#2796
Taught plugin doctor to treat lockfile-only plugins with node_modules entries as installed instead of orphaned.
Added coverage for plugin doctor --fix after linking a local plugin without package dependencies.
Fixes#2742
Included lockfile-only linked plugins when building plugin list output so local symlink installs are visible after successful link operations.
Added regression coverage for plugin link followed by plugin list --json.
Fixes#2742
Run session_shutdown extension handlers concurrently under the existing shutdown cap so /exit and /quit do not wait one full timeout per hanging extension.
Fixes#2736
Installed pi packages declare their entry as `pi.extensions: ["./extensions"]` with the real module at `extensions/<name>/index.ts`, but the plugin manifest resolver only matched a file or a directory containing a direct index.{ts,js,mjs,cjs}. A directory whose entry sits one level below resolved to null, so `omp plugin install` rejected it ("declared extension entry not found on disk") and runtime load silently skipped it.
For the extensions key, resolveManifestEntryFiles now resolves a directory like OMP's configured-directory (-e) scanner: the directory's own package.json omp/pi extensions (authoritative -- a declared-but-missing entry is reported, not replaced by a decoy index), then a direct index.{ts,js,mjs,cjs}, then a one-level scan where each child directory is itself resolved manifest-first plus direct *.{ts,js,mjs,cjs} files. The directory expansion is gated to the extensions key; tools/hooks/commands keep direct-index resolution so a directory entry like `tools: "."` is unaffected.
Adds CHANGELOG Unreleased entry and regression tests for subdir-index, nested-manifest-over-index, missing-declared-entry, and key-aware (tools vs extensions) resolution. Verified: omp plugin install @zosmaai/pi-llm-wiki registers all 14 wiki_* tools; pi-mcp-adapter loads.
ExtensionRunner.emit shared the generic 30s EXTENSION_HANDLER_TIMEOUT_MS budget with every event, including the fire-and-forget session_shutdown teardown event extensions cannot observe. A hung third-party handler — observed on Windows with omp-discord-presence 0.1.2 waiting on a stuck Discord IPC pipe — held AgentSession.dispose() for the full window, making Ctrl+C look ignored for 30s.
session_shutdown now uses a dedicated 2s SESSION_SHUTDOWN_HANDLER_TIMEOUT_MS cap routed through a per-event handlerTimeoutForEvent() lookup so generic and shutdown budgets are independently configurable. The interactive-mode Ctrl+C path adds a defence-in-depth hard-exit: when isShuttingDown is true a fresh Ctrl+C exits with code 130 (the session JSONL has already been sync-flushed by the first press) instead of stacking another no-op shutdown() call.
Fixes#2600
- Normalized agent `setSystemPrompt` to wrap string inputs into one-item arrays.
- Updated session creation to accept string `systemPrompt` values and normalize callback or direct results to string arrays.
- Adjusted extension result handling and test fixtures to accept string `systemPrompt` and missing `assistant_message` fields without crashing.
- agent-loop: raise repetition-detection floor to 180 chars and clear thinking
replay anchors when collapsing a detected loop.
- providers/google: ignore empty text parts, retain terminal thoughtSignatures,
and stop function-call signatures clobbering the prior block.
- autolearn: capture goal-mode at the turn boundary; harden managed-skill writes
against hard-links/symlinks (O_NOFOLLOW + nlink); refuse minting managed skills
whose name an authored skill already claims.
- eager tasks: thread agentKind through the session so a custom top-level agentId
still gets always-mode delegation; split Eager Tasks prompt into hard vs soft.
- title-generator: race the online title model against a local tiny-model fallback.
- eager-todo: keep the soft reminder aligned with the todo init schema.
- mcp/stdio: keep close() detaching the read loop instead of awaiting it.
- stream loop: fix collapsing and tool-call thought-signature handling.
Redirected legacy pi-ai utils/oauth subpaths through the compatibility loader so background workers load the relocated OAuth registry modules.\n\nFixes #2566
The previous `anyBuiltInSkillSourceEnabled` mixed the new
`enableAgentsUser`/`enableAgentsProject` defaults with the named
third-party toggles, so a user who disabled Codex/Claude/Pi to silence
third-party CLI skill sources but kept the default `.agent[s]/skills`
toggles on still saw unknown providers (`opencode`, `github`,
`claude-plugins`, `gemini`) load via the fallback branch. The
`agents` provider already has its own explicit branch in
`isSourceEnabled`, so the fall-through gate now only inspects the named
third-party toggles. Adds a regression test that creates a fake
`~/.config/opencode/skills/leaked-opencode` and verifies it stays
filtered out when the third-party toggles are off and agents toggles
default to on.
Addresses PR #2405 review.
The managed-skill discovery block vetoes a name already in `skillMap` to let a
custom-directory authored skill win. Custom dirs never enter `result.all`, so
they are absent from `enabledAuthoredNames` — the map check is the only veto, and
it only works because the custom-dir loop runs first. Add a comment so a future
refactor doesn't invert the order.
Addresses review thread on PR #2542 (thread 7).
Add a default-off "auto-learn" loop. When `autolearn.enabled` is set, after the
agent stops a session controller nudges it to capture reusable lessons: durable
facts go to long-term memory and repeatable procedures become "managed skills" —
SKILL.md files written to an isolated ~/.omp/agent/managed-skills directory that is
discovered and surfaced like authored skills but never overwrites them.
Two tools back this:
- `manage_skill` — create/update/delete managed skills.
- `learn` — record a lesson, optionally minting/enhancing a managed skill in the
same call (requires a hindsight/mnemopi memory backend).
The nudge is passive by default (a hidden reminder rides the next turn);
`autolearn.autoContinue` instead auto-runs one capture turn at stop, and
`autolearn.minToolCalls` (default 5) gates trivial turns. Plan/goal-mode turns and
subagents are never nudged, and the controller re-checks the live setting at fire
time so a mid-session opt-out takes effect.
Isolation & precedence: managed skills are a separate lowest-priority discovery
provider, so an authored skill of the same name wins across every provider and
custom directory regardless of third-party toggles; a disabled higher-priority
authored skill can never hide a managed one, and managed never masks an enabled
authored skill. Managed names and descriptions are sanitized on both write and
read (control/format chars, angle brackets, and Markdown fences) before they render
into the system prompt, and the SKILL.md byte cap is enforced on the final
serialized file.
Default off → zero footprint when disabled.
- Added session-domain modules and exports for session-entries, context, listing, loader, and migrations.
- Changed persistence to async append writes plus writeTextAtomic, removing sync line APIs.
- Added compaction-aware session context rebuild with dangling tool-call cleanup.
- Added resumable session resolution with status inference, id/stem/suffix matching, and backup recovery.
- Switched extensibility loader imports from namespace-style `zod` imports to named `z` imports in `zod/v4`.
- Updated extensibility type interfaces to use `typeof z` for injected `zod` modules in hook, extension, tool, and command APIs.