Converted user-attributed collab prompt frames to prioritized user messages only on the model-facing path, preserving guest details in persisted transcript frames.
Added regression coverage for the provider role, steering envelope, and retained guest attribution.
Fixes#7288
- Reused the agent's explicit or inherited cache identity for ephemeral side turns.
- Forwarded the same effective key through manual and automatic native compaction.
- Added regression coverage for all three secondary request paths.
Fixes#7218
- Replaced the reactive weekly-only auto-redeem predicate with a pool-wide
planner: an expiry-salvage sweep piggybacks on the 5-minute usage
heartbeat and spends any account's reset that would otherwise expire
within codexResets.salvageHorizonHours, and the blocked-turn path scans
all stored accounts with eligibility built from the exact exhausted
5h/weekly windows (openai/codex#28525), unblocking at the latest reset
among them.
- Made the live 429's parsed unblock timestamp authoritative for the
active account (pre-block snapshots survive cache invalidation via
in-flight adoption and last-good fallback), synthesizing the candidate
when no usable report exists, and overlaying live credit counts from
the dedicated credits route since a stale /wham/usage zero is never
corrected upstream.
- Treated nothing_to_reset, credit_list_failed, and thrown consumes as
non-terminal: the episode key is released and deferred 30 minutes
instead of burying a banked credit; redeemResetCredit now spends the
soonest-expiring credit.
- Added planner unit fixtures plus integration regressions driving the
real triggers end to end, with an injectable per-session coordinator
seam and a sweep settlement handle.
- Subagents inherit the parent's eval session id, so a child's
reset: true destroyed the co-owned kernel and every sibling's
interpreter state mid-session.
- resolveOwnerScopedSessionKey now routes a reset from a non-exclusive
owner onto a deterministic per-owner fork key: the requester gets a
fresh private kernel, co-owners keep the shared one, and the fork
stays sticky for that owner until its teardown reaps it.
- Applied across Python, JavaScript, Ruby, and Julia executors; JS
contexts gained an owner registry plus disposeVmContextsByOwner,
wired into EvalRunner.disposeKernels and SDK session teardown.
- Covered by pure key-resolution contracts and an end-to-end JS test:
co-owner reset forks, shared state survives, fork is sticky, and
per-owner dispose reaps only the fork.
- #7163's context-token anchoring re-triggered #7153's dead-end warning on
the pre-prompt pass; a one-shot marker now spans agent loops and re-arms
only when a persisted cut point appears.
- Updated scrollback tests to the Alt+L display-reset binding main adopted.
Applied the extension-command eligibility check inside the shared AgentSession title gate so editor and CLI bootstrap submissions cannot diverge.
Added direct regression coverage for programmatic startup submissions.
Fixes#7166
Moved automatic title eligibility and persistence into AgentSession so editor and CLI bootstrap submissions share one path.
Added a PTY regression probe covering the positional-message launch flow.
Fixes#7166
Move the dead-end rearm probe below the mid-run turn-persistence barrier so prepareCompaction sees the just-finished assistant and tool result.
Delay message-end persistence in the regression and require compaction at the second tool boundary, before another provider request can be sent.
Fixes#7151
The dead-end mark parked the live tool-loop context permanently, but the array keeps growing: a later smaller tool result can move the oversized turn to the summarizable side. Recheck prepareCompaction each boundary and re-attempt once a cut point appears, instead of suppressing until provider overflow.
Added a regression proving maintenance re-attempts once a cut point becomes available.
Fixes#7151
Remember no-progress mid-turn compaction results for the live agent-loop context so later tool boundaries skip identical rescue work. The weak context key naturally resets for the next user turn.
Added a scripted regression covering one warning and one attempt per oversized tool-loop turn.
Fixes#7151
A before_agent_start extension can replace the base system prompt after the
mount notice was consumed. Catalog-backed additions were then marked
announced and suppressed even though the provider request no longer contained
the catalog.
Reserve the notice's pre-user message position, wait until the extension has
selected the final prompt, and suppress catalog-backed additions only when no
per-turn replacement dropped the base catalog. Explicit replacements retain
the mount notice as the device-discovery channel.
Fixes#7139
Marking rebuild-exposed devices announced (and deleting them from the pending
delta) at rebuild time broke add/remove coalescing: a device mounted by
deferred discovery then unmounted before the first user prompt would leave the
device marked announced with the add already gone, so the unmount produced a
spurious "No longer mounted" notice for a device the model never saw.
Record the catalog the current base prompt exposes in #basePromptXdevNames and
apply the suppression in takePendingXdevMountNotice at delivery instead. The
pending delta is left untouched by rebuilds, so #notifyXdevMountDelta still
cancels an undelivered add against a later remove.
Fixes#7139
On a fresh session with deferred MCP discovery the post-discovery prompt
rebuild renders the full mounted xd:// device catalog, yet the pre-user
xdev-mount-notice re-listed the same names because announcement tracking was
never updated by the rebuild. This double-billed the entire mounted MCP
inventory into the first model request.
rebuildSystemPrompt now reports the catalog it rendered via
BuildSystemPromptResult.xdevCatalogNames, and applyActiveToolsByName folds
those devices into the announced-mount baseline (marking them announced and
dropping them from the pending delta). Notices for mount changes the rebuild
did not expose, and all unmount notices, remain intact.
Fixes#7139
- Add the `app.live.toggle` keybinding defaulted to `Ctrl+L` to start or stop live voice mode.
- Remap the default display-reset action (`app.display.reset`) from `Ctrl+L` to `Alt+L`.
- Update the live visualizer to listen for stop keys so the toggle chord terminates active sessions.
- Captured the prompt generation before an IRC wake and passed it to the post-prompt recovery wait.
- Stops the wait from following a successor turn once an abort supersedes the wake, so the wake monitor finalizes promptly instead of misattributing successor progress.
Fixes#7105
- Kept IRC run monitors attached through post-prompt retry and continuation recovery.
- Flushed the final deferred agent_end before terminal lifecycle emission.
- Covered empty-stop retry ordering for RPC event subscribers.
Fixes#7105
- Monitored autonomous IRC wake turns with the task executor lifecycle and progress channels.
- Preserved monitoring after idle-TTL parking and session revival.
- Covered RPC subscriptions for both idle and parked keep-alive agents.
Fixes#7105
- Removed copy and delete operations across tokenizer, parser, grammar, and clipboard logic.
- Standardized line-editing operations and block resolvers to use cut exclusively.
- Updated documentation, prompts, and test suites to reflect the removal of copy and delete syntax.
- Implemented clipboard register management, parsing, and execution rules for CUT, COPY, and PASTE operations in the hashline engine.
- Added session-persistent clipboard state and integration across agent session execution, diff previews, and streaming tools.
- Added comprehensive validation, error messages, recovery handling, and test coverage for clipboard and block operations.
Three remaining review findings:
- `list_mcp_resources` frames a handler answered now synthesize a
`list_mcp_resources` block and pair a result derived from the same
answer sent on the wire; the streamed `ListMcpResourcesToolCall` /
`ReadMcpResourceToolCall` announcements join the exec-owned set so
they cannot double-render. No-handler frames still synthesize
nothing, since nothing ran.
- Advisors receive the same `MCPManager`-backed resource adapter as the
primary bridge, so their `list_mcp_resources` no longer reports every
server as empty and `read_mcp_resource` no longer answers `not_found`
against live connections the advisor shares.
- An unavailable `pi_edit`/`pi_write` answers with the protocol's
`rejected` variant instead of `error`: refusal and failure are
separate oneof cases, and a denial reported as an execution error
invites a retry of an operation that was never permitted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SSWZTe6YA2PX1cqtukZvYi
(cherry picked from commit 47ce936c8df05d6970504af19e5ef7d2e8c38d7b)
Download-mode resource reads created and overwrote workspace files
without running a registry tool - the same hole the native `delete`
frame had - so a session that withheld `write`/`edit`, or whose `write`
tier is `deny`/`always-ask`, still had files written. Both frames now
share one grant and one policy check, and the download refuses before
the read so a blocked call never fetches the resource.
`allowNativeDelete` is renamed `allowDirectFileMutation`: it now gates
more than deletion. The primary session derives it from the registry
BEFORE its own rewriting (Cursor moves `edit` out of the tool map and
`write` may be auto-registered later, so reading the map at bridge
construction would misjudge both) and unconditionally, since the bridge
is installed for every session and one that starts on another provider
can switch to Cursor later.
`pi_grep` with a match cap: the local tool windows to 20 files and
suggests `skip`, which `PiGrepExecArgs` cannot express - 100 matches
requested over 25 one-match files returned 20, with the cap reported
unreached. A capped search now reads cap+1 files, so a result landing
exactly on the cap is distinguishable from a clipped one, and
`match_limit_reached` is truthful either way.
`read_mcp_resource` synthesized no transcript block and paired no
result, so a read - including a download that mutates the workspace -
was invisible in the UI and stripped from every rebuilt history. It now
synthesizes a `read_mcp_resource` block (not `read`: the name drives
rendering and prune semantics) and pairs success, not-found and error.
(cherry picked from commit 5ff27a3efe8bec522d9d5dbd7763055eb03eae3b)
Two independent bugs found in review.
A stream that dies mid-turn takes the terminal-error path: `settleH2`
rejects when the transport closes without `turnEnded`, so the flush on
the success path never runs. `connect_scm` and native todo blocks are
stamped `kCursorExecResolved` at start, so `agent-loop.ts` synthesizes
no placeholder and only their completion frame pairs a result - the call
was left unpaired and its card animating, and `buildSessionContext`
strips a dangling call from every rebuilt transcript. The catch path now
closes open blocks and pairs those server-owned calls with an
interrupted result. Exec-settled MCP blocks are excluded: the dispatch
that ran them owns their result and `drainInFlightDispatches` awaits it,
so pairing here would duplicate against the same id.
Separately, the advisor bridge supplied no `getToolContext`.
`ExtensionToolWrapper` reads the approval mode, per-tool policies and
`autoApprove` only from that execute-time context, so every wrapped
advisor bridge tool resolved as `yolo` with empty policies - a
configured `ask` or `deny` on `edit`/`grep` did not apply to native
frames. Advisors now get the same `ToolContextStore` as the primary
bridge.
Both are covered against the real paths: the interrupted call through
the HTTP/2 fixture server (a helper-level test passes even with the
catch-path flush removed), and approval through real deny policies.
(cherry picked from commit 5ace682578af96708caf88db2d44b9077a1c0e74)
The primary bridge builds a `replace`-mode `EditTool` because
`PiEditExecArgs` carries `old_text`/`new_text` pairs that no other mode
accepts. The advisor roster passed its own instances straight through,
and those follow the session's configured `edit.mode` - `hashline` by
default, whose schema is a single `input` string - so every native
advisor edit failed validation instead of touching the file.
Both bridge-only tools now come from `cursor-bridge-tools.ts`:
`createBridgeEditTool` builds the wrapped `replace` instance, and
`bridgeToolMap` substitutes it into a granted map. The substitution is
gated on `edit` actually having been granted, since the tool is
constructed rather than looked up - handing one to a read-only roster is
the #5680 escalation. The advisor's own loop keeps its instance; only
the exec map is swapped.
(cherry picked from commit e6cf9f8046c595cab9793b4b2a5795d8488d8d22)
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)