The previous reset dropped #pendingXdevMountDelta alongside the announced
baseline. Unlike /new and different-session switchSession, branch() does
not rebuild the base system prompt afterward, and because the device is
already in mountedNames no later refresh re-queues an add delta. Dropping
the undelivered delta therefore left the branched transcript unaware of a
still-mounted discoverable device.
Only the announced baseline is reset now; pending adds (still-live mounts
awaiting delivery) survive and announce on the next prompt in the new
transcript. Redundant on /new (the rebuilt prompt lists them too) but
harmless, and correct for branch.
Fixes#6921
(cherry picked from commit 5866440f27c15a320657e25fbfa0d2d65aad1fa2)
The announced-mount baseline persisted across /new, switchSession, and
branch, which replace agent.state.messages but only clear session-scoped
tool state. A device announced in the old transcript stayed in the cache,
so reconnecting it into the fresh history was filtered as already known
and never announced, leaving the new conversation unaware of the device.
Reset the announced baseline (and any undelivered pending delta) from
#clearSessionScopedToolState, so the next notice re-seeds from the new
transcript and a reconnecting device announces again.
Fixes#6921
(cherry picked from commit d06dde02b9de4aacacd7aea5ee51edc7e524e3fb)
Replayed the stable added and removed inventory sections from legacy
xdev-mount-notice content when structured details are absent. This keeps
the first post-upgrade resume from re-announcing devices that persisted
history already introduced.
Covered both structured and legacy resume histories, including removed
devices and inline docs that must not be interpreted as inventory.
Fixes#6921
(cherry picked from commit 634a4c2de75f99e219f408c56bed83c04fe1290a)
Mount-notice injection diff-gated only against the in-memory mountedNames
set, which is reseeded on every process resume / host reconnect. Dynamic
devices (MCP / RPC host) already announced in persisted history therefore
re-announced, splicing a redundant developer message that busts the
provider prompt-cache prefix and re-bills the whole suffix at full price
on metered providers.
Notices now persist a structured { added, removed } payload. On the first
consumption after resume the announced-device baseline is reconstructed
from history, and only a net change relative to what the model already
knows is announced, so a resume re-establishing the same inventory emits
nothing.
Fixes#6921
(cherry picked from commit 03c2ed5189510f41431bd164fa80187a69ed8de9)
formatAvailableResources only listed concrete resources, so the
assistant-facing summary shown on a failed mcp:// read understated the
server's contract even though template routing and /mcp resources
already handle them. Now templates are listed alongside concrete
resources.
Fixes#6911
(cherry picked from commit 254f35bb04cda8a615bbe1291d73b13cac0eabaa)
#emit's listeners (ACP's #handleLifetimeEvent -> #pushConfigOptionUpdate
-> #buildConfigOptions) run synchronously up to their first await, and
#buildConfigOptions is evaluated as a synchronous argument expression
before that await. Emitting model_changed immediately after
agent.setModel(previousModel) but before #models.restoreThinkingSnapshot
ran meant ACP could push a { previousModel, target-session-thinking }
config that was never an actual session state -- neither the failed
target nor the restored previous session.
Move the emit after restoreThinkingSnapshot/restoreServiceTiers so it
observes fully-restored state, same as every other rollback consumer
in this catch block already does implicitly by running after both.
Found by Codex on PR #6908 (pullrequestreview-4801303428), against
a33aa07df from this same branch.
bun test test/acp-agent.test.ts (57) + agent-session-switch-prev-context,
agent-session-model-persistence, agent-session-model-switch-auth,
agent-session-openai-completions-model-switch, nonvision-model-switch
-- 90 pass, 0 fail. Workspace typecheck clean.
(cherry picked from commit 0ac190a39a1687ebf85849dde5e1af9c2f9147fc)
STATE_TRIGGER_EVENTS (host.ts) gates the debounced state broadcast
that carries CollabSessionState.model to collab guests. model_changed
was absent, so a guest's model display went stale on the same
internal switches (prewalk hand-off, retry-fallback, model cycling)
this PR fixes for ACP/RPC/TUI, until an unrelated trigger
(agent_start/message_end/...) or the 2s streaming-interval tick
happened to fire first.
#buildState() already reads session.model live, so this is purely a
trigger-registration gap -- no schema change.
Extends the PR's Unreleased changelog entry to cover the TUI render
fix, the collab fix, and the rollback corrective-event fix landed in
this branch.
(cherry picked from commit 066ed2b7c729b7d8b3f4424b2c19f4b65d631f08)
handleEvent has no blanket pre-render (removed for issue #4353), so
each handler must explicitly schedule one when it changes something
visible -- every other statusLine.invalidate() call site in this file
pairs it with ui.requestRender(). The new model_changed handler only
invalidated the cache, so an internal model switch (prewalk hand-off,
retry-fallback) while the TUI was otherwise idle left the status line
showing the stale model until an unrelated event happened to render.
Flagged by @chatgpt-codex-connector on PR #6908.
(cherry picked from commit f565e3a4956bda467b6679a3c3f1e48379395a78)
switchSession's success path may already have called
#setModelWithProviderSessionReset for the target session's model,
which emits model_changed for it. If a later step in the try block
then throws, the catch restores previousModel via a direct
agent.setModel(...) that bypasses that method entirely and emitted
nothing — ACP/RPC/TUI kept advertising the target model that was
never actually committed.
Emit model_changed from the rollback path too, but only when the
restore actually changes the model back (guards the common case where
switchSession never touched the model or restores the same one).
Flagged independently by @roboomp and @chatgpt-codex-connector on
PR #6908.
bun test test/acp-agent.test.ts (57), agent-session-switch-prev-context.test.ts
(3), agent-session-model-persistence.test.ts (10 files) -- 80 pass, 0 fail.
(cherry picked from commit a33aa07df6fd61508956d73cc5b5284051bbfa86)
model_changed has no extension-facing hook (#emitExtensionEvent never
maps it), unlike message_start/tool_execution_end/etc. Routing it
through #emitSessionEvent added an await on extension delivery plus
the FIFO subscriber gate inside every model switch, including
retry-fallback on the hot error-recovery path, for zero benefit.
Match the sibling thinking_level_changed event, which already goes
through the plain synchronous #emit. ACP/RPC/TUI still receive it
identically since they subscribe via #eventListeners either way.
No observable behavior change: bun test test/acp-agent.test.ts stays
at 57 pass, dedup logic for client-initiated model changes unaffected
(push still lands during the awaited #setModelById call).
(cherry picked from commit 3edc9f8495b1b06fd990a64756d92e60354451fc)
Zed (and any other ACP client) never learned about a model switch that
happened from inside the agent loop — prewalk hand-offs, retry-fallback,
model cycling — because #pushConfigOptionUpdate was only ever wired to
the client-initiated setSessionConfigOption/setSessionMode RPCs and to
the thinking_level_changed lifetime event. The model itself did switch
correctly (subsequent requests used the new model), but the client's
model picker/status bar kept showing the session's starting model.
#handleLifetimeEvent now also reacts to the model_changed event added
in the previous commit and re-pushes config_option_update. Extend the
existing thinking-only subscription dedupe in setSessionConfigOption to
cover the model config id too, so a client-initiated model change still
produces exactly one notification once the lifetime subscription is
installed.
Regression tests mirror the existing thinking-level coverage:
- 'pushes config_option_update when the model changes internally'
- 'emits a single config_option_update per setSessionConfigOption(model) call'
Verified: bun test test/acp-agent.test.ts (57/57), plus
agent-session-prewalk.test.ts, agent-session-retry-fallback.test.ts,
retry-fallback.test.ts, model-resolver.test.ts, and the other acp-*.test.ts
files all still pass; tsgo --noEmit and biome check clean.
(cherry picked from commit f5c5081088e8cbac2650a9de89049731de1b2777)
AgentSession#setModelWithProviderSessionReset is the single choke point
every model mutation runs through (explicit /model, prewalk hand-offs,
retry-fallback, model cycling). It previously changed agent.state.model
silently — no session event told subscribers (ACP, RPC, TUI) that the
active model moved.
Emit a new model_changed AgentSessionEvent from that choke point
whenever the model actually changes, and wire it into every consumer
that must exhaustively handle AgentSessionEvent: the TUI event
controller (invalidates the status line, same as thinking_level_changed)
and the RPC client's forwarded-event allowlist.
(cherry picked from commit f76325de2c7821dd7046ddb67546577c3575a263)
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.
`omp ttsr test <file>` inferred the match source from the path extension
against a hardcoded allowlist (`SOURCE_FILE_EXT`); a supplied source file
whose extension was absent silently fell through to the text (prose)
context, where tool-scoped rules can never match. The result was a false
negative indistinguishable from a non-matching regex, contradicting the
documented "a positional that resolves to a file defaults to tool/edit
context" contract.
- Emit an explanatory note when a resolvable file path is supplied and the
source is inferred as `text`, pointing at `--source tool --tool edit`.
Surfaced in both text and `--json` output via `TestReport.inferenceNote`.
- Extend the allowlist with the .NET family and other common source
languages (cs, razor, cshtml, fs, fsx, vb, sh, bash, sql, zig, dart,
scala, ex, exs, proto, tf).
Left the fall-through default itself unchanged (inverting the test is a
behaviour change and a maintainer call).
Fixes#6887
- Update isActionableDisable in usage-cli.ts to accept active accounts and check identity matches.
- Suppress credential disable tombstones when an active account exists for the same provider and identity.
- Add test coverage for suppressing tombstones when active accounts share the same identity.