Commit Graph
604 Commits
Author SHA1 Message Date
can1357 9913c58ec1 Merge branch 'main' into pr-8052 2026-08-17 11:00:58 +03:00
can1357 5b24971163 fix(extensions): charged custom dialog setup to timeout 2026-08-16 02:45:04 +02:00
can1357 06793c72bb Merge PR #8441: fix(extensions): pause tool-call timeout during human dialogs (@Seljuke)
# Conflicts:
#	packages/coding-agent/test/extensions-runner.test.ts
2026-08-16 02:45:04 +02:00
can1357 5febe05649 Merge PR #8608: fix(tui): wait for wire-aliased edit previews (@roboomp) 2026-08-16 02:43:32 +02:00
can1357 e9edc5b62f Merge PR #8628: fix(cli): expand marketplace catalog home paths (@roboomp) 2026-08-16 02:43:32 +02:00
can1357 b445134b4d Merge PR #8370: fix: retry transient Anthropic failures at oneshot LLM call sites (@wonjun3991)
# Conflicts:
#	packages/coding-agent/src/utils/title-generator.ts
2026-08-16 02:14:12 +02:00
can1357 0547176f11 fix(models): skipped configured hooks in catalog listing 2026-08-16 02:13:40 +02:00
can1357 ac4caf3d75 Merge PR #8414: fix(models): isolate ambient hooks from catalog listing (@starlink-awaken) 2026-08-16 02:13:40 +02:00
roboomp 99cd009285 fix(cli): expanded marketplace catalog home paths
Resolved persisted home-relative marketplace catalog paths before cache reads and writes, and migrated them to absolute paths on update.

Added a regression test covering cwd-relative literal tilde writes.

Fixes #8627
2026-08-15 08:17:54 +00:00
roboomp 1fc05fc635 fix(tui): waited for wire-aliased edit previews
Matched model-scheduled tool calls against canonical and custom wire names so approval waits for the rendered edit diff.

Covered canonical and apply_patch alias approval ordering.

Fixes #8607
2026-08-15 03:32:17 +00:00
Larry Gordon cb4b936b93 fix(extensions): rebuilt the fallback context on each invocation
Review follow-up on #8052. The fallback trampolines captured one
`ExtensionContext` at `ExtensionRunner.initialize` and reused it for the
life of the session, unlike every other dispatch site, which builds one
per call. `createContext()` materializes `cwd` and `hasUI` as values, so
a handler kept seeing the workspace the runner initialized in — wrong
the moment `SessionManager.moveTo()` relocates the session (`/move`),
where a handler that scopes or prompts against `ctx.cwd` would allow the
old workspace and deny the new one.

Both trampolines now build the context inside the invocation. A denied
mutation is a rare path, so the extra object costs nothing that matters,
and anything else `createContext()` snapshots is refreshed with it.

Covered by an integration test that moves the session's cwd after
initialize and asserts the handler sees the new one; hoisting the
context back out of the invocation fails it.
2026-08-14 08:55:45 -07:00
Larry Gordon 6ad935d093 fix(extensions): resolved brokered file paths and named their session
Review on #8052 found three problems in the write/delete fallback seam.

A symlink guard that only `lstat`'d the final component let the same
escape through a symlinked ancestor: `ws/link/file` under a
`ws/link -> /outside` link reached a handler as a lexically innocent
path, so a helper's prefix allowlist passed while the bytes landed
outside. Refusing every symlinked component is not available, since
`/var` and `/tmp` are links on macOS and every path under `os.tmpdir()`
traverses one. So `req.dst` is now the path the failed syscall itself
acted on, via `resolveSyscallTarget` beside `confineToWorkspace`: fully
resolved for a write, resolved up to the last component for a delete,
because `unlink` removes a link rather than following it. Resolving also
closes the TOCTOU window a refusal left open. A path that cannot be
canonicalized — a dangling final link, or an ancestor whose own
resolution is denied — is not brokered at all.

Per-handler throw isolation lived outside the per-extension trampoline,
so a throw from one extension's first handler advanced the registry to
the next extension and skipped every later handler that one had
registered. Each handler call is now wrapped individually.

The registry stays process-wide. A subagent spawned with restricted
tools gets `preloadedExtensionPaths: []` and loads no extensions of its
own, so scoping resolution to the originating session would turn its
brokered writes into hard failures, and a host that registers once in
its top-level session expects its subagents covered. The request names
its origin instead: `req.sessionId` against the handler's own
`ctx.sessionManager.getSessionId()`, entered by `ExtensionToolWrapper`,
which `sdk.ts` already puts around the whole tool registry whenever a
runner exists. Both registries are also walked over a snapshot, so a
concurrent session shutdown cannot make another session's walk skip
whichever handler shifted into the hole.

Unit tests go 30 -> 35 and integration 6 -> 7, covering the resolved
target, a symlinked ancestor on both seams, a dangling link, a target
whose own metadata is behind the boundary, same-extension handler
ordering after a throw, and `req.sessionId` matching the handler's own
session end to end.
2026-08-14 08:55:44 -07:00
Larry Gordon 6e4334c003 feat(extensions): broker denied file writes and deletes
A host that runs omp inside an OS sandbox can grant a path mid-session but cannot
apply that grant to an in-process write: `write` and `edit` do their I/O in the
agent process, so an out-of-workspace write fails and stays failed until the
process restarts under a wider profile.

Nothing available today closes that. A `tool_call` handler can block and a
`tool_result` handler can rewrite content, but neither can re-run a tool.
`ctx.invokeTool` delegates execution, but the delegated native tool runs in the
same process under the same restrictions. And the failure lands AT the write
syscall - after the tool computed the final content, before it returned - so the
bytes are gone with the throw, and reconstructing them means reimplementing
`edit`'s hashline protocol and the snapshot bookkeeping.

The byte-write that `write`, `edit` and `apply_patch` perform on an ordinary file
path already funnels through one two-line primitive
(`file ? file.write(content) : Bun.write(dst, content)`) at four call sites.
Routing that primitive through `writeFileWithFallback` gives an embedder a single
seam to intercept a permission-denied write: the native tool still records its own
snapshot under the real destination path once a handler reports success, so a
follow-up hashline `edit` on that path keeps working.

Only a permission boundary diverts - `EPERM`/`EACCES`/`EROFS`. Two cases needed
more than that:

- `Bun.write` creates missing parents itself, and when that `mkdir` is the denied
  operation it reports the subsequent `open()`'s `ENOENT` instead of the denial -
  making a sandboxed write into a new out-of-tree directory indistinguishable from
  an ordinary bad path. Redoing the `mkdir` explicitly recovers the real errno, and
  because it runs through the same enforcement path as the write it also sees
  kernel-level denials (Seatbelt, LSM) that a `stat`/`access` probe reports as
  writable. If no handler takes the write, the original `ENOENT` is still what
  propagates, with the recovered denial attached as its `cause`.
- `apply_patch` creates the parent as a separate step before writing, so a denial
  there threw before the seam was ever reached. That `mkdir` now tolerates a
  permission denial when a fallback is registered, letting the write report it.

A denial reached through a SYMLINK is never brokered. The in-process write follows
the link, so the kernel denied the link's TARGET, but a handler receives `dst` and
a privileged helper opening it with ordinary follow semantics would land the bytes
wherever the link points. That also defeats the obvious helper-side defence, since
a prefix allowlist passes when the link sits inside the allowed root while its
target does not. omp cannot vouch for the destination, so it refuses rather than
hand the ambiguity to a privileged writer - the same answer `confineToWorkspace`
already gives an unresolvable link.

Removing a file is a different primitive, so it gets its own seam
(`deleteFileWithFallback`, `registerFileDeleteFallback`) covering `edit`'s `REM`,
a hashline `MV`'s source unlink, and `apply_patch`'s delete op. Two differences
from the write path: `ENOENT` is never diverted, since nothing is created on the
way to an unlink and `REM` needs it to become a not-found error; and the seam
refuses a target it can confirm is a directory, because `unlink` on a directory
reports `EPERM` on Darwin and is otherwise indistinguishable from a sandbox
denial. That check cannot always run - a sandbox denying the unlink usually denies
the target's metadata too - so the request carries `confirmedFile`, and a handler
is required to use a plain unlink rather than resolving or recursing.

The two registries are deliberately separate. A write handler brokers `content` to
`dst`, so a delete request reaching it with no content invites brokering an empty
write and truncating the file it was asked to remove.

With nothing registered both seams are inert: the primitives run exactly as
before, a failure rethrows from the same place, and no extra syscalls are
performed.

Scope is deliberately narrow. Archive-member and SQLite writes are unchanged -
neither is a byte-write to a path, so brokering them needs a different request
shape - along with the ACP bridge's `writeTextFile`, the `lsp` tool's own
workspace-edit and formatter writes, and directory removal.
2026-08-14 08:52:34 -07:00
Seljuke fd28acf5a1 fix(extensions): harden dialog timeouts 2026-08-13 20:39:12 +02:00
Seljuke 7e384fbc4f fix(extensions): pause tool-call timeout during human dialogs 2026-08-13 18:03:01 +02:00
can1357 fcdaa2162a Merge PR #8425: fix(extensions): lower embedded omptype schemas in Type.Unsafe (@roboomp) 2026-08-13 16:57:20 +02:00
roboomp a4adf17986 fix(extensions): preserved run properties in unsafe schemas
Require the omptype schema brand before treating a raw document's run key as the self-reference copied by a schema spread. This keeps legitimate JSON Schema properties named run intact while retaining spread-schema recovery.

Added regression coverage for a required boolean run parameter.
2026-08-13 09:06:18 +00:00
roboomp 9d7e13a158 fix(extensions): lowered embedded omptype schemas in Type.Unsafe
Legacy Pi extensions build raw tool-parameter documents against real
TypeBox, whose Type.* builders return plain JSON-Schema objects. omptype's
builders return callable schema values instead, so a legacy document that
embeds them (Type.Unsafe({ anyOf: [Type.Array(...), Other] })) or spreads
them (Type.Unsafe({ ...Schema, description })) carries functions. The shim
then structured-cloned that document during plugin install validation and
threw "The object can not be cloned.", rejecting extensions that load fine
when linked (e.g. pi-subagents, all published versions).

Type.Unsafe now lowers embedded builders to their wire JSON and rebuilds
spread documents from the copied run self-reference before cloning or
serializing, matching the plain-object schemas real TypeBox produces.

Fixes #8420
2026-08-13 08:59:50 +00:00
roboomp 7463803c95 fix(extensions): populated runtime mode in context
Expose the Pi-compatible tui, rpc, json, or print host mode to every extension context and cover mode transitions in the runner regression suite.

Fixes #8419
2026-08-13 08:51:05 +00:00
starlink-awaken d3ad83b3a8 fix(models): isolate ambient hooks from catalog listing 2026-08-13 15:01:53 +08:00
wonjun3991 38a2040b8d fix(coding-agent): retry transient failures at oneshot LLM call sites
Session title generation, TTS speech enhancement, commit-message
generation, the auto-thinking and unexpected-stop classifiers, memory
extraction/consolidation, the commit analysis/summary/changelog/map/reduce
passes and the mnemopi LLM callback each aborted or degraded on the first
transient blip. Several returned null, which made a provider overload
indistinguishable from a legitimate empty result.

The commit analysis, summary, changelog and reduce passes also fed a
provider error message straight into their response parsers: they never
checked stopReason, so a failed request produced garbage instead of
surfacing the error. The map phase's own retry helper only caught thrown
errors, so a resolved stopReason "error" bypassed it entirely.

Sites deliberately left unwrapped: the Anthropic web_search provider
(server-side execution, billed per search - a re-request duplicates a real
side effect), the legacy streamSimple shim (already-emitted events would
duplicate), the auth-gateway health probe (a probe must report the state
it observed), and the script paths whose own retry logic tracks per-attempt
usage or re-requests only the failed subset.
2026-08-13 10:54:42 +09:00
can1357 61e12988fe Merge PR #8283: fix(coding-agent): skip idle mid-turn persistence waits (@ethancawse) 2026-08-13 02:00:50 +02:00
can1357 ea10cce8d8 style: applied biome formatting to merged pull request sources 2026-08-13 01:23:48 +02:00
can1357 ae5d58be2f Merge PR #8163: fix(coding-agent): restore is*ToolResult guards on legacy pi shim (@roboomp) 2026-08-13 01:14:49 +02:00
can1357 0c06adc423 fix(extensions): preserve additional property semantics 2026-08-13 01:14:49 +02:00
can1357 00cabe5513 Merge PR #8144: fix(extensions): preserve unsafe schemas during plugin install (@roboomp) 2026-08-13 01:14:49 +02:00
can1357 932bb6d244 Merge PR #8080: fix(session): forward retry fallback events to extensions (@roboomp) 2026-08-13 01:14:47 +02:00
can1357 340eaeb65c fix(coding-agent): avoid mutating shared omptype builder 2026-08-13 01:14:46 +02:00
can1357 bd15362a29 Merge PR #8049: fix(coding-agent): preserve ArkType command API compatibility (@evandrodevbr) 2026-08-13 01:14:46 +02:00
Ethan Cawse 92f0ad71f4 docs(coding-agent): define message-end isolation 2026-08-11 23:05:59 -04:00
can1357 47b282ff9b Merge PR #8069: fix(extensions): register lifecycle tools (@mrexodia) 2026-08-11 15:24:18 +02:00
can1357 1960f80ffd Merge PR #8179: fix(cli): honor dry-run in plugin uninstall (@roboomp) 2026-08-11 15:18:16 +02:00
can1357 f3a3073a3d Merge PR #7959: fix(tui): show edit previews before approval (@roboomp) 2026-08-11 15:18:16 +02:00
can1357 706371bb91 fix(extension-api): preserved overlay option factories 2026-08-11 15:08:39 +02:00
Vankoandcan1357 ed820703a7 fix(extension-api): restore overlayOptions/onHandle passthrough in ui.custom
showHookCustom hardcoded the overlay geometry and never read
overlayOptions/onHandle, which regressed the v0.45.6 API (PR
badlogic/pi-mono#667). Forward overlayOptions to showOverlay (keeping the
full-cover defaults as fallback), invoke onHandle with the returned
OverlayHandle, widen the options type via a shared ExtensionCustomOptions,
and re-export OverlayHandle/OverlayOptions from the extension API.
2026-08-11 15:08:39 +02:00
can1357 7896a58406 Merge PR #8050: fix(tui): keep pasted images when a mode command submits the draft (@fatihaziz) 2026-08-11 15:06:13 +02:00
can1357 8504e4865f Merge PR #7945: fix(coding-agent): resolve xd:// device dispatches against device user policy first (@re2zero) 2026-08-11 15:06:11 +02:00
Fatih Al-Aziz d194f2d76c fix(tui): honor transformed mode attachments 2026-08-11 15:52:58 +07:00
roboomp b7d83ed931 fix(cli): kept marketplace uninstall scope positional
The earlier dry-run change moved scope into an options object, silently
ignoring the legacy uninstallPlugin(id, "user") runtime shape still reachable
from compiled or plain-JS callers. Restore scope as the positional second
argument and carry dryRun in a trailing options bag, preserving the existing
call shape while keeping the non-mutating dry-run path.

Fixes #8178
2026-08-10 18:10:30 +00:00
roboomp 15a9346c29 fix(cli): preserved uninstall dry-run validation
Route marketplace dry-runs through MarketplaceManager's normal pre-mutation
validation so ambiguous or mismatched scopes fail exactly as real uninstalls
do. Move the manager's scope argument into an options object and add a dry-run
option that returns only after all removal planning has succeeded.

Fixes #8178
2026-08-10 18:02:21 +00:00
roboomp 1fa80a89ad fix(coding-agent): preserved legacy find and ls result guards
Legacy find and ls tool definitions still emit tool-result events through
omp's custom-event branch. Export their historical named guards so static
legacy imports validate, while narrowing only toolName and retaining unknown
details.

Fixes #8161
2026-08-10 13:35:27 +00:00
roboomp f178cee9a9 fix(coding-agent): restore is*ToolResult guards on legacy pi shim
Legacy pi's @earendil-works/pi-coding-agent root exported an
is<Tool>ToolResult family of ToolResultEvent type guards. omp dropped
them from the public API in 10.2.3 and the legacy shim's
`export * from "../index"` never forwarded them, so extensions importing
them (pi-lean-ctx@3.9.18 uses isEditToolResult/isWriteToolResult) failed
Bun's static export check and aborted `omp install`.

Restore isBashToolResult, isReadToolResult, isEditToolResult,
isWriteToolResult, and isGrepToolResult on the shim to match the
upstream pi surface.

Fixes #8161
2026-08-10 13:27:52 +00:00
roboomp cafa9930c8 fix(extensions): restored raw TypeBox object properties
- Reintroduced the legacy Object boundary as a normalizer that lifts direct raw JSON Schema properties into callable Unsafe schemas before delegating to omptype Object.
- Normalized raw additionalProperties schemas through the same path while retaining the no-allocation fast path for fully callable property maps.
- Added an extension-remap regression covering raw string properties mixed with optional runtime schemas.

Fixes #8143
2026-08-10 08:12:35 +00:00
roboomp 5e712d176d fix(extensions): preserve nested unsafe schema keywords
- Added type.withJsonSchema so a schema emits a raw JSON document verbatim even when embedded, surviving Type.Object/Type.Optional composition.
- Routed legacy Type.Unsafe through the authoritative JSON Schema validator and the IR-level emission override so JSON-Schema-only keywords (patternProperties, propertyNames, ...) stay in the nested wire schema and runtime no longer over-rejects pattern-matched keys.
- Isolated the emitted document from validator JIT annotations via a pristine clone.
- Updated the typebox remap test to assert the serialized ark wire.

Fixes #8143
2026-08-10 08:02:04 +00:00
roboomp e414585155 fix(extensions): preserved unsafe schemas during install
- Lifted legacy Type.Unsafe documents into callable omptype schemas so Optional and Object composition preserve validation and required fields.
- Ran installed extension factories against the normal throwaway loader surface before accepting a plugin install.
- Added regression coverage and documented the stronger rollback gate.

Fixes #8143
2026-08-10 07:37:28 +00:00
Duncan Ogilvie 0cf54f428c fix(extensions): preserve mutation queue ownership 2026-08-10 01:40:18 +02:00
Duncan Ogilvie 36ed07378c fix(extensions): bound detached tool activation 2026-08-10 01:04:02 +02:00
Duncan Ogilvie 4d0c346f87 fix(extensions): abort timed-out tool activations 2026-08-10 00:54:42 +02:00
Duncan Ogilvie c5c686ca11 fix(extensions): drain tool-call registrations 2026-08-10 00:24:11 +02:00
Duncan Ogilvie 0abb4a9529 fix(extensions): preserve late tool invariants 2026-08-09 23:51:39 +02:00