Commit Graph
543 Commits
Author SHA1 Message Date
roboompandcan1357 a09dfd0ba8 fix(extensions): restored provider unregistration
Added the upstream unregisterProvider lifecycle to queued and initialized extension runtimes. Provider removal now clears runtime model/auth state before replacement, while failed factories restore the prior registration queue.

Fixes #7914
2026-08-07 23:38:25 +02:00
can1357 6530aa4b5b Merge PR #7709: perf(extensions): import extension modules concurrently (@metaphorics)
# Conflicts:
#	packages/coding-agent/src/extensibility/extensions/loader.ts
2026-08-07 13:39:08 +02:00
can1357 22a3393f4d Merge PR #7853: fix(extensions): roll back providers after load failure (@Mustaqeem66) 2026-08-07 13:37:58 +02:00
can1357 68dece477f Merge PR #7772: fix(session): handle subscription-cap retry exhaustion (@roboomp) 2026-08-07 13:37:54 +02:00
can1357 2ad61c7b92 feat(discovery): added Agent Plugins 1.0.0 standard support
- New agent-plugins provider discovers packages with a root plugin.json
  targeting the canonical schema (agent-plugins.org) from marketplace
  installs, --plugin-dir, and configured extension roots; skills/ and
  mcp.json load per spec with closed-schema validation,
  ${PLUGIN_ROOT}/${PLUGIN_DATA} expansion, reserved subprocess
  environment, instance-keyed data dirs, and per-component isolation.
- Package-boundary containment (spec §4.1) is enforced before every
  read via the new contained-path helpers, including skill:// resource
  access from the read tool and bash; plugin skill files must
  realpath-resolve inside the plugin root (skills carry containRoot).
- Legacy claude-plugins/omp-plugins providers yield skills and MCP
  surfaces of standard-targeting roots to the new provider and skip
  fatally invalid packages.
2026-08-07 05:59:52 +02:00
Mustaqeem66 24a334376e fix(extensions): roll back provider registrations on load failure 2026-08-06 20:00:29 +00:00
roboomp f6c5a43a1f fix(session): handled subscription-cap retry exhaustion
- Classified subscription and plan rate caps as credential-rotatable usage limits while preserving transient per-minute throttles.

- Applied reason-specific backoff to transient rate limits and collapsed exhausted retry attempts behind one budget-labeled terminal error.

- Covered classification, delay selection, persisted transcript aggregation, and retry event propagation.

Fixes #7767
2026-08-06 01:31:22 +00:00
can1357 1d181161b0 style: formatted legacy-pi shim changes with biome 2026-08-05 22:18:05 +02:00
can1357 21a7a32eb6 fix(plugins): only rewrite imported createRequire calls 2026-08-05 22:15:47 +02:00
can1357 c20d95fecc Merge PR #7730: fix(plugins): resolve createRequire deps in legacy-pi shim (@roboomp) 2026-08-05 22:15:47 +02:00
can1357 677b6f10f5 Merge PR #7735: fix(coding-agent): return ToolInfo[] from getAllTools extension API (@roboomp) 2026-08-05 22:15:46 +02:00
roboomp 83496b8211 fix(coding-agent): returned ToolInfo[] from getAllTools extension API
The ExtensionAPI getAllTools() wired to session.getAllToolNames(),
returning bare tool-name strings. Upstream @earendil-works/pi-coding-agent
promises ToolInfo[] with sourceInfo, so extensions loaded through the
legacy-pi shim (e.g. gentle-pi) crashed on t.sourceInfo.source at every
session start.

Added SourceInfo/ToolInfo types plus SessionTools.getAllToolInfos(), which
returns { name, description, parameters, sourceInfo } and classifies each
tool as builtin/mcp/sdk/extension. Rewired every getAllTools action site
(interactive, acp, print/rpc, subagent executor) and the example extension.

Fixes #7732
2026-08-05 15:42:06 +00:00
roboomp 31ee049374 fix(plugins): resolve createRequire deps in legacy-pi shim
The legacy-pi load-time rewriter only recognized static require()/import
specifiers, so an extension resolving a bundled dependency through the
createRequire(base)(spec) factory form (e.g. gentle-pi loading
@heyhuynhgiabuu/pi-pretty) left the bare specifier untouched. In a
compiled binary that argument then fell through to native node_modules
resolution, which is unavailable under --compile, failing extension
validation and session load.

collectExtensionSpecifierReferences now detects createRequire(...)(spec)
factory invocations and records the invoked bare specifier as a require
reference, so the existing pipeline pins it to an absolute path. Relative
specifiers are left alone since they resolve against the createRequire
base, which is not rewritten.

Fixes #7728
2026-08-05 15:02:07 +00:00
can1357 e9888367d1 refactor: migrated packages to internal utility modules and removed external dependencies
- 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.
2026-08-05 13:39:09 +02:00
metaphorics d2126b94f4 perf(extensions): import extension modules concurrently
Extension loading iterated paths with await in a for...of loop, so each
module import (file I/O + module evaluation, the dominant cold-start
cost) blocked the next. Split loadExtension into a concurrent import
phase (Promise.all) and a sequential factory-binding phase that runs in
the original path order, keeping registration semantics (last-wins
collisions, shared runtime flag defaults) deterministic and per-extension
error isolation intact.

Fixes #7615
2026-08-05 10:43:18 +00:00
roboomp 2cfaa7adbc fix(coding-agent): restored legacy convertToPng export
- Restored the package-root image conversion contract through the legacy compatibility shim.

- Covered PNG passthrough, image conversion, and decoder failure behavior.

Fixes #7610
2026-08-04 07:31:16 +00:00
can1357 bc39ffa265 feat: introduced omptype validation package and migrated workspace dependencies
- Introduce `@oh-my-pi/omptype` as a new ArkType-compatible schema validation package featuring a lazy JIT runtime, JSON Schema emission, and compatibility adapters.
- Replace `arktype` across workspace packages and test utilities with `@oh-my-pi/omptype`.
- Add benchmark suites, tests, and documentation for the new validation engine and adapters.
- Update workspace build, test runner, and release configurations to include the new package.
2026-08-03 21:56:48 +02:00
can1357 0244e18e43 fix(extensions): refreshed formerly synchronous sources on reload walks
- A reload that drops a module's last require() edge leaves the permanent
  hooks serving it from the synchronous snapshot map, which was only
  refreshed while the path stayed flagged; an edit after the downgrade
  replayed stale bytes. Ensure now re-rewrites and refreshes the snapshot
  for every ever-synchronous path on each graph walk.
- Added the mirror reload regression (require edge dropped + source edited).
2026-08-03 05:42:07 +02:00
can1357 e1ac59e5df fix(extensions): served sync-upgraded modules from earlier async load hooks
- A reload that adds a require() edge to an already-hooked ESM module never
  re-registers hooks, and the original async onLoad filter keeps matching;
  require() rejects async onLoad results, so the async hook now serves the
  pre-rewritten synchronous source inline when one exists.
- Added a same-process reload regression covering the async-to-sync upgrade.
2026-08-03 05:33:06 +02:00
can1357 c83bff390d Merge PR #7406: fix(coding-agent): restore legacy compaction exports (@roboomp) 2026-08-03 05:26:10 +02:00
roboomp b53b1d1e65 fix(extensions): rewalked sync-upgraded ESM graphs
Requeued already-processed ESM modules when a later CommonJS require upgraded them to synchronous loading, propagating the sync marker through their descendants.

Added an end-to-end regression covering normal discovery before a lazy CommonJS require of the same ESM graph.

Fixes #7402
2026-08-02 22:17:59 +00:00
roboomp 326fee3387 fix(coding-agent): preserved legacy simple stream options
- Routed streamSimpleOpenAIResponses through the central simple-stream dispatcher.
- Added wire-level coverage for hidden reasoning summary translation.

Fixes #7403
2026-08-02 22:15:12 +00:00
roboomp fb62feeecf fix(extensions): retained lazy ESM sources
Kept pre-rewritten synchronous ESM sources available to permanent load hooks after the initial extension import settles, while refreshing them on reload.

Added an end-to-end regression for a CommonJS dependency that lazily requires a nested ESM cluster.

Fixes #7402
2026-08-02 22:09:44 +00:00
roboomp c34c2830ce fix(coding-agent): restored legacy compaction shims
- Re-exported serializeConversation from the legacy coding-agent package root.
- Aliased the upstream simple OpenAI Responses stream name to OMPs equivalent.
- Added regression coverage for both compatibility exports.

Fixes #7403
2026-08-02 22:03:20 +00:00
roboomp e682e25b24 fix(extensions): resolved compiled dependency graphs
Resolved nested ESM imports required by CommonJS packages, deterministic dual-package graph convergence, and package-main directory requires in compiled binaries.

Added focused graph-rewrite regressions and documented the fix.

Fixes #7402
2026-08-02 22:02:44 +00:00
can1357 f7480294b4 fix(agent): track rewritten xdev approval tiers 2026-08-02 20:57:06 +02:00
can1357 846f7977dd Merge PR #7246: fix(coding-agent): let customDirectories skills win over default-path duplicates (@zhang17-24) 2026-08-02 20:53:47 +02:00
roboomp e27b0e81ec fix(postmortem): resolve native hard-exit per call
The postmortem module bound the native hard-exit once at module init
(process.reallyExit.bind(process)). The shipped bundle defers this
module's evaluation until first access, which can land inside a
withHostGuard window where process.reallyExit is the ExtensionExitError-
throwing stub; .bind() then froze that stub permanently, so every later
host-owned exit (SIGHUP 129, SIGINT 130, fatal 1) threw and re-entered
the unhandled-rejection fatal path in a loop (exit 129 storm).

Resolve the native exit on every call instead of binding at init, and
have withHostGuard stamp its throwing replacement with the native
primitive it shadows so a signal arriving mid-guard still exits (#6488)
without the guard poisoning later exits (#7393).

Fixes #7393
2026-08-02 17:32:12 +00:00
can1357 72c66c87c1 fix(xdg): adopted legacy secret-placeholder.key and marketplaces.json at XDG paths 2026-08-01 20:46:25 +02:00
can1357 03e54555f9 Merge PR #7065: fix(xdg): fix files and folder for xdg-maintained (@Parsifa1) 2026-08-01 20:39:29 +02:00
Sunil Srivatsa b1a46eec5c fix(coding-agent): ignore unreadable hook dirs 2026-08-01 13:15:19 -04:00
Sunil Srivatsa 5d8a2d6b82 fix(coding-agent): scope explicit hook discovery 2026-08-01 13:04:04 -04:00
Sunil Srivatsa 8a7edb3f3d fix(coding-agent): preserve explicit extensions in isolation 2026-08-01 12:32:19 -04:00
zhang17-24 1596d9231e fix(coding-agent): let customDirectories skills win over default-path duplicates
When a skill name exists both in a default discovery path (e.g.
~/.claude/skills/<name>) and in an explicitly configured
skills.customDirectories entry, the default-path copy loaded first and the
custom-directory skill was silently dropped on the name collision. As a
result skill://<name> resolved to the default path and reported "File not
found" when the user's skill lived only in the custom directory.

Custom-directory skills now override same-named default-path skills (the
user's explicit configuration is the higher-priority source); duplicates
within customDirectories keep first-wins.
2026-08-01 16:05:31 +08:00
Parsifa1 ea437745a3 fix(xdg): move secret-placeholder.key, marketplaces.json, and run/ out of config root
These four paths bypassed DirResolver's XDG-aware rootSubdir/agentSubdir
hooks, resolving directly against getConfigRootDir()/getAgentDir() and
ignoring XDG state/data layout. Add XDG-aware path helpers in dirs.ts
and route all four through them:

- secret-placeholder.key → $XDG_STATE_HOME/omp/ (state, agent flattened)
- marketplaces.json      → $XDG_DATA_HOME/omp/  (data)
- run/daemons/<hash>/    → $XDG_STATE_HOME/omp/run/ (state)
- run/provider-inflight/ → $XDG_STATE_HOME/omp/run/ (state)

omp config init-xdg migrates secret-placeholder.key and marketplaces.json
from their legacy locations; run/ is ephemeral and rebuilds on restart.
2026-08-01 06:40:48 +00:00
can1357 f76ce86966 Merge PR #6840: feat(extensions): add ctx.invokeTool for native built-in delegation (@psyrendust) 2026-07-31 20:17:32 +02:00
can1357 3411bc3c8d Merge PR #7177: fix(coding-agent): keep ExtensionContext.cwd live across /move (@KennethHoff) 2026-07-31 19:07:18 +02:00
Kenneth Hoff ae08bd57cb fix(coding-agent): keep ExtensionContext.cwd live across /move
ExtensionRunner cached cwd from its constructor argument, which is set
once at session start. /move (SessionManager.moveTo) relocates the
active session's directory, but ExtensionRunner never re-read it, so
every ExtensionContext built afterwards (tool calls, hooks, slash
commands) kept reporting the pre-move directory for the rest of the
session -- observed while building an extension that tracks the
session's git worktree via ctx.cwd.

Turn cwd into a getter over this.sessionManager.getCwd() instead of a
constructor-time snapshot. Session-scoped, not the process-global
project directory: the interactive /move handler happens to also
chdir the process (command-controller.ts -> applyCwdChange ->
setProjectDir), but moveTo() itself never touches that global, so a
programmatic AgentSession.moveSession()/SessionManager.moveTo() call,
a collab guest adopting a host's session cwd without chdir'ing, or an
SDK/ACP session opened via createAgentSession({ cwd }) with a cwd that
differs from the process's own would all still observe a stale
ctx.cwd under a getProjectDir()-based getter. Reading the runner's own
sessionManager -- already held for other purposes -- covers every one
of these instead of just the single-session interactive case.

The constructor parameter is kept (renamed _initialCwd, documented as
ignored) so the two existing call sites don't need touching.

Added a regression test constructing a real ExtensionRunner over an
in-memory SessionManager, relocating it via SessionManager.moveTo(),
and asserting both runner.cwd and createContext().cwd observe the new
directory.
2026-07-31 15:13:37 +02:00
roboomp 922e7a58ef fix(coding-agent): re-export compact from legacy pi shim
Legacy pi extensions import `compact` from the `@earendil-works/pi-coding-agent`
package root (aliased to the legacy shim). It lives in
`@oh-my-pi/pi-agent-core/compaction` (same module as the already-bridged
`estimateTokens`) and the coding-agent barrel does not forward it, so the shim's
`export * from "../index"` left it off the surface and a named import failed
Bun's static export check during plugin validation
(`omp plugin install npm:pi-claude-bridge`).

`keyHint` (also named in the report) already resolves via the modes/components
barrel, so only `compact` needed bridging.

Fixes #7174
2026-07-31 12:07:44 +00:00
Larry Gordon e8d9f15e9d fix(extensions): inherit the wrapper call's abort and progress channels
A bare ctx.invokeTool(params) passed undefined for both signal and onUpdate,
so a wrapper that simply delegates did not stop the native tool when the
outer call was aborted, and native progress updates were dropped unless every
wrapper forwarded them by hand.

createContext now takes the delegation wiring as one named object and binds
the wrapper's own signal and onUpdate as defaults for the delegated call, with
explicit invokeTool options still taking precedence. Grouping toolName, depth,
context, signal, and onUpdate together also keeps the signature readable now
that delegation carries five inputs.

Tests: the delegated native call receives the outer signal and onUpdate,
explicit options override them, invokeTool is absent when no native built-in
of that name exists, and recursion stays bounded per call chain.
2026-07-30 10:35:12 -07:00
Larry Gordon efaf4f5d49 fix(extensions): preserve the caller tool context when delegating via invokeTool
The delegated native call built a fresh AgentToolContext with no toolCall
metadata. Native tools read that: write/edit derive LSP batch flushing from
context.toolCall, and computer uses provider metadata plus
providerSafetyApproved for the required screenshot/safety acknowledgement, so
wrapping those tools lost batching and dropped provider result metadata.

Thread the caller's own context (the one the re-registered tool received)
through RegisteredToolAdapter.execute and createContext into invokeNativeTool,
and reuse it for the native call instead of a bare getContext(), falling back
to a fresh session context only when the caller had none.
2026-07-30 10:35:12 -07:00
Larry Gordon a50bcd5fed fix(extensions): wire invokeTool to the extension path as same-tool delegation
Addresses PR review. The initial version put invokeTool on AgentToolContext
via ToolContextStore, but the extension execute path (RegisteredToolAdapter)
builds its own ExtensionContext and never saw it, so the documented
registerTool wrapper use case did not work. It also allowed arbitrary
cross-tool targets (bypassing the target's approval policy), used a
session-global recursion counter that tripped on concurrent independent
delegations, and missed discoverable built-ins that xdev partitioning moves
out of the tool array.

Rework:
- Move invokeTool onto ExtensionContext, and bind it in RegisteredToolAdapter
  to the tool's own name, so a re-registered built-in actually receives it.
- Make delegation same-tool only: invokeTool takes just (params, options) and
  runs the native built-in of the caller's own name. It cannot reach an
  arbitrary target, so it cannot escalate past the approval already granted
  for the call, and the native call is not re-gated.
- Track recursion depth per call chain (threaded through invokeNativeTool and
  createContext) instead of session-global state, so concurrent delegations
  do not interfere.
- Seed the native resolver from the xdev registry when present (it retains
  discoverable built-ins like browser), else the built-in registry.

Replaces the ToolContextStore-level unit test with an end-to-end test that
registers a built-in wrapper through the extension/session path and asserts
the native tool runs the wrapper's delegated input.
2026-07-30 10:35:12 -07:00
roboomp 766ccbf24a fix(coding-agent): add createEditTool/createWriteTool to legacy pi shim
The legacy @oh-my-pi/pi-coding-agent shim exported the read/bash/grep/find/ls
tool factories but omitted the edit and write ones. pi extensions importing
createEditTool or createWriteTool (e.g. gentle-pi) failed Bun's static export
check during extension validation, blocking omp install.

Added createEditTool/createEditToolDefinition and createWriteTool/
createWriteToolDefinition, mirroring the upstream pi surface and the existing
sibling factories. The unsupported operations seam throws a descriptive error
like createGrepTool does.

Fixes #7094
2026-07-30 14:36:10 +00:00
can1357 43e5df012e Merge PR #6791: feat(ai): handle Cursor's modern exec wire protocol (@quantmind-br) 2026-07-30 01:48:52 +02:00
can1357 857b70fe99 Merge remote-tracking branch 'refs/remotes/pr/6535' into prep/6535
# Conflicts:
#	packages/coding-agent/src/extensibility/extensions/runner.ts
2026-07-30 01:42:24 +02:00
Diogo Soares Rodriguesandcan1357 6eaf090ccc fix(cursor): repair pi_edit and close the scoped-grep approval bypass
Three defects the exec bridge shipped with, all found by review.

`pi_edit` never worked. The session removes `edit` from the tool
registry for Cursor so the model is steered to full-file `write`
(8ba0498eb), but that same registry is the bridge's tool source, so the
native frame — which the server sends regardless of the advertised
catalog — resolved nothing and answered `Tool "edit" not available`.
Retaining the instance is not enough either: `PiEditExecArgs` carries
`old_text`/`new_text` pairs, which only `replace` accepts, while the
default mode is `hashline` (`{ input: string }`). `EditTool` now takes
an optional mode, and the bridge resolves a pinned `replace` instance
through its fallback resolver.

A `pi_grep` frame carrying `context` or `limit` escaped the approval
gate. Honoring those needs a per-call tool, and the per-call instance
was built raw while every registry tool is wrapped — so exactly those
calls skipped `tools.approval.grep` and the exec-tier SSH check. Both
callsites now go through one `createBridgeGrepFactory`.

Advisors ignored the same two fields: only the primary session supplied
the factory. They now get it too, gated on the advisor actually holding
`grep` so the factory cannot grant a denied tool.

Also moves the pure Pi arg translation to `providers/cursor-pi-args`.
The legacy shim shares it and is compiled into the bundled virtual
registry, where `./providers/*` cannot match a nested specifier — it
fell through to `Bun.resolveSync`, unsatisfiable under bunfs (#3442) —
and the exec module would have dragged the protobuf graph along.

Verified against real files and the real module graph: `pi_edit` mutates
a temp file, the bundled probe executes the shim's shared module in a
subprocess, and the grep test drives the shared factory. Mutation-
checked: returning a raw tool from the factory, ignoring the pinned edit
mode, dropping the `getTool` fallback, or moving the helpers back to a
nested path each fails a test.

(cherry picked from commit e46ba22b634e449005f7c22b6d0efd19a45ce1f8)
2026-07-30 01:42:23 +02:00
Diogo Soares Rodriguesandcan1357 ab7b457af0 fix(cursor): read pi_bash truncation from the shape BashTool emits
Two truncation records exist locally. `read`/`grep` set
`details.truncation` (`TruncationResult`), which carries an explicit
`truncated` boolean. `bash` sets `details.meta.truncation`
(`TruncationMeta`), which has no such flag — its presence is the signal.

`piTruncation` read only the first and required the boolean, so every
real Bash truncation was dropped: Cursor got clipped output with no
indication it was clipped. Both shapes now translate; `TruncationResult`
stays authoritative when present so an explicit `false` still suppresses.

Also drops the legacy pi shim's copies of the regex-literal escaper and
the path/glob join. Both were verbatim duplicates of the modern bridge's
helpers, which is the drift the shared translation exists to prevent.

Verified producer-to-consumer, not against a hand-built bag: the test
runs a real `BashTool`, asserts its output has no top-level `truncation`
and no `truncated` flag under `meta`, then feeds those exact details to
`piTruncation`. Typed against the producer's own `TruncationMeta`, so a
renamed field fails compilation rather than silently reverting the bug.
Mutation-checked: reverting to the top-level lookup, restoring the flag
requirement, or dropping the null guard each fails a test.

(cherry picked from commit 6699672d52061b832677dd45315f4aba8d330db1)
2026-07-30 01:42:11 +02:00
can1357 5486c8fd1d Merge PR #6939: feat(extensions): expose session async job snapshots (@usr-bin-roygbiv) 2026-07-30 01:26:50 +02:00
usr-bin-roygbiv 085f870faa feat(extensions): expose session async job snapshots 2026-07-28 23:12:28 +00:00
Gy-Hu e404102acc fix: forward parseArgs and CONFIG_DIR_NAME from the legacy pi shim
Legacy pi extensions import both from the `@earendil-works/pi-coding-agent`
package root, which omp aliases to legacy-pi-coding-agent-shim.ts. `parseArgs`
lives in ../cli/args and CONFIG_DIR_NAME in @oh-my-pi/pi-utils, and neither is
reachable through `export * from "../index"`, so Bun's static export check
rejects such extensions during validation.

Same class of barrel gap as issues #5968 and #6583.
2026-07-28 22:14:23 +08:00